audit_core.c revision 005d3feb53a9a10272d4a24b03991575d6a9bcb3
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/kmem.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/user.h>
#include <sys/stropts.h>
#include <sys/systm.h>
#include <sys/pathname.h>
#include <sys/debug.h>
#include <sys/cred_impl.h>
#include <sys/zone.h>
#include <sys/modctl.h>
#include <sys/sysconf.h>
#include <c2/audit.h>
#include <c2/audit_kernel.h>
#include <c2/audit_kevents.h>
#include <c2/audit_record.h>
struct p_audit_data *pad0;
struct t_audit_data *tad0;
extern uint_t num_syscall; /* size of audit_s2e table */
extern kmutex_t pidlock; /* proc table lock */
void
audit_init()
{
kthread_t *au_thread;
auditinfo_addr_t *ainfo;
struct audit_path apempty;
/*
* If the c2audit module is explicitely excluded in /etc/system,
* it cannot be loaded later (e.g. using modload). Make a notice
* that the module won't be present and do nothing.
*/
if (mod_sysctl(SYS_CHECK_EXCLUDE, "c2audit") != 0) {
audit_active = C2AUDIT_DISABLED;
return;
}
/* c2audit module can be loaded anytime */
audit_active = C2AUDIT_UNLOADED;
/* initialize the process audit data (pad) memory allocator */
au_pad_init();
/* initialize the zone audit context */
au_zone_setup();
/* inital thread structure */
tad0 = kmem_zalloc(sizeof (struct t_audit_data), KM_SLEEP);
/* initial process structure */
pad0 = kmem_cache_alloc(au_pad_cache, KM_SLEEP);
bzero(&pad0->pad_data, sizeof (pad0->pad_data));
curthread->t_audit_data = tad0;
curproc->p_audit_data = pad0;
/*
* The kernel allocates a bunch of threads make sure they have
* a valid tad
*/
mutex_enter(&pidlock);
au_thread = curthread;
do {
if (T2A(au_thread) == NULL) {
T2A(au_thread) = tad0;
}
au_thread = au_thread->t_next;
} while (au_thread != curthread);
tad0->tad_ad = NULL;
mutex_exit(&pidlock);
/*
* Initialize audit context in our cred (kcred).
* No copy-on-write needed here because it's so early in init.
*/
ainfo = crgetauinfo_modifiable(kcred);
ASSERT(ainfo != NULL);
bzero(ainfo, sizeof (auditinfo_addr_t));
ainfo->ai_auid = AU_NOAUDITID;
/* fabricate an empty audit_path to extend */
apempty.audp_cnt = 0;
apempty.audp_sect[0] = (char *)(&apempty.audp_sect[1]);
pad0->pad_root = au_pathdup(&apempty, 1, 2);
bcopy("/", pad0->pad_root->audp_sect[0], 2);
au_pathhold(pad0->pad_root);
pad0->pad_cwd = pad0->pad_root;
}
/*
* Check for any pending changes to the audit context for the given proc.
* p_crlock and pad_lock for the process are acquired here. Caller is
* responsible for assuring the process doesn't go away. If context is
* updated, the specified cralloc'ed cred will be used, otherwise it's freed.
* If no cred is given, it will be cralloc'ed here and caller assures that
* it is safe to allocate memory.
*/
void
audit_update_context(proc_t *p, cred_t *ncr)
{
struct p_audit_data *pad;
cred_t *newcred = ncr;
pad = P2A(p);
if (pad == NULL) {
if (newcred != NULL)
crfree(newcred);
return;
}
/* If a mask update is pending, take care of it. */
if (pad->pad_flags & PAD_SETMASK) {
auditinfo_addr_t *ainfo;
if (newcred == NULL)
newcred = cralloc();
mutex_enter(&pad->pad_lock);
/* the condition may have been handled by the time we lock */
if (pad->pad_flags & PAD_SETMASK) {
ainfo = crgetauinfo_modifiable(newcred);
if (ainfo == NULL) {
mutex_enter(&pad->pad_lock);
crfree(newcred);
return;
}
mutex_enter(&p->p_crlock);
crcopy_to(p->p_cred, newcred);
p->p_cred = newcred;
ainfo->ai_mask = pad->pad_newmask;
/* Unlock and cleanup. */
mutex_exit(&p->p_crlock);
pad->pad_flags &= ~PAD_SETMASK;
/*
* For curproc, assure that our thread points to right
* cred, so CRED() will be correct. Otherwise, no need
* to broadcast changes (via set_proc_pre_sys), since
* t_pre_sys is ALWAYS on when audit is enabled... due
* to syscall auditing.
*/
if (p == curproc)
crset(p, newcred);
else
crfree(newcred);
} else {
crfree(newcred);
}
mutex_exit(&pad->pad_lock);
} else {
if (newcred != NULL)
crfree(newcred);
}
}
/*
* ROUTINE: AUDIT_NEWPROC
* PURPOSE: initialize the child p_audit_data structure
* CALLBY: GETPROC
* NOTE: All threads for the parent process are locked at this point.
* We are essentially running singled threaded for this reason.
* GETPROC is called when system creates a new process.
* By the time AUDIT_NEWPROC is called, the child proc
* structure has already been initialized. What we need
* to do is to allocate the child p_audit_data and
* initialize it with the content of current parent process.
*/
void
audit_newproc(struct proc *cp) /* initialized child proc structure */
{
p_audit_data_t *pad; /* child process audit data */
p_audit_data_t *opad; /* parent process audit data */
pad = kmem_cache_alloc(au_pad_cache, KM_SLEEP);
P2A(cp) = pad;
opad = P2A(curproc);
/*
* copy the audit data. Note that all threads of current
* process have been "held". Thus there is no race condition
* here with mutiple threads trying to alter the cwrd
* structure (such as releasing it).
*
* The audit context in the cred is "duplicated" for the new
* proc by elsewhere crhold'ing the parent's cred which it shares.
*
* We still want to hold things since auditon() [A_SETUMASK,
* A_SETSMASK] could be walking through the processes to
* update things.
*/
mutex_enter(&opad->pad_lock); /* lock opad structure during copy */
pad->pad_data = opad->pad_data; /* copy parent's process audit data */
au_pathhold(pad->pad_root);
au_pathhold(pad->pad_cwd);
mutex_exit(&opad->pad_lock); /* current proc will keep cwrd open */
/*
* If we are in the limited mode, there is nothing to audit and
* there could not have been anything to audit, since it is not
* possible to switch from the full mode into the limited mode
* once the full mode is set.
*/
if (audit_active != C2AUDIT_LOADED)
return;
/*
* finish auditing of parent here so that it will be done
* before child has a chance to run. We include the child
* pid since the return value in the return token is a dummy
* one and contains no useful information (it is included to
* make the audit record structure consistant).
*
* tad_flag is set if auditing is on
*/
if (((t_audit_data_t *)T2A(curthread))->tad_flag)
au_uwrite(au_to_arg32(0, "child PID", (uint32_t)cp->p_pid));
/*
* finish up audit record generation here because child process
* is set to run before parent process. We distinguish here
* between FORK, FORK1, or VFORK by the saved system call ID.
*/
audit_finish(0, ((t_audit_data_t *)T2A(curthread))->tad_scid, 0, 0);
}
/*
* ROUTINE: AUDIT_PFREE
* PURPOSE: deallocate the per-process udit data structure
* CALLBY: EXIT
* FORK_FAIL
* NOTE: all lwp except current one have stopped in SEXITLWPS
* why we are single threaded?
* . all lwp except current one have stopped in SEXITLWPS.
*/
void
audit_pfree(struct proc *p) /* proc structure to be freed */
{ /* AUDIT_PFREE */
p_audit_data_t *pad;
pad = P2A(p);
/* better be a per process audit data structure */
ASSERT(pad != (p_audit_data_t *)0);
if (pad == pad0) {
return;
}
/* deallocate all auditing resources for this process */
au_pathrele(pad->pad_root);
au_pathrele(pad->pad_cwd);
/*
* Since the pad structure is completely overwritten after alloc,
* we don't bother to clear it.
*/
kmem_cache_free(au_pad_cache, pad);
}
/*
* ROUTINE: AUDIT_THREAD_CREATE
* PURPOSE: allocate per-process thread audit data structure
* CALLBY: THREAD_CREATE
* NOTE: This is called just after *t was bzero'd.
* We are single threaded in this routine.
* TODO:
* QUESTION:
*/
void
audit_thread_create(kthread_id_t t)
{
t_audit_data_t *tad; /* per-thread audit data */
tad = kmem_zalloc(sizeof (struct t_audit_data), KM_SLEEP);
T2A(t) = tad; /* set up thread audit data ptr */
tad->tad_thread = t; /* back ptr to thread: DEBUG */
}
/*
* ROUTINE: AUDIT_THREAD_FREE
* PURPOSE: free the per-thread audit data structure
* CALLBY: THREAD_FREE
* NOTE: most thread data is clear after return
*/
void
audit_thread_free(kthread_t *t)
{
t_audit_data_t *tad;
au_defer_info_t *attr;
tad = T2A(t);
/* thread audit data must still be set */
if (tad == tad0) {
return;
}
if (tad == NULL) {
return;
}
t->t_audit_data = 0;
/* must not have any audit record residual */
ASSERT(tad->tad_ad == NULL);
/* saved path must be empty */
ASSERT(tad->tad_aupath == NULL);
if (tad->tad_atpath)
au_pathrele(tad->tad_atpath);
if (audit_active == C2AUDIT_LOADED) {
attr = tad->tad_defer_head;
while (attr != NULL) {
au_defer_info_t *tmp_attr = attr;
au_free_rec(attr->audi_ad);
attr = attr->audi_next;
kmem_free(tmp_attr, sizeof (au_defer_info_t));
}
}
kmem_free(tad, sizeof (*tad));
}
/*
* ROUTINE: AUDIT_FALLOC
* PURPOSE: allocating a new file structure
* CALLBY: FALLOC
* NOTE: file structure already initialized
* TODO:
* QUESTION:
*/
void
audit_falloc(struct file *fp)
{ /* AUDIT_FALLOC */
f_audit_data_t *fad;
/* allocate per file audit structure if there a'int any */
ASSERT(F2A(fp) == NULL);
fad = kmem_zalloc(sizeof (struct f_audit_data), KM_SLEEP);
F2A(fp) = fad;
fad->fad_thread = curthread; /* file audit data back ptr; DEBUG */
}
/*
* ROUTINE: AUDIT_UNFALLOC
* PURPOSE: deallocate file audit data structure
* CALLBY: CLOSEF
* UNFALLOC
* NOTE:
* TODO:
* QUESTION:
*/
void
audit_unfalloc(struct file *fp)
{
f_audit_data_t *fad;
fad = F2A(fp);
if (!fad) {
return;
}
if (fad->fad_aupath != NULL) {
au_pathrele(fad->fad_aupath);
}
fp->f_audit_data = 0;
kmem_free(fad, sizeof (struct f_audit_data));
}
uint32_t
audit_getstate()
{
return (audit_active == C2AUDIT_LOADED &&
((AU_AUDIT_MASK) & U2A(u)->tad_audit));
}