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