xref: /freebsd/crypto/openssh/audit-bsm.c (revision cfd6422a5217410fbd66f7a7a8a64d9d85e61229)
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 */
455