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