1240075Sdes/* $Id: audit-bsm.c,v 1.8 2012/02/23 23:40:43 dtucker Exp $ */
2146998Sdes
3146998Sdes/*
4146998Sdes * TODO
5146998Sdes *
6146998Sdes * - deal with overlap between this and sys_auth_allowed_user
7146998Sdes *   sys_auth_record_login and record_failed_login.
8146998Sdes */
9146998Sdes
10146998Sdes/*
11146998Sdes * Copyright 1988-2002 Sun Microsystems, Inc.  All rights reserved.
12146998Sdes * Use is subject to license terms.
13146998Sdes *
14146998Sdes * Redistribution and use in source and binary forms, with or without
15146998Sdes * modification, are permitted provided that the following conditions
16146998Sdes * are met:
17146998Sdes * 1. Redistributions of source code must retain the above copyright
18146998Sdes *    notice, this list of conditions and the following disclaimer.
19146998Sdes * 2. Redistributions in binary form must reproduce the above copyright
20146998Sdes *    notice, this list of conditions and the following disclaimer in the
21146998Sdes *    documentation and/or other materials provided with the distribution.
22146998Sdes *
23146998Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24146998Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25146998Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26146998Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27146998Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28146998Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29146998Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30146998Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31146998Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32146998Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33146998Sdes *
34146998Sdes */
35146998Sdes/* #pragma ident	"@(#)bsmaudit.c	1.1	01/09/17 SMI" */
36146998Sdes
37146998Sdes#include "includes.h"
38146998Sdes#if defined(USE_BSM_AUDIT)
39146998Sdes
40162852Sdes#include <sys/types.h>
41162852Sdes
42162859Sdes#include <errno.h>
43181111Sdes#include <netdb.h>
44162852Sdes#include <stdarg.h>
45181111Sdes#include <string.h>
46162852Sdes#include <unistd.h>
47162852Sdes
48240075Sdes#ifdef BROKEN_BSM_API
49240075Sdes#include <libscf.h>
50240075Sdes#endif
51240075Sdes
52146998Sdes#include "ssh.h"
53146998Sdes#include "log.h"
54162852Sdes#include "key.h"
55162852Sdes#include "hostfile.h"
56146998Sdes#include "auth.h"
57146998Sdes#include "xmalloc.h"
58146998Sdes
59146998Sdes#ifndef AUE_openssh
60146998Sdes# define AUE_openssh     32800
61146998Sdes#endif
62146998Sdes#include <bsm/audit.h>
63146998Sdes#include <bsm/libbsm.h>
64146998Sdes#include <bsm/audit_uevents.h>
65146998Sdes#include <bsm/audit_record.h>
66146998Sdes#include <locale.h>
67146998Sdes
68146998Sdes#if defined(HAVE_GETAUDIT_ADDR)
69146998Sdes#define	AuditInfoStruct		auditinfo_addr
70146998Sdes#define AuditInfoTermID		au_tid_addr_t
71146998Sdes#define SetAuditFunc(a,b)	setaudit_addr((a),(b))
72146998Sdes#define SetAuditFuncText	"setaudit_addr"
73146998Sdes#define AUToSubjectFunc		au_to_subject_ex
74146998Sdes#define AUToReturnFunc(a,b)	au_to_return32((a), (int32_t)(b))
75146998Sdes#else
76146998Sdes#define	AuditInfoStruct		auditinfo
77146998Sdes#define AuditInfoTermID		au_tid_t
78146998Sdes#define SetAuditFunc(a,b)	setaudit(a)
79146998Sdes#define SetAuditFuncText	"setaudit"
80146998Sdes#define AUToSubjectFunc		au_to_subject
81146998Sdes#define AUToReturnFunc(a,b)	au_to_return((a), (u_int)(b))
82146998Sdes#endif
83146998Sdes
84181111Sdes#ifndef cannot_audit
85146998Sdesextern int	cannot_audit(int);
86181111Sdes#endif
87146998Sdesextern void	aug_init(void);
88146998Sdesextern void	aug_save_auid(au_id_t);
89146998Sdesextern void	aug_save_uid(uid_t);
90146998Sdesextern void	aug_save_euid(uid_t);
91146998Sdesextern void	aug_save_gid(gid_t);
92146998Sdesextern void	aug_save_egid(gid_t);
93146998Sdesextern void	aug_save_pid(pid_t);
94146998Sdesextern void	aug_save_asid(au_asid_t);
95146998Sdesextern void	aug_save_tid(dev_t, unsigned int);
96146998Sdesextern void	aug_save_tid_ex(dev_t, u_int32_t *, u_int32_t);
97146998Sdesextern int	aug_save_me(void);
98146998Sdesextern int	aug_save_namask(void);
99146998Sdesextern void	aug_save_event(au_event_t);
100146998Sdesextern void	aug_save_sorf(int);
101146998Sdesextern void	aug_save_text(char *);
102146998Sdesextern void	aug_save_text1(char *);
103146998Sdesextern void	aug_save_text2(char *);
104146998Sdesextern void	aug_save_na(int);
105146998Sdesextern void	aug_save_user(char *);
106146998Sdesextern void	aug_save_path(char *);
107146998Sdesextern int	aug_save_policy(void);
108146998Sdesextern void	aug_save_afunc(int (*)(int));
109146998Sdesextern int	aug_audit(void);
110146998Sdesextern int	aug_na_selected(void);
111146998Sdesextern int	aug_selected(void);
112146998Sdesextern int	aug_daemon_session(void);
113146998Sdes
114146998Sdes#ifndef HAVE_GETTEXT
115146998Sdes# define gettext(a)	(a)
116146998Sdes#endif
117146998Sdes
118146998Sdesextern Authctxt *the_authctxt;
119146998Sdesstatic AuditInfoTermID ssh_bsm_tid;
120146998Sdes
121240075Sdes#ifdef BROKEN_BSM_API
122240075Sdes/* For some reason this constant is no longer defined
123240075Sdes   in Solaris 11. */
124240075Sdes#define BSM_TEXTBUFSZ 256
125240075Sdes#endif
126240075Sdes
127146998Sdes/* Below is the low-level BSM interface code */
128146998Sdes
129146998Sdes/*
130181111Sdes * aug_get_machine is only required on IPv6 capable machines, we use a
131181111Sdes * different mechanism in audit_connection_from() for IPv4-only machines.
132181111Sdes * getaudit_addr() is only present on IPv6 capable machines.
133181111Sdes */
134181111Sdes#if defined(HAVE_AUG_GET_MACHINE) || !defined(HAVE_GETAUDIT_ADDR)
135181111Sdesextern int 	aug_get_machine(char *, u_int32_t *, u_int32_t *);
136181111Sdes#else
137181111Sdesstatic int
138181111Sdesaug_get_machine(char *host, u_int32_t *addr, u_int32_t *type)
139181111Sdes{
140181111Sdes	struct addrinfo *ai;
141181111Sdes	struct sockaddr_in *in4;
142181111Sdes	struct sockaddr_in6 *in6;
143181111Sdes	int ret = 0, r;
144181111Sdes
145181111Sdes	if ((r = getaddrinfo(host, NULL, NULL, &ai)) != 0) {
146181111Sdes		error("BSM audit: getaddrinfo failed for %.100s: %.100s", host,
147181111Sdes		    r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r));
148181111Sdes		return -1;
149181111Sdes	}
150181111Sdes
151181111Sdes	switch (ai->ai_family) {
152181111Sdes	case AF_INET:
153181111Sdes		in4 = (struct sockaddr_in *)ai->ai_addr;
154181111Sdes		*type = AU_IPv4;
155181111Sdes		memcpy(addr, &in4->sin_addr, sizeof(struct in_addr));
156181111Sdes		break;
157181111Sdes#ifdef AU_IPv6
158181111Sdes	case AF_INET6:
159181111Sdes		in6 = (struct sockaddr_in6 *)ai->ai_addr;
160181111Sdes		*type = AU_IPv6;
161181111Sdes		memcpy(addr, &in6->sin6_addr, sizeof(struct in6_addr));
162181111Sdes		break;
163181111Sdes#endif
164181111Sdes	default:
165181111Sdes		error("BSM audit: unknown address family for %.100s: %d",
166181111Sdes		    host, ai->ai_family);
167181111Sdes		ret = -1;
168181111Sdes	}
169181111Sdes	freeaddrinfo(ai);
170181111Sdes	return ret;
171181111Sdes}
172181111Sdes#endif
173181111Sdes
174240075Sdes#ifdef BROKEN_BSM_API
175181111Sdes/*
176240075Sdes  In Solaris 11 the audit daemon has been moved to SMF. In the process
177240075Sdes  they simply dropped getacna() from the API, since it read from a now
178240075Sdes  non-existent config file. This function re-implements getacna() to
179240075Sdes  read from the SMF repository instead.
180240075Sdes */
181240075Sdesint
182240075Sdesgetacna(char *auditstring, int len)
183240075Sdes{
184240075Sdes	scf_handle_t *handle = NULL;
185240075Sdes	scf_property_t *property = NULL;
186240075Sdes	scf_value_t *value = NULL;
187240075Sdes	int ret = 0;
188240075Sdes
189240075Sdes	handle = scf_handle_create(SCF_VERSION);
190240075Sdes	if (handle == NULL)
191240075Sdes	        return -2; /* The man page for getacna on Solaris 10 states
192240075Sdes			      we should return -2 in case of error and set
193240075Sdes			      errno to indicate the error. We don't bother
194240075Sdes			      with errno here, though, since the only use
195240075Sdes			      of this function below doesn't check for errors
196240075Sdes			      anyway.
197240075Sdes			   */
198240075Sdes
199240075Sdes	ret = scf_handle_bind(handle);
200240075Sdes	if (ret == -1)
201240075Sdes	        return -2;
202240075Sdes
203240075Sdes	property = scf_property_create(handle);
204240075Sdes	if (property == NULL)
205240075Sdes	        return -2;
206240075Sdes
207240075Sdes	ret = scf_handle_decode_fmri(handle,
208240075Sdes	     "svc:/system/auditd:default/:properties/preselection/naflags",
209240075Sdes				     NULL, NULL, NULL, NULL, property, 0);
210240075Sdes	if (ret == -1)
211240075Sdes	        return -2;
212240075Sdes
213240075Sdes	value = scf_value_create(handle);
214240075Sdes	if (value == NULL)
215240075Sdes	        return -2;
216240075Sdes
217240075Sdes	ret = scf_property_get_value(property, value);
218240075Sdes	if (ret == -1)
219240075Sdes	        return -2;
220240075Sdes
221240075Sdes	ret = scf_value_get_astring(value, auditstring, len);
222240075Sdes	if (ret == -1)
223240075Sdes	        return -2;
224240075Sdes
225240075Sdes	scf_value_destroy(value);
226240075Sdes	scf_property_destroy(property);
227240075Sdes	scf_handle_destroy(handle);
228240075Sdes
229240075Sdes	return 0;
230240075Sdes}
231240075Sdes#endif
232240075Sdes
233240075Sdes/*
234146998Sdes * Check if the specified event is selected (enabled) for auditing.
235146998Sdes * Returns 1 if the event is selected, 0 if not and -1 on failure.
236146998Sdes */
237146998Sdesstatic int
238146998Sdesselected(char *username, uid_t uid, au_event_t event, int sf)
239146998Sdes{
240146998Sdes	int rc, sorf;
241146998Sdes	char naflags[512];
242146998Sdes	struct au_mask mask;
243146998Sdes
244146998Sdes	mask.am_success = mask.am_failure = 0;
245146998Sdes	if (uid < 0) {
246146998Sdes		/* get flags for non-attributable (to a real user) events */
247146998Sdes		rc = getacna(naflags, sizeof(naflags));
248146998Sdes		if (rc == 0)
249146998Sdes			(void) getauditflagsbin(naflags, &mask);
250146998Sdes	} else
251146998Sdes		rc = au_user_mask(username, &mask);
252146998Sdes
253146998Sdes	sorf = (sf == 0) ? AU_PRS_SUCCESS : AU_PRS_FAILURE;
254146998Sdes	return(au_preselect(event, &mask, sorf, AU_PRS_REREAD));
255146998Sdes}
256146998Sdes
257146998Sdesstatic void
258146998Sdesbsm_audit_record(int typ, char *string, au_event_t event_no)
259146998Sdes{
260146998Sdes	int		ad, rc, sel;
261146998Sdes	uid_t		uid = -1;
262146998Sdes	gid_t		gid = -1;
263146998Sdes	pid_t		pid = getpid();
264146998Sdes	AuditInfoTermID	tid = ssh_bsm_tid;
265146998Sdes
266146998Sdes	if (the_authctxt != NULL && the_authctxt->valid) {
267146998Sdes		uid = the_authctxt->pw->pw_uid;
268146998Sdes		gid = the_authctxt->pw->pw_gid;
269146998Sdes	}
270146998Sdes
271146998Sdes	rc = (typ == 0) ? 0 : -1;
272146998Sdes	sel = selected(the_authctxt->user, uid, event_no, rc);
273146998Sdes	debug3("BSM audit: typ %d rc %d \"%s\"", typ, rc, string);
274146998Sdes	if (!sel)
275146998Sdes		return;	/* audit event does not match mask, do not write */
276146998Sdes
277146998Sdes	debug3("BSM audit: writing audit new record");
278146998Sdes	ad = au_open();
279146998Sdes
280146998Sdes	(void) au_write(ad, AUToSubjectFunc(uid, uid, gid, uid, gid,
281146998Sdes	    pid, pid, &tid));
282146998Sdes	(void) au_write(ad, au_to_text(string));
283146998Sdes	(void) au_write(ad, AUToReturnFunc(typ, rc));
284146998Sdes
285240075Sdes#ifdef BROKEN_BSM_API
286240075Sdes	/* The last argument is the event modifier flags. For
287240075Sdes	   some seemingly undocumented reason it was added in
288240075Sdes	   Solaris 11. */
289240075Sdes	rc = au_close(ad, AU_TO_WRITE, event_no, 0);
290240075Sdes#else
291146998Sdes	rc = au_close(ad, AU_TO_WRITE, event_no);
292240075Sdes#endif
293240075Sdes
294146998Sdes	if (rc < 0)
295146998Sdes		error("BSM audit: %s failed to write \"%s\" record: %s",
296146998Sdes		    __func__, string, strerror(errno));
297146998Sdes}
298146998Sdes
299146998Sdesstatic void
300146998Sdesbsm_audit_session_setup(void)
301146998Sdes{
302146998Sdes	int rc;
303146998Sdes	struct AuditInfoStruct info;
304146998Sdes	au_mask_t mask;
305146998Sdes
306146998Sdes	if (the_authctxt == NULL) {
307146998Sdes		error("BSM audit: session setup internal error (NULL ctxt)");
308146998Sdes		return;
309146998Sdes	}
310146998Sdes
311146998Sdes	if (the_authctxt->valid)
312146998Sdes		info.ai_auid = the_authctxt->pw->pw_uid;
313146998Sdes	else
314146998Sdes		info.ai_auid = -1;
315146998Sdes	info.ai_asid = getpid();
316146998Sdes	mask.am_success = 0;
317146998Sdes	mask.am_failure = 0;
318146998Sdes
319146998Sdes	(void) au_user_mask(the_authctxt->user, &mask);
320146998Sdes
321146998Sdes	info.ai_mask.am_success  = mask.am_success;
322146998Sdes	info.ai_mask.am_failure  = mask.am_failure;
323146998Sdes
324146998Sdes	info.ai_termid = ssh_bsm_tid;
325146998Sdes
326146998Sdes	rc = SetAuditFunc(&info, sizeof(info));
327146998Sdes	if (rc < 0)
328146998Sdes		error("BSM audit: %s: %s failed: %s", __func__,
329146998Sdes		    SetAuditFuncText, strerror(errno));
330146998Sdes}
331146998Sdes
332146998Sdesstatic void
333146998Sdesbsm_audit_bad_login(const char *what)
334146998Sdes{
335146998Sdes	char textbuf[BSM_TEXTBUFSZ];
336146998Sdes
337146998Sdes	if (the_authctxt->valid) {
338146998Sdes		(void) snprintf(textbuf, sizeof (textbuf),
339146998Sdes			gettext("invalid %s for user %s"),
340146998Sdes			    what, the_authctxt->user);
341146998Sdes		bsm_audit_record(4, textbuf, AUE_openssh);
342146998Sdes	} else {
343146998Sdes		(void) snprintf(textbuf, sizeof (textbuf),
344146998Sdes			gettext("invalid user name \"%s\""),
345146998Sdes			    the_authctxt->user);
346146998Sdes		bsm_audit_record(3, textbuf, AUE_openssh);
347146998Sdes	}
348146998Sdes}
349146998Sdes
350146998Sdes/* Below is the sshd audit API code */
351146998Sdes
352146998Sdesvoid
353146998Sdesaudit_connection_from(const char *host, int port)
354146998Sdes{
355146998Sdes	AuditInfoTermID *tid = &ssh_bsm_tid;
356146998Sdes	char buf[1024];
357146998Sdes
358146998Sdes	if (cannot_audit(0))
359146998Sdes		return;
360146998Sdes	debug3("BSM audit: connection from %.100s port %d", host, port);
361146998Sdes
362146998Sdes	/* populate our terminal id structure */
363146998Sdes#if defined(HAVE_GETAUDIT_ADDR)
364146998Sdes	tid->at_port = (dev_t)port;
365146998Sdes	aug_get_machine((char *)host, &(tid->at_addr[0]), &(tid->at_type));
366146998Sdes	snprintf(buf, sizeof(buf), "%08x %08x %08x %08x", tid->at_addr[0],
367146998Sdes	    tid->at_addr[1], tid->at_addr[2], tid->at_addr[3]);
368146998Sdes	debug3("BSM audit: iptype %d machine ID %s", (int)tid->at_type, buf);
369146998Sdes#else
370146998Sdes	/* this is used on IPv4-only machines */
371146998Sdes	tid->port = (dev_t)port;
372146998Sdes	tid->machine = inet_addr(host);
373146998Sdes	snprintf(buf, sizeof(buf), "%08x", tid->machine);
374146998Sdes	debug3("BSM audit: machine ID %s", buf);
375146998Sdes#endif
376146998Sdes}
377146998Sdes
378146998Sdesvoid
379146998Sdesaudit_run_command(const char *command)
380146998Sdes{
381146998Sdes	/* not implemented */
382146998Sdes}
383146998Sdes
384146998Sdesvoid
385221420Sdesaudit_session_open(struct logininfo *li)
386146998Sdes{
387146998Sdes	/* not implemented */
388146998Sdes}
389146998Sdes
390146998Sdesvoid
391221420Sdesaudit_session_close(struct logininfo *li)
392146998Sdes{
393146998Sdes	/* not implemented */
394146998Sdes}
395146998Sdes
396146998Sdesvoid
397146998Sdesaudit_event(ssh_audit_event_t event)
398146998Sdes{
399146998Sdes	char    textbuf[BSM_TEXTBUFSZ];
400146998Sdes	static int logged_in = 0;
401146998Sdes	const char *user = the_authctxt ? the_authctxt->user : "(unknown user)";
402146998Sdes
403146998Sdes	if (cannot_audit(0))
404146998Sdes		return;
405146998Sdes
406146998Sdes	switch(event) {
407146998Sdes	case SSH_AUTH_SUCCESS:
408146998Sdes		logged_in = 1;
409146998Sdes		bsm_audit_session_setup();
410146998Sdes		snprintf(textbuf, sizeof(textbuf),
411146998Sdes		    gettext("successful login %s"), user);
412146998Sdes		bsm_audit_record(0, textbuf, AUE_openssh);
413146998Sdes		break;
414146998Sdes
415146998Sdes	case SSH_CONNECTION_CLOSE:
416146998Sdes		/*
417146998Sdes		 * We can also get a close event if the user attempted auth
418146998Sdes		 * but never succeeded.
419146998Sdes		 */
420146998Sdes		if (logged_in) {
421146998Sdes			snprintf(textbuf, sizeof(textbuf),
422146998Sdes			    gettext("sshd logout %s"), the_authctxt->user);
423146998Sdes			bsm_audit_record(0, textbuf, AUE_logout);
424146998Sdes		} else {
425146998Sdes			debug("%s: connection closed without authentication",
426146998Sdes			    __func__);
427146998Sdes		}
428146998Sdes		break;
429146998Sdes
430146998Sdes	case SSH_NOLOGIN:
431146998Sdes		bsm_audit_record(1,
432146998Sdes		    gettext("logins disabled by /etc/nologin"), AUE_openssh);
433146998Sdes		break;
434146998Sdes
435146998Sdes	case SSH_LOGIN_EXCEED_MAXTRIES:
436146998Sdes		snprintf(textbuf, sizeof(textbuf),
437146998Sdes		    gettext("too many tries for user %s"), the_authctxt->user);
438146998Sdes		bsm_audit_record(1, textbuf, AUE_openssh);
439146998Sdes		break;
440146998Sdes
441146998Sdes	case SSH_LOGIN_ROOT_DENIED:
442146998Sdes		bsm_audit_record(2, gettext("not_console"), AUE_openssh);
443146998Sdes		break;
444146998Sdes
445146998Sdes	case SSH_AUTH_FAIL_PASSWD:
446146998Sdes		bsm_audit_bad_login("password");
447146998Sdes		break;
448146998Sdes
449146998Sdes	case SSH_AUTH_FAIL_KBDINT:
450146998Sdes		bsm_audit_bad_login("interactive password entry");
451146998Sdes		break;
452146998Sdes
453146998Sdes	default:
454146998Sdes		debug("%s: unhandled event %d", __func__, event);
455146998Sdes	}
456146998Sdes}
457146998Sdes#endif /* BSM */
458