1186545Srwatson/*- 2191273Srwatson * Copyright (c) 2004-2009 Apple Inc. 3186545Srwatson * All rights reserved. 4186545Srwatson * 5186545Srwatson * Redistribution and use in source and binary forms, with or without 6186545Srwatson * modification, are permitted provided that the following conditions 7186545Srwatson * are met: 8186545Srwatson * 9186545Srwatson * 1. Redistributions of source code must retain the above copyright 10186545Srwatson * notice, this list of conditions and the following disclaimer. 11186545Srwatson * 2. Redistributions in binary form must reproduce the above copyright 12186545Srwatson * notice, this list of conditions and the following disclaimer in the 13186545Srwatson * documentation and/or other materials provided with the distribution. 14186545Srwatson * 3. Neither the name of Apple Inc. ("Apple") nor the names of 15186545Srwatson * its contributors may be used to endorse or promote products derived 16186545Srwatson * from this software without specific prior written permission. 17186545Srwatson * 18186545Srwatson * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19186545Srwatson * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20186545Srwatson * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21186545Srwatson * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22186545Srwatson * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23186545Srwatson * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24186545Srwatson * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25186545Srwatson * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26186545Srwatson * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27186545Srwatson * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28186545Srwatson * 29191273Srwatson * $P4: //depot/projects/trustedbsd/openbsm/bin/auditd/auditd_darwin.c#5 $ 30186545Srwatson */ 31186545Srwatson 32186545Srwatson#include <sys/types.h> 33186545Srwatson 34186545Srwatson#include <config/config.h> 35186545Srwatson 36186545Srwatson#include <errno.h> 37186545Srwatson#include <stdarg.h> 38186545Srwatson#include <stdlib.h> 39186545Srwatson#include <unistd.h> 40186545Srwatson 41186545Srwatson#include <bsm/audit.h> 42186545Srwatson#include <bsm/audit_uevents.h> 43186545Srwatson#include <bsm/auditd_lib.h> 44186545Srwatson#include <bsm/libbsm.h> 45186545Srwatson 46186545Srwatson#include <asl.h> 47186545Srwatson#include <launch.h> 48186545Srwatson#include <notify.h> 49186545Srwatson#include <mach/port.h> 50186545Srwatson#include <mach/mach_error.h> 51186545Srwatson#include <mach/mach_traps.h> 52186545Srwatson#include <mach/mach.h> 53186545Srwatson#include <mach/host_special_ports.h> 54186545Srwatson 55186545Srwatson#include "auditd.h" 56186545Srwatson 57186545Srwatson#include "auditd_controlServer.h" 58186545Srwatson#include "audit_triggersServer.h" 59186545Srwatson 60186545Srwatson/* 61186545Srwatson * Apple System Logger Handles. 62186545Srwatson */ 63186545Srwatsonstatic aslmsg au_aslmsg = NULL; 64186545Srwatsonstatic aslclient au_aslclient = NULL; 65186545Srwatson 66186545Srwatsonstatic mach_port_t control_port = MACH_PORT_NULL; 67186545Srwatsonstatic mach_port_t signal_port = MACH_PORT_NULL; 68186545Srwatsonstatic mach_port_t port_set = MACH_PORT_NULL; 69186545Srwatson 70186545Srwatson/* 71186545Srwatson * Current auditing state (cache). 72186545Srwatson */ 73186545Srwatsonstatic int auditing_state = AUD_STATE_INIT; 74186545Srwatson 75186545Srwatson/* 76186545Srwatson * Maximum idle time before auditd terminates under launchd. 77186545Srwatson * If it is zero then auditd does not timeout while idle. 78186545Srwatson */ 79186545Srwatsonstatic int max_idletime = 0; 80186545Srwatson 81186545Srwatson#ifndef __BSM_INTERNAL_NOTIFY_KEY 82186545Srwatson#define __BSM_INTERNAL_NOTIFY_KEY "com.apple.audit.change" 83186545Srwatson#endif /* __BSM_INTERNAL_NOTIFY_KEY */ 84186545Srwatson 85186545Srwatson#ifndef __AUDIT_LAUNCHD_LABEL 86187214Srwatson#define __AUDIT_LAUNCHD_LABEL "com.apple.auditd" 87186545Srwatson#endif /* __AUDIT_LAUNCHD_LABEL */ 88186545Srwatson 89186545Srwatson#define MAX_MSG_SIZE 4096 90186545Srwatson 91186545Srwatson/* 92186545Srwatson * Open and set up system logging. 93186545Srwatson */ 94186545Srwatsonvoid 95186545Srwatsonauditd_openlog(int debug, gid_t gid) 96186545Srwatson{ 97186545Srwatson uint32_t opt = 0; 98186545Srwatson char *cp = NULL; 99186545Srwatson 100186545Srwatson if (debug) 101186545Srwatson opt = ASL_OPT_STDERR; 102186545Srwatson 103187214Srwatson au_aslclient = asl_open("auditd", "com.apple.auditd", opt); 104186545Srwatson au_aslmsg = asl_new(ASL_TYPE_MSG); 105186545Srwatson 106186545Srwatson#ifdef ASL_KEY_READ_UID 107186545Srwatson /* 108186545Srwatson * Make it only so the audit administrator and members of the audit 109186545Srwatson * review group (if used) have access to the auditd system log messages. 110186545Srwatson */ 111186545Srwatson asl_set(au_aslmsg, ASL_KEY_READ_UID, "0"); 112186545Srwatson asprintf(&cp, "%u", gid); 113186545Srwatson if (cp != NULL) { 114186545Srwatson#ifdef ASL_KEY_READ_GID 115186545Srwatson asl_set(au_aslmsg, ASL_KEY_READ_GID, cp); 116186545Srwatson#endif 117186545Srwatson free(cp); 118186545Srwatson } 119186545Srwatson#endif 120186545Srwatson 121186545Srwatson /* 122186545Srwatson * Set the client-side system log filtering. 123186545Srwatson */ 124186545Srwatson if (debug) 125186545Srwatson asl_set_filter(au_aslclient, 126186545Srwatson ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); 127186545Srwatson else 128186545Srwatson asl_set_filter(au_aslclient, 129186545Srwatson ASL_FILTER_MASK_UPTO(ASL_LEVEL_INFO)); 130186545Srwatson} 131186545Srwatson 132186545Srwatson/* 133186545Srwatson * Log messages at different priority levels. 134186545Srwatson */ 135186545Srwatsonvoid 136186545Srwatsonauditd_log_err(const char *fmt, ...) 137186545Srwatson{ 138186545Srwatson va_list ap; 139186545Srwatson 140186545Srwatson va_start(ap, fmt); 141186545Srwatson asl_vlog(au_aslclient, au_aslmsg, ASL_LEVEL_ERR, fmt, ap); 142186545Srwatson va_end(ap); 143186545Srwatson} 144186545Srwatson 145186545Srwatsonvoid 146186545Srwatsonauditd_log_notice(const char *fmt, ...) 147186545Srwatson{ 148186545Srwatson va_list ap; 149186545Srwatson 150186545Srwatson va_start(ap, fmt); 151186545Srwatson asl_vlog(au_aslclient, au_aslmsg, ASL_LEVEL_NOTICE, fmt, ap); 152186545Srwatson va_end(ap); 153186545Srwatson} 154186545Srwatson 155186545Srwatsonvoid 156186545Srwatsonauditd_log_info(const char *fmt, ...) 157186545Srwatson{ 158186545Srwatson va_list ap; 159186545Srwatson 160186545Srwatson va_start(ap, fmt); 161186545Srwatson asl_vlog(au_aslclient, au_aslmsg, ASL_LEVEL_INFO, fmt, ap); 162186545Srwatson va_end(ap); 163186545Srwatson} 164186545Srwatson 165186545Srwatsonvoid 166186545Srwatsonauditd_log_debug(const char *fmt, ...) 167186545Srwatson{ 168186545Srwatson va_list ap; 169186545Srwatson 170186545Srwatson va_start(ap, fmt); 171186545Srwatson asl_vlog(au_aslclient, au_aslmsg, ASL_LEVEL_DEBUG, fmt, ap); 172186545Srwatson va_end(ap); 173186545Srwatson} 174186545Srwatson 175186545Srwatson/* 176186545Srwatson * Get the auditing state from the kernel and cache it. 177186545Srwatson */ 178186545Srwatsonstatic void 179186545Srwatsoninit_audit_state(void) 180186545Srwatson{ 181191273Srwatson int au_cond; 182186545Srwatson 183191273Srwatson if (audit_get_cond(&au_cond) < 0) { 184186545Srwatson if (errno != ENOSYS) { 185186545Srwatson auditd_log_err("Audit status check failed (%s)", 186186545Srwatson strerror(errno)); 187186545Srwatson } 188186545Srwatson auditing_state = AUD_STATE_DISABLED; 189186545Srwatson } else 190186545Srwatson if (au_cond == AUC_NOAUDIT || au_cond == AUC_DISABLED) 191186545Srwatson auditing_state = AUD_STATE_DISABLED; 192186545Srwatson else 193186545Srwatson auditing_state = AUD_STATE_ENABLED; 194186545Srwatson} 195186545Srwatson 196186545Srwatson/* 197186545Srwatson * Update the cached auditing state. Let other tasks that may be caching it 198186545Srwatson * as well to update their state via notify(3). 199186545Srwatson */ 200186545Srwatsonvoid 201186545Srwatsonauditd_set_state(int state) 202186545Srwatson{ 203186545Srwatson int old_auditing_state = auditing_state; 204186545Srwatson 205186545Srwatson if (state == AUD_STATE_INIT) 206186545Srwatson init_audit_state(); 207186545Srwatson else 208186545Srwatson auditing_state = state; 209186545Srwatson 210186545Srwatson if (auditing_state != old_auditing_state) { 211186545Srwatson notify_post(__BSM_INTERNAL_NOTIFY_KEY); 212186545Srwatson 213186545Srwatson if (auditing_state == AUD_STATE_ENABLED) 214186545Srwatson auditd_log_notice("Auditing enabled"); 215186545Srwatson if (auditing_state == AUD_STATE_DISABLED) 216186545Srwatson auditd_log_notice("Auditing disabled"); 217186545Srwatson } 218186545Srwatson} 219186545Srwatson 220186545Srwatson/* 221186545Srwatson * Get the cached auditing state. 222186545Srwatson */ 223186545Srwatsonint 224186545Srwatsonauditd_get_state(void) 225186545Srwatson{ 226186545Srwatson 227186545Srwatson if (auditing_state == AUD_STATE_INIT) { 228186545Srwatson init_audit_state(); 229186545Srwatson notify_post(__BSM_INTERNAL_NOTIFY_KEY); 230186545Srwatson } 231186545Srwatson 232186545Srwatson return (auditing_state); 233186545Srwatson} 234186545Srwatson 235186545Srwatson/* 236186545Srwatson * Lookup the audit mach port in the launchd dictionary. 237186545Srwatson */ 238186545Srwatsonstatic mach_port_t 239186545Srwatsonlookup_machport(const char *label) 240186545Srwatson{ 241186545Srwatson launch_data_t msg, msd, ld, cdict, to; 242186545Srwatson mach_port_t mp = MACH_PORT_NULL; 243186545Srwatson 244186545Srwatson msg = launch_data_new_string(LAUNCH_KEY_CHECKIN); 245186545Srwatson 246186545Srwatson cdict = launch_msg(msg); 247186545Srwatson if (cdict == NULL) { 248186545Srwatson auditd_log_err("launch_msg(\"" LAUNCH_KEY_CHECKIN 249186545Srwatson "\") IPC failure: %m"); 250186545Srwatson return (MACH_PORT_NULL); 251186545Srwatson } 252186545Srwatson 253186545Srwatson if (launch_data_get_type(cdict) == LAUNCH_DATA_ERRNO) { 254186545Srwatson errno = launch_data_get_errno(cdict); 255186545Srwatson auditd_log_err("launch_data_get_type() can't get dict: %m"); 256186545Srwatson return (MACH_PORT_NULL); 257186545Srwatson } 258186545Srwatson 259186545Srwatson to = launch_data_dict_lookup(cdict, LAUNCH_JOBKEY_TIMEOUT); 260186545Srwatson if (to) { 261186545Srwatson max_idletime = launch_data_get_integer(to); 262186545Srwatson auditd_log_debug("launchd timeout set to %d", max_idletime); 263186545Srwatson } else { 264186545Srwatson auditd_log_debug("launchd timeout not set, setting to 60"); 265186545Srwatson max_idletime = 60; 266186545Srwatson } 267186545Srwatson 268186545Srwatson msd = launch_data_dict_lookup(cdict, LAUNCH_JOBKEY_MACHSERVICES); 269186545Srwatson if (msd == NULL) { 270186545Srwatson auditd_log_err( 271186545Srwatson "launch_data_dict_lookup() can't get mach services"); 272186545Srwatson return (MACH_PORT_NULL); 273186545Srwatson } 274186545Srwatson 275186545Srwatson ld = launch_data_dict_lookup(msd, label); 276186545Srwatson if (ld == NULL) { 277186545Srwatson auditd_log_err("launch_data_dict_lookup can't find %s", label); 278186545Srwatson return (MACH_PORT_NULL); 279186545Srwatson } 280186545Srwatson 281186545Srwatson mp = launch_data_get_machport(ld); 282186545Srwatson 283186545Srwatson return (mp); 284186545Srwatson} 285186545Srwatson 286186545Srwatsonstatic int 287186545Srwatsonmach_setup(int launchd_flag) 288186545Srwatson{ 289186545Srwatson mach_msg_type_name_t poly; 290186545Srwatson 291186545Srwatson /* 292186545Srwatson * Allocate a port set. 293186545Srwatson */ 294186545Srwatson if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, 295186545Srwatson &port_set) != KERN_SUCCESS) { 296186545Srwatson auditd_log_err("Allocation of port set failed"); 297186545Srwatson return (-1); 298186545Srwatson } 299186545Srwatson 300186545Srwatson 301186545Srwatson /* 302186545Srwatson * Allocate a signal reflection port. 303186545Srwatson */ 304186545Srwatson if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, 305186545Srwatson &signal_port) != KERN_SUCCESS || 306186545Srwatson mach_port_move_member(mach_task_self(), signal_port, port_set) != 307186545Srwatson KERN_SUCCESS) { 308186545Srwatson auditd_log_err("Allocation of signal port failed"); 309186545Srwatson return (-1); 310186545Srwatson } 311186545Srwatson 312186545Srwatson /* 313186545Srwatson * Allocate a trigger port. 314186545Srwatson */ 315186545Srwatson if (launchd_flag) { 316186545Srwatson /* 317186545Srwatson * If started under launchd, lookup port in launchd dictionary. 318186545Srwatson */ 319186545Srwatson if ((control_port = lookup_machport(__AUDIT_LAUNCHD_LABEL)) == 320186545Srwatson MACH_PORT_NULL || mach_port_move_member(mach_task_self(), 321186545Srwatson control_port, port_set) != KERN_SUCCESS) { 322186545Srwatson auditd_log_err("Cannot get Mach control port" 323186545Srwatson " via launchd"); 324186545Srwatson return (-1); 325186545Srwatson } else 326186545Srwatson auditd_log_debug("Mach control port registered" 327186545Srwatson " via launchd"); 328186545Srwatson } else { 329186545Srwatson /* 330186545Srwatson * If not started under launchd, allocate port and register. 331186545Srwatson */ 332186545Srwatson if (mach_port_allocate(mach_task_self(), 333186545Srwatson MACH_PORT_RIGHT_RECEIVE, &control_port) != KERN_SUCCESS || 334186545Srwatson mach_port_move_member(mach_task_self(), control_port, 335186545Srwatson port_set) != KERN_SUCCESS) 336186545Srwatson auditd_log_err("Allocation of trigger port failed"); 337186545Srwatson 338186545Srwatson /* 339186545Srwatson * Create a send right on our trigger port. 340186545Srwatson */ 341186545Srwatson mach_port_extract_right(mach_task_self(), control_port, 342186545Srwatson MACH_MSG_TYPE_MAKE_SEND, &control_port, &poly); 343186545Srwatson 344186545Srwatson /* 345186545Srwatson * Register the trigger port with the kernel. 346186545Srwatson */ 347186545Srwatson if (host_set_audit_control_port(mach_host_self(), 348186545Srwatson control_port) != KERN_SUCCESS) { 349186545Srwatson auditd_log_err("Cannot set Mach control port"); 350186545Srwatson return (-1); 351186545Srwatson } else 352186545Srwatson auditd_log_debug("Mach control port registered"); 353186545Srwatson } 354186545Srwatson 355186545Srwatson return (0); 356186545Srwatson} 357186545Srwatson 358186545Srwatson/* 359186545Srwatson * Open the trigger messaging mechanism. 360186545Srwatson */ 361186545Srwatsonint 362186545Srwatsonauditd_open_trigger(int launchd_flag) 363186545Srwatson{ 364186545Srwatson 365186545Srwatson return (mach_setup(launchd_flag)); 366186545Srwatson} 367186545Srwatson 368186545Srwatson/* 369186545Srwatson * Close the trigger messaging mechanism. 370186545Srwatson */ 371186545Srwatsonint 372186545Srwatsonauditd_close_trigger(void) 373186545Srwatson{ 374186545Srwatson 375186545Srwatson return (0); 376186545Srwatson} 377186545Srwatson 378186545Srwatson/* 379186545Srwatson * Combined server handler. Called by the mach message loop when there is 380186545Srwatson * a trigger or signal message. 381186545Srwatson */ 382186545Srwatsonstatic boolean_t 383186545Srwatsonauditd_combined_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP) 384186545Srwatson{ 385186545Srwatson mach_port_t local_port = InHeadP->msgh_local_port; 386186545Srwatson 387186545Srwatson /* Reset the idle time alarm, if used. */ 388186545Srwatson if (max_idletime) 389186545Srwatson alarm(max_idletime); 390186545Srwatson 391186545Srwatson if (local_port == signal_port) { 392186545Srwatson int signo = InHeadP->msgh_id; 393186545Srwatson 394186545Srwatson switch(signo) { 395186545Srwatson case SIGTERM: 396186545Srwatson case SIGALRM: 397186545Srwatson auditd_terminate(); 398186545Srwatson /* Not reached. */ 399186545Srwatson 400186545Srwatson case SIGCHLD: 401186545Srwatson auditd_reap_children(); 402186545Srwatson return (TRUE); 403186545Srwatson 404186545Srwatson case SIGHUP: 405186545Srwatson auditd_config_controls(); 406186545Srwatson return (TRUE); 407186545Srwatson 408186545Srwatson default: 409186545Srwatson auditd_log_info("Received signal %d", signo); 410186545Srwatson return (TRUE); 411186545Srwatson } 412186545Srwatson } else if (local_port == control_port) { 413186545Srwatson boolean_t result; 414186545Srwatson 415186545Srwatson result = audit_triggers_server(InHeadP, OutHeadP); 416186545Srwatson if (!result) 417186545Srwatson result = auditd_control_server(InHeadP, OutHeadP); 418186545Srwatson return (result); 419186545Srwatson } 420186545Srwatson auditd_log_info("Recevied msg on bad port 0x%x.", local_port); 421186545Srwatson return (FALSE); 422186545Srwatson} 423186545Srwatson 424186545Srwatson/* 425186545Srwatson * The main event loop. Wait for trigger messages or signals and handle them. 426186545Srwatson * It should not return unless there is a problem. 427186545Srwatson */ 428186545Srwatsonvoid 429186545Srwatsonauditd_wait_for_events(void) 430186545Srwatson{ 431186545Srwatson kern_return_t result; 432186545Srwatson 433186545Srwatson /* 434186545Srwatson * Call the mach messaging server loop. 435186545Srwatson */ 436186545Srwatson result = mach_msg_server(auditd_combined_server, MAX_MSG_SIZE, 437186545Srwatson port_set, MACH_MSG_OPTION_NONE); 438186545Srwatson} 439186545Srwatson 440186545Srwatson/* 441186545Srwatson * Implementation of the audit_triggers() MIG simpleroutine. Simply a 442186545Srwatson * wrapper function. This handles input from the kernel on the host 443186545Srwatson * special mach port. 444186545Srwatson */ 445186545Srwatsonkern_return_t 446186545Srwatsonaudit_triggers(mach_port_t __unused audit_port, int trigger) 447186545Srwatson{ 448186545Srwatson 449186545Srwatson auditd_handle_trigger(trigger); 450186545Srwatson 451186545Srwatson return (KERN_SUCCESS); 452186545Srwatson} 453186545Srwatson 454186545Srwatson/* 455186545Srwatson * Implementation of the auditd_control() MIG simpleroutine. Simply a 456186545Srwatson * wrapper function. This handles input from the audit(1) tool. 457186545Srwatson */ 458186545Srwatsonkern_return_t 459186545Srwatsonauditd_control(mach_port_t __unused auditd_port, int trigger) 460186545Srwatson{ 461186545Srwatson 462186545Srwatson auditd_handle_trigger(trigger); 463186545Srwatson 464186545Srwatson return (KERN_SUCCESS); 465186545Srwatson} 466186545Srwatson 467186545Srwatson/* 468186545Srwatson * When we get a signal, we are often not at a clean point. So, little can 469186545Srwatson * be done in the signal handler itself. Instead, we send a message to the 470186545Srwatson * main servicing loop to do proper handling from a non-signal-handler 471186545Srwatson * context. 472186545Srwatson */ 473186545Srwatsonvoid 474186545Srwatsonauditd_relay_signal(int signal) 475186545Srwatson{ 476186545Srwatson mach_msg_empty_send_t msg; 477186545Srwatson 478186545Srwatson msg.header.msgh_id = signal; 479186545Srwatson msg.header.msgh_remote_port = signal_port; 480186545Srwatson msg.header.msgh_local_port = MACH_PORT_NULL; 481186545Srwatson msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); 482186545Srwatson mach_msg(&(msg.header), MACH_SEND_MSG|MACH_SEND_TIMEOUT, sizeof(msg), 483186545Srwatson 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 484186545Srwatson} 485