#include <sys/door.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/statvfs.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/vfs.h>
#include <sys/user.h>
#include <sys/uio.h>
#include <sys/reboot.h>
#include <sys/kmem.h>
#include <sys/resource.h>
#include <sys/cmn_err.h>
#include <sys/systm.h>
#include <sys/debug.h>
#include <sys/sysmacros.h>
#include <sys/syscall.h>
#include <sys/zone.h>
#include <c2/audit.h>
#include <c2/audit_kernel.h>
#include <c2/audit_record.h>
#include <c2/audit_kevents.h>
#include <c2/audit_door_infc.h>
static void au_dequeue(au_kcontext_t *, au_buff_t *);
static void audit_async_finish_backend(void *);
static int audit_sync_block(au_kcontext_t *);
static int state_if_part[] = {
AU_DBUF_FIRST, AU_DBUF_MIDDLE, AU_DBUF_MIDDLE, AU_DBUF_FIRST};
static int state_if_not_part[] = {
AU_DBUF_COMPLETE, AU_DBUF_LAST, AU_DBUF_LAST, AU_DBUF_COMPLETE};
void
au_uwrite(token_t *m)
{
au_write(&(u_ad), m);
}
void
au_write(caddr_t *d, token_t *m)
{
if (d == NULL) {
au_toss_token(m);
return;
}
if (m == (token_t *)0) {
printf("au_write: null token\n");
return;
}
if (*d == NULL)
*d = (caddr_t)m;
else
(void) au_append_rec((au_buff_t *)*d, m, AU_PACK);
}
void
au_close(au_kcontext_t *kctx, caddr_t *d, int flag, au_event_t e_type,
au_emod_t e_mod, timestruc_t *e_time)
{
token_t *dchain;
t_audit_data_t *tad = U2A(u);
ASSERT(tad != NULL);
ASSERT(d != NULL);
ASSERT(kctx != NULL);
if ((dchain = (token_t *)*d) == (token_t *)NULL)
return;
*d = NULL;
if ((flag & AU_DONTBLOCK) || ((flag & AU_DEFER) &&
(tad->tad_scid != 0) && (tad->tad_scid != SYS_exit))) {
au_close_defer(dchain, flag, e_type, e_mod, e_time);
return;
}
au_close_time(kctx, dchain, flag, e_type, e_mod, e_time);
}
void
au_close_defer(token_t *dchain, int flag, au_event_t e_type, au_emod_t e_mod,
timestruc_t *e_time)
{
au_defer_info_t *attr;
t_audit_data_t *tad = U2A(u);
ASSERT(tad != NULL);
if ((flag & AU_OK) == 0) {
au_toss_token(dchain);
return;
}
attr = kmem_alloc(sizeof (au_defer_info_t), KM_NOSLEEP);
if (attr == NULL) {
au_toss_token(dchain);
return;
}
attr->audi_next = NULL;
attr->audi_ad = dchain;
attr->audi_e_type = e_type;
attr->audi_e_mod = e_mod;
attr->audi_flag = flag;
if (e_time != NULL)
attr->audi_atime = *e_time;
else
gethrestime(&attr->audi_atime);
if (flag & AU_DONTBLOCK) {
softcall(audit_async_finish_backend, attr);
audit_async_done(NULL, 0);
return;
}
if (tad->tad_defer_head)
tad->tad_defer_tail->audi_next = attr;
else
tad->tad_defer_head = attr;
tad->tad_defer_tail = attr;
}
static void
au_save_time(adr_t *hadrp, timestruc_t *time, int size)
{
struct {
uint32_t sec;
uint32_t usec;
} tv;
timestruc_t now;
if (time == NULL) {
gethrestime(&now);
time = &now;
}
#ifdef _LP64
if (size)
adr_int64(hadrp, (int64_t *)time, 2);
else
#endif
{
tv.sec = (uint32_t)time->tv_sec;
tv.usec = (uint32_t)time->tv_nsec;
adr_int32(hadrp, (int32_t *)&tv, 2);
}
}
void
au_close_time(au_kcontext_t *kctx, token_t *dchain, int flag, au_event_t e_type,
au_emod_t e_mod, timestruc_t *etime)
{
token_t *record;
int byte_count;
token_t *m;
adr_t hadr;
adr_t sadr;
size_t zone_length;
uint32_t auditing;
ASSERT(dchain != NULL);
if ((flag & AU_OK) == 0) {
au_toss_token(dchain);
return;
}
ASSERT(U2A(u) != NULL);
ASSERT(kctx != NULL);
auditing = (U2A(u)->tad_audit == AUC_UNSET)
? kctx->auk_auditstate
: U2A(u)->tad_audit;
if (auditing & ~(AUC_AUDITING | AUC_INIT_AUDIT)) {
if (e_type != AUE_SYSTEMBOOT) {
au_toss_token(dchain);
return;
}
}
byte_count = au_token_size(dchain);
byte_count += sizeof (char) + sizeof (int32_t) +
sizeof (char) + 2 * sizeof (short) + sizeof (timestruc_t);
if (kctx->auk_hostaddr_valid)
byte_count += sizeof (int32_t) +
kctx->auk_info.ai_termid.at_type;
if (kctx->auk_policy & AUDIT_ZONENAME) {
zone_length = au_zonename_length(NULL);
byte_count += zone_length;
} else {
zone_length = 0;
}
if (kctx->auk_policy & AUDIT_TRAIL)
byte_count += 7;
if (kctx->auk_policy & AUDIT_SEQ)
byte_count += 5;
if (kctx->auk_hostaddr_valid)
record = au_to_header_ex(byte_count, e_type, e_mod);
else
record = au_to_header(byte_count, e_type, e_mod);
adr_start(&hadr, memtod(record, char *));
hadr.adr_now += sizeof (char) + sizeof (int32_t) +
sizeof (char) + 2 * sizeof (short);
if (kctx->auk_hostaddr_valid)
hadr.adr_now += sizeof (int32_t) +
kctx->auk_info.ai_termid.at_type;
if (etime != NULL) {
au_save_time(&hadr, etime, 1);
hadr.adr_now = (char *)NULL;
}
(void) au_append_rec(record, dchain, AU_PACK);
if (zone_length > 0) {
m = au_to_zonename(zone_length, NULL);
(void) au_append_rec(record, m, AU_PACK);
}
if (kctx->auk_policy & AUDIT_SEQ) {
m = au_to_seq();
(void) au_append_rec(record, m, AU_LINK);
adr_start(&sadr, memtod(m, char *));
sadr.adr_now += 1;
} else {
sadr.adr_now = NULL;
}
if (kctx->auk_policy & AUDIT_TRAIL) {
(void) au_append_rec(record, au_to_trailer(byte_count),
AU_PACK);
}
#ifdef _LP64
au_enqueue(kctx, record, &hadr, &sadr, 1, flag & AU_DONTBLOCK);
#else
au_enqueue(kctx, record, &hadr, &sadr, 0, flag & AU_DONTBLOCK);
#endif
AS_INC(as_totalsize, byte_count, kctx);
}
void
au_enqueue(au_kcontext_t *kctx, au_buff_t *m, adr_t *hadrp, adr_t *sadrp,
int size, int dontblock)
{
if (kctx == NULL)
return;
mutex_enter(&(kctx->auk_queue.lock));
if (!dontblock && (kctx->auk_queue.cnt >= kctx->auk_queue.hiwater) &&
audit_sync_block(kctx)) {
mutex_exit(&(kctx->auk_queue.lock));
au_free_rec(m);
return;
}
if (hadrp->adr_now) {
au_save_time(hadrp, NULL, size);
}
if (sadrp->adr_now) {
kctx->auk_sequence++;
adr_int32(sadrp, (int32_t *)&(kctx->auk_sequence), 1);
}
if (kctx->auk_queue.head)
kctx->auk_queue.tail->next_rec = m;
else
kctx->auk_queue.head = m;
kctx->auk_queue.tail = m;
if (++(kctx->auk_queue.cnt) >
kctx->auk_queue.lowater && kctx->auk_queue.rd_block)
cv_broadcast(&(kctx->auk_queue.read_cv));
mutex_exit(&(kctx->auk_queue.lock));
AS_INC(as_enqueue, 1, kctx);
}
static void
au_dequeue(au_kcontext_t *kctx, au_buff_t *freeto)
{
au_buff_t *m, *l, *lastl;
int n = 0;
ASSERT(kctx != NULL);
mutex_enter(&(kctx->auk_queue.lock));
ASSERT(kctx->auk_queue.head != NULL);
ASSERT(freeto != NULL);
l = m = kctx->auk_queue.head;
do {
n++;
lastl = l;
l = l->next_rec;
} while (l != NULL && freeto != lastl);
kctx->auk_queue.cnt -= n;
lastl->next_rec = NULL;
kctx->auk_queue.head = l;
ASSERT(freeto == lastl);
if (kctx->auk_queue.cnt <= kctx->auk_queue.lowater &&
kctx->auk_queue.wt_block)
cv_broadcast(&(kctx->auk_queue.write_cv));
mutex_exit(&(kctx->auk_queue.lock));
while (m) {
l = m->next_rec;
au_free_rec(m);
m = l;
}
AS_INC(as_written, n, kctx);
}
static int
audit_sync_block(au_kcontext_t *kctx)
{
ASSERT(MUTEX_HELD(&(kctx->auk_queue.lock)));
do {
if (((U2A(u)->tad_audit != AUC_UNSET)
? (U2A(u)->tad_audit != AUC_AUDITING)
: (kctx->auk_auditstate != AUC_AUDITING)) ||
(kctx->auk_policy & AUDIT_CNT)) {
AS_INC(as_dropped, 1, kctx);
return (1);
}
if (kctx->auk_queue.rd_block &&
kctx->auk_queue.cnt > kctx->auk_queue.lowater)
cv_broadcast(&(kctx->auk_queue.read_cv));
AS_INC(as_wblocked, 1, kctx);
kctx->auk_queue.wt_block++;
cv_wait(&(kctx->auk_queue.write_cv), &(kctx->auk_queue.lock));
kctx->auk_queue.wt_block--;
} while (kctx->auk_queue.cnt >= kctx->auk_queue.hiwater);
return (0);
}
static int
audit_async_block(au_kcontext_t *kctx, caddr_t *rpp)
{
ASSERT(kctx != NULL);
mutex_enter(&(kctx->auk_queue.lock));
if (kctx->auk_queue.cnt >= kctx->auk_queue.hiwater) {
mutex_exit(&(kctx->auk_queue.lock));
audit_async_drop(rpp, AU_BACKEND);
return (1);
}
mutex_exit(&(kctx->auk_queue.lock));
return (0);
}
#define AGAIN_TICKS 10
static int
au_door_upcall(au_kcontext_t *kctx, au_dbuf_t *aubuf)
{
int rc;
door_arg_t darg;
int retry = 1;
int ticks_to_wait;
darg.data_ptr = (char *)aubuf;
darg.data_size = AU_DBUF_HEADER + aubuf->aub_size;
darg.desc_ptr = NULL;
darg.desc_num = 0;
while (retry == 1) {
darg.rbuf = (char *)aubuf;
darg.rsize = darg.data_size;
retry = 0;
mutex_enter(&(kctx->auk_svc_lock));
if (kctx->auk_current_vp == NULL) {
mutex_exit(&(kctx->auk_svc_lock));
return (-1);
}
rc = door_upcall(kctx->auk_current_vp, &darg, NULL,
SIZE_MAX, 0);
if (rc != 0) {
mutex_exit(&(kctx->auk_svc_lock));
if (rc == EAGAIN)
ticks_to_wait = AGAIN_TICKS;
else
return (rc);
mutex_enter(&(kctx->auk_eagain_mutex));
(void) cv_reltimedwait(&(kctx->auk_eagain_cv),
&(kctx->auk_eagain_mutex), ticks_to_wait,
TR_CLOCK_TICK);
mutex_exit(&(kctx->auk_eagain_mutex));
retry = 1;
} else
mutex_exit(&(kctx->auk_svc_lock));
}
if (darg.rbuf == NULL)
return (-1);
return (*(int *)darg.rbuf);
}
int
au_doormsg(au_kcontext_t *kctx, uint32_t message_code, void *message)
{
int rc;
au_dbuf_t *buf;
size_t alloc_size;
switch (message_code) {
case AU_DBUF_POLICY:
alloc_size = AU_DBUF_HEADER + sizeof (uint32_t);
buf = kmem_alloc(alloc_size, KM_SLEEP);
buf->aub_size = sizeof (uint32_t);
*(uint32_t *)buf->aub_buf = *(uint32_t *)message;
break;
case AU_DBUF_SHUTDOWN:
alloc_size = AU_DBUF_HEADER;
buf = kmem_alloc(alloc_size, KM_SLEEP);
buf->aub_size = 0;
break;
default:
return (1);
}
buf->aub_type = AU_DBUF_NOTIFY | message_code;
rc = au_door_upcall(kctx, buf);
kmem_free(buf, alloc_size);
return (rc);
}
int
au_doorio(au_kcontext_t *kctx)
{
off_t off;
ssize_t used;
token_t *cAR;
token_t *cMB;
token_t *sp;
char *bp;
unsigned char *cp;
int error = 0;
ssize_t sz;
ssize_t len;
ssize_t curr_sz = 0;
int part = 0;
int partial_state = AU_DBUF_COMPLETE;
if (kctx->auk_queue.bufsz != kctx->auk_queue.buflen) {
size_t new_sz = kctx->auk_queue.bufsz;
kmem_free(kctx->auk_dbuffer, AU_DBUF_HEADER +
kctx->auk_queue.buflen);
kctx->auk_dbuffer = kmem_alloc(AU_DBUF_HEADER + new_sz,
KM_SLEEP);
kctx->auk_queue.buflen = new_sz;
}
if (!kctx->auk_queue.head)
goto nodata;
sp = NULL;
off = 0;
used = 0;
cAR = kctx->auk_queue.head;
cMB = cAR;
bp = &(kctx->auk_dbuffer->aub_buf[0]);
while (cMB) {
part = 1;
cp = memtod(cMB, unsigned char *);
sz = (ssize_t)cMB->len - used;
len = (ssize_t)MIN(sz, kctx->auk_queue.buflen - off);
bcopy(cp + used, bp + off, len);
used += len;
off += len;
if (used >= (ssize_t)cMB->len) {
used = 0;
cMB = cMB->next_buf;
}
if (cMB == NULL) {
sp = cAR;
cAR = cAR->next_rec;
cMB = cAR;
part = 0;
}
error = 0;
if ((kctx->auk_queue.buflen == off) || (part == 0)) {
if (part)
partial_state = state_if_part[partial_state];
else
partial_state =
state_if_not_part[partial_state];
kctx->auk_dbuffer->aub_type = partial_state;
kctx->auk_dbuffer->aub_size = off;
error = au_door_upcall(kctx, kctx->auk_dbuffer);
if (error != 0)
goto nodata;
if (sp)
au_dequeue(kctx, sp);
curr_sz += off;
sp = NULL;
off = 0;
}
}
nodata:
return (error);
}
void
audit_async_done(caddr_t *rpp, int flags)
{
t_audit_data_t *tad = U2A(u);
if (!(flags & AU_BACKEND)) {
ASSERT(tad != NULL);
ASSERT(tad->tad_ctrl & TAD_ERRJMP);
tad->tad_ctrl &= ~TAD_ERRJMP;
tad->tad_errjmp = NULL;
}
if ((rpp != NULL) && (*rpp != NULL)) {
au_toss_token((au_buff_t *)*rpp);
*rpp = NULL;
}
}
void
audit_async_drop(caddr_t *rpp, int flags)
{
au_kcontext_t *kctx;
audit_async_done((caddr_t *)rpp, flags);
kctx = GET_KCTX_GZ;
if (((audit_policy & AUDIT_AHLT) == 0) ||
(kctx->auk_auditstate == AUC_INIT_AUDIT)) {
AS_INC(as_dropped, 1, kctx);
return;
}
sync();
sync();
panic("non-attributable halt. should dump core");
}
int
audit_async_start(label_t *jb, au_event_t event, int sorf)
{
t_audit_data_t *tad = U2A(u);
au_state_t estate;
int success = 0, failure = 0;
au_kcontext_t *kctx = GET_KCTX_GZ;
if ((kctx->auk_auditstate != AUC_AUDITING) &&
(kctx->auk_auditstate != AUC_INIT_AUDIT))
return (1);
estate = kctx->auk_ets[event];
if (sorf & AUM_SUCC)
success = kctx->auk_info.ai_namask.as_success & estate;
if (sorf & AUM_FAIL)
failure = kctx->auk_info.ai_namask.as_failure & estate;
if ((success | failure) == 0)
return (1);
ASSERT(tad->tad_errjmp == NULL);
tad->tad_errjmp = (void *)jb;
tad->tad_ctrl |= TAD_ERRJMP;
return (0);
}
void
audit_async_finish(caddr_t *ad, au_event_t aid, au_emod_t amod,
timestruc_t *e_time)
{
au_kcontext_t *kctx;
kctx = GET_KCTX_GZ;
au_close(kctx, ad, AU_DONTBLOCK | AU_OK, aid, PAD_NONATTR|amod, e_time);
}
static void
audit_async_finish_backend(void *addr)
{
au_kcontext_t *kctx;
au_defer_info_t *attr = (au_defer_info_t *)addr;
if (attr == NULL)
return;
kctx = GET_KCTX_GZ;
if (audit_async_block(kctx, (caddr_t *)&attr->audi_ad)) {
kmem_free(attr, sizeof (au_defer_info_t));
return;
}
if (attr->audi_e_type == AUE_EXITPROM) {
au_close_time(kctx, (token_t *)attr->audi_ad, attr->audi_flag,
attr->audi_e_type, attr->audi_e_mod, NULL);
} else {
au_close_time(kctx, (token_t *)attr->audi_ad, attr->audi_flag,
attr->audi_e_type, attr->audi_e_mod, &attr->audi_atime);
}
AS_INC(as_generated, 1, kctx);
AS_INC(as_nonattrib, 1, kctx);
kmem_free(attr, sizeof (au_defer_info_t));
}