xref: /freebsd/contrib/openbsm/bin/auditd/auditd.c (revision 4bd0c025f38ae20e2ec54bfbe3f11a0847e87ffb)
1ca0716f5SRobert Watson /*
2ca0716f5SRobert Watson  * Copyright (c) 2004 Apple Computer, Inc.
3ca0716f5SRobert Watson  * All rights reserved.
4ca0716f5SRobert Watson  *
5ca0716f5SRobert Watson  * @APPLE_BSD_LICENSE_HEADER_START@
6ca0716f5SRobert Watson  *
7ca0716f5SRobert Watson  * Redistribution and use in source and binary forms, with or without
8ca0716f5SRobert Watson  * modification, are permitted provided that the following conditions
9ca0716f5SRobert Watson  * are met:
10ca0716f5SRobert Watson  *
11ca0716f5SRobert Watson  * 1.  Redistributions of source code must retain the above copyright
12ca0716f5SRobert Watson  *     notice, this list of conditions and the following disclaimer.
13ca0716f5SRobert Watson  * 2.  Redistributions in binary form must reproduce the above copyright
14ca0716f5SRobert Watson  *     notice, this list of conditions and the following disclaimer in the
15ca0716f5SRobert Watson  *     documentation and/or other materials provided with the distribution.
16ca0716f5SRobert Watson  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
17ca0716f5SRobert Watson  *     its contributors may be used to endorse or promote products derived
18ca0716f5SRobert Watson  *     from this software without specific prior written permission.
19ca0716f5SRobert Watson  *
20ca0716f5SRobert Watson  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
21ca0716f5SRobert Watson  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22ca0716f5SRobert Watson  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23ca0716f5SRobert Watson  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
24ca0716f5SRobert Watson  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25ca0716f5SRobert Watson  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26ca0716f5SRobert Watson  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27ca0716f5SRobert Watson  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28ca0716f5SRobert Watson  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29ca0716f5SRobert Watson  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30ca0716f5SRobert Watson  *
31ca0716f5SRobert Watson  * @APPLE_BSD_LICENSE_HEADER_END@
32ca0716f5SRobert Watson  *
334bd0c025SRobert Watson  * $P4: //depot/projects/trustedbsd/openbsm/bin/auditd/auditd.c#23 $
34ca0716f5SRobert Watson  */
35ca0716f5SRobert Watson 
36f4e380b0SRobert Watson #include <sys/types.h>
37ca0716f5SRobert Watson #include <sys/dirent.h>
38ca0716f5SRobert Watson #include <sys/mman.h>
39ca0716f5SRobert Watson #include <sys/queue.h>
40ca0716f5SRobert Watson #include <sys/stat.h>
41ca0716f5SRobert Watson #include <sys/wait.h>
42ca0716f5SRobert Watson 
43ca0716f5SRobert Watson #include <bsm/audit.h>
44ca0716f5SRobert Watson #include <bsm/audit_uevents.h>
45ca0716f5SRobert Watson #include <bsm/libbsm.h>
46ca0716f5SRobert Watson 
47506764c6SRobert Watson #include <err.h>
48ca0716f5SRobert Watson #include <errno.h>
49ca0716f5SRobert Watson #include <fcntl.h>
5023bf6e20SRobert Watson #include <grp.h>
51ca0716f5SRobert Watson #include <stdio.h>
52ca0716f5SRobert Watson #include <stdlib.h>
53ca0716f5SRobert Watson #include <time.h>
54ca0716f5SRobert Watson #include <unistd.h>
55ca0716f5SRobert Watson #include <signal.h>
56ca0716f5SRobert Watson #include <string.h>
57ca0716f5SRobert Watson #include <syslog.h>
58ca0716f5SRobert Watson 
59ca0716f5SRobert Watson #include "auditd.h"
60ca0716f5SRobert Watson 
61ca0716f5SRobert Watson #define	NA_EVENT_STR_SIZE	25
62bb97b418SRobert Watson #define	POL_STR_SIZE		128
63ca0716f5SRobert Watson 
64ca0716f5SRobert Watson static int	 ret, minval;
65ca0716f5SRobert Watson static char	*lastfile = NULL;
66ca0716f5SRobert Watson static int	 allhardcount = 0;
67ca0716f5SRobert Watson static int	 triggerfd = 0;
68506764c6SRobert Watson static int	 sigchlds, sigchlds_handled;
69ca0716f5SRobert Watson static int	 sighups, sighups_handled;
70ca0716f5SRobert Watson static int	 sigterms, sigterms_handled;
71ca0716f5SRobert Watson 
72ca0716f5SRobert Watson static TAILQ_HEAD(, dir_ent)	dir_q;
73ca0716f5SRobert Watson 
74ca0716f5SRobert Watson static int	config_audit_controls(void);
75ca0716f5SRobert Watson 
76ca0716f5SRobert Watson /*
77ca0716f5SRobert Watson  * Error starting auditd
78ca0716f5SRobert Watson  */
79ca0716f5SRobert Watson static void
80ca0716f5SRobert Watson fail_exit(void)
81ca0716f5SRobert Watson {
82ca0716f5SRobert Watson 
83ca0716f5SRobert Watson 	audit_warn_nostart();
84ca0716f5SRobert Watson 	exit(1);
85ca0716f5SRobert Watson }
86ca0716f5SRobert Watson 
87ca0716f5SRobert Watson /*
88ca0716f5SRobert Watson  * Free our local list of directory names.
89ca0716f5SRobert Watson  */
90ca0716f5SRobert Watson static void
9122ccb20dSRobert Watson free_dir_q(void)
92ca0716f5SRobert Watson {
93ca0716f5SRobert Watson 	struct dir_ent *dirent;
94ca0716f5SRobert Watson 
95ca0716f5SRobert Watson 	while ((dirent = TAILQ_FIRST(&dir_q))) {
96ca0716f5SRobert Watson 		TAILQ_REMOVE(&dir_q, dirent, dirs);
97ca0716f5SRobert Watson 		free(dirent->dirname);
98ca0716f5SRobert Watson 		free(dirent);
99ca0716f5SRobert Watson 	}
100ca0716f5SRobert Watson }
101ca0716f5SRobert Watson 
102ca0716f5SRobert Watson /*
103ca0716f5SRobert Watson  * Generate the timestamp string.
104ca0716f5SRobert Watson  */
105ca0716f5SRobert Watson static int
106ca0716f5SRobert Watson getTSstr(char *buf, int len)
107ca0716f5SRobert Watson {
108ca0716f5SRobert Watson 	struct timeval ts;
109ca0716f5SRobert Watson 	struct timezone tzp;
110ca0716f5SRobert Watson 	time_t tt;
111ca0716f5SRobert Watson 
112ca0716f5SRobert Watson 	if (gettimeofday(&ts, &tzp) != 0)
113ca0716f5SRobert Watson 		return (-1);
114ca0716f5SRobert Watson 	tt = (time_t)ts.tv_sec;
115ca0716f5SRobert Watson 	if (!strftime(buf, len, "%Y%m%d%H%M%S", gmtime(&tt)))
116ca0716f5SRobert Watson 		return (-1);
117ca0716f5SRobert Watson 	return (0);
118ca0716f5SRobert Watson }
119ca0716f5SRobert Watson 
120ca0716f5SRobert Watson /*
121ca0716f5SRobert Watson  * Concat the directory name to the given file name.
122ca0716f5SRobert Watson  * XXX We should affix the hostname also
123ca0716f5SRobert Watson  */
124ca0716f5SRobert Watson static char *
125ca0716f5SRobert Watson affixdir(char *name, struct dir_ent *dirent)
126ca0716f5SRobert Watson {
127ca0716f5SRobert Watson 	char *fn;
128ca0716f5SRobert Watson 	char *curdir;
129ca0716f5SRobert Watson 	const char *sep = "/";
130ca0716f5SRobert Watson 
131ca0716f5SRobert Watson 	curdir = dirent->dirname;
132506764c6SRobert Watson 	syslog(LOG_DEBUG, "dir = %s", dirent->dirname);
133ca0716f5SRobert Watson 
134ca0716f5SRobert Watson 	fn = malloc(strlen(curdir) + strlen(sep) + (2 * POSTFIX_LEN) + 1);
135ca0716f5SRobert Watson 	if (fn == NULL)
136ca0716f5SRobert Watson 		return (NULL);
137ca0716f5SRobert Watson 	strcpy(fn, curdir);
138ca0716f5SRobert Watson 	strcat(fn, sep);
139ca0716f5SRobert Watson 	strcat(fn, name);
140ca0716f5SRobert Watson 	return (fn);
141ca0716f5SRobert Watson }
142ca0716f5SRobert Watson 
143ca0716f5SRobert Watson /*
144ca0716f5SRobert Watson  * Close the previous audit trail file.
145ca0716f5SRobert Watson  */
146ca0716f5SRobert Watson static int
147ca0716f5SRobert Watson close_lastfile(char *TS)
148ca0716f5SRobert Watson {
149ca0716f5SRobert Watson 	char *ptr;
150ca0716f5SRobert Watson 	char *oldname;
151ca0716f5SRobert Watson 
152ca0716f5SRobert Watson 	if (lastfile != NULL) {
153ca0716f5SRobert Watson 		oldname = (char *)malloc(strlen(lastfile) + 1);
154ca0716f5SRobert Watson 		if (oldname == NULL)
155ca0716f5SRobert Watson 			return (-1);
156ca0716f5SRobert Watson 		strcpy(oldname, lastfile);
157ca0716f5SRobert Watson 
158ca0716f5SRobert Watson 		/* Rename the last file -- append timestamp. */
159ca0716f5SRobert Watson 		if ((ptr = strstr(lastfile, NOT_TERMINATED)) != NULL) {
160ca0716f5SRobert Watson 			*ptr = '.';
161ca0716f5SRobert Watson 			strcpy(ptr+1, TS);
162ca0716f5SRobert Watson 			if (rename(oldname, lastfile) != 0)
163bb97b418SRobert Watson 				syslog(LOG_ERR,
164bb97b418SRobert Watson 				    "Could not rename %s to %s: %m", oldname,
165bb97b418SRobert Watson 				    lastfile);
1664bd0c025SRobert Watson 			else {
167506764c6SRobert Watson 				syslog(LOG_INFO, "renamed %s to %s",
168ca0716f5SRobert Watson 				    oldname, lastfile);
1694bd0c025SRobert Watson 				audit_warn_closefile(lastfile);
1704bd0c025SRobert Watson 			}
171ca0716f5SRobert Watson 		}
172ca0716f5SRobert Watson 		free(lastfile);
173ca0716f5SRobert Watson 		free(oldname);
174ca0716f5SRobert Watson 		lastfile = NULL;
175ca0716f5SRobert Watson 	}
176ca0716f5SRobert Watson 	return (0);
177ca0716f5SRobert Watson }
178ca0716f5SRobert Watson 
179ca0716f5SRobert Watson /*
18023bf6e20SRobert Watson  * Create the new audit file with appropriate permissions and ownership.  Try
18123bf6e20SRobert Watson  * to clean up if something goes wrong.
18223bf6e20SRobert Watson  */
18323bf6e20SRobert Watson static int
18423bf6e20SRobert Watson #ifdef AUDIT_REVIEW_GROUP
18523bf6e20SRobert Watson open_trail(const char *fname, uid_t uid, gid_t gid)
18623bf6e20SRobert Watson #else
18723bf6e20SRobert Watson open_trail(const char *fname)
18823bf6e20SRobert Watson #endif
18923bf6e20SRobert Watson {
19023bf6e20SRobert Watson 	int error, fd;
19123bf6e20SRobert Watson 
19223bf6e20SRobert Watson 	fd = open(fname, O_RDONLY | O_CREAT, S_IRUSR | S_IRGRP);
19323bf6e20SRobert Watson 	if (fd < 0)
19423bf6e20SRobert Watson 		return (-1);
19523bf6e20SRobert Watson #ifdef AUDIT_REVIEW_GROUP
19623bf6e20SRobert Watson 	if (fchown(fd, uid, gid) < 0) {
19723bf6e20SRobert Watson 		error = errno;
19823bf6e20SRobert Watson 		close(fd);
19923bf6e20SRobert Watson 		(void)unlink(fname);
20023bf6e20SRobert Watson 		errno = error;
20123bf6e20SRobert Watson 		return (-1);
20223bf6e20SRobert Watson 	}
20323bf6e20SRobert Watson #endif
20423bf6e20SRobert Watson 	return (fd);
20523bf6e20SRobert Watson }
20623bf6e20SRobert Watson 
20723bf6e20SRobert Watson /*
208ca0716f5SRobert Watson  * Create the new file name, swap with existing audit file.
209ca0716f5SRobert Watson  */
210ca0716f5SRobert Watson static int
211ca0716f5SRobert Watson swap_audit_file(void)
212ca0716f5SRobert Watson {
213ca0716f5SRobert Watson 	char timestr[2 * POSTFIX_LEN];
214ca0716f5SRobert Watson 	char *fn;
215ca0716f5SRobert Watson 	char TS[POSTFIX_LEN];
216ca0716f5SRobert Watson 	struct dir_ent *dirent;
21723bf6e20SRobert Watson #ifdef AUDIT_REVIEW_GROUP
21823bf6e20SRobert Watson 	struct group *grp;
21923bf6e20SRobert Watson 	gid_t gid;
22023bf6e20SRobert Watson 	uid_t uid;
22123bf6e20SRobert Watson #endif
22223bf6e20SRobert Watson 	int error, fd;
223ca0716f5SRobert Watson 
224ca0716f5SRobert Watson 	if (getTSstr(TS, POSTFIX_LEN) != 0)
225ca0716f5SRobert Watson 		return (-1);
226ca0716f5SRobert Watson 
227ca0716f5SRobert Watson 	strcpy(timestr, TS);
228ca0716f5SRobert Watson 	strcat(timestr, NOT_TERMINATED);
229ca0716f5SRobert Watson 
23023bf6e20SRobert Watson #ifdef AUDIT_REVIEW_GROUP
23123bf6e20SRobert Watson 	/*
23223bf6e20SRobert Watson 	 * XXXRW: Currently, this code falls back to the daemon gid, which is
23323bf6e20SRobert Watson 	 * likely the wheel group.  Is there a better way to deal with this?
23423bf6e20SRobert Watson 	 */
23523bf6e20SRobert Watson 	grp = getgrnam(AUDIT_REVIEW_GROUP);
23623bf6e20SRobert Watson 	if (grp == NULL) {
23723bf6e20SRobert Watson 		syslog(LOG_INFO,
23823bf6e20SRobert Watson 		    "Audit review group '%s' not available, using daemon gid",
23923bf6e20SRobert Watson 		    AUDIT_REVIEW_GROUP);
24023bf6e20SRobert Watson 		gid = -1;
24123bf6e20SRobert Watson 	} else
24223bf6e20SRobert Watson 		gid = grp->gr_gid;
24323bf6e20SRobert Watson 	uid = getuid();
24423bf6e20SRobert Watson #endif
24523bf6e20SRobert Watson 
246ca0716f5SRobert Watson 	/* Try until we succeed. */
247ca0716f5SRobert Watson 	while ((dirent = TAILQ_FIRST(&dir_q))) {
248ca0716f5SRobert Watson 		if ((fn = affixdir(timestr, dirent)) == NULL) {
249506764c6SRobert Watson 			syslog(LOG_INFO, "Failed to swap log at time %s",
250ca0716f5SRobert Watson 				timestr);
251ca0716f5SRobert Watson 			return (-1);
252ca0716f5SRobert Watson 		}
253ca0716f5SRobert Watson 
254ca0716f5SRobert Watson 		/*
255ca0716f5SRobert Watson 		 * Create and open the file; then close and pass to the
256ca0716f5SRobert Watson 		 * kernel if all went well.
257ca0716f5SRobert Watson 		 */
258506764c6SRobert Watson 		syslog(LOG_INFO, "New audit file is %s", fn);
25923bf6e20SRobert Watson #ifdef AUDIT_REVIEW_GROUP
26023bf6e20SRobert Watson 		fd = open_trail(fn, uid, gid);
26123bf6e20SRobert Watson #else
26223bf6e20SRobert Watson 		fd = open_trail(fn);
26323bf6e20SRobert Watson #endif
264ca0716f5SRobert Watson 		if (fd < 0)
26523bf6e20SRobert Watson 			warn("open(%s)", fn);
26623bf6e20SRobert Watson 		if (fd >= 0) {
26723bf6e20SRobert Watson 			error = auditctl(fn);
26823bf6e20SRobert Watson 			if (error) {
269ca0716f5SRobert Watson 				syslog(LOG_ERR,
270506764c6SRobert Watson 				    "auditctl failed setting log file! : %s",
271ca0716f5SRobert Watson 				    strerror(errno));
272ca0716f5SRobert Watson 				close(fd);
273ca0716f5SRobert Watson 			} else {
274ca0716f5SRobert Watson 				/* Success. */
275ca0716f5SRobert Watson 				close_lastfile(TS);
276ca0716f5SRobert Watson 				lastfile = fn;
277ca0716f5SRobert Watson 				close(fd);
278ca0716f5SRobert Watson 				return (0);
279ca0716f5SRobert Watson 			}
28023bf6e20SRobert Watson 		}
281ca0716f5SRobert Watson 
282ca0716f5SRobert Watson 		/*
283ca0716f5SRobert Watson 		 * Tell the administrator about lack of permissions for dir.
284ca0716f5SRobert Watson 		 */
285ca0716f5SRobert Watson 		audit_warn_getacdir(dirent->dirname);
286ca0716f5SRobert Watson 
287ca0716f5SRobert Watson 		/* Try again with a different directory. */
288ca0716f5SRobert Watson 		TAILQ_REMOVE(&dir_q, dirent, dirs);
289ca0716f5SRobert Watson 		free(dirent->dirname);
290ca0716f5SRobert Watson 		free(dirent);
291ca0716f5SRobert Watson 	}
292bb97b418SRobert Watson 	syslog(LOG_ERR, "Log directories exhausted");
293ca0716f5SRobert Watson 	return (-1);
294ca0716f5SRobert Watson }
295ca0716f5SRobert Watson 
296ca0716f5SRobert Watson /*
297ca0716f5SRobert Watson  * Read the audit_control file contents.
298ca0716f5SRobert Watson  */
299ca0716f5SRobert Watson static int
300ca0716f5SRobert Watson read_control_file(void)
301ca0716f5SRobert Watson {
302ca0716f5SRobert Watson 	char cur_dir[MAXNAMLEN];
303ca0716f5SRobert Watson 	struct dir_ent *dirent;
304ca0716f5SRobert Watson 	au_qctrl_t qctrl;
305ca0716f5SRobert Watson 
306ca0716f5SRobert Watson 	/*
307ca0716f5SRobert Watson 	 * Clear old values.  Force a re-read of the file the next time.
308ca0716f5SRobert Watson 	 */
309ca0716f5SRobert Watson 	free_dir_q();
310ca0716f5SRobert Watson 	endac();
311ca0716f5SRobert Watson 
312ca0716f5SRobert Watson 	/*
313ca0716f5SRobert Watson 	 * Read the list of directories into a local linked list.
314ca0716f5SRobert Watson 	 *
315ca0716f5SRobert Watson 	 * XXX We should use the reentrant interfaces once they are
316ca0716f5SRobert Watson 	 * available.
317ca0716f5SRobert Watson 	 */
318ca0716f5SRobert Watson 	while (getacdir(cur_dir, MAXNAMLEN) >= 0) {
319ca0716f5SRobert Watson 		dirent = (struct dir_ent *) malloc(sizeof(struct dir_ent));
320ca0716f5SRobert Watson 		if (dirent == NULL)
321ca0716f5SRobert Watson 			return (-1);
322ca0716f5SRobert Watson 		dirent->softlim = 0;
323ca0716f5SRobert Watson 		dirent->dirname = (char *) malloc(MAXNAMLEN);
324ca0716f5SRobert Watson 		if (dirent->dirname == NULL) {
325ca0716f5SRobert Watson 			free(dirent);
326ca0716f5SRobert Watson 			return (-1);
327ca0716f5SRobert Watson 		}
328ca0716f5SRobert Watson 		strcpy(dirent->dirname, cur_dir);
329ca0716f5SRobert Watson 		TAILQ_INSERT_TAIL(&dir_q, dirent, dirs);
330ca0716f5SRobert Watson 	}
331ca0716f5SRobert Watson 
332ca0716f5SRobert Watson 	allhardcount = 0;
333ca0716f5SRobert Watson 	if (swap_audit_file() == -1) {
334506764c6SRobert Watson 		syslog(LOG_ERR, "Could not swap audit file");
335ca0716f5SRobert Watson 		/*
336ca0716f5SRobert Watson 		 * XXX Faulty directory listing? - user should be given
337ca0716f5SRobert Watson 		 * XXX an opportunity to change the audit_control file
338ca0716f5SRobert Watson 		 * XXX switch to a reduced mode of auditing?
339ca0716f5SRobert Watson 		 */
340ca0716f5SRobert Watson 		return (-1);
341ca0716f5SRobert Watson 	}
342ca0716f5SRobert Watson 
343ca0716f5SRobert Watson 	/*
344ca0716f5SRobert Watson 	 * XXX There are synchronization problems here
345ca0716f5SRobert Watson  	 * XXX what should we do if a trigger for the earlier limit
346ca0716f5SRobert Watson 	 * XXX is generated here?
347ca0716f5SRobert Watson 	 */
348ca0716f5SRobert Watson 	if (0 == (ret = getacmin(&minval))) {
349bb97b418SRobert Watson 		syslog(LOG_DEBUG, "min free = %d", minval);
350ca0716f5SRobert Watson 		if (auditon(A_GETQCTRL, &qctrl, sizeof(qctrl)) != 0) {
351ca0716f5SRobert Watson 			syslog(LOG_ERR,
352506764c6SRobert Watson 			    "could not get audit queue settings");
353ca0716f5SRobert Watson 				return (-1);
354ca0716f5SRobert Watson 		}
355ca0716f5SRobert Watson 		qctrl.aq_minfree = minval;
356ca0716f5SRobert Watson 		if (auditon(A_SETQCTRL, &qctrl, sizeof(qctrl)) != 0) {
357ca0716f5SRobert Watson 			syslog(LOG_ERR,
358506764c6SRobert Watson 			    "could not set audit queue settings");
359ca0716f5SRobert Watson 			return (-1);
360ca0716f5SRobert Watson 		}
361ca0716f5SRobert Watson 	}
362ca0716f5SRobert Watson 
363ca0716f5SRobert Watson 	return (0);
364ca0716f5SRobert Watson }
365ca0716f5SRobert Watson 
366ca0716f5SRobert Watson /*
367ca0716f5SRobert Watson  * Close all log files, control files, and tell the audit system.
368ca0716f5SRobert Watson  */
369ca0716f5SRobert Watson static int
370ca0716f5SRobert Watson close_all(void)
371ca0716f5SRobert Watson {
372fdb4472cSRobert Watson 	struct auditinfo ai;
373ca0716f5SRobert Watson 	int err_ret = 0;
374ca0716f5SRobert Watson 	char TS[POSTFIX_LEN];
375ca0716f5SRobert Watson 	int aufd;
376ca0716f5SRobert Watson 	token_t *tok;
377ca0716f5SRobert Watson 	long cond;
378ca0716f5SRobert Watson 
379ca0716f5SRobert Watson 	/* Generate an audit record. */
380ca0716f5SRobert Watson 	if ((aufd = au_open()) == -1)
381506764c6SRobert Watson 		syslog(LOG_ERR, "Could not create audit shutdown event.");
382ca0716f5SRobert Watson 	else {
383ca0716f5SRobert Watson 		if ((tok = au_to_text("auditd::Audit shutdown")) != NULL)
384ca0716f5SRobert Watson 			au_write(aufd, tok);
385fdb4472cSRobert Watson 		/*
386fdb4472cSRobert Watson 		 * XXX we need to implement extended subject tokens so we can
387fdb4472cSRobert Watson 		 * effectively represent terminal lines with this token type.
388fdb4472cSRobert Watson 		 */
389fdb4472cSRobert Watson 		bzero(&ai, sizeof(ai));
390fdb4472cSRobert Watson 		if ((tok = au_to_subject32(getuid(), geteuid(), getegid(),
391fdb4472cSRobert Watson 		    getuid(), getgid(), getpid(), getpid(), &ai.ai_termid))
392fdb4472cSRobert Watson 		    != NULL)
393fdb4472cSRobert Watson 			au_write(aufd, tok);
394fdb4472cSRobert Watson 		if ((tok = au_to_return32(0, 0)) != NULL)
395fdb4472cSRobert Watson 			au_write(aufd, tok);
396ca0716f5SRobert Watson 		if (au_close(aufd, 1, AUE_audit_shutdown) == -1)
397ca0716f5SRobert Watson 			syslog(LOG_ERR,
398506764c6SRobert Watson 			    "Could not close audit shutdown event.");
399ca0716f5SRobert Watson 	}
400ca0716f5SRobert Watson 
401ca0716f5SRobert Watson 	/* Flush contents. */
402ca0716f5SRobert Watson 	cond = AUC_DISABLED;
403ca0716f5SRobert Watson 	err_ret = auditon(A_SETCOND, &cond, sizeof(cond));
404ca0716f5SRobert Watson 	if (err_ret != 0) {
405506764c6SRobert Watson 		syslog(LOG_ERR, "Disabling audit failed! : %s",
406ca0716f5SRobert Watson 		    strerror(errno));
407ca0716f5SRobert Watson 		err_ret = 1;
408ca0716f5SRobert Watson 	}
409ca0716f5SRobert Watson 	if (getTSstr(TS, POSTFIX_LEN) == 0)
410ca0716f5SRobert Watson 		close_lastfile(TS);
411ca0716f5SRobert Watson 	if (lastfile != NULL)
412ca0716f5SRobert Watson 		free(lastfile);
413ca0716f5SRobert Watson 
414ca0716f5SRobert Watson 	free_dir_q();
415ca0716f5SRobert Watson 	if ((remove(AUDITD_PIDFILE) == -1) || err_ret) {
416506764c6SRobert Watson 		syslog(LOG_ERR, "Could not unregister");
417ca0716f5SRobert Watson 		audit_warn_postsigterm();
418ca0716f5SRobert Watson 		return (1);
419ca0716f5SRobert Watson 	}
420ca0716f5SRobert Watson 	endac();
421ca0716f5SRobert Watson 
422ca0716f5SRobert Watson 	if (close(triggerfd) != 0)
423506764c6SRobert Watson 		syslog(LOG_ERR, "Error closing control file");
424506764c6SRobert Watson 	syslog(LOG_INFO, "Finished");
425ca0716f5SRobert Watson 	return (0);
426ca0716f5SRobert Watson }
427ca0716f5SRobert Watson 
428ca0716f5SRobert Watson /*
429ca0716f5SRobert Watson  * When we get a signal, we are often not at a clean point.  So, little can
430ca0716f5SRobert Watson  * be done in the signal handler itself.  Instead,  we send a message to the
431ca0716f5SRobert Watson  * main servicing loop to do proper handling from a non-signal-handler
432ca0716f5SRobert Watson  * context.
433ca0716f5SRobert Watson  */
434ca0716f5SRobert Watson static void
435ca0716f5SRobert Watson relay_signal(int signal)
436ca0716f5SRobert Watson {
437ca0716f5SRobert Watson 
438ca0716f5SRobert Watson 	if (signal == SIGHUP)
439ca0716f5SRobert Watson 		sighups++;
440ca0716f5SRobert Watson 	if (signal == SIGTERM)
441ca0716f5SRobert Watson 		sigterms++;
442506764c6SRobert Watson 	if (signal == SIGCHLD)
443506764c6SRobert Watson 		sigchlds++;
444ca0716f5SRobert Watson }
445ca0716f5SRobert Watson 
446ca0716f5SRobert Watson /*
447ca0716f5SRobert Watson  * Registering the daemon.
448ca0716f5SRobert Watson  */
449ca0716f5SRobert Watson static int
450ca0716f5SRobert Watson register_daemon(void)
451ca0716f5SRobert Watson {
452ca0716f5SRobert Watson 	FILE * pidfile;
453ca0716f5SRobert Watson 	int fd;
454ca0716f5SRobert Watson 	pid_t pid;
455ca0716f5SRobert Watson 
456ca0716f5SRobert Watson 	/* Set up the signal hander. */
457ca0716f5SRobert Watson 	if (signal(SIGTERM, relay_signal) == SIG_ERR) {
458ca0716f5SRobert Watson 		syslog(LOG_ERR,
459506764c6SRobert Watson 		    "Could not set signal handler for SIGTERM");
460ca0716f5SRobert Watson 		fail_exit();
461ca0716f5SRobert Watson 	}
462ca0716f5SRobert Watson 	if (signal(SIGCHLD, relay_signal) == SIG_ERR) {
463ca0716f5SRobert Watson 		syslog(LOG_ERR,
464506764c6SRobert Watson 		    "Could not set signal handler for SIGCHLD");
465ca0716f5SRobert Watson 		fail_exit();
466ca0716f5SRobert Watson 	}
467ca0716f5SRobert Watson 	if (signal(SIGHUP, relay_signal) == SIG_ERR) {
468ca0716f5SRobert Watson 		syslog(LOG_ERR,
469506764c6SRobert Watson 		    "Could not set signal handler for SIGHUP");
470ca0716f5SRobert Watson 		fail_exit();
471ca0716f5SRobert Watson 	}
472ca0716f5SRobert Watson 
473ca0716f5SRobert Watson 	if ((pidfile = fopen(AUDITD_PIDFILE, "a")) == NULL) {
474506764c6SRobert Watson 		syslog(LOG_ERR, "Could not open PID file");
475ca0716f5SRobert Watson 		audit_warn_tmpfile();
476ca0716f5SRobert Watson 		return (-1);
477ca0716f5SRobert Watson 	}
478ca0716f5SRobert Watson 
479ca0716f5SRobert Watson 	/* Attempt to lock the pid file; if a lock is present, exit. */
480ca0716f5SRobert Watson 	fd = fileno(pidfile);
481ca0716f5SRobert Watson 	if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
482ca0716f5SRobert Watson 		syslog(LOG_ERR,
483506764c6SRobert Watson 		    "PID file is locked (is another auditd running?).");
484ca0716f5SRobert Watson 		audit_warn_ebusy();
485ca0716f5SRobert Watson 		return (-1);
486ca0716f5SRobert Watson 	}
487ca0716f5SRobert Watson 
488ca0716f5SRobert Watson 	pid = getpid();
489ca0716f5SRobert Watson 	ftruncate(fd, 0);
490ca0716f5SRobert Watson 	if (fprintf(pidfile, "%u\n", pid) < 0) {
491ca0716f5SRobert Watson 		/* Should not start the daemon. */
492ca0716f5SRobert Watson 		fail_exit();
493ca0716f5SRobert Watson 	}
494ca0716f5SRobert Watson 
495ca0716f5SRobert Watson 	fflush(pidfile);
496ca0716f5SRobert Watson 	return (0);
497ca0716f5SRobert Watson }
498ca0716f5SRobert Watson 
499ca0716f5SRobert Watson /*
500bb97b418SRobert Watson  * Handle the audit trigger event.
501bb97b418SRobert Watson  *
502bb97b418SRobert Watson  * We suppress (ignore) duplicated triggers in close succession in order to
503bb97b418SRobert Watson  * try to avoid thrashing-like behavior.  However, not all triggers can be
504bb97b418SRobert Watson  * ignored, as triggers generally represent edge triggers, not level
505bb97b418SRobert Watson  * triggers, and won't be retransmitted if the condition persists.  Of
506bb97b418SRobert Watson  * specific concern is the rotate trigger -- if one is dropped, then it will
507bb97b418SRobert Watson  * not be retransmitted, and the log file will grow in an unbounded fashion.
508ca0716f5SRobert Watson  */
509ca0716f5SRobert Watson #define	DUPLICATE_INTERVAL	30
510ca0716f5SRobert Watson static void
511ca0716f5SRobert Watson handle_audit_trigger(int trigger)
512ca0716f5SRobert Watson {
513bb97b418SRobert Watson 	static int last_trigger, last_warning;
514ca0716f5SRobert Watson 	static time_t last_time;
515ca0716f5SRobert Watson 	struct dir_ent *dirent;
516ca0716f5SRobert Watson 	struct timeval ts;
517ca0716f5SRobert Watson 	struct timezone tzp;
518ca0716f5SRobert Watson 	time_t tt;
519ca0716f5SRobert Watson 
520bb97b418SRobert Watson 	/*
521bb97b418SRobert Watson 	 * Suppress duplicate messages from the kernel within the specified
522bb97b418SRobert Watson 	 * interval.
523bb97b418SRobert Watson 	 */
524ca0716f5SRobert Watson 	if (gettimeofday(&ts, &tzp) == 0) {
525ca0716f5SRobert Watson 		tt = (time_t)ts.tv_sec;
526bb97b418SRobert Watson 		switch (trigger) {
527bb97b418SRobert Watson 		case AUDIT_TRIGGER_LOW_SPACE:
528bb97b418SRobert Watson 		case AUDIT_TRIGGER_NO_SPACE:
529bb97b418SRobert Watson 			/*
530bb97b418SRobert Watson 			 * Triggers we can suppress.  Of course, we also need
531bb97b418SRobert Watson 			 * to rate limit the warnings, so apply the same
532bb97b418SRobert Watson 			 * interval limit on syslog messages.
533bb97b418SRobert Watson 			 */
534ca0716f5SRobert Watson 			if ((trigger == last_trigger) &&
535bb97b418SRobert Watson 			    (tt < (last_time + DUPLICATE_INTERVAL))) {
536bb97b418SRobert Watson 				if (tt >= (last_warning + DUPLICATE_INTERVAL))
537bb97b418SRobert Watson 					syslog(LOG_INFO,
538bb97b418SRobert Watson 					    "Suppressing duplicate trigger %d",
539bb97b418SRobert Watson 					    trigger);
540ca0716f5SRobert Watson 				return;
541bb97b418SRobert Watson 			}
542bb97b418SRobert Watson 			last_warning = tt;
543bb97b418SRobert Watson 			break;
544bb97b418SRobert Watson 
545bb97b418SRobert Watson 		case AUDIT_TRIGGER_ROTATE_KERNEL:
546bb97b418SRobert Watson 		case AUDIT_TRIGGER_ROTATE_USER:
547bb97b418SRobert Watson 		case AUDIT_TRIGGER_READ_FILE:
548bb97b418SRobert Watson 			/*
549bb97b418SRobert Watson 			 * Triggers that we cannot suppress.
550bb97b418SRobert Watson 			 */
551bb97b418SRobert Watson 			break;
552bb97b418SRobert Watson 		}
553bb97b418SRobert Watson 
554bb97b418SRobert Watson 		/*
555bb97b418SRobert Watson 		 * Only update last_trigger after aborting due to a duplicate
556bb97b418SRobert Watson 		 * trigger, not before, or we will never allow that trigger
557bb97b418SRobert Watson 		 * again.
558bb97b418SRobert Watson 		 */
559ca0716f5SRobert Watson 		last_trigger = trigger;
560ca0716f5SRobert Watson 		last_time = tt;
561ca0716f5SRobert Watson 	}
562ca0716f5SRobert Watson 
563ca0716f5SRobert Watson 	/*
564ca0716f5SRobert Watson 	 * Message processing is done here.
565ca0716f5SRobert Watson  	 */
566ca0716f5SRobert Watson 	dirent = TAILQ_FIRST(&dir_q);
567ca0716f5SRobert Watson 	switch(trigger) {
568ca0716f5SRobert Watson 	case AUDIT_TRIGGER_LOW_SPACE:
569506764c6SRobert Watson 		syslog(LOG_INFO, "Got low space trigger");
570ca0716f5SRobert Watson 		if (dirent && (dirent->softlim != 1)) {
571ca0716f5SRobert Watson 			TAILQ_REMOVE(&dir_q, dirent, dirs);
572ca0716f5SRobert Watson 				/* Add this node to the end of the list. */
573ca0716f5SRobert Watson 				TAILQ_INSERT_TAIL(&dir_q, dirent, dirs);
574ca0716f5SRobert Watson 				audit_warn_soft(dirent->dirname);
575ca0716f5SRobert Watson 				dirent->softlim = 1;
576ca0716f5SRobert Watson 
577ca0716f5SRobert Watson 			if (TAILQ_NEXT(TAILQ_FIRST(&dir_q), dirs) != NULL &&
578ca0716f5SRobert Watson 			    swap_audit_file() == -1)
579506764c6SRobert Watson 				syslog(LOG_ERR, "Error swapping audit file");
580ca0716f5SRobert Watson 
581ca0716f5SRobert Watson 			/*
582ca0716f5SRobert Watson 			 * Check if the next dir has already reached its soft
583ca0716f5SRobert Watson 			 * limit.
584ca0716f5SRobert Watson 			 */
585ca0716f5SRobert Watson 			dirent = TAILQ_FIRST(&dir_q);
586ca0716f5SRobert Watson 			if (dirent->softlim == 1)  {
587ca0716f5SRobert Watson 				/* All dirs have reached their soft limit. */
588ca0716f5SRobert Watson 				audit_warn_allsoft();
589ca0716f5SRobert Watson 			}
590ca0716f5SRobert Watson 		} else {
591ca0716f5SRobert Watson 			/*
592ca0716f5SRobert Watson 			 * Continue auditing to the current file.  Also
593ca0716f5SRobert Watson 			 * generate an allsoft warning.
594bb97b418SRobert Watson 			 *
595ca0716f5SRobert Watson 			 * XXX do we want to do this ?
596ca0716f5SRobert Watson 			 */
597ca0716f5SRobert Watson 			audit_warn_allsoft();
598ca0716f5SRobert Watson 		}
599ca0716f5SRobert Watson 		break;
600ca0716f5SRobert Watson 
601ca0716f5SRobert Watson 	case AUDIT_TRIGGER_NO_SPACE:
602506764c6SRobert Watson 		syslog(LOG_INFO, "Got no space trigger");
603ca0716f5SRobert Watson 
604ca0716f5SRobert Watson 		/* Delete current dir, go on to next. */
605ca0716f5SRobert Watson 		TAILQ_REMOVE(&dir_q, dirent, dirs);
606ca0716f5SRobert Watson 		audit_warn_hard(dirent->dirname);
607ca0716f5SRobert Watson 		free(dirent->dirname);
608ca0716f5SRobert Watson 		free(dirent);
609ca0716f5SRobert Watson 
610ca0716f5SRobert Watson 		if (swap_audit_file() == -1)
611506764c6SRobert Watson 			syslog(LOG_ERR, "Error swapping audit file");
612ca0716f5SRobert Watson 
613ca0716f5SRobert Watson 		/* We are out of log directories. */
614ca0716f5SRobert Watson 		audit_warn_allhard(++allhardcount);
615ca0716f5SRobert Watson 		break;
616ca0716f5SRobert Watson 
617bb97b418SRobert Watson 	case AUDIT_TRIGGER_ROTATE_KERNEL:
618bb97b418SRobert Watson 	case AUDIT_TRIGGER_ROTATE_USER:
619ca0716f5SRobert Watson 		/*
620ca0716f5SRobert Watson 		 * Create a new file and swap with the one being used in
621ca0716f5SRobert Watson 		 * kernel
622ca0716f5SRobert Watson 		 */
623bb97b418SRobert Watson 		syslog(LOG_INFO, "Got open new trigger from %s", trigger ==
624bb97b418SRobert Watson 		    AUDIT_TRIGGER_ROTATE_KERNEL ? "kernel" : "user");
625ca0716f5SRobert Watson 		if (swap_audit_file() == -1)
626506764c6SRobert Watson 			syslog(LOG_ERR, "Error swapping audit file");
627ca0716f5SRobert Watson 		break;
628ca0716f5SRobert Watson 
629ca0716f5SRobert Watson 	case AUDIT_TRIGGER_READ_FILE:
630506764c6SRobert Watson 		syslog(LOG_INFO, "Got read file trigger");
631ca0716f5SRobert Watson 		if (read_control_file() == -1)
632506764c6SRobert Watson 			syslog(LOG_ERR, "Error in audit control file");
633ca0716f5SRobert Watson 		if (config_audit_controls() == -1)
634506764c6SRobert Watson 			syslog(LOG_ERR, "Error setting audit controls");
635ca0716f5SRobert Watson 		break;
636ca0716f5SRobert Watson 
637ca0716f5SRobert Watson 	default:
638506764c6SRobert Watson 		syslog(LOG_ERR, "Got unknown trigger %d", trigger);
639ca0716f5SRobert Watson 		break;
640ca0716f5SRobert Watson 	}
641ca0716f5SRobert Watson }
642ca0716f5SRobert Watson 
643ca0716f5SRobert Watson static void
644ca0716f5SRobert Watson handle_sighup(void)
645ca0716f5SRobert Watson {
646ca0716f5SRobert Watson 
647ca0716f5SRobert Watson 	sighups_handled = sighups;
648ca0716f5SRobert Watson 	config_audit_controls();
649ca0716f5SRobert Watson }
650ca0716f5SRobert Watson 
651ca0716f5SRobert Watson /*
652ca0716f5SRobert Watson  * Reap our children.
653ca0716f5SRobert Watson  */
654ca0716f5SRobert Watson static void
655ca0716f5SRobert Watson reap_children(void)
656ca0716f5SRobert Watson {
657ca0716f5SRobert Watson 	pid_t child;
658ca0716f5SRobert Watson 	int wstatus;
659ca0716f5SRobert Watson 
660ca0716f5SRobert Watson 	while ((child = waitpid(-1, &wstatus, WNOHANG)) > 0) {
661ca0716f5SRobert Watson 		if (!wstatus)
662ca0716f5SRobert Watson 			continue;
663506764c6SRobert Watson 		syslog(LOG_INFO, "warn process [pid=%d] %s %d.", child,
664ca0716f5SRobert Watson 		    ((WIFEXITED(wstatus)) ? "exited with non-zero status" :
665ca0716f5SRobert Watson 		    "exited as a result of signal"),
666ca0716f5SRobert Watson 		    ((WIFEXITED(wstatus)) ? WEXITSTATUS(wstatus) :
667ca0716f5SRobert Watson 		    WTERMSIG(wstatus)));
668ca0716f5SRobert Watson 	}
669ca0716f5SRobert Watson }
670ca0716f5SRobert Watson 
671506764c6SRobert Watson static void
672506764c6SRobert Watson handle_sigchld(void)
673506764c6SRobert Watson {
674506764c6SRobert Watson 
675506764c6SRobert Watson 	sigchlds_handled = sigchlds;
676506764c6SRobert Watson 	reap_children();
677506764c6SRobert Watson }
678506764c6SRobert Watson 
679506764c6SRobert Watson /*
680506764c6SRobert Watson  * Read the control file for triggers/signals and handle appropriately.
681506764c6SRobert Watson  */
682506764c6SRobert Watson static int
683506764c6SRobert Watson wait_for_events(void)
684506764c6SRobert Watson {
685506764c6SRobert Watson 	int num;
686506764c6SRobert Watson 	unsigned int trigger;
687506764c6SRobert Watson 
688506764c6SRobert Watson 	for (;;) {
689506764c6SRobert Watson 		num = read(triggerfd, &trigger, sizeof(trigger));
690506764c6SRobert Watson 		if ((num == -1) && (errno != EINTR)) {
691506764c6SRobert Watson 			syslog(LOG_ERR, "%s: error %d", __FUNCTION__, errno);
692506764c6SRobert Watson 			return (-1);
693506764c6SRobert Watson 		}
694506764c6SRobert Watson 		if (sigterms != sigterms_handled) {
695506764c6SRobert Watson 			syslog(LOG_DEBUG, "%s: SIGTERM", __FUNCTION__);
696506764c6SRobert Watson 			break;
697506764c6SRobert Watson 		}
698bb97b418SRobert Watson 		if (sigchlds != sigchlds_handled)
699506764c6SRobert Watson 			handle_sigchld();
700506764c6SRobert Watson 		if (sighups != sighups_handled) {
701506764c6SRobert Watson 			syslog(LOG_DEBUG, "%s: SIGHUP", __FUNCTION__);
702506764c6SRobert Watson 			handle_sighup();
703506764c6SRobert Watson 		}
704506764c6SRobert Watson 		if ((num == -1) && (errno == EINTR))
705506764c6SRobert Watson 			continue;
706506764c6SRobert Watson 		if (num == 0) {
707506764c6SRobert Watson 			syslog(LOG_ERR, "%s: read EOF", __FUNCTION__);
708506764c6SRobert Watson 			return (-1);
709506764c6SRobert Watson 		}
710506764c6SRobert Watson 		if (trigger == AUDIT_TRIGGER_CLOSE_AND_DIE)
711506764c6SRobert Watson 			break;
712506764c6SRobert Watson 		else
713506764c6SRobert Watson 			handle_audit_trigger(trigger);
714506764c6SRobert Watson 	}
715506764c6SRobert Watson 	return (close_all());
716506764c6SRobert Watson }
717506764c6SRobert Watson 
718ca0716f5SRobert Watson /*
719ca0716f5SRobert Watson  * Configure the audit controls in the kernel: the event to class mapping,
720ca0716f5SRobert Watson  * kernel preselection mask, etc.
721ca0716f5SRobert Watson  */
722ca0716f5SRobert Watson static int
723ca0716f5SRobert Watson config_audit_controls(void)
724ca0716f5SRobert Watson {
725ca0716f5SRobert Watson 	au_event_ent_t ev, *evp;
726ca0716f5SRobert Watson 	au_evclass_map_t evc_map;
727ca0716f5SRobert Watson 	au_mask_t aumask;
728ca0716f5SRobert Watson 	int ctr = 0;
729ca0716f5SRobert Watson 	char naeventstr[NA_EVENT_STR_SIZE];
730bb97b418SRobert Watson 	char polstr[POL_STR_SIZE];
731bb97b418SRobert Watson 	long policy;
7324bd0c025SRobert Watson 	au_fstat_t au_fstat;
7334bd0c025SRobert Watson 	size_t filesz;
734ca0716f5SRobert Watson 
735ca0716f5SRobert Watson 	/*
736ca0716f5SRobert Watson 	 * Process the audit event file, obtaining a class mapping for each
737ca0716f5SRobert Watson 	 * event, and send that mapping into the kernel.
738bb97b418SRobert Watson 	 *
739ca0716f5SRobert Watson 	 * XXX There's a risk here that the BSM library will return NULL
740ca0716f5SRobert Watson 	 * for an event when it can't properly map it to a class. In that
741ca0716f5SRobert Watson 	 * case, we will not process any events beyond the one that failed,
742ca0716f5SRobert Watson 	 * but should. We need a way to get a count of the events.
743ca0716f5SRobert Watson 	*/
744ca0716f5SRobert Watson 	ev.ae_name = (char *)malloc(AU_EVENT_NAME_MAX);
745ca0716f5SRobert Watson 	ev.ae_desc = (char *)malloc(AU_EVENT_DESC_MAX);
746ca0716f5SRobert Watson 	if ((ev.ae_name == NULL) || (ev.ae_desc == NULL)) {
747bb97b418SRobert Watson 		if (ev.ae_name != NULL)
748bb97b418SRobert Watson 			free(ev.ae_name);
749ca0716f5SRobert Watson 		syslog(LOG_ERR,
750ca0716f5SRobert Watson 		    "Memory allocation error when configuring audit controls.");
751ca0716f5SRobert Watson 		return (-1);
752ca0716f5SRobert Watson 	}
753bb97b418SRobert Watson 
754bb97b418SRobert Watson 	/*
755bb97b418SRobert Watson 	 * XXXRW: Currently we have no way to remove mappings from the kernel
756bb97b418SRobert Watson 	 * when they are removed from the file-based mappings.
757bb97b418SRobert Watson 	 */
758ca0716f5SRobert Watson 	evp = &ev;
759ca0716f5SRobert Watson 	setauevent();
760ca0716f5SRobert Watson 	while ((evp = getauevent_r(evp)) != NULL) {
761ca0716f5SRobert Watson 		evc_map.ec_number = evp->ae_number;
762ca0716f5SRobert Watson 		evc_map.ec_class = evp->ae_class;
763ca0716f5SRobert Watson 		if (auditon(A_SETCLASS, &evc_map, sizeof(au_evclass_map_t))
764ca0716f5SRobert Watson 		    != 0)
765ca0716f5SRobert Watson 			syslog(LOG_ERR,
766ca0716f5SRobert Watson 				"Failed to register class mapping for event %s",
767ca0716f5SRobert Watson 				 evp->ae_name);
768ca0716f5SRobert Watson 		else
769ca0716f5SRobert Watson 			ctr++;
770ca0716f5SRobert Watson 	}
771ca0716f5SRobert Watson 	endauevent();
772ca0716f5SRobert Watson 	free(ev.ae_name);
773ca0716f5SRobert Watson 	free(ev.ae_desc);
774ca0716f5SRobert Watson 	if (ctr == 0)
775ca0716f5SRobert Watson 		syslog(LOG_ERR, "No events to class mappings registered.");
776ca0716f5SRobert Watson 	else
777506764c6SRobert Watson 		syslog(LOG_DEBUG, "Registered %d event to class mappings.",
778ca0716f5SRobert Watson 		    ctr);
779ca0716f5SRobert Watson 
780ca0716f5SRobert Watson 	/*
781ca0716f5SRobert Watson 	 * Get the non-attributable event string and set the kernel mask from
782ca0716f5SRobert Watson 	 * that.
783ca0716f5SRobert Watson 	 */
784ca0716f5SRobert Watson 	if ((getacna(naeventstr, NA_EVENT_STR_SIZE) == 0) &&
785ca0716f5SRobert Watson 	    (getauditflagsbin(naeventstr, &aumask) == 0)) {
786ca0716f5SRobert Watson 		if (auditon(A_SETKMASK, &aumask, sizeof(au_mask_t)))
787ca0716f5SRobert Watson 			syslog(LOG_ERR,
788ca0716f5SRobert Watson 			    "Failed to register non-attributable event mask.");
789ca0716f5SRobert Watson 		else
790506764c6SRobert Watson 			syslog(LOG_DEBUG,
791ca0716f5SRobert Watson 			    "Registered non-attributable event mask.");
792ca0716f5SRobert Watson 	} else
793ca0716f5SRobert Watson 		syslog(LOG_ERR,
794ca0716f5SRobert Watson 		    "Failed to obtain non-attributable event mask.");
795ca0716f5SRobert Watson 
796ca0716f5SRobert Watson 	/*
797bb97b418SRobert Watson 	 * If a policy is configured in audit_control(5), implement the
798bb97b418SRobert Watson 	 * policy.  However, if one isn't defined, set AUDIT_CNT to avoid
799bb97b418SRobert Watson 	 * leaving the system in a fragile state.
800ca0716f5SRobert Watson 	 */
801bb97b418SRobert Watson 	if ((getacpol(polstr, POL_STR_SIZE) == 0) &&
802bb97b418SRobert Watson 	    (au_strtopol(polstr, &policy) == 0)) {
803bb97b418SRobert Watson 		if (auditon(A_SETPOLICY, &policy, sizeof(policy)))
804bb97b418SRobert Watson 			syslog(LOG_ERR, "Failed to set audit policy: %m");
805bb97b418SRobert Watson 	} else {
806bb97b418SRobert Watson 		syslog(LOG_ERR, "Failed to obtain policy flags: %m");
807bb97b418SRobert Watson 		policy = AUDIT_CNT;
808bb97b418SRobert Watson 		if (auditon(A_SETPOLICY, &policy, sizeof(policy)))
809bb97b418SRobert Watson 			syslog(LOG_ERR,
810bb97b418SRobert Watson 			    "Failed to set default audit policy: %m");
811bb97b418SRobert Watson 	}
812ca0716f5SRobert Watson 
8134bd0c025SRobert Watson 	/*
8144bd0c025SRobert Watson 	 * Set trail rotation size.
8154bd0c025SRobert Watson 	 */
8164bd0c025SRobert Watson 	if (getacfilesz(&filesz) == 0) {
8174bd0c025SRobert Watson 		bzero(&au_fstat, sizeof(au_fstat));
8184bd0c025SRobert Watson 		au_fstat.af_filesz = filesz;
8194bd0c025SRobert Watson 		if (auditon(A_SETFSIZE, &au_fstat, sizeof(au_fstat)) < 0)
8204bd0c025SRobert Watson 			syslog(LOG_ERR, "Failed to set filesz: %m");
8214bd0c025SRobert Watson 	} else
8224bd0c025SRobert Watson 		syslog(LOG_ERR, "Failed to obtain filesz: %m");
8234bd0c025SRobert Watson 
824ca0716f5SRobert Watson 	return (0);
825ca0716f5SRobert Watson }
826ca0716f5SRobert Watson 
827ca0716f5SRobert Watson static void
828ca0716f5SRobert Watson setup(void)
829ca0716f5SRobert Watson {
830fdb4472cSRobert Watson 	struct auditinfo ai;
831506764c6SRobert Watson 	auditinfo_t auinfo;
832ca0716f5SRobert Watson 	int aufd;
833ca0716f5SRobert Watson 	token_t *tok;
834ca0716f5SRobert Watson 
835ca0716f5SRobert Watson 	if ((triggerfd = open(AUDIT_TRIGGER_FILE, O_RDONLY, 0)) < 0) {
836506764c6SRobert Watson 		syslog(LOG_ERR, "Error opening trigger file");
837506764c6SRobert Watson 		fail_exit();
838506764c6SRobert Watson 	}
839506764c6SRobert Watson 
840506764c6SRobert Watson 	/*
841506764c6SRobert Watson 	 * To provide event feedback cycles and avoid auditd becoming
842506764c6SRobert Watson 	 * stalled if auditing is suspended, auditd and its children run
843506764c6SRobert Watson 	 * without their events being audited.  We allow the uid, tid, and
844506764c6SRobert Watson 	 * mask fields to be implicitly set to zero, but do set the pid.  We
845506764c6SRobert Watson 	 * run this after opening the trigger device to avoid configuring
846506764c6SRobert Watson 	 * audit state without audit present in the system.
847506764c6SRobert Watson 	 *
848506764c6SRobert Watson 	 * XXXRW: Is there more to it than this?
849506764c6SRobert Watson 	 */
850506764c6SRobert Watson 	bzero(&auinfo, sizeof(auinfo));
851506764c6SRobert Watson 	auinfo.ai_asid = getpid();
852506764c6SRobert Watson 	if (setaudit(&auinfo) == -1) {
853506764c6SRobert Watson 		syslog(LOG_ERR, "Error setting audit stat");
854ca0716f5SRobert Watson 		fail_exit();
855ca0716f5SRobert Watson 	}
856ca0716f5SRobert Watson 
857ca0716f5SRobert Watson 	TAILQ_INIT(&dir_q);
858ca0716f5SRobert Watson 	if (read_control_file() == -1) {
859506764c6SRobert Watson 		syslog(LOG_ERR, "Error reading control file");
860ca0716f5SRobert Watson 		fail_exit();
861ca0716f5SRobert Watson 	}
862ca0716f5SRobert Watson 
863ca0716f5SRobert Watson 	/* Generate an audit record. */
864ca0716f5SRobert Watson 	if ((aufd = au_open()) == -1)
865506764c6SRobert Watson 		syslog(LOG_ERR, "Could not create audit startup event.");
866ca0716f5SRobert Watson 	else {
867fdb4472cSRobert Watson 		/*
868fdb4472cSRobert Watson 		 * XXXCSJP Perhaps we wan't more robust audit records for
869fdb4472cSRobert Watson 		 * audit start up and shutdown. This might include capturing
870fdb4472cSRobert Watson 		 * failures to initialize the audit subsystem?
871fdb4472cSRobert Watson 		 */
872fdb4472cSRobert Watson 		bzero(&ai, sizeof(ai));
873fdb4472cSRobert Watson 		if ((tok = au_to_subject32(getuid(), geteuid(), getegid(),
874fdb4472cSRobert Watson 		    getuid(), getgid(), getpid(), getpid(), &ai.ai_termid))
875fdb4472cSRobert Watson 		    != NULL)
876fdb4472cSRobert Watson 			au_write(aufd, tok);
877ca0716f5SRobert Watson 		if ((tok = au_to_text("auditd::Audit startup")) != NULL)
878ca0716f5SRobert Watson 			au_write(aufd, tok);
879fdb4472cSRobert Watson 		if ((tok = au_to_return32(0, 0)) != NULL)
880fdb4472cSRobert Watson 			au_write(aufd, tok);
881ca0716f5SRobert Watson 		if (au_close(aufd, 1, AUE_audit_startup) == -1)
882ca0716f5SRobert Watson 			syslog(LOG_ERR,
883506764c6SRobert Watson 			    "Could not close audit startup event.");
884ca0716f5SRobert Watson 	}
885ca0716f5SRobert Watson 
886ca0716f5SRobert Watson 	if (config_audit_controls() == 0)
887506764c6SRobert Watson 		syslog(LOG_INFO, "Audit controls init successful");
888ca0716f5SRobert Watson 	else
889506764c6SRobert Watson 		syslog(LOG_ERR, "Audit controls init failed");
890ca0716f5SRobert Watson }
891ca0716f5SRobert Watson 
892ca0716f5SRobert Watson int
893ca0716f5SRobert Watson main(int argc, char **argv)
894ca0716f5SRobert Watson {
89523bf6e20SRobert Watson 	int ch;
896ca0716f5SRobert Watson 	int debug = 0;
897ca0716f5SRobert Watson 	int rc;
898ca0716f5SRobert Watson 
899ca0716f5SRobert Watson 	while ((ch = getopt(argc, argv, "dhs")) != -1) {
900ca0716f5SRobert Watson 		switch(ch) {
901ca0716f5SRobert Watson 		case 'd':
902ca0716f5SRobert Watson 			/* Debug option. */
903ca0716f5SRobert Watson 			debug = 1;
904ca0716f5SRobert Watson 			break;
905ca0716f5SRobert Watson 
906ca0716f5SRobert Watson 		case '?':
907ca0716f5SRobert Watson 		default:
908ca0716f5SRobert Watson 			(void)fprintf(stderr,
909bb97b418SRobert Watson 			    "usage: auditd [-d] \n");
910ca0716f5SRobert Watson 			exit(1);
911ca0716f5SRobert Watson 		}
912ca0716f5SRobert Watson 	}
913ca0716f5SRobert Watson 
9143b97a967SRobert Watson #ifdef LOG_SECURITY
915ca0716f5SRobert Watson 	openlog("auditd", LOG_CONS | LOG_PID, LOG_SECURITY);
9163b97a967SRobert Watson #else
9173b97a967SRobert Watson 	openlog("auditd", LOG_CONS | LOG_PID, LOG_AUTH);
9183b97a967SRobert Watson #endif
919506764c6SRobert Watson 	syslog(LOG_INFO, "starting...");
920ca0716f5SRobert Watson 
921ca0716f5SRobert Watson 	if (debug == 0 && daemon(0, 0) == -1) {
922506764c6SRobert Watson 		syslog(LOG_ERR, "Failed to daemonize");
923ca0716f5SRobert Watson 		exit(1);
924ca0716f5SRobert Watson 	}
925ca0716f5SRobert Watson 
926ca0716f5SRobert Watson 	if (register_daemon() == -1) {
927506764c6SRobert Watson 		syslog(LOG_ERR, "Could not register as daemon");
928ca0716f5SRobert Watson 		exit(1);
929ca0716f5SRobert Watson 	}
930ca0716f5SRobert Watson 
931ca0716f5SRobert Watson 	setup();
932ca0716f5SRobert Watson 
933506764c6SRobert Watson 	rc = wait_for_events();
934506764c6SRobert Watson 	syslog(LOG_INFO, "auditd exiting.");
935ca0716f5SRobert Watson 
936ca0716f5SRobert Watson 	exit(rc);
937ca0716f5SRobert Watson }
938