Import OpenSSH-6.1p1.
[dragonfly.git] / crypto / openssh / audit-bsm.c
1 /* $Id: audit-bsm.c,v 1.8 2012/02/23 23:40:43 dtucker Exp $ */
2
3 /*
4  * TODO
5  *
6  * - deal with overlap between this and sys_auth_allowed_user
7  *   sys_auth_record_login and record_failed_login.
8  */
9
10 /*
11  * Copyright 1988-2002 Sun Microsystems, Inc.  All rights reserved.
12  * Use is subject to license terms.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  */
35 /* #pragma ident        "@(#)bsmaudit.c 1.1     01/09/17 SMI" */
36
37 #include "includes.h"
38 #if defined(USE_BSM_AUDIT)
39
40 #include <sys/types.h>
41
42 #include <errno.h>
43 #include <netdb.h>
44 #include <stdarg.h>
45 #include <string.h>
46 #include <unistd.h>
47
48 #ifdef BROKEN_BSM_API
49 #include <libscf.h>
50 #endif
51
52 #include "ssh.h"
53 #include "log.h"
54 #include "key.h"
55 #include "hostfile.h"
56 #include "auth.h"
57 #include "xmalloc.h"
58
59 #ifndef AUE_openssh
60 # define AUE_openssh     32800
61 #endif
62 #include <bsm/audit.h>
63 #include <bsm/libbsm.h>
64 #include <bsm/audit_uevents.h>
65 #include <bsm/audit_record.h>
66 #include <locale.h>
67
68 #if defined(HAVE_GETAUDIT_ADDR)
69 #define AuditInfoStruct         auditinfo_addr
70 #define AuditInfoTermID         au_tid_addr_t
71 #define SetAuditFunc(a,b)       setaudit_addr((a),(b))
72 #define SetAuditFuncText        "setaudit_addr"
73 #define AUToSubjectFunc         au_to_subject_ex
74 #define AUToReturnFunc(a,b)     au_to_return32((a), (int32_t)(b))
75 #else
76 #define AuditInfoStruct         auditinfo
77 #define AuditInfoTermID         au_tid_t
78 #define SetAuditFunc(a,b)       setaudit(a)
79 #define SetAuditFuncText        "setaudit"
80 #define AUToSubjectFunc         au_to_subject
81 #define AUToReturnFunc(a,b)     au_to_return((a), (u_int)(b))
82 #endif
83
84 #ifndef cannot_audit
85 extern int      cannot_audit(int);
86 #endif
87 extern void     aug_init(void);
88 extern void     aug_save_auid(au_id_t);
89 extern void     aug_save_uid(uid_t);
90 extern void     aug_save_euid(uid_t);
91 extern void     aug_save_gid(gid_t);
92 extern void     aug_save_egid(gid_t);
93 extern void     aug_save_pid(pid_t);
94 extern void     aug_save_asid(au_asid_t);
95 extern void     aug_save_tid(dev_t, unsigned int);
96 extern void     aug_save_tid_ex(dev_t, u_int32_t *, u_int32_t);
97 extern int      aug_save_me(void);
98 extern int      aug_save_namask(void);
99 extern void     aug_save_event(au_event_t);
100 extern void     aug_save_sorf(int);
101 extern void     aug_save_text(char *);
102 extern void     aug_save_text1(char *);
103 extern void     aug_save_text2(char *);
104 extern void     aug_save_na(int);
105 extern void     aug_save_user(char *);
106 extern void     aug_save_path(char *);
107 extern int      aug_save_policy(void);
108 extern void     aug_save_afunc(int (*)(int));
109 extern int      aug_audit(void);
110 extern int      aug_na_selected(void);
111 extern int      aug_selected(void);
112 extern int      aug_daemon_session(void);
113
114 #ifndef HAVE_GETTEXT
115 # define gettext(a)     (a)
116 #endif
117
118 extern Authctxt *the_authctxt;
119 static AuditInfoTermID ssh_bsm_tid;
120
121 #ifdef BROKEN_BSM_API
122 /* For some reason this constant is no longer defined
123    in Solaris 11. */
124 #define BSM_TEXTBUFSZ 256
125 #endif
126
127 /* Below is the low-level BSM interface code */
128
129 /*
130  * aug_get_machine is only required on IPv6 capable machines, we use a
131  * different mechanism in audit_connection_from() for IPv4-only machines.
132  * getaudit_addr() is only present on IPv6 capable machines.
133  */
134 #if defined(HAVE_AUG_GET_MACHINE) || !defined(HAVE_GETAUDIT_ADDR)
135 extern int      aug_get_machine(char *, u_int32_t *, u_int32_t *);
136 #else
137 static int
138 aug_get_machine(char *host, u_int32_t *addr, u_int32_t *type)
139 {
140         struct addrinfo *ai; 
141         struct sockaddr_in *in4;
142         struct sockaddr_in6 *in6;
143         int ret = 0, r;
144
145         if ((r = getaddrinfo(host, NULL, NULL, &ai)) != 0) {
146                 error("BSM audit: getaddrinfo failed for %.100s: %.100s", host,
147                     r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r));
148                 return -1;
149         }
150         
151         switch (ai->ai_family) {
152         case AF_INET:
153                 in4 = (struct sockaddr_in *)ai->ai_addr;
154                 *type = AU_IPv4;
155                 memcpy(addr, &in4->sin_addr, sizeof(struct in_addr));
156                 break;
157 #ifdef AU_IPv6
158         case AF_INET6: 
159                 in6 = (struct sockaddr_in6 *)ai->ai_addr;
160                 *type = AU_IPv6;
161                 memcpy(addr, &in6->sin6_addr, sizeof(struct in6_addr));
162                 break;
163 #endif
164         default:
165                 error("BSM audit: unknown address family for %.100s: %d",
166                     host, ai->ai_family);
167                 ret = -1;
168         }
169         freeaddrinfo(ai);
170         return ret;
171 }
172 #endif
173
174 #ifdef BROKEN_BSM_API
175 /*
176   In Solaris 11 the audit daemon has been moved to SMF. In the process
177   they simply dropped getacna() from the API, since it read from a now
178   non-existent config file. This function re-implements getacna() to
179   read from the SMF repository instead.
180  */
181 int
182 getacna(char *auditstring, int len)
183 {
184         scf_handle_t *handle = NULL;
185         scf_property_t *property = NULL;
186         scf_value_t *value = NULL;
187         int ret = 0;
188
189         handle = scf_handle_create(SCF_VERSION);
190         if (handle == NULL) 
191                 return -2; /* The man page for getacna on Solaris 10 states
192                               we should return -2 in case of error and set
193                               errno to indicate the error. We don't bother
194                               with errno here, though, since the only use
195                               of this function below doesn't check for errors
196                               anyway. 
197                            */
198
199         ret = scf_handle_bind(handle);
200         if (ret == -1) 
201                 return -2;
202
203         property = scf_property_create(handle);
204         if (property == NULL) 
205                 return -2;
206
207         ret = scf_handle_decode_fmri(handle, 
208              "svc:/system/auditd:default/:properties/preselection/naflags",
209                                      NULL, NULL, NULL, NULL, property, 0);
210         if (ret == -1) 
211                 return -2;
212
213         value = scf_value_create(handle);
214         if (value == NULL) 
215                 return -2;
216
217         ret = scf_property_get_value(property, value);
218         if (ret == -1) 
219                 return -2;
220
221         ret = scf_value_get_astring(value, auditstring, len);
222         if (ret == -1) 
223                 return -2;
224
225         scf_value_destroy(value);
226         scf_property_destroy(property);
227         scf_handle_destroy(handle);
228
229         return 0;
230 }
231 #endif
232
233 /*
234  * Check if the specified event is selected (enabled) for auditing.
235  * Returns 1 if the event is selected, 0 if not and -1 on failure.
236  */
237 static int
238 selected(char *username, uid_t uid, au_event_t event, int sf)
239 {
240         int rc, sorf;
241         char naflags[512];
242         struct au_mask mask;
243
244         mask.am_success = mask.am_failure = 0;
245         if (uid < 0) {
246                 /* get flags for non-attributable (to a real user) events */
247                 rc = getacna(naflags, sizeof(naflags));
248                 if (rc == 0)
249                         (void) getauditflagsbin(naflags, &mask);
250         } else
251                 rc = au_user_mask(username, &mask);
252
253         sorf = (sf == 0) ? AU_PRS_SUCCESS : AU_PRS_FAILURE;
254         return(au_preselect(event, &mask, sorf, AU_PRS_REREAD));
255 }
256
257 static void
258 bsm_audit_record(int typ, char *string, au_event_t event_no)
259 {
260         int             ad, rc, sel;
261         uid_t           uid = -1;
262         gid_t           gid = -1;
263         pid_t           pid = getpid();
264         AuditInfoTermID tid = ssh_bsm_tid;
265
266         if (the_authctxt != NULL && the_authctxt->valid) {
267                 uid = the_authctxt->pw->pw_uid;
268                 gid = the_authctxt->pw->pw_gid;
269         }
270
271         rc = (typ == 0) ? 0 : -1;
272         sel = selected(the_authctxt->user, uid, event_no, rc);
273         debug3("BSM audit: typ %d rc %d \"%s\"", typ, rc, string);
274         if (!sel)
275                 return; /* audit event does not match mask, do not write */
276
277         debug3("BSM audit: writing audit new record");
278         ad = au_open();
279
280         (void) au_write(ad, AUToSubjectFunc(uid, uid, gid, uid, gid,
281             pid, pid, &tid));
282         (void) au_write(ad, au_to_text(string));
283         (void) au_write(ad, AUToReturnFunc(typ, rc));
284
285 #ifdef BROKEN_BSM_API
286         /* The last argument is the event modifier flags. For
287            some seemingly undocumented reason it was added in
288            Solaris 11. */
289         rc = au_close(ad, AU_TO_WRITE, event_no, 0);
290 #else
291         rc = au_close(ad, AU_TO_WRITE, event_no);
292 #endif
293
294         if (rc < 0)
295                 error("BSM audit: %s failed to write \"%s\" record: %s",
296                     __func__, string, strerror(errno));
297 }
298
299 static void
300 bsm_audit_session_setup(void)
301 {
302         int rc;
303         struct AuditInfoStruct info;
304         au_mask_t mask;
305
306         if (the_authctxt == NULL) {
307                 error("BSM audit: session setup internal error (NULL ctxt)");
308                 return;
309         }
310
311         if (the_authctxt->valid)
312                 info.ai_auid = the_authctxt->pw->pw_uid;
313         else
314                 info.ai_auid = -1;
315         info.ai_asid = getpid();
316         mask.am_success = 0;
317         mask.am_failure = 0;
318
319         (void) au_user_mask(the_authctxt->user, &mask);
320
321         info.ai_mask.am_success  = mask.am_success;
322         info.ai_mask.am_failure  = mask.am_failure;
323
324         info.ai_termid = ssh_bsm_tid;
325
326         rc = SetAuditFunc(&info, sizeof(info));
327         if (rc < 0)
328                 error("BSM audit: %s: %s failed: %s", __func__,
329                     SetAuditFuncText, strerror(errno));
330 }
331
332 static void
333 bsm_audit_bad_login(const char *what)
334 {
335         char textbuf[BSM_TEXTBUFSZ];
336
337         if (the_authctxt->valid) {
338                 (void) snprintf(textbuf, sizeof (textbuf),
339                         gettext("invalid %s for user %s"),
340                             what, the_authctxt->user);
341                 bsm_audit_record(4, textbuf, AUE_openssh);
342         } else {
343                 (void) snprintf(textbuf, sizeof (textbuf),
344                         gettext("invalid user name \"%s\""),
345                             the_authctxt->user);
346                 bsm_audit_record(3, textbuf, AUE_openssh);
347         }
348 }
349
350 /* Below is the sshd audit API code */
351
352 void
353 audit_connection_from(const char *host, int port)
354 {
355         AuditInfoTermID *tid = &ssh_bsm_tid;
356         char buf[1024];
357
358         if (cannot_audit(0))
359                 return;
360         debug3("BSM audit: connection from %.100s port %d", host, port);
361
362         /* populate our terminal id structure */
363 #if defined(HAVE_GETAUDIT_ADDR)
364         tid->at_port = (dev_t)port;
365         aug_get_machine((char *)host, &(tid->at_addr[0]), &(tid->at_type));
366         snprintf(buf, sizeof(buf), "%08x %08x %08x %08x", tid->at_addr[0],
367             tid->at_addr[1], tid->at_addr[2], tid->at_addr[3]);
368         debug3("BSM audit: iptype %d machine ID %s", (int)tid->at_type, buf);
369 #else
370         /* this is used on IPv4-only machines */
371         tid->port = (dev_t)port;
372         tid->machine = inet_addr(host);
373         snprintf(buf, sizeof(buf), "%08x", tid->machine);
374         debug3("BSM audit: machine ID %s", buf);
375 #endif
376 }
377
378 void
379 audit_run_command(const char *command)
380 {
381         /* not implemented */
382 }
383
384 void
385 audit_session_open(struct logininfo *li)
386 {
387         /* not implemented */
388 }
389
390 void
391 audit_session_close(struct logininfo *li)
392 {
393         /* not implemented */
394 }
395
396 void
397 audit_event(ssh_audit_event_t event)
398 {
399         char    textbuf[BSM_TEXTBUFSZ];
400         static int logged_in = 0;
401         const char *user = the_authctxt ? the_authctxt->user : "(unknown user)";
402
403         if (cannot_audit(0))
404                 return;
405
406         switch(event) {
407         case SSH_AUTH_SUCCESS:
408                 logged_in = 1;
409                 bsm_audit_session_setup();
410                 snprintf(textbuf, sizeof(textbuf),
411                     gettext("successful login %s"), user);
412                 bsm_audit_record(0, textbuf, AUE_openssh);
413                 break;
414
415         case SSH_CONNECTION_CLOSE:
416                 /*
417                  * We can also get a close event if the user attempted auth
418                  * but never succeeded.
419                  */
420                 if (logged_in) {
421                         snprintf(textbuf, sizeof(textbuf),
422                             gettext("sshd logout %s"), the_authctxt->user);
423                         bsm_audit_record(0, textbuf, AUE_logout);
424                 } else {
425                         debug("%s: connection closed without authentication",
426                             __func__);
427                 }
428                 break;
429
430         case SSH_NOLOGIN:
431                 bsm_audit_record(1,
432                     gettext("logins disabled by /etc/nologin"), AUE_openssh);
433                 break;
434
435         case SSH_LOGIN_EXCEED_MAXTRIES:
436                 snprintf(textbuf, sizeof(textbuf),
437                     gettext("too many tries for user %s"), the_authctxt->user);
438                 bsm_audit_record(1, textbuf, AUE_openssh);
439                 break;
440
441         case SSH_LOGIN_ROOT_DENIED:
442                 bsm_audit_record(2, gettext("not_console"), AUE_openssh);
443                 break;
444
445         case SSH_AUTH_FAIL_PASSWD:
446                 bsm_audit_bad_login("password");
447                 break;
448
449         case SSH_AUTH_FAIL_KBDINT:
450                 bsm_audit_bad_login("interactive password entry");
451                 break;
452
453         default:
454                 debug("%s: unhandled event %d", __func__, event);
455         }
456 }
457 #endif /* BSM */