#include <sys/kernel.h>
#include <sys/event.h>
#include <sys/kauth.h>
#include <sys/queue.h>
#include <sys/syscall.h>
#include <sys/sysent.h>
#include <sys/sysproto.h>
#include <sys/systm.h>
#include <sys/ucred.h>
#include <libkern/OSAtomic.h>
#include <bsm/audit.h>
#include <security/audit/audit.h>
#include <security/audit/audit_bsd.h>
#include <security/audit/audit_private.h>
#include <vm/vm_protos.h>
#include <kern/audit_sessionport.h>
kern_return_t ipc_object_copyin(ipc_space_t, mach_port_name_t,
mach_msg_type_name_t, ipc_port_t *);
void ipc_port_release_send(ipc_port_t);
struct auditinfo_addr audit_default_aia = {
.ai_auid = AU_DEFAUDITID,
.ai_asid = AU_DEFAUDITSID,
.ai_termid = { .at_type = AU_IPv4, },
};
#if CONFIG_AUDIT
#define HASH_TABLE_SIZE 97
#define HASH_ASID(asid) (audit_session_hash(asid) % HASH_TABLE_SIZE)
struct au_sentry {
auditinfo_addr_t se_auinfo;
#define se_asid se_auinfo.ai_asid
#define se_auid se_auinfo.ai_auid
#define se_mask se_auinfo.ai_mask
#define se_termid se_auinfo.ai_termid
#define se_flags se_auinfo.ai_flags
long se_refcnt;
long se_procnt;
ipc_port_t se_port;
struct klist se_klist;
struct mtx se_klist_mtx;
LIST_ENTRY(au_sentry) se_link;
};
typedef struct au_sentry au_sentry_t;
#define AU_SENTRY_PTR(aia_p) ((au_sentry_t *)(aia_p))
static struct rwlock se_entry_lck;
LIST_HEAD(au_sentry_head, au_sentry);
static struct au_sentry_head *au_sentry_bucket = NULL;
struct au_plist {
struct knote *pl_knote;
LIST_ENTRY(au_plist) pl_link;
};
typedef struct au_plist au_plist_t;
struct au_plisthead {
struct rlck ph_rlck;
LIST_HEAD(au_plhead, au_plist) ph_head;
};
typedef struct au_plisthead au_plisthead_t;
#define EV_ANY_ASID EV_FLAG0
MALLOC_DEFINE(M_AU_SESSION, "audit_session", "Audit session data");
MALLOC_DEFINE(M_AU_EV_PLIST, "audit_ev_plist", "Audit session event plist");
static int audit_filt_sessionattach(struct knote *kn);
static void audit_filt_sessiondetach(struct knote *kn);
static void audit_filt_sessiontouch(struct knote *kn,
struct kevent64_s *kev, long type);
static int audit_filt_session(struct knote *kn, long hint);
static void audit_register_kevents(uint32_t asid, uint32_t auid);
struct filterops audit_session_filtops = {
.f_attach = audit_filt_sessionattach,
.f_detach = audit_filt_sessiondetach,
.f_touch = audit_filt_sessiontouch,
.f_event = audit_filt_session,
};
static struct klist anyas_klist;
struct mtx anyas_klist_mtx;
#define AUDIT_ANYAS_KLIST_LOCK_INIT() mtx_init(&anyas_klist_mtx, \
"audit anyas_klist_mtx", NULL, MTX_DEF)
#define AUDIT_ANYAS_KLIST_LOCK() mtx_lock(&anyas_klist_mtx)
#define AUDIT_ANYAS_KLIST_UNLOCK() mtx_unlock(&anyas_klist_mtx)
#define AUDIT_ANYAS_KLIST_LOCK_ASSERT() mtx_assert(&anyas_klist_mtx, MA_OWNED)
#define AUDIT_SENTRY_RWLOCK_INIT() rw_init(&se_entry_lck, \
"audit se_entry_lck")
#define AUDIT_SENTRY_RLOCK() rw_rlock(&se_entry_lck)
#define AUDIT_SENTRY_WLOCK() rw_wlock(&se_entry_lck)
#define AUDIT_SENTRY_RWLOCK_ASSERT() rw_assert(&se_entry_lck, RA_LOCKED)
#define AUDIT_SENTRY_RUNLOCK() rw_runlock(&se_entry_lck)
#define AUDIT_SENTRY_WUNLOCK() rw_wunlock(&se_entry_lck)
#define AUDIT_SE_KLIST_LOCK_INIT(se, n) mtx_init(&(se)->se_klist_mtx, \
n, NULL, MTX_DEF)
#define AUDIT_SE_KLIST_LOCK(se) mtx_lock(&(se)->se_klist_mtx)
#define AUDIT_SE_KLIST_UNLOCK(se) mtx_unlock(&(se)->se_klist_mtx)
#define AUDIT_SE_KLIST_LOCK_DESTROY(se) mtx_destroy(&(se)->se_klist_mtx)
#define AUDIT_SE_KLIST_LOCK_ASSERT(se) mtx_assert(&(se)->se_klist_mtx, \
MA_OWNED)
#define AUDIT_PLIST_LOCK_INIT(pl) rlck_init(&(pl)->ph_rlck, \
"audit ph_rlck")
#define AUDIT_PLIST_LOCK(pl) rlck_lock(&(pl)->ph_rlck)
#define AUDIT_PLIST_UNLOCK(pl) rlck_unlock(&(pl)->ph_rlck)
#define AUDIT_PLIST_LOCK_DESTROY(pl) rlck_destroy(&(pl)->ph_rlck)
#if AUDIT_SESSION_DEBUG
#include <kern/kalloc.h>
struct au_sentry_debug {
auditinfo_addr_t se_auinfo;
long se_refcnt;
long se_procnt;
};
typedef struct au_sentry_debug au_sentry_debug_t;
static int audit_sysctl_session_debug(struct sysctl_oid *oidp, void *arg1,
int arg2, struct sysctl_req *req);
SYSCTL_PROC(_kern, OID_AUTO, audit_session_debug, CTLFLAG_RD, NULL, 0,
audit_sysctl_session_debug, "S,audit_session_debug",
"Current session debug info for auditing.");
static int
audit_sysctl_session_debug(__unused struct sysctl_oid *oidp,
__unused void *arg1, __unused int arg2, struct sysctl_req *req)
{
au_sentry_t *se;
au_sentry_debug_t *sed_tab, *next_sed;
int i, entry_cnt = 0;
size_t sz;
int err = 0;
if (req->newptr != USER_ADDR_NULL)
return (EPERM);
AUDIT_SENTRY_RLOCK();
for(i = 0; i < HASH_TABLE_SIZE; i++)
LIST_FOREACH(se, &au_sentry_bucket[i], se_link)
if (se != NULL)
entry_cnt++;
if (req->oldptr == USER_ADDR_NULL) {
req->oldidx = (entry_cnt + 3) * sizeof(au_sentry_debug_t);
AUDIT_SENTRY_RUNLOCK();
return (0);
}
if (req->oldlen < (entry_cnt * sizeof(au_sentry_debug_t))) {
AUDIT_SENTRY_RUNLOCK();
return (ENOMEM);
}
sed_tab = (au_sentry_debug_t *)kalloc_noblock(entry_cnt *
sizeof(au_sentry_debug_t));
if (sed_tab == NULL) {
AUDIT_SENTRY_RUNLOCK();
return (ENOMEM);
}
bzero(sed_tab, entry_cnt * sizeof(au_sentry_debug_t));
sz = 0;
next_sed = sed_tab;
for(i = 0; i < HASH_TABLE_SIZE; i++) {
LIST_FOREACH(se, &au_sentry_bucket[i], se_link) {
if (se != NULL) {
bcopy(se, next_sed, sizeof(next_sed));
next_sed++;
sz += sizeof(au_sentry_debug_t);
}
}
}
AUDIT_SENTRY_RUNLOCK();
req->oldlen = sz;
err = SYSCTL_OUT(req, sed_tab, sz);
kfree(sed_tab, entry_cnt * sizeof(au_sentry_debug_t));
return (err);
}
#endif
static inline uint32_t
audit_session_hash(au_asid_t asid)
{
uint32_t a = (uint32_t) asid;
a = (a - (a << 6)) ^ (a >> 17);
a = (a - (a << 9)) ^ (a << 4);
a = (a - (a << 3)) ^ (a << 10);
a = a ^ (a >> 15);
return (a);
}
static au_sentry_t *
audit_session_find(au_asid_t asid)
{
uint32_t hkey;
au_sentry_t *found_se;
AUDIT_SENTRY_RWLOCK_ASSERT();
hkey = HASH_ASID(asid);
LIST_FOREACH(found_se, &au_sentry_bucket[hkey], se_link)
if (found_se->se_asid == asid)
return (found_se);
return (NULL);
}
static void
audit_session_knote(au_sentry_t *se, long hint)
{
AUDIT_SE_KLIST_LOCK(se);
KNOTE(&se->se_klist, hint);
AUDIT_SE_KLIST_UNLOCK(se);
}
static void
audit_session_remove(au_sentry_t *se)
{
uint32_t hkey;
au_sentry_t *found_se, *tmp_se;
KASSERT(se->se_refcnt == 0, ("audit_session_remove: ref count != 0"));
hkey = HASH_ASID(se->se_asid);
AUDIT_SENTRY_WLOCK();
LIST_FOREACH_SAFE(found_se, &au_sentry_bucket[hkey], se_link, tmp_se) {
if (found_se == se) {
audit_session_knote(found_se, NOTE_AS_CLOSE);
LIST_REMOVE(found_se, se_link);
AUDIT_SENTRY_WUNLOCK();
AUDIT_SE_KLIST_LOCK_DESTROY(found_se);
found_se->se_refcnt = 0;
free(found_se, M_AU_SESSION);
return;
}
}
AUDIT_SENTRY_WUNLOCK();
}
static void
audit_ref_session(au_sentry_t *se)
{
long old_val;
old_val = OSAddAtomicLong(1, &se->se_refcnt);
KASSERT(old_val < 100000,
("audit_ref_session: Too many references on session."));
}
static void
audit_unref_session(au_sentry_t *se)
{
long old_val;
old_val = OSAddAtomicLong(-1, &se->se_refcnt);
if (old_val == 1)
audit_session_remove(se);
KASSERT(old_val > 0,
("audit_unref_session: Too few references on session."));
}
static void
audit_inc_procount(au_sentry_t *se)
{
long old_val;
old_val = OSAddAtomicLong(1, &se->se_procnt);
KASSERT(old_val <= PID_MAX,
("audit_inc_procount: proc count > PID_MAX"));
}
static void
audit_dec_procount(au_sentry_t *se)
{
long old_val;
old_val = OSAddAtomicLong(-1, &se->se_procnt);
if (old_val == 1)
audit_session_knote(se, NOTE_AS_END);
KASSERT(old_val >= 1,
("audit_dec_procount: proc count < 0"));
}
static int
audit_update_sentry(au_sentry_t *se, auditinfo_addr_t *new_aia)
{
auditinfo_addr_t *aia = &se->se_auinfo;
int update;
KASSERT(new_aia != &audit_default_aia,
("audit_update_sentry: Trying to update the default aia."));
update = (aia->ai_auid != new_aia->ai_auid ||
bcmp(&aia->ai_termid, &new_aia->ai_termid,
sizeof(new_aia->ai_termid)) ||
aia->ai_flags != new_aia->ai_flags);
if (update)
bcopy(new_aia, aia, sizeof(*aia));
return (update);
}
static uint32_t
audit_session_nextid(void)
{
static uint32_t next_asid = ASSIGNED_ASID_MIN;
AUDIT_SENTRY_RWLOCK_ASSERT();
if (next_asid > ASSIGNED_ASID_MAX)
next_asid = ASSIGNED_ASID_MIN;
return (next_asid++);
}
static auditinfo_addr_t *
audit_session_new(auditinfo_addr_t *new_aia, int newprocess)
{
au_asid_t asid;
au_sentry_t *se = NULL;
auditinfo_addr_t *aia = NULL;
char nm[LOCK_MAX_NAME];
KASSERT(new_aia != NULL, ("audit_session_new: new_aia == NULL"));
asid = new_aia->ai_asid;
#if 0
KASSERT((asid != AU_ASSIGN_ASID && asid <= PID_MAX),
("audit_session_new: illegal ASID value: %d", asid));
#endif
se = malloc(sizeof(au_sentry_t), M_AU_SESSION, M_WAITOK | M_ZERO);
snprintf(nm, sizeof(nm), "audit se_klist_mtx %d", asid);
AUDIT_SE_KLIST_LOCK_INIT(se, nm);
AUDIT_SENTRY_WLOCK();
if (asid == AU_ASSIGN_ASID) {
do {
asid = (au_asid_t)audit_session_nextid();
} while(audit_session_find(asid) != NULL);
} else {
au_sentry_t *found_se = NULL;
if ((found_se = audit_session_find(asid)) != NULL) {
int updated;
updated = audit_update_sentry(found_se, new_aia);
audit_ref_session(found_se);
AUDIT_SENTRY_WUNLOCK();
AUDIT_SE_KLIST_LOCK_DESTROY(se);
free(se, M_AU_SESSION);
if (updated)
audit_session_knote(found_se, NOTE_AS_UPDATE);
if (newprocess)
audit_inc_procount(found_se);
return (&found_se->se_auinfo);
}
}
se->se_refcnt = se->se_procnt = 1;
se->se_port = IPC_PORT_NULL;
aia = &se->se_auinfo;
aia->ai_asid = asid;
aia->ai_auid = new_aia->ai_auid;
bzero(&new_aia->ai_mask, sizeof(new_aia->ai_mask));
bcopy(&new_aia->ai_termid, &aia->ai_termid, sizeof(aia->ai_termid));
aia->ai_flags = new_aia->ai_flags;
LIST_INSERT_HEAD(&au_sentry_bucket[HASH_ASID(asid)], se, se_link);
AUDIT_SENTRY_WUNLOCK();
audit_register_kevents(se->se_asid, se->se_auid);
audit_session_knote(se, NOTE_AS_START);
return (aia);
}
int
audit_session_lookup(au_asid_t asid, auditinfo_addr_t *ret_aia)
{
au_sentry_t *se = NULL;
if ((uint32_t)asid > ASSIGNED_ASID_MAX)
return (-1);
AUDIT_SENTRY_RLOCK();
if ((se = audit_session_find(asid)) == NULL) {
AUDIT_SENTRY_RUNLOCK();
return (1);
}
if (ret_aia != NULL)
bcopy(&se->se_auinfo, ret_aia, sizeof(*ret_aia));
AUDIT_SENTRY_RUNLOCK();
return (0);
}
void
audit_session_ref(kauth_cred_t cred)
{
auditinfo_addr_t *aia_p;
KASSERT(IS_VALID_CRED(cred),
("audit_session_ref: Invalid kauth_cred."));
aia_p = cred->cr_audit.as_aia_p;
if (IS_VALID_SESSION(aia_p))
audit_ref_session(AU_SENTRY_PTR(aia_p));
}
void
audit_session_unref(kauth_cred_t cred)
{
auditinfo_addr_t *aia_p;
KASSERT(IS_VALID_CRED(cred),
("audit_session_unref: Invalid kauth_cred."));
aia_p = cred->cr_audit.as_aia_p;
if (IS_VALID_SESSION(aia_p))
audit_unref_session(AU_SENTRY_PTR(aia_p));
}
void
audit_session_procnew(kauth_cred_t cred)
{
auditinfo_addr_t *aia_p;
KASSERT(IS_VALID_CRED(cred),
("audit_session_procnew: Invalid kauth_cred."));
aia_p = cred->cr_audit.as_aia_p;
if (IS_VALID_SESSION(aia_p))
audit_inc_procount(AU_SENTRY_PTR(aia_p));
}
void
audit_session_procexit(kauth_cred_t cred)
{
auditinfo_addr_t *aia_p;
KASSERT(IS_VALID_CRED(cred),
("audit_session_procexit: Invalid kauth_cred."));
aia_p = cred->cr_audit.as_aia_p;
if (IS_VALID_SESSION(aia_p))
audit_dec_procount(AU_SENTRY_PTR(aia_p));
}
void
audit_session_init(void)
{
int i;
KASSERT((ASSIGNED_ASID_MAX - ASSIGNED_ASID_MIN) > PID_MAX,
("audit_session_init: ASSIGNED_ASID_MAX is not large enough."));
AUDIT_SENTRY_RWLOCK_INIT();
AUDIT_ANYAS_KLIST_LOCK_INIT();
au_sentry_bucket = malloc( sizeof(struct au_sentry) *
HASH_TABLE_SIZE, M_AU_SESSION, M_WAITOK | M_ZERO);
for (i = 0; i < HASH_TABLE_SIZE; i++)
LIST_INIT(&au_sentry_bucket[i]);
}
static caddr_t
audit_new_plist(void)
{
au_plisthead_t *plhead;
plhead = malloc(sizeof(au_plisthead_t), M_AU_EV_PLIST, M_WAITOK |
M_ZERO);
LIST_INIT(&plhead->ph_head);
AUDIT_PLIST_LOCK_INIT(plhead);
return ((caddr_t) plhead);
}
static void
audit_destroy_plist(struct knote *anyas_kn)
{
au_plisthead_t *plhead;
au_plist_t *plentry, *ple_tmp;
struct kevent64_s kev;
KASSERT(anyas_kn != NULL, ("audit_destroy_plist: anyas = NULL"));
plhead = (au_plisthead_t *)anyas_kn->kn_hook;
KASSERT(plhead != NULL, ("audit_destroy_plist: plhead = NULL"));
AUDIT_PLIST_LOCK(plhead);
LIST_FOREACH_SAFE(plentry, &plhead->ph_head, pl_link, ple_tmp) {
struct kqueue *kq = plentry->pl_knote->kn_kq;
kev.ident = plentry->pl_knote->kn_id;
kev.filter = EVFILT_SESSION;
kev.flags = EV_DELETE;
kevent_register(kq, &kev, NULL);
}
AUDIT_PLIST_UNLOCK(plhead);
AUDIT_PLIST_LOCK_DESTROY(plhead);
free(plhead, M_AU_EV_PLIST);
}
static void
audit_add_to_plist(struct knote *anyas_kn, struct knote *kn)
{
au_plisthead_t *plhead;
au_plist_t *plentry;
KASSERT(anyas_kn != NULL, ("audit_add_to_plist: anyas = NULL"));
plhead = (au_plisthead_t *)anyas_kn->kn_hook;
KASSERT(plhead != NULL, ("audit_add_to_plist: plhead = NULL"));
plentry = malloc(sizeof(au_plist_t), M_AU_EV_PLIST, M_WAITOK | M_ZERO);
plentry->pl_knote = kn;
AUDIT_PLIST_LOCK(plhead);
LIST_INSERT_HEAD(&plhead->ph_head, plentry, pl_link);
AUDIT_PLIST_UNLOCK(plhead);
}
static void
audit_rm_from_plist(struct knote *kn)
{
struct knote *anyas_kn;
au_plisthead_t *plhd;
au_plist_t *plentry, *ple_tmp;
KASSERT(kn != NULL, ("audit_rm_from_plist: kn = NULL"));
anyas_kn = (struct knote *)kn->kn_hook;
KASSERT(anyas_kn != NULL, ("audit_rm_to_plist: anyas = NULL"));
plhd = (au_plisthead_t *)anyas_kn->kn_hook;
AUDIT_PLIST_LOCK(plhd);
LIST_FOREACH_SAFE(plentry, &plhd->ph_head, pl_link, ple_tmp) {
if (plentry->pl_knote == kn) {
LIST_REMOVE(plentry, pl_link);
free(plentry, M_AU_EV_PLIST);
AUDIT_PLIST_UNLOCK(plhd);
return;
}
}
AUDIT_PLIST_UNLOCK(plhd);
}
static int
audit_filt_sessionattach(struct knote *kn)
{
au_sentry_t *se = NULL;
if ((kn->kn_sfflags & (NOTE_AS_START | NOTE_AS_END | NOTE_AS_CLOSE
| NOTE_AS_UPDATE | NOTE_AS_ERR)) == 0)
return (ENOTSUP);
if (kn->kn_id == AS_ANY_ASID) {
kn->kn_flags |= EV_CLEAR;
kn->kn_ptr.p_se = NULL;
kn->kn_hook = audit_new_plist();
AUDIT_ANYAS_KLIST_LOCK();
KNOTE_ATTACH(&anyas_klist, kn);
AUDIT_ANYAS_KLIST_UNLOCK();
return (0);
} else {
if (kn->kn_id > ASSIGNED_ASID_MAX)
return (EINVAL);
AUDIT_SENTRY_RLOCK();
se = audit_session_find(kn->kn_id);
AUDIT_SENTRY_RUNLOCK();
if (se == NULL)
return (EINVAL);
AUDIT_SE_KLIST_LOCK(se);
kn->kn_flags |= EV_CLEAR;
kn->kn_ptr.p_se = se;
if (kn->kn_flags & EV_ANY_ASID) {
struct knote *anyas_kn =
(struct knote *)((uintptr_t)kn->kn_kevent.ext[0]);
kn->kn_hook = (caddr_t) anyas_kn;
kn->kn_flags &= ~EV_ANY_ASID;
audit_add_to_plist(anyas_kn, kn);
} else
kn->kn_hook = NULL;
KNOTE_ATTACH(&se->se_klist, kn);
AUDIT_SE_KLIST_UNLOCK(se);
return (0);
}
}
static void
audit_filt_sessiondetach(struct knote *kn)
{
au_sentry_t *se = NULL;
if (kn->kn_id == AS_ANY_ASID) {
AUDIT_ANYAS_KLIST_LOCK();
audit_destroy_plist(kn);
KNOTE_DETACH(&anyas_klist, kn);
AUDIT_ANYAS_KLIST_UNLOCK();
} else {
if (kn->kn_hook != NULL) {
audit_rm_from_plist(kn);
kn->kn_hook = NULL;
}
se = kn->kn_ptr.p_se;
if (se != NULL) {
AUDIT_SE_KLIST_LOCK(se);
kn->kn_ptr.p_se = NULL;
KNOTE_DETACH(&se->se_klist, kn);
AUDIT_SE_KLIST_UNLOCK(se);
}
}
}
static void
audit_filt_sessiontouch(struct knote *kn, struct kevent64_s *kev, long type)
{
struct knote *ple_kn;
struct kqueue *kq;
au_sentry_t *se;
au_plisthead_t *plhead;
au_plist_t *plentry;
struct kevent64_s newkev;
switch (type) {
case EVENT_REGISTER:
kn->kn_sfflags = kev->fflags;
kn->kn_sdata = kev->data;
if (kev->ident == AS_ANY_ASID && kn->kn_hook != NULL) {
plhead = (au_plisthead_t *)kn->kn_hook;
AUDIT_PLIST_LOCK(plhead);
LIST_FOREACH(plentry, &plhead->ph_head, pl_link) {
if ((ple_kn = plentry->pl_knote) == NULL)
continue;
if ((se = ple_kn->kn_ptr.p_se) == NULL)
continue;
if ((kq = ple_kn->kn_kq) == NULL)
continue;
newkev.ident = plentry->pl_knote->kn_id;
newkev.filter = EVFILT_SESSION;
newkev.flags = kev->flags;
newkev.fflags = kev->fflags;
newkev.data = kev->data;
newkev.udata = kev->udata;
kevent_register(kq, &newkev, NULL);
}
AUDIT_PLIST_UNLOCK(plhead);
}
break;
case EVENT_PROCESS:
*kev = kn->kn_kevent;
if (kn->kn_flags & EV_CLEAR) {
kn->kn_data = 0;
kn->kn_fflags = 0;
}
break;
default:
KASSERT((type == EVENT_REGISTER || type == EVENT_PROCESS),
("filt_sessiontouch(): invalid type (%ld)", type));
break;
}
}
static int
audit_filt_session(struct knote *kn, long hint)
{
int events = (int)hint;
au_sentry_t *se = kn->kn_ptr.p_se;
if (hint != 0 && se != NULL) {
if (kn->kn_sfflags & events) {
kn->kn_fflags |= events;
kn->kn_data = se->se_auid;
}
if (events & NOTE_AS_CLOSE) {
if (kn->kn_hook != NULL) {
audit_rm_from_plist(kn);
kn->kn_hook = NULL;
}
kn->kn_flags |= (EV_EOF | EV_ONESHOT);
kn->kn_ptr.p_se = NULL;
AUDIT_SE_KLIST_LOCK_ASSERT(se);
KNOTE_DETACH(&se->se_klist, kn);
return (1);
}
}
return (kn->kn_fflags != 0);
}
static void
audit_register_kevents(uint32_t asid, uint32_t auid)
{
struct knote *kn;
AUDIT_ANYAS_KLIST_LOCK();
SLIST_FOREACH(kn, &anyas_klist, kn_selnext) {
struct kqueue *kq = kn->kn_kq;
struct kevent64_s kev;
int err;
kev.ident = asid;
kev.filter = EVFILT_SESSION;
kev.flags = kn->kn_flags | EV_ADD | EV_ENABLE | EV_ANY_ASID;
kev.fflags = kn->kn_sfflags;
kev.data = auid;
kev.udata = kn->kn_kevent.udata;
kev.ext[0] = (uint64_t)((uintptr_t)kn);
err = kevent_register(kq, &kev, NULL);
if (err)
kn->kn_fflags |= NOTE_AS_ERR;
}
AUDIT_ANYAS_KLIST_UNLOCK();
}
int
audit_session_setaia(proc_t p, auditinfo_addr_t *aia_p, int newprocess)
{
kauth_cred_t my_cred, my_new_cred;
struct au_session as;
struct au_session tmp_as;
auditinfo_addr_t caia;
if (audit_session_lookup(aia_p->ai_asid, &caia) == 0) {
if (caia.ai_auid != AU_DEFAUDITID &&
caia.ai_auid != aia_p->ai_auid)
return (EINVAL);
if ((caia.ai_termid.at_type != AU_IPv4 ||
caia.ai_termid.at_port != 0 ||
caia.ai_termid.at_addr[0] != 0) &&
(caia.ai_termid.at_port != aia_p->ai_termid.at_port ||
caia.ai_termid.at_type != aia_p->ai_termid.at_type ||
bcmp(&caia.ai_termid.at_addr, &aia_p->ai_termid.at_addr,
sizeof (caia.ai_termid.at_addr) )) )
return (EINVAL);
if (caia.ai_flags != aia_p->ai_flags)
return (EINVAL);
}
my_cred = kauth_cred_proc_ref(p);
bcopy(&aia_p->ai_mask, &as.as_mask, sizeof(as.as_mask));
as.as_aia_p = audit_session_new(aia_p, newprocess);
for (;;) {
bcopy(&as, &tmp_as, sizeof(tmp_as));
my_new_cred = kauth_cred_setauditinfo(my_cred, &tmp_as);
if (my_cred != my_new_cred) {
proc_lock(p);
if (p->p_ucred != my_cred) {
proc_unlock(p);
audit_session_unref(my_new_cred);
kauth_cred_unref(&my_new_cred);
my_cred = kauth_cred_proc_ref(p);
continue;
}
p->p_ucred = my_new_cred;
proc_unlock(p);
}
kauth_cred_unref(&my_cred);
break;
}
audit_session_unref(my_new_cred);
set_security_token(p);
return (0);
}
int
audit_session_self(proc_t p, __unused struct audit_session_self_args *uap,
mach_port_name_t *ret_port)
{
ipc_port_t sendport = IPC_PORT_NULL;
kauth_cred_t cred = NULL;
auditinfo_addr_t *aia_p;
au_sentry_t *se;
int err = 0;
cred = kauth_cred_proc_ref(p);
if (!IS_VALID_CRED(cred)) {
err = ESRCH;
goto done;
}
aia_p = cred->cr_audit.as_aia_p;
if (!IS_VALID_SESSION(aia_p)) {
err = EINVAL;
goto done;
}
se = AU_SENTRY_PTR(aia_p);
if (se->se_port == IPC_PORT_NULL)
bcopy(&cred->cr_audit.as_mask, &se->se_mask,
sizeof(se->se_mask));
if ((sendport = audit_session_mksend(aia_p, &se->se_port)) == NULL) {
err = ENOMEM;
goto done;
}
audit_ref_session(se);
done:
if (cred != NULL)
kauth_cred_unref(&cred);
if (err == 0)
*ret_port = ipc_port_copyout_send(sendport,
get_task_ipcspace(p->task));
else
*ret_port = MACH_PORT_NULL;
return (err);
}
void
audit_session_portaiadestroy(struct auditinfo_addr *port_aia_p)
{
au_sentry_t *se;
KASSERT(port_aia_p != NULL,
("audit_session_infodestroy: port_aia_p = NULL"));
se = AU_SENTRY_PTR(port_aia_p);
if (se != NULL) {
se->se_port = IPC_PORT_NULL;
audit_unref_session(se);
}
}
static int
audit_session_join_internal(proc_t p, ipc_port_t port, au_asid_t *new_asid)
{
auditinfo_addr_t *port_aia_p, *old_aia_p;
kauth_cred_t cred = NULL;
au_asid_t old_asid;
int err = 0;
*new_asid = AU_DEFAUDITSID;
if ((port_aia_p = audit_session_porttoaia(port)) == NULL) {
err = EINVAL;
goto done;
}
*new_asid = port_aia_p->ai_asid;
cred = kauth_cred_proc_ref(p);
if (!IS_VALID_CRED(cred)) {
kauth_cred_unref(&cred);
err = ESRCH;
goto done;
}
old_aia_p = cred->cr_audit.as_aia_p;
old_asid = old_aia_p->ai_asid;
if (*new_asid != old_asid) {
audit_session_setaia(p, port_aia_p, 1);
if (IS_VALID_SESSION(old_aia_p))
audit_dec_procount(AU_SENTRY_PTR(old_aia_p));
}
kauth_cred_unref(&cred);
done:
if (port != IPC_PORT_NULL)
ipc_port_release_send(port);
return (err);
}
int
audit_session_spawnjoin(proc_t p, ipc_port_t port)
{
au_asid_t new_asid;
return (audit_session_join_internal(p, port, &new_asid));
}
int
audit_session_join(proc_t p, struct audit_session_join_args *uap,
au_asid_t *ret_asid)
{
ipc_port_t port = IPC_PORT_NULL;
mach_port_name_t send = uap->port;
int err = 0;
if (ipc_object_copyin(get_task_ipcspace(p->task), send,
MACH_MSG_TYPE_COPY_SEND, &port) != KERN_SUCCESS) {
*ret_asid = AU_DEFAUDITSID;
err = EINVAL;
} else
err = audit_session_join_internal(p, port, ret_asid);
return (err);
}
#else
int
audit_session_self(proc_t p, struct audit_session_self_args *uap,
mach_port_name_t *ret_port)
{
#pragma unused(p, uap, ret_port)
return (ENOSYS);
}
int
audit_session_join(proc_t p, struct audit_session_join_args *uap,
au_asid_t *ret_asid)
{
#pragma unused(p, uap, ret_asid)
return (ENOSYS);
}
#endif