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