xref: /dragonfly/crypto/openssh/audit-bsm.c (revision a3127495)
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 */
456