X-Git-Url: https://git.saurik.com/apple/system_cmds.git/blobdiff_plain/a8daac8f698fa38f1ca3aaa2a3ed6ac20e5128e2..ef8ad44b9b5d9c6d8c0fa5b5494f449fb6717387:/auditd.tproj/auditd.c?ds=sidebyside diff --git a/auditd.tproj/auditd.c b/auditd.tproj/auditd.c deleted file mode 100644 index 2d73e1e..0000000 --- a/auditd.tproj/auditd.c +++ /dev/null @@ -1,792 +0,0 @@ -/* - * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that are subject to the Apple Public - * Source License Version 1.0 (the 'License'). You may not use this file - * except in compliance with the License. Please obtain a copy of the - * License at http://www.apple.com/publicsource and read it before using - * this file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License." - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include "auditd_control_server.h" -#include "audit_triggers_server.h" -#define NA_EVENT_STR_SIZE 25 - -static int ret, minval; -static char *lastfile = NULL; - -static int allhardcount = 0; - -mach_port_t bp = MACH_PORT_NULL; -mach_port_t control_port = MACH_PORT_NULL; -mach_port_t signal_port = MACH_PORT_NULL; -mach_port_t port_set = MACH_PORT_NULL; - -#ifndef __BSM_INTERNAL_NOTIFY_KEY -#define __BSM_INTERNAL_NOTIFY_KEY "com.apple.audit.change" -#endif /* __BSM_INTERNAL_NOTIFY_KEY */ - -TAILQ_HEAD(, dir_ent) dir_q; - - -/* Error starting auditd */ -void fail_exit() -{ - audit_warn_nostart(); - exit(1); -} - -/* - * Free our local list of directory names - */ -void free_dir_q() -{ - struct dir_ent *dirent; - - while ((dirent = TAILQ_FIRST(&dir_q))) { - TAILQ_REMOVE(&dir_q, dirent, dirs); - free(dirent->dirname); - free(dirent); - } -} - -/* - * generate the timestamp string - */ -int getTSstr(char *buf, int len) -{ - struct timeval ts; - struct timezone tzp; - time_t tt; - - if(gettimeofday(&ts, &tzp) != 0) { - return -1; - } - tt = (time_t)ts.tv_sec; - if(!strftime(buf, len, "%Y%m%d%H%M%S", gmtime(&tt))) { - return -1; - } - - return 0; -} - -/* - * Concat the directory name to the given file name - * XXX We should affix the hostname also - */ -char *affixdir(char *name, struct dir_ent *dirent) -{ - char *fn; - char *curdir; - const char *sep = "/"; - - curdir = dirent->dirname; - syslog(LOG_INFO, "dir = %s\n", dirent->dirname); - - fn = (char *) malloc (strlen(curdir) + strlen(sep) - + (2 * POSTFIX_LEN) + 1); - if(fn == NULL) { - return NULL; - } - strcpy(fn, curdir); - strcat(fn, sep); - strcat(fn, name); - - return fn; -} - -/* Close the previous audit trail file */ -int close_lastfile(char *TS) -{ - char *ptr; - char *oldname; - - if(lastfile != NULL) { - oldname = (char *)malloc(strlen(lastfile) + 1); - if(oldname == NULL) { - return -1; - } - strcpy(oldname, lastfile); - - /* rename the last file -- append timestamp */ - - if((ptr = strstr(lastfile, NOT_TERMINATED)) != NULL) { - *ptr = '.'; - strcpy(ptr+1, TS); - if(rename(oldname, lastfile) != 0) { - syslog(LOG_ERR, "Could not rename %s to %s \n", - oldname, lastfile); - } - else { - syslog(LOG_INFO, "renamed %s to %s \n", - oldname, lastfile); - } - } - - free(lastfile); - free(oldname); - - lastfile = NULL; - } - - return 0; -} - -/* - * Create the new file name, swap with existing audit file - */ -int swap_audit_file() -{ - char timestr[2 * POSTFIX_LEN]; - char *fn; - char TS[POSTFIX_LEN]; - struct dir_ent *dirent; - - if(getTSstr(TS, POSTFIX_LEN) != 0) { - return -1; - } - - strcpy(timestr, TS); - strcat(timestr, NOT_TERMINATED); - - /* try until we succeed */ - while((dirent = TAILQ_FIRST(&dir_q))) { - if((fn = affixdir(timestr, dirent)) == NULL) { - return -1; - } - - syslog(LOG_INFO, "New audit file is %s\n", fn); - if (open(fn, O_RDONLY | O_CREAT, S_IRUSR | S_IRGRP) < 0) { - perror("File open"); - } - else if (auditctl(fn) != 0) { - syslog(LOG_ERR, "auditctl failed! : %s\n", - strerror(errno)); - } - else { - /* Success */ - close_lastfile(TS); - lastfile = fn; - return 0; - } - - /* Tell the administrator about lack of permissions for dirent */ - audit_warn_getacdir(dirent->dirname); - - /* Try again with a different directory */ - TAILQ_REMOVE(&dir_q, dirent, dirs); - free(dirent->dirname); - free(dirent); - } - return -1; -} - -/* - * Read the audit_control file contents - */ -int read_control_file() -{ - char cur_dir[MAX_DIR_SIZE]; - struct dir_ent *dirent; - au_qctrl_t qctrl; - - /* Clear old values */ - free_dir_q(); - endac(); // force a re-read of the file the next time - - /* Post that the audit config changed */ - notify_post(__BSM_INTERNAL_NOTIFY_KEY); - - /* Read the list of directories into a local linked list */ - /* XXX We should use the reentrant interfaces once they are available */ - while(getacdir(cur_dir, MAX_DIR_SIZE) >= 0) { - dirent = (struct dir_ent *) malloc (sizeof(struct dir_ent)); - if(dirent == NULL) { - return -1; - } - - dirent->softlim = 0; - dirent->dirname = (char *) malloc (MAX_DIR_SIZE); - if(dirent->dirname == NULL) { - free(dirent); - return -1; - } - - strcpy(dirent->dirname, cur_dir); - TAILQ_INSERT_TAIL(&dir_q, dirent, dirs); - } - - allhardcount = 0; - - if(swap_audit_file() == -1) { - syslog(LOG_ERR, "Could not swap audit file\n"); - /* - * XXX Faulty directory listing? - user should be given - * XXX an opportunity to change the audit_control file - * XXX switch to a reduced mode of auditing? - */ - return -1; - } - - /* - * XXX There are synchronization problems here - * XXX what should we do if a trigger for the earlier limit - * XXX is generated here? - */ - if(0 == (ret = getacmin(&minval))) { - - syslog(LOG_INFO, "min free = %d\n", minval); - - if (auditon(A_GETQCTRL, &qctrl, sizeof(qctrl)) != 0) { - syslog(LOG_ERR, - "could not get audit queue settings\n"); - return -1; - } - qctrl.aq_minfree = minval; - if (auditon(A_SETQCTRL, &qctrl, sizeof(qctrl)) != 0) { - syslog(LOG_ERR, - "could not set audit queue settings\n"); - return -1; - } - } - - return 0; -} - -/* - * Close all log files, control files, and tell the audit system. - */ -int close_all() -{ - int err_ret = 0; - char TS[POSTFIX_LEN]; - int aufd; - token_t *tok; - - /* Generate an audit record */ - if((aufd = au_open()) == -1) { - syslog(LOG_ERR, "Could not create audit shutdown event.\n"); - } else { - - if((tok = au_to_text("auditd::Audit shutdown")) != NULL) { - au_write(aufd, tok); - } - - if(au_close(aufd, 1, AUE_audit_shutdown) == -1) { - syslog(LOG_ERR, "Could not close audit shutdown event.\n"); - } - } - - /* flush contents */ - err_ret = auditctl(NULL); - if (err_ret != 0) { - syslog(LOG_ERR, "auditctl failed! : %s\n", - strerror(errno)); - err_ret = 1; - } - if(getTSstr(TS, POSTFIX_LEN) == 0) { - close_lastfile(TS); - } - if(lastfile != NULL) - free(lastfile); - - free_dir_q(); - if((remove(AUDITD_PIDFILE) == -1) || err_ret) { - syslog(LOG_ERR, "Could not unregister\n"); - audit_warn_postsigterm(); - return (1); - } - endac(); - syslog(LOG_INFO, "Finished.\n"); - return (0); -} - -/* - * When we get a signal, we are often not at a clean point. - * So, little can be done in the signal handler itself. Instead, - * we send a message to the main servicing loop to do proper - * handling from a non-signal-handler context. - */ -static void -relay_signal(int signal) -{ - mach_msg_empty_send_t msg; - - msg.header.msgh_id = signal; - msg.header.msgh_remote_port = signal_port; - msg.header.msgh_local_port = MACH_PORT_NULL; - msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); - mach_msg(&(msg.header), MACH_SEND_MSG|MACH_SEND_TIMEOUT, sizeof(msg), - 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); -} - -/* registering the daemon */ -int register_daemon() -{ - FILE * pidfile; - int fd; - pid_t pid; - - /* Set up the signal hander */ - if (signal(SIGTERM, relay_signal) == SIG_ERR) { - fail_exit(); - } - if (signal(SIGCHLD, relay_signal) == SIG_ERR) { - fail_exit(); - } - - if ((pidfile = fopen(AUDITD_PIDFILE, "a")) == NULL) { - audit_warn_tmpfile(); - return -1; - } - - /* attempt to lock the pid file; if a lock is present, exit */ - fd = fileno(pidfile); - if(flock(fd, LOCK_EX | LOCK_NB) < 0) { - syslog(LOG_ERR, "PID file is locked (is another auditd running?).\n"); - audit_warn_ebusy(); - return -1; - } - - pid = getpid(); - ftruncate(fd, 0); - if(fprintf(pidfile, "%u\n", pid) < 0) { - /* should not start the daemon */ - fail_exit(); - } - - fflush(pidfile); - return 0; -} - -/* - * React to input from the audit tool - */ -kern_return_t auditd_control(auditd_port, flags) - mach_port_t auditd_port; - int flags; -{ - int err_ret = 0; - - switch(flags) { - - case OPEN_NEW : - /* create a new file and swap with the one being used in kernel */ - if(swap_audit_file() == -1) { - syslog(LOG_ERR, "Error swapping audit file\n"); - } - break; - - case READ_FILE : - if(read_control_file() == -1) { - syslog(LOG_ERR, "Error in audit control file\n"); - } - break; - - case CLOSE_AND_DIE : - err_ret = close_all(); - exit (err_ret); - break; - - default : - break; - } - - return KERN_SUCCESS; -} - -/* - * Suppress duplicate messages within a 30 second interval. - * This should be enough to time to rotate log files without - * thrashing from soft warnings generated before the log is - * actually rotated. - */ -#define DUPLICATE_INTERVAL 30 -/* - * Implementation of the audit_triggers() MIG routine. - */ -kern_return_t audit_triggers(audit_port, flags) - mach_port_t audit_port; - int flags; -{ - static int last_flags; - static time_t last_time; - struct dir_ent *dirent; - - /* - * Suppres duplicate messages from the kernel within the specified interval - */ - struct timeval ts; - struct timezone tzp; - time_t tt; - - if(gettimeofday(&ts, &tzp) == 0) { - tt = (time_t)ts.tv_sec; - if ((flags == last_flags) && (tt < (last_time + DUPLICATE_INTERVAL))) { - return KERN_SUCCESS; - } - last_flags = flags; - last_time = tt; - } - - syslog(LOG_INFO, - "audit_triggers() called within auditd with flags = %d\n", - flags); - /* - * XXX Message processing is done here - */ - dirent = TAILQ_FIRST(&dir_q); - if(flags == AUDIT_TRIGGER_LOW_SPACE) { - if(dirent && (dirent->softlim != 1)) { - TAILQ_REMOVE(&dir_q, dirent, dirs); - /* add this node to the end of the list */ - TAILQ_INSERT_TAIL(&dir_q, dirent, dirs); - audit_warn_soft(dirent->dirname); - dirent->softlim = 1; - - if (TAILQ_NEXT(TAILQ_FIRST(&dir_q), dirs) != NULL && swap_audit_file() == -1) { - syslog(LOG_ERR, "Error swapping audit file\n"); - } - - /* - * check if the next dir has already reached its - * soft limit - */ - dirent = TAILQ_FIRST(&dir_q); - if(dirent->softlim == 1) { - /* all dirs have reached their soft limit */ - audit_warn_allsoft(); - } - } - else { - /* - * Continue auditing to the current file - * Also generate an allsoft warning - * XXX do we want to do this ? - */ - audit_warn_allsoft(); - } - } - else if (flags == AUDIT_TRIGGER_FILE_FULL) { - - /* delete current dir, go on to next */ - TAILQ_REMOVE(&dir_q, dirent, dirs); - audit_warn_hard(dirent->dirname); - free(dirent->dirname); - free(dirent); - - if(swap_audit_file() == -1) { - syslog(LOG_ERR, "Error swapping audit file in response to AUDIT_TRIGGER_FILE_FULL message\n"); - - /* Nowhere to write to */ - audit_warn_allhard(++allhardcount); - } - } - return KERN_SUCCESS; -} - -/* - * Reap our children. - */ -static void -reap_children(void) -{ - pid_t child; - int wstatus; - - while ((child = waitpid(-1, &wstatus, WNOHANG)) > 0) { - if (wstatus) { - syslog(LOG_INFO, "warn process [pid=%d] %s %d.\n", child, - ((WIFEXITED(wstatus)) ? - "exited with non-zero status" : - "exited as a result of signal"), - ((WIFEXITED(wstatus)) ? - WEXITSTATUS(wstatus) : - WTERMSIG(wstatus))); - } - } -} - -/* - * Handle an RPC call - */ -boolean_t auditd_combined_server( - mach_msg_header_t *InHeadP, - mach_msg_header_t *OutHeadP) -{ - mach_port_t local_port = InHeadP->msgh_local_port; - - if (local_port == signal_port) { - int signo = InHeadP->msgh_id; - int ret; - - if (SIGTERM == signo) { - ret = close_all(); - exit (ret); - } else if (SIGCHLD == signo) { - reap_children(); - return TRUE; - } else { - syslog(LOG_INFO, "Recevied signal %d.\n", signo); - return TRUE; - } - } else if (local_port == control_port) { - boolean_t result; - - result = audit_triggers_server(InHeadP, OutHeadP); - if (!result) - result = auditd_control_server(InHeadP, OutHeadP); - return result; - } - syslog(LOG_INFO, "Recevied msg on bad port 0x%x.\n", local_port); - return FALSE; -} - -void wait_on_audit_trigger(port_set) - mach_port_t port_set; -{ - kern_return_t result; - result = mach_msg_server(auditd_combined_server, 4096, port_set, MACH_MSG_OPTION_NONE); - syslog(LOG_ERR, "abnormal exit\n"); -} - -/* - * Configure the audit controls in the kernel: the event to class mapping, - * kernel preselection mask, etc. - */ -int config_audit_controls(long flags) -{ - au_event_ent_t *ev; - au_evclass_map_t evc_map; - au_mask_t aumask; - int ctr = 0; - char naeventstr[NA_EVENT_STR_SIZE]; - - /* Process the audit event file, obtaining a class mapping for each - * event, and send that mapping into the kernel. - * XXX There's a risk here that the BSM library will return NULL - * for an event when it can't properly map it to a class. In that - * case, we will not process any events beyond the one that failed, - * but should. We need a way to get a count of the events. - */ - - setauevent(); - while((ev = getauevent()) != NULL) { - evc_map.ec_number = ev->ae_number; - evc_map.ec_class = ev->ae_class; - if (auditon(A_SETCLASS, &evc_map, sizeof(au_evclass_map_t)) != 0) { - syslog(LOG_ERR, - "Failed to register class mapping for event %s", - ev->ae_name); - } else { - ctr++; - } - free(ev->ae_name); - free(ev->ae_desc); - free(ev); - } - endauevent(); - if (ctr == 0) - syslog(LOG_ERR, "No events to class mappings registered."); - else - syslog(LOG_INFO, "Registered %d event to class mappings.", ctr); - - /* Get the non-attributable event string and set the kernel mask - * from that. - */ - if ((getacna(naeventstr, NA_EVENT_STR_SIZE) == 0) - && ( getauditflagsbin(naeventstr, &aumask) == 0)) { - - if (auditon(A_SETKMASK, &aumask, sizeof(au_mask_t))){ - syslog(LOG_ERR, - "Failed to register non-attributable event mask."); - } else { - syslog(LOG_INFO, "Registered non-attributable event mask."); - } - - } else { - syslog(LOG_ERR,"Failed to obtain non-attributable event mask."); - } - - /* - * Set the audit policy flags based on passed in parameter values. - */ - if (auditon(A_SETPOLICY, &flags, sizeof(flags))) { - syslog(LOG_ERR, - "Failed to set audit policy."); - } - - return 0; -} - -void setup(long flags) -{ - mach_msg_type_name_t poly; - int aufd; - token_t *tok; - - /* Allocate a port set */ - if (mach_port_allocate(mach_task_self(), - MACH_PORT_RIGHT_PORT_SET, - &port_set) != KERN_SUCCESS) { - syslog(LOG_ERR, "allocation of port set failed\n"); - fail_exit(); - } - - /* Allocate a signal reflection port */ - if (mach_port_allocate(mach_task_self(), - MACH_PORT_RIGHT_RECEIVE, - &signal_port) != KERN_SUCCESS || - mach_port_move_member(mach_task_self(), - signal_port, - port_set) != KERN_SUCCESS) { - syslog(LOG_ERR, "allocation of signal port failed\n"); - fail_exit(); - } - - /* Allocate a trigger port */ - if (mach_port_allocate(mach_task_self(), - MACH_PORT_RIGHT_RECEIVE, - &control_port) != KERN_SUCCESS || - mach_port_move_member(mach_task_self(), - control_port, - port_set) != KERN_SUCCESS) { - syslog(LOG_ERR, "allocation of trigger port failed\n"); - fail_exit(); - } - - /* create a send right on our trigger port */ - mach_port_extract_right(mach_task_self(), control_port, - MACH_MSG_TYPE_MAKE_SEND, &control_port, &poly); - - TAILQ_INIT(&dir_q); - - /* register the trigger port with the kernel */ - if(host_set_audit_control_port(mach_host_self(), control_port) != KERN_SUCCESS) { - syslog(LOG_ERR, "Cannot set Mach control port\n"); - fail_exit(); - } - else { - syslog(LOG_ERR, "Mach control port registered\n"); - } - - if(read_control_file() == -1) { - syslog(LOG_ERR, "Error reading control file\n"); - fail_exit(); - } - - /* Generate an audit record */ - if((aufd = au_open()) == -1) { - syslog(LOG_ERR, "Could not create audit startup event.\n"); - } else { - - if((tok = au_to_text("auditd::Audit startup")) != NULL) { - au_write(aufd, tok); - } - - if(au_close(aufd, 1, AUE_audit_startup) == -1) { - syslog(LOG_ERR, "Could not close audit startup event.\n"); - } - } - - if (config_audit_controls(flags) == 0) - syslog(LOG_INFO, "Initialization successful\n"); - else - syslog(LOG_INFO, "Initialization failed\n"); -} - - -int main(int argc, char **argv) -{ - char ch; - long flags = AUDIT_CNT; - int debug = 0; - - while ((ch = getopt(argc, argv, "dhs")) != -1) { - switch(ch) { - - /* debug option */ - case 'd': - debug = 1; - break; - - /* fail-stop option */ - case 's': - flags &= ~(AUDIT_CNT); - break; - - /* halt-stop option */ - case 'h': - flags |= AUDIT_AHLT; - break; - - case '?': - default: - (void)fprintf(stderr, - "usage: auditd [-h | -s]\n"); - exit(1); - } - } - - openlog("auditd", LOG_CONS | LOG_PID, LOG_DAEMON); - syslog(LOG_INFO, "starting...\n"); - - if (debug == 0 && daemon(0, 0) == -1) { - syslog(LOG_ERR, "Failed to daemonize\n"); - exit(1); - } - - if(register_daemon() == -1) { - syslog(LOG_ERR, "Could not register as daemon\n"); - exit(1); - } - - setup(flags); - wait_on_audit_trigger(port_set); - syslog(LOG_INFO, "exiting.\n"); - - exit(1); -}