2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
22 * @APPLE_LICENSE_HEADER_END@
25 #include <mach/port.h>
26 #include <mach/mach_error.h>
27 #include <mach/mach_traps.h>
28 #include <mach/mach.h>
29 #include <mach/host_special_ports.h>
31 #include <sys/types.h>
33 #include <sys/queue.h>
48 #include <bsm/audit.h>
49 #include <bsm/audit_uevents.h>
50 #include <bsm/libbsm.h>
53 #include "auditd_control_server.h"
54 #include "audit_triggers_server.h"
55 #define NA_EVENT_STR_SIZE 25
57 static int ret
, minval
;
58 static char *lastfile
= NULL
;
60 static int allhardcount
= 0;
62 mach_port_t bp
= MACH_PORT_NULL
;
63 mach_port_t control_port
= MACH_PORT_NULL
;
64 mach_port_t signal_port
= MACH_PORT_NULL
;
65 mach_port_t port_set
= MACH_PORT_NULL
;
67 #ifndef __BSM_INTERNAL_NOTIFY_KEY
68 #define __BSM_INTERNAL_NOTIFY_KEY "com.apple.audit.change"
69 #endif /* __BSM_INTERNAL_NOTIFY_KEY */
71 TAILQ_HEAD(, dir_ent
) dir_q
;
74 /* Error starting auditd */
82 * Free our local list of directory names
86 struct dir_ent
*dirent
;
88 while ((dirent
= TAILQ_FIRST(&dir_q
))) {
89 TAILQ_REMOVE(&dir_q
, dirent
, dirs
);
90 free(dirent
->dirname
);
96 * generate the timestamp string
98 int getTSstr(char *buf
, int len
)
104 if(gettimeofday(&ts
, &tzp
) != 0) {
107 tt
= (time_t)ts
.tv_sec
;
108 if(!strftime(buf
, len
, "%Y%m%d%H%M%S", gmtime(&tt
))) {
116 * Concat the directory name to the given file name
117 * XXX We should affix the hostname also
119 char *affixdir(char *name
, struct dir_ent
*dirent
)
123 const char *sep
= "/";
125 curdir
= dirent
->dirname
;
126 syslog(LOG_INFO
, "dir = %s\n", dirent
->dirname
);
128 fn
= (char *) malloc (strlen(curdir
) + strlen(sep
)
129 + (2 * POSTFIX_LEN
) + 1);
140 /* Close the previous audit trail file */
141 int close_lastfile(char *TS
)
146 if(lastfile
!= NULL
) {
147 oldname
= (char *)malloc(strlen(lastfile
) + 1);
148 if(oldname
== NULL
) {
151 strcpy(oldname
, lastfile
);
153 /* rename the last file -- append timestamp */
155 if((ptr
= strstr(lastfile
, NOT_TERMINATED
)) != NULL
) {
158 if(rename(oldname
, lastfile
) != 0) {
159 syslog(LOG_ERR
, "Could not rename %s to %s \n",
163 syslog(LOG_INFO
, "renamed %s to %s \n",
178 * Create the new file name, swap with existing audit file
180 int swap_audit_file()
182 char timestr
[2 * POSTFIX_LEN
];
184 char TS
[POSTFIX_LEN
];
185 struct dir_ent
*dirent
;
187 if(getTSstr(TS
, POSTFIX_LEN
) != 0) {
192 strcat(timestr
, NOT_TERMINATED
);
194 /* try until we succeed */
195 while((dirent
= TAILQ_FIRST(&dir_q
))) {
196 if((fn
= affixdir(timestr
, dirent
)) == NULL
) {
200 syslog(LOG_INFO
, "New audit file is %s\n", fn
);
201 if (open(fn
, O_RDONLY
| O_CREAT
, S_IRUSR
| S_IRGRP
) < 0) {
204 else if (auditctl(fn
) != 0) {
205 syslog(LOG_ERR
, "auditctl failed! : %s\n",
215 /* Tell the administrator about lack of permissions for dirent */
216 audit_warn_getacdir(dirent
->dirname
);
218 /* Try again with a different directory */
219 TAILQ_REMOVE(&dir_q
, dirent
, dirs
);
220 free(dirent
->dirname
);
227 * Read the audit_control file contents
229 int read_control_file()
231 char cur_dir
[MAX_DIR_SIZE
];
232 struct dir_ent
*dirent
;
235 /* Clear old values */
237 endac(); // force a re-read of the file the next time
239 /* Post that the audit config changed */
240 notify_post(__BSM_INTERNAL_NOTIFY_KEY
);
242 /* Read the list of directories into a local linked list */
243 /* XXX We should use the reentrant interfaces once they are available */
244 while(getacdir(cur_dir
, MAX_DIR_SIZE
) >= 0) {
245 dirent
= (struct dir_ent
*) malloc (sizeof(struct dir_ent
));
251 dirent
->dirname
= (char *) malloc (MAX_DIR_SIZE
);
252 if(dirent
->dirname
== NULL
) {
257 strcpy(dirent
->dirname
, cur_dir
);
258 TAILQ_INSERT_TAIL(&dir_q
, dirent
, dirs
);
263 if(swap_audit_file() == -1) {
264 syslog(LOG_ERR
, "Could not swap audit file\n");
266 * XXX Faulty directory listing? - user should be given
267 * XXX an opportunity to change the audit_control file
268 * XXX switch to a reduced mode of auditing?
274 * XXX There are synchronization problems here
275 * XXX what should we do if a trigger for the earlier limit
276 * XXX is generated here?
278 if(0 == (ret
= getacmin(&minval
))) {
280 syslog(LOG_INFO
, "min free = %d\n", minval
);
282 if (auditon(A_GETQCTRL
, &qctrl
, sizeof(qctrl
)) != 0) {
284 "could not get audit queue settings\n");
287 qctrl
.aq_minfree
= minval
;
288 if (auditon(A_SETQCTRL
, &qctrl
, sizeof(qctrl
)) != 0) {
290 "could not set audit queue settings\n");
299 * Close all log files, control files, and tell the audit system.
304 char TS
[POSTFIX_LEN
];
308 /* Generate an audit record */
309 if((aufd
= au_open()) == -1) {
310 syslog(LOG_ERR
, "Could not create audit shutdown event.\n");
313 if((tok
= au_to_text("auditd::Audit shutdown")) != NULL
) {
317 if(au_close(aufd
, 1, AUE_audit_shutdown
) == -1) {
318 syslog(LOG_ERR
, "Could not close audit shutdown event.\n");
323 err_ret
= auditctl(NULL
);
325 syslog(LOG_ERR
, "auditctl failed! : %s\n",
329 if(getTSstr(TS
, POSTFIX_LEN
) == 0) {
336 if((remove(AUDITD_PIDFILE
) == -1) || err_ret
) {
337 syslog(LOG_ERR
, "Could not unregister\n");
338 audit_warn_postsigterm();
342 syslog(LOG_INFO
, "Finished.\n");
347 * When we get a signal, we are often not at a clean point.
348 * So, little can be done in the signal handler itself. Instead,
349 * we send a message to the main servicing loop to do proper
350 * handling from a non-signal-handler context.
353 relay_signal(int signal
)
355 mach_msg_empty_send_t msg
;
357 msg
.header
.msgh_id
= signal
;
358 msg
.header
.msgh_remote_port
= signal_port
;
359 msg
.header
.msgh_local_port
= MACH_PORT_NULL
;
360 msg
.header
.msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND
, 0);
361 mach_msg(&(msg
.header
), MACH_SEND_MSG
|MACH_SEND_TIMEOUT
, sizeof(msg
),
362 0, MACH_PORT_NULL
, MACH_MSG_TIMEOUT_NONE
, MACH_PORT_NULL
);
365 /* registering the daemon */
366 int register_daemon()
372 /* Set up the signal hander */
373 if (signal(SIGTERM
, relay_signal
) == SIG_ERR
) {
376 if (signal(SIGCHLD
, relay_signal
) == SIG_ERR
) {
380 if ((pidfile
= fopen(AUDITD_PIDFILE
, "a")) == NULL
) {
381 audit_warn_tmpfile();
385 /* attempt to lock the pid file; if a lock is present, exit */
386 fd
= fileno(pidfile
);
387 if(flock(fd
, LOCK_EX
| LOCK_NB
) < 0) {
388 syslog(LOG_ERR
, "PID file is locked (is another auditd running?).\n");
395 if(fprintf(pidfile
, "%u\n", pid
) < 0) {
396 /* should not start the daemon */
405 * React to input from the audit tool
407 kern_return_t
auditd_control(auditd_port
, flags
)
408 mach_port_t auditd_port
;
416 /* create a new file and swap with the one being used in kernel */
417 if(swap_audit_file() == -1) {
418 syslog(LOG_ERR
, "Error swapping audit file\n");
423 if(read_control_file() == -1) {
424 syslog(LOG_ERR
, "Error in audit control file\n");
429 err_ret
= close_all();
441 * Suppress duplicate messages within a 30 second interval.
442 * This should be enough to time to rotate log files without
443 * thrashing from soft warnings generated before the log is
446 #define DUPLICATE_INTERVAL 30
448 * Implementation of the audit_triggers() MIG routine.
450 kern_return_t
audit_triggers(audit_port
, flags
)
451 mach_port_t audit_port
;
454 static int last_flags
;
455 static time_t last_time
;
456 struct dir_ent
*dirent
;
459 * Suppres duplicate messages from the kernel within the specified interval
465 if(gettimeofday(&ts
, &tzp
) == 0) {
466 tt
= (time_t)ts
.tv_sec
;
467 if ((flags
== last_flags
) && (tt
< (last_time
+ DUPLICATE_INTERVAL
))) {
475 "audit_triggers() called within auditd with flags = %d\n",
478 * XXX Message processing is done here
480 dirent
= TAILQ_FIRST(&dir_q
);
481 if(flags
== AUDIT_TRIGGER_LOW_SPACE
) {
482 if(dirent
&& (dirent
->softlim
!= 1)) {
483 TAILQ_REMOVE(&dir_q
, dirent
, dirs
);
484 /* add this node to the end of the list */
485 TAILQ_INSERT_TAIL(&dir_q
, dirent
, dirs
);
486 audit_warn_soft(dirent
->dirname
);
489 if (TAILQ_NEXT(TAILQ_FIRST(&dir_q
), dirs
) != NULL
&& swap_audit_file() == -1) {
490 syslog(LOG_ERR
, "Error swapping audit file\n");
494 * check if the next dir has already reached its
497 dirent
= TAILQ_FIRST(&dir_q
);
498 if(dirent
->softlim
== 1) {
499 /* all dirs have reached their soft limit */
500 audit_warn_allsoft();
505 * Continue auditing to the current file
506 * Also generate an allsoft warning
507 * XXX do we want to do this ?
509 audit_warn_allsoft();
512 else if (flags
== AUDIT_TRIGGER_FILE_FULL
) {
514 /* delete current dir, go on to next */
515 TAILQ_REMOVE(&dir_q
, dirent
, dirs
);
516 audit_warn_hard(dirent
->dirname
);
517 free(dirent
->dirname
);
520 if(swap_audit_file() == -1) {
521 syslog(LOG_ERR
, "Error swapping audit file in response to AUDIT_TRIGGER_FILE_FULL message\n");
523 /* Nowhere to write to */
524 audit_warn_allhard(++allhardcount
);
539 while ((child
= waitpid(-1, &wstatus
, WNOHANG
)) > 0) {
541 syslog(LOG_INFO
, "warn process [pid=%d] %s %d.\n", child
,
542 ((WIFEXITED(wstatus
)) ?
543 "exited with non-zero status" :
544 "exited as a result of signal"),
545 ((WIFEXITED(wstatus
)) ?
546 WEXITSTATUS(wstatus
) :
555 boolean_t
auditd_combined_server(
556 mach_msg_header_t
*InHeadP
,
557 mach_msg_header_t
*OutHeadP
)
559 mach_port_t local_port
= InHeadP
->msgh_local_port
;
561 if (local_port
== signal_port
) {
562 int signo
= InHeadP
->msgh_id
;
565 if (SIGTERM
== signo
) {
568 } else if (SIGCHLD
== signo
) {
572 syslog(LOG_INFO
, "Recevied signal %d.\n", signo
);
575 } else if (local_port
== control_port
) {
578 result
= audit_triggers_server(InHeadP
, OutHeadP
);
580 result
= auditd_control_server(InHeadP
, OutHeadP
);
583 syslog(LOG_INFO
, "Recevied msg on bad port 0x%x.\n", local_port
);
587 void wait_on_audit_trigger(port_set
)
588 mach_port_t port_set
;
590 kern_return_t result
;
591 result
= mach_msg_server(auditd_combined_server
, 4096, port_set
, MACH_MSG_OPTION_NONE
);
592 syslog(LOG_ERR
, "abnormal exit\n");
596 * Configure the audit controls in the kernel: the event to class mapping,
597 * kernel preselection mask, etc.
599 int config_audit_controls(long flags
)
602 au_evclass_map_t evc_map
;
605 char naeventstr
[NA_EVENT_STR_SIZE
];
607 /* Process the audit event file, obtaining a class mapping for each
608 * event, and send that mapping into the kernel.
609 * XXX There's a risk here that the BSM library will return NULL
610 * for an event when it can't properly map it to a class. In that
611 * case, we will not process any events beyond the one that failed,
612 * but should. We need a way to get a count of the events.
616 while((ev
= getauevent()) != NULL
) {
617 evc_map
.ec_number
= ev
->ae_number
;
618 evc_map
.ec_class
= ev
->ae_class
;
619 if (auditon(A_SETCLASS
, &evc_map
, sizeof(au_evclass_map_t
)) != 0) {
621 "Failed to register class mapping for event %s",
632 syslog(LOG_ERR
, "No events to class mappings registered.");
634 syslog(LOG_INFO
, "Registered %d event to class mappings.", ctr
);
636 /* Get the non-attributable event string and set the kernel mask
639 if ((getacna(naeventstr
, NA_EVENT_STR_SIZE
) == 0)
640 && ( getauditflagsbin(naeventstr
, &aumask
) == 0)) {
642 if (auditon(A_SETKMASK
, &aumask
, sizeof(au_mask_t
))){
644 "Failed to register non-attributable event mask.");
646 syslog(LOG_INFO
, "Registered non-attributable event mask.");
650 syslog(LOG_ERR
,"Failed to obtain non-attributable event mask.");
654 * Set the audit policy flags based on passed in parameter values.
656 if (auditon(A_SETPOLICY
, &flags
, sizeof(flags
))) {
658 "Failed to set audit policy.");
664 void setup(long flags
)
666 mach_msg_type_name_t poly
;
670 /* Allocate a port set */
671 if (mach_port_allocate(mach_task_self(),
672 MACH_PORT_RIGHT_PORT_SET
,
673 &port_set
) != KERN_SUCCESS
) {
674 syslog(LOG_ERR
, "allocation of port set failed\n");
678 /* Allocate a signal reflection port */
679 if (mach_port_allocate(mach_task_self(),
680 MACH_PORT_RIGHT_RECEIVE
,
681 &signal_port
) != KERN_SUCCESS
||
682 mach_port_move_member(mach_task_self(),
684 port_set
) != KERN_SUCCESS
) {
685 syslog(LOG_ERR
, "allocation of signal port failed\n");
689 /* Allocate a trigger port */
690 if (mach_port_allocate(mach_task_self(),
691 MACH_PORT_RIGHT_RECEIVE
,
692 &control_port
) != KERN_SUCCESS
||
693 mach_port_move_member(mach_task_self(),
695 port_set
) != KERN_SUCCESS
) {
696 syslog(LOG_ERR
, "allocation of trigger port failed\n");
700 /* create a send right on our trigger port */
701 mach_port_extract_right(mach_task_self(), control_port
,
702 MACH_MSG_TYPE_MAKE_SEND
, &control_port
, &poly
);
706 /* register the trigger port with the kernel */
707 if(host_set_audit_control_port(mach_host_self(), control_port
) != KERN_SUCCESS
) {
708 syslog(LOG_ERR
, "Cannot set Mach control port\n");
712 syslog(LOG_ERR
, "Mach control port registered\n");
715 if(read_control_file() == -1) {
716 syslog(LOG_ERR
, "Error reading control file\n");
720 /* Generate an audit record */
721 if((aufd
= au_open()) == -1) {
722 syslog(LOG_ERR
, "Could not create audit startup event.\n");
725 if((tok
= au_to_text("auditd::Audit startup")) != NULL
) {
729 if(au_close(aufd
, 1, AUE_audit_startup
) == -1) {
730 syslog(LOG_ERR
, "Could not close audit startup event.\n");
734 if (config_audit_controls(flags
) == 0)
735 syslog(LOG_INFO
, "Initialization successful\n");
737 syslog(LOG_INFO
, "Initialization failed\n");
741 int main(int argc
, char **argv
)
744 long flags
= AUDIT_CNT
;
747 while ((ch
= getopt(argc
, argv
, "dhs")) != -1) {
755 /* fail-stop option */
757 flags
&= ~(AUDIT_CNT
);
760 /* halt-stop option */
767 (void)fprintf(stderr
,
768 "usage: auditd [-h | -s]\n");
773 openlog("auditd", LOG_CONS
| LOG_PID
, LOG_DAEMON
);
774 syslog(LOG_INFO
, "starting...\n");
776 if (debug
== 0 && daemon(0, 0) == -1) {
777 syslog(LOG_ERR
, "Failed to daemonize\n");
781 if(register_daemon() == -1) {
782 syslog(LOG_ERR
, "Could not register as daemon\n");
787 wait_on_audit_trigger(port_set
);
788 syslog(LOG_INFO
, "exiting.\n");