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