diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/uts/common/c2/audit.c | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/uts/common/c2/audit.c')
-rw-r--r-- | usr/src/uts/common/c2/audit.c | 2276 |
1 files changed, 2276 insertions, 0 deletions
diff --git a/usr/src/uts/common/c2/audit.c b/usr/src/uts/common/c2/audit.c new file mode 100644 index 0000000000..62230f02d6 --- /dev/null +++ b/usr/src/uts/common/c2/audit.c @@ -0,0 +1,2276 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * This file contains the audit hook support code for auditing. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/vfs.h> +#include <sys/file.h> +#include <sys/user.h> +#include <sys/stropts.h> +#include <sys/systm.h> +#include <sys/pathname.h> +#include <sys/syscall.h> +#include <sys/fcntl.h> +#include <sys/ipc_impl.h> +#include <sys/msg_impl.h> +#include <sys/sem_impl.h> +#include <sys/shm_impl.h> +#include <sys/kmem.h> /* for KM_SLEEP */ +#include <sys/socket.h> +#include <sys/cmn_err.h> /* snprintf... */ +#include <sys/debug.h> +#include <sys/thread.h> +#include <netinet/in.h> +#include <c2/audit.h> /* needs to be included before user.h */ +#include <c2/audit_kernel.h> /* for M_DONTWAIT */ +#include <c2/audit_kevents.h> +#include <c2/audit_record.h> +#include <sys/strsubr.h> +#include <sys/tihdr.h> +#include <sys/tiuser.h> +#include <sys/timod.h> +#include <sys/model.h> /* for model_t */ +#include <sys/disp.h> /* for servicing_interrupt() */ +#include <sys/devpolicy.h> +#include <sys/crypto/ioctladmin.h> + +static void add_return_token(caddr_t *, unsigned int scid, int err, int rval); + +static void audit_pathbuild(struct pathname *pnp); + +/* + * 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 */ + + /* + * 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); + + 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_SAVEPATH + * PURPOSE: + * CALLBY: LOOKUPPN + * + * NOTE: We have reached the end of a path in fs/lookup.c. + * We get two pieces of information here: + * the vnode of the last component (vp) and + * the status of the last access (flag). + * TODO: + * QUESTION: + */ + +/*ARGSUSED*/ +int +audit_savepath( + struct pathname *pnp, /* pathname to lookup */ + struct vnode *vp, /* vnode of the last component */ + int flag, /* status of the last access */ + cred_t *cr) /* cred of requestor */ +{ + + t_audit_data_t *tad; /* current thread */ + p_audit_data_t *pad; /* current process */ + 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 (0); + } + + tad = U2A(u); + ASSERT(tad != (t_audit_data_t *)0); + pad = P2A(curproc); + ASSERT(pad != (p_audit_data_t *)0); + + /* + * this event being audited or do we need path information + * later? This might be for a chdir/chroot or open (add path + * to file pointer. If the path has already been found for an + * open/creat then we don't need to process the path. + * + * S2E_SP (PAD_SAVPATH) flag comes from audit_s2e[].au_ctrl. Used with + * chroot, chdir, open, creat system call processing. It determines + * if audit_savepath() will discard the path or we need it later. + * PAD_PATHFND means path already included in this audit record. It + * is used in cases where multiple path lookups are done per + * system call. The policy flag, AUDIT_PATH, controls if multiple + * paths are allowed. + * S2E_NPT (PAD_NOPATH) flag comes from audit_s2e[].au_ctrl. Used with + * exit processing to inhibit any paths that may be added due to + * closes. + */ + if ((tad->tad_flag == 0 && !(tad->tad_ctrl & PAD_SAVPATH)) || + ((tad->tad_ctrl & PAD_PATHFND) && + !(kctx->auk_policy & AUDIT_PATH)) || + (tad->tad_ctrl & PAD_NOPATH)) { + return (0); + } + + audit_pathbuild(pnp); + tad->tad_vn = vp; + + /* + * are we auditing only if error, or if it is not open or create + * otherwise audit_setf will do it + */ + + if (tad->tad_flag) { + if (flag && (tad->tad_scid == SYS_open || + tad->tad_scid == SYS_open64 || + tad->tad_scid == SYS_creat || + tad->tad_scid == SYS_creat64 || + tad->tad_scid == SYS_fsat)) { + tad->tad_ctrl |= PAD_TRUE_CREATE; + } + + /* add token to audit record for this name */ + au_uwrite(au_to_path(tad->tad_aupath)); + + /* add the attributes of the object */ + if (vp) { + /* + * only capture attributes when there is no error + * lookup will not return the vnode of the failing + * component. + * + * if there was a lookup error, then don't add + * attribute. if lookup in vn_create(), + * then don't add attribute, + * it will be added at end of vn_create(). + */ + if (!flag && !(tad->tad_ctrl & PAD_NOATTRB)) + audit_attributes(vp); + } + } + + /* free up space if we're not going to save path (open, crate) */ + if ((tad->tad_ctrl & PAD_SAVPATH) == 0) { + if (tad->tad_aupath != NULL) { + au_pathrele(tad->tad_aupath); + tad->tad_aupath = NULL; + tad->tad_vn = NULL; + } + } + if (tad->tad_ctrl & PAD_MLD) + tad->tad_ctrl |= PAD_PATHFND; + + return (0); +} + +static void +audit_pathbuild(struct pathname *pnp) +{ + char *pp; /* pointer to path */ + int len; /* length of incoming segment */ + int newsect; /* path requires a new section */ + struct audit_path *pfxapp; /* prefix for path */ + struct audit_path *newapp; /* new audit_path */ + t_audit_data_t *tad; /* current thread */ + p_audit_data_t *pad; /* current process */ + + tad = U2A(u); + ASSERT(tad != NULL); + pad = P2A(curproc); + ASSERT(pad != NULL); + + len = (pnp->pn_path - pnp->pn_buf) + 1; /* +1 for terminator */ + ASSERT(len > 0); + + /* adjust for path prefix: tad_aupath, ATPATH, CRD, or CWD */ + mutex_enter(&pad->pad_lock); + if (tad->tad_aupath != NULL) { + pfxapp = tad->tad_aupath; + } else if (tad->tad_scid == SYS_fsat && pnp->pn_buf[0] != '/') { + ASSERT(tad->tad_atpath != NULL); + pfxapp = tad->tad_atpath; + } else if (tad->tad_ctrl & PAD_ABSPATH) { + pfxapp = pad->pad_root; + } else { + pfxapp = pad->pad_cwd; + } + au_pathhold(pfxapp); + mutex_exit(&pad->pad_lock); + + /* get an expanded buffer to hold the anchored path */ + newsect = tad->tad_ctrl & PAD_ATPATH; + newapp = au_pathdup(pfxapp, newsect, len); + au_pathrele(pfxapp); + + pp = newapp->audp_sect[newapp->audp_cnt] - len; + if (!newsect) { + /* overlay previous NUL terminator */ + *(pp - 1) = '/'; + } + + /* now add string of processed path */ + bcopy(pnp->pn_buf, pp, len); + pp[len - 1] = '\0'; + + /* perform path simplification as necessary */ + audit_fixpath(newapp, len); + + if (tad->tad_aupath) + au_pathrele(tad->tad_aupath); + tad->tad_aupath = newapp; + + /* for case where multiple lookups in one syscall (rename) */ + tad->tad_ctrl &= ~(PAD_ABSPATH | PAD_ATPATH); +} + + + +/*ARGSUSED*/ + +/* + * ROUTINE: AUDIT_ADDCOMPONENT + * PURPOSE: extend the path by the component accepted + * CALLBY: LOOKUPPN + * NOTE: This function is called only when there is an error in + * parsing a path component + * TODO: Add the error component to audit record + * QUESTION: what is this for + */ + +void +audit_addcomponent(struct pathname *pnp) +{ + au_kcontext_t *kctx = SET_KCTX_PZ; + t_audit_data_t *tad; + + if (kctx == NULL) { + zone_status_t zstate = zone_status_get(curproc->p_zone); + ASSERT(zstate != ZONE_IS_READY); + return; + } + + tad = U2A(u); + /* + * S2E_SP (PAD_SAVPATH) flag comes from audit_s2e[].au_ctrl. Used with + * chroot, chdir, open, creat system call processing. It determines + * if audit_savepath() will discard the path or we need it later. + * PAD_PATHFND means path already included in this audit record. It + * is used in cases where multiple path lookups are done per + * system call. The policy flag, AUDIT_PATH, controls if multiple + * paths are allowed. + * S2E_NPT (PAD_NOPATH) flag comes from audit_s2e[].au_ctrl. Used with + * exit processing to inhibit any paths that may be added due to + * closes. + */ + if ((tad->tad_flag == 0 && !(tad->tad_ctrl & PAD_SAVPATH)) || + ((tad->tad_ctrl & PAD_PATHFND) && + !(kctx->auk_policy & AUDIT_PATH)) || + (tad->tad_ctrl & PAD_NOPATH)) { + return; + } + + return; + +} /* AUDIT_ADDCOMPONENT */ + + + + + + + + +/* + * ROUTINE: AUDIT_ANCHORPATH + * PURPOSE: + * CALLBY: LOOKUPPN + * NOTE: + * anchor path at "/". We have seen a symbolic link or entering for the + * first time we will throw away any saved path if path is anchored. + * + * flag = 0, path is relative. + * flag = 1, path is absolute. Free any saved path and set flag to PAD_ABSPATH. + * + * If the (new) path is absolute, then we have to throw away whatever we have + * already accumulated since it is being superceeded by new path which is + * anchored at the root. + * Note that if the path is relative, this function does nothing + * TODO: + * QUESTION: + */ +/*ARGSUSED*/ +void +audit_anchorpath(struct pathname *pnp, int flag) +{ + au_kcontext_t *kctx = SET_KCTX_PZ; + t_audit_data_t *tad; + + if (kctx == NULL) { + zone_status_t zstate = zone_status_get(curproc->p_zone); + ASSERT(zstate != ZONE_IS_READY); + return; + } + + tad = U2A(u); + + /* + * this event being audited or do we need path information + * later? This might be for a chdir/chroot or open (add path + * to file pointer. If the path has already been found for an + * open/creat then we don't need to process the path. + * + * S2E_SP (PAD_SAVPATH) flag comes from audit_s2e[].au_ctrl. Used with + * chroot, chdir, open, creat system call processing. It determines + * if audit_savepath() will discard the path or we need it later. + * PAD_PATHFND means path already included in this audit record. It + * is used in cases where multiple path lookups are done per + * system call. The policy flag, AUDIT_PATH, controls if multiple + * paths are allowed. + * S2E_NPT (PAD_NOPATH) flag comes from audit_s2e[].au_ctrl. Used with + * exit processing to inhibit any paths that may be added due to + * closes. + */ + if ((tad->tad_flag == 0 && !(tad->tad_ctrl & PAD_SAVPATH)) || + ((tad->tad_ctrl & PAD_PATHFND) && + !(kctx->auk_policy & AUDIT_PATH)) || + (tad->tad_ctrl & PAD_NOPATH)) { + return; + } + + if (flag) { + tad->tad_ctrl |= PAD_ABSPATH; + if (tad->tad_aupath != NULL) { + au_pathrele(tad->tad_aupath); + tad->tad_aupath = NULL; + tad->tad_vn = NULL; + } + } +} + + +/* + * symbolic link. Save previous components. + * + * the path seen so far looks like this + * + * +-----------------------+----------------+ + * | path processed so far | remaining path | + * +-----------------------+----------------+ + * \-----------------------/ + * save this string if + * symbolic link relative + * (but don't include symlink component) + */ + +/*ARGSUSED*/ + + +/* + * ROUTINE: AUDIT_SYMLINK + * PURPOSE: + * CALLBY: LOOKUPPN + * NOTE: + * TODO: + * QUESTION: + */ +void +audit_symlink(struct pathname *pnp, struct pathname *sympath) +{ + char *sp; /* saved initial pp */ + char *cp; /* start of symlink path */ + uint_t len_path; /* processed path before symlink */ + t_audit_data_t *tad; + 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); + + /* + * this event being audited or do we need path information + * later? This might be for a chdir/chroot or open (add path + * to file pointer. If the path has already been found for an + * open/creat then we don't need to process the path. + * + * S2E_SP (PAD_SAVPATH) flag comes from audit_s2e[].au_ctrl. Used with + * chroot, chdir, open, creat system call processing. It determines + * if audit_savepath() will discard the path or we need it later. + * PAD_PATHFND means path already included in this audit record. It + * is used in cases where multiple path lookups are done per + * system call. The policy flag, AUDIT_PATH, controls if multiple + * paths are allowed. + * S2E_NPT (PAD_NOPATH) flag comes from audit_s2e[].au_ctrl. Used with + * exit processing to inhibit any paths that may be added due to + * closes. + */ + if ((tad->tad_flag == 0 && + !(tad->tad_ctrl & PAD_SAVPATH)) || + ((tad->tad_ctrl & PAD_PATHFND) && + !(kctx->auk_policy & AUDIT_PATH)) || + (tad->tad_ctrl & PAD_NOPATH)) { + return; + } + + /* + * if symbolic link is anchored at / then do nothing. + * When we cycle back to begin: in lookuppn() we will + * call audit_anchorpath() with a flag indicating if the + * path is anchored at / or is relative. We will release + * any saved path at that point. + * + * Note In the event that an error occurs in pn_combine then + * we want to remain pointing at the component that caused the + * path to overflow the pnp structure. + */ + if (sympath->pn_buf[0] == '/') + return; + + /* backup over last component */ + sp = cp = pnp->pn_path; + while (*--cp != '/' && cp > pnp->pn_buf) + ; + + len_path = cp - pnp->pn_buf; + + /* is there anything to save? */ + if (len_path) { + pnp->pn_path = cp; + audit_pathbuild(pnp); + pnp->pn_path = sp; + } +} + +/* + * file_is_public : determine whether events for the file (corresponding to + * the specified file attr) should be audited or ignored. + * + * returns: 1 - if audit policy and file attributes indicate that + * file is effectively public. read events for + * the file should not be audited. + * 0 - otherwise + * + * The required attributes to be considered a public object are: + * - owned by root, AND + * - world-readable (permissions for other include read), AND + * - NOT world-writeable (permissions for other don't + * include write) + * (mode doesn't need to be checked for symlinks) + */ +int +file_is_public(struct vattr *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 (0); + } + + if (!(kctx->auk_policy & AUDIT_PUBLIC) && (attr->va_uid == 0) && + ((attr->va_type == VLNK) || + ((attr->va_mode & (VREAD>>6)) != 0) && + ((attr->va_mode & (VWRITE>>6)) == 0))) { + return (1); + } + return (0); +} + + +/* + * ROUTINE: AUDIT_ATTRIBUTES + * PURPOSE: Audit the attributes so we can tell why the error occured + * CALLBY: AUDIT_SAVEPATH + * AUDIT_VNCREATE_FINISH + * AUS_FCHOWN...audit_event.c...audit_path.c + * NOTE: + * TODO: + * QUESTION: + */ +void +audit_attributes(struct vnode *vp) +{ + struct vattr attr; + struct t_audit_data *tad; + + tad = U2A(u); + + if (vp) { + attr.va_mask = AT_ALL; + if (VOP_GETATTR(vp, &attr, 0, CRED()) != 0) + return; + + if (file_is_public(&attr) && (tad->tad_ctrl & PAD_PUBLIC_EV)) { + /* + * This is a public object and a "public" event + * (i.e., read only) -- either by definition + * (e.g., stat, access...) or by virtue of write access + * not being requested (e.g. mmap). + * Flag it in the tad to prevent this audit at the end. + */ + tad->tad_ctrl |= PAD_NOAUDIT; + } else { + au_uwrite(au_to_attr(&attr)); + } + } +} + + +/* + * 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)); +} + +/* + * ROUTINE: AUDIT_EXIT + * PURPOSE: + * CALLBY: EXIT + * NOTE: + * TODO: + * QUESTION: why cmw code as offset by 2 but not here + */ +/* ARGSUSED */ +void +audit_exit(int code, int what) +{ + struct t_audit_data *tad; + tad = U2A(u); + + /* + * tad_scid will be set by audit_start even if we are not auditing + * the event. + */ + if (tad->tad_scid == SYS_exit) { + /* + * if we are auditing the exit system call, then complete + * audit record generation (no return from system call). + */ + if (tad->tad_flag && tad->tad_event == AUE_EXIT) + audit_finish(0, SYS_exit, 0, 0); + return; + } + + /* + * Anyone auditing the system call that was aborted? + */ + if (tad->tad_flag) { + au_uwrite(au_to_text("event aborted")); + audit_finish(0, tad->tad_scid, 0, 0); + } + + /* + * Generate an audit record for process exit if preselected. + */ + (void) audit_start(0, SYS_exit, 0, 0); + audit_finish(0, SYS_exit, 0, 0); +} + +/* + * ROUTINE: AUDIT_CORE_START + * PURPOSE: + * CALLBY: PSIG + * NOTE: + * TODO: + */ +void +audit_core_start(int sig) +{ + au_event_t event; + au_state_t estate; + t_audit_data_t *tad; + au_kcontext_t *kctx; + + tad = U2A(u); + + ASSERT(tad != (t_audit_data_t *)0); + + ASSERT(tad->tad_scid == 0); + ASSERT(tad->tad_event == 0); + ASSERT(tad->tad_evmod == 0); + ASSERT(tad->tad_ctrl == 0); + ASSERT(tad->tad_flag == 0); + ASSERT(tad->tad_aupath == NULL); + + kctx = SET_KCTX_PZ; + + if (kctx == NULL) { + zone_status_t zstate = zone_status_get(curproc->p_zone); + ASSERT(zstate != ZONE_IS_READY); + return; + } + + /* get basic event for system call */ + event = AUE_CORE; + estate = kctx->auk_ets[event]; + + if ((tad->tad_flag = auditme(kctx, tad, estate)) == 0) + return; + + /* reset the flags for non-user attributable events */ + tad->tad_ctrl = PAD_CORE; + tad->tad_scid = 0; + + /* if auditing not enabled, then don't generate an audit record */ + + if (!((kctx->auk_auditstate == AUC_AUDITING || + kctx->auk_auditstate == AUC_INIT_AUDIT) || + kctx->auk_auditstate == AUC_NOSPACE)) { + tad->tad_flag = 0; + tad->tad_ctrl = 0; + return; + } + + tad->tad_event = event; + tad->tad_evmod = 0; + + ASSERT(tad->tad_ad == NULL); + + au_write(&(u_ad), au_to_arg32(1, "signal", (uint32_t)sig)); +} + +/* + * ROUTINE: AUDIT_CORE_FINISH + * PURPOSE: + * CALLBY: PSIG + * NOTE: + * TODO: + * QUESTION: + */ + +/*ARGSUSED*/ +void +audit_core_finish(int code) +{ + int flag; + t_audit_data_t *tad; + au_kcontext_t *kctx; + + tad = U2A(u); + + ASSERT(tad != (t_audit_data_t *)0); + + if ((flag = tad->tad_flag) == 0) { + tad->tad_event = 0; + tad->tad_evmod = 0; + tad->tad_ctrl = 0; + ASSERT(tad->tad_aupath == NULL); + return; + } + tad->tad_flag = 0; + + kctx = SET_KCTX_PZ; + if (kctx == NULL) { + zone_status_t zstate = zone_status_get(curproc->p_zone); + ASSERT(zstate != ZONE_IS_READY); + return; + } + + /* kludge for error 0, should use `code==CLD_DUMPED' instead */ + if (flag = audit_success(kctx, tad, 0)) { + cred_t *cr = CRED(); + const auditinfo_addr_t *ainfo = crgetauinfo(cr); + + ASSERT(ainfo != NULL); + + /* + * Add a subject token (no locks since our private copy of + * credential + */ + AUDIT_SETSUBJ(&(u_ad), cr, ainfo); + + /* Add an optional group token */ + AUDIT_SETGROUP(&(u_ad), cr, kctx); + + /* Add a return token (should use f argument) */ + add_return_token((caddr_t *)&(u_ad), tad->tad_scid, 0, 0); + + 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); + + /* 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; + } + tad->tad_event = 0; + tad->tad_evmod = 0; + tad->tad_ctrl = 0; +} + +/*ARGSUSED*/ +void +audit_stropen(struct vnode *vp, dev_t *devp, int flag, cred_t *crp) +{ +} + +/*ARGSUSED*/ +void +audit_strclose(struct vnode *vp, int flag, cred_t *crp) +{ +} + +/*ARGSUSED*/ +void +audit_strioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, + int copyflag, cred_t *crp, int *rvalp) +{ +} + + +/*ARGSUSED*/ +void +audit_strgetmsg(struct vnode *vp, struct strbuf *mctl, struct strbuf *mdata, + unsigned char *pri, int *flag, int fmode) +{ + struct stdata *stp; + t_audit_data_t *tad = U2A(u); + + ASSERT(tad != (t_audit_data_t *)0); + + stp = vp->v_stream; + + /* lock stdata from audit_sock */ + mutex_enter(&stp->sd_lock); + + /* proceed ONLY if user is being audited */ + if (!tad->tad_flag) { + /* + * this is so we will not add audit data onto + * a thread that is not being audited. + */ + stp->sd_t_audit_data = NULL; + mutex_exit(&stp->sd_lock); + return; + } + + stp->sd_t_audit_data = (caddr_t)curthread; + mutex_exit(&stp->sd_lock); +} + +/*ARGSUSED*/ +void +audit_strputmsg(struct vnode *vp, struct strbuf *mctl, struct strbuf *mdata, + unsigned char pri, int flag, int fmode) +{ + struct stdata *stp; + t_audit_data_t *tad = U2A(u); + + ASSERT(tad != (t_audit_data_t *)0); + + stp = vp->v_stream; + + /* lock stdata from audit_sock */ + mutex_enter(&stp->sd_lock); + + /* proceed ONLY if user is being audited */ + if (!tad->tad_flag) { + /* + * this is so we will not add audit data onto + * a thread that is not being audited. + */ + stp->sd_t_audit_data = NULL; + mutex_exit(&stp->sd_lock); + return; + } + + stp->sd_t_audit_data = (caddr_t)curthread; + mutex_exit(&stp->sd_lock); +} + +/* + * ROUTINE: AUDIT_CLOSEF + * PURPOSE: + * CALLBY: CLOSEF + * NOTE: + * release per file audit resources when file structure is being released. + * + * IMPORTANT NOTE: Since we generate an audit record here, we may sleep + * on the audit queue if it becomes full. This means + * audit_closef can not be called when f_count == 0. Since + * f_count == 0 indicates the file structure is free, another + * process could attempt to use the file while we were still + * asleep waiting on the audit queue. This would cause the + * per file audit data to be corrupted when we finally do + * wakeup. + * TODO: + * QUESTION: + */ + +void +audit_closef(struct file *fp) +{ /* AUDIT_CLOSEF */ + f_audit_data_t *fad; + t_audit_data_t *tad; + int success; + au_state_t estate; + struct vnode *vp; + token_t *ad = NULL; + struct vattr attr; + short evmod = 0; + const auditinfo_addr_t *ainfo; + int getattr_ret; + cred_t *cr; + 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; + } + + fad = F2A(fp); + estate = kctx->auk_ets[AUE_CLOSE]; + tad = U2A(u); + cr = CRED(); + + /* audit record already generated by system call envelope */ + if (tad->tad_event == AUE_CLOSE) { + /* so close audit event will have bits set */ + tad->tad_evmod |= (short)fad->fad_flags; + return; + } + + /* if auditing not enabled, then don't generate an audit record */ + if (!((kctx->auk_auditstate == AUC_AUDITING || + kctx->auk_auditstate == AUC_INIT_AUDIT) || + kctx->auk_auditstate == AUC_NOSPACE)) + return; + + ainfo = crgetauinfo(cr); + if (ainfo == NULL) + return; + + success = ainfo->ai_mask.as_success & estate; + + /* not selected for this event */ + if (success == 0) + return; + + /* + * can't use audit_attributes here since we use a private audit area + * to build the audit record instead of the one off the thread. + */ + if ((vp = fp->f_vnode) != NULL) { + attr.va_mask = AT_ALL; + getattr_ret = VOP_GETATTR(vp, &attr, 0, CRED()); + } + + /* + * When write was not used and the file can be considered public, + * then skip the audit. + */ + if ((getattr_ret == 0) && ((fp->f_flag & FWRITE) == 0)) { + if (file_is_public(&attr)) { + return; + } + } + + evmod = (short)fad->fad_flags; + if (fad->fad_aupath != NULL) { + au_write((caddr_t *)&(ad), au_to_path(fad->fad_aupath)); + } else { +#ifdef _LP64 + au_write((caddr_t *)&(ad), au_to_arg64( + 1, "no path: fp", (uint64_t)fp)); +#else + au_write((caddr_t *)&(ad), au_to_arg32( + 1, "no path: fp", (uint32_t)fp)); +#endif + } + + if (getattr_ret == 0) { + au_write((caddr_t *)&(ad), au_to_attr(&attr)); + } + + /* Add a subject token */ + AUDIT_SETSUBJ((caddr_t *)&(ad), cr, ainfo); + + /* add an optional group token */ + AUDIT_SETGROUP((caddr_t *)&(ad), cr, kctx); + + /* add a return token */ + add_return_token((caddr_t *)&(ad), tad->tad_scid, 0, 0); + + AS_INC(as_generated, 1, kctx); + AS_INC(as_kernel, 1, kctx); + + /* + * Close up everything + * Note: path space recovery handled by normal system + * call envelope if not at last close. + * Note there is no failure at this point since + * this represents closes due to exit of process, + * thus we always indicate successful closes. + */ + au_close(kctx, (caddr_t *)&(ad), AU_OK | AU_DEFER, + AUE_CLOSE, evmod); +} + +/* + * ROUTINE: AUDIT_SET + * PURPOSE: Audit the file path and file attributes. + * CALLBY: SETF + * NOTE: SETF associate a file pointer with user area's open files. + * TODO: + * call audit_finish directly ??? + * QUESTION: + */ + +/*ARGSUSED*/ +void +audit_setf(file_t *fp, int fd) +{ + f_audit_data_t *fad; + t_audit_data_t *tad; + + if (fp == NULL) + return; + + tad = T2A(curthread); + fad = F2A(fp); + + if (!(tad->tad_scid == SYS_open || tad->tad_scid == SYS_creat || + tad->tad_scid == SYS_open64 || tad->tad_scid == SYS_creat64 || + tad->tad_scid == SYS_fsat)) + return; + + /* no path */ + if (tad->tad_aupath == 0) + return; + + /* + * assign path information associated with file audit data + * use tad hold + */ + fad->fad_aupath = tad->tad_aupath; + tad->tad_aupath = NULL; + tad->tad_vn = NULL; + + if (!(tad->tad_ctrl & PAD_TRUE_CREATE)) { + /* adjust event type */ + switch (tad->tad_event) { + case AUE_OPEN_RC: + tad->tad_event = AUE_OPEN_R; + tad->tad_ctrl |= PAD_PUBLIC_EV; + break; + case AUE_OPEN_RTC: + tad->tad_event = AUE_OPEN_RT; + break; + case AUE_OPEN_WC: + tad->tad_event = AUE_OPEN_W; + break; + case AUE_OPEN_WTC: + tad->tad_event = AUE_OPEN_WT; + break; + case AUE_OPEN_RWC: + tad->tad_event = AUE_OPEN_RW; + break; + case AUE_OPEN_RWTC: + tad->tad_event = AUE_OPEN_RWT; + break; + default: + break; + } + } +} + + +/* + * ROUTINE: AUDIT_COPEN + * PURPOSE: + * CALLBY: COPEN + * NOTE: + * TODO: + * QUESTION: + */ +/*ARGSUSED*/ +void +audit_copen(int fd, file_t *fp, vnode_t *vp) +{ +} + +void +audit_ipc(int type, int id, void *vp) +{ + /* if not auditing this event, then do nothing */ + if (ad_flag == 0) + return; + + switch (type) { + case AT_IPC_MSG: + au_uwrite(au_to_ipc(AT_IPC_MSG, id)); + au_uwrite(au_to_ipc_perm(&(((kmsqid_t *)vp)->msg_perm))); + break; + case AT_IPC_SEM: + au_uwrite(au_to_ipc(AT_IPC_SEM, id)); + au_uwrite(au_to_ipc_perm(&(((ksemid_t *)vp)->sem_perm))); + break; + case AT_IPC_SHM: + au_uwrite(au_to_ipc(AT_IPC_SHM, id)); + au_uwrite(au_to_ipc_perm(&(((kshmid_t *)vp)->shm_perm))); + break; + } +} + +void +audit_ipcget(int type, void *vp) +{ + /* if not auditing this event, then do nothing */ + if (ad_flag == 0) + return; + + switch (type) { + case NULL: + au_uwrite(au_to_ipc_perm((struct kipc_perm *)vp)); + break; + case AT_IPC_MSG: + au_uwrite(au_to_ipc_perm(&(((kmsqid_t *)vp)->msg_perm))); + break; + case AT_IPC_SEM: + au_uwrite(au_to_ipc_perm(&(((ksemid_t *)vp)->sem_perm))); + break; + case AT_IPC_SHM: + au_uwrite(au_to_ipc_perm(&(((kshmid_t *)vp)->shm_perm))); + break; + } +} + +/* + * ROUTINE: AUDIT_REBOOT + * PURPOSE: + * CALLBY: + * NOTE: + * At this point we know that the system call reboot will not return. We thus + * have to complete the audit record generation and put it onto the queue. + * This might be fairly useless if the auditing daemon is already dead.... + * TODO: + * QUESTION: who calls audit_reboot + */ + +void +audit_reboot(void) +{ + int flag; + t_audit_data_t *tad; + 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); + + /* if not auditing this event, then do nothing */ + if (tad->tad_flag == 0) + return; + + /* do preselection on success/failure */ + if (flag = audit_success(kctx, tad, 0)) { + /* add a process token */ + + cred_t *cr = CRED(); + const auditinfo_addr_t *ainfo = crgetauinfo(cr); + + if (ainfo == NULL) + return; + + AUDIT_SETSUBJ(&(u_ad), cr, ainfo); + + /* add an optional group token */ + AUDIT_SETGROUP(&(u_ad), cr, kctx); + + /* add a return token */ + add_return_token((caddr_t *)&(u_ad), tad->tad_scid, 0, 0); + + AS_INC(as_generated, 1, kctx); + AS_INC(as_kernel, 1, kctx); + } + + /* + * Flow control useless here since we're going + * to drop everything in the queue anyway. Why + * block and wait. There aint anyone left alive to + * read the records remaining anyway. + */ + + /* Close up everything */ + au_close(kctx, &(u_ad), flag | AU_DONTBLOCK, + tad->tad_event, tad->tad_evmod); +} + +void +audit_setfsat_path(int argnum) +{ + klwp_id_t clwp = ttolwp(curthread); + struct file *fp; + uint32_t fd; + t_audit_data_t *tad; + struct f_audit_data *fad; + p_audit_data_t *pad; /* current process */ + + struct b { + long arg1; + long arg2; + long arg3; + long arg4; + long arg5; + } *uap1; + + if (clwp == NULL) + return; + uap1 = (struct b *)&clwp->lwp_ap[1]; + + tad = U2A(u); + + ASSERT(tad != NULL); + + if (tad->tad_scid != SYS_fsat) + return; + + switch (argnum) { + case 1: + fd = (uint32_t)uap1->arg1; + break; + case 2: + fd = (uint32_t)uap1->arg2; + break; + case 3: + fd = (uint32_t)uap1->arg3; + break; + case 4: + fd = (uint32_t)uap1->arg4; + break; + case 5: + fd = (uint32_t)uap1->arg5; + break; + default: + return; + } + + if (tad->tad_atpath != NULL) { + au_pathrele(tad->tad_atpath); + tad->tad_atpath = NULL; + } + if (fd != AT_FDCWD) { + if ((fp = getf(fd)) == NULL) + return; + + fad = F2A(fp); + ASSERT(fad); + au_pathhold(fad->fad_aupath); + tad->tad_atpath = fad->fad_aupath; + releasef(fd); + } else { + pad = P2A(curproc); + mutex_enter(&pad->pad_lock); + au_pathhold(pad->pad_cwd); + tad->tad_atpath = pad->pad_cwd; + mutex_exit(&pad->pad_lock); + } +} + +void +audit_symlink_create(vnode_t *dvp, char *sname, char *target, int error) +{ + t_audit_data_t *tad; + vnode_t *vp; + + tad = U2A(u); + + /* if not auditing this event, then do nothing */ + if (tad->tad_flag == 0) + return; + + au_uwrite(au_to_text(target)); + + if (error) + return; + + error = VOP_LOOKUP(dvp, sname, &vp, NULL, NO_FOLLOW, NULL, CRED()); + if (error == 0) { + audit_attributes(vp); + VN_RELE(vp); + } +} + +/* + * ROUTINE: AUDIT_VNCREATE_START + * PURPOSE: set flag so path name lookup in create will not add attribute + * CALLBY: VN_CREATE + * NOTE: + * TODO: + * QUESTION: + */ + +void +audit_vncreate_start() +{ + t_audit_data_t *tad; + + tad = U2A(u); + tad->tad_ctrl |= PAD_NOATTRB; +} + +/* + * ROUTINE: AUDIT_VNCREATE_FINISH + * PURPOSE: + * CALLBY: VN_CREATE + * NOTE: + * TODO: + * QUESTION: + */ +void +audit_vncreate_finish(struct vnode *vp, int error) +{ + t_audit_data_t *tad; + + if (error) + return; + + tad = U2A(u); + + /* if not auditing this event, then do nothing */ + if (tad->tad_flag == 0) + return; + + if (tad->tad_ctrl & PAD_TRUE_CREATE) { + audit_attributes(vp); + } + + if (tad->tad_ctrl & PAD_CORE) { + audit_attributes(vp); + tad->tad_ctrl &= ~PAD_CORE; + } + + if (!error && ((tad->tad_event == AUE_MKNOD) || + (tad->tad_event == AUE_MKDIR))) { + audit_attributes(vp); + } + + /* for case where multiple lookups in one syscall (rename) */ + tad->tad_ctrl &= ~PAD_NOATTRB; +} + + + + + + + + +/* + * ROUTINE: AUDIT_EXEC + * PURPOSE: Records the function arguments and environment variables + * CALLBY: EXEC_ARGS + * NOTE: + * TODO: + * QUESTION: + */ + +/*ARGSUSED*/ +void +audit_exec( + const char *argstr, /* argument strings */ + const char *envstr, /* environment strings */ + ssize_t argc, /* total # arguments */ + ssize_t envc) /* total # environment variables */ +{ + t_audit_data_t *tad; + au_kcontext_t *kctx = SET_KCTX_PZ; + + ASSERT(kctx != NULL); + + tad = U2A(u); + + /* if not auditing this event, then do nothing */ + if (!tad->tad_flag) + return; + + /* return if not interested in argv or environment variables */ + if (!(kctx->auk_policy & (AUDIT_ARGV|AUDIT_ARGE))) + return; + + if (kctx->auk_policy & AUDIT_ARGV) { + au_uwrite(au_to_exec_args(argstr, argc)); + } + + if (kctx->auk_policy & AUDIT_ARGE) { + au_uwrite(au_to_exec_env(envstr, envc)); + } +} + +/* + * ROUTINE: AUDIT_ENTERPROM + * PURPOSE: + * CALLBY: KBDINPUT + * ZSA_XSINT + * NOTE: + * TODO: + * QUESTION: + */ +void +audit_enterprom(int flg) +{ + token_t *rp = NULL; + int sorf; + + if (flg) + sorf = AUM_SUCC; + else + sorf = AUM_FAIL; + + AUDIT_ASYNC_START(rp, AUE_ENTERPROM, sorf); + + au_write((caddr_t *)&(rp), au_to_text("kmdb")); + + if (flg) + au_write((caddr_t *)&(rp), au_to_return32(0, 0)); + else + au_write((caddr_t *)&(rp), au_to_return32(ECANCELED, 0)); + + AUDIT_ASYNC_FINISH(rp, AUE_ENTERPROM, NULL); +} + + +/* + * ROUTINE: AUDIT_EXITPROM + * PURPOSE: + * CALLBY: KBDINPUT + * ZSA_XSINT + * NOTE: + * TODO: + * QUESTION: + */ +void +audit_exitprom(int flg) +{ + int sorf; + token_t *rp = NULL; + + if (flg) + sorf = AUM_SUCC; + else + sorf = AUM_FAIL; + + AUDIT_ASYNC_START(rp, AUE_EXITPROM, sorf); + + au_write((caddr_t *)&(rp), au_to_text("kmdb")); + + if (flg) + au_write((caddr_t *)&(rp), au_to_return32(0, 0)); + else + au_write((caddr_t *)&(rp), au_to_return32(ECANCELED, 0)); + + AUDIT_ASYNC_FINISH(rp, AUE_EXITPROM, NULL); +} + +struct fcntla { + int fdes; + int cmd; + intptr_t arg; +}; + +/* + * ROUTINE: AUDIT_C2_REVOKE + * PURPOSE: + * CALLBY: FCNTL + * NOTE: + * TODO: + * QUESTION: are we keeping this func + */ + +/*ARGSUSED*/ +int +audit_c2_revoke(struct fcntla *uap, rval_t *rvp) +{ + return (0); +} + + +/* + * ROUTINE: AUDIT_CHDIREC + * PURPOSE: + * CALLBY: CHDIREC + * NOTE: The main function of CHDIREC + * TODO: Move the audit_chdirec hook above the VN_RELE in vncalls.c + * QUESTION: + */ + +/*ARGSUSED*/ +void +audit_chdirec(vnode_t *vp, vnode_t **vpp) +{ + int chdir; + int fchdir; + struct audit_path **appp; + struct file *fp; + f_audit_data_t *fad; + p_audit_data_t *pad = P2A(curproc); + t_audit_data_t *tad = T2A(curthread); + + struct a { + long fd; + } *uap = (struct a *)ttolwp(curthread)->lwp_ap; + + if ((tad->tad_scid == SYS_chdir) || (tad->tad_scid == SYS_chroot)) { + chdir = tad->tad_scid == SYS_chdir; + if (tad->tad_aupath) { + mutex_enter(&pad->pad_lock); + if (chdir) + appp = &(pad->pad_cwd); + else + appp = &(pad->pad_root); + au_pathrele(*appp); + /* use tad hold */ + *appp = tad->tad_aupath; + tad->tad_aupath = NULL; + mutex_exit(&pad->pad_lock); + } + } else if ((tad->tad_scid == SYS_fchdir) || + (tad->tad_scid == SYS_fchroot)) { + fchdir = tad->tad_scid == SYS_fchdir; + if ((fp = getf(uap->fd)) == NULL) + return; + fad = F2A(fp); + if (fad->fad_aupath) { + au_pathhold(fad->fad_aupath); + mutex_enter(&pad->pad_lock); + if (fchdir) + appp = &(pad->pad_cwd); + else + appp = &(pad->pad_root); + au_pathrele(*appp); + *appp = fad->fad_aupath; + mutex_exit(&pad->pad_lock); + if (tad->tad_flag) { + au_uwrite(au_to_path(fad->fad_aupath)); + audit_attributes(fp->f_vnode); + } + } + releasef(uap->fd); + } +} + +/* + * ROUTINE: AUDIT_GETF + * PURPOSE: + * CALLBY: GETF_INTERNAL + * NOTE: The main function of GETF_INTERNAL is to associate a given + * file descriptor with a file structure and increment the + * file pointer reference count. + * TODO: remove pass in of fpp. + * increment a reference count so that even if a thread with same process delete + * the same object, it will not panic our system + * QUESTION: + * where to decrement the f_count????????????????? + * seems like I need to set a flag if f_count incrmented through audit_getf + */ + +/*ARGSUSED*/ +int +audit_getf(int fd) +{ +#ifdef NOTYET + t_audit_data_t *tad; + + tad = T2A(curthread); + + if (!(tad->tad_scid == SYS_open || tad->tad_scid == SYS_creat)) + return; +#endif + return (0); +} + +/* + * Audit hook for stream based socket and tli request. + * Note that we do not have user context while executing + * this code so we had to record them earlier during the + * putmsg/getmsg to figure out which user we are dealing with. + */ + +/*ARGSUSED*/ +void +audit_sock( + int type, /* type of tihdr.h header requests */ + queue_t *q, /* contains the process and thread audit data */ + mblk_t *mp, /* contains the tihdr.h header structures */ + int from) /* timod or sockmod request */ +{ + int32_t len; + int32_t offset; + struct sockaddr_in *sock_data; + struct T_conn_req *conn_req; + struct T_conn_ind *conn_ind; + struct T_unitdata_req *unitdata_req; + struct T_unitdata_ind *unitdata_ind; + au_state_t estate; + t_audit_data_t *tad; + caddr_t saved_thread_ptr; + au_mask_t amask; + const auditinfo_addr_t *ainfo; + au_kcontext_t *kctx; + zone_status_t zstate; + + if (q->q_stream == NULL) + return; + mutex_enter(&q->q_stream->sd_lock); + /* are we being audited */ + saved_thread_ptr = q->q_stream->sd_t_audit_data; + /* no pointer to thread, nothing to do */ + if (saved_thread_ptr == NULL) { + mutex_exit(&q->q_stream->sd_lock); + return; + } + /* only allow one addition of a record token */ + q->q_stream->sd_t_audit_data = NULL; + /* + * thread is not the one being audited, then nothing to do + * This could be the stream thread handling the module + * service routine. In this case, the context for the audit + * record can no longer be assumed. Simplest to just drop + * the operation. + */ + if (curthread != (kthread_id_t)saved_thread_ptr) { + mutex_exit(&q->q_stream->sd_lock); + return; + } + if (curthread->t_sysnum >= SYS_so_socket && + curthread->t_sysnum <= SYS_sockconfig) { + mutex_exit(&q->q_stream->sd_lock); + return; + } + mutex_exit(&q->q_stream->sd_lock); + /* + * we know that the thread that did the put/getmsg is the + * one running. Now we can get the TAD and see if we should + * add an audit token. + */ + tad = U2A(u); + + kctx = SET_KCTX_PZ; + if (kctx == NULL) { + zstate = zone_status_get(curproc->p_zone); + ASSERT(zstate != ZONE_IS_READY); + return; + } + + /* proceed ONLY if user is being audited */ + if (!tad->tad_flag) + return; + + ainfo = crgetauinfo(CRED()); + if (ainfo == NULL) + return; + amask = ainfo->ai_mask; + + /* + * Figure out the type of stream networking request here. + * Note that getmsg and putmsg are always preselected + * because during the beginning of the system call we have + * not yet figure out which of the socket or tli request + * we are looking at until we are here. So we need to check + * against that specific request and reset the type of event. + */ + switch (type) { + case T_CONN_REQ: /* connection request */ + conn_req = (struct T_conn_req *)mp->b_rptr; + if (conn_req->DEST_offset < sizeof (struct T_conn_req)) + return; + offset = conn_req->DEST_offset; + len = conn_req->DEST_length; + estate = kctx->auk_ets[AUE_SOCKCONNECT]; + if (amask.as_success & estate || amask.as_failure & estate) { + tad->tad_event = AUE_SOCKCONNECT; + break; + } else { + return; + } + case T_CONN_IND: /* connectionless receive request */ + conn_ind = (struct T_conn_ind *)mp->b_rptr; + if (conn_ind->SRC_offset < sizeof (struct T_conn_ind)) + return; + offset = conn_ind->SRC_offset; + len = conn_ind->SRC_length; + estate = kctx->auk_ets[AUE_SOCKACCEPT]; + if (amask.as_success & estate || amask.as_failure & estate) { + tad->tad_event = AUE_SOCKACCEPT; + break; + } else { + return; + } + case T_UNITDATA_REQ: /* connectionless send request */ + unitdata_req = (struct T_unitdata_req *)mp->b_rptr; + if (unitdata_req->DEST_offset < sizeof (struct T_unitdata_req)) + return; + offset = unitdata_req->DEST_offset; + len = unitdata_req->DEST_length; + estate = kctx->auk_ets[AUE_SOCKSEND]; + if (amask.as_success & estate || amask.as_failure & estate) { + tad->tad_event = AUE_SOCKSEND; + break; + } else { + return; + } + case T_UNITDATA_IND: /* connectionless receive request */ + unitdata_ind = (struct T_unitdata_ind *)mp->b_rptr; + if (unitdata_ind->SRC_offset < sizeof (struct T_unitdata_ind)) + return; + offset = unitdata_ind->SRC_offset; + len = unitdata_ind->SRC_length; + estate = kctx->auk_ets[AUE_SOCKRECEIVE]; + if (amask.as_success & estate || amask.as_failure & estate) { + tad->tad_event = AUE_SOCKRECEIVE; + break; + } else { + return; + } + default: + return; + } + + /* + * we are only interested in tcp stream connections, + * not unix domain stuff + */ + if ((len < 0) || (len > sizeof (struct sockaddr_in))) { + tad->tad_event = AUE_GETMSG; + return; + } + /* skip over TPI header and point to the ip address */ + sock_data = (struct sockaddr_in *)((char *)mp->b_rptr + offset); + + switch (sock_data->sin_family) { + case AF_INET: + au_write(&(tad->tad_ad), au_to_sock_inet(sock_data)); + break; + default: /* reset to AUE_PUTMSG if not a inet request */ + tad->tad_event = AUE_GETMSG; + break; + } +} + +void +audit_lookupname() +{ +} + +/*ARGSUSED*/ +int +audit_pathcomp(struct pathname *pnp, vnode_t *cvp, cred_t *cr) +{ + return (0); +} + +static void +add_return_token(caddr_t *ad, unsigned int scid, int err, int rval) +{ + unsigned int sy_flags; + +#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_64RVAL) + au_write(ad, au_to_return64(err, rval)); + else + au_write(ad, au_to_return32(err, rval)); + +} + +/*ARGSUSED*/ +void +audit_fdsend(fd, fp, error) + int fd; + struct file *fp; + int error; /* ignore for now */ +{ + t_audit_data_t *tad; /* current thread */ + f_audit_data_t *fad; /* per file audit structure */ + struct vnode *vp; /* for file attributes */ + + /* is this system call being audited */ + tad = U2A(u); + ASSERT(tad != (t_audit_data_t *)0); + if (!tad->tad_flag) + return; + + fad = F2A(fp); + + /* add path and file attributes */ + if (fad != NULL && fad->fad_aupath != NULL) { + au_uwrite(au_to_arg32(0, "send fd", (uint32_t)fd)); + au_uwrite(au_to_path(fad->fad_aupath)); + } else { + au_uwrite(au_to_arg32(0, "send fd", (uint32_t)fd)); +#ifdef _LP64 + au_uwrite(au_to_arg64(0, "no path", (uint64_t)fp)); +#else + au_uwrite(au_to_arg32(0, "no path", (uint32_t)fp)); +#endif + } + vp = fp->f_vnode; /* include vnode attributes */ + audit_attributes(vp); +} + +/* + * Record privileges sucessfully used and we attempted to use but + * didn't have. + */ +void +audit_priv(int priv, const priv_set_t *set, int flag) +{ + t_audit_data_t *tad; + int sbit; + priv_set_t *target; + + /* Make sure this isn't being called in an interrupt context */ + ASSERT(servicing_interrupt() == 0); + + tad = U2A(u); + + if (tad->tad_flag == 0) + return; + + target = flag ? &tad->tad_sprivs : &tad->tad_fprivs; + sbit = flag ? PAD_SPRIVUSE : PAD_FPRIVUSE; + + /* Tell audit_success() and audit_finish() that we saw this case */ + if (!(tad->tad_evmod & sbit)) { + /* Clear set first time around */ + priv_emptyset(target); + tad->tad_evmod |= sbit; + } + + /* Save the privileges in the tad */ + if (priv == PRIV_ALL) { + priv_fillset(target); + } else { + ASSERT(set != NULL || priv != PRIV_NONE); + if (set != NULL) + priv_union(set, target); + if (priv != PRIV_NONE) + priv_addset(target, priv); + } +} + +/* + * Audit the setpriv() system call; the operation, the set name and + * the current value as well as the set argument are put in the + * audit trail. + */ +void +audit_setppriv(int op, int set, const priv_set_t *newpriv, const cred_t *ocr) +{ + t_audit_data_t *tad; + const priv_set_t *oldpriv; + priv_set_t report; + const char *setname; + + tad = U2A(u); + + if (tad->tad_flag == 0) + return; + + oldpriv = priv_getset(ocr, set); + + /* Generate the actual record, include the before and after */ + au_uwrite(au_to_arg32(2, "op", op)); + setname = priv_getsetbynum(set); + + switch (op) { + case PRIV_OFF: + /* Report privileges actually switched off */ + report = *oldpriv; + priv_intersect(newpriv, &report); + au_uwrite(au_to_privset(setname, &report, AUT_PRIV, 0)); + break; + case PRIV_ON: + /* Report privileges actually switched on */ + report = *oldpriv; + priv_inverse(&report); + priv_intersect(newpriv, &report); + au_uwrite(au_to_privset(setname, &report, AUT_PRIV, 0)); + break; + case PRIV_SET: + /* Report before and after */ + au_uwrite(au_to_privset(setname, oldpriv, AUT_PRIV, 0)); + au_uwrite(au_to_privset(setname, newpriv, AUT_PRIV, 0)); + break; + } +} + +/* + * Dump the full device policy setting in the audit trail. + */ +void +audit_devpolicy(int nitems, const devplcysys_t *items) +{ + t_audit_data_t *tad; + int i; + + tad = U2A(u); + + if (tad->tad_flag == 0) + return; + + for (i = 0; i < nitems; i++) { + au_uwrite(au_to_arg32(2, "major", items[i].dps_maj)); + if (items[i].dps_minornm[0] == '\0') { + au_uwrite(au_to_arg32(2, "lomin", items[i].dps_lomin)); + au_uwrite(au_to_arg32(2, "himin", items[i].dps_himin)); + } else + au_uwrite(au_to_text(items[i].dps_minornm)); + + au_uwrite(au_to_privset("read", &items[i].dps_rdp, + AUT_PRIV, 0)); + au_uwrite(au_to_privset("write", &items[i].dps_wrp, + AUT_PRIV, 0)); + } +} + +/*ARGSUSED*/ +void +audit_fdrecv(fd, fp) + int fd; + struct file *fp; +{ + t_audit_data_t *tad; /* current thread */ + f_audit_data_t *fad; /* per file audit structure */ + struct vnode *vp; /* for file attributes */ + + /* is this system call being audited */ + tad = U2A(u); + ASSERT(tad != (t_audit_data_t *)0); + if (!tad->tad_flag) + return; + + fad = F2A(fp); + + /* add path and file attributes */ + if (fad != NULL && fad->fad_aupath != NULL) { + au_uwrite(au_to_arg32(0, "recv fd", (uint32_t)fd)); + au_uwrite(au_to_path(fad->fad_aupath)); + } else { + au_uwrite(au_to_arg32(0, "recv fd", (uint32_t)fd)); +#ifdef _LP64 + au_uwrite(au_to_arg64(0, "no path", (uint64_t)fp)); +#else + au_uwrite(au_to_arg32(0, "no path", (uint32_t)fp)); +#endif + } + vp = fp->f_vnode; /* include vnode attributes */ + audit_attributes(vp); +} + +/* + * ROUTINE: AUDIT_CRYPTOADM + * PURPOSE: Records arguments to administrative ioctls on /dev/cryptoadm + * CALLBY: CRYPTO_LOAD_DEV_DISABLED, CRYPTO_LOAD_SOFT_DISABLED, + * CRYPTO_UNLOAD_SOFT_MODULE, CRYPTO_LOAD_SOFT_CONFIG, + * CRYPTO_POOL_CREATE, CRYPTO_POOL_WAIT, CRYPTO_POOL_RUN, + * CRYPTO_LOAD_DOOR + * NOTE: + * TODO: + * QUESTION: + */ + +void +audit_cryptoadm(int cmd, char *module_name, crypto_mech_name_t *mech_names, + uint_t mech_count, uint_t device_instance, uint32_t rv, int error) +{ + boolean_t mech_list_required = B_FALSE; + cred_t *cr = CRED(); + t_audit_data_t *tad; + token_t *ad = NULL; + const auditinfo_addr_t *ainfo = crgetauinfo(cr); + char buffer[MAXNAMELEN * 2]; + au_kcontext_t *kctx = SET_KCTX_PZ; + + ASSERT(kctx != NULL); + + tad = U2A(u); + if (tad == NULL) + return; + + if (ainfo == NULL) + return; + + tad->tad_event = AUE_CRYPTOADM; + + if (audit_success(kctx, tad, error) != AU_OK) + return; + + /* Add a subject token */ + AUDIT_SETSUBJ((caddr_t *)&(ad), cr, ainfo); + + /* add an optional group token */ + AUDIT_SETGROUP((caddr_t *)&(ad), cr, kctx); + + switch (cmd) { + case CRYPTO_LOAD_DEV_DISABLED: + if (error == 0 && rv == CRYPTO_SUCCESS) { + (void) snprintf(buffer, sizeof (buffer), + "op=CRYPTO_LOAD_DEV_DISABLED, module=%s," + " dev_instance=%d", + module_name, device_instance); + mech_list_required = B_TRUE; + } else { + (void) snprintf(buffer, sizeof (buffer), + "op=CRYPTO_LOAD_DEV_DISABLED, return_val=%d", rv); + } + break; + + case CRYPTO_LOAD_SOFT_DISABLED: + if (error == 0 && rv == CRYPTO_SUCCESS) { + (void) snprintf(buffer, sizeof (buffer), + "op=CRYPTO_LOAD_SOFT_DISABLED, module=%s", + module_name); + mech_list_required = B_TRUE; + } else { + (void) snprintf(buffer, sizeof (buffer), + "op=CRYPTO_LOAD_SOFT_DISABLED, return_val=%d", rv); + } + break; + + case CRYPTO_UNLOAD_SOFT_MODULE: + if (error == 0 && rv == CRYPTO_SUCCESS) { + (void) snprintf(buffer, sizeof (buffer), + "op=CRYPTO_UNLOAD_SOFT_MODULE, module=%s", + module_name); + } else { + (void) snprintf(buffer, sizeof (buffer), + "op=CRYPTO_UNLOAD_SOFT_MODULE, return_val=%d", rv); + } + break; + + case CRYPTO_LOAD_SOFT_CONFIG: + if (error == 0 && rv == CRYPTO_SUCCESS) { + (void) snprintf(buffer, sizeof (buffer), + "op=CRYPTO_LOAD_SOFT_CONFIG, module=%s", + module_name); + mech_list_required = B_TRUE; + } else { + (void) snprintf(buffer, sizeof (buffer), + "op=CRYPTO_LOAD_SOFT_CONFIG, return_val=%d", rv); + } + break; + + case CRYPTO_POOL_CREATE: + (void) snprintf(buffer, sizeof (buffer), + "op=CRYPTO_POOL_CREATE"); + break; + + case CRYPTO_POOL_WAIT: + (void) snprintf(buffer, sizeof (buffer), "op=CRYPTO_POOL_WAIT"); + break; + + case CRYPTO_POOL_RUN: + (void) snprintf(buffer, sizeof (buffer), "op=CRYPTO_POOL_RUN"); + break; + + case CRYPTO_LOAD_DOOR: + if (error == 0 && rv == CRYPTO_SUCCESS) + (void) snprintf(buffer, sizeof (buffer), + "op=CRYPTO_LOAD_DOOR"); + else + (void) snprintf(buffer, sizeof (buffer), + "op=CRYPTO_LOAD_DOOR, return_val=%d", rv); + break; + + default: + return; + } + + au_write((caddr_t *)&ad, au_to_text(buffer)); + + if (mech_list_required) { + int i; + + if (mech_count == 0) { + au_write((caddr_t *)&ad, au_to_text("mech=list empty")); + } else { + char *pb = buffer; + size_t l = sizeof (buffer); + size_t n; + char space[2] = ":"; + + n = snprintf(pb, l, "mech="); + + for (i = 0; i < mech_count; i++) { + pb += n; + l -= n; + if (l < 0) + l = 0; + + if (i == mech_count - 1) + (void) strcpy(space, ""); + + n = snprintf(pb, l, "%s%s", mech_names[i], + space); + } + au_write((caddr_t *)&ad, au_to_text(buffer)); + } + } + + /* add a return token */ + if (error || (rv != CRYPTO_SUCCESS)) + add_return_token((caddr_t *)&ad, tad->tad_scid, -1, error); + else + add_return_token((caddr_t *)&ad, tad->tad_scid, 0, rv); + + AS_INC(as_generated, 1, kctx); + AS_INC(as_kernel, 1, kctx); + + au_close(kctx, (caddr_t *)&ad, AU_OK, AUE_CRYPTOADM, 0); +} |