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
82extern int	cannot_audit(int);
83#endif
84extern void	aug_init(void);
85extern void	aug_save_auid(au_id_t);
86extern void	aug_save_uid(uid_t);
87extern void	aug_save_euid(uid_t);
88extern void	aug_save_gid(gid_t);
89extern void	aug_save_egid(gid_t);
90extern void	aug_save_pid(pid_t);
91extern void	aug_save_asid(au_asid_t);
92extern void	aug_save_tid(dev_t, unsigned int);
93extern void	aug_save_tid_ex(dev_t, u_int32_t *, u_int32_t);
94extern int	aug_save_me(void);
95extern int	aug_save_namask(void);
96extern void	aug_save_event(au_event_t);
97extern void	aug_save_sorf(int);
98extern void	aug_save_text(char *);
99extern void	aug_save_text1(char *);
100extern void	aug_save_text2(char *);
101extern void	aug_save_na(int);
102extern void	aug_save_user(char *);
103extern void	aug_save_path(char *);
104extern int	aug_save_policy(void);
105extern void	aug_save_afunc(int (*)(int));
106extern int	aug_audit(void);
107extern int	aug_na_selected(void);
108extern int	aug_selected(void);
109extern int	aug_daemon_session(void);
110
111#ifndef HAVE_GETTEXT
112# define gettext(a)	(a)
113#endif
114
115extern Authctxt *the_authctxt;
116static 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)
132extern int 	aug_get_machine(char *, u_int32_t *, u_int32_t *);
133#else
134static int
135aug_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 */
178int
179getacna(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 */
234static int
235selected(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
254static void
255bsm_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
296static void
297bsm_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
329static void
330bsm_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
349void
350audit_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
375void
376audit_run_command(const char *command)
377{
378	/* not implemented */
379}
380
381void
382audit_session_open(struct logininfo *li)
383{
384	/* not implemented */
385}
386
387void
388audit_session_close(struct logininfo *li)
389{
390	/* not implemented */
391}
392
393void
394audit_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