1/*- 2 * Copyright (c) 2004-2009 Apple Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of Apple Inc. ("Apple") nor the names of 15 * its contributors may be used to endorse or promote products derived 16 * from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * $P4: //depot/projects/trustedbsd/openbsm/bin/auditd/auditd_darwin.c#5 $ 30 */ 31 32#include <sys/types.h> 33 34#include <config/config.h> 35 36#include <errno.h> 37#include <stdarg.h> 38#include <stdlib.h> 39#include <unistd.h> 40 41#include <bsm/audit.h> 42#include <bsm/audit_uevents.h> 43#include <bsm/auditd_lib.h> 44#include <bsm/libbsm.h> 45 46#include <asl.h> 47#include <launch.h> 48#include <notify.h> 49#include <mach/port.h> 50#include <mach/mach_error.h> 51#include <mach/mach_traps.h> 52#include <mach/mach.h> 53#include <mach/host_special_ports.h> 54 55#include "auditd.h" 56 57#include "auditd_controlServer.h" 58#include "audit_triggersServer.h" 59 60/* 61 * Apple System Logger Handles. 62 */ 63static aslmsg au_aslmsg = NULL; 64static aslclient au_aslclient = NULL; 65 66static mach_port_t control_port = MACH_PORT_NULL; 67static mach_port_t signal_port = MACH_PORT_NULL; 68static mach_port_t port_set = MACH_PORT_NULL; 69 70/* 71 * Current auditing state (cache). 72 */ 73static int auditing_state = AUD_STATE_INIT; 74 75/* 76 * Maximum idle time before auditd terminates under launchd. 77 * If it is zero then auditd does not timeout while idle. 78 */ 79static int max_idletime = 0; 80 81#ifndef __BSM_INTERNAL_NOTIFY_KEY 82#define __BSM_INTERNAL_NOTIFY_KEY "com.apple.audit.change" 83#endif /* __BSM_INTERNAL_NOTIFY_KEY */ 84 85#ifndef __AUDIT_LAUNCHD_LABEL 86#define __AUDIT_LAUNCHD_LABEL "com.apple.auditd" 87#endif /* __AUDIT_LAUNCHD_LABEL */ 88 89#define MAX_MSG_SIZE 4096 90 91/* 92 * Open and set up system logging. 93 */ 94void 95auditd_openlog(int debug, gid_t gid) 96{ 97 uint32_t opt = 0; 98 char *cp = NULL; 99 100 if (debug) 101 opt = ASL_OPT_STDERR; 102 103 au_aslclient = asl_open("auditd", "com.apple.auditd", opt); 104 au_aslmsg = asl_new(ASL_TYPE_MSG); 105 106#ifdef ASL_KEY_READ_UID 107 /* 108 * Make it only so the audit administrator and members of the audit 109 * review group (if used) have access to the auditd system log messages. 110 */ 111 asl_set(au_aslmsg, ASL_KEY_READ_UID, "0"); 112 asprintf(&cp, "%u", gid); 113 if (cp != NULL) { 114#ifdef ASL_KEY_READ_GID 115 asl_set(au_aslmsg, ASL_KEY_READ_GID, cp); 116#endif 117 free(cp); 118 } 119#endif 120 121 /* 122 * Set the client-side system log filtering. 123 */ 124 if (debug) 125 asl_set_filter(au_aslclient, 126 ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); 127 else 128 asl_set_filter(au_aslclient, 129 ASL_FILTER_MASK_UPTO(ASL_LEVEL_INFO)); 130} 131 132/* 133 * Log messages at different priority levels. 134 */ 135void 136auditd_log_err(const char *fmt, ...) 137{ 138 va_list ap; 139 140 va_start(ap, fmt); 141 asl_vlog(au_aslclient, au_aslmsg, ASL_LEVEL_ERR, fmt, ap); 142 va_end(ap); 143} 144 145void 146auditd_log_notice(const char *fmt, ...) 147{ 148 va_list ap; 149 150 va_start(ap, fmt); 151 asl_vlog(au_aslclient, au_aslmsg, ASL_LEVEL_NOTICE, fmt, ap); 152 va_end(ap); 153} 154 155void 156auditd_log_info(const char *fmt, ...) 157{ 158 va_list ap; 159 160 va_start(ap, fmt); 161 asl_vlog(au_aslclient, au_aslmsg, ASL_LEVEL_INFO, fmt, ap); 162 va_end(ap); 163} 164 165void 166auditd_log_debug(const char *fmt, ...) 167{ 168 va_list ap; 169 170 va_start(ap, fmt); 171 asl_vlog(au_aslclient, au_aslmsg, ASL_LEVEL_DEBUG, fmt, ap); 172 va_end(ap); 173} 174 175/* 176 * Get the auditing state from the kernel and cache it. 177 */ 178static void 179init_audit_state(void) 180{ 181 int au_cond; 182 183 if (audit_get_cond(&au_cond) < 0) { 184 if (errno != ENOSYS) { 185 auditd_log_err("Audit status check failed (%s)", 186 strerror(errno)); 187 } 188 auditing_state = AUD_STATE_DISABLED; 189 } else 190 if (au_cond == AUC_NOAUDIT || au_cond == AUC_DISABLED) 191 auditing_state = AUD_STATE_DISABLED; 192 else 193 auditing_state = AUD_STATE_ENABLED; 194} 195 196/* 197 * Update the cached auditing state. Let other tasks that may be caching it 198 * as well to update their state via notify(3). 199 */ 200void 201auditd_set_state(int state) 202{ 203 int old_auditing_state = auditing_state; 204 205 if (state == AUD_STATE_INIT) 206 init_audit_state(); 207 else 208 auditing_state = state; 209 210 if (auditing_state != old_auditing_state) { 211 notify_post(__BSM_INTERNAL_NOTIFY_KEY); 212 213 if (auditing_state == AUD_STATE_ENABLED) 214 auditd_log_notice("Auditing enabled"); 215 if (auditing_state == AUD_STATE_DISABLED) 216 auditd_log_notice("Auditing disabled"); 217 } 218} 219 220/* 221 * Get the cached auditing state. 222 */ 223int 224auditd_get_state(void) 225{ 226 227 if (auditing_state == AUD_STATE_INIT) { 228 init_audit_state(); 229 notify_post(__BSM_INTERNAL_NOTIFY_KEY); 230 } 231 232 return (auditing_state); 233} 234 235/* 236 * Lookup the audit mach port in the launchd dictionary. 237 */ 238static mach_port_t 239lookup_machport(const char *label) 240{ 241 launch_data_t msg, msd, ld, cdict, to; 242 mach_port_t mp = MACH_PORT_NULL; 243 244 msg = launch_data_new_string(LAUNCH_KEY_CHECKIN); 245 246 cdict = launch_msg(msg); 247 if (cdict == NULL) { 248 auditd_log_err("launch_msg(\"" LAUNCH_KEY_CHECKIN 249 "\") IPC failure: %m"); 250 return (MACH_PORT_NULL); 251 } 252 253 if (launch_data_get_type(cdict) == LAUNCH_DATA_ERRNO) { 254 errno = launch_data_get_errno(cdict); 255 auditd_log_err("launch_data_get_type() can't get dict: %m"); 256 return (MACH_PORT_NULL); 257 } 258 259 to = launch_data_dict_lookup(cdict, LAUNCH_JOBKEY_TIMEOUT); 260 if (to) { 261 max_idletime = launch_data_get_integer(to); 262 auditd_log_debug("launchd timeout set to %d", max_idletime); 263 } else { 264 auditd_log_debug("launchd timeout not set, setting to 60"); 265 max_idletime = 60; 266 } 267 268 msd = launch_data_dict_lookup(cdict, LAUNCH_JOBKEY_MACHSERVICES); 269 if (msd == NULL) { 270 auditd_log_err( 271 "launch_data_dict_lookup() can't get mach services"); 272 return (MACH_PORT_NULL); 273 } 274 275 ld = launch_data_dict_lookup(msd, label); 276 if (ld == NULL) { 277 auditd_log_err("launch_data_dict_lookup can't find %s", label); 278 return (MACH_PORT_NULL); 279 } 280 281 mp = launch_data_get_machport(ld); 282 283 return (mp); 284} 285 286static int 287mach_setup(int launchd_flag) 288{ 289 mach_msg_type_name_t poly; 290 291 /* 292 * Allocate a port set. 293 */ 294 if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, 295 &port_set) != KERN_SUCCESS) { 296 auditd_log_err("Allocation of port set failed"); 297 return (-1); 298 } 299 300 301 /* 302 * Allocate a signal reflection port. 303 */ 304 if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, 305 &signal_port) != KERN_SUCCESS || 306 mach_port_move_member(mach_task_self(), signal_port, port_set) != 307 KERN_SUCCESS) { 308 auditd_log_err("Allocation of signal port failed"); 309 return (-1); 310 } 311 312 /* 313 * Allocate a trigger port. 314 */ 315 if (launchd_flag) { 316 /* 317 * If started under launchd, lookup port in launchd dictionary. 318 */ 319 if ((control_port = lookup_machport(__AUDIT_LAUNCHD_LABEL)) == 320 MACH_PORT_NULL || mach_port_move_member(mach_task_self(), 321 control_port, port_set) != KERN_SUCCESS) { 322 auditd_log_err("Cannot get Mach control port" 323 " via launchd"); 324 return (-1); 325 } else 326 auditd_log_debug("Mach control port registered" 327 " via launchd"); 328 } else { 329 /* 330 * If not started under launchd, allocate port and register. 331 */ 332 if (mach_port_allocate(mach_task_self(), 333 MACH_PORT_RIGHT_RECEIVE, &control_port) != KERN_SUCCESS || 334 mach_port_move_member(mach_task_self(), control_port, 335 port_set) != KERN_SUCCESS) 336 auditd_log_err("Allocation of trigger port failed"); 337 338 /* 339 * Create a send right on our trigger port. 340 */ 341 mach_port_extract_right(mach_task_self(), control_port, 342 MACH_MSG_TYPE_MAKE_SEND, &control_port, &poly); 343 344 /* 345 * Register the trigger port with the kernel. 346 */ 347 if (host_set_audit_control_port(mach_host_self(), 348 control_port) != KERN_SUCCESS) { 349 auditd_log_err("Cannot set Mach control port"); 350 return (-1); 351 } else 352 auditd_log_debug("Mach control port registered"); 353 } 354 355 return (0); 356} 357 358/* 359 * Open the trigger messaging mechanism. 360 */ 361int 362auditd_open_trigger(int launchd_flag) 363{ 364 365 return (mach_setup(launchd_flag)); 366} 367 368/* 369 * Close the trigger messaging mechanism. 370 */ 371int 372auditd_close_trigger(void) 373{ 374 375 return (0); 376} 377 378/* 379 * Combined server handler. Called by the mach message loop when there is 380 * a trigger or signal message. 381 */ 382static boolean_t 383auditd_combined_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP) 384{ 385 mach_port_t local_port = InHeadP->msgh_local_port; 386 387 /* Reset the idle time alarm, if used. */ 388 if (max_idletime) 389 alarm(max_idletime); 390 391 if (local_port == signal_port) { 392 int signo = InHeadP->msgh_id; 393 394 switch(signo) { 395 case SIGTERM: 396 case SIGALRM: 397 auditd_terminate(); 398 /* Not reached. */ 399 400 case SIGCHLD: 401 auditd_reap_children(); 402 return (TRUE); 403 404 case SIGHUP: 405 auditd_config_controls(); 406 return (TRUE); 407 408 default: 409 auditd_log_info("Received signal %d", signo); 410 return (TRUE); 411 } 412 } else if (local_port == control_port) { 413 boolean_t result; 414 415 result = audit_triggers_server(InHeadP, OutHeadP); 416 if (!result) 417 result = auditd_control_server(InHeadP, OutHeadP); 418 return (result); 419 } 420 auditd_log_info("Recevied msg on bad port 0x%x.", local_port); 421 return (FALSE); 422} 423 424/* 425 * The main event loop. Wait for trigger messages or signals and handle them. 426 * It should not return unless there is a problem. 427 */ 428void 429auditd_wait_for_events(void) 430{ 431 kern_return_t result; 432 433 /* 434 * Call the mach messaging server loop. 435 */ 436 result = mach_msg_server(auditd_combined_server, MAX_MSG_SIZE, 437 port_set, MACH_MSG_OPTION_NONE); 438} 439 440/* 441 * Implementation of the audit_triggers() MIG simpleroutine. Simply a 442 * wrapper function. This handles input from the kernel on the host 443 * special mach port. 444 */ 445kern_return_t 446audit_triggers(mach_port_t __unused audit_port, int trigger) 447{ 448 449 auditd_handle_trigger(trigger); 450 451 return (KERN_SUCCESS); 452} 453 454/* 455 * Implementation of the auditd_control() MIG simpleroutine. Simply a 456 * wrapper function. This handles input from the audit(1) tool. 457 */ 458kern_return_t 459auditd_control(mach_port_t __unused auditd_port, int trigger) 460{ 461 462 auditd_handle_trigger(trigger); 463 464 return (KERN_SUCCESS); 465} 466 467/* 468 * When we get a signal, we are often not at a clean point. So, little can 469 * be done in the signal handler itself. Instead, we send a message to the 470 * main servicing loop to do proper handling from a non-signal-handler 471 * context. 472 */ 473void 474auditd_relay_signal(int signal) 475{ 476 mach_msg_empty_send_t msg; 477 478 msg.header.msgh_id = signal; 479 msg.header.msgh_remote_port = signal_port; 480 msg.header.msgh_local_port = MACH_PORT_NULL; 481 msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); 482 mach_msg(&(msg.header), MACH_SEND_MSG|MACH_SEND_TIMEOUT, sizeof(msg), 483 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 484} 485