17a0a89d2SRobert Watson /*-
206edd2f1SRobert Watson  * Copyright (c) 2008-2009 Apple Inc.
35e386598SRobert Watson  * Copyright (c) 2016 Robert N. M. Watson
47a0a89d2SRobert Watson  * All rights reserved.
57a0a89d2SRobert Watson  *
65e386598SRobert Watson  * Portions of this software were developed by BAE Systems, the University of
75e386598SRobert Watson  * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL
85e386598SRobert Watson  * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent
95e386598SRobert Watson  * Computing (TC) research program.
105e386598SRobert Watson  *
117a0a89d2SRobert Watson  * Redistribution and use in source and binary forms, with or without
127a0a89d2SRobert Watson  * modification, are permitted provided that the following conditions
137a0a89d2SRobert Watson  * are met:
147a0a89d2SRobert Watson  * 1.  Redistributions of source code must retain the above copyright
157a0a89d2SRobert Watson  *     notice, this list of conditions and the following disclaimer.
167a0a89d2SRobert Watson  * 2.  Redistributions in binary form must reproduce the above copyright
177a0a89d2SRobert Watson  *     notice, this list of conditions and the following disclaimer in the
187a0a89d2SRobert Watson  *     documentation and/or other materials provided with the distribution.
197a0a89d2SRobert Watson  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
207a0a89d2SRobert Watson  *     its contributors may be used to endorse or promote products derived
217a0a89d2SRobert Watson  *     from this software without specific prior written permission.
227a0a89d2SRobert Watson  *
237a0a89d2SRobert Watson  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
247a0a89d2SRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
257a0a89d2SRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
267a0a89d2SRobert Watson  * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
277a0a89d2SRobert Watson  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
287a0a89d2SRobert Watson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
297a0a89d2SRobert Watson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
307a0a89d2SRobert Watson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
317a0a89d2SRobert Watson  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
327a0a89d2SRobert Watson  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
337a0a89d2SRobert Watson  * POSSIBILITY OF SUCH DAMAGE.
347a0a89d2SRobert Watson  */
357a0a89d2SRobert Watson 
367a0a89d2SRobert Watson #include <sys/param.h>
377a0a89d2SRobert Watson 
387a0a89d2SRobert Watson #include <config/config.h>
397a0a89d2SRobert Watson 
407a0a89d2SRobert Watson #include <sys/dirent.h>
417a0a89d2SRobert Watson #ifdef HAVE_FULL_QUEUE_H
427a0a89d2SRobert Watson #include <sys/queue.h>
437a0a89d2SRobert Watson #else /* !HAVE_FULL_QUEUE_H */
447a0a89d2SRobert Watson #include <compat/queue.h>
457a0a89d2SRobert Watson #endif /* !HAVE_FULL_QUEUE_H */
46c0020399SRobert Watson #include <sys/mount.h>
47c0020399SRobert Watson #include <sys/socket.h>
487a0a89d2SRobert Watson 
497a0a89d2SRobert Watson #include <sys/stat.h>
507a0a89d2SRobert Watson #include <sys/time.h>
517a0a89d2SRobert Watson 
527a0a89d2SRobert Watson #include <netinet/in.h>
537a0a89d2SRobert Watson 
547a0a89d2SRobert Watson #include <bsm/audit.h>
557a0a89d2SRobert Watson #include <bsm/audit_uevents.h>
567a0a89d2SRobert Watson #include <bsm/auditd_lib.h>
577a0a89d2SRobert Watson #include <bsm/libbsm.h>
587a0a89d2SRobert Watson 
59aa772005SRobert Watson #include <assert.h>
6006edd2f1SRobert Watson #include <dirent.h>
617a0a89d2SRobert Watson #include <err.h>
627a0a89d2SRobert Watson #include <errno.h>
637a0a89d2SRobert Watson #include <fcntl.h>
647a0a89d2SRobert Watson #include <stdio.h>
657a0a89d2SRobert Watson #include <string.h>
667a0a89d2SRobert Watson #include <stdlib.h>
677a0a89d2SRobert Watson #include <time.h>
687a0a89d2SRobert Watson #include <unistd.h>
697a0a89d2SRobert Watson #include <netdb.h>
707a0a89d2SRobert Watson 
717a0a89d2SRobert Watson #ifdef __APPLE__
727a0a89d2SRobert Watson #include <notify.h>
737a0a89d2SRobert Watson #ifndef __BSM_INTERNAL_NOTIFY_KEY
747a0a89d2SRobert Watson #define	__BSM_INTERNAL_NOTIFY_KEY	"com.apple.audit.change"
757a0a89d2SRobert Watson #endif /* __BSM_INTERNAL_NOTIFY_KEY */
767a0a89d2SRobert Watson #endif /* __APPLE__ */
777a0a89d2SRobert Watson 
787a0a89d2SRobert Watson /*
797a0a89d2SRobert Watson  * XXX This is temporary until this is moved to <bsm/audit.h> and shared with
807a0a89d2SRobert Watson  * the kernel.
817a0a89d2SRobert Watson  */
827a0a89d2SRobert Watson #ifndef	AUDIT_HARD_LIMIT_FREE_BLOCKS
837a0a89d2SRobert Watson #define	AUDIT_HARD_LIMIT_FREE_BLOCKS	4
847a0a89d2SRobert Watson #endif
857a0a89d2SRobert Watson 
8606edd2f1SRobert Watson /*
8706edd2f1SRobert Watson  * Number of seconds to January 1, 2000
8806edd2f1SRobert Watson  */
8906edd2f1SRobert Watson #define	JAN_01_2000	946598400
9006edd2f1SRobert Watson 
917a0a89d2SRobert Watson struct dir_ent {
927a0a89d2SRobert Watson 	char			*dirname;
937a0a89d2SRobert Watson 	uint8_t			 softlim;
947a0a89d2SRobert Watson 	uint8_t			 hardlim;
957a0a89d2SRobert Watson 	TAILQ_ENTRY(dir_ent)	 dirs;
967a0a89d2SRobert Watson };
977a0a89d2SRobert Watson 
987a0a89d2SRobert Watson static TAILQ_HEAD(, dir_ent)	dir_q;
9906edd2f1SRobert Watson 
10006edd2f1SRobert Watson struct audit_trail {
10106edd2f1SRobert Watson 	time_t			 at_time;
10206edd2f1SRobert Watson 	char			*at_path;
10306edd2f1SRobert Watson 	off_t			 at_size;
10406edd2f1SRobert Watson 
10506edd2f1SRobert Watson 	TAILQ_ENTRY(audit_trail) at_trls;
10606edd2f1SRobert Watson };
10706edd2f1SRobert Watson 
10806edd2f1SRobert Watson static int auditd_minval = -1;
109aa772005SRobert Watson static int auditd_dist = 0;
11006edd2f1SRobert Watson 
11106edd2f1SRobert Watson static char auditd_host[MAXHOSTNAMELEN];
11206edd2f1SRobert Watson static int auditd_hostlen = -1;
1137a0a89d2SRobert Watson 
1147a0a89d2SRobert Watson static char *auditd_errmsg[] = {
1157a0a89d2SRobert Watson 	"no error",					/* ADE_NOERR	( 0) */
1167a0a89d2SRobert Watson 	"could not parse audit_control(5) file",	/* ADE_PARSE	( 1) */
1177a0a89d2SRobert Watson 	"auditon(2) failed",				/* ADE_AUDITON	( 2) */
1187a0a89d2SRobert Watson 	"malloc(3) failed",				/* ADE_NOMEM	( 3) */
1197a0a89d2SRobert Watson 	"all audit log directories over soft limit",	/* ADE_SOFTLIM	( 4) */
1207a0a89d2SRobert Watson 	"all audit log directories over hard limit",	/* ADE_HARDLIM	( 5) */
1217a0a89d2SRobert Watson 	"could not create file name string",		/* ADE_STRERR	( 6) */
1227a0a89d2SRobert Watson 	"could not open audit record",			/* ADE_AU_OPEN	( 7) */
1237a0a89d2SRobert Watson 	"could not close audit record",			/* ADE_AU_CLOSE	( 8) */
1247a0a89d2SRobert Watson 	"could not set active audit session state",	/* ADE_SETAUDIT	( 9) */
1257a0a89d2SRobert Watson 	"auditctl(2) failed (trail still swapped)",	/* ADE_ACTL	(10) */
1267a0a89d2SRobert Watson 	"auditctl(2) failed (trail not swapped)",	/* ADE_ACTLERR	(11) */
1277a0a89d2SRobert Watson 	"could not swap audit trail file",		/* ADE_SWAPERR	(12) */
1287a0a89d2SRobert Watson 	"could not rename crash recovery file",		/* ADE_RENAME	(13) */
1297a0a89d2SRobert Watson 	"could not read 'current' link file",		/* ADE_READLINK	(14) */
1307a0a89d2SRobert Watson 	"could not create 'current' link file",		/* ADE_SYMLINK	(15) */
1317a0a89d2SRobert Watson 	"invalid argument",				/* ADE_INVAL	(16) */
1327a0a89d2SRobert Watson 	"could not resolve hostname to address",	/* ADE_GETADDR	(17) */
1337a0a89d2SRobert Watson 	"address family not supported",			/* ADE_ADDRFAM	(18) */
13406edd2f1SRobert Watson 	"error expiring audit trail files",		/* ADE_EXPIRE	(19) */
1357a0a89d2SRobert Watson };
1367a0a89d2SRobert Watson 
1377a0a89d2SRobert Watson #define	MAXERRCODE	(sizeof(auditd_errmsg) / sizeof(auditd_errmsg[0]))
1387a0a89d2SRobert Watson 
139597df30eSRobert Watson #define	NA_EVENT_STR_SIZE	128
1407a0a89d2SRobert Watson #define	POL_STR_SIZE		128
1417a0a89d2SRobert Watson 
1427a0a89d2SRobert Watson 
1437a0a89d2SRobert Watson /*
1447a0a89d2SRobert Watson  * Look up and return the error string for the given audit error code.
1457a0a89d2SRobert Watson  */
1467a0a89d2SRobert Watson const char *
auditd_strerror(int errcode)1477a0a89d2SRobert Watson auditd_strerror(int errcode)
1487a0a89d2SRobert Watson {
1497a0a89d2SRobert Watson 	int idx = -errcode;
1507a0a89d2SRobert Watson 
1517a0a89d2SRobert Watson 	if (idx < 0 || idx > (int)MAXERRCODE)
1527a0a89d2SRobert Watson 		return ("Invalid auditd error code");
1537a0a89d2SRobert Watson 
1547a0a89d2SRobert Watson 	return (auditd_errmsg[idx]);
1557a0a89d2SRobert Watson }
1567a0a89d2SRobert Watson 
1577a0a89d2SRobert Watson 
1587a0a89d2SRobert Watson /*
159aa772005SRobert Watson  * Free our local list of directory names and init list.
1607a0a89d2SRobert Watson  */
1617a0a89d2SRobert Watson static void
free_dir_q(void)1627a0a89d2SRobert Watson free_dir_q(void)
1637a0a89d2SRobert Watson {
1647a0a89d2SRobert Watson 	struct dir_ent *d1, *d2;
1657a0a89d2SRobert Watson 
1667a0a89d2SRobert Watson 	d1 = TAILQ_FIRST(&dir_q);
1677a0a89d2SRobert Watson 	while (d1 != NULL) {
1687a0a89d2SRobert Watson 		d2 = TAILQ_NEXT(d1, dirs);
1697a0a89d2SRobert Watson 		free(d1->dirname);
1707a0a89d2SRobert Watson 		free(d1);
1717a0a89d2SRobert Watson 		d1 = d2;
1727a0a89d2SRobert Watson 	}
1737a0a89d2SRobert Watson 	TAILQ_INIT(&dir_q);
1747a0a89d2SRobert Watson }
1757a0a89d2SRobert Watson 
1767a0a89d2SRobert Watson /*
1777a0a89d2SRobert Watson  * Concat the directory name to the given file name.
1787a0a89d2SRobert Watson  * XXX We should affix the hostname also
1797a0a89d2SRobert Watson  */
1807a0a89d2SRobert Watson static char *
affixdir(char * name,struct dir_ent * dirent)1817a0a89d2SRobert Watson affixdir(char *name, struct dir_ent *dirent)
1827a0a89d2SRobert Watson {
1837a0a89d2SRobert Watson 	char *fn = NULL;
1847a0a89d2SRobert Watson 
1857a0a89d2SRobert Watson 	/*
1867a0a89d2SRobert Watson 	 * Sanity check on file name.
1877a0a89d2SRobert Watson 	 */
188aa772005SRobert Watson 	if (strlen(name) != FILENAME_LEN) {
1897a0a89d2SRobert Watson 		errno = EINVAL;
1907a0a89d2SRobert Watson 		return (NULL);
1917a0a89d2SRobert Watson 	}
1927a0a89d2SRobert Watson 
19306edd2f1SRobert Watson 	/*
19406edd2f1SRobert Watson 	 * If the host is set then also add the hostname to the filename.
19506edd2f1SRobert Watson 	 */
1966d4db583SPawel Jakub Dawidek 	if (auditd_hostlen > 0)
19706edd2f1SRobert Watson 		asprintf(&fn, "%s/%s.%s", dirent->dirname, name, auditd_host);
19806edd2f1SRobert Watson 	else
1997a0a89d2SRobert Watson 		asprintf(&fn, "%s/%s", dirent->dirname, name);
2007a0a89d2SRobert Watson 	return (fn);
2017a0a89d2SRobert Watson }
2027a0a89d2SRobert Watson 
2037a0a89d2SRobert Watson /*
2047a0a89d2SRobert Watson  * Insert the directory entry in the list by the way they are ordered in
2057a0a89d2SRobert Watson  * audit_control(5).  Move the entries that are over the soft and hard limits
2067a0a89d2SRobert Watson  * toward the tail.
2077a0a89d2SRobert Watson  */
2087a0a89d2SRobert Watson static void
insert_orderly(struct dir_ent * denew)2097a0a89d2SRobert Watson insert_orderly(struct dir_ent *denew)
2107a0a89d2SRobert Watson {
2117a0a89d2SRobert Watson 	struct dir_ent *dep;
2127a0a89d2SRobert Watson 
2137a0a89d2SRobert Watson 	TAILQ_FOREACH(dep, &dir_q, dirs) {
2147a0a89d2SRobert Watson 		if (dep->softlim == 1 && denew->softlim == 0) {
2157a0a89d2SRobert Watson 			TAILQ_INSERT_BEFORE(dep, denew, dirs);
2167a0a89d2SRobert Watson 			return;
2177a0a89d2SRobert Watson 		}
2187a0a89d2SRobert Watson 		if (dep->hardlim == 1 && denew->hardlim == 0) {
2197a0a89d2SRobert Watson 			TAILQ_INSERT_BEFORE(dep, denew, dirs);
2207a0a89d2SRobert Watson 			return;
2217a0a89d2SRobert Watson 		}
2227a0a89d2SRobert Watson 	}
2237a0a89d2SRobert Watson 	TAILQ_INSERT_TAIL(&dir_q, denew, dirs);
2247a0a89d2SRobert Watson }
2257a0a89d2SRobert Watson 
2267a0a89d2SRobert Watson /*
227aa772005SRobert Watson  * Get the min percentage of free blocks from audit_control(5) and that
228aa772005SRobert Watson  * value in the kernel.  Return:
229aa772005SRobert Watson  *	ADE_NOERR	on success,
230aa772005SRobert Watson  *	ADE_PARSE	error parsing audit_control(5),
231aa772005SRobert Watson  */
232aa772005SRobert Watson int
auditd_set_dist(void)233aa772005SRobert Watson auditd_set_dist(void)
234aa772005SRobert Watson {
235aa772005SRobert Watson 	int ret;
236aa772005SRobert Watson 
237aa772005SRobert Watson 	ret = getacdist();
238aa772005SRobert Watson 	if (ret < 0)
239aa772005SRobert Watson 		return (ADE_PARSE);
240aa772005SRobert Watson 
241aa772005SRobert Watson 	auditd_dist = ret;
242aa772005SRobert Watson 
243aa772005SRobert Watson 	return (ADE_NOERR);
244aa772005SRobert Watson }
245aa772005SRobert Watson 
246aa772005SRobert Watson /*
2477a0a89d2SRobert Watson  * Get the host from audit_control(5) and set it in the audit kernel
2487a0a89d2SRobert Watson  * information.  Return:
2497a0a89d2SRobert Watson  *	ADE_NOERR	on success.
2507a0a89d2SRobert Watson  *	ADE_PARSE	error parsing audit_control(5).
2517a0a89d2SRobert Watson  *	ADE_AUDITON	error getting/setting auditon(2) value.
2527a0a89d2SRobert Watson  *	ADE_GETADDR	error getting address info for host.
2537a0a89d2SRobert Watson  *	ADE_ADDRFAM	un-supported address family.
2547a0a89d2SRobert Watson  */
2557a0a89d2SRobert Watson int
auditd_set_host(void)2567a0a89d2SRobert Watson auditd_set_host(void)
2577a0a89d2SRobert Watson {
2587a0a89d2SRobert Watson 	struct sockaddr_in6 *sin6;
2597a0a89d2SRobert Watson 	struct sockaddr_in *sin;
2607a0a89d2SRobert Watson 	struct addrinfo *res;
2617a0a89d2SRobert Watson 	struct auditinfo_addr aia;
2627a0a89d2SRobert Watson 	int error, ret = ADE_NOERR;
2637a0a89d2SRobert Watson 
2646e3b0894SAlan Somers 	if ((getachost(auditd_host, sizeof(auditd_host)) != 0) ||
2656e3b0894SAlan Somers 	    ((auditd_hostlen = strlen(auditd_host)) == 0)) {
2667a0a89d2SRobert Watson 		ret = ADE_PARSE;
2677a0a89d2SRobert Watson 
2687a0a89d2SRobert Watson 		/*
2697a0a89d2SRobert Watson 		 * To maintain reverse compatability with older audit_control
2707a0a89d2SRobert Watson 		 * files, simply drop a warning if the host parameter has not
2717a0a89d2SRobert Watson 		 * been set.  However, we will explicitly disable the
2727a0a89d2SRobert Watson 		 * generation of extended audit header by passing in a zeroed
2737a0a89d2SRobert Watson 		 * termid structure.
2747a0a89d2SRobert Watson 		 */
2757a0a89d2SRobert Watson 		bzero(&aia, sizeof(aia));
2767a0a89d2SRobert Watson 		aia.ai_termid.at_type = AU_IPv4;
277c0020399SRobert Watson 		error = audit_set_kaudit(&aia, sizeof(aia));
2787a0a89d2SRobert Watson 		if (error < 0 && errno != ENOSYS)
2797a0a89d2SRobert Watson 			ret = ADE_AUDITON;
2807a0a89d2SRobert Watson 		return (ret);
2817a0a89d2SRobert Watson 	}
28206edd2f1SRobert Watson 	error = getaddrinfo(auditd_host, NULL, NULL, &res);
2837a0a89d2SRobert Watson 	if (error)
2847a0a89d2SRobert Watson 		return (ADE_GETADDR);
2857a0a89d2SRobert Watson 	switch (res->ai_family) {
2867a0a89d2SRobert Watson 	case PF_INET6:
2877a0a89d2SRobert Watson 		sin6 = (struct sockaddr_in6 *) res->ai_addr;
2887a0a89d2SRobert Watson 		bcopy(&sin6->sin6_addr.s6_addr,
2897a0a89d2SRobert Watson 		    &aia.ai_termid.at_addr[0], sizeof(struct in6_addr));
2907a0a89d2SRobert Watson 		aia.ai_termid.at_type = AU_IPv6;
2917a0a89d2SRobert Watson 		break;
2927a0a89d2SRobert Watson 
2937a0a89d2SRobert Watson 	case PF_INET:
2947a0a89d2SRobert Watson 		sin = (struct sockaddr_in *) res->ai_addr;
2957a0a89d2SRobert Watson 		bcopy(&sin->sin_addr.s_addr,
2967a0a89d2SRobert Watson 		    &aia.ai_termid.at_addr[0], sizeof(struct in_addr));
2977a0a89d2SRobert Watson 		aia.ai_termid.at_type = AU_IPv4;
2987a0a89d2SRobert Watson 		break;
2997a0a89d2SRobert Watson 
3007a0a89d2SRobert Watson 	default:
3017a0a89d2SRobert Watson 		/* Un-supported address family in host parameter. */
3027a0a89d2SRobert Watson 		errno = EAFNOSUPPORT;
3037a0a89d2SRobert Watson 		return (ADE_ADDRFAM);
3047a0a89d2SRobert Watson 	}
3057a0a89d2SRobert Watson 
306c0020399SRobert Watson 	if (audit_set_kaudit(&aia, sizeof(aia)) < 0)
3077a0a89d2SRobert Watson 		ret = ADE_AUDITON;
3087a0a89d2SRobert Watson 
3097a0a89d2SRobert Watson 	return (ret);
3107a0a89d2SRobert Watson }
3117a0a89d2SRobert Watson 
3127a0a89d2SRobert Watson /*
3137a0a89d2SRobert Watson  * Get the min percentage of free blocks from audit_control(5) and that
3147a0a89d2SRobert Watson  * value in the kernel.  Return:
3157a0a89d2SRobert Watson  *	ADE_NOERR	on success,
3167a0a89d2SRobert Watson  *	ADE_PARSE	error parsing audit_control(5),
3177a0a89d2SRobert Watson  *	ADE_AUDITON	error getting/setting auditon(2) value.
3187a0a89d2SRobert Watson  */
3197a0a89d2SRobert Watson int
auditd_set_minfree(void)3207a0a89d2SRobert Watson auditd_set_minfree(void)
3217a0a89d2SRobert Watson {
3227a0a89d2SRobert Watson 	au_qctrl_t qctrl;
3237a0a89d2SRobert Watson 
32406edd2f1SRobert Watson 	if (getacmin(&auditd_minval) != 0)
3257a0a89d2SRobert Watson 		return (ADE_PARSE);
3267a0a89d2SRobert Watson 
327c0020399SRobert Watson 	if (audit_get_qctrl(&qctrl, sizeof(qctrl)) != 0)
3287a0a89d2SRobert Watson 		return (ADE_AUDITON);
3297a0a89d2SRobert Watson 
33006edd2f1SRobert Watson 	if (qctrl.aq_minfree != auditd_minval) {
33106edd2f1SRobert Watson 		qctrl.aq_minfree = auditd_minval;
332c0020399SRobert Watson 		if (audit_set_qctrl(&qctrl, sizeof(qctrl)) != 0)
3337a0a89d2SRobert Watson 			return (ADE_AUDITON);
3347a0a89d2SRobert Watson 	}
3357a0a89d2SRobert Watson 
3367a0a89d2SRobert Watson 	return (0);
3377a0a89d2SRobert Watson }
3387a0a89d2SRobert Watson 
3397a0a89d2SRobert Watson /*
34006edd2f1SRobert Watson  * Convert a trailname into a timestamp (seconds).  Return 0 if the conversion
34106edd2f1SRobert Watson  * was successful.
34206edd2f1SRobert Watson  */
34306edd2f1SRobert Watson static int
trailname_to_tstamp(char * fn,time_t * tstamp)34406edd2f1SRobert Watson trailname_to_tstamp(char *fn, time_t *tstamp)
34506edd2f1SRobert Watson {
34606edd2f1SRobert Watson 	struct tm tm;
347aa772005SRobert Watson 	char ts[TIMESTAMP_LEN + 1];
34806edd2f1SRobert Watson 	char *p;
34906edd2f1SRobert Watson 
35006edd2f1SRobert Watson 	*tstamp = 0;
35106edd2f1SRobert Watson 
35206edd2f1SRobert Watson 	/*
35306edd2f1SRobert Watson 	 * Get the ending time stamp.
35406edd2f1SRobert Watson 	 */
35506edd2f1SRobert Watson 	if ((p = strchr(fn, '.')) == NULL)
35606edd2f1SRobert Watson 		return (1);
357aa772005SRobert Watson 	strlcpy(ts, ++p, sizeof(ts));
35806edd2f1SRobert Watson 	if (strlen(ts) != POSTFIX_LEN)
35906edd2f1SRobert Watson 		return (1);
36006edd2f1SRobert Watson 
36106edd2f1SRobert Watson 	bzero(&tm, sizeof(tm));
36206edd2f1SRobert Watson 
36306edd2f1SRobert Watson 	/* seconds (0-60) */
36406edd2f1SRobert Watson 	p = ts + POSTFIX_LEN - 2;
36506edd2f1SRobert Watson 	tm.tm_sec = atol(p);
36606edd2f1SRobert Watson 	if (tm.tm_sec < 0 || tm.tm_sec > 60)
36706edd2f1SRobert Watson 		return (1);
36806edd2f1SRobert Watson 
36906edd2f1SRobert Watson 	/* minutes (0-59) */
37006edd2f1SRobert Watson 	*p = '\0'; p -= 2;
37106edd2f1SRobert Watson 	tm.tm_min = atol(p);
37206edd2f1SRobert Watson 	if (tm.tm_min < 0 || tm.tm_min > 59)
37306edd2f1SRobert Watson 		return (1);
37406edd2f1SRobert Watson 
37506edd2f1SRobert Watson 	/* hours (0 - 23) */
37606edd2f1SRobert Watson 	*p = '\0'; p -= 2;
37706edd2f1SRobert Watson 	tm.tm_hour = atol(p);
37806edd2f1SRobert Watson 	if (tm.tm_hour < 0 || tm.tm_hour > 23)
37906edd2f1SRobert Watson 		return (1);
38006edd2f1SRobert Watson 
38106edd2f1SRobert Watson 	/* day of month (1-31) */
38206edd2f1SRobert Watson 	*p = '\0'; p -= 2;
38306edd2f1SRobert Watson 	tm.tm_mday = atol(p);
38406edd2f1SRobert Watson 	if (tm.tm_mday < 1 || tm.tm_mday > 31)
38506edd2f1SRobert Watson 		return (1);
38606edd2f1SRobert Watson 
38706edd2f1SRobert Watson 	/* month (0 - 11) */
38806edd2f1SRobert Watson 	*p = '\0'; p -= 2;
38906edd2f1SRobert Watson 	tm.tm_mon = atol(p) - 1;
39006edd2f1SRobert Watson 	if (tm.tm_mon < 0 || tm.tm_mon > 11)
39106edd2f1SRobert Watson 		return (1);
39206edd2f1SRobert Watson 
39306edd2f1SRobert Watson 	/* year (year - 1900) */
39406edd2f1SRobert Watson 	*p = '\0'; p -= 4;
39506edd2f1SRobert Watson 	tm.tm_year = atol(p) - 1900;
39606edd2f1SRobert Watson 	if (tm.tm_year < 0)
39706edd2f1SRobert Watson 		return (1);
39806edd2f1SRobert Watson 
39906edd2f1SRobert Watson 	*tstamp = timegm(&tm);
40006edd2f1SRobert Watson 
40106edd2f1SRobert Watson 	return (0);
40206edd2f1SRobert Watson }
40306edd2f1SRobert Watson 
40406edd2f1SRobert Watson /*
40506edd2f1SRobert Watson  * Remove audit trails files according to the expiration conditions.  Returns:
40606edd2f1SRobert Watson  *	ADE_NOERR	on success or there is nothing to do.
40706edd2f1SRobert Watson  *	ADE_PARSE	if error parsing audit_control(5).
40806edd2f1SRobert Watson  *	ADE_NOMEM	if could not allocate memory.
409b6a05070SChristian Brueffer  *	ADE_READLINK	if could not read link file.
410b6a05070SChristian Brueffer  *	ADE_EXPIRE	if there was an unexpected error.
41106edd2f1SRobert Watson  */
41206edd2f1SRobert Watson int
auditd_expire_trails(int (* warn_expired)(char *))41306edd2f1SRobert Watson auditd_expire_trails(int (*warn_expired)(char *))
41406edd2f1SRobert Watson {
415b6a05070SChristian Brueffer 	int andflg, len, ret = ADE_NOERR;
41606edd2f1SRobert Watson 	size_t expire_size, total_size = 0L;
41706edd2f1SRobert Watson 	time_t expire_age, oldest_time, current_time = time(NULL);
41806edd2f1SRobert Watson 	struct dir_ent *traildir;
41906edd2f1SRobert Watson 	struct audit_trail *at;
42006edd2f1SRobert Watson 	char *afnp, *pn;
42106edd2f1SRobert Watson 	TAILQ_HEAD(au_trls_head, audit_trail) head =
42206edd2f1SRobert Watson 	    TAILQ_HEAD_INITIALIZER(head);
42306edd2f1SRobert Watson 	struct stat stbuf;
42406edd2f1SRobert Watson 	char activefn[MAXPATHLEN];
42506edd2f1SRobert Watson 
42606edd2f1SRobert Watson 	/*
42706edd2f1SRobert Watson 	 * Read the expiration conditions.  If no conditions then return no
42806edd2f1SRobert Watson 	 * error.
42906edd2f1SRobert Watson 	 */
43006edd2f1SRobert Watson 	if (getacexpire(&andflg, &expire_age, &expire_size) < 0)
43106edd2f1SRobert Watson 		return (ADE_PARSE);
43206edd2f1SRobert Watson 	if (!expire_age && !expire_size)
43306edd2f1SRobert Watson 		return (ADE_NOERR);
43406edd2f1SRobert Watson 
43506edd2f1SRobert Watson 	/*
43606edd2f1SRobert Watson 	 * Read the 'current' trail file name.  Trim off directory path.
43706edd2f1SRobert Watson 	 */
43806edd2f1SRobert Watson 	activefn[0] = '\0';
439b6a05070SChristian Brueffer 	len = readlink(AUDIT_CURRENT_LINK, activefn, MAXPATHLEN - 1);
440b6a05070SChristian Brueffer 	if (len < 0)
441b6a05070SChristian Brueffer 		return (ADE_READLINK);
44206edd2f1SRobert Watson 	if ((afnp = strrchr(activefn, '/')) != NULL)
44306edd2f1SRobert Watson 		afnp++;
44406edd2f1SRobert Watson 
44506edd2f1SRobert Watson 
44606edd2f1SRobert Watson 	/*
44706edd2f1SRobert Watson 	 * Build tail queue of the trail files.
44806edd2f1SRobert Watson 	 */
44906edd2f1SRobert Watson 	TAILQ_FOREACH(traildir, &dir_q, dirs) {
45006edd2f1SRobert Watson 		DIR *dirp;
45106edd2f1SRobert Watson 		struct dirent *dp;
45206edd2f1SRobert Watson 
45306edd2f1SRobert Watson 		dirp = opendir(traildir->dirname);
45406edd2f1SRobert Watson 		while ((dp = readdir(dirp)) != NULL) {
45506edd2f1SRobert Watson 			time_t tstamp = 0;
45606edd2f1SRobert Watson 			struct audit_trail *new;
45706edd2f1SRobert Watson 
45806edd2f1SRobert Watson 			/*
45906edd2f1SRobert Watson 			 * Quickly filter non-trail files.
46006edd2f1SRobert Watson 			 */
461aa772005SRobert Watson 			if (dp->d_namlen < FILENAME_LEN ||
46206edd2f1SRobert Watson 			    dp->d_name[POSTFIX_LEN] != '.')
46306edd2f1SRobert Watson 				continue;
46406edd2f1SRobert Watson 
46506edd2f1SRobert Watson 			if (asprintf(&pn, "%s/%s", traildir->dirname,
46606edd2f1SRobert Watson 			    dp->d_name) < 0) {
46706edd2f1SRobert Watson 				ret = ADE_NOMEM;
46806edd2f1SRobert Watson 				break;
46906edd2f1SRobert Watson 			}
47006edd2f1SRobert Watson 
47106edd2f1SRobert Watson 			if (stat(pn, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
47206edd2f1SRobert Watson 				free(pn);
47306edd2f1SRobert Watson 				continue;
47406edd2f1SRobert Watson 			}
47506edd2f1SRobert Watson 
47606edd2f1SRobert Watson 			total_size += stbuf.st_size;
47706edd2f1SRobert Watson 
47806edd2f1SRobert Watson 			/*
47906edd2f1SRobert Watson 			 * If this is the 'current' audit trail then
48006edd2f1SRobert Watson 			 * don't add it to the tail queue.
48106edd2f1SRobert Watson 			 */
482aa772005SRobert Watson 			if (NULL != afnp && strcmp(dp->d_name, afnp) == 0) {
48306edd2f1SRobert Watson 				free(pn);
48406edd2f1SRobert Watson 				continue;
48506edd2f1SRobert Watson 			}
48606edd2f1SRobert Watson 
48706edd2f1SRobert Watson 			/*
48806edd2f1SRobert Watson 			 * Get the ending time stamp encoded in the trail
48906edd2f1SRobert Watson 			 * name.  If we can't read it or if it is older
49006edd2f1SRobert Watson 			 * than Jan 1, 2000 then use the mtime.
49106edd2f1SRobert Watson 			 */
49206edd2f1SRobert Watson 			if (trailname_to_tstamp(dp->d_name, &tstamp) != 0 ||
49306edd2f1SRobert Watson 			    tstamp < JAN_01_2000)
49406edd2f1SRobert Watson 				tstamp = stbuf.st_mtime;
49506edd2f1SRobert Watson 
49606edd2f1SRobert Watson 			/*
49706edd2f1SRobert Watson 			 * If the time stamp is older than Jan 1, 2000 then
49806edd2f1SRobert Watson 			 * update the mtime of the trail file to the current
49906edd2f1SRobert Watson 			 * time. This is so we don't prematurely remove a trail
50006edd2f1SRobert Watson 			 * file that was created while the system clock reset
5013008333dSChristian S.J. Peron 			 * to the "beginning of time" but later the system
50206edd2f1SRobert Watson 			 * clock is set to the correct current time.
50306edd2f1SRobert Watson 			 */
50406edd2f1SRobert Watson 			if (current_time >= JAN_01_2000 &&
50506edd2f1SRobert Watson 			    tstamp < JAN_01_2000) {
50606edd2f1SRobert Watson 				struct timeval tv[2];
50706edd2f1SRobert Watson 
50806edd2f1SRobert Watson 				tstamp = stbuf.st_mtime = current_time;
50906edd2f1SRobert Watson 				TIMESPEC_TO_TIMEVAL(&tv[0],
51006edd2f1SRobert Watson 				    &stbuf.st_atimespec);
51106edd2f1SRobert Watson 				TIMESPEC_TO_TIMEVAL(&tv[1],
51206edd2f1SRobert Watson 				    &stbuf.st_mtimespec);
51306edd2f1SRobert Watson 				utimes(pn, tv);
51406edd2f1SRobert Watson 			}
51506edd2f1SRobert Watson 
51606edd2f1SRobert Watson 			/*
51706edd2f1SRobert Watson 			 * Allocate and populate the new entry.
51806edd2f1SRobert Watson 			 */
51906edd2f1SRobert Watson 			new = malloc(sizeof(*new));
52006edd2f1SRobert Watson 			if (NULL == new) {
52106edd2f1SRobert Watson 				free(pn);
52206edd2f1SRobert Watson 				ret = ADE_NOMEM;
52306edd2f1SRobert Watson 				break;
52406edd2f1SRobert Watson 			}
52506edd2f1SRobert Watson 			new->at_time = tstamp;
52606edd2f1SRobert Watson 			new->at_size = stbuf.st_size;
52706edd2f1SRobert Watson 			new->at_path = pn;
52806edd2f1SRobert Watson 
52906edd2f1SRobert Watson 			/*
53006edd2f1SRobert Watson 			 * Check to see if we have a new head.  Otherwise,
53106edd2f1SRobert Watson 			 * walk the tailq from the tail first and do a simple
53206edd2f1SRobert Watson 			 * insertion sort.
53306edd2f1SRobert Watson 			 */
53406edd2f1SRobert Watson 			if (TAILQ_EMPTY(&head) ||
535aa772005SRobert Watson 			    new->at_time <= TAILQ_FIRST(&head)->at_time) {
53606edd2f1SRobert Watson 				TAILQ_INSERT_HEAD(&head, new, at_trls);
53706edd2f1SRobert Watson 				continue;
53806edd2f1SRobert Watson 			}
53906edd2f1SRobert Watson 
54006edd2f1SRobert Watson 			TAILQ_FOREACH_REVERSE(at, &head, au_trls_head, at_trls)
54106edd2f1SRobert Watson 				if (new->at_time >= at->at_time) {
54206edd2f1SRobert Watson 					TAILQ_INSERT_AFTER(&head, at, new,
54306edd2f1SRobert Watson 					    at_trls);
54406edd2f1SRobert Watson 					break;
54506edd2f1SRobert Watson 				}
54606edd2f1SRobert Watson 
54706edd2f1SRobert Watson 		}
5483ee3cd17SRobert Watson 		closedir(dirp);
54906edd2f1SRobert Watson 	}
55006edd2f1SRobert Watson 
55106edd2f1SRobert Watson 	oldest_time = current_time - expire_age;
55206edd2f1SRobert Watson 
55306edd2f1SRobert Watson 	/*
55406edd2f1SRobert Watson 	 * Expire trail files, oldest (mtime) first, if the given
55506edd2f1SRobert Watson 	 * conditions are met.
55606edd2f1SRobert Watson 	 */
55706edd2f1SRobert Watson 	at = TAILQ_FIRST(&head);
55806edd2f1SRobert Watson 	while (NULL != at) {
55906edd2f1SRobert Watson 		struct audit_trail *at_next = TAILQ_NEXT(at, at_trls);
56006edd2f1SRobert Watson 
56106edd2f1SRobert Watson 		if (andflg) {
56206edd2f1SRobert Watson 			if ((expire_size && total_size > expire_size) &&
56306edd2f1SRobert Watson 			    (expire_age && at->at_time < oldest_time)) {
56406edd2f1SRobert Watson 				if (warn_expired)
56506edd2f1SRobert Watson 					(*warn_expired)(at->at_path);
56606edd2f1SRobert Watson 				if (unlink(at->at_path) < 0)
56706edd2f1SRobert Watson 					ret = ADE_EXPIRE;
56806edd2f1SRobert Watson 				total_size -= at->at_size;
56906edd2f1SRobert Watson 			}
57006edd2f1SRobert Watson 		} else {
57106edd2f1SRobert Watson 			if ((expire_size && total_size > expire_size) ||
57206edd2f1SRobert Watson 			    (expire_age && at->at_time < oldest_time)) {
57306edd2f1SRobert Watson 				if (warn_expired)
57406edd2f1SRobert Watson 					(*warn_expired)(at->at_path);
57506edd2f1SRobert Watson 				if (unlink(at->at_path) < 0)
57606edd2f1SRobert Watson 					ret = ADE_EXPIRE;
57706edd2f1SRobert Watson 				total_size -= at->at_size;
57806edd2f1SRobert Watson 			}
57906edd2f1SRobert Watson 		}
58006edd2f1SRobert Watson 
58106edd2f1SRobert Watson 		free(at->at_path);
58206edd2f1SRobert Watson 		free(at);
58306edd2f1SRobert Watson 		at = at_next;
58406edd2f1SRobert Watson 	}
58506edd2f1SRobert Watson 
58606edd2f1SRobert Watson 	return (ret);
58706edd2f1SRobert Watson }
58806edd2f1SRobert Watson 
58906edd2f1SRobert Watson /*
5907a0a89d2SRobert Watson  * Parses the "dir" entry in audit_control(5) into an ordered list.  Also, will
59106edd2f1SRobert Watson  * set the minfree and host values if not already set.  Arguments include
59206edd2f1SRobert Watson  * function pointers to audit_warn functions for soft and hard limits. Returns:
5937a0a89d2SRobert Watson  *	ADE_NOERR	on success,
5947a0a89d2SRobert Watson  *	ADE_PARSE	error parsing audit_control(5),
5957a0a89d2SRobert Watson  *	ADE_AUDITON	error getting/setting auditon(2) value,
5967a0a89d2SRobert Watson  *	ADE_NOMEM	error allocating memory,
5977a0a89d2SRobert Watson  *	ADE_SOFTLIM	if all the directories are over the soft limit,
5987a0a89d2SRobert Watson  *	ADE_HARDLIM	if all the directories are over the hard limit,
5997a0a89d2SRobert Watson  */
6007a0a89d2SRobert Watson int
auditd_read_dirs(int (* warn_soft)(char *),int (* warn_hard)(char *))6017a0a89d2SRobert Watson auditd_read_dirs(int (*warn_soft)(char *), int (*warn_hard)(char *))
6027a0a89d2SRobert Watson {
6037a0a89d2SRobert Watson 	char cur_dir[MAXNAMLEN];
6047a0a89d2SRobert Watson 	struct dir_ent *dirent;
6057a0a89d2SRobert Watson 	struct statfs sfs;
6067a0a89d2SRobert Watson 	int err;
6077a0a89d2SRobert Watson 	char soft, hard;
6087a0a89d2SRobert Watson 	int tcnt = 0;
6097a0a89d2SRobert Watson 	int scnt = 0;
6107a0a89d2SRobert Watson 	int hcnt = 0;
6117a0a89d2SRobert Watson 
61206edd2f1SRobert Watson 	if (auditd_minval == -1 && (err = auditd_set_minfree()) != 0)
6137a0a89d2SRobert Watson 		return (err);
6147a0a89d2SRobert Watson 
61506edd2f1SRobert Watson 	if (auditd_hostlen == -1)
61606edd2f1SRobert Watson 		auditd_set_host();
61706edd2f1SRobert Watson 
6187a0a89d2SRobert Watson 	/*
6197a0a89d2SRobert Watson 	 * Init directory q.  Force a re-read of the file the next time.
6207a0a89d2SRobert Watson 	 */
6217a0a89d2SRobert Watson 	free_dir_q();
6227a0a89d2SRobert Watson 	endac();
6237a0a89d2SRobert Watson 
6247a0a89d2SRobert Watson 	/*
6257a0a89d2SRobert Watson 	 * Read the list of directories into an ordered linked list
6267a0a89d2SRobert Watson 	 * admin's preference, then those over soft limit and, finally,
6277a0a89d2SRobert Watson 	 * those over the hard limit.
6287a0a89d2SRobert Watson 	 *
6297a0a89d2SRobert Watson 	 * XXX We should use the reentrant interfaces once they are
6307a0a89d2SRobert Watson 	 * available.
6317a0a89d2SRobert Watson 	 */
6327a0a89d2SRobert Watson 	while (getacdir(cur_dir, MAXNAMLEN) >= 0) {
6337a0a89d2SRobert Watson 		if (statfs(cur_dir, &sfs) < 0)
6347a0a89d2SRobert Watson 			continue;  /* XXX should warn */
635aa772005SRobert Watson 		soft = (sfs.f_bfree < (sfs.f_blocks * auditd_minval / 100 )) ?
63606edd2f1SRobert Watson 		    1 : 0;
6377a0a89d2SRobert Watson 		hard = (sfs.f_bfree < AUDIT_HARD_LIMIT_FREE_BLOCKS) ? 1 : 0;
6387a0a89d2SRobert Watson 		if (soft) {
6397a0a89d2SRobert Watson 			if (warn_soft)
6407a0a89d2SRobert Watson 				(*warn_soft)(cur_dir);
6417a0a89d2SRobert Watson 			scnt++;
6427a0a89d2SRobert Watson 		}
6437a0a89d2SRobert Watson 		if (hard) {
6447a0a89d2SRobert Watson 			if (warn_hard)
6457a0a89d2SRobert Watson 				(*warn_hard)(cur_dir);
6467a0a89d2SRobert Watson 			hcnt++;
6477a0a89d2SRobert Watson 		}
6487a0a89d2SRobert Watson 		dirent = (struct dir_ent *) malloc(sizeof(struct dir_ent));
6497a0a89d2SRobert Watson 		if (dirent == NULL)
6507a0a89d2SRobert Watson 			return (ADE_NOMEM);
6517a0a89d2SRobert Watson 		dirent->softlim = soft;
6527a0a89d2SRobert Watson 		dirent->hardlim = hard;
6537a0a89d2SRobert Watson 		dirent->dirname = (char *) malloc(MAXNAMLEN);
6547a0a89d2SRobert Watson 		if (dirent->dirname == NULL) {
6557a0a89d2SRobert Watson 			free(dirent);
6567a0a89d2SRobert Watson 			return (ADE_NOMEM);
6577a0a89d2SRobert Watson 		}
6587a0a89d2SRobert Watson 		strlcpy(dirent->dirname, cur_dir, MAXNAMLEN);
6597a0a89d2SRobert Watson 		insert_orderly(dirent);
6607a0a89d2SRobert Watson 		tcnt++;
6617a0a89d2SRobert Watson 	}
6627a0a89d2SRobert Watson 
6637a0a89d2SRobert Watson 	if (hcnt == tcnt)
6647a0a89d2SRobert Watson 		return (ADE_HARDLIM);
6657a0a89d2SRobert Watson 	if (scnt == tcnt)
6667a0a89d2SRobert Watson 		return (ADE_SOFTLIM);
6677a0a89d2SRobert Watson 	return (0);
6687a0a89d2SRobert Watson }
6697a0a89d2SRobert Watson 
6707a0a89d2SRobert Watson void
auditd_close_dirs(void)6717a0a89d2SRobert Watson auditd_close_dirs(void)
6727a0a89d2SRobert Watson {
6737a0a89d2SRobert Watson 	free_dir_q();
67406edd2f1SRobert Watson 	auditd_minval = -1;
67506edd2f1SRobert Watson 	auditd_hostlen = -1;
6767a0a89d2SRobert Watson }
6777a0a89d2SRobert Watson 
6787a0a89d2SRobert Watson 
6797a0a89d2SRobert Watson /*
6807a0a89d2SRobert Watson  * Process the audit event file, obtaining a class mapping for each event, and
6817a0a89d2SRobert Watson  * set that mapping into the kernel. Return:
6827a0a89d2SRobert Watson  *	 n	number of event mappings that were successfully processed,
6837a0a89d2SRobert Watson  *   ADE_NOMEM	if there was an error allocating memory.
6845e386598SRobert Watson  *
6855e386598SRobert Watson  * Historically, this code only set up the in-kernel class mapping.  On
6865e386598SRobert Watson  * systems with an in-kernel event-to-name mapping, it also now installs that,
6875e386598SRobert Watson  * as it is iterating over the event list anyway.  Failures there will be
6885e386598SRobert Watson  * ignored as not all kernels support the feature.
6897a0a89d2SRobert Watson  */
6907a0a89d2SRobert Watson int
auditd_set_evcmap(void)6917a0a89d2SRobert Watson auditd_set_evcmap(void)
6927a0a89d2SRobert Watson {
6937a0a89d2SRobert Watson 	au_event_ent_t ev, *evp;
6947a0a89d2SRobert Watson 	au_evclass_map_t evc_map;
6955e386598SRobert Watson 	au_evname_map_t evn_map;
6967a0a89d2SRobert Watson 	int ctr = 0;
6977a0a89d2SRobert Watson 
6987a0a89d2SRobert Watson 	/*
6997a0a89d2SRobert Watson 	 * XXX There's a risk here that the BSM library will return NULL
7007a0a89d2SRobert Watson 	 * for an event when it can't properly map it to a class. In that
7017a0a89d2SRobert Watson 	 * case, we will not process any events beyond the one that failed,
7027a0a89d2SRobert Watson 	 * but should. We need a way to get a count of the events.
7037a0a89d2SRobert Watson 	 */
7047a0a89d2SRobert Watson 	ev.ae_name = (char *)malloc(AU_EVENT_NAME_MAX);
7057a0a89d2SRobert Watson 	ev.ae_desc = (char *)malloc(AU_EVENT_DESC_MAX);
706aa772005SRobert Watson 	if (ev.ae_name == NULL || ev.ae_desc == NULL) {
7077a0a89d2SRobert Watson 		if (ev.ae_name != NULL)
7087a0a89d2SRobert Watson 			free(ev.ae_name);
7097a0a89d2SRobert Watson 		return (ADE_NOMEM);
7107a0a89d2SRobert Watson 	}
7117a0a89d2SRobert Watson 
7127a0a89d2SRobert Watson 	/*
7137a0a89d2SRobert Watson 	 * XXXRW: Currently we have no way to remove mappings from the kernel
7147a0a89d2SRobert Watson 	 * when they are removed from the file-based mappings.
7157a0a89d2SRobert Watson 	 */
7167a0a89d2SRobert Watson 	evp = &ev;
7177a0a89d2SRobert Watson 	setauevent();
7187a0a89d2SRobert Watson 	while ((evp = getauevent_r(evp)) != NULL) {
7195e386598SRobert Watson 		/*
7205e386598SRobert Watson 		 * Set the event-to-name mapping entry.  If there's not room
7215e386598SRobert Watson 		 * in the in-kernel string, then we skip the entry.  Possibly
7225e386598SRobert Watson 		 * better than truncating...?
7235e386598SRobert Watson 		 */
7245e386598SRobert Watson 		if (strlcpy(evn_map.en_name, evp->ae_name,
7255e386598SRobert Watson 		    sizeof(evn_map.en_name)) < sizeof(evn_map.en_name)) {
7265e386598SRobert Watson 			evn_map.en_number = evp->ae_number;
7275e386598SRobert Watson 			(void)audit_set_event(&evn_map, sizeof(evn_map));
7285e386598SRobert Watson 		}
7295e386598SRobert Watson 
7305e386598SRobert Watson 		/*
7315e386598SRobert Watson 		 * Set the event-to-class mapping entry.
7325e386598SRobert Watson 		 */
7337a0a89d2SRobert Watson 		evc_map.ec_number = evp->ae_number;
7347a0a89d2SRobert Watson 		evc_map.ec_class = evp->ae_class;
735c0020399SRobert Watson 		if (audit_set_class(&evc_map, sizeof(evc_map)) == 0)
7367a0a89d2SRobert Watson 			ctr++;
7377a0a89d2SRobert Watson 	}
7387a0a89d2SRobert Watson 	endauevent();
7397a0a89d2SRobert Watson 	free(ev.ae_name);
7407a0a89d2SRobert Watson 	free(ev.ae_desc);
7417a0a89d2SRobert Watson 
7427a0a89d2SRobert Watson 	return (ctr);
7437a0a89d2SRobert Watson }
7447a0a89d2SRobert Watson 
7457a0a89d2SRobert Watson /*
7467a0a89d2SRobert Watson  * Get the non-attributable event string and set the kernel mask.  Return:
7477a0a89d2SRobert Watson  *	ADE_NOERR	on success,
7487a0a89d2SRobert Watson  *	ADE_PARSE	error parsing audit_control(5),
7497a0a89d2SRobert Watson  *	ADE_AUDITON	error setting the mask using auditon(2).
7507a0a89d2SRobert Watson  */
7517a0a89d2SRobert Watson int
auditd_set_namask(void)7527a0a89d2SRobert Watson auditd_set_namask(void)
7537a0a89d2SRobert Watson {
7547a0a89d2SRobert Watson 	au_mask_t aumask;
7557a0a89d2SRobert Watson 	char naeventstr[NA_EVENT_STR_SIZE];
7567a0a89d2SRobert Watson 
757aa772005SRobert Watson 	if (getacna(naeventstr, NA_EVENT_STR_SIZE) != 0 ||
758aa772005SRobert Watson 	    getauditflagsbin(naeventstr, &aumask) != 0)
7597a0a89d2SRobert Watson 		return (ADE_PARSE);
7607a0a89d2SRobert Watson 
761c0020399SRobert Watson 	if (audit_set_kmask(&aumask, sizeof(aumask)) != 0)
7627a0a89d2SRobert Watson 		return (ADE_AUDITON);
7637a0a89d2SRobert Watson 
7647a0a89d2SRobert Watson 	return (ADE_NOERR);
7657a0a89d2SRobert Watson }
7667a0a89d2SRobert Watson 
7677a0a89d2SRobert Watson /*
7687a0a89d2SRobert Watson  * Set the audit control policy if a policy is configured in audit_control(5),
7697a0a89d2SRobert Watson  * implement the policy. However, if one isn't defined or if there is an error
7707a0a89d2SRobert Watson  * parsing the control file, set AUDIT_CNT to avoid leaving the system in a
7717a0a89d2SRobert Watson  * fragile state.  Return:
7727a0a89d2SRobert Watson  *	ADE_NOERR	on success,
7737a0a89d2SRobert Watson  *	ADE_PARSE	error parsing audit_control(5),
7747a0a89d2SRobert Watson  *	ADE_AUDITON	error setting policy using auditon(2).
7757a0a89d2SRobert Watson  */
7767a0a89d2SRobert Watson int
auditd_set_policy(void)7777a0a89d2SRobert Watson auditd_set_policy(void)
7787a0a89d2SRobert Watson {
779c0020399SRobert Watson 	int policy;
7807a0a89d2SRobert Watson 	char polstr[POL_STR_SIZE];
7817a0a89d2SRobert Watson 
782aa772005SRobert Watson 	if (getacpol(polstr, POL_STR_SIZE) != 0 ||
783aa772005SRobert Watson 	    au_strtopol(polstr, &policy) != 0) {
7847a0a89d2SRobert Watson 		policy = AUDIT_CNT;
785c0020399SRobert Watson 		if (audit_set_policy(&policy) != 0)
7867a0a89d2SRobert Watson 			return (ADE_AUDITON);
7877a0a89d2SRobert Watson 		return (ADE_PARSE);
7887a0a89d2SRobert Watson 	}
7897a0a89d2SRobert Watson 
790c0020399SRobert Watson 	if (audit_set_policy(&policy) != 0)
7917a0a89d2SRobert Watson 		return (ADE_AUDITON);
7927a0a89d2SRobert Watson 
7937a0a89d2SRobert Watson 	return (ADE_NOERR);
7947a0a89d2SRobert Watson }
7957a0a89d2SRobert Watson 
7967a0a89d2SRobert Watson /*
7977a0a89d2SRobert Watson  * Set trail rotation size.  Return:
7987a0a89d2SRobert Watson  *	ADE_NOERR	on success,
7997a0a89d2SRobert Watson  *	ADE_PARSE	error parsing audit_control(5),
8007a0a89d2SRobert Watson  *	ADE_AUDITON	error setting file size using auditon(2).
8017a0a89d2SRobert Watson  */
8027a0a89d2SRobert Watson int
auditd_set_fsize(void)8037a0a89d2SRobert Watson auditd_set_fsize(void)
8047a0a89d2SRobert Watson {
8057a0a89d2SRobert Watson 	size_t filesz;
8067a0a89d2SRobert Watson 	au_fstat_t au_fstat;
8077a0a89d2SRobert Watson 
8087a0a89d2SRobert Watson 	/*
8097a0a89d2SRobert Watson 	 * Set trail rotation size.
8107a0a89d2SRobert Watson 	 */
8117a0a89d2SRobert Watson 	if (getacfilesz(&filesz) != 0)
8127a0a89d2SRobert Watson 		return (ADE_PARSE);
8137a0a89d2SRobert Watson 
8147a0a89d2SRobert Watson 	bzero(&au_fstat, sizeof(au_fstat));
8157a0a89d2SRobert Watson 	au_fstat.af_filesz = filesz;
816c0020399SRobert Watson 	if (audit_set_fsize(&au_fstat, sizeof(au_fstat)) != 0)
8177a0a89d2SRobert Watson 		return (ADE_AUDITON);
8187a0a89d2SRobert Watson 
8197a0a89d2SRobert Watson 	return (ADE_NOERR);
8207a0a89d2SRobert Watson }
8217a0a89d2SRobert Watson 
8225e386598SRobert Watson /*
8235e386598SRobert Watson  * Set trail rotation size.  Return:
8245e386598SRobert Watson  *	ADE_NOERR	on success,
8255e386598SRobert Watson  *	ADE_PARSE	error parsing audit_control(5),
8265e386598SRobert Watson  *	ADE_AUDITON	error setting queue size using auditon(2).
8275e386598SRobert Watson  */
8285e386598SRobert Watson int
auditd_set_qsize(void)8295e386598SRobert Watson auditd_set_qsize(void)
8305e386598SRobert Watson {
8315e386598SRobert Watson 	int qsz;
8325e386598SRobert Watson 	au_qctrl_t au_qctrl;
8335e386598SRobert Watson 
8345e386598SRobert Watson 	/*
8355e386598SRobert Watson 	 * Set trail rotation size.
8365e386598SRobert Watson 	 */
8375e386598SRobert Watson 	if (getacqsize(&qsz) != 0)
8385e386598SRobert Watson 		return (ADE_PARSE);
8395e386598SRobert Watson 
8405e386598SRobert Watson 	if (audit_get_qctrl(&au_qctrl, sizeof(au_qctrl)) != 0)
8415e386598SRobert Watson 		return (ADE_AUDITON);
8425e386598SRobert Watson 	if (qsz != USE_DEFAULT_QSZ)
8435e386598SRobert Watson 		au_qctrl.aq_hiwater = qsz;
8445e386598SRobert Watson 	if (audit_set_qctrl(&au_qctrl, sizeof(au_qctrl)) != 0)
8455e386598SRobert Watson 		return (ADE_AUDITON);
8465e386598SRobert Watson 
8475e386598SRobert Watson 	return (ADE_NOERR);
8485e386598SRobert Watson }
8495e386598SRobert Watson 
850aa772005SRobert Watson static void
inject_dist(const char * fromname,char * toname,size_t tonamesize)851aa772005SRobert Watson inject_dist(const char *fromname, char *toname, size_t tonamesize)
852aa772005SRobert Watson {
853aa772005SRobert Watson 	char *ptr;
854aa772005SRobert Watson 
855aa772005SRobert Watson 	ptr = strrchr(fromname, '/');
856aa772005SRobert Watson 	assert(ptr != NULL);
857aa772005SRobert Watson 	assert(ptr - fromname < (ssize_t)tonamesize);
858aa772005SRobert Watson 	strlcpy(toname, fromname, ptr - fromname + 1);
859aa772005SRobert Watson 	strlcat(toname, "/dist/", tonamesize);
860aa772005SRobert Watson 	strlcat(toname, ptr + 1, tonamesize);
861aa772005SRobert Watson }
862aa772005SRobert Watson 
863aa772005SRobert Watson static int
auditdist_link(const char * filename)864aa772005SRobert Watson auditdist_link(const char *filename)
865aa772005SRobert Watson {
866aa772005SRobert Watson 	char fname[MAXPATHLEN];
867aa772005SRobert Watson 
868aa772005SRobert Watson 	if (auditd_dist) {
869aa772005SRobert Watson 		inject_dist(filename, fname, sizeof(fname));
870aa772005SRobert Watson 		/* Ignore errors. */
871aa772005SRobert Watson 		(void) link(filename, fname);
872aa772005SRobert Watson 	}
873aa772005SRobert Watson 
874aa772005SRobert Watson 	return (0);
875aa772005SRobert Watson }
876aa772005SRobert Watson 
877aa772005SRobert Watson int
auditd_rename(const char * fromname,const char * toname)878aa772005SRobert Watson auditd_rename(const char *fromname, const char *toname)
879aa772005SRobert Watson {
880aa772005SRobert Watson 	char fname[MAXPATHLEN], tname[MAXPATHLEN];
881aa772005SRobert Watson 
882aa772005SRobert Watson 	if (auditd_dist) {
883aa772005SRobert Watson 		inject_dist(fromname, fname, sizeof(fname));
884aa772005SRobert Watson 		inject_dist(toname, tname, sizeof(tname));
885aa772005SRobert Watson 		/* Ignore errors. */
886aa772005SRobert Watson 		(void) rename(fname, tname);
887aa772005SRobert Watson 	}
888aa772005SRobert Watson 
889aa772005SRobert Watson 	return (rename(fromname, toname));
890aa772005SRobert Watson }
891aa772005SRobert Watson 
8927a0a89d2SRobert Watson /*
893aa772005SRobert Watson  * Create the new audit file with appropriate permissions and ownership.
894aa772005SRobert Watson  * Call auditctl(2) for this file.
895aa772005SRobert Watson  * Try to clean up if something goes wrong.
896aa772005SRobert Watson  * *errorp is modified only on auditctl(2) failure.
8977a0a89d2SRobert Watson  */
8987a0a89d2SRobert Watson static int
open_trail(char * fname,gid_t gid,int * errorp)899aa772005SRobert Watson open_trail(char *fname, gid_t gid, int *errorp)
9007a0a89d2SRobert Watson {
901aa772005SRobert Watson 	int fd;
9027a0a89d2SRobert Watson 
903aa772005SRobert Watson 	/* XXXPJD: What should we do if the file already exists? */
904aa772005SRobert Watson 	fd = open(fname, O_RDONLY | O_CREAT, S_IRUSR);
9057a0a89d2SRobert Watson 	if (fd < 0)
9067a0a89d2SRobert Watson 		return (-1);
907aa772005SRobert Watson 	if (fchown(fd, -1, gid) < 0 || fchmod(fd, S_IRUSR | S_IRGRP) < 0) {
908aa772005SRobert Watson 		(void) close(fd);
9097a0a89d2SRobert Watson 		(void) unlink(fname);
9107a0a89d2SRobert Watson 		return (-1);
9117a0a89d2SRobert Watson 	}
912aa772005SRobert Watson 	(void) close(fd);
913aa772005SRobert Watson 	if (auditctl(fname) < 0) {
914aa772005SRobert Watson 		*errorp = errno;
915aa772005SRobert Watson 		(void) unlink(fname);
916aa772005SRobert Watson 		return (-1);
917aa772005SRobert Watson 	}
918aa772005SRobert Watson 	(void) auditdist_link(fname);
919aa772005SRobert Watson 	return (0);
9207a0a89d2SRobert Watson }
9217a0a89d2SRobert Watson 
9227a0a89d2SRobert Watson /*
9237a0a89d2SRobert Watson  * Create the new audit trail file, swap with existing audit file.  Arguments
9247a0a89d2SRobert Watson  * include timestamp for the filename, a pointer to a string for returning the
9257a0a89d2SRobert Watson  * new file name, GID for trail file, and audit_warn function pointer for
9267a0a89d2SRobert Watson  * 'getacdir()' errors.  Returns:
9277a0a89d2SRobert Watson  *	ADE_NOERR	on success,
9287a0a89d2SRobert Watson  *	ADE_STRERR	if the file name string could not be created,
9297a0a89d2SRobert Watson  *	ADE_SWAPERR	if the audit trail file could not be swapped,
9307a0a89d2SRobert Watson  *	ADE_ACTL	if the auditctl(2) call failed but file swap still
9317a0a89d2SRobert Watson  *			successful.
9327a0a89d2SRobert Watson  *	ADE_ACTLERR	if the auditctl(2) call failed and file swap failed.
9337a0a89d2SRobert Watson  *	ADE_SYMLINK	if symlink(2) failed updating the current link.
9347a0a89d2SRobert Watson  */
9357a0a89d2SRobert Watson int
auditd_swap_trail(char * TS,char ** newfile,gid_t gid,int (* warn_getacdir)(char *))9367a0a89d2SRobert Watson auditd_swap_trail(char *TS, char **newfile, gid_t gid,
9377a0a89d2SRobert Watson     int (*warn_getacdir)(char *))
9387a0a89d2SRobert Watson {
939aa772005SRobert Watson 	char timestr[FILENAME_LEN + 1];
9407a0a89d2SRobert Watson 	char *fn;
9417a0a89d2SRobert Watson 	struct dir_ent *dirent;
9427a0a89d2SRobert Watson 	int saverrno = 0;
9437a0a89d2SRobert Watson 
944aa772005SRobert Watson 	if (strlen(TS) != TIMESTAMP_LEN ||
945aa772005SRobert Watson 	    snprintf(timestr, sizeof(timestr), "%s.%s", TS,
946aa772005SRobert Watson 	    NOT_TERMINATED) < 0) {
9477a0a89d2SRobert Watson 		errno = EINVAL;
9487a0a89d2SRobert Watson 		return (ADE_STRERR);
9497a0a89d2SRobert Watson 	}
9507a0a89d2SRobert Watson 
9517a0a89d2SRobert Watson 	/* Try until we succeed. */
95206edd2f1SRobert Watson 	TAILQ_FOREACH(dirent, &dir_q, dirs) {
9537a0a89d2SRobert Watson 		if (dirent->hardlim)
9547a0a89d2SRobert Watson 			continue;
9557a0a89d2SRobert Watson 		if ((fn = affixdir(timestr, dirent)) == NULL)
9567a0a89d2SRobert Watson 			return (ADE_STRERR);
9577a0a89d2SRobert Watson 
9587a0a89d2SRobert Watson 		/*
959aa772005SRobert Watson 		 * Create the file and pass to the kernel if all went well.
9607a0a89d2SRobert Watson 		 */
961aa772005SRobert Watson 		if (open_trail(fn, gid, &saverrno) == 0) {
9627a0a89d2SRobert Watson 			/* Success. */
9637a0a89d2SRobert Watson 			*newfile = fn;
9647a0a89d2SRobert Watson 			if (saverrno) {
9657a0a89d2SRobert Watson 				/*
9667a0a89d2SRobert Watson 				 * auditctl() failed but still
9677a0a89d2SRobert Watson 				 * successful. Return errno and "soft"
9687a0a89d2SRobert Watson 				 * error.
9697a0a89d2SRobert Watson 				 */
9707a0a89d2SRobert Watson 				errno = saverrno;
9717a0a89d2SRobert Watson 				return (ADE_ACTL);
9727a0a89d2SRobert Watson 			}
9737a0a89d2SRobert Watson 			return (ADE_NOERR);
9747a0a89d2SRobert Watson 		}
975aa772005SRobert Watson 		/*
976aa772005SRobert Watson 		 * auditctl failed setting log file. Try again.
977aa772005SRobert Watson 		 */
9787a0a89d2SRobert Watson 		/*
9797a0a89d2SRobert Watson 		 * Tell the administrator about lack of permissions for dir.
9807a0a89d2SRobert Watson 		 */
9817a0a89d2SRobert Watson 		if (warn_getacdir != NULL)
9827a0a89d2SRobert Watson 			(*warn_getacdir)(dirent->dirname);
9837a0a89d2SRobert Watson 	}
9847a0a89d2SRobert Watson 	if (saverrno) {
9857a0a89d2SRobert Watson 		errno = saverrno;
9867a0a89d2SRobert Watson 		return (ADE_ACTLERR);
9877a0a89d2SRobert Watson 	} else
9887a0a89d2SRobert Watson 		return (ADE_SWAPERR);
9897a0a89d2SRobert Watson }
9907a0a89d2SRobert Watson 
9917a0a89d2SRobert Watson /*
9927a0a89d2SRobert Watson  * Mask calling process from being audited. Returns:
9937a0a89d2SRobert Watson  *	ADE_NOERR	on success,
9947a0a89d2SRobert Watson  *	ADE_SETAUDIT	if setaudit(2) fails.
9957a0a89d2SRobert Watson  */
99606edd2f1SRobert Watson #ifdef __APPLE__
99706edd2f1SRobert Watson int
auditd_prevent_audit(void)99806edd2f1SRobert Watson auditd_prevent_audit(void)
99906edd2f1SRobert Watson {
100006edd2f1SRobert Watson 	auditinfo_addr_t aia;
100106edd2f1SRobert Watson 
100206edd2f1SRobert Watson 	/*
100306edd2f1SRobert Watson 	 * To prevent event feedback cycles and avoid audit becoming stalled if
100406edd2f1SRobert Watson 	 * auditing is suspended we mask this processes events from being
100506edd2f1SRobert Watson 	 * audited.  We allow the uid, tid, and mask fields to be implicitly
100606edd2f1SRobert Watson 	 * set to zero, but do set the audit session ID to the PID.
100706edd2f1SRobert Watson 	 *
100806edd2f1SRobert Watson 	 * XXXRW: Is there more to it than this?
100906edd2f1SRobert Watson 	 */
101006edd2f1SRobert Watson 	bzero(&aia, sizeof(aia));
101106edd2f1SRobert Watson 	aia.ai_asid = AU_ASSIGN_ASID;
101206edd2f1SRobert Watson 	aia.ai_termid.at_type = AU_IPv4;
101306edd2f1SRobert Watson 	if (setaudit_addr(&aia, sizeof(aia)) != 0)
101406edd2f1SRobert Watson 		return (ADE_SETAUDIT);
101506edd2f1SRobert Watson 	return (ADE_NOERR);
101606edd2f1SRobert Watson }
101706edd2f1SRobert Watson #else
10187a0a89d2SRobert Watson int
auditd_prevent_audit(void)10197a0a89d2SRobert Watson auditd_prevent_audit(void)
10207a0a89d2SRobert Watson {
10217a0a89d2SRobert Watson 	auditinfo_t ai;
10227a0a89d2SRobert Watson 
10237a0a89d2SRobert Watson 	/*
10247a0a89d2SRobert Watson 	 * To prevent event feedback cycles and avoid audit becoming stalled if
10257a0a89d2SRobert Watson 	 * auditing is suspended we mask this processes events from being
10267a0a89d2SRobert Watson 	 * audited.  We allow the uid, tid, and mask fields to be implicitly
10277a0a89d2SRobert Watson 	 * set to zero, but do set the audit session ID to the PID.
10287a0a89d2SRobert Watson 	 *
10297a0a89d2SRobert Watson 	 * XXXRW: Is there more to it than this?
10307a0a89d2SRobert Watson 	 */
10317a0a89d2SRobert Watson 	bzero(&ai, sizeof(ai));
10327a0a89d2SRobert Watson 	ai.ai_asid = getpid();
10337a0a89d2SRobert Watson 	if (setaudit(&ai) != 0)
10347a0a89d2SRobert Watson 		return (ADE_SETAUDIT);
10357a0a89d2SRobert Watson 	return (ADE_NOERR);
10367a0a89d2SRobert Watson }
1037aa772005SRobert Watson #endif /* !__APPLE__ */
10387a0a89d2SRobert Watson 
10397a0a89d2SRobert Watson /*
10407a0a89d2SRobert Watson  * Generate and submit audit record for audit startup or shutdown.  The event
10417a0a89d2SRobert Watson  * argument can be AUE_audit_recovery, AUE_audit_startup or
10427a0a89d2SRobert Watson  * AUE_audit_shutdown. The path argument will add a path token, if not NULL.
10437a0a89d2SRobert Watson  * Returns:
10447a0a89d2SRobert Watson  *	AUE_NOERR	on success,
10457a0a89d2SRobert Watson  *	ADE_NOMEM	if memory allocation fails,
10467a0a89d2SRobert Watson  *	ADE_AU_OPEN	if au_open(3) fails,
10477a0a89d2SRobert Watson  *	ADE_AU_CLOSE	if au_close(3) fails.
10487a0a89d2SRobert Watson  */
10497a0a89d2SRobert Watson int
auditd_gen_record(int event,char * path)10507a0a89d2SRobert Watson auditd_gen_record(int event, char *path)
10517a0a89d2SRobert Watson {
10527a0a89d2SRobert Watson 	int aufd;
10537a0a89d2SRobert Watson 	uid_t uid;
10547a0a89d2SRobert Watson 	pid_t pid;
10557a0a89d2SRobert Watson 	char *autext = NULL;
10567a0a89d2SRobert Watson 	token_t *tok;
10577a0a89d2SRobert Watson 	struct auditinfo_addr aia;
10587a0a89d2SRobert Watson 
10597a0a89d2SRobert Watson 	if (event == AUE_audit_startup)
10607a0a89d2SRobert Watson 		asprintf(&autext, "%s::Audit startup", getprogname());
10617a0a89d2SRobert Watson 	else if (event == AUE_audit_shutdown)
10627a0a89d2SRobert Watson 		asprintf(&autext, "%s::Audit shutdown", getprogname());
10637a0a89d2SRobert Watson 	else if (event == AUE_audit_recovery)
10647a0a89d2SRobert Watson 		asprintf(&autext, "%s::Audit recovery", getprogname());
10657a0a89d2SRobert Watson 	else
10667a0a89d2SRobert Watson 		return (ADE_INVAL);
10677a0a89d2SRobert Watson 	if (autext == NULL)
10687a0a89d2SRobert Watson 		return (ADE_NOMEM);
10697a0a89d2SRobert Watson 
10707a0a89d2SRobert Watson 	if ((aufd = au_open()) == -1) {
10717a0a89d2SRobert Watson 		free(autext);
10727a0a89d2SRobert Watson 		return (ADE_AU_OPEN);
10737a0a89d2SRobert Watson 	}
10747a0a89d2SRobert Watson 	bzero(&aia, sizeof(aia));
10757a0a89d2SRobert Watson 	uid = getuid(); pid = getpid();
10767a0a89d2SRobert Watson 	if ((tok = au_to_subject32_ex(uid, geteuid(), getegid(), uid, getgid(),
10777a0a89d2SRobert Watson 	    pid, pid, &aia.ai_termid)) != NULL)
10787a0a89d2SRobert Watson 		au_write(aufd, tok);
10797a0a89d2SRobert Watson 	if ((tok = au_to_text(autext)) != NULL)
10807a0a89d2SRobert Watson 		au_write(aufd, tok);
10817a0a89d2SRobert Watson 	free(autext);
10827a0a89d2SRobert Watson 	if (path != NULL && (tok = au_to_path(path)) != NULL)
10837a0a89d2SRobert Watson 		au_write(aufd, tok);
10847a0a89d2SRobert Watson 	if ((tok = au_to_return32(0, 0)) != NULL)
10857a0a89d2SRobert Watson 		au_write(aufd, tok);
10867a0a89d2SRobert Watson 	if (au_close(aufd, 1, event) == -1)
10877a0a89d2SRobert Watson 		return (ADE_AU_CLOSE);
10887a0a89d2SRobert Watson 
10897a0a89d2SRobert Watson 	return (ADE_NOERR);
10907a0a89d2SRobert Watson }
10917a0a89d2SRobert Watson 
10927a0a89d2SRobert Watson /*
10937a0a89d2SRobert Watson  * Check for a 'current' symlink and do crash recovery, if needed. Create a new
10947a0a89d2SRobert Watson  * 'current' symlink. The argument 'curfile' is the file the 'current' symlink
10957a0a89d2SRobert Watson  * should point to.  Returns:
10967a0a89d2SRobert Watson  *	ADE_NOERR	on success,
10977a0a89d2SRobert Watson  *	ADE_AU_OPEN	if au_open(3) fails,
10987a0a89d2SRobert Watson  *	ADE_AU_CLOSE	if au_close(3) fails.
10997a0a89d2SRobert Watson  *	ADE_RENAME	if error renaming audit trail file,
11007a0a89d2SRobert Watson  *	ADE_READLINK	if error reading the 'current' link,
11017a0a89d2SRobert Watson  *	ADE_SYMLINK	if error creating 'current' link.
11027a0a89d2SRobert Watson  */
11037a0a89d2SRobert Watson int
auditd_new_curlink(char * curfile)11047a0a89d2SRobert Watson auditd_new_curlink(char *curfile)
11057a0a89d2SRobert Watson {
11067a0a89d2SRobert Watson 	int len, err;
11077a0a89d2SRobert Watson 	char *ptr;
11087a0a89d2SRobert Watson 	char *path = NULL;
11097a0a89d2SRobert Watson 	struct stat sb;
11107a0a89d2SRobert Watson 	char recoveredname[MAXPATHLEN];
11117a0a89d2SRobert Watson 	char newname[MAXPATHLEN];
11127a0a89d2SRobert Watson 
11137a0a89d2SRobert Watson 	/*
11147a0a89d2SRobert Watson 	 * Check to see if audit was shutdown properly.  If not, clean up,
11157a0a89d2SRobert Watson 	 * recover previous audit trail file, and generate audit record.
11167a0a89d2SRobert Watson 	 */
1117aa772005SRobert Watson 	len = readlink(AUDIT_CURRENT_LINK, recoveredname,
1118aa772005SRobert Watson 	    sizeof(recoveredname) - 1);
11197a0a89d2SRobert Watson 	if (len > 0) {
11207a0a89d2SRobert Watson 		/* 'current' exist but is it pointing at a valid file?  */
11217a0a89d2SRobert Watson 		recoveredname[len++] = '\0';
11227a0a89d2SRobert Watson 		if (stat(recoveredname, &sb) == 0) {
11237a0a89d2SRobert Watson 			/* Yes, rename it to a crash recovery file. */
1124aa772005SRobert Watson 			strlcpy(newname, recoveredname, sizeof(newname));
11257a0a89d2SRobert Watson 
11267a0a89d2SRobert Watson 			if ((ptr = strstr(newname, NOT_TERMINATED)) != NULL) {
112706edd2f1SRobert Watson 				memcpy(ptr, CRASH_RECOVERY, POSTFIX_LEN);
1128aa772005SRobert Watson 				if (auditd_rename(recoveredname, newname) != 0)
11297a0a89d2SRobert Watson 					return (ADE_RENAME);
11307a0a89d2SRobert Watson 			} else
11317a0a89d2SRobert Watson 				return (ADE_STRERR);
11327a0a89d2SRobert Watson 
11337a0a89d2SRobert Watson 			path = newname;
11347a0a89d2SRobert Watson 		}
11357a0a89d2SRobert Watson 
11367a0a89d2SRobert Watson 		/* 'current' symlink is (now) invalid so remove it. */
11377a0a89d2SRobert Watson 		(void) unlink(AUDIT_CURRENT_LINK);
11387a0a89d2SRobert Watson 
11397a0a89d2SRobert Watson 		/* Note the crash recovery in current audit trail */
11407a0a89d2SRobert Watson 		err = auditd_gen_record(AUE_audit_recovery, path);
11417a0a89d2SRobert Watson 		if (err)
11427a0a89d2SRobert Watson 			return (err);
11437a0a89d2SRobert Watson 	}
11447a0a89d2SRobert Watson 
11457a0a89d2SRobert Watson 	if (len < 0 && errno != ENOENT)
11467a0a89d2SRobert Watson 		return (ADE_READLINK);
11477a0a89d2SRobert Watson 
11487a0a89d2SRobert Watson 	if (symlink(curfile, AUDIT_CURRENT_LINK) != 0)
11497a0a89d2SRobert Watson 		return (ADE_SYMLINK);
11507a0a89d2SRobert Watson 
11517a0a89d2SRobert Watson 	return (0);
11527a0a89d2SRobert Watson }
11537a0a89d2SRobert Watson 
11547a0a89d2SRobert Watson /*
11557a0a89d2SRobert Watson  * Do just what we need to quickly start auditing.  Assume no system logging or
11567a0a89d2SRobert Watson  * notify.  Return:
11577a0a89d2SRobert Watson  *   0	 on success,
11587a0a89d2SRobert Watson  *  -1   on failure.
11597a0a89d2SRobert Watson  */
11607a0a89d2SRobert Watson int
audit_quick_start(void)11617a0a89d2SRobert Watson audit_quick_start(void)
11627a0a89d2SRobert Watson {
11637a0a89d2SRobert Watson 	int err;
116406edd2f1SRobert Watson 	char *newfile = NULL;
11657a0a89d2SRobert Watson 	time_t tt;
1166aa772005SRobert Watson 	char TS[TIMESTAMP_LEN + 1];
116706edd2f1SRobert Watson 	int ret = 0;
11687a0a89d2SRobert Watson 
11697a0a89d2SRobert Watson 	/*
11707a0a89d2SRobert Watson 	 * Mask auditing of this process.
11717a0a89d2SRobert Watson 	 */
11727a0a89d2SRobert Watson 	if (auditd_prevent_audit() != 0)
11737a0a89d2SRobert Watson 		return (-1);
11747a0a89d2SRobert Watson 
11757a0a89d2SRobert Watson 	/*
11767a0a89d2SRobert Watson 	 * Read audit_control and get log directories.
11777a0a89d2SRobert Watson 	 */
11787a0a89d2SRobert Watson 	err = auditd_read_dirs(NULL, NULL);
11797a0a89d2SRobert Watson 	if (err != ADE_NOERR && err != ADE_SOFTLIM)
11807a0a89d2SRobert Watson 		return (-1);
11817a0a89d2SRobert Watson 
11827a0a89d2SRobert Watson 	/*
1183aa772005SRobert Watson 	 * Setup trail file distribution.
1184aa772005SRobert Watson 	 */
1185aa772005SRobert Watson 	(void) auditd_set_dist();
1186aa772005SRobert Watson 
1187aa772005SRobert Watson 	/*
11887a0a89d2SRobert Watson 	 *  Create a new audit trail log.
11897a0a89d2SRobert Watson 	 */
1190aa772005SRobert Watson 	if (getTSstr(tt, TS, sizeof(TS)) != 0)
11917a0a89d2SRobert Watson 		return (-1);
11927a0a89d2SRobert Watson 	err = auditd_swap_trail(TS, &newfile, getgid(), NULL);
119306edd2f1SRobert Watson 	if (err != ADE_NOERR && err != ADE_ACTL) {
119406edd2f1SRobert Watson 		ret = -1;
119506edd2f1SRobert Watson 		goto out;
119606edd2f1SRobert Watson 	}
11977a0a89d2SRobert Watson 
11987a0a89d2SRobert Watson 	/*
11997a0a89d2SRobert Watson 	 * Add the current symlink and recover from crash, if needed.
12007a0a89d2SRobert Watson 	 */
120106edd2f1SRobert Watson 	if (auditd_new_curlink(newfile) != 0) {
120206edd2f1SRobert Watson 		ret = -1;
120306edd2f1SRobert Watson 		goto out;
120406edd2f1SRobert Watson 	}
12057a0a89d2SRobert Watson 
12067a0a89d2SRobert Watson 	/*
12077a0a89d2SRobert Watson 	 * At this point auditing has started so generate audit start-up record.
12087a0a89d2SRobert Watson 	 */
120906edd2f1SRobert Watson 	if (auditd_gen_record(AUE_audit_startup, NULL) != 0) {
121006edd2f1SRobert Watson 		ret = -1;
121106edd2f1SRobert Watson 		goto out;
121206edd2f1SRobert Watson 	}
12137a0a89d2SRobert Watson 
12147a0a89d2SRobert Watson 	/*
12157a0a89d2SRobert Watson 	 *  Configure the audit controls.
12167a0a89d2SRobert Watson 	 */
12177a0a89d2SRobert Watson 	(void) auditd_set_evcmap();
12187a0a89d2SRobert Watson 	(void) auditd_set_namask();
12197a0a89d2SRobert Watson 	(void) auditd_set_policy();
12207a0a89d2SRobert Watson 	(void) auditd_set_fsize();
12217a0a89d2SRobert Watson 	(void) auditd_set_minfree();
12227a0a89d2SRobert Watson 	(void) auditd_set_host();
12237a0a89d2SRobert Watson 
122406edd2f1SRobert Watson out:
122506edd2f1SRobert Watson 	if (newfile != NULL)
122606edd2f1SRobert Watson 		free(newfile);
122706edd2f1SRobert Watson 
122806edd2f1SRobert Watson 	return (ret);
12297a0a89d2SRobert Watson }
12307a0a89d2SRobert Watson 
12317a0a89d2SRobert Watson /*
12327a0a89d2SRobert Watson  * Shut down auditing quickly.  Assumes that is only called on system shutdown.
12337a0a89d2SRobert Watson  * Returns:
12347a0a89d2SRobert Watson  *	 0	on success,
12357a0a89d2SRobert Watson  *	-1	on failure.
12367a0a89d2SRobert Watson  */
12377a0a89d2SRobert Watson int
audit_quick_stop(void)12387a0a89d2SRobert Watson audit_quick_stop(void)
12397a0a89d2SRobert Watson {
12407a0a89d2SRobert Watson 	int len;
1241c0020399SRobert Watson 	int cond;
12427a0a89d2SRobert Watson 	char *ptr;
12437a0a89d2SRobert Watson 	time_t tt;
12447a0a89d2SRobert Watson 	char oldname[MAXPATHLEN];
12457a0a89d2SRobert Watson 	char newname[MAXPATHLEN];
1246aa772005SRobert Watson 	char TS[TIMESTAMP_LEN + 1];
12477a0a89d2SRobert Watson 
12487a0a89d2SRobert Watson 	/*
12497a0a89d2SRobert Watson 	 * Auditing already disabled?
12507a0a89d2SRobert Watson 	 */
1251c0020399SRobert Watson 	if (audit_get_cond(&cond) != 0)
12527a0a89d2SRobert Watson 		return (-1);
1253c74c7b73SRobert Watson 	if (cond == AUC_NOAUDIT)
12547a0a89d2SRobert Watson 		return (0);
12557a0a89d2SRobert Watson 
12567a0a89d2SRobert Watson 	/*
12577a0a89d2SRobert Watson 	 *  Generate audit shutdown record.
12587a0a89d2SRobert Watson 	 */
12597a0a89d2SRobert Watson 	(void) auditd_gen_record(AUE_audit_shutdown, NULL);
12607a0a89d2SRobert Watson 
12617a0a89d2SRobert Watson 	/*
12627a0a89d2SRobert Watson 	 * Shutdown auditing in the kernel.
12637a0a89d2SRobert Watson 	 */
12647a0a89d2SRobert Watson 	cond = AUC_DISABLED;
1265c0020399SRobert Watson 	if (audit_set_cond(&cond) != 0)
12667a0a89d2SRobert Watson 		return (-1);
12677a0a89d2SRobert Watson #ifdef	__BSM_INTERNAL_NOTIFY_KEY
12687a0a89d2SRobert Watson 	notify_post(__BSM_INTERNAL_NOTIFY_KEY);
12697a0a89d2SRobert Watson #endif
12707a0a89d2SRobert Watson 
12717a0a89d2SRobert Watson 	/*
12727a0a89d2SRobert Watson 	 * Rename last audit trail and remove 'current' link.
12737a0a89d2SRobert Watson 	 */
1274aa772005SRobert Watson 	len = readlink(AUDIT_CURRENT_LINK, oldname, sizeof(oldname) - 1);
12757a0a89d2SRobert Watson 	if (len < 0)
12767a0a89d2SRobert Watson 		return (-1);
12777a0a89d2SRobert Watson 	oldname[len++] = '\0';
12787a0a89d2SRobert Watson 
1279aa772005SRobert Watson 	if (getTSstr(tt, TS, sizeof(TS)) != 0)
12807a0a89d2SRobert Watson 		return (-1);
12817a0a89d2SRobert Watson 
1282aa772005SRobert Watson 	strlcpy(newname, oldname, sizeof(newname));
12837a0a89d2SRobert Watson 
12847a0a89d2SRobert Watson 	if ((ptr = strstr(newname, NOT_TERMINATED)) != NULL) {
128506edd2f1SRobert Watson 		memcpy(ptr, TS, POSTFIX_LEN);
1286aa772005SRobert Watson 		if (auditd_rename(oldname, newname) != 0)
12877a0a89d2SRobert Watson 			return (-1);
12887a0a89d2SRobert Watson 	} else
12897a0a89d2SRobert Watson 		return (-1);
12907a0a89d2SRobert Watson 
12917a0a89d2SRobert Watson 	(void) unlink(AUDIT_CURRENT_LINK);
12927a0a89d2SRobert Watson 
12937a0a89d2SRobert Watson 	return (0);
12947a0a89d2SRobert Watson }
1295