#include <sys/param.h>
#include <sys/fcntl.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/namei.h>
#include <sys/proc_internal.h>
#include <sys/kauth.h>
#include <sys/queue.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/ucred.h>
#include <sys/uio.h>
#include <sys/unistd.h>
#include <sys/file_internal.h>
#include <sys/vnode_internal.h>
#include <sys/user.h>
#include <sys/syscall.h>
#include <sys/malloc.h>
#include <sys/un.h>
#include <sys/sysent.h>
#include <sys/sysproto.h>
#include <sys/vfs_context.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/socketvar.h>
#include <bsm/audit.h>
#include <bsm/audit_kevents.h>
#include <bsm/audit_klib.h>
#include <bsm/audit_kernel.h>
#include <mach/host_priv.h>
#include <mach/host_special_ports.h>
#include <mach/audit_triggers_server.h>
#include <kern/host.h>
#include <kern/kalloc.h>
#include <kern/zalloc.h>
#include <kern/lock.h>
#include <kern/wait_queue.h>
#include <kern/sched_prim.h>
#if CONFIG_MACF
#include <bsm/audit_record.h>
#include <security/mac.h>
#include <security/mac_framework.h>
#include <security/mac_policy.h>
#define MAC_ARG_PREFIX "arg: "
#define MAC_ARG_PREFIX_LEN 5
#endif
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_pcb.h>
#if AUDIT
#ifdef AUDIT_EXCESSIVELY_VERBOSE
#define AUDIT_PRINTF_ONLY
#define AUDIT_PRINTF(x) printf x
#else
#define AUDIT_PRINTF_ONLY __unused
#define AUDIT_PRINTF(X)
#endif
#if DIAGNOSTIC
#if defined(assert)
#undef assert()
#endif
#define assert(cond) \
((void) ((cond) ? 0 : panic("Assert failed: %s", # cond)))
#else
#include <kern/assert.h>
#endif
int audit_enabled;
int audit_suspended;
static lck_grp_t *audit_grp;
static lck_attr_t *audit_attr;
static lck_grp_attr_t *audit_grp_attr;
static lck_mtx_t *audit_mtx;
static TAILQ_HEAD(, kaudit_record) audit_q;
static size_t audit_q_len;
static size_t audit_pre_q_len;
static wait_queue_t audit_wait_queue;
static zone_t audit_zone;
#if CONFIG_MACF
static zone_t audit_mac_label_zone;
#endif
static int audit_worker_event;
#define AUDIT_WORKER_EVENT ((event_t)&audit_worker_event)
static thread_t audit_worker_thread = THREAD_NULL;
static int audit_replacement_event;
#define AUDIT_REPLACEMENT_EVENT ((event_t)&audit_replacement_event)
static int audit_replacement_flag;
static struct vnode *audit_replacement_vp;
static kauth_cred_t audit_replacement_cred;
static int audit_commit_event;
#define AUDIT_COMMIT_EVENT ((event_t)&audit_commit_event)
static struct au_qctrl audit_qctrl;
static const int audit_open_flags = FWRITE | O_APPEND;
static const int audit_close_flags = FWRITE | O_APPEND;
static struct audit_fstat audit_fstat;
static struct au_mask audit_nae_mask;
static int audit_file_rotate_wait;
static int audit_panic_on_write_fail;
static int audit_fail_stop;
static int audit_in_failure;
static int audit_failure_event;
#define AUDIT_FAILURE_EVENT ((event_t)&audit_failure_event)
extern task_t kernel_task;
extern zone_t mac_audit_data_zone;
static void
audit_free(struct kaudit_record *ar)
{
if (ar->k_ar.ar_arg_upath1 != NULL) {
kfree(ar->k_ar.ar_arg_upath1, MAXPATHLEN);
}
if (ar->k_ar.ar_arg_upath2 != NULL) {
kfree(ar->k_ar.ar_arg_upath2, MAXPATHLEN);
}
if (ar->k_ar.ar_arg_kpath1 != NULL) {
kfree(ar->k_ar.ar_arg_kpath1, MAXPATHLEN);
}
if (ar->k_ar.ar_arg_kpath2 != NULL) {
kfree(ar->k_ar.ar_arg_kpath2, MAXPATHLEN);
}
if (ar->k_ar.ar_arg_text != NULL) {
kfree(ar->k_ar.ar_arg_text, MAXPATHLEN);
}
if (ar->k_udata != NULL) {
kfree(ar->k_udata, ar->k_ulen);
}
#if CONFIG_MACF
if (ar->k_ar.ar_vnode1_mac_labels != NULL) {
zfree(audit_mac_label_zone, ar->k_ar.ar_vnode1_mac_labels);
}
if (ar->k_ar.ar_vnode2_mac_labels != NULL) {
zfree(audit_mac_label_zone, ar->k_ar.ar_vnode2_mac_labels);
}
if (ar->k_ar.ar_cred_mac_labels != NULL) {
zfree(audit_mac_label_zone, ar->k_ar.ar_cred_mac_labels);
}
if (ar->k_ar.ar_arg_mac_string != NULL) {
kfree(ar->k_ar.ar_arg_mac_string,
MAC_MAX_LABEL_BUF_LEN + MAC_ARG_PREFIX_LEN);
}
do {
struct mac_audit_record *head, *next;
head = LIST_FIRST(ar->k_ar.ar_mac_records);
while (head != NULL) {
next = LIST_NEXT(head, records);
zfree(mac_audit_data_zone, head->data);
kfree(head, sizeof(*head));
head = next;
}
kfree(ar->k_ar.ar_mac_records,
sizeof(*ar->k_ar.ar_mac_records));
} while (0);
#endif
zfree(audit_zone, ar);
}
static int
audit_write(struct vnode *vp, struct kaudit_record *ar, vfs_context_t ctx)
{
struct vfsstatfs *mnt_stat = &vp->v_mount->mnt_vfsstat;
int ret = 0;
struct au_record *bsm;
off_t file_size;
mach_port_t audit_port;
if (vnode_getwithref(vp))
return ENOENT;
ret = vfs_update_vfsstat(vp->v_mount, ctx, VFS_KERNEL_EVENT);
if (ret)
goto out;
if ((ret = vnode_size(vp, &file_size, ctx)) != 0)
goto out;
audit_fstat.af_currsz = file_size;
if(host_get_audit_control_port(host_priv_self(), &audit_port)
!= KERN_SUCCESS)
printf("Cannot get audit control port\n");
if (audit_port != MACH_PORT_NULL) {
uint64_t temp;
if (audit_qctrl.aq_minfree != 0) {
temp = mnt_stat->f_blocks / (100 / audit_qctrl.aq_minfree);
if (mnt_stat->f_bfree < temp) {
ret = audit_triggers(audit_port,
AUDIT_TRIGGER_LOW_SPACE);
if (ret != KERN_SUCCESS) {
printf(
"Failed audit_triggers(AUDIT_TRIGGER_LOW_SPACE): %d\n", ret);
}
}
}
if ((audit_fstat.af_filesz != 0) &&
(audit_file_rotate_wait == 0) &&
(file_size >= (off_t)audit_fstat.af_filesz)) {
audit_file_rotate_wait = 1;
ret = audit_triggers(audit_port,
AUDIT_TRIGGER_FILE_FULL);
if (ret != KERN_SUCCESS) {
printf(
"Failed audit_triggers(AUDIT_TRIGGER_FILE_FULL): %d\n", ret);
}
}
}
if (audit_fail_stop &&
(unsigned long)
((audit_q_len + audit_pre_q_len + 1) * MAX_AUDIT_RECORD_SIZE) /
mnt_stat->f_bsize >= (unsigned long)(mnt_stat->f_bfree)) {
printf(
"audit_worker: free space below size of audit queue, failing stop\n");
audit_in_failure = 1;
}
if (ar->k_ar_commit & AR_COMMIT_USER) {
ret = vn_rdwr(UIO_WRITE, vp, (void *)ar->k_udata, ar->k_ulen,
(off_t)0, UIO_SYSSPACE32, IO_APPEND|IO_UNIT, vfs_context_ucred(ctx), NULL, vfs_context_proc(ctx));
if (ret)
goto out;
}
if (!(ar->k_ar_commit & AR_COMMIT_KERNEL)) {
ret = 0;
goto out;
}
ret = kaudit_to_bsm(ar, &bsm);
if (ret == BSM_NOAUDIT) {
ret = 0;
goto out;
}
if (ret == BSM_FAILURE) {
AUDIT_PRINTF(("BSM conversion failure\n"));
ret = EINVAL;
goto out;
}
ret = (vn_rdwr(UIO_WRITE, vp, (void *)bsm->data, bsm->len,
(off_t)0, UIO_SYSSPACE32, IO_APPEND|IO_UNIT, vfs_context_ucred(ctx), NULL, vfs_context_proc(ctx)));
kau_free(bsm);
out:
if (audit_in_failure &&
audit_q_len == 0 && audit_pre_q_len == 0) {
(void)VNOP_FSYNC(vp, MNT_WAIT, ctx);
panic("Audit store overflow; record queue drained.");
}
vnode_put(vp);
return (ret);
}
static void
audit_worker(void)
{
int do_replacement_signal, error;
TAILQ_HEAD(, kaudit_record) ar_worklist;
struct kaudit_record *ar;
struct vnode *audit_vp, *old_vp;
kauth_cred_t audit_cred;
proc_t audit_p;
AUDIT_PRINTF(("audit_worker starting\n"));
TAILQ_INIT(&ar_worklist);
audit_cred = NOCRED;
audit_p = current_proc();
audit_vp = NULL;
lck_mtx_lock(audit_mtx);
while (1) {
struct vfs_context context;
do_replacement_signal = 0;
while (audit_replacement_flag != 0) {
kauth_cred_t old_cred = audit_cred;
old_vp = audit_vp;
audit_cred = audit_replacement_cred;
audit_vp = audit_replacement_vp;
audit_replacement_cred = NOCRED;
audit_replacement_vp = NULL;
audit_replacement_flag = 0;
audit_enabled = (audit_vp != NULL);
if (old_vp != NULL) {
AUDIT_PRINTF(("Closing old audit file vnode %p\n", old_vp));
if (vnode_get(old_vp) == 0) {
vn_close(old_vp, audit_close_flags, vfs_context_kernel());
vnode_put(old_vp);
AUDIT_PRINTF(("Audit file closed\n"));
}
else
printf("audit_worker(): Couldn't close audit file.\n");
kauth_cred_unref(&old_cred);
old_vp = NULL;
}
if (audit_vp != NULL) {
AUDIT_PRINTF(("Opening new audit file\n"));
}
do_replacement_signal = 1;
}
if (do_replacement_signal)
wait_queue_wakeup_all(audit_wait_queue,
AUDIT_REPLACEMENT_EVENT, THREAD_AWAKENED);
if (TAILQ_EMPTY(&audit_q)) {
int ret;
AUDIT_PRINTF(("audit_worker waiting\n"));
ret = wait_queue_assert_wait(audit_wait_queue,
AUDIT_WORKER_EVENT,
THREAD_UNINT,
0);
lck_mtx_unlock(audit_mtx);
assert(ret == THREAD_WAITING);
ret = thread_block(THREAD_CONTINUE_NULL);
assert(ret == THREAD_AWAKENED);
AUDIT_PRINTF(("audit_worker woken up\n"));
AUDIT_PRINTF(("audit_worker: new vp = %p; value of flag %d\n",
audit_replacement_vp, audit_replacement_flag));
lck_mtx_lock(audit_mtx);
continue;
}
if (audit_vp == NULL) {
while ((ar = TAILQ_FIRST(&audit_q))) {
TAILQ_REMOVE(&audit_q, ar, k_q);
audit_q_len--;
if (audit_q_len <= audit_qctrl.aq_lowater)
wait_queue_wakeup_one(
audit_wait_queue,
AUDIT_COMMIT_EVENT,
THREAD_AWAKENED);
TAILQ_INSERT_TAIL(&ar_worklist, ar, k_q);
}
lck_mtx_unlock(audit_mtx);
while ((ar = TAILQ_FIRST(&ar_worklist))) {
TAILQ_REMOVE(&ar_worklist, ar, k_q);
audit_free(ar);
}
lck_mtx_lock(audit_mtx);
continue;
}
while ((ar = TAILQ_FIRST(&audit_q))) {
TAILQ_REMOVE(&audit_q, ar, k_q);
audit_q_len--;
if (audit_q_len <= audit_qctrl.aq_lowater) {
wait_queue_wakeup_one(audit_wait_queue,
AUDIT_COMMIT_EVENT, THREAD_AWAKENED);
}
TAILQ_INSERT_TAIL(&ar_worklist, ar, k_q);
}
lck_mtx_unlock(audit_mtx);
context.vc_thread = current_thread();
context.vc_ucred = audit_cred;
while ((ar = TAILQ_FIRST(&ar_worklist))) {
TAILQ_REMOVE(&ar_worklist, ar, k_q);
if (audit_vp != NULL) {
error = audit_write(audit_vp, ar, &context);
if (error && audit_panic_on_write_fail) {
panic("audit_worker: write error %d\n",
error);
} else if (error) {
printf("audit_worker: write error %d\n",
error);
}
}
audit_free(ar);
}
lck_mtx_lock(audit_mtx);
}
}
void
audit_init(void)
{
printf("Security auditing service present\n");
TAILQ_INIT(&audit_q);
audit_q_len = 0;
audit_enabled = 0;
audit_suspended = 0;
audit_replacement_cred = NULL;
audit_replacement_flag = 0;
audit_file_rotate_wait = 0;
audit_replacement_vp = NULL;
audit_fstat.af_filesz = 0;
audit_fstat.af_currsz = 0;
audit_qctrl.aq_hiwater = AQ_HIWATER;
audit_qctrl.aq_lowater = AQ_LOWATER;
audit_qctrl.aq_bufsz = AQ_BUFSZ;
audit_qctrl.aq_minfree = AU_FS_MINFREE;
audit_grp_attr = lck_grp_attr_alloc_init();
audit_grp = lck_grp_alloc_init("audit", audit_grp_attr);
audit_attr = lck_attr_alloc_init();
audit_mtx = lck_mtx_alloc_init(audit_grp, audit_attr);
audit_wait_queue = wait_queue_alloc(SYNC_POLICY_FIFO);
audit_zone = zinit(sizeof(struct kaudit_record),
AQ_HIWATER*sizeof(struct kaudit_record),
8192,
"audit_zone");
#if CONFIG_MACF
audit_mac_label_zone = zinit(MAC_AUDIT_LABEL_LEN,
AQ_HIWATER * 3*MAC_AUDIT_LABEL_LEN,
8192,
"audit_mac_label_zone");
#endif
kau_init();
}
static void
audit_rotate_vnode(kauth_cred_t cred, struct vnode *vp)
{
int ret;
lck_mtx_lock(audit_mtx);
while (audit_replacement_flag != 0) {
AUDIT_PRINTF(("audit_rotate_vnode: sleeping to wait for "
"flag\n"));
ret = wait_queue_assert_wait(audit_wait_queue,
AUDIT_REPLACEMENT_EVENT,
THREAD_UNINT,
0);
lck_mtx_unlock(audit_mtx);
assert(ret == THREAD_WAITING);
ret = thread_block(THREAD_CONTINUE_NULL);
assert(ret == THREAD_AWAKENED);
AUDIT_PRINTF(("audit_rotate_vnode: woken up (flag %d)\n",
audit_replacement_flag));
lck_mtx_lock(audit_mtx);
}
audit_replacement_cred = cred;
audit_replacement_flag = 1;
audit_replacement_vp = vp;
if (audit_worker_thread == THREAD_NULL)
audit_worker_thread = kernel_thread(kernel_task,
audit_worker);
else
wait_queue_wakeup_one(audit_wait_queue,
AUDIT_WORKER_EVENT,
THREAD_AWAKENED);
AUDIT_PRINTF(("audit_rotate_vnode: waiting for news of "
"replacement\n"));
ret = wait_queue_assert_wait(audit_wait_queue,
AUDIT_REPLACEMENT_EVENT,
THREAD_UNINT,
0);
lck_mtx_unlock(audit_mtx);
assert(ret == THREAD_WAITING);
ret = thread_block(THREAD_CONTINUE_NULL);
assert(ret == THREAD_AWAKENED);
AUDIT_PRINTF(("audit_rotate_vnode: change acknowledged by "
"audit_worker (flag " "now %d)\n", audit_replacement_flag));
audit_file_rotate_wait = 0;
}
void
audit_shutdown(void)
{
if (audit_mtx)
audit_rotate_vnode(NULL, NULL);
}
static __inline__ struct uthread *
curuthread(void)
{
return (get_bsdthread_info(current_thread()));
}
static __inline__ struct kaudit_record *
currecord(void)
{
return (curuthread()->uu_ar);
}
int
audit(proc_t p, struct audit_args *uap, __unused register_t *retval)
{
int error;
void * rec;
struct kaudit_record *ar;
struct uthread *uthr;
error = suser(kauth_cred_get(), &p->p_acflag);
if (error)
return (error);
lck_mtx_lock(audit_mtx);
if ((uap->length <= 0) || (uap->length > (int)audit_qctrl.aq_bufsz)) {
lck_mtx_unlock(audit_mtx);
return (EINVAL);
}
lck_mtx_unlock(audit_mtx);
ar = currecord();
if (ar == NULL) {
uthr = curuthread();
if (uthr == NULL)
return (ENOTSUP);
uthr->uu_ar = audit_new(AUE_NULL, p, uthr);
if (uthr->uu_ar == NULL)
return (ENOTSUP);
ar = uthr->uu_ar;
}
if (uap->length > MAX_AUDIT_RECORD_SIZE)
return (EINVAL);
rec = (void *)kalloc((vm_size_t)uap->length);
error = copyin(uap->record, rec, uap->length);
if (error)
goto free_out;
#if CONFIG_MACF
error = mac_system_check_audit(kauth_cred_get(), rec, uap->length);
if (error)
goto free_out;
#endif
if (bsm_rec_verify(rec) == 0) {
error = EINVAL;
goto free_out;
}
ar->k_udata = rec;
ar->k_ar_commit |= AR_COMMIT_USER;
ar->k_ulen = uap->length;
return (0);
free_out:
kfree(rec, uap->length);
return (error);
}
int
auditon(proc_t p, struct auditon_args *uap, __unused register_t *retval)
{
int ret;
int len;
union auditon_udata udata;
proc_t tp = PROC_NULL;
kauth_cred_t my_cred;
AUDIT_ARG(cmd, uap->cmd);
ret = suser(kauth_cred_get(), &p->p_acflag);
if (ret)
return (ret);
#if CONFIG_MACF
ret = mac_system_check_auditon(kauth_cred_get(), uap->cmd);
if (ret)
return (ret);
#endif
len = uap->length;
if ((len <= 0) || (len > (int)sizeof(union auditon_udata)))
return (EINVAL);
memset((void *)&udata, 0, sizeof(udata));
switch (uap->cmd) {
case A_SETPOLICY:
case A_SETKMASK:
case A_SETQCTRL:
case A_SETSTAT:
case A_SETUMASK:
case A_SETSMASK:
case A_SETCOND:
case A_SETCLASS:
case A_SETPMASK:
case A_SETFSIZE:
case A_SETKAUDIT:
case A_GETCLASS:
case A_GETPINFO:
case A_GETPINFO_ADDR:
ret = copyin(uap->data, (void *)&udata, uap->length);
if (ret)
return (ret);
AUDIT_ARG(auditon, &udata);
break;
}
lck_mtx_lock(audit_mtx);
switch (uap->cmd) {
case A_GETPOLICY:
if (!audit_fail_stop)
udata.au_policy |= AUDIT_CNT;
if (audit_panic_on_write_fail)
udata.au_policy |= AUDIT_AHLT;
break;
case A_SETPOLICY:
if (udata.au_policy & ~(AUDIT_CNT|AUDIT_AHLT)) {
ret = EINVAL;
break;
}
audit_fail_stop = ((udata.au_policy & AUDIT_CNT) == 0);
audit_panic_on_write_fail = (udata.au_policy & AUDIT_AHLT);
break;
case A_GETKMASK:
udata.au_mask = audit_nae_mask;
break;
case A_SETKMASK:
audit_nae_mask = udata.au_mask;
break;
case A_GETQCTRL:
udata.au_qctrl = audit_qctrl;
break;
case A_SETQCTRL:
if ((udata.au_qctrl.aq_hiwater > AQ_MAXHIGH) ||
(udata.au_qctrl.aq_lowater >= udata.au_qctrl.aq_hiwater) ||
(udata.au_qctrl.aq_bufsz > AQ_MAXBUFSZ) ||
(udata.au_qctrl.aq_minfree < 0) ||
(udata.au_qctrl.aq_minfree > 100)) {
ret = EINVAL;
break;
}
audit_qctrl = udata.au_qctrl;
audit_qctrl.aq_delay = -1;
break;
case A_GETCWD:
ret = ENOSYS;
break;
case A_GETCAR:
ret = ENOSYS;
break;
case A_GETSTAT:
ret = ENOSYS;
break;
case A_SETSTAT:
ret = ENOSYS;
break;
case A_SETUMASK:
ret = ENOSYS;
break;
case A_SETSMASK:
ret = ENOSYS;
break;
case A_GETCOND:
if (audit_enabled && !audit_suspended)
udata.au_cond = AUC_AUDITING;
else
udata.au_cond = AUC_NOAUDIT;
break;
case A_SETCOND:
if (udata.au_cond == AUC_NOAUDIT)
audit_suspended = 1;
if (udata.au_cond == AUC_AUDITING)
audit_suspended = 0;
if (udata.au_cond == AUC_DISABLED) {
audit_suspended = 1;
audit_shutdown();
}
break;
case A_GETCLASS:
udata.au_evclass.ec_class =
au_event_class(udata.au_evclass.ec_number);
break;
case A_SETCLASS:
au_evclassmap_insert(udata.au_evclass.ec_number,
udata.au_evclass.ec_class);
break;
case A_GETPINFO:
if (udata.au_aupinfo.ap_pid < 1) {
ret = EINVAL;
break;
}
if ((tp = proc_find(udata.au_aupinfo.ap_pid)) == NULL) {
ret = EINVAL;
break;
}
lck_mtx_unlock(audit_mtx);
my_cred = kauth_cred_proc_ref(tp);
udata.au_aupinfo.ap_auid = my_cred->cr_au.ai_auid;
udata.au_aupinfo.ap_mask.am_success =
my_cred->cr_au.ai_mask.am_success;
udata.au_aupinfo.ap_mask.am_failure =
my_cred->cr_au.ai_mask.am_failure;
udata.au_aupinfo.ap_termid.machine =
my_cred->cr_au.ai_termid.machine;
udata.au_aupinfo.ap_termid.port =
my_cred->cr_au.ai_termid.port;
udata.au_aupinfo.ap_asid = my_cred->cr_au.ai_asid;
kauth_cred_unref(&my_cred);
proc_rele(tp);
tp = PROC_NULL;
lck_mtx_lock(audit_mtx);
break;
case A_SETPMASK:
if (udata.au_aupinfo.ap_pid < 1) {
ret = EINVAL;
break;
}
if ((tp = proc_find(udata.au_aupinfo.ap_pid)) == NULL) {
ret = EINVAL;
break;
}
lck_mtx_unlock(audit_mtx);
for (;;) {
kauth_cred_t my_new_cred;
struct auditinfo temp_auditinfo;
my_cred = kauth_cred_proc_ref(tp);
temp_auditinfo = my_cred->cr_au;
temp_auditinfo.ai_mask.am_success =
udata.au_aupinfo.ap_mask.am_success;
temp_auditinfo.ai_mask.am_failure =
udata.au_aupinfo.ap_mask.am_failure;
my_new_cred = kauth_cred_setauditinfo(my_cred, &temp_auditinfo);
if (my_cred != my_new_cred) {
proc_lock(tp);
if (tp->p_ucred != my_cred) {
proc_unlock(tp);
kauth_cred_unref(&my_new_cred);
continue;
}
tp->p_ucred = my_new_cred;
proc_unlock(tp);
}
kauth_cred_unref(&my_cred);
break;
}
proc_rele(tp);
lck_mtx_lock(audit_mtx);
break;
case A_SETFSIZE:
if ((udata.au_fstat.af_filesz != 0) &&
(udata.au_fstat.af_filesz < MIN_AUDIT_FILE_SIZE)) {
ret = EINVAL;
break;
}
audit_fstat.af_filesz = udata.au_fstat.af_filesz;
break;
case A_GETFSIZE:
udata.au_fstat.af_filesz = audit_fstat.af_filesz;
udata.au_fstat.af_currsz = audit_fstat.af_currsz;
break;
case A_GETPINFO_ADDR:
ret = ENOSYS;
break;
case A_GETKAUDIT:
ret = ENOSYS;
break;
case A_SETKAUDIT:
ret = ENOSYS;
break;
}
if (ret == 0) {
switch (uap->cmd) {
case A_GETPOLICY:
case A_GETKMASK:
case A_GETQCTRL:
case A_GETCWD:
case A_GETCAR:
case A_GETSTAT:
case A_GETCOND:
case A_GETCLASS:
case A_GETPINFO:
case A_GETFSIZE:
case A_GETPINFO_ADDR:
case A_GETKAUDIT:
ret = copyout((void *)&udata, uap->data, uap->length);
break;
}
}
lck_mtx_unlock(audit_mtx);
return (ret);
}
int
getauid(__unused proc_t p, struct getauid_args *uap, __unused register_t *retval)
{
int error;
#if CONFIG_MACF
error = mac_proc_check_getauid(p);
if (error)
return (error);
#endif
error = copyout((void *)&kauth_cred_get()->cr_au.ai_auid,
uap->auid, sizeof(au_id_t));
if (error)
return (error);
return (0);
}
int
setauid(proc_t p, struct setauid_args *uap, __unused register_t *retval)
{
int error;
au_id_t temp_au_id;
error = suser(kauth_cred_get(), &p->p_acflag);
if (error)
return (error);
error = copyin(uap->auid,
(void *)&temp_au_id,
sizeof(au_id_t));
if (error)
return (error);
#if CONFIG_MACF
error = mac_proc_check_setauid(p, temp_au_id);
if (error)
return (error);
#endif
for (;;) {
kauth_cred_t my_cred, my_new_cred;
struct auditinfo temp_auditinfo;
my_cred = kauth_cred_proc_ref(p);
temp_auditinfo = my_cred->cr_au;
temp_auditinfo.ai_auid = temp_au_id;
my_new_cred = kauth_cred_setauditinfo(my_cred, &temp_auditinfo);
if (my_cred != my_new_cred) {
proc_lock(p);
if (p->p_ucred != my_cred) {
proc_unlock(p);
kauth_cred_unref(&my_new_cred);
continue;
}
p->p_ucred = my_new_cred;
proc_unlock(p);
}
kauth_cred_unref(&my_cred);
break;
}
set_security_token(p);
audit_arg_auid(kauth_cred_get()->cr_au.ai_auid);
return (0);
}
int
getaudit(proc_t p, struct getaudit_args *uap, __unused register_t *retval)
{
struct auditinfo ai;
int error;
#if CONFIG_MACF
error = mac_proc_check_getaudit(p);
if (error)
return (error);
#endif
ai = kauth_cred_get()->cr_au;
error = suser(kauth_cred_get(), &p->p_acflag);
if (error) {
ai.ai_mask.am_success = ~0;
ai.ai_mask.am_failure = ~0;
}
error = copyout(&ai, uap->auditinfo, sizeof(ai));
if (error)
return (error);
return (0);
}
int
setaudit(proc_t p, struct setaudit_args *uap, __unused register_t *retval)
{
int error;
struct auditinfo temp_auditinfo;
kauth_cred_t safecred;
error = suser(kauth_cred_get(), &p->p_acflag);
if (error)
return (error);
error = copyin(uap->auditinfo,
(void *)&temp_auditinfo,
sizeof(temp_auditinfo));
if (error)
return (error);
#if CONFIG_MACF
error = mac_proc_check_setaudit(p, &temp_auditinfo);
if (error)
return (error);
#endif
for (;;) {
kauth_cred_t my_cred, my_new_cred;
my_cred = kauth_cred_proc_ref(p);
my_new_cred = kauth_cred_setauditinfo(my_cred, &temp_auditinfo);
if (my_cred != my_new_cred) {
proc_lock(p);
if (p->p_ucred != my_cred) {
proc_unlock(p);
kauth_cred_unref(&my_new_cred);
continue;
}
p->p_ucred = my_new_cred;
proc_unlock(p);
}
kauth_cred_unref(&my_cred);
break;
}
set_security_token(p);
safecred = kauth_cred_proc_ref(p);
audit_arg_auditinfo(&safecred->cr_au);
kauth_cred_unref(&safecred);
return (0);
}
int
getaudit_addr(__unused proc_t p, __unused struct getaudit_addr_args *uap, __unused register_t *retval)
{
return (ENOSYS);
}
int
setaudit_addr(proc_t p, __unused struct setaudit_addr_args *uap, __unused register_t *retval)
{
int error;
error = suser(kauth_cred_get(), &p->p_acflag);
if (error)
return (error);
return (ENOSYS);
}
int
auditctl(proc_t p, struct auditctl_args *uap, __unused register_t *retval)
{
struct nameidata nd;
kauth_cred_t cred;
struct vnode *vp;
int error;
error = suser(kauth_cred_get(), &p->p_acflag);
if (error)
return (error);
vp = NULL;
cred = NULL;
if (uap->path != USER_ADDR_NULL) {
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1,
(IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32),
uap->path, vfs_context_current());
error = vn_open(&nd, audit_open_flags, 0);
if (error)
goto out;
vp = nd.ni_vp;
if (vp->v_type != VREG) {
vn_close(vp, audit_close_flags, vfs_context_current());
vnode_put(vp);
error = EINVAL;
goto out;
}
#if CONFIG_MACF
error = mac_system_check_auditctl(kauth_cred_get(), vp);
if (error) {
vn_close(vp, audit_close_flags, vfs_context_current());
vnode_put(vp);
goto out;
}
#endif
cred = kauth_cred_get_with_ref();
lck_mtx_lock(audit_mtx);
audit_suspended = 0;
lck_mtx_unlock(audit_mtx);
}
#if CONFIG_MACF
else {
error = mac_system_check_auditctl(kauth_cred_get(), NULL);
if (error)
return (error);
}
#endif
audit_rotate_vnode(cred, vp);
if (vp)
vnode_put(vp);
out:
return (error);
}
struct kaudit_record *
audit_new(int event, proc_t p, __unused struct uthread *uthread)
{
struct kaudit_record *ar;
int no_record;
kauth_cred_t safecred;
#if 0
if (event != AUDIT_EVENT_FILESTOP && event != AUDIT_EVENT_FILESTART) {
#endif
lck_mtx_lock(audit_mtx);
no_record = (audit_suspended || !audit_enabled);
lck_mtx_unlock(audit_mtx);
if (no_record)
return (NULL);
#if 0
}
#endif
ar = (struct kaudit_record *)zalloc(audit_zone);
if (ar == NULL)
return NULL;
bzero(ar, sizeof(*ar));
ar->k_ar.ar_magic = AUDIT_RECORD_MAGIC;
ar->k_ar.ar_event = event;
nanotime(&ar->k_ar.ar_starttime);
safecred = kauth_cred_proc_ref(p);
cru2x(safecred, &ar->k_ar.ar_subj_cred);
ar->k_ar.ar_subj_ruid = safecred->cr_ruid;
ar->k_ar.ar_subj_rgid = safecred->cr_rgid;
ar->k_ar.ar_subj_egid = safecred->cr_groups[0];
ar->k_ar.ar_subj_auid = safecred->cr_au.ai_auid;
ar->k_ar.ar_subj_asid = safecred->cr_au.ai_asid;
ar->k_ar.ar_subj_amask = safecred->cr_au.ai_mask;
ar->k_ar.ar_subj_term = safecred->cr_au.ai_termid;
kauth_cred_unref(&safecred);
ar->k_ar.ar_subj_pid = p->p_pid;
bcopy(p->p_comm, ar->k_ar.ar_subj_comm, MAXCOMLEN);
#if CONFIG_MACF
do {
struct mac mac;
ar->k_ar.ar_cred_mac_labels =
(char *)zalloc(audit_mac_label_zone);
if (ar->k_ar.ar_cred_mac_labels == NULL) {
zfree(audit_zone, ar);
return (NULL);
}
mac.m_buflen = MAC_AUDIT_LABEL_LEN;
mac.m_string = ar->k_ar.ar_cred_mac_labels;
mac_cred_label_externalize_audit(p, &mac);
ar->k_ar.ar_mac_records = (struct mac_audit_record_list_t *)
kalloc(sizeof(*ar->k_ar.ar_mac_records));
if (ar->k_ar.ar_mac_records == NULL) {
zfree(audit_mac_label_zone,
ar->k_ar.ar_cred_mac_labels);
zfree(audit_zone, ar);
return (NULL);
}
LIST_INIT(ar->k_ar.ar_mac_records);
ar->k_ar.ar_forced_by_mac = 0;
} while (0);
#endif
lck_mtx_lock(audit_mtx);
audit_pre_q_len++;
lck_mtx_unlock(audit_mtx);
return (ar);
}
void
audit_abort(struct kaudit_record *ar)
{
lck_mtx_lock(audit_mtx);
audit_pre_q_len--;
lck_mtx_unlock(audit_mtx);
audit_free(ar);
}
void
audit_commit(struct kaudit_record *ar, int error, int retval)
{
int ret;
int sorf;
struct au_mask *aumask;
if (ar == NULL)
return;
if (ar->k_ar.ar_subj_auid == AU_DEFAUDITID)
aumask = &audit_nae_mask;
else
aumask = &ar->k_ar.ar_subj_amask;
if (error)
sorf = AU_PRS_FAILURE;
else
sorf = AU_PRS_SUCCESS;
switch(ar->k_ar.ar_event) {
case AUE_OPEN_RWTC:
ar->k_ar.ar_event = flags_and_error_to_openevent(ar->k_ar.ar_arg_fflags, error);
break;
case AUE_SYSCTL:
ar->k_ar.ar_event = ctlname_to_sysctlevent(ar->k_ar.ar_arg_ctlname, ar->k_ar.ar_valid_arg);
break;
case AUE_AUDITON:
ar->k_ar.ar_event = auditon_command_event(ar->k_ar.ar_arg_cmd);
break;
}
if (au_preselect(ar->k_ar.ar_event, aumask, sorf) != 0)
ar->k_ar_commit |= AR_COMMIT_KERNEL;
if ((ar->k_ar_commit & (AR_COMMIT_USER | AR_COMMIT_KERNEL)) == 0) {
lck_mtx_lock(audit_mtx);
audit_pre_q_len--;
lck_mtx_unlock(audit_mtx);
audit_free(ar);
return;
}
ar->k_ar.ar_errno = error;
ar->k_ar.ar_retval = retval;
nanotime(&ar->k_ar.ar_endtime);
lck_mtx_lock(audit_mtx);
if (audit_suspended || !audit_enabled) {
audit_pre_q_len--;
lck_mtx_unlock(audit_mtx);
audit_free(ar);
return;
}
while (audit_q_len >= audit_qctrl.aq_hiwater) {
ret = wait_queue_assert_wait(audit_wait_queue,
AUDIT_COMMIT_EVENT,
THREAD_UNINT,
0);
lck_mtx_unlock(audit_mtx);
assert(ret == THREAD_WAITING);
ret = thread_block(THREAD_CONTINUE_NULL);
assert(ret == THREAD_AWAKENED);
lck_mtx_lock(audit_mtx);
}
TAILQ_INSERT_TAIL(&audit_q, ar, k_q);
audit_q_len++;
audit_pre_q_len--;
wait_queue_wakeup_one(audit_wait_queue, AUDIT_WORKER_EVENT, THREAD_AWAKENED);
lck_mtx_unlock(audit_mtx);
}
static void
audit_new_wait(int audit_event, proc_t proc, struct uthread *uthread)
{
int ret;
if (audit_in_failure &&
suser(kauth_cred_get(), &proc->p_acflag) != 0) {
ret = wait_queue_assert_wait(audit_wait_queue,
AUDIT_FAILURE_EVENT, THREAD_UNINT, 0);
assert(ret == THREAD_WAITING);
(void)thread_block(THREAD_CONTINUE_NULL);
panic("audit_failing_stop: thread continued");
}
uthread->uu_ar = audit_new(audit_event, proc, uthread);
}
void
audit_syscall_enter(unsigned short code, proc_t proc,
struct uthread *uthread)
{
int audit_event;
struct au_mask *aumask;
kauth_cred_t my_cred;
audit_event = sys_au_event[code];
if (audit_event == AUE_NULL)
return;
assert(uthread->uu_ar == NULL);
my_cred = kauth_cred_proc_ref(proc);
if (my_cred->cr_au.ai_auid == AU_DEFAUDITID)
aumask = &audit_nae_mask;
else
aumask = &my_cred->cr_au.ai_mask;
#if CONFIG_MACF
do {
int error;
error = mac_audit_check_preselect(my_cred, code,
(void *) uthread->uu_arg);
if (error == MAC_AUDIT_YES) {
uthread->uu_ar = audit_new(audit_event, proc, uthread);
uthread->uu_ar->k_ar.ar_forced_by_mac = 1;
au_to_text("Forced by a MAC policy");
}
else if (error == MAC_AUDIT_NO) {
uthread->uu_ar = NULL;
}
else if (error == MAC_AUDIT_DEFAULT &&
au_preselect(audit_event, &my_cred->cr_au.ai_mask,
AU_PRS_FAILURE | AU_PRS_SUCCESS))
audit_new_wait(audit_event, proc, uthread);
} while (0);
#else
if (au_preselect(audit_event, &my_cred->cr_au.ai_mask,
AU_PRS_FAILURE | AU_PRS_SUCCESS)) {
audit_new_wait(audit_event, proc, uthread);
} else {
uthread->uu_ar = NULL;
}
#endif
kauth_cred_unref(&my_cred);
}
#if CONFIG_MACF
void
audit_syscall_exit(unsigned short code, int error, AUDIT_PRINTF_ONLY proc_t proc, struct uthread *uthread)
#else
void
audit_syscall_exit(int error, AUDIT_PRINTF_ONLY proc_t proc, struct uthread *uthread)
#endif
{
int retval;
if (error)
retval = -1;
else
retval = uthread->uu_rval[0];
#if CONFIG_MACF
do {
int mac_error;
if (uthread->uu_ar == NULL)
goto out;
mac_error = mac_audit_check_postselect(kauth_cred_get(), code,
(void *) uthread->uu_arg, error, retval,
uthread->uu_ar->k_ar.ar_forced_by_mac);
if (mac_error == MAC_AUDIT_YES)
uthread->uu_ar->k_ar_commit |= AR_COMMIT_KERNEL;
else if (mac_error == MAC_AUDIT_NO) {
audit_free(uthread->uu_ar);
goto out;
}
} while (0);
#endif
audit_commit(uthread->uu_ar, error, retval);
if (uthread->uu_ar != NULL) {
AUDIT_PRINTF(("audit record committed by pid %d\n", proc->p_pid));
}
#if CONFIG_MACF
out:
#endif
uthread->uu_ar = NULL;
}
void
audit_mach_syscall_enter(unsigned short audit_event)
{
struct uthread *uthread;
proc_t proc;
struct au_mask *aumask;
kauth_cred_t my_cred;
if (audit_event == AUE_NULL)
return;
uthread = curuthread();
if (uthread == NULL)
return;
proc = current_proc();
if (proc == NULL)
return;
assert(uthread->uu_ar == NULL);
my_cred = kauth_cred_proc_ref(proc);
if (my_cred->cr_au.ai_auid == AU_DEFAUDITID)
aumask = &audit_nae_mask;
else
aumask = &my_cred->cr_au.ai_mask;
kauth_cred_unref(&my_cred);
if (au_preselect(audit_event, aumask,
AU_PRS_FAILURE | AU_PRS_SUCCESS)) {
uthread->uu_ar = audit_new(audit_event, proc, uthread);
} else {
uthread->uu_ar = NULL;
}
}
void
audit_mach_syscall_exit(int retval, struct uthread *uthread)
{
audit_commit(uthread->uu_ar, retval, retval);
uthread->uu_ar = NULL;
}
void
audit_arg_addr(user_addr_t addr)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_addr = CAST_DOWN(void *, addr);
ar->k_ar.ar_valid_arg |= ARG_ADDR;
}
void
audit_arg_len(user_size_t len)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_len = CAST_DOWN(int, len);
ar->k_ar.ar_valid_arg |= ARG_LEN;
}
void
audit_arg_fd(int fd)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_fd = fd;
ar->k_ar.ar_valid_arg |= ARG_FD;
}
void
audit_arg_fflags(int fflags)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_fflags = fflags;
ar->k_ar.ar_valid_arg |= ARG_FFLAGS;
}
void
audit_arg_gid(gid_t gid, gid_t egid, gid_t rgid, gid_t sgid)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_gid = gid;
ar->k_ar.ar_arg_egid = egid;
ar->k_ar.ar_arg_rgid = rgid;
ar->k_ar.ar_arg_sgid = sgid;
ar->k_ar.ar_valid_arg |= (ARG_GID | ARG_EGID | ARG_RGID | ARG_SGID);
}
void
audit_arg_uid(uid_t uid, uid_t euid, uid_t ruid, uid_t suid)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_uid = uid;
ar->k_ar.ar_arg_euid = euid;
ar->k_ar.ar_arg_ruid = ruid;
ar->k_ar.ar_arg_suid = suid;
ar->k_ar.ar_valid_arg |= (ARG_UID | ARG_EUID | ARG_RUID | ARG_SUID);
}
void
audit_arg_groupset(const gid_t *gidset, u_int gidset_size)
{
uint i;
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
for (i = 0; i < gidset_size; i++)
ar->k_ar.ar_arg_groups.gidset[i] = gidset[i];
ar->k_ar.ar_arg_groups.gidset_size = gidset_size;
ar->k_ar.ar_valid_arg |= ARG_GROUPSET;
}
void
audit_arg_login(const char *login)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
strlcpy(ar->k_ar.ar_arg_login, login, MAXLOGNAME);
ar->k_ar.ar_valid_arg |= ARG_LOGIN;
}
void
audit_arg_ctlname(const int *name, int namelen)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
bcopy(name, &ar->k_ar.ar_arg_ctlname, namelen * sizeof(int));
ar->k_ar.ar_arg_len = namelen;
ar->k_ar.ar_valid_arg |= (ARG_CTLNAME | ARG_LEN);
}
void
audit_arg_mask(int mask)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_mask = mask;
ar->k_ar.ar_valid_arg |= ARG_MASK;
}
void
audit_arg_mode(mode_t mode)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_mode = mode;
ar->k_ar.ar_valid_arg |= ARG_MODE;
}
void
audit_arg_dev(int dev)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_dev = dev;
ar->k_ar.ar_valid_arg |= ARG_DEV;
}
void
audit_arg_value(long value)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_value = value;
ar->k_ar.ar_valid_arg |= ARG_VALUE;
}
void
audit_arg_owner(uid_t uid, gid_t gid)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_uid = uid;
ar->k_ar.ar_arg_gid = gid;
ar->k_ar.ar_valid_arg |= (ARG_UID | ARG_GID);
}
void
audit_arg_pid(pid_t pid)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_pid = pid;
ar->k_ar.ar_valid_arg |= ARG_PID;
}
void
audit_arg_process(proc_t p)
{
struct kaudit_record *ar;
kauth_cred_t my_cred;
ar = currecord();
if ((ar == NULL) || (p == NULL))
return;
my_cred = kauth_cred_proc_ref(p);
ar->k_ar.ar_arg_auid = my_cred->cr_au.ai_auid;
ar->k_ar.ar_arg_euid = my_cred->cr_uid;
ar->k_ar.ar_arg_egid = my_cred->cr_groups[0];
ar->k_ar.ar_arg_ruid = my_cred->cr_ruid;
ar->k_ar.ar_arg_rgid = my_cred->cr_rgid;
ar->k_ar.ar_arg_asid = my_cred->cr_au.ai_asid;
ar->k_ar.ar_arg_termid = my_cred->cr_au.ai_termid;
kauth_cred_unref(&my_cred);
ar->k_ar.ar_valid_arg |= ARG_AUID | ARG_EUID | ARG_EGID | ARG_RUID |
ARG_RGID | ARG_ASID | ARG_TERMID | ARG_PROCESS;
}
void
audit_arg_signum(u_int signum)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_signum = signum;
ar->k_ar.ar_valid_arg |= ARG_SIGNUM;
}
void
audit_arg_socket(int sodomain, int sotype, int soprotocol)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_sockinfo.so_domain = sodomain;
ar->k_ar.ar_arg_sockinfo.so_type = sotype;
ar->k_ar.ar_arg_sockinfo.so_protocol = soprotocol;
ar->k_ar.ar_valid_arg |= ARG_SOCKINFO;
}
void
audit_arg_sockaddr(struct vnode *cwd_vp, struct sockaddr *so)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL || cwd_vp == NULL || so == NULL)
return;
bcopy(so, &ar->k_ar.ar_arg_sockaddr, sizeof(ar->k_ar.ar_arg_sockaddr));
switch (so->sa_family) {
case AF_INET:
ar->k_ar.ar_valid_arg |= ARG_SADDRINET;
break;
case AF_INET6:
ar->k_ar.ar_valid_arg |= ARG_SADDRINET6;
break;
case AF_UNIX:
audit_arg_upath(cwd_vp, ((struct sockaddr_un *)so)->sun_path,
ARG_UPATH1);
ar->k_ar.ar_valid_arg |= ARG_SADDRUNIX;
break;
}
}
void
audit_arg_auid(uid_t auid)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_auid = auid;
ar->k_ar.ar_valid_arg |= ARG_AUID;
}
void
audit_arg_auditinfo(const struct auditinfo *au_info)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_auid = au_info->ai_auid;
ar->k_ar.ar_arg_asid = au_info->ai_asid;
ar->k_ar.ar_arg_amask.am_success = au_info->ai_mask.am_success;
ar->k_ar.ar_arg_amask.am_failure = au_info->ai_mask.am_failure;
ar->k_ar.ar_arg_termid.port = au_info->ai_termid.port;
ar->k_ar.ar_arg_termid.machine = au_info->ai_termid.machine;
ar->k_ar.ar_valid_arg |= ARG_AUID | ARG_ASID | ARG_AMASK | ARG_TERMID;
}
void
audit_arg_text(const char *text)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_TEXT);
if (text == NULL)
return;
if (ar->k_ar.ar_arg_text == NULL) {
ar->k_ar.ar_arg_text = (char *)kalloc(MAXPATHLEN);
if (ar->k_ar.ar_arg_text == NULL)
return;
}
strlcpy(ar->k_ar.ar_arg_text, text, MAXPATHLEN);
ar->k_ar.ar_valid_arg |= ARG_TEXT;
}
void
audit_arg_cmd(int cmd)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_cmd = cmd;
ar->k_ar.ar_valid_arg |= ARG_CMD;
}
void
audit_arg_svipc_cmd(int cmd)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_svipc_cmd = cmd;
ar->k_ar.ar_valid_arg |= ARG_SVIPC_CMD;
}
void
audit_arg_svipc_perm(const struct ipc_perm *perm)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
bcopy(perm, &ar->k_ar.ar_arg_svipc_perm,
sizeof(ar->k_ar.ar_arg_svipc_perm));
ar->k_ar.ar_valid_arg |= ARG_SVIPC_PERM;
}
void
audit_arg_svipc_id(int id)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_svipc_id = id;
ar->k_ar.ar_valid_arg |= ARG_SVIPC_ID;
}
void
audit_arg_svipc_addr(user_addr_t addr)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_svipc_addr = addr;
ar->k_ar.ar_valid_arg |= ARG_SVIPC_ADDR;
}
void
audit_arg_posix_ipc_perm(uid_t uid, gid_t gid, mode_t mode)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_pipc_perm.pipc_uid = uid;
ar->k_ar.ar_arg_pipc_perm.pipc_gid = gid;
ar->k_ar.ar_arg_pipc_perm.pipc_mode = mode;
ar->k_ar.ar_valid_arg |= ARG_POSIX_IPC_PERM;
}
void
audit_arg_auditon(const union auditon_udata *udata)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
bcopy((const void *)udata, &ar->k_ar.ar_arg_auditon,
sizeof(ar->k_ar.ar_arg_auditon));
ar->k_ar.ar_valid_arg |= ARG_AUDITON;
}
void
audit_arg_file(__unused proc_t p, const struct fileproc *fp)
{
struct kaudit_record *ar;
struct socket *so;
struct inpcb *pcb;
if (fp->f_fglob->fg_type == DTYPE_VNODE) {
audit_arg_vnpath_withref((struct vnode *)fp->f_fglob->fg_data, ARG_VNODE1);
return;
}
if (fp->f_fglob->fg_type == DTYPE_SOCKET) {
ar = currecord();
if (ar == NULL)
return;
so = (struct socket *)fp->f_fglob->fg_data;
if (INP_CHECK_SOCKAF(so, PF_INET)) {
if (so->so_pcb == NULL)
return;
ar->k_ar.ar_arg_sockinfo.so_type =
so->so_type;
ar->k_ar.ar_arg_sockinfo.so_domain =
INP_SOCKAF(so);
ar->k_ar.ar_arg_sockinfo.so_protocol =
so->so_proto->pr_protocol;
pcb = (struct inpcb *)so->so_pcb;
ar->k_ar.ar_arg_sockinfo.so_raddr =
pcb->inp_faddr.s_addr;
ar->k_ar.ar_arg_sockinfo.so_laddr =
pcb->inp_laddr.s_addr;
ar->k_ar.ar_arg_sockinfo.so_rport =
pcb->inp_fport;
ar->k_ar.ar_arg_sockinfo.so_lport =
pcb->inp_lport;
ar->k_ar.ar_valid_arg |= ARG_SOCKINFO;
}
}
}
void
audit_arg_upath(struct vnode *cwd_vp, char *upath, u_int64_t flags)
{
struct kaudit_record *ar;
char **pathp;
if (cwd_vp == NULL || upath == NULL)
return;
if ((flags & (ARG_UPATH1 | ARG_UPATH2)) == 0)
return;
ar = currecord();
if (ar == NULL)
return;
if (flags & ARG_UPATH1) {
ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_UPATH1);
pathp = &ar->k_ar.ar_arg_upath1;
}
else {
ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_UPATH2);
pathp = &ar->k_ar.ar_arg_upath2;
}
if (*pathp == NULL) {
*pathp = (char *)kalloc(MAXPATHLEN);
if (*pathp == NULL)
return;
}
if (canon_path(cwd_vp, upath, *pathp) == 0) {
if (flags & ARG_UPATH1)
ar->k_ar.ar_valid_arg |= ARG_UPATH1;
else
ar->k_ar.ar_valid_arg |= ARG_UPATH2;
} else {
kfree(*pathp, MAXPATHLEN);
*pathp = NULL;
}
}
void
audit_arg_vnpath(struct vnode *vp, u_int64_t flags)
{
struct kaudit_record *ar;
struct vnode_attr va;
int error;
int len;
char **pathp;
struct vnode_au_info *vnp;
proc_t p;
#if CONFIG_MACF
char **vnode_mac_labelp;
struct mac mac;
#endif
if (vp == NULL)
return;
ar = currecord();
if (ar == NULL)
return;
if ((flags & (ARG_VNODE1 | ARG_VNODE2)) == 0)
return;
p = current_proc();
if (flags & ARG_VNODE1) {
ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_KPATH1);
ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_VNODE1);
pathp = &ar->k_ar.ar_arg_kpath1;
vnp = &ar->k_ar.ar_arg_vnode1;
#if CONFIG_MACF
vnode_mac_labelp = &ar->k_ar.ar_vnode1_mac_labels;
#endif
}
else {
ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_KPATH2);
ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_VNODE2);
pathp = &ar->k_ar.ar_arg_kpath2;
vnp = &ar->k_ar.ar_arg_vnode2;
#if CONFIG_MACF
vnode_mac_labelp = &ar->k_ar.ar_vnode2_mac_labels;
#endif
}
if (*pathp == NULL) {
*pathp = (char *)kalloc(MAXPATHLEN);
if (*pathp == NULL)
return;
}
len = MAXPATHLEN;
if (vn_getpath(vp, *pathp, &len) == 0) {
if (flags & ARG_VNODE1)
ar->k_ar.ar_valid_arg |= ARG_KPATH1;
else
ar->k_ar.ar_valid_arg |= ARG_KPATH2;
} else {
kfree(*pathp, MAXPATHLEN);
*pathp = NULL;
}
VATTR_INIT(&va);
VATTR_WANTED(&va, va_mode);
VATTR_WANTED(&va, va_uid);
VATTR_WANTED(&va, va_gid);
VATTR_WANTED(&va, va_rdev);
VATTR_WANTED(&va, va_fsid);
VATTR_WANTED(&va, va_fileid);
VATTR_WANTED(&va, va_gen);
error = vnode_getattr(vp, &va, vfs_context_current());
if (error) {
return;
}
#if CONFIG_MACF
if (*vnode_mac_labelp == NULL) {
*vnode_mac_labelp = (char *)zalloc(audit_mac_label_zone);
if (*vnode_mac_labelp != NULL) {
mac.m_buflen = MAC_AUDIT_LABEL_LEN;
mac.m_string = *vnode_mac_labelp;
mac_vnode_label_externalize_audit(vp, &mac);
}
}
#endif
vnp->vn_mode = va.va_mode;
vnp->vn_uid = va.va_uid;
vnp->vn_gid = va.va_gid;
vnp->vn_dev = va.va_rdev;
vnp->vn_fsid = va.va_fsid;
vnp->vn_fileid = (u_long)va.va_fileid;
vnp->vn_gen = va.va_gen;
if (flags & ARG_VNODE1)
ar->k_ar.ar_valid_arg |= ARG_VNODE1;
else
ar->k_ar.ar_valid_arg |= ARG_VNODE2;
}
void
audit_arg_vnpath_withref(struct vnode *vp, u_int64_t flags)
{
if (vp == NULL || vnode_getwithref(vp))
return;
audit_arg_vnpath(vp, flags);
(void)vnode_put(vp);
}
void
audit_arg_mach_port1(mach_port_name_t port)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_mach_port1 = port;
ar->k_ar.ar_valid_arg |= ARG_MACHPORT1;
}
void
audit_arg_mach_port2(mach_port_name_t port)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_mach_port2 = port;
ar->k_ar.ar_valid_arg |= ARG_MACHPORT2;
}
void
audit_sysclose(proc_t p, int fd)
{
struct fileproc *fp;
struct vnode *vp;
audit_arg_fd(fd);
if (fp_getfvp(p, fd, &fp, &vp) != 0)
return;
audit_arg_vnpath_withref((struct vnode *)fp->f_fglob->fg_data, ARG_VNODE1);
file_drop(fd);
}
#if CONFIG_MACF
int
audit_mac_data(int type, int len, u_char *data) {
struct kaudit_record *cur;
struct mac_audit_record *record;
int ret = 0;
if (audit_enabled == 0) {
ret = ENOTSUP;
goto out_fail;
}
cur = currecord();
if (cur == NULL) {
ret = ENOTSUP;
goto out_fail;
}
record = (struct mac_audit_record *)kalloc(sizeof(*record));
if (record == NULL)
goto out_fail;
record->type = type;
record->length = len;
record->data = data;
LIST_INSERT_HEAD(cur->k_ar.ar_mac_records, record, records);
return (0);
out_fail:
kfree(data, len);
return (ret);
}
void
audit_arg_mac_string(const char *string)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
if (ar->k_ar.ar_arg_mac_string == NULL) {
ar->k_ar.ar_arg_mac_string =
(char *)kalloc(MAC_MAX_LABEL_BUF_LEN + MAC_ARG_PREFIX_LEN);
if (ar->k_ar.ar_arg_mac_string == NULL)
return;
}
strncpy(ar->k_ar.ar_arg_mac_string, MAC_ARG_PREFIX, MAC_ARG_PREFIX_LEN);
strncpy(ar->k_ar.ar_arg_mac_string + MAC_ARG_PREFIX_LEN, string, MAC_MAX_LABEL_BUF_LEN);
ar->k_ar.ar_valid_arg |= ARG_MAC_STRING;
}
#endif
int
kau_will_audit(void)
{
return (audit_enabled && currecord() != NULL);
}
#else
void
audit_init(void)
{
}
void
audit_shutdown(void)
{
}
int
audit(proc_t p, struct audit_args *uap, register_t *retval)
{
return (ENOSYS);
}
int
auditon(proc_t p, struct auditon_args *uap, register_t *retval)
{
return (ENOSYS);
}
int
getauid(proc_t p, struct getauid_args *uap, register_t *retval)
{
return (ENOSYS);
}
int
setauid(proc_t p, struct setauid_args *uap, register_t *retval)
{
return (ENOSYS);
}
int
getaudit(proc_t p, struct getaudit_args *uap, register_t *retval)
{
return (ENOSYS);
}
int
setaudit(proc_t p, struct setaudit_args *uap, register_t *retval)
{
return (ENOSYS);
}
int
getaudit_addr(proc_t p, struct getaudit_addr_args *uap, register_t *retval)
{
return (ENOSYS);
}
int
setaudit_addr(proc_t p, struct setaudit_addr_args *uap, register_t *retval)
{
return (ENOSYS);
}
int
auditctl(proc_t p, struct auditctl_args *uap, register_t *retval)
{
return (ENOSYS);
}
#if CONFIG_MACF
void
audit_mac_data(int type, int len, u_char *data)
{
}
int
kau_will_audit()
{
return (0);
}
#endif
#endif