Changeset View
Changeset View
Standalone View
Standalone View
sys/security/audit/audit.c
/*- | /*- | ||||
* SPDX-License-Identifier: BSD-3-Clause | * SPDX-License-Identifier: BSD-3-Clause | ||||
* | * | ||||
* Copyright (c) 1999-2005 Apple Inc. | * Copyright (c) 1999-2005 Apple Inc. | ||||
* Copyright (c) 2006-2007, 2016-2017 Robert N. M. Watson | * Copyright (c) 2006-2007, 2016-2018 Robert N. M. Watson | ||||
* All rights reserved. | * All rights reserved. | ||||
* | * | ||||
* Portions of this software were developed by BAE Systems, the University of | * Portions of this software were developed by BAE Systems, the University of | ||||
* Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL | * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL | ||||
* contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent | * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent | ||||
* Computing (TC) research program. | * Computing (TC) research program. | ||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | static SYSCTL_NODE(_security, OID_AUTO, audit, CTLFLAG_RW, 0, | ||||
"TrustedBSD audit controls"); | "TrustedBSD audit controls"); | ||||
/* | /* | ||||
* Audit control settings that are set/read by system calls and are hence | * Audit control settings that are set/read by system calls and are hence | ||||
* non-static. | * non-static. | ||||
* | * | ||||
* Define the audit control flags. | * Define the audit control flags. | ||||
*/ | */ | ||||
int __read_frequently audit_enabled; | int audit_trail_enabled; | ||||
int audit_suspended; | int audit_trail_suspended; | ||||
#ifdef KDTRACE_HOOKS | |||||
u_int audit_dtrace_enabled; | |||||
#endif | |||||
int __read_frequently audit_syscalls_enabled; | |||||
/* | /* | ||||
* Flags controlling behavior in low storage situations. Should we panic if | * Flags controlling behavior in low storage situations. Should we panic if | ||||
* a write fails? Should we fail stop if we're out of disk space? | * a write fails? Should we fail stop if we're out of disk space? | ||||
*/ | */ | ||||
int audit_panic_on_write_fail; | int audit_panic_on_write_fail; | ||||
int audit_fail_stop; | int audit_fail_stop; | ||||
int audit_argv; | int audit_argv; | ||||
▲ Show 20 Lines • Show All 82 Lines • ▼ Show 20 Lines | |||||
#define KINFO_LOCK_INIT() rw_init(&audit_kinfo_lock, \ | #define KINFO_LOCK_INIT() rw_init(&audit_kinfo_lock, \ | ||||
"audit_kinfo_lock") | "audit_kinfo_lock") | ||||
#define KINFO_RLOCK() rw_rlock(&audit_kinfo_lock) | #define KINFO_RLOCK() rw_rlock(&audit_kinfo_lock) | ||||
#define KINFO_WLOCK() rw_wlock(&audit_kinfo_lock) | #define KINFO_WLOCK() rw_wlock(&audit_kinfo_lock) | ||||
#define KINFO_RUNLOCK() rw_runlock(&audit_kinfo_lock) | #define KINFO_RUNLOCK() rw_runlock(&audit_kinfo_lock) | ||||
#define KINFO_WUNLOCK() rw_wunlock(&audit_kinfo_lock) | #define KINFO_WUNLOCK() rw_wunlock(&audit_kinfo_lock) | ||||
/* | |||||
* Check various policies to see if we should enable system-call audit hooks. | |||||
* Note that despite the mutex being held, we want to assign a value exactly | |||||
* once, as checks of the flag are performed lock-free for performance | |||||
* reasons. The mutex is used to get a consistent snapshot of policy state -- | |||||
* e.g., safely accessing the two audit_trail flags. | |||||
*/ | |||||
void | void | ||||
audit_syscalls_enabled_update(void) | |||||
{ | |||||
mtx_lock(&audit_mtx); | |||||
#ifdef KDTRACE_HOOKS | |||||
if (audit_dtrace_enabled) | |||||
audit_syscalls_enabled = 1; | |||||
else { | |||||
#endif | |||||
if (audit_trail_enabled && !audit_trail_suspended) | |||||
audit_syscalls_enabled = 1; | |||||
else | |||||
audit_syscalls_enabled = 0; | |||||
#ifdef KDTRACE_HOOKS | |||||
} | |||||
#endif | |||||
mtx_unlock(&audit_mtx); | |||||
} | |||||
void | |||||
audit_set_kinfo(struct auditinfo_addr *ak) | audit_set_kinfo(struct auditinfo_addr *ak) | ||||
{ | { | ||||
KASSERT(ak->ai_termid.at_type == AU_IPv4 || | KASSERT(ak->ai_termid.at_type == AU_IPv4 || | ||||
ak->ai_termid.at_type == AU_IPv6, | ak->ai_termid.at_type == AU_IPv6, | ||||
("audit_set_kinfo: invalid address type")); | ("audit_set_kinfo: invalid address type")); | ||||
KINFO_WLOCK(); | KINFO_WLOCK(); | ||||
▲ Show 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | |||||
* Initialize the Audit subsystem: configuration state, work queue, | * Initialize the Audit subsystem: configuration state, work queue, | ||||
* synchronization primitives, worker thread, and trigger device node. Also | * synchronization primitives, worker thread, and trigger device node. Also | ||||
* call into the BSM assembly code to initialize it. | * call into the BSM assembly code to initialize it. | ||||
*/ | */ | ||||
static void | static void | ||||
audit_init(void) | audit_init(void) | ||||
{ | { | ||||
audit_enabled = 0; | audit_trail_enabled = 0; | ||||
audit_suspended = 0; | audit_trail_suspended = 0; | ||||
audit_syscalls_enabled = 0; | |||||
audit_panic_on_write_fail = 0; | audit_panic_on_write_fail = 0; | ||||
audit_fail_stop = 0; | audit_fail_stop = 0; | ||||
audit_in_failure = 0; | audit_in_failure = 0; | ||||
audit_argv = 0; | audit_argv = 0; | ||||
audit_arge = 0; | audit_arge = 0; | ||||
audit_fstat.af_filesz = 0; /* '0' means unset, unbounded. */ | audit_fstat.af_filesz = 0; /* '0' means unset, unbounded. */ | ||||
audit_fstat.af_currsz = 0; | audit_fstat.af_currsz = 0; | ||||
Show All 16 Lines | audit_init(void) | ||||
cv_init(&audit_worker_cv, "audit_worker_cv"); | cv_init(&audit_worker_cv, "audit_worker_cv"); | ||||
cv_init(&audit_watermark_cv, "audit_watermark_cv"); | cv_init(&audit_watermark_cv, "audit_watermark_cv"); | ||||
cv_init(&audit_fail_cv, "audit_fail_cv"); | cv_init(&audit_fail_cv, "audit_fail_cv"); | ||||
audit_record_zone = uma_zcreate("audit_record", | audit_record_zone = uma_zcreate("audit_record", | ||||
sizeof(struct kaudit_record), audit_record_ctor, | sizeof(struct kaudit_record), audit_record_ctor, | ||||
audit_record_dtor, NULL, NULL, UMA_ALIGN_PTR, 0); | audit_record_dtor, NULL, NULL, UMA_ALIGN_PTR, 0); | ||||
/* First initialisation of audit_syscalls_enabled. */ | |||||
audit_syscalls_enabled_update(); | |||||
/* Initialize the BSM audit subsystem. */ | /* Initialize the BSM audit subsystem. */ | ||||
kau_init(); | kau_init(); | ||||
audit_trigger_init(); | audit_trigger_init(); | ||||
/* Register shutdown handler. */ | /* Register shutdown handler. */ | ||||
EVENTHANDLER_REGISTER(shutdown_pre_sync, audit_shutdown, NULL, | EVENTHANDLER_REGISTER(shutdown_pre_sync, audit_shutdown, NULL, | ||||
SHUTDOWN_PRI_FIRST); | SHUTDOWN_PRI_FIRST); | ||||
Show All 25 Lines | |||||
struct kaudit_record * | struct kaudit_record * | ||||
currecord(void) | currecord(void) | ||||
{ | { | ||||
return (curthread->td_ar); | return (curthread->td_ar); | ||||
} | } | ||||
/* | /* | ||||
* XXXAUDIT: There are a number of races present in the code below due to | |||||
* release and re-grab of the mutex. The code should be revised to become | |||||
* slightly less racy. | |||||
* | |||||
* XXXAUDIT: Shouldn't there be logic here to sleep waiting on available | * XXXAUDIT: Shouldn't there be logic here to sleep waiting on available | ||||
* pre_q space, suspending the system call until there is room? | * pre_q space, suspending the system call until there is room? | ||||
*/ | */ | ||||
struct kaudit_record * | struct kaudit_record * | ||||
audit_new(int event, struct thread *td) | audit_new(int event, struct thread *td) | ||||
{ | { | ||||
struct kaudit_record *ar; | struct kaudit_record *ar; | ||||
int no_record; | |||||
mtx_lock(&audit_mtx); | |||||
no_record = (audit_suspended || !audit_enabled); | |||||
asomers: I don't think you can remove this check unless you add similar logic to `audit_proc_coredump`. | |||||
mtx_unlock(&audit_mtx); | |||||
if (no_record) | |||||
return (NULL); | |||||
/* | /* | ||||
* Note: the number of outstanding uncommitted audit records is | * Note: the number of outstanding uncommitted audit records is | ||||
* limited to the number of concurrent threads servicing system calls | * limited to the number of concurrent threads servicing system calls | ||||
* in the kernel. | * in the kernel. | ||||
*/ | */ | ||||
ar = uma_zalloc_arg(audit_record_zone, td, M_WAITOK); | ar = uma_zalloc_arg(audit_record_zone, td, M_WAITOK); | ||||
ar->k_ar.ar_event = event; | ar->k_ar.ar_event = event; | ||||
▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | if ((ar->k_ar_commit & (AR_PRESELECT_TRAIL | AR_PRESELECT_PIPE | | ||||
mtx_unlock(&audit_mtx); | mtx_unlock(&audit_mtx); | ||||
audit_free(ar); | audit_free(ar); | ||||
return; | return; | ||||
} | } | ||||
/* | /* | ||||
* Note: it could be that some records initiated while audit was | * Note: it could be that some records initiated while audit was | ||||
* enabled should still be committed? | * enabled should still be committed? | ||||
* | |||||
* NB: The check here is not for audit_syscalls because any | |||||
* DTrace-related obligations have been fulfilled above -- we're just | |||||
* down to the trail and pipes now. | |||||
*/ | */ | ||||
mtx_lock(&audit_mtx); | mtx_lock(&audit_mtx); | ||||
if (audit_suspended || !audit_enabled) { | if (audit_trail_suspended || !audit_trail_enabled) { | ||||
audit_pre_q_len--; | audit_pre_q_len--; | ||||
mtx_unlock(&audit_mtx); | mtx_unlock(&audit_mtx); | ||||
audit_free(ar); | audit_free(ar); | ||||
return; | return; | ||||
} | } | ||||
/* | /* | ||||
* Constrain the number of committed audit records based on the | * Constrain the number of committed audit records based on the | ||||
Show All 9 Lines | #endif | ||||
mtx_unlock(&audit_mtx); | mtx_unlock(&audit_mtx); | ||||
} | } | ||||
/* | /* | ||||
* audit_syscall_enter() is called on entry to each system call. It is | * audit_syscall_enter() is called on entry to each system call. It is | ||||
* responsible for deciding whether or not to audit the call (preselection), | * responsible for deciding whether or not to audit the call (preselection), | ||||
* and if so, allocating a per-thread audit record. audit_new() will fill in | * and if so, allocating a per-thread audit record. audit_new() will fill in | ||||
* basic thread/credential properties. | * basic thread/credential properties. | ||||
* | |||||
* This function will be entered only if audit_syscalls_enabled was set in the | |||||
* macro wrapper for this function. It could be cleared by the time this | |||||
* function runs, but that is an acceptable race. | |||||
*/ | */ | ||||
void | void | ||||
audit_syscall_enter(unsigned short code, struct thread *td) | audit_syscall_enter(unsigned short code, struct thread *td) | ||||
{ | { | ||||
struct au_mask *aumask; | struct au_mask *aumask; | ||||
#ifdef KDTRACE_HOOKS | #ifdef KDTRACE_HOOKS | ||||
void *dtaudit_state; | void *dtaudit_state; | ||||
#endif | #endif | ||||
▲ Show 20 Lines • Show All 244 Lines • Show Last 20 Lines |
I don't think you can remove this check unless you add similar logic to audit_proc_coredump. Otherwise it will generate AUE_CORE records even when auditing is disabled.