diff options
Diffstat (limited to 'sys/security')
58 files changed, 37958 insertions, 0 deletions
diff --git a/sys/security/audit/audit.c b/sys/security/audit/audit.c new file mode 100644 index 0000000..cb3406d --- /dev/null +++ b/sys/security/audit/audit.c @@ -0,0 +1,715 @@ +/*- + * 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/jail.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> + +FEATURE(audit, "BSM audit support"); + +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"); +MALLOC_DEFINE(M_AUDITGIDSET, "audit_gidset", "Audit GID set storage"); + +static 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; +int audit_q_len; +int 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; + struct prison *pr; + + 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; + /* + * If this process is jailed, make sure we capture the name of the + * jail so we can use it to generate a zonename token when we covert + * this record to BSM. + */ + if (jailed(cred)) { + pr = cred->cr_prison; + (void) strlcpy(ar->k_ar.ar_jailname, pr->pr_name, + sizeof(ar->k_ar.ar_jailname)); + } else + ar->k_ar.ar_jailname[0] = '\0'; + 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); + if (ar->k_ar.ar_arg_groups.gidset != NULL) + free(ar->k_ar.ar_arg_groups.gidset, M_AUDITGIDSET); +} + +/* + * 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; + + /* + * syscalls.master sometimes contains a prototype event number, which + * we will transform into a more specific event number now that we + * have more complete information gathered during the system call. + */ + switch(ar->k_ar.ar_event) { + case AUE_OPEN_RWTC: + ar->k_ar.ar_event = audit_flags_and_error_to_openevent( + ar->k_ar.ar_arg_fflags, error); + break; + + case AUE_OPENAT_RWTC: + ar->k_ar.ar_event = audit_flags_and_error_to_openatevent( + 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")); + KASSERT((td->td_pflags & TDP_AUDITREC) == 0, + ("audit_syscall_enter: TDP_AUDITREC set")); + + /* + * 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); + if (td->td_ar != NULL) + td->td_pflags |= TDP_AUDITREC; + } else if (audit_pipe_preselect(auid, event, class, AU_PRS_BOTH, 0)) { + td->td_ar = audit_new(event, td); + if (td->td_ar != NULL) + td->td_pflags |= TDP_AUDITREC; + } 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; + td->td_pflags &= ~TDP_AUDITREC; +} + +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")); + KASSERT((td->td_pflags & TDP_AUDITREC) == 0, + ("audit_thread_free: TDP_AUDITREC set")); +} + +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, AT_FDCWD, 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..dd55875 --- /dev/null +++ b/sys/security/audit/audit.h @@ -0,0 +1,385 @@ +/*- + * 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; + +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_atfd1(int atfd); +void audit_arg_atfd2(int atfd); +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, int dirfd, 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_upath1(struct thread *td, int dirfd, char *upath); +void audit_arg_upath2(struct thread *td, int dirfd, char *upath); +void audit_arg_vnode1(struct vnode *vp); +void audit_arg_vnode2(struct vnode *vp); +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_arg_rights(cap_rights_t rights); +void audit_arg_fcntl_rights(uint32_t fcntlrights); +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 macros to wrap the audit_arg_* calls by checking the global + * audit_enabled flag before performing the actual call. + */ +#define AUDITING_TD(td) ((td)->td_pflags & TDP_AUDITREC) + +#define AUDIT_ARG_ADDR(addr) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_addr((addr)); \ +} while (0) + +#define AUDIT_ARG_ARGV(argv, argc, length) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_argv((argv), (argc), (length)); \ +} while (0) + +#define AUDIT_ARG_ATFD1(atfd) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_atfd1((atfd)); \ +} while (0) + +#define AUDIT_ARG_ATFD2(atfd) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_atfd2((atfd)); \ +} while (0) + +#define AUDIT_ARG_AUDITON(udata) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_auditon((udata)); \ +} while (0) + +#define AUDIT_ARG_CMD(cmd) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_cmd((cmd)); \ +} while (0) + +#define AUDIT_ARG_DEV(dev) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_dev((dev)); \ +} while (0) + +#define AUDIT_ARG_EGID(egid) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_egid((egid)); \ +} while (0) + +#define AUDIT_ARG_ENVV(envv, envc, length) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_envv((envv), (envc), (length)); \ +} while (0) + +#define AUDIT_ARG_EXIT(status, retval) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_exit((status), (retval)); \ +} while (0) + +#define AUDIT_ARG_EUID(euid) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_euid((euid)); \ +} while (0) + +#define AUDIT_ARG_FD(fd) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_fd((fd)); \ +} while (0) + +#define AUDIT_ARG_FILE(p, fp) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_file((p), (fp)); \ +} while (0) + +#define AUDIT_ARG_FFLAGS(fflags) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_fflags((fflags)); \ +} while (0) + +#define AUDIT_ARG_GID(gid) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_gid((gid)); \ +} while (0) + +#define AUDIT_ARG_GROUPSET(gidset, gidset_size) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_groupset((gidset), (gidset_size)); \ +} while (0) + +#define AUDIT_ARG_MODE(mode) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_mode((mode)); \ +} while (0) + +#define AUDIT_ARG_OWNER(uid, gid) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_owner((uid), (gid)); \ +} while (0) + +#define AUDIT_ARG_PID(pid) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_pid((pid)); \ +} while (0) + +#define AUDIT_ARG_PROCESS(p) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_process((p)); \ +} while (0) + +#define AUDIT_ARG_RGID(rgid) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_rgid((rgid)); \ +} while (0) + +#define AUDIT_ARG_RIGHTS(rights) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_rights((rights)); \ +} while (0) + +#define AUDIT_ARG_FCNTL_RIGHTS(fcntlrights) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_fcntl_rights((fcntlrights)); \ +} while (0) + +#define AUDIT_ARG_RUID(ruid) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_ruid((ruid)); \ +} while (0) + +#define AUDIT_ARG_SIGNUM(signum) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_signum((signum)); \ +} while (0) + +#define AUDIT_ARG_SGID(sgid) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_sgid((sgid)); \ +} while (0) + +#define AUDIT_ARG_SOCKET(sodomain, sotype, soprotocol) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_socket((sodomain), (sotype), (soprotocol)); \ +} while (0) + +#define AUDIT_ARG_SOCKADDR(td, dirfd, sa) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_sockaddr((td), (dirfd), (sa)); \ +} while (0) + +#define AUDIT_ARG_SUID(suid) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_suid((suid)); \ +} while (0) + +#define AUDIT_ARG_TEXT(text) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_text((text)); \ +} while (0) + +#define AUDIT_ARG_UID(uid) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_uid((uid)); \ +} while (0) + +#define AUDIT_ARG_UPATH1(td, dirfd, upath) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_upath1((td), (dirfd), (upath)); \ +} while (0) + +#define AUDIT_ARG_UPATH2(td, dirfd, upath) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_upath2((td), (dirfd), (upath)); \ +} while (0) + +#define AUDIT_ARG_VALUE(value) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_value((value)); \ +} while (0) + +#define AUDIT_ARG_VNODE1(vp) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_vnode1((vp)); \ +} while (0) + +#define AUDIT_ARG_VNODE2(vp) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_vnode2((vp)); \ +} 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 + * we have a audit record on the thread. Audit records can persist after + * auditing is disabled, so we don't just check audit_enabled here. + */ +#define AUDIT_SYSCALL_EXIT(error, td) do { \ + if (td->td_pflags & TDP_AUDITREC) \ + audit_syscall_exit(error, td); \ +} while (0) + +/* + * A Macro to wrap the audit_sysclose() function. + */ +#define AUDIT_SYSCLOSE(td, fd) do { \ + if (td->td_pflags & TDP_AUDITREC) \ + audit_sysclose(td, fd); \ +} while (0) + +#else /* !AUDIT */ + +#define AUDIT_ARG_ADDR(addr) +#define AUDIT_ARG_ARGV(argv, argc, length) +#define AUDIT_ARG_ATFD1(atfd) +#define AUDIT_ARG_ATFD2(atfd) +#define AUDIT_ARG_AUDITON(udata) +#define AUDIT_ARG_CMD(cmd) +#define AUDIT_ARG_DEV(dev) +#define AUDIT_ARG_EGID(egid) +#define AUDIT_ARG_ENVV(envv, envc, length) +#define AUDIT_ARG_EXIT(status, retval) +#define AUDIT_ARG_EUID(euid) +#define AUDIT_ARG_FD(fd) +#define AUDIT_ARG_FILE(p, fp) +#define AUDIT_ARG_FFLAGS(fflags) +#define AUDIT_ARG_GID(gid) +#define AUDIT_ARG_GROUPSET(gidset, gidset_size) +#define AUDIT_ARG_MODE(mode) +#define AUDIT_ARG_OWNER(uid, gid) +#define AUDIT_ARG_PID(pid) +#define AUDIT_ARG_PROCESS(p) +#define AUDIT_ARG_RGID(rgid) +#define AUDIT_ARG_RIGHTS(rights) +#define AUDIT_ARG_FCNTL_RIGHTS(fcntlrights) +#define AUDIT_ARG_RUID(ruid) +#define AUDIT_ARG_SIGNUM(signum) +#define AUDIT_ARG_SGID(sgid) +#define AUDIT_ARG_SOCKET(sodomain, sotype, soprotocol) +#define AUDIT_ARG_SOCKADDR(td, dirfd, sa) +#define AUDIT_ARG_SUID(suid) +#define AUDIT_ARG_TEXT(text) +#define AUDIT_ARG_UID(uid) +#define AUDIT_ARG_UPATH1(td, dirfd, upath) +#define AUDIT_ARG_UPATH2(td, dirfd, upath) +#define AUDIT_ARG_VALUE(value) +#define AUDIT_ARG_VNODE1(vp) +#define AUDIT_ARG_VNODE2(vp) + +#define AUDIT_SYSCALL_ENTER(code, td) +#define AUDIT_SYSCALL_EXIT(error, td) + +#define AUDIT_SYSCLOSE(p, fd) + +#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..4927be0 --- /dev/null +++ b/sys/security/audit/audit_arg.c @@ -0,0 +1,917 @@ +/*- + * 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_atfd1(int atfd) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_atfd1 = atfd; + ARG_SET_VALID(ar, ARG_ATFD1); +} + +void +audit_arg_atfd2(int atfd) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_atfd2 = atfd; + ARG_SET_VALID(ar, ARG_ATFD2); +} + +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; + + KASSERT(gidset_size <= ngroups_max + 1, + ("audit_arg_groupset: gidset_size > (kern.ngroups + 1)")); + + ar = currecord(); + if (ar == NULL) + return; + + if (ar->k_ar.ar_arg_groups.gidset == NULL) + ar->k_ar.ar_arg_groups.gidset = malloc( + sizeof(gid_t) * gidset_size, M_AUDITGIDSET, M_WAITOK); + + 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, int dirfd, 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: + if (dirfd != AT_FDCWD) + audit_arg_atfd1(dirfd); + audit_arg_upath1(td, dirfd, + ((struct sockaddr_un *)sa)->sun_path); + 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; + + 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; + vn_lock(vp, LK_SHARED | LK_RETRY); + audit_arg_vnode1(vp); + VOP_UNLOCK(vp, 0); + 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. + */ +static void +audit_arg_upath(struct thread *td, int dirfd, char *upath, char **pathp) +{ + + if (*pathp == NULL) + *pathp = malloc(MAXPATHLEN, M_AUDITPATH, M_WAITOK); + audit_canon_path(td, dirfd, upath, *pathp); +} + +void +audit_arg_upath1(struct thread *td, int dirfd, char *upath) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + audit_arg_upath(td, dirfd, upath, &ar->k_ar.ar_arg_upath1); + ARG_SET_VALID(ar, ARG_UPATH1); +} + +void +audit_arg_upath2(struct thread *td, int dirfd, char *upath) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + audit_arg_upath(td, dirfd, upath, &ar->k_ar.ar_arg_upath2); + ARG_SET_VALID(ar, ARG_UPATH2); +} + +/* + * 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? + */ +static int +audit_arg_vnode(struct vnode *vp, struct vnode_au_info *vnp) +{ + struct vattr vattr; + int error; + + ASSERT_VOP_LOCKED(vp, "audit_arg_vnode"); + + error = VOP_GETATTR(vp, &vattr, curthread->td_ucred); + if (error) { + /* XXX: How to handle this case? */ + return (error); + } + + 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; + return (0); +} + +void +audit_arg_vnode1(struct vnode *vp) +{ + struct kaudit_record *ar; + int error; + + ar = currecord(); + if (ar == NULL) + return; + + ARG_CLEAR_VALID(ar, ARG_VNODE1); + error = audit_arg_vnode(vp, &ar->k_ar.ar_arg_vnode1); + if (error == 0) + ARG_SET_VALID(ar, ARG_VNODE1); +} + +void +audit_arg_vnode2(struct vnode *vp) +{ + struct kaudit_record *ar; + int error; + + ar = currecord(); + if (ar == NULL) + return; + + ARG_CLEAR_VALID(ar, ARG_VNODE2); + error = audit_arg_vnode(vp, &ar->k_ar.ar_arg_vnode2); + if (error == 0) + 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); +} + +void +audit_arg_rights(cap_rights_t rights) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_rights = rights; + ARG_SET_VALID(ar, ARG_RIGHTS); +} + +void +audit_arg_fcntl_rights(uint32_t fcntlrights) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_fcntl_rights = fcntlrights; + ARG_SET_VALID(ar, ARG_FCNTL_RIGHTS); +} + +/* + * 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; + + 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, 0, &fp) != 0) + return; + + vp = fp->f_vnode; + vn_lock(vp, LK_SHARED | LK_RETRY); + audit_arg_vnode1(vp); + VOP_UNLOCK(vp, 0); + 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..03b3c23 --- /dev/null +++ b/sys/security/audit/audit_bsm.c @@ -0,0 +1,1697 @@ +/* + * Copyright (c) 1999-2009 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 ATFD1_TOKENS(argnum) do { \ + if (ARG_IS_VALID(kar, ARG_ATFD1)) { \ + tok = au_to_arg32(argnum, "at fd 1", ar->ar_arg_atfd1); \ + kau_write(rec, tok); \ + } \ +} while (0) + +#define ATFD2_TOKENS(argnum) do { \ + if (ARG_IS_VALID(kar, ARG_ATFD2)) { \ + tok = au_to_arg32(argnum, "at fd 2", ar->ar_arg_atfd2); \ + kau_write(rec, tok); \ + } \ +} while (0) + +#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_ATFD)) { \ + tok = au_to_arg32(1, "at fd", ar->ar_arg_atfd); \ + kau_write(rec, tok); \ + } \ + 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 { \ + 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(namespace_argnum) 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((namespace_argnum), \ + "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) + +/* + * Not all pointer arguments to system calls are of interest, but in some + * cases they reflect delegation of rights, such as mmap(2) followed by + * minherit(2) before execve(2), so do the best we can. + */ +#define ADDR_TOKEN(argnum, argname) do { \ + if (ARG_IS_VALID(kar, ARG_ADDR)) { \ + if (sizeof(void *) == sizeof(uint32_t)) \ + tok = au_to_arg32((argnum), (argname), \ + (uint32_t)(uintptr_t)ar->ar_arg_addr); \ + else \ + tok = au_to_arg64((argnum), (argname), \ + (uint64_t)(uintptr_t)ar->ar_arg_addr); \ + 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; + + tok = au_to_arg32(3, "length", ar->ar_arg_len); + kau_write(rec, tok); + switch (ar->ar_arg_cmd) { + case A_OLDSETPOLICY: + if ((size_t)ar->ar_arg_len == sizeof(int64_t)) { + tok = au_to_arg64(2, "policy", + ar->ar_arg_auditon.au_policy64); + kau_write(rec, tok); + break; + } + /* FALLTHROUGH */ + + case A_SETPOLICY: + tok = au_to_arg32(2, "policy", ar->ar_arg_auditon.au_policy); + 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_OLDSETQCTRL: + if ((size_t)ar->ar_arg_len == sizeof(au_qctrl64_t)) { + tok = au_to_arg64(2, "setqctrl:aq_hiwater", + ar->ar_arg_auditon.au_qctrl64.aq64_hiwater); + kau_write(rec, tok); + tok = au_to_arg64(2, "setqctrl:aq_lowater", + ar->ar_arg_auditon.au_qctrl64.aq64_lowater); + kau_write(rec, tok); + tok = au_to_arg64(2, "setqctrl:aq_bufsz", + ar->ar_arg_auditon.au_qctrl64.aq64_bufsz); + kau_write(rec, tok); + tok = au_to_arg64(2, "setqctrl:aq_delay", + ar->ar_arg_auditon.au_qctrl64.aq64_delay); + kau_write(rec, tok); + tok = au_to_arg64(2, "setqctrl:aq_minfree", + ar->ar_arg_auditon.au_qctrl64.aq64_minfree); + kau_write(rec, tok); + break; + } + /* FALLTHROUGH */ + + case A_SETQCTRL: + tok = au_to_arg32(2, "setqctrl:aq_hiwater", + ar->ar_arg_auditon.au_qctrl.aq_hiwater); + kau_write(rec, tok); + tok = au_to_arg32(2, "setqctrl:aq_lowater", + ar->ar_arg_auditon.au_qctrl.aq_lowater); + kau_write(rec, tok); + tok = au_to_arg32(2, "setqctrl:aq_bufsz", + ar->ar_arg_auditon.au_qctrl.aq_bufsz); + kau_write(rec, tok); + tok = au_to_arg32(2, "setqctrl:aq_delay", + ar->ar_arg_auditon.au_qctrl.aq_delay); + kau_write(rec, tok); + tok = au_to_arg32(2, "setqctrl:aq_minfree", + ar->ar_arg_auditon.au_qctrl.aq_minfree); + kau_write(rec, tok); + break; + + case A_SETUMASK: + tok = au_to_arg32(2, "setumask:as_success", + ar->ar_arg_auditon.au_auinfo.ai_mask.am_success); + kau_write(rec, tok); + tok = au_to_arg32(2, "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(2, "setsmask:as_success", + ar->ar_arg_auditon.au_auinfo.ai_mask.am_success); + kau_write(rec, tok); + tok = au_to_arg32(2, "setsmask:as_failure", + ar->ar_arg_auditon.au_auinfo.ai_mask.am_failure); + kau_write(rec, tok); + break; + + case A_OLDSETCOND: + if ((size_t)ar->ar_arg_len == sizeof(int64_t)) { + tok = au_to_arg64(2, "setcond", + ar->ar_arg_auditon.au_cond64); + kau_write(rec, tok); + break; + } + /* FALLTHROUGH */ + + case A_SETCOND: + tok = au_to_arg32(2, "setcond", ar->ar_arg_auditon.au_cond); + kau_write(rec, tok); + break; + + case A_SETCLASS: + kau_write(rec, tok); + tok = au_to_arg32(2, "setclass:ec_event", + ar->ar_arg_auditon.au_evclass.ec_number); + kau_write(rec, tok); + tok = au_to_arg32(2, "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, *jail_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. If this credential was jailed be sure to + * generate a zonename token. + */ + if (ar->ar_jailname[0] != '\0') + jail_tok = au_to_zonename(ar->ar_jailname); + else + jail_tok = NULL; + 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_BINDAT: + case AUE_CONNECTAT: + ATFD1_TOKENS(1); + if (ARG_IS_VALID(kar, ARG_FD)) { + tok = au_to_arg32(2, "fd", ar->ar_arg_fd); + 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; + } + 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_MODLOAD: + case AUE_MODUNLOAD: + case AUE_MSGSYS: + case AUE_NTP_ADJTIME: + case AUE_PIPE: + case AUE_POSIX_OPENPT: + 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_CHDIR: + case AUE_CHROOT: + case AUE_FSTATAT: + case AUE_FUTIMESAT: + case AUE_GETATTRLIST: + case AUE_JAIL: + case AUE_LUTIMES: + case AUE_NFS_GETFH: + case AUE_LSTAT: + case AUE_LPATHCONF: + case AUE_PATHCONF: + case AUE_READLINK: + case AUE_READLINKAT: + 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_UNLINKAT: + case AUE_UTIMES: + ATFD1_TOKENS(1); + UPATH1_VNODE1_TOKENS; + break; + + case AUE_ACCESS: + case AUE_EACCESS: + case AUE_FACCESSAT: + ATFD1_TOKENS(1); + UPATH1_VNODE1_TOKENS; + if (ARG_IS_VALID(kar, ARG_VALUE)) { + tok = au_to_arg32(2, "mode", ar->ar_arg_value); + kau_write(rec, tok); + } + 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_FCHMODAT: + ATFD1_TOKENS(1); + if (ARG_IS_VALID(kar, ARG_MODE)) { + tok = au_to_arg32(3, "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_FCHOWNAT: + ATFD1_TOKENS(1); + if (ARG_IS_VALID(kar, ARG_UID)) { + tok = au_to_arg32(3, "new file uid", ar->ar_arg_uid); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_GID)) { + tok = au_to_arg32(4, "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(1, "fd", ar->ar_arg_fd); + kau_write(rec, tok); + } + UPATH1_VNODE1_TOKENS; + break; + + case AUE_CLOSEFROM: + if (ARG_IS_VALID(kar, ARG_FD)) { + tok = au_to_arg32(1, "fd", ar->ar_arg_fd); + kau_write(rec, tok); + } + break; + + case AUE_CORE: + if (ARG_IS_VALID(kar, ARG_SIGNUM)) { + tok = au_to_arg32(1, "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(4); + 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(2); + 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(2); + 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: + case AUE_MAC_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_LSEEK: + 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 (ARG_IS_VALID(kar, ARG_CMD)) { + tok = au_to_arg32(2, "cmd", + au_fcntl_cmd_to_bsm(ar->ar_arg_cmd)); + kau_write(rec, tok); + } + if (ar->ar_arg_cmd == F_GETLK || ar->ar_arg_cmd == F_SETLK || + ar->ar_arg_cmd == F_SETLKW) { + 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_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_LINKAT: + case AUE_RENAME: + case AUE_RENAMEAT: + ATFD1_TOKENS(1); + UPATH1_VNODE1_TOKENS; + ATFD2_TOKENS(3); + UPATH2_TOKENS; + break; + + case AUE_LOADSHFILE: + ADDR_TOKEN(4, "base addr"); + UPATH1_VNODE1_TOKENS; + break; + + case AUE_MKDIR: + case AUE_MKDIRAT: + case AUE_MKFIFO: + case AUE_MKFIFOAT: + ATFD1_TOKENS(1); + 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: + case AUE_MKNODAT: + ATFD1_TOKENS(1); + 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: + ADDR_TOKEN(1, "addr"); + 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_NFS_SVC: + if (ARG_IS_VALID(kar, ARG_CMD)) { + tok = au_to_arg32(1, "flags", ar->ar_arg_cmd); + kau_write(rec, tok); + } + break; + + case AUE_UMOUNT: + if (ARG_IS_VALID(kar, ARG_VALUE)) { + tok = au_to_arg32(2, "flags", ar->ar_arg_value); + kau_write(rec, tok); + } + UPATH1_VNODE1_TOKENS; + if (ARG_IS_VALID(kar, ARG_TEXT)) { + tok = au_to_text(ar->ar_arg_text); + kau_write(rec, tok); + } + 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: + ADDR_TOKEN(1, "base addr"); + 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_OPENAT_RC: + case AUE_OPENAT_RTC: + case AUE_OPENAT_RWC: + case AUE_OPENAT_RWTC: + case AUE_OPENAT_WC: + case AUE_OPENAT_WTC: + if (ARG_IS_VALID(kar, ARG_MODE)) { + tok = au_to_arg32(3, "mode", ar->ar_arg_mode); + kau_write(rec, tok); + } + /* FALLTHROUGH */ + + case AUE_OPENAT_R: + case AUE_OPENAT_RT: + case AUE_OPENAT_RW: + case AUE_OPENAT_RWT: + case AUE_OPENAT_W: + case AUE_OPENAT_WT: + if (ARG_IS_VALID(kar, ARG_FFLAGS)) { + tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); + kau_write(rec, tok); + } + ATFD1_TOKENS(1); + 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_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); + } + if (ARG_IS_VALID(kar, ARG_GID)) { + tok = au_to_arg32(3, "gid", ar->ar_arg_gid); + 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, "egid", ar->ar_arg_egid); + kau_write(rec, tok); + } + break; + + case AUE_SETEUID: + if (ARG_IS_VALID(kar, ARG_EUID)) { + tok = au_to_arg32(1, "euid", 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); + } + PROCESS_PID_TOKENS(2); + if (ARG_IS_VALID(kar, ARG_VALUE)) { + tok = au_to_arg32(3, "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: + case AUE_SYMLINKAT: + if (ARG_IS_VALID(kar, ARG_TEXT)) { + tok = au_to_text(ar->ar_arg_text); + kau_write(rec, tok); + } + ATFD1_TOKENS(1); + 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: + PROCESS_PID_TOKENS(1); + if (ARG_IS_VALID(kar, ARG_VALUE)) { + tok = au_to_arg32(3, "options", ar->ar_arg_value); + kau_write(rec, tok); + } + break; + + case AUE_CAP_NEW: + case AUE_CAP_RIGHTS_LIMIT: + /* + * XXXRW/XXXJA: Would be nice to audit socket/etc information. + */ + FD_VNODE1_TOKENS; + if (ARG_IS_VALID(kar, ARG_RIGHTS)) { + tok = au_to_arg64(2, "rights", ar->ar_arg_rights); + kau_write(rec, tok); + } + break; + + case AUE_CAP_FCNTLS_GET: + case AUE_CAP_IOCTLS_GET: + case AUE_CAP_IOCTLS_LIMIT: + case AUE_CAP_RIGHTS_GET: + if (ARG_IS_VALID(kar, ARG_FD)) { + tok = au_to_arg32(1, "fd", ar->ar_arg_fd); + kau_write(rec, tok); + } + break; + + case AUE_CAP_FCNTLS_LIMIT: + FD_VNODE1_TOKENS; + if (ARG_IS_VALID(kar, ARG_FCNTL_RIGHTS)) { + tok = au_to_arg32(2, "fcntlrights", + ar->ar_arg_fcntl_rights); + kau_write(rec, tok); + } + break; + + case AUE_CAP_ENTER: + case AUE_CAP_GETMODE: + 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. + */ + if (jail_tok != NULL) + kau_write(rec, jail_tok); + kau_write(rec, subj_tok); + kau_free(rec); + return (BSM_NOAUDIT); + } + + if (jail_tok != NULL) + kau_write(rec, jail_tok); + kau_write(rec, subj_tok); + tok = au_to_return32(au_errno_to_bsm(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_klib.c b/sys/security/audit/audit_bsm_klib.c new file mode 100644 index 0000000..5f5d58b --- /dev/null +++ b/sys/security/audit/audit_bsm_klib.c @@ -0,0 +1,571 @@ +/* + * Copyright (c) 1999-2009 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) + +struct aue_open_event { + int aoe_flags; + au_event_t aoe_event; +}; + +static const struct aue_open_event aue_open[] = { + { O_RDONLY, AUE_OPEN_R }, + { (O_RDONLY | O_CREAT), AUE_OPEN_RC }, + { (O_RDONLY | O_CREAT | O_TRUNC), AUE_OPEN_RTC }, + { (O_RDONLY | O_TRUNC), AUE_OPEN_RT }, + { O_RDWR, AUE_OPEN_RW }, + { (O_RDWR | O_CREAT), AUE_OPEN_RWC }, + { (O_RDWR | O_CREAT | O_TRUNC), AUE_OPEN_RWTC }, + { (O_RDWR | O_TRUNC), AUE_OPEN_RWT }, + { O_WRONLY, AUE_OPEN_W }, + { (O_WRONLY | O_CREAT), AUE_OPEN_WC }, + { (O_WRONLY | O_CREAT | O_TRUNC), AUE_OPEN_WTC }, + { (O_WRONLY | O_TRUNC), AUE_OPEN_WT }, +}; +static const int aue_open_count = sizeof(aue_open) / sizeof(aue_open[0]); + +static const struct aue_open_event aue_openat[] = { + { O_RDONLY, AUE_OPENAT_R }, + { (O_RDONLY | O_CREAT), AUE_OPENAT_RC }, + { (O_RDONLY | O_CREAT | O_TRUNC), AUE_OPENAT_RTC }, + { (O_RDONLY | O_TRUNC), AUE_OPENAT_RT }, + { O_RDWR, AUE_OPENAT_RW }, + { (O_RDWR | O_CREAT), AUE_OPENAT_RWC }, + { (O_RDWR | O_CREAT | O_TRUNC), AUE_OPENAT_RWTC }, + { (O_RDWR | O_TRUNC), AUE_OPENAT_RWT }, + { O_WRONLY, AUE_OPENAT_W }, + { (O_WRONLY | O_CREAT), AUE_OPENAT_WC }, + { (O_WRONLY | O_CREAT | O_TRUNC), AUE_OPENAT_WTC }, + { (O_WRONLY | O_TRUNC), AUE_OPENAT_WT }, +}; +static const int aue_openat_count = sizeof(aue_openat) / sizeof(aue_openat[0]); + +/* + * 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) +{ + int i; + + /* + * Need to check only those flags we care about. + */ + oflags = oflags & (O_RDONLY | O_CREAT | O_TRUNC | O_RDWR | O_WRONLY); + for (i = 0; i < aue_open_count; i++) { + if (aue_open[i].aoe_flags == oflags) + return (aue_open[i].aoe_event); + } + return (AUE_OPEN); +} + +au_event_t +audit_flags_and_error_to_openatevent(int oflags, int error) +{ + int i; + + /* + * Need to check only those flags we care about. + */ + oflags = oflags & (O_RDONLY | O_CREAT | O_TRUNC | O_RDWR | O_WRONLY); + for (i = 0; i < aue_openat_count; i++) { + if (aue_openat[i].aoe_flags == oflags) + return (aue_openat[i].aoe_event); + } + return (AUE_OPENAT); +} + +/* + * Convert a MSGCTL command to a specific event. + */ +au_event_t +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. + */ +au_event_t +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. + */ +au_event_t +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, int dirfd, char *path, char *cpath) +{ + struct vnode *cvnp, *rvnp; + char *rbuf, *fbuf, *copy; + struct filedesc *fdp; + struct sbuf sbf; + int error, needslash; + + 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 != '/') { + if (dirfd == AT_FDCWD) { + cvnp = fdp->fd_cdir; + vhold(cvnp); + } else { + /* XXX: fgetvp() that vhold()s vnode instead of vref()ing it would be better */ + error = fgetvp(td, dirfd, 0, &cvnp); + if (error) { + cpath[0] = '\0'; + if (rvnp != NULL) + vdrop(rvnp); + return; + } + vhold(cvnp); + vrele(cvnp); + } + needslash = (fdp->fd_rdir != cvnp); + } else { + needslash = 1; + } + 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 (needslash) + (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_error(&sbf) != 0) { + cpath[0] = '\0'; + return; + } + sbuf_finish(&sbf); +} 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..dc0df3e --- /dev/null +++ b/sys/security/audit/audit_pipe.c @@ -0,0 +1,1129 @@ +/*- + * Copyright (c) 2006 Robert N. M. Watson + * Copyright (c) 2008-2009 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 (1) +#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_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; + + /* Lockless read to avoid acquiring the global lock if not needed. */ + if (TAILQ_EMPTY(&audit_pipe_list)) + return (0); + + 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_mtx(&ap->ap_selinfo.si_note, AUDIT_PIPE_MTX(ap)); + 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); + seldrain(&ap->ap_selinfo); + 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 *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) { + 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..e23ba08 --- /dev/null +++ b/sys/security/audit/audit_private.h @@ -0,0 +1,430 @@ +/*- + * Copyright (c) 1999-2009 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); +MALLOC_DECLARE(M_AUDITGIDSET); +#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; + 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. */ +}; + +/* + * The following is used for A_OLDSETQCTRL and AU_OLDGETQCTRL and a 64-bit + * userland. + */ +struct au_qctrl64 { + u_int64_t aq64_hiwater; + u_int64_t aq64_lowater; + u_int64_t aq64_bufsz; + u_int64_t aq64_delay; + u_int64_t aq64_minfree; +}; +typedef struct au_qctrl64 au_qctrl64_t; + +union auditon_udata { + char *au_path; + int au_cond; + int au_flags; + int au_policy; + int au_trigger; + int64_t au_cond64; + int64_t au_policy64; + 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_qctrl64_t au_qctrl64; + 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_atfd1; + int ar_arg_atfd2; + 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; + cap_rights_t ar_arg_rights; + uint32_t ar_arg_fcntl_rights; + char ar_jailname[MAXHOSTNAMELEN]; +}; + +/* + * 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_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_ATFD1 0x0004000000000000ULL +#define ARG_ATFD2 0x0008000000000000ULL +#define ARG_RIGHTS 0x0010000000000000ULL +#define ARG_FCNTL_RIGHTS 0x0020000000000000ULL +#define ARG_NONE 0x0000000000000000ULL +#define ARG_ALL 0xFFFFFFFFFFFFFFFFULL + +#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) +#define ARG_CLEAR_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 int audit_q_len; +extern int 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); +au_event_t audit_flags_and_error_to_openatevent(int oflags, int error); +au_event_t audit_msgctl_to_event(int cmd); +au_event_t audit_semctl_to_event(int cmr); +void audit_canon_path(struct thread *td, int dirfd, char *path, + char *cpath); +au_event_t 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..acf005e --- /dev/null +++ b/sys/security/audit/audit_syscalls.c @@ -0,0 +1,873 @@ +/*- + * Copyright (c) 1999-2009 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/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 +sys_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); + td->td_pflags |= TDP_AUDITREC; + 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 +sys_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_OLDSETPOLICY: + case A_SETKMASK: + case A_SETQCTRL: + case A_OLDSETQCTRL: + case A_SETSTAT: + case A_SETUMASK: + case A_SETSMASK: + case A_SETCOND: + case A_OLDSETCOND: + 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_OLDGETPOLICY: + case A_GETPOLICY: + if (uap->length == sizeof(udata.au_policy64)) { + if (!audit_fail_stop) + udata.au_policy64 |= AUDIT_CNT; + if (audit_panic_on_write_fail) + udata.au_policy64 |= AUDIT_AHLT; + if (audit_argv) + udata.au_policy64 |= AUDIT_ARGV; + if (audit_arge) + udata.au_policy64 |= AUDIT_ARGE; + break; + } + if (uap->length != sizeof(udata.au_policy)) + return (EINVAL); + 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_OLDSETPOLICY: + case A_SETPOLICY: + if (uap->length == sizeof(udata.au_policy64)) { + if (udata.au_policy & (~AUDIT_CNT|AUDIT_AHLT| + AUDIT_ARGV|AUDIT_ARGE)) + return (EINVAL); + audit_fail_stop = ((udata.au_policy64 & AUDIT_CNT) == + 0); + audit_panic_on_write_fail = (udata.au_policy64 & + AUDIT_AHLT); + audit_argv = (udata.au_policy64 & AUDIT_ARGV); + audit_arge = (udata.au_policy64 & AUDIT_ARGE); + break; + } + if (uap->length != sizeof(udata.au_policy)) + return (EINVAL); + 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: + if (uap->length != sizeof(udata.au_mask)) + return (EINVAL); + udata.au_mask = audit_nae_mask; + break; + + case A_SETKMASK: + if (uap->length != sizeof(udata.au_mask)) + return (EINVAL); + audit_nae_mask = udata.au_mask; + break; + + case A_OLDGETQCTRL: + case A_GETQCTRL: + if (uap->length == sizeof(udata.au_qctrl64)) { + udata.au_qctrl64.aq64_hiwater = + (u_int64_t)audit_qctrl.aq_hiwater; + udata.au_qctrl64.aq64_lowater = + (u_int64_t)audit_qctrl.aq_lowater; + udata.au_qctrl64.aq64_bufsz = + (u_int64_t)audit_qctrl.aq_bufsz; + udata.au_qctrl64.aq64_minfree = + (u_int64_t)audit_qctrl.aq_minfree; + break; + } + if (uap->length != sizeof(udata.au_qctrl)) + return (EINVAL); + udata.au_qctrl = audit_qctrl; + break; + + case A_OLDSETQCTRL: + case A_SETQCTRL: + if (uap->length == sizeof(udata.au_qctrl64)) { + if ((udata.au_qctrl64.aq64_hiwater > AQ_MAXHIGH) || + (udata.au_qctrl64.aq64_lowater >= + udata.au_qctrl.aq_hiwater) || + (udata.au_qctrl64.aq64_bufsz > AQ_MAXBUFSZ) || + (udata.au_qctrl64.aq64_minfree < 0) || + (udata.au_qctrl64.aq64_minfree > 100)) + return (EINVAL); + audit_qctrl.aq_hiwater = + (int)udata.au_qctrl64.aq64_hiwater; + audit_qctrl.aq_lowater = + (int)udata.au_qctrl64.aq64_lowater; + audit_qctrl.aq_bufsz = + (int)udata.au_qctrl64.aq64_bufsz; + audit_qctrl.aq_minfree = + (int)udata.au_qctrl64.aq64_minfree; + audit_qctrl.aq_delay = -1; /* Not used. */ + break; + } + if (uap->length != sizeof(udata.au_qctrl)) + return (EINVAL); + 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_OLDGETCOND: + case A_GETCOND: + if (uap->length == sizeof(udata.au_cond64)) { + if (audit_enabled && !audit_suspended) + udata.au_cond64 = AUC_AUDITING; + else + udata.au_cond64 = AUC_NOAUDIT; + break; + } + if (uap->length != sizeof(udata.au_cond)) + return (EINVAL); + if (audit_enabled && !audit_suspended) + udata.au_cond = AUC_AUDITING; + else + udata.au_cond = AUC_NOAUDIT; + break; + + case A_OLDSETCOND: + case A_SETCOND: + if (uap->length == sizeof(udata.au_cond64)) { + if (udata.au_cond64 == AUC_NOAUDIT) + audit_suspended = 1; + if (udata.au_cond64 == AUC_AUDITING) + audit_suspended = 0; + if (udata.au_cond64 == AUC_DISABLED) { + audit_suspended = 1; + audit_shutdown(NULL, 0); + } + break; + } + if (uap->length != sizeof(udata.au_cond)) + return (EINVAL); + 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: + if (uap->length != sizeof(udata.au_evclass)) + return (EINVAL); + udata.au_evclass.ec_class = au_event_class( + udata.au_evclass.ec_number); + break; + + case A_SETCLASS: + if (uap->length != sizeof(udata.au_evclass)) + return (EINVAL); + au_evclassmap_insert(udata.au_evclass.ec_number, + udata.au_evclass.ec_class); + break; + + case A_GETPINFO: + if (uap->length != sizeof(udata.au_aupinfo)) + return (EINVAL); + 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 (uap->length != sizeof(udata.au_aupinfo)) + return (EINVAL); + 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 (uap->length != sizeof(udata.au_fstat)) + return (EINVAL); + 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: + if (uap->length != sizeof(udata.au_fstat)) + return (EINVAL); + udata.au_fstat.af_filesz = audit_fstat.af_filesz; + udata.au_fstat.af_currsz = audit_fstat.af_currsz; + break; + + case A_GETPINFO_ADDR: + if (uap->length != sizeof(udata.au_aupinfo_addr)) + return (EINVAL); + 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: + if (uap->length != sizeof(udata.au_kau_info)) + return (EINVAL); + audit_get_kinfo(&udata.au_kau_info); + break; + + case A_SETKAUDIT: + if (uap->length != sizeof(udata.au_kau_info)) + return (EINVAL); + 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 (uap->length != sizeof(udata.au_trigger)) + return (EINVAL); + 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_OLDGETPOLICY: + case A_GETKMASK: + case A_GETQCTRL: + case A_OLDGETQCTRL: + case A_GETCWD: + case A_GETCAR: + case A_GETSTAT: + case A_GETCOND: + case A_OLDGETCOND: + 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 +sys_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 +sys_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_cred_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 +sys_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 +sys_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_cred_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 +sys_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 +sys_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_cred_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 +sys_auditctl(struct thread *td, struct auditctl_args *uap) +{ + struct nameidata nd; + struct ucred *cred; + struct vnode *vp; + int error = 0; + int flags; + + 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 | AUDITVNODE1, + UIO_USERSPACE, uap->path, td); + flags = AUDIT_OPEN_FLAGS; + error = vn_open(&nd, &flags, 0, NULL); + if (error) + return (error); + 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); + 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); + return (EINVAL); + } + 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 +sys_audit(struct thread *td, struct audit_args *uap) +{ + + return (ENOSYS); +} + +int +sys_auditon(struct thread *td, struct auditon_args *uap) +{ + + return (ENOSYS); +} + +int +sys_getauid(struct thread *td, struct getauid_args *uap) +{ + + return (ENOSYS); +} + +int +sys_setauid(struct thread *td, struct setauid_args *uap) +{ + + return (ENOSYS); +} + +int +sys_getaudit(struct thread *td, struct getaudit_args *uap) +{ + + return (ENOSYS); +} + +int +sys_setaudit(struct thread *td, struct setaudit_args *uap) +{ + + return (ENOSYS); +} + +int +sys_getaudit_addr(struct thread *td, struct getaudit_addr_args *uap) +{ + + return (ENOSYS); +} + +int +sys_setaudit_addr(struct thread *td, struct setaudit_addr_args *uap) +{ + + return (ENOSYS); +} + +int +sys_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..caad5ac --- /dev/null +++ b/sys/security/audit/audit_worker.c @@ -0,0 +1,495 @@ +/*- + * 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 off_t audit_size; +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; + static int cur_fail; + long temp; + + AUDIT_WORKER_LOCK_ASSERT(); + + if (vp == NULL) + return; + + mnt_stat = &vp->v_mount->mnt_stat; + + /* + * 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); + if (error) + goto fail; + + /* + * 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_size >= audit_fstat.af_filesz * (audit_file_rotate_wait + 1)) { + AUDIT_WORKER_LOCK_ASSERT(); + + audit_file_rotate_wait++; + (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; + AUDIT_WORKER_LOCK_ASSERT(); + audit_size += len; + + /* + * 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."); + } + } + + 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); +} + +/* + * 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; + struct vattr vattr; + + KASSERT((cred != NULL && vp != NULL) || (cred == NULL && vp == NULL), + ("audit_rotate_vnode: cred %p vp %p", cred, vp)); + + if (vp != NULL) { + vn_lock(vp, LK_SHARED | LK_RETRY); + if (VOP_GETATTR(vp, &vattr, cred) != 0) + vattr.va_size = 0; + VOP_UNLOCK(vp, 0); + } else { + vattr.va_size = 0; + } + + /* + * 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_size = vattr.va_size; + 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) { + vn_close(old_audit_vp, AUDIT_CLOSE_FLAGS, old_audit_cred, + curthread); + 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/audit/bsm_domain.c b/sys/security/audit/bsm_domain.c new file mode 100644 index 0000000..cb5939f --- /dev/null +++ b/sys/security/audit/bsm_domain.c @@ -0,0 +1,495 @@ +/*- + * 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_domain.c#3 + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/socket.h> + +#include <security/audit/audit.h> + +#include <bsm/audit_domain.h> +#include <bsm/audit_record.h> + +struct bsm_domain { + u_short bd_bsm_domain; + int bd_local_domain; +}; + +#define PF_NO_LOCAL_MAPPING -600 + +static const struct bsm_domain bsm_domains[] = { + { BSM_PF_UNSPEC, PF_UNSPEC }, + { BSM_PF_LOCAL, PF_LOCAL }, + { BSM_PF_INET, PF_INET }, + { BSM_PF_IMPLINK, +#ifdef PF_IMPLINK + PF_IMPLINK +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_PUP, +#ifdef PF_PUP + PF_PUP +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_CHAOS, +#ifdef PF_CHAOS + PF_CHAOS +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_NS, +#ifdef PF_NS + PF_NS +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_NBS, +#ifdef PF_NBS + PF_NBS +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_ECMA, +#ifdef PF_ECMA + PF_ECMA +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_DATAKIT, +#ifdef PF_DATAKIT + PF_DATAKIT +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_CCITT, +#ifdef PF_CCITT + PF_CCITT +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_SNA, PF_SNA }, + { BSM_PF_DECnet, PF_DECnet }, + { BSM_PF_DLI, +#ifdef PF_DLI + PF_DLI +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_LAT, +#ifdef PF_LAT + PF_LAT +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_HYLINK, +#ifdef PF_HYLINK + PF_HYLINK +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_APPLETALK, PF_APPLETALK }, + { BSM_PF_NIT, +#ifdef PF_NIT + PF_NIT +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_802, +#ifdef PF_802 + PF_802 +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_OSI, +#ifdef PF_OSI + PF_OSI +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_X25, +#ifdef PF_X25 + PF_X25 +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_OSINET, +#ifdef PF_OSINET + PF_OSINET +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_GOSIP, +#ifdef PF_GOSIP + PF_GOSIP +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_IPX, PF_IPX }, + { BSM_PF_ROUTE, PF_ROUTE }, + { BSM_PF_LINK, +#ifdef PF_LINK + PF_LINK +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_INET6, PF_INET6 }, + { BSM_PF_KEY, PF_KEY }, + { BSM_PF_NCA, +#ifdef PF_NCA + PF_NCA +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_POLICY, +#ifdef PF_POLICY + PF_POLICY +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_INET_OFFLOAD, +#ifdef PF_INET_OFFLOAD + PF_INET_OFFLOAD +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_NETBIOS, +#ifdef PF_NETBIOS + PF_NETBIOS +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_ISO, +#ifdef PF_ISO + PF_ISO +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_XTP, +#ifdef PF_XTP + PF_XTP +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_COIP, +#ifdef PF_COIP + PF_COIP +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_CNT, +#ifdef PF_CNT + PF_CNT +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_RTIP, +#ifdef PF_RTIP + PF_RTIP +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_SIP, +#ifdef PF_SIP + PF_SIP +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_PIP, +#ifdef PF_PIP + PF_PIP +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_ISDN, +#ifdef PF_ISDN + PF_ISDN +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_E164, +#ifdef PF_E164 + PF_E164 +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_NATM, +#ifdef PF_NATM + PF_NATM +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_ATM, +#ifdef PF_ATM + PF_ATM +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_NETGRAPH, +#ifdef PF_NETGRAPH + PF_NETGRAPH +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_SLOW, +#ifdef PF_SLOW + PF_SLOW +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_SCLUSTER, +#ifdef PF_SCLUSTER + PF_SCLUSTER +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_ARP, +#ifdef PF_ARP + PF_ARP +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_BLUETOOTH, +#ifdef PF_BLUETOOTH + PF_BLUETOOTH +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_AX25, +#ifdef PF_AX25 + PF_AX25 +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_ROSE, +#ifdef PF_ROSE + PF_ROSE +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_NETBEUI, +#ifdef PF_NETBEUI + PF_NETBEUI +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_SECURITY, +#ifdef PF_SECURITY + PF_SECURITY +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_PACKET, +#ifdef PF_PACKET + PF_PACKET +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_ASH, +#ifdef PF_ASH + PF_ASH +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_ECONET, +#ifdef PF_ECONET + PF_ECONET +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_ATMSVC, +#ifdef PF_ATMSVC + PF_ATMSVC +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_IRDA, +#ifdef PF_IRDA + PF_IRDA +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_PPPOX, +#ifdef PF_PPPOX + PF_PPPOX +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_WANPIPE, +#ifdef PF_WANPIPE + PF_WANPIPE +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_LLC, +#ifdef PF_LLC + PF_LLC +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_CAN, +#ifdef PF_CAN + PF_CAN +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_TIPC, +#ifdef PF_TIPC + PF_TIPC +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_IUCV, +#ifdef PF_IUCV + PF_IUCV +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_RXRPC, +#ifdef PF_RXRPC + PF_RXRPC +#else + PF_NO_LOCAL_MAPPING +#endif + }, + { BSM_PF_PHONET, +#ifdef PF_PHONET + PF_PHONET +#else + PF_NO_LOCAL_MAPPING +#endif + }, +}; +static const int bsm_domains_count = sizeof(bsm_domains) / + sizeof(bsm_domains[0]); + +static const struct bsm_domain * +bsm_lookup_local_domain(int local_domain) +{ + int i; + + for (i = 0; i < bsm_domains_count; i++) { + if (bsm_domains[i].bd_local_domain == local_domain) + return (&bsm_domains[i]); + } + return (NULL); +} + +u_short +au_domain_to_bsm(int local_domain) +{ + const struct bsm_domain *bstp; + + bstp = bsm_lookup_local_domain(local_domain); + if (bstp == NULL) + return (BSM_PF_UNKNOWN); + return (bstp->bd_bsm_domain); +} + +static const struct bsm_domain * +bsm_lookup_bsm_domain(u_short bsm_domain) +{ + int i; + + for (i = 0; i < bsm_domains_count; i++) { + if (bsm_domains[i].bd_bsm_domain == bsm_domain) + return (&bsm_domains[i]); + } + return (NULL); +} + +int +au_bsm_to_domain(u_short bsm_domain, int *local_domainp) +{ + const struct bsm_domain *bstp; + + bstp = bsm_lookup_bsm_domain(bsm_domain); + if (bstp == NULL || bstp->bd_local_domain) + return (-1); + *local_domainp = bstp->bd_local_domain; + return (0); +} diff --git a/sys/security/audit/bsm_errno.c b/sys/security/audit/bsm_errno.c new file mode 100644 index 0000000..e2e1961 --- /dev/null +++ b/sys/security/audit/bsm_errno.c @@ -0,0 +1,775 @@ +/*- + * 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#22 + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> + +#include <security/audit/audit.h> + +#include <bsm/audit_errno.h> +#include <bsm/audit_record.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. + * + * Don't include string definitions when this code is compiled into a kernel. + */ +struct bsm_errno { + int be_bsm_errno; + int be_local_errno; +#if !defined(KERNEL) && !defined(_KERNEL) + const char *be_strerror; +#endif +}; + +#define ERRNO_NO_LOCAL_MAPPING -600 + +#if !defined(KERNEL) && !defined(_KERNEL) +#define ES(x) x +#else +#define ES(x) +#endif + +/* + * 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_errno bsm_errnos[] = { + { BSM_ERRNO_ESUCCESS, 0, ES("Success") }, + { BSM_ERRNO_EPERM, EPERM, ES("Operation not permitted") }, + { BSM_ERRNO_ENOENT, ENOENT, ES("No such file or directory") }, + { BSM_ERRNO_ESRCH, ESRCH, ES("No such process") }, + { BSM_ERRNO_EINTR, EINTR, ES("Interrupted system call") }, + { BSM_ERRNO_EIO, EIO, ES("Input/output error") }, + { BSM_ERRNO_ENXIO, ENXIO, ES("Device not configured") }, + { BSM_ERRNO_E2BIG, E2BIG, ES("Argument list too long") }, + { BSM_ERRNO_ENOEXEC, ENOEXEC, ES("Exec format error") }, + { BSM_ERRNO_EBADF, EBADF, ES("Bad file descriptor") }, + { BSM_ERRNO_ECHILD, ECHILD, ES("No child processes") }, + { BSM_ERRNO_EAGAIN, EAGAIN, ES("Resource temporarily unavailable") }, + { BSM_ERRNO_ENOMEM, ENOMEM, ES("Cannot allocate memory") }, + { BSM_ERRNO_EACCES, EACCES, ES("Permission denied") }, + { BSM_ERRNO_EFAULT, EFAULT, ES("Bad address") }, + { BSM_ERRNO_ENOTBLK, ENOTBLK, ES("Block device required") }, + { BSM_ERRNO_EBUSY, EBUSY, ES("Device busy") }, + { BSM_ERRNO_EEXIST, EEXIST, ES("File exists") }, + { BSM_ERRNO_EXDEV, EXDEV, ES("Cross-device link") }, + { BSM_ERRNO_ENODEV, ENODEV, ES("Operation not supported by device") }, + { BSM_ERRNO_ENOTDIR, ENOTDIR, ES("Not a directory") }, + { BSM_ERRNO_EISDIR, EISDIR, ES("Is a directory") }, + { BSM_ERRNO_EINVAL, EINVAL, ES("Invalid argument") }, + { BSM_ERRNO_ENFILE, ENFILE, ES("Too many open files in system") }, + { BSM_ERRNO_EMFILE, EMFILE, ES("Too many open files") }, + { BSM_ERRNO_ENOTTY, ENOTTY, ES("Inappropriate ioctl for device") }, + { BSM_ERRNO_ETXTBSY, ETXTBSY, ES("Text file busy") }, + { BSM_ERRNO_EFBIG, EFBIG, ES("File too large") }, + { BSM_ERRNO_ENOSPC, ENOSPC, ES("No space left on device") }, + { BSM_ERRNO_ESPIPE, ESPIPE, ES("Illegal seek") }, + { BSM_ERRNO_EROFS, EROFS, ES("Read-only file system") }, + { BSM_ERRNO_EMLINK, EMLINK, ES("Too many links") }, + { BSM_ERRNO_EPIPE, EPIPE, ES("Broken pipe") }, + { BSM_ERRNO_EDOM, EDOM, ES("Numerical argument out of domain") }, + { BSM_ERRNO_ERANGE, ERANGE, ES("Result too large") }, + { BSM_ERRNO_ENOMSG, ENOMSG, ES("No message of desired type") }, + { BSM_ERRNO_EIDRM, EIDRM, ES("Identifier removed") }, + { BSM_ERRNO_ECHRNG, +#ifdef ECHRNG + ECHRNG, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Channel number out of range") }, + { BSM_ERRNO_EL2NSYNC, +#ifdef EL2NSYNC + EL2NSYNC, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Level 2 not synchronized") }, + { BSM_ERRNO_EL3HLT, +#ifdef EL3HLT + EL3HLT, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Level 3 halted") }, + { BSM_ERRNO_EL3RST, +#ifdef EL3RST + EL3RST, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Level 3 reset") }, + { BSM_ERRNO_ELNRNG, +#ifdef ELNRNG + ELNRNG, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Link number out of range") }, + { BSM_ERRNO_EUNATCH, +#ifdef EUNATCH + EUNATCH, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Protocol driver not attached") }, + { BSM_ERRNO_ENOCSI, +#ifdef ENOCSI + ENOCSI, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("No CSI structure available") }, + { BSM_ERRNO_EL2HLT, +#ifdef EL2HLT + EL2HLT, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Level 2 halted") }, + { BSM_ERRNO_EDEADLK, EDEADLK, ES("Resource deadlock avoided") }, + { BSM_ERRNO_ENOLCK, ENOLCK, ES("No locks available") }, + { BSM_ERRNO_ECANCELED, ECANCELED, ES("Operation canceled") }, + { BSM_ERRNO_ENOTSUP, ENOTSUP, ES("Operation not supported") }, + { BSM_ERRNO_EDQUOT, EDQUOT, ES("Disc quota exceeded") }, + { BSM_ERRNO_EBADE, +#ifdef EBADE + EBADE, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Invalid exchange") }, + { BSM_ERRNO_EBADR, +#ifdef EBADR + EBADR, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Invalid request descriptor") }, + { BSM_ERRNO_EXFULL, +#ifdef EXFULL + EXFULL, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Exchange full") }, + { BSM_ERRNO_ENOANO, +#ifdef ENOANO + ENOANO, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("No anode") }, + { BSM_ERRNO_EBADRQC, +#ifdef EBADRQC + EBADRQC, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Invalid request descriptor") }, + { BSM_ERRNO_EBADSLT, +#ifdef EBADSLT + EBADSLT, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Invalid slot") }, + { BSM_ERRNO_EDEADLOCK, +#ifdef EDEADLOCK + EDEADLOCK, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Resource deadlock avoided") }, + { BSM_ERRNO_EBFONT, +#ifdef EBFONT + EBFONT, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Bad font file format") }, + { BSM_ERRNO_EOWNERDEAD, +#ifdef EOWNERDEAD + EOWNERDEAD, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Process died with the lock") }, + { BSM_ERRNO_ENOTRECOVERABLE, +#ifdef ENOTRECOVERABLE + ENOTRECOVERABLE, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Lock is not recoverable") }, + { BSM_ERRNO_ENOSTR, +#ifdef ENOSTR + ENOSTR, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Device not a stream") }, + { BSM_ERRNO_ENONET, +#ifdef ENONET + ENONET, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Machine is not on the network") }, + { BSM_ERRNO_ENOPKG, +#ifdef ENOPKG + ENOPKG, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Package not installed") }, + { BSM_ERRNO_EREMOTE, EREMOTE, + ES("Too many levels of remote in path") }, + { BSM_ERRNO_ENOLINK, +#ifdef ENOLINK + ENOLINK, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Link has been severed") }, + { BSM_ERRNO_EADV, +#ifdef EADV + EADV, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Advertise error") }, + { BSM_ERRNO_ESRMNT, +#ifdef ESRMNT + ESRMNT, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("srmount error") }, + { BSM_ERRNO_ECOMM, +#ifdef ECOMM + ECOMM, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Communication error on send") }, + { BSM_ERRNO_EPROTO, +#ifdef EPROTO + EPROTO, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Protocol error") }, + { BSM_ERRNO_ELOCKUNMAPPED, +#ifdef ELOCKUNMAPPED + ELOCKUNMAPPED, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Locked lock was unmapped") }, + { BSM_ERRNO_ENOTACTIVE, +#ifdef ENOTACTIVE + ENOTACTIVE, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Facility is not active") }, + { BSM_ERRNO_EMULTIHOP, +#ifdef EMULTIHOP + EMULTIHOP, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Multihop attempted") }, + { BSM_ERRNO_EBADMSG, +#ifdef EBADMSG + EBADMSG, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Bad message") }, + { BSM_ERRNO_ENAMETOOLONG, ENAMETOOLONG, ES("File name too long") }, + { BSM_ERRNO_EOVERFLOW, EOVERFLOW, + ES("Value too large to be stored in data type") }, + { BSM_ERRNO_ENOTUNIQ, +#ifdef ENOTUNIQ + ENOTUNIQ, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Given log name not unique") }, + { BSM_ERRNO_EBADFD, +#ifdef EBADFD + EBADFD, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Given f.d. invalid for this operation") }, + { BSM_ERRNO_EREMCHG, +#ifdef EREMCHG + EREMCHG, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Remote address changed") }, + { BSM_ERRNO_ELIBACC, +#ifdef ELIBACC + ELIBACC, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Can't access a needed shared lib") }, + { BSM_ERRNO_ELIBBAD, +#ifdef ELIBBAD + ELIBBAD, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Accessing a corrupted shared lib") }, + { BSM_ERRNO_ELIBSCN, +#ifdef ELIBSCN + ELIBSCN, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES(".lib section in a.out corrupted") }, + { BSM_ERRNO_ELIBMAX, +#ifdef ELIBMAX + ELIBMAX, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Attempting to link in too many libs") }, + { BSM_ERRNO_ELIBEXEC, +#ifdef ELIBEXEC + ELIBEXEC, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Attempting to exec a shared library") }, + { BSM_ERRNO_EILSEQ, EILSEQ, ES("Illegal byte sequence") }, + { BSM_ERRNO_ENOSYS, ENOSYS, ES("Function not implemented") }, + { BSM_ERRNO_ELOOP, ELOOP, ES("Too many levels of symbolic links") }, + { BSM_ERRNO_ERESTART, +#ifdef ERESTART + ERESTART, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Restart syscall") }, + { BSM_ERRNO_ESTRPIPE, +#ifdef ESTRPIPE + ESTRPIPE, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("If pipe/FIFO, don't sleep in stream head") }, + { BSM_ERRNO_ENOTEMPTY, ENOTEMPTY, ES("Directory not empty") }, + { BSM_ERRNO_EUSERS, EUSERS, ES("Too many users") }, + { BSM_ERRNO_ENOTSOCK, ENOTSOCK, + ES("Socket operation on non-socket") }, + { BSM_ERRNO_EDESTADDRREQ, EDESTADDRREQ, + ES("Destination address required") }, + { BSM_ERRNO_EMSGSIZE, EMSGSIZE, ES("Message too long") }, + { BSM_ERRNO_EPROTOTYPE, EPROTOTYPE, + ES("Protocol wrong type for socket") }, + { BSM_ERRNO_ENOPROTOOPT, ENOPROTOOPT, ES("Protocol not available") }, + { BSM_ERRNO_EPROTONOSUPPORT, EPROTONOSUPPORT, + ES("Protocol not supported") }, + { BSM_ERRNO_ESOCKTNOSUPPORT, ESOCKTNOSUPPORT, + ES("Socket type not supported") }, + { BSM_ERRNO_EOPNOTSUPP, EOPNOTSUPP, ES("Operation not supported") }, + { BSM_ERRNO_EPFNOSUPPORT, EPFNOSUPPORT, + ES("Protocol family not supported") }, + { BSM_ERRNO_EAFNOSUPPORT, EAFNOSUPPORT, + ES("Address family not supported by protocol family") }, + { BSM_ERRNO_EADDRINUSE, EADDRINUSE, ES("Address already in use") }, + { BSM_ERRNO_EADDRNOTAVAIL, EADDRNOTAVAIL, + ES("Can't assign requested address") }, + { BSM_ERRNO_ENETDOWN, ENETDOWN, ES("Network is down") }, + { BSM_ERRNO_ENETRESET, ENETRESET, + ES("Network dropped connection on reset") }, + { BSM_ERRNO_ECONNABORTED, ECONNABORTED, + ES("Software caused connection abort") }, + { BSM_ERRNO_ECONNRESET, ECONNRESET, ES("Connection reset by peer") }, + { BSM_ERRNO_ENOBUFS, ENOBUFS, ES("No buffer space available") }, + { BSM_ERRNO_EISCONN, EISCONN, ES("Socket is already connected") }, + { BSM_ERRNO_ENOTCONN, ENOTCONN, ES("Socket is not connected") }, + { BSM_ERRNO_ESHUTDOWN, ESHUTDOWN, + ES("Can't send after socket shutdown") }, + { BSM_ERRNO_ETOOMANYREFS, ETOOMANYREFS, + ES("Too many references: can't splice") }, + { BSM_ERRNO_ETIMEDOUT, ETIMEDOUT, ES("Operation timed out") }, + { BSM_ERRNO_ECONNREFUSED, ECONNREFUSED, ES("Connection refused") }, + { BSM_ERRNO_EHOSTDOWN, EHOSTDOWN, ES("Host is down") }, + { BSM_ERRNO_EHOSTUNREACH, EHOSTUNREACH, ES("No route to host") }, + { BSM_ERRNO_EALREADY, EALREADY, ES("Operation already in progress") }, + { BSM_ERRNO_EINPROGRESS, EINPROGRESS, + ES("Operation now in progress") }, + { BSM_ERRNO_ESTALE, ESTALE, ES("Stale NFS file handle") }, + { BSM_ERRNO_EPROCLIM, +#ifdef EPROCLIM + EPROCLIM, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Too many processes") }, + { BSM_ERRNO_EBADRPC, +#ifdef EBADRPC + EBADRPC, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("RPC struct is bad") }, + { BSM_ERRNO_ERPCMISMATCH, +#ifdef ERPCMISMATCH + ERPCMISMATCH, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("RPC version wrong") }, + { BSM_ERRNO_EPROGUNAVAIL, +#ifdef EPROGUNAVAIL + EPROGUNAVAIL, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("RPC prog. not avail") }, + { BSM_ERRNO_EPROGMISMATCH, +#ifdef EPROGMISMATCH + EPROGMISMATCH, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("RPC version wrong") }, + { BSM_ERRNO_EPROCUNAVAIL, +#ifdef EPROCUNAVAIL + EPROCUNAVAIL, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Bad procedure for program") }, + { BSM_ERRNO_EFTYPE, +#ifdef EFTYPE + EFTYPE, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Inappropriate file type or format") }, + { BSM_ERRNO_EAUTH, +#ifdef EAUTH + EAUTH, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Authenticateion error") }, + { BSM_ERRNO_ENEEDAUTH, +#ifdef ENEEDAUTH + ENEEDAUTH, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Need authenticator") }, + { BSM_ERRNO_ENOATTR, +#ifdef ENOATTR + ENOATTR, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Attribute not found") }, + { BSM_ERRNO_EDOOFUS, +#ifdef EDOOFUS + EDOOFUS, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Programming error") }, + { BSM_ERRNO_EJUSTRETURN, +#ifdef EJUSTRETURN + EJUSTRETURN, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Just return") }, + { BSM_ERRNO_ENOIOCTL, +#ifdef ENOIOCTL + ENOIOCTL, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("ioctl not handled by this layer") }, + { BSM_ERRNO_EDIRIOCTL, +#ifdef EDIRIOCTL + EDIRIOCTL, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("do direct ioctl in GEOM") }, + { BSM_ERRNO_EPWROFF, +#ifdef EPWROFF + EPWROFF, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Device power is off") }, + { BSM_ERRNO_EDEVERR, +#ifdef EDEVERR + EDEVERR, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Device error") }, + { BSM_ERRNO_EBADEXEC, +#ifdef EBADEXEC + EBADEXEC, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Bad executable") }, + { BSM_ERRNO_EBADARCH, +#ifdef EBADARCH + EBADARCH, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Bad CPU type in executable") }, + { BSM_ERRNO_ESHLIBVERS, +#ifdef ESHLIBVERS + ESHLIBVERS, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Shared library version mismatch") }, + { BSM_ERRNO_EBADMACHO, +#ifdef EBADMACHO + EBADMACHO, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Malformed Macho file") }, + { BSM_ERRNO_EPOLICY, +#ifdef EPOLICY + EPOLICY, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Operation failed by policy") }, + { BSM_ERRNO_EDOTDOT, +#ifdef EDOTDOT + EDOTDOT, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("RFS specific error") }, + { BSM_ERRNO_EUCLEAN, +#ifdef EUCLEAN + EUCLEAN, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Structure needs cleaning") }, + { BSM_ERRNO_ENOTNAM, +#ifdef ENOTNAM + ENOTNAM, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Not a XENIX named type file") }, + { BSM_ERRNO_ENAVAIL, +#ifdef ENAVAIL + ENAVAIL, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("No XENIX semaphores available") }, + { BSM_ERRNO_EISNAM, +#ifdef EISNAM + EISNAM, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Is a named type file") }, + { BSM_ERRNO_EREMOTEIO, +#ifdef EREMOTEIO + EREMOTEIO, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Remote I/O error") }, + { BSM_ERRNO_ENOMEDIUM, +#ifdef ENOMEDIUM + ENOMEDIUM, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("No medium found") }, + { BSM_ERRNO_EMEDIUMTYPE, +#ifdef EMEDIUMTYPE + EMEDIUMTYPE, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Wrong medium type") }, + { BSM_ERRNO_ENOKEY, +#ifdef ENOKEY + ENOKEY, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Required key not available") }, + { BSM_ERRNO_EKEYEXPIRED, +#ifdef EKEYEXPIRED + EKEYEXPIRED, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Key has expired") }, + { BSM_ERRNO_EKEYREVOKED, +#ifdef EKEYREVOKED + EKEYREVOKED, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Key has been revoked") }, + { BSM_ERRNO_EKEYREJECTED, +#ifdef EKEYREJECTED + EKEYREJECTED, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Key was rejected by service") }, + { BSM_ERRNO_ENOTCAPABLE, +#ifdef ENOTCAPABLE + ENOTCAPABLE, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Capabilities insufficient") }, + { BSM_ERRNO_ECAPMODE, +#ifdef ECAPMODE + ECAPMODE, +#else + ERRNO_NO_LOCAL_MAPPING, +#endif + ES("Not permitted in capability mode") }, +}; +static const int bsm_errnos_count = sizeof(bsm_errnos) / sizeof(bsm_errnos[0]); + +static const struct bsm_errno * +bsm_lookup_errno_local(int local_errno) +{ + int i; + + for (i = 0; i < bsm_errnos_count; i++) { + if (bsm_errnos[i].be_local_errno == local_errno) + return (&bsm_errnos[i]); + } + return (NULL); +} + +/* + * Conversion to the BSM errno space isn't allowed to fail; we simply map to + * BSM_ERRNO_UNKNOWN and let the remote endpoint deal with it. + */ +u_char +au_errno_to_bsm(int local_errno) +{ + const struct bsm_errno *bsme; + + bsme = bsm_lookup_errno_local(local_errno); + if (bsme == NULL) + return (BSM_ERRNO_UNKNOWN); + return (bsme->be_bsm_errno); +} + +static const struct bsm_errno * +bsm_lookup_errno_bsm(u_char bsm_errno) +{ + int i; + + for (i = 0; i < bsm_errnos_count; i++) { + if (bsm_errnos[i].be_bsm_errno == bsm_errno) + return (&bsm_errnos[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. + */ +int +au_bsm_to_errno(u_char bsm_errno, int *errorp) +{ + const struct bsm_errno *bsme; + + bsme = bsm_lookup_errno_bsm(bsm_errno); + if (bsme == NULL || bsme->be_local_errno == ERRNO_NO_LOCAL_MAPPING) + return (-1); + *errorp = bsme->be_local_errno; + return (0); +} + +#if !defined(KERNEL) && !defined(_KERNEL) +const char * +au_strerror(u_char bsm_errno) +{ + const struct bsm_errno *bsme; + + bsme = bsm_lookup_errno_bsm(bsm_errno); + if (bsme == NULL) + return ("Unrecognized BSM error"); + if (bsme->be_local_errno != ERRNO_NO_LOCAL_MAPPING) + return (strerror(bsme->be_local_errno)); + return (bsme->be_strerror); +} +#endif diff --git a/sys/security/audit/bsm_fcntl.c b/sys/security/audit/bsm_fcntl.c new file mode 100644 index 0000000..1e7e68f --- /dev/null +++ b/sys/security/audit/bsm_fcntl.c @@ -0,0 +1,292 @@ +/*- + * Copyright (c) 2008-2009 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_fcntl.c#2 + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/fcntl.h> + +#include <security/audit/audit.h> + +#include <bsm/audit_fcntl.h> +#include <bsm/audit_record.h> + +struct bsm_fcntl_cmd { + u_short bfc_bsm_fcntl_cmd; + int bfc_local_fcntl_cmd; +}; +typedef struct bsm_fcntl_cmd bsm_fcntl_cmd_t; + +static const bsm_fcntl_cmd_t bsm_fcntl_cmdtab[] = { + { BSM_F_DUPFD, F_DUPFD }, + { BSM_F_GETFD, F_GETFD }, + { BSM_F_SETFD, F_SETFD }, + { BSM_F_GETFL, F_GETFL }, + { BSM_F_SETFL, F_SETFL }, +#ifdef F_O_GETLK + { BSM_F_O_GETLK, F_O_GETLK }, +#endif + { BSM_F_SETLK, F_SETLK }, + { BSM_F_SETLKW, F_SETLK }, +#ifdef F_CHFL + { BSM_F_CHKFL, F_CHKFL }, +#endif +#ifdef F_DUP2FD + { BSM_F_DUP2FD, F_DUP2FD }, +#endif +#ifdef F_ALLOCSP + { BSM_F_ALLOCSP, F_ALLOCSP }, +#endif +#ifdef F_FREESP + { BSM_F_FREESP, F_FREESP }, +#endif +#ifdef F_ISSTREAM + { BSM_F_ISSTREAM, F_ISSTREAM}, +#endif + { BSM_F_GETLK, F_GETLK }, +#ifdef F_PRIV + { BSM_F_PRIV, F_PRIV }, +#endif +#ifdef F_NPRIV + { BSM_F_NPRIV, F_NPRIV }, +#endif +#ifdef F_QUOTACTL + { BSM_F_QUOTACTL, F_QUOTACTL }, +#endif +#ifdef F_BLOCKS + { BSM_F_BLOCKS, F_BLOCKS }, +#endif +#ifdef F_BLKSIZE + { BSM_F_BLKSIZE, F_BLKSIZE }, +#endif + { BSM_F_GETOWN, F_GETOWN }, + { BSM_F_SETOWN, F_SETOWN }, +#ifdef F_REVOKE + { BSM_F_REVOKE, F_REVOKE }, +#endif +#ifdef F_HASREMOTEBLOCKS + { BSM_F_HASREMOTEBLOCKS, + F_HASREMOTEBLOCKS }, +#endif +#ifdef F_FREESP + { BSM_F_FREESP, F_FREESP }, +#endif +#ifdef F_ALLOCSP + { BSM_F_ALLOCSP, F_ALLOCSP }, +#endif +#ifdef F_FREESP64 + { BSM_F_FREESP64, F_FREESP64 }, +#endif +#ifdef F_ALLOCSP64 + { BSM_F_ALLOCSP64, F_ALLOCSP64 }, +#endif +#ifdef F_GETLK64 + { BSM_F_GETLK64, F_GETLK64 }, +#endif +#ifdef F_SETLK64 + { BSM_F_SETLK64, F_SETLK64 }, +#endif +#ifdef F_SETLKW64 + { BSM_F_SETLKW64, F_SETLKW64 }, +#endif +#ifdef F_SHARE + { BSM_F_SHARE, F_SHARE }, +#endif +#ifdef F_UNSHARE + { BSM_F_UNSHARE, F_UNSHARE }, +#endif +#ifdef F_SETLK_NBMAND + { BSM_F_SETLK_NBMAND, F_SETLK_NBMAND }, +#endif +#ifdef F_SHARE_NBMAND + { BSM_F_SHARE_NBMAND, F_SHARE_NBMAND }, +#endif +#ifdef F_SETLK64_NBMAND + { BSM_F_SETLK64_NBMAND, F_SETLK64_NBMAND }, +#endif +#ifdef F_GETXFL + { BSM_F_GETXFL, F_GETXFL }, +#endif +#ifdef F_BADFD + { BSM_F_BADFD, F_BADFD }, +#endif +#ifdef F_OGETLK + { BSM_F_OGETLK, F_OGETLK }, +#endif +#ifdef F_OSETLK + { BSM_F_OSETLK, F_OSETLK }, +#endif +#ifdef F_OSETLKW + { BSM_F_OSETLKW, F_OSETLKW }, +#endif +#ifdef F_SETLK_REMOTE + { BSM_F_SETLK_REMOTE, F_SETLK_REMOTE }, +#endif + +#ifdef F_SETSIG + { BSM_F_SETSIG, F_SETSIG }, +#endif +#ifdef F_GETSIG + { BSM_F_GETSIG, F_GETSIG }, +#endif + +#ifdef F_CHKCLEAN + { BSM_F_CHKCLEAN, F_CHKCLEAN }, +#endif +#ifdef F_PREALLOCATE + { BSM_F_PREALLOCATE, F_PREALLOCATE }, +#endif +#ifdef F_SETSIZE + { BSM_F_SETSIZE, F_SETSIZE }, +#endif +#ifdef F_RDADVISE + { BSM_F_RDADVISE, F_RDADVISE }, +#endif +#ifdef F_RDAHEAD + { BSM_F_RDAHEAD, F_RDAHEAD }, +#endif +#ifdef F_READBOOTSTRAP + { BSM_F_READBOOTSTRAP, F_READBOOTSTRAP }, +#endif +#ifdef F_WRITEBOOTSTRAP + { BSM_F_WRITEBOOTSTRAP, F_WRITEBOOTSTRAP }, +#endif +#ifdef F_NOCACHE + { BSM_F_NOCACHE, F_NOCACHE }, +#endif +#ifdef F_LOG2PHYS + { BSM_F_LOG2PHYS, F_LOG2PHYS }, +#endif +#ifdef F_GETPATH + { BSM_F_GETPATH, F_GETPATH }, +#endif +#ifdef F_FULLFSYNC + { BSM_F_FULLFSYNC, F_FULLFSYNC }, +#endif +#ifdef F_PATHPKG_CHECK + { BSM_F_PATHPKG_CHECK, F_PATHPKG_CHECK }, +#endif +#ifdef F_FREEZE_FS + { BSM_F_FREEZE_FS, F_FREEZE_FS }, +#endif +#ifdef F_THAW_FS + { BSM_F_THAW_FS, F_THAW_FS }, +#endif +#ifdef F_GLOBAL_NOCACHE + { BSM_F_GLOBAL_NOCACHE, F_GLOBAL_NOCACHE }, +#endif +#ifdef F_OPENFROM + { BSM_F_OPENFROM, F_OPENFROM }, +#endif +#ifdef F_UNLINKFROM + { BSM_F_UNLINKFROM, F_UNLINKFROM }, +#endif +#ifdef F_CHECK_OPENEVT + { BSM_F_CHECK_OPENEVT, F_CHECK_OPENEVT }, +#endif +#ifdef F_ADDSIGS + { BSM_F_ADDSIGS, F_ADDSIGS }, +#endif +#ifdef F_MARKDEPENDENCY + { BSM_F_MARKDEPENDENCY, F_MARKDEPENDENCY }, +#endif + +#ifdef FCNTL_FS_SPECIFIC_BASE + { BSM_F_FS_SPECIFIC_0, FCNTL_FS_SPECIFIC_BASE}, + { BSM_F_FS_SPECIFIC_1, FCNTL_FS_SPECIFIC_BASE + 1}, + { BSM_F_FS_SPECIFIC_2, FCNTL_FS_SPECIFIC_BASE + 2}, + { BSM_F_FS_SPECIFIC_3, FCNTL_FS_SPECIFIC_BASE + 3}, + { BSM_F_FS_SPECIFIC_4, FCNTL_FS_SPECIFIC_BASE + 4}, + { BSM_F_FS_SPECIFIC_5, FCNTL_FS_SPECIFIC_BASE + 5}, + { BSM_F_FS_SPECIFIC_6, FCNTL_FS_SPECIFIC_BASE + 6}, + { BSM_F_FS_SPECIFIC_7, FCNTL_FS_SPECIFIC_BASE + 7}, + { BSM_F_FS_SPECIFIC_8, FCNTL_FS_SPECIFIC_BASE + 8}, + { BSM_F_FS_SPECIFIC_9, FCNTL_FS_SPECIFIC_BASE + 9}, + { BSM_F_FS_SPECIFIC_10, FCNTL_FS_SPECIFIC_BASE + 10}, + { BSM_F_FS_SPECIFIC_11, FCNTL_FS_SPECIFIC_BASE + 11}, + { BSM_F_FS_SPECIFIC_12, FCNTL_FS_SPECIFIC_BASE + 12}, + { BSM_F_FS_SPECIFIC_13, FCNTL_FS_SPECIFIC_BASE + 13}, + { BSM_F_FS_SPECIFIC_14, FCNTL_FS_SPECIFIC_BASE + 14}, + { BSM_F_FS_SPECIFIC_15, FCNTL_FS_SPECIFIC_BASE + 15}, +#endif /* FCNTL_FS_SPECIFIC_BASE */ +}; +static const int bsm_fcntl_cmd_count = sizeof(bsm_fcntl_cmdtab) / + sizeof(bsm_fcntl_cmdtab[0]); + +static const bsm_fcntl_cmd_t * +bsm_lookup_local_fcntl_cmd(int local_fcntl_cmd) +{ + int i; + + for (i = 0; i < bsm_fcntl_cmd_count; i++) { + if (bsm_fcntl_cmdtab[i].bfc_local_fcntl_cmd == + local_fcntl_cmd) + return (&bsm_fcntl_cmdtab[i]); + } + return (NULL); +} + +u_short +au_fcntl_cmd_to_bsm(int local_fcntl_cmd) +{ + const bsm_fcntl_cmd_t *bfcp; + + bfcp = bsm_lookup_local_fcntl_cmd(local_fcntl_cmd); + if (bfcp == NULL) + return (BSM_F_UNKNOWN); + return (bfcp->bfc_bsm_fcntl_cmd); +} + +static const bsm_fcntl_cmd_t * +bsm_lookup_bsm_fcntl_cmd(u_short bsm_fcntl_cmd) +{ + int i; + + for (i = 0; i < bsm_fcntl_cmd_count; i++) { + if (bsm_fcntl_cmdtab[i].bfc_bsm_fcntl_cmd == + bsm_fcntl_cmd) + return (&bsm_fcntl_cmdtab[i]); + } + return (NULL); +} + +int +au_bsm_to_fcntl_cmd(u_short bsm_fcntl_cmd, int *local_fcntl_cmdp) +{ + const bsm_fcntl_cmd_t *bfcp; + + bfcp = bsm_lookup_bsm_fcntl_cmd(bsm_fcntl_cmd); + if (bfcp == NULL || bfcp->bfc_local_fcntl_cmd) + return (-1); + *local_fcntl_cmdp = bfcp->bfc_local_fcntl_cmd; + return (0); +} diff --git a/sys/security/audit/bsm_socket_type.c b/sys/security/audit/bsm_socket_type.c new file mode 100644 index 0000000..c049179 --- /dev/null +++ b/sys/security/audit/bsm_socket_type.c @@ -0,0 +1,107 @@ +/*- + * 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_socket_type.c#1 + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/socket.h> + +#include <security/audit/audit.h> + +#include <bsm/audit_record.h> +#include <bsm/audit_socket_type.h> + +struct bsm_socket_type { + u_short bst_bsm_socket_type; + int bst_local_socket_type; +}; + +#define ST_NO_LOCAL_MAPPING -600 + +static const struct bsm_socket_type bsm_socket_types[] = { + { BSM_SOCK_DGRAM, SOCK_DGRAM }, + { BSM_SOCK_STREAM, SOCK_STREAM }, + { BSM_SOCK_RAW, SOCK_RAW }, + { BSM_SOCK_RDM, SOCK_RDM }, + { BSM_SOCK_SEQPACKET, SOCK_SEQPACKET }, +}; +static const int bsm_socket_types_count = sizeof(bsm_socket_types) / + sizeof(bsm_socket_types[0]); + +static const struct bsm_socket_type * +bsm_lookup_local_socket_type(int local_socket_type) +{ + int i; + + for (i = 0; i < bsm_socket_types_count; i++) { + if (bsm_socket_types[i].bst_local_socket_type == + local_socket_type) + return (&bsm_socket_types[i]); + } + return (NULL); +} + +u_short +au_socket_type_to_bsm(int local_socket_type) +{ + const struct bsm_socket_type *bstp; + + bstp = bsm_lookup_local_socket_type(local_socket_type); + if (bstp == NULL) + return (BSM_SOCK_UNKNOWN); + return (bstp->bst_bsm_socket_type); +} + +static const struct bsm_socket_type * +bsm_lookup_bsm_socket_type(u_short bsm_socket_type) +{ + int i; + + for (i = 0; i < bsm_socket_types_count; i++) { + if (bsm_socket_types[i].bst_bsm_socket_type == + bsm_socket_type) + return (&bsm_socket_types[i]); + } + return (NULL); +} + +int +au_bsm_to_socket_type(u_short bsm_socket_type, int *local_socket_typep) +{ + const struct bsm_socket_type *bstp; + + bstp = bsm_lookup_bsm_socket_type(bsm_socket_type); + if (bstp == NULL || bstp->bst_local_socket_type) + return (-1); + *local_socket_typep = bstp->bst_local_socket_type; + return (0); +} diff --git a/sys/security/audit/bsm_token.c b/sys/security/audit/bsm_token.c new file mode 100644 index 0000000..6d0d67f --- /dev/null +++ b/sys/security/audit/bsm_token.c @@ -0,0 +1,1597 @@ +/*- + * Copyright (c) 2004-2009 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#99 + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#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 + * success/failure 1 byte + * privstrlen 2 bytes + * privstr N bytes + 1 (\0 byte) + */ +token_t * +au_to_upriv(char sorf, char *priv) +{ + u_int16_t textlen; + u_char *dptr; + token_t *t; + + textlen = strlen(priv) + 1; + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_char) + + sizeof(u_int16_t) + textlen); + + ADD_U_CHAR(dptr, AUT_UPRIV); + ADD_U_CHAR(dptr, sorf); + ADD_U_INT16(dptr, textlen); + ADD_STRING(dptr, priv, textlen); + return (t); +} + +/* + * token ID 1 byte + * privtstrlen 2 bytes + * privtstr N bytes + 1 + * privstrlen 2 bytes + * privstr N bytes + 1 + */ +token_t * +au_to_privset(char *privtypestr, char *privstr) +{ + u_int16_t type_len, priv_len; + u_char *dptr; + token_t *t; + + type_len = strlen(privtypestr) + 1; + priv_len = strlen(privstr) + 1; + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) + + sizeof(u_int16_t) + type_len + priv_len); + + ADD_U_CHAR(dptr, AUT_PRIV); + ADD_U_INT16(dptr, type_len); + ADD_STRING(dptr, privtypestr, type_len); + ADD_U_INT16(dptr, priv_len); + ADD_STRING(dptr, privstr, priv_len); + return (t); +} + +/* + * 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_int32_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_int32_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, sizeof(u_char) + 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) + * + * Domain and type arguments to this routine are assumed to already have been + * converted to the BSM constant space, so we don't do that here. + */ +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) + 8 * sizeof(u_int32_t)); + else + return (NULL); + + ADD_U_CHAR(dptr, AUT_SOCKET_EX); + ADD_U_INT16(dptr, au_domain_to_bsm(so_domain)); + ADD_U_INT16(dptr, au_socket_type_to_bsm(so_type)); + 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 (up to) 104 bytes + NULL (NULL terminated string) + */ +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; + auditinfo_addr_t aia; + + /* + * Try to use getaudit_addr(2) first. If this kernel does not support + * it, then fall back on to getaudit(2). + */ + if (getaudit_addr(&aia, sizeof(aia)) != 0) { + if (errno == ENOSYS) { + if (getaudit(&auinfo) != 0) + return (NULL); + return (au_to_subject32(auinfo.ai_auid, geteuid(), + getegid(), getuid(), getgid(), getpid(), + auinfo.ai_asid, &auinfo.ai_termid)); + } else { + /* getaudit_addr(2) failed for some other reason. */ + return (NULL); + } + } + + return (au_to_subject32_ex(aia.ai_auid, geteuid(), getegid(), getuid(), + getgid(), getpid(), aia.ai_asid, &aia.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 (audit_get_kaudit(&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/mac/mac_atalk.c b/sys/security/mac/mac_atalk.c new file mode 100644 index 0000000..18bbb8d --- /dev/null +++ b/sys/security/mac/mac_atalk.c @@ -0,0 +1,73 @@ +/*- + * Copyright (c) 2007-2009 Robert N. M. Watson + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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; + + if (mac_policy_count == 0) + return; + + mlabel = mac_mbuf_to_label(m); + + MAC_IFNET_LOCK(ifp); + MAC_POLICY_PERFORM_NOSLEEP(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..4d453b5 --- /dev/null +++ b/sys/security/mac/mac_audit.c @@ -0,0 +1,145 @@ +/*- + * Copyright (c) 1999-2002, 2009 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"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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_kdtrace.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/queue.h> +#include <sys/sdt.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> + +MAC_CHECK_PROBE_DEFINE2(cred_check_setaudit, "struct ucred *", + "struct auditinfo *"); + +int +mac_cred_check_setaudit(struct ucred *cred, struct auditinfo *ai) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(cred_check_setaudit, cred, ai); + MAC_CHECK_PROBE2(cred_check_setaudit, error, cred, ai); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(cred_check_setaudit_addr, "struct ucred *", + "struct auditinfo_addr *"); + +int +mac_cred_check_setaudit_addr(struct ucred *cred, struct auditinfo_addr *aia) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(cred_check_setaudit_addr, cred, aia); + MAC_CHECK_PROBE2(cred_check_setaudit_addr, error, cred, aia); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(cred_check_setauid, "struct ucred *", "uid_t"); + +int +mac_cred_check_setauid(struct ucred *cred, uid_t auid) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(cred_check_setauid, cred, auid); + MAC_CHECK_PROBE2(cred_check_setauid, error, cred, auid); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(system_check_audit, "struct ucred *", "void *", + "int"); + +int +mac_system_check_audit(struct ucred *cred, void *record, int length) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(system_check_audit, cred, record, length); + MAC_CHECK_PROBE3(system_check_audit, error, cred, record, length); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(system_check_auditctl, "struct ucred *", + "struct vnode *"); + +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_POLICY_CHECK(system_check_auditctl, cred, vp, vl); + MAC_CHECK_PROBE2(system_check_auditctl, error, cred, vp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(system_check_auditon, "struct ucred *", "int"); + +int +mac_system_check_auditon(struct ucred *cred, int cmd) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(system_check_auditon, cred, cmd); + MAC_CHECK_PROBE2(system_check_auditon, error, 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..64dab5b --- /dev/null +++ b/sys/security/mac/mac_cred.c @@ -0,0 +1,353 @@ +/*- + * Copyright (c) 1999-2002, 2008-2009 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"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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_kdtrace.h" +#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/sdt.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_POLICY_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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(cred_create_init, cred); +} + +int +mac_cred_externalize_label(struct label *label, char *elements, + char *outbuf, size_t outbuflen) +{ + int error; + + MAC_POLICY_EXTERNALIZE(cred, label, elements, outbuf, outbuflen); + + return (error); +} + +int +mac_cred_internalize_label(struct label *label, char *string) +{ + int error; + + MAC_POLICY_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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(cred_relabel, cred, newlabel); +} + +MAC_CHECK_PROBE_DEFINE2(cred_check_relabel, "struct ucred *", + "struct label *"); + +int +mac_cred_check_relabel(struct ucred *cred, struct label *newlabel) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(cred_check_relabel, cred, newlabel); + MAC_CHECK_PROBE2(cred_check_relabel, error, cred, newlabel); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(cred_check_setuid, "struct ucred *", "uid_t"); + +int +mac_cred_check_setuid(struct ucred *cred, uid_t uid) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(cred_check_setuid, cred, uid); + MAC_CHECK_PROBE2(cred_check_setuid, error, cred, uid); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(cred_check_seteuid, "struct ucred *", "uid_t"); + +int +mac_cred_check_seteuid(struct ucred *cred, uid_t euid) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(cred_check_seteuid, cred, euid); + MAC_CHECK_PROBE2(cred_check_seteuid, error, cred, euid); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(cred_check_setgid, "struct ucred *", "gid_t"); + +int +mac_cred_check_setgid(struct ucred *cred, gid_t gid) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(cred_check_setgid, cred, gid); + MAC_CHECK_PROBE2(cred_check_setgid, error, cred, gid); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(cred_check_setegid, "struct ucred *", "gid_t"); + +int +mac_cred_check_setegid(struct ucred *cred, gid_t egid) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(cred_check_setegid, cred, egid); + MAC_CHECK_PROBE2(cred_check_setegid, error, cred, egid); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(cred_check_setgroups, "struct ucred *", "int", + "gid_t *"); + +int +mac_cred_check_setgroups(struct ucred *cred, int ngroups, gid_t *gidset) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(cred_check_setgroups, cred, ngroups, gidset); + MAC_CHECK_PROBE3(cred_check_setgroups, error, cred, ngroups, gidset); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(cred_check_setreuid, "struct ucred *", "uid_t", + "uid_t"); + +int +mac_cred_check_setreuid(struct ucred *cred, uid_t ruid, uid_t euid) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(cred_check_setreuid, cred, ruid, euid); + MAC_CHECK_PROBE3(cred_check_setreuid, error, cred, ruid, euid); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(cred_check_setregid, "struct ucred *", "gid_t", + "gid_t"); + +int +mac_cred_check_setregid(struct ucred *cred, gid_t rgid, gid_t egid) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(cred_check_setregid, cred, rgid, egid); + MAC_CHECK_PROBE3(cred_check_setregid, error, cred, rgid, egid); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(cred_check_setresuid, "struct ucred *", "uid_t", + "uid_t", "uid_t"); + +int +mac_cred_check_setresuid(struct ucred *cred, uid_t ruid, uid_t euid, + uid_t suid) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(cred_check_setresuid, cred, ruid, euid, suid); + MAC_CHECK_PROBE4(cred_check_setresuid, error, cred, ruid, euid, + suid); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(cred_check_setresgid, "struct ucred *", "gid_t", + "gid_t", "gid_t"); + +int +mac_cred_check_setresgid(struct ucred *cred, gid_t rgid, gid_t egid, + gid_t sgid) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(cred_check_setresgid, cred, rgid, egid, sgid); + MAC_CHECK_PROBE4(cred_check_setresgid, error, cred, rgid, egid, + sgid); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(cred_check_visible, "struct ucred *", + "struct ucred *"); + +int +mac_cred_check_visible(struct ucred *cr1, struct ucred *cr2) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(cred_check_visible, cr1, cr2); + MAC_CHECK_PROBE2(cred_check_visible, error, 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..816bb0b --- /dev/null +++ b/sys/security/mac/mac_framework.c @@ -0,0 +1,597 @@ +/*- + * Copyright (c) 1999-2002, 2006, 2009 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-2009 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"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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_kdtrace.h" +#include "opt_mac.h" + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/condvar.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/mac.h> +#include <sys/module.h> +#include <sys/rmlock.h> +#include <sys/sdt.h> +#include <sys/sx.h> +#include <sys/sysctl.h> + +#include <security/mac/mac_framework.h> +#include <security/mac/mac_internal.h> +#include <security/mac/mac_policy.h> + +/* + * DTrace SDT providers for MAC. + */ +SDT_PROVIDER_DEFINE(mac); +SDT_PROVIDER_DEFINE(mac_framework); + +SDT_PROBE_DEFINE2(mac, kernel, policy, modevent, modevent, "int", + "struct mac_policy_conf *mpc"); +SDT_PROBE_DEFINE1(mac, kernel, policy, register, register, + "struct mac_policy_conf *"); +SDT_PROBE_DEFINE1(mac, kernel, policy, unregister, unregister, + "struct mac_policy_conf *"); + +/* + * 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_UQUAD(_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 policy modules are placed in one of two lists: mac_static_policy_list, + * for policies that are loaded early and cannot be unloaded, and + * mac_policy_list, which holds policies either loaded later in the boot + * cycle or that may be unloaded. The static policy list does not require + * locks to iterate over, but the dynamic list requires synchronization. + * Support for dynamic policy loading can be compiled out using the + * MAC_STATIC kernel option. + * + * The dynamic policy list is protected by two locks: modifying the list + * requires both locks to be held exclusively. One of the locks, + * mac_policy_rm, is acquired over policy entry points that will never sleep; + * the other, mac_policy_sx, is acquire over policy entry points that may + * sleep. The former category will be used when kernel locks may be held + * over calls to the MAC Framework, during network processing in ithreads, + * etc. The latter will tend to involve potentially blocking memory + * allocations, extended attribute I/O, etc. + */ +#ifndef MAC_STATIC +static struct rmlock mac_policy_rm; /* Non-sleeping entry points. */ +static struct sx mac_policy_sx; /* Sleeping entry points. */ +#endif + +struct mac_policy_list_head mac_policy_list; +struct mac_policy_list_head mac_static_policy_list; +u_int mac_policy_count; /* Registered policy count. */ + +static void mac_policy_xlock(void); +static void mac_policy_xlock_assert(void); +static void mac_policy_xunlock(void); + +void +mac_policy_slock_nosleep(struct rm_priotracker *tracker) +{ + +#ifndef MAC_STATIC + if (!mac_late) + return; + + rm_rlock(&mac_policy_rm, tracker); +#endif +} + +void +mac_policy_slock_sleep(void) +{ + + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, + "mac_policy_slock_sleep"); + +#ifndef MAC_STATIC + if (!mac_late) + return; + + sx_slock(&mac_policy_sx); +#endif +} + +void +mac_policy_sunlock_nosleep(struct rm_priotracker *tracker) +{ + +#ifndef MAC_STATIC + if (!mac_late) + return; + + rm_runlock(&mac_policy_rm, tracker); +#endif +} + +void +mac_policy_sunlock_sleep(void) +{ + +#ifndef MAC_STATIC + if (!mac_late) + return; + + sx_sunlock(&mac_policy_sx); +#endif +} + +static void +mac_policy_xlock(void) +{ + + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, + "mac_policy_xlock()"); + +#ifndef MAC_STATIC + if (!mac_late) + return; + + sx_xlock(&mac_policy_sx); + rm_wlock(&mac_policy_rm); +#endif +} + +static void +mac_policy_xunlock(void) +{ + +#ifndef MAC_STATIC + if (!mac_late) + return; + + rm_wunlock(&mac_policy_rm); + sx_xunlock(&mac_policy_sx); +#endif +} + +static void +mac_policy_xlock_assert(void) +{ + +#ifndef MAC_STATIC + if (!mac_late) + return; + + /* XXXRW: rm_assert(&mac_policy_rm, RA_WLOCKED); */ + sx_assert(&mac_policy_sx, SA_XLOCKED); +#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 + rm_init_flags(&mac_policy_rm, "mac_policy_rm", RM_NOWITNESS); + sx_init_flags(&mac_policy_sx, "mac_policy_sx", SX_NOWITNESS); +#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; +} + +/* + * Given a policy, derive from its set of non-NULL label init methods what + * object types the policy is interested in. + */ +static uint64_t +mac_policy_getlabeled(struct mac_policy_conf *mpc) +{ + uint64_t labeled; + +#define MPC_FLAG(method, flag) \ + if (mpc->mpc_ops->mpo_ ## method != NULL) \ + labeled |= (flag); \ + + labeled = 0; + MPC_FLAG(cred_init_label, MPC_OBJECT_CRED); + MPC_FLAG(proc_init_label, MPC_OBJECT_PROC); + MPC_FLAG(vnode_init_label, MPC_OBJECT_VNODE); + MPC_FLAG(inpcb_init_label, MPC_OBJECT_INPCB); + MPC_FLAG(socket_init_label, MPC_OBJECT_SOCKET); + MPC_FLAG(devfs_init_label, MPC_OBJECT_DEVFS); + MPC_FLAG(mbuf_init_label, MPC_OBJECT_MBUF); + MPC_FLAG(ipq_init_label, MPC_OBJECT_IPQ); + MPC_FLAG(ifnet_init_label, MPC_OBJECT_IFNET); + MPC_FLAG(bpfdesc_init_label, MPC_OBJECT_BPFDESC); + MPC_FLAG(pipe_init_label, MPC_OBJECT_PIPE); + MPC_FLAG(mount_init_label, MPC_OBJECT_MOUNT); + MPC_FLAG(posixsem_init_label, MPC_OBJECT_POSIXSEM); + MPC_FLAG(posixshm_init_label, MPC_OBJECT_POSIXSHM); + MPC_FLAG(sysvmsg_init_label, MPC_OBJECT_SYSVMSG); + MPC_FLAG(sysvmsq_init_label, MPC_OBJECT_SYSVMSQ); + MPC_FLAG(sysvsem_init_label, MPC_OBJECT_SYSVSEM); + MPC_FLAG(sysvshm_init_label, MPC_OBJECT_SYSVSHM); + MPC_FLAG(syncache_init_label, MPC_OBJECT_SYNCACHE); + MPC_FLAG(ip6q_init_label, MPC_OBJECT_IP6Q); + +#undef MPC_FLAG + return (labeled); +} + +/* + * When policies are loaded or unloaded, walk the list of registered policies + * and built mac_labeled, a bitmask representing the union of all objects + * requiring labels across all policies. + */ +static void +mac_policy_update(void) +{ + struct mac_policy_conf *mpc; + + mac_policy_xlock_assert(); + + mac_labeled = 0; + mac_policy_count = 0; + LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) { + mac_labeled |= mac_policy_getlabeled(mpc); + mac_policy_count++; + } + LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { + mac_labeled |= mac_policy_getlabeled(mpc); + mac_policy_count++; + } +} + +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_xlock(); + + /* + * 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_update(); + + SDT_PROBE(mac, kernel, policy, register, mpc, 0, 0, 0, 0); + printf("Security policy loaded: %s (%s)\n", mpc->mpc_fullname, + mpc->mpc_name); + +out: + mac_policy_xunlock(); + 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_xlock(); + if ((mpc->mpc_runtime_flags & MPC_RUNTIME_FLAG_REGISTERED) == 0) { + mac_policy_xunlock(); + return (0); + } +#if 0 + /* + * Don't allow unloading modules with private data. + */ + if (mpc->mpc_field_off != NULL) { + mac_policy_xunlock(); + 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_xunlock(); + 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_update(); + mac_policy_xunlock(); + + SDT_PROBE(mac, kernel, policy, unregister, mpc, 0, 0, 0, 0); + 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 + + SDT_PROBE(mac, kernel, policy, modevent, type, mpc, 0, 0, 0); + 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..92aedea --- /dev/null +++ b/sys/security/mac/mac_framework.h @@ -0,0 +1,451 @@ +/*- + * Copyright (c) 1999-2002, 2007-2011 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"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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 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_setaudit(struct ucred *cred, struct auditinfo *ai); +int mac_cred_check_setaudit_addr(struct ucred *cred, + struct auditinfo_addr *aia); +int mac_cred_check_setauid(struct ucred *cred, uid_t auid); +int mac_cred_check_setegid(struct ucred *cred, gid_t egid); +int mac_cred_check_seteuid(struct ucred *cred, uid_t euid); +int mac_cred_check_setgid(struct ucred *cred, gid_t gid); +int mac_cred_check_setgroups(struct ucred *cred, int ngroups, + gid_t *gidset); +int mac_cred_check_setregid(struct ucred *cred, gid_t rgid, gid_t egid); +int mac_cred_check_setresgid(struct ucred *cred, gid_t rgid, gid_t egid, + gid_t sgid); +int mac_cred_check_setresuid(struct ucred *cred, uid_t ruid, uid_t euid, + uid_t suid); +int mac_cred_check_setreuid(struct ucred *cred, uid_t ruid, uid_t euid); +int mac_cred_check_setuid(struct ucred *cred, uid_t uid); +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_setmode(struct ucred *cred, struct ksem *ks, + mode_t mode); +int mac_posixsem_check_setowner(struct ucred *cred, struct ksem *ks, + uid_t uid, gid_t gid); +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_create(struct ucred *cred, const char *path); +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, + accmode_t accmode); +int mac_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd, + mode_t mode); +int mac_posixshm_check_setowner(struct ucred *cred, struct shmfd *shmfd, + uid_t uid, gid_t gid); +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_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); +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); +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..5753bb6 --- /dev/null +++ b/sys/security/mac/mac_inet.c @@ -0,0 +1,508 @@ +/*- + * Copyright (c) 1999-2002, 2007, 2009 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"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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_kdtrace.h" +#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/sdt.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); + if (flag & M_WAITOK) + MAC_POLICY_CHECK(inpcb_init_label, label, flag); + else + MAC_POLICY_CHECK_NOSLEEP(inpcb_init_label, label, flag); + if (error) { + MAC_POLICY_PERFORM_NOSLEEP(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); + + if (flag & M_WAITOK) + MAC_POLICY_CHECK(ipq_init_label, label, flag); + else + MAC_POLICY_CHECK_NOSLEEP(ipq_init_label, label, flag); + if (error) { + MAC_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(inpcb_create, so, so->so_label, inp, + inp->inp_label); +} + +void +mac_ipq_reassemble(struct ipq *q, struct mbuf *m) +{ + struct label *label; + + if (mac_policy_count == 0) + return; + + label = mac_mbuf_to_label(m); + + MAC_POLICY_PERFORM_NOSLEEP(ipq_reassemble, q, q->ipq_label, m, + label); +} + +void +mac_netinet_fragment(struct mbuf *m, struct mbuf *frag) +{ + struct label *mlabel, *fraglabel; + + if (mac_policy_count == 0) + return; + + mlabel = mac_mbuf_to_label(m); + fraglabel = mac_mbuf_to_label(frag); + + MAC_POLICY_PERFORM_NOSLEEP(netinet_fragment, m, mlabel, frag, + fraglabel); +} + +void +mac_ipq_create(struct mbuf *m, struct ipq *q) +{ + struct label *label; + + if (mac_policy_count == 0) + return; + + label = mac_mbuf_to_label(m); + + MAC_POLICY_PERFORM_NOSLEEP(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); + + if (mac_policy_count == 0) + return; + + mlabel = mac_mbuf_to_label(m); + + MAC_POLICY_PERFORM_NOSLEEP(inpcb_create_mbuf, inp, inp->inp_label, m, + mlabel); +} + +int +mac_ipq_match(struct mbuf *m, struct ipq *q) +{ + struct label *label; + int result; + + if (mac_policy_count == 0) + return (1); + + label = mac_mbuf_to_label(m); + + result = 1; + MAC_POLICY_BOOLEAN_NOSLEEP(ipq_match, &&, m, label, q, q->ipq_label); + + return (result); +} + +void +mac_netinet_arp_send(struct ifnet *ifp, struct mbuf *m) +{ + struct label *mlabel; + + if (mac_policy_count == 0) + return; + + mlabel = mac_mbuf_to_label(m); + + MAC_IFNET_LOCK(ifp); + MAC_POLICY_PERFORM_NOSLEEP(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; + + if (mac_policy_count == 0) + return; + + mrecvlabel = mac_mbuf_to_label(mrecv); + msendlabel = mac_mbuf_to_label(msend); + + MAC_POLICY_PERFORM_NOSLEEP(netinet_icmp_reply, mrecv, mrecvlabel, + msend, msendlabel); +} + +void +mac_netinet_icmp_replyinplace(struct mbuf *m) +{ + struct label *label; + + if (mac_policy_count == 0) + return; + + label = mac_mbuf_to_label(m); + + MAC_POLICY_PERFORM_NOSLEEP(netinet_icmp_replyinplace, m, label); +} + +void +mac_netinet_igmp_send(struct ifnet *ifp, struct mbuf *m) +{ + struct label *mlabel; + + if (mac_policy_count == 0) + return; + + mlabel = mac_mbuf_to_label(m); + + MAC_IFNET_LOCK(ifp); + MAC_POLICY_PERFORM_NOSLEEP(netinet_igmp_send, ifp, ifp->if_label, m, + mlabel); + MAC_IFNET_UNLOCK(ifp); +} + +void +mac_netinet_tcp_reply(struct mbuf *m) +{ + struct label *label; + + if (mac_policy_count == 0) + return; + + label = mac_mbuf_to_label(m); + + MAC_POLICY_PERFORM_NOSLEEP(netinet_tcp_reply, m, label); +} + +void +mac_ipq_update(struct mbuf *m, struct ipq *q) +{ + struct label *label; + + if (mac_policy_count == 0) + return; + + label = mac_mbuf_to_label(m); + + MAC_POLICY_PERFORM_NOSLEEP(ipq_update, m, label, q, q->ipq_label); +} + +MAC_CHECK_PROBE_DEFINE2(inpcb_check_deliver, "struct inpcb *", + "struct mbuf *"); + +int +mac_inpcb_check_deliver(struct inpcb *inp, struct mbuf *m) +{ + struct label *label; + int error; + + M_ASSERTPKTHDR(m); + + if (mac_policy_count == 0) + return (0); + + label = mac_mbuf_to_label(m); + + MAC_POLICY_CHECK_NOSLEEP(inpcb_check_deliver, inp, inp->inp_label, m, + label); + MAC_CHECK_PROBE2(inpcb_check_deliver, error, inp, m); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(inpcb_check_visible, "struct ucred *", + "struct inpcb *"); + +int +mac_inpcb_check_visible(struct ucred *cred, struct inpcb *inp) +{ + int error; + + INP_LOCK_ASSERT(inp); + + MAC_POLICY_CHECK_NOSLEEP(inpcb_check_visible, cred, inp, + inp->inp_label); + MAC_CHECK_PROBE2(inpcb_check_visible, error, cred, inp); + + return (error); +} + +void +mac_inpcb_sosetlabel(struct socket *so, struct inpcb *inp) +{ + + INP_WLOCK_ASSERT(inp); + SOCK_LOCK_ASSERT(so); + + MAC_POLICY_PERFORM_NOSLEEP(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); + + if (mac_policy_count == 0) + return; + + mrecvlabel = mac_mbuf_to_label(mrecv); + msendlabel = mac_mbuf_to_label(msend); + + MAC_POLICY_PERFORM_NOSLEEP(netinet_firewall_reply, mrecv, mrecvlabel, + msend, msendlabel); +} + +void +mac_netinet_firewall_send(struct mbuf *m) +{ + struct label *label; + + M_ASSERTPKTHDR(m); + + if (mac_policy_count == 0) + return; + + label = mac_mbuf_to_label(m); + + MAC_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(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_POLICY_CHECK_NOSLEEP(syncache_init_label, *label, + M_NOWAIT); + if (error) { + MAC_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(syncache_create, label, inp); +} + +void +mac_syncache_create_mbuf(struct label *sc_label, struct mbuf *m) +{ + struct label *mlabel; + + M_ASSERTPKTHDR(m); + + if (mac_policy_count == 0) + return; + + mlabel = mac_mbuf_to_label(m); + + MAC_POLICY_PERFORM_NOSLEEP(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..a080a74 --- /dev/null +++ b/sys/security/mac/mac_inet6.c @@ -0,0 +1,188 @@ +/*- + * Copyright (c) 2007-2009 Robert N. M. Watson + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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); + + if (flag & M_WAITOK) + MAC_POLICY_CHECK(ip6q_init_label, label, flag); + else + MAC_POLICY_CHECK_NOSLEEP(ip6q_init_label, label, flag); + if (error) { + MAC_POLICY_PERFORM_NOSLEEP(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_IP6Q) { + 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_POLICY_PERFORM_NOSLEEP(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; + + if (mac_policy_count == 0) + return; + + label = mac_mbuf_to_label(m); + + MAC_POLICY_PERFORM_NOSLEEP(ip6q_reassemble, q6, q6->ip6q_label, m, + label); +} + +void +mac_ip6q_create(struct mbuf *m, struct ip6q *q6) +{ + struct label *label; + + if (mac_policy_count == 0) + return; + + label = mac_mbuf_to_label(m); + + MAC_POLICY_PERFORM_NOSLEEP(ip6q_create, m, label, q6, + q6->ip6q_label); +} + +int +mac_ip6q_match(struct mbuf *m, struct ip6q *q6) +{ + struct label *label; + int result; + + if (mac_policy_count == 0) + return (1); + + label = mac_mbuf_to_label(m); + + result = 1; + MAC_POLICY_BOOLEAN_NOSLEEP(ip6q_match, &&, m, label, q6, + q6->ip6q_label); + + return (result); +} + +void +mac_ip6q_update(struct mbuf *m, struct ip6q *q6) +{ + struct label *label; + + if (mac_policy_count == 0) + return; + + label = mac_mbuf_to_label(m); + + MAC_POLICY_PERFORM_NOSLEEP(ip6q_update, m, label, q6, + q6->ip6q_label); +} + +void +mac_netinet6_nd6_send(struct ifnet *ifp, struct mbuf *m) +{ + struct label *mlabel; + + if (mac_policy_count == 0) + return; + + mlabel = mac_mbuf_to_label(m); + + MAC_POLICY_PERFORM_NOSLEEP(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..7244477 --- /dev/null +++ b/sys/security/mac/mac_internal.h @@ -0,0 +1,516 @@ +/*- + * Copyright (c) 1999-2002, 2006, 2009 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. + * Copyright (c) 2009 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 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"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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 + +#include <sys/lock.h> +#include <sys/rmlock.h> + +/* + * MAC Framework sysctl namespace. + */ +#ifdef SYSCTL_DECL +SYSCTL_DECL(_security_mac); +#endif /* SYSCTL_DECL */ + +/* + * MAC Framework SDT DTrace probe namespace, macros for declaring entry + * point probes, macros for invoking them. + */ +#ifdef SDT_PROVIDER_DECLARE +SDT_PROVIDER_DECLARE(mac); /* MAC Framework-level events. */ +SDT_PROVIDER_DECLARE(mac_framework); /* Entry points to MAC. */ + +#define MAC_CHECK_PROBE_DEFINE4(name, arg0, arg1, arg2, arg3) \ + SDT_PROBE_DEFINE5(mac_framework, kernel, name, mac_check_err, \ + mac-check-ok, "int", arg0, arg1, arg2, arg3); \ + SDT_PROBE_DEFINE5(mac_framework, kernel, name, mac_check_ok, \ + mac-check-ok, "int", arg0, arg1, arg2, arg3); + +#define MAC_CHECK_PROBE_DEFINE3(name, arg0, arg1, arg2) \ + SDT_PROBE_DEFINE4(mac_framework, kernel, name, mac_check_err, \ + mac-check-err, "int", arg0, arg1, arg2); \ + SDT_PROBE_DEFINE4(mac_framework, kernel, name, mac_check_ok, \ + mac-check-ok, "int", arg0, arg1, arg2); + +#define MAC_CHECK_PROBE_DEFINE2(name, arg0, arg1) \ + SDT_PROBE_DEFINE3(mac_framework, kernel, name, mac_check_err, \ + mac-check-err, "int", arg0, arg1); \ + SDT_PROBE_DEFINE3(mac_framework, kernel, name, mac_check_ok, \ + mac-check-ok, "int", arg0, arg1); + +#define MAC_CHECK_PROBE_DEFINE1(name, arg0) \ + SDT_PROBE_DEFINE2(mac_framework, kernel, name, mac_check_err, \ + mac-check-err, "int", arg0); \ + SDT_PROBE_DEFINE2(mac_framework, kernel, name, mac_check_ok, \ + mac-check-ok, "int", arg0); + +#define MAC_CHECK_PROBE4(name, error, arg0, arg1, arg2, arg3) do { \ + if (error) { \ + SDT_PROBE(mac_framework, kernel, name, mac_check_err, \ + error, arg0, arg1, arg2, arg3); \ + } else { \ + SDT_PROBE(mac_framework, kernel, name, mac_check_ok, \ + 0, arg0, arg1, arg2, arg3); \ + } \ +} while (0) + +#define MAC_CHECK_PROBE3(name, error, arg0, arg1, arg2) \ + MAC_CHECK_PROBE4(name, error, arg0, arg1, arg2, 0) +#define MAC_CHECK_PROBE2(name, error, arg0, arg1) \ + MAC_CHECK_PROBE3(name, error, arg0, arg1, 0) +#define MAC_CHECK_PROBE1(name, error, arg0) \ + MAC_CHECK_PROBE2(name, error, arg0, 0) +#endif + +#define MAC_GRANT_PROBE_DEFINE2(name, arg0, arg1) \ + SDT_PROBE_DEFINE3(mac_framework, kernel, name, mac_grant_err, \ + mac-grant-err, "int", arg0, arg1); \ + SDT_PROBE_DEFINE3(mac_framework, kernel, name, mac_grant_ok, \ + mac-grant-ok, "INT", arg0, arg1); + +#define MAC_GRANT_PROBE2(name, error, arg0, arg1) do { \ + if (error) { \ + SDT_PROBE(mac_framework, kernel, name, mac_grant_err, \ + error, arg0, arg1, 0, 0); \ + } else { \ + SDT_PROBE(mac_framework, kernel, name, mac_grant_ok, \ + error, arg0, arg1, 0, 0); \ + } \ +} while (0) + +/* + * 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]; +}; + + +/* + * Flags for mac_labeled, a bitmask of object types need across the union of + * all policies currently registered with the MAC Framework, used to key + * whether or not labels are allocated and constructors for the type are + * invoked. + */ +#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 + +/* + * MAC Framework global variables. + */ +extern struct mac_policy_list_head mac_policy_list; +extern struct mac_policy_list_head mac_static_policy_list; +extern u_int mac_policy_count; +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_slock_nosleep(struct rm_priotracker *tracker); +void mac_policy_slock_sleep(void); +void mac_policy_sunlock_nosleep(struct rm_priotracker *tracker); +void mac_policy_sunlock_sleep(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 Framework composition macros invoke all registered MAC policies for a + * specific entry point. They come in two forms: one which permits policies + * to sleep/block, and another that does not. + * + * MAC_POLICY_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_POLICY_CHECK(check, args...) do { \ + struct mac_policy_conf *mpc; \ + \ + 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 (!LIST_EMPTY(&mac_policy_list)) { \ + mac_policy_slock_sleep(); \ + 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_sunlock_sleep(); \ + } \ +} while (0) + +#define MAC_POLICY_CHECK_NOSLEEP(check, args...) do { \ + struct mac_policy_conf *mpc; \ + \ + 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 (!LIST_EMPTY(&mac_policy_list)) { \ + struct rm_priotracker tracker; \ + \ + mac_policy_slock_nosleep(&tracker); \ + 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_sunlock_nosleep(&tracker); \ + } \ +} while (0) + +/* + * MAC_POLICY_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_POLICY_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_POLICY_GRANT_NOSLEEP(check, args...) do { \ + struct mac_policy_conf *mpc; \ + \ + 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 (!LIST_EMPTY(&mac_policy_list)) { \ + struct rm_priotracker tracker; \ + \ + mac_policy_slock_nosleep(&tracker); \ + 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_sunlock_nosleep(&tracker); \ + } \ +} while (0) + +/* + * MAC_POLICY_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_POLICY_BOOLEAN(operation, composition, args...) do { \ + struct mac_policy_conf *mpc; \ + \ + 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 (!LIST_EMPTY(&mac_policy_list)) { \ + mac_policy_slock_sleep(); \ + 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_sunlock_sleep(); \ + } \ +} while (0) + +#define MAC_POLICY_BOOLEAN_NOSLEEP(operation, composition, args...) do {\ + struct mac_policy_conf *mpc; \ + \ + 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 (!LIST_EMPTY(&mac_policy_list)) { \ + struct rm_priotracker tracker; \ + \ + mac_policy_slock_nosleep(&tracker); \ + 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_sunlock_nosleep(&tracker); \ + } \ +} while (0) + +/* + * MAC_POLICY_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_POLICY_EXTERNALIZE, and an error is returned if any element is matched + * by no policy. + */ +#define MAC_POLICY_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_POLICY_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_POLICY_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_POLICY_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_POLICY_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_POLICY_PERFORM performs the designated operation by walking the policy + * module list and invoking that operation for each policy. + */ +#define MAC_POLICY_PERFORM(operation, args...) do { \ + struct mac_policy_conf *mpc; \ + \ + LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) { \ + if (mpc->mpc_ops->mpo_ ## operation != NULL) \ + mpc->mpc_ops->mpo_ ## operation (args); \ + } \ + if (!LIST_EMPTY(&mac_policy_list)) { \ + mac_policy_slock_sleep(); \ + LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ + if (mpc->mpc_ops->mpo_ ## operation != NULL) \ + mpc->mpc_ops->mpo_ ## operation (args); \ + } \ + mac_policy_sunlock_sleep(); \ + } \ +} while (0) + +#define MAC_POLICY_PERFORM_NOSLEEP(operation, args...) do { \ + struct mac_policy_conf *mpc; \ + \ + LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) { \ + if (mpc->mpc_ops->mpo_ ## operation != NULL) \ + mpc->mpc_ops->mpo_ ## operation (args); \ + } \ + if (!LIST_EMPTY(&mac_policy_list)) { \ + struct rm_priotracker tracker; \ + \ + mac_policy_slock_nosleep(&tracker); \ + LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ + if (mpc->mpc_ops->mpo_ ## operation != NULL) \ + mpc->mpc_ops->mpo_ ## operation (args); \ + } \ + mac_policy_sunlock_nosleep(&tracker); \ + } \ +} 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..b7e4c54 --- /dev/null +++ b/sys/security/mac/mac_net.c @@ -0,0 +1,502 @@ +/*- + * Copyright (c) 1999-2002, 2009 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. + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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_kdtrace.h" +#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/sdt.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_POLICY_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_POLICY_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); + + if (flag & M_WAITOK) + MAC_POLICY_CHECK(mbuf_init_label, label, flag); + else + MAC_POLICY_CHECK_NOSLEEP(mbuf_init_label, label, flag); + if (error) { + MAC_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(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; + + if (mac_policy_count == 0) + return; + + src_label = mac_mbuf_to_label(m_from); + dest_label = mac_mbuf_to_label(m_to); + + MAC_POLICY_PERFORM_NOSLEEP(mbuf_copy_label, src_label, dest_label); +} + +static void +mac_ifnet_copy_label(struct label *src, struct label *dest) +{ + + MAC_POLICY_PERFORM_NOSLEEP(ifnet_copy_label, src, dest); +} + +static int +mac_ifnet_externalize_label(struct label *label, char *elements, + char *outbuf, size_t outbuflen) +{ + int error; + + MAC_POLICY_EXTERNALIZE(ifnet, label, elements, outbuf, outbuflen); + + return (error); +} + +static int +mac_ifnet_internalize_label(struct label *label, char *string) +{ + int error; + + MAC_POLICY_INTERNALIZE(ifnet, label, string); + + return (error); +} + +void +mac_ifnet_create(struct ifnet *ifp) +{ + + if (mac_policy_count == 0) + return; + + MAC_IFNET_LOCK(ifp); + MAC_POLICY_PERFORM_NOSLEEP(ifnet_create, ifp, ifp->if_label); + MAC_IFNET_UNLOCK(ifp); +} + +void +mac_bpfdesc_create(struct ucred *cred, struct bpf_d *d) +{ + + MAC_POLICY_PERFORM_NOSLEEP(bpfdesc_create, cred, d, d->bd_label); +} + +void +mac_bpfdesc_create_mbuf(struct bpf_d *d, struct mbuf *m) +{ + struct label *label; + + /* Assume reader lock is enough. */ + BPFD_LOCK_ASSERT(d); + + if (mac_policy_count == 0) + return; + + label = mac_mbuf_to_label(m); + + MAC_POLICY_PERFORM_NOSLEEP(bpfdesc_create_mbuf, d, d->bd_label, m, + label); +} + +void +mac_ifnet_create_mbuf(struct ifnet *ifp, struct mbuf *m) +{ + struct label *label; + + if (mac_policy_count == 0) + return; + + label = mac_mbuf_to_label(m); + + MAC_IFNET_LOCK(ifp); + MAC_POLICY_PERFORM_NOSLEEP(ifnet_create_mbuf, ifp, ifp->if_label, m, + label); + MAC_IFNET_UNLOCK(ifp); +} + +MAC_CHECK_PROBE_DEFINE2(bpfdesc_check_receive, "struct bpf_d *", + "struct ifnet *"); + +int +mac_bpfdesc_check_receive(struct bpf_d *d, struct ifnet *ifp) +{ + int error; + + /* Assume reader lock is enough. */ + BPFD_LOCK_ASSERT(d); + + if (mac_policy_count == 0) + return (0); + + MAC_IFNET_LOCK(ifp); + MAC_POLICY_CHECK_NOSLEEP(bpfdesc_check_receive, d, d->bd_label, ifp, + ifp->if_label); + MAC_CHECK_PROBE2(bpfdesc_check_receive, error, d, ifp); + MAC_IFNET_UNLOCK(ifp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(ifnet_check_transmit, "struct ifnet *", + "struct mbuf *"); + +int +mac_ifnet_check_transmit(struct ifnet *ifp, struct mbuf *m) +{ + struct label *label; + int error; + + M_ASSERTPKTHDR(m); + + if (mac_policy_count == 0) + return (0); + + label = mac_mbuf_to_label(m); + + MAC_IFNET_LOCK(ifp); + MAC_POLICY_CHECK_NOSLEEP(ifnet_check_transmit, ifp, ifp->if_label, m, + label); + MAC_CHECK_PROBE2(ifnet_check_transmit, error, ifp, m); + 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_POLICY_CHECK_NOSLEEP(ifnet_check_relabel, cred, ifp, + ifp->if_label, intlabel); + if (error) { + MAC_IFNET_UNLOCK(ifp); + mac_ifnet_label_free(intlabel); + return (error); + } + + MAC_POLICY_PERFORM_NOSLEEP(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..91c3cff --- /dev/null +++ b/sys/security/mac/mac_pipe.c @@ -0,0 +1,260 @@ +/*- + * Copyright (c) 2002-2003 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * Copyright (c) 2009 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. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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_kdtrace.h" +#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/sdt.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_POLICY_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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(pipe_copy_label, src, dest); +} + +int +mac_pipe_externalize_label(struct label *label, char *elements, + char *outbuf, size_t outbuflen) +{ + int error; + + MAC_POLICY_EXTERNALIZE(pipe, label, elements, outbuf, outbuflen); + + return (error); +} + +int +mac_pipe_internalize_label(struct label *label, char *string) +{ + int error; + + MAC_POLICY_INTERNALIZE(pipe, label, string); + + return (error); +} + +void +mac_pipe_create(struct ucred *cred, struct pipepair *pp) +{ + + MAC_POLICY_PERFORM_NOSLEEP(pipe_create, cred, pp, pp->pp_label); +} + +static void +mac_pipe_relabel(struct ucred *cred, struct pipepair *pp, + struct label *newlabel) +{ + + MAC_POLICY_PERFORM_NOSLEEP(pipe_relabel, cred, pp, pp->pp_label, + newlabel); +} + +MAC_CHECK_PROBE_DEFINE4(pipe_check_ioctl, "struct ucred *", + "struct pipepair *", "unsigned long", "void *"); + +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_POLICY_CHECK_NOSLEEP(pipe_check_ioctl, cred, pp, pp->pp_label, + cmd, data); + MAC_CHECK_PROBE4(pipe_check_ioctl, error, cred, pp, cmd, data); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(pipe_check_poll, "struct ucred *", + "struct pipepair *"); + +int +mac_pipe_check_poll(struct ucred *cred, struct pipepair *pp) +{ + int error; + + mtx_assert(&pp->pp_mtx, MA_OWNED); + + MAC_POLICY_CHECK_NOSLEEP(pipe_check_poll, cred, pp, pp->pp_label); + MAC_CHECK_PROBE2(pipe_check_poll, error, cred, pp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(pipe_check_read, "struct ucred *", + "struct pipepair *"); + +int +mac_pipe_check_read(struct ucred *cred, struct pipepair *pp) +{ + int error; + + mtx_assert(&pp->pp_mtx, MA_OWNED); + + MAC_POLICY_CHECK_NOSLEEP(pipe_check_read, cred, pp, pp->pp_label); + MAC_CHECK_PROBE2(pipe_check_read, error, cred, pp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(pipe_check_relabel, "struct ucred *", + "struct pipepair *", "struct label *"); + +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_POLICY_CHECK_NOSLEEP(pipe_check_relabel, cred, pp, pp->pp_label, + newlabel); + MAC_CHECK_PROBE3(pipe_check_relabel, error, cred, pp, newlabel); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(pipe_check_stat, "struct ucred *", + "struct pipepair *"); + +int +mac_pipe_check_stat(struct ucred *cred, struct pipepair *pp) +{ + int error; + + mtx_assert(&pp->pp_mtx, MA_OWNED); + + MAC_POLICY_CHECK_NOSLEEP(pipe_check_stat, cred, pp, pp->pp_label); + MAC_CHECK_PROBE2(pipe_check_stat, error, cred, pp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(pipe_check_write, "struct ucred *", + "struct pipepair *"); + +int +mac_pipe_check_write(struct ucred *cred, struct pipepair *pp) +{ + int error; + + mtx_assert(&pp->pp_mtx, MA_OWNED); + + MAC_POLICY_CHECK_NOSLEEP(pipe_check_write, cred, pp, pp->pp_label); + MAC_CHECK_PROBE2(pipe_check_write, error, cred, pp); + + 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..090dc40 --- /dev/null +++ b/sys/security/mac/mac_policy.h @@ -0,0 +1,1037 @@ +/*- + * Copyright (c) 1999-2002, 2007-2011 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"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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 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_setaudit_t)(struct ucred *cred, + struct auditinfo *ai); +typedef int (*mpo_cred_check_setaudit_addr_t)(struct ucred *cred, + struct auditinfo_addr *aia); +typedef int (*mpo_cred_check_setauid_t)(struct ucred *cred, uid_t auid); +typedef int (*mpo_cred_check_setegid_t)(struct ucred *cred, gid_t egid); +typedef int (*mpo_cred_check_seteuid_t)(struct ucred *cred, uid_t euid); +typedef int (*mpo_cred_check_setgid_t)(struct ucred *cred, gid_t gid); +typedef int (*mpo_cred_check_setgroups_t)(struct ucred *cred, int ngroups, + gid_t *gidset); +typedef int (*mpo_cred_check_setregid_t)(struct ucred *cred, gid_t rgid, + gid_t egid); +typedef int (*mpo_cred_check_setresgid_t)(struct ucred *cred, gid_t rgid, + gid_t egid, gid_t sgid); +typedef int (*mpo_cred_check_setresuid_t)(struct ucred *cred, uid_t ruid, + uid_t euid, uid_t suid); +typedef int (*mpo_cred_check_setreuid_t)(struct ucred *cred, uid_t ruid, + uid_t euid); +typedef int (*mpo_cred_check_setuid_t)(struct ucred *cred, uid_t uid); +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_setmode_t)(struct ucred *cred, + struct ksem *ks, struct label *shmlabel, + mode_t mode); +typedef int (*mpo_posixsem_check_setowner_t)(struct ucred *cred, + struct ksem *ks, struct label *shmlabel, + uid_t uid, gid_t gid); +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_create_t)(struct ucred *cred, + const char *path); +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, + accmode_t accmode); +typedef int (*mpo_posixshm_check_setmode_t)(struct ucred *cred, + struct shmfd *shmfd, struct label *shmlabel, + mode_t mode); +typedef int (*mpo_posixshm_check_setowner_t)(struct ucred *cred, + struct shmfd *shmfd, struct label *shmlabel, + uid_t uid, gid_t gid); +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_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); +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); +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_setaudit_t mpo_cred_check_setaudit; + mpo_cred_check_setaudit_addr_t mpo_cred_check_setaudit_addr; + mpo_cred_check_setauid_t mpo_cred_check_setauid; + mpo_cred_check_setuid_t mpo_cred_check_setuid; + mpo_cred_check_seteuid_t mpo_cred_check_seteuid; + mpo_cred_check_setgid_t mpo_cred_check_setgid; + mpo_cred_check_setegid_t mpo_cred_check_setegid; + mpo_cred_check_setgroups_t mpo_cred_check_setgroups; + mpo_cred_check_setreuid_t mpo_cred_check_setreuid; + mpo_cred_check_setregid_t mpo_cred_check_setregid; + mpo_cred_check_setresuid_t mpo_cred_check_setresuid; + mpo_cred_check_setresgid_t mpo_cred_check_setresgid; + 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_setmode_t mpo_posixsem_check_setmode; + mpo_posixsem_check_setowner_t mpo_posixsem_check_setowner; + 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_create_t mpo_posixshm_check_create; + mpo_posixshm_check_mmap_t mpo_posixshm_check_mmap; + mpo_posixshm_check_open_t mpo_posixshm_check_open; + mpo_posixshm_check_setmode_t mpo_posixshm_check_setmode; + mpo_posixshm_check_setowner_t mpo_posixshm_check_setowner; + 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_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_spare2; /* Spare. */ + uint64_t _mpc_spare3; /* Spare. */ + void *_mpc_spare4; /* 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 + +/*- + * 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) \ + 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, \ + }; \ + 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..461e5db --- /dev/null +++ b/sys/security/mac/mac_posix_sem.c @@ -0,0 +1,232 @@ +/*- + * Copyright (c) 2003-2006 SPARTA, Inc. + * Copyright (c) 2009 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. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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_kdtrace.h" +#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/sdt.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_POLICY_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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(posixsem_create, cred, ks, ks->ks_label); +} + +MAC_CHECK_PROBE_DEFINE2(posixsem_check_open, "struct ucred *", + "struct ksem *"); + +int +mac_posixsem_check_open(struct ucred *cred, struct ksem *ks) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(posixsem_check_open, cred, ks, + ks->ks_label); + MAC_CHECK_PROBE2(posixsem_check_open, error, cred, ks); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(posixsem_check_getvalue, "struct ucred *", + "struct ucred *", "struct ksem *"); + +int +mac_posixsem_check_getvalue(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(posixsem_check_getvalue, active_cred, + file_cred, ks, ks->ks_label); + MAC_CHECK_PROBE3(posixsem_check_getvalue, error, active_cred, + file_cred, ks); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(posixsem_check_post, "struct ucred *", + "struct ucred *", "struct ksem *"); + +int +mac_posixsem_check_post(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(posixsem_check_post, active_cred, file_cred, + ks, ks->ks_label); + MAC_CHECK_PROBE3(posixsem_check_post, error, active_cred, file_cred, + ks); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(posixsem_check_stat, "struct ucred *", + "struct ucred *", "struct ksem *"); + +int +mac_posixsem_check_stat(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(posixsem_check_stat, active_cred, file_cred, + ks, ks->ks_label); + MAC_CHECK_PROBE3(posixsem_check_stat, error, active_cred, file_cred, + ks); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(posixsem_check_unlink, "struct ucred *", + "struct ksem *"); + +int +mac_posixsem_check_unlink(struct ucred *cred, struct ksem *ks) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(posixsem_check_unlink, cred, ks, + ks->ks_label); + MAC_CHECK_PROBE2(posixsem_check_unlink, error, cred, ks); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(posixsem_check_wait, "struct ucred *", + "struct ucred *", "struct ksem *"); + +int +mac_posixsem_check_wait(struct ucred *active_cred, struct ucred *file_cred, + struct ksem *ks) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(posixsem_check_wait, active_cred, file_cred, + ks, ks->ks_label); + MAC_CHECK_PROBE3(posixsem_check_wait, error, active_cred, file_cred, + ks); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(posixsem_check_setmode, "struct ucred *", + "struct ksem *", "mode_t"); + +int +mac_posixsem_check_setmode(struct ucred *cred, struct ksem *ks, mode_t mode) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(posixsem_check_setmode, cred, ks, + ks->ks_label, mode); + MAC_CHECK_PROBE3(posixsem_check_setmode, error, cred, ks, mode); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(posixsem_check_setowner, "struct ucred *", + "struct ks *", "uid_t", "gid_t"); + +int +mac_posixsem_check_setowner(struct ucred *cred, struct ksem *ks, uid_t uid, + gid_t gid) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(posixsem_check_setowner, cred, ks, + ks->ks_label, uid, gid); + MAC_CHECK_PROBE4(posixsem_check_setowner, error, cred, ks, + uid, gid); + + 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..d5d15fc --- /dev/null +++ b/sys/security/mac/mac_posix_shm.c @@ -0,0 +1,230 @@ +/*- + * Copyright (c) 2003-2006 SPARTA, Inc. + * Copyright (c) 2009-2011 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. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). * + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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_kdtrace.h" +#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/sdt.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_POLICY_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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(posixshm_create, cred, shmfd, + shmfd->shm_label); +} + +MAC_CHECK_PROBE_DEFINE2(posixshm_check_create, "struct ucred *", + "const char *"); + +int +mac_posixshm_check_create(struct ucred *cred, const char *path) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(posixshm_check_create, cred, path); + MAC_CHECK_PROBE2(posixshm_check_create, error, cred, path); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(posixshm_check_mmap, "struct ucred *", + "struct shmfd *", "int", "int"); + +int +mac_posixshm_check_mmap(struct ucred *cred, struct shmfd *shmfd, int prot, + int flags) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(posixshm_check_mmap, cred, shmfd, + shmfd->shm_label, prot, flags); + MAC_CHECK_PROBE4(posixshm_check_mmap, error, cred, shmfd, prot, + flags); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(posixshm_check_open, "struct ucred *", + "struct shmfd *", "accmode_t accmode"); + +int +mac_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd, + accmode_t accmode) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(posixshm_check_open, cred, shmfd, + shmfd->shm_label, accmode); + MAC_CHECK_PROBE3(posixshm_check_open, error, cred, shmfd, accmode); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(posixshm_check_stat, "struct ucred *", + "struct ucred *", "struct shmfd *"); + +int +mac_posixshm_check_stat(struct ucred *active_cred, struct ucred *file_cred, + struct shmfd *shmfd) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(posixshm_check_stat, active_cred, file_cred, + shmfd, shmfd->shm_label); + MAC_CHECK_PROBE3(posixshm_check_stat, error, active_cred, file_cred, + shmfd); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(posixshm_check_truncate, "struct ucred *", + "struct ucred *", "struct shmfd *"); + +int +mac_posixshm_check_truncate(struct ucred *active_cred, struct ucred *file_cred, + struct shmfd *shmfd) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(posixshm_check_truncate, active_cred, + file_cred, shmfd, shmfd->shm_label); + MAC_CHECK_PROBE3(posixshm_check_truncate, error, active_cred, + file_cred, shmfd); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(posixshm_check_unlink, "struct ucred *", + "struct shmfd *"); + +int +mac_posixshm_check_unlink(struct ucred *cred, struct shmfd *shmfd) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(posixshm_check_unlink, cred, shmfd, + shmfd->shm_label); + MAC_CHECK_PROBE2(posixshm_check_unlink, error, cred, shmfd); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(posixshm_check_setmode, "struct ucred *", + "struct shmfd *", "mode_t"); + +int +mac_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd, mode_t mode) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(posixshm_check_setmode, cred, shmfd, + shmfd->shm_label, mode); + MAC_CHECK_PROBE3(posixshm_check_setmode, error, cred, shmfd, mode); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(posixshm_check_setowner, "struct ucred *", + "struct shmfd *", "uid_t", "gid_t"); + +int +mac_posixshm_check_setowner(struct ucred *cred, struct shmfd *shmfd, uid_t uid, + gid_t gid) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(posixshm_check_setowner, cred, shmfd, + shmfd->shm_label, uid, gid); + MAC_CHECK_PROBE4(posixshm_check_setowner, error, cred, shmfd, + uid, gid); + + return (error); +} diff --git a/sys/security/mac/mac_priv.c b/sys/security/mac/mac_priv.c new file mode 100644 index 0000000..537c002 --- /dev/null +++ b/sys/security/mac/mac_priv.c @@ -0,0 +1,96 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * Copyright (c) 2009 Robert N. M. Watson + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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. + */ + +/* + * MAC checks for system privileges. + */ + +#include "sys/cdefs.h" +__FBSDID("$FreeBSD$"); + +#include "opt_kdtrace.h" +#include "opt_mac.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/priv.h> +#include <sys/sdt.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. + */ + +MAC_CHECK_PROBE_DEFINE2(priv_check, "struct ucred *", "int"); + +/* + * 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_POLICY_CHECK_NOSLEEP(priv_check, cred, priv); + MAC_CHECK_PROBE2(priv_check, error, cred, priv); + + return (error); +} + +MAC_GRANT_PROBE_DEFINE2(priv_grant, "struct ucred *", "int"); + +/* + * 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_POLICY_GRANT_NOSLEEP(priv_grant, cred, priv); + MAC_GRANT_PROBE2(priv_grant, error, 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..15f704d --- /dev/null +++ b/sys/security/mac/mac_process.c @@ -0,0 +1,432 @@ +/*- + * Copyright (c) 1999-2002, 2008-2009 Robert N. M. Watson + * Copyright (c) 2001 Ilmar S. Habibulin + * Copyright (c) 2001-2003 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"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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_kdtrace.h" +#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/mac.h> +#include <sys/proc.h> +#include <sys/rwlock.h> +#include <sys/sbuf.h> +#include <sys/sdt.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_POLICY_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_POLICY_PERFORM_NOSLEEP(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_POLICY_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) +{ + vm_map_entry_t vme; + int 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_WLOCK(object); + while ((backing_object = object->backing_object) != NULL) { + VM_OBJECT_WLOCK(backing_object); + offset += object->backing_object_offset; + VM_OBJECT_WUNLOCK(object); + object = backing_object; + } + VM_OBJECT_WUNLOCK(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; + 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) + 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_WLOCK(object); + vm_object_page_clean(object, offset, offset + + vme->end - vme->start, OBJPC_SYNC); + VM_OBJECT_WUNLOCK(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); + } + } + vm_map_unlock(map); +} + +MAC_CHECK_PROBE_DEFINE2(proc_check_debug, "struct ucred *", "struct proc *"); + +int +mac_proc_check_debug(struct ucred *cred, struct proc *p) +{ + int error; + + PROC_LOCK_ASSERT(p, MA_OWNED); + + MAC_POLICY_CHECK_NOSLEEP(proc_check_debug, cred, p); + MAC_CHECK_PROBE2(proc_check_debug, error, cred, p); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(proc_check_sched, "struct ucred *", "struct proc *"); + +int +mac_proc_check_sched(struct ucred *cred, struct proc *p) +{ + int error; + + PROC_LOCK_ASSERT(p, MA_OWNED); + + MAC_POLICY_CHECK_NOSLEEP(proc_check_sched, cred, p); + MAC_CHECK_PROBE2(proc_check_sched, error, cred, p); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(proc_check_signal, "struct ucred *", "struct proc *", + "int"); + +int +mac_proc_check_signal(struct ucred *cred, struct proc *p, int signum) +{ + int error; + + PROC_LOCK_ASSERT(p, MA_OWNED); + + MAC_POLICY_CHECK_NOSLEEP(proc_check_signal, cred, p, signum); + MAC_CHECK_PROBE3(proc_check_signal, error, cred, p, signum); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(proc_check_wait, "struct ucred *", "struct proc *"); + +int +mac_proc_check_wait(struct ucred *cred, struct proc *p) +{ + int error; + + PROC_LOCK_ASSERT(p, MA_OWNED); + + MAC_POLICY_CHECK_NOSLEEP(proc_check_wait, cred, p); + MAC_CHECK_PROBE2(proc_check_wait, error, 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..11d5e05 --- /dev/null +++ b/sys/security/mac/mac_socket.c @@ -0,0 +1,634 @@ +/*- + * Copyright (c) 1999-2002, 2009 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"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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_kdtrace.h" +#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/sdt.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. + * + * Unlike with many other object types, the lock protecting MAC labels on + * sockets (the socket lock) is not frequently held at the points in code + * where socket-related checks are called. The MAC Framework acquires the + * lock over some entry points in order to enforce atomicity (such as label + * copies) but in other cases the policy modules will have to acquire the + * lock themselves if they use labels. This approach (a) avoids lock + * acquisitions when policies don't require labels and (b) solves a number of + * potential lock order issues when multiple sockets are used in the same + * entry point. + */ + +struct label * +mac_socket_label_alloc(int flag) +{ + struct label *label; + int error; + + label = mac_labelzone_alloc(flag); + if (label == NULL) + return (NULL); + + if (flag & M_WAITOK) + MAC_POLICY_CHECK(socket_init_label, label, flag); + else + MAC_POLICY_CHECK_NOSLEEP(socket_init_label, label, flag); + if (error) { + MAC_POLICY_PERFORM_NOSLEEP(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); + + if (flag & M_WAITOK) + MAC_POLICY_CHECK(socketpeer_init_label, label, flag); + else + MAC_POLICY_CHECK_NOSLEEP(socketpeer_init_label, label, flag); + if (error) { + MAC_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(socket_destroy_label, label); + mac_labelzone_free(label); +} + +static void +mac_socketpeer_label_free(struct label *label) +{ + + MAC_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(socket_copy_label, src, dest); +} + +int +mac_socket_externalize_label(struct label *label, char *elements, + char *outbuf, size_t outbuflen) +{ + int error; + + MAC_POLICY_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_POLICY_EXTERNALIZE(socketpeer, label, elements, outbuf, + outbuflen); + + return (error); +} + +int +mac_socket_internalize_label(struct label *label, char *string) +{ + int error; + + MAC_POLICY_INTERNALIZE(socket, label, string); + + return (error); +} + +void +mac_socket_create(struct ucred *cred, struct socket *so) +{ + + MAC_POLICY_PERFORM_NOSLEEP(socket_create, cred, so, so->so_label); +} + +void +mac_socket_newconn(struct socket *oldso, struct socket *newso) +{ + + MAC_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(socket_relabel, cred, so, so->so_label, + newlabel); +} + +void +mac_socketpeer_set_from_mbuf(struct mbuf *m, struct socket *so) +{ + struct label *label; + + if (mac_policy_count == 0) + return; + + label = mac_mbuf_to_label(m); + + MAC_POLICY_PERFORM_NOSLEEP(socketpeer_set_from_mbuf, m, label, so, + so->so_peerlabel); +} + +void +mac_socketpeer_set_from_socket(struct socket *oldso, struct socket *newso) +{ + + if (mac_policy_count == 0) + return; + + MAC_POLICY_PERFORM_NOSLEEP(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; + + if (mac_policy_count == 0) + return; + + label = mac_mbuf_to_label(m); + + MAC_POLICY_PERFORM_NOSLEEP(socket_create_mbuf, so, so->so_label, m, + label); +} + +MAC_CHECK_PROBE_DEFINE2(socket_check_accept, "struct ucred *", + "struct socket *"); + +int +mac_socket_check_accept(struct ucred *cred, struct socket *so) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(socket_check_accept, cred, so, + so->so_label); + MAC_CHECK_PROBE2(socket_check_accept, error, cred, so); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(socket_check_bind, "struct ucred *", + "struct socket *", "struct sockaddr *"); + +int +mac_socket_check_bind(struct ucred *cred, struct socket *so, + struct sockaddr *sa) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(socket_check_bind, cred, so, so->so_label, + sa); + MAC_CHECK_PROBE3(socket_check_bind, error, cred, so, sa); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(socket_check_connect, "struct ucred *", + "struct socket *", "struct sockaddr *"); + +int +mac_socket_check_connect(struct ucred *cred, struct socket *so, + struct sockaddr *sa) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(socket_check_connect, cred, so, + so->so_label, sa); + MAC_CHECK_PROBE3(socket_check_connect, error, cred, so, sa); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(socket_check_create, "struct ucred *", "int", "int", + "int"); + +int +mac_socket_check_create(struct ucred *cred, int domain, int type, int proto) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(socket_check_create, cred, domain, type, + proto); + MAC_CHECK_PROBE4(socket_check_create, error, cred, domain, type, + proto); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(socket_check_deliver, "struct socket *", + "struct mbuf *"); + +int +mac_socket_check_deliver(struct socket *so, struct mbuf *m) +{ + struct label *label; + int error; + + if (mac_policy_count == 0) + return (0); + + label = mac_mbuf_to_label(m); + + MAC_POLICY_CHECK_NOSLEEP(socket_check_deliver, so, so->so_label, m, + label); + MAC_CHECK_PROBE2(socket_check_deliver, error, so, m); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(socket_check_listen, "struct ucred *", + "struct socket *"); + +int +mac_socket_check_listen(struct ucred *cred, struct socket *so) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(socket_check_listen, cred, so, + so->so_label); + MAC_CHECK_PROBE2(socket_check_listen, error, cred, so); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(socket_check_poll, "struct ucred *", + "struct socket *"); + +int +mac_socket_check_poll(struct ucred *cred, struct socket *so) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(socket_check_poll, cred, so, so->so_label); + MAC_CHECK_PROBE2(socket_check_poll, error, cred, so); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(socket_check_receive, "struct ucred *", + "struct socket *"); + +int +mac_socket_check_receive(struct ucred *cred, struct socket *so) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(socket_check_receive, cred, so, + so->so_label); + MAC_CHECK_PROBE2(socket_check_receive, error, cred, so); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(socket_check_relabel, "struct ucred *", + "struct socket *", "struct label *"); + +static int +mac_socket_check_relabel(struct ucred *cred, struct socket *so, + struct label *newlabel) +{ + int error; + + SOCK_LOCK_ASSERT(so); + + MAC_POLICY_CHECK_NOSLEEP(socket_check_relabel, cred, so, + so->so_label, newlabel); + MAC_CHECK_PROBE3(socket_check_relabel, error, cred, so, newlabel); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(socket_check_send, "struct ucred *", + "struct socket *"); + +int +mac_socket_check_send(struct ucred *cred, struct socket *so) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(socket_check_send, cred, so, so->so_label); + MAC_CHECK_PROBE2(socket_check_send, error, cred, so); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(socket_check_stat, "struct ucred *", + "struct socket *"); + +int +mac_socket_check_stat(struct ucred *cred, struct socket *so) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(socket_check_stat, cred, so, so->so_label); + MAC_CHECK_PROBE2(socket_check_stat, error, cred, so); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(socket_check_visible, "struct ucred *", + "struct socket *"); + +int +mac_socket_check_visible(struct ucred *cred, struct socket *so) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(socket_check_visible, cred, so, + so->so_label); + MAC_CHECK_PROBE2(socket_check_visible, error, cred, so); + + 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..ff55ec9 --- /dev/null +++ b/sys/security/mac/mac_syscalls.c @@ -0,0 +1,731 @@ +/*- + * Copyright (c) 1999-2002, 2006, 2009 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"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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/capability.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/sysctl.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 + +FEATURE(security_mac, "Mandatory Access Control Framework support"); + +int +sys___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 +sys___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 +sys___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 +sys___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 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, CAP_MAC_GET, &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)) { + error = EINVAL; + goto out_fdrop; + } + vp = fp->f_vnode; + intlabel = mac_vnode_label_alloc(); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + mac_vnode_copy_label(vp->v_label, intlabel); + VOP_UNLOCK(vp, 0); + 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)) { + error = EINVAL; + goto out_fdrop; + } + 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)) { + error = EINVAL; + goto out_fdrop; + } + 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; + } + if (error == 0) + error = copyout(buffer, mac.m_string, strlen(buffer)+1); +out_fdrop: + fdrop(fp, td); +out: + free(buffer, M_MACTEMP); + free(elements, M_MACTEMP); + return (error); +} + +int +sys___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 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, LOCKLEAF | FOLLOW, UIO_USERSPACE, + uap->path_p, td); + error = namei(&nd); + if (error) + goto out; + + intlabel = mac_vnode_label_alloc(); + mac_vnode_copy_label(nd.ni_vp->v_label, intlabel); + error = mac_vnode_externalize_label(intlabel, elements, buffer, + mac.m_buflen); + + NDFREE(&nd, 0); + 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 +sys___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 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, LOCKLEAF | NOFOLLOW, UIO_USERSPACE, + uap->path_p, td); + error = namei(&nd); + if (error) + goto out; + + intlabel = mac_vnode_label_alloc(); + mac_vnode_copy_label(nd.ni_vp->v_label, intlabel); + error = mac_vnode_externalize_label(intlabel, elements, buffer, + mac.m_buflen); + NDFREE(&nd, 0); + 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 +sys___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; + + 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, CAP_MAC_SET, &fp); + if (error) + goto out; + + switch (fp->f_type) { + case DTYPE_FIFO: + case DTYPE_VNODE: + if (!(mac_labeled & MPC_OBJECT_VNODE)) { + error = EINVAL; + goto out_fdrop; + } + intlabel = mac_vnode_label_alloc(); + error = mac_vnode_internalize_label(intlabel, buffer); + if (error) { + mac_vnode_label_free(intlabel); + break; + } + vp = fp->f_vnode; + error = vn_start_write(vp, &mp, V_WAIT | PCATCH); + if (error != 0) { + 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); + mac_vnode_label_free(intlabel); + break; + + case DTYPE_PIPE: + if (!(mac_labeled & MPC_OBJECT_PIPE)) { + error = EINVAL; + goto out_fdrop; + } + 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)) { + error = EINVAL; + goto out_fdrop; + } + 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; + } +out_fdrop: + fdrop(fp, td); +out: + free(buffer, M_MACTEMP); + return (error); +} + +int +sys___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 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, LOCKLEAF | FOLLOW, UIO_USERSPACE, + uap->path_p, td); + error = namei(&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); +out: + mac_vnode_label_free(intlabel); + return (error); +} + +int +sys___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 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, LOCKLEAF | NOFOLLOW, UIO_USERSPACE, + uap->path_p, td); + error = namei(&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); +out: + mac_vnode_label_free(intlabel); + return (error); +} + +int +sys_mac_syscall(struct thread *td, struct mac_syscall_args *uap) +{ + struct mac_policy_conf *mpc; + char target[MAC_MAX_POLICY_NAME]; + int 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 (!LIST_EMPTY(&mac_policy_list)) { + mac_policy_slock_sleep(); + 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_sunlock_sleep(); + } +out: + return (error); +} + +#else /* !MAC */ + +int +sys___mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) +{ + + return (ENOSYS); +} + +int +sys___mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) +{ + + return (ENOSYS); +} + +int +sys___mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) +{ + + return (ENOSYS); +} + +int +sys___mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) +{ + + return (ENOSYS); +} + +int +sys___mac_get_file(struct thread *td, struct __mac_get_file_args *uap) +{ + + return (ENOSYS); +} + +int +sys___mac_get_link(struct thread *td, struct __mac_get_link_args *uap) +{ + + return (ENOSYS); +} + +int +sys___mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) +{ + + return (ENOSYS); +} + +int +sys___mac_set_file(struct thread *td, struct __mac_set_file_args *uap) +{ + + return (ENOSYS); +} + +int +sys___mac_set_link(struct thread *td, struct __mac_set_link_args *uap) +{ + + return (ENOSYS); +} + +int +sys_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..b705bbd --- /dev/null +++ b/sys/security/mac/mac_system.c @@ -0,0 +1,237 @@ +/*- + * Copyright (c) 2002-2003 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * Copyright (c) 2007, 2009 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"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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_kdtrace.h" +#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/sdt.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> + +MAC_CHECK_PROBE_DEFINE1(kenv_check_dump, "struct ucred *"); + +int +mac_kenv_check_dump(struct ucred *cred) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(kenv_check_dump, cred); + MAC_CHECK_PROBE1(kenv_check_dump, error, cred); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(kenv_check_get, "struct ucred *", "char *"); + +int +mac_kenv_check_get(struct ucred *cred, char *name) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(kenv_check_get, cred, name); + MAC_CHECK_PROBE2(kenv_check_get, error, cred, name); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(kenv_check_set, "struct ucred *", "char *", + "char *"); + +int +mac_kenv_check_set(struct ucred *cred, char *name, char *value) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(kenv_check_set, cred, name, value); + MAC_CHECK_PROBE3(kenv_check_set, error, cred, name, value); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(kenv_check_unset, "struct ucred *", "char *"); + +int +mac_kenv_check_unset(struct ucred *cred, char *name) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(kenv_check_unset, cred, name); + MAC_CHECK_PROBE2(kenv_check_unset, error, cred, name); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(kld_check_load, "struct ucred *", "struct vnode *"); + +int +mac_kld_check_load(struct ucred *cred, struct vnode *vp) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_kld_check_load"); + + MAC_POLICY_CHECK(kld_check_load, cred, vp, vp->v_label); + MAC_CHECK_PROBE2(kld_check_load, error, cred, vp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE1(kld_check_stat, "struct ucred *"); + +int +mac_kld_check_stat(struct ucred *cred) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(kld_check_stat, cred); + MAC_CHECK_PROBE1(kld_check_stat, error, cred); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(system_check_acct, "struct ucred *", + "struct vnode *"); + +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_POLICY_CHECK(system_check_acct, cred, vp, + vp != NULL ? vp->v_label : NULL); + MAC_CHECK_PROBE2(system_check_acct, error, cred, vp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(system_check_reboot, "struct ucred *", "int"); + +int +mac_system_check_reboot(struct ucred *cred, int howto) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(system_check_reboot, cred, howto); + MAC_CHECK_PROBE2(system_check_reboot, error, cred, howto); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(system_check_swapon, "struct ucred *", + "struct vnode *"); + +int +mac_system_check_swapon(struct ucred *cred, struct vnode *vp) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_system_check_swapon"); + + MAC_POLICY_CHECK(system_check_swapon, cred, vp, vp->v_label); + MAC_CHECK_PROBE2(system_check_swapon, error, cred, vp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(system_check_swapoff, "struct ucred *", + "struct vnode *"); + +int +mac_system_check_swapoff(struct ucred *cred, struct vnode *vp) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_system_check_swapoff"); + + MAC_POLICY_CHECK(system_check_swapoff, cred, vp, vp->v_label); + MAC_CHECK_PROBE2(system_check_swapoff, error, cred, vp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(system_check_sysctl, "struct ucred *", + "struct sysctl_oid *", "struct sysctl_req *"); + +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_POLICY_CHECK_NOSLEEP(system_check_sysctl, cred, oidp, arg1, arg2, + req); + MAC_CHECK_PROBE3(system_check_sysctl, error, cred, oidp, 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..c402322 --- /dev/null +++ b/sys/security/mac/mac_sysv_msg.c @@ -0,0 +1,278 @@ +/*- + * Copyright (c) 2003-2004 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * Copyright (c) 2008 Apple Inc. + * Copyright (c) 2009 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. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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_kdtrace.h" +#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/sdt.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_POLICY_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_POLICY_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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(sysvmsg_create, cred, msqkptr, + msqkptr->label, msgptr, msgptr->label); +} + +void +mac_sysvmsq_create(struct ucred *cred, struct msqid_kernel *msqkptr) +{ + + MAC_POLICY_PERFORM_NOSLEEP(sysvmsq_create, cred, msqkptr, + msqkptr->label); +} + +void +mac_sysvmsg_cleanup(struct msg *msgptr) +{ + + MAC_POLICY_PERFORM_NOSLEEP(sysvmsg_cleanup, msgptr->label); +} + +void +mac_sysvmsq_cleanup(struct msqid_kernel *msqkptr) +{ + + MAC_POLICY_PERFORM_NOSLEEP(sysvmsq_cleanup, msqkptr->label); +} + +MAC_CHECK_PROBE_DEFINE3(sysvmsq_check_msgmsq, "struct ucred *", + "struct msg *", "struct msqid_kernel *"); + +int +mac_sysvmsq_check_msgmsq(struct ucred *cred, struct msg *msgptr, + struct msqid_kernel *msqkptr) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(sysvmsq_check_msgmsq, cred, msgptr, + msgptr->label, msqkptr, msqkptr->label); + MAC_CHECK_PROBE3(sysvmsq_check_msgmsq, error, cred, msgptr, msqkptr); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(sysvmsq_check_msgrcv, "struct ucred *", + "struct msg *"); + +int +mac_sysvmsq_check_msgrcv(struct ucred *cred, struct msg *msgptr) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(sysvmsq_check_msgrcv, cred, msgptr, + msgptr->label); + MAC_CHECK_PROBE2(sysvmsq_check_msgrcv, error, cred, msgptr); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(sysvmsq_check_msgrmid, "struct ucred *", + "struct msg *"); + +int +mac_sysvmsq_check_msgrmid(struct ucred *cred, struct msg *msgptr) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(sysvmsq_check_msgrmid, cred, msgptr, + msgptr->label); + MAC_CHECK_PROBE2(sysvmsq_check_msgrmid, error, cred, msgptr); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(sysvmsq_check_msqget, "struct ucred *", + "struct msqid_kernel *"); + +int +mac_sysvmsq_check_msqget(struct ucred *cred, struct msqid_kernel *msqkptr) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(sysvmsq_check_msqget, cred, msqkptr, + msqkptr->label); + MAC_CHECK_PROBE2(sysvmsq_check_msqget, error, cred, msqkptr); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(sysvmsq_check_msqsnd, "struct ucred *", + "struct msqid_kernel *"); + +int +mac_sysvmsq_check_msqsnd(struct ucred *cred, struct msqid_kernel *msqkptr) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(sysvmsq_check_msqsnd, cred, msqkptr, + msqkptr->label); + MAC_CHECK_PROBE2(sysvmsq_check_msqsnd, error, cred, msqkptr); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(sysvmsq_check_msqrcv, "struct ucred *", + "struct msqid_kernel *"); + +int +mac_sysvmsq_check_msqrcv(struct ucred *cred, struct msqid_kernel *msqkptr) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(sysvmsq_check_msqrcv, cred, msqkptr, + msqkptr->label); + MAC_CHECK_PROBE2(sysvmsq_check_msqrcv, error, cred, msqkptr); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(sysvmsq_check_msqctl, "struct ucred *", + "struct msqid_kernel *", "int"); + +int +mac_sysvmsq_check_msqctl(struct ucred *cred, struct msqid_kernel *msqkptr, + int cmd) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(sysvmsq_check_msqctl, cred, msqkptr, + msqkptr->label, cmd); + MAC_CHECK_PROBE3(sysvmsq_check_msqctl, error, cred, msqkptr, 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..12e7cce --- /dev/null +++ b/sys/security/mac/mac_sysv_sem.c @@ -0,0 +1,163 @@ +/*- + * Copyright (c) 2003-2004 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * Copyright (c) 2009 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. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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_kdtrace.h" +#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/sdt.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_POLICY_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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(sysvsem_create, cred, semakptr, + semakptr->label); +} + +void +mac_sysvsem_cleanup(struct semid_kernel *semakptr) +{ + + MAC_POLICY_PERFORM_NOSLEEP(sysvsem_cleanup, semakptr->label); +} + +MAC_CHECK_PROBE_DEFINE3(sysvsem_check_semctl, "struct ucred *", + "struct semid_kernel *", "int"); + +int +mac_sysvsem_check_semctl(struct ucred *cred, struct semid_kernel *semakptr, + int cmd) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(sysvsem_check_semctl, cred, semakptr, + semakptr->label, cmd); + MAC_CHECK_PROBE3(sysvsem_check_semctl, error, cred, semakptr, cmd); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(sysvsem_check_semget, "struct ucred *", + "struct semid_kernel *"); + +int +mac_sysvsem_check_semget(struct ucred *cred, struct semid_kernel *semakptr) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(sysvsem_check_semget, cred, semakptr, + semakptr->label); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(sysvsem_check_semop, "struct ucred *", + "struct semid_kernel *", "size_t"); + +int +mac_sysvsem_check_semop(struct ucred *cred, struct semid_kernel *semakptr, + size_t accesstype) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(sysvsem_check_semop, cred, semakptr, + semakptr->label, accesstype); + MAC_CHECK_PROBE3(sysvsem_check_semop, error, cred, semakptr, + 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..168e5b6 --- /dev/null +++ b/sys/security/mac/mac_sysv_shm.c @@ -0,0 +1,181 @@ +/*- + * Copyright (c) 2003-2004 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * Copyright (c) 2009 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. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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_kdtrace.h" +#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/sdt.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_POLICY_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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(sysvshm_create, cred, shmsegptr, + shmsegptr->label); +} + +void +mac_sysvshm_cleanup(struct shmid_kernel *shmsegptr) +{ + + MAC_POLICY_PERFORM_NOSLEEP(sysvshm_cleanup, shmsegptr->label); +} + +MAC_CHECK_PROBE_DEFINE3(sysvshm_check_shmat, "struct ucred *", + "struct shmid_kernel *", "int"); + +int +mac_sysvshm_check_shmat(struct ucred *cred, struct shmid_kernel *shmsegptr, + int shmflg) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(sysvshm_check_shmat, cred, shmsegptr, + shmsegptr->label, shmflg); + MAC_CHECK_PROBE3(sysvshm_check_shmat, error, cred, shmsegptr, + shmflg); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(sysvshm_check_shmctl, "struct ucred *", + "struct shmid_kernel *", "int"); + +int +mac_sysvshm_check_shmctl(struct ucred *cred, struct shmid_kernel *shmsegptr, + int cmd) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(sysvshm_check_shmctl, cred, shmsegptr, + shmsegptr->label, cmd); + MAC_CHECK_PROBE3(sysvshm_check_shmctl, error, cred, shmsegptr, cmd); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(sysvshm_check_shmdt, "struct ucred *", + "struct shmid *"); + +int +mac_sysvshm_check_shmdt(struct ucred *cred, struct shmid_kernel *shmsegptr) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(sysvshm_check_shmdt, cred, shmsegptr, + shmsegptr->label); + MAC_CHECK_PROBE2(sysvshm_check_shmdt, error, cred, shmsegptr); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(sysvshm_check_shmget, "struct ucred *", + "struct shmid_kernel *", "int"); + +int +mac_sysvshm_check_shmget(struct ucred *cred, struct shmid_kernel *shmsegptr, + int shmflg) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(sysvshm_check_shmget, cred, shmsegptr, + shmsegptr->label, shmflg); + MAC_CHECK_PROBE3(sysvshm_check_shmget, error, cred, shmsegptr, + 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..c4f305b --- /dev/null +++ b/sys/security/mac/mac_vfs.c @@ -0,0 +1,1071 @@ +/*- + * Copyright (c) 1999-2002, 2009 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"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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_kdtrace.h" +#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/sdt.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_POLICY_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_POLICY_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_POLICY_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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(vnode_copy_label, src, dest); +} + +int +mac_vnode_externalize_label(struct label *label, char *elements, + char *outbuf, size_t outbuflen) +{ + int error; + + MAC_POLICY_EXTERNALIZE(vnode, label, elements, outbuf, outbuflen); + + return (error); +} + +int +mac_vnode_internalize_label(struct label *label, char *string) +{ + int error; + + MAC_POLICY_INTERNALIZE(vnode, label, string); + + return (error); +} + +void +mac_devfs_update(struct mount *mp, struct devfs_dirent *de, struct vnode *vp) +{ + + MAC_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(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_POLICY_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_POLICY_PERFORM_NOSLEEP(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_POLICY_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_POLICY_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_POLICY_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; + /* No sleeping since the process lock will be held by the caller. */ + MAC_POLICY_BOOLEAN_NOSLEEP(vnode_execve_will_transition, ||, old, vp, + vp->v_label, interpvplabel, imgp, imgp->execlabel); + + return (result); +} + +MAC_CHECK_PROBE_DEFINE3(vnode_check_access, "struct ucred *", + "struct vnode *", "accmode_t"); + +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_POLICY_CHECK(vnode_check_access, cred, vp, vp->v_label, accmode); + MAC_CHECK_PROBE3(vnode_check_access, error, cred, vp, accmode); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(vnode_check_chdir, "struct ucred *", + "struct vnode *"); + +int +mac_vnode_check_chdir(struct ucred *cred, struct vnode *dvp) +{ + int error; + + ASSERT_VOP_LOCKED(dvp, "mac_vnode_check_chdir"); + + MAC_POLICY_CHECK(vnode_check_chdir, cred, dvp, dvp->v_label); + MAC_CHECK_PROBE2(vnode_check_chdir, error, cred, dvp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(vnode_check_chroot, "struct ucred *", + "struct vnode *"); + +int +mac_vnode_check_chroot(struct ucred *cred, struct vnode *dvp) +{ + int error; + + ASSERT_VOP_LOCKED(dvp, "mac_vnode_check_chroot"); + + MAC_POLICY_CHECK(vnode_check_chroot, cred, dvp, dvp->v_label); + MAC_CHECK_PROBE2(vnode_check_chroot, error, cred, dvp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(vnode_check_create, "struct ucred *", + "struct vnode *", "struct componentname *", "struct vattr *"); + +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_POLICY_CHECK(vnode_check_create, cred, dvp, dvp->v_label, cnp, + vap); + MAC_CHECK_PROBE4(vnode_check_create, error, cred, dvp, cnp, vap); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(vnode_check_deleteacl, "struct ucred *", + "struct vnode *", "acl_type_t"); + +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_POLICY_CHECK(vnode_check_deleteacl, cred, vp, vp->v_label, type); + MAC_CHECK_PROBE3(vnode_check_deleteacl, error, cred, vp, type); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(vnode_check_deleteextattr, "struct ucred *", + "struct vnode *", "int", "const char *"); + +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_POLICY_CHECK(vnode_check_deleteextattr, cred, vp, vp->v_label, + attrnamespace, name); + MAC_CHECK_PROBE4(vnode_check_deleteextattr, error, cred, vp, + attrnamespace, name); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(vnode_check_exec, "struct ucred *", "struct vnode *", + "struct image_params *"); + +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_POLICY_CHECK(vnode_check_exec, cred, vp, vp->v_label, imgp, + imgp->execlabel); + MAC_CHECK_PROBE3(vnode_check_exec, error, cred, vp, imgp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(vnode_check_getacl, "struct ucred *", + "struct vnode *", "acl_type_t"); + +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_POLICY_CHECK(vnode_check_getacl, cred, vp, vp->v_label, type); + MAC_CHECK_PROBE3(vnode_check_getacl, error, cred, vp, type); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(vnode_check_getextattr, "struct ucred *", + "struct vnode *", "int", "const char *"); + +int +mac_vnode_check_getextattr(struct ucred *cred, struct vnode *vp, + int attrnamespace, const char *name) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_getextattr"); + + MAC_POLICY_CHECK(vnode_check_getextattr, cred, vp, vp->v_label, + attrnamespace, name); + MAC_CHECK_PROBE4(vnode_check_getextattr, error, cred, vp, + attrnamespace, name); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(vnode_check_link, "struct ucred *", "struct vnode *", + "struct vnode *", "struct componentname *"); + +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_POLICY_CHECK(vnode_check_link, cred, dvp, dvp->v_label, vp, + vp->v_label, cnp); + MAC_CHECK_PROBE4(vnode_check_link, error, cred, dvp, vp, cnp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(vnode_check_listextattr, "struct ucred *", + "struct vnode *", "int"); + +int +mac_vnode_check_listextattr(struct ucred *cred, struct vnode *vp, + int attrnamespace) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_listextattr"); + + MAC_POLICY_CHECK(vnode_check_listextattr, cred, vp, vp->v_label, + attrnamespace); + MAC_CHECK_PROBE3(vnode_check_listextattr, error, cred, vp, + attrnamespace); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(vnode_check_lookup, "struct ucred *", + "struct vnode *", "struct componentname *"); + +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_POLICY_CHECK(vnode_check_lookup, cred, dvp, dvp->v_label, cnp); + MAC_CHECK_PROBE3(vnode_check_lookup, error, cred, dvp, cnp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(vnode_check_mmap, "struct ucred *", "struct vnode *", + "int", "int"); + +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_POLICY_CHECK(vnode_check_mmap, cred, vp, vp->v_label, prot, flags); + MAC_CHECK_PROBE4(vnode_check_mmap, error, cred, vp, 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_POLICY_PERFORM(vnode_check_mmap_downgrade, cred, vp, vp->v_label, + &result); + + *prot = result; +} + +MAC_CHECK_PROBE_DEFINE3(vnode_check_mprotect, "struct ucred *", + "struct vnode *", "int"); + +int +mac_vnode_check_mprotect(struct ucred *cred, struct vnode *vp, int prot) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_mprotect"); + + MAC_POLICY_CHECK(vnode_check_mprotect, cred, vp, vp->v_label, prot); + MAC_CHECK_PROBE3(vnode_check_mprotect, error, cred, vp, prot); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(vnode_check_open, "struct ucred *", "struct vnode *", + "accmode_t"); + +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_POLICY_CHECK(vnode_check_open, cred, vp, vp->v_label, accmode); + MAC_CHECK_PROBE3(vnode_check_open, error, cred, vp, accmode); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(vnode_check_poll, "struct ucred *", "struct ucred *", + "struct vnode *"); + +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_POLICY_CHECK(vnode_check_poll, active_cred, file_cred, vp, + vp->v_label); + MAC_CHECK_PROBE3(vnode_check_poll, error, active_cred, file_cred, + vp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(vnode_check_read, "struct ucred *", "struct ucred *", + "struct vnode *"); + +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_POLICY_CHECK(vnode_check_read, active_cred, file_cred, vp, + vp->v_label); + MAC_CHECK_PROBE3(vnode_check_read, error, active_cred, file_cred, + vp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(vnode_check_readdir, "struct ucred *", + "struct vnode *"); + +int +mac_vnode_check_readdir(struct ucred *cred, struct vnode *dvp) +{ + int error; + + ASSERT_VOP_LOCKED(dvp, "mac_vnode_check_readdir"); + + MAC_POLICY_CHECK(vnode_check_readdir, cred, dvp, dvp->v_label); + MAC_CHECK_PROBE2(vnode_check_readdir, error, cred, dvp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(vnode_check_readlink, "struct ucred *", + "struct vnode *"); + +int +mac_vnode_check_readlink(struct ucred *cred, struct vnode *vp) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_readlink"); + + MAC_POLICY_CHECK(vnode_check_readlink, cred, vp, vp->v_label); + MAC_CHECK_PROBE2(vnode_check_readlink, error, cred, vp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(vnode_check_relabel, "struct ucred *", + "struct vnode *", "struct label *"); + +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_POLICY_CHECK(vnode_check_relabel, cred, vp, vp->v_label, newlabel); + MAC_CHECK_PROBE3(vnode_check_relabel, error, cred, vp, newlabel); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(vnode_check_rename_from, "struct ucred *", + "struct vnode *", "struct vnode *", "struct componentname *"); + +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_POLICY_CHECK(vnode_check_rename_from, cred, dvp, dvp->v_label, vp, + vp->v_label, cnp); + MAC_CHECK_PROBE4(vnode_check_rename_from, error, cred, dvp, vp, cnp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(vnode_check_rename_to, "struct ucred *", + "struct vnode *", "struct vnode *", "struct componentname *"); + +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_POLICY_CHECK(vnode_check_rename_to, cred, dvp, dvp->v_label, vp, + vp != NULL ? vp->v_label : NULL, samedir, cnp); + MAC_CHECK_PROBE4(vnode_check_rename_to, error, cred, dvp, vp, cnp); + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(vnode_check_revoke, "struct ucred *", + "struct vnode *"); + +int +mac_vnode_check_revoke(struct ucred *cred, struct vnode *vp) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_revoke"); + + MAC_POLICY_CHECK(vnode_check_revoke, cred, vp, vp->v_label); + MAC_CHECK_PROBE2(vnode_check_revoke, error, cred, vp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(vnode_check_setacl, "struct ucred *", + "struct vnode *", "acl_tpe_t", "struct acl *"); + +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_POLICY_CHECK(vnode_check_setacl, cred, vp, vp->v_label, type, acl); + MAC_CHECK_PROBE4(vnode_check_setacl, error, cred, vp, type, acl); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(vnode_check_setextattr, "struct ucred *", + "struct vnode *", "int", "const char *"); + +int +mac_vnode_check_setextattr(struct ucred *cred, struct vnode *vp, + int attrnamespace, const char *name) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_setextattr"); + + MAC_POLICY_CHECK(vnode_check_setextattr, cred, vp, vp->v_label, + attrnamespace, name); + MAC_CHECK_PROBE4(vnode_check_setextattr, error, cred, vp, + attrnamespace, name); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(vnode_check_setflags, "struct ucred *", + "struct vnode *", "u_long"); + +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_POLICY_CHECK(vnode_check_setflags, cred, vp, vp->v_label, flags); + MAC_CHECK_PROBE3(vnode_check_setflags, error, cred, vp, flags); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(vnode_check_setmode, "struct ucred *", + "struct vnode *", "mode_t"); + +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_POLICY_CHECK(vnode_check_setmode, cred, vp, vp->v_label, mode); + MAC_CHECK_PROBE3(vnode_check_setmode, error, cred, vp, mode); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(vnode_check_setowner, "struct ucred *", + "struct vnode *", "uid_t", "gid_t"); + +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_POLICY_CHECK(vnode_check_setowner, cred, vp, vp->v_label, uid, gid); + MAC_CHECK_PROBE4(vnode_check_setowner, error, cred, vp, uid, gid); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(vnode_check_setutimes, "struct ucred *", + "struct vnode *", "struct timespec *", "struct timespec *"); + +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_POLICY_CHECK(vnode_check_setutimes, cred, vp, vp->v_label, atime, + mtime); + MAC_CHECK_PROBE4(vnode_check_setutimes, error, cred, vp, &atime, + &mtime); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(vnode_check_stat, "struct ucred *", "struct ucred *", + "struct vnode *"); + +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_POLICY_CHECK(vnode_check_stat, active_cred, file_cred, vp, + vp->v_label); + MAC_CHECK_PROBE3(vnode_check_stat, error, active_cred, file_cred, + vp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(vnode_check_unlink, "struct ucred *", + "struct vnode *", "struct vnode *", "struct componentname *"); + +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_POLICY_CHECK(vnode_check_unlink, cred, dvp, dvp->v_label, vp, + vp->v_label, cnp); + MAC_CHECK_PROBE4(vnode_check_unlink, error, cred, dvp, vp, cnp); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(vnode_check_write, "struct ucred *", + "struct ucred *", "struct vnode *"); + +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_POLICY_CHECK(vnode_check_write, active_cred, file_cred, vp, + vp->v_label); + MAC_CHECK_PROBE3(vnode_check_write, error, active_cred, file_cred, + vp); + + return (error); +} + +void +mac_vnode_relabel(struct ucred *cred, struct vnode *vp, + struct label *newlabel) +{ + + MAC_POLICY_PERFORM(vnode_relabel, cred, vp, vp->v_label, newlabel); +} + +void +mac_mount_create(struct ucred *cred, struct mount *mp) +{ + + MAC_POLICY_PERFORM(mount_create, cred, mp, mp->mnt_label); +} + +MAC_CHECK_PROBE_DEFINE2(mount_check_stat, "struct ucred *", + "struct mount *"); + +int +mac_mount_check_stat(struct ucred *cred, struct mount *mount) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(mount_check_stat, cred, mount, mount->mnt_label); + MAC_CHECK_PROBE2(mount_check_stat, error, cred, mount); + + return (error); +} + +void +mac_devfs_create_device(struct ucred *cred, struct mount *mp, + struct cdev *dev, struct devfs_dirent *de) +{ + + MAC_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(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_POLICY_PERFORM_NOSLEEP(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..aa37fc7 --- /dev/null +++ b/sys/security/mac_biba/mac_biba.c @@ -0,0 +1,3782 @@ +/*- + * Copyright (c) 1999-2002, 2007-2011 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"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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); + +static 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 = 1; +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; + const char *dn; + int biba_type; + + mb = SLOT(delabel); + dn = devtoname(dev); + if (strcmp(dn, "null") == 0 || + strcmp(dn, "zero") == 0 || + strcmp(dn, "random") == 0 || + strncmp(dn, "fd/", strlen("fd/")) == 0) + biba_type = MAC_BIBA_TYPE_EQUAL; + else if (ptys_equal && + (strncmp(dn, "ttyp", strlen("ttyp")) == 0 || + strncmp(dn, "pts/", strlen("pts/")) == 0 || + strncmp(dn, "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); + + SOCK_LOCK(so); + biba_copy_effective(source, dest); + SOCK_UNLOCK(so); +} + +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; + + SOCK_LOCK_ASSERT(so); + + 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_setmode(struct ucred *cred, struct ksem *ks, + struct label *kslabel, mode_t mode) +{ + 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_setowner(struct ucred *cred, struct ksem *ks, + struct label *kslabel, uid_t uid, gid_t gid) +{ + 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); +} + +static int +biba_posixshm_check_mmap(struct ucred *cred, struct shmfd *shmfd, + struct label *shmlabel, int prot, int flags) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled || !revocation_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(shmlabel); + + 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_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd, + struct label *shmlabel, accmode_t accmode) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(shmlabel); + + if (accmode & (VREAD | VEXEC | VSTAT_PERMS)) { + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + } + if (accmode & VMODIFY_PERMS) { + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + } + + return (0); +} + +static int +biba_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd, + struct label *shmlabel, mode_t mode) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(shmlabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_posixshm_check_setowner(struct ucred *cred, struct shmfd *shmfd, + struct label *shmlabel, uid_t uid, gid_t gid) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(shmlabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_posixshm_check_stat(struct ucred *active_cred, struct ucred *file_cred, + struct shmfd *shmfd, struct label *shmlabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(shmlabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +biba_posixshm_check_truncate(struct ucred *active_cred, + struct ucred *file_cred, struct shmfd *shmfd, struct label *shmlabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(shmlabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +biba_posixshm_check_unlink(struct ucred *cred, struct shmfd *shmfd, + struct label *shmlabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(shmlabel); + + if (!biba_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static void +biba_posixshm_create(struct ucred *cred, struct shmfd *shmfd, + struct label *shmlabel) +{ + struct mac_biba *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(shmlabel); + + 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_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: + case PRIV_VM_SWAP_NOQUOTA: + case PRIV_VM_SWAP_NORLIMIT: + + /* + * 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; + int error; + + if (!biba_enabled) + return (0); + + p = SLOT(mlabel); + s = SLOT(solabel); + + SOCK_LOCK(so); + error = biba_equal_effective(p, s) ? 0 : EACCES; + SOCK_UNLOCK(so); + return (error); +} + +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; + + SOCK_LOCK_ASSERT(so); + + 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); + + SOCK_LOCK(so); + if (!biba_dominate_effective(obj, subj)) { + SOCK_UNLOCK(so); + return (ENOENT); + } + SOCK_UNLOCK(so); + + 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); + + SOCK_LOCK(so); + biba_copy_effective(source, dest); + SOCK_UNLOCK(so); +} + +static void +biba_socket_newconn(struct socket *oldso, struct label *oldsolabel, + struct socket *newso, struct label *newsolabel) +{ + struct mac_biba source, *dest; + + SOCK_LOCK(oldso); + source = *SLOT(oldsolabel); + SOCK_UNLOCK(oldso); + + dest = SLOT(newsolabel); + + SOCK_LOCK(newso); + biba_copy_effective(&source, dest); + SOCK_UNLOCK(newso); +} + +static void +biba_socket_relabel(struct ucred *cred, struct socket *so, + struct label *solabel, struct label *newlabel) +{ + struct mac_biba *source, *dest; + + SOCK_LOCK_ASSERT(so); + + 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); + + SOCK_LOCK(so); + biba_copy_effective(source, dest); + SOCK_UNLOCK(so); +} + +static void +biba_socketpeer_set_from_socket(struct socket *oldso, + struct label *oldsolabel, struct socket *newso, + struct label *newsopeerlabel) +{ + struct mac_biba source, *dest; + + SOCK_LOCK(oldso); + source = *SLOT(oldsolabel); + SOCK_UNLOCK(oldso); + dest = SLOT(newsopeerlabel); + + SOCK_LOCK(newso); + biba_copy_effective(&source, dest); + SOCK_UNLOCK(newso); +} + +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 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_PERMS)) { + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + } + if (accmode & VMODIFY_PERMS) { + 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 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_setmode = biba_posixsem_check_setmode, + .mpo_posixsem_check_setowner = biba_posixsem_check_setowner, + .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_posixshm_check_mmap = biba_posixshm_check_mmap, + .mpo_posixshm_check_open = biba_posixshm_check_open, + .mpo_posixshm_check_setmode = biba_posixshm_check_setmode, + .mpo_posixshm_check_setowner = biba_posixshm_check_setowner, + .mpo_posixshm_check_stat = biba_posixshm_check_stat, + .mpo_posixshm_check_truncate = biba_posixshm_check_truncate, + .mpo_posixshm_check_unlink = biba_posixshm_check_unlink, + .mpo_posixshm_create = biba_posixshm_create, + .mpo_posixshm_destroy_label = biba_destroy_label, + .mpo_posixshm_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, +}; + +MAC_POLICY_SET(&mac_biba_ops, mac_biba, "TrustedBSD MAC/Biba", + MPC_LOADTIME_FLAG_NOTLATE, &biba_slot); 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..ccbc525 --- /dev/null +++ b/sys/security/mac_bsdextended/mac_bsdextended.c @@ -0,0 +1,527 @@ +/*- + * 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); + +static 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); + +static 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); +} + +static SYSCTL_NODE(_security_mac_bsdextended, OID_AUTO, rules, + CTLFLAG_MPSAFE | 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->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_PERMS) + mbi |= MBI_ADMIN; + if (accmode & VSTAT_PERMS) + 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); 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..5597fd1 --- /dev/null +++ b/sys/security/mac_bsdextended/ugidfw_internal.h @@ -0,0 +1,116 @@ +/*- + * 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); +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); +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..8ec2d48 --- /dev/null +++ b/sys/security/mac_bsdextended/ugidfw_vnode.c @@ -0,0 +1,295 @@ +/*- + * 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) +{ + + 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) +{ + + 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..acfbd25 --- /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); + +static 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); diff --git a/sys/security/mac_lomac/mac_lomac.c b/sys/security/mac_lomac/mac_lomac.c new file mode 100644 index 0000000..40c9c2f --- /dev/null +++ b/sys/security/mac_lomac/mac_lomac.c @@ -0,0 +1,3087 @@ +/*- + * Copyright (c) 1999-2002, 2007-2009 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); + +static 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)) + +static 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 = strchr(string, '('); + if (range == single) + single = NULL; + auxsingle = strchr(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 = strchr(rangelow, '-'); + if (rangehigh == NULL) + return (EINVAL); + rangehigh++; + if (*rangelow == '\0' || *rangehigh == '\0') + return (EINVAL); + rangeend = strchr(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 = strchr(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; + const char *dn; + int lomac_type; + + ml = SLOT(delabel); + dn = devtoname(dev); + if (strcmp(dn, "null") == 0 || + strcmp(dn, "zero") == 0 || + strcmp(dn, "random") == 0 || + strncmp(dn, "fd/", strlen("fd/")) == 0 || + strncmp(dn, "ttyv", strlen("ttyv")) == 0) + lomac_type = MAC_LOMAC_TYPE_EQUAL; + else if (ptys_equal && + (strncmp(dn, "ttyp", strlen("ttyp")) == 0 || + strncmp(dn, "pts/", strlen("pts/")) == 0 || + strncmp(dn, "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; + + SOCK_LOCK_ASSERT(so); + + 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_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: + case PRIV_VM_SWAP_NOQUOTA: + case PRIV_VM_SWAP_NORLIMIT: + + /* + * 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; + int error; + + if (!lomac_enabled) + return (0); + + p = SLOT(mlabel); + s = SLOT(solabel); + + SOCK_LOCK(so); + error = lomac_equal_single(p, s) ? 0 : EACCES; + SOCK_UNLOCK(so); + return (error); +} + +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; + + SOCK_LOCK_ASSERT(so); + + 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); + + SOCK_LOCK(so); + if (!lomac_dominate_single(obj, subj)) { + SOCK_UNLOCK(so); + return (ENOENT); + } + SOCK_UNLOCK(so); + + 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); + + SOCK_LOCK(so); + lomac_copy_single(source, dest); + SOCK_UNLOCK(so); +} + +static void +lomac_socket_newconn(struct socket *oldso, struct label *oldsolabel, + struct socket *newso, struct label *newsolabel) +{ + struct mac_lomac source, *dest; + + SOCK_LOCK(oldso); + source = *SLOT(oldsolabel); + SOCK_UNLOCK(oldso); + + dest = SLOT(newsolabel); + + SOCK_LOCK(newso); + lomac_copy_single(&source, dest); + SOCK_UNLOCK(newso); +} + +static void +lomac_socket_relabel(struct ucred *cred, struct socket *so, + struct label *solabel, struct label *newlabel) +{ + struct mac_lomac *source, *dest; + + SOCK_LOCK_ASSERT(so); + + 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); + + SOCK_LOCK(so); + lomac_copy_single(source, dest); + SOCK_UNLOCK(so); +} + +static void +lomac_socketpeer_set_from_socket(struct socket *oldso, + struct label *oldsolabel, struct socket *newso, + struct label *newsopeerlabel) +{ + struct mac_lomac source, *dest; + + SOCK_LOCK(oldso); + source = *SLOT(oldsolabel); + SOCK_UNLOCK(oldso); + + dest = SLOT(newsopeerlabel); + + SOCK_LOCK(newso); + lomac_copy_single(&source, dest); + SOCK_UNLOCK(newso); +} + +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 & VMODIFY_PERMS) { + 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 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, +}; + +MAC_POLICY_SET(&lomac_ops, mac_lomac, "TrustedBSD MAC/LOMAC", + MPC_LOADTIME_FLAG_NOTLATE, &lomac_slot); 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..ff9084e --- /dev/null +++ b/sys/security/mac_mls/mac_mls.c @@ -0,0 +1,3401 @@ +/*- + * Copyright (c) 1999-2002, 2007-2011 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"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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); + +static 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; + const char *dn; + int mls_type; + + mm = SLOT(delabel); + dn = devtoname(dev); + if (strcmp(dn, "null") == 0 || + strcmp(dn, "zero") == 0 || + strcmp(dn, "random") == 0 || + strncmp(dn, "fd/", strlen("fd/")) == 0) + mls_type = MAC_MLS_TYPE_EQUAL; + else if (strcmp(dn, "kmem") == 0 || + strcmp(dn, "mem") == 0) + mls_type = MAC_MLS_TYPE_HIGH; + else if (ptys_equal && + (strncmp(dn, "ttyp", strlen("ttyp")) == 0 || + strncmp(dn, "pts/", strlen("pts/")) == 0 || + strncmp(dn, "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; + + SOCK_LOCK_ASSERT(so); + + 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_setmode(struct ucred *cred, struct ksem *ks, + struct label *shmlabel, mode_t mode) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(shmlabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_posixsem_check_setowner(struct ucred *cred, struct ksem *ks, + struct label *shmlabel, uid_t uid, gid_t gid) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(shmlabel); + + if (!mls_dominate_effective(obj, subj)) + 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_posixshm_check_mmap(struct ucred *cred, struct shmfd *shmfd, + struct label *shmlabel, int prot, int flags) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(shmlabel); + + 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_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd, + struct label *shmlabel, accmode_t accmode) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(shmlabel); + + if (accmode & (VREAD | VEXEC | VSTAT_PERMS)) { + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + } + if (accmode & VMODIFY_PERMS) { + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + } + + return (0); +} + +static int +mls_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd, + struct label *shmlabel, mode_t mode) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(shmlabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_posixshm_check_setowner(struct ucred *cred, struct shmfd *shmfd, + struct label *shmlabel, uid_t uid, gid_t gid) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(shmlabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_posixshm_check_stat(struct ucred *active_cred, struct ucred *file_cred, + struct shmfd *shmfd, struct label *shmlabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(shmlabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + +static int +mls_posixshm_check_truncate(struct ucred *active_cred, + struct ucred *file_cred, struct shmfd *shmfd, struct label *shmlabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(shmlabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static int +mls_posixshm_check_unlink(struct ucred *cred, struct shmfd *shmfd, + struct label *shmlabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled) + return (0); + + subj = SLOT(cred->cr_label); + obj = SLOT(shmlabel); + + if (!mls_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + +static void +mls_posixshm_create(struct ucred *cred, struct shmfd *shmfd, + struct label *shmlabel) +{ + struct mac_mls *source, *dest; + + source = SLOT(cred->cr_label); + dest = SLOT(shmlabel); + + 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; + int error; + + if (!mls_enabled) + return (0); + + p = SLOT(mlabel); + s = SLOT(solabel); + + SOCK_LOCK(so); + error = mls_equal_effective(p, s) ? 0 : EACCES; + SOCK_UNLOCK(so); + + return (error); +} + +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; + + SOCK_LOCK_ASSERT(so); + + 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); + + SOCK_LOCK(so); + if (!mls_dominate_effective(subj, obj)) { + SOCK_UNLOCK(so); + return (ENOENT); + } + SOCK_UNLOCK(so); + + 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); + + SOCK_LOCK(so); + mls_copy_effective(source, dest); + SOCK_UNLOCK(so); +} + +static void +mls_socket_newconn(struct socket *oldso, struct label *oldsolabel, + struct socket *newso, struct label *newsolabel) +{ + struct mac_mls source, *dest; + + SOCK_LOCK(oldso); + source = *SLOT(oldsolabel); + SOCK_UNLOCK(oldso); + + dest = SLOT(newsolabel); + + SOCK_LOCK(newso); + mls_copy_effective(&source, dest); + SOCK_UNLOCK(newso); +} + +static void +mls_socket_relabel(struct ucred *cred, struct socket *so, + struct label *solabel, struct label *newlabel) +{ + struct mac_mls *source, *dest; + + SOCK_LOCK_ASSERT(so); + + 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); + + SOCK_LOCK(so); + mls_copy_effective(source, dest); + SOCK_UNLOCK(so); +} + +static void +mls_socketpeer_set_from_socket(struct socket *oldso, + struct label *oldsolabel, struct socket *newso, + struct label *newsopeerlabel) +{ + struct mac_mls source, *dest; + + SOCK_LOCK(oldso); + source = *SLOT(oldsolabel); + SOCK_UNLOCK(oldso); + + dest = SLOT(newsopeerlabel); + + SOCK_LOCK(newso); + mls_copy_effective(&source, dest); + SOCK_UNLOCK(newso); +} + +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); + + if (vplabel == NULL) + 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 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_PERMS)) { + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + } + if (accmode & VMODIFY_PERMS) { + 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 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_setmode = mls_posixsem_check_setmode, + .mpo_posixsem_check_setowner = mls_posixsem_check_setowner, + .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_posixshm_check_mmap = mls_posixshm_check_mmap, + .mpo_posixshm_check_open = mls_posixshm_check_open, + .mpo_posixshm_check_setmode = mls_posixshm_check_setmode, + .mpo_posixshm_check_setowner = mls_posixshm_check_setowner, + .mpo_posixshm_check_stat = mls_posixshm_check_stat, + .mpo_posixshm_check_truncate = mls_posixshm_check_truncate, + .mpo_posixshm_check_unlink = mls_posixshm_check_unlink, + .mpo_posixshm_create = mls_posixshm_create, + .mpo_posixshm_destroy_label = mls_destroy_label, + .mpo_posixshm_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, +}; + +MAC_POLICY_SET(&mls_ops, mac_mls, "TrustedBSD MAC/MLS", + MPC_LOADTIME_FLAG_NOTLATE, &mls_slot); 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..8577c73 --- /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); diff --git a/sys/security/mac_partition/mac_partition.c b/sys/security/mac_partition/mac_partition.c new file mode 100644 index 0000000..729413e --- /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); + +static 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); 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..1dbd199 --- /dev/null +++ b/sys/security/mac_portacl/mac_portacl.c @@ -0,0 +1,495 @@ +/*- + * 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); + +static 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); + +static 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-separated 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); + mtx_lock(&rule_mtx); + strcpy(new_string, rule_string); + mtx_unlock(&rule_mtx); + 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); diff --git a/sys/security/mac_seeotheruids/mac_seeotheruids.c b/sys/security/mac_seeotheruids/mac_seeotheruids.c new file mode 100644 index 0000000..cef0d26 --- /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); + +static 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_UINT(_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); diff --git a/sys/security/mac_stub/mac_stub.c b/sys/security/mac_stub/mac_stub.c new file mode 100644 index 0000000..ed25d23 --- /dev/null +++ b/sys/security/mac_stub/mac_stub.c @@ -0,0 +1,1928 @@ +/*- + * Copyright (c) 1999-2002, 2007-2011 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"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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); + +static 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_setaudit(struct ucred *cred, struct auditinfo *ai) +{ + + return (0); +} + +static int +stub_cred_check_setaudit_addr(struct ucred *cred, struct auditinfo_addr *aia) +{ + + return (0); +} + +static int +stub_cred_check_setauid(struct ucred *cred, uid_t auid) +{ + + return (0); +} + +static int +stub_cred_check_setegid(struct ucred *cred, gid_t egid) +{ + + return (0); +} + +static int +stub_cred_check_seteuid(struct ucred *cred, uid_t euid) +{ + + return (0); +} + +static int +stub_cred_check_setgid(struct ucred *cred, gid_t gid) +{ + + return (0); +} + +static int +stub_cred_check_setgroups(struct ucred *cred, int ngroups, + gid_t *gidset) +{ + + return (0); +} + +static int +stub_cred_check_setregid(struct ucred *cred, gid_t rgid, gid_t egid) +{ + + return (0); +} + +static int +stub_cred_check_setresgid(struct ucred *cred, gid_t rgid, gid_t egid, + gid_t sgid) +{ + + return (0); +} + +static int +stub_cred_check_setresuid(struct ucred *cred, uid_t ruid, uid_t euid, + uid_t suid) +{ + + return (0); +} + +static int +stub_cred_check_setreuid(struct ucred *cred, uid_t ruid, uid_t euid) +{ + + return (0); +} + +static int +stub_cred_check_setuid(struct ucred *cred, uid_t uid) +{ + + 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) +{ + + SOCK_LOCK_ASSERT(so); + +} + +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_setmode(struct ucred *cred, struct ksem *ks, + struct label *kslabel, mode_t mode) +{ + + return (0); +} + +static int +stub_posixsem_check_setowner(struct ucred *cred, struct ksem *ks, + struct label *kslabel, uid_t uid, gid_t gid) +{ + + 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_create(struct ucred *cred, const char *path) +{ + + return (0); +} + +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, accmode_t accmode) +{ + + return (0); +} + +static int +stub_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd, + struct label *shmlabel, mode_t mode) +{ + + return (0); +} + +static int +stub_posixshm_check_setowner(struct ucred *cred, struct shmfd *shmfd, + struct label *shmlabel, uid_t uid, gid_t gid) +{ + + 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_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) +{ + +#if 0 + SOCK_LOCK(so); + SOCK_UNLOCK(so); +#endif + + return (0); +} + +static int +stub_socket_check_bind(struct ucred *cred, struct socket *so, + struct label *solabel, struct sockaddr *sa) +{ + +#if 0 + SOCK_LOCK(so); + SOCK_UNLOCK(so); +#endif + + return (0); +} + +static int +stub_socket_check_connect(struct ucred *cred, struct socket *so, + struct label *solabel, struct sockaddr *sa) +{ + +#if 0 + SOCK_LOCK(so); + SOCK_UNLOCK(so); +#endif + + 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) +{ + +#if 0 + SOCK_LOCK(so); + SOCK_UNLOCK(so); +#endif + + return (0); +} + +static int +stub_socket_check_listen(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + +#if 0 + SOCK_LOCK(so); + SOCK_UNLOCK(so); +#endif + + return (0); +} + +static int +stub_socket_check_poll(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + +#if 0 + SOCK_LOCK(so); + SOCK_UNLOCK(so); +#endif + + return (0); +} + +static int +stub_socket_check_receive(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + +#if 0 + SOCK_LOCK(so); + SOCK_UNLOCK(so); +#endif + + return (0); +} + +static int +stub_socket_check_relabel(struct ucred *cred, struct socket *so, + struct label *solabel, struct label *newlabel) +{ + + SOCK_LOCK_ASSERT(so); + + return (0); +} +static int +stub_socket_check_send(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + +#if 0 + SOCK_LOCK(so); + SOCK_UNLOCK(so); +#endif + + return (0); +} + +static int +stub_socket_check_stat(struct ucred *cred, struct socket *so, + struct label *solabel) +{ + +#if 0 + SOCK_LOCK(so); + SOCK_UNLOCK(so); +#endif + + 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) +{ + +#if 0 + SOCK_LOCK(so); + SOCK_UNLOCK(so); +#endif + + 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) +{ + +#if 0 + SOCK_LOCK(so); + SOCK_UNLOCK(so); +#endif +} + +static void +stub_socket_newconn(struct socket *oldso, struct label *oldsolabel, + struct socket *newso, struct label *newsolabel) +{ + +#if 0 + SOCK_LOCK(oldso); + SOCK_UNLOCK(oldso); +#endif +#if 0 + SOCK_LOCK(newso); + SOCK_UNLOCK(newso); +#endif +} + +static void +stub_socket_relabel(struct ucred *cred, struct socket *so, + struct label *solabel, struct label *newlabel) +{ + + SOCK_LOCK_ASSERT(so); +} + +static void +stub_socketpeer_set_from_mbuf(struct mbuf *m, struct label *mlabel, + struct socket *so, struct label *sopeerlabel) +{ + +#if 0 + SOCK_LOCK(so); + SOCK_UNLOCK(so); +#endif +} + +static void +stub_socketpeer_set_from_socket(struct socket *oldso, + struct label *oldsolabel, struct socket *newso, + struct label *newsopeerlabel) +{ + +#if 0 + SOCK_LOCK(oldso); + SOCK_UNLOCK(oldso); +#endif +#if 0 + SOCK_LOCK(newso); + SOCK_UNLOCK(newso); +#endif +} + +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) +{ + + 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) +{ + + 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_setaudit = stub_cred_check_setaudit, + .mpo_cred_check_setaudit_addr = stub_cred_check_setaudit_addr, + .mpo_cred_check_setauid = stub_cred_check_setauid, + .mpo_cred_check_setegid = stub_cred_check_setegid, + .mpo_cred_check_seteuid = stub_cred_check_seteuid, + .mpo_cred_check_setgid = stub_cred_check_setgid, + .mpo_cred_check_setgroups = stub_cred_check_setgroups, + .mpo_cred_check_setregid = stub_cred_check_setregid, + .mpo_cred_check_setresgid = stub_cred_check_setresgid, + .mpo_cred_check_setresuid = stub_cred_check_setresuid, + .mpo_cred_check_setreuid = stub_cred_check_setreuid, + .mpo_cred_check_setuid = stub_cred_check_setuid, + .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_setmode = stub_posixsem_check_setmode, + .mpo_posixsem_check_setowner = stub_posixsem_check_setowner, + .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_create = stub_posixshm_check_create, + .mpo_posixshm_check_mmap = stub_posixshm_check_mmap, + .mpo_posixshm_check_open = stub_posixshm_check_open, + .mpo_posixshm_check_setmode = stub_posixshm_check_setmode, + .mpo_posixshm_check_setowner = stub_posixshm_check_setowner, + .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_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, +}; + +MAC_POLICY_SET(&stub_ops, mac_stub, "TrustedBSD MAC/Stub", + MPC_LOADTIME_FLAG_UNLOADOK, NULL); diff --git a/sys/security/mac_test/mac_test.c b/sys/security/mac_test/mac_test.c new file mode 100644 index 0000000..1781798 --- /dev/null +++ b/sys/security/mac_test/mac_test.c @@ -0,0 +1,3246 @@ +/*- + * Copyright (c) 1999-2002, 2007-2011 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"). + * + * This software was developed at the University of Cambridge Computer + * Laboratory with support from a grant from Google, 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 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); + +static 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"); + +static 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_setaudit); +static int +test_cred_check_setaudit(struct ucred *cred, struct auditinfo *ai) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(cred_check_setaudit); + + return (0); +} + +COUNTER_DECL(cred_check_setaudit_addr); +static int +test_cred_check_setaudit_addr(struct ucred *cred, + struct auditinfo_addr *aia) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(cred_check_setaudit_addr); + + return (0); +} + +COUNTER_DECL(cred_check_setauid); +static int +test_cred_check_setauid(struct ucred *cred, uid_t auid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(cred_check_setauid); + + return (0); +} + +COUNTER_DECL(cred_check_setegid); +static int +test_cred_check_setegid(struct ucred *cred, gid_t egid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(cred_check_setegid); + + return (0); +} + +COUNTER_DECL(proc_check_euid); +static int +test_cred_check_seteuid(struct ucred *cred, uid_t euid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(proc_check_euid); + + return (0); +} + +COUNTER_DECL(cred_check_setregid); +static int +test_cred_check_setregid(struct ucred *cred, gid_t rgid, gid_t egid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(cred_check_setregid); + + return (0); +} + +COUNTER_DECL(cred_check_setreuid); +static int +test_cred_check_setreuid(struct ucred *cred, uid_t ruid, uid_t euid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(cred_check_setreuid); + + return (0); +} + +COUNTER_DECL(cred_check_setgid); +static int +test_cred_check_setgid(struct ucred *cred, gid_t gid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(cred_check_setgid); + + return (0); +} + +COUNTER_DECL(cred_check_setgroups); +static int +test_cred_check_setgroups(struct ucred *cred, int ngroups, + gid_t *gidset) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(cred_check_setgroups); + + return (0); +} + +COUNTER_DECL(cred_check_setresgid); +static int +test_cred_check_setresgid(struct ucred *cred, gid_t rgid, gid_t egid, + gid_t sgid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(cred_check_setresgid); + + return (0); +} + +COUNTER_DECL(cred_check_setresuid); +static int +test_cred_check_setresuid(struct ucred *cred, uid_t ruid, uid_t euid, + uid_t suid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(cred_check_setresuid); + + return (0); +} + +COUNTER_DECL(cred_check_setuid); +static int +test_cred_check_setuid(struct ucred *cred, uid_t uid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + COUNTER_INC(cred_check_setuid); + + 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) +{ + + SOCK_LOCK(so); + LABEL_CHECK(solabel, MAGIC_SOCKET); + SOCK_UNLOCK(so); + 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) +{ + + SOCK_LOCK_ASSERT(so); + + 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_setmode); +static int +test_posixsem_check_setmode(struct ucred *cred, struct ksem *ks, + struct label *kslabel, mode_t mode) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(kslabel, MAGIC_POSIX_SHM); + COUNTER_INC(posixsem_check_setmode); + return (0); +} + +COUNTER_DECL(posixsem_check_setowner); +static int +test_posixsem_check_setowner(struct ucred *cred, struct ksem *ks, + struct label *kslabel, uid_t uid, gid_t gid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(kslabel, MAGIC_POSIX_SHM); + COUNTER_INC(posixsem_check_setowner); + 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_create); +static int +test_posixshm_check_create(struct ucred *cred, const char *path) +{ + + COUNTER_INC(posixshm_check_create); + return (0); +} + +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, accmode_t accmode) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(shmfdlabel, MAGIC_POSIX_SHM); + COUNTER_INC(posixshm_check_open); + return (0); +} + +COUNTER_DECL(posixshm_check_setmode); +static int +test_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd, + struct label *shmfdlabel, mode_t mode) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(shmfdlabel, MAGIC_POSIX_SHM); + COUNTER_INC(posixshm_check_setmode); + return (0); +} + +COUNTER_DECL(posixshm_check_setowner); +static int +test_posixshm_check_setowner(struct ucred *cred, struct shmfd *shmfd, + struct label *shmfdlabel, uid_t uid, gid_t gid) +{ + + LABEL_CHECK(cred->cr_label, MAGIC_CRED); + LABEL_CHECK(shmfdlabel, MAGIC_POSIX_SHM); + COUNTER_INC(posixshm_check_setowner); + 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_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); + SOCK_LOCK(so); + LABEL_CHECK(solabel, MAGIC_SOCKET); + SOCK_UNLOCK(so); + 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); + SOCK_LOCK(so); + LABEL_CHECK(solabel, MAGIC_SOCKET); + SOCK_UNLOCK(so); + 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); + SOCK_LOCK(so); + LABEL_CHECK(solabel, MAGIC_SOCKET); + SOCK_UNLOCK(so); + 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) +{ + + SOCK_LOCK(so); + LABEL_CHECK(solabel, MAGIC_SOCKET); + SOCK_UNLOCK(so); + 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); + SOCK_LOCK(so); + LABEL_CHECK(solabel, MAGIC_SOCKET); + SOCK_UNLOCK(so); + 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); + SOCK_LOCK(so); + LABEL_CHECK(solabel, MAGIC_SOCKET); + SOCK_UNLOCK(so); + 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); + SOCK_LOCK(so); + LABEL_CHECK(solabel, MAGIC_SOCKET); + SOCK_UNLOCK(so); + 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) +{ + + SOCK_LOCK_ASSERT(so); + + 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); + SOCK_LOCK(so); + LABEL_CHECK(solabel, MAGIC_SOCKET); + SOCK_UNLOCK(so); + 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); + SOCK_LOCK(so); + LABEL_CHECK(solabel, MAGIC_SOCKET); + SOCK_UNLOCK(so); + 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); + SOCK_LOCK(so); + LABEL_CHECK(solabel, MAGIC_SOCKET); + SOCK_UNLOCK(so); + 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 *solabel, + struct mbuf *m, struct label *mlabel) +{ + + SOCK_LOCK(so); + LABEL_CHECK(solabel, MAGIC_SOCKET); + SOCK_UNLOCK(so); + 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) +{ + + SOCK_LOCK(oldso); + LABEL_CHECK(oldsolabel, MAGIC_SOCKET); + SOCK_UNLOCK(oldso); + SOCK_LOCK(newso); + LABEL_CHECK(newsolabel, MAGIC_SOCKET); + SOCK_UNLOCK(newso); + 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) +{ + + SOCK_LOCK_ASSERT(so); + + 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 *so, struct label *sopeerlabel) +{ + + LABEL_CHECK(mlabel, MAGIC_MBUF); + SOCK_LOCK(so); + LABEL_CHECK(sopeerlabel, MAGIC_SOCKET); + SOCK_UNLOCK(so); + 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) +{ + + SOCK_LOCK(oldso); + LABEL_CHECK(oldsolabel, MAGIC_SOCKET); + SOCK_UNLOCK(oldso); + SOCK_LOCK(newso); + LABEL_CHECK(newsopeerlabel, MAGIC_SOCKET); + SOCK_UNLOCK(newso); + 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) +{ + + 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) +{ + + 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_setaudit = test_cred_check_setaudit, + .mpo_cred_check_setaudit_addr = test_cred_check_setaudit_addr, + .mpo_cred_check_setauid = test_cred_check_setauid, + .mpo_cred_check_seteuid = test_cred_check_seteuid, + .mpo_cred_check_setegid = test_cred_check_setegid, + .mpo_cred_check_setgid = test_cred_check_setgid, + .mpo_cred_check_setgroups = test_cred_check_setgroups, + .mpo_cred_check_setregid = test_cred_check_setregid, + .mpo_cred_check_setresgid = test_cred_check_setresgid, + .mpo_cred_check_setresuid = test_cred_check_setresuid, + .mpo_cred_check_setreuid = test_cred_check_setreuid, + .mpo_cred_check_setuid = test_cred_check_setuid, + .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_setmode = test_posixsem_check_setmode, + .mpo_posixsem_check_setowner = test_posixsem_check_setowner, + .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_create = test_posixshm_check_create, + .mpo_posixshm_check_mmap = test_posixshm_check_mmap, + .mpo_posixshm_check_open = test_posixshm_check_open, + .mpo_posixshm_check_setmode = test_posixshm_check_setmode, + .mpo_posixshm_check_setowner = test_posixshm_check_setowner, + .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_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, +}; + +MAC_POLICY_SET(&test_ops, mac_test, "TrustedBSD MAC/Test", + MPC_LOADTIME_FLAG_UNLOADOK, &test_slot); |