Changeset View
Standalone View
sys/security/audit/audit.c
Show First 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | |||||
#include <security/audit/audit.h> | #include <security/audit/audit.h> | ||||
#include <security/audit/audit_private.h> | #include <security/audit/audit_private.h> | ||||
#include <vm/uma.h> | #include <vm/uma.h> | ||||
FEATURE(audit, "BSM audit support"); | FEATURE(audit, "BSM audit support"); | ||||
static uma_zone_t audit_record_zone; | static uma_zone_t audit_record_zone; | ||||
static uma_zone_t audit_nfsrecord_zone; | |||||
static MALLOC_DEFINE(M_AUDITCRED, "audit_cred", "Audit cred storage"); | static MALLOC_DEFINE(M_AUDITCRED, "audit_cred", "Audit cred storage"); | ||||
MALLOC_DEFINE(M_AUDITDATA, "audit_data", "Audit data storage"); | MALLOC_DEFINE(M_AUDITDATA, "audit_data", "Audit data storage"); | ||||
MALLOC_DEFINE(M_AUDITPATH, "audit_path", "Audit path storage"); | MALLOC_DEFINE(M_AUDITPATH, "audit_path", "Audit path storage"); | ||||
MALLOC_DEFINE(M_AUDITTEXT, "audit_text", "Audit text storage"); | MALLOC_DEFINE(M_AUDITTEXT, "audit_text", "Audit text storage"); | ||||
MALLOC_DEFINE(M_AUDITGIDSET, "audit_gidset", "Audit GID set storage"); | MALLOC_DEFINE(M_AUDITGIDSET, "audit_gidset", "Audit GID set storage"); | ||||
static SYSCTL_NODE(_security, OID_AUTO, audit, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, | static SYSCTL_NODE(_security, OID_AUTO, audit, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, | ||||
"TrustedBSD audit controls"); | "TrustedBSD audit controls"); | ||||
▲ Show 20 Lines • Show All 174 Lines • ▼ Show 20 Lines | audit_record_ctor(void *mem, int size, void *arg, int flags) | ||||
KASSERT(sizeof(*ar) == size, ("audit_record_ctor: wrong size")); | KASSERT(sizeof(*ar) == size, ("audit_record_ctor: wrong size")); | ||||
td = arg; | td = arg; | ||||
ar = mem; | ar = mem; | ||||
bzero(ar, sizeof(*ar)); | bzero(ar, sizeof(*ar)); | ||||
ar->k_ar.ar_magic = AUDIT_RECORD_MAGIC; | ar->k_ar.ar_magic = AUDIT_RECORD_MAGIC; | ||||
nanotime(&ar->k_ar.ar_starttime); | nanotime(&ar->k_ar.ar_starttime); | ||||
ar->kaudit_record_type = AUDIT_SYSCALL_RECORD; | |||||
/* | /* | ||||
* Export the subject credential. | * Export the subject credential. | ||||
*/ | */ | ||||
cred = td->td_ucred; | cred = td->td_ucred; | ||||
cru2x(cred, &ar->k_ar.ar_subj_cred); | cru2x(cred, &ar->k_ar.ar_subj_cred); | ||||
ar->k_ar.ar_subj_ruid = cred->cr_ruid; | ar->k_ar.ar_subj_ruid = cred->cr_ruid; | ||||
ar->k_ar.ar_subj_rgid = cred->cr_rgid; | ar->k_ar.ar_subj_rgid = cred->cr_rgid; | ||||
Show All 37 Lines | if (ar->k_ar.ar_arg_argv != NULL) | ||||
free(ar->k_ar.ar_arg_argv, M_AUDITTEXT); | free(ar->k_ar.ar_arg_argv, M_AUDITTEXT); | ||||
if (ar->k_ar.ar_arg_envv != NULL) | if (ar->k_ar.ar_arg_envv != NULL) | ||||
free(ar->k_ar.ar_arg_envv, M_AUDITTEXT); | free(ar->k_ar.ar_arg_envv, M_AUDITTEXT); | ||||
if (ar->k_ar.ar_arg_groups.gidset != NULL) | if (ar->k_ar.ar_arg_groups.gidset != NULL) | ||||
free(ar->k_ar.ar_arg_groups.gidset, M_AUDITGIDSET); | free(ar->k_ar.ar_arg_groups.gidset, M_AUDITGIDSET); | ||||
} | } | ||||
/* | /* | ||||
* Construct an audit record for the passed nfs server | |||||
* request description. | |||||
*/ | |||||
static int | |||||
audit_nfsrecord_ctor(void *mem, int size, void *arg, int flags) | |||||
{ | |||||
struct kaudit_record *ar; | |||||
struct nfsrv_descript *nd; | |||||
struct ucred *cred; | |||||
KASSERT(sizeof(*ar) == size, ("audit_nfsrecord_ctor: wrong size")); | |||||
nd = arg; | |||||
ar = mem; | |||||
bzero(ar, sizeof(*ar)); | |||||
ar->k_ar.ar_magic = AUDIT_RECORD_MAGIC; | |||||
nanotime(&ar->k_ar.ar_starttime); | |||||
ar->kaudit_record_type = AUDIT_NFSRPC_RECORD; | |||||
/* | |||||
* Export the subject credential. | |||||
*/ | |||||
cred = nd->nd_cred; | |||||
cru2x(cred, &ar->k_ar.ar_subj_cred); | |||||
ar->k_ar.ar_subj_ruid = cred->cr_ruid; | |||||
ar->k_ar.ar_subj_rgid = cred->cr_rgid; | |||||
ar->k_ar.ar_subj_egid = cred->cr_groups[0]; | |||||
ar->k_ar.ar_subj_auid = cred->cr_audit.ai_auid; | |||||
ar->k_ar.ar_subj_asid = cred->cr_audit.ai_asid; | |||||
ar->k_ar.ar_subj_pid = 0; | |||||
ar->k_ar.ar_subj_amask = cred->cr_audit.ai_mask; | |||||
ar->k_ar.ar_subj_term_addr = cred->cr_audit.ai_termid; | |||||
ar->k_ar.ar_jailname[0] = '\0'; | |||||
return (0); | |||||
} | |||||
/* | |||||
* 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) | ||||
{ | { | ||||
Show All 27 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); | ||||
audit_nfsrecord_zone = uma_zcreate("audit_nfsrecord", | |||||
sizeof(struct kaudit_record), audit_nfsrecord_ctor, | |||||
audit_record_dtor, NULL, NULL, UMA_ALIGN_PTR, 0); | |||||
/* First initialisation of audit_syscalls_enabled. */ | /* First initialisation of audit_syscalls_enabled. */ | ||||
audit_syscalls_enabled_update(); | audit_syscalls_enabled_update(); | ||||
/* Initialize the BSM audit subsystem. */ | /* Initialize the BSM audit subsystem. */ | ||||
kau_init(); | kau_init(); | ||||
audit_trigger_init(); | audit_trigger_init(); | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | audit_new(int event, struct thread *td) | ||||
/* | /* | ||||
* 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; | ||||
asomers: As below, I suggest moving this line into audit_record_ctor | |||||
mtx_lock(&audit_mtx); | mtx_lock(&audit_mtx); | ||||
audit_pre_q_len++; | audit_pre_q_len++; | ||||
mtx_unlock(&audit_mtx); | mtx_unlock(&audit_mtx); | ||||
return (ar); | return (ar); | ||||
} | } | ||||
struct kaudit_record * | |||||
audit_nfs_new(int event, struct nfsrv_descript *nd) | |||||
{ | |||||
struct kaudit_record *ar; | |||||
/* This below comment statement (copied from audit_new) becomes untrue in case NFS audit | |||||
* records are created. Would this create any problem?? | |||||
* Note: the number of outstanding uncommitted audit records is | |||||
* limited to the number of concurrent threads servicing system calls | |||||
* in the kernel. | |||||
Not Done Inline ActionsInstead of allocating an audit record for every RPC, could you instead store an audit record for every nfsd thread, and reuse the storage between RPCs? That would be more efficient. asomers: Instead of allocating an audit record for every RPC, could you instead store an audit record… | |||||
Not Done Inline ActionsCan you please give some hints on how it can be done? shivank: Can you please give some hints on how it can be done? | |||||
Not Done Inline ActionsI was hoping that there would already be some kind of struct describing each nfsd worker thread. But I can't find one. Do you know of any? asomers: I was hoping that there would already be some kind of struct describing each nfsd worker thread. | |||||
Not Done Inline ActionsCan you please suggest something here, rmacklem@? shivank: Can you please suggest something here, rmacklem@? | |||||
*/ | |||||
ar = uma_zalloc_arg(audit_nfsrecord_zone, nd, M_WAITOK); | |||||
ar->k_ar.ar_event = event; | |||||
Done Inline ActionsI can't understand what you mean here. In any case, this must be cleaned up before merging. asomers: I can't understand what you mean here. In any case, this must be cleaned up before merging. | |||||
mtx_lock(&audit_mtx); | |||||
audit_pre_q_len++; | |||||
mtx_unlock(&audit_mtx); | |||||
return (ar); | |||||
} | |||||
void | void | ||||
audit_free(struct kaudit_record *ar) | audit_free(struct kaudit_record *ar) | ||||
{ | { | ||||
Done Inline ActionsCan you move this line to audit_nfsrecord_ctor? That would eliminate the possibility of creating a record and freeing it while the kaudit_record_type field is still invalid. asomers: Can you move this line to audit_nfsrecord_ctor? That would eliminate the possibility of… | |||||
switch (ar->kaudit_record_type) { | |||||
case AUDIT_SYSCALL_RECORD: | |||||
uma_zfree(audit_record_zone, ar); | uma_zfree(audit_record_zone, ar); | ||||
break; | |||||
case AUDIT_NFSRPC_RECORD: | |||||
uma_zfree(audit_nfsrecord_zone, ar); | |||||
break; | |||||
default: | |||||
panic("audit_free: invalid case"); | |||||
} | } | ||||
} | |||||
void | void | ||||
audit_commit(struct kaudit_record *ar, int error, int retval) | audit_commit(struct kaudit_record *ar, int error, int retval) | ||||
{ | { | ||||
au_event_t event; | au_event_t event; | ||||
au_class_t class; | au_class_t class; | ||||
au_id_t auid; | au_id_t auid; | ||||
int sorf; | int sorf; | ||||
▲ Show 20 Lines • Show All 270 Lines • ▼ Show 20 Lines | audit_syscall_exit(int error, struct thread *td) | ||||
if (error) | if (error) | ||||
retval = -1; | retval = -1; | ||||
else | else | ||||
retval = td->td_retval[0]; | retval = td->td_retval[0]; | ||||
audit_commit(td->td_ar, error, retval); | audit_commit(td->td_ar, error, retval); | ||||
td->td_ar = NULL; | td->td_ar = NULL; | ||||
td->td_pflags &= ~TDP_AUDITREC; | td->td_pflags &= ~TDP_AUDITREC; | ||||
} | |||||
/* | |||||
* audit_nfsrpc_enter is called before NFS server is about to do a RPC. | |||||
* This function is very similiar to audit_syscall_enter. | |||||
*/ | |||||
void | |||||
audit_nfsrpc_enter(struct nfsrv_descript *nd, struct thread *td) | |||||
{ | |||||
struct au_mask *aumask; | |||||
au_class_t class; | |||||
au_event_t event; | |||||
au_id_t auid; | |||||
int record_needed; | |||||
KASSERT(nd->nd_ar == NULL, ("audit_nfsrpc_enter: nd->nd_ar != NULL")); | |||||
KASSERT((nd->nd_flag & ND_AUDITREC) == 0, | |||||
("audit_nfsrpc_enter: ND_AUDITREC set")); | |||||
/* Currently, NFSv4 is not supported. */ | |||||
Done Inline ActionsIs this lookup table valid for NFS protocols 2, 3, and 4? asomers: Is this lookup table valid for NFS protocols 2, 3, and 4? | |||||
Done Inline ActionsNo, It's valid for v2 and v3 only. v4 is a little different in terms of nd_procnum, Hence, the table is not valid for NFSv4. Also, NFSv4 audit support is yet to be implemented. shivank: No, It's valid for v2 and v3 only. v4 is a little different in terms of nd_procnum, Hence, the… | |||||
if (!(nd->nd_flag & ND_NFSV4)) | |||||
event = nfsrv_auevent[nd->nd_procnum]; | |||||
else | |||||
event = AUE_NULL; | |||||
/* NFS Procedure NULL do nothing. So, no need to audit this event. */ | |||||
if (event == AUE_NULL) | |||||
return; | |||||
memcpy(&(nd->nd_cred->cr_audit), &(td->td_ucred->cr_audit), | |||||
sizeof(struct auditinfo_addr)); | |||||
/* | |||||
* The auid for NFS Audit events is AU_DEFAUDITID. The kernel | |||||
* non-attributable event mask is used as audit mask as all NFS Audit | |||||
* events are triggered from within the kernel. | |||||
*/ | |||||
auid = nd->nd_cred->cr_audit.ai_auid; | |||||
KASSERT(auid == AU_DEFAUDITID, | |||||
("audit_nfsrpc_enter: NFS auid != AU_DEFAUDITID")); | |||||
aumask = &audit_nae_mask; | |||||
class = au_event_class(event); | |||||
if (au_preselect(event, class, aumask, AU_PRS_BOTH)) { | |||||
/* | |||||
* If we're out of space and need to suspend unprivileged | |||||
* processes, do that here rather than trying to allocate | |||||
* another audit record. | |||||
*/ | |||||
if (audit_in_failure && | |||||
priv_check(td, PRIV_AUDIT_FAILSTOP) != 0) { | |||||
cv_wait(&audit_fail_cv, &audit_mtx); | |||||
panic("audit_failing_stop: thread continued"); | |||||
} | |||||
record_needed = 1; | |||||
} else if (audit_pipe_preselect(auid, event, class, AU_PRS_BOTH, 0)) { | |||||
record_needed = 1; | |||||
} else { | |||||
record_needed = 0; | |||||
} | |||||
if (record_needed) { | |||||
nd->nd_ar = audit_nfs_new(event, nd); | |||||
if (nd->nd_ar != NULL) | |||||
nd->nd_flag |= ND_AUDITREC; | |||||
} else | |||||
nd->nd_ar = NULL; | |||||
} | |||||
/* | |||||
* audit_nfsrpc_exit is called each time after NFS server has completed a | |||||
* RPC call. This function is very similiar to audit_syscall_exit. | |||||
*/ | |||||
void | |||||
audit_nfsrpc_exit(struct nfsrv_descript *nd, struct thread *td) | |||||
{ | |||||
int retval; | |||||
int error; | |||||
error = nd->nd_repstat; | |||||
if (error) | |||||
retval = -1; | |||||
else | |||||
retval = *(nd->nd_errp); | |||||
audit_commit(nd->nd_ar, error, retval); | |||||
nd->nd_ar = NULL; | |||||
nd->nd_flag &= ~ND_AUDITREC; | |||||
} | } | ||||
void | void | ||||
audit_cred_copy(struct ucred *src, struct ucred *dest) | audit_cred_copy(struct ucred *src, struct ucred *dest) | ||||
{ | { | ||||
bcopy(&src->cr_audit, &dest->cr_audit, sizeof(dest->cr_audit)); | bcopy(&src->cr_audit, &dest->cr_audit, sizeof(dest->cr_audit)); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 105 Lines • Show Last 20 Lines |
As below, I suggest moving this line into audit_record_ctor