1186545Srwatson/*-
2189279Srwatson * Copyright (c) 2008-2009 Apple Inc.
3186545Srwatson * All rights reserved.
4186545Srwatson *
5186545Srwatson * Redistribution and use in source and binary forms, with or without
6186545Srwatson * modification, are permitted provided that the following conditions
7186545Srwatson * are met:
8186545Srwatson * 1.  Redistributions of source code must retain the above copyright
9186545Srwatson *     notice, this list of conditions and the following disclaimer.
10186545Srwatson * 2.  Redistributions in binary form must reproduce the above copyright
11186545Srwatson *     notice, this list of conditions and the following disclaimer in the
12186545Srwatson *     documentation and/or other materials provided with the distribution.
13186545Srwatson * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
14186545Srwatson *     its contributors may be used to endorse or promote products derived
15186545Srwatson *     from this software without specific prior written permission.
16186545Srwatson *
17186545Srwatson * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
18186545Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19186545Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20186545Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
21186545Srwatson * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22186545Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23186545Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24186545Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25186545Srwatson * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26186545Srwatson * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27186545Srwatson * POSSIBILITY OF SUCH DAMAGE.
28186545Srwatson */
29186545Srwatson
30186545Srwatson#include <sys/param.h>
31186545Srwatson
32186545Srwatson#include <config/config.h>
33186545Srwatson
34186545Srwatson#include <sys/dirent.h>
35186545Srwatson#ifdef HAVE_FULL_QUEUE_H
36186545Srwatson#include <sys/queue.h>
37186545Srwatson#else /* !HAVE_FULL_QUEUE_H */
38186545Srwatson#include <compat/queue.h>
39186545Srwatson#endif /* !HAVE_FULL_QUEUE_H */
40191273Srwatson#include <sys/mount.h>
41191273Srwatson#include <sys/socket.h>
42186545Srwatson
43186545Srwatson#include <sys/stat.h>
44186545Srwatson#include <sys/time.h>
45186545Srwatson
46186545Srwatson#include <netinet/in.h>
47186545Srwatson
48186545Srwatson#include <bsm/audit.h>
49186545Srwatson#include <bsm/audit_uevents.h>
50186545Srwatson#include <bsm/auditd_lib.h>
51186545Srwatson#include <bsm/libbsm.h>
52186545Srwatson
53243750Srwatson#include <assert.h>
54189279Srwatson#include <dirent.h>
55186545Srwatson#include <err.h>
56186545Srwatson#include <errno.h>
57186545Srwatson#include <fcntl.h>
58186545Srwatson#include <stdio.h>
59186545Srwatson#include <string.h>
60186545Srwatson#include <stdlib.h>
61186545Srwatson#include <time.h>
62186545Srwatson#include <unistd.h>
63186545Srwatson#include <netdb.h>
64186545Srwatson
65186545Srwatson#ifdef __APPLE__
66186545Srwatson#include <notify.h>
67186545Srwatson#ifndef __BSM_INTERNAL_NOTIFY_KEY
68243750Srwatson#define	__BSM_INTERNAL_NOTIFY_KEY	"com.apple.audit.change"
69186545Srwatson#endif /* __BSM_INTERNAL_NOTIFY_KEY */
70186545Srwatson#endif /* __APPLE__ */
71186545Srwatson
72186545Srwatson/*
73186545Srwatson * XXX This is temporary until this is moved to <bsm/audit.h> and shared with
74186545Srwatson * the kernel.
75186545Srwatson */
76186545Srwatson#ifndef	AUDIT_HARD_LIMIT_FREE_BLOCKS
77186545Srwatson#define	AUDIT_HARD_LIMIT_FREE_BLOCKS	4
78186545Srwatson#endif
79186545Srwatson
80189279Srwatson/*
81189279Srwatson * Number of seconds to January 1, 2000
82189279Srwatson */
83189279Srwatson#define	JAN_01_2000	946598400
84189279Srwatson
85186545Srwatsonstruct dir_ent {
86186545Srwatson	char			*dirname;
87186545Srwatson	uint8_t			 softlim;
88186545Srwatson	uint8_t			 hardlim;
89186545Srwatson	TAILQ_ENTRY(dir_ent)	 dirs;
90186545Srwatson};
91186545Srwatson
92186545Srwatsonstatic TAILQ_HEAD(, dir_ent)	dir_q;
93186545Srwatson
94189279Srwatsonstruct audit_trail {
95189279Srwatson	time_t			 at_time;
96189279Srwatson	char			*at_path;
97189279Srwatson	off_t			 at_size;
98189279Srwatson
99189279Srwatson	TAILQ_ENTRY(audit_trail) at_trls;
100189279Srwatson};
101189279Srwatson
102189279Srwatsonstatic int auditd_minval = -1;
103243750Srwatsonstatic int auditd_dist = 0;
104189279Srwatson
105189279Srwatsonstatic char auditd_host[MAXHOSTNAMELEN];
106189279Srwatsonstatic int auditd_hostlen = -1;
107189279Srwatson
108186545Srwatsonstatic char *auditd_errmsg[] = {
109243750Srwatson	"no error",					/* ADE_NOERR	( 0) */
110243750Srwatson	"could not parse audit_control(5) file",	/* ADE_PARSE	( 1) */
111243750Srwatson	"auditon(2) failed",				/* ADE_AUDITON	( 2) */
112243750Srwatson	"malloc(3) failed",				/* ADE_NOMEM	( 3) */
113243750Srwatson	"all audit log directories over soft limit",	/* ADE_SOFTLIM	( 4) */
114243750Srwatson	"all audit log directories over hard limit",	/* ADE_HARDLIM	( 5) */
115243750Srwatson	"could not create file name string",		/* ADE_STRERR	( 6) */
116243750Srwatson	"could not open audit record",			/* ADE_AU_OPEN	( 7) */
117243750Srwatson	"could not close audit record",			/* ADE_AU_CLOSE	( 8) */
118243750Srwatson	"could not set active audit session state",	/* ADE_SETAUDIT	( 9) */
119243750Srwatson	"auditctl(2) failed (trail still swapped)",	/* ADE_ACTL	(10) */
120243750Srwatson	"auditctl(2) failed (trail not swapped)",	/* ADE_ACTLERR	(11) */
121243750Srwatson	"could not swap audit trail file",		/* ADE_SWAPERR	(12) */
122186545Srwatson	"could not rename crash recovery file",		/* ADE_RENAME	(13) */
123186545Srwatson	"could not read 'current' link file",		/* ADE_READLINK	(14) */
124243750Srwatson	"could not create 'current' link file",		/* ADE_SYMLINK	(15) */
125186545Srwatson	"invalid argument",				/* ADE_INVAL	(16) */
126186545Srwatson	"could not resolve hostname to address",	/* ADE_GETADDR	(17) */
127186545Srwatson	"address family not supported",			/* ADE_ADDRFAM	(18) */
128189279Srwatson	"error expiring audit trail files",		/* ADE_EXPIRE	(19) */
129186545Srwatson};
130186545Srwatson
131243750Srwatson#define	MAXERRCODE	(sizeof(auditd_errmsg) / sizeof(auditd_errmsg[0]))
132186545Srwatson
133243750Srwatson#define	NA_EVENT_STR_SIZE	128
134243750Srwatson#define	POL_STR_SIZE		128
135186545Srwatson
136186545Srwatson
137186545Srwatson/*
138186545Srwatson * Look up and return the error string for the given audit error code.
139186545Srwatson */
140186545Srwatsonconst char *
141186545Srwatsonauditd_strerror(int errcode)
142186545Srwatson{
143186545Srwatson	int idx = -errcode;
144186545Srwatson
145186545Srwatson	if (idx < 0 || idx > (int)MAXERRCODE)
146186545Srwatson		return ("Invalid auditd error code");
147243750Srwatson
148186545Srwatson	return (auditd_errmsg[idx]);
149186545Srwatson}
150186545Srwatson
151186545Srwatson
152186545Srwatson/*
153243750Srwatson * Free our local list of directory names and init list.
154186545Srwatson */
155186545Srwatsonstatic void
156186545Srwatsonfree_dir_q(void)
157186545Srwatson{
158186545Srwatson	struct dir_ent *d1, *d2;
159243750Srwatson
160186545Srwatson	d1 = TAILQ_FIRST(&dir_q);
161186545Srwatson	while (d1 != NULL) {
162186545Srwatson		d2 = TAILQ_NEXT(d1, dirs);
163186545Srwatson		free(d1->dirname);
164186545Srwatson		free(d1);
165186545Srwatson		d1 = d2;
166186545Srwatson	}
167186545Srwatson	TAILQ_INIT(&dir_q);
168186545Srwatson}
169186545Srwatson
170186545Srwatson/*
171186545Srwatson * Concat the directory name to the given file name.
172186545Srwatson * XXX We should affix the hostname also
173186545Srwatson */
174186545Srwatsonstatic char *
175186545Srwatsonaffixdir(char *name, struct dir_ent *dirent)
176186545Srwatson{
177186545Srwatson	char *fn = NULL;
178186545Srwatson
179186545Srwatson	/*
180186545Srwatson	 * Sanity check on file name.
181186545Srwatson	 */
182243750Srwatson	if (strlen(name) != FILENAME_LEN) {
183186545Srwatson		errno = EINVAL;
184243750Srwatson		return (NULL);
185186545Srwatson	}
186186545Srwatson
187189279Srwatson	/*
188189279Srwatson	 * If the host is set then also add the hostname to the filename.
189189279Srwatson	 */
190189279Srwatson	if (auditd_hostlen != -1)
191189279Srwatson		asprintf(&fn, "%s/%s.%s", dirent->dirname, name, auditd_host);
192189279Srwatson	else
193189279Srwatson		asprintf(&fn, "%s/%s", dirent->dirname, name);
194186545Srwatson	return (fn);
195186545Srwatson}
196186545Srwatson
197186545Srwatson/*
198186545Srwatson * Insert the directory entry in the list by the way they are ordered in
199186545Srwatson * audit_control(5).  Move the entries that are over the soft and hard limits
200186545Srwatson * toward the tail.
201186545Srwatson */
202186545Srwatsonstatic void
203186545Srwatsoninsert_orderly(struct dir_ent *denew)
204186545Srwatson{
205186545Srwatson	struct dir_ent *dep;
206243750Srwatson
207186545Srwatson	TAILQ_FOREACH(dep, &dir_q, dirs) {
208186545Srwatson		if (dep->softlim == 1 && denew->softlim == 0) {
209186545Srwatson			TAILQ_INSERT_BEFORE(dep, denew, dirs);
210243750Srwatson			return;
211186545Srwatson		}
212186545Srwatson		if (dep->hardlim == 1 && denew->hardlim == 0) {
213186545Srwatson			TAILQ_INSERT_BEFORE(dep, denew, dirs);
214186545Srwatson			return;
215186545Srwatson		}
216186545Srwatson	}
217186545Srwatson	TAILQ_INSERT_TAIL(&dir_q, denew, dirs);
218186545Srwatson}
219186545Srwatson
220186545Srwatson/*
221243750Srwatson * Get the min percentage of free blocks from audit_control(5) and that
222243750Srwatson * value in the kernel.  Return:
223243750Srwatson *	ADE_NOERR	on success,
224243750Srwatson *	ADE_PARSE	error parsing audit_control(5),
225243750Srwatson */
226243750Srwatsonint
227243750Srwatsonauditd_set_dist(void)
228243750Srwatson{
229243750Srwatson	int ret;
230243750Srwatson
231243750Srwatson	ret = getacdist();
232243750Srwatson	if (ret < 0)
233243750Srwatson		return (ADE_PARSE);
234243750Srwatson
235243750Srwatson	auditd_dist = ret;
236243750Srwatson
237243750Srwatson	return (ADE_NOERR);
238243750Srwatson}
239243750Srwatson
240243750Srwatson/*
241186545Srwatson * Get the host from audit_control(5) and set it in the audit kernel
242186545Srwatson * information.  Return:
243186545Srwatson *	ADE_NOERR	on success.
244186545Srwatson *	ADE_PARSE	error parsing audit_control(5).
245186545Srwatson *	ADE_AUDITON	error getting/setting auditon(2) value.
246243750Srwatson *	ADE_GETADDR	error getting address info for host.
247243750Srwatson *	ADE_ADDRFAM	un-supported address family.
248186545Srwatson */
249186545Srwatsonint
250186545Srwatsonauditd_set_host(void)
251186545Srwatson{
252186545Srwatson	struct sockaddr_in6 *sin6;
253186545Srwatson	struct sockaddr_in *sin;
254186545Srwatson	struct addrinfo *res;
255186545Srwatson	struct auditinfo_addr aia;
256186545Srwatson	int error, ret = ADE_NOERR;
257186545Srwatson
258339093Sasomers	if ((getachost(auditd_host, sizeof(auditd_host)) != 0) ||
259339093Sasomers	    ((auditd_hostlen = strlen(auditd_host)) == 0)) {
260243750Srwatson		ret = ADE_PARSE;
261243750Srwatson
262186545Srwatson		/*
263186545Srwatson		 * To maintain reverse compatability with older audit_control
264186545Srwatson		 * files, simply drop a warning if the host parameter has not
265186545Srwatson		 * been set.  However, we will explicitly disable the
266186545Srwatson		 * generation of extended audit header by passing in a zeroed
267186545Srwatson		 * termid structure.
268186545Srwatson		 */
269186545Srwatson		bzero(&aia, sizeof(aia));
270186545Srwatson		aia.ai_termid.at_type = AU_IPv4;
271191273Srwatson		error = audit_set_kaudit(&aia, sizeof(aia));
272186545Srwatson		if (error < 0 && errno != ENOSYS)
273186545Srwatson			ret = ADE_AUDITON;
274186545Srwatson		return (ret);
275186545Srwatson	}
276189279Srwatson	error = getaddrinfo(auditd_host, NULL, NULL, &res);
277186545Srwatson	if (error)
278186545Srwatson		return (ADE_GETADDR);
279186545Srwatson	switch (res->ai_family) {
280186545Srwatson	case PF_INET6:
281186545Srwatson		sin6 = (struct sockaddr_in6 *) res->ai_addr;
282186545Srwatson		bcopy(&sin6->sin6_addr.s6_addr,
283186545Srwatson		    &aia.ai_termid.at_addr[0], sizeof(struct in6_addr));
284186545Srwatson		aia.ai_termid.at_type = AU_IPv6;
285186545Srwatson		break;
286186545Srwatson
287186545Srwatson	case PF_INET:
288186545Srwatson		sin = (struct sockaddr_in *) res->ai_addr;
289186545Srwatson		bcopy(&sin->sin_addr.s_addr,
290186545Srwatson		    &aia.ai_termid.at_addr[0], sizeof(struct in_addr));
291186545Srwatson		aia.ai_termid.at_type = AU_IPv4;
292186545Srwatson		break;
293186545Srwatson
294186545Srwatson	default:
295186545Srwatson		/* Un-supported address family in host parameter. */
296186545Srwatson		errno = EAFNOSUPPORT;
297186545Srwatson		return (ADE_ADDRFAM);
298186545Srwatson	}
299186545Srwatson
300191273Srwatson	if (audit_set_kaudit(&aia, sizeof(aia)) < 0)
301186545Srwatson		ret = ADE_AUDITON;
302186545Srwatson
303186545Srwatson	return (ret);
304186545Srwatson}
305186545Srwatson
306243750Srwatson/*
307186545Srwatson * Get the min percentage of free blocks from audit_control(5) and that
308186545Srwatson * value in the kernel.  Return:
309186545Srwatson *	ADE_NOERR	on success,
310243750Srwatson *	ADE_PARSE	error parsing audit_control(5),
311186545Srwatson *	ADE_AUDITON	error getting/setting auditon(2) value.
312186545Srwatson */
313186545Srwatsonint
314186545Srwatsonauditd_set_minfree(void)
315186545Srwatson{
316186545Srwatson	au_qctrl_t qctrl;
317186545Srwatson
318189279Srwatson	if (getacmin(&auditd_minval) != 0)
319186545Srwatson		return (ADE_PARSE);
320243750Srwatson
321191273Srwatson	if (audit_get_qctrl(&qctrl, sizeof(qctrl)) != 0)
322186545Srwatson		return (ADE_AUDITON);
323186545Srwatson
324189279Srwatson	if (qctrl.aq_minfree != auditd_minval) {
325189279Srwatson		qctrl.aq_minfree = auditd_minval;
326191273Srwatson		if (audit_set_qctrl(&qctrl, sizeof(qctrl)) != 0)
327186545Srwatson			return (ADE_AUDITON);
328186545Srwatson	}
329186545Srwatson
330186545Srwatson	return (0);
331186545Srwatson}
332186545Srwatson
333186545Srwatson/*
334189279Srwatson * Convert a trailname into a timestamp (seconds).  Return 0 if the conversion
335189279Srwatson * was successful.
336189279Srwatson */
337189279Srwatsonstatic int
338189279Srwatsontrailname_to_tstamp(char *fn, time_t *tstamp)
339189279Srwatson{
340189279Srwatson	struct tm tm;
341243750Srwatson	char ts[TIMESTAMP_LEN + 1];
342189279Srwatson	char *p;
343189279Srwatson
344189279Srwatson	*tstamp = 0;
345189279Srwatson
346189279Srwatson	/*
347189279Srwatson	 * Get the ending time stamp.
348189279Srwatson	 */
349189279Srwatson	if ((p = strchr(fn, '.')) == NULL)
350189279Srwatson		return (1);
351243750Srwatson	strlcpy(ts, ++p, sizeof(ts));
352189279Srwatson	if (strlen(ts) != POSTFIX_LEN)
353189279Srwatson		return (1);
354189279Srwatson
355189279Srwatson	bzero(&tm, sizeof(tm));
356189279Srwatson
357189279Srwatson	/* seconds (0-60) */
358189279Srwatson	p = ts + POSTFIX_LEN - 2;
359189279Srwatson	tm.tm_sec = atol(p);
360189279Srwatson	if (tm.tm_sec < 0 || tm.tm_sec > 60)
361189279Srwatson		return (1);
362189279Srwatson
363243750Srwatson	/* minutes (0-59) */
364189279Srwatson	*p = '\0'; p -= 2;
365189279Srwatson	tm.tm_min = atol(p);
366189279Srwatson	if (tm.tm_min < 0 || tm.tm_min > 59)
367189279Srwatson		return (1);
368189279Srwatson
369189279Srwatson	/* hours (0 - 23) */
370189279Srwatson	*p = '\0'; p -= 2;
371189279Srwatson	tm.tm_hour = atol(p);
372189279Srwatson	if (tm.tm_hour < 0 || tm.tm_hour > 23)
373189279Srwatson		return (1);
374189279Srwatson
375189279Srwatson	/* day of month (1-31) */
376189279Srwatson	*p = '\0'; p -= 2;
377189279Srwatson	tm.tm_mday = atol(p);
378189279Srwatson	if (tm.tm_mday < 1 || tm.tm_mday > 31)
379189279Srwatson		return (1);
380189279Srwatson
381189279Srwatson	/* month (0 - 11) */
382189279Srwatson	*p = '\0'; p -= 2;
383189279Srwatson	tm.tm_mon = atol(p) - 1;
384189279Srwatson	if (tm.tm_mon < 0 || tm.tm_mon > 11)
385189279Srwatson		return (1);
386189279Srwatson
387189279Srwatson	/* year (year - 1900) */
388189279Srwatson	*p = '\0'; p -= 4;
389189279Srwatson	tm.tm_year = atol(p) - 1900;
390189279Srwatson	if (tm.tm_year < 0)
391189279Srwatson		return (1);
392189279Srwatson
393189279Srwatson	*tstamp = timegm(&tm);
394189279Srwatson
395189279Srwatson	return (0);
396189279Srwatson}
397189279Srwatson
398189279Srwatson/*
399189279Srwatson * Remove audit trails files according to the expiration conditions.  Returns:
400243750Srwatson *	ADE_NOERR	on success or there is nothing to do.
401243750Srwatson *	ADE_PARSE	if error parsing audit_control(5).
402243750Srwatson *	ADE_NOMEM	if could not allocate memory.
403292432Sbrueffer *	ADE_READLINK	if could not read link file.
404292432Sbrueffer *	ADE_EXPIRE	if there was an unexpected error.
405189279Srwatson */
406189279Srwatsonint
407189279Srwatsonauditd_expire_trails(int (*warn_expired)(char *))
408189279Srwatson{
409292432Sbrueffer	int andflg, len, ret = ADE_NOERR;
410189279Srwatson	size_t expire_size, total_size = 0L;
411189279Srwatson	time_t expire_age, oldest_time, current_time = time(NULL);
412189279Srwatson	struct dir_ent *traildir;
413189279Srwatson	struct audit_trail *at;
414189279Srwatson	char *afnp, *pn;
415189279Srwatson	TAILQ_HEAD(au_trls_head, audit_trail) head =
416189279Srwatson	    TAILQ_HEAD_INITIALIZER(head);
417189279Srwatson	struct stat stbuf;
418189279Srwatson	char activefn[MAXPATHLEN];
419189279Srwatson
420189279Srwatson	/*
421189279Srwatson	 * Read the expiration conditions.  If no conditions then return no
422189279Srwatson	 * error.
423189279Srwatson	 */
424189279Srwatson	if (getacexpire(&andflg, &expire_age, &expire_size) < 0)
425189279Srwatson		return (ADE_PARSE);
426189279Srwatson	if (!expire_age && !expire_size)
427189279Srwatson		return (ADE_NOERR);
428189279Srwatson
429189279Srwatson	/*
430189279Srwatson	 * Read the 'current' trail file name.  Trim off directory path.
431189279Srwatson	 */
432189279Srwatson	activefn[0] = '\0';
433292432Sbrueffer	len = readlink(AUDIT_CURRENT_LINK, activefn, MAXPATHLEN - 1);
434292432Sbrueffer	if (len < 0)
435292432Sbrueffer		return (ADE_READLINK);
436243750Srwatson	if ((afnp = strrchr(activefn, '/')) != NULL)
437189279Srwatson		afnp++;
438189279Srwatson
439189279Srwatson
440189279Srwatson	/*
441189279Srwatson	 * Build tail queue of the trail files.
442189279Srwatson	 */
443189279Srwatson	TAILQ_FOREACH(traildir, &dir_q, dirs) {
444189279Srwatson		DIR *dirp;
445189279Srwatson		struct dirent *dp;
446189279Srwatson
447189279Srwatson		dirp = opendir(traildir->dirname);
448189279Srwatson		while ((dp = readdir(dirp)) != NULL) {
449189279Srwatson			time_t tstamp = 0;
450189279Srwatson			struct audit_trail *new;
451189279Srwatson
452189279Srwatson			/*
453189279Srwatson			 * Quickly filter non-trail files.
454189279Srwatson			 */
455243750Srwatson			if (dp->d_namlen < FILENAME_LEN ||
456189279Srwatson			    dp->d_name[POSTFIX_LEN] != '.')
457189279Srwatson				continue;
458189279Srwatson
459189279Srwatson			if (asprintf(&pn, "%s/%s", traildir->dirname,
460243750Srwatson			    dp->d_name) < 0) {
461189279Srwatson				ret = ADE_NOMEM;
462189279Srwatson				break;
463189279Srwatson			}
464189279Srwatson
465189279Srwatson			if (stat(pn, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
466189279Srwatson				free(pn);
467189279Srwatson				continue;
468189279Srwatson			}
469189279Srwatson
470189279Srwatson			total_size += stbuf.st_size;
471189279Srwatson
472189279Srwatson			/*
473189279Srwatson			 * If this is the 'current' audit trail then
474189279Srwatson			 * don't add it to the tail queue.
475189279Srwatson			 */
476243750Srwatson			if (NULL != afnp && strcmp(dp->d_name, afnp) == 0) {
477189279Srwatson				free(pn);
478189279Srwatson				continue;
479189279Srwatson			}
480189279Srwatson
481189279Srwatson			/*
482189279Srwatson			 * Get the ending time stamp encoded in the trail
483189279Srwatson			 * name.  If we can't read it or if it is older
484189279Srwatson			 * than Jan 1, 2000 then use the mtime.
485189279Srwatson			 */
486189279Srwatson			if (trailname_to_tstamp(dp->d_name, &tstamp) != 0 ||
487189279Srwatson			    tstamp < JAN_01_2000)
488189279Srwatson				tstamp = stbuf.st_mtime;
489189279Srwatson
490189279Srwatson			/*
491189279Srwatson			 * If the time stamp is older than Jan 1, 2000 then
492189279Srwatson			 * update the mtime of the trail file to the current
493189279Srwatson			 * time. This is so we don't prematurely remove a trail
494189279Srwatson			 * file that was created while the system clock reset
495189279Srwatson			 * to the * "beginning of time" but later the system
496189279Srwatson			 * clock is set to the correct current time.
497189279Srwatson			 */
498189279Srwatson			if (current_time >= JAN_01_2000 &&
499189279Srwatson			    tstamp < JAN_01_2000) {
500189279Srwatson				struct timeval tv[2];
501189279Srwatson
502189279Srwatson				tstamp = stbuf.st_mtime = current_time;
503243750Srwatson				TIMESPEC_TO_TIMEVAL(&tv[0],
504189279Srwatson				    &stbuf.st_atimespec);
505243750Srwatson				TIMESPEC_TO_TIMEVAL(&tv[1],
506189279Srwatson				    &stbuf.st_mtimespec);
507189279Srwatson				utimes(pn, tv);
508189279Srwatson			}
509189279Srwatson
510189279Srwatson			/*
511189279Srwatson			 * Allocate and populate the new entry.
512189279Srwatson			 */
513189279Srwatson			new = malloc(sizeof(*new));
514189279Srwatson			if (NULL == new) {
515189279Srwatson				free(pn);
516189279Srwatson				ret = ADE_NOMEM;
517189279Srwatson				break;
518189279Srwatson			}
519189279Srwatson			new->at_time = tstamp;
520189279Srwatson			new->at_size = stbuf.st_size;
521189279Srwatson			new->at_path = pn;
522189279Srwatson
523189279Srwatson			/*
524189279Srwatson			 * Check to see if we have a new head.  Otherwise,
525189279Srwatson			 * walk the tailq from the tail first and do a simple
526189279Srwatson			 * insertion sort.
527189279Srwatson			 */
528189279Srwatson			if (TAILQ_EMPTY(&head) ||
529243750Srwatson			    new->at_time <= TAILQ_FIRST(&head)->at_time) {
530189279Srwatson				TAILQ_INSERT_HEAD(&head, new, at_trls);
531189279Srwatson				continue;
532189279Srwatson			}
533189279Srwatson
534189279Srwatson			TAILQ_FOREACH_REVERSE(at, &head, au_trls_head, at_trls)
535189279Srwatson				if (new->at_time >= at->at_time) {
536189279Srwatson					TAILQ_INSERT_AFTER(&head, at, new,
537189279Srwatson					    at_trls);
538189279Srwatson					break;
539189279Srwatson				}
540189279Srwatson
541189279Srwatson		}
542234034Srwatson		closedir(dirp);
543189279Srwatson	}
544189279Srwatson
545189279Srwatson	oldest_time = current_time - expire_age;
546189279Srwatson
547243750Srwatson	/*
548189279Srwatson	 * Expire trail files, oldest (mtime) first, if the given
549189279Srwatson	 * conditions are met.
550189279Srwatson	 */
551189279Srwatson	at = TAILQ_FIRST(&head);
552189279Srwatson	while (NULL != at) {
553189279Srwatson		struct audit_trail *at_next = TAILQ_NEXT(at, at_trls);
554189279Srwatson
555189279Srwatson		if (andflg) {
556189279Srwatson			if ((expire_size && total_size > expire_size) &&
557189279Srwatson			    (expire_age && at->at_time < oldest_time)) {
558189279Srwatson				if (warn_expired)
559243750Srwatson					(*warn_expired)(at->at_path);
560189279Srwatson				if (unlink(at->at_path) < 0)
561189279Srwatson					ret = ADE_EXPIRE;
562189279Srwatson				total_size -= at->at_size;
563189279Srwatson			}
564189279Srwatson		} else {
565189279Srwatson			if ((expire_size && total_size > expire_size) ||
566189279Srwatson			    (expire_age && at->at_time < oldest_time)) {
567189279Srwatson				if (warn_expired)
568243750Srwatson					(*warn_expired)(at->at_path);
569189279Srwatson				if (unlink(at->at_path) < 0)
570189279Srwatson					ret = ADE_EXPIRE;
571189279Srwatson				total_size -= at->at_size;
572189279Srwatson			}
573189279Srwatson		}
574189279Srwatson
575189279Srwatson		free(at->at_path);
576189279Srwatson		free(at);
577189279Srwatson		at = at_next;
578189279Srwatson	}
579189279Srwatson
580189279Srwatson	return (ret);
581189279Srwatson}
582189279Srwatson
583189279Srwatson/*
584186545Srwatson * Parses the "dir" entry in audit_control(5) into an ordered list.  Also, will
585189279Srwatson * set the minfree and host values if not already set.  Arguments include
586189279Srwatson * function pointers to audit_warn functions for soft and hard limits. Returns:
587186545Srwatson *	ADE_NOERR	on success,
588186545Srwatson *	ADE_PARSE	error parsing audit_control(5),
589186545Srwatson *	ADE_AUDITON	error getting/setting auditon(2) value,
590186545Srwatson *	ADE_NOMEM	error allocating memory,
591186545Srwatson *	ADE_SOFTLIM	if all the directories are over the soft limit,
592186545Srwatson *	ADE_HARDLIM	if all the directories are over the hard limit,
593186545Srwatson */
594186545Srwatsonint
595186545Srwatsonauditd_read_dirs(int (*warn_soft)(char *), int (*warn_hard)(char *))
596186545Srwatson{
597186545Srwatson	char cur_dir[MAXNAMLEN];
598186545Srwatson	struct dir_ent *dirent;
599186545Srwatson	struct statfs sfs;
600186545Srwatson	int err;
601186545Srwatson	char soft, hard;
602186545Srwatson	int tcnt = 0;
603186545Srwatson	int scnt = 0;
604186545Srwatson	int hcnt = 0;
605186545Srwatson
606189279Srwatson	if (auditd_minval == -1 && (err = auditd_set_minfree()) != 0)
607186545Srwatson		return (err);
608186545Srwatson
609189279Srwatson	if (auditd_hostlen == -1)
610189279Srwatson		auditd_set_host();
611189279Srwatson
612243750Srwatson	/*
613243750Srwatson	 * Init directory q.  Force a re-read of the file the next time.
614243750Srwatson	 */
615186545Srwatson	free_dir_q();
616186545Srwatson	endac();
617186545Srwatson
618186545Srwatson	/*
619186545Srwatson	 * Read the list of directories into an ordered linked list
620186545Srwatson	 * admin's preference, then those over soft limit and, finally,
621186545Srwatson	 * those over the hard limit.
622186545Srwatson	 *
623243750Srwatson	 * XXX We should use the reentrant interfaces once they are
624243750Srwatson	 * available.
625243750Srwatson	 */
626186545Srwatson	while (getacdir(cur_dir, MAXNAMLEN) >= 0) {
627186545Srwatson		if (statfs(cur_dir, &sfs) < 0)
628186545Srwatson			continue;  /* XXX should warn */
629243750Srwatson		soft = (sfs.f_bfree < (sfs.f_blocks * auditd_minval / 100 )) ?
630189279Srwatson		    1 : 0;
631186545Srwatson		hard = (sfs.f_bfree < AUDIT_HARD_LIMIT_FREE_BLOCKS) ? 1 : 0;
632186545Srwatson		if (soft) {
633243750Srwatson			if (warn_soft)
634186545Srwatson				(*warn_soft)(cur_dir);
635186545Srwatson			scnt++;
636186545Srwatson		}
637186545Srwatson		if (hard) {
638186545Srwatson			if (warn_hard)
639186545Srwatson				(*warn_hard)(cur_dir);
640186545Srwatson			hcnt++;
641186545Srwatson		}
642186545Srwatson		dirent = (struct dir_ent *) malloc(sizeof(struct dir_ent));
643186545Srwatson		if (dirent == NULL)
644186545Srwatson			return (ADE_NOMEM);
645186545Srwatson		dirent->softlim = soft;
646243750Srwatson		dirent->hardlim = hard;
647186545Srwatson		dirent->dirname = (char *) malloc(MAXNAMLEN);
648186545Srwatson		if (dirent->dirname == NULL) {
649186545Srwatson			free(dirent);
650186545Srwatson			return (ADE_NOMEM);
651186545Srwatson		}
652186545Srwatson		strlcpy(dirent->dirname, cur_dir, MAXNAMLEN);
653186545Srwatson		insert_orderly(dirent);
654186545Srwatson		tcnt++;
655186545Srwatson	}
656186545Srwatson
657186545Srwatson	if (hcnt == tcnt)
658186545Srwatson		return (ADE_HARDLIM);
659186545Srwatson	if (scnt == tcnt)
660186545Srwatson		return (ADE_SOFTLIM);
661186545Srwatson	return (0);
662186545Srwatson}
663186545Srwatson
664186545Srwatsonvoid
665186545Srwatsonauditd_close_dirs(void)
666186545Srwatson{
667186545Srwatson	free_dir_q();
668189279Srwatson	auditd_minval = -1;
669189279Srwatson	auditd_hostlen = -1;
670186545Srwatson}
671186545Srwatson
672186545Srwatson
673186545Srwatson/*
674186545Srwatson * Process the audit event file, obtaining a class mapping for each event, and
675186545Srwatson * set that mapping into the kernel. Return:
676243750Srwatson *	 n	number of event mappings that were successfully processed,
677243750Srwatson *   ADE_NOMEM	if there was an error allocating memory.
678186545Srwatson */
679186545Srwatsonint
680186545Srwatsonauditd_set_evcmap(void)
681186545Srwatson{
682186545Srwatson	au_event_ent_t ev, *evp;
683186545Srwatson	au_evclass_map_t evc_map;
684186545Srwatson	int ctr = 0;
685186545Srwatson
686186545Srwatson	/*
687186545Srwatson	 * XXX There's a risk here that the BSM library will return NULL
688186545Srwatson	 * for an event when it can't properly map it to a class. In that
689186545Srwatson	 * case, we will not process any events beyond the one that failed,
690186545Srwatson	 * but should. We need a way to get a count of the events.
691186545Srwatson	 */
692186545Srwatson	ev.ae_name = (char *)malloc(AU_EVENT_NAME_MAX);
693186545Srwatson	ev.ae_desc = (char *)malloc(AU_EVENT_DESC_MAX);
694243750Srwatson	if (ev.ae_name == NULL || ev.ae_desc == NULL) {
695186545Srwatson		if (ev.ae_name != NULL)
696186545Srwatson			free(ev.ae_name);
697186545Srwatson		return (ADE_NOMEM);
698186545Srwatson	}
699243750Srwatson
700186545Srwatson	/*
701186545Srwatson	 * XXXRW: Currently we have no way to remove mappings from the kernel
702186545Srwatson	 * when they are removed from the file-based mappings.
703186545Srwatson	 */
704186545Srwatson	evp = &ev;
705186545Srwatson	setauevent();
706186545Srwatson	while ((evp = getauevent_r(evp)) != NULL) {
707186545Srwatson		evc_map.ec_number = evp->ae_number;
708186545Srwatson		evc_map.ec_class = evp->ae_class;
709191273Srwatson		if (audit_set_class(&evc_map, sizeof(evc_map)) == 0)
710186545Srwatson			ctr++;
711186545Srwatson	}
712186545Srwatson	endauevent();
713186545Srwatson	free(ev.ae_name);
714186545Srwatson	free(ev.ae_desc);
715186545Srwatson
716186545Srwatson	return (ctr);
717186545Srwatson}
718186545Srwatson
719186545Srwatson/*
720186545Srwatson * Get the non-attributable event string and set the kernel mask.  Return:
721243750Srwatson *	ADE_NOERR	on success,
722186545Srwatson *	ADE_PARSE	error parsing audit_control(5),
723186545Srwatson *	ADE_AUDITON	error setting the mask using auditon(2).
724186545Srwatson */
725186545Srwatsonint
726186545Srwatsonauditd_set_namask(void)
727186545Srwatson{
728186545Srwatson	au_mask_t aumask;
729186545Srwatson	char naeventstr[NA_EVENT_STR_SIZE];
730243750Srwatson
731243750Srwatson	if (getacna(naeventstr, NA_EVENT_STR_SIZE) != 0 ||
732243750Srwatson	    getauditflagsbin(naeventstr, &aumask) != 0)
733186545Srwatson		return (ADE_PARSE);
734186545Srwatson
735191273Srwatson	if (audit_set_kmask(&aumask, sizeof(aumask)) != 0)
736186545Srwatson		return (ADE_AUDITON);
737186545Srwatson
738186545Srwatson	return (ADE_NOERR);
739186545Srwatson}
740186545Srwatson
741186545Srwatson/*
742186545Srwatson * Set the audit control policy if a policy is configured in audit_control(5),
743186545Srwatson * implement the policy. However, if one isn't defined or if there is an error
744186545Srwatson * parsing the control file, set AUDIT_CNT to avoid leaving the system in a
745186545Srwatson * fragile state.  Return:
746243750Srwatson *	ADE_NOERR	on success,
747186545Srwatson *	ADE_PARSE	error parsing audit_control(5),
748186545Srwatson *	ADE_AUDITON	error setting policy using auditon(2).
749186545Srwatson */
750186545Srwatsonint
751186545Srwatsonauditd_set_policy(void)
752186545Srwatson{
753191273Srwatson	int policy;
754186545Srwatson	char polstr[POL_STR_SIZE];
755186545Srwatson
756243750Srwatson	if (getacpol(polstr, POL_STR_SIZE) != 0 ||
757243750Srwatson	    au_strtopol(polstr, &policy) != 0) {
758186545Srwatson		policy = AUDIT_CNT;
759191273Srwatson		if (audit_set_policy(&policy) != 0)
760186545Srwatson			return (ADE_AUDITON);
761186545Srwatson		return (ADE_PARSE);
762243750Srwatson	}
763186545Srwatson
764191273Srwatson	if (audit_set_policy(&policy) != 0)
765186545Srwatson		return (ADE_AUDITON);
766186545Srwatson
767186545Srwatson	return (ADE_NOERR);
768186545Srwatson}
769186545Srwatson
770243750Srwatson/*
771186545Srwatson * Set trail rotation size.  Return:
772243750Srwatson *	ADE_NOERR	on success,
773186545Srwatson *	ADE_PARSE	error parsing audit_control(5),
774186545Srwatson *	ADE_AUDITON	error setting file size using auditon(2).
775186545Srwatson */
776186545Srwatsonint
777186545Srwatsonauditd_set_fsize(void)
778186545Srwatson{
779186545Srwatson	size_t filesz;
780186545Srwatson	au_fstat_t au_fstat;
781186545Srwatson
782186545Srwatson	/*
783186545Srwatson	 * Set trail rotation size.
784186545Srwatson	 */
785186545Srwatson	if (getacfilesz(&filesz) != 0)
786186545Srwatson		return (ADE_PARSE);
787186545Srwatson
788186545Srwatson	bzero(&au_fstat, sizeof(au_fstat));
789186545Srwatson	au_fstat.af_filesz = filesz;
790191273Srwatson	if (audit_set_fsize(&au_fstat, sizeof(au_fstat)) != 0)
791186545Srwatson		return (ADE_AUDITON);
792186545Srwatson
793243750Srwatson	return (ADE_NOERR);
794186545Srwatson}
795186545Srwatson
796243750Srwatsonstatic void
797243750Srwatsoninject_dist(const char *fromname, char *toname, size_t tonamesize)
798243750Srwatson{
799243750Srwatson	char *ptr;
800243750Srwatson
801243750Srwatson	ptr = strrchr(fromname, '/');
802243750Srwatson	assert(ptr != NULL);
803243750Srwatson	assert(ptr - fromname < (ssize_t)tonamesize);
804243750Srwatson	strlcpy(toname, fromname, ptr - fromname + 1);
805243750Srwatson	strlcat(toname, "/dist/", tonamesize);
806243750Srwatson	strlcat(toname, ptr + 1, tonamesize);
807243750Srwatson}
808243750Srwatson
809243750Srwatsonstatic int
810243750Srwatsonauditdist_link(const char *filename)
811243750Srwatson{
812243750Srwatson	char fname[MAXPATHLEN];
813243750Srwatson
814243750Srwatson	if (auditd_dist) {
815243750Srwatson		inject_dist(filename, fname, sizeof(fname));
816243750Srwatson		/* Ignore errors. */
817243750Srwatson		(void) link(filename, fname);
818243750Srwatson	}
819243750Srwatson
820243750Srwatson	return (0);
821243750Srwatson}
822243750Srwatson
823243750Srwatsonint
824243750Srwatsonauditd_rename(const char *fromname, const char *toname)
825243750Srwatson{
826243750Srwatson	char fname[MAXPATHLEN], tname[MAXPATHLEN];
827243750Srwatson
828243750Srwatson	if (auditd_dist) {
829243750Srwatson		inject_dist(fromname, fname, sizeof(fname));
830243750Srwatson		inject_dist(toname, tname, sizeof(tname));
831243750Srwatson		/* Ignore errors. */
832243750Srwatson		(void) rename(fname, tname);
833243750Srwatson	}
834243750Srwatson
835243750Srwatson	return (rename(fromname, toname));
836243750Srwatson}
837243750Srwatson
838186545Srwatson/*
839243750Srwatson * Create the new audit file with appropriate permissions and ownership.
840243750Srwatson * Call auditctl(2) for this file.
841243750Srwatson * Try to clean up if something goes wrong.
842243750Srwatson * *errorp is modified only on auditctl(2) failure.
843186545Srwatson */
844186545Srwatsonstatic int
845243750Srwatsonopen_trail(char *fname, gid_t gid, int *errorp)
846186545Srwatson{
847243750Srwatson	int fd;
848243750Srwatson
849243750Srwatson	/* XXXPJD: What should we do if the file already exists? */
850243750Srwatson	fd = open(fname, O_RDONLY | O_CREAT, S_IRUSR);
851186545Srwatson	if (fd < 0)
852186545Srwatson		return (-1);
853243750Srwatson	if (fchown(fd, -1, gid) < 0 || fchmod(fd, S_IRUSR | S_IRGRP) < 0) {
854243750Srwatson		(void) close(fd);
855243750Srwatson		(void) unlink(fname);
856186545Srwatson		return (-1);
857186545Srwatson	}
858243750Srwatson	(void) close(fd);
859243750Srwatson	if (auditctl(fname) < 0) {
860243750Srwatson		*errorp = errno;
861243750Srwatson		(void) unlink(fname);
862243750Srwatson		return (-1);
863243750Srwatson	}
864243750Srwatson	(void) auditdist_link(fname);
865243750Srwatson	return (0);
866186545Srwatson}
867186545Srwatson
868186545Srwatson/*
869186545Srwatson * Create the new audit trail file, swap with existing audit file.  Arguments
870186545Srwatson * include timestamp for the filename, a pointer to a string for returning the
871243750Srwatson * new file name, GID for trail file, and audit_warn function pointer for
872186545Srwatson * 'getacdir()' errors.  Returns:
873243750Srwatson *	ADE_NOERR	on success,
874243750Srwatson *	ADE_STRERR	if the file name string could not be created,
875243750Srwatson *	ADE_SWAPERR	if the audit trail file could not be swapped,
876243750Srwatson *	ADE_ACTL	if the auditctl(2) call failed but file swap still
877186545Srwatson *			successful.
878186545Srwatson *	ADE_ACTLERR	if the auditctl(2) call failed and file swap failed.
879186545Srwatson *	ADE_SYMLINK	if symlink(2) failed updating the current link.
880186545Srwatson */
881186545Srwatsonint
882243750Srwatsonauditd_swap_trail(char *TS, char **newfile, gid_t gid,
883186545Srwatson    int (*warn_getacdir)(char *))
884186545Srwatson{
885243750Srwatson	char timestr[FILENAME_LEN + 1];
886186545Srwatson	char *fn;
887186545Srwatson	struct dir_ent *dirent;
888186545Srwatson	int saverrno = 0;
889243750Srwatson
890243750Srwatson	if (strlen(TS) != TIMESTAMP_LEN ||
891243750Srwatson	    snprintf(timestr, sizeof(timestr), "%s.%s", TS,
892243750Srwatson	    NOT_TERMINATED) < 0) {
893186545Srwatson		errno = EINVAL;
894186545Srwatson		return (ADE_STRERR);
895186545Srwatson	}
896243750Srwatson
897186545Srwatson	/* Try until we succeed. */
898189279Srwatson	TAILQ_FOREACH(dirent, &dir_q, dirs) {
899243750Srwatson		if (dirent->hardlim)
900186545Srwatson			continue;
901186545Srwatson		if ((fn = affixdir(timestr, dirent)) == NULL)
902186545Srwatson			return (ADE_STRERR);
903186545Srwatson
904186545Srwatson		/*
905243750Srwatson		 * Create the file and pass to the kernel if all went well.
906186545Srwatson		 */
907243750Srwatson		if (open_trail(fn, gid, &saverrno) == 0) {
908243750Srwatson			/* Success. */
909243750Srwatson			*newfile = fn;
910243750Srwatson			if (saverrno) {
911243750Srwatson				/*
912243750Srwatson				 * auditctl() failed but still
913243750Srwatson				 * successful. Return errno and "soft"
914243750Srwatson				 * error.
915186545Srwatson				 */
916243750Srwatson				errno = saverrno;
917243750Srwatson				return (ADE_ACTL);
918243750Srwatson			}
919243750Srwatson			return (ADE_NOERR);
920243750Srwatson		}
921186545Srwatson		/*
922243750Srwatson		 * auditctl failed setting log file. Try again.
923243750Srwatson		 */
924243750Srwatson		/*
925186545Srwatson		 * Tell the administrator about lack of permissions for dir.
926186545Srwatson		 */
927186545Srwatson		if (warn_getacdir != NULL)
928186545Srwatson			(*warn_getacdir)(dirent->dirname);
929186545Srwatson	}
930186545Srwatson	if (saverrno) {
931186545Srwatson		errno = saverrno;
932186545Srwatson		return (ADE_ACTLERR);
933186545Srwatson	} else
934186545Srwatson		return (ADE_SWAPERR);
935186545Srwatson}
936186545Srwatson
937186545Srwatson/*
938186545Srwatson * Mask calling process from being audited. Returns:
939186545Srwatson *	ADE_NOERR	on success,
940186545Srwatson *	ADE_SETAUDIT	if setaudit(2) fails.
941186545Srwatson */
942189279Srwatson#ifdef __APPLE__
943186545Srwatsonint
944186545Srwatsonauditd_prevent_audit(void)
945186545Srwatson{
946189279Srwatson	auditinfo_addr_t aia;
947189279Srwatson
948243750Srwatson	/*
949189279Srwatson	 * To prevent event feedback cycles and avoid audit becoming stalled if
950189279Srwatson	 * auditing is suspended we mask this processes events from being
951189279Srwatson	 * audited.  We allow the uid, tid, and mask fields to be implicitly
952243750Srwatson	 * set to zero, but do set the audit session ID to the PID.
953189279Srwatson	 *
954189279Srwatson	 * XXXRW: Is there more to it than this?
955189279Srwatson	 */
956189279Srwatson	bzero(&aia, sizeof(aia));
957189279Srwatson	aia.ai_asid = AU_ASSIGN_ASID;
958189279Srwatson	aia.ai_termid.at_type = AU_IPv4;
959189279Srwatson	if (setaudit_addr(&aia, sizeof(aia)) != 0)
960243750Srwatson		return (ADE_SETAUDIT);
961189279Srwatson	return (ADE_NOERR);
962189279Srwatson}
963189279Srwatson#else
964189279Srwatsonint
965189279Srwatsonauditd_prevent_audit(void)
966189279Srwatson{
967186545Srwatson	auditinfo_t ai;
968186545Srwatson
969243750Srwatson	/*
970186545Srwatson	 * To prevent event feedback cycles and avoid audit becoming stalled if
971186545Srwatson	 * auditing is suspended we mask this processes events from being
972186545Srwatson	 * audited.  We allow the uid, tid, and mask fields to be implicitly
973243750Srwatson	 * set to zero, but do set the audit session ID to the PID.
974186545Srwatson	 *
975186545Srwatson	 * XXXRW: Is there more to it than this?
976186545Srwatson	 */
977186545Srwatson	bzero(&ai, sizeof(ai));
978186545Srwatson	ai.ai_asid = getpid();
979186545Srwatson	if (setaudit(&ai) != 0)
980243750Srwatson		return (ADE_SETAUDIT);
981186545Srwatson	return (ADE_NOERR);
982186545Srwatson}
983243750Srwatson#endif /* !__APPLE__ */
984186545Srwatson
985186545Srwatson/*
986186545Srwatson * Generate and submit audit record for audit startup or shutdown.  The event
987186545Srwatson * argument can be AUE_audit_recovery, AUE_audit_startup or
988186545Srwatson * AUE_audit_shutdown. The path argument will add a path token, if not NULL.
989186545Srwatson * Returns:
990186545Srwatson *	AUE_NOERR	on success,
991186545Srwatson *	ADE_NOMEM	if memory allocation fails,
992243750Srwatson *	ADE_AU_OPEN	if au_open(3) fails,
993186545Srwatson *	ADE_AU_CLOSE	if au_close(3) fails.
994186545Srwatson */
995186545Srwatsonint
996186545Srwatsonauditd_gen_record(int event, char *path)
997186545Srwatson{
998186545Srwatson	int aufd;
999186545Srwatson	uid_t uid;
1000186545Srwatson	pid_t pid;
1001186545Srwatson	char *autext = NULL;
1002186545Srwatson	token_t *tok;
1003186545Srwatson	struct auditinfo_addr aia;
1004186545Srwatson
1005186545Srwatson	if (event == AUE_audit_startup)
1006186545Srwatson		asprintf(&autext, "%s::Audit startup", getprogname());
1007186545Srwatson	else if (event == AUE_audit_shutdown)
1008186545Srwatson		asprintf(&autext, "%s::Audit shutdown", getprogname());
1009186545Srwatson	else if (event == AUE_audit_recovery)
1010186545Srwatson		asprintf(&autext, "%s::Audit recovery", getprogname());
1011243750Srwatson	else
1012186545Srwatson		return (ADE_INVAL);
1013186545Srwatson	if (autext == NULL)
1014186545Srwatson		return (ADE_NOMEM);
1015186545Srwatson
1016186545Srwatson	if ((aufd = au_open()) == -1) {
1017186545Srwatson		free(autext);
1018186545Srwatson		return (ADE_AU_OPEN);
1019186545Srwatson	}
1020186545Srwatson	bzero(&aia, sizeof(aia));
1021186545Srwatson	uid = getuid(); pid = getpid();
1022186545Srwatson	if ((tok = au_to_subject32_ex(uid, geteuid(), getegid(), uid, getgid(),
1023243750Srwatson	    pid, pid, &aia.ai_termid)) != NULL)
1024186545Srwatson		au_write(aufd, tok);
1025186545Srwatson	if ((tok = au_to_text(autext)) != NULL)
1026186545Srwatson		au_write(aufd, tok);
1027186545Srwatson	free(autext);
1028186545Srwatson	if (path != NULL && (tok = au_to_path(path)) != NULL)
1029186545Srwatson		au_write(aufd, tok);
1030186545Srwatson	if ((tok = au_to_return32(0, 0)) != NULL)
1031186545Srwatson		au_write(aufd, tok);
1032186545Srwatson	if (au_close(aufd, 1, event) == -1)
1033186545Srwatson		return (ADE_AU_CLOSE);
1034186545Srwatson
1035186545Srwatson	return (ADE_NOERR);
1036186545Srwatson}
1037186545Srwatson
1038186545Srwatson/*
1039186545Srwatson * Check for a 'current' symlink and do crash recovery, if needed. Create a new
1040186545Srwatson * 'current' symlink. The argument 'curfile' is the file the 'current' symlink
1041186545Srwatson * should point to.  Returns:
1042186545Srwatson *	ADE_NOERR	on success,
1043243750Srwatson *	ADE_AU_OPEN	if au_open(3) fails,
1044243750Srwatson *	ADE_AU_CLOSE	if au_close(3) fails.
1045186545Srwatson *	ADE_RENAME	if error renaming audit trail file,
1046186545Srwatson *	ADE_READLINK	if error reading the 'current' link,
1047186545Srwatson *	ADE_SYMLINK	if error creating 'current' link.
1048186545Srwatson */
1049186545Srwatsonint
1050186545Srwatsonauditd_new_curlink(char *curfile)
1051186545Srwatson{
1052186545Srwatson	int len, err;
1053186545Srwatson	char *ptr;
1054186545Srwatson	char *path = NULL;
1055186545Srwatson	struct stat sb;
1056186545Srwatson	char recoveredname[MAXPATHLEN];
1057186545Srwatson	char newname[MAXPATHLEN];
1058186545Srwatson
1059186545Srwatson	/*
1060186545Srwatson	 * Check to see if audit was shutdown properly.  If not, clean up,
1061186545Srwatson	 * recover previous audit trail file, and generate audit record.
1062186545Srwatson	 */
1063243750Srwatson	len = readlink(AUDIT_CURRENT_LINK, recoveredname,
1064243750Srwatson	    sizeof(recoveredname) - 1);
1065186545Srwatson	if (len > 0) {
1066186545Srwatson		/* 'current' exist but is it pointing at a valid file?  */
1067186545Srwatson		recoveredname[len++] = '\0';
1068243750Srwatson		if (stat(recoveredname, &sb) == 0) {
1069186545Srwatson			/* Yes, rename it to a crash recovery file. */
1070243750Srwatson			strlcpy(newname, recoveredname, sizeof(newname));
1071186545Srwatson
1072186545Srwatson			if ((ptr = strstr(newname, NOT_TERMINATED)) != NULL) {
1073189279Srwatson				memcpy(ptr, CRASH_RECOVERY, POSTFIX_LEN);
1074243750Srwatson				if (auditd_rename(recoveredname, newname) != 0)
1075186545Srwatson					return (ADE_RENAME);
1076186545Srwatson			} else
1077186545Srwatson				return (ADE_STRERR);
1078186545Srwatson
1079186545Srwatson			path = newname;
1080186545Srwatson		}
1081186545Srwatson
1082186545Srwatson		/* 'current' symlink is (now) invalid so remove it. */
1083186545Srwatson		(void) unlink(AUDIT_CURRENT_LINK);
1084186545Srwatson
1085186545Srwatson		/* Note the crash recovery in current audit trail */
1086186545Srwatson		err = auditd_gen_record(AUE_audit_recovery, path);
1087186545Srwatson		if (err)
1088186545Srwatson			return (err);
1089186545Srwatson	}
1090186545Srwatson
1091186545Srwatson	if (len < 0 && errno != ENOENT)
1092186545Srwatson		return (ADE_READLINK);
1093186545Srwatson
1094186545Srwatson	if (symlink(curfile, AUDIT_CURRENT_LINK) != 0)
1095186545Srwatson		return (ADE_SYMLINK);
1096186545Srwatson
1097186545Srwatson	return (0);
1098186545Srwatson}
1099186545Srwatson
1100186545Srwatson/*
1101186545Srwatson * Do just what we need to quickly start auditing.  Assume no system logging or
1102186545Srwatson * notify.  Return:
1103186545Srwatson *   0	 on success,
1104186545Srwatson *  -1   on failure.
1105186545Srwatson */
1106186545Srwatsonint
1107186545Srwatsonaudit_quick_start(void)
1108186545Srwatson{
1109186545Srwatson	int err;
1110189279Srwatson	char *newfile = NULL;
1111186545Srwatson	time_t tt;
1112243750Srwatson	char TS[TIMESTAMP_LEN + 1];
1113189279Srwatson	int ret = 0;
1114186545Srwatson
1115243750Srwatson	/*
1116186545Srwatson	 * Mask auditing of this process.
1117186545Srwatson	 */
1118186545Srwatson	if (auditd_prevent_audit() != 0)
1119186545Srwatson		return (-1);
1120186545Srwatson
1121186545Srwatson	/*
1122186545Srwatson	 * Read audit_control and get log directories.
1123186545Srwatson	 */
1124243750Srwatson	err = auditd_read_dirs(NULL, NULL);
1125186545Srwatson	if (err != ADE_NOERR && err != ADE_SOFTLIM)
1126186545Srwatson		return (-1);
1127186545Srwatson
1128186545Srwatson	/*
1129243750Srwatson	 * Setup trail file distribution.
1130243750Srwatson	 */
1131243750Srwatson	(void) auditd_set_dist();
1132243750Srwatson
1133243750Srwatson	/*
1134186545Srwatson	 *  Create a new audit trail log.
1135186545Srwatson	 */
1136243750Srwatson	if (getTSstr(tt, TS, sizeof(TS)) != 0)
1137186545Srwatson		return (-1);
1138186545Srwatson	err = auditd_swap_trail(TS, &newfile, getgid(), NULL);
1139189279Srwatson	if (err != ADE_NOERR && err != ADE_ACTL) {
1140189279Srwatson		ret = -1;
1141189279Srwatson		goto out;
1142189279Srwatson	}
1143186545Srwatson
1144186545Srwatson	/*
1145243750Srwatson	 * Add the current symlink and recover from crash, if needed.
1146186545Srwatson	 */
1147189279Srwatson	if (auditd_new_curlink(newfile) != 0) {
1148189279Srwatson		ret = -1;
1149189279Srwatson		goto out;
1150189279Srwatson	}
1151186545Srwatson
1152186545Srwatson	/*
1153186545Srwatson	 * At this point auditing has started so generate audit start-up record.
1154186545Srwatson	 */
1155189279Srwatson	if (auditd_gen_record(AUE_audit_startup, NULL) != 0) {
1156189279Srwatson		ret = -1;
1157189279Srwatson		goto out;
1158189279Srwatson	}
1159186545Srwatson
1160186545Srwatson	/*
1161186545Srwatson	 *  Configure the audit controls.
1162186545Srwatson	 */
1163186545Srwatson	(void) auditd_set_evcmap();
1164186545Srwatson	(void) auditd_set_namask();
1165186545Srwatson	(void) auditd_set_policy();
1166186545Srwatson	(void) auditd_set_fsize();
1167186545Srwatson	(void) auditd_set_minfree();
1168186545Srwatson	(void) auditd_set_host();
1169186545Srwatson
1170189279Srwatsonout:
1171189279Srwatson	if (newfile != NULL)
1172189279Srwatson		free(newfile);
1173189279Srwatson
1174189279Srwatson	return (ret);
1175186545Srwatson}
1176186545Srwatson
1177186545Srwatson/*
1178186545Srwatson * Shut down auditing quickly.  Assumes that is only called on system shutdown.
1179186545Srwatson * Returns:
1180186545Srwatson *	 0	on success,
1181186545Srwatson *	-1	on failure.
1182186545Srwatson */
1183186545Srwatsonint
1184186545Srwatsonaudit_quick_stop(void)
1185186545Srwatson{
1186186545Srwatson	int len;
1187191273Srwatson	int cond;
1188186545Srwatson	char *ptr;
1189186545Srwatson	time_t tt;
1190186545Srwatson	char oldname[MAXPATHLEN];
1191186545Srwatson	char newname[MAXPATHLEN];
1192243750Srwatson	char TS[TIMESTAMP_LEN + 1];
1193186545Srwatson
1194186545Srwatson	/*
1195186545Srwatson	 * Auditing already disabled?
1196186545Srwatson	 */
1197191273Srwatson	if (audit_get_cond(&cond) != 0)
1198186545Srwatson		return (-1);
1199187214Srwatson	if (cond == AUC_NOAUDIT)
1200186545Srwatson		return (0);
1201186545Srwatson
1202186545Srwatson	/*
1203186545Srwatson	 *  Generate audit shutdown record.
1204186545Srwatson	 */
1205186545Srwatson	(void) auditd_gen_record(AUE_audit_shutdown, NULL);
1206186545Srwatson
1207186545Srwatson	/*
1208186545Srwatson	 * Shutdown auditing in the kernel.
1209186545Srwatson	 */
1210186545Srwatson	cond = AUC_DISABLED;
1211191273Srwatson	if (audit_set_cond(&cond) != 0)
1212186545Srwatson		return (-1);
1213186545Srwatson#ifdef	__BSM_INTERNAL_NOTIFY_KEY
1214186545Srwatson	notify_post(__BSM_INTERNAL_NOTIFY_KEY);
1215186545Srwatson#endif
1216186545Srwatson
1217186545Srwatson	/*
1218186545Srwatson	 * Rename last audit trail and remove 'current' link.
1219186545Srwatson	 */
1220243750Srwatson	len = readlink(AUDIT_CURRENT_LINK, oldname, sizeof(oldname) - 1);
1221186545Srwatson	if (len < 0)
1222186545Srwatson		return (-1);
1223186545Srwatson	oldname[len++] = '\0';
1224186545Srwatson
1225243750Srwatson	if (getTSstr(tt, TS, sizeof(TS)) != 0)
1226186545Srwatson		return (-1);
1227186545Srwatson
1228243750Srwatson	strlcpy(newname, oldname, sizeof(newname));
1229186545Srwatson
1230186545Srwatson	if ((ptr = strstr(newname, NOT_TERMINATED)) != NULL) {
1231189279Srwatson		memcpy(ptr, TS, POSTFIX_LEN);
1232243750Srwatson		if (auditd_rename(oldname, newname) != 0)
1233186545Srwatson			return (-1);
1234186545Srwatson	} else
1235186545Srwatson		return (-1);
1236243750Srwatson
1237186545Srwatson	(void) unlink(AUDIT_CURRENT_LINK);
1238186545Srwatson
1239186545Srwatson	return (0);
1240186545Srwatson}
1241