diff options
Diffstat (limited to 'sys/security')
55 files changed, 34599 insertions, 0 deletions
diff --git a/sys/security/audit/audit.c b/sys/security/audit/audit.c new file mode 100644 index 0000000..4ea76c6 --- /dev/null +++ b/sys/security/audit/audit.c @@ -0,0 +1,683 @@ +/*- + * Copyright (c) 1999-2005 Apple Inc. + * Copyright (c) 2006-2007 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/condvar.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/filedesc.h> +#include <sys/fcntl.h> +#include <sys/ipc.h> +#include <sys/kernel.h> +#include <sys/kthread.h> +#include <sys/malloc.h> +#include <sys/mount.h> +#include <sys/namei.h> +#include <sys/priv.h> +#include <sys/proc.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/protosw.h> +#include <sys/domain.h> +#include <sys/sysctl.h> +#include <sys/sysproto.h> +#include <sys/sysent.h> +#include <sys/systm.h> +#include <sys/ucred.h> +#include <sys/uio.h> +#include <sys/un.h> +#include <sys/unistd.h> +#include <sys/vnode.h> + +#include <bsm/audit.h> +#include <bsm/audit_internal.h> +#include <bsm/audit_kevents.h> + +#include <netinet/in.h> +#include <netinet/in_pcb.h> + +#include <security/audit/audit.h> +#include <security/audit/audit_private.h> + +#include <vm/uma.h> + +static uma_zone_t audit_record_zone; +static MALLOC_DEFINE(M_AUDITCRED, "audit_cred", "Audit cred storage"); +MALLOC_DEFINE(M_AUDITDATA, "audit_data", "Audit data storage"); +MALLOC_DEFINE(M_AUDITPATH, "audit_path", "Audit path storage"); +MALLOC_DEFINE(M_AUDITTEXT, "audit_text", "Audit text storage"); + +SYSCTL_NODE(_security, OID_AUTO, audit, CTLFLAG_RW, 0, + "TrustedBSD audit controls"); + +/* + * Audit control settings that are set/read by system calls and are hence + * non-static. + * + * Define the audit control flags. + */ +int audit_enabled; +int audit_suspended; + +/* + * Flags controlling behavior in low storage situations. Should we panic if + * a write fails? Should we fail stop if we're out of disk space? + */ +int audit_panic_on_write_fail; +int audit_fail_stop; +int audit_argv; +int audit_arge; + +/* + * Are we currently "failing stop" due to out of disk space? + */ +int audit_in_failure; + +/* + * Global audit statistics. + */ +struct audit_fstat audit_fstat; + +/* + * Preselection mask for non-attributable events. + */ +struct au_mask audit_nae_mask; + +/* + * Mutex to protect global variables shared between various threads and + * processes. + */ +struct mtx audit_mtx; + +/* + * Queue of audit records ready for delivery to disk. We insert new records + * at the tail, and remove records from the head. Also, a count of the + * number of records used for checking queue depth. In addition, a counter + * of records that we have allocated but are not yet in the queue, which is + * needed to estimate the total size of the combined set of records + * outstanding in the system. + */ +struct kaudit_queue audit_q; +size_t audit_q_len; +size_t audit_pre_q_len; + +/* + * Audit queue control settings (minimum free, low/high water marks, etc.) + */ +struct au_qctrl audit_qctrl; + +/* + * Condition variable to signal to the worker that it has work to do: either + * new records are in the queue, or a log replacement is taking place. + */ +struct cv audit_worker_cv; + +/* + * Condition variable to flag when crossing the low watermark, meaning that + * threads blocked due to hitting the high watermark can wake up and continue + * to commit records. + */ +struct cv audit_watermark_cv; + +/* + * Condition variable for auditing threads wait on when in fail-stop mode. + * Threads wait on this CV forever (and ever), never seeing the light of day + * again. + */ +static struct cv audit_fail_cv; + +/* + * Kernel audit information. This will store the current audit address + * or host information that the kernel will use when it's generating + * audit records. This data is modified by the A_GET{SET}KAUDIT auditon(2) + * command. + */ +static struct auditinfo_addr audit_kinfo; +static struct rwlock audit_kinfo_lock; + +#define KINFO_LOCK_INIT() rw_init(&audit_kinfo_lock, \ + "audit_kinfo_lock") +#define KINFO_RLOCK() rw_rlock(&audit_kinfo_lock) +#define KINFO_WLOCK() rw_wlock(&audit_kinfo_lock) +#define KINFO_RUNLOCK() rw_runlock(&audit_kinfo_lock) +#define KINFO_WUNLOCK() rw_wunlock(&audit_kinfo_lock) + +void +audit_set_kinfo(struct auditinfo_addr *ak) +{ + + KASSERT(ak->ai_termid.at_type == AU_IPv4 || + ak->ai_termid.at_type == AU_IPv6, + ("audit_set_kinfo: invalid address type")); + + KINFO_WLOCK(); + audit_kinfo = *ak; + KINFO_WUNLOCK(); +} + +void +audit_get_kinfo(struct auditinfo_addr *ak) +{ + + KASSERT(audit_kinfo.ai_termid.at_type == AU_IPv4 || + audit_kinfo.ai_termid.at_type == AU_IPv6, + ("audit_set_kinfo: invalid address type")); + + KINFO_RLOCK(); + *ak = audit_kinfo; + KINFO_RUNLOCK(); +} + +/* + * Construct an audit record for the passed thread. + */ +static int +audit_record_ctor(void *mem, int size, void *arg, int flags) +{ + struct kaudit_record *ar; + struct thread *td; + struct ucred *cred; + + KASSERT(sizeof(*ar) == size, ("audit_record_ctor: wrong size")); + + td = arg; + ar = mem; + bzero(ar, sizeof(*ar)); + ar->k_ar.ar_magic = AUDIT_RECORD_MAGIC; + nanotime(&ar->k_ar.ar_starttime); + + /* + * Export the subject credential. + */ + cred = td->td_ucred; + cru2x(cred, &ar->k_ar.ar_subj_cred); + ar->k_ar.ar_subj_ruid = cred->cr_ruid; + ar->k_ar.ar_subj_rgid = cred->cr_rgid; + ar->k_ar.ar_subj_egid = cred->cr_groups[0]; + ar->k_ar.ar_subj_auid = cred->cr_audit.ai_auid; + ar->k_ar.ar_subj_asid = cred->cr_audit.ai_asid; + ar->k_ar.ar_subj_pid = td->td_proc->p_pid; + ar->k_ar.ar_subj_amask = cred->cr_audit.ai_mask; + ar->k_ar.ar_subj_term_addr = cred->cr_audit.ai_termid; + return (0); +} + +static void +audit_record_dtor(void *mem, int size, void *arg) +{ + struct kaudit_record *ar; + + KASSERT(sizeof(*ar) == size, ("audit_record_dtor: wrong size")); + + ar = mem; + if (ar->k_ar.ar_arg_upath1 != NULL) + free(ar->k_ar.ar_arg_upath1, M_AUDITPATH); + if (ar->k_ar.ar_arg_upath2 != NULL) + free(ar->k_ar.ar_arg_upath2, M_AUDITPATH); + if (ar->k_ar.ar_arg_text != NULL) + free(ar->k_ar.ar_arg_text, M_AUDITTEXT); + if (ar->k_udata != NULL) + free(ar->k_udata, M_AUDITDATA); + if (ar->k_ar.ar_arg_argv != NULL) + free(ar->k_ar.ar_arg_argv, M_AUDITTEXT); + if (ar->k_ar.ar_arg_envv != NULL) + free(ar->k_ar.ar_arg_envv, M_AUDITTEXT); +} + +/* + * Initialize the Audit subsystem: configuration state, work queue, + * synchronization primitives, worker thread, and trigger device node. Also + * call into the BSM assembly code to initialize it. + */ +static void +audit_init(void) +{ + + audit_enabled = 0; + audit_suspended = 0; + audit_panic_on_write_fail = 0; + audit_fail_stop = 0; + audit_in_failure = 0; + audit_argv = 0; + audit_arge = 0; + + audit_fstat.af_filesz = 0; /* '0' means unset, unbounded. */ + audit_fstat.af_currsz = 0; + audit_nae_mask.am_success = 0; + audit_nae_mask.am_failure = 0; + + TAILQ_INIT(&audit_q); + audit_q_len = 0; + audit_pre_q_len = 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_kinfo.ai_termid.at_type = AU_IPv4; + audit_kinfo.ai_termid.at_addr[0] = INADDR_ANY; + + mtx_init(&audit_mtx, "audit_mtx", NULL, MTX_DEF); + KINFO_LOCK_INIT(); + cv_init(&audit_worker_cv, "audit_worker_cv"); + cv_init(&audit_watermark_cv, "audit_watermark_cv"); + cv_init(&audit_fail_cv, "audit_fail_cv"); + + audit_record_zone = uma_zcreate("audit_record", + sizeof(struct kaudit_record), audit_record_ctor, + audit_record_dtor, NULL, NULL, UMA_ALIGN_PTR, 0); + + /* Initialize the BSM audit subsystem. */ + kau_init(); + + audit_trigger_init(); + + /* Register shutdown handler. */ + EVENTHANDLER_REGISTER(shutdown_pre_sync, audit_shutdown, NULL, + SHUTDOWN_PRI_FIRST); + + /* Start audit worker thread. */ + audit_worker_init(); +} + +SYSINIT(audit_init, SI_SUB_AUDIT, SI_ORDER_FIRST, audit_init, NULL); + +/* + * Drain the audit queue and close the log at shutdown. Note that this can + * be called both from the system shutdown path and also from audit + * configuration syscalls, so 'arg' and 'howto' are ignored. + * + * XXXRW: In FreeBSD 7.x and 8.x, this fails to wait for the record queue to + * drain before returning, which could lead to lost records on shutdown. + */ +void +audit_shutdown(void *arg, int howto) +{ + + audit_rotate_vnode(NULL, NULL); +} + +/* + * Return the current thread's audit record, if any. + */ +struct kaudit_record * +currecord(void) +{ + + return (curthread->td_ar); +} + +/* + * XXXAUDIT: There are a number of races present in the code below due to + * release and re-grab of the mutex. The code should be revised to become + * slightly less racy. + * + * XXXAUDIT: Shouldn't there be logic here to sleep waiting on available + * pre_q space, suspending the system call until there is room? + */ +struct kaudit_record * +audit_new(int event, struct thread *td) +{ + struct kaudit_record *ar; + int no_record; + + mtx_lock(&audit_mtx); + no_record = (audit_suspended || !audit_enabled); + mtx_unlock(&audit_mtx); + if (no_record) + return (NULL); + + /* + * Note: the number of outstanding uncommitted audit records is + * limited to the number of concurrent threads servicing system calls + * in the kernel. + */ + ar = uma_zalloc_arg(audit_record_zone, td, M_WAITOK); + ar->k_ar.ar_event = event; + + mtx_lock(&audit_mtx); + audit_pre_q_len++; + mtx_unlock(&audit_mtx); + + return (ar); +} + +void +audit_free(struct kaudit_record *ar) +{ + + uma_zfree(audit_record_zone, ar); +} + +void +audit_commit(struct kaudit_record *ar, int error, int retval) +{ + au_event_t event; + au_class_t class; + au_id_t auid; + int sorf; + struct au_mask *aumask; + + if (ar == NULL) + return; + + /* + * Decide whether to commit the audit record by checking the error + * value from the system call and using the appropriate audit mask. + */ + 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: + /* + * The open syscall always writes a AUE_OPEN_RWTC event; + * change it to the proper type of event based on the flags + * and the error value. + */ + ar->k_ar.ar_event = audit_flags_and_error_to_openevent( + ar->k_ar.ar_arg_fflags, error); + break; + + case AUE_SYSCTL: + ar->k_ar.ar_event = audit_ctlname_to_sysctlevent( + ar->k_ar.ar_arg_ctlname, ar->k_ar.ar_valid_arg); + break; + + case AUE_AUDITON: + /* Convert the auditon() command to an event. */ + ar->k_ar.ar_event = auditon_command_event(ar->k_ar.ar_arg_cmd); + break; + } + + auid = ar->k_ar.ar_subj_auid; + event = ar->k_ar.ar_event; + class = au_event_class(event); + + ar->k_ar_commit |= AR_COMMIT_KERNEL; + if (au_preselect(event, class, aumask, sorf) != 0) + ar->k_ar_commit |= AR_PRESELECT_TRAIL; + if (audit_pipe_preselect(auid, event, class, sorf, + ar->k_ar_commit & AR_PRESELECT_TRAIL) != 0) + ar->k_ar_commit |= AR_PRESELECT_PIPE; + if ((ar->k_ar_commit & (AR_PRESELECT_TRAIL | AR_PRESELECT_PIPE | + AR_PRESELECT_USER_TRAIL | AR_PRESELECT_USER_PIPE)) == 0) { + mtx_lock(&audit_mtx); + audit_pre_q_len--; + 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); + + /* + * Note: it could be that some records initiated while audit was + * enabled should still be committed? + */ + mtx_lock(&audit_mtx); + if (audit_suspended || !audit_enabled) { + audit_pre_q_len--; + mtx_unlock(&audit_mtx); + audit_free(ar); + return; + } + + /* + * Constrain the number of committed audit records based on the + * configurable parameter. + */ + while (audit_q_len >= audit_qctrl.aq_hiwater) + cv_wait(&audit_watermark_cv, &audit_mtx); + + TAILQ_INSERT_TAIL(&audit_q, ar, k_q); + audit_q_len++; + audit_pre_q_len--; + cv_signal(&audit_worker_cv); + mtx_unlock(&audit_mtx); +} + +/* + * audit_syscall_enter() is called on entry to each system call. It is + * responsible for deciding whether or not to audit the call (preselection), + * and if so, allocating a per-thread audit record. audit_new() will fill in + * basic thread/credential properties. + */ +void +audit_syscall_enter(unsigned short code, struct thread *td) +{ + struct au_mask *aumask; + au_class_t class; + au_event_t event; + au_id_t auid; + + KASSERT(td->td_ar == NULL, ("audit_syscall_enter: td->td_ar != NULL")); + + /* + * In FreeBSD, each ABI has its own system call table, and hence + * mapping of system call codes to audit events. Convert the code to + * an audit event identifier using the process system call table + * reference. In Darwin, there's only one, so we use the global + * symbol for the system call table. No audit record is generated + * for bad system calls, as no operation has been performed. + */ + if (code >= td->td_proc->p_sysent->sv_size) + return; + + event = td->td_proc->p_sysent->sv_table[code].sy_auevent; + if (event == AUE_NULL) + return; + + /* + * Check which audit mask to use; either the kernel non-attributable + * event mask or the process audit mask. + */ + auid = td->td_ucred->cr_audit.ai_auid; + if (auid == AU_DEFAUDITID) + aumask = &audit_nae_mask; + else + aumask = &td->td_ucred->cr_audit.ai_mask; + + /* + * Allocate an audit record, if preselection allows it, and store in + * the thread for later use. + */ + class = au_event_class(event); + if (au_preselect(event, class, aumask, AU_PRS_BOTH)) { + /* + * If we're out of space and need to suspend unprivileged + * processes, do that here rather than trying to allocate + * another audit record. + * + * Note: we might wish to be able to continue here in the + * future, if the system recovers. That should be possible + * by means of checking the condition in a loop around + * cv_wait(). It might be desirable to reevaluate whether an + * audit record is still required for this event by + * re-calling au_preselect(). + */ + if (audit_in_failure && + priv_check(td, PRIV_AUDIT_FAILSTOP) != 0) { + cv_wait(&audit_fail_cv, &audit_mtx); + panic("audit_failing_stop: thread continued"); + } + td->td_ar = audit_new(event, td); + } else if (audit_pipe_preselect(auid, event, class, AU_PRS_BOTH, 0)) + td->td_ar = audit_new(event, td); + else + td->td_ar = NULL; +} + +/* + * audit_syscall_exit() is called from the return of every system call, or in + * the event of exit1(), during the execution of exit1(). It is responsible + * for committing the audit record, if any, along with return condition. + */ +void +audit_syscall_exit(int error, struct thread *td) +{ + int retval; + + /* + * Commit the audit record as desired; once we pass the record into + * audit_commit(), the memory is owned by the audit subsystem. The + * return value from the system call is stored on the user thread. + * If there was an error, the return value is set to -1, imitating + * the behavior of the cerror routine. + */ + if (error) + retval = -1; + else + retval = td->td_retval[0]; + + audit_commit(td->td_ar, error, retval); + td->td_ar = NULL; +} + +void +audit_cred_copy(struct ucred *src, struct ucred *dest) +{ + + bcopy(&src->cr_audit, &dest->cr_audit, sizeof(dest->cr_audit)); +} + +void +audit_cred_destroy(struct ucred *cred) +{ + +} + +void +audit_cred_init(struct ucred *cred) +{ + + bzero(&cred->cr_audit, sizeof(cred->cr_audit)); +} + +/* + * Initialize audit information for the first kernel process (proc 0) and for + * the first user process (init). + */ +void +audit_cred_kproc0(struct ucred *cred) +{ + + cred->cr_audit.ai_auid = AU_DEFAUDITID; + cred->cr_audit.ai_termid.at_type = AU_IPv4; +} + +void +audit_cred_proc1(struct ucred *cred) +{ + + cred->cr_audit.ai_auid = AU_DEFAUDITID; + cred->cr_audit.ai_termid.at_type = AU_IPv4; +} + +void +audit_thread_alloc(struct thread *td) +{ + + td->td_ar = NULL; +} + +void +audit_thread_free(struct thread *td) +{ + + KASSERT(td->td_ar == NULL, ("audit_thread_free: td_ar != NULL")); +} + +void +audit_proc_coredump(struct thread *td, char *path, int errcode) +{ + struct kaudit_record *ar; + struct au_mask *aumask; + struct ucred *cred; + au_class_t class; + int ret, sorf; + char **pathp; + au_id_t auid; + + ret = 0; + + /* + * Make sure we are using the correct preselection mask. + */ + cred = td->td_ucred; + auid = cred->cr_audit.ai_auid; + if (auid == AU_DEFAUDITID) + aumask = &audit_nae_mask; + else + aumask = &cred->cr_audit.ai_mask; + /* + * It's possible for coredump(9) generation to fail. Make sure that + * we handle this case correctly for preselection. + */ + if (errcode != 0) + sorf = AU_PRS_FAILURE; + else + sorf = AU_PRS_SUCCESS; + class = au_event_class(AUE_CORE); + if (au_preselect(AUE_CORE, class, aumask, sorf) == 0 && + audit_pipe_preselect(auid, AUE_CORE, class, sorf, 0) == 0) + return; + + /* + * If we are interested in seeing this audit record, allocate it. + * Where possible coredump records should contain a pathname and arg32 + * (signal) tokens. + */ + ar = audit_new(AUE_CORE, td); + if (path != NULL) { + pathp = &ar->k_ar.ar_arg_upath1; + *pathp = malloc(MAXPATHLEN, M_AUDITPATH, M_WAITOK); + audit_canon_path(td, path, *pathp); + ARG_SET_VALID(ar, ARG_UPATH1); + } + ar->k_ar.ar_arg_signum = td->td_proc->p_sig; + ARG_SET_VALID(ar, ARG_SIGNUM); + if (errcode != 0) + ret = 1; + audit_commit(ar, errcode, ret); +} diff --git a/sys/security/audit/audit.h b/sys/security/audit/audit.h new file mode 100644 index 0000000..14dbcfd --- /dev/null +++ b/sys/security/audit/audit.h @@ -0,0 +1,233 @@ +/*- + * Copyright (c) 1999-2005 Apple Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * This header includes function prototypes and type definitions that are + * necessary for the kernel as a whole to interact with the audit subsystem. + */ + +#ifndef _SECURITY_AUDIT_KERNEL_H_ +#define _SECURITY_AUDIT_KERNEL_H_ + +#ifndef _KERNEL +#error "no user-serviceable parts inside" +#endif + +#include <bsm/audit.h> + +#include <sys/file.h> +#include <sys/sysctl.h> + +/* + * Audit subsystem condition flags. The audit_enabled flag is set and + * removed automatically as a result of configuring log files, and can be + * observed but should not be directly manipulated. The audit suspension + * flag permits audit to be temporarily disabled without reconfiguring the + * audit target. + */ +extern int audit_enabled; +extern int audit_suspended; + +/* + * Define the masks for the audited arguments. + * + * XXXRW: These need to remain in audit.h for now because our vnode and name + * lookup audit calls rely on passing in flags to indicate which name or + * vnode is being logged. These should move to audit_private.h when that is + * fixed. + */ +#define ARG_EUID 0x0000000000000001ULL +#define ARG_RUID 0x0000000000000002ULL +#define ARG_SUID 0x0000000000000004ULL +#define ARG_EGID 0x0000000000000008ULL +#define ARG_RGID 0x0000000000000010ULL +#define ARG_SGID 0x0000000000000020ULL +#define ARG_PID 0x0000000000000040ULL +#define ARG_UID 0x0000000000000080ULL +#define ARG_AUID 0x0000000000000100ULL +#define ARG_GID 0x0000000000000200ULL +#define ARG_FD 0x0000000000000400ULL +#define ARG_POSIX_IPC_PERM 0x0000000000000800ULL +#define ARG_FFLAGS 0x0000000000001000ULL +#define ARG_MODE 0x0000000000002000ULL +#define ARG_DEV 0x0000000000004000ULL +#define ARG_ADDR 0x0000000000008000ULL +#define ARG_LEN 0x0000000000010000ULL +#define ARG_MASK 0x0000000000020000ULL +#define ARG_SIGNUM 0x0000000000040000ULL +#define ARG_LOGIN 0x0000000000080000ULL +#define ARG_SADDRINET 0x0000000000100000ULL +#define ARG_SADDRINET6 0x0000000000200000ULL +#define ARG_SADDRUNIX 0x0000000000400000ULL +#define ARG_TERMID_ADDR 0x0000000000400000ULL +#define ARG_UNUSED2 0x0000000001000000ULL +#define ARG_UPATH1 0x0000000002000000ULL +#define ARG_UPATH2 0x0000000004000000ULL +#define ARG_TEXT 0x0000000008000000ULL +#define ARG_VNODE1 0x0000000010000000ULL +#define ARG_VNODE2 0x0000000020000000ULL +#define ARG_SVIPC_CMD 0x0000000040000000ULL +#define ARG_SVIPC_PERM 0x0000000080000000ULL +#define ARG_SVIPC_ID 0x0000000100000000ULL +#define ARG_SVIPC_ADDR 0x0000000200000000ULL +#define ARG_GROUPSET 0x0000000400000000ULL +#define ARG_CMD 0x0000000800000000ULL +#define ARG_SOCKINFO 0x0000001000000000ULL +#define ARG_ASID 0x0000002000000000ULL +#define ARG_TERMID 0x0000004000000000ULL +#define ARG_AUDITON 0x0000008000000000ULL +#define ARG_VALUE 0x0000010000000000ULL +#define ARG_AMASK 0x0000020000000000ULL +#define ARG_CTLNAME 0x0000040000000000ULL +#define ARG_PROCESS 0x0000080000000000ULL +#define ARG_MACHPORT1 0x0000100000000000ULL +#define ARG_MACHPORT2 0x0000200000000000ULL +#define ARG_EXIT 0x0000400000000000ULL +#define ARG_IOVECSTR 0x0000800000000000ULL +#define ARG_ARGV 0x0001000000000000ULL +#define ARG_ENVV 0x0002000000000000ULL +#define ARG_NONE 0x0000000000000000ULL +#define ARG_ALL 0xFFFFFFFFFFFFFFFFULL + +void audit_syscall_enter(unsigned short code, struct thread *td); +void audit_syscall_exit(int error, struct thread *td); + +/* + * The remaining kernel functions are conditionally compiled in as they are + * wrapped by a macro, and the macro should be the only place in the source + * tree where these functions are referenced. + */ +#ifdef AUDIT +struct ipc_perm; +struct sockaddr; +union auditon_udata; +void audit_arg_addr(void * addr); +void audit_arg_exit(int status, int retval); +void audit_arg_len(int len); +void audit_arg_fd(int fd); +void audit_arg_fflags(int fflags); +void audit_arg_gid(gid_t gid); +void audit_arg_uid(uid_t uid); +void audit_arg_egid(gid_t egid); +void audit_arg_euid(uid_t euid); +void audit_arg_rgid(gid_t rgid); +void audit_arg_ruid(uid_t ruid); +void audit_arg_sgid(gid_t sgid); +void audit_arg_suid(uid_t suid); +void audit_arg_groupset(gid_t *gidset, u_int gidset_size); +void audit_arg_login(char *login); +void audit_arg_ctlname(int *name, int namelen); +void audit_arg_mask(int mask); +void audit_arg_mode(mode_t mode); +void audit_arg_dev(int dev); +void audit_arg_value(long value); +void audit_arg_owner(uid_t uid, gid_t gid); +void audit_arg_pid(pid_t pid); +void audit_arg_process(struct proc *p); +void audit_arg_signum(u_int signum); +void audit_arg_socket(int sodomain, int sotype, int soprotocol); +void audit_arg_sockaddr(struct thread *td, struct sockaddr *sa); +void audit_arg_auid(uid_t auid); +void audit_arg_auditinfo(struct auditinfo *au_info); +void audit_arg_auditinfo_addr(struct auditinfo_addr *au_info); +void audit_arg_upath(struct thread *td, char *upath, u_int64_t flags); +void audit_arg_vnode(struct vnode *vp, u_int64_t flags); +void audit_arg_text(char *text); +void audit_arg_cmd(int cmd); +void audit_arg_svipc_cmd(int cmd); +void audit_arg_svipc_perm(struct ipc_perm *perm); +void audit_arg_svipc_id(int id); +void audit_arg_svipc_addr(void *addr); +void audit_arg_posix_ipc_perm(uid_t uid, gid_t gid, mode_t mode); +void audit_arg_auditon(union auditon_udata *udata); +void audit_arg_file(struct proc *p, struct file *fp); +void audit_arg_argv(char *argv, int argc, int length); +void audit_arg_envv(char *envv, int envc, int length); +void audit_sysclose(struct thread *td, int fd); +void audit_cred_copy(struct ucred *src, struct ucred *dest); +void audit_cred_destroy(struct ucred *cred); +void audit_cred_init(struct ucred *cred); +void audit_cred_kproc0(struct ucred *cred); +void audit_cred_proc1(struct ucred *cred); +void audit_proc_coredump(struct thread *td, char *path, int errcode); +void audit_thread_alloc(struct thread *td); +void audit_thread_free(struct thread *td); + +/* + * Define a macro to wrap the audit_arg_* calls by checking the global + * audit_enabled flag before performing the actual call. + */ +#define AUDIT_ARG(op, args...) do { \ + if (td->td_ar != NULL) \ + audit_arg_ ## op (args); \ +} while (0) + +#define AUDIT_SYSCALL_ENTER(code, td) do { \ + if (audit_enabled) { \ + audit_syscall_enter(code, td); \ + } \ +} while (0) + +/* + * Wrap the audit_syscall_exit() function so that it is called only when + * auditing is enabled, or we have a audit record on the thread. It is + * possible that an audit record was begun before auditing was turned off. + */ +#define AUDIT_SYSCALL_EXIT(error, td) do { \ + if (audit_enabled || (td->td_ar != NULL)) \ + audit_syscall_exit(error, td); \ +} while (0) + +/* + * A Macro to wrap the audit_sysclose() function. + */ +#define AUDIT_SYSCLOSE(td, fd) do { \ + if (audit_enabled) \ + audit_sysclose(td, fd); \ +} while (0) + +#else /* !AUDIT */ + +#define AUDIT_ARG(op, args...) do { \ +} while (0) + +#define AUDIT_SYSCALL_ENTER(code, td) do { \ +} while (0) + +#define AUDIT_SYSCALL_EXIT(error, td) do { \ +} while (0) + +#define AUDIT_SYSCLOSE(p, fd) do { \ +} while (0) + +#endif /* AUDIT */ + +#endif /* !_SECURITY_AUDIT_KERNEL_H_ */ diff --git a/sys/security/audit/audit_arg.c b/sys/security/audit/audit_arg.c new file mode 100644 index 0000000..2007041 --- /dev/null +++ b/sys/security/audit/audit_arg.c @@ -0,0 +1,859 @@ +/*- + * Copyright (c) 1999-2005 Apple Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/filedesc.h> +#include <sys/ipc.h> +#include <sys/mount.h> +#include <sys/proc.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/protosw.h> +#include <sys/domain.h> +#include <sys/sbuf.h> +#include <sys/systm.h> +#include <sys/un.h> +#include <sys/vnode.h> + +#include <netinet/in.h> +#include <netinet/in_pcb.h> + +#include <security/audit/audit.h> +#include <security/audit/audit_private.h> + +/* + * Calls to manipulate elements of the audit record structure from system + * call code. Macro wrappers will prevent this functions from being entered + * if auditing is disabled, avoiding the function call cost. We check the + * thread audit record pointer anyway, as the audit condition could change, + * and pre-selection may not have allocated an audit record for this event. + * + * XXXAUDIT: Should we assert, in each case, that this field of the record + * hasn't already been filled in? + */ +void +audit_arg_addr(void *addr) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_addr = addr; + ARG_SET_VALID(ar, ARG_ADDR); +} + +void +audit_arg_exit(int status, int retval) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_exitstatus = status; + ar->k_ar.ar_arg_exitretval = retval; + ARG_SET_VALID(ar, ARG_EXIT); +} + +void +audit_arg_len(int len) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_len = len; + ARG_SET_VALID(ar, 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; + ARG_SET_VALID(ar, 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; + ARG_SET_VALID(ar, ARG_FFLAGS); +} + +void +audit_arg_gid(gid_t gid) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_gid = gid; + ARG_SET_VALID(ar, ARG_GID); +} + +void +audit_arg_uid(uid_t uid) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_uid = uid; + ARG_SET_VALID(ar, ARG_UID); +} + +void +audit_arg_egid(gid_t egid) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_egid = egid; + ARG_SET_VALID(ar, ARG_EGID); +} + +void +audit_arg_euid(uid_t euid) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_euid = euid; + ARG_SET_VALID(ar, ARG_EUID); +} + +void +audit_arg_rgid(gid_t rgid) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_rgid = rgid; + ARG_SET_VALID(ar, ARG_RGID); +} + +void +audit_arg_ruid(uid_t ruid) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_ruid = ruid; + ARG_SET_VALID(ar, ARG_RUID); +} + +void +audit_arg_sgid(gid_t sgid) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_sgid = sgid; + ARG_SET_VALID(ar, ARG_SGID); +} + +void +audit_arg_suid(uid_t suid) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_suid = suid; + ARG_SET_VALID(ar, ARG_SUID); +} + +void +audit_arg_groupset(gid_t *gidset, u_int gidset_size) +{ + u_int 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; + ARG_SET_VALID(ar, ARG_GROUPSET); +} + +void +audit_arg_login(char *login) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + strlcpy(ar->k_ar.ar_arg_login, login, MAXLOGNAME); + ARG_SET_VALID(ar, ARG_LOGIN); +} + +void +audit_arg_ctlname(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; + ARG_SET_VALID(ar, 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; + ARG_SET_VALID(ar, 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; + ARG_SET_VALID(ar, 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; + ARG_SET_VALID(ar, 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; + ARG_SET_VALID(ar, 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; + ARG_SET_VALID(ar, 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; + ARG_SET_VALID(ar, ARG_PID); +} + +void +audit_arg_process(struct proc *p) +{ + struct kaudit_record *ar; + struct ucred *cred; + + KASSERT(p != NULL, ("audit_arg_process: p == NULL")); + + PROC_LOCK_ASSERT(p, MA_OWNED); + + ar = currecord(); + if (ar == NULL) + return; + + cred = p->p_ucred; + ar->k_ar.ar_arg_auid = cred->cr_audit.ai_auid; + ar->k_ar.ar_arg_euid = cred->cr_uid; + ar->k_ar.ar_arg_egid = cred->cr_groups[0]; + ar->k_ar.ar_arg_ruid = cred->cr_ruid; + ar->k_ar.ar_arg_rgid = cred->cr_rgid; + ar->k_ar.ar_arg_asid = cred->cr_audit.ai_asid; + ar->k_ar.ar_arg_termid_addr = cred->cr_audit.ai_termid; + ar->k_ar.ar_arg_pid = p->p_pid; + ARG_SET_VALID(ar, ARG_AUID | ARG_EUID | ARG_EGID | ARG_RUID | + ARG_RGID | ARG_ASID | ARG_TERMID_ADDR | ARG_PID | 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; + ARG_SET_VALID(ar, 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; + ARG_SET_VALID(ar, ARG_SOCKINFO); +} + +void +audit_arg_sockaddr(struct thread *td, struct sockaddr *sa) +{ + struct kaudit_record *ar; + + KASSERT(td != NULL, ("audit_arg_sockaddr: td == NULL")); + KASSERT(sa != NULL, ("audit_arg_sockaddr: sa == NULL")); + + ar = currecord(); + if (ar == NULL) + return; + + bcopy(sa, &ar->k_ar.ar_arg_sockaddr, sa->sa_len); + switch (sa->sa_family) { + case AF_INET: + ARG_SET_VALID(ar, ARG_SADDRINET); + break; + + case AF_INET6: + ARG_SET_VALID(ar, ARG_SADDRINET6); + break; + + case AF_UNIX: + audit_arg_upath(td, ((struct sockaddr_un *)sa)->sun_path, + ARG_UPATH1); + ARG_SET_VALID(ar, ARG_SADDRUNIX); + break; + /* XXXAUDIT: default:? */ + } +} + +void +audit_arg_auid(uid_t auid) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_auid = auid; + ARG_SET_VALID(ar, ARG_AUID); +} + +void +audit_arg_auditinfo(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; + ARG_SET_VALID(ar, ARG_AUID | ARG_ASID | ARG_AMASK | ARG_TERMID); +} + +void +audit_arg_auditinfo_addr(struct auditinfo_addr *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_addr.at_type = au_info->ai_termid.at_type; + ar->k_ar.ar_arg_termid_addr.at_port = au_info->ai_termid.at_port; + ar->k_ar.ar_arg_termid_addr.at_addr[0] = au_info->ai_termid.at_addr[0]; + ar->k_ar.ar_arg_termid_addr.at_addr[1] = au_info->ai_termid.at_addr[1]; + ar->k_ar.ar_arg_termid_addr.at_addr[2] = au_info->ai_termid.at_addr[2]; + ar->k_ar.ar_arg_termid_addr.at_addr[3] = au_info->ai_termid.at_addr[3]; + ARG_SET_VALID(ar, ARG_AUID | ARG_ASID | ARG_AMASK | ARG_TERMID_ADDR); +} + +void +audit_arg_text(char *text) +{ + struct kaudit_record *ar; + + KASSERT(text != NULL, ("audit_arg_text: text == NULL")); + + ar = currecord(); + if (ar == NULL) + return; + + /* Invalidate the text string */ + ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_TEXT); + + if (ar->k_ar.ar_arg_text == NULL) + ar->k_ar.ar_arg_text = malloc(MAXPATHLEN, M_AUDITTEXT, + M_WAITOK); + + strncpy(ar->k_ar.ar_arg_text, text, MAXPATHLEN); + ARG_SET_VALID(ar, 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; + ARG_SET_VALID(ar, 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; + ARG_SET_VALID(ar, ARG_SVIPC_CMD); +} + +void +audit_arg_svipc_perm(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)); + ARG_SET_VALID(ar, 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; + ARG_SET_VALID(ar, ARG_SVIPC_ID); +} + +void +audit_arg_svipc_addr(void * addr) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_svipc_addr = addr; + ARG_SET_VALID(ar, 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; + ARG_SET_VALID(ar, ARG_POSIX_IPC_PERM); +} + +void +audit_arg_auditon(union auditon_udata *udata) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + bcopy((void *)udata, &ar->k_ar.ar_arg_auditon, + sizeof(ar->k_ar.ar_arg_auditon)); + ARG_SET_VALID(ar, ARG_AUDITON); +} + +/* + * Audit information about a file, either the file's vnode info, or its + * socket address info. + */ +void +audit_arg_file(struct proc *p, struct file *fp) +{ + struct kaudit_record *ar; + struct socket *so; + struct inpcb *pcb; + struct vnode *vp; + int vfslocked; + + ar = currecord(); + if (ar == NULL) + return; + + switch (fp->f_type) { + case DTYPE_VNODE: + case DTYPE_FIFO: + /* + * XXXAUDIT: Only possibly to record as first vnode? + */ + vp = fp->f_vnode; + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + vn_lock(vp, LK_SHARED | LK_RETRY); + audit_arg_vnode(vp, ARG_VNODE1); + VOP_UNLOCK(vp, 0); + VFS_UNLOCK_GIANT(vfslocked); + break; + + case DTYPE_SOCKET: + so = (struct socket *)fp->f_data; + if (INP_CHECK_SOCKAF(so, PF_INET)) { + SOCK_LOCK(so); + 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; + SOCK_UNLOCK(so); + pcb = (struct inpcb *)so->so_pcb; + INP_RLOCK(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; + INP_RUNLOCK(pcb); + ARG_SET_VALID(ar, ARG_SOCKINFO); + } + break; + + default: + /* XXXAUDIT: else? */ + break; + } +} + +/* + * Store a path as given by the user process for auditing into the audit + * record stored on the user thread. This function will allocate the memory + * to store the path info if not already available. This memory will be + * freed when the audit record is freed. + * + * XXXAUDIT: Possibly assert that the memory isn't already allocated? + */ +void +audit_arg_upath(struct thread *td, char *upath, u_int64_t flag) +{ + struct kaudit_record *ar; + char **pathp; + + KASSERT(td != NULL, ("audit_arg_upath: td == NULL")); + KASSERT(upath != NULL, ("audit_arg_upath: upath == NULL")); + + ar = currecord(); + if (ar == NULL) + return; + + KASSERT((flag == ARG_UPATH1) || (flag == ARG_UPATH2), + ("audit_arg_upath: flag %llu", (unsigned long long)flag)); + KASSERT((flag != ARG_UPATH1) || (flag != ARG_UPATH2), + ("audit_arg_upath: flag %llu", (unsigned long long)flag)); + + if (flag == ARG_UPATH1) + pathp = &ar->k_ar.ar_arg_upath1; + else + pathp = &ar->k_ar.ar_arg_upath2; + + if (*pathp == NULL) + *pathp = malloc(MAXPATHLEN, M_AUDITPATH, M_WAITOK); + + audit_canon_path(td, upath, *pathp); + + ARG_SET_VALID(ar, flag); +} + +/* + * Function to save the path and vnode attr information into the audit + * record. + * + * It is assumed that the caller will hold any vnode locks necessary to + * perform a VOP_GETATTR() on the passed vnode. + * + * XXX: The attr code is very similar to vfs_vnops.c:vn_stat(), but always + * provides access to the generation number as we need that to construct the + * BSM file ID. + * + * XXX: We should accept the process argument from the caller, since it's + * very likely they already have a reference. + * + * XXX: Error handling in this function is poor. + * + * XXXAUDIT: Possibly KASSERT the path pointer is NULL? + */ +void +audit_arg_vnode(struct vnode *vp, u_int64_t flags) +{ + struct kaudit_record *ar; + struct vattr vattr; + int error; + struct vnode_au_info *vnp; + + KASSERT(vp != NULL, ("audit_arg_vnode: vp == NULL")); + KASSERT((flags == ARG_VNODE1) || (flags == ARG_VNODE2), + ("audit_arg_vnode: flags %jd", (intmax_t)flags)); + + /* + * Assume that if the caller is calling audit_arg_vnode() on a + * non-MPSAFE vnode, then it will have acquired Giant. + */ + VFS_ASSERT_GIANT(vp->v_mount); + ASSERT_VOP_LOCKED(vp, "audit_arg_vnode"); + + ar = currecord(); + if (ar == NULL) + return; + + /* + * XXXAUDIT: The below clears, and then resets the flags for valid + * arguments. Ideally, either the new vnode is used, or the old one + * would be. + */ + if (flags & ARG_VNODE1) { + ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_VNODE1); + vnp = &ar->k_ar.ar_arg_vnode1; + } else { + ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_VNODE2); + vnp = &ar->k_ar.ar_arg_vnode2; + } + + error = VOP_GETATTR(vp, &vattr, curthread->td_ucred); + if (error) { + /* XXX: How to handle this case? */ + return; + } + + vnp->vn_mode = vattr.va_mode; + vnp->vn_uid = vattr.va_uid; + vnp->vn_gid = vattr.va_gid; + vnp->vn_dev = vattr.va_rdev; + vnp->vn_fsid = vattr.va_fsid; + vnp->vn_fileid = vattr.va_fileid; + vnp->vn_gen = vattr.va_gen; + if (flags & ARG_VNODE1) + ARG_SET_VALID(ar, ARG_VNODE1); + else + ARG_SET_VALID(ar, ARG_VNODE2); +} + +/* + * Audit the argument strings passed to exec. + */ +void +audit_arg_argv(char *argv, int argc, int length) +{ + struct kaudit_record *ar; + + if (audit_argv == 0) + return; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_argv = malloc(length, M_AUDITTEXT, M_WAITOK); + bcopy(argv, ar->k_ar.ar_arg_argv, length); + ar->k_ar.ar_arg_argc = argc; + ARG_SET_VALID(ar, ARG_ARGV); +} + +/* + * Audit the environment strings passed to exec. + */ +void +audit_arg_envv(char *envv, int envc, int length) +{ + struct kaudit_record *ar; + + if (audit_arge == 0) + return; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_envv = malloc(length, M_AUDITTEXT, M_WAITOK); + bcopy(envv, ar->k_ar.ar_arg_envv, length); + ar->k_ar.ar_arg_envc = envc; + ARG_SET_VALID(ar, ARG_ENVV); +} + +/* + * The close() system call uses it's own audit call to capture the path/vnode + * information because those pieces are not easily obtained within the system + * call itself. + */ +void +audit_sysclose(struct thread *td, int fd) +{ + struct kaudit_record *ar; + struct vnode *vp; + struct file *fp; + int vfslocked; + + KASSERT(td != NULL, ("audit_sysclose: td == NULL")); + + ar = currecord(); + if (ar == NULL) + return; + + audit_arg_fd(fd); + + if (getvnode(td->td_proc->p_fd, fd, &fp) != 0) + return; + + vp = fp->f_vnode; + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + vn_lock(vp, LK_SHARED | LK_RETRY); + audit_arg_vnode(vp, ARG_VNODE1); + VOP_UNLOCK(vp, 0); + VFS_UNLOCK_GIANT(vfslocked); + fdrop(fp, td); +} diff --git a/sys/security/audit/audit_bsm.c b/sys/security/audit/audit_bsm.c new file mode 100644 index 0000000..1ddb8be --- /dev/null +++ b/sys/security/audit/audit_bsm.c @@ -0,0 +1,1495 @@ +/* + * Copyright (c) 1999-2005 Apple Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/vnode.h> +#include <sys/ipc.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/socket.h> +#include <sys/extattr.h> +#include <sys/fcntl.h> +#include <sys/user.h> +#include <sys/systm.h> + +#include <bsm/audit.h> +#include <bsm/audit_internal.h> +#include <bsm/audit_record.h> +#include <bsm/audit_kevents.h> + +#include <security/audit/audit.h> +#include <security/audit/audit_private.h> + +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> + +MALLOC_DEFINE(M_AUDITBSM, "audit_bsm", "Audit BSM data"); + +static void audit_sys_auditon(struct audit_record *ar, + struct au_record *rec); + +/* + * Initialize the BSM auditing subsystem. + */ +void +kau_init(void) +{ + + au_evclassmap_init(); +} + +/* + * This call reserves memory for the audit record. Memory must be guaranteed + * before any auditable event can be generated. The au_record structure + * maintains a reference to the memory allocated above and also the list of + * tokens associated with this record. + */ +static struct au_record * +kau_open(void) +{ + struct au_record *rec; + + rec = malloc(sizeof(*rec), M_AUDITBSM, M_WAITOK); + rec->data = NULL; + TAILQ_INIT(&rec->token_q); + rec->len = 0; + rec->used = 1; + + return (rec); +} + +/* + * Store the token with the record descriptor. + */ +static void +kau_write(struct au_record *rec, struct au_token *tok) +{ + + KASSERT(tok != NULL, ("kau_write: tok == NULL")); + + TAILQ_INSERT_TAIL(&rec->token_q, tok, tokens); + rec->len += tok->len; +} + +/* + * Close out the audit record by adding the header token, identifying any + * missing tokens. Write out the tokens to the record memory. + */ +static void +kau_close(struct au_record *rec, struct timespec *ctime, short event) +{ + u_char *dptr; + size_t tot_rec_size; + token_t *cur, *hdr, *trail; + struct timeval tm; + size_t hdrsize; + struct auditinfo_addr ak; + struct in6_addr *ap; + + audit_get_kinfo(&ak); + hdrsize = 0; + switch (ak.ai_termid.at_type) { + case AU_IPv4: + hdrsize = (ak.ai_termid.at_addr[0] == INADDR_ANY) ? + AUDIT_HEADER_SIZE : AUDIT_HEADER_EX_SIZE(&ak); + break; + case AU_IPv6: + ap = (struct in6_addr *)&ak.ai_termid.at_addr[0]; + hdrsize = (IN6_IS_ADDR_UNSPECIFIED(ap)) ? AUDIT_HEADER_SIZE : + AUDIT_HEADER_EX_SIZE(&ak); + break; + default: + panic("kau_close: invalid address family"); + } + tot_rec_size = rec->len + hdrsize + AUDIT_TRAILER_SIZE; + rec->data = malloc(tot_rec_size, M_AUDITBSM, M_WAITOK | M_ZERO); + + tm.tv_usec = ctime->tv_nsec / 1000; + tm.tv_sec = ctime->tv_sec; + if (hdrsize != AUDIT_HEADER_SIZE) + hdr = au_to_header32_ex_tm(tot_rec_size, event, 0, tm, &ak); + else + hdr = au_to_header32_tm(tot_rec_size, event, 0, tm); + TAILQ_INSERT_HEAD(&rec->token_q, hdr, tokens); + + trail = au_to_trailer(tot_rec_size); + TAILQ_INSERT_TAIL(&rec->token_q, trail, tokens); + + rec->len = tot_rec_size; + dptr = rec->data; + TAILQ_FOREACH(cur, &rec->token_q, tokens) { + memcpy(dptr, cur->t_data, cur->len); + dptr += cur->len; + } +} + +/* + * Free a BSM audit record by releasing all the tokens and clearing the audit + * record information. + */ +void +kau_free(struct au_record *rec) +{ + struct au_token *tok; + + /* Free the token list. */ + while ((tok = TAILQ_FIRST(&rec->token_q))) { + TAILQ_REMOVE(&rec->token_q, tok, tokens); + free(tok->t_data, M_AUDITBSM); + free(tok, M_AUDITBSM); + } + + rec->used = 0; + rec->len = 0; + free(rec->data, M_AUDITBSM); + free(rec, M_AUDITBSM); +} + +/* + * XXX: May want turn some (or all) of these macros into functions in order + * to reduce the generated code size. + * + * XXXAUDIT: These macros assume that 'kar', 'ar', 'rec', and 'tok' in the + * caller are OK with this. + */ +#define UPATH1_TOKENS do { \ + if (ARG_IS_VALID(kar, ARG_UPATH1)) { \ + tok = au_to_path(ar->ar_arg_upath1); \ + kau_write(rec, tok); \ + } \ +} while (0) + +#define UPATH2_TOKENS do { \ + if (ARG_IS_VALID(kar, ARG_UPATH2)) { \ + tok = au_to_path(ar->ar_arg_upath2); \ + kau_write(rec, tok); \ + } \ +} while (0) + +#define VNODE1_TOKENS do { \ + if (ARG_IS_VALID(kar, ARG_VNODE1)) { \ + tok = au_to_attr32(&ar->ar_arg_vnode1); \ + kau_write(rec, tok); \ + } \ +} while (0) + +#define UPATH1_VNODE1_TOKENS do { \ + if (ARG_IS_VALID(kar, ARG_UPATH1)) { \ + UPATH1_TOKENS; \ + } \ + if (ARG_IS_VALID(kar, ARG_VNODE1)) { \ + tok = au_to_attr32(&ar->ar_arg_vnode1); \ + kau_write(rec, tok); \ + } \ +} while (0) + +#define VNODE2_TOKENS do { \ + if (ARG_IS_VALID(kar, ARG_VNODE2)) { \ + tok = au_to_attr32(&ar->ar_arg_vnode2); \ + kau_write(rec, tok); \ + } \ +} while (0) + +#define FD_VNODE1_TOKENS do { \ + if (ARG_IS_VALID(kar, ARG_VNODE1)) { \ + if (ARG_IS_VALID(kar, ARG_FD)) { \ + tok = au_to_arg32(1, "fd", ar->ar_arg_fd); \ + kau_write(rec, tok); \ + } \ + tok = au_to_attr32(&ar->ar_arg_vnode1); \ + kau_write(rec, tok); \ + } else { \ + if (ARG_IS_VALID(kar, ARG_FD)) { \ + tok = au_to_arg32(1, "non-file: fd", \ + ar->ar_arg_fd); \ + kau_write(rec, tok); \ + } \ + } \ +} while (0) + +#define PROCESS_PID_TOKENS(argn) do { \ + if ((ar->ar_arg_pid > 0) /* Reference a single process */ \ + && (ARG_IS_VALID(kar, ARG_PROCESS))) { \ + tok = au_to_process32_ex(ar->ar_arg_auid, \ + ar->ar_arg_euid, ar->ar_arg_egid, \ + ar->ar_arg_ruid, ar->ar_arg_rgid, \ + ar->ar_arg_pid, ar->ar_arg_asid, \ + &ar->ar_arg_termid_addr); \ + kau_write(rec, tok); \ + } else if (ARG_IS_VALID(kar, ARG_PID)) { \ + tok = au_to_arg32(argn, "process", ar->ar_arg_pid); \ + kau_write(rec, tok); \ + } \ +} while (0) + +#define EXTATTR_TOKENS do { \ + if (ARG_IS_VALID(kar, ARG_VALUE)) { \ + switch (ar->ar_arg_value) { \ + case EXTATTR_NAMESPACE_USER: \ + tok = au_to_text(EXTATTR_NAMESPACE_USER_STRING);\ + break; \ + case EXTATTR_NAMESPACE_SYSTEM: \ + tok = au_to_text(EXTATTR_NAMESPACE_SYSTEM_STRING);\ + break; \ + default: \ + tok = au_to_arg32(3, "attrnamespace", \ + ar->ar_arg_value); \ + break; \ + } \ + kau_write(rec, tok); \ + } \ + /* attrname is in the text field */ \ + if (ARG_IS_VALID(kar, ARG_TEXT)) { \ + tok = au_to_text(ar->ar_arg_text); \ + kau_write(rec, tok); \ + } \ +} while (0) + +/* + * Implement auditing for the auditon() system call. The audit tokens that + * are generated depend on the command that was sent into the auditon() + * system call. + */ +static void +audit_sys_auditon(struct audit_record *ar, struct au_record *rec) +{ + struct au_token *tok; + + switch (ar->ar_arg_cmd) { + case A_SETPOLICY: + if (sizeof(ar->ar_arg_auditon.au_flags) > 4) + tok = au_to_arg64(1, "policy", + ar->ar_arg_auditon.au_flags); + else + tok = au_to_arg32(1, "policy", + ar->ar_arg_auditon.au_flags); + kau_write(rec, tok); + break; + + case A_SETKMASK: + tok = au_to_arg32(2, "setkmask:as_success", + ar->ar_arg_auditon.au_mask.am_success); + kau_write(rec, tok); + tok = au_to_arg32(2, "setkmask:as_failure", + ar->ar_arg_auditon.au_mask.am_failure); + kau_write(rec, tok); + break; + + case A_SETQCTRL: + tok = au_to_arg32(3, "setqctrl:aq_hiwater", + ar->ar_arg_auditon.au_qctrl.aq_hiwater); + kau_write(rec, tok); + tok = au_to_arg32(3, "setqctrl:aq_lowater", + ar->ar_arg_auditon.au_qctrl.aq_lowater); + kau_write(rec, tok); + tok = au_to_arg32(3, "setqctrl:aq_bufsz", + ar->ar_arg_auditon.au_qctrl.aq_bufsz); + kau_write(rec, tok); + tok = au_to_arg32(3, "setqctrl:aq_delay", + ar->ar_arg_auditon.au_qctrl.aq_delay); + kau_write(rec, tok); + tok = au_to_arg32(3, "setqctrl:aq_minfree", + ar->ar_arg_auditon.au_qctrl.aq_minfree); + kau_write(rec, tok); + break; + + case A_SETUMASK: + tok = au_to_arg32(3, "setumask:as_success", + ar->ar_arg_auditon.au_auinfo.ai_mask.am_success); + kau_write(rec, tok); + tok = au_to_arg32(3, "setumask:as_failure", + ar->ar_arg_auditon.au_auinfo.ai_mask.am_failure); + kau_write(rec, tok); + break; + + case A_SETSMASK: + tok = au_to_arg32(3, "setsmask:as_success", + ar->ar_arg_auditon.au_auinfo.ai_mask.am_success); + kau_write(rec, tok); + tok = au_to_arg32(3, "setsmask:as_failure", + ar->ar_arg_auditon.au_auinfo.ai_mask.am_failure); + kau_write(rec, tok); + break; + + case A_SETCOND: + if (sizeof(ar->ar_arg_auditon.au_cond) > 4) + tok = au_to_arg64(3, "setcond", + ar->ar_arg_auditon.au_cond); + else + tok = au_to_arg32(3, "setcond", + ar->ar_arg_auditon.au_cond); + kau_write(rec, tok); + break; + + case A_SETCLASS: + tok = au_to_arg32(2, "setclass:ec_event", + ar->ar_arg_auditon.au_evclass.ec_number); + kau_write(rec, tok); + tok = au_to_arg32(3, "setclass:ec_class", + ar->ar_arg_auditon.au_evclass.ec_class); + kau_write(rec, tok); + break; + + case A_SETPMASK: + tok = au_to_arg32(2, "setpmask:as_success", + ar->ar_arg_auditon.au_aupinfo.ap_mask.am_success); + kau_write(rec, tok); + tok = au_to_arg32(2, "setpmask:as_failure", + ar->ar_arg_auditon.au_aupinfo.ap_mask.am_failure); + kau_write(rec, tok); + break; + + case A_SETFSIZE: + tok = au_to_arg32(2, "setfsize:filesize", + ar->ar_arg_auditon.au_fstat.af_filesz); + kau_write(rec, tok); + break; + + default: + break; + } +} + +/* + * Convert an internal kernel audit record to a BSM record and return a + * success/failure indicator. The BSM record is passed as an out parameter to + * this function. + * + * Return conditions: + * BSM_SUCCESS: The BSM record is valid + * BSM_FAILURE: Failure; the BSM record is NULL. + * BSM_NOAUDIT: The event is not auditable for BSM; the BSM record is NULL. + */ +int +kaudit_to_bsm(struct kaudit_record *kar, struct au_record **pau) +{ + struct au_token *tok, *subj_tok; + struct au_record *rec; + au_tid_t tid; + struct audit_record *ar; + int ctr; + + KASSERT(kar != NULL, ("kaudit_to_bsm: kar == NULL")); + + *pau = NULL; + ar = &kar->k_ar; + rec = kau_open(); + + /* + * Create the subject token. + */ + switch (ar->ar_subj_term_addr.at_type) { + case AU_IPv4: + tid.port = ar->ar_subj_term_addr.at_port; + tid.machine = ar->ar_subj_term_addr.at_addr[0]; + subj_tok = au_to_subject32(ar->ar_subj_auid, /* audit ID */ + ar->ar_subj_cred.cr_uid, /* eff uid */ + ar->ar_subj_egid, /* eff group id */ + ar->ar_subj_ruid, /* real uid */ + ar->ar_subj_rgid, /* real group id */ + ar->ar_subj_pid, /* process id */ + ar->ar_subj_asid, /* session ID */ + &tid); + break; + case AU_IPv6: + subj_tok = au_to_subject32_ex(ar->ar_subj_auid, + ar->ar_subj_cred.cr_uid, + ar->ar_subj_egid, + ar->ar_subj_ruid, + ar->ar_subj_rgid, + ar->ar_subj_pid, + ar->ar_subj_asid, + &ar->ar_subj_term_addr); + break; + default: + bzero(&tid, sizeof(tid)); + subj_tok = au_to_subject32(ar->ar_subj_auid, + ar->ar_subj_cred.cr_uid, + ar->ar_subj_egid, + ar->ar_subj_ruid, + ar->ar_subj_rgid, + ar->ar_subj_pid, + ar->ar_subj_asid, + &tid); + } + + /* + * The logic inside each case fills in the tokens required for the + * event, except for the header, trailer, and return tokens. The + * header and trailer tokens are added by the kau_close() function. + * The return token is added outside of the switch statement. + */ + switch(ar->ar_event) { + case AUE_ACCEPT: + case AUE_BIND: + case AUE_LISTEN: + case AUE_CONNECT: + case AUE_RECV: + case AUE_RECVFROM: + case AUE_RECVMSG: + case AUE_SEND: + case AUE_SENDFILE: + case AUE_SENDMSG: + case AUE_SENDTO: + /* + * Socket-related events. + */ + if (ARG_IS_VALID(kar, ARG_FD)) { + tok = au_to_arg32(1, "fd", ar->ar_arg_fd); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_SADDRINET)) { + tok = au_to_sock_inet((struct sockaddr_in *) + &ar->ar_arg_sockaddr); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_SADDRUNIX)) { + tok = au_to_sock_unix((struct sockaddr_un *) + &ar->ar_arg_sockaddr); + kau_write(rec, tok); + UPATH1_TOKENS; + } + /* XXX Need to handle ARG_SADDRINET6 */ + break; + + case AUE_SOCKET: + case AUE_SOCKETPAIR: + if (ARG_IS_VALID(kar, ARG_SOCKINFO)) { + tok = au_to_arg32(1,"domain", + ar->ar_arg_sockinfo.so_domain); + kau_write(rec, tok); + tok = au_to_arg32(2,"type", + ar->ar_arg_sockinfo.so_type); + kau_write(rec, tok); + tok = au_to_arg32(3,"protocol", + ar->ar_arg_sockinfo.so_protocol); + kau_write(rec, tok); + } + break; + + case AUE_SETSOCKOPT: + case AUE_SHUTDOWN: + if (ARG_IS_VALID(kar, ARG_FD)) { + tok = au_to_arg32(1, "fd", ar->ar_arg_fd); + kau_write(rec, tok); + } + break; + + case AUE_ACCT: + if (ARG_IS_VALID(kar, ARG_UPATH1)) { + UPATH1_VNODE1_TOKENS; + } else { + tok = au_to_arg32(1, "accounting off", 0); + kau_write(rec, tok); + } + break; + + case AUE_SETAUID: + if (ARG_IS_VALID(kar, ARG_AUID)) { + tok = au_to_arg32(2, "setauid", ar->ar_arg_auid); + kau_write(rec, tok); + } + break; + + case AUE_SETAUDIT: + if (ARG_IS_VALID(kar, ARG_AUID) && + ARG_IS_VALID(kar, ARG_ASID) && + ARG_IS_VALID(kar, ARG_AMASK) && + ARG_IS_VALID(kar, ARG_TERMID)) { + tok = au_to_arg32(1, "setaudit:auid", + ar->ar_arg_auid); + kau_write(rec, tok); + tok = au_to_arg32(1, "setaudit:port", + ar->ar_arg_termid.port); + kau_write(rec, tok); + tok = au_to_arg32(1, "setaudit:machine", + ar->ar_arg_termid.machine); + kau_write(rec, tok); + tok = au_to_arg32(1, "setaudit:as_success", + ar->ar_arg_amask.am_success); + kau_write(rec, tok); + tok = au_to_arg32(1, "setaudit:as_failure", + ar->ar_arg_amask.am_failure); + kau_write(rec, tok); + tok = au_to_arg32(1, "setaudit:asid", + ar->ar_arg_asid); + kau_write(rec, tok); + } + break; + + case AUE_SETAUDIT_ADDR: + if (ARG_IS_VALID(kar, ARG_AUID) && + ARG_IS_VALID(kar, ARG_ASID) && + ARG_IS_VALID(kar, ARG_AMASK) && + ARG_IS_VALID(kar, ARG_TERMID_ADDR)) { + tok = au_to_arg32(1, "setaudit_addr:auid", + ar->ar_arg_auid); + kau_write(rec, tok); + tok = au_to_arg32(1, "setaudit_addr:as_success", + ar->ar_arg_amask.am_success); + kau_write(rec, tok); + tok = au_to_arg32(1, "setaudit_addr:as_failure", + ar->ar_arg_amask.am_failure); + kau_write(rec, tok); + tok = au_to_arg32(1, "setaudit_addr:asid", + ar->ar_arg_asid); + kau_write(rec, tok); + tok = au_to_arg32(1, "setaudit_addr:type", + ar->ar_arg_termid_addr.at_type); + kau_write(rec, tok); + tok = au_to_arg32(1, "setaudit_addr:port", + ar->ar_arg_termid_addr.at_port); + kau_write(rec, tok); + if (ar->ar_arg_termid_addr.at_type == AU_IPv6) + tok = au_to_in_addr_ex((struct in6_addr *) + &ar->ar_arg_termid_addr.at_addr[0]); + if (ar->ar_arg_termid_addr.at_type == AU_IPv4) + tok = au_to_in_addr((struct in_addr *) + &ar->ar_arg_termid_addr.at_addr[0]); + kau_write(rec, tok); + } + break; + + case AUE_AUDITON: + /* + * For AUDITON commands without own event, audit the cmd. + */ + if (ARG_IS_VALID(kar, ARG_CMD)) { + tok = au_to_arg32(1, "cmd", ar->ar_arg_cmd); + kau_write(rec, tok); + } + /* FALLTHROUGH */ + + case AUE_AUDITON_GETCAR: + case AUE_AUDITON_GETCLASS: + case AUE_AUDITON_GETCOND: + case AUE_AUDITON_GETCWD: + case AUE_AUDITON_GETKMASK: + case AUE_AUDITON_GETSTAT: + case AUE_AUDITON_GPOLICY: + case AUE_AUDITON_GQCTRL: + case AUE_AUDITON_SETCLASS: + case AUE_AUDITON_SETCOND: + case AUE_AUDITON_SETKMASK: + case AUE_AUDITON_SETSMASK: + case AUE_AUDITON_SETSTAT: + case AUE_AUDITON_SETUMASK: + case AUE_AUDITON_SPOLICY: + case AUE_AUDITON_SQCTRL: + if (ARG_IS_VALID(kar, ARG_AUDITON)) + audit_sys_auditon(ar, rec); + break; + + case AUE_AUDITCTL: + UPATH1_VNODE1_TOKENS; + break; + + case AUE_EXIT: + if (ARG_IS_VALID(kar, ARG_EXIT)) { + tok = au_to_exit(ar->ar_arg_exitretval, + ar->ar_arg_exitstatus); + kau_write(rec, tok); + } + break; + + case AUE_ADJTIME: + case AUE_CLOCK_SETTIME: + case AUE_AUDIT: + case AUE_DUP2: + case AUE_GETAUDIT: + case AUE_GETAUDIT_ADDR: + case AUE_GETAUID: + case AUE_GETCWD: + case AUE_GETFSSTAT: + case AUE_GETRESUID: + case AUE_GETRESGID: + case AUE_KQUEUE: + case AUE_LSEEK: + case AUE_MODLOAD: + case AUE_MODUNLOAD: + case AUE_MSGSYS: + case AUE_NFS_SVC: + case AUE_NTP_ADJTIME: + case AUE_PIPE: + case AUE_PROFILE: + case AUE_RTPRIO: + case AUE_SEMSYS: + case AUE_SHMSYS: + case AUE_SETPGRP: + case AUE_SETRLIMIT: + case AUE_SETSID: + case AUE_SETTIMEOFDAY: + case AUE_SYSARCH: + + /* + * Header, subject, and return tokens added at end. + */ + break; + + case AUE_MKFIFO: + if (ARG_IS_VALID(kar, ARG_MODE)) { + tok = au_to_arg32(2, "mode", ar->ar_arg_mode); + kau_write(rec, tok); + } + /* FALLTHROUGH */ + + case AUE_ACCESS: + case AUE_CHDIR: + case AUE_CHROOT: + case AUE_EACCESS: + case AUE_GETATTRLIST: + case AUE_JAIL: + case AUE_LUTIMES: + case AUE_NFS_GETFH: + case AUE_LSTAT: + case AUE_PATHCONF: + case AUE_READLINK: + case AUE_REVOKE: + case AUE_RMDIR: + case AUE_SEARCHFS: + case AUE_SETATTRLIST: + case AUE_STAT: + case AUE_STATFS: + case AUE_SWAPON: + case AUE_SWAPOFF: + case AUE_TRUNCATE: + case AUE_UNDELETE: + case AUE_UNLINK: + case AUE_UTIMES: + UPATH1_VNODE1_TOKENS; + break; + + case AUE_FHSTATFS: + case AUE_FHOPEN: + case AUE_FHSTAT: + /* XXXRW: Need to audit vnode argument. */ + break; + + case AUE_CHFLAGS: + case AUE_LCHFLAGS: + if (ARG_IS_VALID(kar, ARG_FFLAGS)) { + tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); + kau_write(rec, tok); + } + UPATH1_VNODE1_TOKENS; + break; + + case AUE_CHMOD: + case AUE_LCHMOD: + if (ARG_IS_VALID(kar, ARG_MODE)) { + tok = au_to_arg32(2, "new file mode", + ar->ar_arg_mode); + kau_write(rec, tok); + } + UPATH1_VNODE1_TOKENS; + break; + + case AUE_CHOWN: + case AUE_LCHOWN: + if (ARG_IS_VALID(kar, ARG_UID)) { + tok = au_to_arg32(2, "new file uid", ar->ar_arg_uid); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_GID)) { + tok = au_to_arg32(3, "new file gid", ar->ar_arg_gid); + kau_write(rec, tok); + } + UPATH1_VNODE1_TOKENS; + break; + + case AUE_EXCHANGEDATA: + UPATH1_VNODE1_TOKENS; + UPATH2_TOKENS; + break; + + case AUE_CLOSE: + if (ARG_IS_VALID(kar, ARG_FD)) { + tok = au_to_arg32(2, "fd", ar->ar_arg_fd); + kau_write(rec, tok); + } + UPATH1_VNODE1_TOKENS; + break; + + case AUE_CORE: + if (ARG_IS_VALID(kar, ARG_SIGNUM)) { + tok = au_to_arg32(0, "signal", ar->ar_arg_signum); + kau_write(rec, tok); + } + UPATH1_VNODE1_TOKENS; + break; + + case AUE_EXTATTRCTL: + UPATH1_VNODE1_TOKENS; + if (ARG_IS_VALID(kar, ARG_CMD)) { + tok = au_to_arg32(2, "cmd", ar->ar_arg_cmd); + kau_write(rec, tok); + } + /* extattrctl(2) filename parameter is in upath2/vnode2 */ + UPATH2_TOKENS; + VNODE2_TOKENS; + EXTATTR_TOKENS; + break; + + case AUE_EXTATTR_GET_FILE: + case AUE_EXTATTR_SET_FILE: + case AUE_EXTATTR_LIST_FILE: + case AUE_EXTATTR_DELETE_FILE: + case AUE_EXTATTR_GET_LINK: + case AUE_EXTATTR_SET_LINK: + case AUE_EXTATTR_LIST_LINK: + case AUE_EXTATTR_DELETE_LINK: + UPATH1_VNODE1_TOKENS; + EXTATTR_TOKENS; + break; + + case AUE_EXTATTR_GET_FD: + case AUE_EXTATTR_SET_FD: + case AUE_EXTATTR_LIST_FD: + case AUE_EXTATTR_DELETE_FD: + if (ARG_IS_VALID(kar, ARG_FD)) { + tok = au_to_arg32(2, "fd", ar->ar_arg_fd); + kau_write(rec, tok); + } + EXTATTR_TOKENS; + break; + + case AUE_FEXECVE: + if (ARG_IS_VALID(kar, ARG_FD)) { + tok = au_to_arg32(1, "fd", ar->ar_arg_fd); + kau_write(rec, tok); + } + /* FALLTHROUGH */ + + case AUE_EXECVE: + if (ARG_IS_VALID(kar, ARG_ARGV)) { + tok = au_to_exec_args(ar->ar_arg_argv, + ar->ar_arg_argc); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_ENVV)) { + tok = au_to_exec_env(ar->ar_arg_envv, + ar->ar_arg_envc); + kau_write(rec, tok); + } + UPATH1_VNODE1_TOKENS; + break; + + case AUE_FCHMOD: + if (ARG_IS_VALID(kar, ARG_MODE)) { + tok = au_to_arg32(2, "new file mode", + ar->ar_arg_mode); + kau_write(rec, tok); + } + FD_VNODE1_TOKENS; + break; + + /* + * XXXRW: Some of these need to handle non-vnode cases as well. + */ + case AUE_FCHDIR: + case AUE_FPATHCONF: + case AUE_FSTAT: + case AUE_FSTATFS: + case AUE_FSYNC: + case AUE_FTRUNCATE: + case AUE_FUTIMES: + case AUE_GETDIRENTRIES: + case AUE_GETDIRENTRIESATTR: + case AUE_POLL: + case AUE_READ: + case AUE_READV: + case AUE_WRITE: + case AUE_WRITEV: + FD_VNODE1_TOKENS; + break; + + case AUE_FCHOWN: + if (ARG_IS_VALID(kar, ARG_UID)) { + tok = au_to_arg32(2, "new file uid", ar->ar_arg_uid); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_GID)) { + tok = au_to_arg32(3, "new file gid", ar->ar_arg_gid); + kau_write(rec, tok); + } + FD_VNODE1_TOKENS; + break; + + case AUE_FCNTL: + if (ar->ar_arg_cmd == F_GETLK || ar->ar_arg_cmd == F_SETLK || + ar->ar_arg_cmd == F_SETLKW) { + if (ARG_IS_VALID(kar, ARG_CMD)) { + tok = au_to_arg32(2, "cmd", ar->ar_arg_cmd); + kau_write(rec, tok); + } + FD_VNODE1_TOKENS; + } + break; + + case AUE_FCHFLAGS: + if (ARG_IS_VALID(kar, ARG_FFLAGS)) { + tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); + kau_write(rec, tok); + } + FD_VNODE1_TOKENS; + break; + + case AUE_FLOCK: + if (ARG_IS_VALID(kar, ARG_CMD)) { + tok = au_to_arg32(2, "operation", ar->ar_arg_cmd); + kau_write(rec, tok); + } + FD_VNODE1_TOKENS; + break; + + case AUE_RFORK: + if (ARG_IS_VALID(kar, ARG_FFLAGS)) { + tok = au_to_arg32(1, "flags", ar->ar_arg_fflags); + kau_write(rec, tok); + } + /* FALLTHROUGH */ + + case AUE_FORK: + case AUE_VFORK: + if (ARG_IS_VALID(kar, ARG_PID)) { + tok = au_to_arg32(0, "child PID", ar->ar_arg_pid); + kau_write(rec, tok); + } + break; + + case AUE_IOCTL: + if (ARG_IS_VALID(kar, ARG_CMD)) { + tok = au_to_arg32(2, "cmd", ar->ar_arg_cmd); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_ADDR)) { + tok = au_to_arg32(1, "arg", + (u_int32_t)(uintptr_t)ar->ar_arg_addr); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_VNODE1)) + FD_VNODE1_TOKENS; + else { + if (ARG_IS_VALID(kar, ARG_SOCKINFO)) { + tok = kau_to_socket(&ar->ar_arg_sockinfo); + kau_write(rec, tok); + } else { + if (ARG_IS_VALID(kar, ARG_FD)) { + tok = au_to_arg32(1, "fd", + ar->ar_arg_fd); + kau_write(rec, tok); + } + } + } + break; + + case AUE_KILL: + case AUE_KILLPG: + if (ARG_IS_VALID(kar, ARG_SIGNUM)) { + tok = au_to_arg32(2, "signal", ar->ar_arg_signum); + kau_write(rec, tok); + } + PROCESS_PID_TOKENS(1); + break; + + case AUE_KTRACE: + if (ARG_IS_VALID(kar, ARG_CMD)) { + tok = au_to_arg32(2, "ops", ar->ar_arg_cmd); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_VALUE)) { + tok = au_to_arg32(3, "trpoints", ar->ar_arg_value); + kau_write(rec, tok); + } + PROCESS_PID_TOKENS(4); + UPATH1_VNODE1_TOKENS; + break; + + case AUE_LINK: + case AUE_RENAME: + UPATH1_VNODE1_TOKENS; + UPATH2_TOKENS; + break; + + case AUE_LOADSHFILE: + if (ARG_IS_VALID(kar, ARG_ADDR)) { + tok = au_to_arg32(4, "base addr", + (u_int32_t)(uintptr_t)ar->ar_arg_addr); + kau_write(rec, tok); + } + UPATH1_VNODE1_TOKENS; + break; + + case AUE_MKDIR: + if (ARG_IS_VALID(kar, ARG_MODE)) { + tok = au_to_arg32(2, "mode", ar->ar_arg_mode); + kau_write(rec, tok); + } + UPATH1_VNODE1_TOKENS; + break; + + case AUE_MKNOD: + if (ARG_IS_VALID(kar, ARG_MODE)) { + tok = au_to_arg32(2, "mode", ar->ar_arg_mode); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_DEV)) { + tok = au_to_arg32(3, "dev", ar->ar_arg_dev); + kau_write(rec, tok); + } + UPATH1_VNODE1_TOKENS; + break; + + case AUE_MMAP: + case AUE_MUNMAP: + case AUE_MPROTECT: + case AUE_MLOCK: + case AUE_MUNLOCK: + case AUE_MINHERIT: + if (ARG_IS_VALID(kar, ARG_ADDR)) { + tok = au_to_arg32(1, "addr", + (u_int32_t)(uintptr_t)ar->ar_arg_addr); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_LEN)) { + tok = au_to_arg32(2, "len", ar->ar_arg_len); + kau_write(rec, tok); + } + if (ar->ar_event == AUE_MMAP) + FD_VNODE1_TOKENS; + if (ar->ar_event == AUE_MPROTECT) { + if (ARG_IS_VALID(kar, ARG_VALUE)) { + tok = au_to_arg32(3, "protection", + ar->ar_arg_value); + kau_write(rec, tok); + } + } + if (ar->ar_event == AUE_MINHERIT) { + if (ARG_IS_VALID(kar, ARG_VALUE)) { + tok = au_to_arg32(3, "inherit", + ar->ar_arg_value); + kau_write(rec, tok); + } + } + break; + + case AUE_MOUNT: + case AUE_NMOUNT: + /* XXX Need to handle NFS mounts */ + if (ARG_IS_VALID(kar, ARG_FFLAGS)) { + tok = au_to_arg32(3, "flags", ar->ar_arg_fflags); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_TEXT)) { + tok = au_to_text(ar->ar_arg_text); + kau_write(rec, tok); + } + /* FALLTHROUGH */ + + case AUE_UMOUNT: + UPATH1_VNODE1_TOKENS; + break; + + case AUE_MSGCTL: + ar->ar_event = audit_msgctl_to_event(ar->ar_arg_svipc_cmd); + /* Fall through */ + + case AUE_MSGRCV: + case AUE_MSGSND: + tok = au_to_arg32(1, "msg ID", ar->ar_arg_svipc_id); + kau_write(rec, tok); + if (ar->ar_errno != EINVAL) { + tok = au_to_ipc(AT_IPC_MSG, ar->ar_arg_svipc_id); + kau_write(rec, tok); + } + break; + + case AUE_MSGGET: + if (ar->ar_errno == 0) { + if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) { + tok = au_to_ipc(AT_IPC_MSG, + ar->ar_arg_svipc_id); + kau_write(rec, tok); + } + } + break; + + case AUE_RESETSHFILE: + if (ARG_IS_VALID(kar, ARG_ADDR)) { + tok = au_to_arg32(1, "base addr", + (u_int32_t)(uintptr_t)ar->ar_arg_addr); + kau_write(rec, tok); + } + break; + + case AUE_OPEN_RC: + case AUE_OPEN_RTC: + case AUE_OPEN_RWC: + case AUE_OPEN_RWTC: + case AUE_OPEN_WC: + case AUE_OPEN_WTC: + case AUE_CREAT: + if (ARG_IS_VALID(kar, ARG_MODE)) { + tok = au_to_arg32(3, "mode", ar->ar_arg_mode); + kau_write(rec, tok); + } + /* FALLTHROUGH */ + + case AUE_OPEN_R: + case AUE_OPEN_RT: + case AUE_OPEN_RW: + case AUE_OPEN_RWT: + case AUE_OPEN_W: + case AUE_OPEN_WT: + if (ARG_IS_VALID(kar, ARG_FFLAGS)) { + tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); + kau_write(rec, tok); + } + UPATH1_VNODE1_TOKENS; + break; + + case AUE_PTRACE: + if (ARG_IS_VALID(kar, ARG_CMD)) { + tok = au_to_arg32(1, "request", ar->ar_arg_cmd); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_ADDR)) { + tok = au_to_arg32(3, "addr", + (u_int32_t)(uintptr_t)ar->ar_arg_addr); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_VALUE)) { + tok = au_to_arg32(4, "data", ar->ar_arg_value); + kau_write(rec, tok); + } + PROCESS_PID_TOKENS(2); + break; + + case AUE_QUOTACTL: + if (ARG_IS_VALID(kar, ARG_CMD)) { + tok = au_to_arg32(2, "command", ar->ar_arg_cmd); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_UID)) { + tok = au_to_arg32(3, "uid", ar->ar_arg_uid); + kau_write(rec, tok); + } + UPATH1_VNODE1_TOKENS; + break; + + case AUE_REBOOT: + if (ARG_IS_VALID(kar, ARG_CMD)) { + tok = au_to_arg32(1, "howto", ar->ar_arg_cmd); + kau_write(rec, tok); + } + break; + + case AUE_SEMCTL: + ar->ar_event = audit_semctl_to_event(ar->ar_arg_svipc_cmd); + /* Fall through */ + + case AUE_SEMOP: + if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) { + tok = au_to_arg32(1, "sem ID", ar->ar_arg_svipc_id); + kau_write(rec, tok); + if (ar->ar_errno != EINVAL) { + tok = au_to_ipc(AT_IPC_SEM, + ar->ar_arg_svipc_id); + kau_write(rec, tok); + } + } + break; + + case AUE_SEMGET: + if (ar->ar_errno == 0) { + if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) { + tok = au_to_ipc(AT_IPC_SEM, + ar->ar_arg_svipc_id); + kau_write(rec, tok); + } + } + break; + + case AUE_SETEGID: + if (ARG_IS_VALID(kar, ARG_EGID)) { + tok = au_to_arg32(1, "gid", ar->ar_arg_egid); + kau_write(rec, tok); + } + break; + + case AUE_SETEUID: + if (ARG_IS_VALID(kar, ARG_EUID)) { + tok = au_to_arg32(1, "uid", ar->ar_arg_euid); + kau_write(rec, tok); + } + break; + + case AUE_SETREGID: + if (ARG_IS_VALID(kar, ARG_RGID)) { + tok = au_to_arg32(1, "rgid", ar->ar_arg_rgid); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_EGID)) { + tok = au_to_arg32(2, "egid", ar->ar_arg_egid); + kau_write(rec, tok); + } + break; + + case AUE_SETREUID: + if (ARG_IS_VALID(kar, ARG_RUID)) { + tok = au_to_arg32(1, "ruid", ar->ar_arg_ruid); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_EUID)) { + tok = au_to_arg32(2, "euid", ar->ar_arg_euid); + kau_write(rec, tok); + } + break; + + case AUE_SETRESGID: + if (ARG_IS_VALID(kar, ARG_RGID)) { + tok = au_to_arg32(1, "rgid", ar->ar_arg_rgid); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_EGID)) { + tok = au_to_arg32(2, "egid", ar->ar_arg_egid); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_SGID)) { + tok = au_to_arg32(3, "sgid", ar->ar_arg_sgid); + kau_write(rec, tok); + } + break; + + case AUE_SETRESUID: + if (ARG_IS_VALID(kar, ARG_RUID)) { + tok = au_to_arg32(1, "ruid", ar->ar_arg_ruid); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_EUID)) { + tok = au_to_arg32(2, "euid", ar->ar_arg_euid); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_SUID)) { + tok = au_to_arg32(3, "suid", ar->ar_arg_suid); + kau_write(rec, tok); + } + break; + + case AUE_SETGID: + if (ARG_IS_VALID(kar, ARG_GID)) { + tok = au_to_arg32(1, "gid", ar->ar_arg_gid); + kau_write(rec, tok); + } + break; + + case AUE_SETUID: + if (ARG_IS_VALID(kar, ARG_UID)) { + tok = au_to_arg32(1, "uid", ar->ar_arg_uid); + kau_write(rec, tok); + } + break; + + case AUE_SETGROUPS: + if (ARG_IS_VALID(kar, ARG_GROUPSET)) { + for(ctr = 0; ctr < ar->ar_arg_groups.gidset_size; ctr++) + { + tok = au_to_arg32(1, "setgroups", + ar->ar_arg_groups.gidset[ctr]); + kau_write(rec, tok); + } + } + break; + + case AUE_SETLOGIN: + if (ARG_IS_VALID(kar, ARG_TEXT)) { + tok = au_to_text(ar->ar_arg_text); + kau_write(rec, tok); + } + break; + + case AUE_SETPRIORITY: + if (ARG_IS_VALID(kar, ARG_CMD)) { + tok = au_to_arg32(1, "which", ar->ar_arg_cmd); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_UID)) { + tok = au_to_arg32(2, "who", ar->ar_arg_uid); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_VALUE)) { + tok = au_to_arg32(2, "priority", ar->ar_arg_value); + kau_write(rec, tok); + } + break; + + case AUE_SETPRIVEXEC: + if (ARG_IS_VALID(kar, ARG_VALUE)) { + tok = au_to_arg32(1, "flag", ar->ar_arg_value); + kau_write(rec, tok); + } + break; + + /* AUE_SHMAT, AUE_SHMCTL, AUE_SHMDT and AUE_SHMGET are SysV IPC */ + case AUE_SHMAT: + if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) { + tok = au_to_arg32(1, "shmid", ar->ar_arg_svipc_id); + kau_write(rec, tok); + /* XXXAUDIT: Does having the ipc token make sense? */ + tok = au_to_ipc(AT_IPC_SHM, ar->ar_arg_svipc_id); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_SVIPC_ADDR)) { + tok = au_to_arg32(2, "shmaddr", + (int)(uintptr_t)ar->ar_arg_svipc_addr); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_SVIPC_PERM)) { + tok = au_to_ipc_perm(&ar->ar_arg_svipc_perm); + kau_write(rec, tok); + } + break; + + case AUE_SHMCTL: + if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) { + tok = au_to_arg32(1, "shmid", ar->ar_arg_svipc_id); + kau_write(rec, tok); + /* XXXAUDIT: Does having the ipc token make sense? */ + tok = au_to_ipc(AT_IPC_SHM, ar->ar_arg_svipc_id); + kau_write(rec, tok); + } + switch (ar->ar_arg_svipc_cmd) { + case IPC_STAT: + ar->ar_event = AUE_SHMCTL_STAT; + break; + case IPC_RMID: + ar->ar_event = AUE_SHMCTL_RMID; + break; + case IPC_SET: + ar->ar_event = AUE_SHMCTL_SET; + if (ARG_IS_VALID(kar, ARG_SVIPC_PERM)) { + tok = au_to_ipc_perm(&ar->ar_arg_svipc_perm); + kau_write(rec, tok); + } + break; + default: + break; /* We will audit a bad command */ + } + break; + + case AUE_SHMDT: + if (ARG_IS_VALID(kar, ARG_SVIPC_ADDR)) { + tok = au_to_arg32(1, "shmaddr", + (int)(uintptr_t)ar->ar_arg_svipc_addr); + kau_write(rec, tok); + } + break; + + case AUE_SHMGET: + /* This is unusual; the return value is in an argument token */ + if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) { + tok = au_to_arg32(0, "shmid", ar->ar_arg_svipc_id); + kau_write(rec, tok); + tok = au_to_ipc(AT_IPC_SHM, ar->ar_arg_svipc_id); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_SVIPC_PERM)) { + tok = au_to_ipc_perm(&ar->ar_arg_svipc_perm); + kau_write(rec, tok); + } + break; + + /* AUE_SHMOPEN, AUE_SHMUNLINK, AUE_SEMOPEN, AUE_SEMCLOSE + * and AUE_SEMUNLINK are Posix IPC */ + case AUE_SHMOPEN: + if (ARG_IS_VALID(kar, ARG_SVIPC_ADDR)) { + tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_MODE)) { + tok = au_to_arg32(3, "mode", ar->ar_arg_mode); + kau_write(rec, tok); + } + /* FALLTHROUGH */ + + case AUE_SHMUNLINK: + if (ARG_IS_VALID(kar, ARG_TEXT)) { + tok = au_to_text(ar->ar_arg_text); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_POSIX_IPC_PERM)) { + struct ipc_perm perm; + + perm.uid = ar->ar_arg_pipc_perm.pipc_uid; + perm.gid = ar->ar_arg_pipc_perm.pipc_gid; + perm.cuid = ar->ar_arg_pipc_perm.pipc_uid; + perm.cgid = ar->ar_arg_pipc_perm.pipc_gid; + perm.mode = ar->ar_arg_pipc_perm.pipc_mode; + perm.seq = 0; + perm.key = 0; + tok = au_to_ipc_perm(&perm); + kau_write(rec, tok); + } + break; + + case AUE_SEMOPEN: + if (ARG_IS_VALID(kar, ARG_FFLAGS)) { + tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_MODE)) { + tok = au_to_arg32(3, "mode", ar->ar_arg_mode); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_VALUE)) { + tok = au_to_arg32(4, "value", ar->ar_arg_value); + kau_write(rec, tok); + } + /* FALLTHROUGH */ + + case AUE_SEMUNLINK: + if (ARG_IS_VALID(kar, ARG_TEXT)) { + tok = au_to_text(ar->ar_arg_text); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_POSIX_IPC_PERM)) { + struct ipc_perm perm; + + perm.uid = ar->ar_arg_pipc_perm.pipc_uid; + perm.gid = ar->ar_arg_pipc_perm.pipc_gid; + perm.cuid = ar->ar_arg_pipc_perm.pipc_uid; + perm.cgid = ar->ar_arg_pipc_perm.pipc_gid; + perm.mode = ar->ar_arg_pipc_perm.pipc_mode; + perm.seq = 0; + perm.key = 0; + tok = au_to_ipc_perm(&perm); + kau_write(rec, tok); + } + break; + + case AUE_SEMCLOSE: + if (ARG_IS_VALID(kar, ARG_FD)) { + tok = au_to_arg32(1, "sem", ar->ar_arg_fd); + kau_write(rec, tok); + } + break; + + case AUE_SYMLINK: + if (ARG_IS_VALID(kar, ARG_TEXT)) { + tok = au_to_text(ar->ar_arg_text); + kau_write(rec, tok); + } + UPATH1_VNODE1_TOKENS; + break; + + case AUE_SYSCTL: + case AUE_SYSCTL_NONADMIN: + if (ARG_IS_VALID(kar, ARG_CTLNAME | ARG_LEN)) { + for (ctr = 0; ctr < ar->ar_arg_len; ctr++) { + tok = au_to_arg32(1, "name", + ar->ar_arg_ctlname[ctr]); + kau_write(rec, tok); + } + } + if (ARG_IS_VALID(kar, ARG_VALUE)) { + tok = au_to_arg32(5, "newval", ar->ar_arg_value); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_TEXT)) { + tok = au_to_text(ar->ar_arg_text); + kau_write(rec, tok); + } + break; + + case AUE_UMASK: + if (ARG_IS_VALID(kar, ARG_MASK)) { + tok = au_to_arg32(1, "new mask", ar->ar_arg_mask); + kau_write(rec, tok); + } + tok = au_to_arg32(0, "prev mask", ar->ar_retval); + kau_write(rec, tok); + break; + + case AUE_WAIT4: + if (ARG_IS_VALID(kar, ARG_PID)) { + tok = au_to_arg32(0, "pid", ar->ar_arg_pid); + kau_write(rec, tok); + } + break; + + case AUE_NULL: + default: + printf("BSM conversion requested for unknown event %d\n", + ar->ar_event); + + /* + * Write the subject token so it is properly freed here. + */ + kau_write(rec, subj_tok); + kau_free(rec); + return (BSM_NOAUDIT); + } + + kau_write(rec, subj_tok); + tok = au_to_return32((char)ar->ar_errno, ar->ar_retval); + kau_write(rec, tok); /* Every record gets a return token */ + + kau_close(rec, &ar->ar_endtime, ar->ar_event); + + *pau = rec; + return (BSM_SUCCESS); +} + +/* + * Verify that a record is a valid BSM record. This verification is simple + * now, but may be expanded on sometime in the future. Return 1 if the + * record is good, 0 otherwise. + */ +int +bsm_rec_verify(void *rec) +{ + char c = *(char *)rec; + + /* + * Check the token ID of the first token; it has to be a header + * token. + * + * XXXAUDIT There needs to be a token structure to map a token. + * XXXAUDIT 'Shouldn't be simply looking at the first char. + */ + if ((c != AUT_HEADER32) && (c != AUT_HEADER32_EX) && + (c != AUT_HEADER64) && (c != AUT_HEADER64_EX)) + return (0); + return (1); +} diff --git a/sys/security/audit/audit_bsm_errno.c b/sys/security/audit/audit_bsm_errno.c new file mode 100644 index 0000000..7586213 --- /dev/null +++ b/sys/security/audit/audit_bsm_errno.c @@ -0,0 +1,642 @@ +/*- + * Copyright (c) 2008 Apple Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * P4: //depot/projects/trustedbsd/openbsm/libbsm/bsm_errno.c#12 + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> + +#include <bsm/audit_errno.h> +#include <bsm/libbsm.h> + +#include <sys/errno.h> + +/* + * Different operating systems use different numeric constants for different + * error numbers, and sometimes error numbers don't exist in more than one + * operating system. These routines convert between BSM and local error + * number spaces, subject to the above realities. BSM error numbers are + * stored in a single 8-bit character, so don't have a byte order. + */ + +struct bsm_errors { + int be_bsm_error; + int be_os_error; + const char *be_strerror; +}; + +#define ERRNO_NO_LOCAL_MAPPING -600 + +/* + * Mapping table -- please maintain in numeric sorted order with respect to + * the BSM constant. Today we do a linear lookup, but could switch to a + * binary search if it makes sense. We only ifdef errors that aren't + * generally available, but it does make the table a lot more ugly. + * + * XXXRW: It would be nice to have a similar ordered table mapping to BSM + * constant from local constant, but the order of local constants varies by + * OS. Really we need to build that table at compile-time but don't do that + * yet. + * + * XXXRW: We currently embed English-language error strings here, but should + * support catalogues; these are only used if the OS doesn't have an error + * string using strerror(3). + */ +static const struct bsm_errors bsm_errors[] = { + { BSM_ESUCCESS, 0, "Success" }, + { BSM_EPERM, EPERM, "Operation not permitted" }, + { BSM_ENOENT, ENOENT, "No such file or directory" }, + { BSM_ESRCH, ESRCH, "No such process" }, + { BSM_EINTR, EINTR, "Interrupted system call" }, + { BSM_EIO, EIO, "Input/output error" }, + { BSM_ENXIO, ENXIO, "Device not configured" }, + { BSM_E2BIG, E2BIG, "Argument list too long" }, + { BSM_ENOEXEC, ENOEXEC, "Exec format error" }, + { BSM_EBADF, EBADF, "BAd file descriptor" }, + { BSM_ECHILD, ECHILD, "No child processes" }, + { BSM_EAGAIN, EAGAIN, "Resource temporarily unavailable" }, + { BSM_ENOMEM, ENOMEM, "Cannot allocate memory" }, + { BSM_EACCES, EACCES, "Permission denied" }, + { BSM_EFAULT, EFAULT, "Bad address" }, + { BSM_ENOTBLK, ENOTBLK, "Block device required" }, + { BSM_EBUSY, EBUSY, "Device busy" }, + { BSM_EEXIST, EEXIST, "File exists" }, + { BSM_EXDEV, EXDEV, "Cross-device link" }, + { BSM_ENODEV, ENODEV, "Operation not supported by device" }, + { BSM_ENOTDIR, ENOTDIR, "Not a directory" }, + { BSM_EISDIR, EISDIR, "Is a directory" }, + { BSM_EINVAL, EINVAL, "Invalid argument" }, + { BSM_ENFILE, ENFILE, "Too many open files in system" }, + { BSM_EMFILE, EMFILE, "Too many open files" }, + { BSM_ENOTTY, ENOTTY, "Inappropriate ioctl for device" }, + { BSM_ETXTBSY, ETXTBSY, "Text file busy" }, + { BSM_EFBIG, EFBIG, "File too large" }, + { BSM_ENOSPC, ENOSPC, "No space left on device" }, + { BSM_ESPIPE, ESPIPE, "Illegal seek" }, + { BSM_EROFS, EROFS, "Read-only file system" }, + { BSM_EMLINK, EMLINK, "Too many links" }, + { BSM_EPIPE, EPIPE, "Broken pipe" }, + { BSM_EDOM, EDOM, "Numerical argument out of domain" }, + { BSM_ERANGE, ERANGE, "Result too large" }, + { BSM_ENOMSG, ENOMSG, "No message of desired type" }, + { BSM_EIDRM, EIDRM, "Identifier removed" }, + { BSM_ECHRNG, +#ifdef ECHRNG + ECHRNG, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Channel number out of range" }, + { BSM_EL2NSYNC, +#ifdef EL2NSYNC + EL2NSYNC, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Level 2 not synchronized" }, + { BSM_EL3HLT, +#ifdef EL3HLT + EL3HLT, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Level 3 halted" }, + { BSM_EL3RST, +#ifdef EL3RST + EL3RST, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Level 3 reset" }, + { BSM_ELNRNG, +#ifdef ELNRNG + ELNRNG, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Link number out of range" }, + { BSM_EUNATCH, +#ifdef EUNATCH + EUNATCH, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Protocol driver not attached" }, + { BSM_ENOCSI, +#ifdef ENOCSI + ENOCSI, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "No CSI structure available" }, + { BSM_EL2HLT, +#ifdef EL2HLT + EL2HLT, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Level 2 halted" }, + { BSM_EDEADLK, EDEADLK, "Resource deadlock avoided" }, + { BSM_ENOLCK, ENOLCK, "No locks available" }, + { BSM_ECANCELED, ECANCELED, "Operation canceled" }, + { BSM_ENOTSUP, ENOTSUP, "Operation not supported" }, + { BSM_EDQUOT, EDQUOT, "Disc quota exceeded" }, + { BSM_EBADE, +#ifdef EBADE + EBADE, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Invalid exchange" }, + { BSM_EBADR, +#ifdef EBADR + EBADR, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Invalid request descriptor" }, + { BSM_EXFULL, +#ifdef EXFULL + EXFULL, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Exchange full" }, + { BSM_ENOANO, +#ifdef ENOANO + ENOANO, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "No anode" }, + { BSM_EBADRQC, +#ifdef EBADRQC + EBADRQC, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Invalid request descriptor" }, + { BSM_EBADSLT, +#ifdef EBADSLT + EBADSLT, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Invalid slot" }, + { BSM_EDEADLOCK, +#ifdef EDEADLOCK + EDEADLOCK, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Resource deadlock avoided" }, + { BSM_EBFONT, +#ifdef EBFONT + EBFONT, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Bad font file format" }, + { BSM_EOWNERDEAD, +#ifdef EOWNERDEAD + EOWNERDEAD, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Process died with the lock" }, + { BSM_ENOTRECOVERABLE, +#ifdef ENOTRECOVERABLE + ENOTRECOVERABLE, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Lock is not recoverable" }, + { BSM_ENOSTR, +#ifdef ENOSTR + ENOSTR, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Device not a stream" }, + { BSM_ENONET, +#ifdef ENONET + ENONET, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Machine is not on the network" }, + { BSM_ENOPKG, +#ifdef ENOPKG + ENOPKG, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Package not installed" }, + { BSM_EREMOTE, EREMOTE, "Too many levels of remote in path" }, + { BSM_ENOLINK, +#ifdef ENOLINK + ENOLINK, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Link has been severed" }, + { BSM_EADV, +#ifdef EADV + EADV, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Advertise error" }, + { BSM_ESRMNT, +#ifdef ESRMNT + ESRMNT, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "srmount error" }, + { BSM_ECOMM, +#ifdef ECOMM + ECOMM, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Communication error on send" }, + { BSM_EPROTO, +#ifdef EPROTO + EPROTO, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Protocol error" }, + { BSM_ELOCKUNMAPPED, +#ifdef ELOCKUNMAPPED + ELOCKUNMAPPED, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Locked lock was unmapped" }, + { BSM_ENOTACTIVE, +#ifdef ENOTACTIVE + ENOTACTIVE, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Facility is not active" }, + { BSM_EMULTIHOP, +#ifdef EMULTIHOP + EMULTIHOP, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Multihop attempted" }, + { BSM_EBADMSG, +#ifdef EBADMSG + EBADMSG, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Bad message" }, + { BSM_ENAMETOOLONG, ENAMETOOLONG, "File name too long" }, + { BSM_EOVERFLOW, EOVERFLOW, "Value too large to be stored in data type" }, + { BSM_ENOTUNIQ, +#ifdef ENOTUNIQ + ENOTUNIQ, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Given log name not unique" }, + { BSM_EBADFD, +#ifdef EBADFD + EBADFD, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Given f.d. invalid for this operation" }, + { BSM_EREMCHG, +#ifdef EREMCHG + EREMCHG, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Remote address changed" }, + { BSM_ELIBACC, +#ifdef ELIBACC + ELIBACC, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Can't access a needed shared lib" }, + { BSM_ELIBBAD, +#ifdef ELIBBAD + ELIBBAD, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Accessing a corrupted shared lib" }, + { BSM_ELIBSCN, +#ifdef ELIBSCN + ELIBSCN, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ".lib section in a.out corrupted" }, + { BSM_ELIBMAX, +#ifdef ELIBMAX + ELIBMAX, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Attempting to link in too many libs" }, + { BSM_ELIBEXEC, +#ifdef ELIBEXEC + ELIBEXEC, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Attempting to exec a shared library" }, + { BSM_EILSEQ, EILSEQ, "Illegal byte sequence" }, + { BSM_ENOSYS, ENOSYS, "Function not implemented" }, + { BSM_ELOOP, ELOOP, "Too many levels of symbolic links" }, + { BSM_ERESTART, +#ifdef ERESTART + ERESTART, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Restart syscall" }, + { BSM_ESTRPIPE, +#ifdef ESTRPIPE + ESTRPIPE, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "If pipe/FIFO, don't sleep in stream head" }, + { BSM_ENOTEMPTY, ENOTEMPTY, "Directory not empty" }, + { BSM_EUSERS, EUSERS, "Too many users" }, + { BSM_ENOTSOCK, ENOTSOCK, "Socket operation on non-socket" }, + { BSM_EDESTADDRREQ, EDESTADDRREQ, "Destination address required" }, + { BSM_EMSGSIZE, EMSGSIZE, "Message too long" }, + { BSM_EPROTOTYPE, EPROTOTYPE, "Protocol wrong type for socket" }, + { BSM_ENOPROTOOPT, ENOPROTOOPT, "Protocol not available" }, + { BSM_EPROTONOSUPPORT, EPROTONOSUPPORT, "Protocol not supported" }, + { BSM_ESOCKTNOSUPPORT, ESOCKTNOSUPPORT, "Socket type not supported" }, + { BSM_EOPNOTSUPP, EOPNOTSUPP, "Operation not supported" }, + { BSM_EPFNOSUPPORT, EPFNOSUPPORT, "Protocol family not supported" }, + { BSM_EAFNOSUPPORT, EAFNOSUPPORT, "Address family not supported by protocol family" }, + { BSM_EADDRINUSE, EADDRINUSE, "Address already in use" }, + { BSM_EADDRNOTAVAIL, EADDRNOTAVAIL, "Can't assign requested address" }, + { BSM_ENETDOWN, ENETDOWN, "Network is down" }, + { BSM_ENETRESET, ENETRESET, "Network dropped connection on reset" }, + { BSM_ECONNABORTED, ECONNABORTED, "Software caused connection abort" }, + { BSM_ECONNRESET, ECONNRESET, "Connection reset by peer" }, + { BSM_ENOBUFS, ENOBUFS, "No buffer space available" }, + { BSM_EISCONN, EISCONN, "Socket is already connected" }, + { BSM_ENOTCONN, ENOTCONN, "Socket is not connected" }, + { BSM_ESHUTDOWN, ESHUTDOWN, "Can't send after socket shutdown" }, + { BSM_ETOOMANYREFS, ETOOMANYREFS, "Too many references: can't splice" }, + { BSM_ETIMEDOUT, ETIMEDOUT, "Operation timed out" }, + { BSM_ECONNREFUSED, ECONNREFUSED, "Connection refused" }, + { BSM_EHOSTDOWN, EHOSTDOWN, "Host is down" }, + { BSM_EHOSTUNREACH, EHOSTUNREACH, "No route to host" }, + { BSM_EALREADY, EALREADY, "Operation already in progress" }, + { BSM_EINPROGRESS, EINPROGRESS, "Operation now in progress" }, + { BSM_ESTALE, ESTALE, "Stale NFS file handle" }, + { BSM_EPWROFF, +#ifdef EPWROFF + EPWROFF, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Device power is off" }, + { BSM_EDEVERR, +#ifdef EDEVERR + EDEVERR, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Device error" }, + { BSM_EBADEXEC, +#ifdef EBADEXEC + EBADEXEC, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Bad executable" }, + { BSM_EBADARCH, +#ifdef EBADARCH + EBADARCH, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Bad CPU type in executable" }, + { BSM_ESHLIBVERS, +#ifdef ESHLIBVERS + ESHLIBVERS, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Shared library version mismatch" }, + { BSM_EBADMACHO, +#ifdef EBADMACHO + EBADMACHO, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Malfored Macho file" }, + { BSM_EPOLICY, +#ifdef EPOLICY + EPOLICY, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Operation failed by policy" }, + { BSM_EDOTDOT, +#ifdef EDOTDOT + EDOTDOT, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "RFS specific error" }, + { BSM_EUCLEAN, +#ifdef EUCLEAN + EUCLEAN, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Structure needs cleaning" }, + { BSM_ENOTNAM, +#ifdef ENOTNAM + ENOTNAM, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Not a XENIX named type file" }, + { BSM_ENAVAIL, +#ifdef ENAVAIL + ENAVAIL, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "No XENIX semaphores available" }, + { BSM_EISNAM, +#ifdef EISNAM + EISNAM, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Is a named type file" }, + { BSM_EREMOTEIO, +#ifdef EREMOTEIO + EREMOTEIO, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Remote I/O error" }, + { BSM_ENOMEDIUM, +#ifdef ENOMEDIUM + ENOMEDIUM, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "No medium found" }, + { BSM_EMEDIUMTYPE, +#ifdef EMEDIUMTYPE + EMEDIUMTYPE, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Wrong medium type" }, + { BSM_ENOKEY, +#ifdef ENOKEY + ENOKEY, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Required key not available" }, + { BSM_EKEYEXPIRED, +#ifdef EKEEXPIRED + EKEYEXPIRED, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Key has expired" }, + { BSM_EKEYREVOKED, +#ifdef EKEYREVOKED + EKEYREVOKED, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Key has been revoked" }, + { BSM_EKEYREJECTED, +#ifdef EKEREJECTED + EKEYREJECTED, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + "Key was rejected by service" }, +}; +static const int bsm_errors_count = sizeof(bsm_errors) / sizeof(bsm_errors[0]); + +static const struct bsm_errors * +au_bsm_error_lookup_errno(int error) +{ + int i; + + if (error == ERRNO_NO_LOCAL_MAPPING) + return (NULL); + for (i = 0; i < bsm_errors_count; i++) { + if (bsm_errors[i].be_os_error == error) + return (&bsm_errors[i]); + } + return (NULL); +} + +static const struct bsm_errors * +au_bsm_error_lookup_bsm(u_char bsm_error) +{ + int i; + + for (i = 0; i < bsm_errors_count; i++) { + if (bsm_errors[i].be_bsm_error == bsm_error) + return (&bsm_errors[i]); + } + return (NULL); +} + +/* + * Converstion from a BSM error to a local error number may fail if either + * OpenBSM doesn't recognize the error on the wire, or because there is no + * appropriate local mapping. However, we don't allow conversion to BSM to + * fail, we just convert to BSM_UKNOWNERR. + */ +int +au_bsm_to_errno(u_char bsm_error, int *errorp) +{ + const struct bsm_errors *bsme; + + bsme = au_bsm_error_lookup_bsm(bsm_error); + if (bsme == NULL || bsme->be_os_error == ERRNO_NO_LOCAL_MAPPING) + return (-1); + *errorp = bsme->be_os_error; + return (0); +} + +u_char +au_errno_to_bsm(int error) +{ + const struct bsm_errors *bsme; + + /* + * We should never be passed this libbsm-internal constant, and + * because it is ambiguous we just return an error. + */ + if (error == ERRNO_NO_LOCAL_MAPPING) + return (BSM_UNKNOWNERR); + bsme = au_bsm_error_lookup_errno(error); + if (bsme == NULL) + return (BSM_UNKNOWNERR); + return (bsme->be_bsm_error); +} + +#if !defined(KERNEL) && !defined(_KERNEL) +const char * +au_strerror(u_char bsm_error) +{ + const struct bsm_errors *bsme; + + bsme = au_bsm_error_lookup_bsm(bsm_error); + if (bsme == NULL) + return ("Unrecognized BSM error"); + if (bsme->be_os_error != ERRNO_NO_LOCAL_MAPPING) + return (strerror(bsme->be_os_error)); + return (bsme->be_strerror); +} +#endif diff --git a/sys/security/audit/audit_bsm_klib.c b/sys/security/audit/audit_bsm_klib.c new file mode 100644 index 0000000..a75a68b --- /dev/null +++ b/sys/security/audit/audit_bsm_klib.c @@ -0,0 +1,580 @@ +/* + * Copyright (c) 1999-2008 Apple Inc. + * Copyright (c) 2005 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/fcntl.h> +#include <sys/filedesc.h> +#include <sys/libkern.h> +#include <sys/malloc.h> +#include <sys/mount.h> +#include <sys/proc.h> +#include <sys/rwlock.h> +#include <sys/sem.h> +#include <sys/sbuf.h> +#include <sys/syscall.h> +#include <sys/sysctl.h> +#include <sys/sysent.h> +#include <sys/vnode.h> + +#include <bsm/audit.h> +#include <bsm/audit_kevents.h> +#include <security/audit/audit.h> +#include <security/audit/audit_private.h> + +/* + * Hash table functions for the audit event number to event class mask + * mapping. + */ +#define EVCLASSMAP_HASH_TABLE_SIZE 251 +struct evclass_elem { + au_event_t event; + au_class_t class; + LIST_ENTRY(evclass_elem) entry; +}; +struct evclass_list { + LIST_HEAD(, evclass_elem) head; +}; + +static MALLOC_DEFINE(M_AUDITEVCLASS, "audit_evclass", "Audit event class"); +static struct rwlock evclass_lock; +static struct evclass_list evclass_hash[EVCLASSMAP_HASH_TABLE_SIZE]; + +#define EVCLASS_LOCK_INIT() rw_init(&evclass_lock, "evclass_lock") +#define EVCLASS_RLOCK() rw_rlock(&evclass_lock) +#define EVCLASS_RUNLOCK() rw_runlock(&evclass_lock) +#define EVCLASS_WLOCK() rw_wlock(&evclass_lock) +#define EVCLASS_WUNLOCK() rw_wunlock(&evclass_lock) + +/* + * Look up the class for an audit event in the class mapping table. + */ +au_class_t +au_event_class(au_event_t event) +{ + struct evclass_list *evcl; + struct evclass_elem *evc; + au_class_t class; + + EVCLASS_RLOCK(); + evcl = &evclass_hash[event % EVCLASSMAP_HASH_TABLE_SIZE]; + class = 0; + LIST_FOREACH(evc, &evcl->head, entry) { + if (evc->event == event) { + class = evc->class; + goto out; + } + } +out: + EVCLASS_RUNLOCK(); + return (class); +} + +/* + * Insert a event to class mapping. If the event already exists in the + * mapping, then replace the mapping with the new one. + * + * XXX There is currently no constraints placed on the number of mappings. + * May want to either limit to a number, or in terms of memory usage. + */ +void +au_evclassmap_insert(au_event_t event, au_class_t class) +{ + struct evclass_list *evcl; + struct evclass_elem *evc, *evc_new; + + /* + * Pessimistically, always allocate storage before acquiring mutex. + * Free if there is already a mapping for this event. + */ + evc_new = malloc(sizeof(*evc), M_AUDITEVCLASS, M_WAITOK); + + EVCLASS_WLOCK(); + evcl = &evclass_hash[event % EVCLASSMAP_HASH_TABLE_SIZE]; + LIST_FOREACH(evc, &evcl->head, entry) { + if (evc->event == event) { + evc->class = class; + EVCLASS_WUNLOCK(); + free(evc_new, M_AUDITEVCLASS); + return; + } + } + evc = evc_new; + evc->event = event; + evc->class = class; + LIST_INSERT_HEAD(&evcl->head, evc, entry); + EVCLASS_WUNLOCK(); +} + +void +au_evclassmap_init(void) +{ + int i; + + EVCLASS_LOCK_INIT(); + for (i = 0; i < EVCLASSMAP_HASH_TABLE_SIZE; i++) + LIST_INIT(&evclass_hash[i].head); + + /* + * Set up the initial event to class mapping for system calls. + * + * XXXRW: Really, this should walk all possible audit events, not all + * native ABI system calls, as there may be audit events reachable + * only through non-native system calls. It also seems a shame to + * frob the mutex this early. + */ + for (i = 0; i < SYS_MAXSYSCALL; i++) { + if (sysent[i].sy_auevent != AUE_NULL) + au_evclassmap_insert(sysent[i].sy_auevent, 0); + } +} + +/* + * Check whether an event is aditable by comparing the mask of classes this + * event is part of against the given mask. + */ +int +au_preselect(au_event_t event, au_class_t class, au_mask_t *mask_p, int sorf) +{ + au_class_t effmask = 0; + + if (mask_p == NULL) + return (-1); + + /* + * Perform the actual check of the masks against the event. + */ + if (sorf & AU_PRS_SUCCESS) + effmask |= (mask_p->am_success & class); + + if (sorf & AU_PRS_FAILURE) + effmask |= (mask_p->am_failure & class); + + if (effmask) + return (1); + else + return (0); +} + +/* + * Convert sysctl names and present arguments to events. + */ +au_event_t +audit_ctlname_to_sysctlevent(int name[], uint64_t valid_arg) +{ + + /* can't parse it - so return the worst case */ + if ((valid_arg & (ARG_CTLNAME | ARG_LEN)) != (ARG_CTLNAME | ARG_LEN)) + return (AUE_SYSCTL); + + switch (name[0]) { + /* non-admin "lookups" treat them special */ + case KERN_OSTYPE: + case KERN_OSRELEASE: + case KERN_OSREV: + case KERN_VERSION: + case KERN_ARGMAX: + case KERN_CLOCKRATE: + case KERN_BOOTTIME: + case KERN_POSIX1: + case KERN_NGROUPS: + case KERN_JOB_CONTROL: + case KERN_SAVED_IDS: + case KERN_OSRELDATE: + case KERN_DUMMY: + return (AUE_SYSCTL_NONADMIN); + + /* only treat the changeable controls as admin */ + case KERN_MAXVNODES: + case KERN_MAXPROC: + case KERN_MAXFILES: + case KERN_MAXPROCPERUID: + case KERN_MAXFILESPERPROC: + case KERN_HOSTID: + case KERN_SECURELVL: + case KERN_HOSTNAME: + case KERN_VNODE: + case KERN_PROC: + case KERN_FILE: + case KERN_PROF: + case KERN_NISDOMAINNAME: + case KERN_UPDATEINTERVAL: + case KERN_NTP_PLL: + case KERN_BOOTFILE: + case KERN_DUMPDEV: + case KERN_IPC: + case KERN_PS_STRINGS: + case KERN_USRSTACK: + case KERN_LOGSIGEXIT: + case KERN_IOV_MAX: + case KERN_MAXID: + return ((valid_arg & ARG_VALUE) ? + AUE_SYSCTL : AUE_SYSCTL_NONADMIN); + + default: + return (AUE_SYSCTL); + } + /* NOTREACHED */ +} + +/* + * Convert an open flags specifier into a specific type of open event for + * auditing purposes. + */ +au_event_t +audit_flags_and_error_to_openevent(int oflags, int error) +{ + au_event_t aevent; + + /* + * Need to check only those flags we care about. + */ + oflags = oflags & (O_RDONLY | O_CREAT | O_TRUNC | O_RDWR | O_WRONLY); + + /* + * These checks determine what flags are on with the condition that + * ONLY that combination is on, and no other flags are on. + */ + switch (oflags) { + case O_RDONLY: + aevent = AUE_OPEN_R; + break; + + case (O_RDONLY | O_CREAT): + aevent = AUE_OPEN_RC; + break; + + case (O_RDONLY | O_CREAT | O_TRUNC): + aevent = AUE_OPEN_RTC; + break; + + case (O_RDONLY | O_TRUNC): + aevent = AUE_OPEN_RT; + break; + + case O_RDWR: + aevent = AUE_OPEN_RW; + break; + + case (O_RDWR | O_CREAT): + aevent = AUE_OPEN_RWC; + break; + + case (O_RDWR | O_CREAT | O_TRUNC): + aevent = AUE_OPEN_RWTC; + break; + + case (O_RDWR | O_TRUNC): + aevent = AUE_OPEN_RWT; + break; + + case O_WRONLY: + aevent = AUE_OPEN_W; + break; + + case (O_WRONLY | O_CREAT): + aevent = AUE_OPEN_WC; + break; + + case (O_WRONLY | O_CREAT | O_TRUNC): + aevent = AUE_OPEN_WTC; + break; + + case (O_WRONLY | O_TRUNC): + aevent = AUE_OPEN_WT; + break; + + default: + aevent = AUE_OPEN; + break; + } + +#if 0 + /* + * Convert chatty errors to better matching events. Failures to + * find a file are really just attribute events -- so recast them as + * such. + * + * XXXAUDIT: Solaris defines that AUE_OPEN will never be returned, it + * is just a placeholder. However, in Darwin we return that in + * preference to other events. For now, comment this out as we don't + * have a BSM conversion routine for AUE_OPEN. + */ + switch (aevent) { + case AUE_OPEN_R: + case AUE_OPEN_RT: + case AUE_OPEN_RW: + case AUE_OPEN_RWT: + case AUE_OPEN_W: + case AUE_OPEN_WT: + if (error == ENOENT) + aevent = AUE_OPEN; + } +#endif + return (aevent); +} + +/* + * Convert a MSGCTL command to a specific event. + */ +int +audit_msgctl_to_event(int cmd) +{ + + switch (cmd) { + case IPC_RMID: + return (AUE_MSGCTL_RMID); + + case IPC_SET: + return (AUE_MSGCTL_SET); + + case IPC_STAT: + return (AUE_MSGCTL_STAT); + + default: + /* We will audit a bad command. */ + return (AUE_MSGCTL); + } +} + +/* + * Convert a SEMCTL command to a specific event. + */ +int +audit_semctl_to_event(int cmd) +{ + + switch (cmd) { + case GETALL: + return (AUE_SEMCTL_GETALL); + + case GETNCNT: + return (AUE_SEMCTL_GETNCNT); + + case GETPID: + return (AUE_SEMCTL_GETPID); + + case GETVAL: + return (AUE_SEMCTL_GETVAL); + + case GETZCNT: + return (AUE_SEMCTL_GETZCNT); + + case IPC_RMID: + return (AUE_SEMCTL_RMID); + + case IPC_SET: + return (AUE_SEMCTL_SET); + + case SETALL: + return (AUE_SEMCTL_SETALL); + + case SETVAL: + return (AUE_SEMCTL_SETVAL); + + case IPC_STAT: + return (AUE_SEMCTL_STAT); + + default: + /* We will audit a bad command. */ + return (AUE_SEMCTL); + } +} + +/* + * Convert a command for the auditon() system call to a audit event. + */ +int +auditon_command_event(int cmd) +{ + + switch(cmd) { + case A_GETPOLICY: + return (AUE_AUDITON_GPOLICY); + + case A_SETPOLICY: + return (AUE_AUDITON_SPOLICY); + + case A_GETKMASK: + return (AUE_AUDITON_GETKMASK); + + case A_SETKMASK: + return (AUE_AUDITON_SETKMASK); + + case A_GETQCTRL: + return (AUE_AUDITON_GQCTRL); + + case A_SETQCTRL: + return (AUE_AUDITON_SQCTRL); + + case A_GETCWD: + return (AUE_AUDITON_GETCWD); + + case A_GETCAR: + return (AUE_AUDITON_GETCAR); + + case A_GETSTAT: + return (AUE_AUDITON_GETSTAT); + + case A_SETSTAT: + return (AUE_AUDITON_SETSTAT); + + case A_SETUMASK: + return (AUE_AUDITON_SETUMASK); + + case A_SETSMASK: + return (AUE_AUDITON_SETSMASK); + + case A_GETCOND: + return (AUE_AUDITON_GETCOND); + + case A_SETCOND: + return (AUE_AUDITON_SETCOND); + + case A_GETCLASS: + return (AUE_AUDITON_GETCLASS); + + case A_SETCLASS: + return (AUE_AUDITON_SETCLASS); + + case A_GETPINFO: + case A_SETPMASK: + case A_SETFSIZE: + case A_GETFSIZE: + case A_GETPINFO_ADDR: + case A_GETKAUDIT: + case A_SETKAUDIT: + default: + return (AUE_AUDITON); /* No special record */ + } +} + +/* + * Create a canonical path from given path by prefixing either the root + * directory, or the current working directory. If the process working + * directory is NULL, we could use 'rootvnode' to obtain the root directory, + * but this results in a volfs name written to the audit log. So we will + * leave the filename starting with '/' in the audit log in this case. + */ +void +audit_canon_path(struct thread *td, char *path, char *cpath) +{ + struct vnode *cvnp, *rvnp; + char *rbuf, *fbuf, *copy; + struct filedesc *fdp; + struct sbuf sbf; + int error, cwir; + + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "%s: at %s:%d", + __func__, __FILE__, __LINE__); + + copy = path; + rvnp = cvnp = NULL; + fdp = td->td_proc->p_fd; + FILEDESC_SLOCK(fdp); + /* + * Make sure that we handle the chroot(2) case. If there is an + * alternate root directory, prepend it to the audited pathname. + */ + if (fdp->fd_rdir != NULL && fdp->fd_rdir != rootvnode) { + rvnp = fdp->fd_rdir; + vhold(rvnp); + } + /* + * If the supplied path is relative, make sure we capture the current + * working directory so we can prepend it to the supplied relative + * path. + */ + if (*path != '/') { + cvnp = fdp->fd_cdir; + vhold(cvnp); + } + cwir = (fdp->fd_rdir == fdp->fd_cdir); + FILEDESC_SUNLOCK(fdp); + /* + * NB: We require that the supplied array be at least MAXPATHLEN bytes + * long. If this is not the case, then we can run into serious trouble. + */ + (void) sbuf_new(&sbf, cpath, MAXPATHLEN, SBUF_FIXEDLEN); + /* + * Strip leading forward slashes. + */ + while (*copy == '/') + copy++; + /* + * Make sure we handle chroot(2) and prepend the global path to these + * environments. + * + * NB: vn_fullpath(9) on FreeBSD is less reliable than vn_getpath(9) + * on Darwin. As a result, this may need some additional attention + * in the future. + */ + if (rvnp != NULL) { + error = vn_fullpath_global(td, rvnp, &rbuf, &fbuf); + vdrop(rvnp); + if (error) { + cpath[0] = '\0'; + if (cvnp != NULL) + vdrop(cvnp); + return; + } + (void) sbuf_cat(&sbf, rbuf); + free(fbuf, M_TEMP); + } + if (cvnp != NULL) { + error = vn_fullpath(td, cvnp, &rbuf, &fbuf); + vdrop(cvnp); + if (error) { + cpath[0] = '\0'; + return; + } + (void) sbuf_cat(&sbf, rbuf); + free(fbuf, M_TEMP); + } + if (cwir == 0 || (cwir != 0 && cvnp == NULL)) + (void) sbuf_putc(&sbf, '/'); + /* + * Now that we have processed any alternate root and relative path + * names, add the supplied pathname. + */ + (void) sbuf_cat(&sbf, copy); + /* + * One or more of the previous sbuf operations could have resulted in + * the supplied buffer being overflowed. Check to see if this is the + * case. + */ + if (sbuf_overflowed(&sbf) != 0) { + cpath[0] = '\0'; + return; + } + sbuf_finish(&sbf); +} diff --git a/sys/security/audit/audit_bsm_token.c b/sys/security/audit/audit_bsm_token.c new file mode 100644 index 0000000..6733030 --- /dev/null +++ b/sys/security/audit/audit_bsm_token.c @@ -0,0 +1,1526 @@ +/*- + * Copyright (c) 2004-2008 Apple Inc. + * Copyright (c) 2005 SPARTA, Inc. + * All rights reserved. + * + * This code was developed in part by Robert N. M. Watson, Senior Principal + * Scientist, SPARTA, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * P4: //depot/projects/trustedbsd/openbsm/libbsm/bsm_token.c#72 + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/endian.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <sys/ipc.h> +#include <sys/libkern.h> +#include <sys/malloc.h> +#include <sys/un.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> + + +#include <bsm/audit.h> +#include <bsm/audit_internal.h> +#include <bsm/audit_record.h> +#include <security/audit/audit.h> +#include <security/audit/audit_private.h> + +#define GET_TOKEN_AREA(t, dptr, length) do { \ + t = malloc(sizeof(token_t), M_AUDITBSM, M_WAITOK); \ + t->t_data = malloc(length, M_AUDITBSM, M_WAITOK | M_ZERO); \ + t->len = length; \ + dptr = t->t_data; \ +} while (0) + +/* + * token ID 1 byte + * argument # 1 byte + * argument value 4 bytes/8 bytes (32-bit/64-bit value) + * text length 2 bytes + * text N bytes + 1 terminating NULL byte + */ +token_t * +au_to_arg32(char n, const char *text, u_int32_t v) +{ + token_t *t; + u_char *dptr = NULL; + u_int16_t textlen; + + textlen = strlen(text); + textlen += 1; + + GET_TOKEN_AREA(t, dptr, 2 * sizeof(u_char) + sizeof(u_int32_t) + + sizeof(u_int16_t) + textlen); + + ADD_U_CHAR(dptr, AUT_ARG32); + ADD_U_CHAR(dptr, n); + ADD_U_INT32(dptr, v); + ADD_U_INT16(dptr, textlen); + ADD_STRING(dptr, text, textlen); + + return (t); +} + +token_t * +au_to_arg64(char n, const char *text, u_int64_t v) +{ + token_t *t; + u_char *dptr = NULL; + u_int16_t textlen; + + textlen = strlen(text); + textlen += 1; + + GET_TOKEN_AREA(t, dptr, 2 * sizeof(u_char) + sizeof(u_int64_t) + + sizeof(u_int16_t) + textlen); + + ADD_U_CHAR(dptr, AUT_ARG64); + ADD_U_CHAR(dptr, n); + ADD_U_INT64(dptr, v); + ADD_U_INT16(dptr, textlen); + ADD_STRING(dptr, text, textlen); + + return (t); +} + +token_t * +au_to_arg(char n, const char *text, u_int32_t v) +{ + + return (au_to_arg32(n, text, v)); +} + +#if defined(_KERNEL) || defined(KERNEL) +/* + * token ID 1 byte + * file access mode 4 bytes + * owner user ID 4 bytes + * owner group ID 4 bytes + * file system ID 4 bytes + * node ID 8 bytes + * device 4 bytes/8 bytes (32-bit/64-bit) + */ +token_t * +au_to_attr32(struct vnode_au_info *vni) +{ + token_t *t; + u_char *dptr = NULL; + u_int16_t pad0_16 = 0; + u_int16_t pad0_32 = 0; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 2 * sizeof(u_int16_t) + + 3 * sizeof(u_int32_t) + sizeof(u_int64_t) + sizeof(u_int32_t)); + + ADD_U_CHAR(dptr, AUT_ATTR32); + + /* + * BSD defines the size for the file mode as 2 bytes; BSM defines 4 + * so pad with 0. + * + * XXXRW: Possibly should be conditionally compiled. + * + * XXXRW: Should any conversions take place on the mode? + */ + ADD_U_INT16(dptr, pad0_16); + ADD_U_INT16(dptr, vni->vn_mode); + + ADD_U_INT32(dptr, vni->vn_uid); + ADD_U_INT32(dptr, vni->vn_gid); + ADD_U_INT32(dptr, vni->vn_fsid); + + /* + * Some systems use 32-bit file ID's, others use 64-bit file IDs. + * Attempt to handle both, and let the compiler sort it out. If we + * could pick this out at compile-time, it would be better, so as to + * avoid the else case below. + */ + if (sizeof(vni->vn_fileid) == sizeof(uint32_t)) { + ADD_U_INT32(dptr, pad0_32); + ADD_U_INT32(dptr, vni->vn_fileid); + } else if (sizeof(vni->vn_fileid) == sizeof(uint64_t)) + ADD_U_INT64(dptr, vni->vn_fileid); + else + ADD_U_INT64(dptr, 0LL); + + ADD_U_INT32(dptr, vni->vn_dev); + + return (t); +} + +token_t * +au_to_attr64(struct vnode_au_info *vni) +{ + token_t *t; + u_char *dptr = NULL; + u_int16_t pad0_16 = 0; + u_int16_t pad0_32 = 0; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 2 * sizeof(u_int16_t) + + 3 * sizeof(u_int32_t) + sizeof(u_int64_t) * 2); + + ADD_U_CHAR(dptr, AUT_ATTR64); + + /* + * BSD defines the size for the file mode as 2 bytes; BSM defines 4 + * so pad with 0. + * + * XXXRW: Possibly should be conditionally compiled. + * + * XXXRW: Should any conversions take place on the mode? + */ + ADD_U_INT16(dptr, pad0_16); + ADD_U_INT16(dptr, vni->vn_mode); + + ADD_U_INT32(dptr, vni->vn_uid); + ADD_U_INT32(dptr, vni->vn_gid); + ADD_U_INT32(dptr, vni->vn_fsid); + + /* + * Some systems use 32-bit file ID's, other's use 64-bit file IDs. + * Attempt to handle both, and let the compiler sort it out. If we + * could pick this out at compile-time, it would be better, so as to + * avoid the else case below. + */ + if (sizeof(vni->vn_fileid) == sizeof(uint32_t)) { + ADD_U_INT32(dptr, pad0_32); + ADD_U_INT32(dptr, vni->vn_fileid); + } else if (sizeof(vni->vn_fileid) == sizeof(uint64_t)) + ADD_U_INT64(dptr, vni->vn_fileid); + else + ADD_U_INT64(dptr, 0LL); + + ADD_U_INT64(dptr, vni->vn_dev); + + return (t); +} + +token_t * +au_to_attr(struct vnode_au_info *vni) +{ + + return (au_to_attr32(vni)); +} +#endif /* !(defined(_KERNEL) || defined(KERNEL) */ + +/* + * token ID 1 byte + * how to print 1 byte + * basic unit 1 byte + * unit count 1 byte + * data items (depends on basic unit) + */ +token_t * +au_to_data(char unit_print, char unit_type, char unit_count, const char *p) +{ + token_t *t; + u_char *dptr = NULL; + size_t datasize, totdata; + + /* Determine the size of the basic unit. */ + switch (unit_type) { + case AUR_BYTE: + /* case AUR_CHAR: */ + datasize = AUR_BYTE_SIZE; + break; + + case AUR_SHORT: + datasize = AUR_SHORT_SIZE; + break; + + case AUR_INT32: + /* case AUR_INT: */ + datasize = AUR_INT32_SIZE; + break; + + case AUR_INT64: + datasize = AUR_INT64_SIZE; + break; + + default: + return (NULL); + } + + totdata = datasize * unit_count; + + GET_TOKEN_AREA(t, dptr, 4 * sizeof(u_char) + totdata); + + /* + * XXXRW: We should be byte-swapping each data item for multi-byte + * types. + */ + ADD_U_CHAR(dptr, AUT_DATA); + ADD_U_CHAR(dptr, unit_print); + ADD_U_CHAR(dptr, unit_type); + ADD_U_CHAR(dptr, unit_count); + ADD_MEM(dptr, p, totdata); + + return (t); +} + + +/* + * token ID 1 byte + * status 4 bytes + * return value 4 bytes + */ +token_t * +au_to_exit(int retval, int err) +{ + token_t *t; + u_char *dptr = NULL; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 2 * sizeof(u_int32_t)); + + ADD_U_CHAR(dptr, AUT_EXIT); + ADD_U_INT32(dptr, err); + ADD_U_INT32(dptr, retval); + + return (t); +} + +/* + */ +token_t * +au_to_groups(int *groups) +{ + + return (au_to_newgroups(AUDIT_MAX_GROUPS, (gid_t *)groups)); +} + +/* + * token ID 1 byte + * number groups 2 bytes + * group list count * 4 bytes + */ +token_t * +au_to_newgroups(u_int16_t n, gid_t *groups) +{ + token_t *t; + u_char *dptr = NULL; + int i; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) + + n * sizeof(u_int32_t)); + + ADD_U_CHAR(dptr, AUT_NEWGROUPS); + ADD_U_INT16(dptr, n); + for (i = 0; i < n; i++) + ADD_U_INT32(dptr, groups[i]); + + return (t); +} + +/* + * token ID 1 byte + * internet address 4 bytes + */ +token_t * +au_to_in_addr(struct in_addr *internet_addr) +{ + token_t *t; + u_char *dptr = NULL; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(uint32_t)); + + ADD_U_CHAR(dptr, AUT_IN_ADDR); + ADD_MEM(dptr, &internet_addr->s_addr, sizeof(uint32_t)); + + return (t); +} + +/* + * token ID 1 byte + * address type/length 4 bytes + * address 16 bytes + */ +token_t * +au_to_in_addr_ex(struct in6_addr *internet_addr) +{ + token_t *t; + u_char *dptr = NULL; + u_int32_t type = AU_IPv6; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 5 * sizeof(uint32_t)); + + ADD_U_CHAR(dptr, AUT_IN_ADDR_EX); + ADD_U_INT32(dptr, type); + ADD_MEM(dptr, internet_addr, 4 * sizeof(uint32_t)); + + return (t); +} + +/* + * token ID 1 byte + * ip header 20 bytes + * + * The IP header should be submitted in network byte order. + */ +token_t * +au_to_ip(struct ip *ip) +{ + token_t *t; + u_char *dptr = NULL; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(struct ip)); + + ADD_U_CHAR(dptr, AUT_IP); + ADD_MEM(dptr, ip, sizeof(struct ip)); + + return (t); +} + +/* + * token ID 1 byte + * object ID type 1 byte + * object ID 4 bytes + */ +token_t * +au_to_ipc(char type, int id) +{ + token_t *t; + u_char *dptr = NULL; + + GET_TOKEN_AREA(t, dptr, 2 * sizeof(u_char) + sizeof(u_int32_t)); + + ADD_U_CHAR(dptr, AUT_IPC); + ADD_U_CHAR(dptr, type); + ADD_U_INT32(dptr, id); + + return (t); +} + +/* + * token ID 1 byte + * owner user ID 4 bytes + * owner group ID 4 bytes + * creator user ID 4 bytes + * creator group ID 4 bytes + * access mode 4 bytes + * slot sequence # 4 bytes + * key 4 bytes + */ +token_t * +au_to_ipc_perm(struct ipc_perm *perm) +{ + token_t *t; + u_char *dptr = NULL; + u_int16_t pad0 = 0; + + GET_TOKEN_AREA(t, dptr, 12 * sizeof(u_int16_t) + sizeof(u_int32_t)); + + ADD_U_CHAR(dptr, AUT_IPC_PERM); + + /* + * Systems vary significantly in what types they use in struct + * ipc_perm; at least a few still use 16-bit uid's and gid's, so + * allow for that, as BSM define 32-bit values here. + * Some systems define the sizes for ipc_perm members as 2 bytes; + * BSM defines 4 so pad with 0. + * + * XXXRW: Possibly shoulid be conditionally compiled, and more cases + * need to be handled. + */ + if (sizeof(perm->uid) != sizeof(u_int32_t)) { + ADD_U_INT16(dptr, pad0); + ADD_U_INT16(dptr, perm->uid); + ADD_U_INT16(dptr, pad0); + ADD_U_INT16(dptr, perm->gid); + ADD_U_INT16(dptr, pad0); + ADD_U_INT16(dptr, perm->cuid); + ADD_U_INT16(dptr, pad0); + ADD_U_INT16(dptr, perm->cgid); + } else { + ADD_U_INT32(dptr, perm->uid); + ADD_U_INT32(dptr, perm->gid); + ADD_U_INT32(dptr, perm->cuid); + ADD_U_INT32(dptr, perm->cgid); + } + + ADD_U_INT16(dptr, pad0); + ADD_U_INT16(dptr, perm->mode); + + ADD_U_INT16(dptr, pad0); + + ADD_U_INT16(dptr, perm->seq); + + ADD_U_INT32(dptr, perm->key); + + return (t); +} + +/* + * token ID 1 byte + * port IP address 2 bytes + */ +token_t * +au_to_iport(u_int16_t iport) +{ + token_t *t; + u_char *dptr = NULL; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t)); + + ADD_U_CHAR(dptr, AUT_IPORT); + ADD_U_INT16(dptr, iport); + + return (t); +} + +/* + * token ID 1 byte + * size 2 bytes + * data size bytes + */ +token_t * +au_to_opaque(const char *data, u_int16_t bytes) +{ + token_t *t; + u_char *dptr = NULL; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) + bytes); + + ADD_U_CHAR(dptr, AUT_OPAQUE); + ADD_U_INT16(dptr, bytes); + ADD_MEM(dptr, data, bytes); + + return (t); +} + +/* + * token ID 1 byte + * seconds of time 4 bytes + * milliseconds of time 4 bytes + * file name len 2 bytes + * file pathname N bytes + 1 terminating NULL byte + */ +token_t * +au_to_file(const char *file, struct timeval tm) +{ + token_t *t; + u_char *dptr = NULL; + u_int16_t filelen; + u_int32_t timems; + + filelen = strlen(file); + filelen += 1; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 2 * sizeof(u_int32_t) + + sizeof(u_int16_t) + filelen); + + timems = tm.tv_usec/1000; + + ADD_U_CHAR(dptr, AUT_OTHER_FILE32); + ADD_U_INT32(dptr, tm.tv_sec); + ADD_U_INT32(dptr, timems); /* We need time in ms. */ + ADD_U_INT16(dptr, filelen); + ADD_STRING(dptr, file, filelen); + + return (t); +} + +/* + * token ID 1 byte + * text length 2 bytes + * text N bytes + 1 terminating NULL byte + */ +token_t * +au_to_text(const char *text) +{ + token_t *t; + u_char *dptr = NULL; + u_int16_t textlen; + + textlen = strlen(text); + textlen += 1; + + /* XXXRW: Should validate length against token size limit. */ + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) + textlen); + + ADD_U_CHAR(dptr, AUT_TEXT); + ADD_U_INT16(dptr, textlen); + ADD_STRING(dptr, text, textlen); + + return (t); +} + +/* + * token ID 1 byte + * path length 2 bytes + * path N bytes + 1 terminating NULL byte + */ +token_t * +au_to_path(const char *text) +{ + token_t *t; + u_char *dptr = NULL; + u_int16_t textlen; + + textlen = strlen(text); + textlen += 1; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) + textlen); + + ADD_U_CHAR(dptr, AUT_PATH); + ADD_U_INT16(dptr, textlen); + ADD_STRING(dptr, text, textlen); + + return (t); +} + +/* + * token ID 1 byte + * audit ID 4 bytes + * effective user ID 4 bytes + * effective group ID 4 bytes + * real user ID 4 bytes + * real group ID 4 bytes + * process ID 4 bytes + * session ID 4 bytes + * terminal ID + * port ID 4 bytes/8 bytes (32-bit/64-bit value) + * machine address 4 bytes + */ +token_t * +au_to_process32(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid, + pid_t pid, au_asid_t sid, au_tid_t *tid) +{ + token_t *t; + u_char *dptr = NULL; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 9 * sizeof(u_int32_t)); + + ADD_U_CHAR(dptr, AUT_PROCESS32); + ADD_U_INT32(dptr, auid); + ADD_U_INT32(dptr, euid); + ADD_U_INT32(dptr, egid); + ADD_U_INT32(dptr, ruid); + ADD_U_INT32(dptr, rgid); + ADD_U_INT32(dptr, pid); + ADD_U_INT32(dptr, sid); + ADD_U_INT32(dptr, tid->port); + + /* + * Note: Solaris will write out IPv6 addresses here as a 32-bit + * address type and 16 bytes of address, but for IPv4 addresses it + * simply writes the 4-byte address directly. We support only IPv4 + * addresses for process32 tokens. + */ + ADD_MEM(dptr, &tid->machine, sizeof(u_int32_t)); + + return (t); +} + +token_t * +au_to_process64(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid, + pid_t pid, au_asid_t sid, au_tid_t *tid) +{ + token_t *t; + u_char *dptr = NULL; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 8 * sizeof(u_int32_t) + + sizeof(u_int64_t)); + + ADD_U_CHAR(dptr, AUT_PROCESS64); + ADD_U_INT32(dptr, auid); + ADD_U_INT32(dptr, euid); + ADD_U_INT32(dptr, egid); + ADD_U_INT32(dptr, ruid); + ADD_U_INT32(dptr, rgid); + ADD_U_INT32(dptr, pid); + ADD_U_INT32(dptr, sid); + ADD_U_INT64(dptr, tid->port); + + /* + * Note: Solaris will write out IPv6 addresses here as a 32-bit + * address type and 16 bytes of address, but for IPv4 addresses it + * simply writes the 4-byte address directly. We support only IPv4 + * addresses for process64 tokens. + */ + ADD_MEM(dptr, &tid->machine, sizeof(u_int32_t)); + + return (t); +} + +token_t * +au_to_process(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid, + pid_t pid, au_asid_t sid, au_tid_t *tid) +{ + + return (au_to_process32(auid, euid, egid, ruid, rgid, pid, sid, + tid)); +} + +/* + * token ID 1 byte + * audit ID 4 bytes + * effective user ID 4 bytes + * effective group ID 4 bytes + * real user ID 4 bytes + * real group ID 4 bytes + * process ID 4 bytes + * session ID 4 bytes + * terminal ID + * port ID 4 bytes/8 bytes (32-bit/64-bit value) + * address type-len 4 bytes + * machine address 16 bytes + */ +token_t * +au_to_process32_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, + gid_t rgid, pid_t pid, au_asid_t sid, au_tid_addr_t *tid) +{ + token_t *t; + u_char *dptr = NULL; + + KASSERT((tid->at_type == AU_IPv4) || (tid->at_type == AU_IPv6), + ("au_to_process32_ex: type %u", (unsigned int)tid->at_type)); + if (tid->at_type == AU_IPv4) + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + + 10 * sizeof(u_int32_t)); + else + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + + 13 * sizeof(u_int32_t)); + + ADD_U_CHAR(dptr, AUT_PROCESS32_EX); + ADD_U_INT32(dptr, auid); + ADD_U_INT32(dptr, euid); + ADD_U_INT32(dptr, egid); + ADD_U_INT32(dptr, ruid); + ADD_U_INT32(dptr, rgid); + ADD_U_INT32(dptr, pid); + ADD_U_INT32(dptr, sid); + ADD_U_INT32(dptr, tid->at_port); + ADD_U_INT32(dptr, tid->at_type); + ADD_MEM(dptr, &tid->at_addr[0], sizeof(u_int32_t)); + if (tid->at_type == AU_IPv6) { + ADD_MEM(dptr, &tid->at_addr[1], sizeof(u_int32_t)); + ADD_MEM(dptr, &tid->at_addr[2], sizeof(u_int32_t)); + ADD_MEM(dptr, &tid->at_addr[3], sizeof(u_int32_t)); + } + + return (t); +} + +token_t * +au_to_process64_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, + gid_t rgid, pid_t pid, au_asid_t sid, au_tid_addr_t *tid) +{ + token_t *t; + u_char *dptr = NULL; + + if (tid->at_type == AU_IPv4) + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + + 7 * sizeof(u_int32_t) + sizeof(u_int64_t) + + 2 * sizeof(u_int32_t)); + else if (tid->at_type == AU_IPv6) + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + + 7 * sizeof(u_int32_t) + sizeof(u_int64_t) + + 5 * sizeof(u_int32_t)); + else + panic("au_to_process64_ex: invalidate at_type (%d)", + tid->at_type); + + ADD_U_CHAR(dptr, AUT_PROCESS64_EX); + ADD_U_INT32(dptr, auid); + ADD_U_INT32(dptr, euid); + ADD_U_INT32(dptr, egid); + ADD_U_INT32(dptr, ruid); + ADD_U_INT32(dptr, rgid); + ADD_U_INT32(dptr, pid); + ADD_U_INT32(dptr, sid); + ADD_U_INT64(dptr, tid->at_port); + ADD_U_INT32(dptr, tid->at_type); + ADD_MEM(dptr, &tid->at_addr[0], sizeof(u_int32_t)); + if (tid->at_type == AU_IPv6) { + ADD_MEM(dptr, &tid->at_addr[1], sizeof(u_int32_t)); + ADD_MEM(dptr, &tid->at_addr[2], sizeof(u_int32_t)); + ADD_MEM(dptr, &tid->at_addr[3], sizeof(u_int32_t)); + } + + return (t); +} + +token_t * +au_to_process_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, + gid_t rgid, pid_t pid, au_asid_t sid, au_tid_addr_t *tid) +{ + + return (au_to_process32_ex(auid, euid, egid, ruid, rgid, pid, sid, + tid)); +} + +/* + * token ID 1 byte + * error status 1 byte + * return value 4 bytes/8 bytes (32-bit/64-bit value) + */ +token_t * +au_to_return32(char status, u_int32_t ret) +{ + token_t *t; + u_char *dptr = NULL; + + GET_TOKEN_AREA(t, dptr, 2 * sizeof(u_char) + sizeof(u_int32_t)); + + ADD_U_CHAR(dptr, AUT_RETURN32); + ADD_U_CHAR(dptr, status); + ADD_U_INT32(dptr, ret); + + return (t); +} + +token_t * +au_to_return64(char status, u_int64_t ret) +{ + token_t *t; + u_char *dptr = NULL; + + GET_TOKEN_AREA(t, dptr, 2 * sizeof(u_char) + sizeof(u_int64_t)); + + ADD_U_CHAR(dptr, AUT_RETURN64); + ADD_U_CHAR(dptr, status); + ADD_U_INT64(dptr, ret); + + return (t); +} + +token_t * +au_to_return(char status, u_int32_t ret) +{ + + return (au_to_return32(status, ret)); +} + +/* + * token ID 1 byte + * sequence number 4 bytes + */ +token_t * +au_to_seq(long audit_count) +{ + token_t *t; + u_char *dptr = NULL; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t)); + + ADD_U_CHAR(dptr, AUT_SEQ); + ADD_U_INT32(dptr, audit_count); + + return (t); +} + +/* + * token ID 1 byte + * socket domain 2 bytes + * socket type 2 bytes + * address type 2 byte + * local port 2 bytes + * local address 4 bytes/16 bytes (IPv4/IPv6 address) + * remote port 2 bytes + * remote address 4 bytes/16 bytes (IPv4/IPv6 address) + */ +token_t * +au_to_socket_ex(u_short so_domain, u_short so_type, + struct sockaddr *sa_local, struct sockaddr *sa_remote) +{ + token_t *t; + u_char *dptr = NULL; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + if (so_domain == AF_INET) + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + + 5 * sizeof(u_int16_t) + 2 * sizeof(u_int32_t)); + else if (so_domain == AF_INET6) + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + + 5 * sizeof(u_int16_t) + 16 * sizeof(u_int32_t)); + else + return (NULL); + + ADD_U_CHAR(dptr, AUT_SOCKET_EX); + ADD_U_INT16(dptr, so_domain); /* XXXRW: explicitly convert? */ + ADD_U_INT16(dptr, so_type); /* XXXRW: explicitly convert? */ + if (so_domain == AF_INET) { + ADD_U_INT16(dptr, AU_IPv4); + sin = (struct sockaddr_in *)sa_local; + ADD_MEM(dptr, &sin->sin_port, sizeof(uint16_t)); + ADD_MEM(dptr, &sin->sin_addr.s_addr, sizeof(uint32_t)); + sin = (struct sockaddr_in *)sa_remote; + ADD_MEM(dptr, &sin->sin_port, sizeof(uint16_t)); + ADD_MEM(dptr, &sin->sin_addr.s_addr, sizeof(uint32_t)); + } else { + ADD_U_INT16(dptr, AU_IPv6); + sin6 = (struct sockaddr_in6 *)sa_local; + ADD_MEM(dptr, &sin6->sin6_port, sizeof(uint16_t)); + ADD_MEM(dptr, &sin6->sin6_addr, 4 * sizeof(uint32_t)); + sin6 = (struct sockaddr_in6 *)sa_remote; + ADD_MEM(dptr, &sin6->sin6_port, sizeof(uint16_t)); + ADD_MEM(dptr, &sin6->sin6_addr, 4 * sizeof(uint32_t)); + } + + return (t); +} + +/* + * Kernel-specific version of the above function. + * + * XXXRW: Should now use au_to_socket_ex() here. + */ +#ifdef _KERNEL +token_t * +kau_to_socket(struct socket_au_info *soi) +{ + token_t *t; + u_char *dptr; + u_int16_t so_type; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 2 * sizeof(u_int16_t) + + sizeof(u_int32_t) + sizeof(u_int16_t) + sizeof(u_int32_t)); + + ADD_U_CHAR(dptr, AUT_SOCKET); + /* Coerce the socket type into a short value */ + so_type = soi->so_type; + ADD_U_INT16(dptr, so_type); + ADD_U_INT16(dptr, soi->so_lport); + ADD_U_INT32(dptr, soi->so_laddr); + ADD_U_INT16(dptr, soi->so_rport); + ADD_U_INT32(dptr, soi->so_raddr); + + return (t); +} +#endif + +/* + * token ID 1 byte + * socket family 2 bytes + * path 104 bytes + */ +token_t * +au_to_sock_unix(struct sockaddr_un *so) +{ + token_t *t; + u_char *dptr; + + GET_TOKEN_AREA(t, dptr, 3 * sizeof(u_char) + strlen(so->sun_path) + 1); + + ADD_U_CHAR(dptr, AUT_SOCKUNIX); + /* BSM token has two bytes for family */ + ADD_U_CHAR(dptr, 0); + ADD_U_CHAR(dptr, so->sun_family); + ADD_STRING(dptr, so->sun_path, strlen(so->sun_path) + 1); + + return (t); +} + +/* + * token ID 1 byte + * socket family 2 bytes + * local port 2 bytes + * socket address 4 bytes + */ +token_t * +au_to_sock_inet32(struct sockaddr_in *so) +{ + token_t *t; + u_char *dptr = NULL; + uint16_t family; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 2 * sizeof(uint16_t) + + sizeof(uint32_t)); + + ADD_U_CHAR(dptr, AUT_SOCKINET32); + /* + * BSM defines the family field as 16 bits, but many operating + * systems have an 8-bit sin_family field. Extend to 16 bits before + * writing into the token. Assume that both the port and the address + * in the sockaddr_in are already in network byte order, but family + * is in local byte order. + * + * XXXRW: Should a name space conversion be taking place on the value + * of sin_family? + */ + family = so->sin_family; + ADD_U_INT16(dptr, family); + ADD_MEM(dptr, &so->sin_port, sizeof(uint16_t)); + ADD_MEM(dptr, &so->sin_addr.s_addr, sizeof(uint32_t)); + + return (t); +} + +token_t * +au_to_sock_inet128(struct sockaddr_in6 *so) +{ + token_t *t; + u_char *dptr = NULL; + + GET_TOKEN_AREA(t, dptr, 3 * sizeof(u_char) + sizeof(u_int16_t) + + 4 * sizeof(u_int32_t)); + + ADD_U_CHAR(dptr, AUT_SOCKINET128); + /* + * In BSD, sin6_family is one octet, but BSM defines the token to + * store two. So we copy in a 0 first. XXXRW: Possibly should be + * conditionally compiled. + */ + ADD_U_CHAR(dptr, 0); + ADD_U_CHAR(dptr, so->sin6_family); + + ADD_U_INT16(dptr, so->sin6_port); + ADD_MEM(dptr, &so->sin6_addr, 4 * sizeof(uint32_t)); + + return (t); +} + +token_t * +au_to_sock_inet(struct sockaddr_in *so) +{ + + return (au_to_sock_inet32(so)); +} + +/* + * token ID 1 byte + * audit ID 4 bytes + * effective user ID 4 bytes + * effective group ID 4 bytes + * real user ID 4 bytes + * real group ID 4 bytes + * process ID 4 bytes + * session ID 4 bytes + * terminal ID + * port ID 4 bytes/8 bytes (32-bit/64-bit value) + * machine address 4 bytes + */ +token_t * +au_to_subject32(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid, + pid_t pid, au_asid_t sid, au_tid_t *tid) +{ + token_t *t; + u_char *dptr = NULL; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 9 * sizeof(u_int32_t)); + + ADD_U_CHAR(dptr, AUT_SUBJECT32); + ADD_U_INT32(dptr, auid); + ADD_U_INT32(dptr, euid); + ADD_U_INT32(dptr, egid); + ADD_U_INT32(dptr, ruid); + ADD_U_INT32(dptr, rgid); + ADD_U_INT32(dptr, pid); + ADD_U_INT32(dptr, sid); + ADD_U_INT32(dptr, tid->port); + ADD_MEM(dptr, &tid->machine, sizeof(u_int32_t)); + + return (t); +} + +token_t * +au_to_subject64(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid, + pid_t pid, au_asid_t sid, au_tid_t *tid) +{ + token_t *t; + u_char *dptr = NULL; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 7 * sizeof(u_int32_t) + + sizeof(u_int64_t) + sizeof(u_int32_t)); + + ADD_U_CHAR(dptr, AUT_SUBJECT64); + ADD_U_INT32(dptr, auid); + ADD_U_INT32(dptr, euid); + ADD_U_INT32(dptr, egid); + ADD_U_INT32(dptr, ruid); + ADD_U_INT32(dptr, rgid); + ADD_U_INT32(dptr, pid); + ADD_U_INT32(dptr, sid); + ADD_U_INT64(dptr, tid->port); + ADD_MEM(dptr, &tid->machine, sizeof(u_int32_t)); + + return (t); +} + +token_t * +au_to_subject(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid, + pid_t pid, au_asid_t sid, au_tid_t *tid) +{ + + return (au_to_subject32(auid, euid, egid, ruid, rgid, pid, sid, + tid)); +} + +/* + * token ID 1 byte + * audit ID 4 bytes + * effective user ID 4 bytes + * effective group ID 4 bytes + * real user ID 4 bytes + * real group ID 4 bytes + * process ID 4 bytes + * session ID 4 bytes + * terminal ID + * port ID 4 bytes/8 bytes (32-bit/64-bit value) + * address type/length 4 bytes + * machine address 16 bytes + */ +token_t * +au_to_subject32_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, + gid_t rgid, pid_t pid, au_asid_t sid, au_tid_addr_t *tid) +{ + token_t *t; + u_char *dptr = NULL; + + KASSERT((tid->at_type == AU_IPv4) || (tid->at_type == AU_IPv6), + ("au_to_subject32_ex: type %u", (unsigned int)tid->at_type)); + + if (tid->at_type == AU_IPv4) + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 10 * + sizeof(u_int32_t)); + else + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 13 * + sizeof(u_int32_t)); + + ADD_U_CHAR(dptr, AUT_SUBJECT32_EX); + ADD_U_INT32(dptr, auid); + ADD_U_INT32(dptr, euid); + ADD_U_INT32(dptr, egid); + ADD_U_INT32(dptr, ruid); + ADD_U_INT32(dptr, rgid); + ADD_U_INT32(dptr, pid); + ADD_U_INT32(dptr, sid); + ADD_U_INT32(dptr, tid->at_port); + ADD_U_INT32(dptr, tid->at_type); + if (tid->at_type == AU_IPv6) + ADD_MEM(dptr, &tid->at_addr[0], 4 * sizeof(u_int32_t)); + else + ADD_MEM(dptr, &tid->at_addr[0], sizeof(u_int32_t)); + + return (t); +} + +token_t * +au_to_subject64_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, + gid_t rgid, pid_t pid, au_asid_t sid, au_tid_addr_t *tid) +{ + token_t *t; + u_char *dptr = NULL; + + KASSERT((tid->at_type == AU_IPv4) || (tid->at_type == AU_IPv6), + ("au_to_subject64_ex: type %u", (unsigned int)tid->at_type)); + + if (tid->at_type == AU_IPv4) + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + + 7 * sizeof(u_int32_t) + sizeof(u_int64_t) + + 2 * sizeof(u_int32_t)); + else + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + + 7 * sizeof(u_int32_t) + sizeof(u_int64_t) + + 5 * sizeof(u_int32_t)); + + ADD_U_CHAR(dptr, AUT_SUBJECT64_EX); + ADD_U_INT32(dptr, auid); + ADD_U_INT32(dptr, euid); + ADD_U_INT32(dptr, egid); + ADD_U_INT32(dptr, ruid); + ADD_U_INT32(dptr, rgid); + ADD_U_INT32(dptr, pid); + ADD_U_INT32(dptr, sid); + ADD_U_INT64(dptr, tid->at_port); + ADD_U_INT32(dptr, tid->at_type); + if (tid->at_type == AU_IPv6) + ADD_MEM(dptr, &tid->at_addr[0], 4 * sizeof(u_int32_t)); + else + ADD_MEM(dptr, &tid->at_addr[0], sizeof(u_int32_t)); + + return (t); +} + +token_t * +au_to_subject_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, + gid_t rgid, pid_t pid, au_asid_t sid, au_tid_addr_t *tid) +{ + + return (au_to_subject32_ex(auid, euid, egid, ruid, rgid, pid, sid, + tid)); +} + +#if !defined(_KERNEL) && !defined(KERNEL) && defined(HAVE_AUDIT_SYSCALLS) +/* + * Collects audit information for the current process and creates a subject + * token from it. + */ +token_t * +au_to_me(void) +{ + auditinfo_t auinfo; + + if (getaudit(&auinfo) != 0) + return (NULL); + + return (au_to_subject32(auinfo.ai_auid, geteuid(), getegid(), + getuid(), getgid(), getpid(), auinfo.ai_asid, &auinfo.ai_termid)); +} +#endif + +#if defined(_KERNEL) || defined(KERNEL) +static token_t * +au_to_exec_strings(char *strs, int count, u_char type) +{ + token_t *t; + u_char *dptr = NULL; + u_int32_t totlen; + int ctr; + char *p; + + totlen = 0; + ctr = count; + p = strs; + while (ctr-- > 0) { + totlen += strlen(p) + 1; + p = strs + totlen; + } + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t) + totlen); + ADD_U_CHAR(dptr, type); + ADD_U_INT32(dptr, count); + ADD_STRING(dptr, strs, totlen); + + return (t); +} + +/* + * token ID 1 byte + * count 4 bytes + * text count null-terminated strings + */ +token_t * +au_to_exec_args(char *args, int argc) +{ + + return (au_to_exec_strings(args, argc, AUT_EXEC_ARGS)); +} + +/* + * token ID 1 byte + * count 4 bytes + * text count null-terminated strings + */ +token_t * +au_to_exec_env(char *envs, int envc) +{ + + return (au_to_exec_strings(envs, envc, AUT_EXEC_ENV)); +} +#else +/* + * token ID 1 byte + * count 4 bytes + * text count null-terminated strings + */ +token_t * +au_to_exec_args(char **argv) +{ + token_t *t; + u_char *dptr = NULL; + const char *nextarg; + int i, count = 0; + size_t totlen = 0; + + nextarg = *argv; + + while (nextarg != NULL) { + int nextlen; + + nextlen = strlen(nextarg); + totlen += nextlen + 1; + count++; + nextarg = *(argv + count); + } + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t) + totlen); + + ADD_U_CHAR(dptr, AUT_EXEC_ARGS); + ADD_U_INT32(dptr, count); + + for (i = 0; i < count; i++) { + nextarg = *(argv + i); + ADD_MEM(dptr, nextarg, strlen(nextarg) + 1); + } + + return (t); +} + +/* + * token ID 1 byte + * count 4 bytes + * text count null-terminated strings + */ +token_t * +au_to_exec_env(char **envp) +{ + token_t *t; + u_char *dptr = NULL; + int i, count = 0; + size_t totlen = 0; + const char *nextenv; + + nextenv = *envp; + + while (nextenv != NULL) { + int nextlen; + + nextlen = strlen(nextenv); + totlen += nextlen + 1; + count++; + nextenv = *(envp + count); + } + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t) + totlen); + + ADD_U_CHAR(dptr, AUT_EXEC_ENV); + ADD_U_INT32(dptr, count); + + for (i = 0; i < count; i++) { + nextenv = *(envp + i); + ADD_MEM(dptr, nextenv, strlen(nextenv) + 1); + } + + return (t); +} +#endif + +/* + * token ID 1 byte + * zonename length 2 bytes + * zonename N bytes + 1 terminating NULL byte + */ +token_t * +au_to_zonename(const char *zonename) +{ + u_char *dptr = NULL; + u_int16_t textlen; + token_t *t; + + textlen = strlen(zonename) + 1; + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) + textlen); + + ADD_U_CHAR(dptr, AUT_ZONENAME); + ADD_U_INT16(dptr, textlen); + ADD_STRING(dptr, zonename, textlen); + return (t); +} + +/* + * token ID 1 byte + * record byte count 4 bytes + * version # 1 byte [2] + * event type 2 bytes + * event modifier 2 bytes + * seconds of time 4 bytes/8 bytes (32-bit/64-bit value) + * milliseconds of time 4 bytes/8 bytes (32-bit/64-bit value) + */ +token_t * +au_to_header32_tm(int rec_size, au_event_t e_type, au_emod_t e_mod, + struct timeval tm) +{ + token_t *t; + u_char *dptr = NULL; + u_int32_t timems; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t) + + sizeof(u_char) + 2 * sizeof(u_int16_t) + 2 * sizeof(u_int32_t)); + + ADD_U_CHAR(dptr, AUT_HEADER32); + ADD_U_INT32(dptr, rec_size); + ADD_U_CHAR(dptr, AUDIT_HEADER_VERSION_OPENBSM); + ADD_U_INT16(dptr, e_type); + ADD_U_INT16(dptr, e_mod); + + timems = tm.tv_usec/1000; + /* Add the timestamp */ + ADD_U_INT32(dptr, tm.tv_sec); + ADD_U_INT32(dptr, timems); /* We need time in ms. */ + + return (t); +} + +/* + * token ID 1 byte + * record byte count 4 bytes + * version # 1 byte [2] + * event type 2 bytes + * event modifier 2 bytes + * address type/length 4 bytes + * machine address 4 bytes/16 bytes (IPv4/IPv6 address) + * seconds of time 4 bytes/8 bytes (32-bit/64-bit value) + * milliseconds of time 4 bytes/8 bytes (32-bit/64-bit value) + */ +token_t * +au_to_header32_ex_tm(int rec_size, au_event_t e_type, au_emod_t e_mod, + struct timeval tm, struct auditinfo_addr *aia) +{ + token_t *t; + u_char *dptr = NULL; + u_int32_t timems; + au_tid_addr_t *tid; + + tid = &aia->ai_termid; + KASSERT(tid->at_type == AU_IPv4 || tid->at_type == AU_IPv6, + ("au_to_header32_ex_tm: invalid address family")); + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t) + + sizeof(u_char) + 2 * sizeof(u_int16_t) + 3 * + sizeof(u_int32_t) + tid->at_type); + + ADD_U_CHAR(dptr, AUT_HEADER32_EX); + ADD_U_INT32(dptr, rec_size); + ADD_U_CHAR(dptr, AUDIT_HEADER_VERSION_OPENBSM); + ADD_U_INT16(dptr, e_type); + ADD_U_INT16(dptr, e_mod); + + ADD_U_INT32(dptr, tid->at_type); + if (tid->at_type == AU_IPv6) + ADD_MEM(dptr, &tid->at_addr[0], 4 * sizeof(u_int32_t)); + else + ADD_MEM(dptr, &tid->at_addr[0], sizeof(u_int32_t)); + timems = tm.tv_usec/1000; + /* Add the timestamp */ + ADD_U_INT32(dptr, tm.tv_sec); + ADD_U_INT32(dptr, timems); /* We need time in ms. */ + + return (t); +} + +token_t * +au_to_header64_tm(int rec_size, au_event_t e_type, au_emod_t e_mod, + struct timeval tm) +{ + token_t *t; + u_char *dptr = NULL; + u_int32_t timems; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t) + + sizeof(u_char) + 2 * sizeof(u_int16_t) + 2 * sizeof(u_int64_t)); + + ADD_U_CHAR(dptr, AUT_HEADER64); + ADD_U_INT32(dptr, rec_size); + ADD_U_CHAR(dptr, AUDIT_HEADER_VERSION_OPENBSM); + ADD_U_INT16(dptr, e_type); + ADD_U_INT16(dptr, e_mod); + + timems = tm.tv_usec/1000; + /* Add the timestamp */ + ADD_U_INT64(dptr, tm.tv_sec); + ADD_U_INT64(dptr, timems); /* We need time in ms. */ + + return (t); +} + +#if !defined(KERNEL) && !defined(_KERNEL) +#ifdef HAVE_AUDIT_SYSCALLS +token_t * +au_to_header32_ex(int rec_size, au_event_t e_type, au_emod_t e_mod) +{ + struct timeval tm; + struct auditinfo_addr aia; + + if (gettimeofday(&tm, NULL) == -1) + return (NULL); + if (auditon(A_GETKAUDIT, &aia, sizeof(aia)) < 0) { + if (errno != ENOSYS) + return (NULL); + return (au_to_header32_tm(rec_size, e_type, e_mod, tm)); + } + return (au_to_header32_ex_tm(rec_size, e_type, e_mod, tm, &aia)); +} +#endif /* HAVE_AUDIT_SYSCALLS */ + +token_t * +au_to_header32(int rec_size, au_event_t e_type, au_emod_t e_mod) +{ + struct timeval tm; + + if (gettimeofday(&tm, NULL) == -1) + return (NULL); + return (au_to_header32_tm(rec_size, e_type, e_mod, tm)); +} + +token_t * +au_to_header64(__unused int rec_size, __unused au_event_t e_type, + __unused au_emod_t e_mod) +{ + struct timeval tm; + + if (gettimeofday(&tm, NULL) == -1) + return (NULL); + return (au_to_header64_tm(rec_size, e_type, e_mod, tm)); +} + +token_t * +au_to_header(int rec_size, au_event_t e_type, au_emod_t e_mod) +{ + + return (au_to_header32(rec_size, e_type, e_mod)); +} + +#ifdef HAVE_AUDIT_SYSCALLS +token_t * +au_to_header_ex(int rec_size, au_event_t e_type, au_emod_t e_mod) +{ + + return (au_to_header32_ex(rec_size, e_type, e_mod)); +} +#endif /* HAVE_AUDIT_SYSCALLS */ +#endif /* !defined(KERNEL) && !defined(_KERNEL) */ + +/* + * token ID 1 byte + * trailer magic number 2 bytes + * record byte count 4 bytes + */ +token_t * +au_to_trailer(int rec_size) +{ + token_t *t; + u_char *dptr = NULL; + u_int16_t magic = AUT_TRAILER_MAGIC; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) + + sizeof(u_int32_t)); + + ADD_U_CHAR(dptr, AUT_TRAILER); + ADD_U_INT16(dptr, magic); + ADD_U_INT32(dptr, rec_size); + + return (t); +} diff --git a/sys/security/audit/audit_ioctl.h b/sys/security/audit/audit_ioctl.h new file mode 100644 index 0000000..edf0339 --- /dev/null +++ b/sys/security/audit/audit_ioctl.h @@ -0,0 +1,83 @@ +/*- + * Copyright (c) 2006 Robert N. M. Watson + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SECURITY_AUDIT_AUDIT_IOCTL_H_ +#define _SECURITY_AUDIT_AUDIT_IOCTL_H_ + +#define AUDITPIPE_IOBASE 'A' + +/* + * Data structures used for complex ioctl arguments. Do not change existing + * structures, add new revised ones to be used by new ioctls, and keep the + * old structures and ioctls for backwards compatibility. + */ +struct auditpipe_ioctl_preselect { + au_id_t aip_auid; + au_mask_t aip_mask; +}; + +/* + * Possible modes of operation for audit pipe preselection. + */ +#define AUDITPIPE_PRESELECT_MODE_TRAIL 1 /* Global audit trail. */ +#define AUDITPIPE_PRESELECT_MODE_LOCAL 2 /* Local audit trail. */ + +/* + * Ioctls to read and control the behavior of individual audit pipe devices. + */ +#define AUDITPIPE_GET_QLEN _IOR(AUDITPIPE_IOBASE, 1, u_int) +#define AUDITPIPE_GET_QLIMIT _IOR(AUDITPIPE_IOBASE, 2, u_int) +#define AUDITPIPE_SET_QLIMIT _IOW(AUDITPIPE_IOBASE, 3, u_int) +#define AUDITPIPE_GET_QLIMIT_MIN _IOR(AUDITPIPE_IOBASE, 4, u_int) +#define AUDITPIPE_GET_QLIMIT_MAX _IOR(AUDITPIPE_IOBASE, 5, u_int) +#define AUDITPIPE_GET_PRESELECT_FLAGS _IOR(AUDITPIPE_IOBASE, 6, au_mask_t) +#define AUDITPIPE_SET_PRESELECT_FLAGS _IOW(AUDITPIPE_IOBASE, 7, au_mask_t) +#define AUDITPIPE_GET_PRESELECT_NAFLAGS _IOR(AUDITPIPE_IOBASE, 8, au_mask_t) +#define AUDITPIPE_SET_PRESELECT_NAFLAGS _IOW(AUDITPIPE_IOBASE, 9, au_mask_t) +#define AUDITPIPE_GET_PRESELECT_AUID _IOR(AUDITPIPE_IOBASE, 10, \ + struct auditpipe_ioctl_preselect) +#define AUDITPIPE_SET_PRESELECT_AUID _IOW(AUDITPIPE_IOBASE, 11, \ + struct auditpipe_ioctl_preselect) +#define AUDITPIPE_DELETE_PRESELECT_AUID _IOW(AUDITPIPE_IOBASE, 12, au_id_t) +#define AUDITPIPE_FLUSH_PRESELECT_AUID _IO(AUDITPIPE_IOBASE, 13) +#define AUDITPIPE_GET_PRESELECT_MODE _IOR(AUDITPIPE_IOBASE, 14, int) +#define AUDITPIPE_SET_PRESELECT_MODE _IOW(AUDITPIPE_IOBASE, 15, int) +#define AUDITPIPE_FLUSH _IO(AUDITPIPE_IOBASE, 16) +#define AUDITPIPE_GET_MAXAUDITDATA _IOR(AUDITPIPE_IOBASE, 17, u_int) + +/* + * Ioctls to retrieve audit pipe statistics. + */ +#define AUDITPIPE_GET_INSERTS _IOR(AUDITPIPE_IOBASE, 100, u_int64_t) +#define AUDITPIPE_GET_READS _IOR(AUDITPIPE_IOBASE, 101, u_int64_t) +#define AUDITPIPE_GET_DROPS _IOR(AUDITPIPE_IOBASE, 102, u_int64_t) +#define AUDITPIPE_GET_TRUNCATES _IOR(AUDITPIPE_IOBASE, 103, u_int64_t) + +#endif /* _SECURITY_AUDIT_AUDIT_IOCTL_H_ */ diff --git a/sys/security/audit/audit_pipe.c b/sys/security/audit/audit_pipe.c new file mode 100644 index 0000000..f12a2b1 --- /dev/null +++ b/sys/security/audit/audit_pipe.c @@ -0,0 +1,1129 @@ +/*- + * Copyright (c) 2006 Robert N. M. Watson + * Copyright (c) 2008 Apple, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/condvar.h> +#include <sys/conf.h> +#include <sys/eventhandler.h> +#include <sys/filio.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/poll.h> +#include <sys/proc.h> +#include <sys/queue.h> +#include <sys/rwlock.h> +#include <sys/selinfo.h> +#include <sys/sigio.h> +#include <sys/signal.h> +#include <sys/signalvar.h> +#include <sys/sx.h> +#include <sys/systm.h> +#include <sys/uio.h> + +#include <security/audit/audit.h> +#include <security/audit/audit_ioctl.h> +#include <security/audit/audit_private.h> + +/* + * Implementation of a clonable special device providing a live stream of BSM + * audit data. Consumers receive a "tee" of the system audit trail by + * default, but may also define alternative event selections using ioctls. + * This interface provides unreliable but timely access to audit events. + * Consumers should be very careful to avoid introducing event cycles. + */ + +/* + * Memory types. + */ +static MALLOC_DEFINE(M_AUDIT_PIPE, "audit_pipe", "Audit pipes"); +static MALLOC_DEFINE(M_AUDIT_PIPE_ENTRY, "audit_pipeent", + "Audit pipe entries and buffers"); +static MALLOC_DEFINE(M_AUDIT_PIPE_PRESELECT, "audit_pipe_presel", + "Audit pipe preselection structure"); + +/* + * Audit pipe buffer parameters. + */ +#define AUDIT_PIPE_QLIMIT_DEFAULT (128) +#define AUDIT_PIPE_QLIMIT_MIN (0) +#define AUDIT_PIPE_QLIMIT_MAX (1024) + +/* + * Description of an entry in an audit_pipe. + */ +struct audit_pipe_entry { + void *ape_record; + u_int ape_record_len; + TAILQ_ENTRY(audit_pipe_entry) ape_queue; +}; + +/* + * Audit pipes allow processes to express "interest" in the set of records + * that are delivered via the pipe. They do this in a similar manner to the + * mechanism for audit trail configuration, by expressing two global masks, + * and optionally expressing per-auid masks. The following data structure is + * the per-auid mask description. The global state is stored in the audit + * pipe data structure. + * + * We may want to consider a more space/time-efficient data structure once + * usage patterns for per-auid specifications are clear. + */ +struct audit_pipe_preselect { + au_id_t app_auid; + au_mask_t app_mask; + TAILQ_ENTRY(audit_pipe_preselect) app_list; +}; + +/* + * Description of an individual audit_pipe. Consists largely of a bounded + * length queue. + */ +#define AUDIT_PIPE_ASYNC 0x00000001 +#define AUDIT_PIPE_NBIO 0x00000002 +struct audit_pipe { + int ap_open; /* Device open? */ + u_int ap_flags; + + struct selinfo ap_selinfo; + struct sigio *ap_sigio; + + /* + * Per-pipe mutex protecting most fields in this data structure. + */ + struct mtx ap_mtx; + + /* + * Per-pipe sleep lock serializing user-generated reads and flushes. + * uiomove() is called to copy out the current head record's data + * while the record remains in the queue, so we prevent other threads + * from removing it using this lock. + */ + struct sx ap_sx; + + /* + * Condition variable to signal when data has been delivered to a + * pipe. + */ + struct cv ap_cv; + + /* + * Various queue-reated variables: qlen and qlimit are a count of + * records in the queue; qbyteslen is the number of bytes of data + * across all records, and qoffset is the amount read so far of the + * first record in the queue. The number of bytes available for + * reading in the queue is qbyteslen - qoffset. + */ + u_int ap_qlen; + u_int ap_qlimit; + u_int ap_qbyteslen; + u_int ap_qoffset; + + /* + * Per-pipe operation statistics. + */ + u_int64_t ap_inserts; /* Records added. */ + u_int64_t ap_reads; /* Records read. */ + u_int64_t ap_drops; /* Records dropped. */ + + /* + * Fields relating to pipe interest: global masks for unmatched + * processes (attributable, non-attributable), and a list of specific + * interest specifications by auid. + */ + int ap_preselect_mode; + au_mask_t ap_preselect_flags; + au_mask_t ap_preselect_naflags; + TAILQ_HEAD(, audit_pipe_preselect) ap_preselect_list; + + /* + * Current pending record list. Protected by a combination of ap_mtx + * and ap_sx. Note particularly that *both* locks are required to + * remove a record from the head of the queue, as an in-progress read * may sleep while copying and therefore cannot hold ap_mtx. + */ + TAILQ_HEAD(, audit_pipe_entry) ap_queue; + + /* + * Global pipe list. + */ + TAILQ_ENTRY(audit_pipe) ap_list; +}; + +#define AUDIT_PIPE_LOCK(ap) mtx_lock(&(ap)->ap_mtx) +#define AUDIT_PIPE_LOCK_ASSERT(ap) mtx_assert(&(ap)->ap_mtx, MA_OWNED) +#define AUDIT_PIPE_LOCK_DESTROY(ap) mtx_destroy(&(ap)->ap_mtx) +#define AUDIT_PIPE_LOCK_INIT(ap) mtx_init(&(ap)->ap_mtx, \ + "audit_pipe_mtx", NULL, MTX_DEF) +#define AUDIT_PIPE_UNLOCK(ap) mtx_unlock(&(ap)->ap_mtx) +#define AUDIT_PIPE_MTX(ap) (&(ap)->ap_mtx) + +#define AUDIT_PIPE_SX_LOCK_DESTROY(ap) sx_destroy(&(ap)->ap_sx) +#define AUDIT_PIPE_SX_LOCK_INIT(ap) sx_init(&(ap)->ap_sx, "audit_pipe_sx") +#define AUDIT_PIPE_SX_XLOCK_ASSERT(ap) sx_assert(&(ap)->ap_sx, SA_XLOCKED) +#define AUDIT_PIPE_SX_XLOCK_SIG(ap) sx_xlock_sig(&(ap)->ap_sx) +#define AUDIT_PIPE_SX_XUNLOCK(ap) sx_xunlock(&(ap)->ap_sx) + +/* + * Global list of audit pipes, rwlock to protect it. Individual record + * queues on pipes are protected by per-pipe locks; these locks synchronize + * between threads walking the list to deliver to individual pipes and add/ + * remove of pipes, and are mostly acquired for read. + */ +static TAILQ_HEAD(, audit_pipe) audit_pipe_list; +static struct rwlock audit_pipe_lock; + +#define AUDIT_PIPE_LIST_LOCK_INIT() rw_init(&audit_pipe_lock, \ + "audit_pipe_list_lock") +#define AUDIT_PIPE_LIST_RLOCK() rw_rlock(&audit_pipe_lock) +#define AUDIT_PIPE_LIST_RUNLOCK() rw_runlock(&audit_pipe_lock) +#define AUDIT_PIPE_LIST_WLOCK() rw_wlock(&audit_pipe_lock) +#define AUDIT_PIPE_LIST_WLOCK_ASSERT() rw_assert(&audit_pipe_lock, \ + RA_WLOCKED) +#define AUDIT_PIPE_LIST_WUNLOCK() rw_wunlock(&audit_pipe_lock) + +/* + * Cloning related variables and constants. + */ +#define AUDIT_PIPE_NAME "auditpipe" +static eventhandler_tag audit_pipe_eh_tag; +static struct clonedevs *audit_pipe_clones; + +/* + * Special device methods and definition. + */ +static d_open_t audit_pipe_open; +static d_close_t audit_pipe_close; +static d_read_t audit_pipe_read; +static d_ioctl_t audit_pipe_ioctl; +static d_poll_t audit_pipe_poll; +static d_kqfilter_t audit_pipe_kqfilter; + +static struct cdevsw audit_pipe_cdevsw = { + .d_version = D_VERSION, + .d_flags = D_PSEUDO | D_NEEDGIANT | D_NEEDMINOR, + .d_open = audit_pipe_open, + .d_close = audit_pipe_close, + .d_read = audit_pipe_read, + .d_ioctl = audit_pipe_ioctl, + .d_poll = audit_pipe_poll, + .d_kqfilter = audit_pipe_kqfilter, + .d_name = AUDIT_PIPE_NAME, +}; + +static int audit_pipe_kqread(struct knote *note, long hint); +static void audit_pipe_kqdetach(struct knote *note); + +static struct filterops audit_pipe_read_filterops = { + .f_isfd = 1, + .f_attach = NULL, + .f_detach = audit_pipe_kqdetach, + .f_event = audit_pipe_kqread, +}; + +/* + * Some global statistics on audit pipes. + */ +static int audit_pipe_count; /* Current number of pipes. */ +static u_int64_t audit_pipe_ever; /* Pipes ever allocated. */ +static u_int64_t audit_pipe_records; /* Records seen. */ +static u_int64_t audit_pipe_drops; /* Global record drop count. */ + +/* + * Free an audit pipe entry. + */ +static void +audit_pipe_entry_free(struct audit_pipe_entry *ape) +{ + + free(ape->ape_record, M_AUDIT_PIPE_ENTRY); + free(ape, M_AUDIT_PIPE_ENTRY); +} + +/* + * Find an audit pipe preselection specification for an auid, if any. + */ +static struct audit_pipe_preselect * +audit_pipe_preselect_find(struct audit_pipe *ap, au_id_t auid) +{ + struct audit_pipe_preselect *app; + + AUDIT_PIPE_LOCK_ASSERT(ap); + + TAILQ_FOREACH(app, &ap->ap_preselect_list, app_list) { + if (app->app_auid == auid) + return (app); + } + return (NULL); +} + +/* + * Query the per-pipe mask for a specific auid. + */ +static int +audit_pipe_preselect_get(struct audit_pipe *ap, au_id_t auid, + au_mask_t *maskp) +{ + struct audit_pipe_preselect *app; + int error; + + AUDIT_PIPE_LOCK(ap); + app = audit_pipe_preselect_find(ap, auid); + if (app != NULL) { + *maskp = app->app_mask; + error = 0; + } else + error = ENOENT; + AUDIT_PIPE_UNLOCK(ap); + return (error); +} + +/* + * Set the per-pipe mask for a specific auid. Add a new entry if needed; + * otherwise, update the current entry. + */ +static void +audit_pipe_preselect_set(struct audit_pipe *ap, au_id_t auid, au_mask_t mask) +{ + struct audit_pipe_preselect *app, *app_new; + + /* + * Pessimistically assume that the auid doesn't already have a mask + * set, and allocate. We will free it if it is unneeded. + */ + app_new = malloc(sizeof(*app_new), M_AUDIT_PIPE_PRESELECT, M_WAITOK); + AUDIT_PIPE_LOCK(ap); + app = audit_pipe_preselect_find(ap, auid); + if (app == NULL) { + app = app_new; + app_new = NULL; + app->app_auid = auid; + TAILQ_INSERT_TAIL(&ap->ap_preselect_list, app, app_list); + } + app->app_mask = mask; + AUDIT_PIPE_UNLOCK(ap); + if (app_new != NULL) + free(app_new, M_AUDIT_PIPE_PRESELECT); +} + +/* + * Delete a per-auid mask on an audit pipe. + */ +static int +audit_pipe_preselect_delete(struct audit_pipe *ap, au_id_t auid) +{ + struct audit_pipe_preselect *app; + int error; + + AUDIT_PIPE_LOCK(ap); + app = audit_pipe_preselect_find(ap, auid); + if (app != NULL) { + TAILQ_REMOVE(&ap->ap_preselect_list, app, app_list); + error = 0; + } else + error = ENOENT; + AUDIT_PIPE_UNLOCK(ap); + if (app != NULL) + free(app, M_AUDIT_PIPE_PRESELECT); + return (error); +} + +/* + * Delete all per-auid masks on an audit pipe. + */ +static void +audit_pipe_preselect_flush_locked(struct audit_pipe *ap) +{ + struct audit_pipe_preselect *app; + + AUDIT_PIPE_LOCK_ASSERT(ap); + + while ((app = TAILQ_FIRST(&ap->ap_preselect_list)) != NULL) { + TAILQ_REMOVE(&ap->ap_preselect_list, app, app_list); + free(app, M_AUDIT_PIPE_PRESELECT); + } +} + +static void +audit_pipe_preselect_flush(struct audit_pipe *ap) +{ + + AUDIT_PIPE_LOCK(ap); + audit_pipe_preselect_flush_locked(ap); + AUDIT_PIPE_UNLOCK(ap); +} + +/*- + * Determine whether a specific audit pipe matches a record with these + * properties. Algorithm is as follows: + * + * - If the pipe is configured to track the default trail configuration, then + * use the results of global preselection matching. + * - If not, search for a specifically configured auid entry matching the + * event. If an entry is found, use that. + * - Otherwise, use the default flags or naflags configured for the pipe. + */ +static int +audit_pipe_preselect_check(struct audit_pipe *ap, au_id_t auid, + au_event_t event, au_class_t class, int sorf, int trail_preselect) +{ + struct audit_pipe_preselect *app; + + AUDIT_PIPE_LOCK_ASSERT(ap); + + switch (ap->ap_preselect_mode) { + case AUDITPIPE_PRESELECT_MODE_TRAIL: + return (trail_preselect); + + case AUDITPIPE_PRESELECT_MODE_LOCAL: + app = audit_pipe_preselect_find(ap, auid); + if (app == NULL) { + if (auid == AU_DEFAUDITID) + return (au_preselect(event, class, + &ap->ap_preselect_naflags, sorf)); + else + return (au_preselect(event, class, + &ap->ap_preselect_flags, sorf)); + } else + return (au_preselect(event, class, &app->app_mask, + sorf)); + + default: + panic("audit_pipe_preselect_check: mode %d", + ap->ap_preselect_mode); + } + + return (0); +} + +/* + * Determine whether there exists a pipe interested in a record with specific + * properties. + */ +int +audit_pipe_preselect(au_id_t auid, au_event_t event, au_class_t class, + int sorf, int trail_preselect) +{ + struct audit_pipe *ap; + + AUDIT_PIPE_LIST_RLOCK(); + TAILQ_FOREACH(ap, &audit_pipe_list, ap_list) { + AUDIT_PIPE_LOCK(ap); + if (audit_pipe_preselect_check(ap, auid, event, class, sorf, + trail_preselect)) { + AUDIT_PIPE_UNLOCK(ap); + AUDIT_PIPE_LIST_RUNLOCK(); + return (1); + } + AUDIT_PIPE_UNLOCK(ap); + } + AUDIT_PIPE_LIST_RUNLOCK(); + return (0); +} + +/* + * Append individual record to a queue -- allocate queue-local buffer, and + * add to the queue. If the queue is full or we can't allocate memory, drop + * the newest record. + */ +static void +audit_pipe_append(struct audit_pipe *ap, void *record, u_int record_len) +{ + struct audit_pipe_entry *ape; + + AUDIT_PIPE_LOCK_ASSERT(ap); + + if (ap->ap_qlen >= ap->ap_qlimit) { + ap->ap_drops++; + audit_pipe_drops++; + return; + } + + ape = malloc(sizeof(*ape), M_AUDIT_PIPE_ENTRY, M_NOWAIT | M_ZERO); + if (ape == NULL) { + ap->ap_drops++; + audit_pipe_drops++; + return; + } + + ape->ape_record = malloc(record_len, M_AUDIT_PIPE_ENTRY, M_NOWAIT); + if (ape->ape_record == NULL) { + free(ape, M_AUDIT_PIPE_ENTRY); + ap->ap_drops++; + audit_pipe_drops++; + return; + } + + bcopy(record, ape->ape_record, record_len); + ape->ape_record_len = record_len; + + TAILQ_INSERT_TAIL(&ap->ap_queue, ape, ape_queue); + ap->ap_inserts++; + ap->ap_qlen++; + ap->ap_qbyteslen += ape->ape_record_len; + selwakeuppri(&ap->ap_selinfo, PSOCK); + KNOTE_LOCKED(&ap->ap_selinfo.si_note, 0); + if (ap->ap_flags & AUDIT_PIPE_ASYNC) + pgsigio(&ap->ap_sigio, SIGIO, 0); + cv_broadcast(&ap->ap_cv); +} + +/* + * audit_pipe_submit(): audit_worker submits audit records via this + * interface, which arranges for them to be delivered to pipe queues. + */ +void +audit_pipe_submit(au_id_t auid, au_event_t event, au_class_t class, int sorf, + int trail_select, void *record, u_int record_len) +{ + struct audit_pipe *ap; + + /* + * Lockless read to avoid lock overhead if pipes are not in use. + */ + if (TAILQ_FIRST(&audit_pipe_list) == NULL) + return; + + AUDIT_PIPE_LIST_RLOCK(); + TAILQ_FOREACH(ap, &audit_pipe_list, ap_list) { + AUDIT_PIPE_LOCK(ap); + if (audit_pipe_preselect_check(ap, auid, event, class, sorf, + trail_select)) + audit_pipe_append(ap, record, record_len); + AUDIT_PIPE_UNLOCK(ap); + } + AUDIT_PIPE_LIST_RUNLOCK(); + + /* Unlocked increment. */ + audit_pipe_records++; +} + +/* + * audit_pipe_submit_user(): the same as audit_pipe_submit(), except that + * since we don't currently have selection information available, it is + * delivered to the pipe unconditionally. + * + * XXXRW: This is a bug. The BSM check routine for submitting a user record + * should parse that information and return it. + */ +void +audit_pipe_submit_user(void *record, u_int record_len) +{ + struct audit_pipe *ap; + + /* + * Lockless read to avoid lock overhead if pipes are not in use. + */ + if (TAILQ_FIRST(&audit_pipe_list) == NULL) + return; + + AUDIT_PIPE_LIST_RLOCK(); + TAILQ_FOREACH(ap, &audit_pipe_list, ap_list) { + AUDIT_PIPE_LOCK(ap); + audit_pipe_append(ap, record, record_len); + AUDIT_PIPE_UNLOCK(ap); + } + AUDIT_PIPE_LIST_RUNLOCK(); + + /* Unlocked increment. */ + audit_pipe_records++; +} + +/* + * Allocate a new audit pipe. Connects the pipe, on success, to the global + * list and updates statistics. + */ +static struct audit_pipe * +audit_pipe_alloc(void) +{ + struct audit_pipe *ap; + + AUDIT_PIPE_LIST_WLOCK_ASSERT(); + + ap = malloc(sizeof(*ap), M_AUDIT_PIPE, M_NOWAIT | M_ZERO); + if (ap == NULL) + return (NULL); + ap->ap_qlimit = AUDIT_PIPE_QLIMIT_DEFAULT; + TAILQ_INIT(&ap->ap_queue); + knlist_init(&ap->ap_selinfo.si_note, AUDIT_PIPE_MTX(ap), NULL, NULL, + NULL); + AUDIT_PIPE_LOCK_INIT(ap); + AUDIT_PIPE_SX_LOCK_INIT(ap); + cv_init(&ap->ap_cv, "audit_pipe"); + + /* + * Default flags, naflags, and auid-specific preselection settings to + * 0. Initialize the mode to the global trail so that if praudit(1) + * is run on /dev/auditpipe, it sees events associated with the + * default trail. Pipe-aware application can clear the flag, set + * custom masks, and flush the pipe as needed. + */ + bzero(&ap->ap_preselect_flags, sizeof(ap->ap_preselect_flags)); + bzero(&ap->ap_preselect_naflags, sizeof(ap->ap_preselect_naflags)); + TAILQ_INIT(&ap->ap_preselect_list); + ap->ap_preselect_mode = AUDITPIPE_PRESELECT_MODE_TRAIL; + + /* + * Add to global list and update global statistics. + */ + TAILQ_INSERT_HEAD(&audit_pipe_list, ap, ap_list); + audit_pipe_count++; + audit_pipe_ever++; + + return (ap); +} + +/* + * Flush all records currently present in an audit pipe; assume mutex is held. + */ +static void +audit_pipe_flush(struct audit_pipe *ap) +{ + struct audit_pipe_entry *ape; + + AUDIT_PIPE_LOCK_ASSERT(ap); + + while ((ape = TAILQ_FIRST(&ap->ap_queue)) != NULL) { + TAILQ_REMOVE(&ap->ap_queue, ape, ape_queue); + ap->ap_qbyteslen -= ape->ape_record_len; + audit_pipe_entry_free(ape); + ap->ap_qlen--; + } + ap->ap_qoffset = 0; + + KASSERT(ap->ap_qlen == 0, ("audit_pipe_free: ap_qbyteslen")); + KASSERT(ap->ap_qbyteslen == 0, ("audit_pipe_flush: ap_qbyteslen")); +} + +/* + * Free an audit pipe; this means freeing all preselection state and all + * records in the pipe. Assumes global write lock and pipe mutex are held to + * prevent any new records from being inserted during the free, and that the + * audit pipe is still on the global list. + */ +static void +audit_pipe_free(struct audit_pipe *ap) +{ + + AUDIT_PIPE_LIST_WLOCK_ASSERT(); + AUDIT_PIPE_LOCK_ASSERT(ap); + + audit_pipe_preselect_flush_locked(ap); + audit_pipe_flush(ap); + cv_destroy(&ap->ap_cv); + AUDIT_PIPE_SX_LOCK_DESTROY(ap); + AUDIT_PIPE_LOCK_DESTROY(ap); + knlist_destroy(&ap->ap_selinfo.si_note); + TAILQ_REMOVE(&audit_pipe_list, ap, ap_list); + free(ap, M_AUDIT_PIPE); + audit_pipe_count--; +} + +/* + * Audit pipe clone routine -- provide specific requested audit pipe, or a + * fresh one if a specific one is not requested. + */ +static void +audit_pipe_clone(void *arg, struct ucred *cred, char *name, int namelen, + struct cdev **dev) +{ + int i, u; + + if (*dev != NULL) + return; + + if (strcmp(name, AUDIT_PIPE_NAME) == 0) + u = -1; + else if (dev_stdclone(name, NULL, AUDIT_PIPE_NAME, &u) != 1) + return; + + i = clone_create(&audit_pipe_clones, &audit_pipe_cdevsw, &u, dev, 0); + if (i) { + *dev = make_dev(&audit_pipe_cdevsw, u, UID_ROOT, + GID_WHEEL, 0600, "%s%d", AUDIT_PIPE_NAME, u); + if (*dev != NULL) { + dev_ref(*dev); + (*dev)->si_flags |= SI_CHEAPCLONE; + } + } +} + +/* + * Audit pipe open method. Explicit privilege check isn't used as this + * allows file permissions on the special device to be used to grant audit + * review access. Those file permissions should be managed carefully. + */ +static int +audit_pipe_open(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + struct audit_pipe *ap; + + AUDIT_PIPE_LIST_WLOCK(); + ap = dev->si_drv1; + if (ap == NULL) { + ap = audit_pipe_alloc(); + if (ap == NULL) { + AUDIT_PIPE_LIST_WUNLOCK(); + return (ENOMEM); + } + dev->si_drv1 = ap; + } else { + KASSERT(ap->ap_open, ("audit_pipe_open: ap && !ap_open")); + AUDIT_PIPE_LIST_WUNLOCK(); + return (EBUSY); + } + ap->ap_open = 1; /* No lock required yet. */ + AUDIT_PIPE_LIST_WUNLOCK(); + fsetown(td->td_proc->p_pid, &ap->ap_sigio); + return (0); +} + +/* + * Close audit pipe, tear down all records, etc. + */ +static int +audit_pipe_close(struct cdev *dev, int fflag, int devtype, struct thread *td) +{ + struct audit_pipe *ap; + + ap = dev->si_drv1; + KASSERT(ap != NULL, ("audit_pipe_close: ap == NULL")); + KASSERT(ap->ap_open, ("audit_pipe_close: !ap_open")); + + funsetown(&ap->ap_sigio); + AUDIT_PIPE_LIST_WLOCK(); + AUDIT_PIPE_LOCK(ap); + ap->ap_open = 0; + audit_pipe_free(ap); + dev->si_drv1 = NULL; + AUDIT_PIPE_LIST_WUNLOCK(); + return (0); +} + +/* + * Audit pipe ioctl() routine. Handle file descriptor and audit pipe layer + * commands. + */ +static int +audit_pipe_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, + struct thread *td) +{ + struct auditpipe_ioctl_preselect *aip; + struct audit_pipe *ap; + au_mask_t *maskp; + int error, mode; + au_id_t auid; + + ap = dev->si_drv1; + KASSERT(ap != NULL, ("audit_pipe_ioctl: ap == NULL")); + + /* + * Audit pipe ioctls: first come standard device node ioctls, then + * manipulation of pipe settings, and finally, statistics query + * ioctls. + */ + switch (cmd) { + case FIONBIO: + AUDIT_PIPE_LOCK(ap); + if (*(int *)data) + ap->ap_flags |= AUDIT_PIPE_NBIO; + else + ap->ap_flags &= ~AUDIT_PIPE_NBIO; + AUDIT_PIPE_UNLOCK(ap); + error = 0; + break; + + case FIONREAD: + AUDIT_PIPE_LOCK(ap); + *(int *)data = ap->ap_qbyteslen - ap->ap_qoffset; + AUDIT_PIPE_UNLOCK(ap); + error = 0; + break; + + case FIOASYNC: + AUDIT_PIPE_LOCK(ap); + if (*(int *)data) + ap->ap_flags |= AUDIT_PIPE_ASYNC; + else + ap->ap_flags &= ~AUDIT_PIPE_ASYNC; + AUDIT_PIPE_UNLOCK(ap); + error = 0; + break; + + case FIOSETOWN: + error = fsetown(*(int *)data, &ap->ap_sigio); + break; + + case FIOGETOWN: + *(int *)data = fgetown(&ap->ap_sigio); + error = 0; + break; + + case AUDITPIPE_GET_QLEN: + *(u_int *)data = ap->ap_qlen; + error = 0; + break; + + case AUDITPIPE_GET_QLIMIT: + *(u_int *)data = ap->ap_qlimit; + error = 0; + break; + + case AUDITPIPE_SET_QLIMIT: + /* Lockless integer write. */ + if (*(u_int *)data >= AUDIT_PIPE_QLIMIT_MIN || + *(u_int *)data <= AUDIT_PIPE_QLIMIT_MAX) { + ap->ap_qlimit = *(u_int *)data; + error = 0; + } else + error = EINVAL; + break; + + case AUDITPIPE_GET_QLIMIT_MIN: + *(u_int *)data = AUDIT_PIPE_QLIMIT_MIN; + error = 0; + break; + + case AUDITPIPE_GET_QLIMIT_MAX: + *(u_int *)data = AUDIT_PIPE_QLIMIT_MAX; + error = 0; + break; + + case AUDITPIPE_GET_PRESELECT_FLAGS: + AUDIT_PIPE_LOCK(ap); + maskp = (au_mask_t *)data; + *maskp = ap->ap_preselect_flags; + AUDIT_PIPE_UNLOCK(ap); + error = 0; + break; + + case AUDITPIPE_SET_PRESELECT_FLAGS: + AUDIT_PIPE_LOCK(ap); + maskp = (au_mask_t *)data; + ap->ap_preselect_flags = *maskp; + AUDIT_PIPE_UNLOCK(ap); + error = 0; + break; + + case AUDITPIPE_GET_PRESELECT_NAFLAGS: + AUDIT_PIPE_LOCK(ap); + maskp = (au_mask_t *)data; + *maskp = ap->ap_preselect_naflags; + AUDIT_PIPE_UNLOCK(ap); + error = 0; + break; + + case AUDITPIPE_SET_PRESELECT_NAFLAGS: + AUDIT_PIPE_LOCK(ap); + maskp = (au_mask_t *)data; + ap->ap_preselect_naflags = *maskp; + AUDIT_PIPE_UNLOCK(ap); + error = 0; + break; + + case AUDITPIPE_GET_PRESELECT_AUID: + aip = (struct auditpipe_ioctl_preselect *)data; + error = audit_pipe_preselect_get(ap, aip->aip_auid, + &aip->aip_mask); + break; + + case AUDITPIPE_SET_PRESELECT_AUID: + aip = (struct auditpipe_ioctl_preselect *)data; + audit_pipe_preselect_set(ap, aip->aip_auid, aip->aip_mask); + error = 0; + break; + + case AUDITPIPE_DELETE_PRESELECT_AUID: + auid = *(au_id_t *)data; + error = audit_pipe_preselect_delete(ap, auid); + break; + + case AUDITPIPE_FLUSH_PRESELECT_AUID: + audit_pipe_preselect_flush(ap); + error = 0; + break; + + case AUDITPIPE_GET_PRESELECT_MODE: + AUDIT_PIPE_LOCK(ap); + *(int *)data = ap->ap_preselect_mode; + AUDIT_PIPE_UNLOCK(ap); + error = 0; + break; + + case AUDITPIPE_SET_PRESELECT_MODE: + mode = *(int *)data; + switch (mode) { + case AUDITPIPE_PRESELECT_MODE_TRAIL: + case AUDITPIPE_PRESELECT_MODE_LOCAL: + AUDIT_PIPE_LOCK(ap); + ap->ap_preselect_mode = mode; + AUDIT_PIPE_UNLOCK(ap); + error = 0; + break; + + default: + error = EINVAL; + } + break; + + case AUDITPIPE_FLUSH: + if (AUDIT_PIPE_SX_XLOCK_SIG(ap) != 0) + return (EINTR); + AUDIT_PIPE_LOCK(ap); + audit_pipe_flush(ap); + AUDIT_PIPE_UNLOCK(ap); + AUDIT_PIPE_SX_XUNLOCK(ap); + error = 0; + break; + + case AUDITPIPE_GET_MAXAUDITDATA: + *(u_int *)data = MAXAUDITDATA; + error = 0; + break; + + case AUDITPIPE_GET_INSERTS: + *(u_int *)data = ap->ap_inserts; + error = 0; + break; + + case AUDITPIPE_GET_READS: + *(u_int *)data = ap->ap_reads; + error = 0; + break; + + case AUDITPIPE_GET_DROPS: + *(u_int *)data = ap->ap_drops; + error = 0; + break; + + case AUDITPIPE_GET_TRUNCATES: + *(u_int *)data = 0; + error = 0; + break; + + default: + error = ENOTTY; + } + return (error); +} + +/* + * Audit pipe read. Read one or more partial or complete records to user + * memory. + */ +static int +audit_pipe_read(struct cdev *dev, struct uio *uio, int flag) +{ + struct audit_pipe_entry *ape; + struct audit_pipe *ap; + u_int toread; + int error; + + ap = dev->si_drv1; + KASSERT(ap != NULL, ("audit_pipe_read: ap == NULL")); + + /* + * We hold an sx(9) lock over read and flush because we rely on the + * stability of a record in the queue during uiomove(9). + */ + if (AUDIT_PIPE_SX_XLOCK_SIG(ap) != 0) + return (EINTR); + AUDIT_PIPE_LOCK(ap); + while (TAILQ_EMPTY(&ap->ap_queue)) { + if (ap->ap_flags & AUDIT_PIPE_NBIO) { + AUDIT_PIPE_UNLOCK(ap); + AUDIT_PIPE_SX_XUNLOCK(ap); + return (EAGAIN); + } + error = cv_wait_sig(&ap->ap_cv, AUDIT_PIPE_MTX(ap)); + if (error) { + AUDIT_PIPE_UNLOCK(ap); + AUDIT_PIPE_SX_XUNLOCK(ap); + return (error); + } + } + + /* + * Copy as many remaining bytes from the current record to userspace + * as we can. Keep processing records until we run out of records in + * the queue, or until the user buffer runs out of space. + * + * Note: we rely on the SX lock to maintain ape's stability here. + */ + ap->ap_reads++; + while ((ape = TAILQ_FIRST(&ap->ap_queue)) != NULL && + uio->uio_resid > 0) { + AUDIT_PIPE_LOCK_ASSERT(ap); + + KASSERT(ape->ape_record_len > ap->ap_qoffset, + ("audit_pipe_read: record_len > qoffset (1)")); + toread = MIN(ape->ape_record_len - ap->ap_qoffset, + uio->uio_resid); + AUDIT_PIPE_UNLOCK(ap); + error = uiomove((char *)ape->ape_record + ap->ap_qoffset, + toread, uio); + if (error) { + AUDIT_PIPE_SX_XUNLOCK(ap); + return (error); + } + + /* + * If the copy succeeded, update book-keeping, and if no + * bytes remain in the current record, free it. + */ + AUDIT_PIPE_LOCK(ap); + KASSERT(TAILQ_FIRST(&ap->ap_queue) == ape, + ("audit_pipe_read: queue out of sync after uiomove")); + ap->ap_qoffset += toread; + KASSERT(ape->ape_record_len >= ap->ap_qoffset, + ("audit_pipe_read: record_len >= qoffset (2)")); + if (ap->ap_qoffset == ape->ape_record_len) { + TAILQ_REMOVE(&ap->ap_queue, ape, ape_queue); + ap->ap_qbyteslen -= ape->ape_record_len; + audit_pipe_entry_free(ape); + ap->ap_qlen--; + ap->ap_qoffset = 0; + } + } + AUDIT_PIPE_UNLOCK(ap); + AUDIT_PIPE_SX_XUNLOCK(ap); + return (0); +} + +/* + * Audit pipe poll. + */ +static int +audit_pipe_poll(struct cdev *dev, int events, struct thread *td) +{ + struct audit_pipe *ap; + int revents; + + revents = 0; + ap = dev->si_drv1; + KASSERT(ap != NULL, ("audit_pipe_poll: ap == NULL")); + + if (events & (POLLIN | POLLRDNORM)) { + AUDIT_PIPE_LOCK(ap); + if (TAILQ_FIRST(&ap->ap_queue) != NULL) + revents |= events & (POLLIN | POLLRDNORM); + else + selrecord(td, &ap->ap_selinfo); + AUDIT_PIPE_UNLOCK(ap); + } + return (revents); +} + +/* + * Audit pipe kqfilter. + */ +static int +audit_pipe_kqfilter(struct cdev *dev, struct knote *kn) +{ + struct audit_pipe *ap; + + ap = dev->si_drv1; + KASSERT(ap != NULL, ("audit_pipe_kqfilter: ap == NULL")); + + if (kn->kn_filter != EVFILT_READ) + return (EINVAL); + + kn->kn_fop = &audit_pipe_read_filterops; + kn->kn_hook = ap; + + AUDIT_PIPE_LOCK(ap); + knlist_add(&ap->ap_selinfo.si_note, kn, 1); + AUDIT_PIPE_UNLOCK(ap); + return (0); +} + +/* + * Return true if there are records available for reading on the pipe. + */ +static int +audit_pipe_kqread(struct knote *kn, long hint) +{ + struct audit_pipe_entry *ape; + struct audit_pipe *ap; + + ap = (struct audit_pipe *)kn->kn_hook; + KASSERT(ap != NULL, ("audit_pipe_kqread: ap == NULL")); + + AUDIT_PIPE_LOCK_ASSERT(ap); + + if (ap->ap_qlen != 0) { + ape = TAILQ_FIRST(&ap->ap_queue); + KASSERT(ape != NULL, ("audit_pipe_kqread: ape == NULL")); + + kn->kn_data = ap->ap_qbyteslen - ap->ap_qoffset; + return (1); + } else { + kn->kn_data = 0; + return (0); + } +} + +/* + * Detach kqueue state from audit pipe. + */ +static void +audit_pipe_kqdetach(struct knote *kn) +{ + struct audit_pipe *ap; + + ap = (struct audit_pipe *)kn->kn_hook; + KASSERT(ap != NULL, ("audit_pipe_kqdetach: ap == NULL")); + + AUDIT_PIPE_LOCK(ap); + knlist_remove(&ap->ap_selinfo.si_note, kn, 1); + AUDIT_PIPE_UNLOCK(ap); +} + +/* + * Initialize the audit pipe system. + */ +static void +audit_pipe_init(void *unused) +{ + + TAILQ_INIT(&audit_pipe_list); + AUDIT_PIPE_LIST_LOCK_INIT(); + + clone_setup(&audit_pipe_clones); + audit_pipe_eh_tag = EVENTHANDLER_REGISTER(dev_clone, + audit_pipe_clone, 0, 1000); + if (audit_pipe_eh_tag == NULL) + panic("audit_pipe_init: EVENTHANDLER_REGISTER"); +} + +SYSINIT(audit_pipe_init, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, audit_pipe_init, + NULL); diff --git a/sys/security/audit/audit_private.h b/sys/security/audit/audit_private.h new file mode 100644 index 0000000..b79d966 --- /dev/null +++ b/sys/security/audit/audit_private.h @@ -0,0 +1,346 @@ +/*- + * Copyright (c) 1999-2005 Apple Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * This include file contains function prototypes and type definitions used + * within the audit implementation. + */ + +#ifndef _SECURITY_AUDIT_PRIVATE_H_ +#define _SECURITY_AUDIT_PRIVATE_H_ + +#ifndef _KERNEL +#error "no user-serviceable parts inside" +#endif + +#include <sys/ipc.h> +#include <sys/socket.h> +#include <sys/ucred.h> + +#ifdef MALLOC_DECLARE +MALLOC_DECLARE(M_AUDITBSM); +MALLOC_DECLARE(M_AUDITDATA); +MALLOC_DECLARE(M_AUDITPATH); +MALLOC_DECLARE(M_AUDITTEXT); +#endif + +/* + * Audit control variables that are usually set/read via system calls and + * used to control various aspects of auditing. + */ +extern struct au_qctrl audit_qctrl; +extern struct audit_fstat audit_fstat; +extern struct au_mask audit_nae_mask; +extern int audit_panic_on_write_fail; +extern int audit_fail_stop; +extern int audit_argv; +extern int audit_arge; + +/* + * Success/failure conditions for the conversion of a kernel audit record to + * BSM format. + */ +#define BSM_SUCCESS 0 +#define BSM_FAILURE 1 +#define BSM_NOAUDIT 2 + +/* + * Defines for the kernel audit record k_ar_commit field. Flags are set to + * indicate what sort of record it is, and which preselection mechanism + * selected it. + */ +#define AR_COMMIT_KERNEL 0x00000001U +#define AR_COMMIT_USER 0x00000010U + +#define AR_PRESELECT_TRAIL 0x00001000U +#define AR_PRESELECT_PIPE 0x00002000U + +#define AR_PRESELECT_USER_TRAIL 0x00004000U +#define AR_PRESELECT_USER_PIPE 0x00008000U + +/* + * Audit data is generated as a stream of struct audit_record structures, + * linked by struct kaudit_record, and contain storage for possible audit so + * that it will not need to be allocated during the processing of a system + * call, both improving efficiency and avoiding sleeping at untimely moments. + * This structure is converted to BSM format before being written to disk. + */ +struct vnode_au_info { + mode_t vn_mode; + uid_t vn_uid; + gid_t vn_gid; + dev_t vn_dev; + long vn_fsid; + long vn_fileid; + long vn_gen; +}; + +struct groupset { + gid_t gidset[NGROUPS]; + u_int gidset_size; +}; + +struct socket_au_info { + int so_domain; + int so_type; + int so_protocol; + in_addr_t so_raddr; /* Remote address if INET socket. */ + in_addr_t so_laddr; /* Local address if INET socket. */ + u_short so_rport; /* Remote port. */ + u_short so_lport; /* Local port. */ +}; + +union auditon_udata { + char *au_path; + long au_cond; + long au_flags; + long au_policy; + int au_trigger; + au_evclass_map_t au_evclass; + au_mask_t au_mask; + auditinfo_t au_auinfo; + auditpinfo_t au_aupinfo; + auditpinfo_addr_t au_aupinfo_addr; + au_qctrl_t au_qctrl; + au_stat_t au_stat; + au_fstat_t au_fstat; + auditinfo_addr_t au_kau_info; +}; + +struct posix_ipc_perm { + uid_t pipc_uid; + gid_t pipc_gid; + mode_t pipc_mode; +}; + +struct audit_record { + /* Audit record header. */ + u_int32_t ar_magic; + int ar_event; + int ar_retval; /* value returned to the process */ + int ar_errno; /* return status of system call */ + struct timespec ar_starttime; + struct timespec ar_endtime; + u_int64_t ar_valid_arg; /* Bitmask of valid arguments */ + + /* Audit subject information. */ + struct xucred ar_subj_cred; + uid_t ar_subj_ruid; + gid_t ar_subj_rgid; + gid_t ar_subj_egid; + uid_t ar_subj_auid; /* Audit user ID */ + pid_t ar_subj_asid; /* Audit session ID */ + pid_t ar_subj_pid; + struct au_tid ar_subj_term; + struct au_tid_addr ar_subj_term_addr; + struct au_mask ar_subj_amask; + + /* Operation arguments. */ + uid_t ar_arg_euid; + uid_t ar_arg_ruid; + uid_t ar_arg_suid; + gid_t ar_arg_egid; + gid_t ar_arg_rgid; + gid_t ar_arg_sgid; + pid_t ar_arg_pid; + pid_t ar_arg_asid; + struct au_tid ar_arg_termid; + struct au_tid_addr ar_arg_termid_addr; + uid_t ar_arg_uid; + uid_t ar_arg_auid; + gid_t ar_arg_gid; + struct groupset ar_arg_groups; + int ar_arg_fd; + int ar_arg_fflags; + mode_t ar_arg_mode; + int ar_arg_dev; + long ar_arg_value; + void * ar_arg_addr; + int ar_arg_len; + int ar_arg_mask; + u_int ar_arg_signum; + char ar_arg_login[MAXLOGNAME]; + int ar_arg_ctlname[CTL_MAXNAME]; + struct socket_au_info ar_arg_sockinfo; + char *ar_arg_upath1; + char *ar_arg_upath2; + char *ar_arg_text; + struct au_mask ar_arg_amask; + struct vnode_au_info ar_arg_vnode1; + struct vnode_au_info ar_arg_vnode2; + int ar_arg_cmd; + int ar_arg_svipc_cmd; + struct ipc_perm ar_arg_svipc_perm; + int ar_arg_svipc_id; + void * ar_arg_svipc_addr; + struct posix_ipc_perm ar_arg_pipc_perm; + union auditon_udata ar_arg_auditon; + char *ar_arg_argv; + int ar_arg_argc; + char *ar_arg_envv; + int ar_arg_envc; + int ar_arg_exitstatus; + int ar_arg_exitretval; + struct sockaddr_storage ar_arg_sockaddr; +}; + +/* + * Arguments in the audit record are initially not defined; flags are set to + * indicate if they are present so they can be included in the audit log + * stream only if defined. + */ +#define ARG_IS_VALID(kar, arg) ((kar)->k_ar.ar_valid_arg & (arg)) +#define ARG_SET_VALID(kar, arg) do { \ + (kar)->k_ar.ar_valid_arg |= (arg); \ +} while (0) + +/* + * In-kernel version of audit record; the basic record plus queue meta-data. + * This record can also have a pointer set to some opaque data that will be + * passed through to the audit writing mechanism. + */ +struct kaudit_record { + struct audit_record k_ar; + u_int32_t k_ar_commit; + void *k_udata; /* User data. */ + u_int k_ulen; /* User data length. */ + struct uthread *k_uthread; /* Audited thread. */ + TAILQ_ENTRY(kaudit_record) k_q; +}; +TAILQ_HEAD(kaudit_queue, kaudit_record); + +/* + * Functions to manage the allocation, release, and commit of kernel audit + * records. + */ +void audit_abort(struct kaudit_record *ar); +void audit_commit(struct kaudit_record *ar, int error, + int retval); +struct kaudit_record *audit_new(int event, struct thread *td); + +/* + * Functions relating to the conversion of internal kernel audit records to + * the BSM file format. + */ +struct au_record; +int kaudit_to_bsm(struct kaudit_record *kar, struct au_record **pau); +int bsm_rec_verify(void *rec); + +/* + * Kernel versions of the libbsm audit record functions. + */ +void kau_free(struct au_record *rec); +void kau_init(void); + +/* + * Return values for pre-selection and post-selection decisions. + */ +#define AU_PRS_SUCCESS 1 +#define AU_PRS_FAILURE 2 +#define AU_PRS_BOTH (AU_PRS_SUCCESS|AU_PRS_FAILURE) + +/* + * Data structures relating to the kernel audit queue. Ideally, these might + * be abstracted so that only accessor methods are exposed. + */ +extern struct mtx audit_mtx; +extern struct cv audit_watermark_cv; +extern struct cv audit_worker_cv; +extern struct kaudit_queue audit_q; +extern size_t audit_q_len; +extern size_t audit_pre_q_len; +extern int audit_in_failure; + +/* + * Flags to use on audit files when opening and closing. + */ +#define AUDIT_OPEN_FLAGS (FWRITE | O_APPEND) +#define AUDIT_CLOSE_FLAGS (FWRITE | O_APPEND) + +#include <sys/fcntl.h> +#include <sys/kernel.h> +#include <sys/malloc.h> + +/* + * Some of the BSM tokenizer functions take different parameters in the + * kernel implementations in order to save the copying of large kernel data + * structures. The prototypes of these functions are declared here. + */ +token_t *kau_to_socket(struct socket_au_info *soi); + +/* + * audit_klib prototypes + */ +int au_preselect(au_event_t event, au_class_t class, + au_mask_t *mask_p, int sorf); +void au_evclassmap_init(void); +void au_evclassmap_insert(au_event_t event, au_class_t class); +au_class_t au_event_class(au_event_t event); +au_event_t audit_ctlname_to_sysctlevent(int name[], uint64_t valid_arg); +au_event_t audit_flags_and_error_to_openevent(int oflags, int error); +int audit_msgctl_to_event(int cmd); +int audit_semctl_to_event(int cmr); +void audit_canon_path(struct thread *td, char *path, char *cpath); +int auditon_command_event(int cmd); + +/* + * Audit trigger events notify user space of kernel audit conditions + * asynchronously. + */ +void audit_trigger_init(void); +int audit_send_trigger(unsigned int trigger); + +/* + * Accessor functions to manage global audit state. + */ +void audit_set_kinfo(struct auditinfo_addr *); +void audit_get_kinfo(struct auditinfo_addr *); + +/* + * General audit related functions. + */ +struct kaudit_record *currecord(void); +void audit_free(struct kaudit_record *ar); +void audit_shutdown(void *arg, int howto); +void audit_rotate_vnode(struct ucred *cred, + struct vnode *vp); +void audit_worker_init(void); + +/* + * Audit pipe functions. + */ +int audit_pipe_preselect(au_id_t auid, au_event_t event, + au_class_t class, int sorf, int trail_select); +void audit_pipe_submit(au_id_t auid, au_event_t event, au_class_t class, + int sorf, int trail_select, void *record, u_int record_len); +void audit_pipe_submit_user(void *record, u_int record_len); + +#endif /* ! _SECURITY_AUDIT_PRIVATE_H_ */ diff --git a/sys/security/audit/audit_syscalls.c b/sys/security/audit/audit_syscalls.c new file mode 100644 index 0000000..f8d45fe --- /dev/null +++ b/sys/security/audit/audit_syscalls.c @@ -0,0 +1,759 @@ +/*- + * Copyright (c) 1999-2005 Apple Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/namei.h> +#include <sys/priv.h> +#include <sys/proc.h> +#include <sys/sysproto.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/jail.h> + +#include <bsm/audit.h> +#include <bsm/audit_kevents.h> + +#include <security/audit/audit.h> +#include <security/audit/audit_private.h> +#include <security/mac/mac_framework.h> + +#ifdef AUDIT + +/* + * System call to allow a user space application to submit a BSM audit record + * to the kernel for inclusion in the audit log. This function does little + * verification on the audit record that is submitted. + * + * XXXAUDIT: Audit preselection for user records does not currently work, + * since we pre-select only based on the AUE_audit event type, not the event + * type submitted as part of the user audit data. + */ +/* ARGSUSED */ +int +audit(struct thread *td, struct audit_args *uap) +{ + int error; + void * rec; + struct kaudit_record *ar; + + if (jailed(td->td_ucred)) + return (ENOSYS); + error = priv_check(td, PRIV_AUDIT_SUBMIT); + if (error) + return (error); + + if ((uap->length <= 0) || (uap->length > audit_qctrl.aq_bufsz)) + return (EINVAL); + + ar = currecord(); + + /* + * If there's no current audit record (audit() itself not audited) + * commit the user audit record. + */ + if (ar == NULL) { + + /* + * This is not very efficient; we're required to allocate a + * complete kernel audit record just so the user record can + * tag along. + * + * XXXAUDIT: Maybe AUE_AUDIT in the system call context and + * special pre-select handling? + */ + td->td_ar = audit_new(AUE_NULL, td); + if (td->td_ar == NULL) + return (ENOTSUP); + ar = td->td_ar; + } + + if (uap->length > MAX_AUDIT_RECORD_SIZE) + return (EINVAL); + + rec = malloc(uap->length, M_AUDITDATA, M_WAITOK); + + error = copyin(uap->record, rec, uap->length); + if (error) + goto free_out; + + /* Verify the record. */ + if (bsm_rec_verify(rec) == 0) { + error = EINVAL; + goto free_out; + } + +#ifdef MAC + error = mac_system_check_audit(td->td_ucred, rec, uap->length); + if (error) + goto free_out; +#endif + + /* + * Attach the user audit record to the kernel audit record. Because + * this system call is an auditable event, we will write the user + * record along with the record for this audit event. + * + * XXXAUDIT: KASSERT appropriate starting values of k_udata, k_ulen, + * k_ar_commit & AR_COMMIT_USER? + */ + ar->k_udata = rec; + ar->k_ulen = uap->length; + ar->k_ar_commit |= AR_COMMIT_USER; + + /* + * Currently we assume that all preselection has been performed in + * userspace. We unconditionally set these masks so that the records + * get committed both to the trail and pipe. In the future we will + * want to setup kernel based preselection. + */ + ar->k_ar_commit |= (AR_PRESELECT_USER_TRAIL | AR_PRESELECT_USER_PIPE); + return (0); + +free_out: + /* + * audit_syscall_exit() will free the audit record on the thread even + * if we allocated it above. + */ + free(rec, M_AUDITDATA); + return (error); +} + +/* + * System call to manipulate auditing. + */ +/* ARGSUSED */ +int +auditon(struct thread *td, struct auditon_args *uap) +{ + struct ucred *cred, *newcred, *oldcred; + int error; + union auditon_udata udata; + struct proc *tp; + + if (jailed(td->td_ucred)) + return (ENOSYS); + AUDIT_ARG(cmd, uap->cmd); + +#ifdef MAC + error = mac_system_check_auditon(td->td_ucred, uap->cmd); + if (error) + return (error); +#endif + + error = priv_check(td, PRIV_AUDIT_CONTROL); + if (error) + return (error); + + if ((uap->length <= 0) || (uap->length > sizeof(union auditon_udata))) + return (EINVAL); + + memset((void *)&udata, 0, sizeof(udata)); + + /* + * Some of the GET commands use the arguments too. + */ + 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: + case A_SENDTRIGGER: + error = copyin(uap->data, (void *)&udata, uap->length); + if (error) + return (error); + AUDIT_ARG(auditon, &udata); + break; + } + + /* + * XXXAUDIT: Locking? + */ + 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; + if (audit_argv) + udata.au_policy |= AUDIT_ARGV; + if (audit_arge) + udata.au_policy |= AUDIT_ARGE; + break; + + case A_SETPOLICY: + if (udata.au_policy & ~(AUDIT_CNT|AUDIT_AHLT|AUDIT_ARGV| + AUDIT_ARGE)) + return (EINVAL); + /* + * XXX - Need to wake up waiters if the policy relaxes? + */ + audit_fail_stop = ((udata.au_policy & AUDIT_CNT) == 0); + audit_panic_on_write_fail = (udata.au_policy & AUDIT_AHLT); + audit_argv = (udata.au_policy & AUDIT_ARGV); + audit_arge = (udata.au_policy & AUDIT_ARGE); + 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)) + return (EINVAL); + + audit_qctrl = udata.au_qctrl; + /* XXX The queue delay value isn't used with the kernel. */ + audit_qctrl.aq_delay = -1; + break; + + case A_GETCWD: + return (ENOSYS); + break; + + case A_GETCAR: + return (ENOSYS); + break; + + case A_GETSTAT: + return (ENOSYS); + break; + + case A_SETSTAT: + return (ENOSYS); + break; + + case A_SETUMASK: + return (ENOSYS); + break; + + case A_SETSMASK: + return (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(NULL, 0); + } + 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) + return (ESRCH); + if ((tp = pfind(udata.au_aupinfo.ap_pid)) == NULL) + return (ESRCH); + if ((error = p_cansee(td, tp)) != 0) { + PROC_UNLOCK(tp); + return (error); + } + cred = tp->p_ucred; + if (cred->cr_audit.ai_termid.at_type == AU_IPv6) { + PROC_UNLOCK(tp); + return (EINVAL); + } + udata.au_aupinfo.ap_auid = cred->cr_audit.ai_auid; + udata.au_aupinfo.ap_mask.am_success = + cred->cr_audit.ai_mask.am_success; + udata.au_aupinfo.ap_mask.am_failure = + cred->cr_audit.ai_mask.am_failure; + udata.au_aupinfo.ap_termid.machine = + cred->cr_audit.ai_termid.at_addr[0]; + udata.au_aupinfo.ap_termid.port = + (dev_t)cred->cr_audit.ai_termid.at_port; + udata.au_aupinfo.ap_asid = cred->cr_audit.ai_asid; + PROC_UNLOCK(tp); + break; + + case A_SETPMASK: + if (udata.au_aupinfo.ap_pid < 1) + return (ESRCH); + newcred = crget(); + if ((tp = pfind(udata.au_aupinfo.ap_pid)) == NULL) { + crfree(newcred); + return (ESRCH); + } + if ((error = p_cansee(td, tp)) != 0) { + PROC_UNLOCK(tp); + crfree(newcred); + return (error); + } + oldcred = tp->p_ucred; + crcopy(newcred, oldcred); + newcred->cr_audit.ai_mask.am_success = + udata.au_aupinfo.ap_mask.am_success; + newcred->cr_audit.ai_mask.am_failure = + udata.au_aupinfo.ap_mask.am_failure; + td->td_proc->p_ucred = newcred; + PROC_UNLOCK(tp); + crfree(oldcred); + break; + + case A_SETFSIZE: + if ((udata.au_fstat.af_filesz != 0) && + (udata.au_fstat.af_filesz < MIN_AUDIT_FILE_SIZE)) + return (EINVAL); + 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: + if (udata.au_aupinfo_addr.ap_pid < 1) + return (ESRCH); + if ((tp = pfind(udata.au_aupinfo_addr.ap_pid)) == NULL) + return (ESRCH); + cred = tp->p_ucred; + udata.au_aupinfo_addr.ap_auid = cred->cr_audit.ai_auid; + udata.au_aupinfo_addr.ap_mask.am_success = + cred->cr_audit.ai_mask.am_success; + udata.au_aupinfo_addr.ap_mask.am_failure = + cred->cr_audit.ai_mask.am_failure; + udata.au_aupinfo_addr.ap_termid = cred->cr_audit.ai_termid; + udata.au_aupinfo_addr.ap_asid = cred->cr_audit.ai_asid; + PROC_UNLOCK(tp); + break; + + case A_GETKAUDIT: + audit_get_kinfo(&udata.au_kau_info); + break; + + case A_SETKAUDIT: + if (udata.au_kau_info.ai_termid.at_type != AU_IPv4 && + udata.au_kau_info.ai_termid.at_type != AU_IPv6) + return (EINVAL); + audit_set_kinfo(&udata.au_kau_info); + break; + + case A_SENDTRIGGER: + if ((udata.au_trigger < AUDIT_TRIGGER_MIN) || + (udata.au_trigger > AUDIT_TRIGGER_MAX)) + return (EINVAL); + return (audit_send_trigger(udata.au_trigger)); + + default: + return (EINVAL); + } + + /* + * Copy data back to userspace for the GET comands. + */ + 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: + error = copyout((void *)&udata, uap->data, uap->length); + if (error) + return (error); + break; + } + + return (0); +} + +/* + * System calls to manage the user audit information. + */ +/* ARGSUSED */ +int +getauid(struct thread *td, struct getauid_args *uap) +{ + int error; + + if (jailed(td->td_ucred)) + return (ENOSYS); + error = priv_check(td, PRIV_AUDIT_GETAUDIT); + if (error) + return (error); + return (copyout(&td->td_ucred->cr_audit.ai_auid, uap->auid, + sizeof(td->td_ucred->cr_audit.ai_auid))); +} + +/* ARGSUSED */ +int +setauid(struct thread *td, struct setauid_args *uap) +{ + struct ucred *newcred, *oldcred; + au_id_t id; + int error; + + if (jailed(td->td_ucred)) + return (ENOSYS); + error = copyin(uap->auid, &id, sizeof(id)); + if (error) + return (error); + audit_arg_auid(id); + newcred = crget(); + PROC_LOCK(td->td_proc); + oldcred = td->td_proc->p_ucred; + crcopy(newcred, oldcred); +#ifdef MAC + error = mac_proc_check_setauid(oldcred, id); + if (error) + goto fail; +#endif + error = priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT, 0); + if (error) + goto fail; + newcred->cr_audit.ai_auid = id; + td->td_proc->p_ucred = newcred; + PROC_UNLOCK(td->td_proc); + crfree(oldcred); + return (0); +fail: + PROC_UNLOCK(td->td_proc); + crfree(newcred); + return (error); +} + +/* + * System calls to get and set process audit information. + */ +/* ARGSUSED */ +int +getaudit(struct thread *td, struct getaudit_args *uap) +{ + struct auditinfo ai; + struct ucred *cred; + int error; + + cred = td->td_ucred; + if (jailed(cred)) + return (ENOSYS); + error = priv_check(td, PRIV_AUDIT_GETAUDIT); + if (error) + return (error); + if (cred->cr_audit.ai_termid.at_type == AU_IPv6) + return (E2BIG); + bzero(&ai, sizeof(ai)); + ai.ai_auid = cred->cr_audit.ai_auid; + ai.ai_mask = cred->cr_audit.ai_mask; + ai.ai_asid = cred->cr_audit.ai_asid; + ai.ai_termid.machine = cred->cr_audit.ai_termid.at_addr[0]; + ai.ai_termid.port = cred->cr_audit.ai_termid.at_port; + return (copyout(&ai, uap->auditinfo, sizeof(ai))); +} + +/* ARGSUSED */ +int +setaudit(struct thread *td, struct setaudit_args *uap) +{ + struct ucred *newcred, *oldcred; + struct auditinfo ai; + int error; + + if (jailed(td->td_ucred)) + return (ENOSYS); + error = copyin(uap->auditinfo, &ai, sizeof(ai)); + if (error) + return (error); + audit_arg_auditinfo(&ai); + newcred = crget(); + PROC_LOCK(td->td_proc); + oldcred = td->td_proc->p_ucred; + crcopy(newcred, oldcred); +#ifdef MAC + error = mac_proc_check_setaudit(oldcred, &ai); + if (error) + goto fail; +#endif + error = priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT, 0); + if (error) + goto fail; + bzero(&newcred->cr_audit, sizeof(newcred->cr_audit)); + newcred->cr_audit.ai_auid = ai.ai_auid; + newcred->cr_audit.ai_mask = ai.ai_mask; + newcred->cr_audit.ai_asid = ai.ai_asid; + newcred->cr_audit.ai_termid.at_addr[0] = ai.ai_termid.machine; + newcred->cr_audit.ai_termid.at_port = ai.ai_termid.port; + newcred->cr_audit.ai_termid.at_type = AU_IPv4; + td->td_proc->p_ucred = newcred; + PROC_UNLOCK(td->td_proc); + crfree(oldcred); + return (0); +fail: + PROC_UNLOCK(td->td_proc); + crfree(newcred); + return (error); +} + +/* ARGSUSED */ +int +getaudit_addr(struct thread *td, struct getaudit_addr_args *uap) +{ + int error; + + if (jailed(td->td_ucred)) + return (ENOSYS); + if (uap->length < sizeof(*uap->auditinfo_addr)) + return (EOVERFLOW); + error = priv_check(td, PRIV_AUDIT_GETAUDIT); + if (error) + return (error); + return (copyout(&td->td_ucred->cr_audit, uap->auditinfo_addr, + sizeof(*uap->auditinfo_addr))); +} + +/* ARGSUSED */ +int +setaudit_addr(struct thread *td, struct setaudit_addr_args *uap) +{ + struct ucred *newcred, *oldcred; + struct auditinfo_addr aia; + int error; + + if (jailed(td->td_ucred)) + return (ENOSYS); + error = copyin(uap->auditinfo_addr, &aia, sizeof(aia)); + if (error) + return (error); + audit_arg_auditinfo_addr(&aia); + if (aia.ai_termid.at_type != AU_IPv6 && + aia.ai_termid.at_type != AU_IPv4) + return (EINVAL); + newcred = crget(); + PROC_LOCK(td->td_proc); + oldcred = td->td_proc->p_ucred; + crcopy(newcred, oldcred); +#ifdef MAC + error = mac_proc_check_setaudit_addr(oldcred, &aia); + if (error) + goto fail; +#endif + error = priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT, 0); + if (error) + goto fail; + newcred->cr_audit = aia; + td->td_proc->p_ucred = newcred; + PROC_UNLOCK(td->td_proc); + crfree(oldcred); + return (0); +fail: + PROC_UNLOCK(td->td_proc); + crfree(newcred); + return (error); +} + +/* + * Syscall to manage audit files. + */ +/* ARGSUSED */ +int +auditctl(struct thread *td, struct auditctl_args *uap) +{ + struct nameidata nd; + struct ucred *cred; + struct vnode *vp; + int error = 0; + int flags, vfslocked; + + if (jailed(td->td_ucred)) + return (ENOSYS); + error = priv_check(td, PRIV_AUDIT_CONTROL); + if (error) + return (error); + + vp = NULL; + cred = NULL; + + /* + * If a path is specified, open the replacement vnode, perform + * validity checks, and grab another reference to the current + * credential. + * + * On Darwin, a NULL path argument is also used to disable audit. + */ + if (uap->path == NULL) + return (EINVAL); + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1, + UIO_USERSPACE, uap->path, td); + flags = AUDIT_OPEN_FLAGS; + error = vn_open(&nd, &flags, 0, NULL); + if (error) + return (error); + vfslocked = NDHASGIANT(&nd); + vp = nd.ni_vp; +#ifdef MAC + error = mac_system_check_auditctl(td->td_ucred, vp); + VOP_UNLOCK(vp, 0); + if (error) { + vn_close(vp, AUDIT_CLOSE_FLAGS, td->td_ucred, td); + VFS_UNLOCK_GIANT(vfslocked); + return (error); + } +#else + VOP_UNLOCK(vp, 0); +#endif + NDFREE(&nd, NDF_ONLY_PNBUF); + if (vp->v_type != VREG) { + vn_close(vp, AUDIT_CLOSE_FLAGS, td->td_ucred, td); + VFS_UNLOCK_GIANT(vfslocked); + return (EINVAL); + } + VFS_UNLOCK_GIANT(vfslocked); + cred = td->td_ucred; + crhold(cred); + + /* + * XXXAUDIT: Should audit_suspended actually be cleared by + * audit_worker? + */ + audit_suspended = 0; + + audit_rotate_vnode(cred, vp); + + return (error); +} + +#else /* !AUDIT */ + +int +audit(struct thread *td, struct audit_args *uap) +{ + + return (ENOSYS); +} + +int +auditon(struct thread *td, struct auditon_args *uap) +{ + + return (ENOSYS); +} + +int +getauid(struct thread *td, struct getauid_args *uap) +{ + + return (ENOSYS); +} + +int +setauid(struct thread *td, struct setauid_args *uap) +{ + + return (ENOSYS); +} + +int +getaudit(struct thread *td, struct getaudit_args *uap) +{ + + return (ENOSYS); +} + +int +setaudit(struct thread *td, struct setaudit_args *uap) +{ + + return (ENOSYS); +} + +int +getaudit_addr(struct thread *td, struct getaudit_addr_args *uap) +{ + + return (ENOSYS); +} + +int +setaudit_addr(struct thread *td, struct setaudit_addr_args *uap) +{ + + return (ENOSYS); +} + +int +auditctl(struct thread *td, struct auditctl_args *uap) +{ + + return (ENOSYS); +} +#endif /* AUDIT */ diff --git a/sys/security/audit/audit_trigger.c b/sys/security/audit/audit_trigger.c new file mode 100644 index 0000000..7eb7899 --- /dev/null +++ b/sys/security/audit/audit_trigger.c @@ -0,0 +1,178 @@ +/*- + * Copyright (c) 2005 Wayne J. Salamon + * All rights reserved. + * + * This software was developed by Wayne Salamon for the TrustedBSD Project. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/queue.h> +#include <sys/systm.h> +#include <sys/uio.h> + +#include <security/audit/audit.h> +#include <security/audit/audit_private.h> + +/* + * Structures and operations to support the basic character special device + * used to communicate with userland. /dev/audit reliably delivers one-byte + * messages to a listening application (or discards them if there is no + * listening application). + * + * Currently, select/poll are not supported on the trigger device. + */ +struct trigger_info { + unsigned int trigger; + TAILQ_ENTRY(trigger_info) list; +}; + +static MALLOC_DEFINE(M_AUDITTRIGGER, "audit_trigger", "Audit trigger events"); +static struct cdev *audit_dev; +static int audit_isopen = 0; +static TAILQ_HEAD(, trigger_info) trigger_list; +static struct mtx audit_trigger_mtx; + +static int +audit_open(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + int error; + + /* Only one process may open the device at a time. */ + mtx_lock(&audit_trigger_mtx); + if (!audit_isopen) { + error = 0; + audit_isopen = 1; + } else + error = EBUSY; + mtx_unlock(&audit_trigger_mtx); + + return (error); +} + +static int +audit_close(struct cdev *dev, int fflag, int devtype, struct thread *td) +{ + struct trigger_info *ti; + + /* Flush the queue of pending trigger events. */ + mtx_lock(&audit_trigger_mtx); + audit_isopen = 0; + while (!TAILQ_EMPTY(&trigger_list)) { + ti = TAILQ_FIRST(&trigger_list); + TAILQ_REMOVE(&trigger_list, ti, list); + free(ti, M_AUDITTRIGGER); + } + mtx_unlock(&audit_trigger_mtx); + + return (0); +} + +static int +audit_read(struct cdev *dev, struct uio *uio, int ioflag) +{ + int error = 0; + struct trigger_info *ti = NULL; + + mtx_lock(&audit_trigger_mtx); + while (TAILQ_EMPTY(&trigger_list)) { + error = msleep(&trigger_list, &audit_trigger_mtx, + PSOCK | PCATCH, "auditd", 0); + if (error) + break; + } + if (!error) { + ti = TAILQ_FIRST(&trigger_list); + TAILQ_REMOVE(&trigger_list, ti, list); + } + mtx_unlock(&audit_trigger_mtx); + if (!error) { + error = uiomove(&ti->trigger, sizeof(ti->trigger), uio); + free(ti, M_AUDITTRIGGER); + } + return (error); +} + +static int +audit_write(struct cdev *dev, struct uio *uio, int ioflag) +{ + + /* Communication is kernel->userspace only. */ + return (EOPNOTSUPP); +} + +int +audit_send_trigger(unsigned int trigger) +{ + struct trigger_info *ti; + + ti = malloc(sizeof *ti, M_AUDITTRIGGER, M_WAITOK); + mtx_lock(&audit_trigger_mtx); + if (!audit_isopen) { + /* If nobody's listening, we ain't talking. */ + mtx_unlock(&audit_trigger_mtx); + free(ti, M_AUDITTRIGGER); + return (ENODEV); + } + ti->trigger = trigger; + TAILQ_INSERT_TAIL(&trigger_list, ti, list); + wakeup(&trigger_list); + mtx_unlock(&audit_trigger_mtx); + return (0); +} + +static struct cdevsw audit_cdevsw = { + .d_version = D_VERSION, + .d_open = audit_open, + .d_close = audit_close, + .d_read = audit_read, + .d_write = audit_write, + .d_name = "audit" +}; + +void +audit_trigger_init(void) +{ + + TAILQ_INIT(&trigger_list); + mtx_init(&audit_trigger_mtx, "audit_trigger_mtx", NULL, MTX_DEF); +} + +static void +audit_trigger_cdev_init(void *unused) +{ + + /* Create the special device file. */ + audit_dev = make_dev(&audit_cdevsw, 0, UID_ROOT, GID_KMEM, 0600, + AUDITDEV_FILENAME); +} + +SYSINIT(audit_trigger_cdev_init, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, + audit_trigger_cdev_init, NULL); diff --git a/sys/security/audit/audit_worker.c b/sys/security/audit/audit_worker.c new file mode 100644 index 0000000..191d583 --- /dev/null +++ b/sys/security/audit/audit_worker.c @@ -0,0 +1,494 @@ +/*- + * Copyright (c) 1999-2008 Apple Inc. + * Copyright (c) 2006-2008 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/condvar.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/filedesc.h> +#include <sys/fcntl.h> +#include <sys/ipc.h> +#include <sys/kernel.h> +#include <sys/kthread.h> +#include <sys/malloc.h> +#include <sys/mount.h> +#include <sys/namei.h> +#include <sys/proc.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/protosw.h> +#include <sys/domain.h> +#include <sys/sx.h> +#include <sys/sysproto.h> +#include <sys/sysent.h> +#include <sys/systm.h> +#include <sys/ucred.h> +#include <sys/uio.h> +#include <sys/un.h> +#include <sys/unistd.h> +#include <sys/vnode.h> + +#include <bsm/audit.h> +#include <bsm/audit_internal.h> +#include <bsm/audit_kevents.h> + +#include <netinet/in.h> +#include <netinet/in_pcb.h> + +#include <security/audit/audit.h> +#include <security/audit/audit_private.h> + +#include <vm/uma.h> + +/* + * Worker thread that will schedule disk I/O, etc. + */ +static struct proc *audit_thread; + +/* + * audit_cred and audit_vp are the stored credential and vnode to use for + * active audit trail. They are protected by the audit worker lock, which + * will be held across all I/O and all rotation to prevent them from being + * replaced (rotated) while in use. The audit_file_rotate_wait flag is set + * when the kernel has delivered a trigger to auditd to rotate the trail, and + * is cleared when the next rotation takes place. It is also protected by + * the audit worker lock. + */ +static int audit_file_rotate_wait; +static struct ucred *audit_cred; +static struct vnode *audit_vp; +static struct sx audit_worker_lock; + +#define AUDIT_WORKER_LOCK_INIT() sx_init(&audit_worker_lock, \ + "audit_worker_lock"); +#define AUDIT_WORKER_LOCK_ASSERT() sx_assert(&audit_worker_lock, \ + SA_XLOCKED) +#define AUDIT_WORKER_LOCK() sx_xlock(&audit_worker_lock) +#define AUDIT_WORKER_UNLOCK() sx_xunlock(&audit_worker_lock) + +/* + * Write an audit record to a file, performed as the last stage after both + * preselection and BSM conversion. Both space management and write failures + * are handled in this function. + * + * No attempt is made to deal with possible failure to deliver a trigger to + * the audit daemon, since the message is asynchronous anyway. + */ +static void +audit_record_write(struct vnode *vp, struct ucred *cred, void *data, + size_t len) +{ + static struct timeval last_lowspace_trigger; + static struct timeval last_fail; + static int cur_lowspace_trigger; + struct statfs *mnt_stat; + int error, vfslocked; + static int cur_fail; + struct vattr vattr; + long temp; + + AUDIT_WORKER_LOCK_ASSERT(); + + if (vp == NULL) + return; + + mnt_stat = &vp->v_mount->mnt_stat; + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + + /* + * First, gather statistics on the audit log file and file system so + * that we know how we're doing on space. Consider failure of these + * operations to indicate a future inability to write to the file. + */ + error = VFS_STATFS(vp->v_mount, mnt_stat, curthread); + if (error) + goto fail; + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + error = VOP_GETATTR(vp, &vattr, cred); + VOP_UNLOCK(vp, 0); + if (error) + goto fail; + audit_fstat.af_currsz = vattr.va_size; + + /* + * We handle four different space-related limits: + * + * - A fixed (hard) limit on the minimum free blocks we require on + * the file system, and results in record loss, a trigger, and + * possible fail stop due to violating invariants. + * + * - An administrative (soft) limit, which when fallen below, results + * in the kernel notifying the audit daemon of low space. + * + * - An audit trail size limit, which when gone above, results in the + * kernel notifying the audit daemon that rotation is desired. + * + * - The total depth of the kernel audit record exceeding free space, + * which can lead to possible fail stop (with drain), in order to + * prevent violating invariants. Failure here doesn't halt + * immediately, but prevents new records from being generated. + * + * Possibly, the last of these should be handled differently, always + * allowing a full queue to be lost, rather than trying to prevent + * loss. + * + * First, handle the hard limit, which generates a trigger and may + * fail stop. This is handled in the same manner as ENOSPC from + * VOP_WRITE, and results in record loss. + */ + if (mnt_stat->f_bfree < AUDIT_HARD_LIMIT_FREE_BLOCKS) { + error = ENOSPC; + goto fail_enospc; + } + + /* + * Second, handle falling below the soft limit, if defined; we send + * the daemon a trigger and continue processing the record. Triggers + * are limited to 1/sec. + */ + if (audit_qctrl.aq_minfree != 0) { + temp = mnt_stat->f_blocks / (100 / audit_qctrl.aq_minfree); + if (mnt_stat->f_bfree < temp) { + if (ppsratecheck(&last_lowspace_trigger, + &cur_lowspace_trigger, 1)) { + (void)audit_send_trigger( + AUDIT_TRIGGER_LOW_SPACE); + printf("Warning: disk space low (< %d%% free) " + "on audit log file-system\n", + audit_qctrl.aq_minfree); + } + } + } + + /* + * If the current file is getting full, generate a rotation trigger + * to the daemon. This is only approximate, which is fine as more + * records may be generated before the daemon rotates the file. + */ + if ((audit_fstat.af_filesz != 0) && (audit_file_rotate_wait == 0) && + (vattr.va_size >= audit_fstat.af_filesz)) { + AUDIT_WORKER_LOCK_ASSERT(); + + audit_file_rotate_wait = 1; + (void)audit_send_trigger(AUDIT_TRIGGER_ROTATE_KERNEL); + } + + /* + * If the estimated amount of audit data in the audit event queue + * (plus records allocated but not yet queued) has reached the amount + * of free space on the disk, then we need to go into an audit fail + * stop state, in which we do not permit the allocation/committing of + * any new audit records. We continue to process records but don't + * allow any activities that might generate new records. In the + * future, we might want to detect when space is available again and + * allow operation to continue, but this behavior is sufficient to + * meet fail stop requirements in CAPP. + */ + if (audit_fail_stop) { + if ((unsigned long)((audit_q_len + audit_pre_q_len + 1) * + MAX_AUDIT_RECORD_SIZE) / mnt_stat->f_bsize >= + (unsigned long)(mnt_stat->f_bfree)) { + if (ppsratecheck(&last_fail, &cur_fail, 1)) + printf("audit_record_write: free space " + "below size of audit queue, failing " + "stop\n"); + audit_in_failure = 1; + } else if (audit_in_failure) { + /* + * Note: if we want to handle recovery, this is the + * spot to do it: unset audit_in_failure, and issue a + * wakeup on the cv. + */ + } + } + + error = vn_rdwr(UIO_WRITE, vp, data, len, (off_t)0, UIO_SYSSPACE, + IO_APPEND|IO_UNIT, cred, NULL, NULL, curthread); + if (error == ENOSPC) + goto fail_enospc; + else if (error) + goto fail; + + /* + * Catch completion of a queue drain here; if we're draining and the + * queue is now empty, fail stop. That audit_fail_stop is implicitly + * true, since audit_in_failure can only be set of audit_fail_stop is + * set. + * + * Note: if we handle recovery from audit_in_failure, then we need to + * make panic here conditional. + */ + if (audit_in_failure) { + if (audit_q_len == 0 && audit_pre_q_len == 0) { + VOP_LOCK(vp, LK_EXCLUSIVE | LK_RETRY); + (void)VOP_FSYNC(vp, MNT_WAIT, curthread); + VOP_UNLOCK(vp, 0); + panic("Audit store overflow; record queue drained."); + } + } + + VFS_UNLOCK_GIANT(vfslocked); + return; + +fail_enospc: + /* + * ENOSPC is considered a special case with respect to failures, as + * this can reflect either our preemptive detection of insufficient + * space, or ENOSPC returned by the vnode write call. + */ + if (audit_fail_stop) { + VOP_LOCK(vp, LK_EXCLUSIVE | LK_RETRY); + (void)VOP_FSYNC(vp, MNT_WAIT, curthread); + VOP_UNLOCK(vp, 0); + panic("Audit log space exhausted and fail-stop set."); + } + (void)audit_send_trigger(AUDIT_TRIGGER_NO_SPACE); + audit_suspended = 1; + + /* FALLTHROUGH */ +fail: + /* + * We have failed to write to the file, so the current record is + * lost, which may require an immediate system halt. + */ + if (audit_panic_on_write_fail) { + VOP_LOCK(vp, LK_EXCLUSIVE | LK_RETRY); + (void)VOP_FSYNC(vp, MNT_WAIT, curthread); + VOP_UNLOCK(vp, 0); + panic("audit_worker: write error %d\n", error); + } else if (ppsratecheck(&last_fail, &cur_fail, 1)) + printf("audit_worker: write error %d\n", error); + VFS_UNLOCK_GIANT(vfslocked); +} + +/* + * Given a kernel audit record, process as required. Kernel audit records + * are converted to one, or possibly two, BSM records, depending on whether + * there is a user audit record present also. Kernel records need be + * converted to BSM before they can be written out. Both types will be + * written to disk, and audit pipes. + */ +static void +audit_worker_process_record(struct kaudit_record *ar) +{ + struct au_record *bsm; + au_class_t class; + au_event_t event; + au_id_t auid; + int error, sorf; + int locked; + + /* + * We hold the audit worker lock over both writes, if there are two, + * so that the two records won't be split across a rotation and end + * up in two different trail files. + */ + if (((ar->k_ar_commit & AR_COMMIT_USER) && + (ar->k_ar_commit & AR_PRESELECT_USER_TRAIL)) || + (ar->k_ar_commit & AR_PRESELECT_TRAIL)) { + AUDIT_WORKER_LOCK(); + locked = 1; + } else + locked = 0; + + /* + * First, handle the user record, if any: commit to the system trail + * and audit pipes as selected. + */ + if ((ar->k_ar_commit & AR_COMMIT_USER) && + (ar->k_ar_commit & AR_PRESELECT_USER_TRAIL)) { + AUDIT_WORKER_LOCK_ASSERT(); + audit_record_write(audit_vp, audit_cred, ar->k_udata, + ar->k_ulen); + } + + if ((ar->k_ar_commit & AR_COMMIT_USER) && + (ar->k_ar_commit & AR_PRESELECT_USER_PIPE)) + audit_pipe_submit_user(ar->k_udata, ar->k_ulen); + + if (!(ar->k_ar_commit & AR_COMMIT_KERNEL) || + ((ar->k_ar_commit & AR_PRESELECT_PIPE) == 0 && + (ar->k_ar_commit & AR_PRESELECT_TRAIL) == 0)) + goto out; + + auid = ar->k_ar.ar_subj_auid; + event = ar->k_ar.ar_event; + class = au_event_class(event); + if (ar->k_ar.ar_errno == 0) + sorf = AU_PRS_SUCCESS; + else + sorf = AU_PRS_FAILURE; + + error = kaudit_to_bsm(ar, &bsm); + switch (error) { + case BSM_NOAUDIT: + goto out; + + case BSM_FAILURE: + printf("audit_worker_process_record: BSM_FAILURE\n"); + goto out; + + case BSM_SUCCESS: + break; + + default: + panic("kaudit_to_bsm returned %d", error); + } + + if (ar->k_ar_commit & AR_PRESELECT_TRAIL) { + AUDIT_WORKER_LOCK_ASSERT(); + audit_record_write(audit_vp, audit_cred, bsm->data, bsm->len); + } + + if (ar->k_ar_commit & AR_PRESELECT_PIPE) + audit_pipe_submit(auid, event, class, sorf, + ar->k_ar_commit & AR_PRESELECT_TRAIL, bsm->data, + bsm->len); + + kau_free(bsm); +out: + if (locked) + AUDIT_WORKER_UNLOCK(); +} + +/* + * The audit_worker thread is responsible for watching the event queue, + * dequeueing records, converting them to BSM format, and committing them to + * disk. In order to minimize lock thrashing, records are dequeued in sets + * to a thread-local work queue. + * + * Note: this means that the effect bound on the size of the pending record + * queue is 2x the length of the global queue. + */ +static void +audit_worker(void *arg) +{ + struct kaudit_queue ar_worklist; + struct kaudit_record *ar; + int lowater_signal; + + TAILQ_INIT(&ar_worklist); + mtx_lock(&audit_mtx); + while (1) { + mtx_assert(&audit_mtx, MA_OWNED); + + /* + * Wait for a record. + */ + while (TAILQ_EMPTY(&audit_q)) + cv_wait(&audit_worker_cv, &audit_mtx); + + /* + * If there are records in the global audit record queue, + * transfer them to a thread-local queue and process them + * one by one. If we cross the low watermark threshold, + * signal any waiting processes that they may wake up and + * continue generating records. + */ + lowater_signal = 0; + while ((ar = TAILQ_FIRST(&audit_q))) { + TAILQ_REMOVE(&audit_q, ar, k_q); + audit_q_len--; + if (audit_q_len == audit_qctrl.aq_lowater) + lowater_signal++; + TAILQ_INSERT_TAIL(&ar_worklist, ar, k_q); + } + if (lowater_signal) + cv_broadcast(&audit_watermark_cv); + + mtx_unlock(&audit_mtx); + while ((ar = TAILQ_FIRST(&ar_worklist))) { + TAILQ_REMOVE(&ar_worklist, ar, k_q); + audit_worker_process_record(ar); + audit_free(ar); + } + mtx_lock(&audit_mtx); + } +} + +/* + * audit_rotate_vnode() is called by a user or kernel thread to configure or + * de-configure auditing on a vnode. The arguments are the replacement + * credential (referenced) and vnode (referenced and opened) to substitute + * for the current credential and vnode, if any. If either is set to NULL, + * both should be NULL, and this is used to indicate that audit is being + * disabled. Any previous cred/vnode will be closed and freed. We re-enable + * generating rotation requests to auditd. + */ +void +audit_rotate_vnode(struct ucred *cred, struct vnode *vp) +{ + struct ucred *old_audit_cred; + struct vnode *old_audit_vp; + int vfslocked; + + KASSERT((cred != NULL && vp != NULL) || (cred == NULL && vp == NULL), + ("audit_rotate_vnode: cred %p vp %p", cred, vp)); + + /* + * Rotate the vnode/cred, and clear the rotate flag so that we will + * send a rotate trigger if the new file fills. + */ + AUDIT_WORKER_LOCK(); + old_audit_cred = audit_cred; + old_audit_vp = audit_vp; + audit_cred = cred; + audit_vp = vp; + audit_file_rotate_wait = 0; + audit_enabled = (audit_vp != NULL); + AUDIT_WORKER_UNLOCK(); + + /* + * If there was an old vnode/credential, close and free. + */ + if (old_audit_vp != NULL) { + vfslocked = VFS_LOCK_GIANT(old_audit_vp->v_mount); + vn_close(old_audit_vp, AUDIT_CLOSE_FLAGS, old_audit_cred, + curthread); + VFS_UNLOCK_GIANT(vfslocked); + crfree(old_audit_cred); + } +} + +void +audit_worker_init(void) +{ + int error; + + AUDIT_WORKER_LOCK_INIT(); + error = kproc_create(audit_worker, NULL, &audit_thread, RFHIGHPID, + 0, "audit"); + if (error) + panic("audit_worker_init: kproc_create returned %d", error); +} diff --git a/sys/security/mac/mac_atalk.c b/sys/security/mac/mac_atalk.c new file mode 100644 index 0000000..0992ee5 --- /dev/null +++ b/sys/security/mac/mac_atalk.c @@ -0,0 +1,66 @@ +/*- + * Copyright (c) 2007 Robert N. M. Watson + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/sbuf.h> +#include <sys/systm.h> +#include <sys/mount.h> +#include <sys/file.h> +#include <sys/namei.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_var.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +void +mac_netatalk_aarp_send(struct ifnet *ifp, struct mbuf *m) +{ + struct label *mlabel; + + mlabel = mac_mbuf_to_label(m); + + MAC_IFNET_LOCK(ifp); + MAC_PERFORM(netatalk_aarp_send, ifp, ifp->if_label, m, mlabel); + MAC_IFNET_UNLOCK(ifp); +} diff --git a/sys/security/mac/mac_audit.c b/sys/security/mac/mac_audit.c new file mode 100644 index 0000000..d8cd8e6 --- /dev/null +++ b/sys/security/mac/mac_audit.c @@ -0,0 +1,115 @@ +/*- + * Copyright (c) 1999-2002 Robert N. M. Watson + * Copyright (c) 2001 Ilmar S. Habibulin + * Copyright (c) 2001-2004 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * + * This software was developed by Robert Watson and Ilmar Habibulin for the + * TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/module.h> +#include <sys/vnode.h> + +#include <security/audit/audit.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +int +mac_proc_check_setaudit(struct ucred *cred, struct auditinfo *ai) +{ + int error; + + MAC_CHECK(proc_check_setaudit, cred, ai); + + return (error); +} + +int +mac_proc_check_setaudit_addr(struct ucred *cred, struct auditinfo_addr *aia) +{ + int error; + + MAC_CHECK(proc_check_setaudit_addr, cred, aia); + + return (error); +} + +int +mac_proc_check_setauid(struct ucred *cred, uid_t auid) +{ + int error; + + MAC_CHECK(proc_check_setauid, cred, auid); + + return (error); +} + +int +mac_system_check_audit(struct ucred *cred, void *record, int length) +{ + int error; + + MAC_CHECK(system_check_audit, cred, record, length); + + return (error); +} + +int +mac_system_check_auditctl(struct ucred *cred, struct vnode *vp) +{ + int error; + struct label *vl; + + ASSERT_VOP_LOCKED(vp, "mac_system_check_auditctl"); + + vl = (vp != NULL) ? vp->v_label : NULL; + + MAC_CHECK(system_check_auditctl, cred, vp, vl); + + return (error); +} + +int +mac_system_check_auditon(struct ucred *cred, int cmd) +{ + int error; + + MAC_CHECK(system_check_auditon, cred, cmd); + + return (error); +} diff --git a/sys/security/mac/mac_cred.c b/sys/security/mac/mac_cred.c new file mode 100644 index 0000000..4d46f9a --- /dev/null +++ b/sys/security/mac/mac_cred.c @@ -0,0 +1,213 @@ +/*- + * Copyright (c) 1999-2002, 2008 Robert N. M. Watson + * Copyright (c) 2001 Ilmar S. Habibulin + * Copyright (c) 2001-2003 Networks Associates Technology, Inc. + * Copyright (c) 2005 Samy Al Bahra + * Copyright (c) 2006 SPARTA, Inc. + * Copyright (c) 2008 Apple Inc. + * All rights reserved. + * + * This software was developed by Robert Watson and Ilmar Habibulin for the + * TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/condvar.h> +#include <sys/imgact.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/mac.h> +#include <sys/proc.h> +#include <sys/sbuf.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/file.h> +#include <sys/namei.h> +#include <sys/sysctl.h> + +#include <vm/vm.h> +#include <vm/pmap.h> +#include <vm/vm_map.h> +#include <vm/vm_object.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +struct label * +mac_cred_label_alloc(void) +{ + struct label *label; + + label = mac_labelzone_alloc(M_WAITOK); + MAC_PERFORM(cred_init_label, label); + return (label); +} + +void +mac_cred_init(struct ucred *cred) +{ + + if (mac_labeled & MPC_OBJECT_CRED) + cred->cr_label = mac_cred_label_alloc(); + else + cred->cr_label = NULL; +} + +void +mac_cred_label_free(struct label *label) +{ + + MAC_PERFORM(cred_destroy_label, label); + mac_labelzone_free(label); +} + +void +mac_cred_destroy(struct ucred *cred) +{ + + if (cred->cr_label != NULL) { + mac_cred_label_free(cred->cr_label); + cred->cr_label = NULL; + } +} + +/* + * When a thread becomes an NFS server daemon, its credential may need to be + * updated to reflect this so that policies can recognize when file system + * operations originate from the network. + * + * At some point, it would be desirable if the credential used for each NFS + * RPC could be set based on the RPC context (i.e., source system, etc) to + * provide more fine-grained access control. + */ +void +mac_cred_associate_nfsd(struct ucred *cred) +{ + + MAC_PERFORM(cred_associate_nfsd, cred); +} + +/* + * Initialize MAC label for the first kernel process, from which other kernel + * processes and threads are spawned. + */ +void +mac_cred_create_swapper(struct ucred *cred) +{ + + MAC_PERFORM(cred_create_swapper, cred); +} + +/* + * Initialize MAC label for the first userland process, from which other + * userland processes and threads are spawned. + */ +void +mac_cred_create_init(struct ucred *cred) +{ + + MAC_PERFORM(cred_create_init, cred); +} + +int +mac_cred_externalize_label(struct label *label, char *elements, + char *outbuf, size_t outbuflen) +{ + int error; + + MAC_EXTERNALIZE(cred, label, elements, outbuf, outbuflen); + + return (error); +} + +int +mac_cred_internalize_label(struct label *label, char *string) +{ + int error; + + MAC_INTERNALIZE(cred, label, string); + + return (error); +} + +/* + * When a new process is created, its label must be initialized. Generally, + * this involves inheritence from the parent process, modulo possible deltas. + * This function allows that processing to take place. + */ +void +mac_cred_copy(struct ucred *src, struct ucred *dest) +{ + + MAC_PERFORM(cred_copy_label, src->cr_label, dest->cr_label); +} + +/* + * When the subject's label changes, it may require revocation of privilege + * to mapped objects. This can't be done on-the-fly later with a unified + * buffer cache. + */ +void +mac_cred_relabel(struct ucred *cred, struct label *newlabel) +{ + + MAC_PERFORM(cred_relabel, cred, newlabel); +} + +int +mac_cred_check_relabel(struct ucred *cred, struct label *newlabel) +{ + int error; + + MAC_CHECK(cred_check_relabel, cred, newlabel); + + return (error); +} + +int +mac_cred_check_visible(struct ucred *cr1, struct ucred *cr2) +{ + int error; + + MAC_CHECK(cred_check_visible, cr1, cr2); + + return (error); +} diff --git a/sys/security/mac/mac_framework.c b/sys/security/mac/mac_framework.c new file mode 100644 index 0000000..0a7b085 --- /dev/null +++ b/sys/security/mac/mac_framework.c @@ -0,0 +1,571 @@ +/*- + * Copyright (c) 1999-2002, 2006 Robert N. M. Watson + * Copyright (c) 2001 Ilmar S. Habibulin + * Copyright (c) 2001-2005 Networks Associates Technology, Inc. + * Copyright (c) 2005-2006 SPARTA, Inc. + * Copyright (c) 2008 Apple Inc. + * All rights reserved. + * + * This software was developed by Robert Watson and Ilmar Habibulin for the + * TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/*- + * Framework for extensible kernel access control. This file contains core + * kernel infrastructure for the TrustedBSD MAC Framework, including policy + * registration, versioning, locking, error composition operator, and system + * calls. + * + * The MAC Framework implements three programming interfaces: + * + * - The kernel MAC interface, defined in mac_framework.h, and invoked + * throughout the kernel to request security decisions, notify of security + * related events, etc. + * + * - The MAC policy module interface, defined in mac_policy.h, which is + * implemented by MAC policy modules and invoked by the MAC Framework to + * forward kernel security requests and notifications to policy modules. + * + * - The user MAC API, defined in mac.h, which allows user programs to query + * and set label state on objects. + * + * The majority of the MAC Framework implementation may be found in + * src/sys/security/mac. Sample policy modules may be found in + * src/sys/security/mac_*. + */ + +#include "opt_mac.h" + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/condvar.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/mac.h> +#include <sys/module.h> +#include <sys/systm.h> +#include <sys/sysctl.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +/* + * Root sysctl node for all MAC and MAC policy controls. + */ +SYSCTL_NODE(_security, OID_AUTO, mac, CTLFLAG_RW, 0, + "TrustedBSD MAC policy controls"); + +/* + * Declare that the kernel provides MAC support, version 3 (FreeBSD 7.x). + * This permits modules to refuse to be loaded if the necessary support isn't + * present, even if it's pre-boot. + */ +MODULE_VERSION(kernel_mac_support, MAC_VERSION); + +static unsigned int mac_version = MAC_VERSION; +SYSCTL_UINT(_security_mac, OID_AUTO, version, CTLFLAG_RD, &mac_version, 0, + ""); + +/* + * Labels consist of a indexed set of "slots", which are allocated policies + * as required. The MAC Framework maintains a bitmask of slots allocated so + * far to prevent reuse. Slots cannot be reused, as the MAC Framework + * guarantees that newly allocated slots in labels will be NULL unless + * otherwise initialized, and because we do not have a mechanism to garbage + * collect slots on policy unload. As labeled policies tend to be statically + * loaded during boot, and not frequently unloaded and reloaded, this is not + * generally an issue. + */ +#if MAC_MAX_SLOTS > 32 +#error "MAC_MAX_SLOTS too large" +#endif + +static unsigned int mac_max_slots = MAC_MAX_SLOTS; +static unsigned int mac_slot_offsets_free = (1 << MAC_MAX_SLOTS) - 1; +SYSCTL_UINT(_security_mac, OID_AUTO, max_slots, CTLFLAG_RD, &mac_max_slots, + 0, ""); + +/* + * Has the kernel started generating labeled objects yet? All read/write + * access to this variable is serialized during the boot process. Following + * the end of serialization, we don't update this flag; no locking. + */ +static int mac_late = 0; + +/* + * Each policy declares a mask of object types requiring labels to be + * allocated for them. For convenience, we combine and cache the bitwise or + * of the per-policy object flags to track whether we will allocate a label + * for an object type at run-time. + */ +uint64_t mac_labeled; +SYSCTL_QUAD(_security_mac, OID_AUTO, labeled, CTLFLAG_RD, &mac_labeled, 0, + "Mask of object types being labeled"); + +MALLOC_DEFINE(M_MACTEMP, "mactemp", "MAC temporary label storage"); + +/* + * mac_static_policy_list holds a list of policy modules that are not loaded + * while the system is "live", and cannot be unloaded. These policies can be + * invoked without holding the busy count. + * + * mac_policy_list stores the list of dynamic policies. A busy count is + * maintained for the list, stored in mac_policy_busy. The busy count is + * protected by mac_policy_mtx; the list may be modified only while the busy + * count is 0, requiring that the lock be held to prevent new references to + * the list from being acquired. For almost all operations, incrementing the + * busy count is sufficient to guarantee consistency, as the list cannot be + * modified while the busy count is elevated. For a few special operations + * involving a change to the list of active policies, the mtx itself must be + * held. A condition variable, mac_policy_cv, is used to signal potential + * exclusive consumers that they should try to acquire the lock if a first + * attempt at exclusive access fails. + * + * This design intentionally avoids fairness, and may starve attempts to + * acquire an exclusive lock on a busy system. This is required because we + * do not ever want acquiring a read reference to perform an unbounded length + * sleep. Read references are acquired in ithreads, network isrs, etc, and + * any unbounded blocking could lead quickly to deadlock. + * + * Another reason for never blocking on read references is that the MAC + * Framework may recurse: if a policy calls a VOP, for example, this might + * lead to vnode life cycle operations (such as init/destroy). + * + * If the kernel option MAC_STATIC has been compiled in, all locking becomes + * a no-op, and the global list of policies is not allowed to change after + * early boot. + * + * XXXRW: Currently, we signal mac_policy_cv every time the framework becomes + * unbusy and there is a thread waiting to enter it exclusively. Since it + * may take some time before the thread runs, we may issue a lot of signals. + * We should instead keep track of the fact that we've signalled, taking into + * account that the framework may be busy again by the time the thread runs, + * requiring us to re-signal. + */ +#ifndef MAC_STATIC +static struct mtx mac_policy_mtx; +static struct cv mac_policy_cv; +static int mac_policy_count; +static int mac_policy_wait; +#endif +struct mac_policy_list_head mac_policy_list; +struct mac_policy_list_head mac_static_policy_list; + +/* + * We manually invoke WITNESS_WARN() to allow Witness to generate warnings + * even if we don't end up ever triggering the wait at run-time. The + * consumer of the exclusive interface must not hold any locks (other than + * potentially Giant) since we may sleep for long (potentially indefinite) + * periods of time waiting for the framework to become quiescent so that a + * policy list change may be made. + */ +void +mac_policy_grab_exclusive(void) +{ + +#ifndef MAC_STATIC + if (!mac_late) + return; + + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, + "mac_policy_grab_exclusive() at %s:%d", __FILE__, __LINE__); + mtx_lock(&mac_policy_mtx); + while (mac_policy_count != 0) { + mac_policy_wait++; + cv_wait(&mac_policy_cv, &mac_policy_mtx); + mac_policy_wait--; + } +#endif +} + +void +mac_policy_assert_exclusive(void) +{ + +#ifndef MAC_STATIC + if (!mac_late) + return; + + mtx_assert(&mac_policy_mtx, MA_OWNED); + KASSERT(mac_policy_count == 0, + ("mac_policy_assert_exclusive(): not exclusive")); +#endif +} + +void +mac_policy_release_exclusive(void) +{ +#ifndef MAC_STATIC + int dowakeup; + + if (!mac_late) + return; + + KASSERT(mac_policy_count == 0, + ("mac_policy_release_exclusive(): not exclusive")); + dowakeup = (mac_policy_wait != 0); + mtx_unlock(&mac_policy_mtx); + if (dowakeup) + cv_signal(&mac_policy_cv); +#endif +} + +void +mac_policy_list_busy(void) +{ + +#ifndef MAC_STATIC + if (!mac_late) + return; + + mtx_lock(&mac_policy_mtx); + mac_policy_count++; + mtx_unlock(&mac_policy_mtx); +#endif +} + +int +mac_policy_list_conditional_busy(void) +{ +#ifndef MAC_STATIC + int ret; + + if (!mac_late) + return (1); + + mtx_lock(&mac_policy_mtx); + if (!LIST_EMPTY(&mac_policy_list)) { + mac_policy_count++; + ret = 1; + } else + ret = 0; + mtx_unlock(&mac_policy_mtx); + return (ret); +#else + return (1); +#endif +} + +void +mac_policy_list_unbusy(void) +{ +#ifndef MAC_STATIC + int dowakeup; + + if (!mac_late) + return; + + mtx_lock(&mac_policy_mtx); + mac_policy_count--; + KASSERT(mac_policy_count >= 0, ("MAC_POLICY_LIST_LOCK")); + dowakeup = (mac_policy_count == 0 && mac_policy_wait != 0); + mtx_unlock(&mac_policy_mtx); + + if (dowakeup) + cv_signal(&mac_policy_cv); +#endif +} + +/* + * Initialize the MAC subsystem, including appropriate SMP locks. + */ +static void +mac_init(void) +{ + + LIST_INIT(&mac_static_policy_list); + LIST_INIT(&mac_policy_list); + mac_labelzone_init(); + +#ifndef MAC_STATIC + mtx_init(&mac_policy_mtx, "mac_policy_mtx", NULL, MTX_DEF); + cv_init(&mac_policy_cv, "mac_policy_cv"); +#endif +} + +/* + * For the purposes of modules that want to know if they were loaded "early", + * set the mac_late flag once we've processed modules either linked into the + * kernel, or loaded before the kernel startup. + */ +static void +mac_late_init(void) +{ + + mac_late = 1; +} + +/* + * After the policy list has changed, walk the list to update any global + * flags. Currently, we support only one flag, and it's conditionally + * defined; as a result, the entire function is conditional. Eventually, the + * #else case might also iterate across the policies. + */ +static void +mac_policy_updateflags(void) +{ + struct mac_policy_conf *mpc; + + mac_policy_assert_exclusive(); + + mac_labeled = 0; + LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) + mac_labeled |= mpc->mpc_labeled; + LIST_FOREACH(mpc, &mac_policy_list, mpc_list) + mac_labeled |= mpc->mpc_labeled; +} + +static int +mac_policy_register(struct mac_policy_conf *mpc) +{ + struct mac_policy_conf *tmpc; + int error, slot, static_entry; + + error = 0; + + /* + * We don't technically need exclusive access while !mac_late, but + * hold it for assertion consistency. + */ + mac_policy_grab_exclusive(); + + /* + * If the module can potentially be unloaded, or we're loading late, + * we have to stick it in the non-static list and pay an extra + * performance overhead. Otherwise, we can pay a light locking cost + * and stick it in the static list. + */ + static_entry = (!mac_late && + !(mpc->mpc_loadtime_flags & MPC_LOADTIME_FLAG_UNLOADOK)); + + if (static_entry) { + LIST_FOREACH(tmpc, &mac_static_policy_list, mpc_list) { + if (strcmp(tmpc->mpc_name, mpc->mpc_name) == 0) { + error = EEXIST; + goto out; + } + } + } else { + LIST_FOREACH(tmpc, &mac_policy_list, mpc_list) { + if (strcmp(tmpc->mpc_name, mpc->mpc_name) == 0) { + error = EEXIST; + goto out; + } + } + } + if (mpc->mpc_field_off != NULL) { + slot = ffs(mac_slot_offsets_free); + if (slot == 0) { + error = ENOMEM; + goto out; + } + slot--; + mac_slot_offsets_free &= ~(1 << slot); + *mpc->mpc_field_off = slot; + } + mpc->mpc_runtime_flags |= MPC_RUNTIME_FLAG_REGISTERED; + + /* + * If we're loading a MAC module after the framework has initialized, + * it has to go into the dynamic list. If we're loading it before + * we've finished initializing, it can go into the static list with + * weaker locker requirements. + */ + if (static_entry) + LIST_INSERT_HEAD(&mac_static_policy_list, mpc, mpc_list); + else + LIST_INSERT_HEAD(&mac_policy_list, mpc, mpc_list); + + /* + * Per-policy initialization. Currently, this takes place under the + * exclusive lock, so policies must not sleep in their init method. + * In the future, we may want to separate "init" from "start", with + * "init" occuring without the lock held. Likewise, on tear-down, + * breaking out "stop" from "destroy". + */ + if (mpc->mpc_ops->mpo_init != NULL) + (*(mpc->mpc_ops->mpo_init))(mpc); + mac_policy_updateflags(); + + printf("Security policy loaded: %s (%s)\n", mpc->mpc_fullname, + mpc->mpc_name); + +out: + mac_policy_release_exclusive(); + return (error); +} + +static int +mac_policy_unregister(struct mac_policy_conf *mpc) +{ + + /* + * If we fail the load, we may get a request to unload. Check to see + * if we did the run-time registration, and if not, silently succeed. + */ + mac_policy_grab_exclusive(); + if ((mpc->mpc_runtime_flags & MPC_RUNTIME_FLAG_REGISTERED) == 0) { + mac_policy_release_exclusive(); + return (0); + } +#if 0 + /* + * Don't allow unloading modules with private data. + */ + if (mpc->mpc_field_off != NULL) { + MAC_POLICY_LIST_UNLOCK(); + return (EBUSY); + } +#endif + /* + * Only allow the unload to proceed if the module is unloadable by + * its own definition. + */ + if ((mpc->mpc_loadtime_flags & MPC_LOADTIME_FLAG_UNLOADOK) == 0) { + mac_policy_release_exclusive(); + return (EBUSY); + } + if (mpc->mpc_ops->mpo_destroy != NULL) + (*(mpc->mpc_ops->mpo_destroy))(mpc); + + LIST_REMOVE(mpc, mpc_list); + mpc->mpc_runtime_flags &= ~MPC_RUNTIME_FLAG_REGISTERED; + mac_policy_updateflags(); + + mac_policy_release_exclusive(); + + printf("Security policy unload: %s (%s)\n", mpc->mpc_fullname, + mpc->mpc_name); + + return (0); +} + +/* + * Allow MAC policy modules to register during boot, etc. + */ +int +mac_policy_modevent(module_t mod, int type, void *data) +{ + struct mac_policy_conf *mpc; + int error; + + error = 0; + mpc = (struct mac_policy_conf *) data; + +#ifdef MAC_STATIC + if (mac_late) { + printf("mac_policy_modevent: MAC_STATIC and late\n"); + return (EBUSY); + } +#endif + + switch (type) { + case MOD_LOAD: + if (mpc->mpc_loadtime_flags & MPC_LOADTIME_FLAG_NOTLATE && + mac_late) { + printf("mac_policy_modevent: can't load %s policy " + "after booting\n", mpc->mpc_name); + error = EBUSY; + break; + } + error = mac_policy_register(mpc); + break; + case MOD_UNLOAD: + /* Don't unregister the module if it was never registered. */ + if ((mpc->mpc_runtime_flags & MPC_RUNTIME_FLAG_REGISTERED) + != 0) + error = mac_policy_unregister(mpc); + else + error = 0; + break; + default: + error = EOPNOTSUPP; + break; + } + + return (error); +} + +/* + * Define an error value precedence, and given two arguments, selects the + * value with the higher precedence. + */ +int +mac_error_select(int error1, int error2) +{ + + /* Certain decision-making errors take top priority. */ + if (error1 == EDEADLK || error2 == EDEADLK) + return (EDEADLK); + + /* Invalid arguments should be reported where possible. */ + if (error1 == EINVAL || error2 == EINVAL) + return (EINVAL); + + /* Precedence goes to "visibility", with both process and file. */ + if (error1 == ESRCH || error2 == ESRCH) + return (ESRCH); + + if (error1 == ENOENT || error2 == ENOENT) + return (ENOENT); + + /* Precedence goes to DAC/MAC protections. */ + if (error1 == EACCES || error2 == EACCES) + return (EACCES); + + /* Precedence goes to privilege. */ + if (error1 == EPERM || error2 == EPERM) + return (EPERM); + + /* Precedence goes to error over success; otherwise, arbitrary. */ + if (error1 != 0) + return (error1); + return (error2); +} + +int +mac_check_structmac_consistent(struct mac *mac) +{ + + if (mac->m_buflen < 0 || + mac->m_buflen > MAC_MAX_LABEL_BUF_LEN) + return (EINVAL); + + return (0); +} + +SYSINIT(mac, SI_SUB_MAC, SI_ORDER_FIRST, mac_init, NULL); +SYSINIT(mac_late, SI_SUB_MAC_LATE, SI_ORDER_FIRST, mac_late_init, NULL); diff --git a/sys/security/mac/mac_framework.h b/sys/security/mac/mac_framework.h new file mode 100644 index 0000000..4da4af9 --- /dev/null +++ b/sys/security/mac/mac_framework.h @@ -0,0 +1,445 @@ +/*- + * Copyright (c) 1999-2002, 2007-2008 Robert N. M. Watson + * Copyright (c) 2001-2005 Networks Associates Technology, Inc. + * Copyright (c) 2005-2006 SPARTA, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Kernel interface for Mandatory Access Control -- how kernel services + * interact with the TrustedBSD MAC Framework. + */ + +#ifndef _SECURITY_MAC_MAC_FRAMEWORK_H_ +#define _SECURITY_MAC_MAC_FRAMEWORK_H_ + +#ifndef _KERNEL +#error "no user-serviceable parts inside" +#endif + +struct auditinfo; +struct auditinfo_addr; +struct bpf_d; +struct cdev; +struct componentname; +struct devfs_dirent; +struct ifnet; +struct ifreq; +struct image_params; +struct inpcb; +struct ip6q; +struct ipq; +struct ksem; +struct label; +struct m_tag; +struct mac; +struct mbuf; +struct mount; +struct msg; +struct msqid_kernel; +struct proc; +struct semid_kernel; +struct shmfd; +struct shmid_kernel; +struct sockaddr; +struct socket; +struct sysctl_oid; +struct sysctl_req; +struct pipepair; +struct thread; +struct timespec; +struct ucred; +struct uio; +struct vattr; +struct vnode; +struct vop_setlabel_args; + +#include <sys/acl.h> /* XXX acl_type_t */ +#include <sys/types.h> /* accmode_t */ + +/* + * Entry points to the TrustedBSD MAC Framework from the remainder of the + * kernel: entry points are named based on a principle object type and an + * action relating to it. They are sorted alphabetically first by object + * type and then action. In some situations, the principle object type is + * obvious, and in other cases, less so as multiple objects may be inolved + * in the operation. + */ +int mac_bpfdesc_check_receive(struct bpf_d *d, struct ifnet *ifp); +void mac_bpfdesc_create(struct ucred *cred, struct bpf_d *d); +void mac_bpfdesc_create_mbuf(struct bpf_d *d, struct mbuf *m); +void mac_bpfdesc_destroy(struct bpf_d *); +void mac_bpfdesc_init(struct bpf_d *); + +void mac_cred_associate_nfsd(struct ucred *cred); +int mac_cred_check_visible(struct ucred *cr1, struct ucred *cr2); +void mac_cred_copy(struct ucred *cr1, struct ucred *cr2); +void mac_cred_create_init(struct ucred *cred); +void mac_cred_create_swapper(struct ucred *cred); +void mac_cred_destroy(struct ucred *); +void mac_cred_init(struct ucred *); + +void mac_devfs_create_device(struct ucred *cred, struct mount *mp, + struct cdev *dev, struct devfs_dirent *de); +void mac_devfs_create_directory(struct mount *mp, char *dirname, + int dirnamelen, struct devfs_dirent *de); +void mac_devfs_create_symlink(struct ucred *cred, struct mount *mp, + struct devfs_dirent *dd, struct devfs_dirent *de); +void mac_devfs_destroy(struct devfs_dirent *); +void mac_devfs_init(struct devfs_dirent *); +void mac_devfs_update(struct mount *mp, struct devfs_dirent *de, + struct vnode *vp); +void mac_devfs_vnode_associate(struct mount *mp, struct devfs_dirent *de, + struct vnode *vp); + +int mac_ifnet_check_transmit(struct ifnet *ifp, struct mbuf *m); +void mac_ifnet_create(struct ifnet *ifp); +void mac_ifnet_create_mbuf(struct ifnet *ifp, struct mbuf *m); +void mac_ifnet_destroy(struct ifnet *); +void mac_ifnet_init(struct ifnet *); +int mac_ifnet_ioctl_get(struct ucred *cred, struct ifreq *ifr, + struct ifnet *ifp); +int mac_ifnet_ioctl_set(struct ucred *cred, struct ifreq *ifr, + struct ifnet *ifp); + +int mac_inpcb_check_deliver(struct inpcb *inp, struct mbuf *m); +int mac_inpcb_check_visible(struct ucred *cred, struct inpcb *inp); +void mac_inpcb_create(struct socket *so, struct inpcb *inp); +void mac_inpcb_create_mbuf(struct inpcb *inp, struct mbuf *m); +void mac_inpcb_destroy(struct inpcb *); +int mac_inpcb_init(struct inpcb *, int); +void mac_inpcb_sosetlabel(struct socket *so, struct inpcb *inp); + +void mac_ip6q_create(struct mbuf *m, struct ip6q *q6); +void mac_ip6q_destroy(struct ip6q *q6); +int mac_ip6q_init(struct ip6q *q6, int); +int mac_ip6q_match(struct mbuf *m, struct ip6q *q6); +void mac_ip6q_reassemble(struct ip6q *q6, struct mbuf *m); +void mac_ip6q_update(struct mbuf *m, struct ip6q *q6); + +void mac_ipq_create(struct mbuf *m, struct ipq *q); +void mac_ipq_destroy(struct ipq *q); +int mac_ipq_init(struct ipq *q, int); +int mac_ipq_match(struct mbuf *m, struct ipq *q); +void mac_ipq_reassemble(struct ipq *q, struct mbuf *m); +void mac_ipq_update(struct mbuf *m, struct ipq *q); + +int mac_kenv_check_dump(struct ucred *cred); +int mac_kenv_check_get(struct ucred *cred, char *name); +int mac_kenv_check_set(struct ucred *cred, char *name, char *value); +int mac_kenv_check_unset(struct ucred *cred, char *name); + +int mac_kld_check_load(struct ucred *cred, struct vnode *vp); +int mac_kld_check_stat(struct ucred *cred); + +void mac_mbuf_copy(struct mbuf *, struct mbuf *); +int mac_mbuf_init(struct mbuf *, int); + +void mac_mbuf_tag_copy(struct m_tag *, struct m_tag *); +void mac_mbuf_tag_destroy(struct m_tag *); +int mac_mbuf_tag_init(struct m_tag *, int); + +int mac_mount_check_stat(struct ucred *cred, struct mount *mp); +void mac_mount_create(struct ucred *cred, struct mount *mp); +void mac_mount_destroy(struct mount *); +void mac_mount_init(struct mount *); + +void mac_netatalk_aarp_send(struct ifnet *ifp, struct mbuf *m); + +void mac_netinet_arp_send(struct ifnet *ifp, struct mbuf *m); +void mac_netinet_firewall_reply(struct mbuf *mrecv, struct mbuf *msend); +void mac_netinet_firewall_send(struct mbuf *m); +void mac_netinet_fragment(struct mbuf *m, struct mbuf *frag); +void mac_netinet_icmp_reply(struct mbuf *mrecv, struct mbuf *msend); +void mac_netinet_icmp_replyinplace(struct mbuf *m); +void mac_netinet_igmp_send(struct ifnet *ifp, struct mbuf *m); +void mac_netinet_tcp_reply(struct mbuf *m); + +void mac_netinet6_nd6_send(struct ifnet *ifp, struct mbuf *m); + +int mac_pipe_check_ioctl(struct ucred *cred, struct pipepair *pp, + unsigned long cmd, void *data); +int mac_pipe_check_poll(struct ucred *cred, struct pipepair *pp); +int mac_pipe_check_read(struct ucred *cred, struct pipepair *pp); +int mac_pipe_check_stat(struct ucred *cred, struct pipepair *pp); +int mac_pipe_check_write(struct ucred *cred, struct pipepair *pp); +void mac_pipe_create(struct ucred *cred, struct pipepair *pp); +void mac_pipe_destroy(struct pipepair *); +void mac_pipe_init(struct pipepair *); +int mac_pipe_label_set(struct ucred *cred, struct pipepair *pp, + struct label *label); + +int mac_posixsem_check_getvalue(struct ucred *active_cred, + struct ucred *file_cred, struct ksem *ks); +int mac_posixsem_check_open(struct ucred *cred, struct ksem *ks); +int mac_posixsem_check_post(struct ucred *active_cred, + struct ucred *file_cred, struct ksem *ks); +int mac_posixsem_check_stat(struct ucred *active_cred, + struct ucred *file_cred, struct ksem *ks); +int mac_posixsem_check_unlink(struct ucred *cred, struct ksem *ks); +int mac_posixsem_check_wait(struct ucred *active_cred, + struct ucred *file_cred, struct ksem *ks); +void mac_posixsem_create(struct ucred *cred, struct ksem *ks); +void mac_posixsem_destroy(struct ksem *); +void mac_posixsem_init(struct ksem *); + +int mac_posixshm_check_mmap(struct ucred *cred, struct shmfd *shmfd, + int prot, int flags); +int mac_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd); +int mac_posixshm_check_stat(struct ucred *active_cred, + struct ucred *file_cred, struct shmfd *shmfd); +int mac_posixshm_check_truncate(struct ucred *active_cred, + struct ucred *file_cred, struct shmfd *shmfd); +int mac_posixshm_check_unlink(struct ucred *cred, struct shmfd *shmfd); +void mac_posixshm_create(struct ucred *cred, struct shmfd *shmfd); +void mac_posixshm_destroy(struct shmfd *); +void mac_posixshm_init(struct shmfd *); + +int mac_priv_check(struct ucred *cred, int priv); +int mac_priv_grant(struct ucred *cred, int priv); + +int mac_proc_check_debug(struct ucred *cred, struct proc *p); +int mac_proc_check_sched(struct ucred *cred, struct proc *p); +int mac_proc_check_setaudit(struct ucred *cred, struct auditinfo *ai); +int mac_proc_check_setaudit_addr(struct ucred *cred, + struct auditinfo_addr *aia); +int mac_proc_check_setauid(struct ucred *cred, uid_t auid); +int mac_proc_check_setegid(struct proc *p, struct ucred *cred, + gid_t egid); +int mac_proc_check_seteuid(struct proc *p, struct ucred *cred, + uid_t euid); +int mac_proc_check_setgid(struct proc *p, struct ucred *cred, + gid_t gid); +int mac_proc_check_setgroups(struct proc *p, struct ucred *cred, + int ngroups, gid_t *gidset); +int mac_proc_check_setregid(struct proc *p, struct ucred *cred, + gid_t rgid, gid_t egid); +int mac_proc_check_setresgid(struct proc *p, struct ucred *cred, + gid_t rgid, gid_t egid, gid_t sgid); +int mac_proc_check_setresuid(struct proc *p, struct ucred *cred, + uid_t ruid, uid_t euid, uid_t suid); +int mac_proc_check_setreuid(struct proc *p, struct ucred *cred, + uid_t ruid, uid_t euid); +int mac_proc_check_setuid(struct proc *p, struct ucred *cred, + uid_t uid); +int mac_proc_check_signal(struct ucred *cred, struct proc *p, + int signum); +int mac_proc_check_wait(struct ucred *cred, struct proc *p); +void mac_proc_destroy(struct proc *); +void mac_proc_init(struct proc *); +void mac_proc_vm_revoke(struct thread *td); +int mac_execve_enter(struct image_params *imgp, struct mac *mac_p); +void mac_execve_exit(struct image_params *imgp); +void mac_execve_interpreter_enter(struct vnode *interpvp, + struct label **interplabel); +void mac_execve_interpreter_exit(struct label *interpvplabel); + +int mac_socket_check_accept(struct ucred *cred, struct socket *so); +int mac_socket_check_bind(struct ucred *cred, struct socket *so, + struct sockaddr *sa); +int mac_socket_check_connect(struct ucred *cred, struct socket *so, + struct sockaddr *sa); +int mac_socket_check_create(struct ucred *cred, int domain, int type, + int proto); +int mac_socket_check_deliver(struct socket *so, struct mbuf *m); +int mac_socket_check_listen(struct ucred *cred, struct socket *so); +int mac_socket_check_poll(struct ucred *cred, struct socket *so); +int mac_socket_check_receive(struct ucred *cred, struct socket *so); +int mac_socket_check_send(struct ucred *cred, struct socket *so); +int mac_socket_check_stat(struct ucred *cred, struct socket *so); +int mac_socket_check_visible(struct ucred *cred, struct socket *so); +void mac_socket_create_mbuf(struct socket *so, struct mbuf *m); +void mac_socket_create(struct ucred *cred, struct socket *so); +void mac_socket_destroy(struct socket *); +int mac_socket_init(struct socket *, int); +void mac_socket_newconn(struct socket *oldso, struct socket *newso); +int mac_getsockopt_label(struct ucred *cred, struct socket *so, + struct mac *extmac); +int mac_getsockopt_peerlabel(struct ucred *cred, struct socket *so, + struct mac *extmac); +int mac_setsockopt_label(struct ucred *cred, struct socket *so, + struct mac *extmac); + +void mac_socketpeer_set_from_mbuf(struct mbuf *m, struct socket *so); +void mac_socketpeer_set_from_socket(struct socket *oldso, + struct socket *newso); + +void mac_syncache_create(struct label *l, struct inpcb *inp); +void mac_syncache_create_mbuf(struct label *l, struct mbuf *m); +void mac_syncache_destroy(struct label **l); +int mac_syncache_init(struct label **l); + +int mac_system_check_acct(struct ucred *cred, struct vnode *vp); +int mac_system_check_audit(struct ucred *cred, void *record, int length); +int mac_system_check_auditctl(struct ucred *cred, struct vnode *vp); +int mac_system_check_auditon(struct ucred *cred, int cmd); +int mac_system_check_reboot(struct ucred *cred, int howto); +int mac_system_check_swapon(struct ucred *cred, struct vnode *vp); +int mac_system_check_swapoff(struct ucred *cred, struct vnode *vp); +int mac_system_check_sysctl(struct ucred *cred, struct sysctl_oid *oidp, + void *arg1, int arg2, struct sysctl_req *req); + +void mac_sysvmsg_cleanup(struct msg *msgptr); +void mac_sysvmsg_create(struct ucred *cred, struct msqid_kernel *msqkptr, + struct msg *msgptr); +void mac_sysvmsg_destroy(struct msg *); +void mac_sysvmsg_init(struct msg *); + +int mac_sysvmsq_check_msgmsq(struct ucred *cred, struct msg *msgptr, + struct msqid_kernel *msqkptr); +int mac_sysvmsq_check_msgrcv(struct ucred *cred, struct msg *msgptr); +int mac_sysvmsq_check_msgrmid(struct ucred *cred, struct msg *msgptr); +int mac_sysvmsq_check_msqctl(struct ucred *cred, + struct msqid_kernel *msqkptr, int cmd); +int mac_sysvmsq_check_msqget(struct ucred *cred, + struct msqid_kernel *msqkptr); +int mac_sysvmsq_check_msqrcv(struct ucred *cred, + struct msqid_kernel *msqkptr); +int mac_sysvmsq_check_msqsnd(struct ucred *cred, + struct msqid_kernel *msqkptr); +void mac_sysvmsq_cleanup(struct msqid_kernel *msqkptr); +void mac_sysvmsq_create(struct ucred *cred, struct msqid_kernel *msqkptr); +void mac_sysvmsq_destroy(struct msqid_kernel *); +void mac_sysvmsq_init(struct msqid_kernel *); + +int mac_sysvsem_check_semctl(struct ucred *cred, + struct semid_kernel *semakptr, int cmd); +int mac_sysvsem_check_semget(struct ucred *cred, + struct semid_kernel *semakptr); +int mac_sysvsem_check_semop(struct ucred *cred, + struct semid_kernel *semakptr, size_t accesstype); +void mac_sysvsem_cleanup(struct semid_kernel *semakptr); +void mac_sysvsem_create(struct ucred *cred, + struct semid_kernel *semakptr); +void mac_sysvsem_destroy(struct semid_kernel *); +void mac_sysvsem_init(struct semid_kernel *); + +int mac_sysvshm_check_shmat(struct ucred *cred, + struct shmid_kernel *shmsegptr, int shmflg); +int mac_sysvshm_check_shmctl(struct ucred *cred, + struct shmid_kernel *shmsegptr, int cmd); +int mac_sysvshm_check_shmdt(struct ucred *cred, + struct shmid_kernel *shmsegptr); +int mac_sysvshm_check_shmget(struct ucred *cred, + struct shmid_kernel *shmsegptr, int shmflg); +void mac_sysvshm_cleanup(struct shmid_kernel *shmsegptr); +void mac_sysvshm_create(struct ucred *cred, + struct shmid_kernel *shmsegptr); +void mac_sysvshm_destroy(struct shmid_kernel *); +void mac_sysvshm_init(struct shmid_kernel *); + +void mac_thread_userret(struct thread *td); + +int mac_vnode_associate_extattr(struct mount *mp, struct vnode *vp); +void mac_vnode_associate_singlelabel(struct mount *mp, struct vnode *vp); +int mac_vnode_check_access(struct ucred *cred, struct vnode *vp, + accmode_t accmode); +int mac_vnode_check_chdir(struct ucred *cred, struct vnode *dvp); +int mac_vnode_check_chroot(struct ucred *cred, struct vnode *dvp); +int mac_vnode_check_create(struct ucred *cred, struct vnode *dvp, + struct componentname *cnp, struct vattr *vap); +int mac_vnode_check_deleteacl(struct ucred *cred, struct vnode *vp, + acl_type_t type); +int mac_vnode_check_deleteextattr(struct ucred *cred, struct vnode *vp, + int attrnamespace, const char *name); +int mac_vnode_check_exec(struct ucred *cred, struct vnode *vp, + struct image_params *imgp); +int mac_vnode_check_getacl(struct ucred *cred, struct vnode *vp, + acl_type_t type); +int mac_vnode_check_getextattr(struct ucred *cred, struct vnode *vp, + int attrnamespace, const char *name, struct uio *uio); +int mac_vnode_check_link(struct ucred *cred, struct vnode *dvp, + struct vnode *vp, struct componentname *cnp); +int mac_vnode_check_listextattr(struct ucred *cred, struct vnode *vp, + int attrnamespace); +int mac_vnode_check_lookup(struct ucred *cred, struct vnode *dvp, + struct componentname *cnp); +int mac_vnode_check_mmap(struct ucred *cred, struct vnode *vp, int prot, + int flags); +int mac_vnode_check_mprotect(struct ucred *cred, struct vnode *vp, + int prot); +int mac_vnode_check_open(struct ucred *cred, struct vnode *vp, + accmode_t accmode); +int mac_vnode_check_poll(struct ucred *active_cred, + struct ucred *file_cred, struct vnode *vp); +int mac_vnode_check_read(struct ucred *active_cred, + struct ucred *file_cred, struct vnode *vp); +int mac_vnode_check_readdir(struct ucred *cred, struct vnode *vp); +int mac_vnode_check_readlink(struct ucred *cred, struct vnode *vp); +int mac_vnode_check_rename_from(struct ucred *cred, struct vnode *dvp, + struct vnode *vp, struct componentname *cnp); +int mac_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp, + struct vnode *vp, int samedir, struct componentname *cnp); +int mac_vnode_check_revoke(struct ucred *cred, struct vnode *vp); +int mac_vnode_check_setacl(struct ucred *cred, struct vnode *vp, + acl_type_t type, struct acl *acl); +int mac_vnode_check_setextattr(struct ucred *cred, struct vnode *vp, + int attrnamespace, const char *name, struct uio *uio); +int mac_vnode_check_setflags(struct ucred *cred, struct vnode *vp, + u_long flags); +int mac_vnode_check_setmode(struct ucred *cred, struct vnode *vp, + mode_t mode); +int mac_vnode_check_setowner(struct ucred *cred, struct vnode *vp, + uid_t uid, gid_t gid); +int mac_vnode_check_setutimes(struct ucred *cred, struct vnode *vp, + struct timespec atime, struct timespec mtime); +int mac_vnode_check_stat(struct ucred *active_cred, + struct ucred *file_cred, struct vnode *vp); +int mac_vnode_check_unlink(struct ucred *cred, struct vnode *dvp, + struct vnode *vp, struct componentname *cnp); +int mac_vnode_check_write(struct ucred *active_cred, + struct ucred *file_cred, struct vnode *vp); +void mac_vnode_copy_label(struct label *, struct label *); +void mac_vnode_init(struct vnode *); +int mac_vnode_create_extattr(struct ucred *cred, struct mount *mp, + struct vnode *dvp, struct vnode *vp, struct componentname *cnp); +void mac_vnode_destroy(struct vnode *); +void mac_vnode_execve_transition(struct ucred *oldcred, + struct ucred *newcred, struct vnode *vp, + struct label *interpvplabel, struct image_params *imgp); +int mac_vnode_execve_will_transition(struct ucred *cred, + struct vnode *vp, struct label *interpvplabel, + struct image_params *imgp); +void mac_vnode_relabel(struct ucred *cred, struct vnode *vp, + struct label *newlabel); + +/* + * Calls to help various file systems implement labeling functionality using + * their existing EA implementation. + */ +int vop_stdsetlabel_ea(struct vop_setlabel_args *ap); + +#endif /* !_SECURITY_MAC_MAC_FRAMEWORK_H_ */ diff --git a/sys/security/mac/mac_inet.c b/sys/security/mac/mac_inet.c new file mode 100644 index 0000000..b11f5b7 --- /dev/null +++ b/sys/security/mac/mac_inet.c @@ -0,0 +1,425 @@ +/*- + * Copyright (c) 1999-2002, 2007 Robert N. M. Watson + * Copyright (c) 2001 Ilmar S. Habibulin + * Copyright (c) 2001-2004 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * Copyright (c) 2008 Apple Inc. + * All rights reserved. + * + * This software was developed by Robert Watson and Ilmar Habibulin for the + * TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/sbuf.h> +#include <sys/systm.h> +#include <sys/mount.h> +#include <sys/file.h> +#include <sys/namei.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_var.h> + +#include <netinet/in.h> +#include <netinet/in_pcb.h> +#include <netinet/ip_var.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +static struct label * +mac_inpcb_label_alloc(int flag) +{ + struct label *label; + int error; + + label = mac_labelzone_alloc(flag); + if (label == NULL) + return (NULL); + MAC_CHECK(inpcb_init_label, label, flag); + if (error) { + MAC_PERFORM(inpcb_destroy_label, label); + mac_labelzone_free(label); + return (NULL); + } + return (label); +} + +int +mac_inpcb_init(struct inpcb *inp, int flag) +{ + + if (mac_labeled & MPC_OBJECT_INPCB) { + inp->inp_label = mac_inpcb_label_alloc(flag); + if (inp->inp_label == NULL) + return (ENOMEM); + } else + inp->inp_label = NULL; + return (0); +} + +static struct label * +mac_ipq_label_alloc(int flag) +{ + struct label *label; + int error; + + label = mac_labelzone_alloc(flag); + if (label == NULL) + return (NULL); + + MAC_CHECK(ipq_init_label, label, flag); + if (error) { + MAC_PERFORM(ipq_destroy_label, label); + mac_labelzone_free(label); + return (NULL); + } + return (label); +} + +int +mac_ipq_init(struct ipq *q, int flag) +{ + + if (mac_labeled & MPC_OBJECT_IPQ) { + q->ipq_label = mac_ipq_label_alloc(flag); + if (q->ipq_label == NULL) + return (ENOMEM); + } else + q->ipq_label = NULL; + return (0); +} + +static void +mac_inpcb_label_free(struct label *label) +{ + + MAC_PERFORM(inpcb_destroy_label, label); + mac_labelzone_free(label); +} + +void +mac_inpcb_destroy(struct inpcb *inp) +{ + + if (inp->inp_label != NULL) { + mac_inpcb_label_free(inp->inp_label); + inp->inp_label = NULL; + } +} + +static void +mac_ipq_label_free(struct label *label) +{ + + MAC_PERFORM(ipq_destroy_label, label); + mac_labelzone_free(label); +} + +void +mac_ipq_destroy(struct ipq *q) +{ + + if (q->ipq_label != NULL) { + mac_ipq_label_free(q->ipq_label); + q->ipq_label = NULL; + } +} + +void +mac_inpcb_create(struct socket *so, struct inpcb *inp) +{ + + MAC_PERFORM(inpcb_create, so, so->so_label, inp, inp->inp_label); +} + +void +mac_ipq_reassemble(struct ipq *q, struct mbuf *m) +{ + struct label *label; + + label = mac_mbuf_to_label(m); + + MAC_PERFORM(ipq_reassemble, q, q->ipq_label, m, label); +} + +void +mac_netinet_fragment(struct mbuf *m, struct mbuf *frag) +{ + struct label *mlabel, *fraglabel; + + mlabel = mac_mbuf_to_label(m); + fraglabel = mac_mbuf_to_label(frag); + + MAC_PERFORM(netinet_fragment, m, mlabel, frag, fraglabel); +} + +void +mac_ipq_create(struct mbuf *m, struct ipq *q) +{ + struct label *label; + + label = mac_mbuf_to_label(m); + + MAC_PERFORM(ipq_create, m, label, q, q->ipq_label); +} + +void +mac_inpcb_create_mbuf(struct inpcb *inp, struct mbuf *m) +{ + struct label *mlabel; + + INP_LOCK_ASSERT(inp); + mlabel = mac_mbuf_to_label(m); + + MAC_PERFORM(inpcb_create_mbuf, inp, inp->inp_label, m, mlabel); +} + +int +mac_ipq_match(struct mbuf *m, struct ipq *q) +{ + struct label *label; + int result; + + label = mac_mbuf_to_label(m); + + result = 1; + MAC_BOOLEAN(ipq_match, &&, m, label, q, q->ipq_label); + + return (result); +} + +void +mac_netinet_arp_send(struct ifnet *ifp, struct mbuf *m) +{ + struct label *mlabel; + + mlabel = mac_mbuf_to_label(m); + + MAC_IFNET_LOCK(ifp); + MAC_PERFORM(netinet_arp_send, ifp, ifp->if_label, m, mlabel); + MAC_IFNET_UNLOCK(ifp); +} + +void +mac_netinet_icmp_reply(struct mbuf *mrecv, struct mbuf *msend) +{ + struct label *mrecvlabel, *msendlabel; + + mrecvlabel = mac_mbuf_to_label(mrecv); + msendlabel = mac_mbuf_to_label(msend); + + MAC_PERFORM(netinet_icmp_reply, mrecv, mrecvlabel, msend, + msendlabel); +} + +void +mac_netinet_icmp_replyinplace(struct mbuf *m) +{ + struct label *label; + + label = mac_mbuf_to_label(m); + + MAC_PERFORM(netinet_icmp_replyinplace, m, label); +} + +void +mac_netinet_igmp_send(struct ifnet *ifp, struct mbuf *m) +{ + struct label *mlabel; + + mlabel = mac_mbuf_to_label(m); + + MAC_IFNET_LOCK(ifp); + MAC_PERFORM(netinet_igmp_send, ifp, ifp->if_label, m, mlabel); + MAC_IFNET_UNLOCK(ifp); +} + +void +mac_netinet_tcp_reply(struct mbuf *m) +{ + struct label *label; + + label = mac_mbuf_to_label(m); + + MAC_PERFORM(netinet_tcp_reply, m, label); +} + +void +mac_ipq_update(struct mbuf *m, struct ipq *q) +{ + struct label *label; + + label = mac_mbuf_to_label(m); + + MAC_PERFORM(ipq_update, m, label, q, q->ipq_label); +} + +int +mac_inpcb_check_deliver(struct inpcb *inp, struct mbuf *m) +{ + struct label *label; + int error; + + M_ASSERTPKTHDR(m); + + label = mac_mbuf_to_label(m); + + MAC_CHECK(inpcb_check_deliver, inp, inp->inp_label, m, label); + + return (error); +} + +int +mac_inpcb_check_visible(struct ucred *cred, struct inpcb *inp) +{ + int error; + + INP_LOCK_ASSERT(inp); + + MAC_CHECK(inpcb_check_visible, cred, inp, inp->inp_label); + + return (error); +} + +void +mac_inpcb_sosetlabel(struct socket *so, struct inpcb *inp) +{ + + INP_WLOCK_ASSERT(inp); + SOCK_LOCK_ASSERT(so); + MAC_PERFORM(inpcb_sosetlabel, so, so->so_label, inp, inp->inp_label); +} + +void +mac_netinet_firewall_reply(struct mbuf *mrecv, struct mbuf *msend) +{ + struct label *mrecvlabel, *msendlabel; + + M_ASSERTPKTHDR(mrecv); + M_ASSERTPKTHDR(msend); + + mrecvlabel = mac_mbuf_to_label(mrecv); + msendlabel = mac_mbuf_to_label(msend); + + MAC_PERFORM(netinet_firewall_reply, mrecv, mrecvlabel, msend, + msendlabel); +} + +void +mac_netinet_firewall_send(struct mbuf *m) +{ + struct label *label; + + M_ASSERTPKTHDR(m); + label = mac_mbuf_to_label(m); + MAC_PERFORM(netinet_firewall_send, m, label); +} + +/* + * These functions really should be referencing the syncache structure + * instead of the label. However, due to some of the complexities associated + * with exposing this syncache structure we operate directly on it's label + * pointer. This should be OK since we aren't making any access control + * decisions within this code directly, we are merely allocating and copying + * label storage so we can properly initialize mbuf labels for any packets + * the syncache code might create. + */ +void +mac_syncache_destroy(struct label **label) +{ + + if (*label != NULL) { + MAC_PERFORM(syncache_destroy_label, *label); + mac_labelzone_free(*label); + *label = NULL; + } +} + +int +mac_syncache_init(struct label **label) +{ + int error; + + if (mac_labeled & MPC_OBJECT_SYNCACHE) { + *label = mac_labelzone_alloc(M_NOWAIT); + if (*label == NULL) + return (ENOMEM); + /* + * Since we are holding the inpcb locks the policy can not + * allocate policy specific label storage using M_WAITOK. So + * we need to do a MAC_CHECK instead of the typical + * MAC_PERFORM so we can propagate allocation failures back + * to the syncache code. + */ + MAC_CHECK(syncache_init_label, *label, M_NOWAIT); + if (error) { + MAC_PERFORM(syncache_destroy_label, *label); + mac_labelzone_free(*label); + } + return (error); + } else + *label = NULL; + return (0); +} + +void +mac_syncache_create(struct label *label, struct inpcb *inp) +{ + + INP_WLOCK_ASSERT(inp); + MAC_PERFORM(syncache_create, label, inp); +} + +void +mac_syncache_create_mbuf(struct label *sc_label, struct mbuf *m) +{ + struct label *mlabel; + + M_ASSERTPKTHDR(m); + mlabel = mac_mbuf_to_label(m); + MAC_PERFORM(syncache_create_mbuf, sc_label, m, mlabel); +} diff --git a/sys/security/mac/mac_inet6.c b/sys/security/mac/mac_inet6.c new file mode 100644 index 0000000..068455b --- /dev/null +++ b/sys/security/mac/mac_inet6.c @@ -0,0 +1,162 @@ +/*- + * Copyright (c) 2007-2008 Robert N. M. Watson + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/sbuf.h> +#include <sys/systm.h> +#include <sys/mount.h> +#include <sys/file.h> +#include <sys/namei.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_var.h> + +#include <netinet/in.h> +#include <netinet/ip6.h> +#include <netinet6/ip6_var.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +static struct label * +mac_ip6q_label_alloc(int flag) +{ + struct label *label; + int error; + + label = mac_labelzone_alloc(flag); + if (label == NULL) + return (NULL); + + MAC_CHECK(ip6q_init_label, label, flag); + if (error) { + MAC_PERFORM(ip6q_destroy_label, label); + mac_labelzone_free(label); + return (NULL); + } + return (label); +} + +int +mac_ip6q_init(struct ip6q *q6, int flag) +{ + + if (mac_labeled & MPC_OBJECT_IPQ) { + q6->ip6q_label = mac_ip6q_label_alloc(flag); + if (q6->ip6q_label == NULL) + return (ENOMEM); + } else + q6->ip6q_label = NULL; + return (0); +} + +static void +mac_ip6q_label_free(struct label *label) +{ + + MAC_PERFORM(ip6q_destroy_label, label); + mac_labelzone_free(label); +} + +void +mac_ip6q_destroy(struct ip6q *q6) +{ + + if (q6->ip6q_label != NULL) { + mac_ip6q_label_free(q6->ip6q_label); + q6->ip6q_label = NULL; + } +} + +void +mac_ip6q_reassemble(struct ip6q *q6, struct mbuf *m) +{ + struct label *label; + + label = mac_mbuf_to_label(m); + + MAC_PERFORM(ip6q_reassemble, q6, q6->ip6q_label, m, label); +} + +void +mac_ip6q_create(struct mbuf *m, struct ip6q *q6) +{ + struct label *label; + + label = mac_mbuf_to_label(m); + + MAC_PERFORM(ip6q_create, m, label, q6, q6->ip6q_label); +} + +int +mac_ip6q_match(struct mbuf *m, struct ip6q *q6) +{ + struct label *label; + int result; + + label = mac_mbuf_to_label(m); + + result = 1; + MAC_BOOLEAN(ip6q_match, &&, m, label, q6, q6->ip6q_label); + + return (result); +} + +void +mac_ip6q_update(struct mbuf *m, struct ip6q *q6) +{ + struct label *label; + + label = mac_mbuf_to_label(m); + + MAC_PERFORM(ip6q_update, m, label, q6, q6->ip6q_label); +} + +void +mac_netinet6_nd6_send(struct ifnet *ifp, struct mbuf *m) +{ + struct label *mlabel; + + mlabel = mac_mbuf_to_label(m); + + MAC_PERFORM(netinet6_nd6_send, ifp, ifp->if_label, m, mlabel); +} diff --git a/sys/security/mac/mac_internal.h b/sys/security/mac/mac_internal.h new file mode 100644 index 0000000..23da90a --- /dev/null +++ b/sys/security/mac/mac_internal.h @@ -0,0 +1,345 @@ +/*- + * Copyright (c) 1999-2002, 2006 Robert N. M. Watson + * Copyright (c) 2001 Ilmar S. Habibulin + * Copyright (c) 2001-2004 Networks Associates Technology, Inc. + * Copyright (c) 2006 nCircle Network Security, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson and Ilmar Habibulin for the + * TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SECURITY_MAC_MAC_INTERNAL_H_ +#define _SECURITY_MAC_MAC_INTERNAL_H_ + +#ifndef _KERNEL +#error "no user-serviceable parts inside" +#endif + +/* + * MAC Framework sysctl namespace. + */ +#ifdef SYSCTL_DECL +SYSCTL_DECL(_security_mac); +#endif /* SYSCTL_DECL */ + +/* + * MAC Framework global types and typedefs. + */ +LIST_HEAD(mac_policy_list_head, mac_policy_conf); +#ifdef MALLOC_DECLARE +MALLOC_DECLARE(M_MACTEMP); +#endif + +/* + * MAC labels -- in-kernel storage format. + * + * In general, struct label pointers are embedded in kernel data structures + * representing objects that may be labeled (and protected). Struct label is + * opaque to both kernel services that invoke the MAC Framework and MAC + * policy modules. In particular, we do not wish to encode the layout of the + * label structure into any ABIs. Historically, the slot array contained + * unions of {long, void} but now contains uintptr_t. + */ +#define MAC_MAX_SLOTS 4 +#define MAC_FLAG_INITIALIZED 0x0000001 /* Is initialized for use. */ +struct label { + int l_flags; + intptr_t l_perpolicy[MAC_MAX_SLOTS]; +}; + +/* + * MAC Framework global variables. + */ +extern struct mac_policy_list_head mac_policy_list; +extern struct mac_policy_list_head mac_static_policy_list; +extern uint64_t mac_labeled; +extern struct mtx mac_ifnet_mtx; + +/* + * MAC Framework infrastructure functions. + */ +int mac_error_select(int error1, int error2); + +void mac_policy_grab_exclusive(void); +void mac_policy_assert_exclusive(void); +void mac_policy_release_exclusive(void); +void mac_policy_list_busy(void); +int mac_policy_list_conditional_busy(void); +void mac_policy_list_unbusy(void); + +struct label *mac_labelzone_alloc(int flags); +void mac_labelzone_free(struct label *label); +void mac_labelzone_init(void); + +void mac_init_label(struct label *label); +void mac_destroy_label(struct label *label); +int mac_check_structmac_consistent(struct mac *mac); +int mac_allocate_slot(void); + +#define MAC_IFNET_LOCK(ifp) mtx_lock(&mac_ifnet_mtx) +#define MAC_IFNET_UNLOCK(ifp) mtx_unlock(&mac_ifnet_mtx) + +/* + * MAC Framework per-object type functions. It's not yet clear how the + * namespaces, etc, should work for these, so for now, sort by object type. + */ +struct label *mac_cred_label_alloc(void); +void mac_cred_label_free(struct label *label); +struct label *mac_pipe_label_alloc(void); +void mac_pipe_label_free(struct label *label); +struct label *mac_socket_label_alloc(int flag); +void mac_socket_label_free(struct label *label); +struct label *mac_vnode_label_alloc(void); +void mac_vnode_label_free(struct label *label); + +int mac_cred_check_relabel(struct ucred *cred, struct label *newlabel); +int mac_cred_externalize_label(struct label *label, char *elements, + char *outbuf, size_t outbuflen); +int mac_cred_internalize_label(struct label *label, char *string); +void mac_cred_relabel(struct ucred *cred, struct label *newlabel); + +struct label *mac_mbuf_to_label(struct mbuf *m); + +void mac_pipe_copy_label(struct label *src, struct label *dest); +int mac_pipe_externalize_label(struct label *label, char *elements, + char *outbuf, size_t outbuflen); +int mac_pipe_internalize_label(struct label *label, char *string); + +int mac_socket_label_set(struct ucred *cred, struct socket *so, + struct label *label); +void mac_socket_copy_label(struct label *src, struct label *dest); +int mac_socket_externalize_label(struct label *label, char *elements, + char *outbuf, size_t outbuflen); +int mac_socket_internalize_label(struct label *label, char *string); + +int mac_vnode_externalize_label(struct label *label, char *elements, + char *outbuf, size_t outbuflen); +int mac_vnode_internalize_label(struct label *label, char *string); +void mac_vnode_check_mmap_downgrade(struct ucred *cred, struct vnode *vp, + int *prot); +int vn_setlabel(struct vnode *vp, struct label *intlabel, + struct ucred *cred); + +/* + * MAC_CHECK performs the designated check by walking the policy module list + * and checking with each as to how it feels about the request. Note that it + * returns its value via 'error' in the scope of the caller. + */ +#define MAC_CHECK(check, args...) do { \ + struct mac_policy_conf *mpc; \ + int entrycount; \ + \ + error = 0; \ + LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) { \ + if (mpc->mpc_ops->mpo_ ## check != NULL) \ + error = mac_error_select( \ + mpc->mpc_ops->mpo_ ## check (args), \ + error); \ + } \ + if ((entrycount = mac_policy_list_conditional_busy()) != 0) { \ + LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ + if (mpc->mpc_ops->mpo_ ## check != NULL) \ + error = mac_error_select( \ + mpc->mpc_ops->mpo_ ## check (args), \ + error); \ + } \ + mac_policy_list_unbusy(); \ + } \ +} while (0) + +/* + * MAC_GRANT performs the designated check by walking the policy module list + * and checking with each as to how it feels about the request. Unlike + * MAC_CHECK, it grants if any policies return '0', and otherwise returns + * EPERM. Note that it returns its value via 'error' in the scope of the + * caller. + */ +#define MAC_GRANT(check, args...) do { \ + struct mac_policy_conf *mpc; \ + int entrycount; \ + \ + error = EPERM; \ + LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) { \ + if (mpc->mpc_ops->mpo_ ## check != NULL) { \ + if (mpc->mpc_ops->mpo_ ## check(args) == 0) \ + error = 0; \ + } \ + } \ + if ((entrycount = mac_policy_list_conditional_busy()) != 0) { \ + LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ + if (mpc->mpc_ops->mpo_ ## check != NULL) { \ + if (mpc->mpc_ops->mpo_ ## check (args) \ + == 0) \ + error = 0; \ + } \ + } \ + mac_policy_list_unbusy(); \ + } \ +} while (0) + +/* + * MAC_BOOLEAN performs the designated boolean composition by walking the + * module list, invoking each instance of the operation, and combining the + * results using the passed C operator. Note that it returns its value via + * 'result' in the scope of the caller, which should be initialized by the + * caller in a meaningful way to get a meaningful result. + */ +#define MAC_BOOLEAN(operation, composition, args...) do { \ + struct mac_policy_conf *mpc; \ + int entrycount; \ + \ + LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) { \ + if (mpc->mpc_ops->mpo_ ## operation != NULL) \ + result = result composition \ + mpc->mpc_ops->mpo_ ## operation (args); \ + } \ + if ((entrycount = mac_policy_list_conditional_busy()) != 0) { \ + LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ + if (mpc->mpc_ops->mpo_ ## operation != NULL) \ + result = result composition \ + mpc->mpc_ops->mpo_ ## operation \ + (args); \ + } \ + mac_policy_list_unbusy(); \ + } \ +} while (0) + +/* + * MAC_EXTERNALIZE queries each policy to see if it can generate an + * externalized version of a label element by name. Policies declare whether + * they have matched a particular element name, parsed from the string by + * MAC_EXTERNALIZE, and an error is returned if any element is matched by no + * policy. + */ +#define MAC_EXTERNALIZE(type, label, elementlist, outbuf, \ + outbuflen) do { \ + int claimed, first, ignorenotfound, savedlen; \ + char *element_name, *element_temp; \ + struct sbuf sb; \ + \ + error = 0; \ + first = 1; \ + sbuf_new(&sb, outbuf, outbuflen, SBUF_FIXEDLEN); \ + element_temp = elementlist; \ + while ((element_name = strsep(&element_temp, ",")) != NULL) { \ + if (element_name[0] == '?') { \ + element_name++; \ + ignorenotfound = 1; \ + } else \ + ignorenotfound = 0; \ + savedlen = sbuf_len(&sb); \ + if (first) \ + error = sbuf_printf(&sb, "%s/", element_name); \ + else \ + error = sbuf_printf(&sb, ",%s/", element_name); \ + if (error == -1) { \ + error = EINVAL; /* XXX: E2BIG? */ \ + break; \ + } \ + claimed = 0; \ + MAC_CHECK(type ## _externalize_label, label, \ + element_name, &sb, &claimed); \ + if (error) \ + break; \ + if (claimed == 0 && ignorenotfound) { \ + /* Revert last label name. */ \ + sbuf_setpos(&sb, savedlen); \ + } else if (claimed != 1) { \ + error = EINVAL; /* XXX: ENOLABEL? */ \ + break; \ + } else { \ + first = 0; \ + } \ + } \ + sbuf_finish(&sb); \ +} while (0) + +/* + * MAC_INTERNALIZE presents parsed element names and data to each policy to + * see if any is willing to claim it and internalize the label data. If no + * policies match, an error is returned. + */ +#define MAC_INTERNALIZE(type, label, instring) do { \ + char *element, *element_name, *element_data; \ + int claimed; \ + \ + error = 0; \ + element = instring; \ + while ((element_name = strsep(&element, ",")) != NULL) { \ + element_data = element_name; \ + element_name = strsep(&element_data, "/"); \ + if (element_data == NULL) { \ + error = EINVAL; \ + break; \ + } \ + claimed = 0; \ + MAC_CHECK(type ## _internalize_label, label, \ + element_name, element_data, &claimed); \ + if (error) \ + break; \ + if (claimed != 1) { \ + /* XXXMAC: Another error here? */ \ + error = EINVAL; \ + break; \ + } \ + } \ +} while (0) + +/* + * MAC_PERFORM performs the designated operation by walking the policy module + * list and invoking that operation for each policy. + */ +#define MAC_PERFORM(operation, args...) do { \ + struct mac_policy_conf *mpc; \ + int entrycount; \ + \ + LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) { \ + if (mpc->mpc_ops->mpo_ ## operation != NULL) \ + mpc->mpc_ops->mpo_ ## operation (args); \ + } \ + if ((entrycount = mac_policy_list_conditional_busy()) != 0) { \ + LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ + if (mpc->mpc_ops->mpo_ ## operation != NULL) \ + mpc->mpc_ops->mpo_ ## operation (args); \ + } \ + mac_policy_list_unbusy(); \ + } \ +} while (0) + +#endif /* !_SECURITY_MAC_MAC_INTERNAL_H_ */ diff --git a/sys/security/mac/mac_label.c b/sys/security/mac/mac_label.c new file mode 100644 index 0000000..c058653 --- /dev/null +++ b/sys/security/mac/mac_label.c @@ -0,0 +1,151 @@ +/*- + * Copyright (c) 2003-2004 Networks Associates Technology, Inc. + * Copyright (c) 2007 Robert N. M. Watson + * All rights reserved. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/module.h> +#include <sys/sysctl.h> +#include <sys/systm.h> + +#include <vm/uma.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +/* + * zone_label is the UMA zone from which most labels are allocated. Label + * structures are initialized to zero bytes so that policies see a NULL/0 + * slot on first use, even if the policy is loaded after the label is + * allocated for an object. + */ +static uma_zone_t zone_label; + +static int mac_labelzone_ctor(void *mem, int size, void *arg, int flags); +static void mac_labelzone_dtor(void *mem, int size, void *arg); + +void +mac_labelzone_init(void) +{ + + zone_label = uma_zcreate("MAC labels", sizeof(struct label), + mac_labelzone_ctor, mac_labelzone_dtor, NULL, NULL, + UMA_ALIGN_PTR, 0); +} + +/* + * mac_init_label() and mac_destroy_label() are exported so that they can be + * used in mbuf tag initialization, where labels are not slab allocated from + * the zone_label zone. + */ +void +mac_init_label(struct label *label) +{ + + bzero(label, sizeof(*label)); + label->l_flags = MAC_FLAG_INITIALIZED; +} + +void +mac_destroy_label(struct label *label) +{ + + KASSERT(label->l_flags & MAC_FLAG_INITIALIZED, + ("destroying uninitialized label")); + +#ifdef DIAGNOSTIC + bzero(label, sizeof(*label)); +#else + label->l_flags &= ~MAC_FLAG_INITIALIZED; +#endif +} + + +static int +mac_labelzone_ctor(void *mem, int size, void *arg, int flags) +{ + struct label *label; + + KASSERT(size == sizeof(*label), ("mac_labelzone_ctor: wrong size\n")); + label = mem; + mac_init_label(label); + return (0); +} + +static void +mac_labelzone_dtor(void *mem, int size, void *arg) +{ + struct label *label; + + KASSERT(size == sizeof(*label), ("mac_labelzone_dtor: wrong size\n")); + label = mem; + mac_destroy_label(label); +} + +struct label * +mac_labelzone_alloc(int flags) +{ + + return (uma_zalloc(zone_label, flags)); +} + +void +mac_labelzone_free(struct label *label) +{ + + uma_zfree(zone_label, label); +} + +/* + * Functions used by policy modules to get and set label values. + */ +intptr_t +mac_label_get(struct label *l, int slot) +{ + + KASSERT(l != NULL, ("mac_label_get: NULL label")); + + return (l->l_perpolicy[slot]); +} + +void +mac_label_set(struct label *l, int slot, intptr_t v) +{ + + KASSERT(l != NULL, ("mac_label_set: NULL label")); + + l->l_perpolicy[slot] = v; +} diff --git a/sys/security/mac/mac_net.c b/sys/security/mac/mac_net.c new file mode 100644 index 0000000..8e8afea --- /dev/null +++ b/sys/security/mac/mac_net.c @@ -0,0 +1,460 @@ +/*- + * Copyright (c) 1999-2002 Robert N. M. Watson + * Copyright (c) 2001 Ilmar S. Habibulin + * Copyright (c) 2001-2004 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * Copyright (c) 2008 Apple Inc. + * All rights reserved. + * + * This software was developed by Robert Watson and Ilmar Habibulin for the + * TrustedBSD Project. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/mac.h> +#include <sys/priv.h> +#include <sys/sbuf.h> +#include <sys/systm.h> +#include <sys/mount.h> +#include <sys/file.h> +#include <sys/namei.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> + +#include <net/bpfdesc.h> +#include <net/if.h> +#include <net/if_var.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +/* + * XXXRW: struct ifnet locking is incomplete in the network code, so we use + * our own global mutex for struct ifnet. Non-ideal, but should help in the + * SMP environment. + */ +struct mtx mac_ifnet_mtx; +MTX_SYSINIT(mac_ifnet_mtx, &mac_ifnet_mtx, "mac_ifnet", MTX_DEF); + +/* + * Retrieve the label associated with an mbuf by searching for the tag. + * Depending on the value of mac_labelmbufs, it's possible that a label will + * not be present, in which case NULL is returned. Policies must handle the + * possibility of an mbuf not having label storage if they do not enforce + * early loading. + */ +struct label * +mac_mbuf_to_label(struct mbuf *m) +{ + struct m_tag *tag; + struct label *label; + + if (m == NULL) + return (NULL); + tag = m_tag_find(m, PACKET_TAG_MACLABEL, NULL); + if (tag == NULL) + return (NULL); + label = (struct label *)(tag+1); + return (label); +} + +static struct label * +mac_bpfdesc_label_alloc(void) +{ + struct label *label; + + label = mac_labelzone_alloc(M_WAITOK); + MAC_PERFORM(bpfdesc_init_label, label); + return (label); +} + +void +mac_bpfdesc_init(struct bpf_d *d) +{ + + if (mac_labeled & MPC_OBJECT_BPFDESC) + d->bd_label = mac_bpfdesc_label_alloc(); + else + d->bd_label = NULL; +} + +static struct label * +mac_ifnet_label_alloc(void) +{ + struct label *label; + + label = mac_labelzone_alloc(M_WAITOK); + MAC_PERFORM(ifnet_init_label, label); + return (label); +} + +void +mac_ifnet_init(struct ifnet *ifp) +{ + + if (mac_labeled & MPC_OBJECT_IFNET) + ifp->if_label = mac_ifnet_label_alloc(); + else + ifp->if_label = NULL; +} + +int +mac_mbuf_tag_init(struct m_tag *tag, int flag) +{ + struct label *label; + int error; + + label = (struct label *) (tag + 1); + mac_init_label(label); + + MAC_CHECK(mbuf_init_label, label, flag); + if (error) { + MAC_PERFORM(mbuf_destroy_label, label); + mac_destroy_label(label); + } + return (error); +} + +int +mac_mbuf_init(struct mbuf *m, int flag) +{ + struct m_tag *tag; + int error; + + M_ASSERTPKTHDR(m); + + if (mac_labeled & MPC_OBJECT_MBUF) { + tag = m_tag_get(PACKET_TAG_MACLABEL, sizeof(struct label), + flag); + if (tag == NULL) + return (ENOMEM); + error = mac_mbuf_tag_init(tag, flag); + if (error) { + m_tag_free(tag); + return (error); + } + m_tag_prepend(m, tag); + } + return (0); +} + +static void +mac_bpfdesc_label_free(struct label *label) +{ + + MAC_PERFORM(bpfdesc_destroy_label, label); + mac_labelzone_free(label); +} + +void +mac_bpfdesc_destroy(struct bpf_d *d) +{ + + if (d->bd_label != NULL) { + mac_bpfdesc_label_free(d->bd_label); + d->bd_label = NULL; + } +} + +static void +mac_ifnet_label_free(struct label *label) +{ + + MAC_PERFORM(ifnet_destroy_label, label); + mac_labelzone_free(label); +} + +void +mac_ifnet_destroy(struct ifnet *ifp) +{ + + if (ifp->if_label != NULL) { + mac_ifnet_label_free(ifp->if_label); + ifp->if_label = NULL; + } +} + +void +mac_mbuf_tag_destroy(struct m_tag *tag) +{ + struct label *label; + + label = (struct label *)(tag+1); + + MAC_PERFORM(mbuf_destroy_label, label); + mac_destroy_label(label); +} + +/* + * mac_mbuf_tag_copy is called when an mbuf header is duplicated, in which + * case the labels must also be duplicated. + */ +void +mac_mbuf_tag_copy(struct m_tag *src, struct m_tag *dest) +{ + struct label *src_label, *dest_label; + + src_label = (struct label *)(src+1); + dest_label = (struct label *)(dest+1); + + /* + * mac_mbuf_tag_init() is called on the target tag in m_tag_copy(), + * so we don't need to call it here. + */ + MAC_PERFORM(mbuf_copy_label, src_label, dest_label); +} + +void +mac_mbuf_copy(struct mbuf *m_from, struct mbuf *m_to) +{ + struct label *src_label, *dest_label; + + src_label = mac_mbuf_to_label(m_from); + dest_label = mac_mbuf_to_label(m_to); + + MAC_PERFORM(mbuf_copy_label, src_label, dest_label); +} + +static void +mac_ifnet_copy_label(struct label *src, struct label *dest) +{ + + MAC_PERFORM(ifnet_copy_label, src, dest); +} + +static int +mac_ifnet_externalize_label(struct label *label, char *elements, + char *outbuf, size_t outbuflen) +{ + int error; + + MAC_EXTERNALIZE(ifnet, label, elements, outbuf, outbuflen); + + return (error); +} + +static int +mac_ifnet_internalize_label(struct label *label, char *string) +{ + int error; + + MAC_INTERNALIZE(ifnet, label, string); + + return (error); +} + +void +mac_ifnet_create(struct ifnet *ifp) +{ + + MAC_IFNET_LOCK(ifp); + MAC_PERFORM(ifnet_create, ifp, ifp->if_label); + MAC_IFNET_UNLOCK(ifp); +} + +void +mac_bpfdesc_create(struct ucred *cred, struct bpf_d *d) +{ + + MAC_PERFORM(bpfdesc_create, cred, d, d->bd_label); +} + +void +mac_bpfdesc_create_mbuf(struct bpf_d *d, struct mbuf *m) +{ + struct label *label; + + BPFD_LOCK_ASSERT(d); + + label = mac_mbuf_to_label(m); + + MAC_PERFORM(bpfdesc_create_mbuf, d, d->bd_label, m, label); +} + +void +mac_ifnet_create_mbuf(struct ifnet *ifp, struct mbuf *m) +{ + struct label *label; + + label = mac_mbuf_to_label(m); + + MAC_IFNET_LOCK(ifp); + MAC_PERFORM(ifnet_create_mbuf, ifp, ifp->if_label, m, label); + MAC_IFNET_UNLOCK(ifp); +} + +int +mac_bpfdesc_check_receive(struct bpf_d *d, struct ifnet *ifp) +{ + int error; + + BPFD_LOCK_ASSERT(d); + + MAC_IFNET_LOCK(ifp); + MAC_CHECK(bpfdesc_check_receive, d, d->bd_label, ifp, ifp->if_label); + MAC_IFNET_UNLOCK(ifp); + + return (error); +} + +int +mac_ifnet_check_transmit(struct ifnet *ifp, struct mbuf *m) +{ + struct label *label; + int error; + + M_ASSERTPKTHDR(m); + + label = mac_mbuf_to_label(m); + + MAC_IFNET_LOCK(ifp); + MAC_CHECK(ifnet_check_transmit, ifp, ifp->if_label, m, label); + MAC_IFNET_UNLOCK(ifp); + + return (error); +} + +int +mac_ifnet_ioctl_get(struct ucred *cred, struct ifreq *ifr, + struct ifnet *ifp) +{ + char *elements, *buffer; + struct label *intlabel; + struct mac mac; + int error; + + if (!(mac_labeled & MPC_OBJECT_IFNET)) + return (EINVAL); + + error = copyin(ifr->ifr_ifru.ifru_data, &mac, sizeof(mac)); + if (error) + return (error); + + error = mac_check_structmac_consistent(&mac); + if (error) + return (error); + + elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); + error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL); + if (error) { + free(elements, M_MACTEMP); + return (error); + } + + buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); + intlabel = mac_ifnet_label_alloc(); + MAC_IFNET_LOCK(ifp); + mac_ifnet_copy_label(ifp->if_label, intlabel); + MAC_IFNET_UNLOCK(ifp); + error = mac_ifnet_externalize_label(intlabel, elements, buffer, + mac.m_buflen); + mac_ifnet_label_free(intlabel); + if (error == 0) + error = copyout(buffer, mac.m_string, strlen(buffer)+1); + + free(buffer, M_MACTEMP); + free(elements, M_MACTEMP); + + return (error); +} + +int +mac_ifnet_ioctl_set(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifp) +{ + struct label *intlabel; + struct mac mac; + char *buffer; + int error; + + if (!(mac_labeled & MPC_OBJECT_IFNET)) + return (EINVAL); + + error = copyin(ifr->ifr_ifru.ifru_data, &mac, sizeof(mac)); + if (error) + return (error); + + error = mac_check_structmac_consistent(&mac); + if (error) + return (error); + + buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); + error = copyinstr(mac.m_string, buffer, mac.m_buflen, NULL); + if (error) { + free(buffer, M_MACTEMP); + return (error); + } + + intlabel = mac_ifnet_label_alloc(); + error = mac_ifnet_internalize_label(intlabel, buffer); + free(buffer, M_MACTEMP); + if (error) { + mac_ifnet_label_free(intlabel); + return (error); + } + + /* + * XXX: Note that this is a redundant privilege check, since policies + * impose this check themselves if required by the policy + * Eventually, this should go away. + */ + error = priv_check_cred(cred, PRIV_NET_SETIFMAC, 0); + if (error) { + mac_ifnet_label_free(intlabel); + return (error); + } + + MAC_IFNET_LOCK(ifp); + MAC_CHECK(ifnet_check_relabel, cred, ifp, ifp->if_label, intlabel); + if (error) { + MAC_IFNET_UNLOCK(ifp); + mac_ifnet_label_free(intlabel); + return (error); + } + + MAC_PERFORM(ifnet_relabel, cred, ifp, ifp->if_label, intlabel); + MAC_IFNET_UNLOCK(ifp); + + mac_ifnet_label_free(intlabel); + return (0); +} diff --git a/sys/security/mac/mac_pipe.c b/sys/security/mac/mac_pipe.c new file mode 100644 index 0000000..1d8ce04 --- /dev/null +++ b/sys/security/mac/mac_pipe.c @@ -0,0 +1,227 @@ +/*- + * Copyright (c) 2002-2003 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/sbuf.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/pipe.h> +#include <sys/sysctl.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +struct label * +mac_pipe_label_alloc(void) +{ + struct label *label; + + label = mac_labelzone_alloc(M_WAITOK); + MAC_PERFORM(pipe_init_label, label); + return (label); +} + +void +mac_pipe_init(struct pipepair *pp) +{ + + if (mac_labeled & MPC_OBJECT_PIPE) + pp->pp_label = mac_pipe_label_alloc(); + else + pp->pp_label = NULL; +} + +void +mac_pipe_label_free(struct label *label) +{ + + MAC_PERFORM(pipe_destroy_label, label); + mac_labelzone_free(label); +} + +void +mac_pipe_destroy(struct pipepair *pp) +{ + + if (pp->pp_label != NULL) { + mac_pipe_label_free(pp->pp_label); + pp->pp_label = NULL; + } +} + +void +mac_pipe_copy_label(struct label *src, struct label *dest) +{ + + MAC_PERFORM(pipe_copy_label, src, dest); +} + +int +mac_pipe_externalize_label(struct label *label, char *elements, + char *outbuf, size_t outbuflen) +{ + int error; + + MAC_EXTERNALIZE(pipe, label, elements, outbuf, outbuflen); + + return (error); +} + +int +mac_pipe_internalize_label(struct label *label, char *string) +{ + int error; + + MAC_INTERNALIZE(pipe, label, string); + + return (error); +} + +void +mac_pipe_create(struct ucred *cred, struct pipepair *pp) +{ + + MAC_PERFORM(pipe_create, cred, pp, pp->pp_label); +} + +static void +mac_pipe_relabel(struct ucred *cred, struct pipepair *pp, + struct label *newlabel) +{ + + MAC_PERFORM(pipe_relabel, cred, pp, pp->pp_label, newlabel); +} + +int +mac_pipe_check_ioctl(struct ucred *cred, struct pipepair *pp, + unsigned long cmd, void *data) +{ + int error; + + mtx_assert(&pp->pp_mtx, MA_OWNED); + + MAC_CHECK(pipe_check_ioctl, cred, pp, pp->pp_label, cmd, data); + + return (error); +} + +int +mac_pipe_check_poll(struct ucred *cred, struct pipepair *pp) +{ + int error; + + mtx_assert(&pp->pp_mtx, MA_OWNED); + + MAC_CHECK(pipe_check_poll, cred, pp, pp->pp_label); + + return (error); +} + +int +mac_pipe_check_read(struct ucred *cred, struct pipepair *pp) +{ + int error; + + mtx_assert(&pp->pp_mtx, MA_OWNED); + + MAC_CHECK(pipe_check_read, cred, pp, pp->pp_label); + + return (error); +} + +static int +mac_pipe_check_relabel(struct ucred *cred, struct pipepair *pp, + struct label *newlabel) +{ + int error; + + mtx_assert(&pp->pp_mtx, MA_OWNED); + + MAC_CHECK(pipe_check_relabel, cred, pp, pp->pp_label, newlabel); + + return (error); +} + +int +mac_pipe_check_stat(struct ucred *cred, struct pipepair *pp) +{ + int error; + + mtx_assert(&pp->pp_mtx, MA_OWNED); + + MAC_CHECK(pipe_check_stat, cred, pp, pp->pp_label); + + return (error); +} + +int +mac_pipe_check_write(struct ucred *cred, struct pipepair *pp) +{ + int error; + + mtx_assert(&pp->pp_mtx, MA_OWNED); + + MAC_CHECK(pipe_check_write, cred, pp, pp->pp_label); + + return (error); +} + +int +mac_pipe_label_set(struct ucred *cred, struct pipepair *pp, + struct label *label) +{ + int error; + + mtx_assert(&pp->pp_mtx, MA_OWNED); + + error = mac_pipe_check_relabel(cred, pp, label); + if (error) + return (error); + + mac_pipe_relabel(cred, pp, label); + + return (0); +} diff --git a/sys/security/mac/mac_policy.h b/sys/security/mac/mac_policy.h new file mode 100644 index 0000000..ecf68a6 --- /dev/null +++ b/sys/security/mac/mac_policy.h @@ -0,0 +1,1042 @@ +/*- + * Copyright (c) 1999-2002, 2007-2008 Robert N. M. Watson + * Copyright (c) 2001-2005 Networks Associates Technology, Inc. + * Copyright (c) 2005-2006 SPARTA, Inc. + * Copyright (c) 2008 Apple Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +/* + * Kernel interface for MAC policy modules. + */ +#ifndef _SECURITY_MAC_MAC_POLICY_H_ +#define _SECURITY_MAC_MAC_POLICY_H_ + +#ifndef _KERNEL +#error "no user-serviceable parts inside" +#endif + +/*- + * Pluggable access control policy definition structure. + * + * List of operations that are performed as part of the implementation of a + * MAC policy. Policy implementors declare operations with a mac_policy_ops + * structure, and using the MAC_POLICY_SET() macro. If an entry point is not + * declared, then then the policy will be ignored during evaluation of that + * event or check. + * + * Operations are sorted first by general class of operation, then + * alphabetically. + */ +#include <sys/acl.h> /* XXX acl_type_t */ +#include <sys/types.h> /* XXX accmode_t */ + +struct acl; +struct auditinfo; +struct auditinfo_addr; +struct bpf_d; +struct cdev; +struct componentname; +struct devfs_dirent; +struct ifnet; +struct image_params; +struct inpcb; +struct ip6q; +struct ipq; +struct ksem; +struct label; +struct mac_policy_conf; +struct mbuf; +struct mount; +struct msg; +struct msqid_kernel; +struct pipepair; +struct proc; +struct sbuf; +struct semid_kernel; +struct shmfd; +struct shmid_kernel; +struct sockaddr; +struct socket; +struct sysctl_oid; +struct sysctl_req; +struct thread; +struct ucred; +struct uio; +struct vattr; +struct vnode; + +/* + * Policy module operations. + */ +typedef void (*mpo_destroy_t)(struct mac_policy_conf *mpc); +typedef void (*mpo_init_t)(struct mac_policy_conf *mpc); + +/* + * General policy-directed security system call so that policies may + * implement new services without reserving explicit system call numbers. + */ +typedef int (*mpo_syscall_t)(struct thread *td, int call, void *arg); + +/* + * Place-holder function pointers for ABI-compatibility purposes. + */ +typedef void (*mpo_placeholder_t)(void); + +/* + * Operations sorted alphabetically by primary object type and then method. + */ +typedef int (*mpo_bpfdesc_check_receive_t)(struct bpf_d *d, + struct label *dlabel, struct ifnet *ifp, + struct label *ifplabel); +typedef void (*mpo_bpfdesc_create_t)(struct ucred *cred, + struct bpf_d *d, struct label *dlabel); +typedef void (*mpo_bpfdesc_create_mbuf_t)(struct bpf_d *d, + struct label *dlabel, struct mbuf *m, + struct label *mlabel); +typedef void (*mpo_bpfdesc_destroy_label_t)(struct label *label); +typedef void (*mpo_bpfdesc_init_label_t)(struct label *label); + +typedef void (*mpo_cred_associate_nfsd_t)(struct ucred *cred); +typedef int (*mpo_cred_check_relabel_t)(struct ucred *cred, + struct label *newlabel); +typedef int (*mpo_cred_check_visible_t)(struct ucred *cr1, + struct ucred *cr2); +typedef void (*mpo_cred_copy_label_t)(struct label *src, + struct label *dest); +typedef void (*mpo_cred_create_init_t)(struct ucred *cred); +typedef void (*mpo_cred_create_swapper_t)(struct ucred *cred); +typedef void (*mpo_cred_destroy_label_t)(struct label *label); +typedef int (*mpo_cred_externalize_label_t)(struct label *label, + char *element_name, struct sbuf *sb, int *claimed); +typedef void (*mpo_cred_init_label_t)(struct label *label); +typedef int (*mpo_cred_internalize_label_t)(struct label *label, + char *element_name, char *element_data, int *claimed); +typedef void (*mpo_cred_relabel_t)(struct ucred *cred, + struct label *newlabel); + +typedef void (*mpo_devfs_create_device_t)(struct ucred *cred, + struct mount *mp, struct cdev *dev, + struct devfs_dirent *de, struct label *delabel); +typedef void (*mpo_devfs_create_directory_t)(struct mount *mp, + char *dirname, int dirnamelen, struct devfs_dirent *de, + struct label *delabel); +typedef void (*mpo_devfs_create_symlink_t)(struct ucred *cred, + struct mount *mp, struct devfs_dirent *dd, + struct label *ddlabel, struct devfs_dirent *de, + struct label *delabel); +typedef void (*mpo_devfs_destroy_label_t)(struct label *label); +typedef void (*mpo_devfs_init_label_t)(struct label *label); +typedef void (*mpo_devfs_update_t)(struct mount *mp, + struct devfs_dirent *de, struct label *delabel, + struct vnode *vp, struct label *vplabel); +typedef void (*mpo_devfs_vnode_associate_t)(struct mount *mp, + struct label *mplabel, struct devfs_dirent *de, + struct label *delabel, struct vnode *vp, + struct label *vplabel); + +typedef int (*mpo_ifnet_check_relabel_t)(struct ucred *cred, + struct ifnet *ifp, struct label *ifplabel, + struct label *newlabel); +typedef int (*mpo_ifnet_check_transmit_t)(struct ifnet *ifp, + struct label *ifplabel, struct mbuf *m, + struct label *mlabel); +typedef void (*mpo_ifnet_copy_label_t)(struct label *src, + struct label *dest); +typedef void (*mpo_ifnet_create_t)(struct ifnet *ifp, + struct label *ifplabel); +typedef void (*mpo_ifnet_create_mbuf_t)(struct ifnet *ifp, + struct label *ifplabel, struct mbuf *m, + struct label *mlabel); +typedef void (*mpo_ifnet_destroy_label_t)(struct label *label); +typedef int (*mpo_ifnet_externalize_label_t)(struct label *label, + char *element_name, struct sbuf *sb, int *claimed); +typedef void (*mpo_ifnet_init_label_t)(struct label *label); +typedef int (*mpo_ifnet_internalize_label_t)(struct label *label, + char *element_name, char *element_data, int *claimed); +typedef void (*mpo_ifnet_relabel_t)(struct ucred *cred, struct ifnet *ifp, + struct label *ifplabel, struct label *newlabel); + +typedef int (*mpo_inpcb_check_deliver_t)(struct inpcb *inp, + struct label *inplabel, struct mbuf *m, + struct label *mlabel); +typedef int (*mpo_inpcb_check_visible_t)(struct ucred *cred, + struct inpcb *inp, struct label *inplabel); +typedef void (*mpo_inpcb_create_t)(struct socket *so, + struct label *solabel, struct inpcb *inp, + struct label *inplabel); +typedef void (*mpo_inpcb_create_mbuf_t)(struct inpcb *inp, + struct label *inplabel, struct mbuf *m, + struct label *mlabel); +typedef void (*mpo_inpcb_destroy_label_t)(struct label *label); +typedef int (*mpo_inpcb_init_label_t)(struct label *label, int flag); +typedef void (*mpo_inpcb_sosetlabel_t)(struct socket *so, + struct label *label, struct inpcb *inp, + struct label *inplabel); + +typedef void (*mpo_ip6q_create_t)(struct mbuf *m, struct label *mlabel, + struct ip6q *q6, struct label *q6label); +typedef void (*mpo_ip6q_destroy_label_t)(struct label *label); +typedef int (*mpo_ip6q_init_label_t)(struct label *label, int flag); +typedef int (*mpo_ip6q_match_t)(struct mbuf *m, struct label *mlabel, + struct ip6q *q6, struct label *q6label); +typedef void (*mpo_ip6q_reassemble)(struct ip6q *q6, struct label *q6label, + struct mbuf *m, struct label *mlabel); +typedef void (*mpo_ip6q_update_t)(struct mbuf *m, struct label *mlabel, + struct ip6q *q6, struct label *q6label); + +typedef void (*mpo_ipq_create_t)(struct mbuf *m, struct label *mlabel, + struct ipq *q, struct label *qlabel); +typedef void (*mpo_ipq_destroy_label_t)(struct label *label); +typedef int (*mpo_ipq_init_label_t)(struct label *label, int flag); +typedef int (*mpo_ipq_match_t)(struct mbuf *m, struct label *mlabel, + struct ipq *q, struct label *qlabel); +typedef void (*mpo_ipq_reassemble)(struct ipq *q, struct label *qlabel, + struct mbuf *m, struct label *mlabel); +typedef void (*mpo_ipq_update_t)(struct mbuf *m, struct label *mlabel, + struct ipq *q, struct label *qlabel); + +typedef int (*mpo_kenv_check_dump_t)(struct ucred *cred); +typedef int (*mpo_kenv_check_get_t)(struct ucred *cred, char *name); +typedef int (*mpo_kenv_check_set_t)(struct ucred *cred, char *name, + char *value); +typedef int (*mpo_kenv_check_unset_t)(struct ucred *cred, char *name); + +typedef int (*mpo_kld_check_load_t)(struct ucred *cred, struct vnode *vp, + struct label *vplabel); +typedef int (*mpo_kld_check_stat_t)(struct ucred *cred); + +typedef void (*mpo_mbuf_copy_label_t)(struct label *src, + struct label *dest); +typedef void (*mpo_mbuf_destroy_label_t)(struct label *label); +typedef int (*mpo_mbuf_init_label_t)(struct label *label, int flag); + +typedef int (*mpo_mount_check_stat_t)(struct ucred *cred, + struct mount *mp, struct label *mplabel); +typedef void (*mpo_mount_create_t)(struct ucred *cred, struct mount *mp, + struct label *mplabel); +typedef void (*mpo_mount_destroy_label_t)(struct label *label); +typedef void (*mpo_mount_init_label_t)(struct label *label); + +typedef void (*mpo_netatalk_aarp_send_t)(struct ifnet *ifp, + struct label *ifplabel, struct mbuf *m, + struct label *mlabel); + +typedef void (*mpo_netinet_arp_send_t)(struct ifnet *ifp, + struct label *ifplabel, struct mbuf *m, + struct label *mlabel); +typedef void (*mpo_netinet_firewall_reply_t)(struct mbuf *mrecv, + struct label *mrecvlabel, struct mbuf *msend, + struct label *msendlabel); +typedef void (*mpo_netinet_firewall_send_t)(struct mbuf *m, + struct label *mlabel); +typedef void (*mpo_netinet_fragment_t)(struct mbuf *m, + struct label *mlabel, struct mbuf *frag, + struct label *fraglabel); +typedef void (*mpo_netinet_icmp_reply_t)(struct mbuf *mrecv, + struct label *mrecvlabel, struct mbuf *msend, + struct label *msendlabel); +typedef void (*mpo_netinet_icmp_replyinplace_t)(struct mbuf *m, + struct label *mlabel); +typedef void (*mpo_netinet_igmp_send_t)(struct ifnet *ifp, + struct label *ifplabel, struct mbuf *m, + struct label *mlabel); +typedef void (*mpo_netinet_tcp_reply_t)(struct mbuf *m, + struct label *mlabel); + +typedef void (*mpo_netinet6_nd6_send_t)(struct ifnet *ifp, + struct label *ifplabel, struct mbuf *m, + struct label *mlabel); + +typedef int (*mpo_pipe_check_ioctl_t)(struct ucred *cred, + struct pipepair *pp, struct label *pplabel, + unsigned long cmd, void *data); +typedef int (*mpo_pipe_check_poll_t)(struct ucred *cred, + struct pipepair *pp, struct label *pplabel); +typedef int (*mpo_pipe_check_read_t)(struct ucred *cred, + struct pipepair *pp, struct label *pplabel); +typedef int (*mpo_pipe_check_relabel_t)(struct ucred *cred, + struct pipepair *pp, struct label *pplabel, + struct label *newlabel); +typedef int (*mpo_pipe_check_stat_t)(struct ucred *cred, + struct pipepair *pp, struct label *pplabel); +typedef int (*mpo_pipe_check_write_t)(struct ucred *cred, + struct pipepair *pp, struct label *pplabel); +typedef void (*mpo_pipe_copy_label_t)(struct label *src, + struct label *dest); +typedef void (*mpo_pipe_create_t)(struct ucred *cred, struct pipepair *pp, + struct label *pplabel); +typedef void (*mpo_pipe_destroy_label_t)(struct label *label); +typedef int (*mpo_pipe_externalize_label_t)(struct label *label, + char *element_name, struct sbuf *sb, int *claimed); +typedef void (*mpo_pipe_init_label_t)(struct label *label); +typedef int (*mpo_pipe_internalize_label_t)(struct label *label, + char *element_name, char *element_data, int *claimed); +typedef void (*mpo_pipe_relabel_t)(struct ucred *cred, struct pipepair *pp, + struct label *oldlabel, struct label *newlabel); + +typedef int (*mpo_posixsem_check_getvalue_t)(struct ucred *active_cred, + struct ucred *file_cred, struct ksem *ks, + struct label *kslabel); +typedef int (*mpo_posixsem_check_open_t)(struct ucred *cred, + struct ksem *ks, struct label *kslabel); +typedef int (*mpo_posixsem_check_post_t)(struct ucred *active_cred, + struct ucred *file_cred, struct ksem *ks, + struct label *kslabel); +typedef int (*mpo_posixsem_check_stat_t)(struct ucred *active_cred, + struct ucred *file_cred, struct ksem *ks, + struct label *kslabel); +typedef int (*mpo_posixsem_check_unlink_t)(struct ucred *cred, + struct ksem *ks, struct label *kslabel); +typedef int (*mpo_posixsem_check_wait_t)(struct ucred *active_cred, + struct ucred *file_cred, struct ksem *ks, + struct label *kslabel); +typedef void (*mpo_posixsem_create_t)(struct ucred *cred, + struct ksem *ks, struct label *kslabel); +typedef void (*mpo_posixsem_destroy_label_t)(struct label *label); +typedef void (*mpo_posixsem_init_label_t)(struct label *label); + +typedef int (*mpo_posixshm_check_mmap_t)(struct ucred *cred, + struct shmfd *shmfd, struct label *shmlabel, int prot, + int flags); +typedef int (*mpo_posixshm_check_open_t)(struct ucred *cred, + struct shmfd *shmfd, struct label *shmlabel); +typedef int (*mpo_posixshm_check_stat_t)(struct ucred *active_cred, + struct ucred *file_cred, struct shmfd *shmfd, + struct label *shmlabel); +typedef int (*mpo_posixshm_check_truncate_t)(struct ucred *active_cred, + struct ucred *file_cred, struct shmfd *shmfd, + struct label *shmlabel); +typedef int (*mpo_posixshm_check_unlink_t)(struct ucred *cred, + struct shmfd *shmfd, struct label *shmlabel); +typedef void (*mpo_posixshm_create_t)(struct ucred *cred, + struct shmfd *shmfd, struct label *shmlabel); +typedef void (*mpo_posixshm_destroy_label_t)(struct label *label); +typedef void (*mpo_posixshm_init_label_t)(struct label *label); + +typedef int (*mpo_priv_check_t)(struct ucred *cred, int priv); +typedef int (*mpo_priv_grant_t)(struct ucred *cred, int priv); + +typedef int (*mpo_proc_check_debug_t)(struct ucred *cred, + struct proc *p); +typedef int (*mpo_proc_check_sched_t)(struct ucred *cred, + struct proc *p); +typedef int (*mpo_proc_check_setaudit_t)(struct ucred *cred, + struct auditinfo *ai); +typedef int (*mpo_proc_check_setaudit_addr_t)(struct ucred *cred, + struct auditinfo_addr *aia); +typedef int (*mpo_proc_check_setauid_t)(struct ucred *cred, uid_t auid); +typedef int (*mpo_proc_check_setegid_t)(struct ucred *cred, gid_t egid); +typedef int (*mpo_proc_check_seteuid_t)(struct ucred *cred, uid_t euid); +typedef int (*mpo_proc_check_setgid_t)(struct ucred *cred, gid_t gid); +typedef int (*mpo_proc_check_setgroups_t)(struct ucred *cred, int ngroups, + gid_t *gidset); +typedef int (*mpo_proc_check_setregid_t)(struct ucred *cred, gid_t rgid, + gid_t egid); +typedef int (*mpo_proc_check_setresgid_t)(struct ucred *cred, gid_t rgid, + gid_t egid, gid_t sgid); +typedef int (*mpo_proc_check_setresuid_t)(struct ucred *cred, uid_t ruid, + uid_t euid, uid_t suid); +typedef int (*mpo_proc_check_setreuid_t)(struct ucred *cred, uid_t ruid, + uid_t euid); +typedef int (*mpo_proc_check_setuid_t)(struct ucred *cred, uid_t uid); +typedef int (*mpo_proc_check_signal_t)(struct ucred *cred, + struct proc *proc, int signum); +typedef int (*mpo_proc_check_wait_t)(struct ucred *cred, + struct proc *proc); +typedef void (*mpo_proc_destroy_label_t)(struct label *label); +typedef void (*mpo_proc_init_label_t)(struct label *label); + +typedef int (*mpo_socket_check_accept_t)(struct ucred *cred, + struct socket *so, struct label *solabel); +typedef int (*mpo_socket_check_bind_t)(struct ucred *cred, + struct socket *so, struct label *solabel, + struct sockaddr *sa); +typedef int (*mpo_socket_check_connect_t)(struct ucred *cred, + struct socket *so, struct label *solabel, + struct sockaddr *sa); +typedef int (*mpo_socket_check_create_t)(struct ucred *cred, int domain, + int type, int protocol); +typedef int (*mpo_socket_check_deliver_t)(struct socket *so, + struct label *solabel, struct mbuf *m, + struct label *mlabel); +typedef int (*mpo_socket_check_listen_t)(struct ucred *cred, + struct socket *so, struct label *solabel); +typedef int (*mpo_socket_check_poll_t)(struct ucred *cred, + struct socket *so, struct label *solabel); +typedef int (*mpo_socket_check_receive_t)(struct ucred *cred, + struct socket *so, struct label *solabel); +typedef int (*mpo_socket_check_relabel_t)(struct ucred *cred, + struct socket *so, struct label *solabel, + struct label *newlabel); +typedef int (*mpo_socket_check_send_t)(struct ucred *cred, + struct socket *so, struct label *solabel); +typedef int (*mpo_socket_check_stat_t)(struct ucred *cred, + struct socket *so, struct label *solabel); +typedef int (*mpo_socket_check_visible_t)(struct ucred *cred, + struct socket *so, struct label *solabel); +typedef void (*mpo_socket_copy_label_t)(struct label *src, + struct label *dest); +typedef void (*mpo_socket_create_t)(struct ucred *cred, struct socket *so, + struct label *solabel); +typedef void (*mpo_socket_create_mbuf_t)(struct socket *so, + struct label *solabel, struct mbuf *m, + struct label *mlabel); +typedef void (*mpo_socket_destroy_label_t)(struct label *label); +typedef int (*mpo_socket_externalize_label_t)(struct label *label, + char *element_name, struct sbuf *sb, int *claimed); +typedef int (*mpo_socket_init_label_t)(struct label *label, int flag); +typedef int (*mpo_socket_internalize_label_t)(struct label *label, + char *element_name, char *element_data, int *claimed); +typedef void (*mpo_socket_newconn_t)(struct socket *oldso, + struct label *oldsolabel, struct socket *newso, + struct label *newsolabel); +typedef void (*mpo_socket_relabel_t)(struct ucred *cred, struct socket *so, + struct label *oldlabel, struct label *newlabel); + +typedef void (*mpo_socketpeer_destroy_label_t)(struct label *label); +typedef int (*mpo_socketpeer_externalize_label_t)(struct label *label, + char *element_name, struct sbuf *sb, int *claimed); +typedef int (*mpo_socketpeer_init_label_t)(struct label *label, + int flag); +typedef void (*mpo_socketpeer_set_from_mbuf_t)(struct mbuf *m, + struct label *mlabel, struct socket *so, + struct label *sopeerlabel); +typedef void (*mpo_socketpeer_set_from_socket_t)(struct socket *oldso, + struct label *oldsolabel, struct socket *newso, + struct label *newsopeerlabel); + +typedef void (*mpo_syncache_create_t)(struct label *label, + struct inpcb *inp); +typedef void (*mpo_syncache_create_mbuf_t)(struct label *sc_label, + struct mbuf *m, struct label *mlabel); +typedef void (*mpo_syncache_destroy_label_t)(struct label *label); +typedef int (*mpo_syncache_init_label_t)(struct label *label, int flag); + +typedef int (*mpo_system_check_acct_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel); +typedef int (*mpo_system_check_audit_t)(struct ucred *cred, void *record, + int length); +typedef int (*mpo_system_check_auditctl_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel); +typedef int (*mpo_system_check_auditon_t)(struct ucred *cred, int cmd); +typedef int (*mpo_system_check_reboot_t)(struct ucred *cred, int howto); +typedef int (*mpo_system_check_swapon_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel); +typedef int (*mpo_system_check_swapoff_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel); +typedef int (*mpo_system_check_sysctl_t)(struct ucred *cred, + struct sysctl_oid *oidp, void *arg1, int arg2, + struct sysctl_req *req); + +typedef void (*mpo_sysvmsg_cleanup_t)(struct label *msglabel); +typedef void (*mpo_sysvmsg_create_t)(struct ucred *cred, + struct msqid_kernel *msqkptr, struct label *msqlabel, + struct msg *msgptr, struct label *msglabel); +typedef void (*mpo_sysvmsg_destroy_label_t)(struct label *label); +typedef void (*mpo_sysvmsg_init_label_t)(struct label *label); + +typedef int (*mpo_sysvmsq_check_msgmsq_t)(struct ucred *cred, + struct msg *msgptr, struct label *msglabel, + struct msqid_kernel *msqkptr, struct label *msqklabel); +typedef int (*mpo_sysvmsq_check_msgrcv_t)(struct ucred *cred, + struct msg *msgptr, struct label *msglabel); +typedef int (*mpo_sysvmsq_check_msgrmid_t)(struct ucred *cred, + struct msg *msgptr, struct label *msglabel); +typedef int (*mpo_sysvmsq_check_msqget_t)(struct ucred *cred, + struct msqid_kernel *msqkptr, struct label *msqklabel); +typedef int (*mpo_sysvmsq_check_msqctl_t)(struct ucred *cred, + struct msqid_kernel *msqkptr, struct label *msqklabel, + int cmd); +typedef int (*mpo_sysvmsq_check_msqrcv_t)(struct ucred *cred, + struct msqid_kernel *msqkptr, struct label *msqklabel); +typedef int (*mpo_sysvmsq_check_msqsnd_t)(struct ucred *cred, + struct msqid_kernel *msqkptr, struct label *msqklabel); +typedef void (*mpo_sysvmsq_cleanup_t)(struct label *msqlabel); +typedef void (*mpo_sysvmsq_create_t)(struct ucred *cred, + struct msqid_kernel *msqkptr, struct label *msqlabel); +typedef void (*mpo_sysvmsq_destroy_label_t)(struct label *label); +typedef void (*mpo_sysvmsq_init_label_t)(struct label *label); + +typedef int (*mpo_sysvsem_check_semctl_t)(struct ucred *cred, + struct semid_kernel *semakptr, struct label *semaklabel, + int cmd); +typedef int (*mpo_sysvsem_check_semget_t)(struct ucred *cred, + struct semid_kernel *semakptr, struct label *semaklabel); +typedef int (*mpo_sysvsem_check_semop_t)(struct ucred *cred, + struct semid_kernel *semakptr, struct label *semaklabel, + size_t accesstype); +typedef void (*mpo_sysvsem_cleanup_t)(struct label *semalabel); +typedef void (*mpo_sysvsem_create_t)(struct ucred *cred, + struct semid_kernel *semakptr, struct label *semalabel); +typedef void (*mpo_sysvsem_destroy_label_t)(struct label *label); +typedef void (*mpo_sysvsem_init_label_t)(struct label *label); + +typedef int (*mpo_sysvshm_check_shmat_t)(struct ucred *cred, + struct shmid_kernel *shmsegptr, + struct label *shmseglabel, int shmflg); +typedef int (*mpo_sysvshm_check_shmctl_t)(struct ucred *cred, + struct shmid_kernel *shmsegptr, + struct label *shmseglabel, int cmd); +typedef int (*mpo_sysvshm_check_shmdt_t)(struct ucred *cred, + struct shmid_kernel *shmsegptr, + struct label *shmseglabel); +typedef int (*mpo_sysvshm_check_shmget_t)(struct ucred *cred, + struct shmid_kernel *shmsegptr, + struct label *shmseglabel, int shmflg); +typedef void (*mpo_sysvshm_cleanup_t)(struct label *shmlabel); +typedef void (*mpo_sysvshm_create_t)(struct ucred *cred, + struct shmid_kernel *shmsegptr, struct label *shmlabel); +typedef void (*mpo_sysvshm_destroy_label_t)(struct label *label); +typedef void (*mpo_sysvshm_init_label_t)(struct label *label); + +typedef void (*mpo_thread_userret_t)(struct thread *thread); + +typedef int (*mpo_vnode_associate_extattr_t)(struct mount *mp, + struct label *mplabel, struct vnode *vp, + struct label *vplabel); +typedef void (*mpo_vnode_associate_singlelabel_t)(struct mount *mp, + struct label *mplabel, struct vnode *vp, + struct label *vplabel); +typedef int (*mpo_vnode_check_access_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, + accmode_t accmode); +typedef int (*mpo_vnode_check_chdir_t)(struct ucred *cred, + struct vnode *dvp, struct label *dvplabel); +typedef int (*mpo_vnode_check_chroot_t)(struct ucred *cred, + struct vnode *dvp, struct label *dvplabel); +typedef int (*mpo_vnode_check_create_t)(struct ucred *cred, + struct vnode *dvp, struct label *dvplabel, + struct componentname *cnp, struct vattr *vap); +typedef int (*mpo_vnode_check_deleteacl_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, + acl_type_t type); +typedef int (*mpo_vnode_check_deleteextattr_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, + int attrnamespace, const char *name); +typedef int (*mpo_vnode_check_exec_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, + struct image_params *imgp, struct label *execlabel); +typedef int (*mpo_vnode_check_getacl_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, + acl_type_t type); +typedef int (*mpo_vnode_check_getextattr_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, + int attrnamespace, const char *name, struct uio *uio); +typedef int (*mpo_vnode_check_link_t)(struct ucred *cred, + struct vnode *dvp, struct label *dvplabel, + struct vnode *vp, struct label *vplabel, + struct componentname *cnp); +typedef int (*mpo_vnode_check_listextattr_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, + int attrnamespace); +typedef int (*mpo_vnode_check_lookup_t)(struct ucred *cred, + struct vnode *dvp, struct label *dvplabel, + struct componentname *cnp); +typedef int (*mpo_vnode_check_mmap_t)(struct ucred *cred, + struct vnode *vp, struct label *label, int prot, + int flags); +typedef void (*mpo_vnode_check_mmap_downgrade_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, int *prot); +typedef int (*mpo_vnode_check_mprotect_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, int prot); +typedef int (*mpo_vnode_check_open_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, + accmode_t accmode); +typedef int (*mpo_vnode_check_poll_t)(struct ucred *active_cred, + struct ucred *file_cred, struct vnode *vp, + struct label *vplabel); +typedef int (*mpo_vnode_check_read_t)(struct ucred *active_cred, + struct ucred *file_cred, struct vnode *vp, + struct label *vplabel); +typedef int (*mpo_vnode_check_readdir_t)(struct ucred *cred, + struct vnode *dvp, struct label *dvplabel); +typedef int (*mpo_vnode_check_readlink_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel); +typedef int (*mpo_vnode_check_relabel_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, + struct label *newlabel); +typedef int (*mpo_vnode_check_rename_from_t)(struct ucred *cred, + struct vnode *dvp, struct label *dvplabel, + struct vnode *vp, struct label *vplabel, + struct componentname *cnp); +typedef int (*mpo_vnode_check_rename_to_t)(struct ucred *cred, + struct vnode *dvp, struct label *dvplabel, + struct vnode *vp, struct label *vplabel, int samedir, + struct componentname *cnp); +typedef int (*mpo_vnode_check_revoke_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel); +typedef int (*mpo_vnode_check_setacl_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, acl_type_t type, + struct acl *acl); +typedef int (*mpo_vnode_check_setextattr_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, + int attrnamespace, const char *name, struct uio *uio); +typedef int (*mpo_vnode_check_setflags_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, u_long flags); +typedef int (*mpo_vnode_check_setmode_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, mode_t mode); +typedef int (*mpo_vnode_check_setowner_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, uid_t uid, + gid_t gid); +typedef int (*mpo_vnode_check_setutimes_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, + struct timespec atime, struct timespec mtime); +typedef int (*mpo_vnode_check_stat_t)(struct ucred *active_cred, + struct ucred *file_cred, struct vnode *vp, + struct label *vplabel); +typedef int (*mpo_vnode_check_unlink_t)(struct ucred *cred, + struct vnode *dvp, struct label *dvplabel, + struct vnode *vp, struct label *vplabel, + struct componentname *cnp); +typedef int (*mpo_vnode_check_write_t)(struct ucred *active_cred, + struct ucred *file_cred, struct vnode *vp, + struct label *vplabel); +typedef void (*mpo_vnode_copy_label_t)(struct label *src, + struct label *dest); +typedef int (*mpo_vnode_create_extattr_t)(struct ucred *cred, + struct mount *mp, struct label *mplabel, + struct vnode *dvp, struct label *dvplabel, + struct vnode *vp, struct label *vplabel, + struct componentname *cnp); +typedef void (*mpo_vnode_destroy_label_t)(struct label *label); +typedef void (*mpo_vnode_execve_transition_t)(struct ucred *old, + struct ucred *new, struct vnode *vp, + struct label *vplabel, struct label *interpvplabel, + struct image_params *imgp, struct label *execlabel); +typedef int (*mpo_vnode_execve_will_transition_t)(struct ucred *old, + struct vnode *vp, struct label *vplabel, + struct label *interpvplabel, struct image_params *imgp, + struct label *execlabel); +typedef int (*mpo_vnode_externalize_label_t)(struct label *label, + char *element_name, struct sbuf *sb, int *claimed); +typedef void (*mpo_vnode_init_label_t)(struct label *label); +typedef int (*mpo_vnode_internalize_label_t)(struct label *label, + char *element_name, char *element_data, int *claimed); +typedef void (*mpo_vnode_relabel_t)(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct label *label); +typedef int (*mpo_vnode_setlabel_extattr_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, + struct label *intlabel); + +struct mac_policy_ops { + /* + * Policy module operations. + */ + mpo_destroy_t mpo_destroy; + mpo_init_t mpo_init; + + /* + * General policy-directed security system call so that policies may + * implement new services without reserving explicit system call + * numbers. + */ + mpo_syscall_t mpo_syscall; + + /* + * Label operations. Initialize label storage, destroy label + * storage, recycle for re-use without init/destroy, copy a label to + * initialized storage, and externalize/internalize from/to + * initialized storage. + */ + mpo_bpfdesc_check_receive_t mpo_bpfdesc_check_receive; + mpo_bpfdesc_create_t mpo_bpfdesc_create; + mpo_bpfdesc_create_mbuf_t mpo_bpfdesc_create_mbuf; + mpo_bpfdesc_destroy_label_t mpo_bpfdesc_destroy_label; + mpo_bpfdesc_init_label_t mpo_bpfdesc_init_label; + + mpo_cred_associate_nfsd_t mpo_cred_associate_nfsd; + mpo_cred_check_relabel_t mpo_cred_check_relabel; + mpo_cred_check_visible_t mpo_cred_check_visible; + mpo_cred_copy_label_t mpo_cred_copy_label; + mpo_cred_create_swapper_t mpo_cred_create_swapper; + mpo_cred_create_init_t mpo_cred_create_init; + mpo_cred_destroy_label_t mpo_cred_destroy_label; + mpo_cred_externalize_label_t mpo_cred_externalize_label; + mpo_cred_init_label_t mpo_cred_init_label; + mpo_cred_internalize_label_t mpo_cred_internalize_label; + mpo_cred_relabel_t mpo_cred_relabel; + + mpo_devfs_create_device_t mpo_devfs_create_device; + mpo_devfs_create_directory_t mpo_devfs_create_directory; + mpo_devfs_create_symlink_t mpo_devfs_create_symlink; + mpo_devfs_destroy_label_t mpo_devfs_destroy_label; + mpo_devfs_init_label_t mpo_devfs_init_label; + mpo_devfs_update_t mpo_devfs_update; + mpo_devfs_vnode_associate_t mpo_devfs_vnode_associate; + + mpo_ifnet_check_relabel_t mpo_ifnet_check_relabel; + mpo_ifnet_check_transmit_t mpo_ifnet_check_transmit; + mpo_ifnet_copy_label_t mpo_ifnet_copy_label; + mpo_ifnet_create_t mpo_ifnet_create; + mpo_ifnet_create_mbuf_t mpo_ifnet_create_mbuf; + mpo_ifnet_destroy_label_t mpo_ifnet_destroy_label; + mpo_ifnet_externalize_label_t mpo_ifnet_externalize_label; + mpo_ifnet_init_label_t mpo_ifnet_init_label; + mpo_ifnet_internalize_label_t mpo_ifnet_internalize_label; + mpo_ifnet_relabel_t mpo_ifnet_relabel; + + mpo_inpcb_check_deliver_t mpo_inpcb_check_deliver; + mpo_inpcb_check_visible_t mpo_inpcb_check_visible; + mpo_inpcb_create_t mpo_inpcb_create; + mpo_inpcb_create_mbuf_t mpo_inpcb_create_mbuf; + mpo_inpcb_destroy_label_t mpo_inpcb_destroy_label; + mpo_inpcb_init_label_t mpo_inpcb_init_label; + mpo_inpcb_sosetlabel_t mpo_inpcb_sosetlabel; + + mpo_ip6q_create_t mpo_ip6q_create; + mpo_ip6q_destroy_label_t mpo_ip6q_destroy_label; + mpo_ip6q_init_label_t mpo_ip6q_init_label; + mpo_ip6q_match_t mpo_ip6q_match; + mpo_ip6q_reassemble mpo_ip6q_reassemble; + mpo_ip6q_update_t mpo_ip6q_update; + + mpo_ipq_create_t mpo_ipq_create; + mpo_ipq_destroy_label_t mpo_ipq_destroy_label; + mpo_ipq_init_label_t mpo_ipq_init_label; + mpo_ipq_match_t mpo_ipq_match; + mpo_ipq_reassemble mpo_ipq_reassemble; + mpo_ipq_update_t mpo_ipq_update; + + mpo_kenv_check_dump_t mpo_kenv_check_dump; + mpo_kenv_check_get_t mpo_kenv_check_get; + mpo_kenv_check_set_t mpo_kenv_check_set; + mpo_kenv_check_unset_t mpo_kenv_check_unset; + + mpo_kld_check_load_t mpo_kld_check_load; + mpo_kld_check_stat_t mpo_kld_check_stat; + + mpo_mbuf_copy_label_t mpo_mbuf_copy_label; + mpo_mbuf_destroy_label_t mpo_mbuf_destroy_label; + mpo_mbuf_init_label_t mpo_mbuf_init_label; + + mpo_mount_check_stat_t mpo_mount_check_stat; + mpo_mount_create_t mpo_mount_create; + mpo_mount_destroy_label_t mpo_mount_destroy_label; + mpo_mount_init_label_t mpo_mount_init_label; + + mpo_netatalk_aarp_send_t mpo_netatalk_aarp_send; + + mpo_netinet_arp_send_t mpo_netinet_arp_send; + mpo_netinet_firewall_reply_t mpo_netinet_firewall_reply; + mpo_netinet_firewall_send_t mpo_netinet_firewall_send; + mpo_netinet_fragment_t mpo_netinet_fragment; + mpo_netinet_icmp_reply_t mpo_netinet_icmp_reply; + mpo_netinet_icmp_replyinplace_t mpo_netinet_icmp_replyinplace; + mpo_netinet_igmp_send_t mpo_netinet_igmp_send; + mpo_netinet_tcp_reply_t mpo_netinet_tcp_reply; + + mpo_netinet6_nd6_send_t mpo_netinet6_nd6_send; + + mpo_pipe_check_ioctl_t mpo_pipe_check_ioctl; + mpo_pipe_check_poll_t mpo_pipe_check_poll; + mpo_pipe_check_read_t mpo_pipe_check_read; + mpo_pipe_check_relabel_t mpo_pipe_check_relabel; + mpo_pipe_check_stat_t mpo_pipe_check_stat; + mpo_pipe_check_write_t mpo_pipe_check_write; + mpo_pipe_copy_label_t mpo_pipe_copy_label; + mpo_pipe_create_t mpo_pipe_create; + mpo_pipe_destroy_label_t mpo_pipe_destroy_label; + mpo_pipe_externalize_label_t mpo_pipe_externalize_label; + mpo_pipe_init_label_t mpo_pipe_init_label; + mpo_pipe_internalize_label_t mpo_pipe_internalize_label; + mpo_pipe_relabel_t mpo_pipe_relabel; + + mpo_posixsem_check_getvalue_t mpo_posixsem_check_getvalue; + mpo_posixsem_check_open_t mpo_posixsem_check_open; + mpo_posixsem_check_post_t mpo_posixsem_check_post; + mpo_posixsem_check_stat_t mpo_posixsem_check_stat; + mpo_posixsem_check_unlink_t mpo_posixsem_check_unlink; + mpo_posixsem_check_wait_t mpo_posixsem_check_wait; + mpo_posixsem_create_t mpo_posixsem_create; + mpo_posixsem_destroy_label_t mpo_posixsem_destroy_label; + mpo_posixsem_init_label_t mpo_posixsem_init_label; + + mpo_posixshm_check_mmap_t mpo_posixshm_check_mmap; + mpo_posixshm_check_open_t mpo_posixshm_check_open; + mpo_posixshm_check_stat_t mpo_posixshm_check_stat; + mpo_posixshm_check_truncate_t mpo_posixshm_check_truncate; + mpo_posixshm_check_unlink_t mpo_posixshm_check_unlink; + mpo_posixshm_create_t mpo_posixshm_create; + mpo_posixshm_destroy_label_t mpo_posixshm_destroy_label; + mpo_posixshm_init_label_t mpo_posixshm_init_label; + + mpo_priv_check_t mpo_priv_check; + mpo_priv_grant_t mpo_priv_grant; + + mpo_proc_check_debug_t mpo_proc_check_debug; + mpo_proc_check_sched_t mpo_proc_check_sched; + mpo_proc_check_setaudit_t mpo_proc_check_setaudit; + mpo_proc_check_setaudit_addr_t mpo_proc_check_setaudit_addr; + mpo_proc_check_setauid_t mpo_proc_check_setauid; + mpo_proc_check_setuid_t mpo_proc_check_setuid; + mpo_proc_check_seteuid_t mpo_proc_check_seteuid; + mpo_proc_check_setgid_t mpo_proc_check_setgid; + mpo_proc_check_setegid_t mpo_proc_check_setegid; + mpo_proc_check_setgroups_t mpo_proc_check_setgroups; + mpo_proc_check_setreuid_t mpo_proc_check_setreuid; + mpo_proc_check_setregid_t mpo_proc_check_setregid; + mpo_proc_check_setresuid_t mpo_proc_check_setresuid; + mpo_proc_check_setresgid_t mpo_proc_check_setresgid; + mpo_proc_check_signal_t mpo_proc_check_signal; + mpo_proc_check_wait_t mpo_proc_check_wait; + mpo_proc_destroy_label_t mpo_proc_destroy_label; + mpo_proc_init_label_t mpo_proc_init_label; + + mpo_socket_check_accept_t mpo_socket_check_accept; + mpo_socket_check_bind_t mpo_socket_check_bind; + mpo_socket_check_connect_t mpo_socket_check_connect; + mpo_socket_check_create_t mpo_socket_check_create; + mpo_socket_check_deliver_t mpo_socket_check_deliver; + mpo_socket_check_listen_t mpo_socket_check_listen; + mpo_socket_check_poll_t mpo_socket_check_poll; + mpo_socket_check_receive_t mpo_socket_check_receive; + mpo_socket_check_relabel_t mpo_socket_check_relabel; + mpo_socket_check_send_t mpo_socket_check_send; + mpo_socket_check_stat_t mpo_socket_check_stat; + mpo_socket_check_visible_t mpo_socket_check_visible; + mpo_socket_copy_label_t mpo_socket_copy_label; + mpo_socket_create_t mpo_socket_create; + mpo_socket_create_mbuf_t mpo_socket_create_mbuf; + mpo_socket_destroy_label_t mpo_socket_destroy_label; + mpo_socket_externalize_label_t mpo_socket_externalize_label; + mpo_socket_init_label_t mpo_socket_init_label; + mpo_socket_internalize_label_t mpo_socket_internalize_label; + mpo_socket_newconn_t mpo_socket_newconn; + mpo_socket_relabel_t mpo_socket_relabel; + + mpo_socketpeer_destroy_label_t mpo_socketpeer_destroy_label; + mpo_socketpeer_externalize_label_t mpo_socketpeer_externalize_label; + mpo_socketpeer_init_label_t mpo_socketpeer_init_label; + mpo_socketpeer_set_from_mbuf_t mpo_socketpeer_set_from_mbuf; + mpo_socketpeer_set_from_socket_t mpo_socketpeer_set_from_socket; + + mpo_syncache_init_label_t mpo_syncache_init_label; + mpo_syncache_destroy_label_t mpo_syncache_destroy_label; + mpo_syncache_create_t mpo_syncache_create; + mpo_syncache_create_mbuf_t mpo_syncache_create_mbuf; + + mpo_system_check_acct_t mpo_system_check_acct; + mpo_system_check_audit_t mpo_system_check_audit; + mpo_system_check_auditctl_t mpo_system_check_auditctl; + mpo_system_check_auditon_t mpo_system_check_auditon; + mpo_system_check_reboot_t mpo_system_check_reboot; + mpo_system_check_swapon_t mpo_system_check_swapon; + mpo_system_check_swapoff_t mpo_system_check_swapoff; + mpo_system_check_sysctl_t mpo_system_check_sysctl; + + mpo_sysvmsg_cleanup_t mpo_sysvmsg_cleanup; + mpo_sysvmsg_create_t mpo_sysvmsg_create; + mpo_sysvmsg_destroy_label_t mpo_sysvmsg_destroy_label; + mpo_sysvmsg_init_label_t mpo_sysvmsg_init_label; + + mpo_sysvmsq_check_msgmsq_t mpo_sysvmsq_check_msgmsq; + mpo_sysvmsq_check_msgrcv_t mpo_sysvmsq_check_msgrcv; + mpo_sysvmsq_check_msgrmid_t mpo_sysvmsq_check_msgrmid; + mpo_sysvmsq_check_msqctl_t mpo_sysvmsq_check_msqctl; + mpo_sysvmsq_check_msqget_t mpo_sysvmsq_check_msqget; + mpo_sysvmsq_check_msqrcv_t mpo_sysvmsq_check_msqrcv; + mpo_sysvmsq_check_msqsnd_t mpo_sysvmsq_check_msqsnd; + mpo_sysvmsq_cleanup_t mpo_sysvmsq_cleanup; + mpo_sysvmsq_create_t mpo_sysvmsq_create; + mpo_sysvmsq_destroy_label_t mpo_sysvmsq_destroy_label; + mpo_sysvmsq_init_label_t mpo_sysvmsq_init_label; + + mpo_sysvsem_check_semctl_t mpo_sysvsem_check_semctl; + mpo_sysvsem_check_semget_t mpo_sysvsem_check_semget; + mpo_sysvsem_check_semop_t mpo_sysvsem_check_semop; + mpo_sysvsem_cleanup_t mpo_sysvsem_cleanup; + mpo_sysvsem_create_t mpo_sysvsem_create; + mpo_sysvsem_destroy_label_t mpo_sysvsem_destroy_label; + mpo_sysvsem_init_label_t mpo_sysvsem_init_label; + + mpo_sysvshm_check_shmat_t mpo_sysvshm_check_shmat; + mpo_sysvshm_check_shmctl_t mpo_sysvshm_check_shmctl; + mpo_sysvshm_check_shmdt_t mpo_sysvshm_check_shmdt; + mpo_sysvshm_check_shmget_t mpo_sysvshm_check_shmget; + mpo_sysvshm_cleanup_t mpo_sysvshm_cleanup; + mpo_sysvshm_create_t mpo_sysvshm_create; + mpo_sysvshm_destroy_label_t mpo_sysvshm_destroy_label; + mpo_sysvshm_init_label_t mpo_sysvshm_init_label; + + mpo_thread_userret_t mpo_thread_userret; + + mpo_vnode_check_access_t mpo_vnode_check_access; + mpo_vnode_check_chdir_t mpo_vnode_check_chdir; + mpo_vnode_check_chroot_t mpo_vnode_check_chroot; + mpo_vnode_check_create_t mpo_vnode_check_create; + mpo_vnode_check_deleteacl_t mpo_vnode_check_deleteacl; + mpo_vnode_check_deleteextattr_t mpo_vnode_check_deleteextattr; + mpo_vnode_check_exec_t mpo_vnode_check_exec; + mpo_vnode_check_getacl_t mpo_vnode_check_getacl; + mpo_vnode_check_getextattr_t mpo_vnode_check_getextattr; + mpo_vnode_check_link_t mpo_vnode_check_link; + mpo_vnode_check_listextattr_t mpo_vnode_check_listextattr; + mpo_vnode_check_lookup_t mpo_vnode_check_lookup; + mpo_vnode_check_mmap_t mpo_vnode_check_mmap; + mpo_vnode_check_mmap_downgrade_t mpo_vnode_check_mmap_downgrade; + mpo_vnode_check_mprotect_t mpo_vnode_check_mprotect; + mpo_vnode_check_open_t mpo_vnode_check_open; + mpo_vnode_check_poll_t mpo_vnode_check_poll; + mpo_vnode_check_read_t mpo_vnode_check_read; + mpo_vnode_check_readdir_t mpo_vnode_check_readdir; + mpo_vnode_check_readlink_t mpo_vnode_check_readlink; + mpo_vnode_check_relabel_t mpo_vnode_check_relabel; + mpo_vnode_check_rename_from_t mpo_vnode_check_rename_from; + mpo_vnode_check_rename_to_t mpo_vnode_check_rename_to; + mpo_vnode_check_revoke_t mpo_vnode_check_revoke; + mpo_vnode_check_setacl_t mpo_vnode_check_setacl; + mpo_vnode_check_setextattr_t mpo_vnode_check_setextattr; + mpo_vnode_check_setflags_t mpo_vnode_check_setflags; + mpo_vnode_check_setmode_t mpo_vnode_check_setmode; + mpo_vnode_check_setowner_t mpo_vnode_check_setowner; + mpo_vnode_check_setutimes_t mpo_vnode_check_setutimes; + mpo_vnode_check_stat_t mpo_vnode_check_stat; + mpo_vnode_check_unlink_t mpo_vnode_check_unlink; + mpo_vnode_check_write_t mpo_vnode_check_write; + mpo_vnode_associate_extattr_t mpo_vnode_associate_extattr; + mpo_vnode_associate_singlelabel_t mpo_vnode_associate_singlelabel; + mpo_vnode_destroy_label_t mpo_vnode_destroy_label; + mpo_vnode_copy_label_t mpo_vnode_copy_label; + mpo_vnode_create_extattr_t mpo_vnode_create_extattr; + mpo_vnode_execve_transition_t mpo_vnode_execve_transition; + mpo_vnode_execve_will_transition_t mpo_vnode_execve_will_transition; + mpo_vnode_externalize_label_t mpo_vnode_externalize_label; + mpo_vnode_init_label_t mpo_vnode_init_label; + mpo_vnode_internalize_label_t mpo_vnode_internalize_label; + mpo_vnode_relabel_t mpo_vnode_relabel; + mpo_vnode_setlabel_extattr_t mpo_vnode_setlabel_extattr; +}; + +/* + * struct mac_policy_conf is the registration structure for policies, and is + * provided to the MAC Framework using MAC_POLICY_SET() to invoke a SYSINIT + * to register the policy. In general, the fields are immutable, with the + * exception of the "security field", run-time flags, and policy list entry, + * which are managed by the MAC Framework. Be careful when modifying this + * structure, as its layout is statically compiled into all policies. + */ +struct mac_policy_conf { + char *mpc_name; /* policy name */ + char *mpc_fullname; /* policy full name */ + struct mac_policy_ops *mpc_ops; /* policy operations */ + int mpc_loadtime_flags; /* flags */ + int *mpc_field_off; /* security field */ + int mpc_runtime_flags; /* flags */ + int _mpc_spare1; /* Spare. */ + uint64_t mpc_labeled; /* Labeled objects. */ + uint64_t _mpc_spare2; /* Spare. */ + void *_mpc_spare3; /* Spare. */ + LIST_ENTRY(mac_policy_conf) mpc_list; /* global list */ +}; + +/* Flags for the mpc_loadtime_flags field. */ +#define MPC_LOADTIME_FLAG_NOTLATE 0x00000001 +#define MPC_LOADTIME_FLAG_UNLOADOK 0x00000002 + +/* Flags for the mpc_runtime_flags field. */ +#define MPC_RUNTIME_FLAG_REGISTERED 0x00000001 + +/* + * Flags for mpc_labeled declaring which objects should have labels allocated + * for them by the MAC Framework. + */ +#define MPC_OBJECT_CRED 0x0000000000000001 +#define MPC_OBJECT_PROC 0x0000000000000002 +#define MPC_OBJECT_VNODE 0x0000000000000004 +#define MPC_OBJECT_INPCB 0x0000000000000008 +#define MPC_OBJECT_SOCKET 0x0000000000000010 +#define MPC_OBJECT_DEVFS 0x0000000000000020 +#define MPC_OBJECT_MBUF 0x0000000000000040 +#define MPC_OBJECT_IPQ 0x0000000000000080 +#define MPC_OBJECT_IFNET 0x0000000000000100 +#define MPC_OBJECT_BPFDESC 0x0000000000000200 +#define MPC_OBJECT_PIPE 0x0000000000000400 +#define MPC_OBJECT_MOUNT 0x0000000000000800 +#define MPC_OBJECT_POSIXSEM 0x0000000000001000 +#define MPC_OBJECT_POSIXSHM 0x0000000000002000 +#define MPC_OBJECT_SYSVMSG 0x0000000000004000 +#define MPC_OBJECT_SYSVMSQ 0x0000000000008000 +#define MPC_OBJECT_SYSVSEM 0x0000000000010000 +#define MPC_OBJECT_SYSVSHM 0x0000000000020000 +#define MPC_OBJECT_SYNCACHE 0x0000000000040000 +#define MPC_OBJECT_IP6Q 0x0000000000080000 + +/*- + * The TrustedBSD MAC Framework has a major version number, MAC_VERSION, + * which defines the ABI of the Framework present in the kernel (and depended + * on by policy modules compiled against that kernel). Currently, + * MAC_POLICY_SET() requires that the kernel and module ABI version numbers + * exactly match. The following major versions have been defined to date: + * + * MAC version FreeBSD versions + * 1 5.x + * 2 6.x + * 3 7.x + * 4 8.x + */ +#define MAC_VERSION 4 + +#define MAC_POLICY_SET(mpops, mpname, mpfullname, mpflags, privdata_wanted, \ + labeled) \ + static struct mac_policy_conf mpname##_mac_policy_conf = { \ + .mpc_name = #mpname, \ + .mpc_fullname = mpfullname, \ + .mpc_ops = mpops, \ + .mpc_loadtime_flags = mpflags, \ + .mpc_field_off = privdata_wanted, \ + .mpc_labeled = labeled, \ + }; \ + static moduledata_t mpname##_mod = { \ + #mpname, \ + mac_policy_modevent, \ + &mpname##_mac_policy_conf \ + }; \ + MODULE_DEPEND(mpname, kernel_mac_support, MAC_VERSION, \ + MAC_VERSION, MAC_VERSION); \ + DECLARE_MODULE(mpname, mpname##_mod, SI_SUB_MAC_POLICY, \ + SI_ORDER_MIDDLE) + +int mac_policy_modevent(module_t mod, int type, void *data); + +/* + * Policy interface to map a struct label pointer to per-policy data. + * Typically, policies wrap this in their own accessor macro that casts a + * uintptr_t to a policy-specific data type. + */ +intptr_t mac_label_get(struct label *l, int slot); +void mac_label_set(struct label *l, int slot, intptr_t v); + +#endif /* !_SECURITY_MAC_MAC_POLICY_H_ */ diff --git a/sys/security/mac/mac_posix_sem.c b/sys/security/mac/mac_posix_sem.c new file mode 100644 index 0000000..1cda22c --- /dev/null +++ b/sys/security/mac/mac_posix_sem.c @@ -0,0 +1,164 @@ +/*- + * Copyright (c) 2003-2006 SPARTA, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mac.h" +#include "opt_posix.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/ksem.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/systm.h> +#include <sys/sysctl.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +static struct label * +mac_posixsem_label_alloc(void) +{ + struct label *label; + + label = mac_labelzone_alloc(M_WAITOK); + MAC_PERFORM(posixsem_init_label, label); + return (label); +} + +void +mac_posixsem_init(struct ksem *ks) +{ + + if (mac_labeled & MPC_OBJECT_POSIXSEM) + ks->ks_label = mac_posixsem_label_alloc(); + else + ks->ks_label = NULL; +} + +static void +mac_posixsem_label_free(struct label *label) +{ + + MAC_PERFORM(posixsem_destroy_label, label); + mac_labelzone_free(label); +} + +void +mac_posixsem_destroy(struct ksem *ks) +{ + + if (ks->ks_label != NULL) { + mac_posixsem_label_free(ks->ks_label); + ks->ks_label = NULL; + } +} + +void +mac_posixsem_create(struct ucred *cred, struct ksem *ks) +{ + + MAC_PERFORM(posixsem_create, cred, ks, ks->ks_label); +} + +int +mac_posixsem_check_open(struct ucred *cred, struct ksem *ks) +{ + int error; + + MAC_CHECK(posixsem_check_open, cred, ks, ks->ks_label); + + return (error); +} + +int +mac_posixsem_check_getvalue(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks) +{ + int error; + + MAC_CHECK(posixsem_check_getvalue, active_cred, file_cred, ks, + ks->ks_label); + + return (error); +} + +int +mac_posixsem_check_post(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks) +{ + int error; + + MAC_CHECK(posixsem_check_post, active_cred, file_cred, ks, + ks->ks_label); + + return (error); +} + +int +mac_posixsem_check_stat(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks) +{ + int error; + + MAC_CHECK(posixsem_check_stat, active_cred, file_cred, ks, + ks->ks_label); + + return (error); +} + +int +mac_posixsem_check_unlink(struct ucred *cred, struct ksem *ks) +{ + int error; + + MAC_CHECK(posixsem_check_unlink, cred, ks, ks->ks_label); + + return (error); +} + +int +mac_posixsem_check_wait(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks) +{ + int error; + + MAC_CHECK(posixsem_check_wait, active_cred, file_cred, ks, + ks->ks_label); + + return (error); +} diff --git a/sys/security/mac/mac_posix_shm.c b/sys/security/mac/mac_posix_shm.c new file mode 100644 index 0000000..97587ad --- /dev/null +++ b/sys/security/mac/mac_posix_shm.c @@ -0,0 +1,151 @@ +/*- + * Copyright (c) 2003-2006 SPARTA, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/mman.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/systm.h> +#include <sys/sysctl.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +static struct label * +mac_posixshm_label_alloc(void) +{ + struct label *label; + + label = mac_labelzone_alloc(M_WAITOK); + MAC_PERFORM(posixshm_init_label, label); + return (label); +} + +void +mac_posixshm_init(struct shmfd *shmfd) +{ + + if (mac_labeled & MPC_OBJECT_POSIXSHM) + shmfd->shm_label = mac_posixshm_label_alloc(); + else + shmfd->shm_label = NULL; +} + +static void +mac_posixshm_label_free(struct label *label) +{ + + MAC_PERFORM(posixshm_destroy_label, label); + mac_labelzone_free(label); +} + +void +mac_posixshm_destroy(struct shmfd *shmfd) +{ + + if (shmfd->shm_label != NULL) { + mac_posixshm_label_free(shmfd->shm_label); + shmfd->shm_label = NULL; + } +} + +void +mac_posixshm_create(struct ucred *cred, struct shmfd *shmfd) +{ + + MAC_PERFORM(posixshm_create, cred, shmfd, shmfd->shm_label); +} + +int +mac_posixshm_check_mmap(struct ucred *cred, struct shmfd *shmfd, int prot, + int flags) +{ + int error; + + MAC_CHECK(posixshm_check_mmap, cred, shmfd, shmfd->shm_label, prot, + flags); + + return (error); +} + +int +mac_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd) +{ + int error; + + MAC_CHECK(posixshm_check_open, cred, shmfd, shmfd->shm_label); + + return (error); +} + +int +mac_posixshm_check_stat(struct ucred *active_cred, struct ucred *file_cred, + struct shmfd *shmfd) +{ + int error; + + MAC_CHECK(posixshm_check_stat, active_cred, file_cred, shmfd, + shmfd->shm_label); + + return (error); +} + +int +mac_posixshm_check_truncate(struct ucred *active_cred, struct ucred *file_cred, + struct shmfd *shmfd) +{ + int error; + + MAC_CHECK(posixshm_check_truncate, active_cred, file_cred, shmfd, + shmfd->shm_label); + + return (error); +} + +int +mac_posixshm_check_unlink(struct ucred *cred, struct shmfd *shmfd) +{ + int error; + + MAC_CHECK(posixshm_check_unlink, cred, shmfd, shmfd->shm_label); + + return (error); +} diff --git a/sys/security/mac/mac_priv.c b/sys/security/mac/mac_priv.c new file mode 100644 index 0000000..45a8c81 --- /dev/null +++ b/sys/security/mac/mac_priv.c @@ -0,0 +1,82 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR, NCIRCLE NETWORK SECURITY, + * INC., OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * MAC checks for system privileges. + */ + +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/priv.h> +#include <sys/module.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +/* + * The MAC Framework interacts with kernel privilege checks in two ways: it + * may restrict the granting of privilege to a subject, and it may grant + * additional privileges to the subject. Policies may implement none, one, + * or both of these entry points. Restriction of privilege by any policy + * always overrides granting of privilege by any policy or other privilege + * mechanism. See kern_priv.c:priv_check_cred() for details of the + * composition. + */ + +/* + * Restrict access to a privilege for a credential. Return failure if any + * policy denies access. + */ +int +mac_priv_check(struct ucred *cred, int priv) +{ + int error; + + MAC_CHECK(priv_check, cred, priv); + + return (error); +} + +/* + * Grant access to a privilege for a credential. Return success if any + * policy grants access. + */ +int +mac_priv_grant(struct ucred *cred, int priv) +{ + int error; + + MAC_GRANT(priv_grant, cred, priv); + + return (error); +} diff --git a/sys/security/mac/mac_process.c b/sys/security/mac/mac_process.c new file mode 100644 index 0000000..9e95d10 --- /dev/null +++ b/sys/security/mac/mac_process.c @@ -0,0 +1,531 @@ +/*- + * Copyright (c) 1999-2002, 2008 Robert N. M. Watson + * Copyright (c) 2001 Ilmar S. Habibulin + * Copyright (c) 2001-2003 Networks Associates Technology, Inc. + * Copyright (c) 2005 Samy Al Bahra + * Copyright (c) 2006 SPARTA, Inc. + * Copyright (c) 2008 Apple Inc. + * All rights reserved. + * + * This software was developed by Robert Watson and Ilmar Habibulin for the + * TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/condvar.h> +#include <sys/imgact.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/mac.h> +#include <sys/proc.h> +#include <sys/sbuf.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/file.h> +#include <sys/namei.h> +#include <sys/sysctl.h> + +#include <vm/vm.h> +#include <vm/pmap.h> +#include <vm/vm_map.h> +#include <vm/vm_object.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +static int mac_mmap_revocation = 1; +SYSCTL_INT(_security_mac, OID_AUTO, mmap_revocation, CTLFLAG_RW, + &mac_mmap_revocation, 0, "Revoke mmap access to files on subject " + "relabel"); + +static int mac_mmap_revocation_via_cow = 0; +SYSCTL_INT(_security_mac, OID_AUTO, mmap_revocation_via_cow, CTLFLAG_RW, + &mac_mmap_revocation_via_cow, 0, "Revoke mmap access to files via " + "copy-on-write semantics, or by removing all write access"); + +static void mac_proc_vm_revoke_recurse(struct thread *td, + struct ucred *cred, struct vm_map *map); + +static struct label * +mac_proc_label_alloc(void) +{ + struct label *label; + + label = mac_labelzone_alloc(M_WAITOK); + MAC_PERFORM(proc_init_label, label); + return (label); +} + +void +mac_proc_init(struct proc *p) +{ + + if (mac_labeled & MPC_OBJECT_PROC) + p->p_label = mac_proc_label_alloc(); + else + p->p_label = NULL; +} + +static void +mac_proc_label_free(struct label *label) +{ + + MAC_PERFORM(proc_destroy_label, label); + mac_labelzone_free(label); +} + +void +mac_proc_destroy(struct proc *p) +{ + + if (p->p_label != NULL) { + mac_proc_label_free(p->p_label); + p->p_label = NULL; + } +} + +void +mac_thread_userret(struct thread *td) +{ + + MAC_PERFORM(thread_userret, td); +} + +int +mac_execve_enter(struct image_params *imgp, struct mac *mac_p) +{ + struct label *label; + struct mac mac; + char *buffer; + int error; + + if (mac_p == NULL) + return (0); + + if (!(mac_labeled & MPC_OBJECT_CRED)) + return (EINVAL); + + error = copyin(mac_p, &mac, sizeof(mac)); + if (error) + return (error); + + error = mac_check_structmac_consistent(&mac); + if (error) + return (error); + + buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); + error = copyinstr(mac.m_string, buffer, mac.m_buflen, NULL); + if (error) { + free(buffer, M_MACTEMP); + return (error); + } + + label = mac_cred_label_alloc(); + error = mac_cred_internalize_label(label, buffer); + free(buffer, M_MACTEMP); + if (error) { + mac_cred_label_free(label); + return (error); + } + imgp->execlabel = label; + return (0); +} + +void +mac_execve_exit(struct image_params *imgp) +{ + if (imgp->execlabel != NULL) { + mac_cred_label_free(imgp->execlabel); + imgp->execlabel = NULL; + } +} + +void +mac_execve_interpreter_enter(struct vnode *interpvp, + struct label **interpvplabel) +{ + + if (mac_labeled & MPC_OBJECT_VNODE) { + *interpvplabel = mac_vnode_label_alloc(); + mac_vnode_copy_label(interpvp->v_label, *interpvplabel); + } else + *interpvplabel = NULL; +} + +void +mac_execve_interpreter_exit(struct label *interpvplabel) +{ + + if (interpvplabel != NULL) + mac_vnode_label_free(interpvplabel); +} + +/* + * When relabeling a process, call out to the policies for the maximum + * permission allowed for each object type we know about in its memory space, + * and revoke access (in the least surprising ways we know) when necessary. + * The process lock is not held here. + */ +void +mac_proc_vm_revoke(struct thread *td) +{ + struct ucred *cred; + + PROC_LOCK(td->td_proc); + cred = crhold(td->td_proc->p_ucred); + PROC_UNLOCK(td->td_proc); + + /* XXX freeze all other threads */ + mac_proc_vm_revoke_recurse(td, cred, + &td->td_proc->p_vmspace->vm_map); + /* XXX allow other threads to continue */ + + crfree(cred); +} + +static __inline const char * +prot2str(vm_prot_t prot) +{ + + switch (prot & VM_PROT_ALL) { + case VM_PROT_READ: + return ("r--"); + case VM_PROT_READ | VM_PROT_WRITE: + return ("rw-"); + case VM_PROT_READ | VM_PROT_EXECUTE: + return ("r-x"); + case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE: + return ("rwx"); + case VM_PROT_WRITE: + return ("-w-"); + case VM_PROT_EXECUTE: + return ("--x"); + case VM_PROT_WRITE | VM_PROT_EXECUTE: + return ("-wx"); + default: + return ("---"); + } +} + +static void +mac_proc_vm_revoke_recurse(struct thread *td, struct ucred *cred, + struct vm_map *map) +{ + struct vm_map_entry *vme; + int vfslocked, result; + vm_prot_t revokeperms; + vm_object_t backing_object, object; + vm_ooffset_t offset; + struct vnode *vp; + struct mount *mp; + + if (!mac_mmap_revocation) + return; + + vm_map_lock(map); + for (vme = map->header.next; vme != &map->header; vme = vme->next) { + if (vme->eflags & MAP_ENTRY_IS_SUB_MAP) { + mac_proc_vm_revoke_recurse(td, cred, + vme->object.sub_map); + continue; + } + /* + * Skip over entries that obviously are not shared. + */ + if (vme->eflags & (MAP_ENTRY_COW | MAP_ENTRY_NOSYNC) || + !vme->max_protection) + continue; + /* + * Drill down to the deepest backing object. + */ + offset = vme->offset; + object = vme->object.vm_object; + if (object == NULL) + continue; + VM_OBJECT_LOCK(object); + while ((backing_object = object->backing_object) != NULL) { + VM_OBJECT_LOCK(backing_object); + offset += object->backing_object_offset; + VM_OBJECT_UNLOCK(object); + object = backing_object; + } + VM_OBJECT_UNLOCK(object); + /* + * At the moment, vm_maps and objects aren't considered by + * the MAC system, so only things with backing by a normal + * object (read: vnodes) are checked. + */ + if (object->type != OBJT_VNODE) + continue; + vp = (struct vnode *)object->handle; + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + result = vme->max_protection; + mac_vnode_check_mmap_downgrade(cred, vp, &result); + VOP_UNLOCK(vp, 0); + /* + * Find out what maximum protection we may be allowing now + * but a policy needs to get removed. + */ + revokeperms = vme->max_protection & ~result; + if (!revokeperms) { + VFS_UNLOCK_GIANT(vfslocked); + continue; + } + printf("pid %ld: revoking %s perms from %#lx:%ld " + "(max %s/cur %s)\n", (long)td->td_proc->p_pid, + prot2str(revokeperms), (u_long)vme->start, + (long)(vme->end - vme->start), + prot2str(vme->max_protection), prot2str(vme->protection)); + /* + * This is the really simple case: if a map has more + * max_protection than is allowed, but it's not being + * actually used (that is, the current protection is still + * allowed), we can just wipe it out and do nothing more. + */ + if ((vme->protection & revokeperms) == 0) { + vme->max_protection -= revokeperms; + } else { + if (revokeperms & VM_PROT_WRITE) { + /* + * In the more complicated case, flush out all + * pending changes to the object then turn it + * copy-on-write. + */ + vm_object_reference(object); + (void) vn_start_write(vp, &mp, V_WAIT); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + VM_OBJECT_LOCK(object); + vm_object_page_clean(object, + OFF_TO_IDX(offset), + OFF_TO_IDX(offset + vme->end - vme->start + + PAGE_MASK), + OBJPC_SYNC); + VM_OBJECT_UNLOCK(object); + VOP_UNLOCK(vp, 0); + vn_finished_write(mp); + vm_object_deallocate(object); + /* + * Why bother if there's no read permissions + * anymore? For the rest, we need to leave + * the write permissions on for COW, or + * remove them entirely if configured to. + */ + if (!mac_mmap_revocation_via_cow) { + vme->max_protection &= ~VM_PROT_WRITE; + vme->protection &= ~VM_PROT_WRITE; + } if ((revokeperms & VM_PROT_READ) == 0) + vme->eflags |= MAP_ENTRY_COW | + MAP_ENTRY_NEEDS_COPY; + } + if (revokeperms & VM_PROT_EXECUTE) { + vme->max_protection &= ~VM_PROT_EXECUTE; + vme->protection &= ~VM_PROT_EXECUTE; + } + if (revokeperms & VM_PROT_READ) { + vme->max_protection = 0; + vme->protection = 0; + } + pmap_protect(map->pmap, vme->start, vme->end, + vme->protection & ~revokeperms); + vm_map_simplify_entry(map, vme); + } + VFS_UNLOCK_GIANT(vfslocked); + } + vm_map_unlock(map); +} + +int +mac_proc_check_debug(struct ucred *cred, struct proc *p) +{ + int error; + + PROC_LOCK_ASSERT(p, MA_OWNED); + + MAC_CHECK(proc_check_debug, cred, p); + + return (error); +} + +int +mac_proc_check_sched(struct ucred *cred, struct proc *p) +{ + int error; + + PROC_LOCK_ASSERT(p, MA_OWNED); + + MAC_CHECK(proc_check_sched, cred, p); + + return (error); +} + +int +mac_proc_check_signal(struct ucred *cred, struct proc *p, int signum) +{ + int error; + + PROC_LOCK_ASSERT(p, MA_OWNED); + + MAC_CHECK(proc_check_signal, cred, p, signum); + + return (error); +} + +int +mac_proc_check_setuid(struct proc *p, struct ucred *cred, uid_t uid) +{ + int error; + + PROC_LOCK_ASSERT(p, MA_OWNED); + + MAC_CHECK(proc_check_setuid, cred, uid); + return (error); +} + +int +mac_proc_check_seteuid(struct proc *p, struct ucred *cred, uid_t euid) +{ + int error; + + PROC_LOCK_ASSERT(p, MA_OWNED); + + MAC_CHECK(proc_check_seteuid, cred, euid); + return (error); +} + +int +mac_proc_check_setgid(struct proc *p, struct ucred *cred, gid_t gid) +{ + int error; + + PROC_LOCK_ASSERT(p, MA_OWNED); + + MAC_CHECK(proc_check_setgid, cred, gid); + + return (error); +} + +int +mac_proc_check_setegid(struct proc *p, struct ucred *cred, gid_t egid) +{ + int error; + + PROC_LOCK_ASSERT(p, MA_OWNED); + + MAC_CHECK(proc_check_setegid, cred, egid); + + return (error); +} + +int +mac_proc_check_setgroups(struct proc *p, struct ucred *cred, int ngroups, + gid_t *gidset) +{ + int error; + + PROC_LOCK_ASSERT(p, MA_OWNED); + + MAC_CHECK(proc_check_setgroups, cred, ngroups, gidset); + return (error); +} + +int +mac_proc_check_setreuid(struct proc *p, struct ucred *cred, uid_t ruid, + uid_t euid) +{ + int error; + + PROC_LOCK_ASSERT(p, MA_OWNED); + + MAC_CHECK(proc_check_setreuid, cred, ruid, euid); + + return (error); +} + +int +mac_proc_check_setregid(struct proc *proc, struct ucred *cred, gid_t rgid, + gid_t egid) +{ + int error; + + PROC_LOCK_ASSERT(proc, MA_OWNED); + + MAC_CHECK(proc_check_setregid, cred, rgid, egid); + + return (error); +} + +int +mac_proc_check_setresuid(struct proc *p, struct ucred *cred, uid_t ruid, + uid_t euid, uid_t suid) +{ + int error; + + PROC_LOCK_ASSERT(p, MA_OWNED); + + MAC_CHECK(proc_check_setresuid, cred, ruid, euid, suid); + return (error); +} + +int +mac_proc_check_setresgid(struct proc *p, struct ucred *cred, gid_t rgid, + gid_t egid, gid_t sgid) +{ + int error; + + PROC_LOCK_ASSERT(p, MA_OWNED); + + MAC_CHECK(proc_check_setresgid, cred, rgid, egid, sgid); + + return (error); +} + +int +mac_proc_check_wait(struct ucred *cred, struct proc *p) +{ + int error; + + PROC_LOCK_ASSERT(p, MA_OWNED); + + MAC_CHECK(proc_check_wait, cred, p); + + return (error); +} diff --git a/sys/security/mac/mac_socket.c b/sys/security/mac/mac_socket.c new file mode 100644 index 0000000..fe297ce --- /dev/null +++ b/sys/security/mac/mac_socket.c @@ -0,0 +1,571 @@ +/*- + * Copyright (c) 1999-2002 Robert N. M. Watson + * Copyright (c) 2001 Ilmar S. Habibulin + * Copyright (c) 2001-2005 Networks Associates Technology, Inc. + * Copyright (c) 2005-2006 SPARTA, Inc. + * Copyright (c) 2008 Apple Inc. + * All rights reserved. + * + * This software was developed by Robert Watson and Ilmar Habibulin for the + * TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by McAfee + * Research, the Technology Research Division of Network Associates, Inc. + * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the + * DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/mac.h> +#include <sys/sbuf.h> +#include <sys/systm.h> +#include <sys/mount.h> +#include <sys/file.h> +#include <sys/namei.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> + +#include <net/bpfdesc.h> +#include <net/if.h> +#include <net/if_var.h> + +#include <netinet/in.h> +#include <netinet/in_pcb.h> +#include <netinet/ip_var.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +/* + * Currently, sockets hold two labels: the label of the socket itself, and a + * peer label, which may be used by policies to hold a copy of the label of + * any remote endpoint. + * + * Possibly, this peer label should be maintained at the protocol layer + * (inpcb, unpcb, etc), as this would allow protocol-aware code to maintain + * the label consistently. For example, it might be copied live from a + * remote socket for UNIX domain sockets rather than keeping a local copy on + * this endpoint, but be cached and updated based on packets received for + * TCP/IP. + */ + +struct label * +mac_socket_label_alloc(int flag) +{ + struct label *label; + int error; + + label = mac_labelzone_alloc(flag); + if (label == NULL) + return (NULL); + + MAC_CHECK(socket_init_label, label, flag); + if (error) { + MAC_PERFORM(socket_destroy_label, label); + mac_labelzone_free(label); + return (NULL); + } + return (label); +} + +static struct label * +mac_socketpeer_label_alloc(int flag) +{ + struct label *label; + int error; + + label = mac_labelzone_alloc(flag); + if (label == NULL) + return (NULL); + + MAC_CHECK(socketpeer_init_label, label, flag); + if (error) { + MAC_PERFORM(socketpeer_destroy_label, label); + mac_labelzone_free(label); + return (NULL); + } + return (label); +} + +int +mac_socket_init(struct socket *so, int flag) +{ + + if (mac_labeled & MPC_OBJECT_SOCKET) { + so->so_label = mac_socket_label_alloc(flag); + if (so->so_label == NULL) + return (ENOMEM); + so->so_peerlabel = mac_socketpeer_label_alloc(flag); + if (so->so_peerlabel == NULL) { + mac_socket_label_free(so->so_label); + so->so_label = NULL; + return (ENOMEM); + } + } else { + so->so_label = NULL; + so->so_peerlabel = NULL; + } + return (0); +} + +void +mac_socket_label_free(struct label *label) +{ + + MAC_PERFORM(socket_destroy_label, label); + mac_labelzone_free(label); +} + +static void +mac_socketpeer_label_free(struct label *label) +{ + + MAC_PERFORM(socketpeer_destroy_label, label); + mac_labelzone_free(label); +} + +void +mac_socket_destroy(struct socket *so) +{ + + if (so->so_label != NULL) { + mac_socket_label_free(so->so_label); + so->so_label = NULL; + mac_socketpeer_label_free(so->so_peerlabel); + so->so_peerlabel = NULL; + } +} + +void +mac_socket_copy_label(struct label *src, struct label *dest) +{ + + MAC_PERFORM(socket_copy_label, src, dest); +} + +int +mac_socket_externalize_label(struct label *label, char *elements, + char *outbuf, size_t outbuflen) +{ + int error; + + MAC_EXTERNALIZE(socket, label, elements, outbuf, outbuflen); + + return (error); +} + +static int +mac_socketpeer_externalize_label(struct label *label, char *elements, + char *outbuf, size_t outbuflen) +{ + int error; + + MAC_EXTERNALIZE(socketpeer, label, elements, outbuf, outbuflen); + + return (error); +} + +int +mac_socket_internalize_label(struct label *label, char *string) +{ + int error; + + MAC_INTERNALIZE(socket, label, string); + + return (error); +} + +void +mac_socket_create(struct ucred *cred, struct socket *so) +{ + + MAC_PERFORM(socket_create, cred, so, so->so_label); +} + +void +mac_socket_newconn(struct socket *oldso, struct socket *newso) +{ + + SOCK_LOCK_ASSERT(oldso); + + MAC_PERFORM(socket_newconn, oldso, oldso->so_label, newso, + newso->so_label); +} + +static void +mac_socket_relabel(struct ucred *cred, struct socket *so, + struct label *newlabel) +{ + + SOCK_LOCK_ASSERT(so); + + MAC_PERFORM(socket_relabel, cred, so, so->so_label, newlabel); +} + +void +mac_socketpeer_set_from_mbuf(struct mbuf *m, struct socket *so) +{ + struct label *label; + + SOCK_LOCK_ASSERT(so); + + label = mac_mbuf_to_label(m); + + MAC_PERFORM(socketpeer_set_from_mbuf, m, label, so, + so->so_peerlabel); +} + +void +mac_socketpeer_set_from_socket(struct socket *oldso, struct socket *newso) +{ + + /* + * XXXRW: only hold the socket lock on one at a time, as one socket + * is the original, and one is the new. However, it's called in both + * directions, so we can't assert the lock here currently. + */ + MAC_PERFORM(socketpeer_set_from_socket, oldso, oldso->so_label, + newso, newso->so_peerlabel); +} + +void +mac_socket_create_mbuf(struct socket *so, struct mbuf *m) +{ + struct label *label; + + SOCK_LOCK_ASSERT(so); + + label = mac_mbuf_to_label(m); + + MAC_PERFORM(socket_create_mbuf, so, so->so_label, m, label); +} + +int +mac_socket_check_accept(struct ucred *cred, struct socket *so) +{ + int error; + + SOCK_LOCK_ASSERT(so); + + MAC_CHECK(socket_check_accept, cred, so, so->so_label); + + return (error); +} + +int +mac_socket_check_bind(struct ucred *ucred, struct socket *so, + struct sockaddr *sa) +{ + int error; + + SOCK_LOCK_ASSERT(so); + + MAC_CHECK(socket_check_bind, ucred, so, so->so_label, sa); + + return (error); +} + +int +mac_socket_check_connect(struct ucred *cred, struct socket *so, + struct sockaddr *sa) +{ + int error; + + SOCK_LOCK_ASSERT(so); + + MAC_CHECK(socket_check_connect, cred, so, so->so_label, sa); + + return (error); +} + +int +mac_socket_check_create(struct ucred *cred, int domain, int type, int proto) +{ + int error; + + MAC_CHECK(socket_check_create, cred, domain, type, proto); + + return (error); +} + +int +mac_socket_check_deliver(struct socket *so, struct mbuf *m) +{ + struct label *label; + int error; + + SOCK_LOCK_ASSERT(so); + + label = mac_mbuf_to_label(m); + + MAC_CHECK(socket_check_deliver, so, so->so_label, m, label); + + return (error); +} + +int +mac_socket_check_listen(struct ucred *cred, struct socket *so) +{ + int error; + + SOCK_LOCK_ASSERT(so); + + MAC_CHECK(socket_check_listen, cred, so, so->so_label); + + return (error); +} + +int +mac_socket_check_poll(struct ucred *cred, struct socket *so) +{ + int error; + + SOCK_LOCK_ASSERT(so); + + MAC_CHECK(socket_check_poll, cred, so, so->so_label); + + return (error); +} + +int +mac_socket_check_receive(struct ucred *cred, struct socket *so) +{ + int error; + + SOCK_LOCK_ASSERT(so); + + MAC_CHECK(socket_check_receive, cred, so, so->so_label); + + return (error); +} + +static int +mac_socket_check_relabel(struct ucred *cred, struct socket *so, + struct label *newlabel) +{ + int error; + + SOCK_LOCK_ASSERT(so); + + MAC_CHECK(socket_check_relabel, cred, so, so->so_label, newlabel); + + return (error); +} + +int +mac_socket_check_send(struct ucred *cred, struct socket *so) +{ + int error; + + SOCK_LOCK_ASSERT(so); + + MAC_CHECK(socket_check_send, cred, so, so->so_label); + + return (error); +} + +int +mac_socket_check_stat(struct ucred *cred, struct socket *so) +{ + int error; + + SOCK_LOCK_ASSERT(so); + + MAC_CHECK(socket_check_stat, cred, so, so->so_label); + + return (error); +} + +int +mac_socket_check_visible(struct ucred *cred, struct socket *so) +{ + int error; + + SOCK_LOCK_ASSERT(so); + + MAC_CHECK(socket_check_visible, cred, so, so->so_label); + + return (error); +} + +int +mac_socket_label_set(struct ucred *cred, struct socket *so, + struct label *label) +{ + int error; + + /* + * We acquire the socket lock when we perform the test and set, but + * have to release it as the pcb code needs to acquire the pcb lock, + * which will precede the socket lock in the lock order. However, + * this is fine, as any race will simply result in the inpcb being + * refreshed twice, but still consistently, as the inpcb code will + * acquire the socket lock before refreshing, holding both locks. + */ + SOCK_LOCK(so); + error = mac_socket_check_relabel(cred, so, label); + if (error) { + SOCK_UNLOCK(so); + return (error); + } + + mac_socket_relabel(cred, so, label); + SOCK_UNLOCK(so); + + /* + * If the protocol has expressed interest in socket layer changes, + * such as if it needs to propagate changes to a cached pcb label + * from the socket, notify it of the label change while holding the + * socket lock. + */ + if (so->so_proto->pr_usrreqs->pru_sosetlabel != NULL) + (so->so_proto->pr_usrreqs->pru_sosetlabel)(so); + + return (0); +} + +int +mac_setsockopt_label(struct ucred *cred, struct socket *so, struct mac *mac) +{ + struct label *intlabel; + char *buffer; + int error; + + if (!(mac_labeled & MPC_OBJECT_SOCKET)) + return (EINVAL); + + error = mac_check_structmac_consistent(mac); + if (error) + return (error); + + buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK); + error = copyinstr(mac->m_string, buffer, mac->m_buflen, NULL); + if (error) { + free(buffer, M_MACTEMP); + return (error); + } + + intlabel = mac_socket_label_alloc(M_WAITOK); + error = mac_socket_internalize_label(intlabel, buffer); + free(buffer, M_MACTEMP); + if (error) + goto out; + + error = mac_socket_label_set(cred, so, intlabel); +out: + mac_socket_label_free(intlabel); + return (error); +} + +int +mac_getsockopt_label(struct ucred *cred, struct socket *so, struct mac *mac) +{ + char *buffer, *elements; + struct label *intlabel; + int error; + + if (!(mac_labeled & MPC_OBJECT_SOCKET)) + return (EINVAL); + + error = mac_check_structmac_consistent(mac); + if (error) + return (error); + + elements = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK); + error = copyinstr(mac->m_string, elements, mac->m_buflen, NULL); + if (error) { + free(elements, M_MACTEMP); + return (error); + } + + buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); + intlabel = mac_socket_label_alloc(M_WAITOK); + SOCK_LOCK(so); + mac_socket_copy_label(so->so_label, intlabel); + SOCK_UNLOCK(so); + error = mac_socket_externalize_label(intlabel, elements, buffer, + mac->m_buflen); + mac_socket_label_free(intlabel); + if (error == 0) + error = copyout(buffer, mac->m_string, strlen(buffer)+1); + + free(buffer, M_MACTEMP); + free(elements, M_MACTEMP); + + return (error); +} + +int +mac_getsockopt_peerlabel(struct ucred *cred, struct socket *so, + struct mac *mac) +{ + char *elements, *buffer; + struct label *intlabel; + int error; + + if (!(mac_labeled & MPC_OBJECT_SOCKET)) + return (EINVAL); + + error = mac_check_structmac_consistent(mac); + if (error) + return (error); + + elements = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK); + error = copyinstr(mac->m_string, elements, mac->m_buflen, NULL); + if (error) { + free(elements, M_MACTEMP); + return (error); + } + + buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); + intlabel = mac_socket_label_alloc(M_WAITOK); + SOCK_LOCK(so); + mac_socket_copy_label(so->so_peerlabel, intlabel); + SOCK_UNLOCK(so); + error = mac_socketpeer_externalize_label(intlabel, elements, buffer, + mac->m_buflen); + mac_socket_label_free(intlabel); + if (error == 0) + error = copyout(buffer, mac->m_string, strlen(buffer)+1); + + free(buffer, M_MACTEMP); + free(elements, M_MACTEMP); + + return (error); +} diff --git a/sys/security/mac/mac_syscalls.c b/sys/security/mac/mac_syscalls.c new file mode 100644 index 0000000..c4b0606 --- /dev/null +++ b/sys/security/mac/mac_syscalls.c @@ -0,0 +1,723 @@ +/*- + * Copyright (c) 1999-2002, 2006 Robert N. M. Watson + * Copyright (c) 2001 Ilmar S. Habibulin + * Copyright (c) 2001-2005 Networks Associates Technology, Inc. + * Copyright (c) 2005-2006 SPARTA, Inc. + * Copyright (c) 2008 Apple Inc. + * All rights reserved. + * + * This software was developed by Robert Watson and Ilmar Habibulin for the + * TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/fcntl.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/mac.h> +#include <sys/proc.h> +#include <sys/systm.h> +#include <sys/sysproto.h> +#include <sys/sysent.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/file.h> +#include <sys/namei.h> +#include <sys/socket.h> +#include <sys/pipe.h> +#include <sys/socketvar.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +#ifdef MAC + +int +__mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) +{ + char *elements, *buffer; + struct mac mac; + struct proc *tproc; + struct ucred *tcred; + int error; + + error = copyin(uap->mac_p, &mac, sizeof(mac)); + if (error) + return (error); + + error = mac_check_structmac_consistent(&mac); + if (error) + return (error); + + tproc = pfind(uap->pid); + if (tproc == NULL) + return (ESRCH); + + tcred = NULL; /* Satisfy gcc. */ + error = p_cansee(td, tproc); + if (error == 0) + tcred = crhold(tproc->p_ucred); + PROC_UNLOCK(tproc); + if (error) + return (error); + + elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); + error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL); + if (error) { + free(elements, M_MACTEMP); + crfree(tcred); + return (error); + } + + buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); + error = mac_cred_externalize_label(tcred->cr_label, elements, + buffer, mac.m_buflen); + if (error == 0) + error = copyout(buffer, mac.m_string, strlen(buffer)+1); + + free(buffer, M_MACTEMP); + free(elements, M_MACTEMP); + crfree(tcred); + return (error); +} + +int +__mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) +{ + char *elements, *buffer; + struct mac mac; + int error; + + error = copyin(uap->mac_p, &mac, sizeof(mac)); + if (error) + return (error); + + error = mac_check_structmac_consistent(&mac); + if (error) + return (error); + + elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); + error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL); + if (error) { + free(elements, M_MACTEMP); + return (error); + } + + buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); + error = mac_cred_externalize_label(td->td_ucred->cr_label, + elements, buffer, mac.m_buflen); + if (error == 0) + error = copyout(buffer, mac.m_string, strlen(buffer)+1); + + free(buffer, M_MACTEMP); + free(elements, M_MACTEMP); + return (error); +} + +int +__mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) +{ + struct ucred *newcred, *oldcred; + struct label *intlabel; + struct proc *p; + struct mac mac; + char *buffer; + int error; + + if (!(mac_labeled & MPC_OBJECT_CRED)) + return (EINVAL); + + error = copyin(uap->mac_p, &mac, sizeof(mac)); + if (error) + return (error); + + error = mac_check_structmac_consistent(&mac); + if (error) + return (error); + + buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); + error = copyinstr(mac.m_string, buffer, mac.m_buflen, NULL); + if (error) { + free(buffer, M_MACTEMP); + return (error); + } + + intlabel = mac_cred_label_alloc(); + error = mac_cred_internalize_label(intlabel, buffer); + free(buffer, M_MACTEMP); + if (error) + goto out; + + newcred = crget(); + + p = td->td_proc; + PROC_LOCK(p); + oldcred = p->p_ucred; + + error = mac_cred_check_relabel(oldcred, intlabel); + if (error) { + PROC_UNLOCK(p); + crfree(newcred); + goto out; + } + + setsugid(p); + crcopy(newcred, oldcred); + mac_cred_relabel(newcred, intlabel); + p->p_ucred = newcred; + + PROC_UNLOCK(p); + crfree(oldcred); + mac_proc_vm_revoke(td); + +out: + mac_cred_label_free(intlabel); + return (error); +} + +int +__mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) +{ + char *elements, *buffer; + struct label *intlabel; + struct file *fp; + struct mac mac; + struct vnode *vp; + struct pipe *pipe; + struct socket *so; + short label_type; + int vfslocked, error; + + error = copyin(uap->mac_p, &mac, sizeof(mac)); + if (error) + return (error); + + error = mac_check_structmac_consistent(&mac); + if (error) + return (error); + + elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); + error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL); + if (error) { + free(elements, M_MACTEMP); + return (error); + } + + buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); + error = fget(td, uap->fd, &fp); + if (error) + goto out; + + label_type = fp->f_type; + switch (fp->f_type) { + case DTYPE_FIFO: + case DTYPE_VNODE: + if (!(mac_labeled & MPC_OBJECT_VNODE)) + return (EINVAL); + vp = fp->f_vnode; + intlabel = mac_vnode_label_alloc(); + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + mac_vnode_copy_label(vp->v_label, intlabel); + VOP_UNLOCK(vp, 0); + VFS_UNLOCK_GIANT(vfslocked); + error = mac_vnode_externalize_label(intlabel, elements, + buffer, mac.m_buflen); + mac_vnode_label_free(intlabel); + break; + + case DTYPE_PIPE: + if (!(mac_labeled & MPC_OBJECT_PIPE)) + return (EINVAL); + pipe = fp->f_data; + intlabel = mac_pipe_label_alloc(); + PIPE_LOCK(pipe); + mac_pipe_copy_label(pipe->pipe_pair->pp_label, intlabel); + PIPE_UNLOCK(pipe); + error = mac_pipe_externalize_label(intlabel, elements, + buffer, mac.m_buflen); + mac_pipe_label_free(intlabel); + break; + + case DTYPE_SOCKET: + if (!(mac_labeled & MPC_OBJECT_SOCKET)) + return (EINVAL); + so = fp->f_data; + intlabel = mac_socket_label_alloc(M_WAITOK); + SOCK_LOCK(so); + mac_socket_copy_label(so->so_label, intlabel); + SOCK_UNLOCK(so); + error = mac_socket_externalize_label(intlabel, elements, + buffer, mac.m_buflen); + mac_socket_label_free(intlabel); + break; + + default: + error = EINVAL; + } + fdrop(fp, td); + if (error == 0) + error = copyout(buffer, mac.m_string, strlen(buffer)+1); + +out: + free(buffer, M_MACTEMP); + free(elements, M_MACTEMP); + return (error); +} + +int +__mac_get_file(struct thread *td, struct __mac_get_file_args *uap) +{ + char *elements, *buffer; + struct nameidata nd; + struct label *intlabel; + struct mac mac; + int vfslocked, error; + + if (!(mac_labeled & MPC_OBJECT_VNODE)) + return (EINVAL); + + error = copyin(uap->mac_p, &mac, sizeof(mac)); + if (error) + return (error); + + error = mac_check_structmac_consistent(&mac); + if (error) + return (error); + + elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); + error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL); + if (error) { + free(elements, M_MACTEMP); + return (error); + } + + buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); + NDINIT(&nd, LOOKUP, MPSAFE | LOCKLEAF | FOLLOW, UIO_USERSPACE, + uap->path_p, td); + error = namei(&nd); + if (error) + goto out; + + intlabel = mac_vnode_label_alloc(); + vfslocked = NDHASGIANT(&nd); + mac_vnode_copy_label(nd.ni_vp->v_label, intlabel); + error = mac_vnode_externalize_label(intlabel, elements, buffer, + mac.m_buflen); + + NDFREE(&nd, 0); + VFS_UNLOCK_GIANT(vfslocked); + mac_vnode_label_free(intlabel); + if (error == 0) + error = copyout(buffer, mac.m_string, strlen(buffer)+1); + +out: + free(buffer, M_MACTEMP); + free(elements, M_MACTEMP); + + return (error); +} + +int +__mac_get_link(struct thread *td, struct __mac_get_link_args *uap) +{ + char *elements, *buffer; + struct nameidata nd; + struct label *intlabel; + struct mac mac; + int vfslocked, error; + + if (!(mac_labeled & MPC_OBJECT_VNODE)) + return (EINVAL); + + error = copyin(uap->mac_p, &mac, sizeof(mac)); + if (error) + return (error); + + error = mac_check_structmac_consistent(&mac); + if (error) + return (error); + + elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); + error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL); + if (error) { + free(elements, M_MACTEMP); + return (error); + } + + buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); + NDINIT(&nd, LOOKUP, MPSAFE | LOCKLEAF | NOFOLLOW, UIO_USERSPACE, + uap->path_p, td); + error = namei(&nd); + if (error) + goto out; + + intlabel = mac_vnode_label_alloc(); + vfslocked = NDHASGIANT(&nd); + mac_vnode_copy_label(nd.ni_vp->v_label, intlabel); + error = mac_vnode_externalize_label(intlabel, elements, buffer, + mac.m_buflen); + NDFREE(&nd, 0); + VFS_UNLOCK_GIANT(vfslocked); + mac_vnode_label_free(intlabel); + + if (error == 0) + error = copyout(buffer, mac.m_string, strlen(buffer)+1); + +out: + free(buffer, M_MACTEMP); + free(elements, M_MACTEMP); + + return (error); +} + +int +__mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) +{ + struct label *intlabel; + struct pipe *pipe; + struct socket *so; + struct file *fp; + struct mount *mp; + struct vnode *vp; + struct mac mac; + char *buffer; + int error, vfslocked; + + error = copyin(uap->mac_p, &mac, sizeof(mac)); + if (error) + return (error); + + error = mac_check_structmac_consistent(&mac); + if (error) + return (error); + + buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); + error = copyinstr(mac.m_string, buffer, mac.m_buflen, NULL); + if (error) { + free(buffer, M_MACTEMP); + return (error); + } + + error = fget(td, uap->fd, &fp); + if (error) + goto out; + + switch (fp->f_type) { + case DTYPE_FIFO: + case DTYPE_VNODE: + if (!(mac_labeled & MPC_OBJECT_VNODE)) + return (EINVAL); + intlabel = mac_vnode_label_alloc(); + error = mac_vnode_internalize_label(intlabel, buffer); + if (error) { + mac_vnode_label_free(intlabel); + break; + } + vp = fp->f_vnode; + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + error = vn_start_write(vp, &mp, V_WAIT | PCATCH); + if (error != 0) { + VFS_UNLOCK_GIANT(vfslocked); + mac_vnode_label_free(intlabel); + break; + } + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + error = vn_setlabel(vp, intlabel, td->td_ucred); + VOP_UNLOCK(vp, 0); + vn_finished_write(mp); + VFS_UNLOCK_GIANT(vfslocked); + mac_vnode_label_free(intlabel); + break; + + case DTYPE_PIPE: + if (!(mac_labeled & MPC_OBJECT_PIPE)) + return (EINVAL); + intlabel = mac_pipe_label_alloc(); + error = mac_pipe_internalize_label(intlabel, buffer); + if (error == 0) { + pipe = fp->f_data; + PIPE_LOCK(pipe); + error = mac_pipe_label_set(td->td_ucred, + pipe->pipe_pair, intlabel); + PIPE_UNLOCK(pipe); + } + mac_pipe_label_free(intlabel); + break; + + case DTYPE_SOCKET: + if (!(mac_labeled & MPC_OBJECT_SOCKET)) + return (EINVAL); + intlabel = mac_socket_label_alloc(M_WAITOK); + error = mac_socket_internalize_label(intlabel, buffer); + if (error == 0) { + so = fp->f_data; + error = mac_socket_label_set(td->td_ucred, so, + intlabel); + } + mac_socket_label_free(intlabel); + break; + + default: + error = EINVAL; + } + fdrop(fp, td); +out: + free(buffer, M_MACTEMP); + return (error); +} + +int +__mac_set_file(struct thread *td, struct __mac_set_file_args *uap) +{ + struct label *intlabel; + struct nameidata nd; + struct mount *mp; + struct mac mac; + char *buffer; + int vfslocked, error; + + if (!(mac_labeled & MPC_OBJECT_VNODE)) + return (EINVAL); + + error = copyin(uap->mac_p, &mac, sizeof(mac)); + if (error) + return (error); + + error = mac_check_structmac_consistent(&mac); + if (error) + return (error); + + buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); + error = copyinstr(mac.m_string, buffer, mac.m_buflen, NULL); + if (error) { + free(buffer, M_MACTEMP); + return (error); + } + + intlabel = mac_vnode_label_alloc(); + error = mac_vnode_internalize_label(intlabel, buffer); + free(buffer, M_MACTEMP); + if (error) + goto out; + + NDINIT(&nd, LOOKUP, MPSAFE | LOCKLEAF | FOLLOW, UIO_USERSPACE, + uap->path_p, td); + error = namei(&nd); + vfslocked = NDHASGIANT(&nd); + if (error == 0) { + error = vn_start_write(nd.ni_vp, &mp, V_WAIT | PCATCH); + if (error == 0) { + error = vn_setlabel(nd.ni_vp, intlabel, + td->td_ucred); + vn_finished_write(mp); + } + } + + NDFREE(&nd, 0); + VFS_UNLOCK_GIANT(vfslocked); +out: + mac_vnode_label_free(intlabel); + return (error); +} + +int +__mac_set_link(struct thread *td, struct __mac_set_link_args *uap) +{ + struct label *intlabel; + struct nameidata nd; + struct mount *mp; + struct mac mac; + char *buffer; + int vfslocked, error; + + if (!(mac_labeled & MPC_OBJECT_VNODE)) + return (EINVAL); + + error = copyin(uap->mac_p, &mac, sizeof(mac)); + if (error) + return (error); + + error = mac_check_structmac_consistent(&mac); + if (error) + return (error); + + buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); + error = copyinstr(mac.m_string, buffer, mac.m_buflen, NULL); + if (error) { + free(buffer, M_MACTEMP); + return (error); + } + + intlabel = mac_vnode_label_alloc(); + error = mac_vnode_internalize_label(intlabel, buffer); + free(buffer, M_MACTEMP); + if (error) + goto out; + + NDINIT(&nd, LOOKUP, MPSAFE | LOCKLEAF | NOFOLLOW, UIO_USERSPACE, + uap->path_p, td); + error = namei(&nd); + vfslocked = NDHASGIANT(&nd); + if (error == 0) { + error = vn_start_write(nd.ni_vp, &mp, V_WAIT | PCATCH); + if (error == 0) { + error = vn_setlabel(nd.ni_vp, intlabel, + td->td_ucred); + vn_finished_write(mp); + } + } + + NDFREE(&nd, 0); + VFS_UNLOCK_GIANT(vfslocked); +out: + mac_vnode_label_free(intlabel); + return (error); +} + +int +mac_syscall(struct thread *td, struct mac_syscall_args *uap) +{ + struct mac_policy_conf *mpc; + char target[MAC_MAX_POLICY_NAME]; + int entrycount, error; + + error = copyinstr(uap->policy, target, sizeof(target), NULL); + if (error) + return (error); + + error = ENOSYS; + LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) { + if (strcmp(mpc->mpc_name, target) == 0 && + mpc->mpc_ops->mpo_syscall != NULL) { + error = mpc->mpc_ops->mpo_syscall(td, + uap->call, uap->arg); + goto out; + } + } + + if ((entrycount = mac_policy_list_conditional_busy()) != 0) { + LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { + if (strcmp(mpc->mpc_name, target) == 0 && + mpc->mpc_ops->mpo_syscall != NULL) { + error = mpc->mpc_ops->mpo_syscall(td, + uap->call, uap->arg); + break; + } + } + mac_policy_list_unbusy(); + } +out: + return (error); +} + +#else /* !MAC */ + +int +__mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) +{ + + return (ENOSYS); +} + +int +__mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) +{ + + return (ENOSYS); +} + +int +__mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) +{ + + return (ENOSYS); +} + +int +__mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) +{ + + return (ENOSYS); +} + +int +__mac_get_file(struct thread *td, struct __mac_get_file_args *uap) +{ + + return (ENOSYS); +} + +int +__mac_get_link(struct thread *td, struct __mac_get_link_args *uap) +{ + + return (ENOSYS); +} + +int +__mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) +{ + + return (ENOSYS); +} + +int +__mac_set_file(struct thread *td, struct __mac_set_file_args *uap) +{ + + return (ENOSYS); +} + +int +__mac_set_link(struct thread *td, struct __mac_set_link_args *uap) +{ + + return (ENOSYS); +} + +int +mac_syscall(struct thread *td, struct mac_syscall_args *uap) +{ + + return (ENOSYS); +} + +#endif /* !MAC */ diff --git a/sys/security/mac/mac_system.c b/sys/security/mac/mac_system.c new file mode 100644 index 0000000..588e019 --- /dev/null +++ b/sys/security/mac/mac_system.c @@ -0,0 +1,191 @@ +/*- + * Copyright (c) 2002-2003 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * Copyright (c) 2007 Robert N. M. Watson + * All rights reserved. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * Portions of this software were developed by Robert Watson for the + * TrustedBSD Project. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * MAC Framework entry points relating to overall operation of system, + * including global services such as the kernel environment and loadable + * modules. + * + * System checks often align with existing privilege checks, but provide + * additional security context that may be relevant to policies, such as the + * specific object being operated on. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/sysctl.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +int +mac_kenv_check_dump(struct ucred *cred) +{ + int error; + + MAC_CHECK(kenv_check_dump, cred); + + return (error); +} + +int +mac_kenv_check_get(struct ucred *cred, char *name) +{ + int error; + + MAC_CHECK(kenv_check_get, cred, name); + + return (error); +} + +int +mac_kenv_check_set(struct ucred *cred, char *name, char *value) +{ + int error; + + MAC_CHECK(kenv_check_set, cred, name, value); + + return (error); +} + +int +mac_kenv_check_unset(struct ucred *cred, char *name) +{ + int error; + + MAC_CHECK(kenv_check_unset, cred, name); + + return (error); +} + +int +mac_kld_check_load(struct ucred *cred, struct vnode *vp) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_kld_check_load"); + + MAC_CHECK(kld_check_load, cred, vp, vp->v_label); + + return (error); +} + +int +mac_kld_check_stat(struct ucred *cred) +{ + int error; + + MAC_CHECK(kld_check_stat, cred); + + return (error); +} + +int +mac_system_check_acct(struct ucred *cred, struct vnode *vp) +{ + int error; + + if (vp != NULL) { + ASSERT_VOP_LOCKED(vp, "mac_system_check_acct"); + } + + MAC_CHECK(system_check_acct, cred, vp, + vp != NULL ? vp->v_label : NULL); + + return (error); +} + +int +mac_system_check_reboot(struct ucred *cred, int howto) +{ + int error; + + MAC_CHECK(system_check_reboot, cred, howto); + + return (error); +} + +int +mac_system_check_swapon(struct ucred *cred, struct vnode *vp) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_system_check_swapon"); + + MAC_CHECK(system_check_swapon, cred, vp, vp->v_label); + return (error); +} + +int +mac_system_check_swapoff(struct ucred *cred, struct vnode *vp) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_system_check_swapoff"); + + MAC_CHECK(system_check_swapoff, cred, vp, vp->v_label); + return (error); +} + +int +mac_system_check_sysctl(struct ucred *cred, struct sysctl_oid *oidp, + void *arg1, int arg2, struct sysctl_req *req) +{ + int error; + + /* + * XXXMAC: We would very much like to assert the SYSCTL_LOCK here, + * but since it's not exported from kern_sysctl.c, we can't. + */ + MAC_CHECK(system_check_sysctl, cred, oidp, arg1, arg2, req); + + return (error); +} diff --git a/sys/security/mac/mac_sysv_msg.c b/sys/security/mac/mac_sysv_msg.c new file mode 100644 index 0000000..2c5bbca --- /dev/null +++ b/sys/security/mac/mac_sysv_msg.c @@ -0,0 +1,237 @@ +/*- + * Copyright (c) 2003-2004 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * Copyright (c) 2008 Apple Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/sbuf.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/file.h> +#include <sys/namei.h> +#include <sys/sysctl.h> +#include <sys/msg.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +static struct label * +mac_sysv_msgmsg_label_alloc(void) +{ + struct label *label; + + label = mac_labelzone_alloc(M_WAITOK); + MAC_PERFORM(sysvmsg_init_label, label); + return (label); +} + +void +mac_sysvmsg_init(struct msg *msgptr) +{ + + if (mac_labeled & MPC_OBJECT_SYSVMSG) + msgptr->label = mac_sysv_msgmsg_label_alloc(); + else + msgptr->label = NULL; +} + +static struct label * +mac_sysv_msgqueue_label_alloc(void) +{ + struct label *label; + + label = mac_labelzone_alloc(M_WAITOK); + MAC_PERFORM(sysvmsq_init_label, label); + return (label); +} + +void +mac_sysvmsq_init(struct msqid_kernel *msqkptr) +{ + + if (mac_labeled & MPC_OBJECT_SYSVMSQ) + msqkptr->label = mac_sysv_msgqueue_label_alloc(); + else + msqkptr->label = NULL; +} + +static void +mac_sysv_msgmsg_label_free(struct label *label) +{ + + MAC_PERFORM(sysvmsg_destroy_label, label); + mac_labelzone_free(label); +} + +void +mac_sysvmsg_destroy(struct msg *msgptr) +{ + + if (msgptr->label != NULL) { + mac_sysv_msgmsg_label_free(msgptr->label); + msgptr->label = NULL; + } +} + +static void +mac_sysv_msgqueue_label_free(struct label *label) +{ + + MAC_PERFORM(sysvmsq_destroy_label, label); + mac_labelzone_free(label); +} + +void +mac_sysvmsq_destroy(struct msqid_kernel *msqkptr) +{ + + if (msqkptr->label != NULL) { + mac_sysv_msgqueue_label_free(msqkptr->label); + msqkptr->label = NULL; + } +} + +void +mac_sysvmsg_create(struct ucred *cred, struct msqid_kernel *msqkptr, + struct msg *msgptr) +{ + + MAC_PERFORM(sysvmsg_create, cred, msqkptr, msqkptr->label, + msgptr, msgptr->label); +} + +void +mac_sysvmsq_create(struct ucred *cred, struct msqid_kernel *msqkptr) +{ + + MAC_PERFORM(sysvmsq_create, cred, msqkptr, msqkptr->label); +} + +void +mac_sysvmsg_cleanup(struct msg *msgptr) +{ + + MAC_PERFORM(sysvmsg_cleanup, msgptr->label); +} + +void +mac_sysvmsq_cleanup(struct msqid_kernel *msqkptr) +{ + + MAC_PERFORM(sysvmsq_cleanup, msqkptr->label); +} + +int +mac_sysvmsq_check_msgmsq(struct ucred *cred, struct msg *msgptr, + struct msqid_kernel *msqkptr) +{ + int error; + + MAC_CHECK(sysvmsq_check_msgmsq, cred, msgptr, msgptr->label, + msqkptr, msqkptr->label); + + return (error); +} + +int +mac_sysvmsq_check_msgrcv(struct ucred *cred, struct msg *msgptr) +{ + int error; + + MAC_CHECK(sysvmsq_check_msgrcv, cred, msgptr, msgptr->label); + + return (error); +} + +int +mac_sysvmsq_check_msgrmid(struct ucred *cred, struct msg *msgptr) +{ + int error; + + MAC_CHECK(sysvmsq_check_msgrmid, cred, msgptr, msgptr->label); + + return (error); +} + +int +mac_sysvmsq_check_msqget(struct ucred *cred, struct msqid_kernel *msqkptr) +{ + int error; + + MAC_CHECK(sysvmsq_check_msqget, cred, msqkptr, msqkptr->label); + + return (error); +} + +int +mac_sysvmsq_check_msqsnd(struct ucred *cred, struct msqid_kernel *msqkptr) +{ + int error; + + MAC_CHECK(sysvmsq_check_msqsnd, cred, msqkptr, msqkptr->label); + + return (error); +} + +int +mac_sysvmsq_check_msqrcv(struct ucred *cred, struct msqid_kernel *msqkptr) +{ + int error; + + MAC_CHECK(sysvmsq_check_msqrcv, cred, msqkptr, msqkptr->label); + + return (error); +} + +int +mac_sysvmsq_check_msqctl(struct ucred *cred, struct msqid_kernel *msqkptr, + int cmd) +{ + int error; + + MAC_CHECK(sysvmsq_check_msqctl, cred, msqkptr, msqkptr->label, cmd); + + return (error); +} diff --git a/sys/security/mac/mac_sysv_sem.c b/sys/security/mac/mac_sysv_sem.c new file mode 100644 index 0000000..94a1107 --- /dev/null +++ b/sys/security/mac/mac_sysv_sem.c @@ -0,0 +1,143 @@ +/*- + * Copyright (c) 2003-2004 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/sbuf.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/file.h> +#include <sys/namei.h> +#include <sys/sysctl.h> +#include <sys/sem.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +static struct label * +mac_sysv_sem_label_alloc(void) +{ + struct label *label; + + label = mac_labelzone_alloc(M_WAITOK); + MAC_PERFORM(sysvsem_init_label, label); + return (label); +} + +void +mac_sysvsem_init(struct semid_kernel *semakptr) +{ + + if (mac_labeled & MPC_OBJECT_SYSVSEM) + semakptr->label = mac_sysv_sem_label_alloc(); + else + semakptr->label = NULL; +} + +static void +mac_sysv_sem_label_free(struct label *label) +{ + + MAC_PERFORM(sysvsem_destroy_label, label); + mac_labelzone_free(label); +} + +void +mac_sysvsem_destroy(struct semid_kernel *semakptr) +{ + + if (semakptr->label != NULL) { + mac_sysv_sem_label_free(semakptr->label); + semakptr->label = NULL; + } +} + +void +mac_sysvsem_create(struct ucred *cred, struct semid_kernel *semakptr) +{ + + MAC_PERFORM(sysvsem_create, cred, semakptr, semakptr->label); +} + +void +mac_sysvsem_cleanup(struct semid_kernel *semakptr) +{ + + MAC_PERFORM(sysvsem_cleanup, semakptr->label); +} + +int +mac_sysvsem_check_semctl(struct ucred *cred, struct semid_kernel *semakptr, + int cmd) +{ + int error; + + MAC_CHECK(sysvsem_check_semctl, cred, semakptr, semakptr->label, + cmd); + + return (error); +} + +int +mac_sysvsem_check_semget(struct ucred *cred, struct semid_kernel *semakptr) +{ + int error; + + MAC_CHECK(sysvsem_check_semget, cred, semakptr, semakptr->label); + + return (error); +} + +int +mac_sysvsem_check_semop(struct ucred *cred, struct semid_kernel *semakptr, + size_t accesstype) +{ + int error; + + MAC_CHECK(sysvsem_check_semop, cred, semakptr, semakptr->label, + accesstype); + + return (error); +} diff --git a/sys/security/mac/mac_sysv_shm.c b/sys/security/mac/mac_sysv_shm.c new file mode 100644 index 0000000..950c23e --- /dev/null +++ b/sys/security/mac/mac_sysv_shm.c @@ -0,0 +1,155 @@ +/*- + * Copyright (c) 2003-2004 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/sbuf.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/file.h> +#include <sys/namei.h> +#include <sys/sysctl.h> +#include <sys/shm.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +static struct label * +mac_sysv_shm_label_alloc(void) +{ + struct label *label; + + label = mac_labelzone_alloc(M_WAITOK); + MAC_PERFORM(sysvshm_init_label, label); + return (label); +} + +void +mac_sysvshm_init(struct shmid_kernel *shmsegptr) +{ + + if (mac_labeled & MPC_OBJECT_SYSVSHM) + shmsegptr->label = mac_sysv_shm_label_alloc(); + else + shmsegptr->label = NULL; +} + +static void +mac_sysv_shm_label_free(struct label *label) +{ + + MAC_PERFORM(sysvshm_destroy_label, label); + mac_labelzone_free(label); +} + +void +mac_sysvshm_destroy(struct shmid_kernel *shmsegptr) +{ + + if (shmsegptr->label != NULL) { + mac_sysv_shm_label_free(shmsegptr->label); + shmsegptr->label = NULL; + } +} + +void +mac_sysvshm_create(struct ucred *cred, struct shmid_kernel *shmsegptr) +{ + + MAC_PERFORM(sysvshm_create, cred, shmsegptr, shmsegptr->label); +} + +void +mac_sysvshm_cleanup(struct shmid_kernel *shmsegptr) +{ + + MAC_PERFORM(sysvshm_cleanup, shmsegptr->label); +} + +int +mac_sysvshm_check_shmat(struct ucred *cred, struct shmid_kernel *shmsegptr, + int shmflg) +{ + int error; + + MAC_CHECK(sysvshm_check_shmat, cred, shmsegptr, shmsegptr->label, + shmflg); + + return (error); +} + +int +mac_sysvshm_check_shmctl(struct ucred *cred, struct shmid_kernel *shmsegptr, + int cmd) +{ + int error; + + MAC_CHECK(sysvshm_check_shmctl, cred, shmsegptr, shmsegptr->label, + cmd); + + return (error); +} + +int +mac_sysvshm_check_shmdt(struct ucred *cred, struct shmid_kernel *shmsegptr) +{ + int error; + + MAC_CHECK(sysvshm_check_shmdt, cred, shmsegptr, shmsegptr->label); + + return (error); +} + +int +mac_sysvshm_check_shmget(struct ucred *cred, struct shmid_kernel *shmsegptr, + int shmflg) +{ + int error; + + MAC_CHECK(sysvshm_check_shmget, cred, shmsegptr, shmsegptr->label, + shmflg); + + return (error); +} diff --git a/sys/security/mac/mac_vfs.c b/sys/security/mac/mac_vfs.c new file mode 100644 index 0000000..42da76c --- /dev/null +++ b/sys/security/mac/mac_vfs.c @@ -0,0 +1,894 @@ +/*- + * Copyright (c) 1999-2002 Robert N. M. Watson + * Copyright (c) 2001 Ilmar S. Habibulin + * Copyright (c) 2001-2005 McAfee, Inc. + * Copyright (c) 2005-2006 SPARTA, Inc. + * Copyright (c) 2008 Apple Inc. + * All rights reserved. + * + * This software was developed by Robert Watson and Ilmar Habibulin for the + * TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by McAfee + * Research, the Security Research Division of McAfee, Inc. under + * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA + * CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/condvar.h> +#include <sys/extattr.h> +#include <sys/imgact.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/proc.h> +#include <sys/sbuf.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/file.h> +#include <sys/namei.h> +#include <sys/sysctl.h> + +#include <vm/vm.h> +#include <vm/pmap.h> +#include <vm/vm_map.h> +#include <vm/vm_object.h> + +#include <fs/devfs/devfs.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +/* + * Warn about EA transactions only the first time they happen. No locking on + * this variable. + */ +static int ea_warn_once = 0; + +static int mac_vnode_setlabel_extattr(struct ucred *cred, + struct vnode *vp, struct label *intlabel); + +static struct label * +mac_devfs_label_alloc(void) +{ + struct label *label; + + label = mac_labelzone_alloc(M_WAITOK); + MAC_PERFORM(devfs_init_label, label); + return (label); +} + +void +mac_devfs_init(struct devfs_dirent *de) +{ + + if (mac_labeled & MPC_OBJECT_DEVFS) + de->de_label = mac_devfs_label_alloc(); + else + de->de_label = NULL; +} + +static struct label * +mac_mount_label_alloc(void) +{ + struct label *label; + + label = mac_labelzone_alloc(M_WAITOK); + MAC_PERFORM(mount_init_label, label); + return (label); +} + +void +mac_mount_init(struct mount *mp) +{ + + if (mac_labeled & MPC_OBJECT_MOUNT) + mp->mnt_label = mac_mount_label_alloc(); + else + mp->mnt_label = NULL; +} + +struct label * +mac_vnode_label_alloc(void) +{ + struct label *label; + + label = mac_labelzone_alloc(M_WAITOK); + MAC_PERFORM(vnode_init_label, label); + return (label); +} + +void +mac_vnode_init(struct vnode *vp) +{ + + if (mac_labeled & MPC_OBJECT_VNODE) + vp->v_label = mac_vnode_label_alloc(); + else + vp->v_label = NULL; +} + +static void +mac_devfs_label_free(struct label *label) +{ + + MAC_PERFORM(devfs_destroy_label, label); + mac_labelzone_free(label); +} + +void +mac_devfs_destroy(struct devfs_dirent *de) +{ + + if (de->de_label != NULL) { + mac_devfs_label_free(de->de_label); + de->de_label = NULL; + } +} + +static void +mac_mount_label_free(struct label *label) +{ + + MAC_PERFORM(mount_destroy_label, label); + mac_labelzone_free(label); +} + +void +mac_mount_destroy(struct mount *mp) +{ + + if (mp->mnt_label != NULL) { + mac_mount_label_free(mp->mnt_label); + mp->mnt_label = NULL; + } +} + +void +mac_vnode_label_free(struct label *label) +{ + + MAC_PERFORM(vnode_destroy_label, label); + mac_labelzone_free(label); +} + +void +mac_vnode_destroy(struct vnode *vp) +{ + + if (vp->v_label != NULL) { + mac_vnode_label_free(vp->v_label); + vp->v_label = NULL; + } +} + +void +mac_vnode_copy_label(struct label *src, struct label *dest) +{ + + MAC_PERFORM(vnode_copy_label, src, dest); +} + +int +mac_vnode_externalize_label(struct label *label, char *elements, + char *outbuf, size_t outbuflen) +{ + int error; + + MAC_EXTERNALIZE(vnode, label, elements, outbuf, outbuflen); + + return (error); +} + +int +mac_vnode_internalize_label(struct label *label, char *string) +{ + int error; + + MAC_INTERNALIZE(vnode, label, string); + + return (error); +} + +void +mac_devfs_update(struct mount *mp, struct devfs_dirent *de, struct vnode *vp) +{ + + MAC_PERFORM(devfs_update, mp, de, de->de_label, vp, vp->v_label); +} + +void +mac_devfs_vnode_associate(struct mount *mp, struct devfs_dirent *de, + struct vnode *vp) +{ + + MAC_PERFORM(devfs_vnode_associate, mp, mp->mnt_label, de, + de->de_label, vp, vp->v_label); +} + +int +mac_vnode_associate_extattr(struct mount *mp, struct vnode *vp) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_associate_extattr"); + + MAC_CHECK(vnode_associate_extattr, mp, mp->mnt_label, vp, + vp->v_label); + + return (error); +} + +void +mac_vnode_associate_singlelabel(struct mount *mp, struct vnode *vp) +{ + + MAC_PERFORM(vnode_associate_singlelabel, mp, mp->mnt_label, vp, + vp->v_label); +} + +/* + * Functions implementing extended-attribute backed labels for file systems + * that support it. + * + * Where possible, we use EA transactions to make writes to multiple + * attributes across difference policies mutually atomic. We allow work to + * continue on file systems not supporting EA transactions, but generate a + * printf warning. + */ +int +mac_vnode_create_extattr(struct ucred *cred, struct mount *mp, + struct vnode *dvp, struct vnode *vp, struct componentname *cnp) +{ + int error; + + ASSERT_VOP_LOCKED(dvp, "mac_vnode_create_extattr"); + ASSERT_VOP_LOCKED(vp, "mac_vnode_create_extattr"); + + error = VOP_OPENEXTATTR(vp, cred, curthread); + if (error == EOPNOTSUPP) { + if (ea_warn_once == 0) { + printf("Warning: transactions not supported " + "in EA write.\n"); + ea_warn_once = 1; + } + } else if (error) + return (error); + + MAC_CHECK(vnode_create_extattr, cred, mp, mp->mnt_label, dvp, + dvp->v_label, vp, vp->v_label, cnp); + + if (error) { + VOP_CLOSEEXTATTR(vp, 0, NOCRED, curthread); + return (error); + } + + error = VOP_CLOSEEXTATTR(vp, 1, NOCRED, curthread); + if (error == EOPNOTSUPP) + error = 0; + + return (error); +} + +static int +mac_vnode_setlabel_extattr(struct ucred *cred, struct vnode *vp, + struct label *intlabel) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_setlabel_extattr"); + + error = VOP_OPENEXTATTR(vp, cred, curthread); + if (error == EOPNOTSUPP) { + if (ea_warn_once == 0) { + printf("Warning: transactions not supported " + "in EA write.\n"); + ea_warn_once = 1; + } + } else if (error) + return (error); + + MAC_CHECK(vnode_setlabel_extattr, cred, vp, vp->v_label, intlabel); + + if (error) { + VOP_CLOSEEXTATTR(vp, 0, NOCRED, curthread); + return (error); + } + + error = VOP_CLOSEEXTATTR(vp, 1, NOCRED, curthread); + if (error == EOPNOTSUPP) + error = 0; + + return (error); +} + +void +mac_vnode_execve_transition(struct ucred *old, struct ucred *new, + struct vnode *vp, struct label *interpvplabel, struct image_params *imgp) +{ + + ASSERT_VOP_LOCKED(vp, "mac_vnode_execve_transition"); + + MAC_PERFORM(vnode_execve_transition, old, new, vp, vp->v_label, + interpvplabel, imgp, imgp->execlabel); +} + +int +mac_vnode_execve_will_transition(struct ucred *old, struct vnode *vp, + struct label *interpvplabel, struct image_params *imgp) +{ + int result; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_execve_will_transition"); + + result = 0; + MAC_BOOLEAN(vnode_execve_will_transition, ||, old, vp, vp->v_label, + interpvplabel, imgp, imgp->execlabel); + + return (result); +} + +int +mac_vnode_check_access(struct ucred *cred, struct vnode *vp, accmode_t accmode) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_access"); + + MAC_CHECK(vnode_check_access, cred, vp, vp->v_label, accmode); + return (error); +} + +int +mac_vnode_check_chdir(struct ucred *cred, struct vnode *dvp) +{ + int error; + + ASSERT_VOP_LOCKED(dvp, "mac_vnode_check_chdir"); + + MAC_CHECK(vnode_check_chdir, cred, dvp, dvp->v_label); + return (error); +} + +int +mac_vnode_check_chroot(struct ucred *cred, struct vnode *dvp) +{ + int error; + + ASSERT_VOP_LOCKED(dvp, "mac_vnode_check_chroot"); + + MAC_CHECK(vnode_check_chroot, cred, dvp, dvp->v_label); + return (error); +} + +int +mac_vnode_check_create(struct ucred *cred, struct vnode *dvp, + struct componentname *cnp, struct vattr *vap) +{ + int error; + + ASSERT_VOP_LOCKED(dvp, "mac_vnode_check_create"); + + MAC_CHECK(vnode_check_create, cred, dvp, dvp->v_label, cnp, vap); + return (error); +} + +int +mac_vnode_check_deleteacl(struct ucred *cred, struct vnode *vp, + acl_type_t type) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_deleteacl"); + + MAC_CHECK(vnode_check_deleteacl, cred, vp, vp->v_label, type); + return (error); +} + +int +mac_vnode_check_deleteextattr(struct ucred *cred, struct vnode *vp, + int attrnamespace, const char *name) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_deleteextattr"); + + MAC_CHECK(vnode_check_deleteextattr, cred, vp, vp->v_label, + attrnamespace, name); + return (error); +} + +int +mac_vnode_check_exec(struct ucred *cred, struct vnode *vp, + struct image_params *imgp) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_exec"); + + MAC_CHECK(vnode_check_exec, cred, vp, vp->v_label, imgp, + imgp->execlabel); + + return (error); +} + +int +mac_vnode_check_getacl(struct ucred *cred, struct vnode *vp, acl_type_t type) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_getacl"); + + MAC_CHECK(vnode_check_getacl, cred, vp, vp->v_label, type); + return (error); +} + +int +mac_vnode_check_getextattr(struct ucred *cred, struct vnode *vp, + int attrnamespace, const char *name, struct uio *uio) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_getextattr"); + + MAC_CHECK(vnode_check_getextattr, cred, vp, vp->v_label, + attrnamespace, name, uio); + return (error); +} + +int +mac_vnode_check_link(struct ucred *cred, struct vnode *dvp, + struct vnode *vp, struct componentname *cnp) +{ + int error; + + ASSERT_VOP_LOCKED(dvp, "mac_vnode_check_link"); + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_link"); + + MAC_CHECK(vnode_check_link, cred, dvp, dvp->v_label, vp, + vp->v_label, cnp); + return (error); +} + +int +mac_vnode_check_listextattr(struct ucred *cred, struct vnode *vp, + int attrnamespace) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_listextattr"); + + MAC_CHECK(vnode_check_listextattr, cred, vp, vp->v_label, + attrnamespace); + return (error); +} + +int +mac_vnode_check_lookup(struct ucred *cred, struct vnode *dvp, + struct componentname *cnp) +{ + int error; + + ASSERT_VOP_LOCKED(dvp, "mac_vnode_check_lookup"); + + MAC_CHECK(vnode_check_lookup, cred, dvp, dvp->v_label, cnp); + return (error); +} + +int +mac_vnode_check_mmap(struct ucred *cred, struct vnode *vp, int prot, + int flags) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_mmap"); + + MAC_CHECK(vnode_check_mmap, cred, vp, vp->v_label, prot, flags); + return (error); +} + +void +mac_vnode_check_mmap_downgrade(struct ucred *cred, struct vnode *vp, + int *prot) +{ + int result = *prot; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_mmap_downgrade"); + + MAC_PERFORM(vnode_check_mmap_downgrade, cred, vp, vp->v_label, + &result); + + *prot = result; +} + +int +mac_vnode_check_mprotect(struct ucred *cred, struct vnode *vp, int prot) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_mprotect"); + + MAC_CHECK(vnode_check_mprotect, cred, vp, vp->v_label, prot); + return (error); +} + +int +mac_vnode_check_open(struct ucred *cred, struct vnode *vp, accmode_t accmode) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_open"); + + MAC_CHECK(vnode_check_open, cred, vp, vp->v_label, accmode); + return (error); +} + +int +mac_vnode_check_poll(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_poll"); + + MAC_CHECK(vnode_check_poll, active_cred, file_cred, vp, + vp->v_label); + + return (error); +} + +int +mac_vnode_check_read(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_read"); + + MAC_CHECK(vnode_check_read, active_cred, file_cred, vp, + vp->v_label); + + return (error); +} + +int +mac_vnode_check_readdir(struct ucred *cred, struct vnode *dvp) +{ + int error; + + ASSERT_VOP_LOCKED(dvp, "mac_vnode_check_readdir"); + + MAC_CHECK(vnode_check_readdir, cred, dvp, dvp->v_label); + return (error); +} + +int +mac_vnode_check_readlink(struct ucred *cred, struct vnode *vp) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_readlink"); + + MAC_CHECK(vnode_check_readlink, cred, vp, vp->v_label); + return (error); +} + +static int +mac_vnode_check_relabel(struct ucred *cred, struct vnode *vp, + struct label *newlabel) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_relabel"); + + MAC_CHECK(vnode_check_relabel, cred, vp, vp->v_label, newlabel); + + return (error); +} + +int +mac_vnode_check_rename_from(struct ucred *cred, struct vnode *dvp, + struct vnode *vp, struct componentname *cnp) +{ + int error; + + ASSERT_VOP_LOCKED(dvp, "mac_vnode_check_rename_from"); + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_rename_from"); + + MAC_CHECK(vnode_check_rename_from, cred, dvp, dvp->v_label, vp, + vp->v_label, cnp); + return (error); +} + +int +mac_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp, + struct vnode *vp, int samedir, struct componentname *cnp) +{ + int error; + + ASSERT_VOP_LOCKED(dvp, "mac_vnode_check_rename_to"); + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_rename_to"); + + MAC_CHECK(vnode_check_rename_to, cred, dvp, dvp->v_label, vp, + vp != NULL ? vp->v_label : NULL, samedir, cnp); + return (error); +} + +int +mac_vnode_check_revoke(struct ucred *cred, struct vnode *vp) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_revoke"); + + MAC_CHECK(vnode_check_revoke, cred, vp, vp->v_label); + return (error); +} + +int +mac_vnode_check_setacl(struct ucred *cred, struct vnode *vp, acl_type_t type, + struct acl *acl) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_setacl"); + + MAC_CHECK(vnode_check_setacl, cred, vp, vp->v_label, type, acl); + return (error); +} + +int +mac_vnode_check_setextattr(struct ucred *cred, struct vnode *vp, + int attrnamespace, const char *name, struct uio *uio) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_setextattr"); + + MAC_CHECK(vnode_check_setextattr, cred, vp, vp->v_label, + attrnamespace, name, uio); + return (error); +} + +int +mac_vnode_check_setflags(struct ucred *cred, struct vnode *vp, u_long flags) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_setflags"); + + MAC_CHECK(vnode_check_setflags, cred, vp, vp->v_label, flags); + return (error); +} + +int +mac_vnode_check_setmode(struct ucred *cred, struct vnode *vp, mode_t mode) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_setmode"); + + MAC_CHECK(vnode_check_setmode, cred, vp, vp->v_label, mode); + return (error); +} + +int +mac_vnode_check_setowner(struct ucred *cred, struct vnode *vp, uid_t uid, + gid_t gid) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_setowner"); + + MAC_CHECK(vnode_check_setowner, cred, vp, vp->v_label, uid, gid); + return (error); +} + +int +mac_vnode_check_setutimes(struct ucred *cred, struct vnode *vp, + struct timespec atime, struct timespec mtime) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_setutimes"); + + MAC_CHECK(vnode_check_setutimes, cred, vp, vp->v_label, atime, + mtime); + return (error); +} + +int +mac_vnode_check_stat(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_stat"); + + MAC_CHECK(vnode_check_stat, active_cred, file_cred, vp, + vp->v_label); + return (error); +} + +int +mac_vnode_check_unlink(struct ucred *cred, struct vnode *dvp, + struct vnode *vp, struct componentname *cnp) +{ + int error; + + ASSERT_VOP_LOCKED(dvp, "mac_vnode_check_unlink"); + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_unlink"); + + MAC_CHECK(vnode_check_unlink, cred, dvp, dvp->v_label, vp, + vp->v_label, cnp); + return (error); +} + +int +mac_vnode_check_write(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_write"); + + MAC_CHECK(vnode_check_write, active_cred, file_cred, vp, + vp->v_label); + + return (error); +} + +void +mac_vnode_relabel(struct ucred *cred, struct vnode *vp, + struct label *newlabel) +{ + + MAC_PERFORM(vnode_relabel, cred, vp, vp->v_label, newlabel); +} + +void +mac_mount_create(struct ucred *cred, struct mount *mp) +{ + + MAC_PERFORM(mount_create, cred, mp, mp->mnt_label); +} + +int +mac_mount_check_stat(struct ucred *cred, struct mount *mount) +{ + int error; + + MAC_CHECK(mount_check_stat, cred, mount, mount->mnt_label); + + return (error); +} + +void +mac_devfs_create_device(struct ucred *cred, struct mount *mp, + struct cdev *dev, struct devfs_dirent *de) +{ + + MAC_PERFORM(devfs_create_device, cred, mp, dev, de, de->de_label); +} + +void +mac_devfs_create_symlink(struct ucred *cred, struct mount *mp, + struct devfs_dirent *dd, struct devfs_dirent *de) +{ + + MAC_PERFORM(devfs_create_symlink, cred, mp, dd, dd->de_label, de, + de->de_label); +} + +void +mac_devfs_create_directory(struct mount *mp, char *dirname, int dirnamelen, + struct devfs_dirent *de) +{ + + MAC_PERFORM(devfs_create_directory, mp, dirname, dirnamelen, de, + de->de_label); +} + +/* + * Implementation of VOP_SETLABEL() that relies on extended attributes to + * store label data. Can be referenced by filesystems supporting extended + * attributes. + */ +int +vop_stdsetlabel_ea(struct vop_setlabel_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct label *intlabel = ap->a_label; + int error; + + ASSERT_VOP_LOCKED(vp, "vop_stdsetlabel_ea"); + + if ((vp->v_mount->mnt_flag & MNT_MULTILABEL) == 0) + return (EOPNOTSUPP); + + error = mac_vnode_setlabel_extattr(ap->a_cred, vp, intlabel); + if (error) + return (error); + + mac_vnode_relabel(ap->a_cred, vp, intlabel); + + return (0); +} + +int +vn_setlabel(struct vnode *vp, struct label *intlabel, struct ucred *cred) +{ + int error; + + if (vp->v_mount == NULL) { + /* printf("vn_setlabel: null v_mount\n"); */ + if (vp->v_type != VNON) + printf("vn_setlabel: null v_mount with non-VNON\n"); + return (EBADF); + } + + if ((vp->v_mount->mnt_flag & MNT_MULTILABEL) == 0) + return (EOPNOTSUPP); + + /* + * Multi-phase commit. First check the policies to confirm the + * change is OK. Then commit via the filesystem. Finally, update + * the actual vnode label. + * + * Question: maybe the filesystem should update the vnode at the end + * as part of VOP_SETLABEL()? + */ + error = mac_vnode_check_relabel(cred, vp, intlabel); + if (error) + return (error); + + /* + * VADMIN provides the opportunity for the filesystem to make + * decisions about who is and is not able to modify labels and + * protections on files. This might not be right. We can't assume + * VOP_SETLABEL() will do it, because we might implement that as part + * of vop_stdsetlabel_ea(). + */ + error = VOP_ACCESS(vp, VADMIN, cred, curthread); + if (error) + return (error); + + error = VOP_SETLABEL(vp, intlabel, cred, curthread); + if (error) + return (error); + + return (0); +} diff --git a/sys/security/mac_biba/mac_biba.c b/sys/security/mac_biba/mac_biba.c new file mode 100644 index 0000000..4e10f27 --- /dev/null +++ b/sys/security/mac_biba/mac_biba.c @@ -0,0 +1,3569 @@ +/*- + * Copyright (c) 1999-2002, 2007-2008 Robert N. M. Watson + * Copyright (c) 2001-2005 McAfee, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by McAfee + * Research, the Security Research Division of McAfee, Inc. under + * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA + * CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Developed by the TrustedBSD Project. + * + * Biba fixed label mandatory integrity policy. + */ + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/extattr.h> +#include <sys/kernel.h> +#include <sys/ksem.h> +#include <sys/malloc.h> +#include <sys/mman.h> +#include <sys/mount.h> +#include <sys/priv.h> +#include <sys/proc.h> +#include <sys/sbuf.h> +#include <sys/systm.h> +#include <sys/sysproto.h> +#include <sys/sysent.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/pipe.h> +#include <sys/sx.h> +#include <sys/sysctl.h> +#include <sys/msg.h> +#include <sys/sem.h> +#include <sys/shm.h> + +#include <fs/devfs/devfs.h> + +#include <net/bpfdesc.h> +#include <net/if.h> +#include <net/if_types.h> +#include <net/if_var.h> + +#include <netinet/in.h> +#include <netinet/in_pcb.h> +#include <netinet/ip_var.h> + +#include <vm/uma.h> +#include <vm/vm.h> + +#include <security/mac/mac_policy.h> +#include <security/mac_biba/mac_biba.h> + +SYSCTL_DECL(_security_mac); + +SYSCTL_NODE(_security_mac, OID_AUTO, biba, CTLFLAG_RW, 0, + "TrustedBSD mac_biba policy controls"); + +static int biba_label_size = sizeof(struct mac_biba); +SYSCTL_INT(_security_mac_biba, OID_AUTO, label_size, CTLFLAG_RD, + &biba_label_size, 0, "Size of struct mac_biba"); + +static int biba_enabled = 1; +SYSCTL_INT(_security_mac_biba, OID_AUTO, enabled, CTLFLAG_RW, &biba_enabled, + 0, "Enforce MAC/Biba policy"); +TUNABLE_INT("security.mac.biba.enabled", &biba_enabled); + +static int destroyed_not_inited; +SYSCTL_INT(_security_mac_biba, OID_AUTO, destroyed_not_inited, CTLFLAG_RD, + &destroyed_not_inited, 0, "Count of labels destroyed but not inited"); + +static int trust_all_interfaces = 0; +SYSCTL_INT(_security_mac_biba, OID_AUTO, trust_all_interfaces, CTLFLAG_RD, + &trust_all_interfaces, 0, "Consider all interfaces 'trusted' by MAC/Biba"); +TUNABLE_INT("security.mac.biba.trust_all_interfaces", &trust_all_interfaces); + +static char trusted_interfaces[128]; +SYSCTL_STRING(_security_mac_biba, OID_AUTO, trusted_interfaces, CTLFLAG_RD, + trusted_interfaces, 0, "Interfaces considered 'trusted' by MAC/Biba"); +TUNABLE_STR("security.mac.biba.trusted_interfaces", trusted_interfaces, + sizeof(trusted_interfaces)); + +static int max_compartments = MAC_BIBA_MAX_COMPARTMENTS; +SYSCTL_INT(_security_mac_biba, OID_AUTO, max_compartments, CTLFLAG_RD, + &max_compartments, 0, "Maximum supported compartments"); + +static int ptys_equal = 0; +SYSCTL_INT(_security_mac_biba, OID_AUTO, ptys_equal, CTLFLAG_RW, &ptys_equal, + 0, "Label pty devices as biba/equal on create"); +TUNABLE_INT("security.mac.biba.ptys_equal", &ptys_equal); + +static int interfaces_equal; +SYSCTL_INT(_security_mac_biba, OID_AUTO, interfaces_equal, CTLFLAG_RW, + &interfaces_equal, 0, "Label network interfaces as biba/equal on create"); +TUNABLE_INT("security.mac.biba.interfaces_equal", &interfaces_equal); + +static int revocation_enabled = 0; +SYSCTL_INT(_security_mac_biba, OID_AUTO, revocation_enabled, CTLFLAG_RW, + &revocation_enabled, 0, "Revoke access to objects on relabel"); +TUNABLE_INT("security.mac.biba.revocation_enabled", &revocation_enabled); + +static int biba_slot; +#define SLOT(l) ((struct mac_biba *)mac_label_get((l), biba_slot)) +#define SLOT_SET(l, val) mac_label_set((l), biba_slot, (uintptr_t)(val)) + +static uma_zone_t zone_biba; + +static __inline int +biba_bit_set_empty(u_char *set) { + int i; + + for (i = 0; i < MAC_BIBA_MAX_COMPARTMENTS >> 3; i++) + if (set[i] != 0) + return (0); + return (1); +} + +static struct mac_biba * +biba_alloc(int flag) +{ + + return (uma_zalloc(zone_biba, flag | M_ZERO)); +} + +static void +biba_free(struct mac_biba *mb) +{ + + if (mb != NULL) + uma_zfree(zone_biba, mb); + else + atomic_add_int(&destroyed_not_inited, 1); +} + +static int +biba_atmostflags(struct mac_biba *mb, int flags) +{ + + if ((mb->mb_flags & flags) != mb->mb_flags) + return (EINVAL); + return (0); +} + +static int +biba_dominate_element(struct mac_biba_element *a, struct mac_biba_element *b) +{ + int bit; + + switch (a->mbe_type) { + case MAC_BIBA_TYPE_EQUAL: + case MAC_BIBA_TYPE_HIGH: + return (1); + + case MAC_BIBA_TYPE_LOW: + switch (b->mbe_type) { + case MAC_BIBA_TYPE_GRADE: + case MAC_BIBA_TYPE_HIGH: + return (0); + + case MAC_BIBA_TYPE_EQUAL: + case MAC_BIBA_TYPE_LOW: + return (1); + + default: + panic("biba_dominate_element: b->mbe_type invalid"); + } + + case MAC_BIBA_TYPE_GRADE: + switch (b->mbe_type) { + case MAC_BIBA_TYPE_EQUAL: + case MAC_BIBA_TYPE_LOW: + return (1); + + case MAC_BIBA_TYPE_HIGH: + return (0); + + case MAC_BIBA_TYPE_GRADE: + for (bit = 1; bit <= MAC_BIBA_MAX_COMPARTMENTS; bit++) + if (!MAC_BIBA_BIT_TEST(bit, + a->mbe_compartments) && + MAC_BIBA_BIT_TEST(bit, b->mbe_compartments)) + return (0); + return (a->mbe_grade >= b->mbe_grade); + + default: + panic("biba_dominate_element: b->mbe_type invalid"); + } + + default: + panic("biba_dominate_element: a->mbe_type invalid"); + } + + return (0); +} + +static int +biba_subject_dominate_high(struct mac_biba *mb) +{ + struct mac_biba_element *element; + + KASSERT((mb->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, + ("biba_effective_in_range: mb not effective")); + element = &mb->mb_effective; + + return (element->mbe_type == MAC_BIBA_TYPE_EQUAL || + element->mbe_type == MAC_BIBA_TYPE_HIGH); +} + +static int +biba_range_in_range(struct mac_biba *rangea, struct mac_biba *rangeb) +{ + + return (biba_dominate_element(&rangeb->mb_rangehigh, + &rangea->mb_rangehigh) && + biba_dominate_element(&rangea->mb_rangelow, + &rangeb->mb_rangelow)); +} + +static int +biba_effective_in_range(struct mac_biba *effective, struct mac_biba *range) +{ + + KASSERT((effective->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, + ("biba_effective_in_range: a not effective")); + KASSERT((range->mb_flags & MAC_BIBA_FLAG_RANGE) != 0, + ("biba_effective_in_range: b not range")); + + return (biba_dominate_element(&range->mb_rangehigh, + &effective->mb_effective) && + biba_dominate_element(&effective->mb_effective, + &range->mb_rangelow)); + + return (1); +} + +static int +biba_dominate_effective(struct mac_biba *a, struct mac_biba *b) +{ + KASSERT((a->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, + ("biba_dominate_effective: a not effective")); + KASSERT((b->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, + ("biba_dominate_effective: b not effective")); + + return (biba_dominate_element(&a->mb_effective, &b->mb_effective)); +} + +static int +biba_equal_element(struct mac_biba_element *a, struct mac_biba_element *b) +{ + + if (a->mbe_type == MAC_BIBA_TYPE_EQUAL || + b->mbe_type == MAC_BIBA_TYPE_EQUAL) + return (1); + + return (a->mbe_type == b->mbe_type && a->mbe_grade == b->mbe_grade); +} + +static int +biba_equal_effective(struct mac_biba *a, struct mac_biba *b) +{ + + KASSERT((a->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, + ("biba_equal_effective: a not effective")); + KASSERT((b->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, + ("biba_equal_effective: b not effective")); + + return (biba_equal_element(&a->mb_effective, &b->mb_effective)); +} + +static int +biba_contains_equal(struct mac_biba *mb) +{ + + if (mb->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { + if (mb->mb_effective.mbe_type == MAC_BIBA_TYPE_EQUAL) + return (1); + } + + if (mb->mb_flags & MAC_BIBA_FLAG_RANGE) { + if (mb->mb_rangelow.mbe_type == MAC_BIBA_TYPE_EQUAL) + return (1); + if (mb->mb_rangehigh.mbe_type == MAC_BIBA_TYPE_EQUAL) + return (1); + } + + return (0); +} + +static int +biba_subject_privileged(struct mac_biba *mb) +{ + + KASSERT((mb->mb_flags & MAC_BIBA_FLAGS_BOTH) == MAC_BIBA_FLAGS_BOTH, + ("biba_subject_privileged: subject doesn't have both labels")); + + /* If the effective is EQUAL, it's ok. */ + if (mb->mb_effective.mbe_type == MAC_BIBA_TYPE_EQUAL) + return (0); + + /* If either range endpoint is EQUAL, it's ok. */ + if (mb->mb_rangelow.mbe_type == MAC_BIBA_TYPE_EQUAL || + mb->mb_rangehigh.mbe_type == MAC_BIBA_TYPE_EQUAL) + return (0); + + /* If the range is low-high, it's ok. */ + if (mb->mb_rangelow.mbe_type == MAC_BIBA_TYPE_LOW && + mb->mb_rangehigh.mbe_type == MAC_BIBA_TYPE_HIGH) + return (0); + + /* It's not ok. */ + return (EPERM); +} + +static int +biba_high_effective(struct mac_biba *mb) +{ + + KASSERT((mb->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, + ("biba_equal_effective: mb not effective")); + + return (mb->mb_effective.mbe_type == MAC_BIBA_TYPE_HIGH); +} + +static int +biba_valid(struct mac_biba *mb) +{ + + if (mb->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { + switch (mb->mb_effective.mbe_type) { + case MAC_BIBA_TYPE_GRADE: + break; + + case MAC_BIBA_TYPE_EQUAL: + case MAC_BIBA_TYPE_HIGH: + case MAC_BIBA_TYPE_LOW: + if (mb->mb_effective.mbe_grade != 0 || + !MAC_BIBA_BIT_SET_EMPTY( + mb->mb_effective.mbe_compartments)) + return (EINVAL); + break; + + default: + return (EINVAL); + } + } else { + if (mb->mb_effective.mbe_type != MAC_BIBA_TYPE_UNDEF) + return (EINVAL); + } + + if (mb->mb_flags & MAC_BIBA_FLAG_RANGE) { + switch (mb->mb_rangelow.mbe_type) { + case MAC_BIBA_TYPE_GRADE: + break; + + case MAC_BIBA_TYPE_EQUAL: + case MAC_BIBA_TYPE_HIGH: + case MAC_BIBA_TYPE_LOW: + if (mb->mb_rangelow.mbe_grade != 0 || + !MAC_BIBA_BIT_SET_EMPTY( + mb->mb_rangelow.mbe_compartments)) + return (EINVAL); + break; + + default: + return (EINVAL); + } + + switch (mb->mb_rangehigh.mbe_type) { + case MAC_BIBA_TYPE_GRADE: + break; + + case MAC_BIBA_TYPE_EQUAL: + case MAC_BIBA_TYPE_HIGH: + case MAC_BIBA_TYPE_LOW: + if (mb->mb_rangehigh.mbe_grade != 0 || + !MAC_BIBA_BIT_SET_EMPTY( + mb->mb_rangehigh.mbe_compartments)) + return (EINVAL); + break; + + default: + return (EINVAL); + } + if (!biba_dominate_element(&mb->mb_rangehigh, + &mb->mb_rangelow)) + return (EINVAL); + } else { + if (mb->mb_rangelow.mbe_type != MAC_BIBA_TYPE_UNDEF || + mb->mb_rangehigh.mbe_type != MAC_BIBA_TYPE_UNDEF) + return (EINVAL); + } + + return (0); +} + +static void +biba_set_range(struct mac_biba *mb, u_short typelow, u_short gradelow, + u_char *compartmentslow, u_short typehigh, u_short gradehigh, + u_char *compartmentshigh) +{ + + mb->mb_rangelow.mbe_type = typelow; + mb->mb_rangelow.mbe_grade = gradelow; + if (compartmentslow != NULL) + memcpy(mb->mb_rangelow.mbe_compartments, compartmentslow, + sizeof(mb->mb_rangelow.mbe_compartments)); + mb->mb_rangehigh.mbe_type = typehigh; + mb->mb_rangehigh.mbe_grade = gradehigh; + if (compartmentshigh != NULL) + memcpy(mb->mb_rangehigh.mbe_compartments, compartmentshigh, + sizeof(mb->mb_rangehigh.mbe_compartments)); + mb->mb_flags |= MAC_BIBA_FLAG_RANGE; +} + +static void +biba_set_effective(struct mac_biba *mb, u_short type, u_short grade, + u_char *compartments) +{ + + mb->mb_effective.mbe_type = type; + mb->mb_effective.mbe_grade = grade; + if (compartments != NULL) + memcpy(mb->mb_effective.mbe_compartments, compartments, + sizeof(mb->mb_effective.mbe_compartments)); + mb->mb_flags |= MAC_BIBA_FLAG_EFFECTIVE; +} + +static void +biba_copy_range(struct mac_biba *labelfrom, struct mac_biba *labelto) +{ + + KASSERT((labelfrom->mb_flags & MAC_BIBA_FLAG_RANGE) != 0, + ("biba_copy_range: labelfrom not range")); + + labelto->mb_rangelow = labelfrom->mb_rangelow; + labelto->mb_rangehigh = labelfrom->mb_rangehigh; + labelto->mb_flags |= MAC_BIBA_FLAG_RANGE; +} + +static void +biba_copy_effective(struct mac_biba *labelfrom, struct mac_biba *labelto) +{ + + KASSERT((labelfrom->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, + ("biba_copy_effective: labelfrom not effective")); + + labelto->mb_effective = labelfrom->mb_effective; + labelto->mb_flags |= MAC_BIBA_FLAG_EFFECTIVE; +} + +static void +biba_copy(struct mac_biba *source, struct mac_biba *dest) +{ + + if (source->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) + biba_copy_effective(source, dest); + if (source->mb_flags & MAC_BIBA_FLAG_RANGE) + biba_copy_range(source, dest); +} + +/* + * Policy module operations. + */ +static void +biba_init(struct mac_policy_conf *conf) +{ + + zone_biba = uma_zcreate("mac_biba", sizeof(struct mac_biba), NULL, + NULL, NULL, NULL, UMA_ALIGN_PTR, 0); +} + +/* + * Label operations. + */ +static void +biba_init_label(struct label *label) +{ + + SLOT_SET(label, biba_alloc(M_WAITOK)); +} + +static int +biba_init_label_waitcheck(struct label *label, int flag) +{ + + SLOT_SET(label, biba_alloc(flag)); + if (SLOT(label) == NULL) + return (ENOMEM); + + return (0); +} + +static void +biba_destroy_label(struct label *label) +{ + + biba_free(SLOT(label)); + SLOT_SET(label, NULL); +} + +/* + * biba_element_to_string() accepts an sbuf and Biba element. It converts + * the Biba element to a string and stores the result in the sbuf; if there + * isn't space in the sbuf, -1 is returned. + */ +static int +biba_element_to_string(struct sbuf *sb, struct mac_biba_element *element) +{ + int i, first; + + switch (element->mbe_type) { + case MAC_BIBA_TYPE_HIGH: + return (sbuf_printf(sb, "high")); + + case MAC_BIBA_TYPE_LOW: + return (sbuf_printf(sb, "low")); + + case MAC_BIBA_TYPE_EQUAL: + return (sbuf_printf(sb, "equal")); + + case MAC_BIBA_TYPE_GRADE: + if (sbuf_printf(sb, "%d", element->mbe_grade) == -1) + return (-1); + + first = 1; + for (i = 1; i <= MAC_BIBA_MAX_COMPARTMENTS; i++) { + if (MAC_BIBA_BIT_TEST(i, element->mbe_compartments)) { + if (first) { + if (sbuf_putc(sb, ':') == -1) + return (-1); + if (sbuf_printf(sb, "%d", i) == -1) + return (-1); + first = 0; + } else { + if (sbuf_printf(sb, "+%d", i) == -1) + return (-1); + } + } + } + return (0); + + default: + panic("biba_element_to_string: invalid type (%d)", + element->mbe_type); + } +} + +/* + * biba_to_string() converts a Biba label to a string, and places the results + * in the passed sbuf. It returns 0 on success, or EINVAL if there isn't + * room in the sbuf. Note: the sbuf will be modified even in a failure case, + * so the caller may need to revert the sbuf by restoring the offset if + * that's undesired. + */ +static int +biba_to_string(struct sbuf *sb, struct mac_biba *mb) +{ + + if (mb->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { + if (biba_element_to_string(sb, &mb->mb_effective) == -1) + return (EINVAL); + } + + if (mb->mb_flags & MAC_BIBA_FLAG_RANGE) { + if (sbuf_putc(sb, '(') == -1) + return (EINVAL); + + if (biba_element_to_string(sb, &mb->mb_rangelow) == -1) + return (EINVAL); + + if (sbuf_putc(sb, '-') == -1) + return (EINVAL); + + if (biba_element_to_string(sb, &mb->mb_rangehigh) == -1) + return (EINVAL); + + if (sbuf_putc(sb, ')') == -1) + return (EINVAL); + } + + return (0); +} + +static int +biba_externalize_label(struct label *label, char *element_name, + struct sbuf *sb, int *claimed) +{ + struct mac_biba *mb; + + if (strcmp(MAC_BIBA_LABEL_NAME, element_name) != 0) + return (0); + + (*claimed)++; + + mb = SLOT(label); + return (biba_to_string(sb, mb)); +} + +static int +biba_parse_element(struct mac_biba_element *element, char *string) +{ + char *compartment, *end, *grade; + int value; + + if (strcmp(string, "high") == 0 || strcmp(string, "hi") == 0) { + element->mbe_type = MAC_BIBA_TYPE_HIGH; + element->mbe_grade = MAC_BIBA_TYPE_UNDEF; + } else if (strcmp(string, "low") == 0 || strcmp(string, "lo") == 0) { + element->mbe_type = MAC_BIBA_TYPE_LOW; + element->mbe_grade = MAC_BIBA_TYPE_UNDEF; + } else if (strcmp(string, "equal") == 0 || + strcmp(string, "eq") == 0) { + element->mbe_type = MAC_BIBA_TYPE_EQUAL; + element->mbe_grade = MAC_BIBA_TYPE_UNDEF; + } else { + element->mbe_type = MAC_BIBA_TYPE_GRADE; + + /* + * Numeric grade piece of the element. + */ + grade = strsep(&string, ":"); + value = strtol(grade, &end, 10); + if (end == grade || *end != '\0') + return (EINVAL); + if (value < 0 || value > 65535) + return (EINVAL); + element->mbe_grade = value; + + /* + * Optional compartment piece of the element. If none are + * included, we assume that the label has no compartments. + */ + if (string == NULL) + return (0); + if (*string == '\0') + return (0); + + while ((compartment = strsep(&string, "+")) != NULL) { + value = strtol(compartment, &end, 10); + if (compartment == end || *end != '\0') + return (EINVAL); + if (value < 1 || value > MAC_BIBA_MAX_COMPARTMENTS) + return (EINVAL); + MAC_BIBA_BIT_SET(value, element->mbe_compartments); + } + } + + return (0); +} + +/* + * Note: destructively consumes the string, make a local copy before calling + * if that's a problem. + */ +static int +biba_parse(struct mac_biba *mb, char *string) +{ + char *rangehigh, *rangelow, *effective; + int error; + + effective = strsep(&string, "("); + if (*effective == '\0') + effective = NULL; + + if (string != NULL) { + rangelow = strsep(&string, "-"); + if (string == NULL) + return (EINVAL); + rangehigh = strsep(&string, ")"); + if (string == NULL) + return (EINVAL); + if (*string != '\0') + return (EINVAL); + } else { + rangelow = NULL; + rangehigh = NULL; + } + + KASSERT((rangelow != NULL && rangehigh != NULL) || + (rangelow == NULL && rangehigh == NULL), + ("biba_parse: range mismatch")); + + bzero(mb, sizeof(*mb)); + if (effective != NULL) { + error = biba_parse_element(&mb->mb_effective, effective); + if (error) + return (error); + mb->mb_flags |= MAC_BIBA_FLAG_EFFECTIVE; + } + + if (rangelow != NULL) { + error = biba_parse_element(&mb->mb_rangelow, rangelow); + if (error) + return (error); + error = biba_parse_element(&mb->mb_rangehigh, rangehigh); + if (error) + return (error); + mb->mb_flags |= MAC_BIBA_FLAG_RANGE; + } + + error = biba_valid(mb); + if (error) + return (error); + + return (0); +} + +static int +biba_internalize_label(struct label *label, char *element_name, + char *element_data, int *claimed) +{ + struct mac_biba *mb, mb_temp; + int error; + + if (strcmp(MAC_BIBA_LABEL_NAME, element_name) != 0) + return (0); + + (*claimed)++; + + error = biba_parse(&mb_temp, element_data); + if (error) + return (error); + + mb = SLOT(label); + *mb = mb_temp; + + return (0); +} + +static void +biba_copy_label(struct label *src, struct label *dest) +{ + + *SLOT(dest) = *SLOT(src); +} + +/* + * Object-specific entry point implementations are sorted alphabetically by + * object type name and then by operation. + */ +static int +biba_bpfdesc_check_receive(struct bpf_d *d, struct label *dlabel, + struct ifnet *ifp, struct label *ifplabel) +{ + struct mac_biba *a, *b; + + if (!biba_enabled) + return (0); + + a = SLOT(dlabel); + b = SLOT(ifplabel); + + if (biba_equal_effective(a, b)) + return (0); + return (EACCES); +} + +static void +biba_bpfdesc_create(struct ucred *cred, struct bpf_d *d, + struct label *dlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(dlabel); + + biba_copy_effective(source, dest); +} + +static void +biba_bpfdesc_create_mbuf(struct bpf_d *d, struct label *dlabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(dlabel); + dest = SLOT(mlabel); + + biba_copy_effective(source, dest); +} + +static void +biba_cred_associate_nfsd(struct ucred *cred) +{ + struct mac_biba *label; + + label = SLOT(cred->cr_label); + biba_set_effective(label, MAC_BIBA_TYPE_LOW, 0, NULL); + biba_set_range(label, MAC_BIBA_TYPE_LOW, 0, NULL, MAC_BIBA_TYPE_HIGH, + 0, NULL); +} + +static int +biba_cred_check_relabel(struct ucred *cred, struct label *newlabel) +{ + struct mac_biba *subj, *new; + int error; + + subj = SLOT(cred->cr_label); + new = SLOT(newlabel); + + /* + * If there is a Biba label update for the credential, it may + * be an update of the effective, range, or both. + */ + error = biba_atmostflags(new, MAC_BIBA_FLAGS_BOTH); + if (error) + return (error); + + /* + * If the Biba label is to be changed, authorize as appropriate. + */ + if (new->mb_flags & MAC_BIBA_FLAGS_BOTH) { + /* + * If the change request modifies both the Biba label + * effective and range, check that the new effective will be + * in the new range. + */ + if ((new->mb_flags & MAC_BIBA_FLAGS_BOTH) == + MAC_BIBA_FLAGS_BOTH && + !biba_effective_in_range(new, new)) + return (EINVAL); + + /* + * To change the Biba effective label on a credential, the + * new effective label must be in the current range. + */ + if (new->mb_flags & MAC_BIBA_FLAG_EFFECTIVE && + !biba_effective_in_range(new, subj)) + return (EPERM); + + /* + * To change the Biba range on a credential, the new range + * label must be in the current range. + */ + if (new->mb_flags & MAC_BIBA_FLAG_RANGE && + !biba_range_in_range(new, subj)) + return (EPERM); + + /* + * To have EQUAL in any component of the new credential Biba + * label, the subject must already have EQUAL in their label. + */ + if (biba_contains_equal(new)) { + error = biba_subject_privileged(subj); + if (error) + return (error); + } + } + + return (0); +} + +static int +biba_cred_check_visible(struct ucred *u1, struct ucred *u2) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(u1->cr_label); + obj = SLOT(u2->cr_label); + + /* XXX: range */ + if (!biba_dominate_effective(obj, subj)) + return (ESRCH); + + return (0); +} + +static void +biba_cred_create_init(struct ucred *cred) +{ + struct mac_biba *dest; + + dest = SLOT(cred->cr_label); + + biba_set_effective(dest, MAC_BIBA_TYPE_HIGH, 0, NULL); + biba_set_range(dest, MAC_BIBA_TYPE_LOW, 0, NULL, MAC_BIBA_TYPE_HIGH, + 0, NULL); +} + +static void +biba_cred_create_swapper(struct ucred *cred) +{ + struct mac_biba *dest; + + dest = SLOT(cred->cr_label); + + biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); + biba_set_range(dest, MAC_BIBA_TYPE_LOW, 0, NULL, MAC_BIBA_TYPE_HIGH, + 0, NULL); +} + +static void +biba_cred_relabel(struct ucred *cred, struct label *newlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(newlabel); + dest = SLOT(cred->cr_label); + + biba_copy(source, dest); +} + +static void +biba_devfs_create_device(struct ucred *cred, struct mount *mp, + struct cdev *dev, struct devfs_dirent *de, struct label *delabel) +{ + struct mac_biba *mb; + int biba_type; + + mb = SLOT(delabel); + if (strcmp(dev->si_name, "null") == 0 || + strcmp(dev->si_name, "zero") == 0 || + strcmp(dev->si_name, "random") == 0 || + strncmp(dev->si_name, "fd/", strlen("fd/")) == 0) + biba_type = MAC_BIBA_TYPE_EQUAL; + else if (ptys_equal && + (strncmp(dev->si_name, "ttyp", strlen("ttyp")) == 0 || + strncmp(dev->si_name, "ptyp", strlen("ptyp")) == 0)) + biba_type = MAC_BIBA_TYPE_EQUAL; + else + biba_type = MAC_BIBA_TYPE_HIGH; + biba_set_effective(mb, biba_type, 0, NULL); +} + +static void +biba_devfs_create_directory(struct mount *mp, char *dirname, int dirnamelen, + struct devfs_dirent *de, struct label *delabel) +{ + struct mac_biba *mb; + + mb = SLOT(delabel); + + biba_set_effective(mb, MAC_BIBA_TYPE_HIGH, 0, NULL); +} + +static void +biba_devfs_create_symlink(struct ucred *cred, struct mount *mp, + struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de, + struct label *delabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(delabel); + + biba_copy_effective(source, dest); +} + +static void +biba_devfs_update(struct mount *mp, struct devfs_dirent *de, + struct label *delabel, struct vnode *vp, struct label *vplabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(vplabel); + dest = SLOT(delabel); + + biba_copy(source, dest); +} + +static void +biba_devfs_vnode_associate(struct mount *mp, struct label *mntlabel, + struct devfs_dirent *de, struct label *delabel, struct vnode *vp, + struct label *vplabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(delabel); + dest = SLOT(vplabel); + + biba_copy_effective(source, dest); +} + +static int +biba_ifnet_check_relabel(struct ucred *cred, struct ifnet *ifp, + struct label *ifplabel, struct label *newlabel) +{ + struct mac_biba *subj, *new; + int error; + + subj = SLOT(cred->cr_label); + new = SLOT(newlabel); + + /* + * If there is a Biba label update for the interface, it may be an + * update of the effective, range, or both. + */ + error = biba_atmostflags(new, MAC_BIBA_FLAGS_BOTH); + if (error) + return (error); + + /* + * Relabling network interfaces requires Biba privilege. + */ + error = biba_subject_privileged(subj); + if (error) + return (error); + + return (0); +} + +static int +biba_ifnet_check_transmit(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_biba *p, *i; + + if (!biba_enabled) + return (0); + + p = SLOT(mlabel); + i = SLOT(ifplabel); + + return (biba_effective_in_range(p, i) ? 0 : EACCES); +} + +static void +biba_ifnet_create(struct ifnet *ifp, struct label *ifplabel) +{ + char tifname[IFNAMSIZ], *p, *q; + char tiflist[sizeof(trusted_interfaces)]; + struct mac_biba *dest; + int len, type; + + dest = SLOT(ifplabel); + + if (ifp->if_type == IFT_LOOP || interfaces_equal != 0) { + type = MAC_BIBA_TYPE_EQUAL; + goto set; + } + + if (trust_all_interfaces) { + type = MAC_BIBA_TYPE_HIGH; + goto set; + } + + type = MAC_BIBA_TYPE_LOW; + + if (trusted_interfaces[0] == '\0' || + !strvalid(trusted_interfaces, sizeof(trusted_interfaces))) + goto set; + + bzero(tiflist, sizeof(tiflist)); + for (p = trusted_interfaces, q = tiflist; *p != '\0'; p++, q++) + if(*p != ' ' && *p != '\t') + *q = *p; + + for (p = q = tiflist;; p++) { + if (*p == ',' || *p == '\0') { + len = p - q; + if (len < IFNAMSIZ) { + bzero(tifname, sizeof(tifname)); + bcopy(q, tifname, len); + if (strcmp(tifname, ifp->if_xname) == 0) { + type = MAC_BIBA_TYPE_HIGH; + break; + } + } else { + *p = '\0'; + printf("mac_biba warning: interface name " + "\"%s\" is too long (must be < %d)\n", + q, IFNAMSIZ); + } + if (*p == '\0') + break; + q = p + 1; + } + } +set: + biba_set_effective(dest, type, 0, NULL); + biba_set_range(dest, type, 0, NULL, type, 0, NULL); +} + +static void +biba_ifnet_create_mbuf(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(ifplabel); + dest = SLOT(mlabel); + + biba_copy_effective(source, dest); +} + +static void +biba_ifnet_relabel(struct ucred *cred, struct ifnet *ifp, + struct label *ifplabel, struct label *newlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(newlabel); + dest = SLOT(ifplabel); + + biba_copy(source, dest); +} + +static int +biba_inpcb_check_deliver(struct inpcb *inp, struct label *inplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_biba *p, *i; + + if (!biba_enabled) + return (0); + + p = SLOT(mlabel); + i = SLOT(inplabel); + + return (biba_equal_effective(p, i) ? 0 : EACCES); +} + +static int +biba_inpcb_check_visible(struct ucred *cred, struct inpcb *inp, + struct label *inplabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(inplabel); + + if (!biba_dominate_effective(obj, subj)) + return (ENOENT); + + return (0); +} + +static void +biba_inpcb_create(struct socket *so, struct label *solabel, + struct inpcb *inp, struct label *inplabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(solabel); + dest = SLOT(inplabel); + + biba_copy_effective(source, dest); +} + +static void +biba_inpcb_create_mbuf(struct inpcb *inp, struct label *inplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(inplabel); + dest = SLOT(mlabel); + + biba_copy_effective(source, dest); +} + +static void +biba_inpcb_sosetlabel(struct socket *so, struct label *solabel, + struct inpcb *inp, struct label *inplabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(solabel); + dest = SLOT(inplabel); + + biba_copy(source, dest); +} + +static void +biba_ip6q_create(struct mbuf *m, struct label *mlabel, struct ip6q *q6, + struct label *q6label) +{ + struct mac_biba *source, *dest; + + source = SLOT(mlabel); + dest = SLOT(q6label); + + biba_copy_effective(source, dest); +} + +static int +biba_ip6q_match(struct mbuf *m, struct label *mlabel, struct ip6q *q6, + struct label *q6label) +{ + struct mac_biba *a, *b; + + a = SLOT(q6label); + b = SLOT(mlabel); + + return (biba_equal_effective(a, b)); +} + +static void +biba_ip6q_reassemble(struct ip6q *q6, struct label *q6label, struct mbuf *m, + struct label *mlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(q6label); + dest = SLOT(mlabel); + + /* Just use the head, since we require them all to match. */ + biba_copy_effective(source, dest); +} + +static void +biba_ip6q_update(struct mbuf *m, struct label *mlabel, struct ip6q *q6, + struct label *q6label) +{ + + /* NOOP: we only accept matching labels, so no need to update */ +} + +static void +biba_ipq_create(struct mbuf *m, struct label *mlabel, struct ipq *q, + struct label *qlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(mlabel); + dest = SLOT(qlabel); + + biba_copy_effective(source, dest); +} + +static int +biba_ipq_match(struct mbuf *m, struct label *mlabel, struct ipq *q, + struct label *qlabel) +{ + struct mac_biba *a, *b; + + a = SLOT(qlabel); + b = SLOT(mlabel); + + return (biba_equal_effective(a, b)); +} + +static void +biba_ipq_reassemble(struct ipq *q, struct label *qlabel, struct mbuf *m, + struct label *mlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(qlabel); + dest = SLOT(mlabel); + + /* Just use the head, since we require them all to match. */ + biba_copy_effective(source, dest); +} + +static void +biba_ipq_update(struct mbuf *m, struct label *mlabel, struct ipq *q, + struct label *qlabel) +{ + + /* NOOP: we only accept matching labels, so no need to update */ +} + +static int +biba_kld_check_load(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + struct mac_biba *subj, *obj; + int error; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + + error = biba_subject_privileged(subj); + if (error) + return (error); + + obj = SLOT(vplabel); + if (!biba_high_effective(obj)) + return (EACCES); + + return (0); +} + +static int +biba_mount_check_stat(struct ucred *cred, struct mount *mp, + struct label *mplabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(mplabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static void +biba_mount_create(struct ucred *cred, struct mount *mp, + struct label *mplabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(mplabel); + + biba_copy_effective(source, dest); +} + +static void +biba_netatalk_aarp_send(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_biba *dest; + + dest = SLOT(mlabel); + + biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); +} + +static void +biba_netinet_arp_send(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_biba *dest; + + dest = SLOT(mlabel); + + biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); +} + +static void +biba_netinet_firewall_reply(struct mbuf *mrecv, struct label *mrecvlabel, + struct mbuf *msend, struct label *msendlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(mrecvlabel); + dest = SLOT(msendlabel); + + biba_copy_effective(source, dest); +} + +static void +biba_netinet_firewall_send(struct mbuf *m, struct label *mlabel) +{ + struct mac_biba *dest; + + dest = SLOT(mlabel); + + /* XXX: where is the label for the firewall really coming from? */ + biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); +} + +static void +biba_netinet_fragment(struct mbuf *m, struct label *mlabel, + struct mbuf *frag, struct label *fraglabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(mlabel); + dest = SLOT(fraglabel); + + biba_copy_effective(source, dest); +} + +static void +biba_netinet_icmp_reply(struct mbuf *mrecv, struct label *mrecvlabel, + struct mbuf *msend, struct label *msendlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(mrecvlabel); + dest = SLOT(msendlabel); + + biba_copy_effective(source, dest); +} + +static void +biba_netinet_igmp_send(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_biba *dest; + + dest = SLOT(mlabel); + + biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); +} + +static void +biba_netinet6_nd6_send(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_biba *dest; + + dest = SLOT(mlabel); + + biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); +} + +static int +biba_pipe_check_ioctl(struct ucred *cred, struct pipepair *pp, + struct label *pplabel, unsigned long cmd, void /* caddr_t */ *data) +{ + + if(!biba_enabled) + return (0); + + /* XXX: This will be implemented soon... */ + + return (0); +} + +static int +biba_pipe_check_poll(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(pplabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_pipe_check_read(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(pplabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_pipe_check_relabel(struct ucred *cred, struct pipepair *pp, + struct label *pplabel, struct label *newlabel) +{ + struct mac_biba *subj, *obj, *new; + int error; + + new = SLOT(newlabel); + subj = SLOT(cred->cr_label); + obj = SLOT(pplabel); + + /* + * If there is a Biba label update for a pipe, it must be a effective + * update. + */ + error = biba_atmostflags(new, MAC_BIBA_FLAG_EFFECTIVE); + if (error) + return (error); + + /* + * To perform a relabel of a pipe (Biba label or not), Biba must + * authorize the relabel. + */ + if (!biba_effective_in_range(obj, subj)) + return (EPERM); + + /* + * If the Biba label is to be changed, authorize as appropriate. + */ + if (new->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { + /* + * To change the Biba label on a pipe, the new pipe label + * must be in the subject range. + */ + if (!biba_effective_in_range(new, subj)) + return (EPERM); + + /* + * To change the Biba label on a pipe to be EQUAL, the + * subject must have appropriate privilege. + */ + if (biba_contains_equal(new)) { + error = biba_subject_privileged(subj); + if (error) + return (error); + } + } + + return (0); +} + +static int +biba_pipe_check_stat(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(pplabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_pipe_check_write(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(pplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static void +biba_pipe_create(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(pplabel); + + biba_copy_effective(source, dest); +} + +static void +biba_pipe_relabel(struct ucred *cred, struct pipepair *pp, + struct label *pplabel, struct label *newlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(newlabel); + dest = SLOT(pplabel); + + biba_copy(source, dest); +} + +static int +biba_posixsem_check_openunlink(struct ucred *cred, struct ksem *ks, + struct label *kslabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(kslabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_posixsem_check_write(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks, struct label *kslabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(kslabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_posixsem_check_rdonly(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks, struct label *kslabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(kslabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static void +biba_posixsem_create(struct ucred *cred, struct ksem *ks, + struct label *kslabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(kslabel); + + biba_copy_effective(source, dest); +} + +/* + * Some system privileges are allowed regardless of integrity grade; others + * are allowed only when running with privilege with respect to the Biba + * policy as they might otherwise allow bypassing of the integrity policy. + */ +static int +biba_priv_check(struct ucred *cred, int priv) +{ + struct mac_biba *subj; + int error; + + if (!biba_enabled) + return (0); + + /* + * Exempt only specific privileges from the Biba integrity policy. + */ + switch (priv) { + case PRIV_KTRACE: + case PRIV_MSGBUF: + + /* + * Allow processes to manipulate basic process audit properties, and + * to submit audit records. + */ + case PRIV_AUDIT_GETAUDIT: + case PRIV_AUDIT_SETAUDIT: + case PRIV_AUDIT_SUBMIT: + + /* + * Allow processes to manipulate their regular UNIX credentials. + */ + case PRIV_CRED_SETUID: + case PRIV_CRED_SETEUID: + case PRIV_CRED_SETGID: + case PRIV_CRED_SETEGID: + case PRIV_CRED_SETGROUPS: + case PRIV_CRED_SETREUID: + case PRIV_CRED_SETREGID: + case PRIV_CRED_SETRESUID: + case PRIV_CRED_SETRESGID: + + /* + * Allow processes to perform system monitoring. + */ + case PRIV_SEEOTHERGIDS: + case PRIV_SEEOTHERUIDS: + break; + + /* + * Allow access to general process debugging facilities. We + * separately control debugging based on MAC label. + */ + case PRIV_DEBUG_DIFFCRED: + case PRIV_DEBUG_SUGID: + case PRIV_DEBUG_UNPRIV: + + /* + * Allow manipulating jails. + */ + case PRIV_JAIL_ATTACH: + + /* + * Allow privilege with respect to the Partition policy, but not the + * Privs policy. + */ + case PRIV_MAC_PARTITION: + + /* + * Allow privilege with respect to process resource limits and login + * context. + */ + case PRIV_PROC_LIMIT: + case PRIV_PROC_SETLOGIN: + case PRIV_PROC_SETRLIMIT: + + /* + * Allow System V and POSIX IPC privileges. + */ + case PRIV_IPC_READ: + case PRIV_IPC_WRITE: + case PRIV_IPC_ADMIN: + case PRIV_IPC_MSGSIZE: + case PRIV_MQ_ADMIN: + + /* + * Allow certain scheduler manipulations -- possibly this should be + * controlled by more fine-grained policy, as potentially low + * integrity processes can deny CPU to higher integrity ones. + */ + case PRIV_SCHED_DIFFCRED: + case PRIV_SCHED_SETPRIORITY: + case PRIV_SCHED_RTPRIO: + case PRIV_SCHED_SETPOLICY: + case PRIV_SCHED_SET: + case PRIV_SCHED_SETPARAM: + + /* + * More IPC privileges. + */ + case PRIV_SEM_WRITE: + + /* + * Allow signaling privileges subject to integrity policy. + */ + case PRIV_SIGNAL_DIFFCRED: + case PRIV_SIGNAL_SUGID: + + /* + * Allow access to only limited sysctls from lower integrity levels; + * piggy-back on the Jail definition. + */ + case PRIV_SYSCTL_WRITEJAIL: + + /* + * Allow TTY-based privileges, subject to general device access using + * labels on TTY device nodes, but not console privilege. + */ + case PRIV_TTY_DRAINWAIT: + case PRIV_TTY_DTRWAIT: + case PRIV_TTY_EXCLUSIVE: + case PRIV_TTY_PRISON: + case PRIV_TTY_STI: + case PRIV_TTY_SETA: + + /* + * Grant most VFS privileges, as almost all are in practice bounded + * by more specific checks using labels. + */ + case PRIV_VFS_READ: + case PRIV_VFS_WRITE: + case PRIV_VFS_ADMIN: + case PRIV_VFS_EXEC: + case PRIV_VFS_LOOKUP: + case PRIV_VFS_CHFLAGS_DEV: + case PRIV_VFS_CHOWN: + case PRIV_VFS_CHROOT: + case PRIV_VFS_RETAINSUGID: + case PRIV_VFS_EXCEEDQUOTA: + case PRIV_VFS_FCHROOT: + case PRIV_VFS_FHOPEN: + case PRIV_VFS_FHSTATFS: + case PRIV_VFS_GENERATION: + case PRIV_VFS_GETFH: + case PRIV_VFS_GETQUOTA: + case PRIV_VFS_LINK: + case PRIV_VFS_MOUNT: + case PRIV_VFS_MOUNT_OWNER: + case PRIV_VFS_MOUNT_PERM: + case PRIV_VFS_MOUNT_SUIDDIR: + case PRIV_VFS_MOUNT_NONUSER: + case PRIV_VFS_SETGID: + case PRIV_VFS_STICKYFILE: + case PRIV_VFS_SYSFLAGS: + case PRIV_VFS_UNMOUNT: + + /* + * Allow VM privileges; it would be nice if these were subject to + * resource limits. + */ + case PRIV_VM_MADV_PROTECT: + case PRIV_VM_MLOCK: + case PRIV_VM_MUNLOCK: + + /* + * Allow some but not all network privileges. In general, dont allow + * reconfiguring the network stack, just normal use. + */ + case PRIV_NETATALK_RESERVEDPORT: + case PRIV_NETINET_RESERVEDPORT: + case PRIV_NETINET_RAW: + case PRIV_NETINET_REUSEPORT: + case PRIV_NETIPX_RESERVEDPORT: + case PRIV_NETIPX_RAW: + break; + + /* + * All remaining system privileges are allow only if the process + * holds privilege with respect to the Biba policy. + */ + default: + subj = SLOT(cred->cr_label); + error = biba_subject_privileged(subj); + if (error) + return (error); + } + return (0); +} + +static int +biba_proc_check_debug(struct ucred *cred, struct proc *p) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(p->p_ucred->cr_label); + + /* XXX: range checks */ + if (!biba_dominate_effective(obj, subj)) + return (ESRCH); + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_proc_check_sched(struct ucred *cred, struct proc *p) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(p->p_ucred->cr_label); + + /* XXX: range checks */ + if (!biba_dominate_effective(obj, subj)) + return (ESRCH); + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_proc_check_signal(struct ucred *cred, struct proc *p, int signum) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(p->p_ucred->cr_label); + + /* XXX: range checks */ + if (!biba_dominate_effective(obj, subj)) + return (ESRCH); + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_socket_check_deliver(struct socket *so, struct label *solabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_biba *p, *s; + + if (!biba_enabled) + return (0); + + p = SLOT(mlabel); + s = SLOT(solabel); + + return (biba_equal_effective(p, s) ? 0 : EACCES); +} + +static int +biba_socket_check_relabel(struct ucred *cred, struct socket *so, + struct label *solabel, struct label *newlabel) +{ + struct mac_biba *subj, *obj, *new; + int error; + + new = SLOT(newlabel); + subj = SLOT(cred->cr_label); + obj = SLOT(solabel); + + /* + * If there is a Biba label update for the socket, it may be an + * update of effective. + */ + error = biba_atmostflags(new, MAC_BIBA_FLAG_EFFECTIVE); + if (error) + return (error); + + /* + * To relabel a socket, the old socket effective must be in the + * subject range. + */ + if (!biba_effective_in_range(obj, subj)) + return (EPERM); + + /* + * If the Biba label is to be changed, authorize as appropriate. + */ + if (new->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { + /* + * To relabel a socket, the new socket effective must be in + * the subject range. + */ + if (!biba_effective_in_range(new, subj)) + return (EPERM); + + /* + * To change the Biba label on the socket to contain EQUAL, + * the subject must have appropriate privilege. + */ + if (biba_contains_equal(new)) { + error = biba_subject_privileged(subj); + if (error) + return (error); + } + } + + return (0); +} + +static int +biba_socket_check_visible(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(solabel); + + if (!biba_dominate_effective(obj, subj)) + return (ENOENT); + + return (0); +} + +static void +biba_socket_create(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(solabel); + + biba_copy_effective(source, dest); +} + +static void +biba_socket_create_mbuf(struct socket *so, struct label *solabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(solabel); + dest = SLOT(mlabel); + + biba_copy_effective(source, dest); +} + +static void +biba_socket_newconn(struct socket *oldso, struct label *oldsolabel, + struct socket *newso, struct label *newsolabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(oldsolabel); + dest = SLOT(newsolabel); + + biba_copy_effective(source, dest); +} + +static void +biba_socket_relabel(struct ucred *cred, struct socket *so, + struct label *solabel, struct label *newlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(newlabel); + dest = SLOT(solabel); + + biba_copy(source, dest); +} + +static void +biba_socketpeer_set_from_mbuf(struct mbuf *m, struct label *mlabel, + struct socket *so, struct label *sopeerlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(mlabel); + dest = SLOT(sopeerlabel); + + biba_copy_effective(source, dest); +} + +static void +biba_socketpeer_set_from_socket(struct socket *oldso, + struct label *oldsolabel, struct socket *newso, + struct label *newsopeerlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(oldsolabel); + dest = SLOT(newsopeerlabel); + + biba_copy_effective(source, dest); +} + +static void +biba_syncache_create(struct label *label, struct inpcb *inp) +{ + struct mac_biba *source, *dest; + + source = SLOT(inp->inp_label); + dest = SLOT(label); + biba_copy_effective(source, dest); +} + +static void +biba_syncache_create_mbuf(struct label *sc_label, struct mbuf *m, + struct label *mlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(sc_label); + dest = SLOT(mlabel); + biba_copy_effective(source, dest); +} + +static int +biba_system_check_acct(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + struct mac_biba *subj, *obj; + int error; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + + error = biba_subject_privileged(subj); + if (error) + return (error); + + if (vplabel == NULL) + return (0); + + obj = SLOT(vplabel); + if (!biba_high_effective(obj)) + return (EACCES); + + return (0); +} + +static int +biba_system_check_auditctl(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + struct mac_biba *subj, *obj; + int error; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + + error = biba_subject_privileged(subj); + if (error) + return (error); + + if (vplabel == NULL) + return (0); + + obj = SLOT(vplabel); + if (!biba_high_effective(obj)) + return (EACCES); + + return (0); +} + +static int +biba_system_check_auditon(struct ucred *cred, int cmd) +{ + struct mac_biba *subj; + int error; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + + error = biba_subject_privileged(subj); + if (error) + return (error); + + return (0); +} + +static int +biba_system_check_swapoff(struct ucred *cred, struct vnode *vp, + struct label *label) +{ + struct mac_biba *subj; + int error; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + + error = biba_subject_privileged(subj); + if (error) + return (error); + + return (0); +} + +static int +biba_system_check_swapon(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + struct mac_biba *subj, *obj; + int error; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + error = biba_subject_privileged(subj); + if (error) + return (error); + + if (!biba_high_effective(obj)) + return (EACCES); + + return (0); +} + +static int +biba_system_check_sysctl(struct ucred *cred, struct sysctl_oid *oidp, + void *arg1, int arg2, struct sysctl_req *req) +{ + struct mac_biba *subj; + int error; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + + /* + * Treat sysctl variables without CTLFLAG_ANYBODY flag as biba/high, + * but also require privilege to change them. + */ + if (req->newptr != NULL && (oidp->oid_kind & CTLFLAG_ANYBODY) == 0) { + if (!biba_subject_dominate_high(subj)) + return (EACCES); + + error = biba_subject_privileged(subj); + if (error) + return (error); + } + + return (0); +} + +static void +biba_sysvmsg_cleanup(struct label *msglabel) +{ + + bzero(SLOT(msglabel), sizeof(struct mac_biba)); +} + +static void +biba_sysvmsg_create(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqlabel, struct msg *msgptr, struct label *msglabel) +{ + struct mac_biba *source, *dest; + + /* Ignore the msgq label */ + source = SLOT(cred->cr_label); + dest = SLOT(msglabel); + + biba_copy_effective(source, dest); +} + +static int +biba_sysvmsq_check_msgrcv(struct ucred *cred, struct msg *msgptr, + struct label *msglabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(msglabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_sysvmsq_check_msgrmid(struct ucred *cred, struct msg *msgptr, + struct label *msglabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(msglabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_sysvmsq_check_msqget(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqklabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(msqklabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_sysvmsq_check_msqsnd(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqklabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(msqklabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_sysvmsq_check_msqrcv(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqklabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(msqklabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_sysvmsq_check_msqctl(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqklabel, int cmd) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(msqklabel); + + switch(cmd) { + case IPC_RMID: + case IPC_SET: + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + break; + + case IPC_STAT: + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + break; + + default: + return (EACCES); + } + + return (0); +} + +static void +biba_sysvmsq_cleanup(struct label *msqlabel) +{ + + bzero(SLOT(msqlabel), sizeof(struct mac_biba)); +} + +static void +biba_sysvmsq_create(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(msqlabel); + + biba_copy_effective(source, dest); +} + +static int +biba_sysvsem_check_semctl(struct ucred *cred, struct semid_kernel *semakptr, + struct label *semaklabel, int cmd) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(semaklabel); + + switch(cmd) { + case IPC_RMID: + case IPC_SET: + case SETVAL: + case SETALL: + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + break; + + case IPC_STAT: + case GETVAL: + case GETPID: + case GETNCNT: + case GETZCNT: + case GETALL: + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + break; + + default: + return (EACCES); + } + + return (0); +} + +static int +biba_sysvsem_check_semget(struct ucred *cred, struct semid_kernel *semakptr, + struct label *semaklabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(semaklabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_sysvsem_check_semop(struct ucred *cred, struct semid_kernel *semakptr, + struct label *semaklabel, size_t accesstype) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(semaklabel); + + if (accesstype & SEM_R) + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + if (accesstype & SEM_A) + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static void +biba_sysvsem_cleanup(struct label *semalabel) +{ + + bzero(SLOT(semalabel), sizeof(struct mac_biba)); +} + +static void +biba_sysvsem_create(struct ucred *cred, struct semid_kernel *semakptr, + struct label *semalabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(semalabel); + + biba_copy_effective(source, dest); +} + +static int +biba_sysvshm_check_shmat(struct ucred *cred, struct shmid_kernel *shmsegptr, + struct label *shmseglabel, int shmflg) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(shmseglabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + if ((shmflg & SHM_RDONLY) == 0) { + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + } + + return (0); +} + +static int +biba_sysvshm_check_shmctl(struct ucred *cred, struct shmid_kernel *shmsegptr, + struct label *shmseglabel, int cmd) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(shmseglabel); + + switch(cmd) { + case IPC_RMID: + case IPC_SET: + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + break; + + case IPC_STAT: + case SHM_STAT: + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + break; + + default: + return (EACCES); + } + + return (0); +} + +static int +biba_sysvshm_check_shmget(struct ucred *cred, struct shmid_kernel *shmsegptr, + struct label *shmseglabel, int shmflg) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(shmseglabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static void +biba_sysvshm_cleanup(struct label *shmlabel) +{ + + bzero(SLOT(shmlabel), sizeof(struct mac_biba)); +} + +static void +biba_sysvshm_create(struct ucred *cred, struct shmid_kernel *shmsegptr, + struct label *shmlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(shmlabel); + + biba_copy_effective(source, dest); +} + +static int +biba_vnode_associate_extattr(struct mount *mp, struct label *mplabel, + struct vnode *vp, struct label *vplabel) +{ + struct mac_biba mb_temp, *source, *dest; + int buflen, error; + + source = SLOT(mplabel); + dest = SLOT(vplabel); + + buflen = sizeof(mb_temp); + bzero(&mb_temp, buflen); + + error = vn_extattr_get(vp, IO_NODELOCKED, MAC_BIBA_EXTATTR_NAMESPACE, + MAC_BIBA_EXTATTR_NAME, &buflen, (char *) &mb_temp, curthread); + if (error == ENOATTR || error == EOPNOTSUPP) { + /* Fall back to the mntlabel. */ + biba_copy_effective(source, dest); + return (0); + } else if (error) + return (error); + + if (buflen != sizeof(mb_temp)) { + printf("biba_vnode_associate_extattr: bad size %d\n", + buflen); + return (EPERM); + } + if (biba_valid(&mb_temp) != 0) { + printf("biba_vnode_associate_extattr: invalid\n"); + return (EPERM); + } + if ((mb_temp.mb_flags & MAC_BIBA_FLAGS_BOTH) != + MAC_BIBA_FLAG_EFFECTIVE) { + printf("biba_vnode_associate_extattr: not effective\n"); + return (EPERM); + } + + biba_copy_effective(&mb_temp, dest); + return (0); +} + +static void +biba_vnode_associate_singlelabel(struct mount *mp, struct label *mplabel, + struct vnode *vp, struct label *vplabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(mplabel); + dest = SLOT(vplabel); + + biba_copy_effective(source, dest); +} + +static int +biba_vnode_check_chdir(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_chroot(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_create(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct componentname *cnp, struct vattr *vap) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_deleteacl(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_deleteextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace, const char *name) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_exec(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct image_params *imgp, + struct label *execlabel) +{ + struct mac_biba *subj, *obj, *exec; + int error; + + if (execlabel != NULL) { + /* + * We currently don't permit labels to be changed at + * exec-time as part of Biba, so disallow non-NULL Biba label + * elements in the execlabel. + */ + exec = SLOT(execlabel); + error = biba_atmostflags(exec, 0); + if (error) + return (error); + } + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_getacl(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_getextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace, const char *name, + struct uio *uio) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_link(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + obj = SLOT(vplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_listextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_lookup(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct componentname *cnp) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_mmap(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int prot, int flags) +{ + struct mac_biba *subj, *obj; + + /* + * Rely on the use of open()-time protections to handle + * non-revocation cases. + */ + if (!biba_enabled || !revocation_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) { + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + } + if (((prot & VM_PROT_WRITE) != 0) && ((flags & MAP_SHARED) != 0)) { + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + } + + return (0); +} + +static int +biba_vnode_check_open(struct ucred *cred, struct vnode *vp, + struct label *vplabel, accmode_t accmode) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + /* XXX privilege override for admin? */ + if (accmode & (VREAD | VEXEC | VSTAT)) { + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + } + if (accmode & (VWRITE | VAPPEND | VADMIN)) { + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + } + + return (0); +} + +static int +biba_vnode_check_poll(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp, struct label *vplabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled || !revocation_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(vplabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_read(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp, struct label *vplabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled || !revocation_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(vplabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_readdir(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_readlink(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_relabel(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct label *newlabel) +{ + struct mac_biba *old, *new, *subj; + int error; + + old = SLOT(vplabel); + new = SLOT(newlabel); + subj = SLOT(cred->cr_label); + + /* + * If there is a Biba label update for the vnode, it must be a + * effective label. + */ + error = biba_atmostflags(new, MAC_BIBA_FLAG_EFFECTIVE); + if (error) + return (error); + + /* + * To perform a relabel of the vnode (Biba label or not), Biba must + * authorize the relabel. + */ + if (!biba_effective_in_range(old, subj)) + return (EPERM); + + /* + * If the Biba label is to be changed, authorize as appropriate. + */ + if (new->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { + /* + * To change the Biba label on a vnode, the new vnode label + * must be in the subject range. + */ + if (!biba_effective_in_range(new, subj)) + return (EPERM); + + /* + * To change the Biba label on the vnode to be EQUAL, the + * subject must have appropriate privilege. + */ + if (biba_contains_equal(new)) { + error = biba_subject_privileged(subj); + if (error) + return (error); + } + } + + return (0); +} + +static int +biba_vnode_check_rename_from(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + obj = SLOT(vplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + int samedir, struct componentname *cnp) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + if (vp != NULL) { + obj = SLOT(vplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + } + + return (0); +} + +static int +biba_vnode_check_revoke(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_setacl(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type, struct acl *acl) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_setextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace, const char *name, + struct uio *uio) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + /* XXX: protect the MAC EA in a special way? */ + + return (0); +} + +static int +biba_vnode_check_setflags(struct ucred *cred, struct vnode *vp, + struct label *vplabel, u_long flags) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_setmode(struct ucred *cred, struct vnode *vp, + struct label *vplabel, mode_t mode) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_setowner(struct ucred *cred, struct vnode *vp, + struct label *vplabel, uid_t uid, gid_t gid) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_setutimes(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct timespec atime, struct timespec mtime) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_stat(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp, struct label *vplabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(vplabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_unlink(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + obj = SLOT(vplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_check_write(struct ucred *active_cred, + struct ucred *file_cred, struct vnode *vp, struct label *vplabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled || !revocation_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(vplabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_vnode_create_extattr(struct ucred *cred, struct mount *mp, + struct label *mplabel, struct vnode *dvp, struct label *dvplabel, + struct vnode *vp, struct label *vplabel, struct componentname *cnp) +{ + struct mac_biba *source, *dest, mb_temp; + size_t buflen; + int error; + + buflen = sizeof(mb_temp); + bzero(&mb_temp, buflen); + + source = SLOT(cred->cr_label); + dest = SLOT(vplabel); + biba_copy_effective(source, &mb_temp); + + error = vn_extattr_set(vp, IO_NODELOCKED, MAC_BIBA_EXTATTR_NAMESPACE, + MAC_BIBA_EXTATTR_NAME, buflen, (char *) &mb_temp, curthread); + if (error == 0) + biba_copy_effective(source, dest); + return (error); +} + +static void +biba_vnode_relabel(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct label *newlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(newlabel); + dest = SLOT(vplabel); + + biba_copy(source, dest); +} + +static int +biba_vnode_setlabel_extattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct label *intlabel) +{ + struct mac_biba *source, mb_temp; + size_t buflen; + int error; + + buflen = sizeof(mb_temp); + bzero(&mb_temp, buflen); + + source = SLOT(intlabel); + if ((source->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) == 0) + return (0); + + biba_copy_effective(source, &mb_temp); + + error = vn_extattr_set(vp, IO_NODELOCKED, MAC_BIBA_EXTATTR_NAMESPACE, + MAC_BIBA_EXTATTR_NAME, buflen, (char *) &mb_temp, curthread); + return (error); +} + +static struct mac_policy_ops mac_biba_ops = +{ + .mpo_init = biba_init, + + .mpo_bpfdesc_check_receive = biba_bpfdesc_check_receive, + .mpo_bpfdesc_create = biba_bpfdesc_create, + .mpo_bpfdesc_create_mbuf = biba_bpfdesc_create_mbuf, + .mpo_bpfdesc_destroy_label = biba_destroy_label, + .mpo_bpfdesc_init_label = biba_init_label, + + .mpo_cred_associate_nfsd = biba_cred_associate_nfsd, + .mpo_cred_check_relabel = biba_cred_check_relabel, + .mpo_cred_check_visible = biba_cred_check_visible, + .mpo_cred_copy_label = biba_copy_label, + .mpo_cred_create_init = biba_cred_create_init, + .mpo_cred_create_swapper = biba_cred_create_swapper, + .mpo_cred_destroy_label = biba_destroy_label, + .mpo_cred_externalize_label = biba_externalize_label, + .mpo_cred_init_label = biba_init_label, + .mpo_cred_internalize_label = biba_internalize_label, + .mpo_cred_relabel = biba_cred_relabel, + + .mpo_devfs_create_device = biba_devfs_create_device, + .mpo_devfs_create_directory = biba_devfs_create_directory, + .mpo_devfs_create_symlink = biba_devfs_create_symlink, + .mpo_devfs_destroy_label = biba_destroy_label, + .mpo_devfs_init_label = biba_init_label, + .mpo_devfs_update = biba_devfs_update, + .mpo_devfs_vnode_associate = biba_devfs_vnode_associate, + + .mpo_ifnet_check_relabel = biba_ifnet_check_relabel, + .mpo_ifnet_check_transmit = biba_ifnet_check_transmit, + .mpo_ifnet_copy_label = biba_copy_label, + .mpo_ifnet_create = biba_ifnet_create, + .mpo_ifnet_create_mbuf = biba_ifnet_create_mbuf, + .mpo_ifnet_destroy_label = biba_destroy_label, + .mpo_ifnet_externalize_label = biba_externalize_label, + .mpo_ifnet_init_label = biba_init_label, + .mpo_ifnet_internalize_label = biba_internalize_label, + .mpo_ifnet_relabel = biba_ifnet_relabel, + + .mpo_inpcb_check_deliver = biba_inpcb_check_deliver, + .mpo_inpcb_check_visible = biba_inpcb_check_visible, + .mpo_inpcb_create = biba_inpcb_create, + .mpo_inpcb_create_mbuf = biba_inpcb_create_mbuf, + .mpo_inpcb_destroy_label = biba_destroy_label, + .mpo_inpcb_init_label = biba_init_label_waitcheck, + .mpo_inpcb_sosetlabel = biba_inpcb_sosetlabel, + + .mpo_ip6q_create = biba_ip6q_create, + .mpo_ip6q_destroy_label = biba_destroy_label, + .mpo_ip6q_init_label = biba_init_label_waitcheck, + .mpo_ip6q_match = biba_ip6q_match, + .mpo_ip6q_reassemble = biba_ip6q_reassemble, + .mpo_ip6q_update = biba_ip6q_update, + + .mpo_ipq_create = biba_ipq_create, + .mpo_ipq_destroy_label = biba_destroy_label, + .mpo_ipq_init_label = biba_init_label_waitcheck, + .mpo_ipq_match = biba_ipq_match, + .mpo_ipq_reassemble = biba_ipq_reassemble, + .mpo_ipq_update = biba_ipq_update, + + .mpo_kld_check_load = biba_kld_check_load, + + .mpo_mbuf_copy_label = biba_copy_label, + .mpo_mbuf_destroy_label = biba_destroy_label, + .mpo_mbuf_init_label = biba_init_label_waitcheck, + + .mpo_mount_check_stat = biba_mount_check_stat, + .mpo_mount_create = biba_mount_create, + .mpo_mount_destroy_label = biba_destroy_label, + .mpo_mount_init_label = biba_init_label, + + .mpo_netatalk_aarp_send = biba_netatalk_aarp_send, + + .mpo_netinet_arp_send = biba_netinet_arp_send, + .mpo_netinet_firewall_reply = biba_netinet_firewall_reply, + .mpo_netinet_firewall_send = biba_netinet_firewall_send, + .mpo_netinet_fragment = biba_netinet_fragment, + .mpo_netinet_icmp_reply = biba_netinet_icmp_reply, + .mpo_netinet_igmp_send = biba_netinet_igmp_send, + + .mpo_netinet6_nd6_send = biba_netinet6_nd6_send, + + .mpo_pipe_check_ioctl = biba_pipe_check_ioctl, + .mpo_pipe_check_poll = biba_pipe_check_poll, + .mpo_pipe_check_read = biba_pipe_check_read, + .mpo_pipe_check_relabel = biba_pipe_check_relabel, + .mpo_pipe_check_stat = biba_pipe_check_stat, + .mpo_pipe_check_write = biba_pipe_check_write, + .mpo_pipe_copy_label = biba_copy_label, + .mpo_pipe_create = biba_pipe_create, + .mpo_pipe_destroy_label = biba_destroy_label, + .mpo_pipe_externalize_label = biba_externalize_label, + .mpo_pipe_init_label = biba_init_label, + .mpo_pipe_internalize_label = biba_internalize_label, + .mpo_pipe_relabel = biba_pipe_relabel, + + .mpo_posixsem_check_getvalue = biba_posixsem_check_rdonly, + .mpo_posixsem_check_open = biba_posixsem_check_openunlink, + .mpo_posixsem_check_post = biba_posixsem_check_write, + .mpo_posixsem_check_stat = biba_posixsem_check_rdonly, + .mpo_posixsem_check_unlink = biba_posixsem_check_openunlink, + .mpo_posixsem_check_wait = biba_posixsem_check_write, + .mpo_posixsem_create = biba_posixsem_create, + .mpo_posixsem_destroy_label = biba_destroy_label, + .mpo_posixsem_init_label = biba_init_label, + + .mpo_priv_check = biba_priv_check, + + .mpo_proc_check_debug = biba_proc_check_debug, + .mpo_proc_check_sched = biba_proc_check_sched, + .mpo_proc_check_signal = biba_proc_check_signal, + + .mpo_socket_check_deliver = biba_socket_check_deliver, + .mpo_socket_check_relabel = biba_socket_check_relabel, + .mpo_socket_check_visible = biba_socket_check_visible, + .mpo_socket_copy_label = biba_copy_label, + .mpo_socket_create = biba_socket_create, + .mpo_socket_create_mbuf = biba_socket_create_mbuf, + .mpo_socket_destroy_label = biba_destroy_label, + .mpo_socket_externalize_label = biba_externalize_label, + .mpo_socket_init_label = biba_init_label_waitcheck, + .mpo_socket_internalize_label = biba_internalize_label, + .mpo_socket_newconn = biba_socket_newconn, + .mpo_socket_relabel = biba_socket_relabel, + + .mpo_socketpeer_destroy_label = biba_destroy_label, + .mpo_socketpeer_externalize_label = biba_externalize_label, + .mpo_socketpeer_init_label = biba_init_label_waitcheck, + .mpo_socketpeer_set_from_mbuf = biba_socketpeer_set_from_mbuf, + .mpo_socketpeer_set_from_socket = biba_socketpeer_set_from_socket, + + .mpo_syncache_create = biba_syncache_create, + .mpo_syncache_create_mbuf = biba_syncache_create_mbuf, + .mpo_syncache_destroy_label = biba_destroy_label, + .mpo_syncache_init_label = biba_init_label_waitcheck, + + .mpo_system_check_acct = biba_system_check_acct, + .mpo_system_check_auditctl = biba_system_check_auditctl, + .mpo_system_check_auditon = biba_system_check_auditon, + .mpo_system_check_swapoff = biba_system_check_swapoff, + .mpo_system_check_swapon = biba_system_check_swapon, + .mpo_system_check_sysctl = biba_system_check_sysctl, + + .mpo_sysvmsg_cleanup = biba_sysvmsg_cleanup, + .mpo_sysvmsg_create = biba_sysvmsg_create, + .mpo_sysvmsg_destroy_label = biba_destroy_label, + .mpo_sysvmsg_init_label = biba_init_label, + + .mpo_sysvmsq_check_msgrcv = biba_sysvmsq_check_msgrcv, + .mpo_sysvmsq_check_msgrmid = biba_sysvmsq_check_msgrmid, + .mpo_sysvmsq_check_msqget = biba_sysvmsq_check_msqget, + .mpo_sysvmsq_check_msqsnd = biba_sysvmsq_check_msqsnd, + .mpo_sysvmsq_check_msqrcv = biba_sysvmsq_check_msqrcv, + .mpo_sysvmsq_check_msqctl = biba_sysvmsq_check_msqctl, + .mpo_sysvmsq_cleanup = biba_sysvmsq_cleanup, + .mpo_sysvmsq_create = biba_sysvmsq_create, + .mpo_sysvmsq_destroy_label = biba_destroy_label, + .mpo_sysvmsq_init_label = biba_init_label, + + .mpo_sysvsem_check_semctl = biba_sysvsem_check_semctl, + .mpo_sysvsem_check_semget = biba_sysvsem_check_semget, + .mpo_sysvsem_check_semop = biba_sysvsem_check_semop, + .mpo_sysvsem_cleanup = biba_sysvsem_cleanup, + .mpo_sysvsem_create = biba_sysvsem_create, + .mpo_sysvsem_destroy_label = biba_destroy_label, + .mpo_sysvsem_init_label = biba_init_label, + + .mpo_sysvshm_check_shmat = biba_sysvshm_check_shmat, + .mpo_sysvshm_check_shmctl = biba_sysvshm_check_shmctl, + .mpo_sysvshm_check_shmget = biba_sysvshm_check_shmget, + .mpo_sysvshm_cleanup = biba_sysvshm_cleanup, + .mpo_sysvshm_create = biba_sysvshm_create, + .mpo_sysvshm_destroy_label = biba_destroy_label, + .mpo_sysvshm_init_label = biba_init_label, + + .mpo_vnode_associate_extattr = biba_vnode_associate_extattr, + .mpo_vnode_associate_singlelabel = biba_vnode_associate_singlelabel, + .mpo_vnode_check_access = biba_vnode_check_open, + .mpo_vnode_check_chdir = biba_vnode_check_chdir, + .mpo_vnode_check_chroot = biba_vnode_check_chroot, + .mpo_vnode_check_create = biba_vnode_check_create, + .mpo_vnode_check_deleteacl = biba_vnode_check_deleteacl, + .mpo_vnode_check_deleteextattr = biba_vnode_check_deleteextattr, + .mpo_vnode_check_exec = biba_vnode_check_exec, + .mpo_vnode_check_getacl = biba_vnode_check_getacl, + .mpo_vnode_check_getextattr = biba_vnode_check_getextattr, + .mpo_vnode_check_link = biba_vnode_check_link, + .mpo_vnode_check_listextattr = biba_vnode_check_listextattr, + .mpo_vnode_check_lookup = biba_vnode_check_lookup, + .mpo_vnode_check_mmap = biba_vnode_check_mmap, + .mpo_vnode_check_open = biba_vnode_check_open, + .mpo_vnode_check_poll = biba_vnode_check_poll, + .mpo_vnode_check_read = biba_vnode_check_read, + .mpo_vnode_check_readdir = biba_vnode_check_readdir, + .mpo_vnode_check_readlink = biba_vnode_check_readlink, + .mpo_vnode_check_relabel = biba_vnode_check_relabel, + .mpo_vnode_check_rename_from = biba_vnode_check_rename_from, + .mpo_vnode_check_rename_to = biba_vnode_check_rename_to, + .mpo_vnode_check_revoke = biba_vnode_check_revoke, + .mpo_vnode_check_setacl = biba_vnode_check_setacl, + .mpo_vnode_check_setextattr = biba_vnode_check_setextattr, + .mpo_vnode_check_setflags = biba_vnode_check_setflags, + .mpo_vnode_check_setmode = biba_vnode_check_setmode, + .mpo_vnode_check_setowner = biba_vnode_check_setowner, + .mpo_vnode_check_setutimes = biba_vnode_check_setutimes, + .mpo_vnode_check_stat = biba_vnode_check_stat, + .mpo_vnode_check_unlink = biba_vnode_check_unlink, + .mpo_vnode_check_write = biba_vnode_check_write, + .mpo_vnode_create_extattr = biba_vnode_create_extattr, + .mpo_vnode_copy_label = biba_copy_label, + .mpo_vnode_destroy_label = biba_destroy_label, + .mpo_vnode_externalize_label = biba_externalize_label, + .mpo_vnode_init_label = biba_init_label, + .mpo_vnode_internalize_label = biba_internalize_label, + .mpo_vnode_relabel = biba_vnode_relabel, + .mpo_vnode_setlabel_extattr = biba_vnode_setlabel_extattr, +}; + +#define BIBA_OBJECTS (MPC_OBJECT_CRED | \ + /* MPC_OBJECT_PROC | */ \ + MPC_OBJECT_VNODE | \ + MPC_OBJECT_INPCB | \ + MPC_OBJECT_SOCKET | \ + MPC_OBJECT_DEVFS | \ + MPC_OBJECT_MBUF | \ + MPC_OBJECT_IPQ | \ + MPC_OBJECT_IFNET | \ + MPC_OBJECT_BPFDESC | \ + MPC_OBJECT_PIPE | \ + MPC_OBJECT_MOUNT | \ + MPC_OBJECT_POSIXSEM | \ + /* MPC_OBJECT_POSIXSHM | */ \ + MPC_OBJECT_SYSVMSG | \ + MPC_OBJECT_SYSVMSQ | \ + MPC_OBJECT_SYSVSEM | \ + MPC_OBJECT_SYSVSHM | \ + MPC_OBJECT_SYNCACHE) + +MAC_POLICY_SET(&mac_biba_ops, mac_biba, "TrustedBSD MAC/Biba", + MPC_LOADTIME_FLAG_NOTLATE, &biba_slot, BIBA_OBJECTS); diff --git a/sys/security/mac_biba/mac_biba.h b/sys/security/mac_biba/mac_biba.h new file mode 100644 index 0000000..05eefab --- /dev/null +++ b/sys/security/mac_biba/mac_biba.h @@ -0,0 +1,97 @@ +/*- + * Copyright (c) 1999-2002 Robert N. M. Watson + * Copyright (c) 2001-2004 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +/* + * Definitions for the TrustedBSD Biba integrity policy module. + */ +#ifndef _SYS_SECURITY_MAC_BIBA_H +#define _SYS_SECURITY_MAC_BIBA_H + +#define MAC_BIBA_EXTATTR_NAMESPACE EXTATTR_NAMESPACE_SYSTEM +#define MAC_BIBA_EXTATTR_NAME "mac_biba" + +#define MAC_BIBA_LABEL_NAME "biba" + +#define MAC_BIBA_FLAG_EFFECTIVE 0x00000001 /* mb_effective initialized */ +#define MAC_BIBA_FLAG_RANGE 0x00000002 /* mb_range* initialized */ +#define MAC_BIBA_FLAGS_BOTH (MAC_BIBA_FLAG_EFFECTIVE | MAC_BIBA_FLAG_RANGE) + +#define MAC_BIBA_TYPE_UNDEF 0 /* Undefined */ +#define MAC_BIBA_TYPE_GRADE 1 /* Hierarchal grade with mb_grade. */ +#define MAC_BIBA_TYPE_LOW 2 /* Dominated by any + * MAC_BIBA_TYPE_LABEL. */ +#define MAC_BIBA_TYPE_HIGH 3 /* Dominates any + * MAC_BIBA_TYPE_LABEL. */ +#define MAC_BIBA_TYPE_EQUAL 4 /* Equivilent to any + * MAC_BIBA_TYPE_LABEL. */ + +/* + * Structures and constants associated with a Biba Integrity policy. + * mac_biba represents a Biba label, with mb_type determining its properties, + * and mb_grade represents the hierarchal grade if valid for the current + * mb_type. + */ + +#define MAC_BIBA_MAX_COMPARTMENTS 256 + +struct mac_biba_element { + u_short mbe_type; + u_short mbe_grade; + u_char mbe_compartments[MAC_BIBA_MAX_COMPARTMENTS >> 3]; +}; + +/* + * Biba labels consist of two components: an effective label, and a label + * range. Depending on the context, one or both may be used; the mb_flags + * field permits the provider to indicate what fields are intended for + * use. + */ +struct mac_biba { + int mb_flags; + struct mac_biba_element mb_effective; + struct mac_biba_element mb_rangelow, mb_rangehigh; +}; + +/* + * Biba compartments bit test/set macros. + * The range is 1 to MAC_BIBA_MAX_COMPARTMENTS. + */ +#define MAC_BIBA_BIT_TEST(b, w) \ + ((w)[(((b) - 1) >> 3)] & (1 << (((b) - 1) & 7))) +#define MAC_BIBA_BIT_SET(b, w) \ + ((w)[(((b) - 1) >> 3)] |= (1 << (((b) - 1) & 7))) +#define MAC_BIBA_BIT_SET_EMPTY(set) biba_bit_set_empty(set) + +#endif /* !_SYS_SECURITY_MAC_BIBA_H */ diff --git a/sys/security/mac_bsdextended/mac_bsdextended.c b/sys/security/mac_bsdextended/mac_bsdextended.c new file mode 100644 index 0000000..b30b214 --- /dev/null +++ b/sys/security/mac_bsdextended/mac_bsdextended.c @@ -0,0 +1,526 @@ +/*- + * Copyright (c) 1999-2002, 2007-2008 Robert N. M. Watson + * Copyright (c) 2001-2005 Networks Associates Technology, Inc. + * Copyright (c) 2005 Tom Rhodes + * Copyright (c) 2006 SPARTA, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * It was later enhanced by Tom Rhodes for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Developed by the TrustedBSD Project. + * + * "BSD Extended" MAC policy, allowing the administrator to impose mandatory + * firewall-like rules regarding users and file system objects. + */ + +#include <sys/param.h> +#include <sys/acl.h> +#include <sys/kernel.h> +#include <sys/jail.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/mount.h> +#include <sys/mutex.h> +#include <sys/priv.h> +#include <sys/proc.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/sysctl.h> +#include <sys/syslog.h> +#include <sys/stat.h> + +#include <security/mac/mac_policy.h> +#include <security/mac_bsdextended/mac_bsdextended.h> +#include <security/mac_bsdextended/ugidfw_internal.h> + +static struct mtx ugidfw_mtx; + +SYSCTL_DECL(_security_mac); + +SYSCTL_NODE(_security_mac, OID_AUTO, bsdextended, CTLFLAG_RW, 0, + "TrustedBSD extended BSD MAC policy controls"); + +static int ugidfw_enabled = 1; +SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, enabled, CTLFLAG_RW, + &ugidfw_enabled, 0, "Enforce extended BSD policy"); +TUNABLE_INT("security.mac.bsdextended.enabled", &ugidfw_enabled); + +MALLOC_DEFINE(M_MACBSDEXTENDED, "mac_bsdextended", "BSD Extended MAC rule"); + +#define MAC_BSDEXTENDED_MAXRULES 250 +static struct mac_bsdextended_rule *rules[MAC_BSDEXTENDED_MAXRULES]; +static int rule_count = 0; +static int rule_slots = 0; +static int rule_version = MB_VERSION; + +SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, rule_count, CTLFLAG_RD, + &rule_count, 0, "Number of defined rules\n"); +SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, rule_slots, CTLFLAG_RD, + &rule_slots, 0, "Number of used rule slots\n"); +SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, rule_version, CTLFLAG_RD, + &rule_version, 0, "Version number for API\n"); + +/* + * This is just used for logging purposes, eventually we would like to log + * much more then failed requests. + */ +static int ugidfw_logging; +SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, logging, CTLFLAG_RW, + &ugidfw_logging, 0, "Log failed authorization requests"); + +/* + * This tunable is here for compatibility. It will allow the user to switch + * between the new mode (first rule matches) and the old functionality (all + * rules match). + */ +static int ugidfw_firstmatch_enabled; +SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, firstmatch_enabled, + CTLFLAG_RW, &ugidfw_firstmatch_enabled, 1, + "Disable/enable match first rule functionality"); + +static int +ugidfw_rule_valid(struct mac_bsdextended_rule *rule) +{ + + if ((rule->mbr_subject.mbs_flags | MBS_ALL_FLAGS) != MBS_ALL_FLAGS) + return (EINVAL); + if ((rule->mbr_subject.mbs_neg | MBS_ALL_FLAGS) != MBS_ALL_FLAGS) + return (EINVAL); + if ((rule->mbr_object.mbo_flags | MBO_ALL_FLAGS) != MBO_ALL_FLAGS) + return (EINVAL); + if ((rule->mbr_object.mbo_neg | MBO_ALL_FLAGS) != MBO_ALL_FLAGS) + return (EINVAL); + if ((rule->mbr_object.mbo_neg | MBO_TYPE_DEFINED) && + (rule->mbr_object.mbo_type | MBO_ALL_TYPE) != MBO_ALL_TYPE) + return (EINVAL); + if ((rule->mbr_mode | MBI_ALLPERM) != MBI_ALLPERM) + return (EINVAL); + return (0); +} + +static int +sysctl_rule(SYSCTL_HANDLER_ARGS) +{ + struct mac_bsdextended_rule temprule, *ruleptr; + u_int namelen; + int error, index, *name; + + error = 0; + name = (int *)arg1; + namelen = arg2; + if (namelen != 1) + return (EINVAL); + index = name[0]; + if (index >= MAC_BSDEXTENDED_MAXRULES) + return (ENOENT); + + ruleptr = NULL; + if (req->newptr && req->newlen != 0) { + error = SYSCTL_IN(req, &temprule, sizeof(temprule)); + if (error) + return (error); + ruleptr = malloc(sizeof(*ruleptr), M_MACBSDEXTENDED, + M_WAITOK | M_ZERO); + } + + mtx_lock(&ugidfw_mtx); + if (req->oldptr) { + if (index < 0 || index > rule_slots + 1) { + error = ENOENT; + goto out; + } + if (rules[index] == NULL) { + error = ENOENT; + goto out; + } + temprule = *rules[index]; + } + if (req->newptr && req->newlen == 0) { + KASSERT(ruleptr == NULL, ("sysctl_rule: ruleptr != NULL")); + ruleptr = rules[index]; + if (ruleptr == NULL) { + error = ENOENT; + goto out; + } + rule_count--; + rules[index] = NULL; + } else if (req->newptr) { + error = ugidfw_rule_valid(&temprule); + if (error) + goto out; + if (rules[index] == NULL) { + *ruleptr = temprule; + rules[index] = ruleptr; + ruleptr = NULL; + if (index + 1 > rule_slots) + rule_slots = index + 1; + rule_count++; + } else + *rules[index] = temprule; + } +out: + mtx_unlock(&ugidfw_mtx); + if (ruleptr != NULL) + free(ruleptr, M_MACBSDEXTENDED); + if (req->oldptr && error == 0) + error = SYSCTL_OUT(req, &temprule, sizeof(temprule)); + return (error); +} + +SYSCTL_NODE(_security_mac_bsdextended, OID_AUTO, rules, CTLFLAG_RW, + sysctl_rule, "BSD extended MAC rules"); + +static void +ugidfw_init(struct mac_policy_conf *mpc) +{ + + mtx_init(&ugidfw_mtx, "mac_bsdextended lock", NULL, MTX_DEF); +} + +static void +ugidfw_destroy(struct mac_policy_conf *mpc) +{ + int i; + + for (i = 0; i < MAC_BSDEXTENDED_MAXRULES; i++) { + if (rules[i] != NULL) + free(rules[i], M_MACBSDEXTENDED); + } + mtx_destroy(&ugidfw_mtx); +} + +static int +ugidfw_rulecheck(struct mac_bsdextended_rule *rule, + struct ucred *cred, struct vnode *vp, struct vattr *vap, int acc_mode) +{ + int mac_granted, match, priv_granted; + int i; + + /* + * Is there a subject match? + */ + mtx_assert(&ugidfw_mtx, MA_OWNED); + if (rule->mbr_subject.mbs_flags & MBS_UID_DEFINED) { + match = ((cred->cr_uid <= rule->mbr_subject.mbs_uid_max && + cred->cr_uid >= rule->mbr_subject.mbs_uid_min) || + (cred->cr_ruid <= rule->mbr_subject.mbs_uid_max && + cred->cr_ruid >= rule->mbr_subject.mbs_uid_min) || + (cred->cr_svuid <= rule->mbr_subject.mbs_uid_max && + cred->cr_svuid >= rule->mbr_subject.mbs_uid_min)); + if (rule->mbr_subject.mbs_neg & MBS_UID_DEFINED) + match = !match; + if (!match) + return (0); + } + + if (rule->mbr_subject.mbs_flags & MBS_GID_DEFINED) { + match = ((cred->cr_rgid <= rule->mbr_subject.mbs_gid_max && + cred->cr_rgid >= rule->mbr_subject.mbs_gid_min) || + (cred->cr_svgid <= rule->mbr_subject.mbs_gid_max && + cred->cr_svgid >= rule->mbr_subject.mbs_gid_min)); + if (!match) { + for (i = 0; i < cred->cr_ngroups; i++) { + if (cred->cr_groups[i] + <= rule->mbr_subject.mbs_gid_max && + cred->cr_groups[i] + >= rule->mbr_subject.mbs_gid_min) { + match = 1; + break; + } + } + } + if (rule->mbr_subject.mbs_neg & MBS_GID_DEFINED) + match = !match; + if (!match) + return (0); + } + + if (rule->mbr_subject.mbs_flags & MBS_PRISON_DEFINED) { + match = (cred->cr_prison != NULL && + cred->cr_prison->pr_id == rule->mbr_subject.mbs_prison); + if (rule->mbr_subject.mbs_neg & MBS_PRISON_DEFINED) + match = !match; + if (!match) + return (0); + } + + /* + * Is there an object match? + */ + if (rule->mbr_object.mbo_flags & MBO_UID_DEFINED) { + match = (vap->va_uid <= rule->mbr_object.mbo_uid_max && + vap->va_uid >= rule->mbr_object.mbo_uid_min); + if (rule->mbr_object.mbo_neg & MBO_UID_DEFINED) + match = !match; + if (!match) + return (0); + } + + if (rule->mbr_object.mbo_flags & MBO_GID_DEFINED) { + match = (vap->va_gid <= rule->mbr_object.mbo_gid_max && + vap->va_gid >= rule->mbr_object.mbo_gid_min); + if (rule->mbr_object.mbo_neg & MBO_GID_DEFINED) + match = !match; + if (!match) + return (0); + } + + if (rule->mbr_object.mbo_flags & MBO_FSID_DEFINED) { + match = (bcmp(&(vp->v_mount->mnt_stat.f_fsid), + &(rule->mbr_object.mbo_fsid), + sizeof(rule->mbr_object.mbo_fsid)) == 0); + if (rule->mbr_object.mbo_neg & MBO_FSID_DEFINED) + match = !match; + if (!match) + return (0); + } + + if (rule->mbr_object.mbo_flags & MBO_SUID) { + match = (vap->va_mode & S_ISUID); + if (rule->mbr_object.mbo_neg & MBO_SUID) + match = !match; + if (!match) + return (0); + } + + if (rule->mbr_object.mbo_flags & MBO_SGID) { + match = (vap->va_mode & S_ISGID); + if (rule->mbr_object.mbo_neg & MBO_SGID) + match = !match; + if (!match) + return (0); + } + + if (rule->mbr_object.mbo_flags & MBO_UID_SUBJECT) { + match = (vap->va_uid == cred->cr_uid || + vap->va_uid == cred->cr_ruid || + vap->va_uid == cred->cr_svuid); + if (rule->mbr_object.mbo_neg & MBO_UID_SUBJECT) + match = !match; + if (!match) + return (0); + } + + if (rule->mbr_object.mbo_flags & MBO_GID_SUBJECT) { + match = (groupmember(vap->va_gid, cred) || + vap->va_gid == cred->cr_rgid || + vap->va_gid == cred->cr_svgid); + if (rule->mbr_object.mbo_neg & MBO_GID_SUBJECT) + match = !match; + if (!match) + return (0); + } + + if (rule->mbr_object.mbo_flags & MBO_TYPE_DEFINED) { + switch (vap->va_type) { + case VREG: + match = (rule->mbr_object.mbo_type & MBO_TYPE_REG); + break; + case VDIR: + match = (rule->mbr_object.mbo_type & MBO_TYPE_DIR); + break; + case VBLK: + match = (rule->mbr_object.mbo_type & MBO_TYPE_BLK); + break; + case VCHR: + match = (rule->mbr_object.mbo_type & MBO_TYPE_CHR); + break; + case VLNK: + match = (rule->mbr_object.mbo_type & MBO_TYPE_LNK); + break; + case VSOCK: + match = (rule->mbr_object.mbo_type & MBO_TYPE_SOCK); + break; + case VFIFO: + match = (rule->mbr_object.mbo_type & MBO_TYPE_FIFO); + break; + default: + match = 0; + } + if (rule->mbr_object.mbo_neg & MBO_TYPE_DEFINED) + match = !match; + if (!match) + return (0); + } + + /* + * MBI_APPEND should not be here as it should get converted to + * MBI_WRITE. + */ + priv_granted = 0; + mac_granted = rule->mbr_mode; + if ((acc_mode & MBI_ADMIN) && (mac_granted & MBI_ADMIN) == 0 && + priv_check_cred(cred, PRIV_VFS_ADMIN, 0) == 0) + priv_granted |= MBI_ADMIN; + if ((acc_mode & MBI_EXEC) && (mac_granted & MBI_EXEC) == 0 && + priv_check_cred(cred, (vap->va_type == VDIR) ? PRIV_VFS_LOOKUP : + PRIV_VFS_EXEC, 0) == 0) + priv_granted |= MBI_EXEC; + if ((acc_mode & MBI_READ) && (mac_granted & MBI_READ) == 0 && + priv_check_cred(cred, PRIV_VFS_READ, 0) == 0) + priv_granted |= MBI_READ; + if ((acc_mode & MBI_STAT) && (mac_granted & MBI_STAT) == 0 && + priv_check_cred(cred, PRIV_VFS_STAT, 0) == 0) + priv_granted |= MBI_STAT; + if ((acc_mode & MBI_WRITE) && (mac_granted & MBI_WRITE) == 0 && + priv_check_cred(cred, PRIV_VFS_WRITE, 0) == 0) + priv_granted |= MBI_WRITE; + /* + * Is the access permitted? + */ + if (((mac_granted | priv_granted) & acc_mode) != acc_mode) { + if (ugidfw_logging) + log(LOG_AUTHPRIV, "mac_bsdextended: %d:%d request %d" + " on %d:%d failed. \n", cred->cr_ruid, + cred->cr_rgid, acc_mode, vap->va_uid, + vap->va_gid); + return (EACCES); + } + + /* + * If the rule matched, permits access, and first match is enabled, + * return success. + */ + if (ugidfw_firstmatch_enabled) + return (EJUSTRETURN); + else + return (0); +} + +int +ugidfw_check(struct ucred *cred, struct vnode *vp, struct vattr *vap, + int acc_mode) +{ + int error, i; + + /* + * Since we do not separately handle append, map append to write. + */ + if (acc_mode & MBI_APPEND) { + acc_mode &= ~MBI_APPEND; + acc_mode |= MBI_WRITE; + } + mtx_lock(&ugidfw_mtx); + for (i = 0; i < rule_slots; i++) { + if (rules[i] == NULL) + continue; + error = ugidfw_rulecheck(rules[i], cred, + vp, vap, acc_mode); + if (error == EJUSTRETURN) + break; + if (error) { + mtx_unlock(&ugidfw_mtx); + return (error); + } + } + mtx_unlock(&ugidfw_mtx); + return (0); +} + +int +ugidfw_check_vp(struct ucred *cred, struct vnode *vp, int acc_mode) +{ + int error; + struct vattr vap; + + if (!ugidfw_enabled) + return (0); + error = VOP_GETATTR(vp, &vap, cred); + if (error) + return (error); + return (ugidfw_check(cred, vp, &vap, acc_mode)); +} + +int +ugidfw_accmode2mbi(accmode_t accmode) +{ + int mbi; + + mbi = 0; + if (accmode & VEXEC) + mbi |= MBI_EXEC; + if (accmode & VWRITE) + mbi |= MBI_WRITE; + if (accmode & VREAD) + mbi |= MBI_READ; + if (accmode & VADMIN) + mbi |= MBI_ADMIN; + if (accmode & VSTAT) + mbi |= MBI_STAT; + if (accmode & VAPPEND) + mbi |= MBI_APPEND; + return (mbi); +} + +static struct mac_policy_ops ugidfw_ops = +{ + .mpo_destroy = ugidfw_destroy, + .mpo_init = ugidfw_init, + .mpo_system_check_acct = ugidfw_system_check_acct, + .mpo_system_check_auditctl = ugidfw_system_check_auditctl, + .mpo_system_check_swapon = ugidfw_system_check_swapon, + .mpo_vnode_check_access = ugidfw_vnode_check_access, + .mpo_vnode_check_chdir = ugidfw_vnode_check_chdir, + .mpo_vnode_check_chroot = ugidfw_vnode_check_chroot, + .mpo_vnode_check_create = ugidfw_check_create_vnode, + .mpo_vnode_check_deleteacl = ugidfw_vnode_check_deleteacl, + .mpo_vnode_check_deleteextattr = ugidfw_vnode_check_deleteextattr, + .mpo_vnode_check_exec = ugidfw_vnode_check_exec, + .mpo_vnode_check_getacl = ugidfw_vnode_check_getacl, + .mpo_vnode_check_getextattr = ugidfw_vnode_check_getextattr, + .mpo_vnode_check_link = ugidfw_vnode_check_link, + .mpo_vnode_check_listextattr = ugidfw_vnode_check_listextattr, + .mpo_vnode_check_lookup = ugidfw_vnode_check_lookup, + .mpo_vnode_check_open = ugidfw_vnode_check_open, + .mpo_vnode_check_readdir = ugidfw_vnode_check_readdir, + .mpo_vnode_check_readlink = ugidfw_vnode_check_readdlink, + .mpo_vnode_check_rename_from = ugidfw_vnode_check_rename_from, + .mpo_vnode_check_rename_to = ugidfw_vnode_check_rename_to, + .mpo_vnode_check_revoke = ugidfw_vnode_check_revoke, + .mpo_vnode_check_setacl = ugidfw_check_setacl_vnode, + .mpo_vnode_check_setextattr = ugidfw_vnode_check_setextattr, + .mpo_vnode_check_setflags = ugidfw_vnode_check_setflags, + .mpo_vnode_check_setmode = ugidfw_vnode_check_setmode, + .mpo_vnode_check_setowner = ugidfw_vnode_check_setowner, + .mpo_vnode_check_setutimes = ugidfw_vnode_check_setutimes, + .mpo_vnode_check_stat = ugidfw_vnode_check_stat, + .mpo_vnode_check_unlink = ugidfw_vnode_check_unlink, +}; + +MAC_POLICY_SET(&ugidfw_ops, mac_bsdextended, "TrustedBSD MAC/BSD Extended", + MPC_LOADTIME_FLAG_UNLOADOK, NULL, 0); diff --git a/sys/security/mac_bsdextended/mac_bsdextended.h b/sys/security/mac_bsdextended/mac_bsdextended.h new file mode 100644 index 0000000..c09abc0 --- /dev/null +++ b/sys/security/mac_bsdextended/mac_bsdextended.h @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 1999-2002 Robert N. M. Watson + * Copyright (c) 2001-2004 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SYS_SECURITY_MAC_BSDEXTENDED_H +#define _SYS_SECURITY_MAC_BSDEXTENDED_H + +#define MB_VERSION 2 /* Used to check library and kernel are the same. */ + +/* + * Rights that can be represented in mbr_mode. These have the same values as + * the V* rights in vnode.h, but in order to avoid sharing user and kernel + * constants, we define them here. That will also improve ABI stability if + * the in-kernel values change. + */ +#define MBI_EXEC 000100 +#define MBI_WRITE 000200 +#define MBI_READ 000400 +#define MBI_ADMIN 010000 +#define MBI_STAT 020000 +#define MBI_APPEND 040000 +#define MBI_ALLPERM (MBI_EXEC | MBI_WRITE | MBI_READ | MBI_ADMIN | \ + MBI_STAT | MBI_APPEND) + +#define MBS_UID_DEFINED 0x00000001 /* uid field should be matched */ +#define MBS_GID_DEFINED 0x00000002 /* gid field should be matched */ +#define MBS_PRISON_DEFINED 0x00000004 /* prison field should be matched */ + +#define MBS_ALL_FLAGS (MBS_UID_DEFINED | MBS_GID_DEFINED | MBS_PRISON_DEFINED) + +struct mac_bsdextended_subject { + int mbs_flags; + int mbs_neg; + uid_t mbs_uid_min; + uid_t mbs_uid_max; + gid_t mbs_gid_min; + gid_t mbs_gid_max; + int mbs_prison; +}; + +#define MBO_UID_DEFINED 0x00000001 /* uid field should be matched */ +#define MBO_GID_DEFINED 0x00000002 /* gid field should be matched */ +#define MBO_FSID_DEFINED 0x00000004 /* fsid field should be matched */ +#define MBO_SUID 0x00000008 /* object must be suid */ +#define MBO_SGID 0x00000010 /* object must be sgid */ +#define MBO_UID_SUBJECT 0x00000020 /* uid must match subject */ +#define MBO_GID_SUBJECT 0x00000040 /* gid must match subject */ +#define MBO_TYPE_DEFINED 0x00000080 /* object type should be matched */ + +#define MBO_ALL_FLAGS (MBO_UID_DEFINED | MBO_GID_DEFINED | MBO_FSID_DEFINED | \ + MBO_SUID | MBO_SGID | MBO_UID_SUBJECT | MBO_GID_SUBJECT | \ + MBO_TYPE_DEFINED) + +#define MBO_TYPE_REG 0x00000001 +#define MBO_TYPE_DIR 0x00000002 +#define MBO_TYPE_BLK 0x00000004 +#define MBO_TYPE_CHR 0x00000008 +#define MBO_TYPE_LNK 0x00000010 +#define MBO_TYPE_SOCK 0x00000020 +#define MBO_TYPE_FIFO 0x00000040 + +#define MBO_ALL_TYPE (MBO_TYPE_REG | MBO_TYPE_DIR | MBO_TYPE_BLK | \ + MBO_TYPE_CHR | MBO_TYPE_LNK | MBO_TYPE_SOCK | MBO_TYPE_FIFO) + +struct mac_bsdextended_object { + int mbo_flags; + int mbo_neg; + uid_t mbo_uid_min; + uid_t mbo_uid_max; + gid_t mbo_gid_min; + gid_t mbo_gid_max; + struct fsid mbo_fsid; + int mbo_type; +}; + +struct mac_bsdextended_rule { + struct mac_bsdextended_subject mbr_subject; + struct mac_bsdextended_object mbr_object; + mode_t mbr_mode; /* maximum access */ +}; + +#endif /* _SYS_SECURITY_MAC_BSDEXTENDED_H */ diff --git a/sys/security/mac_bsdextended/ugidfw_internal.h b/sys/security/mac_bsdextended/ugidfw_internal.h new file mode 100644 index 0000000..c53326d --- /dev/null +++ b/sys/security/mac_bsdextended/ugidfw_internal.h @@ -0,0 +1,118 @@ +/*- + * Copyright (c) 2008 Robert N. M. Watson + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SYS_SECURITY_MAC_BSDEXTENDED_UGIDFW_INTERNAL_H +#define _SYS_SECURITY_MAC_BSDEXTENDED_UGIDFW_INTERNAL_H + +/* + * Central access control routines used by object-specific checks. + */ +int ugidfw_accmode2mbi(accmode_t accmode); +int ugidfw_check(struct ucred *cred, struct vnode *vp, struct vattr *vap, + int acc_mode); +int ugidfw_check_vp(struct ucred *cred, struct vnode *vp, int acc_mode); + +/* + * System access control checks. + */ +int ugidfw_system_check_acct(struct ucred *cred, struct vnode *vp, + struct label *vplabel); +int ugidfw_system_check_auditctl(struct ucred *cred, struct vnode *vp, + struct label *vplabel); +int ugidfw_system_check_swapon(struct ucred *cred, struct vnode *vp, + struct label *vplabel); + +/* + * Vnode access control checks. + */ +int ugidfw_vnode_check_access(struct ucred *cred, struct vnode *vp, + struct label *vplabel, accmode_t accmode); +int ugidfw_vnode_check_chdir(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel); +int ugidfw_vnode_check_chroot(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel); +int ugidfw_check_create_vnode(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct componentname *cnp, + struct vattr *vap); +int ugidfw_vnode_check_deleteacl(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type); +int ugidfw_vnode_check_deleteextattr(struct ucred *cred, + struct vnode *vp, struct label *vplabel, int attrnamespace, + const char *name); +int ugidfw_vnode_check_exec(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct image_params *imgp, + struct label *execlabel); +int ugidfw_vnode_check_getacl(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type); +int ugidfw_vnode_check_getextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace, const char *name, + struct uio *uio); +int ugidfw_vnode_check_link(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *label, + struct componentname *cnp); +int ugidfw_vnode_check_listextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace); +int ugidfw_vnode_check_lookup(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct componentname *cnp); +int ugidfw_vnode_check_open(struct ucred *cred, struct vnode *vp, + struct label *vplabel, accmode_t accmode); +int ugidfw_vnode_check_readdir(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel); +int ugidfw_vnode_check_readdlink(struct ucred *cred, struct vnode *vp, + struct label *vplabel); +int ugidfw_vnode_check_rename_from(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp); +int ugidfw_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + int samedir, struct componentname *cnp); +int ugidfw_vnode_check_revoke(struct ucred *cred, struct vnode *vp, + struct label *vplabel); +int ugidfw_check_setacl_vnode(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type, struct acl *acl); +int ugidfw_vnode_check_setextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace, const char *name, + struct uio *uio); +int ugidfw_vnode_check_setflags(struct ucred *cred, struct vnode *vp, + struct label *vplabel, u_long flags); +int ugidfw_vnode_check_setmode(struct ucred *cred, struct vnode *vp, + struct label *vplabel, mode_t mode); +int ugidfw_vnode_check_setowner(struct ucred *cred, struct vnode *vp, + struct label *vplabel, uid_t uid, gid_t gid); +int ugidfw_vnode_check_setutimes(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct timespec atime, + struct timespec utime); +int ugidfw_vnode_check_stat(struct ucred *active_cred, + struct ucred *file_cred, struct vnode *vp, struct label *vplabel); +int ugidfw_vnode_check_unlink(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp); + +#endif /* _SYS_SECURITY_MAC_BSDEXTENDED_UGIDFW_INTERNAL_H */ diff --git a/sys/security/mac_bsdextended/ugidfw_system.c b/sys/security/mac_bsdextended/ugidfw_system.c new file mode 100644 index 0000000..49e4f1d --- /dev/null +++ b/sys/security/mac_bsdextended/ugidfw_system.c @@ -0,0 +1,91 @@ +/*- + * Copyright (c) 1999-2002, 2007 Robert N. M. Watson + * Copyright (c) 2001-2005 Networks Associates Technology, Inc. + * Copyright (c) 2005 Tom Rhodes + * Copyright (c) 2006 SPARTA, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * It was later enhanced by Tom Rhodes for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/acl.h> +#include <sys/kernel.h> +#include <sys/jail.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/mount.h> +#include <sys/mutex.h> +#include <sys/priv.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/sysctl.h> +#include <sys/syslog.h> +#include <sys/stat.h> + +#include <security/mac/mac_policy.h> +#include <security/mac_bsdextended/mac_bsdextended.h> +#include <security/mac_bsdextended/ugidfw_internal.h> + +int +ugidfw_system_check_acct(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + + if (vp != NULL) + return (ugidfw_check_vp(cred, vp, MBI_WRITE)); + else + return (0); +} + +int +ugidfw_system_check_auditctl(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + + if (vp != NULL) + return (ugidfw_check_vp(cred, vp, MBI_WRITE)); + else + return (0); +} + +int +ugidfw_system_check_swapon(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + + return (ugidfw_check_vp(cred, vp, MBI_WRITE)); +} diff --git a/sys/security/mac_bsdextended/ugidfw_vnode.c b/sys/security/mac_bsdextended/ugidfw_vnode.c new file mode 100644 index 0000000..bfcc5ac --- /dev/null +++ b/sys/security/mac_bsdextended/ugidfw_vnode.c @@ -0,0 +1,297 @@ +/*- + * Copyright (c) 1999-2002, 2007-2008 Robert N. M. Watson + * Copyright (c) 2001-2005 Networks Associates Technology, Inc. + * Copyright (c) 2005 Tom Rhodes + * Copyright (c) 2006 SPARTA, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * It was later enhanced by Tom Rhodes for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/acl.h> +#include <sys/kernel.h> +#include <sys/jail.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/mount.h> +#include <sys/mutex.h> +#include <sys/priv.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/sysctl.h> +#include <sys/syslog.h> +#include <sys/stat.h> + +#include <security/mac/mac_policy.h> +#include <security/mac_bsdextended/mac_bsdextended.h> +#include <security/mac_bsdextended/ugidfw_internal.h> + +int +ugidfw_vnode_check_access(struct ucred *cred, struct vnode *vp, + struct label *vplabel, accmode_t accmode) +{ + + return (ugidfw_check_vp(cred, vp, ugidfw_accmode2mbi(accmode))); +} + +int +ugidfw_vnode_check_chdir(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel) +{ + + return (ugidfw_check_vp(cred, dvp, MBI_EXEC)); +} + +int +ugidfw_vnode_check_chroot(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel) +{ + + return (ugidfw_check_vp(cred, dvp, MBI_EXEC)); +} + +int +ugidfw_check_create_vnode(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct componentname *cnp, struct vattr *vap) +{ + + return (ugidfw_check_vp(cred, dvp, MBI_WRITE)); +} + +int +ugidfw_vnode_check_deleteacl(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type) +{ + + return (ugidfw_check_vp(cred, vp, MBI_ADMIN)); +} + +int +ugidfw_vnode_check_deleteextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace, const char *name) +{ + + return (ugidfw_check_vp(cred, vp, MBI_WRITE)); +} + +int +ugidfw_vnode_check_exec(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct image_params *imgp, + struct label *execlabel) +{ + + return (ugidfw_check_vp(cred, vp, MBI_READ|MBI_EXEC)); +} + +int +ugidfw_vnode_check_getacl(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type) +{ + + return (ugidfw_check_vp(cred, vp, MBI_STAT)); +} + +int +ugidfw_vnode_check_getextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace, const char *name, + struct uio *uio) +{ + + return (ugidfw_check_vp(cred, vp, MBI_READ)); +} + +int +ugidfw_vnode_check_link(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *label, + struct componentname *cnp) +{ + int error; + + error = ugidfw_check_vp(cred, dvp, MBI_WRITE); + if (error) + return (error); + error = ugidfw_check_vp(cred, vp, MBI_WRITE); + if (error) + return (error); + return (0); +} + +int +ugidfw_vnode_check_listextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace) +{ + + return (ugidfw_check_vp(cred, vp, MBI_READ)); +} + +int +ugidfw_vnode_check_lookup(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct componentname *cnp) +{ + + return (ugidfw_check_vp(cred, dvp, MBI_EXEC)); +} + +int +ugidfw_vnode_check_open(struct ucred *cred, struct vnode *vp, + struct label *vplabel, accmode_t accmode) +{ + + return (ugidfw_check_vp(cred, vp, ugidfw_accmode2mbi(accmode))); +} + +int +ugidfw_vnode_check_readdir(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel) +{ + + return (ugidfw_check_vp(cred, dvp, MBI_READ)); +} + +int +ugidfw_vnode_check_readdlink(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + + return (ugidfw_check_vp(cred, vp, MBI_READ)); +} + +int +ugidfw_vnode_check_rename_from(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp) +{ + int error; + + error = ugidfw_check_vp(cred, dvp, MBI_WRITE); + if (error) + return (error); + return (ugidfw_check_vp(cred, vp, MBI_WRITE)); +} + +int +ugidfw_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + int samedir, struct componentname *cnp) +{ + int error; + + error = ugidfw_check_vp(cred, dvp, MBI_WRITE); + if (error) + return (error); + if (vp != NULL) + error = ugidfw_check_vp(cred, vp, MBI_WRITE); + return (error); +} + +int +ugidfw_vnode_check_revoke(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + + return (ugidfw_check_vp(cred, vp, MBI_ADMIN)); +} + +int +ugidfw_check_setacl_vnode(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type, struct acl *acl) +{ + + return (ugidfw_check_vp(cred, vp, MBI_ADMIN)); +} + +int +ugidfw_vnode_check_setextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace, const char *name, + struct uio *uio) +{ + + return (ugidfw_check_vp(cred, vp, MBI_WRITE)); +} + +int +ugidfw_vnode_check_setflags(struct ucred *cred, struct vnode *vp, + struct label *vplabel, u_long flags) +{ + + return (ugidfw_check_vp(cred, vp, MBI_ADMIN)); +} + +int +ugidfw_vnode_check_setmode(struct ucred *cred, struct vnode *vp, + struct label *vplabel, mode_t mode) +{ + + return (ugidfw_check_vp(cred, vp, MBI_ADMIN)); +} + +int +ugidfw_vnode_check_setowner(struct ucred *cred, struct vnode *vp, + struct label *vplabel, uid_t uid, gid_t gid) +{ + + return (ugidfw_check_vp(cred, vp, MBI_ADMIN)); +} + +int +ugidfw_vnode_check_setutimes(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct timespec atime, struct timespec utime) +{ + + return (ugidfw_check_vp(cred, vp, MBI_ADMIN)); +} + +int +ugidfw_vnode_check_stat(struct ucred *active_cred, + struct ucred *file_cred, struct vnode *vp, struct label *vplabel) +{ + + return (ugidfw_check_vp(active_cred, vp, MBI_STAT)); +} + +int +ugidfw_vnode_check_unlink(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp) +{ + int error; + + error = ugidfw_check_vp(cred, dvp, MBI_WRITE); + if (error) + return (error); + return (ugidfw_check_vp(cred, vp, MBI_WRITE)); +} diff --git a/sys/security/mac_ifoff/mac_ifoff.c b/sys/security/mac_ifoff/mac_ifoff.c new file mode 100644 index 0000000..8543d2b --- /dev/null +++ b/sys/security/mac_ifoff/mac_ifoff.c @@ -0,0 +1,173 @@ +/*- + * Copyright (c) 1999-2002, 2007 Robert N. M. Watson + * Copyright (c) 2001-2002 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Developed by the TrustedBSD Project. + * + * Limit access to interfaces until they are specifically administratively + * enabled. Prevents protocol stack-driven packet leakage in unsafe + * environments. + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <net/bpfdesc.h> +#include <net/if_types.h> + +#include <security/mac/mac_policy.h> + +SYSCTL_DECL(_security_mac); + +SYSCTL_NODE(_security_mac, OID_AUTO, ifoff, CTLFLAG_RW, 0, + "TrustedBSD mac_ifoff policy controls"); + +static int ifoff_enabled = 1; +SYSCTL_INT(_security_mac_ifoff, OID_AUTO, enabled, CTLFLAG_RW, + &ifoff_enabled, 0, "Enforce ifoff policy"); +TUNABLE_INT("security.mac.ifoff.enabled", &ifoff_enabled); + +static int ifoff_lo_enabled = 1; +SYSCTL_INT(_security_mac_ifoff, OID_AUTO, lo_enabled, CTLFLAG_RW, + &ifoff_lo_enabled, 0, "Enable loopback interfaces"); +TUNABLE_INT("security.mac.ifoff.lo_enabled", &ifoff_lo_enabled); + +static int ifoff_other_enabled = 0; +SYSCTL_INT(_security_mac_ifoff, OID_AUTO, other_enabled, CTLFLAG_RW, + &ifoff_other_enabled, 0, "Enable other interfaces"); +TUNABLE_INT("security.mac.ifoff.other_enabled", &ifoff_other_enabled); + +static int ifoff_bpfrecv_enabled = 0; +SYSCTL_INT(_security_mac_ifoff, OID_AUTO, bpfrecv_enabled, CTLFLAG_RW, + &ifoff_bpfrecv_enabled, 0, "Enable BPF reception even when interface " + "is disabled"); +TUNABLE_INT("security.mac.ifoff.bpfrecv.enabled", &ifoff_bpfrecv_enabled); + +static int +ifnet_check_outgoing(struct ifnet *ifp) +{ + + if (!ifoff_enabled) + return (0); + + if (ifoff_lo_enabled && ifp->if_type == IFT_LOOP) + return (0); + + if (ifoff_other_enabled && ifp->if_type != IFT_LOOP) + return (0); + + return (EPERM); +} + +static int +ifnet_check_incoming(struct ifnet *ifp, int viabpf) +{ + if (!ifoff_enabled) + return (0); + + if (ifoff_lo_enabled && ifp->if_type == IFT_LOOP) + return (0); + + if (ifoff_other_enabled && ifp->if_type != IFT_LOOP) + return (0); + + if (viabpf && ifoff_bpfrecv_enabled) + return (0); + + return (EPERM); +} + +/* + * Object-specific entry point implementations are sorted alphabetically by + * object type and then by operation. + */ +static int +ifoff_bpfdesc_check_receive(struct bpf_d *d, struct label *dlabel, + struct ifnet *ifp, struct label *ifplabel) +{ + + return (ifnet_check_incoming(ifp, 1)); +} + +static int +ifoff_ifnet_check_transmit(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + + return (ifnet_check_outgoing(ifp)); +} + +static int +ifoff_inpcb_check_deliver(struct inpcb *inp, struct label *inplabel, + struct mbuf *m, struct label *mlabel) +{ + + M_ASSERTPKTHDR(m); + if (m->m_pkthdr.rcvif != NULL) + return (ifnet_check_incoming(m->m_pkthdr.rcvif, 0)); + + return (0); +} + +static int +ifoff_socket_check_deliver(struct socket *so, struct label *solabel, + struct mbuf *m, struct label *mlabel) +{ + + M_ASSERTPKTHDR(m); + if (m->m_pkthdr.rcvif != NULL) + return (ifnet_check_incoming(m->m_pkthdr.rcvif, 0)); + + return (0); +} + +static struct mac_policy_ops ifoff_ops = +{ + .mpo_bpfdesc_check_receive = ifoff_bpfdesc_check_receive, + .mpo_ifnet_check_transmit = ifoff_ifnet_check_transmit, + .mpo_inpcb_check_deliver = ifoff_inpcb_check_deliver, + .mpo_socket_check_deliver = ifoff_socket_check_deliver, +}; + +MAC_POLICY_SET(&ifoff_ops, mac_ifoff, "TrustedBSD MAC/ifoff", + MPC_LOADTIME_FLAG_UNLOADOK, NULL, 0); diff --git a/sys/security/mac_lomac/mac_lomac.c b/sys/security/mac_lomac/mac_lomac.c new file mode 100644 index 0000000..9eb2701 --- /dev/null +++ b/sys/security/mac_lomac/mac_lomac.c @@ -0,0 +1,3076 @@ +/*- + * Copyright (c) 1999-2002, 2007-2008 Robert N. M. Watson + * Copyright (c) 2001-2005 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by NAI Labs, + * the Security Research Division of Network Associates, Inc. under + * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA + * CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Developed by the TrustedBSD Project. + * + * Low-watermark floating label mandatory integrity policy. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/acl.h> +#include <sys/conf.h> +#include <sys/extattr.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mman.h> +#include <sys/mount.h> +#include <sys/priv.h> +#include <sys/proc.h> +#include <sys/sbuf.h> +#include <sys/systm.h> +#include <sys/sysproto.h> +#include <sys/sysent.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sx.h> +#include <sys/pipe.h> +#include <sys/sysctl.h> +#include <sys/syslog.h> + +#include <fs/devfs/devfs.h> + +#include <net/bpfdesc.h> +#include <net/if.h> +#include <net/if_types.h> +#include <net/if_var.h> + +#include <netinet/in.h> +#include <netinet/in_pcb.h> +#include <netinet/ip_var.h> + +#include <vm/vm.h> + +#include <security/mac/mac_policy.h> +#include <security/mac/mac_framework.h> +#include <security/mac_lomac/mac_lomac.h> + +struct mac_lomac_proc { + struct mac_lomac mac_lomac; + struct mtx mtx; +}; + +SYSCTL_DECL(_security_mac); + +SYSCTL_NODE(_security_mac, OID_AUTO, lomac, CTLFLAG_RW, 0, + "TrustedBSD mac_lomac policy controls"); + +static int lomac_label_size = sizeof(struct mac_lomac); +SYSCTL_INT(_security_mac_lomac, OID_AUTO, label_size, CTLFLAG_RD, + &lomac_label_size, 0, "Size of struct mac_lomac"); + +static int lomac_enabled = 1; +SYSCTL_INT(_security_mac_lomac, OID_AUTO, enabled, CTLFLAG_RW, + &lomac_enabled, 0, "Enforce MAC/LOMAC policy"); +TUNABLE_INT("security.mac.lomac.enabled", &lomac_enabled); + +static int destroyed_not_inited; +SYSCTL_INT(_security_mac_lomac, OID_AUTO, destroyed_not_inited, CTLFLAG_RD, + &destroyed_not_inited, 0, "Count of labels destroyed but not inited"); + +static int trust_all_interfaces = 0; +SYSCTL_INT(_security_mac_lomac, OID_AUTO, trust_all_interfaces, CTLFLAG_RD, + &trust_all_interfaces, 0, "Consider all interfaces 'trusted' by MAC/LOMAC"); +TUNABLE_INT("security.mac.lomac.trust_all_interfaces", &trust_all_interfaces); + +static char trusted_interfaces[128]; +SYSCTL_STRING(_security_mac_lomac, OID_AUTO, trusted_interfaces, CTLFLAG_RD, + trusted_interfaces, 0, "Interfaces considered 'trusted' by MAC/LOMAC"); +TUNABLE_STR("security.mac.lomac.trusted_interfaces", trusted_interfaces, + sizeof(trusted_interfaces)); + +static int ptys_equal = 0; +SYSCTL_INT(_security_mac_lomac, OID_AUTO, ptys_equal, CTLFLAG_RW, + &ptys_equal, 0, "Label pty devices as lomac/equal on create"); +TUNABLE_INT("security.mac.lomac.ptys_equal", &ptys_equal); + +static int revocation_enabled = 1; +SYSCTL_INT(_security_mac_lomac, OID_AUTO, revocation_enabled, CTLFLAG_RW, + &revocation_enabled, 0, "Revoke access to objects on relabel"); +TUNABLE_INT("security.mac.lomac.revocation_enabled", &revocation_enabled); + +static int lomac_slot; +#define SLOT(l) ((struct mac_lomac *)mac_label_get((l), lomac_slot)) +#define SLOT_SET(l, val) mac_label_set((l), lomac_slot, (uintptr_t)(val)) +#define PSLOT(l) ((struct mac_lomac_proc *) \ + mac_label_get((l), lomac_slot)) +#define PSLOT_SET(l, val) mac_label_set((l), lomac_slot, (uintptr_t)(val)) + +MALLOC_DEFINE(M_LOMAC, "mac_lomac_label", "MAC/LOMAC labels"); + +static struct mac_lomac * +lomac_alloc(int flag) +{ + struct mac_lomac *ml; + + ml = malloc(sizeof(*ml), M_LOMAC, M_ZERO | flag); + + return (ml); +} + +static void +lomac_free(struct mac_lomac *ml) +{ + + if (ml != NULL) + free(ml, M_LOMAC); + else + atomic_add_int(&destroyed_not_inited, 1); +} + +static int +lomac_atmostflags(struct mac_lomac *ml, int flags) +{ + + if ((ml->ml_flags & flags) != ml->ml_flags) + return (EINVAL); + return (0); +} + +static int +lomac_dominate_element(struct mac_lomac_element *a, + struct mac_lomac_element *b) +{ + + switch (a->mle_type) { + case MAC_LOMAC_TYPE_EQUAL: + case MAC_LOMAC_TYPE_HIGH: + return (1); + + case MAC_LOMAC_TYPE_LOW: + switch (b->mle_type) { + case MAC_LOMAC_TYPE_GRADE: + case MAC_LOMAC_TYPE_HIGH: + return (0); + + case MAC_LOMAC_TYPE_EQUAL: + case MAC_LOMAC_TYPE_LOW: + return (1); + + default: + panic("lomac_dominate_element: b->mle_type invalid"); + } + + case MAC_LOMAC_TYPE_GRADE: + switch (b->mle_type) { + case MAC_LOMAC_TYPE_EQUAL: + case MAC_LOMAC_TYPE_LOW: + return (1); + + case MAC_LOMAC_TYPE_HIGH: + return (0); + + case MAC_LOMAC_TYPE_GRADE: + return (a->mle_grade >= b->mle_grade); + + default: + panic("lomac_dominate_element: b->mle_type invalid"); + } + + default: + panic("lomac_dominate_element: a->mle_type invalid"); + } +} + +static int +lomac_range_in_range(struct mac_lomac *rangea, struct mac_lomac *rangeb) +{ + + return (lomac_dominate_element(&rangeb->ml_rangehigh, + &rangea->ml_rangehigh) && + lomac_dominate_element(&rangea->ml_rangelow, + &rangeb->ml_rangelow)); +} + +static int +lomac_single_in_range(struct mac_lomac *single, struct mac_lomac *range) +{ + + KASSERT((single->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, + ("lomac_single_in_range: a not single")); + KASSERT((range->ml_flags & MAC_LOMAC_FLAG_RANGE) != 0, + ("lomac_single_in_range: b not range")); + + return (lomac_dominate_element(&range->ml_rangehigh, + &single->ml_single) && lomac_dominate_element(&single->ml_single, + &range->ml_rangelow)); +} + +static int +lomac_auxsingle_in_range(struct mac_lomac *single, struct mac_lomac *range) +{ + + KASSERT((single->ml_flags & MAC_LOMAC_FLAG_AUX) != 0, + ("lomac_single_in_range: a not auxsingle")); + KASSERT((range->ml_flags & MAC_LOMAC_FLAG_RANGE) != 0, + ("lomac_single_in_range: b not range")); + + return (lomac_dominate_element(&range->ml_rangehigh, + &single->ml_auxsingle) && + lomac_dominate_element(&single->ml_auxsingle, + &range->ml_rangelow)); +} + +static int +lomac_dominate_single(struct mac_lomac *a, struct mac_lomac *b) +{ + KASSERT((a->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, + ("lomac_dominate_single: a not single")); + KASSERT((b->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, + ("lomac_dominate_single: b not single")); + + return (lomac_dominate_element(&a->ml_single, &b->ml_single)); +} + +static int +lomac_subject_dominate(struct mac_lomac *a, struct mac_lomac *b) +{ + KASSERT((~a->ml_flags & + (MAC_LOMAC_FLAG_SINGLE | MAC_LOMAC_FLAG_RANGE)) == 0, + ("lomac_dominate_single: a not subject")); + KASSERT((b->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, + ("lomac_dominate_single: b not single")); + + return (lomac_dominate_element(&a->ml_rangehigh, &b->ml_single)); +} + +static int +lomac_equal_element(struct mac_lomac_element *a, struct mac_lomac_element *b) +{ + + if (a->mle_type == MAC_LOMAC_TYPE_EQUAL || + b->mle_type == MAC_LOMAC_TYPE_EQUAL) + return (1); + + return (a->mle_type == b->mle_type && a->mle_grade == b->mle_grade); +} + +static int +lomac_equal_single(struct mac_lomac *a, struct mac_lomac *b) +{ + + KASSERT((a->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, + ("lomac_equal_single: a not single")); + KASSERT((b->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, + ("lomac_equal_single: b not single")); + + return (lomac_equal_element(&a->ml_single, &b->ml_single)); +} + +static int +lomac_contains_equal(struct mac_lomac *ml) +{ + + if (ml->ml_flags & MAC_LOMAC_FLAG_SINGLE) + if (ml->ml_single.mle_type == MAC_LOMAC_TYPE_EQUAL) + return (1); + if (ml->ml_flags & MAC_LOMAC_FLAG_AUX) + if (ml->ml_auxsingle.mle_type == MAC_LOMAC_TYPE_EQUAL) + return (1); + + if (ml->ml_flags & MAC_LOMAC_FLAG_RANGE) { + if (ml->ml_rangelow.mle_type == MAC_LOMAC_TYPE_EQUAL) + return (1); + if (ml->ml_rangehigh.mle_type == MAC_LOMAC_TYPE_EQUAL) + return (1); + } + + return (0); +} + +static int +lomac_subject_privileged(struct mac_lomac *ml) +{ + + KASSERT((ml->ml_flags & MAC_LOMAC_FLAGS_BOTH) == + MAC_LOMAC_FLAGS_BOTH, + ("lomac_subject_privileged: subject doesn't have both labels")); + + /* If the single is EQUAL, it's ok. */ + if (ml->ml_single.mle_type == MAC_LOMAC_TYPE_EQUAL) + return (0); + + /* If either range endpoint is EQUAL, it's ok. */ + if (ml->ml_rangelow.mle_type == MAC_LOMAC_TYPE_EQUAL || + ml->ml_rangehigh.mle_type == MAC_LOMAC_TYPE_EQUAL) + return (0); + + /* If the range is low-high, it's ok. */ + if (ml->ml_rangelow.mle_type == MAC_LOMAC_TYPE_LOW && + ml->ml_rangehigh.mle_type == MAC_LOMAC_TYPE_HIGH) + return (0); + + /* It's not ok. */ + return (EPERM); +} + +static int +lomac_high_single(struct mac_lomac *ml) +{ + + KASSERT((ml->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, + ("lomac_high_single: mac_lomac not single")); + + return (ml->ml_single.mle_type == MAC_LOMAC_TYPE_HIGH); +} + +static int +lomac_valid(struct mac_lomac *ml) +{ + + if (ml->ml_flags & MAC_LOMAC_FLAG_SINGLE) { + switch (ml->ml_single.mle_type) { + case MAC_LOMAC_TYPE_GRADE: + case MAC_LOMAC_TYPE_EQUAL: + case MAC_LOMAC_TYPE_HIGH: + case MAC_LOMAC_TYPE_LOW: + break; + + default: + return (EINVAL); + } + } else { + if (ml->ml_single.mle_type != MAC_LOMAC_TYPE_UNDEF) + return (EINVAL); + } + + if (ml->ml_flags & MAC_LOMAC_FLAG_AUX) { + switch (ml->ml_auxsingle.mle_type) { + case MAC_LOMAC_TYPE_GRADE: + case MAC_LOMAC_TYPE_EQUAL: + case MAC_LOMAC_TYPE_HIGH: + case MAC_LOMAC_TYPE_LOW: + break; + + default: + return (EINVAL); + } + } else { + if (ml->ml_auxsingle.mle_type != MAC_LOMAC_TYPE_UNDEF) + return (EINVAL); + } + + if (ml->ml_flags & MAC_LOMAC_FLAG_RANGE) { + switch (ml->ml_rangelow.mle_type) { + case MAC_LOMAC_TYPE_GRADE: + case MAC_LOMAC_TYPE_EQUAL: + case MAC_LOMAC_TYPE_HIGH: + case MAC_LOMAC_TYPE_LOW: + break; + + default: + return (EINVAL); + } + + switch (ml->ml_rangehigh.mle_type) { + case MAC_LOMAC_TYPE_GRADE: + case MAC_LOMAC_TYPE_EQUAL: + case MAC_LOMAC_TYPE_HIGH: + case MAC_LOMAC_TYPE_LOW: + break; + + default: + return (EINVAL); + } + if (!lomac_dominate_element(&ml->ml_rangehigh, + &ml->ml_rangelow)) + return (EINVAL); + } else { + if (ml->ml_rangelow.mle_type != MAC_LOMAC_TYPE_UNDEF || + ml->ml_rangehigh.mle_type != MAC_LOMAC_TYPE_UNDEF) + return (EINVAL); + } + + return (0); +} + +static void +lomac_set_range(struct mac_lomac *ml, u_short typelow, u_short gradelow, + u_short typehigh, u_short gradehigh) +{ + + ml->ml_rangelow.mle_type = typelow; + ml->ml_rangelow.mle_grade = gradelow; + ml->ml_rangehigh.mle_type = typehigh; + ml->ml_rangehigh.mle_grade = gradehigh; + ml->ml_flags |= MAC_LOMAC_FLAG_RANGE; +} + +static void +lomac_set_single(struct mac_lomac *ml, u_short type, u_short grade) +{ + + ml->ml_single.mle_type = type; + ml->ml_single.mle_grade = grade; + ml->ml_flags |= MAC_LOMAC_FLAG_SINGLE; +} + +static void +lomac_copy_range(struct mac_lomac *labelfrom, struct mac_lomac *labelto) +{ + + KASSERT((labelfrom->ml_flags & MAC_LOMAC_FLAG_RANGE) != 0, + ("lomac_copy_range: labelfrom not range")); + + labelto->ml_rangelow = labelfrom->ml_rangelow; + labelto->ml_rangehigh = labelfrom->ml_rangehigh; + labelto->ml_flags |= MAC_LOMAC_FLAG_RANGE; +} + +static void +lomac_copy_single(struct mac_lomac *labelfrom, struct mac_lomac *labelto) +{ + + KASSERT((labelfrom->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, + ("lomac_copy_single: labelfrom not single")); + + labelto->ml_single = labelfrom->ml_single; + labelto->ml_flags |= MAC_LOMAC_FLAG_SINGLE; +} + +static void +lomac_copy_auxsingle(struct mac_lomac *labelfrom, struct mac_lomac *labelto) +{ + + KASSERT((labelfrom->ml_flags & MAC_LOMAC_FLAG_AUX) != 0, + ("lomac_copy_auxsingle: labelfrom not auxsingle")); + + labelto->ml_auxsingle = labelfrom->ml_auxsingle; + labelto->ml_flags |= MAC_LOMAC_FLAG_AUX; +} + +static void +lomac_copy(struct mac_lomac *source, struct mac_lomac *dest) +{ + + if (source->ml_flags & MAC_LOMAC_FLAG_SINGLE) + lomac_copy_single(source, dest); + if (source->ml_flags & MAC_LOMAC_FLAG_AUX) + lomac_copy_auxsingle(source, dest); + if (source->ml_flags & MAC_LOMAC_FLAG_RANGE) + lomac_copy_range(source, dest); +} + +static int lomac_to_string(struct sbuf *sb, struct mac_lomac *ml); + +static int +maybe_demote(struct mac_lomac *subjlabel, struct mac_lomac *objlabel, + const char *actionname, const char *objname, struct vnode *vp) +{ + struct sbuf subjlabel_sb, subjtext_sb, objlabel_sb; + char *subjlabeltext, *objlabeltext, *subjtext; + struct mac_lomac cached_subjlabel; + struct mac_lomac_proc *subj; + struct vattr va; + struct proc *p; + pid_t pgid; + + subj = PSLOT(curthread->td_proc->p_label); + + p = curthread->td_proc; + mtx_lock(&subj->mtx); + if (subj->mac_lomac.ml_flags & MAC_LOMAC_FLAG_UPDATE) { + /* + * Check to see if the pending demotion would be more or less + * severe than this one, and keep the more severe. This can + * only happen for a multi-threaded application. + */ + if (lomac_dominate_single(objlabel, &subj->mac_lomac)) { + mtx_unlock(&subj->mtx); + return (0); + } + } + bzero(&subj->mac_lomac, sizeof(subj->mac_lomac)); + /* + * Always demote the single label. + */ + lomac_copy_single(objlabel, &subj->mac_lomac); + /* + * Start with the original range, then minimize each side of the + * range to the point of not dominating the object. The high side + * will always be demoted, of course. + */ + lomac_copy_range(subjlabel, &subj->mac_lomac); + if (!lomac_dominate_element(&objlabel->ml_single, + &subj->mac_lomac.ml_rangelow)) + subj->mac_lomac.ml_rangelow = objlabel->ml_single; + subj->mac_lomac.ml_rangehigh = objlabel->ml_single; + subj->mac_lomac.ml_flags |= MAC_LOMAC_FLAG_UPDATE; + thread_lock(curthread); + curthread->td_flags |= TDF_ASTPENDING | TDF_MACPEND; + thread_unlock(curthread); + + /* + * Avoid memory allocation while holding a mutex; cache the label. + */ + lomac_copy_single(&subj->mac_lomac, &cached_subjlabel); + mtx_unlock(&subj->mtx); + + sbuf_new(&subjlabel_sb, NULL, 0, SBUF_AUTOEXTEND); + lomac_to_string(&subjlabel_sb, subjlabel); + sbuf_finish(&subjlabel_sb); + subjlabeltext = sbuf_data(&subjlabel_sb); + + sbuf_new(&subjtext_sb, NULL, 0, SBUF_AUTOEXTEND); + lomac_to_string(&subjtext_sb, &subj->mac_lomac); + sbuf_finish(&subjtext_sb); + subjtext = sbuf_data(&subjtext_sb); + + sbuf_new(&objlabel_sb, NULL, 0, SBUF_AUTOEXTEND); + lomac_to_string(&objlabel_sb, objlabel); + sbuf_finish(&objlabel_sb); + objlabeltext = sbuf_data(&objlabel_sb); + + pgid = p->p_pgrp->pg_id; /* XXX could be stale? */ + if (vp != NULL && VOP_GETATTR(vp, &va, curthread->td_ucred) == 0) { + log(LOG_INFO, "LOMAC: level-%s subject p%dg%du%d:%s demoted to" + " level %s after %s a level-%s %s (inode=%ld, " + "mountpount=%s)\n", + subjlabeltext, p->p_pid, pgid, curthread->td_ucred->cr_uid, + p->p_comm, subjtext, actionname, objlabeltext, objname, + va.va_fileid, vp->v_mount->mnt_stat.f_mntonname); + } else { + log(LOG_INFO, "LOMAC: level-%s subject p%dg%du%d:%s demoted to" + " level %s after %s a level-%s %s\n", + subjlabeltext, p->p_pid, pgid, curthread->td_ucred->cr_uid, + p->p_comm, subjtext, actionname, objlabeltext, objname); + } + + sbuf_delete(&subjlabel_sb); + sbuf_delete(&subjtext_sb); + sbuf_delete(&objlabel_sb); + + return (0); +} + +/* + * Relabel "to" to "from" only if "from" is a valid label (contains at least + * a single), as for a relabel operation which may or may not involve a + * relevant label. + */ +static void +try_relabel(struct mac_lomac *from, struct mac_lomac *to) +{ + + if (from->ml_flags & MAC_LOMAC_FLAG_SINGLE) { + bzero(to, sizeof(*to)); + lomac_copy(from, to); + } +} + +/* + * Policy module operations. + */ +static void +lomac_init(struct mac_policy_conf *conf) +{ + +} + +/* + * Label operations. + */ +static void +lomac_init_label(struct label *label) +{ + + SLOT_SET(label, lomac_alloc(M_WAITOK)); +} + +static int +lomac_init_label_waitcheck(struct label *label, int flag) +{ + + SLOT_SET(label, lomac_alloc(flag)); + if (SLOT(label) == NULL) + return (ENOMEM); + + return (0); +} + +static void +lomac_destroy_label(struct label *label) +{ + + lomac_free(SLOT(label)); + SLOT_SET(label, NULL); +} + +static int +lomac_element_to_string(struct sbuf *sb, struct mac_lomac_element *element) +{ + + switch (element->mle_type) { + case MAC_LOMAC_TYPE_HIGH: + return (sbuf_printf(sb, "high")); + + case MAC_LOMAC_TYPE_LOW: + return (sbuf_printf(sb, "low")); + + case MAC_LOMAC_TYPE_EQUAL: + return (sbuf_printf(sb, "equal")); + + case MAC_LOMAC_TYPE_GRADE: + return (sbuf_printf(sb, "%d", element->mle_grade)); + + default: + panic("lomac_element_to_string: invalid type (%d)", + element->mle_type); + } +} + +static int +lomac_to_string(struct sbuf *sb, struct mac_lomac *ml) +{ + + if (ml->ml_flags & MAC_LOMAC_FLAG_SINGLE) { + if (lomac_element_to_string(sb, &ml->ml_single) == -1) + return (EINVAL); + } + + if (ml->ml_flags & MAC_LOMAC_FLAG_AUX) { + if (sbuf_putc(sb, '[') == -1) + return (EINVAL); + + if (lomac_element_to_string(sb, &ml->ml_auxsingle) == -1) + return (EINVAL); + + if (sbuf_putc(sb, ']') == -1) + return (EINVAL); + } + + if (ml->ml_flags & MAC_LOMAC_FLAG_RANGE) { + if (sbuf_putc(sb, '(') == -1) + return (EINVAL); + + if (lomac_element_to_string(sb, &ml->ml_rangelow) == -1) + return (EINVAL); + + if (sbuf_putc(sb, '-') == -1) + return (EINVAL); + + if (lomac_element_to_string(sb, &ml->ml_rangehigh) == -1) + return (EINVAL); + + if (sbuf_putc(sb, ')') == -1) + return (EINVAL); + } + + return (0); +} + +static int +lomac_externalize_label(struct label *label, char *element_name, + struct sbuf *sb, int *claimed) +{ + struct mac_lomac *ml; + + if (strcmp(MAC_LOMAC_LABEL_NAME, element_name) != 0) + return (0); + + (*claimed)++; + + ml = SLOT(label); + + return (lomac_to_string(sb, ml)); +} + +static int +lomac_parse_element(struct mac_lomac_element *element, char *string) +{ + + if (strcmp(string, "high") == 0 || strcmp(string, "hi") == 0) { + element->mle_type = MAC_LOMAC_TYPE_HIGH; + element->mle_grade = MAC_LOMAC_TYPE_UNDEF; + } else if (strcmp(string, "low") == 0 || strcmp(string, "lo") == 0) { + element->mle_type = MAC_LOMAC_TYPE_LOW; + element->mle_grade = MAC_LOMAC_TYPE_UNDEF; + } else if (strcmp(string, "equal") == 0 || + strcmp(string, "eq") == 0) { + element->mle_type = MAC_LOMAC_TYPE_EQUAL; + element->mle_grade = MAC_LOMAC_TYPE_UNDEF; + } else { + char *p0, *p1; + int d; + + p0 = string; + d = strtol(p0, &p1, 10); + + if (d < 0 || d > 65535) + return (EINVAL); + element->mle_type = MAC_LOMAC_TYPE_GRADE; + element->mle_grade = d; + + if (p1 == p0 || *p1 != '\0') + return (EINVAL); + } + + return (0); +} + +/* + * Note: destructively consumes the string, make a local copy before calling + * if that's a problem. + */ +static int +lomac_parse(struct mac_lomac *ml, char *string) +{ + char *range, *rangeend, *rangehigh, *rangelow, *single, *auxsingle, + *auxsingleend; + int error; + + /* Do we have a range? */ + single = string; + range = index(string, '('); + if (range == single) + single = NULL; + auxsingle = index(string, '['); + if (auxsingle == single) + single = NULL; + if (range != NULL && auxsingle != NULL) + return (EINVAL); + rangelow = rangehigh = NULL; + if (range != NULL) { + /* Nul terminate the end of the single string. */ + *range = '\0'; + range++; + rangelow = range; + rangehigh = index(rangelow, '-'); + if (rangehigh == NULL) + return (EINVAL); + rangehigh++; + if (*rangelow == '\0' || *rangehigh == '\0') + return (EINVAL); + rangeend = index(rangehigh, ')'); + if (rangeend == NULL) + return (EINVAL); + if (*(rangeend + 1) != '\0') + return (EINVAL); + /* Nul terminate the ends of the ranges. */ + *(rangehigh - 1) = '\0'; + *rangeend = '\0'; + } + KASSERT((rangelow != NULL && rangehigh != NULL) || + (rangelow == NULL && rangehigh == NULL), + ("lomac_internalize_label: range mismatch")); + if (auxsingle != NULL) { + /* Nul terminate the end of the single string. */ + *auxsingle = '\0'; + auxsingle++; + auxsingleend = index(auxsingle, ']'); + if (auxsingleend == NULL) + return (EINVAL); + if (*(auxsingleend + 1) != '\0') + return (EINVAL); + /* Nul terminate the end of the auxsingle. */ + *auxsingleend = '\0'; + } + + bzero(ml, sizeof(*ml)); + if (single != NULL) { + error = lomac_parse_element(&ml->ml_single, single); + if (error) + return (error); + ml->ml_flags |= MAC_LOMAC_FLAG_SINGLE; + } + + if (auxsingle != NULL) { + error = lomac_parse_element(&ml->ml_auxsingle, auxsingle); + if (error) + return (error); + ml->ml_flags |= MAC_LOMAC_FLAG_AUX; + } + + if (rangelow != NULL) { + error = lomac_parse_element(&ml->ml_rangelow, rangelow); + if (error) + return (error); + error = lomac_parse_element(&ml->ml_rangehigh, rangehigh); + if (error) + return (error); + ml->ml_flags |= MAC_LOMAC_FLAG_RANGE; + } + + error = lomac_valid(ml); + if (error) + return (error); + + return (0); +} + +static int +lomac_internalize_label(struct label *label, char *element_name, + char *element_data, int *claimed) +{ + struct mac_lomac *ml, ml_temp; + int error; + + if (strcmp(MAC_LOMAC_LABEL_NAME, element_name) != 0) + return (0); + + (*claimed)++; + + error = lomac_parse(&ml_temp, element_data); + if (error) + return (error); + + ml = SLOT(label); + *ml = ml_temp; + + return (0); +} + +static void +lomac_copy_label(struct label *src, struct label *dest) +{ + + *SLOT(dest) = *SLOT(src); +} + +/* + * Object-specific entry point implementations are sorted alphabetically by + * object type name and then by operation. + */ +static int +lomac_bpfdesc_check_receive(struct bpf_d *d, struct label *dlabel, + struct ifnet *ifp, struct label *ifplabel) +{ + struct mac_lomac *a, *b; + + if (!lomac_enabled) + return (0); + + a = SLOT(dlabel); + b = SLOT(ifplabel); + + if (lomac_equal_single(a, b)) + return (0); + return (EACCES); +} + +static void +lomac_bpfdesc_create(struct ucred *cred, struct bpf_d *d, + struct label *dlabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(dlabel); + + lomac_copy_single(source, dest); +} + +static void +lomac_bpfdesc_create_mbuf(struct bpf_d *d, struct label *dlabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(dlabel); + dest = SLOT(mlabel); + + lomac_copy_single(source, dest); +} + +static int +lomac_cred_check_relabel(struct ucred *cred, struct label *newlabel) +{ + struct mac_lomac *subj, *new; + int error; + + subj = SLOT(cred->cr_label); + new = SLOT(newlabel); + + /* + * If there is a LOMAC label update for the credential, it may be an + * update of the single, range, or both. + */ + error = lomac_atmostflags(new, MAC_LOMAC_FLAGS_BOTH); + if (error) + return (error); + + /* + * If the LOMAC label is to be changed, authorize as appropriate. + */ + if (new->ml_flags & MAC_LOMAC_FLAGS_BOTH) { + /* + * Fill in the missing parts from the previous label. + */ + if ((new->ml_flags & MAC_LOMAC_FLAG_SINGLE) == 0) + lomac_copy_single(subj, new); + if ((new->ml_flags & MAC_LOMAC_FLAG_RANGE) == 0) + lomac_copy_range(subj, new); + + /* + * To change the LOMAC range on a credential, the new range + * label must be in the current range. + */ + if (!lomac_range_in_range(new, subj)) + return (EPERM); + + /* + * To change the LOMAC single label on a credential, the new + * single label must be in the new range. Implicitly from + * the previous check, the new single is in the old range. + */ + if (!lomac_single_in_range(new, new)) + return (EPERM); + + /* + * To have EQUAL in any component of the new credential LOMAC + * label, the subject must already have EQUAL in their label. + */ + if (lomac_contains_equal(new)) { + error = lomac_subject_privileged(subj); + if (error) + return (error); + } + + /* + * XXXMAC: Additional consistency tests regarding the single + * and range of the new label might be performed here. + */ + } + + return (0); +} + +static int +lomac_cred_check_visible(struct ucred *cr1, struct ucred *cr2) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cr1->cr_label); + obj = SLOT(cr2->cr_label); + + /* XXX: range */ + if (!lomac_dominate_single(obj, subj)) + return (ESRCH); + + return (0); +} + +static void +lomac_cred_create_init(struct ucred *cred) +{ + struct mac_lomac *dest; + + dest = SLOT(cred->cr_label); + + lomac_set_single(dest, MAC_LOMAC_TYPE_HIGH, 0); + lomac_set_range(dest, MAC_LOMAC_TYPE_LOW, 0, MAC_LOMAC_TYPE_HIGH, 0); +} + +static void +lomac_cred_create_swapper(struct ucred *cred) +{ + struct mac_lomac *dest; + + dest = SLOT(cred->cr_label); + + lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); + lomac_set_range(dest, MAC_LOMAC_TYPE_LOW, 0, MAC_LOMAC_TYPE_HIGH, 0); +} + +static void +lomac_cred_relabel(struct ucred *cred, struct label *newlabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(newlabel); + dest = SLOT(cred->cr_label); + + try_relabel(source, dest); +} + +static void +lomac_devfs_create_device(struct ucred *cred, struct mount *mp, + struct cdev *dev, struct devfs_dirent *de, struct label *delabel) +{ + struct mac_lomac *ml; + int lomac_type; + + ml = SLOT(delabel); + if (strcmp(dev->si_name, "null") == 0 || + strcmp(dev->si_name, "zero") == 0 || + strcmp(dev->si_name, "random") == 0 || + strncmp(dev->si_name, "fd/", strlen("fd/")) == 0 || + strncmp(dev->si_name, "ttyv", strlen("ttyv")) == 0) + lomac_type = MAC_LOMAC_TYPE_EQUAL; + else if (ptys_equal && + (strncmp(dev->si_name, "ttyp", strlen("ttyp")) == 0 || + strncmp(dev->si_name, "ptyp", strlen("ptyp")) == 0)) + lomac_type = MAC_LOMAC_TYPE_EQUAL; + else + lomac_type = MAC_LOMAC_TYPE_HIGH; + lomac_set_single(ml, lomac_type, 0); +} + +static void +lomac_devfs_create_directory(struct mount *mp, char *dirname, int dirnamelen, + struct devfs_dirent *de, struct label *delabel) +{ + struct mac_lomac *ml; + + ml = SLOT(delabel); + lomac_set_single(ml, MAC_LOMAC_TYPE_HIGH, 0); +} + +static void +lomac_devfs_create_symlink(struct ucred *cred, struct mount *mp, + struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de, + struct label *delabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(delabel); + + lomac_copy_single(source, dest); +} + +static void +lomac_devfs_update(struct mount *mp, struct devfs_dirent *de, + struct label *delabel, struct vnode *vp, struct label *vplabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(vplabel); + dest = SLOT(delabel); + + lomac_copy(source, dest); +} + +static void +lomac_devfs_vnode_associate(struct mount *mp, struct label *mplabel, + struct devfs_dirent *de, struct label *delabel, struct vnode *vp, + struct label *vplabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(delabel); + dest = SLOT(vplabel); + + lomac_copy_single(source, dest); +} + +static int +lomac_ifnet_check_relabel(struct ucred *cred, struct ifnet *ifp, + struct label *ifplabel, struct label *newlabel) +{ + struct mac_lomac *subj, *new; + int error; + + subj = SLOT(cred->cr_label); + new = SLOT(newlabel); + + /* + * If there is a LOMAC label update for the interface, it may be an + * update of the single, range, or both. + */ + error = lomac_atmostflags(new, MAC_LOMAC_FLAGS_BOTH); + if (error) + return (error); + + /* + * Relabling network interfaces requires LOMAC privilege. + */ + error = lomac_subject_privileged(subj); + if (error) + return (error); + + /* + * If the LOMAC label is to be changed, authorize as appropriate. + */ + if (new->ml_flags & MAC_LOMAC_FLAGS_BOTH) { + /* + * Fill in the missing parts from the previous label. + */ + if ((new->ml_flags & MAC_LOMAC_FLAG_SINGLE) == 0) + lomac_copy_single(subj, new); + if ((new->ml_flags & MAC_LOMAC_FLAG_RANGE) == 0) + lomac_copy_range(subj, new); + + /* + * Rely on the traditional superuser status for the LOMAC + * interface relabel requirements. XXXMAC: This will go + * away. + * + * XXXRW: This is also redundant to a higher layer check. + */ + error = priv_check_cred(cred, PRIV_NET_SETIFMAC, 0); + if (error) + return (EPERM); + + /* + * XXXMAC: Additional consistency tests regarding the single + * and the range of the new label might be performed here. + */ + } + + return (0); +} + +static int +lomac_ifnet_check_transmit(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_lomac *p, *i; + + if (!lomac_enabled) + return (0); + + p = SLOT(mlabel); + i = SLOT(ifplabel); + + return (lomac_single_in_range(p, i) ? 0 : EACCES); +} + +static void +lomac_ifnet_create(struct ifnet *ifp, struct label *ifplabel) +{ + char tifname[IFNAMSIZ], *p, *q; + char tiflist[sizeof(trusted_interfaces)]; + struct mac_lomac *dest; + int len, grade; + + dest = SLOT(ifplabel); + + if (ifp->if_type == IFT_LOOP) { + grade = MAC_LOMAC_TYPE_EQUAL; + goto set; + } + + if (trust_all_interfaces) { + grade = MAC_LOMAC_TYPE_HIGH; + goto set; + } + + grade = MAC_LOMAC_TYPE_LOW; + + if (trusted_interfaces[0] == '\0' || + !strvalid(trusted_interfaces, sizeof(trusted_interfaces))) + goto set; + + bzero(tiflist, sizeof(tiflist)); + for (p = trusted_interfaces, q = tiflist; *p != '\0'; p++, q++) + if(*p != ' ' && *p != '\t') + *q = *p; + + for (p = q = tiflist;; p++) { + if (*p == ',' || *p == '\0') { + len = p - q; + if (len < IFNAMSIZ) { + bzero(tifname, sizeof(tifname)); + bcopy(q, tifname, len); + if (strcmp(tifname, ifp->if_xname) == 0) { + grade = MAC_LOMAC_TYPE_HIGH; + break; + } + } + else { + *p = '\0'; + printf("MAC/LOMAC warning: interface name " + "\"%s\" is too long (must be < %d)\n", + q, IFNAMSIZ); + } + if (*p == '\0') + break; + q = p + 1; + } + } +set: + lomac_set_single(dest, grade, 0); + lomac_set_range(dest, grade, 0, grade, 0); +} + +static void +lomac_ifnet_create_mbuf(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(ifplabel); + dest = SLOT(mlabel); + + lomac_copy_single(source, dest); +} + +static void +lomac_ifnet_relabel(struct ucred *cred, struct ifnet *ifp, + struct label *ifplabel, struct label *newlabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(newlabel); + dest = SLOT(ifplabel); + + try_relabel(source, dest); +} + +static int +lomac_inpcb_check_deliver(struct inpcb *inp, struct label *inplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_lomac *p, *i; + + if (!lomac_enabled) + return (0); + + p = SLOT(mlabel); + i = SLOT(inplabel); + + return (lomac_equal_single(p, i) ? 0 : EACCES); +} + +static int +lomac_inpcb_check_visible(struct ucred *cred, struct inpcb *inp, + struct label *inplabel) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(inplabel); + + if (!lomac_dominate_single(obj, subj)) + return (ENOENT); + + return (0); +} + +static void +lomac_inpcb_create(struct socket *so, struct label *solabel, + struct inpcb *inp, struct label *inplabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(solabel); + dest = SLOT(inplabel); + + lomac_copy_single(source, dest); +} + +static void +lomac_inpcb_create_mbuf(struct inpcb *inp, struct label *inplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(inplabel); + dest = SLOT(mlabel); + + lomac_copy_single(source, dest); +} + +static void +lomac_inpcb_sosetlabel(struct socket *so, struct label *solabel, + struct inpcb *inp, struct label *inplabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(solabel); + dest = SLOT(inplabel); + + lomac_copy_single(source, dest); +} + +static void +lomac_ip6q_create(struct mbuf *m, struct label *mlabel, struct ip6q *q6, + struct label *q6label) +{ + struct mac_lomac *source, *dest; + + source = SLOT(mlabel); + dest = SLOT(q6label); + + lomac_copy_single(source, dest); +} + +static int +lomac_ip6q_match(struct mbuf *m, struct label *mlabel, struct ip6q *q6, + struct label *q6label) +{ + struct mac_lomac *a, *b; + + a = SLOT(q6label); + b = SLOT(mlabel); + + return (lomac_equal_single(a, b)); +} + +static void +lomac_ip6q_reassemble(struct ip6q *q6, struct label *q6label, struct mbuf *m, + struct label *mlabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(q6label); + dest = SLOT(mlabel); + + /* Just use the head, since we require them all to match. */ + lomac_copy_single(source, dest); +} + +static void +lomac_ip6q_update(struct mbuf *m, struct label *mlabel, struct ip6q *q6, + struct label *q6label) +{ + + /* NOOP: we only accept matching labels, so no need to update */ +} + +static void +lomac_ipq_create(struct mbuf *m, struct label *mlabel, struct ipq *q, + struct label *qlabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(mlabel); + dest = SLOT(qlabel); + + lomac_copy_single(source, dest); +} + +static int +lomac_ipq_match(struct mbuf *m, struct label *mlabel, struct ipq *q, + struct label *qlabel) +{ + struct mac_lomac *a, *b; + + a = SLOT(qlabel); + b = SLOT(mlabel); + + return (lomac_equal_single(a, b)); +} + +static void +lomac_ipq_reassemble(struct ipq *q, struct label *qlabel, struct mbuf *m, + struct label *mlabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(qlabel); + dest = SLOT(mlabel); + + /* Just use the head, since we require them all to match. */ + lomac_copy_single(source, dest); +} + +static void +lomac_ipq_update(struct mbuf *m, struct label *mlabel, struct ipq *q, + struct label *qlabel) +{ + + /* NOOP: we only accept matching labels, so no need to update */ +} + +static int +lomac_kld_check_load(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (lomac_subject_privileged(subj)) + return (EPERM); + + if (!lomac_high_single(obj)) + return (EACCES); + + return (0); +} + +static void +lomac_mount_create(struct ucred *cred, struct mount *mp, + struct label *mplabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(mplabel); + lomac_copy_single(source, dest); +} + +static void +lomac_netatalk_aarp_send(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_lomac *dest; + + dest = SLOT(mlabel); + + lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); +} + +static void +lomac_netinet_arp_send(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_lomac *dest; + + dest = SLOT(mlabel); + + lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); +} + +static void +lomac_netinet_firewall_reply(struct mbuf *mrecv, struct label *mrecvlabel, + struct mbuf *msend, struct label *msendlabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(mrecvlabel); + dest = SLOT(msendlabel); + + lomac_copy_single(source, dest); +} + +static void +lomac_netinet_firewall_send(struct mbuf *m, struct label *mlabel) +{ + struct mac_lomac *dest; + + dest = SLOT(mlabel); + + /* XXX: where is the label for the firewall really comming from? */ + lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); +} + +static void +lomac_netinet_fragment(struct mbuf *m, struct label *mlabel, + struct mbuf *frag, struct label *fraglabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(mlabel); + dest = SLOT(fraglabel); + + lomac_copy_single(source, dest); +} + +static void +lomac_netinet_icmp_reply(struct mbuf *mrecv, struct label *mrecvlabel, + struct mbuf *msend, struct label *msendlabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(mrecvlabel); + dest = SLOT(msendlabel); + + lomac_copy_single(source, dest); +} + +static void +lomac_netinet_igmp_send(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_lomac *dest; + + dest = SLOT(mlabel); + + lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); +} + +static void +lomac_netinet6_nd6_send(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_lomac *dest; + + dest = SLOT(mlabel); + + lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); +} + +static int +lomac_pipe_check_ioctl(struct ucred *cred, struct pipepair *pp, + struct label *pplabel, unsigned long cmd, void /* caddr_t */ *data) +{ + + if (!lomac_enabled) + return (0); + + /* XXX: This will be implemented soon... */ + + return (0); +} + +static int +lomac_pipe_check_read(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(pplabel); + + if (!lomac_dominate_single(obj, subj)) + return (maybe_demote(subj, obj, "reading", "pipe", NULL)); + + return (0); +} + +static int +lomac_pipe_check_relabel(struct ucred *cred, struct pipepair *pp, + struct label *pplabel, struct label *newlabel) +{ + struct mac_lomac *subj, *obj, *new; + int error; + + new = SLOT(newlabel); + subj = SLOT(cred->cr_label); + obj = SLOT(pplabel); + + /* + * If there is a LOMAC label update for a pipe, it must be a single + * update. + */ + error = lomac_atmostflags(new, MAC_LOMAC_FLAG_SINGLE); + if (error) + return (error); + + /* + * To perform a relabel of a pipe (LOMAC label or not), LOMAC must + * authorize the relabel. + */ + if (!lomac_single_in_range(obj, subj)) + return (EPERM); + + /* + * If the LOMAC label is to be changed, authorize as appropriate. + */ + if (new->ml_flags & MAC_LOMAC_FLAG_SINGLE) { + /* + * To change the LOMAC label on a pipe, the new pipe label + * must be in the subject range. + */ + if (!lomac_single_in_range(new, subj)) + return (EPERM); + + /* + * To change the LOMAC label on a pipe to be EQUAL, the + * subject must have appropriate privilege. + */ + if (lomac_contains_equal(new)) { + error = lomac_subject_privileged(subj); + if (error) + return (error); + } + } + + return (0); +} + +static int +lomac_pipe_check_write(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(pplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + return (0); +} + +static void +lomac_pipe_create(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(pplabel); + + lomac_copy_single(source, dest); +} + +static void +lomac_pipe_relabel(struct ucred *cred, struct pipepair *pp, + struct label *pplabel, struct label *newlabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(newlabel); + dest = SLOT(pplabel); + + try_relabel(source, dest); +} + +/* + * Some system privileges are allowed regardless of integrity grade; others + * are allowed only when running with privilege with respect to the LOMAC + * policy as they might otherwise allow bypassing of the integrity policy. + */ +static int +lomac_priv_check(struct ucred *cred, int priv) +{ + struct mac_lomac *subj; + int error; + + if (!lomac_enabled) + return (0); + + /* + * Exempt only specific privileges from the LOMAC integrity policy. + */ + switch (priv) { + case PRIV_KTRACE: + case PRIV_MSGBUF: + + /* + * Allow processes to manipulate basic process audit properties, and + * to submit audit records. + */ + case PRIV_AUDIT_GETAUDIT: + case PRIV_AUDIT_SETAUDIT: + case PRIV_AUDIT_SUBMIT: + + /* + * Allow processes to manipulate their regular UNIX credentials. + */ + case PRIV_CRED_SETUID: + case PRIV_CRED_SETEUID: + case PRIV_CRED_SETGID: + case PRIV_CRED_SETEGID: + case PRIV_CRED_SETGROUPS: + case PRIV_CRED_SETREUID: + case PRIV_CRED_SETREGID: + case PRIV_CRED_SETRESUID: + case PRIV_CRED_SETRESGID: + + /* + * Allow processes to perform system monitoring. + */ + case PRIV_SEEOTHERGIDS: + case PRIV_SEEOTHERUIDS: + break; + + /* + * Allow access to general process debugging facilities. We + * separately control debugging based on MAC label. + */ + case PRIV_DEBUG_DIFFCRED: + case PRIV_DEBUG_SUGID: + case PRIV_DEBUG_UNPRIV: + + /* + * Allow manipulating jails. + */ + case PRIV_JAIL_ATTACH: + + /* + * Allow privilege with respect to the Partition policy, but not the + * Privs policy. + */ + case PRIV_MAC_PARTITION: + + /* + * Allow privilege with respect to process resource limits and login + * context. + */ + case PRIV_PROC_LIMIT: + case PRIV_PROC_SETLOGIN: + case PRIV_PROC_SETRLIMIT: + + /* + * Allow System V and POSIX IPC privileges. + */ + case PRIV_IPC_READ: + case PRIV_IPC_WRITE: + case PRIV_IPC_ADMIN: + case PRIV_IPC_MSGSIZE: + case PRIV_MQ_ADMIN: + + /* + * Allow certain scheduler manipulations -- possibly this should be + * controlled by more fine-grained policy, as potentially low + * integrity processes can deny CPU to higher integrity ones. + */ + case PRIV_SCHED_DIFFCRED: + case PRIV_SCHED_SETPRIORITY: + case PRIV_SCHED_RTPRIO: + case PRIV_SCHED_SETPOLICY: + case PRIV_SCHED_SET: + case PRIV_SCHED_SETPARAM: + + /* + * More IPC privileges. + */ + case PRIV_SEM_WRITE: + + /* + * Allow signaling privileges subject to integrity policy. + */ + case PRIV_SIGNAL_DIFFCRED: + case PRIV_SIGNAL_SUGID: + + /* + * Allow access to only limited sysctls from lower integrity levels; + * piggy-back on the Jail definition. + */ + case PRIV_SYSCTL_WRITEJAIL: + + /* + * Allow TTY-based privileges, subject to general device access using + * labels on TTY device nodes, but not console privilege. + */ + case PRIV_TTY_DRAINWAIT: + case PRIV_TTY_DTRWAIT: + case PRIV_TTY_EXCLUSIVE: + case PRIV_TTY_PRISON: + case PRIV_TTY_STI: + case PRIV_TTY_SETA: + + /* + * Grant most VFS privileges, as almost all are in practice bounded + * by more specific checks using labels. + */ + case PRIV_VFS_READ: + case PRIV_VFS_WRITE: + case PRIV_VFS_ADMIN: + case PRIV_VFS_EXEC: + case PRIV_VFS_LOOKUP: + case PRIV_VFS_CHFLAGS_DEV: + case PRIV_VFS_CHOWN: + case PRIV_VFS_CHROOT: + case PRIV_VFS_RETAINSUGID: + case PRIV_VFS_EXCEEDQUOTA: + case PRIV_VFS_FCHROOT: + case PRIV_VFS_FHOPEN: + case PRIV_VFS_FHSTATFS: + case PRIV_VFS_GENERATION: + case PRIV_VFS_GETFH: + case PRIV_VFS_GETQUOTA: + case PRIV_VFS_LINK: + case PRIV_VFS_MOUNT: + case PRIV_VFS_MOUNT_OWNER: + case PRIV_VFS_MOUNT_PERM: + case PRIV_VFS_MOUNT_SUIDDIR: + case PRIV_VFS_MOUNT_NONUSER: + case PRIV_VFS_SETGID: + case PRIV_VFS_STICKYFILE: + case PRIV_VFS_SYSFLAGS: + case PRIV_VFS_UNMOUNT: + + /* + * Allow VM privileges; it would be nice if these were subject to + * resource limits. + */ + case PRIV_VM_MADV_PROTECT: + case PRIV_VM_MLOCK: + case PRIV_VM_MUNLOCK: + + /* + * Allow some but not all network privileges. In general, dont allow + * reconfiguring the network stack, just normal use. + */ + case PRIV_NETATALK_RESERVEDPORT: + case PRIV_NETINET_RESERVEDPORT: + case PRIV_NETINET_RAW: + case PRIV_NETINET_REUSEPORT: + case PRIV_NETIPX_RESERVEDPORT: + case PRIV_NETIPX_RAW: + break; + + /* + * All remaining system privileges are allow only if the process + * holds privilege with respect to the LOMAC policy. + */ + default: + subj = SLOT(cred->cr_label); + error = lomac_subject_privileged(subj); + if (error) + return (error); + } + return (0); +} + +static int +lomac_proc_check_debug(struct ucred *cred, struct proc *p) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(p->p_ucred->cr_label); + + /* XXX: range checks */ + if (!lomac_dominate_single(obj, subj)) + return (ESRCH); + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + return (0); +} + +static int +lomac_proc_check_sched(struct ucred *cred, struct proc *p) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(p->p_ucred->cr_label); + + /* XXX: range checks */ + if (!lomac_dominate_single(obj, subj)) + return (ESRCH); + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + return (0); +} + +static int +lomac_proc_check_signal(struct ucred *cred, struct proc *p, int signum) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(p->p_ucred->cr_label); + + /* XXX: range checks */ + if (!lomac_dominate_single(obj, subj)) + return (ESRCH); + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + return (0); +} + +static void +lomac_proc_destroy_label(struct label *label) +{ + + mtx_destroy(&PSLOT(label)->mtx); + free(PSLOT(label), M_LOMAC); + PSLOT_SET(label, NULL); +} + +static void +lomac_proc_init_label(struct label *label) +{ + + PSLOT_SET(label, malloc(sizeof(struct mac_lomac_proc), M_LOMAC, + M_ZERO | M_WAITOK)); + mtx_init(&PSLOT(label)->mtx, "MAC/Lomac proc lock", NULL, MTX_DEF); +} + +static int +lomac_socket_check_deliver(struct socket *so, struct label *solabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_lomac *p, *s; + + if (!lomac_enabled) + return (0); + + p = SLOT(mlabel); + s = SLOT(solabel); + + return (lomac_equal_single(p, s) ? 0 : EACCES); +} + +static int +lomac_socket_check_relabel(struct ucred *cred, struct socket *so, + struct label *solabel, struct label *newlabel) +{ + struct mac_lomac *subj, *obj, *new; + int error; + + new = SLOT(newlabel); + subj = SLOT(cred->cr_label); + obj = SLOT(solabel); + + /* + * If there is a LOMAC label update for the socket, it may be an + * update of single. + */ + error = lomac_atmostflags(new, MAC_LOMAC_FLAG_SINGLE); + if (error) + return (error); + + /* + * To relabel a socket, the old socket single must be in the subject + * range. + */ + if (!lomac_single_in_range(obj, subj)) + return (EPERM); + + /* + * If the LOMAC label is to be changed, authorize as appropriate. + */ + if (new->ml_flags & MAC_LOMAC_FLAG_SINGLE) { + /* + * To relabel a socket, the new socket single must be in the + * subject range. + */ + if (!lomac_single_in_range(new, subj)) + return (EPERM); + + /* + * To change the LOMAC label on the socket to contain EQUAL, + * the subject must have appropriate privilege. + */ + if (lomac_contains_equal(new)) { + error = lomac_subject_privileged(subj); + if (error) + return (error); + } + } + + return (0); +} + +static int +lomac_socket_check_visible(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(solabel); + + if (!lomac_dominate_single(obj, subj)) + return (ENOENT); + + return (0); +} + +static void +lomac_socket_create(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(solabel); + + lomac_copy_single(source, dest); +} + +static void +lomac_socket_create_mbuf(struct socket *so, struct label *solabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(solabel); + dest = SLOT(mlabel); + + lomac_copy_single(source, dest); +} + +static void +lomac_socket_newconn(struct socket *oldso, struct label *oldsolabel, + struct socket *newso, struct label *newsolabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(oldsolabel); + dest = SLOT(newsolabel); + + lomac_copy_single(source, dest); +} + +static void +lomac_socket_relabel(struct ucred *cred, struct socket *so, + struct label *solabel, struct label *newlabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(newlabel); + dest = SLOT(solabel); + + try_relabel(source, dest); +} + +static void +lomac_socketpeer_set_from_mbuf(struct mbuf *m, struct label *mlabel, + struct socket *so, struct label *sopeerlabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(mlabel); + dest = SLOT(sopeerlabel); + + lomac_copy_single(source, dest); +} + +static void +lomac_socketpeer_set_from_socket(struct socket *oldso, + struct label *oldsolabel, struct socket *newso, + struct label *newsopeerlabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(oldsolabel); + dest = SLOT(newsopeerlabel); + + lomac_copy_single(source, dest); +} + +static void +lomac_syncache_create(struct label *label, struct inpcb *inp) +{ + struct mac_lomac *source, *dest; + + source = SLOT(inp->inp_label); + dest = SLOT(label); + lomac_copy(source, dest); +} + +static void +lomac_syncache_create_mbuf(struct label *sc_label, struct mbuf *m, + struct label *mlabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(sc_label); + dest = SLOT(mlabel); + lomac_copy(source, dest); +} + +static int +lomac_system_check_acct(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (lomac_subject_privileged(subj)) + return (EPERM); + + if (!lomac_high_single(obj)) + return (EACCES); + + return (0); +} + +static int +lomac_system_check_auditctl(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (lomac_subject_privileged(subj)) + return (EPERM); + + if (!lomac_high_single(obj)) + return (EACCES); + + return (0); +} + +static int +lomac_system_check_swapoff(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + struct mac_lomac *subj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + + if (lomac_subject_privileged(subj)) + return (EPERM); + + return (0); +} + +static int +lomac_system_check_swapon(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (lomac_subject_privileged(subj)) + return (EPERM); + + if (!lomac_high_single(obj)) + return (EACCES); + + return (0); +} + +static int +lomac_system_check_sysctl(struct ucred *cred, struct sysctl_oid *oidp, + void *arg1, int arg2, struct sysctl_req *req) +{ + struct mac_lomac *subj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + + /* + * Treat sysctl variables without CTLFLAG_ANYBODY flag as lomac/high, + * but also require privilege to change them. + */ + if (req->newptr != NULL && (oidp->oid_kind & CTLFLAG_ANYBODY) == 0) { +#ifdef notdef + if (!lomac_subject_dominate_high(subj)) + return (EACCES); +#endif + + if (lomac_subject_privileged(subj)) + return (EPERM); + } + + return (0); +} + +static void +lomac_thread_userret(struct thread *td) +{ + struct proc *p = td->td_proc; + struct mac_lomac_proc *subj = PSLOT(p->p_label); + struct ucred *newcred, *oldcred; + int dodrop; + + mtx_lock(&subj->mtx); + if (subj->mac_lomac.ml_flags & MAC_LOMAC_FLAG_UPDATE) { + dodrop = 0; + mtx_unlock(&subj->mtx); + newcred = crget(); + /* + * Prevent a lock order reversal in mac_proc_vm_revoke; + * ideally, the other user of subj->mtx wouldn't be holding + * Giant. + */ + mtx_lock(&Giant); + PROC_LOCK(p); + mtx_lock(&subj->mtx); + /* + * Check if we lost the race while allocating the cred. + */ + if ((subj->mac_lomac.ml_flags & MAC_LOMAC_FLAG_UPDATE) == 0) { + crfree(newcred); + goto out; + } + oldcred = p->p_ucred; + crcopy(newcred, oldcred); + crhold(newcred); + lomac_copy(&subj->mac_lomac, SLOT(newcred->cr_label)); + p->p_ucred = newcred; + crfree(oldcred); + dodrop = 1; + out: + mtx_unlock(&subj->mtx); + PROC_UNLOCK(p); + if (dodrop) + mac_proc_vm_revoke(curthread); + mtx_unlock(&Giant); + } else { + mtx_unlock(&subj->mtx); + } +} + +static int +lomac_vnode_associate_extattr(struct mount *mp, struct label *mplabel, + struct vnode *vp, struct label *vplabel) +{ + struct mac_lomac ml_temp, *source, *dest; + int buflen, error; + + source = SLOT(mplabel); + dest = SLOT(vplabel); + + buflen = sizeof(ml_temp); + bzero(&ml_temp, buflen); + + error = vn_extattr_get(vp, IO_NODELOCKED, MAC_LOMAC_EXTATTR_NAMESPACE, + MAC_LOMAC_EXTATTR_NAME, &buflen, (char *)&ml_temp, curthread); + if (error == ENOATTR || error == EOPNOTSUPP) { + /* Fall back to the mntlabel. */ + lomac_copy_single(source, dest); + return (0); + } else if (error) + return (error); + + if (buflen != sizeof(ml_temp)) { + if (buflen != sizeof(ml_temp) - sizeof(ml_temp.ml_auxsingle)) { + printf("lomac_vnode_associate_extattr: bad size %d\n", + buflen); + return (EPERM); + } + bzero(&ml_temp.ml_auxsingle, sizeof(ml_temp.ml_auxsingle)); + buflen = sizeof(ml_temp); + (void)vn_extattr_set(vp, IO_NODELOCKED, + MAC_LOMAC_EXTATTR_NAMESPACE, MAC_LOMAC_EXTATTR_NAME, + buflen, (char *)&ml_temp, curthread); + } + if (lomac_valid(&ml_temp) != 0) { + printf("lomac_vnode_associate_extattr: invalid\n"); + return (EPERM); + } + if ((ml_temp.ml_flags & MAC_LOMAC_FLAGS_BOTH) != + MAC_LOMAC_FLAG_SINGLE) { + printf("lomac_vnode_associate_extattr: not single\n"); + return (EPERM); + } + + lomac_copy_single(&ml_temp, dest); + return (0); +} + +static void +lomac_vnode_associate_singlelabel(struct mount *mp, struct label *mplabel, + struct vnode *vp, struct label *vplabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(mplabel); + dest = SLOT(vplabel); + + lomac_copy_single(source, dest); +} + +static int +lomac_vnode_check_create(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct componentname *cnp, struct vattr *vap) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + if (obj->ml_flags & MAC_LOMAC_FLAG_AUX && + !lomac_dominate_element(&subj->ml_single, &obj->ml_auxsingle)) + return (EACCES); + + return (0); +} + +static int +lomac_vnode_check_deleteacl(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + return (0); +} + +static int +lomac_vnode_check_link(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + obj = SLOT(vplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + return (0); +} + +static int +lomac_vnode_check_mmap(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int prot, int flags) +{ + struct mac_lomac *subj, *obj; + + /* + * Rely on the use of open()-time protections to handle + * non-revocation cases. + */ + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (((prot & VM_PROT_WRITE) != 0) && ((flags & MAP_SHARED) != 0)) { + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + } + if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) { + if (!lomac_dominate_single(obj, subj)) + return (maybe_demote(subj, obj, "mapping", "file", vp)); + } + + return (0); +} + +static void +lomac_vnode_check_mmap_downgrade(struct ucred *cred, struct vnode *vp, + struct label *vplabel, /* XXX vm_prot_t */ int *prot) +{ + struct mac_lomac *subj, *obj; + + /* + * Rely on the use of open()-time protections to handle + * non-revocation cases. + */ + if (!lomac_enabled || !revocation_enabled) + return; + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!lomac_subject_dominate(subj, obj)) + *prot &= ~VM_PROT_WRITE; +} + +static int +lomac_vnode_check_open(struct ucred *cred, struct vnode *vp, + struct label *vplabel, accmode_t accmode) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + /* XXX privilege override for admin? */ + if (accmode & (VWRITE | VAPPEND | VADMIN)) { + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + } + + return (0); +} + +static int +lomac_vnode_check_read(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp, struct label *vplabel) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled || !revocation_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(vplabel); + + if (!lomac_dominate_single(obj, subj)) + return (maybe_demote(subj, obj, "reading", "file", vp)); + + return (0); +} + +static int +lomac_vnode_check_relabel(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct label *newlabel) +{ + struct mac_lomac *old, *new, *subj; + int error; + + old = SLOT(vplabel); + new = SLOT(newlabel); + subj = SLOT(cred->cr_label); + + /* + * If there is a LOMAC label update for the vnode, it must be a + * single label, with an optional explicit auxiliary single. + */ + error = lomac_atmostflags(new, + MAC_LOMAC_FLAG_SINGLE | MAC_LOMAC_FLAG_AUX); + if (error) + return (error); + + /* + * To perform a relabel of the vnode (LOMAC label or not), LOMAC must + * authorize the relabel. + */ + if (!lomac_single_in_range(old, subj)) + return (EPERM); + + /* + * If the LOMAC label is to be changed, authorize as appropriate. + */ + if (new->ml_flags & MAC_LOMAC_FLAG_SINGLE) { + /* + * To change the LOMAC label on a vnode, the new vnode label + * must be in the subject range. + */ + if (!lomac_single_in_range(new, subj)) + return (EPERM); + + /* + * To change the LOMAC label on the vnode to be EQUAL, the + * subject must have appropriate privilege. + */ + if (lomac_contains_equal(new)) { + error = lomac_subject_privileged(subj); + if (error) + return (error); + } + } + if (new->ml_flags & MAC_LOMAC_FLAG_AUX) { + /* + * Fill in the missing parts from the previous label. + */ + if ((new->ml_flags & MAC_LOMAC_FLAG_SINGLE) == 0) + lomac_copy_single(subj, new); + + /* + * To change the auxiliary LOMAC label on a vnode, the new + * vnode label must be in the subject range. + */ + if (!lomac_auxsingle_in_range(new, subj)) + return (EPERM); + + /* + * To change the auxiliary LOMAC label on the vnode to be + * EQUAL, the subject must have appropriate privilege. + */ + if (lomac_contains_equal(new)) { + error = lomac_subject_privileged(subj); + if (error) + return (error); + } + } + + return (0); +} + +static int +lomac_vnode_check_rename_from(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + obj = SLOT(vplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + return (0); +} + +static int +lomac_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + int samedir, struct componentname *cnp) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + if (vp != NULL) { + obj = SLOT(vplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + } + + return (0); +} + +static int +lomac_vnode_check_revoke(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + return (0); +} + +static int +lomac_vnode_check_setacl(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type, struct acl *acl) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + return (0); +} + +static int +lomac_vnode_check_setextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace, const char *name, + struct uio *uio) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + /* XXX: protect the MAC EA in a special way? */ + + return (0); +} + +static int +lomac_vnode_check_setflags(struct ucred *cred, struct vnode *vp, + struct label *vplabel, u_long flags) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + return (0); +} + +static int +lomac_vnode_check_setmode(struct ucred *cred, struct vnode *vp, + struct label *vplabel, mode_t mode) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + return (0); +} + +static int +lomac_vnode_check_setowner(struct ucred *cred, struct vnode *vp, + struct label *vplabel, uid_t uid, gid_t gid) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + return (0); +} + +static int +lomac_vnode_check_setutimes(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct timespec atime, struct timespec mtime) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + return (0); +} + +static int +lomac_vnode_check_unlink(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + obj = SLOT(vplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + return (0); +} + +static int +lomac_vnode_check_write(struct ucred *active_cred, + struct ucred *file_cred, struct vnode *vp, struct label *vplabel) +{ + struct mac_lomac *subj, *obj; + + if (!lomac_enabled || !revocation_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(vplabel); + + if (!lomac_subject_dominate(subj, obj)) + return (EACCES); + + return (0); +} + +static int +lomac_vnode_create_extattr(struct ucred *cred, struct mount *mp, + struct label *mplabel, struct vnode *dvp, struct label *dvplabel, + struct vnode *vp, struct label *vplabel, struct componentname *cnp) +{ + struct mac_lomac *source, *dest, *dir, temp; + size_t buflen; + int error; + + buflen = sizeof(temp); + bzero(&temp, buflen); + + source = SLOT(cred->cr_label); + dest = SLOT(vplabel); + dir = SLOT(dvplabel); + if (dir->ml_flags & MAC_LOMAC_FLAG_AUX) { + lomac_copy_auxsingle(dir, &temp); + lomac_set_single(&temp, dir->ml_auxsingle.mle_type, + dir->ml_auxsingle.mle_grade); + } else { + lomac_copy_single(source, &temp); + } + + error = vn_extattr_set(vp, IO_NODELOCKED, MAC_LOMAC_EXTATTR_NAMESPACE, + MAC_LOMAC_EXTATTR_NAME, buflen, (char *)&temp, curthread); + if (error == 0) + lomac_copy(&temp, dest); + return (error); +} + +static void +lomac_vnode_execve_transition(struct ucred *old, struct ucred *new, + struct vnode *vp, struct label *vplabel, struct label *interpvplabel, + struct image_params *imgp, struct label *execlabel) +{ + struct mac_lomac *source, *dest, *obj, *robj; + + source = SLOT(old->cr_label); + dest = SLOT(new->cr_label); + obj = SLOT(vplabel); + robj = interpvplabel != NULL ? SLOT(interpvplabel) : obj; + + lomac_copy(source, dest); + /* + * If there's an auxiliary label on the real object, respect it and + * assume that this level should be assumed immediately if a higher + * level is currently in place. + */ + if (robj->ml_flags & MAC_LOMAC_FLAG_AUX && + !lomac_dominate_element(&robj->ml_auxsingle, &dest->ml_single) + && lomac_auxsingle_in_range(robj, dest)) + lomac_set_single(dest, robj->ml_auxsingle.mle_type, + robj->ml_auxsingle.mle_grade); + /* + * Restructuring to use the execve transitioning mechanism instead of + * the normal demotion mechanism here would be difficult, so just + * copy the label over and perform standard demotion. This is also + * non-optimal because it will result in the intermediate label "new" + * being created and immediately recycled. + */ + if (lomac_enabled && revocation_enabled && + !lomac_dominate_single(obj, source)) + (void)maybe_demote(source, obj, "executing", "file", vp); +} + +static int +lomac_vnode_execve_will_transition(struct ucred *old, struct vnode *vp, + struct label *vplabel, struct label *interpvplabel, + struct image_params *imgp, struct label *execlabel) +{ + struct mac_lomac *subj, *obj, *robj; + + if (!lomac_enabled || !revocation_enabled) + return (0); + + subj = SLOT(old->cr_label); + obj = SLOT(vplabel); + robj = interpvplabel != NULL ? SLOT(interpvplabel) : obj; + + return ((robj->ml_flags & MAC_LOMAC_FLAG_AUX && + !lomac_dominate_element(&robj->ml_auxsingle, &subj->ml_single) + && lomac_auxsingle_in_range(robj, subj)) || + !lomac_dominate_single(obj, subj)); +} + +static void +lomac_vnode_relabel(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct label *newlabel) +{ + struct mac_lomac *source, *dest; + + source = SLOT(newlabel); + dest = SLOT(vplabel); + + try_relabel(source, dest); +} + +static int +lomac_vnode_setlabel_extattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct label *intlabel) +{ + struct mac_lomac *source, temp; + size_t buflen; + int error; + + buflen = sizeof(temp); + bzero(&temp, buflen); + + source = SLOT(intlabel); + if ((source->ml_flags & MAC_LOMAC_FLAG_SINGLE) == 0) + return (0); + + lomac_copy_single(source, &temp); + error = vn_extattr_set(vp, IO_NODELOCKED, MAC_LOMAC_EXTATTR_NAMESPACE, + MAC_LOMAC_EXTATTR_NAME, buflen, (char *)&temp, curthread); + return (error); +} + +static struct mac_policy_ops lomac_ops = +{ + .mpo_init = lomac_init, + + .mpo_bpfdesc_check_receive = lomac_bpfdesc_check_receive, + .mpo_bpfdesc_create = lomac_bpfdesc_create, + .mpo_bpfdesc_create_mbuf = lomac_bpfdesc_create_mbuf, + .mpo_bpfdesc_destroy_label = lomac_destroy_label, + .mpo_bpfdesc_init_label = lomac_init_label, + + .mpo_cred_check_relabel = lomac_cred_check_relabel, + .mpo_cred_check_visible = lomac_cred_check_visible, + .mpo_cred_copy_label = lomac_copy_label, + .mpo_cred_create_swapper = lomac_cred_create_swapper, + .mpo_cred_create_init = lomac_cred_create_init, + .mpo_cred_destroy_label = lomac_destroy_label, + .mpo_cred_externalize_label = lomac_externalize_label, + .mpo_cred_init_label = lomac_init_label, + .mpo_cred_internalize_label = lomac_internalize_label, + .mpo_cred_relabel = lomac_cred_relabel, + + .mpo_devfs_create_device = lomac_devfs_create_device, + .mpo_devfs_create_directory = lomac_devfs_create_directory, + .mpo_devfs_create_symlink = lomac_devfs_create_symlink, + .mpo_devfs_destroy_label = lomac_destroy_label, + .mpo_devfs_init_label = lomac_init_label, + .mpo_devfs_update = lomac_devfs_update, + .mpo_devfs_vnode_associate = lomac_devfs_vnode_associate, + + .mpo_ifnet_check_relabel = lomac_ifnet_check_relabel, + .mpo_ifnet_check_transmit = lomac_ifnet_check_transmit, + .mpo_ifnet_copy_label = lomac_copy_label, + .mpo_ifnet_create = lomac_ifnet_create, + .mpo_ifnet_create_mbuf = lomac_ifnet_create_mbuf, + .mpo_ifnet_destroy_label = lomac_destroy_label, + .mpo_ifnet_externalize_label = lomac_externalize_label, + .mpo_ifnet_init_label = lomac_init_label, + .mpo_ifnet_internalize_label = lomac_internalize_label, + .mpo_ifnet_relabel = lomac_ifnet_relabel, + + .mpo_syncache_create = lomac_syncache_create, + .mpo_syncache_destroy_label = lomac_destroy_label, + .mpo_syncache_init_label = lomac_init_label_waitcheck, + + .mpo_inpcb_check_deliver = lomac_inpcb_check_deliver, + .mpo_inpcb_check_visible = lomac_inpcb_check_visible, + .mpo_inpcb_create = lomac_inpcb_create, + .mpo_inpcb_create_mbuf = lomac_inpcb_create_mbuf, + .mpo_inpcb_destroy_label = lomac_destroy_label, + .mpo_inpcb_init_label = lomac_init_label_waitcheck, + .mpo_inpcb_sosetlabel = lomac_inpcb_sosetlabel, + + .mpo_ip6q_create = lomac_ip6q_create, + .mpo_ip6q_destroy_label = lomac_destroy_label, + .mpo_ip6q_init_label = lomac_init_label_waitcheck, + .mpo_ip6q_match = lomac_ip6q_match, + .mpo_ip6q_reassemble = lomac_ip6q_reassemble, + .mpo_ip6q_update = lomac_ip6q_update, + + .mpo_ipq_create = lomac_ipq_create, + .mpo_ipq_destroy_label = lomac_destroy_label, + .mpo_ipq_init_label = lomac_init_label_waitcheck, + .mpo_ipq_match = lomac_ipq_match, + .mpo_ipq_reassemble = lomac_ipq_reassemble, + .mpo_ipq_update = lomac_ipq_update, + + .mpo_kld_check_load = lomac_kld_check_load, + + .mpo_mbuf_copy_label = lomac_copy_label, + .mpo_mbuf_destroy_label = lomac_destroy_label, + .mpo_mbuf_init_label = lomac_init_label_waitcheck, + + .mpo_mount_create = lomac_mount_create, + .mpo_mount_destroy_label = lomac_destroy_label, + .mpo_mount_init_label = lomac_init_label, + + .mpo_netatalk_aarp_send = lomac_netatalk_aarp_send, + + .mpo_netinet_arp_send = lomac_netinet_arp_send, + .mpo_netinet_firewall_reply = lomac_netinet_firewall_reply, + .mpo_netinet_firewall_send = lomac_netinet_firewall_send, + .mpo_netinet_fragment = lomac_netinet_fragment, + .mpo_netinet_icmp_reply = lomac_netinet_icmp_reply, + .mpo_netinet_igmp_send = lomac_netinet_igmp_send, + + .mpo_netinet6_nd6_send = lomac_netinet6_nd6_send, + + .mpo_pipe_check_ioctl = lomac_pipe_check_ioctl, + .mpo_pipe_check_read = lomac_pipe_check_read, + .mpo_pipe_check_relabel = lomac_pipe_check_relabel, + .mpo_pipe_check_write = lomac_pipe_check_write, + .mpo_pipe_copy_label = lomac_copy_label, + .mpo_pipe_create = lomac_pipe_create, + .mpo_pipe_destroy_label = lomac_destroy_label, + .mpo_pipe_externalize_label = lomac_externalize_label, + .mpo_pipe_init_label = lomac_init_label, + .mpo_pipe_internalize_label = lomac_internalize_label, + .mpo_pipe_relabel = lomac_pipe_relabel, + + .mpo_priv_check = lomac_priv_check, + + .mpo_proc_check_debug = lomac_proc_check_debug, + .mpo_proc_check_sched = lomac_proc_check_sched, + .mpo_proc_check_signal = lomac_proc_check_signal, + .mpo_proc_destroy_label = lomac_proc_destroy_label, + .mpo_proc_init_label = lomac_proc_init_label, + + .mpo_socket_check_deliver = lomac_socket_check_deliver, + .mpo_socket_check_relabel = lomac_socket_check_relabel, + .mpo_socket_check_visible = lomac_socket_check_visible, + .mpo_socket_copy_label = lomac_copy_label, + .mpo_socket_create = lomac_socket_create, + .mpo_socket_create_mbuf = lomac_socket_create_mbuf, + .mpo_socket_destroy_label = lomac_destroy_label, + .mpo_socket_externalize_label = lomac_externalize_label, + .mpo_socket_init_label = lomac_init_label_waitcheck, + .mpo_socket_internalize_label = lomac_internalize_label, + .mpo_socket_newconn = lomac_socket_newconn, + .mpo_socket_relabel = lomac_socket_relabel, + + .mpo_socketpeer_destroy_label = lomac_destroy_label, + .mpo_socketpeer_externalize_label = lomac_externalize_label, + .mpo_socketpeer_init_label = lomac_init_label_waitcheck, + .mpo_socketpeer_set_from_mbuf = lomac_socketpeer_set_from_mbuf, + .mpo_socketpeer_set_from_socket = lomac_socketpeer_set_from_socket, + + .mpo_syncache_create_mbuf = lomac_syncache_create_mbuf, + + .mpo_system_check_acct = lomac_system_check_acct, + .mpo_system_check_auditctl = lomac_system_check_auditctl, + .mpo_system_check_swapoff = lomac_system_check_swapoff, + .mpo_system_check_swapon = lomac_system_check_swapon, + .mpo_system_check_sysctl = lomac_system_check_sysctl, + + .mpo_thread_userret = lomac_thread_userret, + + .mpo_vnode_associate_extattr = lomac_vnode_associate_extattr, + .mpo_vnode_associate_singlelabel = lomac_vnode_associate_singlelabel, + .mpo_vnode_check_access = lomac_vnode_check_open, + .mpo_vnode_check_create = lomac_vnode_check_create, + .mpo_vnode_check_deleteacl = lomac_vnode_check_deleteacl, + .mpo_vnode_check_link = lomac_vnode_check_link, + .mpo_vnode_check_mmap = lomac_vnode_check_mmap, + .mpo_vnode_check_mmap_downgrade = lomac_vnode_check_mmap_downgrade, + .mpo_vnode_check_open = lomac_vnode_check_open, + .mpo_vnode_check_read = lomac_vnode_check_read, + .mpo_vnode_check_relabel = lomac_vnode_check_relabel, + .mpo_vnode_check_rename_from = lomac_vnode_check_rename_from, + .mpo_vnode_check_rename_to = lomac_vnode_check_rename_to, + .mpo_vnode_check_revoke = lomac_vnode_check_revoke, + .mpo_vnode_check_setacl = lomac_vnode_check_setacl, + .mpo_vnode_check_setextattr = lomac_vnode_check_setextattr, + .mpo_vnode_check_setflags = lomac_vnode_check_setflags, + .mpo_vnode_check_setmode = lomac_vnode_check_setmode, + .mpo_vnode_check_setowner = lomac_vnode_check_setowner, + .mpo_vnode_check_setutimes = lomac_vnode_check_setutimes, + .mpo_vnode_check_unlink = lomac_vnode_check_unlink, + .mpo_vnode_check_write = lomac_vnode_check_write, + .mpo_vnode_copy_label = lomac_copy_label, + .mpo_vnode_create_extattr = lomac_vnode_create_extattr, + .mpo_vnode_destroy_label = lomac_destroy_label, + .mpo_vnode_execve_transition = lomac_vnode_execve_transition, + .mpo_vnode_execve_will_transition = lomac_vnode_execve_will_transition, + .mpo_vnode_externalize_label = lomac_externalize_label, + .mpo_vnode_init_label = lomac_init_label, + .mpo_vnode_internalize_label = lomac_internalize_label, + .mpo_vnode_relabel = lomac_vnode_relabel, + .mpo_vnode_setlabel_extattr = lomac_vnode_setlabel_extattr, +}; + +#define LOMAC_OBJECTS (MPC_OBJECT_CRED | \ + /* MPC_OBJECT_PROC | */ \ + MPC_OBJECT_VNODE | \ + MPC_OBJECT_INPCB | \ + MPC_OBJECT_SOCKET | \ + MPC_OBJECT_DEVFS | \ + MPC_OBJECT_MBUF | \ + MPC_OBJECT_IPQ | \ + MPC_OBJECT_IFNET | \ + MPC_OBJECT_BPFDESC | \ + MPC_OBJECT_PIPE | \ + MPC_OBJECT_MOUNT | \ + /* MPC_OBJECT_POSIXSEM | */ \ + /* MPC_OBJECT_POSIXSHM | */ \ + /* MPC_OBJECT_SYSVMSG | */ \ + /* MPC_OBJECT_SYSVMSQ | */ \ + /* MPC_OBJECT_SYSVSEM | */ \ + /* MPC_OBJECT_SYSVSHM | */ \ + MPC_OBJECT_SYNCACHE) + +MAC_POLICY_SET(&lomac_ops, mac_lomac, "TrustedBSD MAC/LOMAC", + MPC_LOADTIME_FLAG_NOTLATE, &lomac_slot, LOMAC_OBJECTS); diff --git a/sys/security/mac_lomac/mac_lomac.h b/sys/security/mac_lomac/mac_lomac.h new file mode 100644 index 0000000..9edba98 --- /dev/null +++ b/sys/security/mac_lomac/mac_lomac.h @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 1999-2002 Robert N. M. Watson + * Copyright (c) 2001-2002 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by NAI Labs, + * the Security Research Division of Network Associates, Inc. under + * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA + * CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +/* + * Definitions for the TrustedBSD LOMAC integrity policy module. + */ +#ifndef _SYS_SECURITY_MAC_LOMAC_H +#define _SYS_SECURITY_MAC_LOMAC_H + +#define MAC_LOMAC_EXTATTR_NAMESPACE EXTATTR_NAMESPACE_SYSTEM +#define MAC_LOMAC_EXTATTR_NAME "mac_lomac" + +#define MAC_LOMAC_LABEL_NAME "lomac" + +#define MAC_LOMAC_FLAG_SINGLE 0x00000001 /* ml_single initialized */ +#define MAC_LOMAC_FLAG_RANGE 0x00000002 /* ml_range* initialized */ +#define MAC_LOMAC_FLAG_AUX 0x00000004 /* ml_auxsingle initialized */ +#define MAC_LOMAC_FLAGS_BOTH (MAC_LOMAC_FLAG_SINGLE | MAC_LOMAC_FLAG_RANGE) +#define MAC_LOMAC_FLAG_UPDATE 0x00000008 /* must demote this process */ + +#define MAC_LOMAC_TYPE_UNDEF 0 /* Undefined */ +#define MAC_LOMAC_TYPE_GRADE 1 /* Hierarchal grade with mb_grade. */ +#define MAC_LOMAC_TYPE_LOW 2 /* Dominated by any + * MAC_LOMAC_TYPE_LABEL. */ +#define MAC_LOMAC_TYPE_HIGH 3 /* Dominates any + * MAC_LOMAC_TYPE_LABEL. */ +#define MAC_LOMAC_TYPE_EQUAL 4 /* Equivilent to any + * MAC_LOMAC_TYPE_LABEL. */ + +/* + * Structures and constants associated with a LOMAC Integrity policy. + * mac_lomac represents a LOMAC label, with mb_type determining its properties, + * and mb_grade represents the hierarchal grade if valid for the current + * mb_type. + */ + +struct mac_lomac_element { + u_short mle_type; + u_short mle_grade; +}; + +/* + * LOMAC labels start with two components: a single label, and a label + * range. Depending on the context, one or both may be used; the ml_flags + * field permits the provider to indicate what fields are intended for + * use. The auxiliary label works the same way, but is only valid on + * filesystem objects to provide inheritance semantics on directories + * and "non-demoting" execution on executable files. + */ +struct mac_lomac { + int ml_flags; + struct mac_lomac_element ml_single; + struct mac_lomac_element ml_rangelow, ml_rangehigh; + struct mac_lomac_element ml_auxsingle; +}; + +#endif /* !_SYS_SECURITY_MAC_LOMAC_H */ diff --git a/sys/security/mac_mls/mac_mls.c b/sys/security/mac_mls/mac_mls.c new file mode 100644 index 0000000..217fa5f --- /dev/null +++ b/sys/security/mac_mls/mac_mls.c @@ -0,0 +1,3186 @@ +/*- + * Copyright (c) 1999-2002, 2007-2008 Robert N. M. Watson + * Copyright (c) 2001-2005 McAfee, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by McAfee + * Research, the Security Research Division of McAfee, Inc. under + * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA + * CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Developed by the TrustedBSD Project. + * + * MLS fixed label mandatory confidentiality policy. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/acl.h> +#include <sys/conf.h> +#include <sys/extattr.h> +#include <sys/kernel.h> +#include <sys/ksem.h> +#include <sys/mman.h> +#include <sys/malloc.h> +#include <sys/mount.h> +#include <sys/proc.h> +#include <sys/sbuf.h> +#include <sys/systm.h> +#include <sys/sysproto.h> +#include <sys/sysent.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/pipe.h> +#include <sys/sx.h> +#include <sys/sysctl.h> +#include <sys/msg.h> +#include <sys/sem.h> +#include <sys/shm.h> + +#include <fs/devfs/devfs.h> + +#include <net/bpfdesc.h> +#include <net/if.h> +#include <net/if_types.h> +#include <net/if_var.h> + +#include <netinet/in.h> +#include <netinet/in_pcb.h> +#include <netinet/ip_var.h> + +#include <vm/uma.h> +#include <vm/vm.h> + +#include <security/mac/mac_policy.h> +#include <security/mac_mls/mac_mls.h> + +SYSCTL_DECL(_security_mac); + +SYSCTL_NODE(_security_mac, OID_AUTO, mls, CTLFLAG_RW, 0, + "TrustedBSD mac_mls policy controls"); + +static int mls_label_size = sizeof(struct mac_mls); +SYSCTL_INT(_security_mac_mls, OID_AUTO, label_size, CTLFLAG_RD, + &mls_label_size, 0, "Size of struct mac_mls"); + +static int mls_enabled = 1; +SYSCTL_INT(_security_mac_mls, OID_AUTO, enabled, CTLFLAG_RW, &mls_enabled, 0, + "Enforce MAC/MLS policy"); +TUNABLE_INT("security.mac.mls.enabled", &mls_enabled); + +static int destroyed_not_inited; +SYSCTL_INT(_security_mac_mls, OID_AUTO, destroyed_not_inited, CTLFLAG_RD, + &destroyed_not_inited, 0, "Count of labels destroyed but not inited"); + +static int ptys_equal = 0; +SYSCTL_INT(_security_mac_mls, OID_AUTO, ptys_equal, CTLFLAG_RW, + &ptys_equal, 0, "Label pty devices as mls/equal on create"); +TUNABLE_INT("security.mac.mls.ptys_equal", &ptys_equal); + +static int revocation_enabled = 0; +SYSCTL_INT(_security_mac_mls, OID_AUTO, revocation_enabled, CTLFLAG_RW, + &revocation_enabled, 0, "Revoke access to objects on relabel"); +TUNABLE_INT("security.mac.mls.revocation_enabled", &revocation_enabled); + +static int max_compartments = MAC_MLS_MAX_COMPARTMENTS; +SYSCTL_INT(_security_mac_mls, OID_AUTO, max_compartments, CTLFLAG_RD, + &max_compartments, 0, "Maximum compartments the policy supports"); + +static int mls_slot; +#define SLOT(l) ((struct mac_mls *)mac_label_get((l), mls_slot)) +#define SLOT_SET(l, val) mac_label_set((l), mls_slot, (uintptr_t)(val)) + +static uma_zone_t zone_mls; + +static __inline int +mls_bit_set_empty(u_char *set) { + int i; + + for (i = 0; i < MAC_MLS_MAX_COMPARTMENTS >> 3; i++) + if (set[i] != 0) + return (0); + return (1); +} + +static struct mac_mls * +mls_alloc(int flag) +{ + + return (uma_zalloc(zone_mls, flag | M_ZERO)); +} + +static void +mls_free(struct mac_mls *mm) +{ + + if (mm != NULL) + uma_zfree(zone_mls, mm); + else + atomic_add_int(&destroyed_not_inited, 1); +} + +static int +mls_atmostflags(struct mac_mls *mm, int flags) +{ + + if ((mm->mm_flags & flags) != mm->mm_flags) + return (EINVAL); + return (0); +} + +static int +mls_dominate_element(struct mac_mls_element *a, struct mac_mls_element *b) +{ + int bit; + + switch (a->mme_type) { + case MAC_MLS_TYPE_EQUAL: + case MAC_MLS_TYPE_HIGH: + return (1); + + case MAC_MLS_TYPE_LOW: + switch (b->mme_type) { + case MAC_MLS_TYPE_LEVEL: + case MAC_MLS_TYPE_HIGH: + return (0); + + case MAC_MLS_TYPE_EQUAL: + case MAC_MLS_TYPE_LOW: + return (1); + + default: + panic("mls_dominate_element: b->mme_type invalid"); + } + + case MAC_MLS_TYPE_LEVEL: + switch (b->mme_type) { + case MAC_MLS_TYPE_EQUAL: + case MAC_MLS_TYPE_LOW: + return (1); + + case MAC_MLS_TYPE_HIGH: + return (0); + + case MAC_MLS_TYPE_LEVEL: + for (bit = 1; bit <= MAC_MLS_MAX_COMPARTMENTS; bit++) + if (!MAC_MLS_BIT_TEST(bit, + a->mme_compartments) && + MAC_MLS_BIT_TEST(bit, b->mme_compartments)) + return (0); + return (a->mme_level >= b->mme_level); + + default: + panic("mls_dominate_element: b->mme_type invalid"); + } + + default: + panic("mls_dominate_element: a->mme_type invalid"); + } + + return (0); +} + +static int +mls_range_in_range(struct mac_mls *rangea, struct mac_mls *rangeb) +{ + + return (mls_dominate_element(&rangeb->mm_rangehigh, + &rangea->mm_rangehigh) && + mls_dominate_element(&rangea->mm_rangelow, + &rangeb->mm_rangelow)); +} + +static int +mls_effective_in_range(struct mac_mls *effective, struct mac_mls *range) +{ + + KASSERT((effective->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, + ("mls_effective_in_range: a not effective")); + KASSERT((range->mm_flags & MAC_MLS_FLAG_RANGE) != 0, + ("mls_effective_in_range: b not range")); + + return (mls_dominate_element(&range->mm_rangehigh, + &effective->mm_effective) && + mls_dominate_element(&effective->mm_effective, + &range->mm_rangelow)); + + return (1); +} + +static int +mls_dominate_effective(struct mac_mls *a, struct mac_mls *b) +{ + KASSERT((a->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, + ("mls_dominate_effective: a not effective")); + KASSERT((b->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, + ("mls_dominate_effective: b not effective")); + + return (mls_dominate_element(&a->mm_effective, &b->mm_effective)); +} + +static int +mls_equal_element(struct mac_mls_element *a, struct mac_mls_element *b) +{ + + if (a->mme_type == MAC_MLS_TYPE_EQUAL || + b->mme_type == MAC_MLS_TYPE_EQUAL) + return (1); + + return (a->mme_type == b->mme_type && a->mme_level == b->mme_level); +} + +static int +mls_equal_effective(struct mac_mls *a, struct mac_mls *b) +{ + + KASSERT((a->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, + ("mls_equal_effective: a not effective")); + KASSERT((b->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, + ("mls_equal_effective: b not effective")); + + return (mls_equal_element(&a->mm_effective, &b->mm_effective)); +} + +static int +mls_contains_equal(struct mac_mls *mm) +{ + + if (mm->mm_flags & MAC_MLS_FLAG_EFFECTIVE) + if (mm->mm_effective.mme_type == MAC_MLS_TYPE_EQUAL) + return (1); + + if (mm->mm_flags & MAC_MLS_FLAG_RANGE) { + if (mm->mm_rangelow.mme_type == MAC_MLS_TYPE_EQUAL) + return (1); + if (mm->mm_rangehigh.mme_type == MAC_MLS_TYPE_EQUAL) + return (1); + } + + return (0); +} + +static int +mls_subject_privileged(struct mac_mls *mm) +{ + + KASSERT((mm->mm_flags & MAC_MLS_FLAGS_BOTH) == MAC_MLS_FLAGS_BOTH, + ("mls_subject_privileged: subject doesn't have both labels")); + + /* If the effective is EQUAL, it's ok. */ + if (mm->mm_effective.mme_type == MAC_MLS_TYPE_EQUAL) + return (0); + + /* If either range endpoint is EQUAL, it's ok. */ + if (mm->mm_rangelow.mme_type == MAC_MLS_TYPE_EQUAL || + mm->mm_rangehigh.mme_type == MAC_MLS_TYPE_EQUAL) + return (0); + + /* If the range is low-high, it's ok. */ + if (mm->mm_rangelow.mme_type == MAC_MLS_TYPE_LOW && + mm->mm_rangehigh.mme_type == MAC_MLS_TYPE_HIGH) + return (0); + + /* It's not ok. */ + return (EPERM); +} + +static int +mls_valid(struct mac_mls *mm) +{ + + if (mm->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { + switch (mm->mm_effective.mme_type) { + case MAC_MLS_TYPE_LEVEL: + break; + + case MAC_MLS_TYPE_EQUAL: + case MAC_MLS_TYPE_HIGH: + case MAC_MLS_TYPE_LOW: + if (mm->mm_effective.mme_level != 0 || + !MAC_MLS_BIT_SET_EMPTY( + mm->mm_effective.mme_compartments)) + return (EINVAL); + break; + + default: + return (EINVAL); + } + } else { + if (mm->mm_effective.mme_type != MAC_MLS_TYPE_UNDEF) + return (EINVAL); + } + + if (mm->mm_flags & MAC_MLS_FLAG_RANGE) { + switch (mm->mm_rangelow.mme_type) { + case MAC_MLS_TYPE_LEVEL: + break; + + case MAC_MLS_TYPE_EQUAL: + case MAC_MLS_TYPE_HIGH: + case MAC_MLS_TYPE_LOW: + if (mm->mm_rangelow.mme_level != 0 || + !MAC_MLS_BIT_SET_EMPTY( + mm->mm_rangelow.mme_compartments)) + return (EINVAL); + break; + + default: + return (EINVAL); + } + + switch (mm->mm_rangehigh.mme_type) { + case MAC_MLS_TYPE_LEVEL: + break; + + case MAC_MLS_TYPE_EQUAL: + case MAC_MLS_TYPE_HIGH: + case MAC_MLS_TYPE_LOW: + if (mm->mm_rangehigh.mme_level != 0 || + !MAC_MLS_BIT_SET_EMPTY( + mm->mm_rangehigh.mme_compartments)) + return (EINVAL); + break; + + default: + return (EINVAL); + } + if (!mls_dominate_element(&mm->mm_rangehigh, + &mm->mm_rangelow)) + return (EINVAL); + } else { + if (mm->mm_rangelow.mme_type != MAC_MLS_TYPE_UNDEF || + mm->mm_rangehigh.mme_type != MAC_MLS_TYPE_UNDEF) + return (EINVAL); + } + + return (0); +} + +static void +mls_set_range(struct mac_mls *mm, u_short typelow, u_short levellow, + u_char *compartmentslow, u_short typehigh, u_short levelhigh, + u_char *compartmentshigh) +{ + + mm->mm_rangelow.mme_type = typelow; + mm->mm_rangelow.mme_level = levellow; + if (compartmentslow != NULL) + memcpy(mm->mm_rangelow.mme_compartments, compartmentslow, + sizeof(mm->mm_rangelow.mme_compartments)); + mm->mm_rangehigh.mme_type = typehigh; + mm->mm_rangehigh.mme_level = levelhigh; + if (compartmentshigh != NULL) + memcpy(mm->mm_rangehigh.mme_compartments, compartmentshigh, + sizeof(mm->mm_rangehigh.mme_compartments)); + mm->mm_flags |= MAC_MLS_FLAG_RANGE; +} + +static void +mls_set_effective(struct mac_mls *mm, u_short type, u_short level, + u_char *compartments) +{ + + mm->mm_effective.mme_type = type; + mm->mm_effective.mme_level = level; + if (compartments != NULL) + memcpy(mm->mm_effective.mme_compartments, compartments, + sizeof(mm->mm_effective.mme_compartments)); + mm->mm_flags |= MAC_MLS_FLAG_EFFECTIVE; +} + +static void +mls_copy_range(struct mac_mls *labelfrom, struct mac_mls *labelto) +{ + + KASSERT((labelfrom->mm_flags & MAC_MLS_FLAG_RANGE) != 0, + ("mls_copy_range: labelfrom not range")); + + labelto->mm_rangelow = labelfrom->mm_rangelow; + labelto->mm_rangehigh = labelfrom->mm_rangehigh; + labelto->mm_flags |= MAC_MLS_FLAG_RANGE; +} + +static void +mls_copy_effective(struct mac_mls *labelfrom, struct mac_mls *labelto) +{ + + KASSERT((labelfrom->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, + ("mls_copy_effective: labelfrom not effective")); + + labelto->mm_effective = labelfrom->mm_effective; + labelto->mm_flags |= MAC_MLS_FLAG_EFFECTIVE; +} + +static void +mls_copy(struct mac_mls *source, struct mac_mls *dest) +{ + + if (source->mm_flags & MAC_MLS_FLAG_EFFECTIVE) + mls_copy_effective(source, dest); + if (source->mm_flags & MAC_MLS_FLAG_RANGE) + mls_copy_range(source, dest); +} + +/* + * Policy module operations. + */ +static void +mls_init(struct mac_policy_conf *conf) +{ + + zone_mls = uma_zcreate("mac_mls", sizeof(struct mac_mls), NULL, + NULL, NULL, NULL, UMA_ALIGN_PTR, 0); +} + +/* + * Label operations. + */ +static void +mls_init_label(struct label *label) +{ + + SLOT_SET(label, mls_alloc(M_WAITOK)); +} + +static int +mls_init_label_waitcheck(struct label *label, int flag) +{ + + SLOT_SET(label, mls_alloc(flag)); + if (SLOT(label) == NULL) + return (ENOMEM); + + return (0); +} + +static void +mls_destroy_label(struct label *label) +{ + + mls_free(SLOT(label)); + SLOT_SET(label, NULL); +} + +/* + * mls_element_to_string() accepts an sbuf and MLS element. It converts the + * MLS element to a string and stores the result in the sbuf; if there isn't + * space in the sbuf, -1 is returned. + */ +static int +mls_element_to_string(struct sbuf *sb, struct mac_mls_element *element) +{ + int i, first; + + switch (element->mme_type) { + case MAC_MLS_TYPE_HIGH: + return (sbuf_printf(sb, "high")); + + case MAC_MLS_TYPE_LOW: + return (sbuf_printf(sb, "low")); + + case MAC_MLS_TYPE_EQUAL: + return (sbuf_printf(sb, "equal")); + + case MAC_MLS_TYPE_LEVEL: + if (sbuf_printf(sb, "%d", element->mme_level) == -1) + return (-1); + + first = 1; + for (i = 1; i <= MAC_MLS_MAX_COMPARTMENTS; i++) { + if (MAC_MLS_BIT_TEST(i, element->mme_compartments)) { + if (first) { + if (sbuf_putc(sb, ':') == -1) + return (-1); + if (sbuf_printf(sb, "%d", i) == -1) + return (-1); + first = 0; + } else { + if (sbuf_printf(sb, "+%d", i) == -1) + return (-1); + } + } + } + return (0); + + default: + panic("mls_element_to_string: invalid type (%d)", + element->mme_type); + } +} + +/* + * mls_to_string() converts an MLS label to a string, and places the results + * in the passed sbuf. It returns 0 on success, or EINVAL if there isn't + * room in the sbuf. Note: the sbuf will be modified even in a failure case, + * so the caller may need to revert the sbuf by restoring the offset if + * that's undesired. + */ +static int +mls_to_string(struct sbuf *sb, struct mac_mls *mm) +{ + + if (mm->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { + if (mls_element_to_string(sb, &mm->mm_effective) == -1) + return (EINVAL); + } + + if (mm->mm_flags & MAC_MLS_FLAG_RANGE) { + if (sbuf_putc(sb, '(') == -1) + return (EINVAL); + + if (mls_element_to_string(sb, &mm->mm_rangelow) == -1) + return (EINVAL); + + if (sbuf_putc(sb, '-') == -1) + return (EINVAL); + + if (mls_element_to_string(sb, &mm->mm_rangehigh) == -1) + return (EINVAL); + + if (sbuf_putc(sb, ')') == -1) + return (EINVAL); + } + + return (0); +} + +static int +mls_externalize_label(struct label *label, char *element_name, + struct sbuf *sb, int *claimed) +{ + struct mac_mls *mm; + + if (strcmp(MAC_MLS_LABEL_NAME, element_name) != 0) + return (0); + + (*claimed)++; + + mm = SLOT(label); + + return (mls_to_string(sb, mm)); +} + +static int +mls_parse_element(struct mac_mls_element *element, char *string) +{ + char *compartment, *end, *level; + int value; + + if (strcmp(string, "high") == 0 || strcmp(string, "hi") == 0) { + element->mme_type = MAC_MLS_TYPE_HIGH; + element->mme_level = MAC_MLS_TYPE_UNDEF; + } else if (strcmp(string, "low") == 0 || strcmp(string, "lo") == 0) { + element->mme_type = MAC_MLS_TYPE_LOW; + element->mme_level = MAC_MLS_TYPE_UNDEF; + } else if (strcmp(string, "equal") == 0 || + strcmp(string, "eq") == 0) { + element->mme_type = MAC_MLS_TYPE_EQUAL; + element->mme_level = MAC_MLS_TYPE_UNDEF; + } else { + element->mme_type = MAC_MLS_TYPE_LEVEL; + + /* + * Numeric level piece of the element. + */ + level = strsep(&string, ":"); + value = strtol(level, &end, 10); + if (end == level || *end != '\0') + return (EINVAL); + if (value < 0 || value > 65535) + return (EINVAL); + element->mme_level = value; + + /* + * Optional compartment piece of the element. If none are + * included, we assume that the label has no compartments. + */ + if (string == NULL) + return (0); + if (*string == '\0') + return (0); + + while ((compartment = strsep(&string, "+")) != NULL) { + value = strtol(compartment, &end, 10); + if (compartment == end || *end != '\0') + return (EINVAL); + if (value < 1 || value > MAC_MLS_MAX_COMPARTMENTS) + return (EINVAL); + MAC_MLS_BIT_SET(value, element->mme_compartments); + } + } + + return (0); +} + +/* + * Note: destructively consumes the string, make a local copy before calling + * if that's a problem. + */ +static int +mls_parse(struct mac_mls *mm, char *string) +{ + char *rangehigh, *rangelow, *effective; + int error; + + effective = strsep(&string, "("); + if (*effective == '\0') + effective = NULL; + + if (string != NULL) { + rangelow = strsep(&string, "-"); + if (string == NULL) + return (EINVAL); + rangehigh = strsep(&string, ")"); + if (string == NULL) + return (EINVAL); + if (*string != '\0') + return (EINVAL); + } else { + rangelow = NULL; + rangehigh = NULL; + } + + KASSERT((rangelow != NULL && rangehigh != NULL) || + (rangelow == NULL && rangehigh == NULL), + ("mls_parse: range mismatch")); + + bzero(mm, sizeof(*mm)); + if (effective != NULL) { + error = mls_parse_element(&mm->mm_effective, effective); + if (error) + return (error); + mm->mm_flags |= MAC_MLS_FLAG_EFFECTIVE; + } + + if (rangelow != NULL) { + error = mls_parse_element(&mm->mm_rangelow, rangelow); + if (error) + return (error); + error = mls_parse_element(&mm->mm_rangehigh, rangehigh); + if (error) + return (error); + mm->mm_flags |= MAC_MLS_FLAG_RANGE; + } + + error = mls_valid(mm); + if (error) + return (error); + + return (0); +} + +static int +mls_internalize_label(struct label *label, char *element_name, + char *element_data, int *claimed) +{ + struct mac_mls *mm, mm_temp; + int error; + + if (strcmp(MAC_MLS_LABEL_NAME, element_name) != 0) + return (0); + + (*claimed)++; + + error = mls_parse(&mm_temp, element_data); + if (error) + return (error); + + mm = SLOT(label); + *mm = mm_temp; + + return (0); +} + +static void +mls_copy_label(struct label *src, struct label *dest) +{ + + *SLOT(dest) = *SLOT(src); +} + +/* + * Object-specific entry point implementations are sorted alphabetically by + * object type name and then by operation. + */ +static int +mls_bpfdesc_check_receive(struct bpf_d *d, struct label *dlabel, + struct ifnet *ifp, struct label *ifplabel) +{ + struct mac_mls *a, *b; + + if (!mls_enabled) + return (0); + + a = SLOT(dlabel); + b = SLOT(ifplabel); + + if (mls_equal_effective(a, b)) + return (0); + return (EACCES); +} + +static void +mls_bpfdesc_create(struct ucred *cred, struct bpf_d *d, struct label *dlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(dlabel); + + mls_copy_effective(source, dest); +} + +static void +mls_bpfdesc_create_mbuf(struct bpf_d *d, struct label *dlabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(dlabel); + dest = SLOT(mlabel); + + mls_copy_effective(source, dest); +} + +static void +mls_cred_associate_nfsd(struct ucred *cred) +{ + struct mac_mls *label; + + label = SLOT(cred->cr_label); + mls_set_effective(label, MAC_MLS_TYPE_LOW, 0, NULL); + mls_set_range(label, MAC_MLS_TYPE_LOW, 0, NULL, MAC_MLS_TYPE_HIGH, 0, + NULL); +} + +static int +mls_cred_check_relabel(struct ucred *cred, struct label *newlabel) +{ + struct mac_mls *subj, *new; + int error; + + subj = SLOT(cred->cr_label); + new = SLOT(newlabel); + + /* + * If there is an MLS label update for the credential, it may be an + * update of effective, range, or both. + */ + error = mls_atmostflags(new, MAC_MLS_FLAGS_BOTH); + if (error) + return (error); + + /* + * If the MLS label is to be changed, authorize as appropriate. + */ + if (new->mm_flags & MAC_MLS_FLAGS_BOTH) { + /* + * If the change request modifies both the MLS label + * effective and range, check that the new effective will be + * in the new range. + */ + if ((new->mm_flags & MAC_MLS_FLAGS_BOTH) == + MAC_MLS_FLAGS_BOTH && !mls_effective_in_range(new, new)) + return (EINVAL); + + /* + * To change the MLS effective label on a credential, the new + * effective label must be in the current range. + */ + if (new->mm_flags & MAC_MLS_FLAG_EFFECTIVE && + !mls_effective_in_range(new, subj)) + return (EPERM); + + /* + * To change the MLS range label on a credential, the new + * range must be in the current range. + */ + if (new->mm_flags & MAC_MLS_FLAG_RANGE && + !mls_range_in_range(new, subj)) + return (EPERM); + + /* + * To have EQUAL in any component of the new credential MLS + * label, the subject must already have EQUAL in their label. + */ + if (mls_contains_equal(new)) { + error = mls_subject_privileged(subj); + if (error) + return (error); + } + } + + return (0); +} + +static int +mls_cred_check_visible(struct ucred *cr1, struct ucred *cr2) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cr1->cr_label); + obj = SLOT(cr2->cr_label); + + /* XXX: range */ + if (!mls_dominate_effective(subj, obj)) + return (ESRCH); + + return (0); +} + +static void +mls_cred_create_init(struct ucred *cred) +{ + struct mac_mls *dest; + + dest = SLOT(cred->cr_label); + + mls_set_effective(dest, MAC_MLS_TYPE_LOW, 0, NULL); + mls_set_range(dest, MAC_MLS_TYPE_LOW, 0, NULL, MAC_MLS_TYPE_HIGH, 0, + NULL); +} + +static void +mls_cred_create_swapper(struct ucred *cred) +{ + struct mac_mls *dest; + + dest = SLOT(cred->cr_label); + + mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); + mls_set_range(dest, MAC_MLS_TYPE_LOW, 0, NULL, MAC_MLS_TYPE_HIGH, 0, + NULL); +} + +static void +mls_cred_relabel(struct ucred *cred, struct label *newlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(newlabel); + dest = SLOT(cred->cr_label); + + mls_copy(source, dest); +} + +static void +mls_devfs_create_device(struct ucred *cred, struct mount *mp, + struct cdev *dev, struct devfs_dirent *de, struct label *delabel) +{ + struct mac_mls *mm; + int mls_type; + + mm = SLOT(delabel); + if (strcmp(dev->si_name, "null") == 0 || + strcmp(dev->si_name, "zero") == 0 || + strcmp(dev->si_name, "random") == 0 || + strncmp(dev->si_name, "fd/", strlen("fd/")) == 0) + mls_type = MAC_MLS_TYPE_EQUAL; + else if (strcmp(dev->si_name, "kmem") == 0 || + strcmp(dev->si_name, "mem") == 0) + mls_type = MAC_MLS_TYPE_HIGH; + else if (ptys_equal && + (strncmp(dev->si_name, "ttyp", strlen("ttyp")) == 0 || + strncmp(dev->si_name, "ptyp", strlen("ptyp")) == 0)) + mls_type = MAC_MLS_TYPE_EQUAL; + else + mls_type = MAC_MLS_TYPE_LOW; + mls_set_effective(mm, mls_type, 0, NULL); +} + +static void +mls_devfs_create_directory(struct mount *mp, char *dirname, int dirnamelen, + struct devfs_dirent *de, struct label *delabel) +{ + struct mac_mls *mm; + + mm = SLOT(delabel); + mls_set_effective(mm, MAC_MLS_TYPE_LOW, 0, NULL); +} + +static void +mls_devfs_create_symlink(struct ucred *cred, struct mount *mp, + struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de, + struct label *delabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(delabel); + + mls_copy_effective(source, dest); +} + +static void +mls_devfs_update(struct mount *mp, struct devfs_dirent *de, + struct label *delabel, struct vnode *vp, struct label *vplabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(vplabel); + dest = SLOT(delabel); + + mls_copy_effective(source, dest); +} + +static void +mls_devfs_vnode_associate(struct mount *mp, struct label *mplabel, + struct devfs_dirent *de, struct label *delabel, struct vnode *vp, + struct label *vplabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(delabel); + dest = SLOT(vplabel); + + mls_copy_effective(source, dest); +} + +static int +mls_ifnet_check_relabel(struct ucred *cred, struct ifnet *ifp, + struct label *ifplabel, struct label *newlabel) +{ + struct mac_mls *subj, *new; + int error; + + subj = SLOT(cred->cr_label); + new = SLOT(newlabel); + + /* + * If there is an MLS label update for the interface, it may be an + * update of effective, range, or both. + */ + error = mls_atmostflags(new, MAC_MLS_FLAGS_BOTH); + if (error) + return (error); + + /* + * Relabeling network interfaces requires MLS privilege. + */ + return (mls_subject_privileged(subj)); +} + +static int +mls_ifnet_check_transmit(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_mls *p, *i; + + if (!mls_enabled) + return (0); + + p = SLOT(mlabel); + i = SLOT(ifplabel); + + return (mls_effective_in_range(p, i) ? 0 : EACCES); +} + +static void +mls_ifnet_create(struct ifnet *ifp, struct label *ifplabel) +{ + struct mac_mls *dest; + int type; + + dest = SLOT(ifplabel); + + if (ifp->if_type == IFT_LOOP) + type = MAC_MLS_TYPE_EQUAL; + else + type = MAC_MLS_TYPE_LOW; + + mls_set_effective(dest, type, 0, NULL); + mls_set_range(dest, type, 0, NULL, type, 0, NULL); +} + +static void +mls_ifnet_create_mbuf(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(ifplabel); + dest = SLOT(mlabel); + + mls_copy_effective(source, dest); +} + +static void +mls_ifnet_relabel(struct ucred *cred, struct ifnet *ifp, + struct label *ifplabel, struct label *newlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(newlabel); + dest = SLOT(ifplabel); + + mls_copy(source, dest); +} + +static int +mls_inpcb_check_deliver(struct inpcb *inp, struct label *inplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_mls *p, *i; + + if (!mls_enabled) + return (0); + + p = SLOT(mlabel); + i = SLOT(inplabel); + + return (mls_equal_effective(p, i) ? 0 : EACCES); +} + +static int +mls_inpcb_check_visible(struct ucred *cred, struct inpcb *inp, + struct label *inplabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(inplabel); + + if (!mls_dominate_effective(subj, obj)) + return (ENOENT); + + return (0); +} + +static void +mls_inpcb_create(struct socket *so, struct label *solabel, struct inpcb *inp, + struct label *inplabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(solabel); + dest = SLOT(inplabel); + + mls_copy_effective(source, dest); +} + +static void +mls_inpcb_create_mbuf(struct inpcb *inp, struct label *inplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(inplabel); + dest = SLOT(mlabel); + + mls_copy_effective(source, dest); +} + +static void +mls_inpcb_sosetlabel(struct socket *so, struct label *solabel, + struct inpcb *inp, struct label *inplabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(solabel); + dest = SLOT(inplabel); + + mls_copy(source, dest); +} + +static void +mls_ip6q_create(struct mbuf *m, struct label *mlabel, struct ip6q *q6, + struct label *q6label) +{ + struct mac_mls *source, *dest; + + source = SLOT(mlabel); + dest = SLOT(q6label); + + mls_copy_effective(source, dest); +} + +static int +mls_ip6q_match(struct mbuf *m, struct label *mlabel, struct ip6q *q6, + struct label *q6label) +{ + struct mac_mls *a, *b; + + a = SLOT(q6label); + b = SLOT(mlabel); + + return (mls_equal_effective(a, b)); +} + +static void +mls_ip6q_reassemble(struct ip6q *q6, struct label *q6label, struct mbuf *m, + struct label *mlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(q6label); + dest = SLOT(mlabel); + + /* Just use the head, since we require them all to match. */ + mls_copy_effective(source, dest); +} + +static void +mls_ip6q_update(struct mbuf *m, struct label *mlabel, struct ip6q *q6, + struct label *q6label) +{ + + /* NOOP: we only accept matching labels, so no need to update */ +} + +static void +mls_ipq_create(struct mbuf *m, struct label *mlabel, struct ipq *q, + struct label *qlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(mlabel); + dest = SLOT(qlabel); + + mls_copy_effective(source, dest); +} + +static int +mls_ipq_match(struct mbuf *m, struct label *mlabel, struct ipq *q, + struct label *qlabel) +{ + struct mac_mls *a, *b; + + a = SLOT(qlabel); + b = SLOT(mlabel); + + return (mls_equal_effective(a, b)); +} + +static void +mls_ipq_reassemble(struct ipq *q, struct label *qlabel, struct mbuf *m, + struct label *mlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(qlabel); + dest = SLOT(mlabel); + + /* Just use the head, since we require them all to match. */ + mls_copy_effective(source, dest); +} + +static void +mls_ipq_update(struct mbuf *m, struct label *mlabel, struct ipq *q, + struct label *qlabel) +{ + + /* NOOP: we only accept matching labels, so no need to update */ +} + +static int +mls_mount_check_stat(struct ucred *cred, struct mount *mp, + struct label *mntlabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(mntlabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static void +mls_mount_create(struct ucred *cred, struct mount *mp, struct label *mplabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(mplabel); + + mls_copy_effective(source, dest); +} + +static void +mls_netatalk_aarp_send(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_mls *dest; + + dest = SLOT(mlabel); + + mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); +} + +static void +mls_netinet_arp_send(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_mls *dest; + + dest = SLOT(mlabel); + + mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); +} + +static void +mls_netinet_firewall_reply(struct mbuf *mrecv, struct label *mrecvlabel, + struct mbuf *msend, struct label *msendlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(mrecvlabel); + dest = SLOT(msendlabel); + + mls_copy_effective(source, dest); +} + +static void +mls_netinet_firewall_send(struct mbuf *m, struct label *mlabel) +{ + struct mac_mls *dest; + + dest = SLOT(mlabel); + + /* XXX: where is the label for the firewall really comming from? */ + mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); +} + +static void +mls_netinet_fragment(struct mbuf *m, struct label *mlabel, struct mbuf *frag, + struct label *fraglabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(mlabel); + dest = SLOT(fraglabel); + + mls_copy_effective(source, dest); +} + +static void +mls_netinet_icmp_reply(struct mbuf *mrecv, struct label *mrecvlabel, + struct mbuf *msend, struct label *msendlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(mrecvlabel); + dest = SLOT(msendlabel); + + mls_copy_effective(source, dest); +} + +static void +mls_netinet_igmp_send(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_mls *dest; + + dest = SLOT(mlabel); + + mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); +} + +static void +mls_netinet6_nd6_send(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_mls *dest; + + dest = SLOT(mlabel); + + mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); +} + +static int +mls_pipe_check_ioctl(struct ucred *cred, struct pipepair *pp, + struct label *pplabel, unsigned long cmd, void /* caddr_t */ *data) +{ + + if (!mls_enabled) + return (0); + + /* XXX: This will be implemented soon... */ + + return (0); +} + +static int +mls_pipe_check_poll(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(pplabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_pipe_check_read(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(pplabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_pipe_check_relabel(struct ucred *cred, struct pipepair *pp, + struct label *pplabel, struct label *newlabel) +{ + struct mac_mls *subj, *obj, *new; + int error; + + new = SLOT(newlabel); + subj = SLOT(cred->cr_label); + obj = SLOT(pplabel); + + /* + * If there is an MLS label update for a pipe, it must be a effective + * update. + */ + error = mls_atmostflags(new, MAC_MLS_FLAG_EFFECTIVE); + if (error) + return (error); + + /* + * To perform a relabel of a pipe (MLS label or not), MLS must + * authorize the relabel. + */ + if (!mls_effective_in_range(obj, subj)) + return (EPERM); + + /* + * If the MLS label is to be changed, authorize as appropriate. + */ + if (new->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { + /* + * To change the MLS label on a pipe, the new pipe label must + * be in the subject range. + */ + if (!mls_effective_in_range(new, subj)) + return (EPERM); + + /* + * To change the MLS label on a pipe to be EQUAL, the subject + * must have appropriate privilege. + */ + if (mls_contains_equal(new)) { + error = mls_subject_privileged(subj); + if (error) + return (error); + } + } + + return (0); +} + +static int +mls_pipe_check_stat(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(pplabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_pipe_check_write(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(pplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static void +mls_pipe_create(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(pplabel); + + mls_copy_effective(source, dest); +} + +static void +mls_pipe_relabel(struct ucred *cred, struct pipepair *pp, + struct label *pplabel, struct label *newlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(newlabel); + dest = SLOT(pplabel); + + mls_copy(source, dest); +} + +static int +mls_posixsem_check_openunlink(struct ucred *cred, struct ksem *ks, + struct label *kslabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(kslabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_posixsem_check_rdonly(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks, struct label *kslabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(kslabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_posixsem_check_write(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks, struct label *kslabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(kslabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static void +mls_posixsem_create(struct ucred *cred, struct ksem *ks, + struct label *kslabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(kslabel); + + mls_copy_effective(source, dest); +} + +static int +mls_proc_check_debug(struct ucred *cred, struct proc *p) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(p->p_ucred->cr_label); + + /* XXX: range checks */ + if (!mls_dominate_effective(subj, obj)) + return (ESRCH); + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_proc_check_sched(struct ucred *cred, struct proc *p) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(p->p_ucred->cr_label); + + /* XXX: range checks */ + if (!mls_dominate_effective(subj, obj)) + return (ESRCH); + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_proc_check_signal(struct ucred *cred, struct proc *p, int signum) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(p->p_ucred->cr_label); + + /* XXX: range checks */ + if (!mls_dominate_effective(subj, obj)) + return (ESRCH); + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_socket_check_deliver(struct socket *so, struct label *solabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_mls *p, *s; + + if (!mls_enabled) + return (0); + + p = SLOT(mlabel); + s = SLOT(solabel); + + return (mls_equal_effective(p, s) ? 0 : EACCES); +} + +static int +mls_socket_check_relabel(struct ucred *cred, struct socket *so, + struct label *solabel, struct label *newlabel) +{ + struct mac_mls *subj, *obj, *new; + int error; + + new = SLOT(newlabel); + subj = SLOT(cred->cr_label); + obj = SLOT(solabel); + + /* + * If there is an MLS label update for the socket, it may be an + * update of effective. + */ + error = mls_atmostflags(new, MAC_MLS_FLAG_EFFECTIVE); + if (error) + return (error); + + /* + * To relabel a socket, the old socket effective must be in the + * subject range. + */ + if (!mls_effective_in_range(obj, subj)) + return (EPERM); + + /* + * If the MLS label is to be changed, authorize as appropriate. + */ + if (new->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { + /* + * To relabel a socket, the new socket effective must be in + * the subject range. + */ + if (!mls_effective_in_range(new, subj)) + return (EPERM); + + /* + * To change the MLS label on the socket to contain EQUAL, + * the subject must have appropriate privilege. + */ + if (mls_contains_equal(new)) { + error = mls_subject_privileged(subj); + if (error) + return (error); + } + } + + return (0); +} + +static int +mls_socket_check_visible(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(solabel); + + if (!mls_dominate_effective(subj, obj)) + return (ENOENT); + + return (0); +} + +static void +mls_socket_create(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(solabel); + + mls_copy_effective(source, dest); +} + +static void +mls_socket_create_mbuf(struct socket *so, struct label *solabel, + struct mbuf *m, struct label *mlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(solabel); + dest = SLOT(mlabel); + + mls_copy_effective(source, dest); +} + +static void +mls_socket_newconn(struct socket *oldso, struct label *oldsolabel, + struct socket *newso, struct label *newsolabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(oldsolabel); + dest = SLOT(newsolabel); + + mls_copy_effective(source, dest); +} + +static void +mls_socket_relabel(struct ucred *cred, struct socket *so, + struct label *solabel, struct label *newlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(newlabel); + dest = SLOT(solabel); + + mls_copy(source, dest); +} + +static void +mls_socketpeer_set_from_mbuf(struct mbuf *m, struct label *mlabel, + struct socket *so, struct label *sopeerlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(mlabel); + dest = SLOT(sopeerlabel); + + mls_copy_effective(source, dest); +} + +static void +mls_socketpeer_set_from_socket(struct socket *oldso, + struct label *oldsolabel, struct socket *newso, + struct label *newsopeerlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(oldsolabel); + dest = SLOT(newsopeerlabel); + + mls_copy_effective(source, dest); +} + +static void +mls_syncache_create(struct label *label, struct inpcb *inp) +{ + struct mac_mls *source, *dest; + + source = SLOT(inp->inp_label); + dest = SLOT(label); + + mls_copy_effective(source, dest); +} + +static void +mls_syncache_create_mbuf(struct label *sc_label, struct mbuf *m, + struct label *mlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(sc_label); + dest = SLOT(mlabel); + + mls_copy_effective(source, dest); +} + +static int +mls_system_check_acct(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(obj, subj) || + !mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_system_check_auditctl(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(obj, subj) || + !mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_system_check_swapon(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(obj, subj) || + !mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static void +mls_sysvmsg_cleanup(struct label *msglabel) +{ + + bzero(SLOT(msglabel), sizeof(struct mac_mls)); +} + +static void +mls_sysvmsg_create(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqlabel, struct msg *msgptr, struct label *msglabel) +{ + struct mac_mls *source, *dest; + + /* Ignore the msgq label. */ + source = SLOT(cred->cr_label); + dest = SLOT(msglabel); + + mls_copy_effective(source, dest); +} + +static int +mls_sysvmsq_check_msgrcv(struct ucred *cred, struct msg *msgptr, + struct label *msglabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(msglabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_sysvmsq_check_msgrmid(struct ucred *cred, struct msg *msgptr, + struct label *msglabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(msglabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_sysvmsq_check_msqget(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqklabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(msqklabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_sysvmsq_check_msqsnd(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqklabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(msqklabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_sysvmsq_check_msqrcv(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqklabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(msqklabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_sysvmsq_check_msqctl(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqklabel, int cmd) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(msqklabel); + + switch(cmd) { + case IPC_RMID: + case IPC_SET: + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + break; + + case IPC_STAT: + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + break; + + default: + return (EACCES); + } + + return (0); +} + +static void +mls_sysvmsq_cleanup(struct label *msqlabel) +{ + + bzero(SLOT(msqlabel), sizeof(struct mac_mls)); +} + +static void +mls_sysvmsq_create(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(msqlabel); + + mls_copy_effective(source, dest); +} + +static int +mls_sysvsem_check_semctl(struct ucred *cred, struct semid_kernel *semakptr, + struct label *semaklabel, int cmd) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(semaklabel); + + switch(cmd) { + case IPC_RMID: + case IPC_SET: + case SETVAL: + case SETALL: + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + break; + + case IPC_STAT: + case GETVAL: + case GETPID: + case GETNCNT: + case GETZCNT: + case GETALL: + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + break; + + default: + return (EACCES); + } + + return (0); +} + +static int +mls_sysvsem_check_semget(struct ucred *cred, struct semid_kernel *semakptr, + struct label *semaklabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(semaklabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_sysvsem_check_semop(struct ucred *cred, struct semid_kernel *semakptr, + struct label *semaklabel, size_t accesstype) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(semaklabel); + + if( accesstype & SEM_R ) + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + if( accesstype & SEM_A ) + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static void +mls_sysvsem_cleanup(struct label *semalabel) +{ + + bzero(SLOT(semalabel), sizeof(struct mac_mls)); +} + +static void +mls_sysvsem_create(struct ucred *cred, struct semid_kernel *semakptr, + struct label *semalabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(semalabel); + + mls_copy_effective(source, dest); +} + +static int +mls_sysvshm_check_shmat(struct ucred *cred, struct shmid_kernel *shmsegptr, + struct label *shmseglabel, int shmflg) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(shmseglabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + if ((shmflg & SHM_RDONLY) == 0) { + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + } + + return (0); +} + +static int +mls_sysvshm_check_shmctl(struct ucred *cred, struct shmid_kernel *shmsegptr, + struct label *shmseglabel, int cmd) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(shmseglabel); + + switch(cmd) { + case IPC_RMID: + case IPC_SET: + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + break; + + case IPC_STAT: + case SHM_STAT: + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + break; + + default: + return (EACCES); + } + + return (0); +} + +static int +mls_sysvshm_check_shmget(struct ucred *cred, struct shmid_kernel *shmsegptr, + struct label *shmseglabel, int shmflg) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(shmseglabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static void +mls_sysvshm_cleanup(struct label *shmlabel) +{ + + bzero(SLOT(shmlabel), sizeof(struct mac_mls)); +} + +static void +mls_sysvshm_create(struct ucred *cred, struct shmid_kernel *shmsegptr, + struct label *shmlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(shmlabel); + + mls_copy_effective(source, dest); +} + +static int +mls_vnode_associate_extattr(struct mount *mp, struct label *mplabel, + struct vnode *vp, struct label *vplabel) +{ + struct mac_mls mm_temp, *source, *dest; + int buflen, error; + + source = SLOT(mplabel); + dest = SLOT(vplabel); + + buflen = sizeof(mm_temp); + bzero(&mm_temp, buflen); + + error = vn_extattr_get(vp, IO_NODELOCKED, MAC_MLS_EXTATTR_NAMESPACE, + MAC_MLS_EXTATTR_NAME, &buflen, (char *) &mm_temp, curthread); + if (error == ENOATTR || error == EOPNOTSUPP) { + /* Fall back to the mntlabel. */ + mls_copy_effective(source, dest); + return (0); + } else if (error) + return (error); + + if (buflen != sizeof(mm_temp)) { + printf("mls_vnode_associate_extattr: bad size %d\n", buflen); + return (EPERM); + } + if (mls_valid(&mm_temp) != 0) { + printf("mls_vnode_associate_extattr: invalid\n"); + return (EPERM); + } + if ((mm_temp.mm_flags & MAC_MLS_FLAGS_BOTH) != + MAC_MLS_FLAG_EFFECTIVE) { + printf("mls_associated_vnode_extattr: not effective\n"); + return (EPERM); + } + + mls_copy_effective(&mm_temp, dest); + return (0); +} + +static void +mls_vnode_associate_singlelabel(struct mount *mp, struct label *mplabel, + struct vnode *vp, struct label *vplabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(mplabel); + dest = SLOT(vplabel); + + mls_copy_effective(source, dest); +} + +static int +mls_vnode_check_chdir(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_chroot(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_create(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct componentname *cnp, struct vattr *vap) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_deleteacl(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_deleteextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace, const char *name) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_exec(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct image_params *imgp, + struct label *execlabel) +{ + struct mac_mls *subj, *obj, *exec; + int error; + + if (execlabel != NULL) { + /* + * We currently don't permit labels to be changed at + * exec-time as part of MLS, so disallow non-NULL MLS label + * elements in the execlabel. + */ + exec = SLOT(execlabel); + error = mls_atmostflags(exec, 0); + if (error) + return (error); + } + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_getacl(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_getextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace, const char *name, + struct uio *uio) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_link(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + obj = SLOT(vplabel); + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_listextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace) +{ + + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_lookup(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct componentname *cnp) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_mmap(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int prot, int flags) +{ + struct mac_mls *subj, *obj; + + /* + * Rely on the use of open()-time protections to handle + * non-revocation cases. + */ + if (!mls_enabled || !revocation_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) { + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + } + if (((prot & VM_PROT_WRITE) != 0) && ((flags & MAP_SHARED) != 0)) { + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + } + + return (0); +} + +static int +mls_vnode_check_open(struct ucred *cred, struct vnode *vp, + struct label *vplabel, accmode_t accmode) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + /* XXX privilege override for admin? */ + if (accmode & (VREAD | VEXEC | VSTAT)) { + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + } + if (accmode & (VWRITE | VAPPEND | VADMIN)) { + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + } + + return (0); +} + +static int +mls_vnode_check_poll(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp, struct label *vplabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled || !revocation_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_read(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp, struct label *vplabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled || !revocation_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_readdir(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_readlink(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_relabel(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct label *newlabel) +{ + struct mac_mls *old, *new, *subj; + int error; + + old = SLOT(vplabel); + new = SLOT(newlabel); + subj = SLOT(cred->cr_label); + + /* + * If there is an MLS label update for the vnode, it must be a + * effective label. + */ + error = mls_atmostflags(new, MAC_MLS_FLAG_EFFECTIVE); + if (error) + return (error); + + /* + * To perform a relabel of the vnode (MLS label or not), MLS must + * authorize the relabel. + */ + if (!mls_effective_in_range(old, subj)) + return (EPERM); + + /* + * If the MLS label is to be changed, authorize as appropriate. + */ + if (new->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { + /* + * To change the MLS label on a vnode, the new vnode label + * must be in the subject range. + */ + if (!mls_effective_in_range(new, subj)) + return (EPERM); + + /* + * To change the MLS label on the vnode to be EQUAL, the + * subject must have appropriate privilege. + */ + if (mls_contains_equal(new)) { + error = mls_subject_privileged(subj); + if (error) + return (error); + } + } + + return (0); +} + +static int +mls_vnode_check_rename_from(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + obj = SLOT(vplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + int samedir, struct componentname *cnp) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + if (vp != NULL) { + obj = SLOT(vplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + } + + return (0); +} + +static int +mls_vnode_check_revoke(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_setacl(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type, struct acl *acl) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_setextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace, const char *name, + struct uio *uio) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + /* XXX: protect the MAC EA in a special way? */ + + return (0); +} + +static int +mls_vnode_check_setflags(struct ucred *cred, struct vnode *vp, + struct label *vplabel, u_long flags) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_setmode(struct ucred *cred, struct vnode *vp, + struct label *vplabel, mode_t mode) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_setowner(struct ucred *cred, struct vnode *vp, + struct label *vplabel, uid_t uid, gid_t gid) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_setutimes(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct timespec atime, struct timespec mtime) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_stat(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp, struct label *vplabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_unlink(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(dvplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + obj = SLOT(vplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_check_write(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp, struct label *vplabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled || !revocation_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(vplabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_vnode_create_extattr(struct ucred *cred, struct mount *mp, + struct label *mplabel, struct vnode *dvp, struct label *dvplabel, + struct vnode *vp, struct label *vplabel, struct componentname *cnp) +{ + struct mac_mls *source, *dest, mm_temp; + size_t buflen; + int error; + + buflen = sizeof(mm_temp); + bzero(&mm_temp, buflen); + + source = SLOT(cred->cr_label); + dest = SLOT(vplabel); + mls_copy_effective(source, &mm_temp); + + error = vn_extattr_set(vp, IO_NODELOCKED, MAC_MLS_EXTATTR_NAMESPACE, + MAC_MLS_EXTATTR_NAME, buflen, (char *) &mm_temp, curthread); + if (error == 0) + mls_copy_effective(source, dest); + return (error); +} + +static void +mls_vnode_relabel(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct label *label) +{ + struct mac_mls *source, *dest; + + source = SLOT(label); + dest = SLOT(vplabel); + + mls_copy(source, dest); +} + +static int +mls_vnode_setlabel_extattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct label *intlabel) +{ + struct mac_mls *source, mm_temp; + size_t buflen; + int error; + + buflen = sizeof(mm_temp); + bzero(&mm_temp, buflen); + + source = SLOT(intlabel); + if ((source->mm_flags & MAC_MLS_FLAG_EFFECTIVE) == 0) + return (0); + + mls_copy_effective(source, &mm_temp); + + error = vn_extattr_set(vp, IO_NODELOCKED, MAC_MLS_EXTATTR_NAMESPACE, + MAC_MLS_EXTATTR_NAME, buflen, (char *) &mm_temp, curthread); + return (error); +} + +static struct mac_policy_ops mls_ops = +{ + .mpo_init = mls_init, + + .mpo_bpfdesc_check_receive = mls_bpfdesc_check_receive, + .mpo_bpfdesc_create = mls_bpfdesc_create, + .mpo_bpfdesc_create_mbuf = mls_bpfdesc_create_mbuf, + .mpo_bpfdesc_destroy_label = mls_destroy_label, + .mpo_bpfdesc_init_label = mls_init_label, + + .mpo_cred_associate_nfsd = mls_cred_associate_nfsd, + .mpo_cred_check_relabel = mls_cred_check_relabel, + .mpo_cred_check_visible = mls_cred_check_visible, + .mpo_cred_copy_label = mls_copy_label, + .mpo_cred_create_init = mls_cred_create_init, + .mpo_cred_create_swapper = mls_cred_create_swapper, + .mpo_cred_destroy_label = mls_destroy_label, + .mpo_cred_externalize_label = mls_externalize_label, + .mpo_cred_init_label = mls_init_label, + .mpo_cred_internalize_label = mls_internalize_label, + .mpo_cred_relabel = mls_cred_relabel, + + .mpo_devfs_create_device = mls_devfs_create_device, + .mpo_devfs_create_directory = mls_devfs_create_directory, + .mpo_devfs_create_symlink = mls_devfs_create_symlink, + .mpo_devfs_destroy_label = mls_destroy_label, + .mpo_devfs_init_label = mls_init_label, + .mpo_devfs_update = mls_devfs_update, + .mpo_devfs_vnode_associate = mls_devfs_vnode_associate, + + .mpo_ifnet_check_relabel = mls_ifnet_check_relabel, + .mpo_ifnet_check_transmit = mls_ifnet_check_transmit, + .mpo_ifnet_copy_label = mls_copy_label, + .mpo_ifnet_create = mls_ifnet_create, + .mpo_ifnet_create_mbuf = mls_ifnet_create_mbuf, + .mpo_ifnet_destroy_label = mls_destroy_label, + .mpo_ifnet_externalize_label = mls_externalize_label, + .mpo_ifnet_init_label = mls_init_label, + .mpo_ifnet_internalize_label = mls_internalize_label, + .mpo_ifnet_relabel = mls_ifnet_relabel, + + .mpo_inpcb_check_deliver = mls_inpcb_check_deliver, + .mpo_inpcb_check_visible = mls_inpcb_check_visible, + .mpo_inpcb_create = mls_inpcb_create, + .mpo_inpcb_create_mbuf = mls_inpcb_create_mbuf, + .mpo_inpcb_destroy_label = mls_destroy_label, + .mpo_inpcb_init_label = mls_init_label_waitcheck, + .mpo_inpcb_sosetlabel = mls_inpcb_sosetlabel, + + .mpo_ip6q_create = mls_ip6q_create, + .mpo_ip6q_destroy_label = mls_destroy_label, + .mpo_ip6q_init_label = mls_init_label_waitcheck, + .mpo_ip6q_match = mls_ip6q_match, + .mpo_ip6q_reassemble = mls_ip6q_reassemble, + .mpo_ip6q_update = mls_ip6q_update, + + .mpo_ipq_create = mls_ipq_create, + .mpo_ipq_destroy_label = mls_destroy_label, + .mpo_ipq_init_label = mls_init_label_waitcheck, + .mpo_ipq_match = mls_ipq_match, + .mpo_ipq_reassemble = mls_ipq_reassemble, + .mpo_ipq_update = mls_ipq_update, + + .mpo_mbuf_copy_label = mls_copy_label, + .mpo_mbuf_destroy_label = mls_destroy_label, + .mpo_mbuf_init_label = mls_init_label_waitcheck, + + .mpo_mount_check_stat = mls_mount_check_stat, + .mpo_mount_create = mls_mount_create, + .mpo_mount_destroy_label = mls_destroy_label, + .mpo_mount_init_label = mls_init_label, + + .mpo_netatalk_aarp_send = mls_netatalk_aarp_send, + + .mpo_netinet_arp_send = mls_netinet_arp_send, + .mpo_netinet_firewall_reply = mls_netinet_firewall_reply, + .mpo_netinet_firewall_send = mls_netinet_firewall_send, + .mpo_netinet_fragment = mls_netinet_fragment, + .mpo_netinet_icmp_reply = mls_netinet_icmp_reply, + .mpo_netinet_igmp_send = mls_netinet_igmp_send, + + .mpo_netinet6_nd6_send = mls_netinet6_nd6_send, + + .mpo_pipe_check_ioctl = mls_pipe_check_ioctl, + .mpo_pipe_check_poll = mls_pipe_check_poll, + .mpo_pipe_check_read = mls_pipe_check_read, + .mpo_pipe_check_relabel = mls_pipe_check_relabel, + .mpo_pipe_check_stat = mls_pipe_check_stat, + .mpo_pipe_check_write = mls_pipe_check_write, + .mpo_pipe_copy_label = mls_copy_label, + .mpo_pipe_create = mls_pipe_create, + .mpo_pipe_destroy_label = mls_destroy_label, + .mpo_pipe_externalize_label = mls_externalize_label, + .mpo_pipe_init_label = mls_init_label, + .mpo_pipe_internalize_label = mls_internalize_label, + .mpo_pipe_relabel = mls_pipe_relabel, + + .mpo_posixsem_check_getvalue = mls_posixsem_check_rdonly, + .mpo_posixsem_check_open = mls_posixsem_check_openunlink, + .mpo_posixsem_check_post = mls_posixsem_check_write, + .mpo_posixsem_check_stat = mls_posixsem_check_rdonly, + .mpo_posixsem_check_unlink = mls_posixsem_check_openunlink, + .mpo_posixsem_check_wait = mls_posixsem_check_write, + .mpo_posixsem_create = mls_posixsem_create, + .mpo_posixsem_destroy_label = mls_destroy_label, + .mpo_posixsem_init_label = mls_init_label, + + .mpo_proc_check_debug = mls_proc_check_debug, + .mpo_proc_check_sched = mls_proc_check_sched, + .mpo_proc_check_signal = mls_proc_check_signal, + + .mpo_socket_check_deliver = mls_socket_check_deliver, + .mpo_socket_check_relabel = mls_socket_check_relabel, + .mpo_socket_check_visible = mls_socket_check_visible, + .mpo_socket_copy_label = mls_copy_label, + .mpo_socket_create = mls_socket_create, + .mpo_socket_create_mbuf = mls_socket_create_mbuf, + .mpo_socket_destroy_label = mls_destroy_label, + .mpo_socket_externalize_label = mls_externalize_label, + .mpo_socket_init_label = mls_init_label_waitcheck, + .mpo_socket_internalize_label = mls_internalize_label, + .mpo_socket_newconn = mls_socket_newconn, + .mpo_socket_relabel = mls_socket_relabel, + + .mpo_socketpeer_destroy_label = mls_destroy_label, + .mpo_socketpeer_externalize_label = mls_externalize_label, + .mpo_socketpeer_init_label = mls_init_label_waitcheck, + .mpo_socketpeer_set_from_mbuf = mls_socketpeer_set_from_mbuf, + .mpo_socketpeer_set_from_socket = mls_socketpeer_set_from_socket, + + .mpo_syncache_create = mls_syncache_create, + .mpo_syncache_create_mbuf = mls_syncache_create_mbuf, + .mpo_syncache_destroy_label = mls_destroy_label, + .mpo_syncache_init_label = mls_init_label_waitcheck, + + .mpo_sysvmsg_cleanup = mls_sysvmsg_cleanup, + .mpo_sysvmsg_create = mls_sysvmsg_create, + .mpo_sysvmsg_destroy_label = mls_destroy_label, + .mpo_sysvmsg_init_label = mls_init_label, + + .mpo_sysvmsq_check_msgrcv = mls_sysvmsq_check_msgrcv, + .mpo_sysvmsq_check_msgrmid = mls_sysvmsq_check_msgrmid, + .mpo_sysvmsq_check_msqget = mls_sysvmsq_check_msqget, + .mpo_sysvmsq_check_msqsnd = mls_sysvmsq_check_msqsnd, + .mpo_sysvmsq_check_msqrcv = mls_sysvmsq_check_msqrcv, + .mpo_sysvmsq_check_msqctl = mls_sysvmsq_check_msqctl, + .mpo_sysvmsq_cleanup = mls_sysvmsq_cleanup, + .mpo_sysvmsq_destroy_label = mls_destroy_label, + .mpo_sysvmsq_init_label = mls_init_label, + .mpo_sysvmsq_create = mls_sysvmsq_create, + + .mpo_sysvsem_check_semctl = mls_sysvsem_check_semctl, + .mpo_sysvsem_check_semget = mls_sysvsem_check_semget, + .mpo_sysvsem_check_semop = mls_sysvsem_check_semop, + .mpo_sysvsem_cleanup = mls_sysvsem_cleanup, + .mpo_sysvsem_create = mls_sysvsem_create, + .mpo_sysvsem_destroy_label = mls_destroy_label, + .mpo_sysvsem_init_label = mls_init_label, + + .mpo_sysvshm_check_shmat = mls_sysvshm_check_shmat, + .mpo_sysvshm_check_shmctl = mls_sysvshm_check_shmctl, + .mpo_sysvshm_check_shmget = mls_sysvshm_check_shmget, + .mpo_sysvshm_cleanup = mls_sysvshm_cleanup, + .mpo_sysvshm_create = mls_sysvshm_create, + .mpo_sysvshm_destroy_label = mls_destroy_label, + .mpo_sysvshm_init_label = mls_init_label, + + + .mpo_system_check_acct = mls_system_check_acct, + .mpo_system_check_auditctl = mls_system_check_auditctl, + .mpo_system_check_swapon = mls_system_check_swapon, + + .mpo_vnode_associate_extattr = mls_vnode_associate_extattr, + .mpo_vnode_associate_singlelabel = mls_vnode_associate_singlelabel, + .mpo_vnode_check_access = mls_vnode_check_open, + .mpo_vnode_check_chdir = mls_vnode_check_chdir, + .mpo_vnode_check_chroot = mls_vnode_check_chroot, + .mpo_vnode_check_create = mls_vnode_check_create, + .mpo_vnode_check_deleteacl = mls_vnode_check_deleteacl, + .mpo_vnode_check_deleteextattr = mls_vnode_check_deleteextattr, + .mpo_vnode_check_exec = mls_vnode_check_exec, + .mpo_vnode_check_getacl = mls_vnode_check_getacl, + .mpo_vnode_check_getextattr = mls_vnode_check_getextattr, + .mpo_vnode_check_link = mls_vnode_check_link, + .mpo_vnode_check_listextattr = mls_vnode_check_listextattr, + .mpo_vnode_check_lookup = mls_vnode_check_lookup, + .mpo_vnode_check_mmap = mls_vnode_check_mmap, + .mpo_vnode_check_open = mls_vnode_check_open, + .mpo_vnode_check_poll = mls_vnode_check_poll, + .mpo_vnode_check_read = mls_vnode_check_read, + .mpo_vnode_check_readdir = mls_vnode_check_readdir, + .mpo_vnode_check_readlink = mls_vnode_check_readlink, + .mpo_vnode_check_relabel = mls_vnode_check_relabel, + .mpo_vnode_check_rename_from = mls_vnode_check_rename_from, + .mpo_vnode_check_rename_to = mls_vnode_check_rename_to, + .mpo_vnode_check_revoke = mls_vnode_check_revoke, + .mpo_vnode_check_setacl = mls_vnode_check_setacl, + .mpo_vnode_check_setextattr = mls_vnode_check_setextattr, + .mpo_vnode_check_setflags = mls_vnode_check_setflags, + .mpo_vnode_check_setmode = mls_vnode_check_setmode, + .mpo_vnode_check_setowner = mls_vnode_check_setowner, + .mpo_vnode_check_setutimes = mls_vnode_check_setutimes, + .mpo_vnode_check_stat = mls_vnode_check_stat, + .mpo_vnode_check_unlink = mls_vnode_check_unlink, + .mpo_vnode_check_write = mls_vnode_check_write, + .mpo_vnode_copy_label = mls_copy_label, + .mpo_vnode_create_extattr = mls_vnode_create_extattr, + .mpo_vnode_destroy_label = mls_destroy_label, + .mpo_vnode_externalize_label = mls_externalize_label, + .mpo_vnode_init_label = mls_init_label, + .mpo_vnode_internalize_label = mls_internalize_label, + .mpo_vnode_relabel = mls_vnode_relabel, + .mpo_vnode_setlabel_extattr = mls_vnode_setlabel_extattr, +}; + +#define MLS_OBJECTS (MPC_OBJECT_CRED | \ + /* MPC_OBJECT_PROC | */ \ + MPC_OBJECT_VNODE | \ + MPC_OBJECT_INPCB | \ + MPC_OBJECT_SOCKET | \ + MPC_OBJECT_DEVFS | \ + MPC_OBJECT_MBUF | \ + MPC_OBJECT_IPQ | \ + MPC_OBJECT_IFNET | \ + MPC_OBJECT_BPFDESC | \ + MPC_OBJECT_PIPE | \ + MPC_OBJECT_MOUNT | \ + MPC_OBJECT_POSIXSEM | \ + /* MPC_OBJECT_POSIXSHM | */ \ + MPC_OBJECT_SYSVMSG | \ + MPC_OBJECT_SYSVMSQ | \ + MPC_OBJECT_SYSVSEM | \ + MPC_OBJECT_SYSVSHM | \ + MPC_OBJECT_SYNCACHE) + +MAC_POLICY_SET(&mls_ops, mac_mls, "TrustedBSD MAC/MLS", + MPC_LOADTIME_FLAG_NOTLATE, &mls_slot, MLS_OBJECTS); diff --git a/sys/security/mac_mls/mac_mls.h b/sys/security/mac_mls/mac_mls.h new file mode 100644 index 0000000..7bed921 --- /dev/null +++ b/sys/security/mac_mls/mac_mls.h @@ -0,0 +1,102 @@ +/*- + * Copyright (c) 1999-2002 Robert N. M. Watson + * Copyright (c) 2001-2004 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +/* + * Definitions for the TrustedBSD MLS confidentiality policy module. + */ +#ifndef _SYS_SECURITY_MAC_MLS_H +#define _SYS_SECURITY_MAC_MLS_H + +#define MAC_MLS_EXTATTR_NAMESPACE EXTATTR_NAMESPACE_SYSTEM +#define MAC_MLS_EXTATTR_NAME "mac_mls" + +#define MAC_MLS_LABEL_NAME "mls" + +#define MAC_MLS_FLAG_EFFECTIVE 0x00000001 /* mm_effective initialized */ +#define MAC_MLS_FLAG_RANGE 0x00000002 /* mm_range* initialized */ +#define MAC_MLS_FLAGS_BOTH (MAC_MLS_FLAG_EFFECTIVE | MAC_MLS_FLAG_RANGE) + +#define MAC_MLS_TYPE_UNDEF 0 /* Undefined */ +#define MAC_MLS_TYPE_LEVEL 1 /* Hierarchal level with mm_level. */ +#define MAC_MLS_TYPE_LOW 2 /* Dominated by any + * MAC_MLS_TYPE_LABEL. */ +#define MAC_MLS_TYPE_HIGH 3 /* Dominates any + * MAC_MLS_TYPE_LABEL. */ +#define MAC_MLS_TYPE_EQUAL 4 /* Equivilent to any + * MAC_MLS_TYPE_LABEL. */ + +/* + * Structures and constants associated with a Multi-Level Security policy. + * mac_mls represents an MLS label, with mm_type determining its properties, + * and mm_level represents the hierarchal sensitivity level if valid for the + * current mm_type. If compartments are used, the same semantics apply as + * long as the suject is in every compartment the object is in. LOW, EQUAL + * and HIGH cannot be in compartments. + */ + +/* + * MLS compartments bit set size (in bits). + */ +#define MAC_MLS_MAX_COMPARTMENTS 256 + +struct mac_mls_element { + u_short mme_type; + u_short mme_level; + u_char mme_compartments[MAC_MLS_MAX_COMPARTMENTS >> 3]; +}; + +/* + * MLS labels consist of two components: an effective label, and a label + * range. Depending on the context, one or both may be used; the mb_flags + * field permits the provider to indicate what fields are intended for + * use. + */ +struct mac_mls { + int mm_flags; + struct mac_mls_element mm_effective; + struct mac_mls_element mm_rangelow, mm_rangehigh; +}; + +/* + * MLS compartments bit test/set macros. + * The range is 1 to MAC_MLS_MAX_COMPARTMENTS. + */ +#define MAC_MLS_BIT_TEST(b, w) \ + ((w)[(((b) - 1) >> 3)] & (1 << (((b) - 1) & 7))) +#define MAC_MLS_BIT_SET(b, w) \ + ((w)[(((b) - 1) >> 3)] |= (1 << (((b) - 1) & 7))) +#define MAC_MLS_BIT_SET_EMPTY(set) mls_bit_set_empty(set) + +#endif /* !_SYS_SECURITY_MAC_MLS_H */ diff --git a/sys/security/mac_none/mac_none.c b/sys/security/mac_none/mac_none.c new file mode 100644 index 0000000..d57c309 --- /dev/null +++ b/sys/security/mac_none/mac_none.c @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 1999-2002, 2007 Robert N. M. Watson + * Copyright (c) 2001-2003 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Developed by the TrustedBSD Project. + * + * Sample policy implementing no entry points; for performance measurement + * purposes only. If you're looking for a stub policy to base new policies + * on, try mac_stub. + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/module.h> + +#include <security/mac/mac_policy.h> + +static struct mac_policy_ops none_ops = +{ +}; + +MAC_POLICY_SET(&none_ops, mac_none, "TrustedBSD MAC/None", + MPC_LOADTIME_FLAG_UNLOADOK, NULL, 0); diff --git a/sys/security/mac_partition/mac_partition.c b/sys/security/mac_partition/mac_partition.c new file mode 100644 index 0000000..7eca1f9 --- /dev/null +++ b/sys/security/mac_partition/mac_partition.c @@ -0,0 +1,319 @@ +/*- + * Copyright (c) 1999-2002, 2007-2008 Robert N. M. Watson + * Copyright (c) 2001-2002 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * Copyright (c) 2008 Apple Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Developed by the TrustedBSD Project. + * + * Experiment with a partition-like model. + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/priv.h> +#include <sys/proc.h> +#include <sys/sbuf.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/systm.h> +#include <sys/sysctl.h> + +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_pcb.h> + +#include <security/mac/mac_policy.h> +#include <security/mac_partition/mac_partition.h> + +SYSCTL_DECL(_security_mac); + +SYSCTL_NODE(_security_mac, OID_AUTO, partition, CTLFLAG_RW, 0, + "TrustedBSD mac_partition policy controls"); + +static int partition_enabled = 1; +SYSCTL_INT(_security_mac_partition, OID_AUTO, enabled, CTLFLAG_RW, + &partition_enabled, 0, "Enforce partition policy"); + +static int partition_slot; +#define SLOT(l) mac_label_get((l), partition_slot) +#define SLOT_SET(l, v) mac_label_set((l), partition_slot, (v)) + +static int +partition_check(struct label *subject, struct label *object) +{ + + if (partition_enabled == 0) + return (0); + + if (subject == NULL) + return (0); + + if (SLOT(subject) == 0) + return (0); + + /* + * If the object label hasn't been allocated, then it's effectively + * not in a partition, and we know the subject is as it has a label + * and it's not 0, so reject. + */ + if (object == NULL) + return (EPERM); + + if (SLOT(subject) == SLOT(object)) + return (0); + + return (EPERM); +} + +/* + * Object-specific entry points are sorted alphabetically by object type name + * and then by operation. + */ +static int +partition_cred_check_relabel(struct ucred *cred, struct label *newlabel) +{ + int error; + + error = 0; + + /* + * Treat "0" as a no-op request because it reflects an unset + * partition label. If we ever want to support switching back to an + * unpartitioned state for a process, we'll need to differentiate the + * "not in a partition" and "no partition defined during internalize" + * conditions. + */ + if (SLOT(newlabel) != 0) { + /* + * Require BSD privilege in order to change the partition. + * Originally we also required that the process not be in a + * partition in the first place, but this didn't interact + * well with sendmail. + */ + error = priv_check_cred(cred, PRIV_MAC_PARTITION, 0); + } + + return (error); +} + +static int +partition_cred_check_visible(struct ucred *cr1, struct ucred *cr2) +{ + int error; + + error = partition_check(cr1->cr_label, cr2->cr_label); + + return (error == 0 ? 0 : ESRCH); +} + +static void +partition_cred_copy_label(struct label *src, struct label *dest) +{ + + if (src != NULL && dest != NULL) + SLOT_SET(dest, SLOT(src)); + else if (dest != NULL) + SLOT_SET(dest, 0); +} + +static void +partition_cred_create_init(struct ucred *cred) +{ + + SLOT_SET(cred->cr_label, 0); +} + +static void +partition_cred_create_swapper(struct ucred *cred) +{ + + SLOT_SET(cred->cr_label, 0); +} + +static void +partition_cred_destroy_label(struct label *label) +{ + + SLOT_SET(label, 0); +} + +static int +partition_cred_externalize_label(struct label *label, char *element_name, + struct sbuf *sb, int *claimed) +{ + + if (strcmp(MAC_PARTITION_LABEL_NAME, element_name) != 0) + return (0); + + (*claimed)++; + + if (label != NULL) { + if (sbuf_printf(sb, "%jd", (intmax_t)SLOT(label)) == -1) + return (EINVAL); + } else { + if (sbuf_printf(sb, "0") == -1) + return (EINVAL); + } + return (0); +} + +static void +partition_cred_init_label(struct label *label) +{ + + SLOT_SET(label, 0); +} + +static int +partition_cred_internalize_label(struct label *label, char *element_name, + char *element_data, int *claimed) +{ + + if (strcmp(MAC_PARTITION_LABEL_NAME, element_name) != 0) + return (0); + + (*claimed)++; + SLOT_SET(label, strtol(element_data, NULL, 10)); + return (0); +} + +static void +partition_cred_relabel(struct ucred *cred, struct label *newlabel) +{ + + if (newlabel != NULL && SLOT(newlabel) != 0) + SLOT_SET(cred->cr_label, SLOT(newlabel)); +} + +static int +partition_inpcb_check_visible(struct ucred *cred, struct inpcb *inp, + struct label *inplabel) +{ + int error; + + error = partition_check(cred->cr_label, inp->inp_cred->cr_label); + + return (error ? ENOENT : 0); +} + +static int +partition_proc_check_debug(struct ucred *cred, struct proc *p) +{ + int error; + + error = partition_check(cred->cr_label, p->p_ucred->cr_label); + + return (error ? ESRCH : 0); +} + +static int +partition_proc_check_sched(struct ucred *cred, struct proc *p) +{ + int error; + + error = partition_check(cred->cr_label, p->p_ucred->cr_label); + + return (error ? ESRCH : 0); +} + +static int +partition_proc_check_signal(struct ucred *cred, struct proc *p, + int signum) +{ + int error; + + error = partition_check(cred->cr_label, p->p_ucred->cr_label); + + return (error ? ESRCH : 0); +} + +static int +partition_socket_check_visible(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + int error; + + error = partition_check(cred->cr_label, so->so_cred->cr_label); + + return (error ? ENOENT : 0); +} + +static int +partition_vnode_check_exec(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct image_params *imgp, + struct label *execlabel) +{ + + if (execlabel != NULL) { + /* + * We currently don't permit labels to be changed at + * exec-time as part of the partition model, so disallow + * non-NULL partition label changes in execlabel. + */ + if (SLOT(execlabel) != 0) + return (EINVAL); + } + + return (0); +} + +static struct mac_policy_ops partition_ops = +{ + .mpo_cred_check_relabel = partition_cred_check_relabel, + .mpo_cred_check_visible = partition_cred_check_visible, + .mpo_cred_copy_label = partition_cred_copy_label, + .mpo_cred_create_init = partition_cred_create_init, + .mpo_cred_create_swapper = partition_cred_create_swapper, + .mpo_cred_destroy_label = partition_cred_destroy_label, + .mpo_cred_externalize_label = partition_cred_externalize_label, + .mpo_cred_init_label = partition_cred_init_label, + .mpo_cred_internalize_label = partition_cred_internalize_label, + .mpo_cred_relabel = partition_cred_relabel, + .mpo_inpcb_check_visible = partition_inpcb_check_visible, + .mpo_proc_check_debug = partition_proc_check_debug, + .mpo_proc_check_sched = partition_proc_check_sched, + .mpo_proc_check_signal = partition_proc_check_signal, + .mpo_socket_check_visible = partition_socket_check_visible, + .mpo_vnode_check_exec = partition_vnode_check_exec, +}; + +MAC_POLICY_SET(&partition_ops, mac_partition, "TrustedBSD MAC/Partition", + MPC_LOADTIME_FLAG_UNLOADOK, &partition_slot, MPC_OBJECT_CRED); diff --git a/sys/security/mac_partition/mac_partition.h b/sys/security/mac_partition/mac_partition.h new file mode 100644 index 0000000..f5675e2 --- /dev/null +++ b/sys/security/mac_partition/mac_partition.h @@ -0,0 +1,46 @@ +/*- + * Copyright (c) 1999-2002 Robert N. M. Watson + * Copyright (c) 2001-2002 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Definitions for the TrustedBSD partition policy module. + */ + +#ifndef _SYS_SECURITY_MAC_PARTITION_H +#define _SYS_SECURITY_MAC_PARTITION_H + +#define MAC_PARTITION_LABEL_NAME "partition" + +#endif /* !_SYS_SECURITY_MAC_PARTITION_H */ diff --git a/sys/security/mac_portacl/mac_portacl.c b/sys/security/mac_portacl/mac_portacl.c new file mode 100644 index 0000000..eb388cc --- /dev/null +++ b/sys/security/mac_portacl/mac_portacl.c @@ -0,0 +1,493 @@ +/*- + * Copyright (c) 2003-2004 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Developed by the TrustedBSD Project. + * + * Administratively limit access to local UDP/TCP ports for binding purposes. + * Intended to be combined with net.inet.ip.portrange.reservedhigh to allow + * specific uids and gids to bind specific ports for specific purposes, + * while not opening the door to any user replacing an "official" service + * while you're restarting it. This only affects ports explicitly bound by + * the user process (either for listen/outgoing socket for TCP, or send/ + * receive for UDP). This module will not limit ports bound implicitly for + * out-going connections where the process hasn't explicitly selected a port: + * these are automatically selected by the IP stack. + * + * To use this module, security.mac.enforce_socket must be enabled, and you + * will probably want to twiddle the net.inet sysctl listed above. Then use + * sysctl(8) to modify the rules string: + * + * # sysctl security.mac.portacl.rules="uid:425:tcp:80,uid:425:tcp:79" + * + * This ruleset, for example, permits uid 425 to bind TCP ports 80 (http) and + * 79 (finger). User names and group names can't be used directly because + * the kernel only knows about uids and gids. + */ + +#include <sys/param.h> +#include <sys/domain.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/priv.h> +#include <sys/proc.h> +#include <sys/protosw.h> +#include <sys/queue.h> +#include <sys/systm.h> +#include <sys/sbuf.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> + +#include <netinet/in.h> +#include <netinet/in_pcb.h> + +#include <security/mac/mac_policy.h> + +SYSCTL_DECL(_security_mac); + +SYSCTL_NODE(_security_mac, OID_AUTO, portacl, CTLFLAG_RW, 0, + "TrustedBSD mac_portacl policy controls"); + +static int portacl_enabled = 1; +SYSCTL_INT(_security_mac_portacl, OID_AUTO, enabled, CTLFLAG_RW, + &portacl_enabled, 0, "Enforce portacl policy"); +TUNABLE_INT("security.mac.portacl.enabled", &portacl_enabled); + +static int portacl_suser_exempt = 1; +SYSCTL_INT(_security_mac_portacl, OID_AUTO, suser_exempt, CTLFLAG_RW, + &portacl_suser_exempt, 0, "Privilege permits binding of any port"); +TUNABLE_INT("security.mac.portacl.suser_exempt", + &portacl_suser_exempt); + +static int portacl_autoport_exempt = 1; +SYSCTL_INT(_security_mac_portacl, OID_AUTO, autoport_exempt, CTLFLAG_RW, + &portacl_autoport_exempt, 0, "Allow automatic allocation through " + "binding port 0 if not IP_PORTRANGELOW"); +TUNABLE_INT("security.mac.portacl.autoport_exempt", + &portacl_autoport_exempt); + +static int portacl_port_high = 1023; +SYSCTL_INT(_security_mac_portacl, OID_AUTO, port_high, CTLFLAG_RW, + &portacl_port_high, 0, "Highest port to enforce for"); +TUNABLE_INT("security.mac.portacl.port_high", &portacl_port_high); + +MALLOC_DEFINE(M_PORTACL, "portacl_rule", "Rules for mac_portacl"); + +#define MAC_RULE_STRING_LEN 1024 + +#define RULE_GID 1 +#define RULE_UID 2 +#define RULE_PROTO_TCP 1 +#define RULE_PROTO_UDP 2 +struct rule { + id_t r_id; + int r_idtype; + u_int16_t r_port; + int r_protocol; + + TAILQ_ENTRY(rule) r_entries; +}; + +#define GID_STRING "gid" +#define TCP_STRING "tcp" +#define UID_STRING "uid" +#define UDP_STRING "udp" + +/* + * Text format for the rule string is that a rule consists of a + * comma-seperated list of elements. Each element is in the form + * idtype:id:protocol:portnumber, and constitutes granting of permission + * for the specified binding. + */ + +static struct mtx rule_mtx; +static TAILQ_HEAD(rulehead, rule) rule_head; +static char rule_string[MAC_RULE_STRING_LEN]; + +static void +toast_rules(struct rulehead *head) +{ + struct rule *rule; + + while ((rule = TAILQ_FIRST(head)) != NULL) { + TAILQ_REMOVE(head, rule, r_entries); + free(rule, M_PORTACL); + } +} + +/* + * Note that there is an inherent race condition in the unload of modules + * and access via sysctl. + */ +static void +destroy(struct mac_policy_conf *mpc) +{ + + mtx_destroy(&rule_mtx); + toast_rules(&rule_head); +} + +static void +init(struct mac_policy_conf *mpc) +{ + + mtx_init(&rule_mtx, "rule_mtx", NULL, MTX_DEF); + TAILQ_INIT(&rule_head); +} + +/* + * Note: parsing routines are destructive on the passed string. + */ +static int +parse_rule_element(char *element, struct rule **rule) +{ + char *idtype, *id, *protocol, *portnumber, *p; + struct rule *new; + int error; + + error = 0; + new = malloc(sizeof(*new), M_PORTACL, M_ZERO | M_WAITOK); + + idtype = strsep(&element, ":"); + if (idtype == NULL) { + error = EINVAL; + goto out; + } + id = strsep(&element, ":"); + if (id == NULL) { + error = EINVAL; + goto out; + } + new->r_id = strtol(id, &p, 10); + if (*p != '\0') { + error = EINVAL; + goto out; + } + if (strcmp(idtype, UID_STRING) == 0) + new->r_idtype = RULE_UID; + else if (strcmp(idtype, GID_STRING) == 0) + new->r_idtype = RULE_GID; + else { + error = EINVAL; + goto out; + } + protocol = strsep(&element, ":"); + if (protocol == NULL) { + error = EINVAL; + goto out; + } + if (strcmp(protocol, TCP_STRING) == 0) + new->r_protocol = RULE_PROTO_TCP; + else if (strcmp(protocol, UDP_STRING) == 0) + new->r_protocol = RULE_PROTO_UDP; + else { + error = EINVAL; + goto out; + } + portnumber = element; + if (portnumber == NULL) { + error = EINVAL; + goto out; + } + new->r_port = strtol(portnumber, &p, 10); + if (*p != '\0') { + error = EINVAL; + goto out; + } + +out: + if (error != 0) { + free(new, M_PORTACL); + *rule = NULL; + } else + *rule = new; + return (error); +} + +static int +parse_rules(char *string, struct rulehead *head) +{ + struct rule *new; + char *element; + int error; + + error = 0; + while ((element = strsep(&string, ",")) != NULL) { + if (strlen(element) == 0) + continue; + error = parse_rule_element(element, &new); + if (error) + goto out; + TAILQ_INSERT_TAIL(head, new, r_entries); + } +out: + if (error != 0) + toast_rules(head); + return (error); +} + +/* + * rule_printf() and rules_to_string() are unused currently because they rely + * on sbufs with auto-extension, which may sleep while holding a mutex. + * Instead, the non-canonical user-generated rule string is returned to the + * user when the rules are queried, which is faster anyway. + */ +#if 0 +static void +rule_printf(struct sbuf *sb, struct rule *rule) +{ + const char *idtype, *protocol; + + switch(rule->r_idtype) { + case RULE_GID: + idtype = GID_STRING; + break; + case RULE_UID: + idtype = UID_STRING; + break; + default: + panic("rule_printf: unknown idtype (%d)\n", rule->r_idtype); + } + + switch (rule->r_protocol) { + case RULE_PROTO_TCP: + protocol = TCP_STRING; + break; + case RULE_PROTO_UDP: + protocol = UDP_STRING; + break; + default: + panic("rule_printf: unknown protocol (%d)\n", + rule->r_protocol); + } + sbuf_printf(sb, "%s:%jd:%s:%d", idtype, (intmax_t)rule->r_id, + protocol, rule->r_port); +} + +static char * +rules_to_string(void) +{ + struct rule *rule; + struct sbuf *sb; + int needcomma; + char *temp; + + sb = sbuf_new_auto(); + needcomma = 0; + mtx_lock(&rule_mtx); + for (rule = TAILQ_FIRST(&rule_head); rule != NULL; + rule = TAILQ_NEXT(rule, r_entries)) { + if (!needcomma) + needcomma = 1; + else + sbuf_printf(sb, ","); + rule_printf(sb, rule); + } + mtx_unlock(&rule_mtx); + sbuf_finish(sb); + temp = strdup(sbuf_data(sb), M_PORTACL); + sbuf_delete(sb); + return (temp); +} +#endif + +/* + * Note: due to races, there is not a single serializable order + * between parallel calls to the sysctl. + */ +static int +sysctl_rules(SYSCTL_HANDLER_ARGS) +{ + char *string, *copy_string, *new_string; + struct rulehead head, save_head; + int error; + + new_string = NULL; + if (req->newptr == NULL) { + new_string = malloc(MAC_RULE_STRING_LEN, M_PORTACL, + M_WAITOK | M_ZERO); + strcpy(new_string, rule_string); + string = new_string; + } else + string = rule_string; + + error = sysctl_handle_string(oidp, string, MAC_RULE_STRING_LEN, req); + if (error) + goto out; + + if (req->newptr != NULL) { + copy_string = strdup(string, M_PORTACL); + TAILQ_INIT(&head); + error = parse_rules(copy_string, &head); + free(copy_string, M_PORTACL); + if (error) + goto out; + + TAILQ_INIT(&save_head); + mtx_lock(&rule_mtx); + TAILQ_CONCAT(&save_head, &rule_head, r_entries); + TAILQ_CONCAT(&rule_head, &head, r_entries); + strcpy(rule_string, string); + mtx_unlock(&rule_mtx); + toast_rules(&save_head); + } +out: + if (new_string != NULL) + free(new_string, M_PORTACL); + return (error); +} + +SYSCTL_PROC(_security_mac_portacl, OID_AUTO, rules, + CTLTYPE_STRING|CTLFLAG_RW, 0, 0, sysctl_rules, "A", "Rules"); + +static int +rules_check(struct ucred *cred, int family, int type, u_int16_t port) +{ + struct rule *rule; + int error; + +#if 0 + printf("Check requested for euid %d, family %d, type %d, port %d\n", + cred->cr_uid, family, type, port); +#endif + + if (port > portacl_port_high) + return (0); + + error = EPERM; + mtx_lock(&rule_mtx); + for (rule = TAILQ_FIRST(&rule_head); + rule != NULL; + rule = TAILQ_NEXT(rule, r_entries)) { + if (type == SOCK_DGRAM && rule->r_protocol != RULE_PROTO_UDP) + continue; + if (type == SOCK_STREAM && rule->r_protocol != RULE_PROTO_TCP) + continue; + if (port != rule->r_port) + continue; + if (rule->r_idtype == RULE_UID) { + if (cred->cr_uid == rule->r_id) { + error = 0; + break; + } + } else if (rule->r_idtype == RULE_GID) { + if (cred->cr_gid == rule->r_id) { + error = 0; + break; + } else if (groupmember(rule->r_id, cred)) { + error = 0; + break; + } + } else + panic("rules_check: unknown rule type %d", + rule->r_idtype); + } + mtx_unlock(&rule_mtx); + + if (error != 0 && portacl_suser_exempt != 0) + error = priv_check_cred(cred, PRIV_NETINET_RESERVEDPORT, 0); + + return (error); +} + +/* + * Note, this only limits the ability to explicitly bind a port, it + * doesn't limit implicitly bound ports for outgoing connections where + * the source port is left up to the IP stack to determine automatically. + */ +static int +socket_check_bind(struct ucred *cred, struct socket *so, + struct label *solabel, struct sockaddr *sa) +{ + struct sockaddr_in *sin; + struct inpcb *inp; + int family, type; + u_int16_t port; + + /* Only run if we are enabled. */ + if (portacl_enabled == 0) + return (0); + + /* Only interested in IPv4 and IPv6 sockets. */ + if (so->so_proto->pr_domain->dom_family != PF_INET && + so->so_proto->pr_domain->dom_family != PF_INET6) + return (0); + + /* Currently, we don't attempt to deal with SOCK_RAW, etc. */ + if (so->so_type != SOCK_DGRAM && + so->so_type != SOCK_STREAM) + return (0); + + /* Reject addresses we don't understand; fail closed. */ + if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6) + return (EINVAL); + + family = so->so_proto->pr_domain->dom_family; + type = so->so_type; + sin = (struct sockaddr_in *) sa; + port = ntohs(sin->sin_port); + + /* + * Sockets are frequently bound with a specific IP address but a port + * number of '0' to request automatic port allocation. This is often + * desirable as long as IP_PORTRANGELOW isn't set, which might permit + * automatic allocation of a "privileged" port. The autoport exempt + * flag exempts port 0 allocation from rule checking as long as a low + * port isn't required. + */ + if (portacl_autoport_exempt && port == 0) { + inp = sotoinpcb(so); + if ((inp->inp_flags & INP_LOWPORT) == 0) + return (0); + } + + return (rules_check(cred, family, type, port)); +} + +static struct mac_policy_ops portacl_ops = +{ + .mpo_destroy = destroy, + .mpo_init = init, + .mpo_socket_check_bind = socket_check_bind, +}; + +MAC_POLICY_SET(&portacl_ops, mac_portacl, "TrustedBSD MAC/portacl", + MPC_LOADTIME_FLAG_UNLOADOK, NULL, 0); diff --git a/sys/security/mac_seeotheruids/mac_seeotheruids.c b/sys/security/mac_seeotheruids/mac_seeotheruids.c new file mode 100644 index 0000000..ddbdaec --- /dev/null +++ b/sys/security/mac_seeotheruids/mac_seeotheruids.c @@ -0,0 +1,189 @@ +/*- + * Copyright (c) 1999-2002, 2007 Robert N. M. Watson + * Copyright (c) 2001-2002 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Developed by the TrustedBSD Project. + * + * Prevent processes owned by a particular uid from seeing various transient + * kernel objects associated with other uids. + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/priv.h> +#include <sys/proc.h> +#include <sys/systm.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> + +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_pcb.h> + +#include <security/mac/mac_policy.h> + +SYSCTL_DECL(_security_mac); + +SYSCTL_NODE(_security_mac, OID_AUTO, seeotheruids, CTLFLAG_RW, 0, + "TrustedBSD mac_seeotheruids policy controls"); + +static int seeotheruids_enabled = 1; +SYSCTL_INT(_security_mac_seeotheruids, OID_AUTO, enabled, CTLFLAG_RW, + &seeotheruids_enabled, 0, "Enforce seeotheruids policy"); + +/* + * Exception: allow credentials to be aware of other credentials with the + * same primary gid. + */ +static int primarygroup_enabled = 0; +SYSCTL_INT(_security_mac_seeotheruids, OID_AUTO, primarygroup_enabled, + CTLFLAG_RW, &primarygroup_enabled, 0, "Make an exception for credentials " + "with the same real primary group id"); + +/* + * Exception: allow the root user to be aware of other credentials by virtue + * of privilege. + */ +static int suser_privileged = 1; +SYSCTL_INT(_security_mac_seeotheruids, OID_AUTO, suser_privileged, + CTLFLAG_RW, &suser_privileged, 0, "Make an exception for superuser"); + +/* + * Exception: allow processes with a specific gid to be exempt from the + * policy. One sysctl enables this functionality; the other sets the + * exempt gid. + */ +static int specificgid_enabled = 0; +SYSCTL_INT(_security_mac_seeotheruids, OID_AUTO, specificgid_enabled, + CTLFLAG_RW, &specificgid_enabled, 0, "Make an exception for credentials " + "with a specific gid as their real primary group id or group set"); + +static gid_t specificgid = 0; +SYSCTL_INT(_security_mac_seeotheruids, OID_AUTO, specificgid, CTLFLAG_RW, + &specificgid, 0, "Specific gid to be exempt from seeotheruids policy"); + +static int +seeotheruids_check(struct ucred *cr1, struct ucred *cr2) +{ + + if (!seeotheruids_enabled) + return (0); + + if (primarygroup_enabled) { + if (cr1->cr_rgid == cr2->cr_rgid) + return (0); + } + + if (specificgid_enabled) { + if (cr1->cr_rgid == specificgid || + groupmember(specificgid, cr1)) + return (0); + } + + if (cr1->cr_ruid == cr2->cr_ruid) + return (0); + + if (suser_privileged) { + if (priv_check_cred(cr1, PRIV_SEEOTHERUIDS, 0) == 0) + return (0); + } + + return (ESRCH); +} + +static int +seeotheruids_proc_check_debug(struct ucred *cred, struct proc *p) +{ + + return (seeotheruids_check(cred, p->p_ucred)); +} + +static int +seeotheruids_proc_check_sched(struct ucred *cred, struct proc *p) +{ + + return (seeotheruids_check(cred, p->p_ucred)); +} + +static int +seeotheruids_proc_check_signal(struct ucred *cred, struct proc *p, + int signum) +{ + + return (seeotheruids_check(cred, p->p_ucred)); +} + +static int +seeotheruids_cred_check_visible(struct ucred *cr1, struct ucred *cr2) +{ + + return (seeotheruids_check(cr1, cr2)); +} + +static int +seeotheruids_inpcb_check_visible(struct ucred *cred, struct inpcb *inp, + struct label *inplabel) +{ + + return (seeotheruids_check(cred, inp->inp_cred)); +} + +static int +seeotheruids_socket_check_visible(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + + return (seeotheruids_check(cred, so->so_cred)); +} + +static struct mac_policy_ops seeotheruids_ops = +{ + .mpo_proc_check_debug = seeotheruids_proc_check_debug, + .mpo_proc_check_sched = seeotheruids_proc_check_sched, + .mpo_proc_check_signal = seeotheruids_proc_check_signal, + .mpo_cred_check_visible = seeotheruids_cred_check_visible, + .mpo_inpcb_check_visible = seeotheruids_inpcb_check_visible, + .mpo_socket_check_visible = seeotheruids_socket_check_visible, +}; + +MAC_POLICY_SET(&seeotheruids_ops, mac_seeotheruids, + "TrustedBSD MAC/seeotheruids", MPC_LOADTIME_FLAG_UNLOADOK, NULL, 0); diff --git a/sys/security/mac_stub/mac_stub.c b/sys/security/mac_stub/mac_stub.c new file mode 100644 index 0000000..02cb541 --- /dev/null +++ b/sys/security/mac_stub/mac_stub.c @@ -0,0 +1,1824 @@ +/*- + * Copyright (c) 1999-2002, 2007-2008 Robert N. M. Watson + * Copyright (c) 2001-2005 McAfee, Inc. + * Copyright (c) 2005-2006 SPARTA, Inc. + * Copyright (c) 2008 Apple Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by McAfee + * Research, the Security Research Division of McAfee, Inc. under + * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA + * CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Developed by the TrustedBSD Project. + * + * Stub module that implements a NOOP for most (if not all) MAC Framework + * policy entry points. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/acl.h> +#include <sys/conf.h> +#include <sys/extattr.h> +#include <sys/kernel.h> +#include <sys/ksem.h> +#include <sys/mount.h> +#include <sys/proc.h> +#include <sys/systm.h> +#include <sys/sysproto.h> +#include <sys/sysent.h> +#include <sys/vnode.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/pipe.h> +#include <sys/sx.h> +#include <sys/sysctl.h> +#include <sys/msg.h> +#include <sys/sem.h> +#include <sys/shm.h> + +#include <fs/devfs/devfs.h> + +#include <net/bpfdesc.h> +#include <net/if.h> +#include <net/if_types.h> +#include <net/if_var.h> + +#include <netinet/in.h> +#include <netinet/in_pcb.h> +#include <netinet/ip_var.h> + +#include <vm/vm.h> + +#include <security/mac/mac_policy.h> + +SYSCTL_DECL(_security_mac); + +SYSCTL_NODE(_security_mac, OID_AUTO, stub, CTLFLAG_RW, 0, + "TrustedBSD mac_stub policy controls"); + +static int stub_enabled = 1; +SYSCTL_INT(_security_mac_stub, OID_AUTO, enabled, CTLFLAG_RW, + &stub_enabled, 0, "Enforce mac_stub policy"); + +/* + * Policy module operations. + */ +static void +stub_destroy(struct mac_policy_conf *conf) +{ + +} + +static void +stub_init(struct mac_policy_conf *conf) +{ + +} + +static int +stub_syscall(struct thread *td, int call, void *arg) +{ + + return (0); +} + +/* + * Label operations. + */ +static void +stub_init_label(struct label *label) +{ + +} + +static int +stub_init_label_waitcheck(struct label *label, int flag) +{ + + return (0); +} + +static void +stub_destroy_label(struct label *label) +{ + +} + +static void +stub_copy_label(struct label *src, struct label *dest) +{ + +} + +static int +stub_externalize_label(struct label *label, char *element_name, + struct sbuf *sb, int *claimed) +{ + + return (0); +} + +static int +stub_internalize_label(struct label *label, char *element_name, + char *element_data, int *claimed) +{ + + return (0); +} + +/* + * Object-specific entry point imeplementations are sorted alphabetically by + * object type name and then by operation. + */ +static int +stub_bpfdesc_check_receive(struct bpf_d *d, struct label *dlabel, + struct ifnet *ifp, struct label *ifplabel) +{ + + return (0); +} + +static void +stub_bpfdesc_create(struct ucred *cred, struct bpf_d *d, + struct label *dlabel) +{ + +} + +static void +stub_bpfdesc_create_mbuf(struct bpf_d *d, struct label *dlabel, + struct mbuf *m, struct label *mlabel) +{ + +} + +static void +stub_cred_associate_nfsd(struct ucred *cred) +{ + +} + +static int +stub_cred_check_relabel(struct ucred *cred, struct label *newlabel) +{ + + return (0); +} + +static int +stub_cred_check_visible(struct ucred *cr1, struct ucred *cr2) +{ + + return (0); +} + +static void +stub_cred_create_init(struct ucred *cred) +{ + +} + +static void +stub_cred_create_swapper(struct ucred *cred) +{ + +} + +static void +stub_cred_relabel(struct ucred *cred, struct label *newlabel) +{ + +} + +static void +stub_devfs_create_device(struct ucred *cred, struct mount *mp, + struct cdev *dev, struct devfs_dirent *de, struct label *delabel) +{ + +} + +static void +stub_devfs_create_directory(struct mount *mp, char *dirname, + int dirnamelen, struct devfs_dirent *de, struct label *delabel) +{ + +} + +static void +stub_devfs_create_symlink(struct ucred *cred, struct mount *mp, + struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de, + struct label *delabel) +{ + +} + +static void +stub_devfs_update(struct mount *mp, struct devfs_dirent *de, + struct label *delabel, struct vnode *vp, struct label *vplabel) +{ + +} + +static void +stub_devfs_vnode_associate(struct mount *mp, struct label *mplabel, + struct devfs_dirent *de, struct label *delabel, struct vnode *vp, + struct label *vplabel) +{ + +} + +static int +stub_ifnet_check_relabel(struct ucred *cred, struct ifnet *ifp, + struct label *ifplabel, struct label *newlabel) +{ + + return (0); +} + +static int +stub_ifnet_check_transmit(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + + return (0); +} + +static void +stub_ifnet_create(struct ifnet *ifp, struct label *ifplabel) +{ + +} + +static void +stub_ifnet_create_mbuf(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + +} + +static void +stub_ifnet_relabel(struct ucred *cred, struct ifnet *ifp, + struct label *ifplabel, struct label *newlabel) +{ + +} + +static int +stub_inpcb_check_deliver(struct inpcb *inp, struct label *inplabel, + struct mbuf *m, struct label *mlabel) +{ + + return (0); +} + +static void +stub_inpcb_create(struct socket *so, struct label *solabel, + struct inpcb *inp, struct label *inplabel) +{ + +} + +static void +stub_inpcb_create_mbuf(struct inpcb *inp, struct label *inplabel, + struct mbuf *m, struct label *mlabel) +{ + +} + +static void +stub_inpcb_sosetlabel(struct socket *so, struct label *solabel, + struct inpcb *inp, struct label *inplabel) +{ + +} + +static void +stub_ip6q_create(struct mbuf *m, struct label *mlabel, struct ip6q *q6, + struct label *q6label) +{ + +} + +static int +stub_ip6q_match(struct mbuf *m, struct label *mlabel, struct ip6q *q6, + struct label *q6label) +{ + + return (1); +} + +static void +stub_ip6q_reassemble(struct ip6q *q6, struct label *q6label, struct mbuf *m, + struct label *mlabel) +{ + +} + +static void +stub_ip6q_update(struct mbuf *m, struct label *mlabel, struct ip6q *q6, + struct label *q6label) +{ + +} + +static void +stub_ipq_create(struct mbuf *m, struct label *mlabel, struct ipq *q, + struct label *qlabel) +{ + +} + +static int +stub_ipq_match(struct mbuf *m, struct label *mlabel, struct ipq *q, + struct label *qlabel) +{ + + return (1); +} + +static void +stub_ipq_reassemble(struct ipq *q, struct label *qlabel, struct mbuf *m, + struct label *mlabel) +{ + +} + +static void +stub_ipq_update(struct mbuf *m, struct label *mlabel, struct ipq *q, + struct label *qlabel) +{ + +} + +static int +stub_kenv_check_dump(struct ucred *cred) +{ + + return (0); +} + +static int +stub_kenv_check_get(struct ucred *cred, char *name) +{ + + return (0); +} + +static int +stub_kenv_check_set(struct ucred *cred, char *name, char *value) +{ + + return (0); +} + +static int +stub_kenv_check_unset(struct ucred *cred, char *name) +{ + + return (0); +} + +static int +stub_kld_check_load(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + + return (0); +} + +static int +stub_kld_check_stat(struct ucred *cred) +{ + + return (0); +} + +static int +stub_mount_check_stat(struct ucred *cred, struct mount *mp, + struct label *mplabel) +{ + + return (0); +} + +static void +stub_mount_create(struct ucred *cred, struct mount *mp, + struct label *mplabel) +{ + +} + +static void +stub_netatalk_aarp_send(struct ifnet *ifp, struct label *iflpabel, + struct mbuf *m, struct label *mlabel) +{ + +} + +static void +stub_netinet_arp_send(struct ifnet *ifp, struct label *iflpabel, + struct mbuf *m, struct label *mlabel) +{ + +} + +static void +stub_netinet_firewall_reply(struct mbuf *mrecv, struct label *mrecvlabel, + struct mbuf *msend, struct label *msendlabel) +{ + +} + +static void +stub_netinet_firewall_send(struct mbuf *m, struct label *mlabel) +{ + +} + +static void +stub_netinet_fragment(struct mbuf *m, struct label *mlabel, struct mbuf *frag, + struct label *fraglabel) +{ + +} + +static void +stub_netinet_icmp_reply(struct mbuf *mrecv, struct label *mrecvlabel, + struct mbuf *msend, struct label *msendlabel) +{ + +} + +static void +stub_netinet_icmp_replyinplace(struct mbuf *m, struct label *mlabel) +{ + +} + +static void +stub_netinet_igmp_send(struct ifnet *ifp, struct label *iflpabel, + struct mbuf *m, struct label *mlabel) +{ + +} + +static void +stub_netinet_tcp_reply(struct mbuf *m, struct label *mlabel) +{ + +} + +static void +stub_netinet6_nd6_send(struct ifnet *ifp, struct label *iflpabel, + struct mbuf *m, struct label *mlabel) +{ + +} + +static int +stub_pipe_check_ioctl(struct ucred *cred, struct pipepair *pp, + struct label *pplabel, unsigned long cmd, void /* caddr_t */ *data) +{ + + return (0); +} + +static int +stub_pipe_check_poll(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + + return (0); +} + +static int +stub_pipe_check_read(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + + return (0); +} + +static int +stub_pipe_check_relabel(struct ucred *cred, struct pipepair *pp, + struct label *pplabel, struct label *newlabel) +{ + + return (0); +} + +static int +stub_pipe_check_stat(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + + return (0); +} + +static int +stub_pipe_check_write(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + + return (0); +} + +static void +stub_pipe_create(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + +} + +static void +stub_pipe_relabel(struct ucred *cred, struct pipepair *pp, + struct label *pplabel, struct label *newlabel) +{ + +} + +static int +stub_posixsem_check_getvalue(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks, struct label *kslabel) +{ + + return (0); +} + +static int +stub_posixsem_check_open(struct ucred *cred, struct ksem *ks, + struct label *kslabel) +{ + + return (0); +} + +static int +stub_posixsem_check_post(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks, struct label *kslabel) +{ + + return (0); +} + +static int +stub_posixsem_check_stat(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks, struct label *kslabel) +{ + + return (0); +} + +static int +stub_posixsem_check_unlink(struct ucred *cred, struct ksem *ks, + struct label *kslabel) +{ + + return (0); +} + +static int +stub_posixsem_check_wait(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks, struct label *kslabel) +{ + + return (0); +} + +static void +stub_posixsem_create(struct ucred *cred, struct ksem *ks, + struct label *kslabel) +{ + +} + +static int +stub_posixshm_check_mmap(struct ucred *cred, struct shmfd *shmfd, + struct label *shmlabel, int prot, int flags) +{ + + return (0); +} + +static int +stub_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd, + struct label *shmlabel) +{ + + return (0); +} + +static int +stub_posixshm_check_stat(struct ucred *active_cred, struct ucred *file_cred, + struct shmfd *shmfd, struct label *shmlabel) +{ + + return (0); +} + +static int +stub_posixshm_check_truncate(struct ucred *active_cred, + struct ucred *file_cred, struct shmfd *shmfd, struct label *shmlabel) +{ + + return (0); +} + +static int +stub_posixshm_check_unlink(struct ucred *cred, struct shmfd *shmfd, + struct label *shmlabel) +{ + + return (0); +} + +static void +stub_posixshm_create(struct ucred *cred, struct shmfd *shmfd, + struct label *shmlabel) +{ + +} + +static int +stub_priv_check(struct ucred *cred, int priv) +{ + + return (0); +} + +static int +stub_priv_grant(struct ucred *cred, int priv) +{ + + return (EPERM); +} + +static int +stub_proc_check_debug(struct ucred *cred, struct proc *p) +{ + + return (0); +} + +static int +stub_proc_check_sched(struct ucred *cred, struct proc *p) +{ + + return (0); +} + +static int +stub_proc_check_setaudit(struct ucred *cred, struct auditinfo *ai) +{ + + return (0); +} + +static int +stub_proc_check_setaudit_addr(struct ucred *cred, struct auditinfo_addr *aia) +{ + + return (0); +} + +static int +stub_proc_check_setauid(struct ucred *cred, uid_t auid) +{ + + return (0); +} + +static int +stub_proc_check_setegid(struct ucred *cred, gid_t egid) +{ + + return (0); +} + +static int +stub_proc_check_seteuid(struct ucred *cred, uid_t euid) +{ + + return (0); +} + +static int +stub_proc_check_setgid(struct ucred *cred, gid_t gid) +{ + + return (0); +} + +static int +stub_proc_check_setgroups(struct ucred *cred, int ngroups, + gid_t *gidset) +{ + + return (0); +} + +static int +stub_proc_check_setregid(struct ucred *cred, gid_t rgid, gid_t egid) +{ + + return (0); +} + +static int +stub_proc_check_setresgid(struct ucred *cred, gid_t rgid, gid_t egid, + gid_t sgid) +{ + + return (0); +} + +static int +stub_proc_check_setresuid(struct ucred *cred, uid_t ruid, uid_t euid, + uid_t suid) +{ + + return (0); +} + +static int +stub_proc_check_setreuid(struct ucred *cred, uid_t ruid, uid_t euid) +{ + + return (0); +} + +static int +stub_proc_check_setuid(struct ucred *cred, uid_t uid) +{ + + return (0); +} + +static int +stub_proc_check_signal(struct ucred *cred, struct proc *p, int signum) +{ + + return (0); +} + +static int +stub_proc_check_wait(struct ucred *cred, struct proc *p) +{ + + return (0); +} + +static int +stub_socket_check_accept(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + + return (0); +} + +static int +stub_socket_check_bind(struct ucred *cred, struct socket *so, + struct label *solabel, struct sockaddr *sa) +{ + + return (0); +} + +static int +stub_socket_check_connect(struct ucred *cred, struct socket *so, + struct label *solabel, struct sockaddr *sa) +{ + + return (0); +} + +static int +stub_socket_check_create(struct ucred *cred, int domain, int type, int proto) +{ + + return (0); +} + +static int +stub_socket_check_deliver(struct socket *so, struct label *solabel, + struct mbuf *m, struct label *mlabel) +{ + + return (0); +} + +static int +stub_socket_check_listen(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + + return (0); +} + +static int +stub_socket_check_poll(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + + return (0); +} + +static int +stub_socket_check_receive(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + + return (0); +} + +static int +stub_socket_check_relabel(struct ucred *cred, struct socket *so, + struct label *solabel, struct label *newlabel) +{ + + return (0); +} +static int +stub_socket_check_send(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + + return (0); +} + +static int +stub_socket_check_stat(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + + return (0); +} + +static int +stub_inpcb_check_visible(struct ucred *cred, struct inpcb *inp, + struct label *inplabel) +{ + + return (0); +} + +static int +stub_socket_check_visible(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + + return (0); +} + +static void +stub_socket_create(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + +} + +static void +stub_socket_create_mbuf(struct socket *so, struct label *solabel, + struct mbuf *m, struct label *mlabel) +{ + +} + +static void +stub_socket_newconn(struct socket *oldso, struct label *oldsolabel, + struct socket *newso, struct label *newsolabel) +{ + +} + +static void +stub_socket_relabel(struct ucred *cred, struct socket *so, + struct label *solabel, struct label *newlabel) +{ + +} + +static void +stub_socketpeer_set_from_mbuf(struct mbuf *m, struct label *mlabel, + struct socket *so, struct label *sopeerlabel) +{ + +} + +static void +stub_socketpeer_set_from_socket(struct socket *oldso, + struct label *oldsolabel, struct socket *newso, + struct label *newsopeerlabel) +{ + +} + +static void +stub_syncache_create(struct label *label, struct inpcb *inp) +{ + +} + +static void +stub_syncache_create_mbuf(struct label *sc_label, struct mbuf *m, + struct label *mlabel) +{ + +} + +static int +stub_system_check_acct(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + + return (0); +} + +static int +stub_system_check_audit(struct ucred *cred, void *record, int length) +{ + + return (0); +} + +static int +stub_system_check_auditctl(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + + return (0); +} + +static int +stub_system_check_auditon(struct ucred *cred, int cmd) +{ + + return (0); +} + +static int +stub_system_check_reboot(struct ucred *cred, int how) +{ + + return (0); +} + +static int +stub_system_check_swapoff(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + + return (0); +} + +static int +stub_system_check_swapon(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + + return (0); +} + +static int +stub_system_check_sysctl(struct ucred *cred, struct sysctl_oid *oidp, + void *arg1, int arg2, struct sysctl_req *req) +{ + + return (0); +} + +static void +stub_sysvmsg_cleanup(struct label *msglabel) +{ + +} + +static void +stub_sysvmsg_create(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqlabel, struct msg *msgptr, struct label *msglabel) +{ + +} + +static int +stub_sysvmsq_check_msgmsq(struct ucred *cred, struct msg *msgptr, + struct label *msglabel, struct msqid_kernel *msqkptr, + struct label *msqklabel) +{ + + return (0); +} + +static int +stub_sysvmsq_check_msgrcv(struct ucred *cred, struct msg *msgptr, + struct label *msglabel) +{ + + return (0); +} + + +static int +stub_sysvmsq_check_msgrmid(struct ucred *cred, struct msg *msgptr, + struct label *msglabel) +{ + + return (0); +} + + +static int +stub_sysvmsq_check_msqget(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqklabel) +{ + + return (0); +} + + +static int +stub_sysvmsq_check_msqsnd(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqklabel) +{ + + return (0); +} + +static int +stub_sysvmsq_check_msqrcv(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqklabel) +{ + + return (0); +} + + +static int +stub_sysvmsq_check_msqctl(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqklabel, int cmd) +{ + + return (0); +} + + +static void +stub_sysvmsq_cleanup(struct label *msqlabel) +{ + +} + +static void +stub_sysvmsq_create(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqlabel) +{ + +} + +static int +stub_sysvsem_check_semctl(struct ucred *cred, struct semid_kernel *semakptr, + struct label *semaklabel, int cmd) +{ + + return (0); +} + +static int +stub_sysvsem_check_semget(struct ucred *cred, struct semid_kernel *semakptr, + struct label *semaklabel) +{ + + return (0); +} + + +static int +stub_sysvsem_check_semop(struct ucred *cred, struct semid_kernel *semakptr, + struct label *semaklabel, size_t accesstype) +{ + + return (0); +} + +static void +stub_sysvsem_cleanup(struct label *semalabel) +{ + +} + +static void +stub_sysvsem_create(struct ucred *cred, struct semid_kernel *semakptr, + struct label *semalabel) +{ + +} + +static int +stub_sysvshm_check_shmat(struct ucred *cred, struct shmid_kernel *shmsegptr, + struct label *shmseglabel, int shmflg) +{ + + return (0); +} + +static int +stub_sysvshm_check_shmctl(struct ucred *cred, struct shmid_kernel *shmsegptr, + struct label *shmseglabel, int cmd) +{ + + return (0); +} + +static int +stub_sysvshm_check_shmdt(struct ucred *cred, struct shmid_kernel *shmsegptr, + struct label *shmseglabel) +{ + + return (0); +} + + +static int +stub_sysvshm_check_shmget(struct ucred *cred, struct shmid_kernel *shmsegptr, + struct label *shmseglabel, int shmflg) +{ + + return (0); +} + +static void +stub_sysvshm_cleanup(struct label *shmlabel) +{ + +} + +static void +stub_sysvshm_create(struct ucred *cred, struct shmid_kernel *shmsegptr, + struct label *shmalabel) +{ + +} + +static void +stub_thread_userret(struct thread *td) +{ + +} + +static int +stub_vnode_associate_extattr(struct mount *mp, struct label *mplabel, + struct vnode *vp, struct label *vplabel) +{ + + return (0); +} + +static void +stub_vnode_associate_singlelabel(struct mount *mp, struct label *mplabel, + struct vnode *vp, struct label *vplabel) +{ + +} + +static int +stub_vnode_check_access(struct ucred *cred, struct vnode *vp, + struct label *vplabel, accmode_t accmode) +{ + + return (0); +} + +static int +stub_vnode_check_chdir(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel) +{ + + return (0); +} + +static int +stub_vnode_check_chroot(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel) +{ + + return (0); +} + +static int +stub_vnode_check_create(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct componentname *cnp, struct vattr *vap) +{ + + return (0); +} + +static int +stub_vnode_check_deleteacl(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type) +{ + + return (0); +} + +static int +stub_vnode_check_deleteextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace, const char *name) +{ + + return (0); +} + +static int +stub_vnode_check_exec(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct image_params *imgp, + struct label *execlabel) +{ + + return (0); +} + +static int +stub_vnode_check_getacl(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type) +{ + + return (0); +} + +static int +stub_vnode_check_getextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace, const char *name, + struct uio *uio) +{ + + return (0); +} + +static int +stub_vnode_check_link(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp) +{ + + return (0); +} + +static int +stub_vnode_check_listextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace) +{ + + return (0); +} + +static int +stub_vnode_check_lookup(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct componentname *cnp) +{ + + return (0); +} + +static int +stub_vnode_check_mmap(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int prot, int flags) +{ + + return (0); +} + +static void +stub_vnode_check_mmap_downgrade(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int *prot) +{ + +} + +static int +stub_vnode_check_mprotect(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int prot) +{ + + return (0); +} + +static int +stub_vnode_check_open(struct ucred *cred, struct vnode *vp, + struct label *vplabel, accmode_t accmode) +{ + + return (0); +} + +static int +stub_vnode_check_poll(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp, struct label *vplabel) +{ + + return (0); +} + +static int +stub_vnode_check_read(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp, struct label *vplabel) +{ + + return (0); +} + +static int +stub_vnode_check_readdir(struct ucred *cred, struct vnode *vp, + struct label *dvplabel) +{ + + return (0); +} + +static int +stub_vnode_check_readlink(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + + return (0); +} + +static int +stub_vnode_check_relabel(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct label *newlabel) +{ + + return (0); +} + +static int +stub_vnode_check_rename_from(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp) +{ + + return (0); +} + +static int +stub_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + int samedir, struct componentname *cnp) +{ + + return (0); +} + +static int +stub_vnode_check_revoke(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + + return (0); +} + +static int +stub_vnode_check_setacl(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type, struct acl *acl) +{ + + return (0); +} + +static int +stub_vnode_check_setextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace, const char *name, + struct uio *uio) +{ + + return (0); +} + +static int +stub_vnode_check_setflags(struct ucred *cred, struct vnode *vp, + struct label *vplabel, u_long flags) +{ + + return (0); +} + +static int +stub_vnode_check_setmode(struct ucred *cred, struct vnode *vp, + struct label *vplabel, mode_t mode) +{ + + return (0); +} + +static int +stub_vnode_check_setowner(struct ucred *cred, struct vnode *vp, + struct label *vplabel, uid_t uid, gid_t gid) +{ + + return (0); +} + +static int +stub_vnode_check_setutimes(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct timespec atime, struct timespec mtime) +{ + + return (0); +} + +static int +stub_vnode_check_stat(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp, struct label *vplabel) +{ + + return (0); +} + +static int +stub_vnode_check_unlink(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp) +{ + + return (0); +} + +static int +stub_vnode_check_write(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp, struct label *vplabel) +{ + + return (0); +} + +static int +stub_vnode_create_extattr(struct ucred *cred, struct mount *mp, + struct label *mntlabel, struct vnode *dvp, struct label *dvplabel, + struct vnode *vp, struct label *vplabel, struct componentname *cnp) +{ + + return (0); +} + +static void +stub_vnode_execve_transition(struct ucred *old, struct ucred *new, + struct vnode *vp, struct label *vplabel, struct label *interpvplabel, + struct image_params *imgp, struct label *execlabel) +{ + +} + +static int +stub_vnode_execve_will_transition(struct ucred *old, struct vnode *vp, + struct label *vplabel, struct label *interpvplabel, + struct image_params *imgp, struct label *execlabel) +{ + + return (0); +} + +static void +stub_vnode_relabel(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct label *label) +{ + +} + +static int +stub_vnode_setlabel_extattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct label *intlabel) +{ + + return (0); +} + +/* + * Register functions with MAC Framework policy entry points. + */ +static struct mac_policy_ops stub_ops = +{ + .mpo_destroy = stub_destroy, + .mpo_init = stub_init, + .mpo_syscall = stub_syscall, + + .mpo_bpfdesc_check_receive = stub_bpfdesc_check_receive, + .mpo_bpfdesc_create = stub_bpfdesc_create, + .mpo_bpfdesc_create_mbuf = stub_bpfdesc_create_mbuf, + .mpo_bpfdesc_destroy_label = stub_destroy_label, + .mpo_bpfdesc_init_label = stub_init_label, + + .mpo_cred_associate_nfsd = stub_cred_associate_nfsd, + .mpo_cred_check_relabel = stub_cred_check_relabel, + .mpo_cred_check_visible = stub_cred_check_visible, + .mpo_cred_copy_label = stub_copy_label, + .mpo_cred_create_init = stub_cred_create_init, + .mpo_cred_create_swapper = stub_cred_create_swapper, + .mpo_cred_destroy_label = stub_destroy_label, + .mpo_cred_externalize_label = stub_externalize_label, + .mpo_cred_init_label = stub_init_label, + .mpo_cred_internalize_label = stub_internalize_label, + .mpo_cred_relabel= stub_cred_relabel, + + .mpo_devfs_create_device = stub_devfs_create_device, + .mpo_devfs_create_directory = stub_devfs_create_directory, + .mpo_devfs_create_symlink = stub_devfs_create_symlink, + .mpo_devfs_destroy_label = stub_destroy_label, + .mpo_devfs_init_label = stub_init_label, + .mpo_devfs_update = stub_devfs_update, + .mpo_devfs_vnode_associate = stub_devfs_vnode_associate, + + .mpo_ifnet_check_relabel = stub_ifnet_check_relabel, + .mpo_ifnet_check_transmit = stub_ifnet_check_transmit, + .mpo_ifnet_copy_label = stub_copy_label, + .mpo_ifnet_create = stub_ifnet_create, + .mpo_ifnet_create_mbuf = stub_ifnet_create_mbuf, + .mpo_ifnet_destroy_label = stub_destroy_label, + .mpo_ifnet_externalize_label = stub_externalize_label, + .mpo_ifnet_init_label = stub_init_label, + .mpo_ifnet_internalize_label = stub_internalize_label, + .mpo_ifnet_relabel = stub_ifnet_relabel, + + .mpo_inpcb_check_deliver = stub_inpcb_check_deliver, + .mpo_inpcb_check_visible = stub_inpcb_check_visible, + .mpo_inpcb_create = stub_inpcb_create, + .mpo_inpcb_create_mbuf = stub_inpcb_create_mbuf, + .mpo_inpcb_destroy_label = stub_destroy_label, + .mpo_inpcb_init_label = stub_init_label_waitcheck, + .mpo_inpcb_sosetlabel = stub_inpcb_sosetlabel, + + .mpo_ip6q_create = stub_ip6q_create, + .mpo_ip6q_destroy_label = stub_destroy_label, + .mpo_ip6q_init_label = stub_init_label_waitcheck, + .mpo_ip6q_match = stub_ip6q_match, + .mpo_ip6q_update = stub_ip6q_update, + .mpo_ip6q_reassemble = stub_ip6q_reassemble, + + .mpo_ipq_create = stub_ipq_create, + .mpo_ipq_destroy_label = stub_destroy_label, + .mpo_ipq_init_label = stub_init_label_waitcheck, + .mpo_ipq_match = stub_ipq_match, + .mpo_ipq_update = stub_ipq_update, + .mpo_ipq_reassemble = stub_ipq_reassemble, + + .mpo_kenv_check_dump = stub_kenv_check_dump, + .mpo_kenv_check_get = stub_kenv_check_get, + .mpo_kenv_check_set = stub_kenv_check_set, + .mpo_kenv_check_unset = stub_kenv_check_unset, + + .mpo_kld_check_load = stub_kld_check_load, + .mpo_kld_check_stat = stub_kld_check_stat, + + .mpo_mbuf_copy_label = stub_copy_label, + .mpo_mbuf_destroy_label = stub_destroy_label, + .mpo_mbuf_init_label = stub_init_label_waitcheck, + + .mpo_mount_check_stat = stub_mount_check_stat, + .mpo_mount_create = stub_mount_create, + .mpo_mount_destroy_label = stub_destroy_label, + .mpo_mount_init_label = stub_init_label, + + .mpo_netatalk_aarp_send = stub_netatalk_aarp_send, + + .mpo_netinet_arp_send = stub_netinet_arp_send, + .mpo_netinet_firewall_reply = stub_netinet_firewall_reply, + .mpo_netinet_firewall_send = stub_netinet_firewall_send, + .mpo_netinet_fragment = stub_netinet_fragment, + .mpo_netinet_icmp_reply = stub_netinet_icmp_reply, + .mpo_netinet_icmp_replyinplace = stub_netinet_icmp_replyinplace, + .mpo_netinet_tcp_reply = stub_netinet_tcp_reply, + .mpo_netinet_igmp_send = stub_netinet_igmp_send, + + .mpo_netinet6_nd6_send = stub_netinet6_nd6_send, + + .mpo_pipe_check_ioctl = stub_pipe_check_ioctl, + .mpo_pipe_check_poll = stub_pipe_check_poll, + .mpo_pipe_check_read = stub_pipe_check_read, + .mpo_pipe_check_relabel = stub_pipe_check_relabel, + .mpo_pipe_check_stat = stub_pipe_check_stat, + .mpo_pipe_check_write = stub_pipe_check_write, + .mpo_pipe_copy_label = stub_copy_label, + .mpo_pipe_create = stub_pipe_create, + .mpo_pipe_destroy_label = stub_destroy_label, + .mpo_pipe_externalize_label = stub_externalize_label, + .mpo_pipe_init_label = stub_init_label, + .mpo_pipe_internalize_label = stub_internalize_label, + .mpo_pipe_relabel = stub_pipe_relabel, + + .mpo_posixsem_check_getvalue = stub_posixsem_check_getvalue, + .mpo_posixsem_check_open = stub_posixsem_check_open, + .mpo_posixsem_check_post = stub_posixsem_check_post, + .mpo_posixsem_check_stat = stub_posixsem_check_stat, + .mpo_posixsem_check_unlink = stub_posixsem_check_unlink, + .mpo_posixsem_check_wait = stub_posixsem_check_wait, + .mpo_posixsem_create = stub_posixsem_create, + .mpo_posixsem_destroy_label = stub_destroy_label, + .mpo_posixsem_init_label = stub_init_label, + + .mpo_posixshm_check_mmap = stub_posixshm_check_mmap, + .mpo_posixshm_check_open = stub_posixshm_check_open, + .mpo_posixshm_check_stat = stub_posixshm_check_stat, + .mpo_posixshm_check_truncate = stub_posixshm_check_truncate, + .mpo_posixshm_check_unlink = stub_posixshm_check_unlink, + .mpo_posixshm_create = stub_posixshm_create, + .mpo_posixshm_destroy_label = stub_destroy_label, + .mpo_posixshm_init_label = stub_init_label, + + .mpo_priv_check = stub_priv_check, + .mpo_priv_grant = stub_priv_grant, + + .mpo_proc_check_debug = stub_proc_check_debug, + .mpo_proc_check_sched = stub_proc_check_sched, + .mpo_proc_check_setaudit = stub_proc_check_setaudit, + .mpo_proc_check_setaudit_addr = stub_proc_check_setaudit_addr, + .mpo_proc_check_setauid = stub_proc_check_setauid, + .mpo_proc_check_setegid = stub_proc_check_setegid, + .mpo_proc_check_seteuid = stub_proc_check_seteuid, + .mpo_proc_check_setgid = stub_proc_check_setgid, + .mpo_proc_check_setgroups = stub_proc_check_setgroups, + .mpo_proc_check_setregid = stub_proc_check_setregid, + .mpo_proc_check_setresgid = stub_proc_check_setresgid, + .mpo_proc_check_setresuid = stub_proc_check_setresuid, + .mpo_proc_check_setreuid = stub_proc_check_setreuid, + .mpo_proc_check_setuid = stub_proc_check_setuid, + .mpo_proc_check_signal = stub_proc_check_signal, + .mpo_proc_check_wait = stub_proc_check_wait, + + .mpo_socket_check_accept = stub_socket_check_accept, + .mpo_socket_check_bind = stub_socket_check_bind, + .mpo_socket_check_connect = stub_socket_check_connect, + .mpo_socket_check_create = stub_socket_check_create, + .mpo_socket_check_deliver = stub_socket_check_deliver, + .mpo_socket_check_listen = stub_socket_check_listen, + .mpo_socket_check_poll = stub_socket_check_poll, + .mpo_socket_check_receive = stub_socket_check_receive, + .mpo_socket_check_relabel = stub_socket_check_relabel, + .mpo_socket_check_send = stub_socket_check_send, + .mpo_socket_check_stat = stub_socket_check_stat, + .mpo_socket_check_visible = stub_socket_check_visible, + .mpo_socket_copy_label = stub_copy_label, + .mpo_socket_create = stub_socket_create, + .mpo_socket_create_mbuf = stub_socket_create_mbuf, + .mpo_socket_destroy_label = stub_destroy_label, + .mpo_socket_externalize_label = stub_externalize_label, + .mpo_socket_init_label = stub_init_label_waitcheck, + .mpo_socket_internalize_label = stub_internalize_label, + .mpo_socket_newconn = stub_socket_newconn, + .mpo_socket_relabel = stub_socket_relabel, + + .mpo_socketpeer_destroy_label = stub_destroy_label, + .mpo_socketpeer_externalize_label = stub_externalize_label, + .mpo_socketpeer_init_label = stub_init_label_waitcheck, + .mpo_socketpeer_set_from_mbuf = stub_socketpeer_set_from_mbuf, + .mpo_socketpeer_set_from_socket = stub_socketpeer_set_from_socket, + + .mpo_syncache_init_label = stub_init_label_waitcheck, + .mpo_syncache_destroy_label = stub_destroy_label, + .mpo_syncache_create = stub_syncache_create, + .mpo_syncache_create_mbuf= stub_syncache_create_mbuf, + + .mpo_sysvmsg_cleanup = stub_sysvmsg_cleanup, + .mpo_sysvmsg_create = stub_sysvmsg_create, + .mpo_sysvmsg_destroy_label = stub_destroy_label, + .mpo_sysvmsg_init_label = stub_init_label, + + .mpo_sysvmsq_check_msgmsq = stub_sysvmsq_check_msgmsq, + .mpo_sysvmsq_check_msgrcv = stub_sysvmsq_check_msgrcv, + .mpo_sysvmsq_check_msgrmid = stub_sysvmsq_check_msgrmid, + .mpo_sysvmsq_check_msqget = stub_sysvmsq_check_msqget, + .mpo_sysvmsq_check_msqsnd = stub_sysvmsq_check_msqsnd, + .mpo_sysvmsq_check_msqrcv = stub_sysvmsq_check_msqrcv, + .mpo_sysvmsq_check_msqctl = stub_sysvmsq_check_msqctl, + .mpo_sysvmsq_cleanup = stub_sysvmsq_cleanup, + .mpo_sysvmsq_create = stub_sysvmsq_create, + .mpo_sysvmsq_destroy_label = stub_destroy_label, + .mpo_sysvmsq_init_label = stub_init_label, + + .mpo_sysvsem_check_semctl = stub_sysvsem_check_semctl, + .mpo_sysvsem_check_semget = stub_sysvsem_check_semget, + .mpo_sysvsem_check_semop = stub_sysvsem_check_semop, + .mpo_sysvsem_cleanup = stub_sysvsem_cleanup, + .mpo_sysvsem_create = stub_sysvsem_create, + .mpo_sysvsem_destroy_label = stub_destroy_label, + .mpo_sysvsem_init_label = stub_init_label, + + .mpo_sysvshm_check_shmat = stub_sysvshm_check_shmat, + .mpo_sysvshm_check_shmctl = stub_sysvshm_check_shmctl, + .mpo_sysvshm_check_shmdt = stub_sysvshm_check_shmdt, + .mpo_sysvshm_check_shmget = stub_sysvshm_check_shmget, + .mpo_sysvshm_cleanup = stub_sysvshm_cleanup, + .mpo_sysvshm_create = stub_sysvshm_create, + .mpo_sysvshm_destroy_label = stub_destroy_label, + .mpo_sysvshm_init_label = stub_init_label, + + .mpo_system_check_acct = stub_system_check_acct, + .mpo_system_check_audit = stub_system_check_audit, + .mpo_system_check_auditctl = stub_system_check_auditctl, + .mpo_system_check_auditon = stub_system_check_auditon, + .mpo_system_check_reboot = stub_system_check_reboot, + .mpo_system_check_swapoff = stub_system_check_swapoff, + .mpo_system_check_swapon = stub_system_check_swapon, + .mpo_system_check_sysctl = stub_system_check_sysctl, + + .mpo_thread_userret = stub_thread_userret, + + .mpo_vnode_associate_extattr = stub_vnode_associate_extattr, + .mpo_vnode_associate_singlelabel = stub_vnode_associate_singlelabel, + .mpo_vnode_check_access = stub_vnode_check_access, + .mpo_vnode_check_chdir = stub_vnode_check_chdir, + .mpo_vnode_check_chroot = stub_vnode_check_chroot, + .mpo_vnode_check_create = stub_vnode_check_create, + .mpo_vnode_check_deleteacl = stub_vnode_check_deleteacl, + .mpo_vnode_check_deleteextattr = stub_vnode_check_deleteextattr, + .mpo_vnode_check_exec = stub_vnode_check_exec, + .mpo_vnode_check_getacl = stub_vnode_check_getacl, + .mpo_vnode_check_getextattr = stub_vnode_check_getextattr, + .mpo_vnode_check_link = stub_vnode_check_link, + .mpo_vnode_check_listextattr = stub_vnode_check_listextattr, + .mpo_vnode_check_lookup = stub_vnode_check_lookup, + .mpo_vnode_check_mmap = stub_vnode_check_mmap, + .mpo_vnode_check_mmap_downgrade = stub_vnode_check_mmap_downgrade, + .mpo_vnode_check_mprotect = stub_vnode_check_mprotect, + .mpo_vnode_check_open = stub_vnode_check_open, + .mpo_vnode_check_poll = stub_vnode_check_poll, + .mpo_vnode_check_read = stub_vnode_check_read, + .mpo_vnode_check_readdir = stub_vnode_check_readdir, + .mpo_vnode_check_readlink = stub_vnode_check_readlink, + .mpo_vnode_check_relabel = stub_vnode_check_relabel, + .mpo_vnode_check_rename_from = stub_vnode_check_rename_from, + .mpo_vnode_check_rename_to = stub_vnode_check_rename_to, + .mpo_vnode_check_revoke = stub_vnode_check_revoke, + .mpo_vnode_check_setacl = stub_vnode_check_setacl, + .mpo_vnode_check_setextattr = stub_vnode_check_setextattr, + .mpo_vnode_check_setflags = stub_vnode_check_setflags, + .mpo_vnode_check_setmode = stub_vnode_check_setmode, + .mpo_vnode_check_setowner = stub_vnode_check_setowner, + .mpo_vnode_check_setutimes = stub_vnode_check_setutimes, + .mpo_vnode_check_stat = stub_vnode_check_stat, + .mpo_vnode_check_unlink = stub_vnode_check_unlink, + .mpo_vnode_check_write = stub_vnode_check_write, + .mpo_vnode_copy_label = stub_copy_label, + .mpo_vnode_create_extattr = stub_vnode_create_extattr, + .mpo_vnode_destroy_label = stub_destroy_label, + .mpo_vnode_execve_transition = stub_vnode_execve_transition, + .mpo_vnode_execve_will_transition = stub_vnode_execve_will_transition, + .mpo_vnode_externalize_label = stub_externalize_label, + .mpo_vnode_init_label = stub_init_label, + .mpo_vnode_internalize_label = stub_internalize_label, + .mpo_vnode_relabel = stub_vnode_relabel, + .mpo_vnode_setlabel_extattr = stub_vnode_setlabel_extattr, +}; + +#define STUB_OBJECTS (MPC_OBJECT_CRED | \ + /* XXX: MPC_OBJECT_PROC | */ \ + MPC_OBJECT_VNODE | \ + MPC_OBJECT_INPCB | \ + MPC_OBJECT_SOCKET | \ + MPC_OBJECT_DEVFS | \ + MPC_OBJECT_MBUF | \ + MPC_OBJECT_IPQ | \ + MPC_OBJECT_IFNET | \ + MPC_OBJECT_BPFDESC | \ + MPC_OBJECT_PIPE | \ + MPC_OBJECT_MOUNT | \ + MPC_OBJECT_POSIXSEM | \ + MPC_OBJECT_POSIXSHM | \ + MPC_OBJECT_SYSVMSG | \ + MPC_OBJECT_SYSVMSQ | \ + MPC_OBJECT_SYSVSEM | \ + MPC_OBJECT_SYSVSHM | \ + MPC_OBJECT_SYNCACHE) + +MAC_POLICY_SET(&stub_ops, mac_stub, "TrustedBSD MAC/Stub", + MPC_LOADTIME_FLAG_UNLOADOK, NULL, STUB_OBJECTS); diff --git a/sys/security/mac_test/mac_test.c b/sys/security/mac_test/mac_test.c new file mode 100644 index 0000000..08f8dd8 --- /dev/null +++ b/sys/security/mac_test/mac_test.c @@ -0,0 +1,3163 @@ +/*- + * Copyright (c) 1999-2002, 2007-2008 Robert N. M. Watson + * Copyright (c) 2001-2005 McAfee, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * Copyright (c) 2008 Apple Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by McAfee + * Research, the Security Research Division of McAfee, Inc. under + * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA + * CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Developed by the TrustedBSD Project. + * + * MAC Test policy - tests MAC Framework labeling by assigning object class + * magic numbers to each label and validates that each time an object label + * is passed into the policy, it has a consistent object type, catching + * incorrectly passed labels, labels passed after free, etc. + */ + +#include <sys/param.h> +#include <sys/acl.h> +#include <sys/kdb.h> +#include <sys/kernel.h> +#include <sys/ksem.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/mount.h> +#include <sys/msg.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/sem.h> +#include <sys/shm.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sx.h> +#include <sys/sysctl.h> + +#include <fs/devfs/devfs.h> + +#include <net/bpfdesc.h> +#include <net/if.h> +#include <net/if_types.h> +#include <net/if_var.h> + +#include <security/mac/mac_policy.h> + +SYSCTL_DECL(_security_mac); + +SYSCTL_NODE(_security_mac, OID_AUTO, test, CTLFLAG_RW, 0, + "TrustedBSD mac_test policy controls"); + +#define MAGIC_BPF 0xfe1ad1b6 +#define MAGIC_DEVFS 0x9ee79c32 +#define MAGIC_IFNET 0xc218b120 +#define MAGIC_INPCB 0x4440f7bb +#define MAGIC_IP6Q 0x0870e1b7 +#define MAGIC_IPQ 0x206188ef +#define MAGIC_MBUF 0xbbefa5bb +#define MAGIC_MOUNT 0xc7c46e47 +#define MAGIC_SOCKET 0x9199c6cd +#define MAGIC_SYNCACHE 0x7fb838a8 +#define MAGIC_SYSV_MSG 0x8bbba61e +#define MAGIC_SYSV_MSQ 0xea672391 +#define MAGIC_SYSV_SEM 0x896e8a0b +#define MAGIC_SYSV_SHM 0x76119ab0 +#define MAGIC_PIPE 0xdc6c9919 +#define MAGIC_POSIX_SEM 0x78ae980c +#define MAGIC_POSIX_SHM 0x4e853fc9 +#define MAGIC_PROC 0x3b4be98f +#define MAGIC_CRED 0x9a5a4987 +#define MAGIC_VNODE 0x1a67a45c +#define MAGIC_FREE 0x849ba1fd + +#define SLOT(x) mac_label_get((x), test_slot) +#define SLOT_SET(x, v) mac_label_set((x), test_slot, (v)) + +static int test_slot; +SYSCTL_INT(_security_mac_test, OID_AUTO, slot, CTLFLAG_RD, + &test_slot, 0, "Slot allocated by framework"); + +SYSCTL_NODE(_security_mac_test, OID_AUTO, counter, CTLFLAG_RW, 0, + "TrustedBSD mac_test counters controls"); + +#define COUNTER_DECL(variable) \ + static int counter_##variable; \ + SYSCTL_INT(_security_mac_test_counter, OID_AUTO, variable, \ + CTLFLAG_RD, &counter_##variable, 0, #variable) + +#define COUNTER_INC(variable) atomic_add_int(&counter_##variable, 1) + +#ifdef KDB +#define DEBUGGER(func, string) kdb_enter(KDB_WHY_MAC, (string)) +#else +#define DEBUGGER(func, string) printf("mac_test: %s: %s\n", (func), (string)) +#endif + +#define LABEL_CHECK(label, magic) do { \ + if (label != NULL) { \ + KASSERT(SLOT(label) == magic || SLOT(label) == 0, \ + ("%s: bad %s label", __func__, #magic)); \ + } \ +} while (0) + +#define LABEL_DESTROY(label, magic) do { \ + if (SLOT(label) == magic || SLOT(label) == 0) { \ + SLOT_SET(label, MAGIC_FREE); \ + } else if (SLOT(label) == MAGIC_FREE) { \ + DEBUGGER("%s: dup destroy", __func__); \ + } else { \ + DEBUGGER("%s: corrupted label", __func__); \ + } \ +} while (0) + +#define LABEL_INIT(label, magic) do { \ + SLOT_SET(label, magic); \ +} while (0) + +#define LABEL_NOTFREE(label) do { \ + KASSERT(SLOT(label) != MAGIC_FREE, \ + ("%s: destroyed label", __func__)); \ +} while (0) + +/* + * Object-specific entry point implementations are sorted alphabetically by + * object type name and then by operation. + */ +COUNTER_DECL(bpfdesc_check_receive); +static int +test_bpfdesc_check_receive(struct bpf_d *d, struct label *dlabel, + struct ifnet *ifp, struct label *ifplabel) +{ + + LABEL_CHECK(dlabel, MAGIC_BPF); + LABEL_CHECK(ifplabel, MAGIC_IFNET); + COUNTER_INC(bpfdesc_check_receive); + + return (0); +} + +COUNTER_DECL(bpfdesc_create); +static void +test_bpfdesc_create(struct ucred *cred, struct bpf_d *d, + struct label *dlabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(dlabel, MAGIC_BPF); + COUNTER_INC(bpfdesc_create); +} + +COUNTER_DECL(bpfdesc_create_mbuf); +static void +test_bpfdesc_create_mbuf(struct bpf_d *d, struct label *dlabel, + struct mbuf *m, struct label *mlabel) +{ + + LABEL_CHECK(dlabel, MAGIC_BPF); + LABEL_CHECK(mlabel, MAGIC_MBUF); + COUNTER_INC(bpfdesc_create_mbuf); +} + +COUNTER_DECL(bpfdesc_destroy_label); +static void +test_bpfdesc_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_BPF); + COUNTER_INC(bpfdesc_destroy_label); +} + +COUNTER_DECL(bpfdesc_init_label); +static void +test_bpfdesc_init_label(struct label *label) +{ + + LABEL_INIT(label, MAGIC_BPF); + COUNTER_INC(bpfdesc_init_label); +} + +COUNTER_DECL(cred_check_relabel); +static int +test_cred_check_relabel(struct ucred *cred, struct label *newlabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(newlabel, MAGIC_CRED); + COUNTER_INC(cred_check_relabel); + + return (0); +} + +COUNTER_DECL(cred_check_visible); +static int +test_cred_check_visible(struct ucred *u1, struct ucred *u2) +{ + + LABEL_CHECK(u1->cr_label, MAGIC_CRED); + LABEL_CHECK(u2->cr_label, MAGIC_CRED); + COUNTER_INC(cred_check_visible); + + return (0); +} + +COUNTER_DECL(cred_copy_label); +static void +test_cred_copy_label(struct label *src, struct label *dest) +{ + + LABEL_CHECK(src, MAGIC_CRED); + LABEL_CHECK(dest, MAGIC_CRED); + COUNTER_INC(cred_copy_label); +} + +COUNTER_DECL(cred_create_init); +static void +test_cred_create_init(struct ucred *cred) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(cred_create_init); +} + +COUNTER_DECL(cred_create_swapper); +static void +test_cred_create_swapper(struct ucred *cred) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(cred_create_swapper); +} + +COUNTER_DECL(cred_destroy_label); +static void +test_cred_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_CRED); + COUNTER_INC(cred_destroy_label); +} + +COUNTER_DECL(cred_externalize_label); +static int +test_cred_externalize_label(struct label *label, char *element_name, + struct sbuf *sb, int *claimed) +{ + + LABEL_CHECK(label, MAGIC_CRED); + COUNTER_INC(cred_externalize_label); + + return (0); +} + +COUNTER_DECL(cred_init_label); +static void +test_cred_init_label(struct label *label) +{ + + LABEL_INIT(label, MAGIC_CRED); + COUNTER_INC(cred_init_label); +} + +COUNTER_DECL(cred_internalize_label); +static int +test_cred_internalize_label(struct label *label, char *element_name, + char *element_data, int *claimed) +{ + + LABEL_CHECK(label, MAGIC_CRED); + COUNTER_INC(cred_internalize_label); + + return (0); +} + +COUNTER_DECL(cred_relabel); +static void +test_cred_relabel(struct ucred *cred, struct label *newlabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(newlabel, MAGIC_CRED); + COUNTER_INC(cred_relabel); +} + +COUNTER_DECL(devfs_create_device); +static void +test_devfs_create_device(struct ucred *cred, struct mount *mp, + struct cdev *dev, struct devfs_dirent *de, struct label *delabel) +{ + + if (cred != NULL) + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(delabel, MAGIC_DEVFS); + COUNTER_INC(devfs_create_device); +} + +COUNTER_DECL(devfs_create_directory); +static void +test_devfs_create_directory(struct mount *mp, char *dirname, + int dirnamelen, struct devfs_dirent *de, struct label *delabel) +{ + + LABEL_CHECK(delabel, MAGIC_DEVFS); + COUNTER_INC(devfs_create_directory); +} + +COUNTER_DECL(devfs_create_symlink); +static void +test_devfs_create_symlink(struct ucred *cred, struct mount *mp, + struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de, + struct label *delabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(ddlabel, MAGIC_DEVFS); + LABEL_CHECK(delabel, MAGIC_DEVFS); + COUNTER_INC(devfs_create_symlink); +} + +COUNTER_DECL(devfs_destroy_label); +static void +test_devfs_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_DEVFS); + COUNTER_INC(devfs_destroy_label); +} + +COUNTER_DECL(devfs_init_label); +static void +test_devfs_init_label(struct label *label) +{ + + LABEL_INIT(label, MAGIC_DEVFS); + COUNTER_INC(devfs_init_label); +} + +COUNTER_DECL(devfs_update); +static void +test_devfs_update(struct mount *mp, struct devfs_dirent *devfs_dirent, + struct label *direntlabel, struct vnode *vp, struct label *vplabel) +{ + + LABEL_CHECK(direntlabel, MAGIC_DEVFS); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(devfs_update); +} + +COUNTER_DECL(devfs_vnode_associate); +static void +test_devfs_vnode_associate(struct mount *mp, struct label *mplabel, + struct devfs_dirent *de, struct label *delabel, struct vnode *vp, + struct label *vplabel) +{ + + LABEL_CHECK(mplabel, MAGIC_MOUNT); + LABEL_CHECK(delabel, MAGIC_DEVFS); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(devfs_vnode_associate); +} + +COUNTER_DECL(ifnet_check_relabel); +static int +test_ifnet_check_relabel(struct ucred *cred, struct ifnet *ifp, + struct label *ifplabel, struct label *newlabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(ifplabel, MAGIC_IFNET); + LABEL_CHECK(newlabel, MAGIC_IFNET); + COUNTER_INC(ifnet_check_relabel); + + return (0); +} + +COUNTER_DECL(ifnet_check_transmit); +static int +test_ifnet_check_transmit(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + + LABEL_CHECK(ifplabel, MAGIC_IFNET); + LABEL_CHECK(mlabel, MAGIC_MBUF); + COUNTER_INC(ifnet_check_transmit); + + return (0); +} + +COUNTER_DECL(ifnet_copy_label); +static void +test_ifnet_copy_label(struct label *src, struct label *dest) +{ + + LABEL_CHECK(src, MAGIC_IFNET); + LABEL_CHECK(dest, MAGIC_IFNET); + COUNTER_INC(ifnet_copy_label); +} + +COUNTER_DECL(ifnet_create); +static void +test_ifnet_create(struct ifnet *ifp, struct label *ifplabel) +{ + + LABEL_CHECK(ifplabel, MAGIC_IFNET); + COUNTER_INC(ifnet_create); +} + +COUNTER_DECL(ifnet_create_mbuf); +static void +test_ifnet_create_mbuf(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + + LABEL_CHECK(ifplabel, MAGIC_IFNET); + LABEL_CHECK(mlabel, MAGIC_MBUF); + COUNTER_INC(ifnet_create_mbuf); +} + +COUNTER_DECL(ifnet_destroy_label); +static void +test_ifnet_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_IFNET); + COUNTER_INC(ifnet_destroy_label); +} + +COUNTER_DECL(ifnet_externalize_label); +static int +test_ifnet_externalize_label(struct label *label, char *element_name, + struct sbuf *sb, int *claimed) +{ + + LABEL_CHECK(label, MAGIC_IFNET); + COUNTER_INC(ifnet_externalize_label); + + return (0); +} + +COUNTER_DECL(ifnet_init_label); +static void +test_ifnet_init_label(struct label *label) +{ + + LABEL_INIT(label, MAGIC_IFNET); + COUNTER_INC(ifnet_init_label); +} + +COUNTER_DECL(ifnet_internalize_label); +static int +test_ifnet_internalize_label(struct label *label, char *element_name, + char *element_data, int *claimed) +{ + + LABEL_CHECK(label, MAGIC_IFNET); + COUNTER_INC(ifnet_internalize_label); + + return (0); +} + +COUNTER_DECL(ifnet_relabel); +static void +test_ifnet_relabel(struct ucred *cred, struct ifnet *ifp, + struct label *ifplabel, struct label *newlabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(ifplabel, MAGIC_IFNET); + LABEL_CHECK(newlabel, MAGIC_IFNET); + COUNTER_INC(ifnet_relabel); +} + +COUNTER_DECL(inpcb_check_deliver); +static int +test_inpcb_check_deliver(struct inpcb *inp, struct label *inplabel, + struct mbuf *m, struct label *mlabel) +{ + + LABEL_CHECK(inplabel, MAGIC_INPCB); + LABEL_CHECK(mlabel, MAGIC_MBUF); + COUNTER_INC(inpcb_check_deliver); + + return (0); +} + +COUNTER_DECL(inpcb_check_visible); +static int +test_inpcb_check_visible(struct ucred *cred, struct inpcb *inp, + struct label *inplabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(inplabel, MAGIC_INPCB); + COUNTER_INC(inpcb_check_visible); + + return (0); +} + +COUNTER_DECL(inpcb_create); +static void +test_inpcb_create(struct socket *so, struct label *solabel, + struct inpcb *inp, struct label *inplabel) +{ + + LABEL_CHECK(solabel, MAGIC_SOCKET); + LABEL_CHECK(inplabel, MAGIC_INPCB); + COUNTER_INC(inpcb_create); +} + +COUNTER_DECL(inpcb_create_mbuf); +static void +test_inpcb_create_mbuf(struct inpcb *inp, struct label *inplabel, + struct mbuf *m, struct label *mlabel) +{ + + LABEL_CHECK(inplabel, MAGIC_INPCB); + LABEL_CHECK(mlabel, MAGIC_MBUF); + COUNTER_INC(inpcb_create_mbuf); +} + +COUNTER_DECL(inpcb_destroy_label); +static void +test_inpcb_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_INPCB); + COUNTER_INC(inpcb_destroy_label); +} + +COUNTER_DECL(inpcb_init_label); +static int +test_inpcb_init_label(struct label *label, int flag) +{ + + if (flag & M_WAITOK) + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, + "test_inpcb_init_label() at %s:%d", __FILE__, + __LINE__); + + LABEL_INIT(label, MAGIC_INPCB); + COUNTER_INC(inpcb_init_label); + return (0); +} + +COUNTER_DECL(inpcb_sosetlabel); +static void +test_inpcb_sosetlabel(struct socket *so, struct label *solabel, + struct inpcb *inp, struct label *inplabel) +{ + + LABEL_CHECK(solabel, MAGIC_SOCKET); + LABEL_CHECK(inplabel, MAGIC_INPCB); + COUNTER_INC(inpcb_sosetlabel); +} + +COUNTER_DECL(ip6q_create); +static void +test_ip6q_create(struct mbuf *fragment, struct label *fragmentlabel, + struct ip6q *q6, struct label *q6label) +{ + + LABEL_CHECK(fragmentlabel, MAGIC_MBUF); + LABEL_CHECK(q6label, MAGIC_IP6Q); + COUNTER_INC(ip6q_create); +} + +COUNTER_DECL(ip6q_destroy_label); +static void +test_ip6q_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_IP6Q); + COUNTER_INC(ip6q_destroy_label); +} + +COUNTER_DECL(ip6q_init_label); +static int +test_ip6q_init_label(struct label *label, int flag) +{ + + if (flag & M_WAITOK) + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, + "test_ip6q_init_label() at %s:%d", __FILE__, + __LINE__); + + LABEL_INIT(label, MAGIC_IP6Q); + COUNTER_INC(ip6q_init_label); + return (0); +} + +COUNTER_DECL(ip6q_match); +static int +test_ip6q_match(struct mbuf *fragment, struct label *fragmentlabel, + struct ip6q *q6, struct label *q6label) +{ + + LABEL_CHECK(fragmentlabel, MAGIC_MBUF); + LABEL_CHECK(q6label, MAGIC_IP6Q); + COUNTER_INC(ip6q_match); + + return (1); +} + +COUNTER_DECL(ip6q_reassemble); +static void +test_ip6q_reassemble(struct ip6q *q6, struct label *q6label, struct mbuf *m, + struct label *mlabel) +{ + + LABEL_CHECK(q6label, MAGIC_IP6Q); + LABEL_CHECK(mlabel, MAGIC_MBUF); + COUNTER_INC(ip6q_reassemble); +} + +COUNTER_DECL(ip6q_update); +static void +test_ip6q_update(struct mbuf *m, struct label *mlabel, struct ip6q *q6, + struct label *q6label) +{ + + LABEL_CHECK(mlabel, MAGIC_MBUF); + LABEL_CHECK(q6label, MAGIC_IP6Q); + COUNTER_INC(ip6q_update); +} + +COUNTER_DECL(ipq_create); +static void +test_ipq_create(struct mbuf *fragment, struct label *fragmentlabel, + struct ipq *q, struct label *qlabel) +{ + + LABEL_CHECK(fragmentlabel, MAGIC_MBUF); + LABEL_CHECK(qlabel, MAGIC_IPQ); + COUNTER_INC(ipq_create); +} + +COUNTER_DECL(ipq_destroy_label); +static void +test_ipq_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_IPQ); + COUNTER_INC(ipq_destroy_label); +} + +COUNTER_DECL(ipq_init_label); +static int +test_ipq_init_label(struct label *label, int flag) +{ + + if (flag & M_WAITOK) + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, + "test_ipq_init_label() at %s:%d", __FILE__, + __LINE__); + + LABEL_INIT(label, MAGIC_IPQ); + COUNTER_INC(ipq_init_label); + return (0); +} + +COUNTER_DECL(ipq_match); +static int +test_ipq_match(struct mbuf *fragment, struct label *fragmentlabel, + struct ipq *q, struct label *qlabel) +{ + + LABEL_CHECK(fragmentlabel, MAGIC_MBUF); + LABEL_CHECK(qlabel, MAGIC_IPQ); + COUNTER_INC(ipq_match); + + return (1); +} + +COUNTER_DECL(ipq_reassemble); +static void +test_ipq_reassemble(struct ipq *q, struct label *qlabel, struct mbuf *m, + struct label *mlabel) +{ + + LABEL_CHECK(qlabel, MAGIC_IPQ); + LABEL_CHECK(mlabel, MAGIC_MBUF); + COUNTER_INC(ipq_reassemble); +} + +COUNTER_DECL(ipq_update); +static void +test_ipq_update(struct mbuf *m, struct label *mlabel, struct ipq *q, + struct label *qlabel) +{ + + LABEL_CHECK(mlabel, MAGIC_MBUF); + LABEL_CHECK(qlabel, MAGIC_IPQ); + COUNTER_INC(ipq_update); +} + +COUNTER_DECL(kenv_check_dump); +static int +test_kenv_check_dump(struct ucred *cred) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(kenv_check_dump); + + return (0); +} + +COUNTER_DECL(kenv_check_get); +static int +test_kenv_check_get(struct ucred *cred, char *name) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(kenv_check_get); + + return (0); +} + +COUNTER_DECL(kenv_check_set); +static int +test_kenv_check_set(struct ucred *cred, char *name, char *value) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(kenv_check_set); + + return (0); +} + +COUNTER_DECL(kenv_check_unset); +static int +test_kenv_check_unset(struct ucred *cred, char *name) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(kenv_check_unset); + + return (0); +} + +COUNTER_DECL(kld_check_load); +static int +test_kld_check_load(struct ucred *cred, struct vnode *vp, + struct label *label) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(label, MAGIC_VNODE); + COUNTER_INC(kld_check_load); + + return (0); +} + +COUNTER_DECL(kld_check_stat); +static int +test_kld_check_stat(struct ucred *cred) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(kld_check_stat); + + return (0); +} + +COUNTER_DECL(mbuf_copy_label); +static void +test_mbuf_copy_label(struct label *src, struct label *dest) +{ + + LABEL_CHECK(src, MAGIC_MBUF); + LABEL_CHECK(dest, MAGIC_MBUF); + COUNTER_INC(mbuf_copy_label); +} + +COUNTER_DECL(mbuf_destroy_label); +static void +test_mbuf_destroy_label(struct label *label) +{ + + /* + * If we're loaded dynamically, there may be mbufs in flight that + * didn't have label storage allocated for them. Handle this + * gracefully. + */ + if (label == NULL) + return; + + LABEL_DESTROY(label, MAGIC_MBUF); + COUNTER_INC(mbuf_destroy_label); +} + +COUNTER_DECL(mbuf_init_label); +static int +test_mbuf_init_label(struct label *label, int flag) +{ + + if (flag & M_WAITOK) + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, + "test_mbuf_init_label() at %s:%d", __FILE__, + __LINE__); + + LABEL_INIT(label, MAGIC_MBUF); + COUNTER_INC(mbuf_init_label); + return (0); +} + +COUNTER_DECL(mount_check_stat); +static int +test_mount_check_stat(struct ucred *cred, struct mount *mp, + struct label *mplabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(mplabel, MAGIC_MOUNT); + COUNTER_INC(mount_check_stat); + + return (0); +} + +COUNTER_DECL(mount_create); +static void +test_mount_create(struct ucred *cred, struct mount *mp, + struct label *mplabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(mplabel, MAGIC_MOUNT); + COUNTER_INC(mount_create); +} + +COUNTER_DECL(mount_destroy_label); +static void +test_mount_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_MOUNT); + COUNTER_INC(mount_destroy_label); +} + +COUNTER_DECL(mount_init_label); +static void +test_mount_init_label(struct label *label) +{ + + LABEL_INIT(label, MAGIC_MOUNT); + COUNTER_INC(mount_init_label); +} + +COUNTER_DECL(netatalk_aarp_send); +static void +test_netatalk_aarp_send(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + + LABEL_CHECK(ifplabel, MAGIC_IFNET); + LABEL_CHECK(mlabel, MAGIC_MBUF); + COUNTER_INC(netatalk_aarp_send); +} + +COUNTER_DECL(netinet_arp_send); +static void +test_netinet_arp_send(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + + LABEL_CHECK(ifplabel, MAGIC_IFNET); + LABEL_CHECK(mlabel, MAGIC_MBUF); + COUNTER_INC(netinet_arp_send); +} + +COUNTER_DECL(netinet_fragment); +static void +test_netinet_fragment(struct mbuf *m, struct label *mlabel, + struct mbuf *frag, struct label *fraglabel) +{ + + LABEL_CHECK(mlabel, MAGIC_MBUF); + LABEL_CHECK(fraglabel, MAGIC_MBUF); + COUNTER_INC(netinet_fragment); +} + +COUNTER_DECL(netinet_icmp_reply); +static void +test_netinet_icmp_reply(struct mbuf *mrecv, struct label *mrecvlabel, + struct mbuf *msend, struct label *msendlabel) +{ + + LABEL_CHECK(mrecvlabel, MAGIC_MBUF); + LABEL_CHECK(msendlabel, MAGIC_MBUF); + COUNTER_INC(netinet_icmp_reply); +} + +COUNTER_DECL(netinet_icmp_replyinplace); +static void +test_netinet_icmp_replyinplace(struct mbuf *m, struct label *mlabel) +{ + + LABEL_CHECK(mlabel, MAGIC_MBUF); + COUNTER_INC(netinet_icmp_replyinplace); +} + +COUNTER_DECL(netinet_igmp_send); +static void +test_netinet_igmp_send(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + + LABEL_CHECK(ifplabel, MAGIC_IFNET); + LABEL_CHECK(mlabel, MAGIC_MBUF); + COUNTER_INC(netinet_igmp_send); +} + +COUNTER_DECL(netinet_tcp_reply); +static void +test_netinet_tcp_reply(struct mbuf *m, struct label *mlabel) +{ + + LABEL_CHECK(mlabel, MAGIC_MBUF); + COUNTER_INC(netinet_tcp_reply); +} + +COUNTER_DECL(netinet6_nd6_send); +static void +test_netinet6_nd6_send(struct ifnet *ifp, struct label *ifplabel, + struct mbuf *m, struct label *mlabel) +{ + + LABEL_CHECK(ifplabel, MAGIC_IFNET); + LABEL_CHECK(mlabel, MAGIC_MBUF); + COUNTER_INC(netinet6_nd6_send); +} + +COUNTER_DECL(pipe_check_ioctl); +static int +test_pipe_check_ioctl(struct ucred *cred, struct pipepair *pp, + struct label *pplabel, unsigned long cmd, void /* caddr_t */ *data) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(pplabel, MAGIC_PIPE); + COUNTER_INC(pipe_check_ioctl); + + return (0); +} + +COUNTER_DECL(pipe_check_poll); +static int +test_pipe_check_poll(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(pplabel, MAGIC_PIPE); + COUNTER_INC(pipe_check_poll); + + return (0); +} + +COUNTER_DECL(pipe_check_read); +static int +test_pipe_check_read(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(pplabel, MAGIC_PIPE); + COUNTER_INC(pipe_check_read); + + return (0); +} + +COUNTER_DECL(pipe_check_relabel); +static int +test_pipe_check_relabel(struct ucred *cred, struct pipepair *pp, + struct label *pplabel, struct label *newlabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(pplabel, MAGIC_PIPE); + LABEL_CHECK(newlabel, MAGIC_PIPE); + COUNTER_INC(pipe_check_relabel); + + return (0); +} + +COUNTER_DECL(pipe_check_stat); +static int +test_pipe_check_stat(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(pplabel, MAGIC_PIPE); + COUNTER_INC(pipe_check_stat); + + return (0); +} + +COUNTER_DECL(pipe_check_write); +static int +test_pipe_check_write(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(pplabel, MAGIC_PIPE); + COUNTER_INC(pipe_check_write); + + return (0); +} + +COUNTER_DECL(pipe_copy_label); +static void +test_pipe_copy_label(struct label *src, struct label *dest) +{ + + LABEL_CHECK(src, MAGIC_PIPE); + LABEL_CHECK(dest, MAGIC_PIPE); + COUNTER_INC(pipe_copy_label); +} + +COUNTER_DECL(pipe_create); +static void +test_pipe_create(struct ucred *cred, struct pipepair *pp, + struct label *pplabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(pplabel, MAGIC_PIPE); + COUNTER_INC(pipe_create); +} + +COUNTER_DECL(pipe_destroy_label); +static void +test_pipe_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_PIPE); + COUNTER_INC(pipe_destroy_label); +} + +COUNTER_DECL(pipe_externalize_label); +static int +test_pipe_externalize_label(struct label *label, char *element_name, + struct sbuf *sb, int *claimed) +{ + + LABEL_CHECK(label, MAGIC_PIPE); + COUNTER_INC(pipe_externalize_label); + + return (0); +} + +COUNTER_DECL(pipe_init_label); +static void +test_pipe_init_label(struct label *label) +{ + + LABEL_INIT(label, MAGIC_PIPE); + COUNTER_INC(pipe_init_label); +} + +COUNTER_DECL(pipe_internalize_label); +static int +test_pipe_internalize_label(struct label *label, char *element_name, + char *element_data, int *claimed) +{ + + LABEL_CHECK(label, MAGIC_PIPE); + COUNTER_INC(pipe_internalize_label); + + return (0); +} + +COUNTER_DECL(pipe_relabel); +static void +test_pipe_relabel(struct ucred *cred, struct pipepair *pp, + struct label *pplabel, struct label *newlabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(pplabel, MAGIC_PIPE); + LABEL_CHECK(newlabel, MAGIC_PIPE); + COUNTER_INC(pipe_relabel); +} + +COUNTER_DECL(posixsem_check_getvalue); +static int +test_posixsem_check_getvalue(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks, struct label *kslabel) +{ + + LABEL_CHECK(active_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(file_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(kslabel, MAGIC_POSIX_SEM); + COUNTER_INC(posixsem_check_getvalue); + + return (0); +} + +COUNTER_DECL(posixsem_check_open); +static int +test_posixsem_check_open(struct ucred *cred, struct ksem *ks, + struct label *kslabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(kslabel, MAGIC_POSIX_SEM); + COUNTER_INC(posixsem_check_open); + + return (0); +} + +COUNTER_DECL(posixsem_check_post); +static int +test_posixsem_check_post(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks, struct label *kslabel) +{ + + LABEL_CHECK(active_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(file_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(kslabel, MAGIC_POSIX_SEM); + COUNTER_INC(posixsem_check_post); + + return (0); +} + +COUNTER_DECL(posixsem_check_stat); +static int +test_posixsem_check_stat(struct ucred *active_cred, + struct ucred *file_cred, struct ksem *ks, struct label *kslabel) +{ + + LABEL_CHECK(active_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(file_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(kslabel, MAGIC_POSIX_SEM); + COUNTER_INC(posixsem_check_stat); + return (0); +} + +COUNTER_DECL(posixsem_check_unlink); +static int +test_posixsem_check_unlink(struct ucred *cred, struct ksem *ks, + struct label *kslabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(kslabel, MAGIC_POSIX_SEM); + COUNTER_INC(posixsem_check_unlink); + + return (0); +} + +COUNTER_DECL(posixsem_check_wait); +static int +test_posixsem_check_wait(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks, struct label *kslabel) +{ + + LABEL_CHECK(active_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(file_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(kslabel, MAGIC_POSIX_SEM); + COUNTER_INC(posixsem_check_wait); + + return (0); +} + +COUNTER_DECL(posixsem_create); +static void +test_posixsem_create(struct ucred *cred, struct ksem *ks, + struct label *kslabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(kslabel, MAGIC_POSIX_SEM); + COUNTER_INC(posixsem_create); +} + +COUNTER_DECL(posixsem_destroy_label); +static void +test_posixsem_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_POSIX_SEM); + COUNTER_INC(posixsem_destroy_label); +} + +COUNTER_DECL(posixsem_init_label); +static void +test_posixsem_init_label(struct label *label) +{ + + LABEL_INIT(label, MAGIC_POSIX_SEM); + COUNTER_INC(posixsem_init_label); +} + +COUNTER_DECL(posixshm_check_mmap); +static int +test_posixshm_check_mmap(struct ucred *cred, struct shmfd *shmfd, + struct label *shmfdlabel, int prot, int flags) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(shmfdlabel, MAGIC_POSIX_SHM); + COUNTER_INC(posixshm_check_mmap); + return (0); +} + +COUNTER_DECL(posixshm_check_open); +static int +test_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd, + struct label *shmfdlabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(shmfdlabel, MAGIC_POSIX_SHM); + COUNTER_INC(posixshm_check_open); + return (0); +} + +COUNTER_DECL(posixshm_check_stat); +static int +test_posixshm_check_stat(struct ucred *active_cred, + struct ucred *file_cred, struct shmfd *shmfd, struct label *shmfdlabel) +{ + + LABEL_CHECK(active_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(file_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(shmfdlabel, MAGIC_POSIX_SHM); + COUNTER_INC(posixshm_check_stat); + return (0); +} + +COUNTER_DECL(posixshm_check_truncate); +static int +test_posixshm_check_truncate(struct ucred *active_cred, + struct ucred *file_cred, struct shmfd *shmfd, struct label *shmfdlabel) +{ + + LABEL_CHECK(active_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(file_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(shmfdlabel, MAGIC_POSIX_SHM); + COUNTER_INC(posixshm_check_truncate); + return (0); +} + +COUNTER_DECL(posixshm_check_unlink); +static int +test_posixshm_check_unlink(struct ucred *cred, struct shmfd *shmfd, + struct label *shmfdlabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(shmfdlabel, MAGIC_POSIX_SHM); + COUNTER_INC(posixshm_check_unlink); + return (0); +} + +COUNTER_DECL(posixshm_create); +static void +test_posixshm_create(struct ucred *cred, struct shmfd *shmfd, + struct label *shmfdlabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(shmfdlabel, MAGIC_POSIX_SHM); + COUNTER_INC(posixshm_create); +} + +COUNTER_DECL(posixshm_destroy_label); +static void +test_posixshm_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_POSIX_SHM); + COUNTER_INC(posixshm_destroy_label); +} + +COUNTER_DECL(posixshm_init_label); +static void +test_posixshm_init_label(struct label *label) +{ + + LABEL_INIT(label, MAGIC_POSIX_SHM); + COUNTER_INC(posixshm_init_label); +} + +COUNTER_DECL(proc_check_debug); +static int +test_proc_check_debug(struct ucred *cred, struct proc *p) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(p->p_ucred->cr_label, MAGIC_CRED); + COUNTER_INC(proc_check_debug); + + return (0); +} + +COUNTER_DECL(proc_check_sched); +static int +test_proc_check_sched(struct ucred *cred, struct proc *p) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(p->p_ucred->cr_label, MAGIC_CRED); + COUNTER_INC(proc_check_sched); + + return (0); +} + +COUNTER_DECL(proc_check_signal); +static int +test_proc_check_signal(struct ucred *cred, struct proc *p, int signum) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(p->p_ucred->cr_label, MAGIC_CRED); + COUNTER_INC(proc_check_signal); + + return (0); +} + +COUNTER_DECL(proc_check_setaudit); +static int +test_proc_check_setaudit(struct ucred *cred, struct auditinfo *ai) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(proc_check_setaudit); + + return (0); +} + +COUNTER_DECL(proc_check_setaudit_addr); +static int +test_proc_check_setaudit_addr(struct ucred *cred, + struct auditinfo_addr *aia) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(proc_check_setaudit_addr); + + return (0); +} + +COUNTER_DECL(proc_check_setauid); +static int +test_proc_check_setauid(struct ucred *cred, uid_t auid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(proc_check_setauid); + + return (0); +} + +COUNTER_DECL(proc_check_setegid); +static int +test_proc_check_setegid(struct ucred *cred, gid_t egid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(proc_check_setegid); + + return (0); +} + +COUNTER_DECL(proc_check_euid); +static int +test_proc_check_seteuid(struct ucred *cred, uid_t euid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(proc_check_euid); + + return (0); +} + +COUNTER_DECL(proc_check_setregid); +static int +test_proc_check_setregid(struct ucred *cred, gid_t rgid, gid_t egid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(proc_check_setregid); + + return (0); +} + +COUNTER_DECL(proc_check_setreuid); +static int +test_proc_check_setreuid(struct ucred *cred, uid_t ruid, uid_t euid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(proc_check_setreuid); + + return (0); +} + +COUNTER_DECL(proc_check_setgid); +static int +test_proc_check_setgid(struct ucred *cred, gid_t gid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(proc_check_setgid); + + return (0); +} + +COUNTER_DECL(proc_check_setgroups); +static int +test_proc_check_setgroups(struct ucred *cred, int ngroups, + gid_t *gidset) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(proc_check_setgroups); + + return (0); +} + +COUNTER_DECL(proc_check_setresgid); +static int +test_proc_check_setresgid(struct ucred *cred, gid_t rgid, gid_t egid, + gid_t sgid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(proc_check_setresgid); + + return (0); +} + +COUNTER_DECL(proc_check_setresuid); +static int +test_proc_check_setresuid(struct ucred *cred, uid_t ruid, uid_t euid, + uid_t suid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(proc_check_setresuid); + + return (0); +} + +COUNTER_DECL(proc_check_setuid); +static int +test_proc_check_setuid(struct ucred *cred, uid_t uid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(proc_check_setuid); + + return (0); +} + +COUNTER_DECL(proc_check_wait); +static int +test_proc_check_wait(struct ucred *cred, struct proc *p) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(p->p_ucred->cr_label, MAGIC_CRED); + COUNTER_INC(proc_check_wait); + + return (0); +} + +COUNTER_DECL(proc_destroy_label); +static void +test_proc_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_PROC); + COUNTER_INC(proc_destroy_label); +} + +COUNTER_DECL(proc_init_label); +static void +test_proc_init_label(struct label *label) +{ + + LABEL_INIT(label, MAGIC_PROC); + COUNTER_INC(proc_init_label); +} + +COUNTER_DECL(socket_check_accept); +static int +test_socket_check_accept(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(solabel, MAGIC_SOCKET); + COUNTER_INC(socket_check_accept); + + return (0); +} + +COUNTER_DECL(socket_check_bind); +static int +test_socket_check_bind(struct ucred *cred, struct socket *so, + struct label *solabel, struct sockaddr *sa) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(solabel, MAGIC_SOCKET); + COUNTER_INC(socket_check_bind); + + return (0); +} + +COUNTER_DECL(socket_check_connect); +static int +test_socket_check_connect(struct ucred *cred, struct socket *so, + struct label *solabel, struct sockaddr *sa) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(solabel, MAGIC_SOCKET); + COUNTER_INC(socket_check_connect); + + return (0); +} + +COUNTER_DECL(socket_check_deliver); +static int +test_socket_check_deliver(struct socket *so, struct label *solabel, + struct mbuf *m, struct label *mlabel) +{ + + LABEL_CHECK(solabel, MAGIC_SOCKET); + LABEL_CHECK(mlabel, MAGIC_MBUF); + COUNTER_INC(socket_check_deliver); + + return (0); +} + +COUNTER_DECL(socket_check_listen); +static int +test_socket_check_listen(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(solabel, MAGIC_SOCKET); + COUNTER_INC(socket_check_listen); + + return (0); +} + +COUNTER_DECL(socket_check_poll); +static int +test_socket_check_poll(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(solabel, MAGIC_SOCKET); + COUNTER_INC(socket_check_poll); + + return (0); +} + +COUNTER_DECL(socket_check_receive); +static int +test_socket_check_receive(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(solabel, MAGIC_SOCKET); + COUNTER_INC(socket_check_receive); + + return (0); +} + +COUNTER_DECL(socket_check_relabel); +static int +test_socket_check_relabel(struct ucred *cred, struct socket *so, + struct label *solabel, struct label *newlabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(solabel, MAGIC_SOCKET); + LABEL_CHECK(newlabel, MAGIC_SOCKET); + COUNTER_INC(socket_check_relabel); + + return (0); +} + +COUNTER_DECL(socket_check_send); +static int +test_socket_check_send(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(solabel, MAGIC_SOCKET); + COUNTER_INC(socket_check_send); + + return (0); +} + +COUNTER_DECL(socket_check_stat); +static int +test_socket_check_stat(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(solabel, MAGIC_SOCKET); + COUNTER_INC(socket_check_stat); + + return (0); +} + +COUNTER_DECL(socket_check_visible); +static int +test_socket_check_visible(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(solabel, MAGIC_SOCKET); + COUNTER_INC(socket_check_visible); + + return (0); +} + +COUNTER_DECL(socket_copy_label); +static void +test_socket_copy_label(struct label *src, struct label *dest) +{ + + LABEL_CHECK(src, MAGIC_SOCKET); + LABEL_CHECK(dest, MAGIC_SOCKET); + COUNTER_INC(socket_copy_label); +} + +COUNTER_DECL(socket_create); +static void +test_socket_create(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(solabel, MAGIC_SOCKET); + COUNTER_INC(socket_create); +} + +COUNTER_DECL(socket_create_mbuf); +static void +test_socket_create_mbuf(struct socket *so, struct label *socketlabel, + struct mbuf *m, struct label *mlabel) +{ + + LABEL_CHECK(socketlabel, MAGIC_SOCKET); + LABEL_CHECK(mlabel, MAGIC_MBUF); + COUNTER_INC(socket_create_mbuf); +} + +COUNTER_DECL(socket_destroy_label); +static void +test_socket_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_SOCKET); + COUNTER_INC(socket_destroy_label); +} + +COUNTER_DECL(socket_externalize_label); +static int +test_socket_externalize_label(struct label *label, char *element_name, + struct sbuf *sb, int *claimed) +{ + + LABEL_CHECK(label, MAGIC_SOCKET); + COUNTER_INC(socket_externalize_label); + + return (0); +} + +COUNTER_DECL(socket_init_label); +static int +test_socket_init_label(struct label *label, int flag) +{ + + if (flag & M_WAITOK) + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, + "test_socket_init_label() at %s:%d", __FILE__, + __LINE__); + + LABEL_INIT(label, MAGIC_SOCKET); + COUNTER_INC(socket_init_label); + return (0); +} + +COUNTER_DECL(socket_internalize_label); +static int +test_socket_internalize_label(struct label *label, char *element_name, + char *element_data, int *claimed) +{ + + LABEL_CHECK(label, MAGIC_SOCKET); + COUNTER_INC(socket_internalize_label); + + return (0); +} + +COUNTER_DECL(socket_newconn); +static void +test_socket_newconn(struct socket *oldso, struct label *oldsolabel, + struct socket *newso, struct label *newsolabel) +{ + + LABEL_CHECK(oldsolabel, MAGIC_SOCKET); + LABEL_CHECK(newsolabel, MAGIC_SOCKET); + COUNTER_INC(socket_newconn); +} + +COUNTER_DECL(socket_relabel); +static void +test_socket_relabel(struct ucred *cred, struct socket *so, + struct label *solabel, struct label *newlabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(solabel, MAGIC_SOCKET); + LABEL_CHECK(newlabel, MAGIC_SOCKET); + COUNTER_INC(socket_relabel); +} + +COUNTER_DECL(socketpeer_destroy_label); +static void +test_socketpeer_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_SOCKET); + COUNTER_INC(socketpeer_destroy_label); +} + +COUNTER_DECL(socketpeer_externalize_label); +static int +test_socketpeer_externalize_label(struct label *label, char *element_name, + struct sbuf *sb, int *claimed) +{ + + LABEL_CHECK(label, MAGIC_SOCKET); + COUNTER_INC(socketpeer_externalize_label); + + return (0); +} + +COUNTER_DECL(socketpeer_init_label); +static int +test_socketpeer_init_label(struct label *label, int flag) +{ + + if (flag & M_WAITOK) + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, + "test_socketpeer_init_label() at %s:%d", __FILE__, + __LINE__); + + LABEL_INIT(label, MAGIC_SOCKET); + COUNTER_INC(socketpeer_init_label); + return (0); +} + +COUNTER_DECL(socketpeer_set_from_mbuf); +static void +test_socketpeer_set_from_mbuf(struct mbuf *m, struct label *mlabel, + struct socket *socket, struct label *socketpeerlabel) +{ + + LABEL_CHECK(mlabel, MAGIC_MBUF); + LABEL_CHECK(socketpeerlabel, MAGIC_SOCKET); + COUNTER_INC(socketpeer_set_from_mbuf); +} + +COUNTER_DECL(socketpeer_set_from_socket); +static void +test_socketpeer_set_from_socket(struct socket *oldso, + struct label *oldsolabel, struct socket *newso, + struct label *newsopeerlabel) +{ + + LABEL_CHECK(oldsolabel, MAGIC_SOCKET); + LABEL_CHECK(newsopeerlabel, MAGIC_SOCKET); + COUNTER_INC(socketpeer_set_from_socket); +} + +COUNTER_DECL(syncache_create); +static void +test_syncache_create(struct label *label, struct inpcb *inp) +{ + + LABEL_CHECK(label, MAGIC_SYNCACHE); + COUNTER_INC(syncache_create); +} + +COUNTER_DECL(syncache_create_mbuf); +static void +test_syncache_create_mbuf(struct label *sc_label, struct mbuf *m, + struct label *mlabel) +{ + + LABEL_CHECK(sc_label, MAGIC_SYNCACHE); + LABEL_CHECK(mlabel, MAGIC_MBUF); + COUNTER_INC(syncache_create_mbuf); +} + +COUNTER_DECL(syncache_destroy_label); +static void +test_syncache_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_SYNCACHE); + COUNTER_INC(syncache_destroy_label); +} + +COUNTER_DECL(syncache_init_label); +static int +test_syncache_init_label(struct label *label, int flag) +{ + + if (flag & M_WAITOK) + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, + "test_syncache_init_label() at %s:%d", __FILE__, + __LINE__); + LABEL_INIT(label, MAGIC_SYNCACHE); + COUNTER_INC(syncache_init_label); + return (0); +} + +COUNTER_DECL(system_check_acct); +static int +test_system_check_acct(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(system_check_acct); + + return (0); +} + +COUNTER_DECL(system_check_audit); +static int +test_system_check_audit(struct ucred *cred, void *record, int length) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(system_check_audit); + + return (0); +} + +COUNTER_DECL(system_check_auditctl); +static int +test_system_check_auditctl(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(system_check_auditctl); + + return (0); +} + +COUNTER_DECL(system_check_auditon); +static int +test_system_check_auditon(struct ucred *cred, int cmd) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(system_check_auditon); + + return (0); +} + +COUNTER_DECL(system_check_reboot); +static int +test_system_check_reboot(struct ucred *cred, int how) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(system_check_reboot); + + return (0); +} + +COUNTER_DECL(system_check_swapoff); +static int +test_system_check_swapoff(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(system_check_swapoff); + + return (0); +} + +COUNTER_DECL(system_check_swapon); +static int +test_system_check_swapon(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(system_check_swapon); + + return (0); +} + +COUNTER_DECL(system_check_sysctl); +static int +test_system_check_sysctl(struct ucred *cred, struct sysctl_oid *oidp, + void *arg1, int arg2, struct sysctl_req *req) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(system_check_sysctl); + + return (0); +} + +COUNTER_DECL(sysvmsg_cleanup); +static void +test_sysvmsg_cleanup(struct label *msglabel) +{ + + LABEL_CHECK(msglabel, MAGIC_SYSV_MSG); + COUNTER_INC(sysvmsg_cleanup); +} + +COUNTER_DECL(sysvmsg_create); +static void +test_sysvmsg_create(struct ucred *cred, struct msqid_kernel *msqkptr, + struct label *msqlabel, struct msg *msgptr, struct label *msglabel) +{ + + LABEL_CHECK(msglabel, MAGIC_SYSV_MSG); + LABEL_CHECK(msqlabel, MAGIC_SYSV_MSQ); + COUNTER_INC(sysvmsg_create); +} + +COUNTER_DECL(sysvmsg_destroy_label); +static void +test_sysvmsg_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_SYSV_MSG); + COUNTER_INC(sysvmsg_destroy_label); +} + +COUNTER_DECL(sysvmsg_init_label); +static void +test_sysvmsg_init_label(struct label *label) +{ + LABEL_INIT(label, MAGIC_SYSV_MSG); + COUNTER_INC(sysvmsg_init_label); +} + +COUNTER_DECL(sysvmsq_check_msgmsq); +static int +test_sysvmsq_check_msgmsq(struct ucred *cred, struct msg *msgptr, + struct label *msglabel, struct msqid_kernel *msqkptr, + struct label *msqklabel) +{ + + LABEL_CHECK(msqklabel, MAGIC_SYSV_MSQ); + LABEL_CHECK(msglabel, MAGIC_SYSV_MSG); + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(sysvmsq_check_msgmsq); + + return (0); +} + +COUNTER_DECL(sysvmsq_check_msgrcv); +static int +test_sysvmsq_check_msgrcv(struct ucred *cred, struct msg *msgptr, + struct label *msglabel) +{ + + LABEL_CHECK(msglabel, MAGIC_SYSV_MSG); + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(sysvmsq_check_msgrcv); + + return (0); +} + +COUNTER_DECL(sysvmsq_check_msgrmid); +static int +test_sysvmsq_check_msgrmid(struct ucred *cred, struct msg *msgptr, + struct label *msglabel) +{ + + LABEL_CHECK(msglabel, MAGIC_SYSV_MSG); + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(sysvmsq_check_msgrmid); + + return (0); +} + +COUNTER_DECL(sysvmsq_check_msqget); +static int +test_sysvmsq_check_msqget(struct ucred *cred, + struct msqid_kernel *msqkptr, struct label *msqklabel) +{ + + LABEL_CHECK(msqklabel, MAGIC_SYSV_MSQ); + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(sysvmsq_check_msqget); + + return (0); +} + +COUNTER_DECL(sysvmsq_check_msqsnd); +static int +test_sysvmsq_check_msqsnd(struct ucred *cred, + struct msqid_kernel *msqkptr, struct label *msqklabel) +{ + + LABEL_CHECK(msqklabel, MAGIC_SYSV_MSQ); + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(sysvmsq_check_msqsnd); + + return (0); +} + +COUNTER_DECL(sysvmsq_check_msqrcv); +static int +test_sysvmsq_check_msqrcv(struct ucred *cred, + struct msqid_kernel *msqkptr, struct label *msqklabel) +{ + + LABEL_CHECK(msqklabel, MAGIC_SYSV_MSQ); + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(sysvmsq_check_msqrcv); + + return (0); +} + +COUNTER_DECL(sysvmsq_check_msqctl); +static int +test_sysvmsq_check_msqctl(struct ucred *cred, + struct msqid_kernel *msqkptr, struct label *msqklabel, int cmd) +{ + + LABEL_CHECK(msqklabel, MAGIC_SYSV_MSQ); + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(sysvmsq_check_msqctl); + + return (0); +} + +COUNTER_DECL(sysvmsq_cleanup); +static void +test_sysvmsq_cleanup(struct label *msqlabel) +{ + + LABEL_CHECK(msqlabel, MAGIC_SYSV_MSQ); + COUNTER_INC(sysvmsq_cleanup); +} + +COUNTER_DECL(sysvmsq_create); +static void +test_sysvmsq_create(struct ucred *cred, + struct msqid_kernel *msqkptr, struct label *msqlabel) +{ + + LABEL_CHECK(msqlabel, MAGIC_SYSV_MSQ); + COUNTER_INC(sysvmsq_create); +} + +COUNTER_DECL(sysvmsq_destroy_label); +static void +test_sysvmsq_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_SYSV_MSQ); + COUNTER_INC(sysvmsq_destroy_label); +} + +COUNTER_DECL(sysvmsq_init_label); +static void +test_sysvmsq_init_label(struct label *label) +{ + LABEL_INIT(label, MAGIC_SYSV_MSQ); + COUNTER_INC(sysvmsq_init_label); +} + +COUNTER_DECL(sysvsem_check_semctl); +static int +test_sysvsem_check_semctl(struct ucred *cred, + struct semid_kernel *semakptr, struct label *semaklabel, int cmd) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(semaklabel, MAGIC_SYSV_SEM); + COUNTER_INC(sysvsem_check_semctl); + + return (0); +} + +COUNTER_DECL(sysvsem_check_semget); +static int +test_sysvsem_check_semget(struct ucred *cred, + struct semid_kernel *semakptr, struct label *semaklabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(semaklabel, MAGIC_SYSV_SEM); + COUNTER_INC(sysvsem_check_semget); + + return (0); +} + +COUNTER_DECL(sysvsem_check_semop); +static int +test_sysvsem_check_semop(struct ucred *cred, + struct semid_kernel *semakptr, struct label *semaklabel, size_t accesstype) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(semaklabel, MAGIC_SYSV_SEM); + COUNTER_INC(sysvsem_check_semop); + + return (0); +} + +COUNTER_DECL(sysvsem_cleanup); +static void +test_sysvsem_cleanup(struct label *semalabel) +{ + + LABEL_CHECK(semalabel, MAGIC_SYSV_SEM); + COUNTER_INC(sysvsem_cleanup); +} + +COUNTER_DECL(sysvsem_create); +static void +test_sysvsem_create(struct ucred *cred, struct semid_kernel *semakptr, + struct label *semalabel) +{ + + LABEL_CHECK(semalabel, MAGIC_SYSV_SEM); + COUNTER_INC(sysvsem_create); +} + +COUNTER_DECL(sysvsem_destroy_label); +static void +test_sysvsem_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_SYSV_SEM); + COUNTER_INC(sysvsem_destroy_label); +} + +COUNTER_DECL(sysvsem_init_label); +static void +test_sysvsem_init_label(struct label *label) +{ + LABEL_INIT(label, MAGIC_SYSV_SEM); + COUNTER_INC(sysvsem_init_label); +} + +COUNTER_DECL(sysvshm_check_shmat); +static int +test_sysvshm_check_shmat(struct ucred *cred, + struct shmid_kernel *shmsegptr, struct label *shmseglabel, int shmflg) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(shmseglabel, MAGIC_SYSV_SHM); + COUNTER_INC(sysvshm_check_shmat); + + return (0); +} + +COUNTER_DECL(sysvshm_check_shmctl); +static int +test_sysvshm_check_shmctl(struct ucred *cred, + struct shmid_kernel *shmsegptr, struct label *shmseglabel, int cmd) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(shmseglabel, MAGIC_SYSV_SHM); + COUNTER_INC(sysvshm_check_shmctl); + + return (0); +} + +COUNTER_DECL(sysvshm_check_shmdt); +static int +test_sysvshm_check_shmdt(struct ucred *cred, + struct shmid_kernel *shmsegptr, struct label *shmseglabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(shmseglabel, MAGIC_SYSV_SHM); + COUNTER_INC(sysvshm_check_shmdt); + + return (0); +} + +COUNTER_DECL(sysvshm_check_shmget); +static int +test_sysvshm_check_shmget(struct ucred *cred, + struct shmid_kernel *shmsegptr, struct label *shmseglabel, int shmflg) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(shmseglabel, MAGIC_SYSV_SHM); + COUNTER_INC(sysvshm_check_shmget); + + return (0); +} + +COUNTER_DECL(sysvshm_cleanup); +static void +test_sysvshm_cleanup(struct label *shmlabel) +{ + + LABEL_CHECK(shmlabel, MAGIC_SYSV_SHM); + COUNTER_INC(sysvshm_cleanup); +} + +COUNTER_DECL(sysvshm_create); +static void +test_sysvshm_create(struct ucred *cred, struct shmid_kernel *shmsegptr, + struct label *shmlabel) +{ + + LABEL_CHECK(shmlabel, MAGIC_SYSV_SHM); + COUNTER_INC(sysvshm_create); +} + +COUNTER_DECL(sysvshm_destroy_label); +static void +test_sysvshm_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_SYSV_SHM); + COUNTER_INC(sysvshm_destroy_label); +} + +COUNTER_DECL(sysvshm_init_label); +static void +test_sysvshm_init_label(struct label *label) +{ + LABEL_INIT(label, MAGIC_SYSV_SHM); + COUNTER_INC(sysvshm_init_label); +} + +COUNTER_DECL(thread_userret); +static void +test_thread_userret(struct thread *td) +{ + + COUNTER_INC(thread_userret); +} + +COUNTER_DECL(vnode_associate_extattr); +static int +test_vnode_associate_extattr(struct mount *mp, struct label *mplabel, + struct vnode *vp, struct label *vplabel) +{ + + LABEL_CHECK(mplabel, MAGIC_MOUNT); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_associate_extattr); + + return (0); +} + +COUNTER_DECL(vnode_associate_singlelabel); +static void +test_vnode_associate_singlelabel(struct mount *mp, struct label *mplabel, + struct vnode *vp, struct label *vplabel) +{ + + LABEL_CHECK(mplabel, MAGIC_MOUNT); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_associate_singlelabel); +} + +COUNTER_DECL(vnode_check_access); +static int +test_vnode_check_access(struct ucred *cred, struct vnode *vp, + struct label *vplabel, accmode_t accmode) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_access); + + return (0); +} + +COUNTER_DECL(vnode_check_chdir); +static int +test_vnode_check_chdir(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(dvplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_chdir); + + return (0); +} + +COUNTER_DECL(vnode_check_chroot); +static int +test_vnode_check_chroot(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(dvplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_chroot); + + return (0); +} + +COUNTER_DECL(vnode_check_create); +static int +test_vnode_check_create(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct componentname *cnp, struct vattr *vap) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(dvplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_create); + + return (0); +} + +COUNTER_DECL(vnode_check_deleteacl); +static int +test_vnode_check_deleteacl(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_deleteacl); + + return (0); +} + +COUNTER_DECL(vnode_check_deleteextattr); +static int +test_vnode_check_deleteextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace, const char *name) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_deleteextattr); + + return (0); +} + +COUNTER_DECL(vnode_check_exec); +static int +test_vnode_check_exec(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct image_params *imgp, + struct label *execlabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + LABEL_CHECK(execlabel, MAGIC_CRED); + COUNTER_INC(vnode_check_exec); + + return (0); +} + +COUNTER_DECL(vnode_check_getacl); +static int +test_vnode_check_getacl(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_getacl); + + return (0); +} + +COUNTER_DECL(vnode_check_getextattr); +static int +test_vnode_check_getextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace, const char *name, + struct uio *uio) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_getextattr); + + return (0); +} + +COUNTER_DECL(vnode_check_link); +static int +test_vnode_check_link(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(dvplabel, MAGIC_VNODE); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_link); + + return (0); +} + +COUNTER_DECL(vnode_check_listextattr); +static int +test_vnode_check_listextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_listextattr); + + return (0); +} + +COUNTER_DECL(vnode_check_lookup); +static int +test_vnode_check_lookup(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct componentname *cnp) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(dvplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_lookup); + + return (0); +} + +COUNTER_DECL(vnode_check_mmap); +static int +test_vnode_check_mmap(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int prot, int flags) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_mmap); + + return (0); +} + +COUNTER_DECL(vnode_check_open); +static int +test_vnode_check_open(struct ucred *cred, struct vnode *vp, + struct label *vplabel, accmode_t accmode) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_open); + + return (0); +} + +COUNTER_DECL(vnode_check_poll); +static int +test_vnode_check_poll(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp, struct label *vplabel) +{ + + LABEL_CHECK(active_cred->cr_label, MAGIC_CRED); + if (file_cred != NULL) + LABEL_CHECK(file_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_poll); + + return (0); +} + +COUNTER_DECL(vnode_check_read); +static int +test_vnode_check_read(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp, struct label *vplabel) +{ + + LABEL_CHECK(active_cred->cr_label, MAGIC_CRED); + if (file_cred != NULL) + LABEL_CHECK(file_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_read); + + return (0); +} + +COUNTER_DECL(vnode_check_readdir); +static int +test_vnode_check_readdir(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(dvplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_readdir); + + return (0); +} + +COUNTER_DECL(vnode_check_readlink); +static int +test_vnode_check_readlink(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_readlink); + + return (0); +} + +COUNTER_DECL(vnode_check_relabel); +static int +test_vnode_check_relabel(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct label *newlabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + LABEL_CHECK(newlabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_relabel); + + return (0); +} + +COUNTER_DECL(vnode_check_rename_from); +static int +test_vnode_check_rename_from(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(dvplabel, MAGIC_VNODE); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_rename_from); + + return (0); +} + +COUNTER_DECL(vnode_check_rename_to); +static int +test_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + int samedir, struct componentname *cnp) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(dvplabel, MAGIC_VNODE); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_rename_to); + + return (0); +} + +COUNTER_DECL(vnode_check_revoke); +static int +test_vnode_check_revoke(struct ucred *cred, struct vnode *vp, + struct label *vplabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_revoke); + + return (0); +} + +COUNTER_DECL(vnode_check_setacl); +static int +test_vnode_check_setacl(struct ucred *cred, struct vnode *vp, + struct label *vplabel, acl_type_t type, struct acl *acl) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_setacl); + + return (0); +} + +COUNTER_DECL(vnode_check_setextattr); +static int +test_vnode_check_setextattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int attrnamespace, const char *name, + struct uio *uio) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_setextattr); + + return (0); +} + +COUNTER_DECL(vnode_check_setflags); +static int +test_vnode_check_setflags(struct ucred *cred, struct vnode *vp, + struct label *vplabel, u_long flags) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_setflags); + + return (0); +} + +COUNTER_DECL(vnode_check_setmode); +static int +test_vnode_check_setmode(struct ucred *cred, struct vnode *vp, + struct label *vplabel, mode_t mode) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_setmode); + + return (0); +} + +COUNTER_DECL(vnode_check_setowner); +static int +test_vnode_check_setowner(struct ucred *cred, struct vnode *vp, + struct label *vplabel, uid_t uid, gid_t gid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_setowner); + + return (0); +} + +COUNTER_DECL(vnode_check_setutimes); +static int +test_vnode_check_setutimes(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct timespec atime, struct timespec mtime) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_setutimes); + + return (0); +} + +COUNTER_DECL(vnode_check_stat); +static int +test_vnode_check_stat(struct ucred *active_cred, struct ucred *file_cred, + struct vnode *vp, struct label *vplabel) +{ + + LABEL_CHECK(active_cred->cr_label, MAGIC_CRED); + if (file_cred != NULL) + LABEL_CHECK(file_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_stat); + + return (0); +} + +COUNTER_DECL(vnode_check_unlink); +static int +test_vnode_check_unlink(struct ucred *cred, struct vnode *dvp, + struct label *dvplabel, struct vnode *vp, struct label *vplabel, + struct componentname *cnp) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(dvplabel, MAGIC_VNODE); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_unlink); + + return (0); +} + +COUNTER_DECL(vnode_check_write); +static int +test_vnode_check_write(struct ucred *active_cred, + struct ucred *file_cred, struct vnode *vp, struct label *vplabel) +{ + + LABEL_CHECK(active_cred->cr_label, MAGIC_CRED); + if (file_cred != NULL) + LABEL_CHECK(file_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + COUNTER_INC(vnode_check_write); + + return (0); +} + +COUNTER_DECL(vnode_copy_label); +static void +test_vnode_copy_label(struct label *src, struct label *dest) +{ + + LABEL_CHECK(src, MAGIC_VNODE); + LABEL_CHECK(dest, MAGIC_VNODE); + COUNTER_INC(vnode_copy_label); +} + +COUNTER_DECL(vnode_create_extattr); +static int +test_vnode_create_extattr(struct ucred *cred, struct mount *mp, + struct label *mplabel, struct vnode *dvp, struct label *dvplabel, + struct vnode *vp, struct label *vplabel, struct componentname *cnp) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(mplabel, MAGIC_MOUNT); + LABEL_CHECK(dvplabel, MAGIC_VNODE); + COUNTER_INC(vnode_create_extattr); + + return (0); +} + +COUNTER_DECL(vnode_destroy_label); +static void +test_vnode_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_VNODE); + COUNTER_INC(vnode_destroy_label); +} + +COUNTER_DECL(vnode_execve_transition); +static void +test_vnode_execve_transition(struct ucred *old, struct ucred *new, + struct vnode *vp, struct label *filelabel, + struct label *interpvplabel, struct image_params *imgp, + struct label *execlabel) +{ + + LABEL_CHECK(old->cr_label, MAGIC_CRED); + LABEL_CHECK(new->cr_label, MAGIC_CRED); + LABEL_CHECK(filelabel, MAGIC_VNODE); + LABEL_CHECK(interpvplabel, MAGIC_VNODE); + LABEL_CHECK(execlabel, MAGIC_CRED); + COUNTER_INC(vnode_execve_transition); +} + +COUNTER_DECL(vnode_execve_will_transition); +static int +test_vnode_execve_will_transition(struct ucred *old, struct vnode *vp, + struct label *filelabel, struct label *interpvplabel, + struct image_params *imgp, struct label *execlabel) +{ + + LABEL_CHECK(old->cr_label, MAGIC_CRED); + LABEL_CHECK(filelabel, MAGIC_VNODE); + LABEL_CHECK(interpvplabel, MAGIC_VNODE); + LABEL_CHECK(execlabel, MAGIC_CRED); + COUNTER_INC(vnode_execve_will_transition); + + return (0); +} + +COUNTER_DECL(vnode_externalize_label); +static int +test_vnode_externalize_label(struct label *label, char *element_name, + struct sbuf *sb, int *claimed) +{ + + LABEL_CHECK(label, MAGIC_VNODE); + COUNTER_INC(vnode_externalize_label); + + return (0); +} + +COUNTER_DECL(vnode_init_label); +static void +test_vnode_init_label(struct label *label) +{ + + LABEL_INIT(label, MAGIC_VNODE); + COUNTER_INC(vnode_init_label); +} + +COUNTER_DECL(vnode_internalize_label); +static int +test_vnode_internalize_label(struct label *label, char *element_name, + char *element_data, int *claimed) +{ + + LABEL_CHECK(label, MAGIC_VNODE); + COUNTER_INC(vnode_internalize_label); + + return (0); +} + +COUNTER_DECL(vnode_relabel); +static void +test_vnode_relabel(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct label *label) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + LABEL_CHECK(label, MAGIC_VNODE); + COUNTER_INC(vnode_relabel); +} + +COUNTER_DECL(vnode_setlabel_extattr); +static int +test_vnode_setlabel_extattr(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct label *intlabel) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(vplabel, MAGIC_VNODE); + LABEL_CHECK(intlabel, MAGIC_VNODE); + COUNTER_INC(vnode_setlabel_extattr); + + return (0); +} + +static struct mac_policy_ops test_ops = +{ + .mpo_bpfdesc_check_receive = test_bpfdesc_check_receive, + .mpo_bpfdesc_create = test_bpfdesc_create, + .mpo_bpfdesc_create_mbuf = test_bpfdesc_create_mbuf, + .mpo_bpfdesc_destroy_label = test_bpfdesc_destroy_label, + .mpo_bpfdesc_init_label = test_bpfdesc_init_label, + + .mpo_cred_check_relabel = test_cred_check_relabel, + .mpo_cred_check_visible = test_cred_check_visible, + .mpo_cred_copy_label = test_cred_copy_label, + .mpo_cred_create_init = test_cred_create_init, + .mpo_cred_create_swapper = test_cred_create_swapper, + .mpo_cred_destroy_label = test_cred_destroy_label, + .mpo_cred_externalize_label = test_cred_externalize_label, + .mpo_cred_init_label = test_cred_init_label, + .mpo_cred_internalize_label = test_cred_internalize_label, + .mpo_cred_relabel = test_cred_relabel, + + .mpo_devfs_create_device = test_devfs_create_device, + .mpo_devfs_create_directory = test_devfs_create_directory, + .mpo_devfs_create_symlink = test_devfs_create_symlink, + .mpo_devfs_destroy_label = test_devfs_destroy_label, + .mpo_devfs_init_label = test_devfs_init_label, + .mpo_devfs_update = test_devfs_update, + .mpo_devfs_vnode_associate = test_devfs_vnode_associate, + + .mpo_ifnet_check_relabel = test_ifnet_check_relabel, + .mpo_ifnet_check_transmit = test_ifnet_check_transmit, + .mpo_ifnet_copy_label = test_ifnet_copy_label, + .mpo_ifnet_create = test_ifnet_create, + .mpo_ifnet_create_mbuf = test_ifnet_create_mbuf, + .mpo_ifnet_destroy_label = test_ifnet_destroy_label, + .mpo_ifnet_externalize_label = test_ifnet_externalize_label, + .mpo_ifnet_init_label = test_ifnet_init_label, + .mpo_ifnet_internalize_label = test_ifnet_internalize_label, + .mpo_ifnet_relabel = test_ifnet_relabel, + + .mpo_syncache_destroy_label = test_syncache_destroy_label, + .mpo_syncache_init_label = test_syncache_init_label, + + .mpo_sysvmsg_destroy_label = test_sysvmsg_destroy_label, + .mpo_sysvmsg_init_label = test_sysvmsg_init_label, + + .mpo_sysvmsq_destroy_label = test_sysvmsq_destroy_label, + .mpo_sysvmsq_init_label = test_sysvmsq_init_label, + + .mpo_sysvsem_destroy_label = test_sysvsem_destroy_label, + .mpo_sysvsem_init_label = test_sysvsem_init_label, + + .mpo_sysvshm_destroy_label = test_sysvshm_destroy_label, + .mpo_sysvshm_init_label = test_sysvshm_init_label, + + .mpo_inpcb_check_deliver = test_inpcb_check_deliver, + .mpo_inpcb_check_visible = test_inpcb_check_visible, + .mpo_inpcb_create = test_inpcb_create, + .mpo_inpcb_create_mbuf = test_inpcb_create_mbuf, + .mpo_inpcb_destroy_label = test_inpcb_destroy_label, + .mpo_inpcb_init_label = test_inpcb_init_label, + .mpo_inpcb_sosetlabel = test_inpcb_sosetlabel, + + .mpo_ip6q_create = test_ip6q_create, + .mpo_ip6q_destroy_label = test_ip6q_destroy_label, + .mpo_ip6q_init_label = test_ip6q_init_label, + .mpo_ip6q_match = test_ip6q_match, + .mpo_ip6q_reassemble = test_ip6q_reassemble, + .mpo_ip6q_update = test_ip6q_update, + + .mpo_ipq_create = test_ipq_create, + .mpo_ipq_destroy_label = test_ipq_destroy_label, + .mpo_ipq_init_label = test_ipq_init_label, + .mpo_ipq_match = test_ipq_match, + .mpo_ipq_reassemble = test_ipq_reassemble, + .mpo_ipq_update = test_ipq_update, + + .mpo_kenv_check_dump = test_kenv_check_dump, + .mpo_kenv_check_get = test_kenv_check_get, + .mpo_kenv_check_set = test_kenv_check_set, + .mpo_kenv_check_unset = test_kenv_check_unset, + + .mpo_kld_check_load = test_kld_check_load, + .mpo_kld_check_stat = test_kld_check_stat, + + .mpo_mbuf_copy_label = test_mbuf_copy_label, + .mpo_mbuf_destroy_label = test_mbuf_destroy_label, + .mpo_mbuf_init_label = test_mbuf_init_label, + + .mpo_mount_check_stat = test_mount_check_stat, + .mpo_mount_create = test_mount_create, + .mpo_mount_destroy_label = test_mount_destroy_label, + .mpo_mount_init_label = test_mount_init_label, + + .mpo_netatalk_aarp_send = test_netatalk_aarp_send, + + .mpo_netinet_arp_send = test_netinet_arp_send, + .mpo_netinet_fragment = test_netinet_fragment, + .mpo_netinet_icmp_reply = test_netinet_icmp_reply, + .mpo_netinet_icmp_replyinplace = test_netinet_icmp_replyinplace, + .mpo_netinet_igmp_send = test_netinet_igmp_send, + .mpo_netinet_tcp_reply = test_netinet_tcp_reply, + + .mpo_netinet6_nd6_send = test_netinet6_nd6_send, + + .mpo_pipe_check_ioctl = test_pipe_check_ioctl, + .mpo_pipe_check_poll = test_pipe_check_poll, + .mpo_pipe_check_read = test_pipe_check_read, + .mpo_pipe_check_relabel = test_pipe_check_relabel, + .mpo_pipe_check_stat = test_pipe_check_stat, + .mpo_pipe_check_write = test_pipe_check_write, + .mpo_pipe_copy_label = test_pipe_copy_label, + .mpo_pipe_create = test_pipe_create, + .mpo_pipe_destroy_label = test_pipe_destroy_label, + .mpo_pipe_externalize_label = test_pipe_externalize_label, + .mpo_pipe_init_label = test_pipe_init_label, + .mpo_pipe_internalize_label = test_pipe_internalize_label, + .mpo_pipe_relabel = test_pipe_relabel, + + .mpo_posixsem_check_getvalue = test_posixsem_check_getvalue, + .mpo_posixsem_check_open = test_posixsem_check_open, + .mpo_posixsem_check_post = test_posixsem_check_post, + .mpo_posixsem_check_stat = test_posixsem_check_stat, + .mpo_posixsem_check_unlink = test_posixsem_check_unlink, + .mpo_posixsem_check_wait = test_posixsem_check_wait, + .mpo_posixsem_create = test_posixsem_create, + .mpo_posixsem_destroy_label = test_posixsem_destroy_label, + .mpo_posixsem_init_label = test_posixsem_init_label, + + .mpo_posixshm_check_mmap = test_posixshm_check_mmap, + .mpo_posixshm_check_open = test_posixshm_check_open, + .mpo_posixshm_check_stat = test_posixshm_check_stat, + .mpo_posixshm_check_truncate = test_posixshm_check_truncate, + .mpo_posixshm_check_unlink = test_posixshm_check_unlink, + .mpo_posixshm_create = test_posixshm_create, + .mpo_posixshm_destroy_label = test_posixshm_destroy_label, + .mpo_posixshm_init_label = test_posixshm_init_label, + + .mpo_proc_check_debug = test_proc_check_debug, + .mpo_proc_check_sched = test_proc_check_sched, + .mpo_proc_check_setaudit = test_proc_check_setaudit, + .mpo_proc_check_setaudit_addr = test_proc_check_setaudit_addr, + .mpo_proc_check_setauid = test_proc_check_setauid, + .mpo_proc_check_seteuid = test_proc_check_seteuid, + .mpo_proc_check_setegid = test_proc_check_setegid, + .mpo_proc_check_setgid = test_proc_check_setgid, + .mpo_proc_check_setgroups = test_proc_check_setgroups, + .mpo_proc_check_setregid = test_proc_check_setregid, + .mpo_proc_check_setresgid = test_proc_check_setresgid, + .mpo_proc_check_setresuid = test_proc_check_setresuid, + .mpo_proc_check_setreuid = test_proc_check_setreuid, + .mpo_proc_check_setuid = test_proc_check_setuid, + .mpo_proc_check_signal = test_proc_check_signal, + .mpo_proc_check_wait = test_proc_check_wait, + .mpo_proc_destroy_label = test_proc_destroy_label, + .mpo_proc_init_label = test_proc_init_label, + + .mpo_socket_check_accept = test_socket_check_accept, + .mpo_socket_check_bind = test_socket_check_bind, + .mpo_socket_check_connect = test_socket_check_connect, + .mpo_socket_check_deliver = test_socket_check_deliver, + .mpo_socket_check_listen = test_socket_check_listen, + .mpo_socket_check_poll = test_socket_check_poll, + .mpo_socket_check_receive = test_socket_check_receive, + .mpo_socket_check_relabel = test_socket_check_relabel, + .mpo_socket_check_send = test_socket_check_send, + .mpo_socket_check_stat = test_socket_check_stat, + .mpo_socket_check_visible = test_socket_check_visible, + .mpo_socket_copy_label = test_socket_copy_label, + .mpo_socket_create = test_socket_create, + .mpo_socket_create_mbuf = test_socket_create_mbuf, + .mpo_socket_destroy_label = test_socket_destroy_label, + .mpo_socket_externalize_label = test_socket_externalize_label, + .mpo_socket_init_label = test_socket_init_label, + .mpo_socket_internalize_label = test_socket_internalize_label, + .mpo_socket_newconn = test_socket_newconn, + .mpo_socket_relabel = test_socket_relabel, + + .mpo_socketpeer_destroy_label = test_socketpeer_destroy_label, + .mpo_socketpeer_externalize_label = test_socketpeer_externalize_label, + .mpo_socketpeer_init_label = test_socketpeer_init_label, + .mpo_socketpeer_set_from_mbuf = test_socketpeer_set_from_mbuf, + .mpo_socketpeer_set_from_socket = test_socketpeer_set_from_socket, + + .mpo_syncache_create = test_syncache_create, + .mpo_syncache_create_mbuf = test_syncache_create_mbuf, + + .mpo_system_check_acct = test_system_check_acct, + .mpo_system_check_audit = test_system_check_audit, + .mpo_system_check_auditctl = test_system_check_auditctl, + .mpo_system_check_auditon = test_system_check_auditon, + .mpo_system_check_reboot = test_system_check_reboot, + .mpo_system_check_swapoff = test_system_check_swapoff, + .mpo_system_check_swapon = test_system_check_swapon, + .mpo_system_check_sysctl = test_system_check_sysctl, + + .mpo_vnode_check_access = test_vnode_check_access, + .mpo_sysvmsg_cleanup = test_sysvmsg_cleanup, + .mpo_sysvmsg_create = test_sysvmsg_create, + + .mpo_sysvmsq_check_msgmsq = test_sysvmsq_check_msgmsq, + .mpo_sysvmsq_check_msgrcv = test_sysvmsq_check_msgrcv, + .mpo_sysvmsq_check_msgrmid = test_sysvmsq_check_msgrmid, + .mpo_sysvmsq_check_msqget = test_sysvmsq_check_msqget, + .mpo_sysvmsq_check_msqsnd = test_sysvmsq_check_msqsnd, + .mpo_sysvmsq_check_msqrcv = test_sysvmsq_check_msqrcv, + .mpo_sysvmsq_check_msqctl = test_sysvmsq_check_msqctl, + .mpo_sysvmsq_cleanup = test_sysvmsq_cleanup, + .mpo_sysvmsq_create = test_sysvmsq_create, + + .mpo_sysvsem_check_semctl = test_sysvsem_check_semctl, + .mpo_sysvsem_check_semget = test_sysvsem_check_semget, + .mpo_sysvsem_check_semop = test_sysvsem_check_semop, + .mpo_sysvsem_cleanup = test_sysvsem_cleanup, + .mpo_sysvsem_create = test_sysvsem_create, + + .mpo_sysvshm_check_shmat = test_sysvshm_check_shmat, + .mpo_sysvshm_check_shmctl = test_sysvshm_check_shmctl, + .mpo_sysvshm_check_shmdt = test_sysvshm_check_shmdt, + .mpo_sysvshm_check_shmget = test_sysvshm_check_shmget, + .mpo_sysvshm_cleanup = test_sysvshm_cleanup, + .mpo_sysvshm_create = test_sysvshm_create, + + .mpo_thread_userret = test_thread_userret, + + .mpo_vnode_associate_extattr = test_vnode_associate_extattr, + .mpo_vnode_associate_singlelabel = test_vnode_associate_singlelabel, + .mpo_vnode_check_chdir = test_vnode_check_chdir, + .mpo_vnode_check_chroot = test_vnode_check_chroot, + .mpo_vnode_check_create = test_vnode_check_create, + .mpo_vnode_check_deleteacl = test_vnode_check_deleteacl, + .mpo_vnode_check_deleteextattr = test_vnode_check_deleteextattr, + .mpo_vnode_check_exec = test_vnode_check_exec, + .mpo_vnode_check_getacl = test_vnode_check_getacl, + .mpo_vnode_check_getextattr = test_vnode_check_getextattr, + .mpo_vnode_check_link = test_vnode_check_link, + .mpo_vnode_check_listextattr = test_vnode_check_listextattr, + .mpo_vnode_check_lookup = test_vnode_check_lookup, + .mpo_vnode_check_mmap = test_vnode_check_mmap, + .mpo_vnode_check_open = test_vnode_check_open, + .mpo_vnode_check_poll = test_vnode_check_poll, + .mpo_vnode_check_read = test_vnode_check_read, + .mpo_vnode_check_readdir = test_vnode_check_readdir, + .mpo_vnode_check_readlink = test_vnode_check_readlink, + .mpo_vnode_check_relabel = test_vnode_check_relabel, + .mpo_vnode_check_rename_from = test_vnode_check_rename_from, + .mpo_vnode_check_rename_to = test_vnode_check_rename_to, + .mpo_vnode_check_revoke = test_vnode_check_revoke, + .mpo_vnode_check_setacl = test_vnode_check_setacl, + .mpo_vnode_check_setextattr = test_vnode_check_setextattr, + .mpo_vnode_check_setflags = test_vnode_check_setflags, + .mpo_vnode_check_setmode = test_vnode_check_setmode, + .mpo_vnode_check_setowner = test_vnode_check_setowner, + .mpo_vnode_check_setutimes = test_vnode_check_setutimes, + .mpo_vnode_check_stat = test_vnode_check_stat, + .mpo_vnode_check_unlink = test_vnode_check_unlink, + .mpo_vnode_check_write = test_vnode_check_write, + .mpo_vnode_copy_label = test_vnode_copy_label, + .mpo_vnode_create_extattr = test_vnode_create_extattr, + .mpo_vnode_destroy_label = test_vnode_destroy_label, + .mpo_vnode_execve_transition = test_vnode_execve_transition, + .mpo_vnode_execve_will_transition = test_vnode_execve_will_transition, + .mpo_vnode_externalize_label = test_vnode_externalize_label, + .mpo_vnode_init_label = test_vnode_init_label, + .mpo_vnode_internalize_label = test_vnode_internalize_label, + .mpo_vnode_relabel = test_vnode_relabel, + .mpo_vnode_setlabel_extattr = test_vnode_setlabel_extattr, +}; + +#define TEST_OBJECTS (MPC_OBJECT_CRED | \ + MPC_OBJECT_PROC | \ + MPC_OBJECT_VNODE | \ + MPC_OBJECT_INPCB | \ + MPC_OBJECT_SOCKET | \ + MPC_OBJECT_DEVFS | \ + MPC_OBJECT_MBUF | \ + MPC_OBJECT_IPQ | \ + MPC_OBJECT_IFNET | \ + MPC_OBJECT_BPFDESC | \ + MPC_OBJECT_PIPE | \ + MPC_OBJECT_MOUNT | \ + MPC_OBJECT_POSIXSEM | \ + MPC_OBJECT_POSIXSHM | \ + MPC_OBJECT_SYSVMSG | \ + MPC_OBJECT_SYSVMSQ | \ + MPC_OBJECT_SYSVSEM | \ + MPC_OBJECT_SYSVSHM | \ + MPC_OBJECT_SYNCACHE) + +MAC_POLICY_SET(&test_ops, mac_test, "TrustedBSD MAC/Test", + MPC_LOADTIME_FLAG_UNLOADOK, &test_slot, TEST_OBJECTS); |