2 * Copyright (c) 2004 Apple Computer, Inc.
5 * @APPLE_BSD_LICENSE_HEADER_START@
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
17 * its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 * @APPLE_BSD_LICENSE_HEADER_END@
33 * $P4: //depot/projects/trustedbsd/openbsm/bin/auditd/auditd.c#18 $
36 #include <sys/types.h>
37 #include <sys/dirent.h>
39 #include <sys/queue.h>
43 #include <bsm/audit.h>
44 #include <bsm/audit_uevents.h>
45 #include <bsm/libbsm.h>
61 #define NA_EVENT_STR_SIZE 25
63 static int ret, minval;
64 static char *lastfile = NULL;
65 static int allhardcount = 0;
66 static int triggerfd = 0;
67 static int sigchlds, sigchlds_handled;
68 static int sighups, sighups_handled;
69 static int sigterms, sigterms_handled;
70 static long global_flags;
72 static TAILQ_HEAD(, dir_ent) dir_q;
74 static int config_audit_controls(void);
77 * Error starting auditd
88 * Free our local list of directory names.
93 struct dir_ent *dirent;
95 while ((dirent = TAILQ_FIRST(&dir_q))) {
96 TAILQ_REMOVE(&dir_q, dirent, dirs);
97 free(dirent->dirname);
103 * Generate the timestamp string.
106 getTSstr(char *buf, int len)
112 if (gettimeofday(&ts, &tzp) != 0)
114 tt = (time_t)ts.tv_sec;
115 if (!strftime(buf, len, "%Y%m%d%H%M%S", gmtime(&tt)))
121 * Concat the directory name to the given file name.
122 * XXX We should affix the hostname also
125 affixdir(char *name, struct dir_ent *dirent)
129 const char *sep = "/";
131 curdir = dirent->dirname;
132 syslog(LOG_DEBUG, "dir = %s", dirent->dirname);
134 fn = malloc(strlen(curdir) + strlen(sep) + (2 * POSTFIX_LEN) + 1);
144 * Close the previous audit trail file.
147 close_lastfile(char *TS)
152 if (lastfile != NULL) {
153 oldname = (char *)malloc(strlen(lastfile) + 1);
156 strcpy(oldname, lastfile);
158 /* Rename the last file -- append timestamp. */
159 if ((ptr = strstr(lastfile, NOT_TERMINATED)) != NULL) {
162 if (rename(oldname, lastfile) != 0)
163 syslog(LOG_ERR, "Could not rename %s to %s",
166 syslog(LOG_INFO, "renamed %s to %s",
177 * Create the new audit file with appropriate permissions and ownership. Try
178 * to clean up if something goes wrong.
181 #ifdef AUDIT_REVIEW_GROUP
182 open_trail(const char *fname, uid_t uid, gid_t gid)
184 open_trail(const char *fname)
189 fd = open(fname, O_RDONLY | O_CREAT, S_IRUSR | S_IRGRP);
192 #ifdef AUDIT_REVIEW_GROUP
193 if (fchown(fd, uid, gid) < 0) {
205 * Create the new file name, swap with existing audit file.
208 swap_audit_file(void)
210 char timestr[2 * POSTFIX_LEN];
212 char TS[POSTFIX_LEN];
213 struct dir_ent *dirent;
214 #ifdef AUDIT_REVIEW_GROUP
221 if (getTSstr(TS, POSTFIX_LEN) != 0)
225 strcat(timestr, NOT_TERMINATED);
227 #ifdef AUDIT_REVIEW_GROUP
229 * XXXRW: Currently, this code falls back to the daemon gid, which is
230 * likely the wheel group. Is there a better way to deal with this?
232 grp = getgrnam(AUDIT_REVIEW_GROUP);
235 "Audit review group '%s' not available, using daemon gid",
243 /* Try until we succeed. */
244 while ((dirent = TAILQ_FIRST(&dir_q))) {
245 if ((fn = affixdir(timestr, dirent)) == NULL) {
246 syslog(LOG_INFO, "Failed to swap log at time %s",
252 * Create and open the file; then close and pass to the
253 * kernel if all went well.
255 syslog(LOG_INFO, "New audit file is %s", fn);
256 #ifdef AUDIT_REVIEW_GROUP
257 fd = open_trail(fn, uid, gid);
262 warn("open(%s)", fn);
264 error = auditctl(fn);
267 "auditctl failed setting log file! : %s",
280 * Tell the administrator about lack of permissions for dir.
282 audit_warn_getacdir(dirent->dirname);
284 /* Try again with a different directory. */
285 TAILQ_REMOVE(&dir_q, dirent, dirs);
286 free(dirent->dirname);
289 syslog(LOG_ERR, "Log directories exhausted\n");
294 * Read the audit_control file contents.
297 read_control_file(void)
299 char cur_dir[MAXNAMLEN];
300 struct dir_ent *dirent;
304 * Clear old values. Force a re-read of the file the next time.
310 * Read the list of directories into a local linked list.
312 * XXX We should use the reentrant interfaces once they are
315 while (getacdir(cur_dir, MAXNAMLEN) >= 0) {
316 dirent = (struct dir_ent *) malloc(sizeof(struct dir_ent));
320 dirent->dirname = (char *) malloc(MAXNAMLEN);
321 if (dirent->dirname == NULL) {
325 strcpy(dirent->dirname, cur_dir);
326 TAILQ_INSERT_TAIL(&dir_q, dirent, dirs);
330 if (swap_audit_file() == -1) {
331 syslog(LOG_ERR, "Could not swap audit file");
333 * XXX Faulty directory listing? - user should be given
334 * XXX an opportunity to change the audit_control file
335 * XXX switch to a reduced mode of auditing?
341 * XXX There are synchronization problems here
342 * XXX what should we do if a trigger for the earlier limit
343 * XXX is generated here?
345 if (0 == (ret = getacmin(&minval))) {
346 syslog(LOG_DEBUG, "min free = %d\n", minval);
347 if (auditon(A_GETQCTRL, &qctrl, sizeof(qctrl)) != 0) {
349 "could not get audit queue settings");
352 qctrl.aq_minfree = minval;
353 if (auditon(A_SETQCTRL, &qctrl, sizeof(qctrl)) != 0) {
355 "could not set audit queue settings");
364 * Close all log files, control files, and tell the audit system.
371 char TS[POSTFIX_LEN];
376 /* Generate an audit record. */
377 if ((aufd = au_open()) == -1)
378 syslog(LOG_ERR, "Could not create audit shutdown event.");
380 if ((tok = au_to_text("auditd::Audit shutdown")) != NULL)
383 * XXX we need to implement extended subject tokens so we can
384 * effectively represent terminal lines with this token type.
386 bzero(&ai, sizeof(ai));
387 if ((tok = au_to_subject32(getuid(), geteuid(), getegid(),
388 getuid(), getgid(), getpid(), getpid(), &ai.ai_termid))
391 if ((tok = au_to_return32(0, 0)) != NULL)
393 if (au_close(aufd, 1, AUE_audit_shutdown) == -1)
395 "Could not close audit shutdown event.");
398 /* Flush contents. */
400 err_ret = auditon(A_SETCOND, &cond, sizeof(cond));
402 syslog(LOG_ERR, "Disabling audit failed! : %s",
406 if (getTSstr(TS, POSTFIX_LEN) == 0)
408 if (lastfile != NULL)
412 if ((remove(AUDITD_PIDFILE) == -1) || err_ret) {
413 syslog(LOG_ERR, "Could not unregister");
414 audit_warn_postsigterm();
419 if (close(triggerfd) != 0)
420 syslog(LOG_ERR, "Error closing control file");
421 syslog(LOG_INFO, "Finished");
426 * When we get a signal, we are often not at a clean point. So, little can
427 * be done in the signal handler itself. Instead, we send a message to the
428 * main servicing loop to do proper handling from a non-signal-handler
432 relay_signal(int signal)
435 if (signal == SIGHUP)
437 if (signal == SIGTERM)
439 if (signal == SIGCHLD)
444 * Registering the daemon.
447 register_daemon(void)
453 /* Set up the signal hander. */
454 if (signal(SIGTERM, relay_signal) == SIG_ERR) {
456 "Could not set signal handler for SIGTERM");
459 if (signal(SIGCHLD, relay_signal) == SIG_ERR) {
461 "Could not set signal handler for SIGCHLD");
464 if (signal(SIGHUP, relay_signal) == SIG_ERR) {
466 "Could not set signal handler for SIGHUP");
470 if ((pidfile = fopen(AUDITD_PIDFILE, "a")) == NULL) {
471 syslog(LOG_ERR, "Could not open PID file");
472 audit_warn_tmpfile();
476 /* Attempt to lock the pid file; if a lock is present, exit. */
477 fd = fileno(pidfile);
478 if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
480 "PID file is locked (is another auditd running?).");
487 if (fprintf(pidfile, "%u\n", pid) < 0) {
488 /* Should not start the daemon. */
497 * Suppress duplicate messages within a 30 second interval. This should be
498 * enough to time to rotate log files without thrashing from soft warnings
499 * generated before the log is actually rotated.
501 #define DUPLICATE_INTERVAL 30
503 handle_audit_trigger(int trigger)
505 static int last_trigger;
506 static time_t last_time;
507 struct dir_ent *dirent;
510 * Suppres duplicate messages from the kernel within the specified
517 if (gettimeofday(&ts, &tzp) == 0) {
518 tt = (time_t)ts.tv_sec;
519 if ((trigger == last_trigger) &&
520 (tt < (last_time + DUPLICATE_INTERVAL)))
522 last_trigger = trigger;
527 * Message processing is done here.
529 dirent = TAILQ_FIRST(&dir_q);
532 case AUDIT_TRIGGER_LOW_SPACE:
533 syslog(LOG_INFO, "Got low space trigger");
534 if (dirent && (dirent->softlim != 1)) {
535 TAILQ_REMOVE(&dir_q, dirent, dirs);
536 /* Add this node to the end of the list. */
537 TAILQ_INSERT_TAIL(&dir_q, dirent, dirs);
538 audit_warn_soft(dirent->dirname);
541 if (TAILQ_NEXT(TAILQ_FIRST(&dir_q), dirs) != NULL &&
542 swap_audit_file() == -1)
543 syslog(LOG_ERR, "Error swapping audit file");
546 * Check if the next dir has already reached its soft
549 dirent = TAILQ_FIRST(&dir_q);
550 if (dirent->softlim == 1) {
551 /* All dirs have reached their soft limit. */
552 audit_warn_allsoft();
556 * Continue auditing to the current file. Also
557 * generate an allsoft warning.
558 * XXX do we want to do this ?
560 audit_warn_allsoft();
564 case AUDIT_TRIGGER_NO_SPACE:
565 syslog(LOG_INFO, "Got no space trigger");
567 /* Delete current dir, go on to next. */
568 TAILQ_REMOVE(&dir_q, dirent, dirs);
569 audit_warn_hard(dirent->dirname);
570 free(dirent->dirname);
573 if (swap_audit_file() == -1)
574 syslog(LOG_ERR, "Error swapping audit file");
576 /* We are out of log directories. */
577 audit_warn_allhard(++allhardcount);
580 case AUDIT_TRIGGER_OPEN_NEW:
582 * Create a new file and swap with the one being used in
585 syslog(LOG_INFO, "Got open new trigger");
586 if (swap_audit_file() == -1)
587 syslog(LOG_ERR, "Error swapping audit file");
590 case AUDIT_TRIGGER_READ_FILE:
591 syslog(LOG_INFO, "Got read file trigger");
592 if (read_control_file() == -1)
593 syslog(LOG_ERR, "Error in audit control file");
594 if (config_audit_controls() == -1)
595 syslog(LOG_ERR, "Error setting audit controls");
599 syslog(LOG_ERR, "Got unknown trigger %d", trigger);
608 sighups_handled = sighups;
609 config_audit_controls();
621 while ((child = waitpid(-1, &wstatus, WNOHANG)) > 0) {
624 syslog(LOG_INFO, "warn process [pid=%d] %s %d.", child,
625 ((WIFEXITED(wstatus)) ? "exited with non-zero status" :
626 "exited as a result of signal"),
627 ((WIFEXITED(wstatus)) ? WEXITSTATUS(wstatus) :
636 sigchlds_handled = sigchlds;
641 * Read the control file for triggers/signals and handle appropriately.
644 wait_for_events(void)
647 unsigned int trigger;
650 num = read(triggerfd, &trigger, sizeof(trigger));
651 if ((num == -1) && (errno != EINTR)) {
652 syslog(LOG_ERR, "%s: error %d", __FUNCTION__, errno);
655 if (sigterms != sigterms_handled) {
656 syslog(LOG_DEBUG, "%s: SIGTERM", __FUNCTION__);
659 if (sigchlds != sigchlds_handled) {
660 syslog(LOG_DEBUG, "%s: SIGCHLD", __FUNCTION__);
663 if (sighups != sighups_handled) {
664 syslog(LOG_DEBUG, "%s: SIGHUP", __FUNCTION__);
667 if ((num == -1) && (errno == EINTR))
670 syslog(LOG_ERR, "%s: read EOF", __FUNCTION__);
673 syslog(LOG_DEBUG, "%s: read %d", __FUNCTION__, trigger);
674 if (trigger == AUDIT_TRIGGER_CLOSE_AND_DIE)
677 handle_audit_trigger(trigger);
679 return (close_all());
683 * Configure the audit controls in the kernel: the event to class mapping,
684 * kernel preselection mask, etc.
687 config_audit_controls(void)
689 au_event_ent_t ev, *evp;
690 au_evclass_map_t evc_map;
693 char naeventstr[NA_EVENT_STR_SIZE];
696 * Process the audit event file, obtaining a class mapping for each
697 * event, and send that mapping into the kernel.
698 * XXX There's a risk here that the BSM library will return NULL
699 * for an event when it can't properly map it to a class. In that
700 * case, we will not process any events beyond the one that failed,
701 * but should. We need a way to get a count of the events.
703 ev.ae_name = (char *)malloc(AU_EVENT_NAME_MAX);
704 ev.ae_desc = (char *)malloc(AU_EVENT_DESC_MAX);
705 if ((ev.ae_name == NULL) || (ev.ae_desc == NULL)) {
707 "Memory allocation error when configuring audit controls.");
712 while ((evp = getauevent_r(evp)) != NULL) {
713 evc_map.ec_number = evp->ae_number;
714 evc_map.ec_class = evp->ae_class;
715 if (auditon(A_SETCLASS, &evc_map, sizeof(au_evclass_map_t))
718 "Failed to register class mapping for event %s",
727 syslog(LOG_ERR, "No events to class mappings registered.");
729 syslog(LOG_DEBUG, "Registered %d event to class mappings.",
733 * Get the non-attributable event string and set the kernel mask from
736 if ((getacna(naeventstr, NA_EVENT_STR_SIZE) == 0) &&
737 (getauditflagsbin(naeventstr, &aumask) == 0)) {
738 if (auditon(A_SETKMASK, &aumask, sizeof(au_mask_t)))
740 "Failed to register non-attributable event mask.");
743 "Registered non-attributable event mask.");
746 "Failed to obtain non-attributable event mask.");
749 * Set the audit policy flags based on passed in parameter values.
751 if (auditon(A_SETPOLICY, &global_flags, sizeof(global_flags)))
752 syslog(LOG_ERR, "Failed to set audit policy.");
765 if ((triggerfd = open(AUDIT_TRIGGER_FILE, O_RDONLY, 0)) < 0) {
766 syslog(LOG_ERR, "Error opening trigger file");
771 * To provide event feedback cycles and avoid auditd becoming
772 * stalled if auditing is suspended, auditd and its children run
773 * without their events being audited. We allow the uid, tid, and
774 * mask fields to be implicitly set to zero, but do set the pid. We
775 * run this after opening the trigger device to avoid configuring
776 * audit state without audit present in the system.
778 * XXXRW: Is there more to it than this?
780 bzero(&auinfo, sizeof(auinfo));
781 auinfo.ai_asid = getpid();
782 if (setaudit(&auinfo) == -1) {
783 syslog(LOG_ERR, "Error setting audit stat");
788 if (read_control_file() == -1) {
789 syslog(LOG_ERR, "Error reading control file");
793 /* Generate an audit record. */
794 if ((aufd = au_open()) == -1)
795 syslog(LOG_ERR, "Could not create audit startup event.");
798 * XXXCSJP Perhaps we wan't more robust audit records for
799 * audit start up and shutdown. This might include capturing
800 * failures to initialize the audit subsystem?
802 bzero(&ai, sizeof(ai));
803 if ((tok = au_to_subject32(getuid(), geteuid(), getegid(),
804 getuid(), getgid(), getpid(), getpid(), &ai.ai_termid))
807 if ((tok = au_to_text("auditd::Audit startup")) != NULL)
809 if ((tok = au_to_return32(0, 0)) != NULL)
811 if (au_close(aufd, 1, AUE_audit_startup) == -1)
813 "Could not close audit startup event.");
816 if (config_audit_controls() == 0)
817 syslog(LOG_INFO, "Audit controls init successful");
819 syslog(LOG_ERR, "Audit controls init failed");
823 main(int argc, char **argv)
829 global_flags |= AUDIT_CNT;
830 while ((ch = getopt(argc, argv, "dhs")) != -1) {
838 /* Fail-stop option. */
839 global_flags &= ~(AUDIT_CNT);
843 /* Halt-stop option. */
844 global_flags |= AUDIT_AHLT;
849 (void)fprintf(stderr,
850 "usage: auditd [-h | -s] [-d] \n");
856 openlog("auditd", LOG_CONS | LOG_PID, LOG_SECURITY);
858 openlog("auditd", LOG_CONS | LOG_PID, LOG_AUTH);
860 syslog(LOG_INFO, "starting...");
862 if (debug == 0 && daemon(0, 0) == -1) {
863 syslog(LOG_ERR, "Failed to daemonize");
867 if (register_daemon() == -1) {
868 syslog(LOG_ERR, "Could not register as daemon");
874 rc = wait_for_events();
875 syslog(LOG_INFO, "auditd exiting.");