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
aug_get_machine(char * host,u_int32_t * addr,u_int32_t * type)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
getacna(char * auditstring,int len)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
selected(char * username,uid_t uid,au_event_t event,int sf)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
bsm_audit_record(int typ,char * string,au_event_t event_no)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
bsm_audit_session_setup(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
bsm_audit_bad_login(const char * what)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
audit_connection_from(const char * host,int port)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
audit_run_command(const char * command)377 audit_run_command(const char *command)
378 {
379 /* not implemented */
380 }
381
382 void
audit_session_open(struct logininfo * li)383 audit_session_open(struct logininfo *li)
384 {
385 /* not implemented */
386 }
387
388 void
audit_session_close(struct logininfo * li)389 audit_session_close(struct logininfo *li)
390 {
391 /* not implemented */
392 }
393
394 void
audit_event(ssh_audit_event_t event)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 */
456