Changeset View
Standalone View
sys/security/audit/audit.c
/*- | /*- | ||||
* Copyright (c) 1999-2005 Apple Inc. | * Copyright (c) 1999-2005 Apple Inc. | ||||
* Copyright (c) 2006-2007 Robert N. M. Watson | * Copyright (c) 2006-2007, 2016 Robert N. M. Watson | ||||
jonathan: And 2017 at all? | |||||
Not Done Inline ActionsThis code was written and distributed via GitHub in 2016, hence the date. If I make substantive changes (e.g., during review) I will be sure to update it :-). rwatson: This code was written and distributed via GitHub in 2016, hence the date. If I make substantive… | |||||
* All rights reserved. | * All rights reserved. | ||||
* | * | ||||
* Portions of this software were developed by BAE Systems, the University of | |||||
* Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL | |||||
* contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent | |||||
* 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 | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
* 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | ||||
* notice, this list of conditions and the following disclaimer in the | * notice, this list of conditions and the following disclaimer in the | ||||
* documentation and/or other materials provided with the distribution. | * documentation and/or other materials provided with the distribution. | ||||
▲ Show 20 Lines • Show All 143 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Condition variable for auditing threads wait on when in fail-stop mode. | * Condition variable for auditing threads wait on when in fail-stop mode. | ||||
* Threads wait on this CV forever (and ever), never seeing the light of day | * Threads wait on this CV forever (and ever), never seeing the light of day | ||||
* again. | * again. | ||||
*/ | */ | ||||
static struct cv audit_fail_cv; | static struct cv audit_fail_cv; | ||||
/* | /* | ||||
* Optional DTrace audit provider support: function pointers for preselection | |||||
* and commit events. | |||||
*/ | |||||
#ifdef KDTRACE_HOOKS | |||||
void *(*dtaudit_hook_preselect)(au_id_t auid, au_event_t event, | |||||
au_class_t class); | |||||
int (*dtaudit_hook_commit)(struct kaudit_record *kar, au_id_t auid, | |||||
au_event_t event, au_class_t class, int sorf); | |||||
void (*dtaudit_hook_bsm)(struct kaudit_record *kar, au_id_t auid, | |||||
au_event_t event, au_class_t class, int sorf, | |||||
void *bsm_data, size_t bsm_lenlen); | |||||
#endif | |||||
/* | |||||
* Kernel audit information. This will store the current audit address | * Kernel audit information. This will store the current audit address | ||||
* or host information that the kernel will use when it's generating | * or host information that the kernel will use when it's generating | ||||
* audit records. This data is modified by the A_GET{SET}KAUDIT auditon(2) | * audit records. This data is modified by the A_GET{SET}KAUDIT auditon(2) | ||||
* command. | * command. | ||||
*/ | */ | ||||
static struct auditinfo_addr audit_kinfo; | static struct auditinfo_addr audit_kinfo; | ||||
static struct rwlock audit_kinfo_lock; | static struct rwlock audit_kinfo_lock; | ||||
▲ Show 20 Lines • Show All 232 Lines • ▼ Show 20 Lines | audit_commit(struct kaudit_record *ar, int error, int retval) | ||||
au_class_t class; | au_class_t class; | ||||
au_id_t auid; | au_id_t auid; | ||||
int sorf; | int sorf; | ||||
struct au_mask *aumask; | struct au_mask *aumask; | ||||
if (ar == NULL) | if (ar == NULL) | ||||
return; | return; | ||||
ar->k_ar.ar_errno = error; | |||||
ar->k_ar.ar_retval = retval; | |||||
nanotime(&ar->k_ar.ar_endtime); | |||||
/* | /* | ||||
* Decide whether to commit the audit record by checking the error | * Decide whether to commit the audit record by checking the error | ||||
* value from the system call and using the appropriate audit mask. | * value from the system call and using the appropriate audit mask. | ||||
*/ | */ | ||||
if (ar->k_ar.ar_subj_auid == AU_DEFAUDITID) | if (ar->k_ar.ar_subj_auid == AU_DEFAUDITID) | ||||
aumask = &audit_nae_mask; | aumask = &audit_nae_mask; | ||||
else | else | ||||
aumask = &ar->k_ar.ar_subj_amask; | aumask = &ar->k_ar.ar_subj_amask; | ||||
Show All 35 Lines | audit_commit(struct kaudit_record *ar, int error, int retval) | ||||
class = au_event_class(event); | class = au_event_class(event); | ||||
ar->k_ar_commit |= AR_COMMIT_KERNEL; | ar->k_ar_commit |= AR_COMMIT_KERNEL; | ||||
if (au_preselect(event, class, aumask, sorf) != 0) | if (au_preselect(event, class, aumask, sorf) != 0) | ||||
ar->k_ar_commit |= AR_PRESELECT_TRAIL; | ar->k_ar_commit |= AR_PRESELECT_TRAIL; | ||||
if (audit_pipe_preselect(auid, event, class, sorf, | if (audit_pipe_preselect(auid, event, class, sorf, | ||||
ar->k_ar_commit & AR_PRESELECT_TRAIL) != 0) | ar->k_ar_commit & AR_PRESELECT_TRAIL) != 0) | ||||
ar->k_ar_commit |= AR_PRESELECT_PIPE; | ar->k_ar_commit |= AR_PRESELECT_PIPE; | ||||
#ifdef KDTRACE_HOOKS | |||||
/* | |||||
* Expose the audit record to DTrace, both to allow the "commit" probe | |||||
* to fire if it's desirable, and also to allow a decision to be made | |||||
* about later firing with BSM in the audit worker. | |||||
*/ | |||||
if (dtaudit_hook_commit != NULL) { | |||||
if (dtaudit_hook_commit(ar, auid, event, class, sorf) != 0) | |||||
ar->k_ar_commit |= AR_PRESELECT_DTRACE; | |||||
} | |||||
#endif | |||||
if ((ar->k_ar_commit & (AR_PRESELECT_TRAIL | AR_PRESELECT_PIPE | | if ((ar->k_ar_commit & (AR_PRESELECT_TRAIL | AR_PRESELECT_PIPE | | ||||
AR_PRESELECT_USER_TRAIL | AR_PRESELECT_USER_PIPE)) == 0) { | AR_PRESELECT_USER_TRAIL | AR_PRESELECT_USER_PIPE | | ||||
AR_PRESELECT_DTRACE)) == 0) { | |||||
mtx_lock(&audit_mtx); | mtx_lock(&audit_mtx); | ||||
audit_pre_q_len--; | audit_pre_q_len--; | ||||
mtx_unlock(&audit_mtx); | mtx_unlock(&audit_mtx); | ||||
audit_free(ar); | audit_free(ar); | ||||
return; | return; | ||||
} | } | ||||
ar->k_ar.ar_errno = error; | |||||
ar->k_ar.ar_retval = retval; | |||||
nanotime(&ar->k_ar.ar_endtime); | |||||
/* | /* | ||||
* 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? | ||||
*/ | */ | ||||
mtx_lock(&audit_mtx); | mtx_lock(&audit_mtx); | ||||
if (audit_suspended || !audit_enabled) { | if (audit_suspended || !audit_enabled) { | ||||
audit_pre_q_len--; | audit_pre_q_len--; | ||||
mtx_unlock(&audit_mtx); | mtx_unlock(&audit_mtx); | ||||
Show All 20 Lines | |||||
* 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. | ||||
*/ | */ | ||||
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 | |||||
void *dtaudit_state; | |||||
#endif | |||||
au_class_t class; | au_class_t class; | ||||
au_event_t event; | au_event_t event; | ||||
au_id_t auid; | au_id_t auid; | ||||
int record_needed; | |||||
KASSERT(td->td_ar == NULL, ("audit_syscall_enter: td->td_ar != NULL")); | KASSERT(td->td_ar == NULL, ("audit_syscall_enter: td->td_ar != NULL")); | ||||
KASSERT((td->td_pflags & TDP_AUDITREC) == 0, | KASSERT((td->td_pflags & TDP_AUDITREC) == 0, | ||||
("audit_syscall_enter: TDP_AUDITREC set")); | ("audit_syscall_enter: TDP_AUDITREC set")); | ||||
/* | /* | ||||
* In FreeBSD, each ABI has its own system call table, and hence | * In FreeBSD, each ABI has its own system call table, and hence | ||||
* mapping of system call codes to audit events. Convert the code to | * mapping of system call codes to audit events. Convert the code to | ||||
Show All 15 Lines | #endif | ||||
*/ | */ | ||||
auid = td->td_ucred->cr_audit.ai_auid; | auid = td->td_ucred->cr_audit.ai_auid; | ||||
if (auid == AU_DEFAUDITID) | if (auid == AU_DEFAUDITID) | ||||
aumask = &audit_nae_mask; | aumask = &audit_nae_mask; | ||||
else | else | ||||
aumask = &td->td_ucred->cr_audit.ai_mask; | aumask = &td->td_ucred->cr_audit.ai_mask; | ||||
/* | /* | ||||
* Allocate an audit record, if preselection allows it, and store in | * Determine whether trail or pipe preselection would like an audit | ||||
* the thread for later use. | * record allocated for this system call. | ||||
*/ | */ | ||||
class = au_event_class(event); | class = au_event_class(event); | ||||
if (au_preselect(event, class, aumask, AU_PRS_BOTH)) { | if (au_preselect(event, class, aumask, AU_PRS_BOTH)) { | ||||
/* | /* | ||||
* If we're out of space and need to suspend unprivileged | * If we're out of space and need to suspend unprivileged | ||||
* processes, do that here rather than trying to allocate | * processes, do that here rather than trying to allocate | ||||
* another audit record. | * another audit record. | ||||
* | * | ||||
* Note: we might wish to be able to continue here in the | * Note: we might wish to be able to continue here in the | ||||
* future, if the system recovers. That should be possible | * future, if the system recovers. That should be possible | ||||
* by means of checking the condition in a loop around | * by means of checking the condition in a loop around | ||||
* cv_wait(). It might be desirable to reevaluate whether an | * cv_wait(). It might be desirable to reevaluate whether an | ||||
* audit record is still required for this event by | * audit record is still required for this event by | ||||
* re-calling au_preselect(). | * re-calling au_preselect(). | ||||
*/ | */ | ||||
if (audit_in_failure && | if (audit_in_failure && | ||||
priv_check(td, PRIV_AUDIT_FAILSTOP) != 0) { | priv_check(td, PRIV_AUDIT_FAILSTOP) != 0) { | ||||
cv_wait(&audit_fail_cv, &audit_mtx); | cv_wait(&audit_fail_cv, &audit_mtx); | ||||
panic("audit_failing_stop: thread continued"); | panic("audit_failing_stop: thread continued"); | ||||
} | } | ||||
td->td_ar = audit_new(event, td); | record_needed = 1; | ||||
if (td->td_ar != NULL) | |||||
td->td_pflags |= TDP_AUDITREC; | |||||
} else if (audit_pipe_preselect(auid, event, class, AU_PRS_BOTH, 0)) { | } else if (audit_pipe_preselect(auid, event, class, AU_PRS_BOTH, 0)) { | ||||
record_needed = 1; | |||||
} else { | |||||
record_needed = 0; | |||||
} | |||||
/* | |||||
* After audit trails and pipes have made their policy choices, DTrace | |||||
* may request that records be generated as well. This is a slightly | |||||
* complex affair, as the DTrace audit provider needs the audit | |||||
* framework to maintain some state on the audit record, which has not | |||||
* been allocated at the point where the decision has to be made. | |||||
* This hook must run even if we are not changing the decision, as | |||||
* DTrace may want to stick event state onto a record we were going to | |||||
* produce due to the trail or pipes. The event state returned by the | |||||
* DTrace provider must be safe without locks held between here and | |||||
* below -- i.e., dtaudit_state must must refer to stable memory. | |||||
Done Inline ActionsI don't believe that the name ene has been used in this function: should the comment refer to dtaudit_state instead? jonathan: I don't believe that the name `ene` has been used in this function: should the comment refer to… | |||||
Done Inline ActionsThis reflects some evolution of the code towards trying to hide dtaudit details from the audit framework here. I'll update the comment. rwatson: This reflects some evolution of the code towards trying to hide dtaudit details from the audit… | |||||
*/ | |||||
#ifdef KDTRACE_HOOKS | |||||
dtaudit_state = NULL; | |||||
if (dtaudit_hook_preselect != NULL) { | |||||
dtaudit_state = dtaudit_hook_preselect(auid, event, class); | |||||
if (dtaudit_state != NULL) | |||||
record_needed = 1; | |||||
} | |||||
#endif | |||||
/* | |||||
* If a record is required, allocate it and attach it to the thread | |||||
Done Inline Actionsallocated -> allocate? jonathan: `allocated` -> `allocate`? | |||||
Done Inline ActionsWill fix, thanks! rwatson: Will fix, thanks! | |||||
* for use throughout the system call. Also attach DTrace state if | |||||
* required. | |||||
* | |||||
* XXXRW: If we decide to reference count the evname_elem underlying | |||||
Not Done Inline ActionsDitto re: ene... perhaps referencing evname_elem would make sense here? Is evname_elem likely to grow a reference count in the future? If so, might it make sense to introduce an EVNAME_PUT that #ifdefs away to nothing for now but will help prevent future foot-shooting? Perhaps not... jonathan: Ditto re: `ene`... perhaps referencing `evname_elem` would make sense here?
Is `evname_elem`… | |||||
Not Done Inline ActionsI think it will, but I'm not quite sure how soon. I'm not sure I want to put in placeholders though, as they will probably prove to be incorrect. But keeping the comment here to warn future comers is definitely useful. rwatson: I think it will, but I'm not quite sure how soon. I'm not sure I want to put in placeholders… | |||||
* dtaudit_state, we will need to free here if no record is allocated | |||||
* or allocatable. | |||||
*/ | |||||
if (record_needed) { | |||||
td->td_ar = audit_new(event, td); | td->td_ar = audit_new(event, td); | ||||
if (td->td_ar != NULL) | if (td->td_ar != NULL) { | ||||
td->td_pflags |= TDP_AUDITREC; | td->td_pflags |= TDP_AUDITREC; | ||||
#ifdef KDTRACE_HOOKS | |||||
td->td_ar->k_dtaudit_state = dtaudit_state; | |||||
#endif | |||||
} | |||||
} else | } else | ||||
td->td_ar = NULL; | td->td_ar = NULL; | ||||
} | } | ||||
/* | /* | ||||
* audit_syscall_exit() is called from the return of every system call, or in | * audit_syscall_exit() is called from the return of every system call, or in | ||||
* the event of exit1(), during the execution of exit1(). It is responsible | * the event of exit1(), during the execution of exit1(). It is responsible | ||||
* for committing the audit record, if any, along with return condition. | * for committing the audit record, if any, along with return condition. | ||||
▲ Show 20 Lines • Show All 134 Lines • Show Last 20 Lines |
And 2017 at all?