#include <sys/param.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/vnode.h>
#include <sys/vfs.h>
#include <sys/session.h>
#include <sys/kmem.h>
#include <sys/cred.h>
#include <sys/types.h>
#include <sys/proc.h>
#include <sys/uio.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/pathname.h>
#include <sys/acct.h>
#include <sys/stropts.h>
#include <sys/exec.h>
#include <sys/thread.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>
#include <sys/disp.h>
#include <sys/kobj.h>
#include <sys/sysmacros.h>
#include <sys/policy.h>
#include <sys/taskq.h>
#include <sys/zone.h>
#include <c2/audit.h>
#include <c2/audit_kernel.h>
#include <c2/audit_record.h>
#define HEADER_SIZE64 1;
#define HEADER_SIZE32 0;
#define AU_MIN_FILE_SZ 0x80000
#define AUDIT_REC_SIZE 0x8000
extern pri_t minclsyspri;
static clock_t au_resid = 15;
static void au_output_thread();
#include <sys/modctl.h>
static struct modlmisc modlmisc = {
&mod_miscops, "Solaris Auditing (C2)"
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modlmisc, 0
};
int
_init()
{
return (mod_install(&modlinkage));
}
int
_fini()
{
return (EBUSY);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
int
audit(caddr_t record, int length)
{
char c;
int count, l;
token_t *m, *n, *s, *ad;
int hdrlen, delta;
adr_t hadr;
adr_t sadr;
int size;
int host_len;
size_t zlen;
au_kcontext_t *kctx = GET_KCTX_PZ;
uint32_t auditing;
auditing = (U2A(u)->tad_audit != AUC_UNSET) ?
U2A(u)->tad_audit : kctx->auk_auditstate;
if (auditing & ~(AUC_AUDITING | AUC_INIT_AUDIT))
return (0);
if (secpolicy_audit_modify(CRED()) != 0)
return (EPERM);
if (length > AUDIT_REC_SIZE)
return (E2BIG);
#define AU_MIN_HEADER_LEN (sizeof (char) + sizeof (int32_t) + \
sizeof (char) + sizeof (short) + sizeof (short) + \
(sizeof (int32_t) * 2))
if (length < AU_MIN_HEADER_LEN)
return (EINVAL);
count = length;
m = n = s = ad = NULL;
while (count) {
m = au_getclr();
if (!s)
s = n = m;
else {
n->next_buf = m;
n = m;
}
l = MIN(count, AU_BUFSIZE);
if (copyin(record, memtod(m, caddr_t), (size_t)l)) {
au_free_rec(s);
return (EFAULT);
}
record += l;
count -= l;
m->len = (uchar_t)l;
}
au_write((caddr_t *)&(ad), s);
adr_start(&hadr, memtod(s, char *));
(void) adr_getchar(&hadr, &c);
switch (c) {
case AUT_HEADER32:
delta = 1 + 2 + 2;
hdrlen = 1 + 4 + delta + (sizeof (int32_t) * 2);
size = HEADER_SIZE32;
break;
#ifdef _LP64
case AUT_HEADER64:
delta = 1 + 2 + 2;
hdrlen = 1 + 4 + delta + (sizeof (int64_t) * 2);
size = HEADER_SIZE64;
break;
#endif
case AUT_HEADER32_EX:
hadr.adr_now += 9;
(void) adr_getint32(&hadr, &host_len);
hadr.adr_now -= 9 + sizeof (int32_t);
delta = 1 + 2 + 2 + 4 + host_len;
hdrlen = 1 + 4 + delta + (sizeof (int32_t) * 2);
size = HEADER_SIZE32;
break;
#ifdef _LP64
case AUT_HEADER64_EX:
hadr.adr_now += 9;
(void) adr_getint32(&hadr, &host_len);
hadr.adr_now -= 9 + sizeof (int32_t);
delta = 1 + 2 + 2 + 4 + host_len;
hdrlen = 1 + 4 + delta + (sizeof (int64_t) * 2);
size = HEADER_SIZE64;
break;
#endif
default:
au_free_rec(s);
return (EINVAL);
}
if (length < hdrlen) {
au_free_rec(s);
return (0);
}
hadr.adr_now += 4;
(void) adr_getchar(&hadr, &c);
if (c != TOKEN_VERSION) {
au_free_rec(s);
return (EINVAL);
}
hadr.adr_now -= 5;
if (kctx->auk_policy & AUDIT_ZONENAME) {
zlen = au_zonename_length(NULL);
if (zlen > 0) {
length += zlen;
m = au_to_zonename(zlen, NULL);
(void) au_append_rec(ad, m, AU_PACK);
}
}
if (kctx->auk_policy & AUDIT_SEQ) {
m = au_to_seq();
length += 5;
(void) au_append_rec(ad, m, AU_LINK);
adr_start(&sadr, memtod(m, char *));
sadr.adr_now += 1;
} else
sadr.adr_now = (char *)NULL;
if (kctx->auk_policy & AUDIT_TRAIL) {
length += 7;
(void) au_append_rec(ad, au_to_trailer(length), AU_PACK);
}
adr_int32(&hadr, (int32_t *)&length, 1);
hadr.adr_now += delta;
AS_INC(as_generated, 1, kctx);
AS_INC(as_audit, 1, kctx);
au_enqueue(kctx, s, &hadr, &sadr, size, 0);
AS_INC(as_totalsize, length, kctx);
return (0);
}
int
auditdoor(int fd)
{
struct file *fp;
struct vnode *vp;
int do_create = 0;
au_kcontext_t *kctx;
if (secpolicy_audit_config(CRED()) != 0)
return (EPERM);
if (!(audit_policy & AUDIT_PERZONE) && !INGLOBALZONE(curproc))
return (EINVAL);
kctx = GET_KCTX_NGZ;
if ((fp = (struct file *)getf(fd)) == NULL) {
return (EBADF);
}
vp = fp->f_vnode;
if (vp->v_type != VDOOR) {
cmn_err(CE_WARN,
"auditdoor() did not get the expected door descriptor\n");
releasef(fd);
return (EINVAL);
}
mutex_enter(&(kctx->auk_svc_lock));
if (kctx->auk_current_vp != NULL)
VN_RELE(kctx->auk_current_vp);
kctx->auk_current_vp = vp;
VN_HOLD(kctx->auk_current_vp);
releasef(fd);
if (!kctx->auk_output_active) {
kctx->auk_output_active = 1;
do_create = 1;
}
mutex_exit(&(kctx->auk_svc_lock));
if (do_create) {
kctx->auk_taskq =
taskq_create("output_master", 1, minclsyspri, 1, 1, 0);
(void) taskq_dispatch(kctx->auk_taskq,
(task_func_t *)au_output_thread,
kctx, TQ_SLEEP);
}
return (0);
}
static void
audit_dont_stop(void *kctx)
{
if ((((au_kcontext_t *)kctx)->auk_valid != AUK_VALID) ||
(((au_kcontext_t *)kctx)->auk_auditstate == AUC_NOAUDIT))
return;
mutex_enter(&(((au_kcontext_t *)kctx)->auk_queue.lock));
cv_broadcast(&(((au_kcontext_t *)kctx)->auk_queue.write_cv));
mutex_exit(&(((au_kcontext_t *)kctx)->auk_queue.lock));
}
static void
au_queue_kick(void *kctx)
{
if ((((au_kcontext_t *)kctx)->auk_valid != AUK_VALID) ||
(((au_kcontext_t *)kctx)->auk_auditstate == AUC_NOAUDIT))
return;
if (((au_kcontext_t *)kctx)->auk_queue.cnt &&
((au_kcontext_t *)kctx)->auk_queue.rd_block)
cv_broadcast(&((au_kcontext_t *)kctx)->auk_queue.read_cv);
(void) timeout(au_queue_kick, kctx,
((au_kcontext_t *)kctx)->auk_queue.delay);
}
static void
au_output_thread(au_kcontext_t *kctx)
{
int error = 0;
(void) timeout(au_queue_kick, kctx, kctx->auk_queue.delay);
while (!error) {
if (kctx->auk_auditstate == AUC_AUDITING) {
mutex_enter(&(kctx->auk_queue.lock));
while (kctx->auk_queue.head == NULL) {
if (kctx->auk_queue.wt_block) {
cv_broadcast(&(kctx->
auk_queue.write_cv));
}
kctx->auk_queue.rd_block = 1;
AS_INC(as_rblocked, 1, kctx);
cv_wait(&(kctx->auk_queue.read_cv),
&(kctx->auk_queue.lock));
kctx->auk_queue.rd_block = 0;
if (kctx->auk_auditstate != AUC_AUDITING) {
mutex_exit(&(kctx->auk_queue.lock));
(void) timeout(audit_dont_stop, kctx,
au_resid);
goto output_exit;
}
kctx->auk_queue.rd_block = 0;
}
mutex_exit(&(kctx->auk_queue.lock));
error = au_doorio(kctx);
} else {
break;
}
}
output_exit:
mutex_enter(&(kctx->auk_svc_lock));
VN_RELE(kctx->auk_current_vp);
kctx->auk_current_vp = NULL;
kctx->auk_output_active = 0;
mutex_exit(&(kctx->auk_svc_lock));
}