audit_start.c revision 45916cd2fec6e79bca5dee0421bd39e3c2910d1e
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This file contains the envelope code for system call auditing.
*/
#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/tsol/label.h>
#include <c2/audit.h>
#include <c2/audit_kernel.h>
#include <c2/audit_kevents.h>
#include <c2/audit_record.h>
#include "audit_door_infc.h"
extern uint_t num_syscall; /* size of audit_s2e table */
extern kmutex_t pidlock; /* proc table lock */
int audit_load = 0; /* set from /etc/system */
struct p_audit_data *pad0;
struct t_audit_data *tad0;
/*
* Das Boot. Initialize first process. Also generate an audit record indicating
* that the system has been booted.
*/
void
audit_init()
{
kthread_t *au_thread;
token_t *rp = NULL;
label_t jb;
struct audit_path apempty;
auditinfo_addr_t *ainfo;
if (audit_load == 0) {
audit_active = 0;
au_auditstate = AUC_DISABLED;
return;
#ifdef DEBUG
} else if (audit_load == 2) {
debug_enter((char *)NULL);
#endif
}
audit_active = 1;
set_all_proc_sys(); /* set pre- and post-syscall flags */
/* initialize memory allocators */
au_mem_init();
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));
T2A(curthread) = tad0;
P2A(curproc) = 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;
/*
* setup environment for asynchronous auditing. We can't use
* audit_async_start() here since it assumes the audit system
* has been started via auditd(1m). auditd sets the variable,
* auk_auditstate, to indicate audit record generation should
* commence. Here we want to always generate an audit record.
*/
if (setjmp(&jb)) {
/* process audit policy (AUDIT_AHLT) for asynchronous events */
audit_async_drop((caddr_t *)(&rp), 0);
return;
}
ASSERT(tad0->tad_errjmp == NULL);
tad0->tad_errjmp = (void *)&jb;
tad0->tad_ctrl |= PAD_ERRJMP;
/* generate a system-booted audit record */
au_write((caddr_t *)&rp, au_to_text("booting kernel"));
audit_async_finish((caddr_t *)&rp, AUE_SYSTEMBOOT, NULL);
}
void
audit_free()
{
}
/*
* 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);
}
}
/*
* Enter system call. Do any necessary setup here. allocate resouces, etc.
*/
#include <sys/syscall.h>
/*ARGSUSED*/
int
audit_start(
unsigned type,
unsigned scid,
int error,
klwp_t *lwp)
{
int ctrl;
au_event_t event;
au_state_t estate;
struct t_audit_data *tad;
au_kcontext_t *kctx;
tad = U2A(u);
ASSERT(tad != NULL);
if (error) {
tad->tad_ctrl = 0;
tad->tad_flag = 0;
return (0);
}
audit_update_context(curproc, NULL);
/*
* if this is an indirect system call then don't do anything.
* audit_start will be called again from indir() in trap.c
*/
if (scid == 0) {
tad->tad_ctrl = 0;
tad->tad_flag = 0;
return (0);
}
if (scid >= num_syscall)
scid = 0;
/* we can no longer ber guarantied a valid lwp_ap */
/* so we need to force it valid a lot of stuff needs it */
(void) save_syscall_args();
/* get control information */
ctrl = audit_s2e[scid].au_ctrl;
/*
* We need to gather paths for certain system calls even if they are
* not audited so that we can audit the various f* calls and be
* sure to have a CWD and CAR. Thus we thus set tad_ctrl over the
* system call regardless if the call is audited or not.
* We allow the au_init() routine to adjust the tad_ctrl.
*/
tad->tad_ctrl = ctrl;
tad->tad_scid = scid;
/* get basic event for system call */
event = (*audit_s2e[scid].au_init)(audit_s2e[scid].au_event);
kctx = SET_KCTX_PZ;
if (kctx == NULL) {
zone_status_t zstate = zone_status_get(curproc->p_zone);
ASSERT(zstate != ZONE_IS_READY);
return (0);
}
estate = kctx->auk_ets[event];
/* now do preselection. Audit or not to Audit, that is the question */
if ((tad->tad_flag = auditme(kctx, tad, estate)) == 0) {
/*
* we assume that audit_finish will always be called.
*/
return (0);
}
/*
* if auditing not enabled, then don't generate an audit record
* and don't count it.
*/
if ((kctx->auk_auditstate != AUC_AUDITING &&
kctx->auk_auditstate != AUC_INIT_AUDIT)) {
/*
* we assume that audit_finish will always be called.
*/
tad->tad_flag = 0;
return (0);
}
/*
* audit daemon has informed us that there is no longer any
* space left to hold audit records. We decide here if records
* should be dropped (but counted).
*/
if (kctx->auk_auditstate == AUC_NOSPACE) {
if ((kctx->auk_policy & AUDIT_CNT) ||
(kctx->auk_policy & AUDIT_SCNT)) {
/* assume that audit_finish will always be called. */
tad->tad_flag = 0;
/* just count # of dropped audit records */
AS_INC(as_dropped, 1, kctx);
return (0);
}
}
tad->tad_event = event;
tad->tad_evmod = 0;
(*audit_s2e[scid].au_start)(tad);
return (0);
}
/*
* system call has completed. Now determine if we genearate an audit record
* or not.
*/
/*ARGSUSED*/
void
audit_finish(
unsigned type,
unsigned scid,
int error,
rval_t *rval)
{
struct t_audit_data *tad;
int flag;
au_defer_info_t *attr;
au_kcontext_t *kctx = SET_KCTX_PZ;
if (kctx == NULL) {
zone_status_t zstate = zone_status_get(curproc->p_zone);
ASSERT(zstate != ZONE_IS_READY);
return;
}
tad = U2A(u);
/*
* Process all deferred events first.
*/
attr = tad->tad_defer_head;
while (attr != NULL) {
au_defer_info_t *tmp_attr = attr;
au_close_time(kctx, (token_t *)attr->audi_ad, attr->audi_flag,
attr->audi_e_type, attr->audi_e_mod, &(attr->audi_atime));
attr = attr->audi_next;
kmem_free(tmp_attr, sizeof (au_defer_info_t));
}
tad->tad_defer_head = tad->tad_defer_tail = NULL;
if (tad->tad_flag == 0 && !(tad->tad_ctrl & PAD_SAVPATH)) {
/*
* clear the ctrl flag so that we don't have spurious
* collection of audit information.
*/
tad->tad_scid = 0;
tad->tad_event = 0;
tad->tad_evmod = 0;
tad->tad_ctrl = 0;
ASSERT(tad->tad_aupath == NULL);
return;
}
scid = tad->tad_scid;
/*
* Perform any extra processing and determine if we are
* really going to generate any audit record.
*/
(*audit_s2e[scid].au_finish)(tad, error, rval);
if (tad->tad_flag) {
tad->tad_flag = 0;
if (flag = audit_success(kctx, tad, error)) {
unsigned int sy_flags;
cred_t *cr = CRED();
const auditinfo_addr_t *ainfo = crgetauinfo(cr);
ASSERT(ainfo != NULL);
/* Add a subject token */
AUDIT_SETSUBJ(&(u_ad), cr, ainfo);
/* Add an optional group token */
AUDIT_SETGROUP(&(u_ad), cr, kctx);
/* Add token for process SL */
if (is_system_labeled())
au_write(&(u_ad), au_to_label(CR_SL(cr)));
if (tad->tad_evmod & PAD_SPRIVUSE)
au_write(&(u_ad),
au_to_privset("", &tad->tad_sprivs,
AUT_UPRIV, 1));
if (tad->tad_evmod & PAD_FPRIVUSE)
au_write(&(u_ad),
au_to_privset("", &tad->tad_fprivs,
AUT_UPRIV, 0));
/* Add a return token */
#ifdef _SYSCALL32_IMPL
if (lwp_getdatamodel(
ttolwp(curthread)) == DATAMODEL_NATIVE)
sy_flags = sysent[scid].sy_flags & SE_RVAL_MASK;
else
sy_flags = sysent32[scid].sy_flags & SE_RVAL_MASK;
#else
sy_flags = sysent[scid].sy_flags & SE_RVAL_MASK;
#endif
if (sy_flags == SE_32RVAL1) {
if (type == 0) {
au_write(&(u_ad), au_to_return32(error, 0));
} else {
au_write(&(u_ad), au_to_return32(error,
rval->r_val1));
}
}
if (sy_flags == (SE_32RVAL2|SE_32RVAL1)) {
if (type == 0) {
au_write(&(u_ad), au_to_return32(error, 0));
} else {
au_write(&(u_ad), au_to_return32(error,
rval->r_val1));
#ifdef NOTYET /* for possible future support */
au_write(&(u_ad), au_to_return32(error,
rval->r_val2));
#endif
}
}
if (sy_flags == SE_64RVAL) {
if (type == 0) {
au_write(&(u_ad), au_to_return64(error, 0));
} else {
au_write(&(u_ad), au_to_return64(error,
rval->r_vals));
}
}
AS_INC(as_generated, 1, kctx);
AS_INC(as_kernel, 1, kctx);
}
/* Close up everything */
au_close(kctx, &(u_ad), flag, tad->tad_event, tad->tad_evmod);
}
ASSERT(u_ad == NULL);
/* free up any space remaining with the path's */
if (tad->tad_aupath != NULL) {
au_pathrele(tad->tad_aupath);
tad->tad_aupath = NULL;
tad->tad_vn = NULL;
}
/* free up any space remaining with openat path's */
if (tad->tad_atpath) {
au_pathrele(tad->tad_atpath);
tad->tad_atpath = NULL;
}
/*
* clear the ctrl flag so that we don't have spurious collection of
* audit information.
*/
tad->tad_scid = 0;
tad->tad_event = 0;
tad->tad_evmod = 0;
tad->tad_ctrl = 0;
}
int
audit_success(au_kcontext_t *kctx, struct t_audit_data *tad, int error)
{
au_state_t ess;
au_state_t esf;
au_mask_t amask;
const auditinfo_addr_t *ainfo;
ess = esf = kctx->auk_ets[tad->tad_event];
if (error)
tad->tad_evmod |= PAD_FAILURE;
/* see if we really want to generate an audit record */
if (tad->tad_ctrl & PAD_NOAUDIT)
return (0);
/*
* nfs operation and we're auditing privilege or MAC. This
* is so we have a client audit record to match a nfs server
* audit record.
*/
if (tad->tad_ctrl & PAD_AUDITME)
return (AU_OK);
ainfo = crgetauinfo(CRED());
if (ainfo == NULL)
return (0);
amask = ainfo->ai_mask;
if (error == 0)
return ((ess & amask.as_success) ? AU_OK : 0);
else
return ((esf & amask.as_failure) ? AU_OK : 0);
}
/*
* determine if we've preselected this event (system call).
*/
int
auditme(au_kcontext_t *kctx, struct t_audit_data *tad, au_state_t estate)
{
int flag = 0;
au_mask_t amask;
const auditinfo_addr_t *ainfo;
ainfo = crgetauinfo(CRED());
if (ainfo == NULL)
return (0);
amask = ainfo->ai_mask;
/* preselected system call */
if (amask.as_success & estate || amask.as_failure & estate) {
flag = 1;
} else if ((tad->tad_scid == SYS_putmsg) ||
(tad->tad_scid == SYS_getmsg)) {
estate = kctx->auk_ets[AUE_SOCKCONNECT] |
kctx->auk_ets[AUE_SOCKACCEPT] |
kctx->auk_ets[AUE_SOCKSEND] |
kctx->auk_ets[AUE_SOCKRECEIVE];
if (amask.as_success & estate || amask.as_failure & estate)
flag = 1;
}
return (flag);
}