17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
54e5fbfedStz  * Common Development and Distribution License (the "License").
64e5fbfedStz  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*005d3febSMarek Pospisil  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  * This file contains the auditing system call code.
287c478bd9Sstevel@tonic-gate  *
297c478bd9Sstevel@tonic-gate  */
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate #include <sys/param.h>
327c478bd9Sstevel@tonic-gate #include <sys/systm.h>
337c478bd9Sstevel@tonic-gate #include <sys/user.h>
347c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
357c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
367c478bd9Sstevel@tonic-gate #include <sys/session.h>	/* for session structure (auditctl(2) */
377c478bd9Sstevel@tonic-gate #include <sys/kmem.h>		/* for KM_SLEEP */
3867dbe2beSCasper H.S. Dik #include <sys/cred.h>
397c478bd9Sstevel@tonic-gate #include <sys/types.h>
407c478bd9Sstevel@tonic-gate #include <sys/proc.h>
417c478bd9Sstevel@tonic-gate #include <sys/uio.h>
427c478bd9Sstevel@tonic-gate #include <sys/file.h>
437c478bd9Sstevel@tonic-gate #include <sys/stat.h>
447c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
457c478bd9Sstevel@tonic-gate #include <sys/acct.h>
467c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
477c478bd9Sstevel@tonic-gate #include <sys/exec.h>
487c478bd9Sstevel@tonic-gate #include <sys/thread.h>
497c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
507c478bd9Sstevel@tonic-gate #include <sys/debug.h>
517c478bd9Sstevel@tonic-gate #include <sys/disp.h>
527c478bd9Sstevel@tonic-gate #include <sys/kobj.h>
537c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
547c478bd9Sstevel@tonic-gate #include <sys/policy.h>
557c478bd9Sstevel@tonic-gate #include <sys/taskq.h>
567c478bd9Sstevel@tonic-gate #include <sys/zone.h>
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate #include <c2/audit.h>
597c478bd9Sstevel@tonic-gate #include <c2/audit_kernel.h>
607c478bd9Sstevel@tonic-gate #include <c2/audit_record.h>
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate #define	HEADER_SIZE64	1;
637c478bd9Sstevel@tonic-gate #define	HEADER_SIZE32	0;
647c478bd9Sstevel@tonic-gate #define	AU_MIN_FILE_SZ	0x80000	/* minumum audit file size */
657c478bd9Sstevel@tonic-gate #define	AUDIT_REC_SIZE	0x8000	/* maximum user audit record size */
667c478bd9Sstevel@tonic-gate 
67*005d3febSMarek Pospisil extern pri_t	minclsyspri;	/* priority for taskq */
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate static clock_t	au_resid = 15;	/* wait .15 sec before droping a rec */
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate static void	au_output_thread();
72*005d3febSMarek Pospisil 
737c478bd9Sstevel@tonic-gate /*
747c478bd9Sstevel@tonic-gate  * This is the loadable module wrapper.
757c478bd9Sstevel@tonic-gate  */
767c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate /*
797c478bd9Sstevel@tonic-gate  * Module linkage information for the kernel.
807c478bd9Sstevel@tonic-gate  */
81*005d3febSMarek Pospisil static struct modlmisc modlmisc = {
82*005d3febSMarek Pospisil 	&mod_miscops, "Solaris Auditing (C2)"
837c478bd9Sstevel@tonic-gate };
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
86*005d3febSMarek Pospisil 	MODREV_1, (void *)&modlmisc, 0
877c478bd9Sstevel@tonic-gate };
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate int
_init()907c478bd9Sstevel@tonic-gate _init()
917c478bd9Sstevel@tonic-gate {
92*005d3febSMarek Pospisil 	return (mod_install(&modlinkage));
937c478bd9Sstevel@tonic-gate }
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate int
_fini()967c478bd9Sstevel@tonic-gate _fini()
977c478bd9Sstevel@tonic-gate {
987c478bd9Sstevel@tonic-gate 	return (EBUSY);
997c478bd9Sstevel@tonic-gate }
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate int
_info(struct modinfo * modinfop)1027c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
1037c478bd9Sstevel@tonic-gate {
1047c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
1057c478bd9Sstevel@tonic-gate }
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate /*
1087c478bd9Sstevel@tonic-gate  * The audit system call. Trust what the user has sent down and save it
1097c478bd9Sstevel@tonic-gate  * away in the audit file. User passes a complete audit record and its
1107c478bd9Sstevel@tonic-gate  * length.  We will fill in the time stamp, check the header and the length
1117c478bd9Sstevel@tonic-gate  * Put a trailer and a sequence token if policy requires.
1127c478bd9Sstevel@tonic-gate  * In the future length might become size_t instead of an int.
1137c478bd9Sstevel@tonic-gate  *
1147c478bd9Sstevel@tonic-gate  * The call is valid whether or not AUDIT_PERZONE is set (think of
1157c478bd9Sstevel@tonic-gate  * login to a zone).  When the local audit state (auk_auditstate) is
1167c478bd9Sstevel@tonic-gate  * AUC_INIT_AUDIT, records are accepted even though auditd isn't
1177c478bd9Sstevel@tonic-gate  * running.
1187c478bd9Sstevel@tonic-gate  */
1197c478bd9Sstevel@tonic-gate int
audit(caddr_t record,int length)1207c478bd9Sstevel@tonic-gate audit(caddr_t record, int length)
1217c478bd9Sstevel@tonic-gate {
1227c478bd9Sstevel@tonic-gate 	char	c;
1237c478bd9Sstevel@tonic-gate 	int	count, l;
1247c478bd9Sstevel@tonic-gate 	token_t	*m, *n, *s, *ad;
1257c478bd9Sstevel@tonic-gate 	int	hdrlen, delta;
1267c478bd9Sstevel@tonic-gate 	adr_t	hadr;
1277c478bd9Sstevel@tonic-gate 	adr_t	sadr;
1287c478bd9Sstevel@tonic-gate 	int	size;	/* 0: 32 bit utility  1: 64 bit utility */
1297c478bd9Sstevel@tonic-gate 	int	host_len;
1307c478bd9Sstevel@tonic-gate 	size_t	zlen;
1319e9e6ab8Spaulson 	au_kcontext_t	*kctx = GET_KCTX_PZ;
132*005d3febSMarek Pospisil 	uint32_t auditing;
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 	/* if auditing not enabled, then don't generate an audit record */
135*005d3febSMarek Pospisil 	auditing = (U2A(u)->tad_audit != AUC_UNSET) ?
136*005d3febSMarek Pospisil 	    U2A(u)->tad_audit : kctx->auk_auditstate;
137*005d3febSMarek Pospisil 	if (auditing & ~(AUC_AUDITING | AUC_INIT_AUDIT))
1387c478bd9Sstevel@tonic-gate 		return (0);
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate 	/* Only privileged processes can audit */
1417c478bd9Sstevel@tonic-gate 	if (secpolicy_audit_modify(CRED()) != 0)
1427c478bd9Sstevel@tonic-gate 		return (EPERM);
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate 	/* Max user record size is 32K */
1457c478bd9Sstevel@tonic-gate 	if (length > AUDIT_REC_SIZE)
1467c478bd9Sstevel@tonic-gate 		return (E2BIG);
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate 	/*
1497c478bd9Sstevel@tonic-gate 	 * The specified length must be at least as big as the smallest
1507c478bd9Sstevel@tonic-gate 	 * possible header token. Later after beginning to scan the
1517c478bd9Sstevel@tonic-gate 	 * header we'll determine the true minimum length according to
1527c478bd9Sstevel@tonic-gate 	 * the header type and attributes.
1537c478bd9Sstevel@tonic-gate 	 */
1547c478bd9Sstevel@tonic-gate #define	AU_MIN_HEADER_LEN	(sizeof (char) + sizeof (int32_t) + \
1557c478bd9Sstevel@tonic-gate 	sizeof (char) + sizeof (short) + sizeof (short) + \
1567c478bd9Sstevel@tonic-gate 	(sizeof (int32_t) * 2))
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate 	if (length < AU_MIN_HEADER_LEN)
1597c478bd9Sstevel@tonic-gate 		return (EINVAL);
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate 	/* Read in user's audit record */
1627c478bd9Sstevel@tonic-gate 	count = length;
1637c478bd9Sstevel@tonic-gate 	m = n = s = ad = NULL;
1647c478bd9Sstevel@tonic-gate 	while (count) {
1657c478bd9Sstevel@tonic-gate 		m = au_getclr();
1667c478bd9Sstevel@tonic-gate 		if (!s)
1677c478bd9Sstevel@tonic-gate 			s = n = m;
1687c478bd9Sstevel@tonic-gate 		else {
1697c478bd9Sstevel@tonic-gate 			n->next_buf = m;
1707c478bd9Sstevel@tonic-gate 			n = m;
1717c478bd9Sstevel@tonic-gate 		}
1727c478bd9Sstevel@tonic-gate 		l = MIN(count, AU_BUFSIZE);
173787b48eaSgww 		if (copyin(record, memtod(m, caddr_t), (size_t)l)) {
174787b48eaSgww 			/* copyin failed release au_membuf */
175787b48eaSgww 			au_free_rec(s);
176787b48eaSgww 			return (EFAULT);
1777c478bd9Sstevel@tonic-gate 		}
1787c478bd9Sstevel@tonic-gate 		record += l;
1797c478bd9Sstevel@tonic-gate 		count -= l;
1807c478bd9Sstevel@tonic-gate 		m->len = (uchar_t)l;
1817c478bd9Sstevel@tonic-gate 	}
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate 	/* Now attach the entire thing to ad */
1847c478bd9Sstevel@tonic-gate 	au_write((caddr_t *)&(ad), s);
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate 	/* validate header token type. trust everything following it */
1877c478bd9Sstevel@tonic-gate 	adr_start(&hadr, memtod(s, char *));
1887c478bd9Sstevel@tonic-gate 	(void) adr_getchar(&hadr, &c);
1897c478bd9Sstevel@tonic-gate 	switch (c) {
1907c478bd9Sstevel@tonic-gate 	case AUT_HEADER32:
1917c478bd9Sstevel@tonic-gate 		/* size vers+event_ID+event_modifier fields */
1927c478bd9Sstevel@tonic-gate 		delta = 1 + 2 + 2;
1937c478bd9Sstevel@tonic-gate 		hdrlen = 1 + 4 + delta + (sizeof (int32_t) * 2);
1947c478bd9Sstevel@tonic-gate 		size = HEADER_SIZE32;
1957c478bd9Sstevel@tonic-gate 		break;
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate #ifdef _LP64
1987c478bd9Sstevel@tonic-gate 	case AUT_HEADER64:
1997c478bd9Sstevel@tonic-gate 		/* size vers+event_ID+event_modifier fields */
2007c478bd9Sstevel@tonic-gate 		delta = 1 + 2 + 2;
2017c478bd9Sstevel@tonic-gate 		hdrlen = 1 + 4 + delta + (sizeof (int64_t) * 2);
2027c478bd9Sstevel@tonic-gate 		size = HEADER_SIZE64;
2037c478bd9Sstevel@tonic-gate 		break;
2047c478bd9Sstevel@tonic-gate #endif
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 	case AUT_HEADER32_EX:
2077c478bd9Sstevel@tonic-gate 		/*
2087c478bd9Sstevel@tonic-gate 		 * Skip over the length/version/type/mod fields and
2097c478bd9Sstevel@tonic-gate 		 * grab the host address type (length), then rewind.
2107c478bd9Sstevel@tonic-gate 		 * This is safe per the previous minimum length check.
2117c478bd9Sstevel@tonic-gate 		 */
2127c478bd9Sstevel@tonic-gate 		hadr.adr_now += 9;
2137c478bd9Sstevel@tonic-gate 		(void) adr_getint32(&hadr, &host_len);
2147c478bd9Sstevel@tonic-gate 		hadr.adr_now -= 9 + sizeof (int32_t);
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 		/* size: vers+event_ID+event_modifier+IP_type+IP_addr_array */
2177c478bd9Sstevel@tonic-gate 		delta = 1 + 2 + 2 + 4 + host_len;
2187c478bd9Sstevel@tonic-gate 		hdrlen = 1 + 4 + delta + (sizeof (int32_t) * 2);
2197c478bd9Sstevel@tonic-gate 		size = HEADER_SIZE32;
2207c478bd9Sstevel@tonic-gate 		break;
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate #ifdef _LP64
2237c478bd9Sstevel@tonic-gate 	case AUT_HEADER64_EX:
2247c478bd9Sstevel@tonic-gate 		/*
2257c478bd9Sstevel@tonic-gate 		 * Skip over the length/version/type/mod fields and grab
2267c478bd9Sstevel@tonic-gate 		 * the host address type (length), then rewind.
2277c478bd9Sstevel@tonic-gate 		 * This is safe per the previous minimum length check.
2287c478bd9Sstevel@tonic-gate 		 */
2297c478bd9Sstevel@tonic-gate 		hadr.adr_now += 9;
2307c478bd9Sstevel@tonic-gate 		(void) adr_getint32(&hadr, &host_len);
2317c478bd9Sstevel@tonic-gate 		hadr.adr_now -= 9 + sizeof (int32_t);
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 		/* size: vers+event_ID+event_modifier+IP_type+IP_addr_array */
2347c478bd9Sstevel@tonic-gate 		delta = 1 + 2 + 2 + 4 + host_len;
2357c478bd9Sstevel@tonic-gate 		hdrlen = 1 + 4 + delta + (sizeof (int64_t) * 2);
2367c478bd9Sstevel@tonic-gate 		size = HEADER_SIZE64;
2377c478bd9Sstevel@tonic-gate 		break;
2387c478bd9Sstevel@tonic-gate #endif
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	default:
2417c478bd9Sstevel@tonic-gate 		/* Header is wrong, reject message */
2427c478bd9Sstevel@tonic-gate 		au_free_rec(s);
2437c478bd9Sstevel@tonic-gate 		return (EINVAL);
2447c478bd9Sstevel@tonic-gate 	}
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 	if (length < hdrlen) {
2477c478bd9Sstevel@tonic-gate 		au_free_rec(s);
2487c478bd9Sstevel@tonic-gate 		return (0);
2497c478bd9Sstevel@tonic-gate 	}
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 	/* advance over header token length field */
2527c478bd9Sstevel@tonic-gate 	hadr.adr_now += 4;
2537c478bd9Sstevel@tonic-gate 
2547c478bd9Sstevel@tonic-gate 	/* validate version */
2557c478bd9Sstevel@tonic-gate 	(void) adr_getchar(&hadr, &c);
2567c478bd9Sstevel@tonic-gate 	if (c != TOKEN_VERSION) {
2577c478bd9Sstevel@tonic-gate 		/* version is wrong, reject message */
2587c478bd9Sstevel@tonic-gate 		au_free_rec(s);
2597c478bd9Sstevel@tonic-gate 		return (EINVAL);
2607c478bd9Sstevel@tonic-gate 	}
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 	/* backup to header length field (including version field) */
2637c478bd9Sstevel@tonic-gate 	hadr.adr_now -= 5;
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate 	/*
2667c478bd9Sstevel@tonic-gate 	 * add on the zonename token if policy AUDIT_ZONENAME is set
2677c478bd9Sstevel@tonic-gate 	 */
2687c478bd9Sstevel@tonic-gate 	if (kctx->auk_policy & AUDIT_ZONENAME) {
2691d7bfecdStz 		zlen = au_zonename_length(NULL);
2707c478bd9Sstevel@tonic-gate 		if (zlen > 0) {
2717c478bd9Sstevel@tonic-gate 			length += zlen;
2721d7bfecdStz 			m = au_to_zonename(zlen, NULL);
2737c478bd9Sstevel@tonic-gate 			(void) au_append_rec(ad, m, AU_PACK);
2747c478bd9Sstevel@tonic-gate 		}
2757c478bd9Sstevel@tonic-gate 	}
2767c478bd9Sstevel@tonic-gate 	/* Add an (optional) sequence token. NULL offset if none */
2777c478bd9Sstevel@tonic-gate 	if (kctx->auk_policy & AUDIT_SEQ) {
2787c478bd9Sstevel@tonic-gate 		/* get the sequnce token */
2797c478bd9Sstevel@tonic-gate 		m = au_to_seq();
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate 		/* sequence token 5 bytes long */
2827c478bd9Sstevel@tonic-gate 		length += 5;
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 		/* link to audit record (i.e. don't pack the data) */
2857c478bd9Sstevel@tonic-gate 		(void) au_append_rec(ad, m, AU_LINK);
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate 		/* advance to count field of token */
2887c478bd9Sstevel@tonic-gate 		adr_start(&sadr, memtod(m, char *));
2897c478bd9Sstevel@tonic-gate 		sadr.adr_now += 1;
2907c478bd9Sstevel@tonic-gate 	} else
2917c478bd9Sstevel@tonic-gate 		sadr.adr_now = (char *)NULL;
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate 	/* add the (optional) trailer token */
2947c478bd9Sstevel@tonic-gate 	if (kctx->auk_policy & AUDIT_TRAIL) {
2957c478bd9Sstevel@tonic-gate 		/* trailer token is 7 bytes long */
2967c478bd9Sstevel@tonic-gate 		length += 7;
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 		/* append to audit record */
2997c478bd9Sstevel@tonic-gate 		(void) au_append_rec(ad, au_to_trailer(length), AU_PACK);
3007c478bd9Sstevel@tonic-gate 	}
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 	/* audit record completely assembled. set the length */
3037c478bd9Sstevel@tonic-gate 	adr_int32(&hadr, (int32_t *)&length, 1);
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 	/* advance to date/time field of header */
3067c478bd9Sstevel@tonic-gate 	hadr.adr_now += delta;
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate 	/* We are done  put it on the queue */
3097c478bd9Sstevel@tonic-gate 	AS_INC(as_generated, 1, kctx);
3107c478bd9Sstevel@tonic-gate 	AS_INC(as_audit, 1, kctx);
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 	au_enqueue(kctx, s, &hadr, &sadr, size, 0);
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate 	AS_INC(as_totalsize, length, kctx);
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate 	return (0);
3177c478bd9Sstevel@tonic-gate }
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate /*
3207c478bd9Sstevel@tonic-gate  * auditdoor starts a kernel thread to generate output from the audit
3217c478bd9Sstevel@tonic-gate  * queue.  The thread terminates when it detects auditing being turned
3227c478bd9Sstevel@tonic-gate  * off, such as when auditd exits with a SIGTERM.  If a subsequent
3237c478bd9Sstevel@tonic-gate  * auditdoor arrives while the thread is running, the door descriptor
3247c478bd9Sstevel@tonic-gate  * of the last auditdoor in will be used for output.  auditd is responsible
3257c478bd9Sstevel@tonic-gate  * for insuring that multiple copies are not running.
3267c478bd9Sstevel@tonic-gate  */
3277c478bd9Sstevel@tonic-gate 
328*005d3febSMarek Pospisil int
auditdoor(int fd)3297c478bd9Sstevel@tonic-gate auditdoor(int fd)
3307c478bd9Sstevel@tonic-gate {
3317c478bd9Sstevel@tonic-gate 	struct file	*fp;
3327c478bd9Sstevel@tonic-gate 	struct vnode	*vp;
3337c478bd9Sstevel@tonic-gate 	int		do_create = 0;
3347c478bd9Sstevel@tonic-gate 	au_kcontext_t	*kctx;
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate 	if (secpolicy_audit_config(CRED()) != 0)
3377c478bd9Sstevel@tonic-gate 		return (EPERM);
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate 	if (!(audit_policy & AUDIT_PERZONE) && !INGLOBALZONE(curproc))
3407c478bd9Sstevel@tonic-gate 		return (EINVAL);
3417c478bd9Sstevel@tonic-gate 
3429e9e6ab8Spaulson 	kctx = GET_KCTX_NGZ;
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 	/*
3457c478bd9Sstevel@tonic-gate 	 * convert file pointer to file descriptor
3467c478bd9Sstevel@tonic-gate 	 *   Note: fd ref count incremented here.
3477c478bd9Sstevel@tonic-gate 	 */
3487c478bd9Sstevel@tonic-gate 	if ((fp = (struct file *)getf(fd)) == NULL) {
349787b48eaSgww 		return (EBADF);
3507c478bd9Sstevel@tonic-gate 	}
3517c478bd9Sstevel@tonic-gate 	vp = fp->f_vnode;
3527c478bd9Sstevel@tonic-gate 	if (vp->v_type != VDOOR) {
3537c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN,
3547c478bd9Sstevel@tonic-gate 		    "auditdoor() did not get the expected door descriptor\n");
3557c478bd9Sstevel@tonic-gate 		releasef(fd);
356787b48eaSgww 		return (EINVAL);
3577c478bd9Sstevel@tonic-gate 	}
3587c478bd9Sstevel@tonic-gate 	/*
3597c478bd9Sstevel@tonic-gate 	 * If the output thread is already running, then replace the
3607c478bd9Sstevel@tonic-gate 	 * door descriptor with the new one and continue; otherwise
3617c478bd9Sstevel@tonic-gate 	 * create the thread too.  Since au_output_thread makes a call
3627c478bd9Sstevel@tonic-gate 	 * to au_doorio() which also does
3637c478bd9Sstevel@tonic-gate 	 * mutex_lock(&(kctx->auk_svc_lock)), the create/dispatch is
3647c478bd9Sstevel@tonic-gate 	 * done after the unlock...
3657c478bd9Sstevel@tonic-gate 	 */
3667c478bd9Sstevel@tonic-gate 	mutex_enter(&(kctx->auk_svc_lock));
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 	if (kctx->auk_current_vp != NULL)
3697c478bd9Sstevel@tonic-gate 		VN_RELE(kctx->auk_current_vp);
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 	kctx->auk_current_vp = vp;
3727c478bd9Sstevel@tonic-gate 	VN_HOLD(kctx->auk_current_vp);
3737c478bd9Sstevel@tonic-gate 	releasef(fd);
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate 	if (!kctx->auk_output_active) {
3767c478bd9Sstevel@tonic-gate 		kctx->auk_output_active = 1;
3777c478bd9Sstevel@tonic-gate 		do_create = 1;
3787c478bd9Sstevel@tonic-gate 	}
3797c478bd9Sstevel@tonic-gate 	mutex_exit(&(kctx->auk_svc_lock));
3807c478bd9Sstevel@tonic-gate 	if (do_create) {
3817c478bd9Sstevel@tonic-gate 		kctx->auk_taskq =
3827c478bd9Sstevel@tonic-gate 		    taskq_create("output_master", 1, minclsyspri, 1, 1, 0);
3837c478bd9Sstevel@tonic-gate 		(void) taskq_dispatch(kctx->auk_taskq,
3847c478bd9Sstevel@tonic-gate 		    (task_func_t *)au_output_thread,
3857c478bd9Sstevel@tonic-gate 		    kctx, TQ_SLEEP);
3867c478bd9Sstevel@tonic-gate 	}
387787b48eaSgww 	return (0);
3887c478bd9Sstevel@tonic-gate }
3897c478bd9Sstevel@tonic-gate 
390*005d3febSMarek Pospisil static void
audit_dont_stop(void * kctx)391*005d3febSMarek Pospisil audit_dont_stop(void *kctx)
392*005d3febSMarek Pospisil {
393*005d3febSMarek Pospisil 
394*005d3febSMarek Pospisil 	if ((((au_kcontext_t *)kctx)->auk_valid != AUK_VALID) ||
395*005d3febSMarek Pospisil 	    (((au_kcontext_t *)kctx)->auk_auditstate == AUC_NOAUDIT))
396*005d3febSMarek Pospisil 		return;
397*005d3febSMarek Pospisil 
398*005d3febSMarek Pospisil 	mutex_enter(&(((au_kcontext_t *)kctx)->auk_queue.lock));
399*005d3febSMarek Pospisil 	cv_broadcast(&(((au_kcontext_t *)kctx)->auk_queue.write_cv));
400*005d3febSMarek Pospisil 	mutex_exit(&(((au_kcontext_t *)kctx)->auk_queue.lock));
401*005d3febSMarek Pospisil }
402*005d3febSMarek Pospisil 
4037c478bd9Sstevel@tonic-gate /*
4047c478bd9Sstevel@tonic-gate  * au_queue_kick -- wake up the output queue after delay ticks
4057c478bd9Sstevel@tonic-gate  */
4067c478bd9Sstevel@tonic-gate static void
au_queue_kick(void * kctx)4077c478bd9Sstevel@tonic-gate au_queue_kick(void *kctx)
4087c478bd9Sstevel@tonic-gate {
4097c478bd9Sstevel@tonic-gate 	/*
4107c478bd9Sstevel@tonic-gate 	 * wakeup reader if its not running and there is something
4117c478bd9Sstevel@tonic-gate 	 * to do.  It also helps that kctx still be valid...
4127c478bd9Sstevel@tonic-gate 	 */
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 	if ((((au_kcontext_t *)kctx)->auk_valid != AUK_VALID) ||
4157c478bd9Sstevel@tonic-gate 	    (((au_kcontext_t *)kctx)->auk_auditstate == AUC_NOAUDIT))
4167c478bd9Sstevel@tonic-gate 		return;
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate 	if (((au_kcontext_t *)kctx)->auk_queue.cnt &&
4197c478bd9Sstevel@tonic-gate 	    ((au_kcontext_t *)kctx)->auk_queue.rd_block)
4207c478bd9Sstevel@tonic-gate 		cv_broadcast(&((au_kcontext_t *)kctx)->auk_queue.read_cv);
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate 	/* fire off timeout event to kick audit queue awake */
4237c478bd9Sstevel@tonic-gate 	(void) timeout(au_queue_kick, kctx,
4247c478bd9Sstevel@tonic-gate 	    ((au_kcontext_t *)kctx)->auk_queue.delay);
4257c478bd9Sstevel@tonic-gate }
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate /*
4287c478bd9Sstevel@tonic-gate  * output thread
4297c478bd9Sstevel@tonic-gate  *
4307c478bd9Sstevel@tonic-gate  * this runs "forever" where "forever" means until either auk_auditstate
4317c478bd9Sstevel@tonic-gate  * changes from AUC_AUDITING or if the door descriptor becomes invalid.
4327c478bd9Sstevel@tonic-gate  *
4337c478bd9Sstevel@tonic-gate  * there is one thread per active zone if AUC_PERZONE is set.  Since
4347c478bd9Sstevel@tonic-gate  * there is the possibility that a zone may go down without auditd
4357c478bd9Sstevel@tonic-gate  * terminating properly, a zone shutdown kills its au_output_thread()
4367c478bd9Sstevel@tonic-gate  * via taskq_destroy().
4377c478bd9Sstevel@tonic-gate  */
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate static void
au_output_thread(au_kcontext_t * kctx)4407c478bd9Sstevel@tonic-gate au_output_thread(au_kcontext_t *kctx)
4417c478bd9Sstevel@tonic-gate {
4427c478bd9Sstevel@tonic-gate 	int		error = 0;
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 	(void) timeout(au_queue_kick, kctx, kctx->auk_queue.delay);
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate 	/*
4477c478bd9Sstevel@tonic-gate 	 * Wait for work, until a signal arrives,
4487c478bd9Sstevel@tonic-gate 	 * or until auditing is disabled.
4497c478bd9Sstevel@tonic-gate 	 */
4507c478bd9Sstevel@tonic-gate 
4517c478bd9Sstevel@tonic-gate 	while (!error) {
452787b48eaSgww 		if (kctx->auk_auditstate == AUC_AUDITING) {
453787b48eaSgww 			mutex_enter(&(kctx->auk_queue.lock));
454787b48eaSgww 			while (kctx->auk_queue.head == NULL) {
455787b48eaSgww 				/* safety check. kick writer awake */
456787b48eaSgww 				if (kctx->auk_queue.wt_block) {
457787b48eaSgww 					cv_broadcast(&(kctx->
458787b48eaSgww 					    auk_queue.write_cv));
459787b48eaSgww 				}
460787b48eaSgww 
461787b48eaSgww 				kctx->auk_queue.rd_block = 1;
462787b48eaSgww 				AS_INC(as_rblocked, 1, kctx);
463787b48eaSgww 
464787b48eaSgww 				cv_wait(&(kctx->auk_queue.read_cv),
465787b48eaSgww 				    &(kctx->auk_queue.lock));
466787b48eaSgww 				kctx->auk_queue.rd_block = 0;
467787b48eaSgww 
468787b48eaSgww 				if (kctx->auk_auditstate != AUC_AUDITING) {
469787b48eaSgww 					mutex_exit(&(kctx->auk_queue.lock));
470787b48eaSgww 					(void) timeout(audit_dont_stop, kctx,
471787b48eaSgww 					    au_resid);
472787b48eaSgww 					goto output_exit;
473787b48eaSgww 				}
474787b48eaSgww 				kctx->auk_queue.rd_block = 0;
475787b48eaSgww 			}
4767c478bd9Sstevel@tonic-gate 			mutex_exit(&(kctx->auk_queue.lock));
477787b48eaSgww 			/*
478787b48eaSgww 			 * au_doorio() calls au_door_upcall which holds
479787b48eaSgww 			 * auk_svc_lock; au_doorio empties the queue before
480787b48eaSgww 			 * returning.
481787b48eaSgww 			 */
4827c478bd9Sstevel@tonic-gate 
483787b48eaSgww 			error = au_doorio(kctx);
484787b48eaSgww 		} else {
485787b48eaSgww 			/* auditing turned off while we slept */
486787b48eaSgww 			break;
487787b48eaSgww 		}
4887c478bd9Sstevel@tonic-gate 	}
4897c478bd9Sstevel@tonic-gate output_exit:
4907c478bd9Sstevel@tonic-gate 	mutex_enter(&(kctx->auk_svc_lock));
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate 	VN_RELE(kctx->auk_current_vp);
4937c478bd9Sstevel@tonic-gate 	kctx->auk_current_vp = NULL;
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate 	kctx->auk_output_active = 0;
4967c478bd9Sstevel@tonic-gate 
4977c478bd9Sstevel@tonic-gate 	mutex_exit(&(kctx->auk_svc_lock));
4987c478bd9Sstevel@tonic-gate }
499