Index: head/sys/kern/kern_mac.c =================================================================== --- head/sys/kern/kern_mac.c (revision 106024) +++ head/sys/kern/kern_mac.c (revision 106025) @@ -1,4010 +1,4038 @@ /*- * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson * Copyright (c) 2001 Ilmar S. Habibulin * Copyright (c) 2001, 2002 Networks Associates Technology, 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 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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. * * Framework for extensible kernel access control. Kernel and userland * interface to the framework, policy registration and composition. */ #include "opt_mac.h" #include "opt_devfs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MAC /* * Declare that the kernel provides MAC support, version 1. 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, 1); SYSCTL_DECL(_security); SYSCTL_NODE(_security, OID_AUTO, mac, CTLFLAG_RW, 0, "TrustedBSD MAC policy controls"); #if MAC_MAX_POLICIES > 32 #error "MAC_MAX_POLICIES too large" #endif static unsigned int mac_max_policies = MAC_MAX_POLICIES; static unsigned int mac_policy_offsets_free = (1 << MAC_MAX_POLICIES) - 1; SYSCTL_UINT(_security_mac, OID_AUTO, max_policies, CTLFLAG_RD, &mac_max_policies, 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; /* * Warn about EA transactions only the first time they happen. * Weak coherency, no locking. */ static int ea_warn_once = 0; static int mac_enforce_fs = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_fs, CTLFLAG_RW, &mac_enforce_fs, 0, "Enforce MAC policy on file system objects"); TUNABLE_INT("security.mac.enforce_fs", &mac_enforce_fs); static int mac_enforce_network = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_network, CTLFLAG_RW, &mac_enforce_network, 0, "Enforce MAC policy on network packets"); TUNABLE_INT("security.mac.enforce_network", &mac_enforce_network); static int mac_enforce_pipe = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_pipe, CTLFLAG_RW, &mac_enforce_pipe, 0, "Enforce MAC policy on pipe operations"); TUNABLE_INT("security.mac.enforce_pipe", &mac_enforce_pipe); static int mac_enforce_process = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_process, CTLFLAG_RW, &mac_enforce_process, 0, "Enforce MAC policy on inter-process operations"); TUNABLE_INT("security.mac.enforce_process", &mac_enforce_process); static int mac_enforce_reboot = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_reboot, CTLFLAG_RW, &mac_enforce_reboot, 0, "Enforce MAC policy for reboot operations"); TUNABLE_INT("security.mac.enforce_reboot", &mac_enforce_reboot); static int mac_enforce_socket = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_socket, CTLFLAG_RW, &mac_enforce_socket, 0, "Enforce MAC policy on socket operations"); TUNABLE_INT("security.mac.enforce_socket", &mac_enforce_socket); +static int mac_enforce_sysctl = 1; +SYSCTL_INT(_security_mac, OID_AUTO, enforce_sysctl, CTLFLAG_RW, + &mac_enforce_sysctl, 0, "Enforce MAC policy on sysctl operations"); +TUNABLE_INT("security.mac.enforce_sysctl", &mac_enforce_sysctl); + static int mac_enforce_vm = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_vm, CTLFLAG_RW, &mac_enforce_vm, 0, "Enforce MAC policy on vm operations"); TUNABLE_INT("security.mac.enforce_vm", &mac_enforce_vm); static int mac_cache_fslabel_in_vnode = 1; SYSCTL_INT(_security_mac, OID_AUTO, cache_fslabel_in_vnode, CTLFLAG_RW, &mac_cache_fslabel_in_vnode, 0, "Cache mount fslabel in vnode"); TUNABLE_INT("security.mac.cache_fslabel_in_vnode", &mac_cache_fslabel_in_vnode); 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"); #ifdef MAC_DEBUG SYSCTL_NODE(_security_mac, OID_AUTO, debug, CTLFLAG_RW, 0, "TrustedBSD MAC debug info"); static int mac_debug_label_fallback = 0; SYSCTL_INT(_security_mac_debug, OID_AUTO, label_fallback, CTLFLAG_RW, &mac_debug_label_fallback, 0, "Filesystems should fall back to fs label" "when label is corrupted."); TUNABLE_INT("security.mac.debug_label_fallback", &mac_debug_label_fallback); SYSCTL_NODE(_security_mac_debug, OID_AUTO, counters, CTLFLAG_RW, 0, "TrustedBSD MAC object counters"); static unsigned int nmacmbufs, nmaccreds, nmacifnets, nmacbpfdescs, nmacsockets, nmacmounts, nmactemp, nmacvnodes, nmacdevfsdirents, nmacipqs, nmacpipes; SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, mbufs, CTLFLAG_RD, &nmacmbufs, 0, "number of mbufs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, creds, CTLFLAG_RD, &nmaccreds, 0, "number of ucreds in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ifnets, CTLFLAG_RD, &nmacifnets, 0, "number of ifnets in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ipqs, CTLFLAG_RD, &nmacipqs, 0, "number of ipqs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, bpfdescs, CTLFLAG_RD, &nmacbpfdescs, 0, "number of bpfdescs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, sockets, CTLFLAG_RD, &nmacsockets, 0, "number of sockets in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, pipes, CTLFLAG_RD, &nmacpipes, 0, "number of pipes in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, mounts, CTLFLAG_RD, &nmacmounts, 0, "number of mounts in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, temp, CTLFLAG_RD, &nmactemp, 0, "number of temporary labels in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, vnodes, CTLFLAG_RD, &nmacvnodes, 0, "number of vnodes in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, devfsdirents, CTLFLAG_RD, &nmacdevfsdirents, 0, "number of devfs dirents inuse"); #endif static int error_select(int error1, int error2); static int mac_policy_register(struct mac_policy_conf *mpc); static int mac_policy_unregister(struct mac_policy_conf *mpc); static void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot); static void mac_cred_mmapped_drop_perms_recurse(struct thread *td, struct ucred *cred, struct vm_map *map); static void mac_destroy_socket_label(struct label *label); static int mac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *intlabel); MALLOC_DEFINE(M_MACOPVEC, "macopvec", "MAC policy operation vector"); MALLOC_DEFINE(M_MACPIPELABEL, "macpipelabel", "MAC labels for pipes"); MALLOC_DEFINE(M_MACTEMP, "mactemp", "MAC temporary label storage"); /* * mac_policy_list_lock protects the consistency of 'mac_policy_list', * the linked list of attached policy modules. Read-only consumers of * the list must acquire a shared lock for the duration of their use; * writers must acquire an exclusive lock. Note that for compound * operations, locks should be held for the entire compound operation, * and that this is not yet done for relabel requests. */ static struct mtx mac_policy_list_lock; static LIST_HEAD(, mac_policy_conf) mac_policy_list; static int mac_policy_list_busy; #define MAC_POLICY_LIST_LOCKINIT() mtx_init(&mac_policy_list_lock, \ "mac_policy_list_lock", NULL, MTX_DEF); #define MAC_POLICY_LIST_LOCK() mtx_lock(&mac_policy_list_lock); #define MAC_POLICY_LIST_UNLOCK() mtx_unlock(&mac_policy_list_lock); #define MAC_POLICY_LIST_BUSY() do { \ MAC_POLICY_LIST_LOCK(); \ mac_policy_list_busy++; \ MAC_POLICY_LIST_UNLOCK(); \ } while (0) #define MAC_POLICY_LIST_UNBUSY() do { \ MAC_POLICY_LIST_LOCK(); \ mac_policy_list_busy--; \ if (mac_policy_list_busy < 0) \ panic("Extra mac_policy_list_busy--"); \ MAC_POLICY_LIST_UNLOCK(); \ } while (0) /* * MAC_CHECK performs the designated check by walking the policy * module list and checking with each as to how it feels about the * request. Note that it returns its value via 'error' in the scope * of the caller. */ #define MAC_CHECK(check, args...) do { \ struct mac_policy_conf *mpc; \ \ error = 0; \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## check != NULL) \ error = error_select( \ mpc->mpc_ops->mpo_ ## check (args), \ error); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) /* * MAC_BOOLEAN performs the designated boolean composition by walking * the module list, invoking each instance of the operation, and * combining the results using the passed C operator. Note that it * returns its value via 'result' in the scope of the caller, which * should be initialized by the caller in a meaningful way to get * a meaningful result. */ #define MAC_BOOLEAN(operation, composition, args...) do { \ struct mac_policy_conf *mpc; \ \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ result = result composition \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) #define MAC_EXTERNALIZE(type, label, elementlist, outbuf, \ outbuflen) do { \ char *curptr, *curptr_start, *element_name, *element_temp; \ size_t left, left_start, len; \ int claimed, first, first_start, ignorenotfound; \ \ error = 0; \ element_temp = elementlist; \ curptr = outbuf; \ curptr[0] = '\0'; \ left = outbuflen; \ first = 1; \ while ((element_name = strsep(&element_temp, ",")) != NULL) { \ curptr_start = curptr; \ left_start = left; \ first_start = first; \ if (element_name[0] == '?') { \ element_name++; \ ignorenotfound = 1; \ } else \ ignorenotfound = 0; \ claimed = 0; \ if (first) { \ len = snprintf(curptr, left, "%s/", \ element_name); \ first = 0; \ } else \ len = snprintf(curptr, left, ",%s/", \ element_name); \ if (len >= left) { \ error = EINVAL; /* XXXMAC: E2BIG */ \ break; \ } \ curptr += len; \ left -= len; \ \ MAC_CHECK(externalize_ ## type, label, element_name, \ curptr, left, &len, &claimed); \ if (error) \ break; \ if (claimed == 1) { \ if (len >= outbuflen) { \ error = EINVAL; /* XXXMAC: E2BIG */ \ break; \ } \ curptr += len; \ left -= len; \ } else if (claimed == 0 && ignorenotfound) { \ /* \ * Revert addition of the label element \ * name. \ */ \ curptr = curptr_start; \ *curptr = '\0'; \ left = left_start; \ first = first_start; \ } else { \ error = EINVAL; /* XXXMAC: ENOLABEL */ \ break; \ } \ } \ } while (0) #define MAC_INTERNALIZE(type, label, instring) do { \ char *element, *element_name, *element_data; \ int claimed; \ \ error = 0; \ element = instring; \ while ((element_name = strsep(&element, ",")) != NULL) { \ element_data = element_name; \ element_name = strsep(&element_data, "/"); \ if (element_data == NULL) { \ error = EINVAL; \ break; \ } \ claimed = 0; \ MAC_CHECK(internalize_ ## type, label, element_name, \ element_data, &claimed); \ if (error) \ break; \ if (claimed != 1) { \ /* XXXMAC: Another error here? */ \ error = EINVAL; \ break; \ } \ } \ } while (0) /* * MAC_PERFORM performs the designated operation by walking the policy * module list and invoking that operation for each policy. */ #define MAC_PERFORM(operation, args...) do { \ struct mac_policy_conf *mpc; \ \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) /* * Initialize the MAC subsystem, including appropriate SMP locks. */ static void mac_init(void) { LIST_INIT(&mac_policy_list); MAC_POLICY_LIST_LOCKINIT(); } /* * 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; } /* * 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; 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: break; } return (error); } static int mac_policy_register(struct mac_policy_conf *mpc) { struct mac_policy_conf *tmpc; struct mac_policy_op_entry *mpe; int slot; MALLOC(mpc->mpc_ops, struct mac_policy_ops *, sizeof(*mpc->mpc_ops), M_MACOPVEC, M_WAITOK | M_ZERO); for (mpe = mpc->mpc_entries; mpe->mpe_constant != MAC_OP_LAST; mpe++) { switch (mpe->mpe_constant) { case MAC_OP_LAST: /* * Doesn't actually happen, but this allows checking * that all enumerated values are handled. */ break; case MAC_DESTROY: mpc->mpc_ops->mpo_destroy = mpe->mpe_function; break; case MAC_INIT: mpc->mpc_ops->mpo_init = mpe->mpe_function; break; case MAC_SYSCALL: mpc->mpc_ops->mpo_syscall = mpe->mpe_function; break; case MAC_INIT_BPFDESC_LABEL: mpc->mpc_ops->mpo_init_bpfdesc_label = mpe->mpe_function; break; case MAC_INIT_CRED_LABEL: mpc->mpc_ops->mpo_init_cred_label = mpe->mpe_function; break; case MAC_INIT_DEVFSDIRENT_LABEL: mpc->mpc_ops->mpo_init_devfsdirent_label = mpe->mpe_function; break; case MAC_INIT_IFNET_LABEL: mpc->mpc_ops->mpo_init_ifnet_label = mpe->mpe_function; break; case MAC_INIT_IPQ_LABEL: mpc->mpc_ops->mpo_init_ipq_label = mpe->mpe_function; break; case MAC_INIT_MBUF_LABEL: mpc->mpc_ops->mpo_init_mbuf_label = mpe->mpe_function; break; case MAC_INIT_MOUNT_LABEL: mpc->mpc_ops->mpo_init_mount_label = mpe->mpe_function; break; case MAC_INIT_MOUNT_FS_LABEL: mpc->mpc_ops->mpo_init_mount_fs_label = mpe->mpe_function; break; case MAC_INIT_PIPE_LABEL: mpc->mpc_ops->mpo_init_pipe_label = mpe->mpe_function; break; case MAC_INIT_SOCKET_LABEL: mpc->mpc_ops->mpo_init_socket_label = mpe->mpe_function; break; case MAC_INIT_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_init_socket_peer_label = mpe->mpe_function; break; case MAC_INIT_VNODE_LABEL: mpc->mpc_ops->mpo_init_vnode_label = mpe->mpe_function; break; case MAC_DESTROY_BPFDESC_LABEL: mpc->mpc_ops->mpo_destroy_bpfdesc_label = mpe->mpe_function; break; case MAC_DESTROY_CRED_LABEL: mpc->mpc_ops->mpo_destroy_cred_label = mpe->mpe_function; break; case MAC_DESTROY_DEVFSDIRENT_LABEL: mpc->mpc_ops->mpo_destroy_devfsdirent_label = mpe->mpe_function; break; case MAC_DESTROY_IFNET_LABEL: mpc->mpc_ops->mpo_destroy_ifnet_label = mpe->mpe_function; break; case MAC_DESTROY_IPQ_LABEL: mpc->mpc_ops->mpo_destroy_ipq_label = mpe->mpe_function; break; case MAC_DESTROY_MBUF_LABEL: mpc->mpc_ops->mpo_destroy_mbuf_label = mpe->mpe_function; break; case MAC_DESTROY_MOUNT_LABEL: mpc->mpc_ops->mpo_destroy_mount_label = mpe->mpe_function; break; case MAC_DESTROY_MOUNT_FS_LABEL: mpc->mpc_ops->mpo_destroy_mount_fs_label = mpe->mpe_function; break; case MAC_DESTROY_PIPE_LABEL: mpc->mpc_ops->mpo_destroy_pipe_label = mpe->mpe_function; break; case MAC_DESTROY_SOCKET_LABEL: mpc->mpc_ops->mpo_destroy_socket_label = mpe->mpe_function; break; case MAC_DESTROY_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_destroy_socket_peer_label = mpe->mpe_function; break; case MAC_DESTROY_VNODE_LABEL: mpc->mpc_ops->mpo_destroy_vnode_label = mpe->mpe_function; break; case MAC_COPY_PIPE_LABEL: mpc->mpc_ops->mpo_copy_pipe_label = mpe->mpe_function; break; case MAC_COPY_VNODE_LABEL: mpc->mpc_ops->mpo_copy_vnode_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_CRED_LABEL: mpc->mpc_ops->mpo_externalize_cred_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_IFNET_LABEL: mpc->mpc_ops->mpo_externalize_ifnet_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_PIPE_LABEL: mpc->mpc_ops->mpo_externalize_pipe_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_SOCKET_LABEL: mpc->mpc_ops->mpo_externalize_socket_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_externalize_socket_peer_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_VNODE_LABEL: mpc->mpc_ops->mpo_externalize_vnode_label = mpe->mpe_function; break; case MAC_INTERNALIZE_CRED_LABEL: mpc->mpc_ops->mpo_internalize_cred_label = mpe->mpe_function; break; case MAC_INTERNALIZE_IFNET_LABEL: mpc->mpc_ops->mpo_internalize_ifnet_label = mpe->mpe_function; break; case MAC_INTERNALIZE_PIPE_LABEL: mpc->mpc_ops->mpo_internalize_pipe_label = mpe->mpe_function; break; case MAC_INTERNALIZE_SOCKET_LABEL: mpc->mpc_ops->mpo_internalize_socket_label = mpe->mpe_function; break; case MAC_INTERNALIZE_VNODE_LABEL: mpc->mpc_ops->mpo_internalize_vnode_label = mpe->mpe_function; break; case MAC_CREATE_DEVFS_DEVICE: mpc->mpc_ops->mpo_create_devfs_device = mpe->mpe_function; break; case MAC_CREATE_DEVFS_DIRECTORY: mpc->mpc_ops->mpo_create_devfs_directory = mpe->mpe_function; break; case MAC_CREATE_DEVFS_SYMLINK: mpc->mpc_ops->mpo_create_devfs_symlink = mpe->mpe_function; break; case MAC_CREATE_DEVFS_VNODE: mpc->mpc_ops->mpo_create_devfs_vnode = mpe->mpe_function; break; case MAC_CREATE_MOUNT: mpc->mpc_ops->mpo_create_mount = mpe->mpe_function; break; case MAC_CREATE_ROOT_MOUNT: mpc->mpc_ops->mpo_create_root_mount = mpe->mpe_function; break; case MAC_RELABEL_VNODE: mpc->mpc_ops->mpo_relabel_vnode = mpe->mpe_function; break; case MAC_UPDATE_DEVFSDIRENT: mpc->mpc_ops->mpo_update_devfsdirent = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_DEVFS: mpc->mpc_ops->mpo_associate_vnode_devfs = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_EXTATTR: mpc->mpc_ops->mpo_associate_vnode_extattr = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_SINGLELABEL: mpc->mpc_ops->mpo_associate_vnode_singlelabel = mpe->mpe_function; break; case MAC_CREATE_VNODE_EXTATTR: mpc->mpc_ops->mpo_create_vnode_extattr = mpe->mpe_function; break; case MAC_SETLABEL_VNODE_EXTATTR: mpc->mpc_ops->mpo_setlabel_vnode_extattr = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_SOCKET: mpc->mpc_ops->mpo_create_mbuf_from_socket = mpe->mpe_function; break; case MAC_CREATE_PIPE: mpc->mpc_ops->mpo_create_pipe = mpe->mpe_function; break; case MAC_CREATE_SOCKET: mpc->mpc_ops->mpo_create_socket = mpe->mpe_function; break; case MAC_CREATE_SOCKET_FROM_SOCKET: mpc->mpc_ops->mpo_create_socket_from_socket = mpe->mpe_function; break; case MAC_RELABEL_PIPE: mpc->mpc_ops->mpo_relabel_pipe = mpe->mpe_function; break; case MAC_RELABEL_SOCKET: mpc->mpc_ops->mpo_relabel_socket = mpe->mpe_function; break; case MAC_SET_SOCKET_PEER_FROM_MBUF: mpc->mpc_ops->mpo_set_socket_peer_from_mbuf = mpe->mpe_function; break; case MAC_SET_SOCKET_PEER_FROM_SOCKET: mpc->mpc_ops->mpo_set_socket_peer_from_socket = mpe->mpe_function; break; case MAC_CREATE_BPFDESC: mpc->mpc_ops->mpo_create_bpfdesc = mpe->mpe_function; break; case MAC_CREATE_DATAGRAM_FROM_IPQ: mpc->mpc_ops->mpo_create_datagram_from_ipq = mpe->mpe_function; break; case MAC_CREATE_FRAGMENT: mpc->mpc_ops->mpo_create_fragment = mpe->mpe_function; break; case MAC_CREATE_IFNET: mpc->mpc_ops->mpo_create_ifnet = mpe->mpe_function; break; case MAC_CREATE_IPQ: mpc->mpc_ops->mpo_create_ipq = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_MBUF: mpc->mpc_ops->mpo_create_mbuf_from_mbuf = mpe->mpe_function; break; case MAC_CREATE_MBUF_LINKLAYER: mpc->mpc_ops->mpo_create_mbuf_linklayer = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_BPFDESC: mpc->mpc_ops->mpo_create_mbuf_from_bpfdesc = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_IFNET: mpc->mpc_ops->mpo_create_mbuf_from_ifnet = mpe->mpe_function; break; case MAC_CREATE_MBUF_MULTICAST_ENCAP: mpc->mpc_ops->mpo_create_mbuf_multicast_encap = mpe->mpe_function; break; case MAC_CREATE_MBUF_NETLAYER: mpc->mpc_ops->mpo_create_mbuf_netlayer = mpe->mpe_function; break; case MAC_FRAGMENT_MATCH: mpc->mpc_ops->mpo_fragment_match = mpe->mpe_function; break; case MAC_RELABEL_IFNET: mpc->mpc_ops->mpo_relabel_ifnet = mpe->mpe_function; break; case MAC_UPDATE_IPQ: mpc->mpc_ops->mpo_update_ipq = mpe->mpe_function; break; case MAC_CREATE_CRED: mpc->mpc_ops->mpo_create_cred = mpe->mpe_function; break; case MAC_EXECVE_TRANSITION: mpc->mpc_ops->mpo_execve_transition = mpe->mpe_function; break; case MAC_EXECVE_WILL_TRANSITION: mpc->mpc_ops->mpo_execve_will_transition = mpe->mpe_function; break; case MAC_CREATE_PROC0: mpc->mpc_ops->mpo_create_proc0 = mpe->mpe_function; break; case MAC_CREATE_PROC1: mpc->mpc_ops->mpo_create_proc1 = mpe->mpe_function; break; case MAC_RELABEL_CRED: mpc->mpc_ops->mpo_relabel_cred = mpe->mpe_function; break; case MAC_THREAD_USERRET: mpc->mpc_ops->mpo_thread_userret = mpe->mpe_function; break; case MAC_CHECK_BPFDESC_RECEIVE: mpc->mpc_ops->mpo_check_bpfdesc_receive = mpe->mpe_function; break; case MAC_CHECK_CRED_RELABEL: mpc->mpc_ops->mpo_check_cred_relabel = mpe->mpe_function; break; case MAC_CHECK_CRED_VISIBLE: mpc->mpc_ops->mpo_check_cred_visible = mpe->mpe_function; break; case MAC_CHECK_IFNET_RELABEL: mpc->mpc_ops->mpo_check_ifnet_relabel = mpe->mpe_function; break; case MAC_CHECK_IFNET_TRANSMIT: mpc->mpc_ops->mpo_check_ifnet_transmit = mpe->mpe_function; break; case MAC_CHECK_MOUNT_STAT: mpc->mpc_ops->mpo_check_mount_stat = mpe->mpe_function; break; case MAC_CHECK_PIPE_IOCTL: mpc->mpc_ops->mpo_check_pipe_ioctl = mpe->mpe_function; break; case MAC_CHECK_PIPE_POLL: mpc->mpc_ops->mpo_check_pipe_poll = mpe->mpe_function; break; case MAC_CHECK_PIPE_READ: mpc->mpc_ops->mpo_check_pipe_read = mpe->mpe_function; break; case MAC_CHECK_PIPE_RELABEL: mpc->mpc_ops->mpo_check_pipe_relabel = mpe->mpe_function; break; case MAC_CHECK_PIPE_STAT: mpc->mpc_ops->mpo_check_pipe_stat = mpe->mpe_function; break; case MAC_CHECK_PIPE_WRITE: mpc->mpc_ops->mpo_check_pipe_write = mpe->mpe_function; break; case MAC_CHECK_PROC_DEBUG: mpc->mpc_ops->mpo_check_proc_debug = mpe->mpe_function; break; case MAC_CHECK_PROC_SCHED: mpc->mpc_ops->mpo_check_proc_sched = mpe->mpe_function; break; case MAC_CHECK_PROC_SIGNAL: mpc->mpc_ops->mpo_check_proc_signal = mpe->mpe_function; break; case MAC_CHECK_SOCKET_BIND: mpc->mpc_ops->mpo_check_socket_bind = mpe->mpe_function; break; case MAC_CHECK_SOCKET_CONNECT: mpc->mpc_ops->mpo_check_socket_connect = mpe->mpe_function; break; case MAC_CHECK_SOCKET_DELIVER: mpc->mpc_ops->mpo_check_socket_deliver = mpe->mpe_function; break; case MAC_CHECK_SOCKET_LISTEN: mpc->mpc_ops->mpo_check_socket_listen = mpe->mpe_function; break; case MAC_CHECK_SOCKET_RECEIVE: mpc->mpc_ops->mpo_check_socket_receive = mpe->mpe_function; break; case MAC_CHECK_SOCKET_RELABEL: mpc->mpc_ops->mpo_check_socket_relabel = mpe->mpe_function; break; case MAC_CHECK_SOCKET_SEND: mpc->mpc_ops->mpo_check_socket_send = mpe->mpe_function; break; case MAC_CHECK_SOCKET_VISIBLE: mpc->mpc_ops->mpo_check_socket_visible = mpe->mpe_function; break; case MAC_CHECK_SYSTEM_REBOOT: mpc->mpc_ops->mpo_check_system_reboot = mpe->mpe_function; break; case MAC_CHECK_SYSTEM_SWAPON: mpc->mpc_ops->mpo_check_system_swapon = mpe->mpe_function; break; + case MAC_CHECK_SYSTEM_SYSCTL: + mpc->mpc_ops->mpo_check_system_sysctl = + mpe->mpe_function; + break; case MAC_CHECK_VNODE_ACCESS: mpc->mpc_ops->mpo_check_vnode_access = mpe->mpe_function; break; case MAC_CHECK_VNODE_CHDIR: mpc->mpc_ops->mpo_check_vnode_chdir = mpe->mpe_function; break; case MAC_CHECK_VNODE_CHROOT: mpc->mpc_ops->mpo_check_vnode_chroot = mpe->mpe_function; break; case MAC_CHECK_VNODE_CREATE: mpc->mpc_ops->mpo_check_vnode_create = mpe->mpe_function; break; case MAC_CHECK_VNODE_DELETE: mpc->mpc_ops->mpo_check_vnode_delete = mpe->mpe_function; break; case MAC_CHECK_VNODE_DELETEACL: mpc->mpc_ops->mpo_check_vnode_deleteacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_EXEC: mpc->mpc_ops->mpo_check_vnode_exec = mpe->mpe_function; break; case MAC_CHECK_VNODE_GETACL: mpc->mpc_ops->mpo_check_vnode_getacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_GETEXTATTR: mpc->mpc_ops->mpo_check_vnode_getextattr = mpe->mpe_function; break; case MAC_CHECK_VNODE_LINK: mpc->mpc_ops->mpo_check_vnode_link = mpe->mpe_function; break; case MAC_CHECK_VNODE_LOOKUP: mpc->mpc_ops->mpo_check_vnode_lookup = mpe->mpe_function; break; case MAC_CHECK_VNODE_MMAP: mpc->mpc_ops->mpo_check_vnode_mmap = mpe->mpe_function; break; case MAC_CHECK_VNODE_MMAP_DOWNGRADE: mpc->mpc_ops->mpo_check_vnode_mmap_downgrade = mpe->mpe_function; break; case MAC_CHECK_VNODE_MPROTECT: mpc->mpc_ops->mpo_check_vnode_mprotect = mpe->mpe_function; break; case MAC_CHECK_VNODE_OPEN: mpc->mpc_ops->mpo_check_vnode_open = mpe->mpe_function; break; case MAC_CHECK_VNODE_POLL: mpc->mpc_ops->mpo_check_vnode_poll = mpe->mpe_function; break; case MAC_CHECK_VNODE_READ: mpc->mpc_ops->mpo_check_vnode_read = mpe->mpe_function; break; case MAC_CHECK_VNODE_READDIR: mpc->mpc_ops->mpo_check_vnode_readdir = mpe->mpe_function; break; case MAC_CHECK_VNODE_READLINK: mpc->mpc_ops->mpo_check_vnode_readlink = mpe->mpe_function; break; case MAC_CHECK_VNODE_RELABEL: mpc->mpc_ops->mpo_check_vnode_relabel = mpe->mpe_function; break; case MAC_CHECK_VNODE_RENAME_FROM: mpc->mpc_ops->mpo_check_vnode_rename_from = mpe->mpe_function; break; case MAC_CHECK_VNODE_RENAME_TO: mpc->mpc_ops->mpo_check_vnode_rename_to = mpe->mpe_function; break; case MAC_CHECK_VNODE_REVOKE: mpc->mpc_ops->mpo_check_vnode_revoke = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETACL: mpc->mpc_ops->mpo_check_vnode_setacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETEXTATTR: mpc->mpc_ops->mpo_check_vnode_setextattr = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETFLAGS: mpc->mpc_ops->mpo_check_vnode_setflags = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETMODE: mpc->mpc_ops->mpo_check_vnode_setmode = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETOWNER: mpc->mpc_ops->mpo_check_vnode_setowner = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETUTIMES: mpc->mpc_ops->mpo_check_vnode_setutimes = mpe->mpe_function; break; case MAC_CHECK_VNODE_STAT: mpc->mpc_ops->mpo_check_vnode_stat = mpe->mpe_function; break; case MAC_CHECK_VNODE_WRITE: mpc->mpc_ops->mpo_check_vnode_write = mpe->mpe_function; break; /* default: printf("MAC policy `%s': unknown operation %d\n", mpc->mpc_name, mpe->mpe_constant); return (EINVAL); */ } } MAC_POLICY_LIST_LOCK(); if (mac_policy_list_busy > 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (EBUSY); } LIST_FOREACH(tmpc, &mac_policy_list, mpc_list) { if (strcmp(tmpc->mpc_name, mpc->mpc_name) == 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (EEXIST); } } if (mpc->mpc_field_off != NULL) { slot = ffs(mac_policy_offsets_free); if (slot == 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (ENOMEM); } slot--; mac_policy_offsets_free &= ~(1 << slot); *mpc->mpc_field_off = slot; } mpc->mpc_runtime_flags |= MPC_RUNTIME_FLAG_REGISTERED; LIST_INSERT_HEAD(&mac_policy_list, mpc, mpc_list); /* Per-policy initialization. */ if (mpc->mpc_ops->mpo_init != NULL) (*(mpc->mpc_ops->mpo_init))(mpc); MAC_POLICY_LIST_UNLOCK(); printf("Security policy loaded: %s (%s)\n", mpc->mpc_fullname, mpc->mpc_name); return (0); } 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_LIST_LOCK(); if ((mpc->mpc_runtime_flags & MPC_RUNTIME_FLAG_REGISTERED) == 0) { MAC_POLICY_LIST_UNLOCK(); return (0); } #if 0 /* * Don't allow unloading modules with private data. */ if (mpc->mpc_field_off != NULL) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } #endif /* * Only allow the unload to proceed if the module is unloadable * by its own definition. */ if ((mpc->mpc_loadtime_flags & MPC_LOADTIME_FLAG_UNLOADOK) == 0) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } /* * Right now, we EBUSY if the list is in use. In the future, * for reliability reasons, we might want to sleep and wakeup * later to try again. */ if (mac_policy_list_busy > 0) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } if (mpc->mpc_ops->mpo_destroy != NULL) (*(mpc->mpc_ops->mpo_destroy))(mpc); LIST_REMOVE(mpc, mpc_list); MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; mpc->mpc_runtime_flags &= ~MPC_RUNTIME_FLAG_REGISTERED; printf("Security policy unload: %s (%s)\n", mpc->mpc_fullname, mpc->mpc_name); return (0); } /* * Define an error value precedence, and given two arguments, selects the * value with the higher precedence. */ static int 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); } static void mac_init_label(struct label *label) { bzero(label, sizeof(*label)); label->l_flags = MAC_FLAG_INITIALIZED; } static void mac_destroy_label(struct label *label) { KASSERT(label->l_flags & MAC_FLAG_INITIALIZED, ("destroying uninitialized label")); bzero(label, sizeof(*label)); /* implicit: label->l_flags &= ~MAC_FLAG_INITIALIZED; */ } void mac_init_bpfdesc(struct bpf_d *bpf_d) { mac_init_label(&bpf_d->bd_label); MAC_PERFORM(init_bpfdesc_label, &bpf_d->bd_label); #ifdef MAC_DEBUG atomic_add_int(&nmacbpfdescs, 1); #endif } static void mac_init_cred_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_cred_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmaccreds, 1); #endif } void mac_init_cred(struct ucred *cred) { mac_init_cred_label(&cred->cr_label); } void mac_init_devfsdirent(struct devfs_dirent *de) { mac_init_label(&de->de_label); MAC_PERFORM(init_devfsdirent_label, &de->de_label); #ifdef MAC_DEBUG atomic_add_int(&nmacdevfsdirents, 1); #endif } static void mac_init_ifnet_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_ifnet_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacifnets, 1); #endif } void mac_init_ifnet(struct ifnet *ifp) { mac_init_ifnet_label(&ifp->if_label); } void mac_init_ipq(struct ipq *ipq) { mac_init_label(&ipq->ipq_label); MAC_PERFORM(init_ipq_label, &ipq->ipq_label); #ifdef MAC_DEBUG atomic_add_int(&nmacipqs, 1); #endif } int mac_init_mbuf(struct mbuf *m, int flag) { int error; KASSERT(m->m_flags & M_PKTHDR, ("mac_init_mbuf on non-header mbuf")); mac_init_label(&m->m_pkthdr.label); MAC_CHECK(init_mbuf_label, &m->m_pkthdr.label, flag); if (error) { MAC_PERFORM(destroy_mbuf_label, &m->m_pkthdr.label); mac_destroy_label(&m->m_pkthdr.label); } #ifdef MAC_DEBUG if (error == 0) atomic_add_int(&nmacmbufs, 1); #endif return (error); } void mac_init_mount(struct mount *mp) { mac_init_label(&mp->mnt_mntlabel); mac_init_label(&mp->mnt_fslabel); MAC_PERFORM(init_mount_label, &mp->mnt_mntlabel); MAC_PERFORM(init_mount_fs_label, &mp->mnt_fslabel); #ifdef MAC_DEBUG atomic_add_int(&nmacmounts, 1); #endif } static void mac_init_pipe_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_pipe_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacpipes, 1); #endif } void mac_init_pipe(struct pipe *pipe) { struct label *label; label = malloc(sizeof(struct label), M_MACPIPELABEL, M_ZERO|M_WAITOK); pipe->pipe_label = label; pipe->pipe_peer->pipe_label = label; mac_init_pipe_label(label); } static int mac_init_socket_label(struct label *label, int flag) { int error; mac_init_label(label); MAC_CHECK(init_socket_label, label, flag); if (error) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); } #ifdef MAC_DEBUG if (error == 0) atomic_add_int(&nmacsockets, 1); #endif return (error); } static int mac_init_socket_peer_label(struct label *label, int flag) { int error; mac_init_label(label); MAC_CHECK(init_socket_peer_label, label, flag); if (error) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); } return (error); } int mac_init_socket(struct socket *socket, int flag) { int error; error = mac_init_socket_label(&socket->so_label, flag); if (error) return (error); error = mac_init_socket_peer_label(&socket->so_peerlabel, flag); if (error) mac_destroy_socket_label(&socket->so_label); return (error); } void mac_init_vnode_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_vnode_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacvnodes, 1); #endif } void mac_init_vnode(struct vnode *vp) { mac_init_vnode_label(&vp->v_label); } void mac_destroy_bpfdesc(struct bpf_d *bpf_d) { MAC_PERFORM(destroy_bpfdesc_label, &bpf_d->bd_label); mac_destroy_label(&bpf_d->bd_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacbpfdescs, 1); #endif } static void mac_destroy_cred_label(struct label *label) { MAC_PERFORM(destroy_cred_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmaccreds, 1); #endif } void mac_destroy_cred(struct ucred *cred) { mac_destroy_cred_label(&cred->cr_label); } void mac_destroy_devfsdirent(struct devfs_dirent *de) { MAC_PERFORM(destroy_devfsdirent_label, &de->de_label); mac_destroy_label(&de->de_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacdevfsdirents, 1); #endif } static void mac_destroy_ifnet_label(struct label *label) { MAC_PERFORM(destroy_ifnet_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacifnets, 1); #endif } void mac_destroy_ifnet(struct ifnet *ifp) { mac_destroy_ifnet_label(&ifp->if_label); } void mac_destroy_ipq(struct ipq *ipq) { MAC_PERFORM(destroy_ipq_label, &ipq->ipq_label); mac_destroy_label(&ipq->ipq_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacipqs, 1); #endif } void mac_destroy_mbuf(struct mbuf *m) { MAC_PERFORM(destroy_mbuf_label, &m->m_pkthdr.label); mac_destroy_label(&m->m_pkthdr.label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacmbufs, 1); #endif } void mac_destroy_mount(struct mount *mp) { MAC_PERFORM(destroy_mount_label, &mp->mnt_mntlabel); MAC_PERFORM(destroy_mount_fs_label, &mp->mnt_fslabel); mac_destroy_label(&mp->mnt_fslabel); mac_destroy_label(&mp->mnt_mntlabel); #ifdef MAC_DEBUG atomic_subtract_int(&nmacmounts, 1); #endif } static void mac_destroy_pipe_label(struct label *label) { MAC_PERFORM(destroy_pipe_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacpipes, 1); #endif } void mac_destroy_pipe(struct pipe *pipe) { mac_destroy_pipe_label(pipe->pipe_label); free(pipe->pipe_label, M_MACPIPELABEL); } static void mac_destroy_socket_label(struct label *label) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacsockets, 1); #endif } static void mac_destroy_socket_peer_label(struct label *label) { MAC_PERFORM(destroy_socket_peer_label, label); mac_destroy_label(label); } void mac_destroy_socket(struct socket *socket) { mac_destroy_socket_label(&socket->so_label); mac_destroy_socket_peer_label(&socket->so_peerlabel); } void mac_destroy_vnode_label(struct label *label) { MAC_PERFORM(destroy_vnode_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacvnodes, 1); #endif } void mac_destroy_vnode(struct vnode *vp) { mac_destroy_vnode_label(&vp->v_label); } static void mac_copy_pipe_label(struct label *src, struct label *dest) { MAC_PERFORM(copy_pipe_label, src, dest); } void mac_copy_vnode_label(struct label *src, struct label *dest) { MAC_PERFORM(copy_vnode_label, src, dest); } static int mac_check_structmac_consistent(struct mac *mac) { if (mac->m_buflen > MAC_MAX_LABEL_BUF_LEN) return (EINVAL); return (0); } static int mac_externalize_cred_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(cred_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_ifnet_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(ifnet_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_pipe_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(pipe_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_socket_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(socket_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_socket_peer_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(socket_peer_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_vnode_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(vnode_label, label, elements, outbuf, outbuflen); return (error); } static int mac_internalize_cred_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(cred_label, label, string); return (error); } static int mac_internalize_ifnet_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(ifnet_label, label, string); return (error); } static int mac_internalize_pipe_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(pipe_label, label, string); return (error); } static int mac_internalize_socket_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(socket_label, label, string); return (error); } static int mac_internalize_vnode_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(vnode_label, label, string); return (error); } /* * Initialize MAC label for the first kernel process, from which other * kernel processes and threads are spawned. */ void mac_create_proc0(struct ucred *cred) { MAC_PERFORM(create_proc0, cred); } /* * Initialize MAC label for the first userland process, from which other * userland processes and threads are spawned. */ void mac_create_proc1(struct ucred *cred) { MAC_PERFORM(create_proc1, cred); } void mac_thread_userret(struct thread *td) { MAC_PERFORM(thread_userret, td); } /* * 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_create_cred(struct ucred *parent_cred, struct ucred *child_cred) { MAC_PERFORM(create_cred, parent_cred, child_cred); } void mac_update_devfsdirent(struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(update_devfsdirent, de, &de->de_label, vp, &vp->v_label); } void mac_associate_vnode_devfs(struct mount *mp, struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(associate_vnode_devfs, mp, &mp->mnt_fslabel, de, &de->de_label, vp, &vp->v_label); } int mac_associate_vnode_extattr(struct mount *mp, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_associate_vnode_extattr"); MAC_CHECK(associate_vnode_extattr, mp, &mp->mnt_fslabel, vp, &vp->v_label); return (error); } void mac_associate_vnode_singlelabel(struct mount *mp, struct vnode *vp) { MAC_PERFORM(associate_vnode_singlelabel, mp, &mp->mnt_fslabel, vp, &vp->v_label); } int mac_create_vnode_extattr(struct ucred *cred, struct mount *mp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_create_vnode_extattr"); ASSERT_VOP_LOCKED(vp, "mac_create_vnode_extattr"); error = VOP_OPENEXTATTR(vp, cred, curthread); if (error == EOPNOTSUPP) { /* XXX: Optionally abort if transactions not supported. */ if (ea_warn_once == 0) { printf("Warning: transactions not supported " "in EA write.\n"); ea_warn_once = 1; } } else if (error) return (error); MAC_CHECK(create_vnode_extattr, cred, mp, &mp->mnt_fslabel, 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; /* XXX */ return (error); } static int mac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *intlabel) { int error; ASSERT_VOP_LOCKED(vp, "mac_setlabel_vnode_extattr"); error = VOP_OPENEXTATTR(vp, cred, curthread); if (error == EOPNOTSUPP) { /* XXX: Optionally abort if transactions not supported. */ if (ea_warn_once == 0) { printf("Warning: transactions not supported " "in EA write.\n"); ea_warn_once = 1; } } else if (error) return (error); MAC_CHECK(setlabel_vnode_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; /* XXX */ return (error); } void mac_execve_transition(struct ucred *old, struct ucred *new, struct vnode *vp) { ASSERT_VOP_LOCKED(vp, "mac_execve_transition"); MAC_PERFORM(execve_transition, old, new, vp, &vp->v_label); } int mac_execve_will_transition(struct ucred *old, struct vnode *vp) { int result; result = 0; MAC_BOOLEAN(execve_will_transition, ||, old, vp, &vp->v_label); return (result); } int mac_check_vnode_access(struct ucred *cred, struct vnode *vp, int flags) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_access"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_access, cred, vp, &vp->v_label, flags); return (error); } int mac_check_vnode_chdir(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_chdir"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_chdir, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_chroot(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_chroot"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_chroot, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_create(struct ucred *cred, struct vnode *dvp, struct componentname *cnp, struct vattr *vap) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_create"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_create, cred, dvp, &dvp->v_label, cnp, vap); return (error); } int mac_check_vnode_delete(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_delete"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_delete"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_delete, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp, acl_type_t type) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_deleteacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_deleteacl, cred, vp, &vp->v_label, type); return (error); } int mac_check_vnode_exec(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_exec"); if (!mac_enforce_process && !mac_enforce_fs) return (0); MAC_CHECK(check_vnode_exec, cred, vp, &vp->v_label); return (error); } int mac_check_vnode_getacl(struct ucred *cred, struct vnode *vp, acl_type_t type) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_getacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_getacl, cred, vp, &vp->v_label, type); return (error); } int mac_check_vnode_getextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_getextattr"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_getextattr, cred, vp, &vp->v_label, attrnamespace, name, uio); return (error); } int mac_check_vnode_link(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_link"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_link"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_link, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_lookup(struct ucred *cred, struct vnode *dvp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_lookup"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_lookup, cred, dvp, &dvp->v_label, cnp); return (error); } int mac_check_vnode_mmap(struct ucred *cred, struct vnode *vp, int prot) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mmap"); if (!mac_enforce_fs || !mac_enforce_vm) return (0); MAC_CHECK(check_vnode_mmap, cred, vp, &vp->v_label, prot); return (error); } void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot) { int result = *prot; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mmap_downgrade"); if (!mac_enforce_fs || !mac_enforce_vm) return; MAC_PERFORM(check_vnode_mmap_downgrade, cred, vp, &vp->v_label, &result); *prot = result; } int mac_check_vnode_mprotect(struct ucred *cred, struct vnode *vp, int prot) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mprotect"); if (!mac_enforce_fs || !mac_enforce_vm) return (0); MAC_CHECK(check_vnode_mprotect, cred, vp, &vp->v_label, prot); return (error); } int mac_check_vnode_open(struct ucred *cred, struct vnode *vp, mode_t acc_mode) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_open"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_open, cred, vp, &vp->v_label, acc_mode); return (error); } int mac_check_vnode_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_poll"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_poll, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_read"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_read, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_readdir(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_readdir"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_readdir, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_readlink(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_readlink"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_readlink, cred, vp, &vp->v_label); return (error); } static int mac_check_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *newlabel) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_relabel"); MAC_CHECK(check_vnode_relabel, cred, vp, &vp->v_label, newlabel); return (error); } int mac_check_vnode_rename_from(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_rename_from"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_rename_from"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_rename_from, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp, struct vnode *vp, int samedir, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_rename_to"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_rename_to"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_rename_to, cred, dvp, &dvp->v_label, vp, vp != NULL ? &vp->v_label : NULL, samedir, cnp); return (error); } int mac_check_vnode_revoke(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_revoke"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_revoke, cred, vp, &vp->v_label); return (error); } int mac_check_vnode_setacl(struct ucred *cred, struct vnode *vp, acl_type_t type, struct acl *acl) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setacl, cred, vp, &vp->v_label, type, acl); return (error); } int mac_check_vnode_setextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setextattr"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setextattr, cred, vp, &vp->v_label, attrnamespace, name, uio); return (error); } int mac_check_vnode_setflags(struct ucred *cred, struct vnode *vp, u_long flags) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setflags"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setflags, cred, vp, &vp->v_label, flags); return (error); } int mac_check_vnode_setmode(struct ucred *cred, struct vnode *vp, mode_t mode) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setmode"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setmode, cred, vp, &vp->v_label, mode); return (error); } int mac_check_vnode_setowner(struct ucred *cred, struct vnode *vp, uid_t uid, gid_t gid) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setowner"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setowner, cred, vp, &vp->v_label, uid, gid); return (error); } int mac_check_vnode_setutimes(struct ucred *cred, struct vnode *vp, struct timespec atime, struct timespec mtime) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setutimes"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setutimes, cred, vp, &vp->v_label, atime, mtime); return (error); } int mac_check_vnode_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_stat"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_stat, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_write"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_write, active_cred, file_cred, vp, &vp->v_label); return (error); } /* * 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. */ static void mac_cred_mmapped_drop_perms(struct thread *td, struct ucred *cred) { /* XXX freeze all other threads */ mac_cred_mmapped_drop_perms_recurse(td, cred, &td->td_proc->p_vmspace->vm_map); /* XXX allow other threads to continue */ } 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_cred_mmapped_drop_perms_recurse(struct thread *td, struct ucred *cred, struct vm_map *map) { struct vm_map_entry *vme; int result; vm_prot_t revokeperms; vm_object_t object; vm_ooffset_t offset; struct vnode *vp; if (!mac_mmap_revocation) return; vm_map_lock_read(map); for (vme = map->header.next; vme != &map->header; vme = vme->next) { if (vme->eflags & MAP_ENTRY_IS_SUB_MAP) { mac_cred_mmapped_drop_perms_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; while (object->backing_object != NULL) { object = object->backing_object; offset += object->backing_object_offset; } /* * 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, td); result = vme->max_protection; mac_check_vnode_mmap_downgrade(cred, vp, &result); VOP_UNLOCK(vp, 0, td); /* * 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)); vm_map_lock_upgrade(map); /* * 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); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); vm_object_page_clean(object, OFF_TO_IDX(offset), OFF_TO_IDX(offset + vme->end - vme->start + PAGE_MASK), OBJPC_SYNC); VOP_UNLOCK(vp, 0, td); 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_lock_downgrade(map); } vm_map_unlock_read(map); } /* * 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. */ static void mac_relabel_cred(struct ucred *cred, struct label *newlabel) { MAC_PERFORM(relabel_cred, cred, newlabel); } void mac_relabel_vnode(struct ucred *cred, struct vnode *vp, struct label *newlabel) { MAC_PERFORM(relabel_vnode, cred, vp, &vp->v_label, newlabel); } void mac_create_ifnet(struct ifnet *ifnet) { MAC_PERFORM(create_ifnet, ifnet, &ifnet->if_label); } void mac_create_bpfdesc(struct ucred *cred, struct bpf_d *bpf_d) { MAC_PERFORM(create_bpfdesc, cred, bpf_d, &bpf_d->bd_label); } void mac_create_socket(struct ucred *cred, struct socket *socket) { MAC_PERFORM(create_socket, cred, socket, &socket->so_label); } void mac_create_pipe(struct ucred *cred, struct pipe *pipe) { MAC_PERFORM(create_pipe, cred, pipe, pipe->pipe_label); } void mac_create_socket_from_socket(struct socket *oldsocket, struct socket *newsocket) { MAC_PERFORM(create_socket_from_socket, oldsocket, &oldsocket->so_label, newsocket, &newsocket->so_label); } static void mac_relabel_socket(struct ucred *cred, struct socket *socket, struct label *newlabel) { MAC_PERFORM(relabel_socket, cred, socket, &socket->so_label, newlabel); } static void mac_relabel_pipe(struct ucred *cred, struct pipe *pipe, struct label *newlabel) { MAC_PERFORM(relabel_pipe, cred, pipe, pipe->pipe_label, newlabel); } void mac_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct socket *socket) { MAC_PERFORM(set_socket_peer_from_mbuf, mbuf, &mbuf->m_pkthdr.label, socket, &socket->so_peerlabel); } void mac_set_socket_peer_from_socket(struct socket *oldsocket, struct socket *newsocket) { MAC_PERFORM(set_socket_peer_from_socket, oldsocket, &oldsocket->so_label, newsocket, &newsocket->so_peerlabel); } void mac_create_datagram_from_ipq(struct ipq *ipq, struct mbuf *datagram) { MAC_PERFORM(create_datagram_from_ipq, ipq, &ipq->ipq_label, datagram, &datagram->m_pkthdr.label); } void mac_create_fragment(struct mbuf *datagram, struct mbuf *fragment) { MAC_PERFORM(create_fragment, datagram, &datagram->m_pkthdr.label, fragment, &fragment->m_pkthdr.label); } void mac_create_ipq(struct mbuf *fragment, struct ipq *ipq) { MAC_PERFORM(create_ipq, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); } void mac_create_mbuf_from_mbuf(struct mbuf *oldmbuf, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_from_mbuf, oldmbuf, &oldmbuf->m_pkthdr.label, newmbuf, &newmbuf->m_pkthdr.label); } void mac_create_mbuf_from_bpfdesc(struct bpf_d *bpf_d, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_bpfdesc, bpf_d, &bpf_d->bd_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_linklayer(struct ifnet *ifnet, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_linklayer, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_from_ifnet(struct ifnet *ifnet, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_ifnet, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_multicast_encap(struct mbuf *oldmbuf, struct ifnet *ifnet, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_multicast_encap, oldmbuf, &oldmbuf->m_pkthdr.label, ifnet, &ifnet->if_label, newmbuf, &newmbuf->m_pkthdr.label); } void mac_create_mbuf_netlayer(struct mbuf *oldmbuf, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_netlayer, oldmbuf, &oldmbuf->m_pkthdr.label, newmbuf, &newmbuf->m_pkthdr.label); } int mac_fragment_match(struct mbuf *fragment, struct ipq *ipq) { int result; result = 1; MAC_BOOLEAN(fragment_match, &&, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); return (result); } void mac_update_ipq(struct mbuf *fragment, struct ipq *ipq) { MAC_PERFORM(update_ipq, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); } void mac_create_mbuf_from_socket(struct socket *socket, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_socket, socket, &socket->so_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mount(struct ucred *cred, struct mount *mp) { MAC_PERFORM(create_mount, cred, mp, &mp->mnt_mntlabel, &mp->mnt_fslabel); } void mac_create_root_mount(struct ucred *cred, struct mount *mp) { MAC_PERFORM(create_root_mount, cred, mp, &mp->mnt_mntlabel, &mp->mnt_fslabel); } int mac_check_bpfdesc_receive(struct bpf_d *bpf_d, struct ifnet *ifnet) { int error; if (!mac_enforce_network) return (0); MAC_CHECK(check_bpfdesc_receive, bpf_d, &bpf_d->bd_label, ifnet, &ifnet->if_label); return (error); } static int mac_check_cred_relabel(struct ucred *cred, struct label *newlabel) { int error; MAC_CHECK(check_cred_relabel, cred, newlabel); return (error); } int mac_check_cred_visible(struct ucred *u1, struct ucred *u2) { int error; if (!mac_enforce_process) return (0); MAC_CHECK(check_cred_visible, u1, u2); return (error); } int mac_check_ifnet_transmit(struct ifnet *ifnet, struct mbuf *mbuf) { int error; if (!mac_enforce_network) return (0); KASSERT(mbuf->m_flags & M_PKTHDR, ("packet has no pkthdr")); if (!(mbuf->m_pkthdr.label.l_flags & MAC_FLAG_INITIALIZED)) if_printf(ifnet, "not initialized\n"); MAC_CHECK(check_ifnet_transmit, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); return (error); } int mac_check_mount_stat(struct ucred *cred, struct mount *mount) { int error; if (!mac_enforce_fs) return (0); MAC_CHECK(check_mount_stat, cred, mount, &mount->mnt_mntlabel); return (error); } int mac_check_pipe_ioctl(struct ucred *cred, struct pipe *pipe, unsigned long cmd, void *data) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_ioctl, cred, pipe, pipe->pipe_label, cmd, data); return (error); } int mac_check_pipe_poll(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_poll, cred, pipe, pipe->pipe_label); return (error); } int mac_check_pipe_read(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_read, cred, pipe, pipe->pipe_label); return (error); } static int mac_check_pipe_relabel(struct ucred *cred, struct pipe *pipe, struct label *newlabel) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_relabel, cred, pipe, pipe->pipe_label, newlabel); return (error); } int mac_check_pipe_stat(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_stat, cred, pipe, pipe->pipe_label); return (error); } int mac_check_pipe_write(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_write, cred, pipe, pipe->pipe_label); return (error); } int mac_check_proc_debug(struct ucred *cred, struct proc *proc) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_debug, cred, proc); return (error); } int mac_check_proc_sched(struct ucred *cred, struct proc *proc) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_sched, cred, proc); return (error); } int mac_check_proc_signal(struct ucred *cred, struct proc *proc, int signum) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_signal, cred, proc, signum); return (error); } int mac_check_socket_bind(struct ucred *ucred, struct socket *socket, struct sockaddr *sockaddr) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_bind, ucred, socket, &socket->so_label, sockaddr); return (error); } int mac_check_socket_connect(struct ucred *cred, struct socket *socket, struct sockaddr *sockaddr) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_connect, cred, socket, &socket->so_label, sockaddr); return (error); } int mac_check_socket_deliver(struct socket *socket, struct mbuf *mbuf) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_deliver, socket, &socket->so_label, mbuf, &mbuf->m_pkthdr.label); return (error); } int mac_check_socket_listen(struct ucred *cred, struct socket *socket) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_listen, cred, socket, &socket->so_label); return (error); } int mac_check_socket_receive(struct ucred *cred, struct socket *so) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_receive, cred, so, &so->so_label); return (error); } static int mac_check_socket_relabel(struct ucred *cred, struct socket *socket, struct label *newlabel) { int error; MAC_CHECK(check_socket_relabel, cred, socket, &socket->so_label, newlabel); return (error); } int mac_check_socket_send(struct ucred *cred, struct socket *so) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_send, cred, so, &so->so_label); return (error); } int mac_check_socket_visible(struct ucred *cred, struct socket *socket) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_visible, cred, socket, &socket->so_label); return (error); } int mac_check_system_reboot(struct ucred *cred, int howto) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_system_reboot"); if (!mac_enforce_reboot) return (0); MAC_CHECK(check_system_reboot, cred, howto); return (error); } int mac_check_system_swapon(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_system_swapon"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_system_swapon, cred, vp, &vp->v_label); + return (error); +} + +int +mac_check_system_sysctl(struct ucred *cred, int *name, u_int namelen, + void *old, size_t *oldlenp, int inkernel, void *new, size_t newlen) +{ + int error; + + /* + * XXXMAC: We're very much like to assert the SYSCTL_LOCK here, + * but since it's not exported from kern_sysctl.c, we can't. + */ + if (!mac_enforce_sysctl) + return (0); + + MAC_CHECK(check_system_sysctl, cred, name, namelen, old, oldlenp, + inkernel, new, newlen); + return (error); } int mac_ioctl_ifnet_get(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet) { char *elements, *buffer; struct mac mac; int error; 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); error = mac_externalize_ifnet_label(&ifnet->if_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } int mac_ioctl_ifnet_set(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet) { struct label intlabel; struct mac mac; char *buffer; int error; 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); } mac_init_ifnet_label(&intlabel); error = mac_internalize_ifnet_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_ifnet_label(&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 = suser_cred(cred, 0); if (error) { mac_destroy_ifnet_label(&intlabel); return (error); } MAC_CHECK(check_ifnet_relabel, cred, ifnet, &ifnet->if_label, &intlabel); if (error) { mac_destroy_ifnet_label(&intlabel); return (error); } MAC_PERFORM(relabel_ifnet, cred, ifnet, &ifnet->if_label, &intlabel); mac_destroy_ifnet_label(&intlabel); return (0); } void mac_create_devfs_vnode(struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(create_devfs_vnode, de, &de->de_label, vp, &vp->v_label); } void mac_create_devfs_device(dev_t dev, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_device, dev, de, &de->de_label); } void mac_create_devfs_symlink(struct ucred *cred, struct devfs_dirent *dd, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_symlink, cred, dd, &dd->de_label, de, &de->de_label); } void mac_create_devfs_directory(char *dirname, int dirnamelen, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_directory, dirname, dirnamelen, de, &de->de_label); } int mac_setsockopt_label_set(struct ucred *cred, struct socket *so, struct mac *mac) { struct label intlabel; char *buffer; int 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); } mac_init_socket_label(&intlabel, M_WAITOK); error = mac_internalize_socket_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_socket_label(&intlabel); return (error); } mac_check_socket_relabel(cred, so, &intlabel); if (error) { mac_destroy_socket_label(&intlabel); return (error); } mac_relabel_socket(cred, so, &intlabel); mac_destroy_socket_label(&intlabel); return (0); } int mac_pipe_label_set(struct ucred *cred, struct pipe *pipe, struct label *label) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); error = mac_check_pipe_relabel(cred, pipe, label); if (error) return (error); mac_relabel_pipe(cred, pipe, label); return (0); } int mac_getsockopt_label_get(struct ucred *cred, struct socket *so, struct mac *mac) { char *buffer, *elements; int 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_externalize_socket_label(&so->so_label, elements, buffer, mac->m_buflen, M_WAITOK); 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_get(struct ucred *cred, struct socket *so, struct mac *mac) { char *elements, *buffer; int 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_externalize_socket_peer_label(&so->so_peerlabel, elements, buffer, mac->m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac->m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * 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_setlabel_vnode_extattr(ap->a_cred, vp, intlabel); if (error) return (error); mac_relabel_vnode(ap->a_cred, vp, intlabel); return (0); } static 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_check_vnode_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); } int __mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) { char *elements, *buffer; struct mac mac; struct proc *tproc; struct ucred *tcred; int error; error = copyin(SCARG(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_externalize_cred_label(&tcred->cr_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); crfree(tcred); return (error); } /* * MPSAFE */ int __mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) { char *elements, *buffer; struct mac mac; int error; error = copyin(uap->mac_p, &mac, sizeof(mac)); if (error) return (error); error = mac_check_structmac_consistent(&mac); if (error) return (error); elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL); if (error) { free(elements, M_MACTEMP); return (error); } buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); error = mac_externalize_cred_label(&td->td_ucred->cr_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) { struct ucred *newcred, *oldcred; struct label intlabel; struct proc *p; struct mac mac; char *buffer; int error; 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); } mac_init_cred_label(&intlabel); error = mac_internalize_cred_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_cred_label(&intlabel); return (error); } newcred = crget(); p = td->td_proc; PROC_LOCK(p); oldcred = p->p_ucred; error = mac_check_cred_relabel(oldcred, &intlabel); if (error) { PROC_UNLOCK(p); crfree(newcred); goto out; } setsugid(p); crcopy(newcred, oldcred); mac_relabel_cred(newcred, &intlabel); p->p_ucred = newcred; /* * Grab additional reference for use while revoking mmaps, prior * to releasing the proc lock and sharing the cred. */ crhold(newcred); PROC_UNLOCK(p); if (mac_enforce_vm) { mtx_lock(&Giant); mac_cred_mmapped_drop_perms(td, newcred); mtx_unlock(&Giant); } crfree(newcred); /* Free revocation reference. */ crfree(oldcred); out: mac_destroy_cred_label(&intlabel); return (error); } /* * MPSAFE */ int __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) { char *elements, *buffer; struct label intlabel; struct file *fp; struct mac mac; struct vnode *vp; struct pipe *pipe; 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); mtx_lock(&Giant); /* VFS */ error = fget(td, SCARG(uap, fd), &fp); if (error) goto out; label_type = fp->f_type; switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: vp = (struct vnode *)fp->f_data; mac_init_vnode_label(&intlabel); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); mac_copy_vnode_label(&vp->v_label, &intlabel); VOP_UNLOCK(vp, 0, td); break; case DTYPE_PIPE: pipe = (struct pipe *)fp->f_data; mac_init_pipe_label(&intlabel); PIPE_LOCK(pipe); mac_copy_pipe_label(pipe->pipe_label, &intlabel); PIPE_UNLOCK(pipe); break; default: error = EINVAL; fdrop(fp, td); goto out; } fdrop(fp, td); switch (label_type) { case DTYPE_FIFO: case DTYPE_VNODE: if (error == 0) error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); mac_destroy_vnode_label(&intlabel); break; case DTYPE_PIPE: error = mac_externalize_pipe_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); mac_destroy_pipe_label(&intlabel); break; default: panic("__mac_get_fd: corrupted label_type"); } if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_get_file(struct thread *td, struct __mac_get_file_args *uap) { char *elements, *buffer; struct nameidata nd; struct label intlabel; struct mac mac; int 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); mtx_lock(&Giant); /* VFS */ NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_USERSPACE, uap->path_p, td); error = namei(&nd); if (error) goto out; mac_init_vnode_label(&intlabel); mac_copy_vnode_label(&nd.ni_vp->v_label, &intlabel); error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); NDFREE(&nd, 0); mac_destroy_vnode_label(&intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_get_link(struct thread *td, struct __mac_get_link_args *uap) { char *elements, *buffer; struct nameidata nd; struct label intlabel; struct mac mac; int 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); mtx_lock(&Giant); /* VFS */ NDINIT(&nd, LOOKUP, LOCKLEAF | NOFOLLOW, UIO_USERSPACE, uap->path_p, td); error = namei(&nd); if (error) goto out; mac_init_vnode_label(&intlabel); mac_copy_vnode_label(&nd.ni_vp->v_label, &intlabel); error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); NDFREE(&nd, 0); mac_destroy_vnode_label(&intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) { struct label intlabel; struct pipe *pipe; 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); } mtx_lock(&Giant); /* VFS */ error = fget(td, SCARG(uap, fd), &fp); if (error) goto out; switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); if (error) { mac_destroy_vnode_label(&intlabel); break; } vp = (struct vnode *)fp->f_data; error = vn_start_write(vp, &mp, V_WAIT | PCATCH); if (error != 0) { mac_destroy_vnode_label(&intlabel); break; } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); error = vn_setlabel(vp, &intlabel, td->td_ucred); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); mac_destroy_vnode_label(&intlabel); break; case DTYPE_PIPE: mac_init_pipe_label(&intlabel); error = mac_internalize_pipe_label(&intlabel, buffer); if (error == 0) { pipe = (struct pipe *)fp->f_data; PIPE_LOCK(pipe); error = mac_pipe_label_set(td->td_ucred, pipe, &intlabel); PIPE_UNLOCK(pipe); } mac_destroy_pipe_label(&intlabel); break; default: error = EINVAL; } fdrop(fp, td); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_file(struct thread *td, struct __mac_set_file_args *uap) { struct label intlabel; struct nameidata nd; struct mount *mp; struct mac mac; char *buffer; int 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); } mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_vnode_label(&intlabel); return (error); } mtx_lock(&Giant); /* VFS */ 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); mtx_unlock(&Giant); /* VFS */ mac_destroy_vnode_label(&intlabel); return (error); } /* * MPSAFE */ int __mac_set_link(struct thread *td, struct __mac_set_link_args *uap) { struct label intlabel; struct nameidata nd; struct mount *mp; struct mac mac; char *buffer; int 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); } mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_vnode_label(&intlabel); return (error); } mtx_lock(&Giant); /* VFS */ 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); mtx_unlock(&Giant); /* VFS */ mac_destroy_vnode_label(&intlabel); return (error); } /* * MPSAFE */ int 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(SCARG(uap, policy), target, sizeof(target), NULL); if (error) return (error); error = ENOSYS; MAC_POLICY_LIST_BUSY(); 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, SCARG(uap, call), SCARG(uap, arg)); goto out; } } out: MAC_POLICY_LIST_UNBUSY(); return (error); } 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); #else /* !MAC */ int __mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) { return (ENOSYS); } int __mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) { return (ENOSYS); } int __mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) { return (ENOSYS); } int __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) { return (ENOSYS); } int __mac_get_file(struct thread *td, struct __mac_get_file_args *uap) { return (ENOSYS); } int __mac_get_link(struct thread *td, struct __mac_get_link_args *uap) { return (ENOSYS); } int __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) { return (ENOSYS); } int __mac_set_file(struct thread *td, struct __mac_set_file_args *uap) { return (ENOSYS); } int __mac_set_link(struct thread *td, struct __mac_set_link_args *uap) { return (ENOSYS); } int mac_syscall(struct thread *td, struct mac_syscall_args *uap) { return (ENOSYS); } #endif Index: head/sys/kern/kern_sysctl.c =================================================================== --- head/sys/kern/kern_sysctl.c (revision 106024) +++ head/sys/kern/kern_sysctl.c (revision 106025) @@ -1,1479 +1,1490 @@ /*- * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Karels at Berkeley Software Design, Inc. * * Quite extensively rewritten by Poul-Henning Kamp of the FreeBSD * project, to make these variables more userfriendly. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. * * @(#)kern_sysctl.c 8.4 (Berkeley) 4/14/94 * $FreeBSD$ */ #include "opt_compat.h" +#include "opt_mac.h" #include #include #include #include +#include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_SYSCTL, "sysctl", "sysctl internal magic"); static MALLOC_DEFINE(M_SYSCTLOID, "sysctloid", "sysctl dynamic oids"); static MALLOC_DEFINE(M_SYSCTLTMP, "sysctltmp", "sysctl temp output buffer"); /* * Locking - this locks the sysctl tree in memory. */ static struct sx sysctllock; #define SYSCTL_LOCK() sx_xlock(&sysctllock) #define SYSCTL_UNLOCK() sx_xunlock(&sysctllock) #define SYSCTL_INIT() sx_init(&sysctllock, "sysctl sysctllock") static int sysctl_root(SYSCTL_HANDLER_ARGS); struct sysctl_oid_list sysctl__children; /* root list */ static struct sysctl_oid * sysctl_find_oidname(const char *name, struct sysctl_oid_list *list) { struct sysctl_oid *oidp; SLIST_FOREACH(oidp, list, oid_link) { if (strcmp(oidp->oid_name, name) == 0) { return (oidp); } } return (NULL); } /* * Initialization of the MIB tree. * * Order by number in each list. */ void sysctl_register_oid(struct sysctl_oid *oidp) { struct sysctl_oid_list *parent = oidp->oid_parent; struct sysctl_oid *p; struct sysctl_oid *q; /* * First check if another oid with the same name already * exists in the parent's list. */ p = sysctl_find_oidname(oidp->oid_name, parent); if (p != NULL) { if ((p->oid_kind & CTLTYPE) == CTLTYPE_NODE) { p->oid_refcnt++; return; } else { printf("can't re-use a leaf (%s)!\n", p->oid_name); return; } } /* * If this oid has a number OID_AUTO, give it a number which * is greater than any current oid. * NOTE: DO NOT change the starting value here, change it in * , and make sure it is at least 256 to * accomodate e.g. net.inet.raw as a static sysctl node. */ if (oidp->oid_number == OID_AUTO) { static int newoid = CTL_AUTO_START; oidp->oid_number = newoid++; if (newoid == 0x7fffffff) panic("out of oids"); } #if 0 else if (oidp->oid_number >= CTL_AUTO_START) { /* do not panic; this happens when unregistering sysctl sets */ printf("static sysctl oid too high: %d", oidp->oid_number); } #endif /* * Insert the oid into the parent's list in order. */ q = NULL; SLIST_FOREACH(p, parent, oid_link) { if (oidp->oid_number < p->oid_number) break; q = p; } if (q) SLIST_INSERT_AFTER(q, oidp, oid_link); else SLIST_INSERT_HEAD(parent, oidp, oid_link); } void sysctl_unregister_oid(struct sysctl_oid *oidp) { SLIST_REMOVE(oidp->oid_parent, oidp, sysctl_oid, oid_link); } /* Initialize a new context to keep track of dynamically added sysctls. */ int sysctl_ctx_init(struct sysctl_ctx_list *c) { if (c == NULL) { return (EINVAL); } TAILQ_INIT(c); return (0); } /* Free the context, and destroy all dynamic oids registered in this context */ int sysctl_ctx_free(struct sysctl_ctx_list *clist) { struct sysctl_ctx_entry *e, *e1; int error; error = 0; /* * First perform a "dry run" to check if it's ok to remove oids. * XXX FIXME * XXX This algorithm is a hack. But I don't know any * XXX better solution for now... */ TAILQ_FOREACH(e, clist, link) { error = sysctl_remove_oid(e->entry, 0, 0); if (error) break; } /* * Restore deregistered entries, either from the end, * or from the place where error occured. * e contains the entry that was not unregistered */ if (error) e1 = TAILQ_PREV(e, sysctl_ctx_list, link); else e1 = TAILQ_LAST(clist, sysctl_ctx_list); while (e1 != NULL) { sysctl_register_oid(e1->entry); e1 = TAILQ_PREV(e1, sysctl_ctx_list, link); } if (error) return(EBUSY); /* Now really delete the entries */ e = TAILQ_FIRST(clist); while (e != NULL) { e1 = TAILQ_NEXT(e, link); error = sysctl_remove_oid(e->entry, 1, 0); if (error) panic("sysctl_remove_oid: corrupt tree, entry: %s", e->entry->oid_name); free(e, M_SYSCTLOID); e = e1; } return (error); } /* Add an entry to the context */ struct sysctl_ctx_entry * sysctl_ctx_entry_add(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp) { struct sysctl_ctx_entry *e; if (clist == NULL || oidp == NULL) return(NULL); e = malloc(sizeof(struct sysctl_ctx_entry), M_SYSCTLOID, M_WAITOK); e->entry = oidp; TAILQ_INSERT_HEAD(clist, e, link); return (e); } /* Find an entry in the context */ struct sysctl_ctx_entry * sysctl_ctx_entry_find(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp) { struct sysctl_ctx_entry *e; if (clist == NULL || oidp == NULL) return(NULL); TAILQ_FOREACH(e, clist, link) { if(e->entry == oidp) return(e); } return (e); } /* * Delete an entry from the context. * NOTE: this function doesn't free oidp! You have to remove it * with sysctl_remove_oid(). */ int sysctl_ctx_entry_del(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp) { struct sysctl_ctx_entry *e; if (clist == NULL || oidp == NULL) return (EINVAL); e = sysctl_ctx_entry_find(clist, oidp); if (e != NULL) { TAILQ_REMOVE(clist, e, link); free(e, M_SYSCTLOID); return (0); } else return (ENOENT); } /* * Remove dynamically created sysctl trees. * oidp - top of the tree to be removed * del - if 0 - just deregister, otherwise free up entries as well * recurse - if != 0 traverse the subtree to be deleted */ int sysctl_remove_oid(struct sysctl_oid *oidp, int del, int recurse) { struct sysctl_oid *p; int error; if (oidp == NULL) return(EINVAL); if ((oidp->oid_kind & CTLFLAG_DYN) == 0) { printf("can't remove non-dynamic nodes!\n"); return (EINVAL); } /* * WARNING: normal method to do this should be through * sysctl_ctx_free(). Use recursing as the last resort * method to purge your sysctl tree of leftovers... * However, if some other code still references these nodes, * it will panic. */ if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) { if (oidp->oid_refcnt == 1) { SLIST_FOREACH(p, SYSCTL_CHILDREN(oidp), oid_link) { if (!recurse) return (ENOTEMPTY); error = sysctl_remove_oid(p, del, recurse); if (error) return (error); } if (del) free(SYSCTL_CHILDREN(oidp), M_SYSCTLOID); } } if (oidp->oid_refcnt > 1 ) { oidp->oid_refcnt--; } else { if (oidp->oid_refcnt == 0) { printf("Warning: bad oid_refcnt=%u (%s)!\n", oidp->oid_refcnt, oidp->oid_name); return (EINVAL); } sysctl_unregister_oid(oidp); if (del) { if (oidp->descr) free((void *)(uintptr_t)(const void *)oidp->descr, M_SYSCTLOID); free((void *)(uintptr_t)(const void *)oidp->oid_name, M_SYSCTLOID); free(oidp, M_SYSCTLOID); } } return (0); } /* * Create new sysctls at run time. * clist may point to a valid context initialized with sysctl_ctx_init(). */ struct sysctl_oid * sysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent, int number, const char *name, int kind, void *arg1, int arg2, int (*handler)(SYSCTL_HANDLER_ARGS), const char *fmt, const char *descr) { struct sysctl_oid *oidp; ssize_t len; char *newname; /* You have to hook up somewhere.. */ if (parent == NULL) return(NULL); /* Check if the node already exists, otherwise create it */ oidp = sysctl_find_oidname(name, parent); if (oidp != NULL) { if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) { oidp->oid_refcnt++; /* Update the context */ if (clist != NULL) sysctl_ctx_entry_add(clist, oidp); return (oidp); } else { printf("can't re-use a leaf (%s)!\n", name); return (NULL); } } oidp = malloc(sizeof(struct sysctl_oid), M_SYSCTLOID, M_WAITOK|M_ZERO); oidp->oid_parent = parent; SLIST_NEXT(oidp, oid_link) = NULL; oidp->oid_number = number; oidp->oid_refcnt = 1; len = strlen(name); newname = malloc(len + 1, M_SYSCTLOID, M_WAITOK); bcopy(name, newname, len + 1); newname[len] = '\0'; oidp->oid_name = newname; oidp->oid_handler = handler; oidp->oid_kind = CTLFLAG_DYN | kind; if ((kind & CTLTYPE) == CTLTYPE_NODE) { /* Allocate space for children */ SYSCTL_CHILDREN(oidp) = malloc(sizeof(struct sysctl_oid_list), M_SYSCTLOID, M_WAITOK); SLIST_INIT(SYSCTL_CHILDREN(oidp)); } else { oidp->oid_arg1 = arg1; oidp->oid_arg2 = arg2; } oidp->oid_fmt = fmt; if (descr) { int len = strlen(descr) + 1; oidp->descr = malloc(len, M_SYSCTLOID, M_WAITOK); if (oidp->descr) strcpy((char *)(uintptr_t)(const void *)oidp->descr, descr); } /* Update the context, if used */ if (clist != NULL) sysctl_ctx_entry_add(clist, oidp); /* Register this oid */ sysctl_register_oid(oidp); return (oidp); } /* * Register the kernel's oids on startup. */ SET_DECLARE(sysctl_set, struct sysctl_oid); static void sysctl_register_all(void *arg) { struct sysctl_oid **oidp; SYSCTL_INIT(); SET_FOREACH(oidp, sysctl_set) sysctl_register_oid(*oidp); } SYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_ANY, sysctl_register_all, 0); /* * "Staff-functions" * * These functions implement a presently undocumented interface * used by the sysctl program to walk the tree, and get the type * so it can print the value. * This interface is under work and consideration, and should probably * be killed with a big axe by the first person who can find the time. * (be aware though, that the proper interface isn't as obvious as it * may seem, there are various conflicting requirements. * * {0,0} printf the entire MIB-tree. * {0,1,...} return the name of the "..." OID. * {0,2,...} return the next OID. * {0,3} return the OID of the name in "new" * {0,4,...} return the kind & format info for the "..." OID. * {0,5,...} return the description the "..." OID. */ static void sysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i) { int k; struct sysctl_oid *oidp; SLIST_FOREACH(oidp, l, oid_link) { for (k=0; koid_number, oidp->oid_name); printf("%c%c", oidp->oid_kind & CTLFLAG_RD ? 'R':' ', oidp->oid_kind & CTLFLAG_WR ? 'W':' '); if (oidp->oid_handler) printf(" *Handler"); switch (oidp->oid_kind & CTLTYPE) { case CTLTYPE_NODE: printf(" Node\n"); if (!oidp->oid_handler) { sysctl_sysctl_debug_dump_node( oidp->oid_arg1, i+2); } break; case CTLTYPE_INT: printf(" Int\n"); break; case CTLTYPE_STRING: printf(" String\n"); break; case CTLTYPE_QUAD: printf(" Quad\n"); break; case CTLTYPE_OPAQUE: printf(" Opaque/struct\n"); break; default: printf("\n"); } } } static int sysctl_sysctl_debug(SYSCTL_HANDLER_ARGS) { int error; error = suser(req->td); if (error) return error; sysctl_sysctl_debug_dump_node(&sysctl__children, 0); return ENOENT; } SYSCTL_PROC(_sysctl, 0, debug, CTLTYPE_STRING|CTLFLAG_RD, 0, 0, sysctl_sysctl_debug, "-", ""); static int sysctl_sysctl_name(SYSCTL_HANDLER_ARGS) { int *name = (int *) arg1; u_int namelen = arg2; int error = 0; struct sysctl_oid *oid; struct sysctl_oid_list *lsp = &sysctl__children, *lsp2; char buf[10]; while (namelen) { if (!lsp) { snprintf(buf,sizeof(buf),"%d",*name); if (req->oldidx) error = SYSCTL_OUT(req, ".", 1); if (!error) error = SYSCTL_OUT(req, buf, strlen(buf)); if (error) return (error); namelen--; name++; continue; } lsp2 = 0; SLIST_FOREACH(oid, lsp, oid_link) { if (oid->oid_number != *name) continue; if (req->oldidx) error = SYSCTL_OUT(req, ".", 1); if (!error) error = SYSCTL_OUT(req, oid->oid_name, strlen(oid->oid_name)); if (error) return (error); namelen--; name++; if ((oid->oid_kind & CTLTYPE) != CTLTYPE_NODE) break; if (oid->oid_handler) break; lsp2 = (struct sysctl_oid_list *)oid->oid_arg1; break; } lsp = lsp2; } return (SYSCTL_OUT(req, "", 1)); } SYSCTL_NODE(_sysctl, 1, name, CTLFLAG_RD, sysctl_sysctl_name, ""); static int sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, int *name, u_int namelen, int *next, int *len, int level, struct sysctl_oid **oidpp) { struct sysctl_oid *oidp; *len = level; SLIST_FOREACH(oidp, lsp, oid_link) { *next = oidp->oid_number; *oidpp = oidp; if (oidp->oid_kind & CTLFLAG_SKIP) continue; if (!namelen) { if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) return 0; if (oidp->oid_handler) /* We really should call the handler here...*/ return 0; lsp = (struct sysctl_oid_list *)oidp->oid_arg1; if (!sysctl_sysctl_next_ls(lsp, 0, 0, next+1, len, level+1, oidpp)) return 0; goto next; } if (oidp->oid_number < *name) continue; if (oidp->oid_number > *name) { if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) return 0; if (oidp->oid_handler) return 0; lsp = (struct sysctl_oid_list *)oidp->oid_arg1; if (!sysctl_sysctl_next_ls(lsp, name+1, namelen-1, next+1, len, level+1, oidpp)) return (0); goto next; } if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) continue; if (oidp->oid_handler) continue; lsp = (struct sysctl_oid_list *)oidp->oid_arg1; if (!sysctl_sysctl_next_ls(lsp, name+1, namelen-1, next+1, len, level+1, oidpp)) return (0); next: namelen = 1; *len = level; } return 1; } static int sysctl_sysctl_next(SYSCTL_HANDLER_ARGS) { int *name = (int *) arg1; u_int namelen = arg2; int i, j, error; struct sysctl_oid *oid; struct sysctl_oid_list *lsp = &sysctl__children; int newoid[CTL_MAXNAME]; i = sysctl_sysctl_next_ls(lsp, name, namelen, newoid, &j, 1, &oid); if (i) return ENOENT; error = SYSCTL_OUT(req, newoid, j * sizeof (int)); return (error); } SYSCTL_NODE(_sysctl, 2, next, CTLFLAG_RD, sysctl_sysctl_next, ""); static int name2oid (char *name, int *oid, int *len, struct sysctl_oid **oidpp) { int i; struct sysctl_oid *oidp; struct sysctl_oid_list *lsp = &sysctl__children; char *p; if (!*name) return ENOENT; p = name + strlen(name) - 1 ; if (*p == '.') *p = '\0'; *len = 0; for (p = name; *p && *p != '.'; p++) ; i = *p; if (i == '.') *p = '\0'; oidp = SLIST_FIRST(lsp); while (oidp && *len < CTL_MAXNAME) { if (strcmp(name, oidp->oid_name)) { oidp = SLIST_NEXT(oidp, oid_link); continue; } *oid++ = oidp->oid_number; (*len)++; if (!i) { if (oidpp) *oidpp = oidp; return (0); } if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) break; if (oidp->oid_handler) break; lsp = (struct sysctl_oid_list *)oidp->oid_arg1; oidp = SLIST_FIRST(lsp); name = p+1; for (p = name; *p && *p != '.'; p++) ; i = *p; if (i == '.') *p = '\0'; } return ENOENT; } static int sysctl_sysctl_name2oid(SYSCTL_HANDLER_ARGS) { char *p; int error, oid[CTL_MAXNAME], len; struct sysctl_oid *op = 0; if (!req->newlen) return ENOENT; if (req->newlen >= MAXPATHLEN) /* XXX arbitrary, undocumented */ return (ENAMETOOLONG); p = malloc(req->newlen+1, M_SYSCTL, M_WAITOK); error = SYSCTL_IN(req, p, req->newlen); if (error) { free(p, M_SYSCTL); return (error); } p [req->newlen] = '\0'; error = name2oid(p, oid, &len, &op); free(p, M_SYSCTL); if (error) return (error); error = SYSCTL_OUT(req, oid, len * sizeof *oid); return (error); } SYSCTL_PROC(_sysctl, 3, name2oid, CTLFLAG_RW|CTLFLAG_ANYBODY, 0, 0, sysctl_sysctl_name2oid, "I", ""); static int sysctl_sysctl_oidfmt(SYSCTL_HANDLER_ARGS) { struct sysctl_oid *oid; int error; error = sysctl_find_oid(arg1, arg2, &oid, NULL, req); if (error) return (error); if (!oid->oid_fmt) return (ENOENT); error = SYSCTL_OUT(req, &oid->oid_kind, sizeof(oid->oid_kind)); if (error) return (error); error = SYSCTL_OUT(req, oid->oid_fmt, strlen(oid->oid_fmt) + 1); return (error); } SYSCTL_NODE(_sysctl, 4, oidfmt, CTLFLAG_RD, sysctl_sysctl_oidfmt, ""); static int sysctl_sysctl_oiddescr(SYSCTL_HANDLER_ARGS) { struct sysctl_oid *oid; int error; error = sysctl_find_oid(arg1, arg2, &oid, NULL, req); if (error) return (error); if (!oid->descr) return (ENOENT); error = SYSCTL_OUT(req, oid->descr, strlen(oid->descr) + 1); return (error); } SYSCTL_NODE(_sysctl, 5, oiddescr, CTLFLAG_RD, sysctl_sysctl_oiddescr, ""); /* * Default "handler" functions. */ /* * Handle an int, signed or unsigned. * Two cases: * a variable: point arg1 at it. * a constant: pass it in arg2. */ int sysctl_handle_int(SYSCTL_HANDLER_ARGS) { int tmpout, error = 0; /* * Attempt to get a coherent snapshot by making a copy of the data. */ if (arg1) tmpout = *(int *)arg1; else tmpout = arg2; error = SYSCTL_OUT(req, &tmpout, sizeof(int)); if (error || !req->newptr) return (error); if (!arg1) error = EPERM; else error = SYSCTL_IN(req, arg1, sizeof(int)); return (error); } /* * Handle a long, signed or unsigned. arg1 points to it. */ int sysctl_handle_long(SYSCTL_HANDLER_ARGS) { int error = 0; long tmpout; /* * Attempt to get a coherent snapshot by making a copy of the data. */ if (!arg1) return (EINVAL); tmpout = *(long *)arg1; error = SYSCTL_OUT(req, &tmpout, sizeof(long)); if (error || !req->newptr) return (error); error = SYSCTL_IN(req, arg1, sizeof(long)); return (error); } /* * Handle our generic '\0' terminated 'C' string. * Two cases: * a variable string: point arg1 at it, arg2 is max length. * a constant string: point arg1 at it, arg2 is zero. */ int sysctl_handle_string(SYSCTL_HANDLER_ARGS) { int error=0; char *tmparg; size_t outlen; /* * Attempt to get a coherent snapshot by copying to a * temporary kernel buffer. */ retry: outlen = strlen((char *)arg1)+1; tmparg = malloc(outlen, M_SYSCTLTMP, M_WAITOK); if (strlcpy(tmparg, (char *)arg1, outlen) >= outlen) { free(tmparg, M_SYSCTLTMP); goto retry; } error = SYSCTL_OUT(req, tmparg, outlen); free(tmparg, M_SYSCTLTMP); if (error || !req->newptr) return (error); if ((req->newlen - req->newidx) >= arg2) { error = EINVAL; } else { arg2 = (req->newlen - req->newidx); error = SYSCTL_IN(req, arg1, arg2); ((char *)arg1)[arg2] = '\0'; } return (error); } /* * Handle any kind of opaque data. * arg1 points to it, arg2 is the size. */ int sysctl_handle_opaque(SYSCTL_HANDLER_ARGS) { int error; void *tmparg; /* * Attempt to get a coherent snapshot, either by wiring the * user space buffer or copying to a temporary kernel buffer * depending on the size of the data. */ if (arg2 > PAGE_SIZE) { sysctl_wire_old_buffer(req, arg2); error = SYSCTL_OUT(req, arg1, arg2); } else { tmparg = malloc(arg2, M_SYSCTLTMP, M_WAITOK); bcopy(arg1, tmparg, arg2); error = SYSCTL_OUT(req, tmparg, arg2); free(tmparg, M_SYSCTLTMP); } if (error || !req->newptr) return (error); error = SYSCTL_IN(req, arg1, arg2); return (error); } /* * Transfer functions to/from kernel space. * XXX: rather untested at this point */ static int sysctl_old_kernel(struct sysctl_req *req, const void *p, size_t l) { size_t i = 0; if (req->oldptr) { i = l; if (req->oldlen <= req->oldidx) i = 0; else if (i > req->oldlen - req->oldidx) i = req->oldlen - req->oldidx; if (i > 0) bcopy(p, (char *)req->oldptr + req->oldidx, i); } req->oldidx += l; if (req->oldptr && i != l) return (ENOMEM); return (0); } static int sysctl_new_kernel(struct sysctl_req *req, void *p, size_t l) { if (!req->newptr) return 0; if (req->newlen - req->newidx < l) return (EINVAL); bcopy((char *)req->newptr + req->newidx, p, l); req->newidx += l; return (0); } int kernel_sysctl(struct thread *td, int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen, size_t *retval) { int error = 0; struct sysctl_req req; bzero(&req, sizeof req); req.td = td; if (oldlenp) { req.oldlen = *oldlenp; } if (old) { req.oldptr= old; } if (new != NULL) { req.newlen = newlen; req.newptr = new; } req.oldfunc = sysctl_old_kernel; req.newfunc = sysctl_new_kernel; req.lock = 1; SYSCTL_LOCK(); error = sysctl_root(0, name, namelen, &req); if (req.lock == 2) vsunlock(req.oldptr, req.oldlen); SYSCTL_UNLOCK(); if (error && error != ENOMEM) return (error); if (retval) { if (req.oldptr && req.oldidx > req.oldlen) *retval = req.oldlen; else *retval = req.oldidx; } return (error); } int kernel_sysctlbyname(struct thread *td, char *name, void *old, size_t *oldlenp, void *new, size_t newlen, size_t *retval) { int oid[CTL_MAXNAME]; size_t oidlen, plen; int error; oid[0] = 0; /* sysctl internal magic */ oid[1] = 3; /* name2oid */ oidlen = sizeof(oid); error = kernel_sysctl(td, oid, 2, oid, &oidlen, (void *)name, strlen(name), &plen); if (error) return (error); error = kernel_sysctl(td, oid, plen / sizeof(int), old, oldlenp, new, newlen, retval); return (error); } /* * Transfer function to/from user space. */ static int sysctl_old_user(struct sysctl_req *req, const void *p, size_t l) { int error = 0; size_t i = 0; if (req->lock == 1 && req->oldptr) WITNESS_SLEEP(1, NULL); if (req->oldptr) { i = l; if (req->oldlen <= req->oldidx) i = 0; else if (i > req->oldlen - req->oldidx) i = req->oldlen - req->oldidx; if (i > 0) error = copyout(p, (char *)req->oldptr + req->oldidx, i); } req->oldidx += l; if (error) return (error); if (req->oldptr && i < l) return (ENOMEM); return (0); } static int sysctl_new_user(struct sysctl_req *req, void *p, size_t l) { int error; if (!req->newptr) return 0; if (req->newlen - req->newidx < l) return (EINVAL); error = copyin((char *)req->newptr + req->newidx, p, l); req->newidx += l; return (error); } /* * Wire the user space destination buffer. If set to a value greater than * zero, the len parameter limits the maximum amount of wired memory. * * XXX - The len parameter is currently ignored due to the lack of * a place to save it in the sysctl_req structure so that the matching * amount of memory can be unwired in the sysctl exit code. */ void sysctl_wire_old_buffer(struct sysctl_req *req, size_t len) { if (req->lock == 1 && req->oldptr && req->oldfunc == sysctl_old_user) { vslock(req->oldptr, req->oldlen); req->lock = 2; } } int sysctl_find_oid(int *name, u_int namelen, struct sysctl_oid **noid, int *nindx, struct sysctl_req *req) { struct sysctl_oid *oid; int indx; oid = SLIST_FIRST(&sysctl__children); indx = 0; while (oid && indx < CTL_MAXNAME) { if (oid->oid_number == name[indx]) { indx++; if (oid->oid_kind & CTLFLAG_NOLOCK) req->lock = 0; if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) { if (oid->oid_handler != NULL || indx == namelen) { *noid = oid; if (nindx != NULL) *nindx = indx; return (0); } oid = SLIST_FIRST( (struct sysctl_oid_list *)oid->oid_arg1); } else if (indx == namelen) { *noid = oid; if (nindx != NULL) *nindx = indx; return (0); } else { return (ENOTDIR); } } else { oid = SLIST_NEXT(oid, oid_link); } } return (ENOENT); } /* * Traverse our tree, and find the right node, execute whatever it points * to, and return the resulting error code. */ static int sysctl_root(SYSCTL_HANDLER_ARGS) { struct sysctl_oid *oid; int error, indx; error = sysctl_find_oid(arg1, arg2, &oid, &indx, req); if (error) return (error); if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) { /* * You can't call a sysctl when it's a node, but has * no handler. Inform the user that it's a node. * The indx may or may not be the same as namelen. */ if (oid->oid_handler == NULL) return (EISDIR); } /* Is this sysctl writable? */ if (req->newptr && !(oid->oid_kind & CTLFLAG_WR)) return (EPERM); KASSERT(req->td != NULL, ("sysctl_root(): req->td == NULL")); /* Is this sysctl sensitive to securelevels? */ if (req->newptr && (oid->oid_kind & CTLFLAG_SECURE)) { error = securelevel_gt(req->td->td_ucred, 0); if (error) return (error); } /* Is this sysctl writable by only privileged users? */ if (req->newptr && !(oid->oid_kind & CTLFLAG_ANYBODY)) { int flags; if (oid->oid_kind & CTLFLAG_PRISON) flags = PRISON_ROOT; else flags = 0; error = suser_cred(req->td->td_ucred, flags); if (error) return (error); } if (!oid->oid_handler) return EINVAL; if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) error = oid->oid_handler(oid, (int *)arg1 + indx, arg2 - indx, req); else error = oid->oid_handler(oid, oid->oid_arg1, oid->oid_arg2, req); return (error); } #ifndef _SYS_SYSPROTO_H_ struct sysctl_args { int *name; u_int namelen; void *old; size_t *oldlenp; void *new; size_t newlen; }; #endif /* * MPSAFE */ int __sysctl(struct thread *td, struct sysctl_args *uap) { int error, name[CTL_MAXNAME]; size_t j; if (uap->namelen > CTL_MAXNAME || uap->namelen < 2) return (EINVAL); error = copyin(uap->name, &name, uap->namelen * sizeof(int)); if (error) return (error); mtx_lock(&Giant); error = userland_sysctl(td, name, uap->namelen, uap->old, uap->oldlenp, 0, uap->new, uap->newlen, &j); if (error && error != ENOMEM) goto done2; if (uap->oldlenp) { int i = copyout(&j, uap->oldlenp, sizeof(j)); if (i) error = i; } done2: mtx_unlock(&Giant); return (error); } /* * This is used from various compatibility syscalls too. That's why name * must be in kernel space. */ int userland_sysctl(struct thread *td, int *name, u_int namelen, void *old, size_t *oldlenp, int inkernel, void *new, size_t newlen, size_t *retval) { int error = 0; struct sysctl_req req, req2; bzero(&req, sizeof req); req.td = td; if (oldlenp) { if (inkernel) { req.oldlen = *oldlenp; } else { error = copyin(oldlenp, &req.oldlen, sizeof(*oldlenp)); if (error) return (error); } } if (old) { if (!useracc(old, req.oldlen, VM_PROT_WRITE)) return (EFAULT); req.oldptr= old; } if (new != NULL) { if (!useracc(new, req.newlen, VM_PROT_READ)) return (EFAULT); req.newlen = newlen; req.newptr = new; } req.oldfunc = sysctl_old_user; req.newfunc = sysctl_new_user; req.lock = 1; SYSCTL_LOCK(); + +#ifdef MAC + error = mac_check_system_sysctl(td->td_ucred, name, namelen, old, + oldlenp, inkernel, new, newlen); + if (error) { + SYSCTL_UNLOCK(); + return (error); + } +#endif do { req2 = req; error = sysctl_root(0, name, namelen, &req2); } while (error == EAGAIN); req = req2; if (req.lock == 2) vsunlock(req.oldptr, req.oldlen); SYSCTL_UNLOCK(); if (error && error != ENOMEM) return (error); if (retval) { if (req.oldptr && req.oldidx > req.oldlen) *retval = req.oldlen; else *retval = req.oldidx; } return (error); } #ifdef COMPAT_43 #include #include #define KINFO_PROC (0<<8) #define KINFO_RT (1<<8) #define KINFO_VNODE (2<<8) #define KINFO_FILE (3<<8) #define KINFO_METER (4<<8) #define KINFO_LOADAVG (5<<8) #define KINFO_CLOCKRATE (6<<8) /* Non-standard BSDI extension - only present on their 4.3 net-2 releases */ #define KINFO_BSDI_SYSINFO (101<<8) /* * XXX this is bloat, but I hope it's better here than on the potentially * limited kernel stack... -Peter */ static struct { int bsdi_machine; /* "i386" on BSD/386 */ /* ^^^ this is an offset to the string, relative to the struct start */ char *pad0; long pad1; long pad2; long pad3; u_long pad4; u_long pad5; u_long pad6; int bsdi_ostype; /* "BSD/386" on BSD/386 */ int bsdi_osrelease; /* "1.1" on BSD/386 */ long pad7; long pad8; char *pad9; long pad10; long pad11; int pad12; long pad13; quad_t pad14; long pad15; struct timeval pad16; /* we dont set this, because BSDI's uname used gethostname() instead */ int bsdi_hostname; /* hostname on BSD/386 */ /* the actual string data is appended here */ } bsdi_si; /* * this data is appended to the end of the bsdi_si structure during copyout. * The "char *" offsets are relative to the base of the bsdi_si struct. * This contains "FreeBSD\02.0-BUILT-nnnnnn\0i386\0", and these strings * should not exceed the length of the buffer here... (or else!! :-) */ static char bsdi_strings[80]; /* It had better be less than this! */ #ifndef _SYS_SYSPROTO_H_ struct getkerninfo_args { int op; char *where; size_t *size; int arg; }; #endif /* * MPSAFE */ int ogetkerninfo(struct thread *td, struct getkerninfo_args *uap) { int error, name[6]; size_t size; u_int needed = 0; mtx_lock(&Giant); switch (uap->op & 0xff00) { case KINFO_RT: name[0] = CTL_NET; name[1] = PF_ROUTE; name[2] = 0; name[3] = (uap->op & 0xff0000) >> 16; name[4] = uap->op & 0xff; name[5] = uap->arg; error = userland_sysctl(td, name, 6, uap->where, uap->size, 0, 0, 0, &size); break; case KINFO_VNODE: name[0] = CTL_KERN; name[1] = KERN_VNODE; error = userland_sysctl(td, name, 2, uap->where, uap->size, 0, 0, 0, &size); break; case KINFO_PROC: name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = uap->op & 0xff; name[3] = uap->arg; error = userland_sysctl(td, name, 4, uap->where, uap->size, 0, 0, 0, &size); break; case KINFO_FILE: name[0] = CTL_KERN; name[1] = KERN_FILE; error = userland_sysctl(td, name, 2, uap->where, uap->size, 0, 0, 0, &size); break; case KINFO_METER: name[0] = CTL_VM; name[1] = VM_METER; error = userland_sysctl(td, name, 2, uap->where, uap->size, 0, 0, 0, &size); break; case KINFO_LOADAVG: name[0] = CTL_VM; name[1] = VM_LOADAVG; error = userland_sysctl(td, name, 2, uap->where, uap->size, 0, 0, 0, &size); break; case KINFO_CLOCKRATE: name[0] = CTL_KERN; name[1] = KERN_CLOCKRATE; error = userland_sysctl(td, name, 2, uap->where, uap->size, 0, 0, 0, &size); break; case KINFO_BSDI_SYSINFO: { /* * this is pretty crude, but it's just enough for uname() * from BSDI's 1.x libc to work. * * *size gives the size of the buffer before the call, and * the amount of data copied after a successful call. * If successful, the return value is the amount of data * available, which can be larger than *size. * * BSDI's 2.x product apparently fails with ENOMEM if *size * is too small. */ u_int left; char *s; bzero((char *)&bsdi_si, sizeof(bsdi_si)); bzero(bsdi_strings, sizeof(bsdi_strings)); s = bsdi_strings; bsdi_si.bsdi_ostype = (s - bsdi_strings) + sizeof(bsdi_si); strcpy(s, ostype); s += strlen(s) + 1; bsdi_si.bsdi_osrelease = (s - bsdi_strings) + sizeof(bsdi_si); strcpy(s, osrelease); s += strlen(s) + 1; bsdi_si.bsdi_machine = (s - bsdi_strings) + sizeof(bsdi_si); strcpy(s, machine); s += strlen(s) + 1; needed = sizeof(bsdi_si) + (s - bsdi_strings); if ((uap->where == NULL) || (uap->size == NULL)) { /* process is asking how much buffer to supply.. */ size = needed; error = 0; break; } if ((error = copyin(uap->size, &size, sizeof(size))) != 0) break; /* if too much buffer supplied, trim it down */ if (size > needed) size = needed; /* how much of the buffer is remaining */ left = size; if ((error = copyout((char *)&bsdi_si, uap->where, left)) != 0) break; /* is there any point in continuing? */ if (left > sizeof(bsdi_si)) { left -= sizeof(bsdi_si); error = copyout(&bsdi_strings, uap->where + sizeof(bsdi_si), left); } break; } default: error = EOPNOTSUPP; break; } if (error == 0) { td->td_retval[0] = needed ? needed : size; if (uap->size) { error = copyout(&size, uap->size, sizeof(size)); } } mtx_unlock(&Giant); return (error); } #endif /* COMPAT_43 */ Index: head/sys/security/mac/mac_framework.c =================================================================== --- head/sys/security/mac/mac_framework.c (revision 106024) +++ head/sys/security/mac/mac_framework.c (revision 106025) @@ -1,4010 +1,4038 @@ /*- * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson * Copyright (c) 2001 Ilmar S. Habibulin * Copyright (c) 2001, 2002 Networks Associates Technology, 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 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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. * * Framework for extensible kernel access control. Kernel and userland * interface to the framework, policy registration and composition. */ #include "opt_mac.h" #include "opt_devfs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MAC /* * Declare that the kernel provides MAC support, version 1. 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, 1); SYSCTL_DECL(_security); SYSCTL_NODE(_security, OID_AUTO, mac, CTLFLAG_RW, 0, "TrustedBSD MAC policy controls"); #if MAC_MAX_POLICIES > 32 #error "MAC_MAX_POLICIES too large" #endif static unsigned int mac_max_policies = MAC_MAX_POLICIES; static unsigned int mac_policy_offsets_free = (1 << MAC_MAX_POLICIES) - 1; SYSCTL_UINT(_security_mac, OID_AUTO, max_policies, CTLFLAG_RD, &mac_max_policies, 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; /* * Warn about EA transactions only the first time they happen. * Weak coherency, no locking. */ static int ea_warn_once = 0; static int mac_enforce_fs = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_fs, CTLFLAG_RW, &mac_enforce_fs, 0, "Enforce MAC policy on file system objects"); TUNABLE_INT("security.mac.enforce_fs", &mac_enforce_fs); static int mac_enforce_network = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_network, CTLFLAG_RW, &mac_enforce_network, 0, "Enforce MAC policy on network packets"); TUNABLE_INT("security.mac.enforce_network", &mac_enforce_network); static int mac_enforce_pipe = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_pipe, CTLFLAG_RW, &mac_enforce_pipe, 0, "Enforce MAC policy on pipe operations"); TUNABLE_INT("security.mac.enforce_pipe", &mac_enforce_pipe); static int mac_enforce_process = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_process, CTLFLAG_RW, &mac_enforce_process, 0, "Enforce MAC policy on inter-process operations"); TUNABLE_INT("security.mac.enforce_process", &mac_enforce_process); static int mac_enforce_reboot = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_reboot, CTLFLAG_RW, &mac_enforce_reboot, 0, "Enforce MAC policy for reboot operations"); TUNABLE_INT("security.mac.enforce_reboot", &mac_enforce_reboot); static int mac_enforce_socket = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_socket, CTLFLAG_RW, &mac_enforce_socket, 0, "Enforce MAC policy on socket operations"); TUNABLE_INT("security.mac.enforce_socket", &mac_enforce_socket); +static int mac_enforce_sysctl = 1; +SYSCTL_INT(_security_mac, OID_AUTO, enforce_sysctl, CTLFLAG_RW, + &mac_enforce_sysctl, 0, "Enforce MAC policy on sysctl operations"); +TUNABLE_INT("security.mac.enforce_sysctl", &mac_enforce_sysctl); + static int mac_enforce_vm = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_vm, CTLFLAG_RW, &mac_enforce_vm, 0, "Enforce MAC policy on vm operations"); TUNABLE_INT("security.mac.enforce_vm", &mac_enforce_vm); static int mac_cache_fslabel_in_vnode = 1; SYSCTL_INT(_security_mac, OID_AUTO, cache_fslabel_in_vnode, CTLFLAG_RW, &mac_cache_fslabel_in_vnode, 0, "Cache mount fslabel in vnode"); TUNABLE_INT("security.mac.cache_fslabel_in_vnode", &mac_cache_fslabel_in_vnode); 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"); #ifdef MAC_DEBUG SYSCTL_NODE(_security_mac, OID_AUTO, debug, CTLFLAG_RW, 0, "TrustedBSD MAC debug info"); static int mac_debug_label_fallback = 0; SYSCTL_INT(_security_mac_debug, OID_AUTO, label_fallback, CTLFLAG_RW, &mac_debug_label_fallback, 0, "Filesystems should fall back to fs label" "when label is corrupted."); TUNABLE_INT("security.mac.debug_label_fallback", &mac_debug_label_fallback); SYSCTL_NODE(_security_mac_debug, OID_AUTO, counters, CTLFLAG_RW, 0, "TrustedBSD MAC object counters"); static unsigned int nmacmbufs, nmaccreds, nmacifnets, nmacbpfdescs, nmacsockets, nmacmounts, nmactemp, nmacvnodes, nmacdevfsdirents, nmacipqs, nmacpipes; SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, mbufs, CTLFLAG_RD, &nmacmbufs, 0, "number of mbufs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, creds, CTLFLAG_RD, &nmaccreds, 0, "number of ucreds in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ifnets, CTLFLAG_RD, &nmacifnets, 0, "number of ifnets in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ipqs, CTLFLAG_RD, &nmacipqs, 0, "number of ipqs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, bpfdescs, CTLFLAG_RD, &nmacbpfdescs, 0, "number of bpfdescs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, sockets, CTLFLAG_RD, &nmacsockets, 0, "number of sockets in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, pipes, CTLFLAG_RD, &nmacpipes, 0, "number of pipes in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, mounts, CTLFLAG_RD, &nmacmounts, 0, "number of mounts in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, temp, CTLFLAG_RD, &nmactemp, 0, "number of temporary labels in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, vnodes, CTLFLAG_RD, &nmacvnodes, 0, "number of vnodes in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, devfsdirents, CTLFLAG_RD, &nmacdevfsdirents, 0, "number of devfs dirents inuse"); #endif static int error_select(int error1, int error2); static int mac_policy_register(struct mac_policy_conf *mpc); static int mac_policy_unregister(struct mac_policy_conf *mpc); static void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot); static void mac_cred_mmapped_drop_perms_recurse(struct thread *td, struct ucred *cred, struct vm_map *map); static void mac_destroy_socket_label(struct label *label); static int mac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *intlabel); MALLOC_DEFINE(M_MACOPVEC, "macopvec", "MAC policy operation vector"); MALLOC_DEFINE(M_MACPIPELABEL, "macpipelabel", "MAC labels for pipes"); MALLOC_DEFINE(M_MACTEMP, "mactemp", "MAC temporary label storage"); /* * mac_policy_list_lock protects the consistency of 'mac_policy_list', * the linked list of attached policy modules. Read-only consumers of * the list must acquire a shared lock for the duration of their use; * writers must acquire an exclusive lock. Note that for compound * operations, locks should be held for the entire compound operation, * and that this is not yet done for relabel requests. */ static struct mtx mac_policy_list_lock; static LIST_HEAD(, mac_policy_conf) mac_policy_list; static int mac_policy_list_busy; #define MAC_POLICY_LIST_LOCKINIT() mtx_init(&mac_policy_list_lock, \ "mac_policy_list_lock", NULL, MTX_DEF); #define MAC_POLICY_LIST_LOCK() mtx_lock(&mac_policy_list_lock); #define MAC_POLICY_LIST_UNLOCK() mtx_unlock(&mac_policy_list_lock); #define MAC_POLICY_LIST_BUSY() do { \ MAC_POLICY_LIST_LOCK(); \ mac_policy_list_busy++; \ MAC_POLICY_LIST_UNLOCK(); \ } while (0) #define MAC_POLICY_LIST_UNBUSY() do { \ MAC_POLICY_LIST_LOCK(); \ mac_policy_list_busy--; \ if (mac_policy_list_busy < 0) \ panic("Extra mac_policy_list_busy--"); \ MAC_POLICY_LIST_UNLOCK(); \ } while (0) /* * MAC_CHECK performs the designated check by walking the policy * module list and checking with each as to how it feels about the * request. Note that it returns its value via 'error' in the scope * of the caller. */ #define MAC_CHECK(check, args...) do { \ struct mac_policy_conf *mpc; \ \ error = 0; \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## check != NULL) \ error = error_select( \ mpc->mpc_ops->mpo_ ## check (args), \ error); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) /* * MAC_BOOLEAN performs the designated boolean composition by walking * the module list, invoking each instance of the operation, and * combining the results using the passed C operator. Note that it * returns its value via 'result' in the scope of the caller, which * should be initialized by the caller in a meaningful way to get * a meaningful result. */ #define MAC_BOOLEAN(operation, composition, args...) do { \ struct mac_policy_conf *mpc; \ \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ result = result composition \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) #define MAC_EXTERNALIZE(type, label, elementlist, outbuf, \ outbuflen) do { \ char *curptr, *curptr_start, *element_name, *element_temp; \ size_t left, left_start, len; \ int claimed, first, first_start, ignorenotfound; \ \ error = 0; \ element_temp = elementlist; \ curptr = outbuf; \ curptr[0] = '\0'; \ left = outbuflen; \ first = 1; \ while ((element_name = strsep(&element_temp, ",")) != NULL) { \ curptr_start = curptr; \ left_start = left; \ first_start = first; \ if (element_name[0] == '?') { \ element_name++; \ ignorenotfound = 1; \ } else \ ignorenotfound = 0; \ claimed = 0; \ if (first) { \ len = snprintf(curptr, left, "%s/", \ element_name); \ first = 0; \ } else \ len = snprintf(curptr, left, ",%s/", \ element_name); \ if (len >= left) { \ error = EINVAL; /* XXXMAC: E2BIG */ \ break; \ } \ curptr += len; \ left -= len; \ \ MAC_CHECK(externalize_ ## type, label, element_name, \ curptr, left, &len, &claimed); \ if (error) \ break; \ if (claimed == 1) { \ if (len >= outbuflen) { \ error = EINVAL; /* XXXMAC: E2BIG */ \ break; \ } \ curptr += len; \ left -= len; \ } else if (claimed == 0 && ignorenotfound) { \ /* \ * Revert addition of the label element \ * name. \ */ \ curptr = curptr_start; \ *curptr = '\0'; \ left = left_start; \ first = first_start; \ } else { \ error = EINVAL; /* XXXMAC: ENOLABEL */ \ break; \ } \ } \ } while (0) #define MAC_INTERNALIZE(type, label, instring) do { \ char *element, *element_name, *element_data; \ int claimed; \ \ error = 0; \ element = instring; \ while ((element_name = strsep(&element, ",")) != NULL) { \ element_data = element_name; \ element_name = strsep(&element_data, "/"); \ if (element_data == NULL) { \ error = EINVAL; \ break; \ } \ claimed = 0; \ MAC_CHECK(internalize_ ## type, label, element_name, \ element_data, &claimed); \ if (error) \ break; \ if (claimed != 1) { \ /* XXXMAC: Another error here? */ \ error = EINVAL; \ break; \ } \ } \ } while (0) /* * MAC_PERFORM performs the designated operation by walking the policy * module list and invoking that operation for each policy. */ #define MAC_PERFORM(operation, args...) do { \ struct mac_policy_conf *mpc; \ \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) /* * Initialize the MAC subsystem, including appropriate SMP locks. */ static void mac_init(void) { LIST_INIT(&mac_policy_list); MAC_POLICY_LIST_LOCKINIT(); } /* * 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; } /* * 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; 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: break; } return (error); } static int mac_policy_register(struct mac_policy_conf *mpc) { struct mac_policy_conf *tmpc; struct mac_policy_op_entry *mpe; int slot; MALLOC(mpc->mpc_ops, struct mac_policy_ops *, sizeof(*mpc->mpc_ops), M_MACOPVEC, M_WAITOK | M_ZERO); for (mpe = mpc->mpc_entries; mpe->mpe_constant != MAC_OP_LAST; mpe++) { switch (mpe->mpe_constant) { case MAC_OP_LAST: /* * Doesn't actually happen, but this allows checking * that all enumerated values are handled. */ break; case MAC_DESTROY: mpc->mpc_ops->mpo_destroy = mpe->mpe_function; break; case MAC_INIT: mpc->mpc_ops->mpo_init = mpe->mpe_function; break; case MAC_SYSCALL: mpc->mpc_ops->mpo_syscall = mpe->mpe_function; break; case MAC_INIT_BPFDESC_LABEL: mpc->mpc_ops->mpo_init_bpfdesc_label = mpe->mpe_function; break; case MAC_INIT_CRED_LABEL: mpc->mpc_ops->mpo_init_cred_label = mpe->mpe_function; break; case MAC_INIT_DEVFSDIRENT_LABEL: mpc->mpc_ops->mpo_init_devfsdirent_label = mpe->mpe_function; break; case MAC_INIT_IFNET_LABEL: mpc->mpc_ops->mpo_init_ifnet_label = mpe->mpe_function; break; case MAC_INIT_IPQ_LABEL: mpc->mpc_ops->mpo_init_ipq_label = mpe->mpe_function; break; case MAC_INIT_MBUF_LABEL: mpc->mpc_ops->mpo_init_mbuf_label = mpe->mpe_function; break; case MAC_INIT_MOUNT_LABEL: mpc->mpc_ops->mpo_init_mount_label = mpe->mpe_function; break; case MAC_INIT_MOUNT_FS_LABEL: mpc->mpc_ops->mpo_init_mount_fs_label = mpe->mpe_function; break; case MAC_INIT_PIPE_LABEL: mpc->mpc_ops->mpo_init_pipe_label = mpe->mpe_function; break; case MAC_INIT_SOCKET_LABEL: mpc->mpc_ops->mpo_init_socket_label = mpe->mpe_function; break; case MAC_INIT_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_init_socket_peer_label = mpe->mpe_function; break; case MAC_INIT_VNODE_LABEL: mpc->mpc_ops->mpo_init_vnode_label = mpe->mpe_function; break; case MAC_DESTROY_BPFDESC_LABEL: mpc->mpc_ops->mpo_destroy_bpfdesc_label = mpe->mpe_function; break; case MAC_DESTROY_CRED_LABEL: mpc->mpc_ops->mpo_destroy_cred_label = mpe->mpe_function; break; case MAC_DESTROY_DEVFSDIRENT_LABEL: mpc->mpc_ops->mpo_destroy_devfsdirent_label = mpe->mpe_function; break; case MAC_DESTROY_IFNET_LABEL: mpc->mpc_ops->mpo_destroy_ifnet_label = mpe->mpe_function; break; case MAC_DESTROY_IPQ_LABEL: mpc->mpc_ops->mpo_destroy_ipq_label = mpe->mpe_function; break; case MAC_DESTROY_MBUF_LABEL: mpc->mpc_ops->mpo_destroy_mbuf_label = mpe->mpe_function; break; case MAC_DESTROY_MOUNT_LABEL: mpc->mpc_ops->mpo_destroy_mount_label = mpe->mpe_function; break; case MAC_DESTROY_MOUNT_FS_LABEL: mpc->mpc_ops->mpo_destroy_mount_fs_label = mpe->mpe_function; break; case MAC_DESTROY_PIPE_LABEL: mpc->mpc_ops->mpo_destroy_pipe_label = mpe->mpe_function; break; case MAC_DESTROY_SOCKET_LABEL: mpc->mpc_ops->mpo_destroy_socket_label = mpe->mpe_function; break; case MAC_DESTROY_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_destroy_socket_peer_label = mpe->mpe_function; break; case MAC_DESTROY_VNODE_LABEL: mpc->mpc_ops->mpo_destroy_vnode_label = mpe->mpe_function; break; case MAC_COPY_PIPE_LABEL: mpc->mpc_ops->mpo_copy_pipe_label = mpe->mpe_function; break; case MAC_COPY_VNODE_LABEL: mpc->mpc_ops->mpo_copy_vnode_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_CRED_LABEL: mpc->mpc_ops->mpo_externalize_cred_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_IFNET_LABEL: mpc->mpc_ops->mpo_externalize_ifnet_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_PIPE_LABEL: mpc->mpc_ops->mpo_externalize_pipe_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_SOCKET_LABEL: mpc->mpc_ops->mpo_externalize_socket_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_externalize_socket_peer_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_VNODE_LABEL: mpc->mpc_ops->mpo_externalize_vnode_label = mpe->mpe_function; break; case MAC_INTERNALIZE_CRED_LABEL: mpc->mpc_ops->mpo_internalize_cred_label = mpe->mpe_function; break; case MAC_INTERNALIZE_IFNET_LABEL: mpc->mpc_ops->mpo_internalize_ifnet_label = mpe->mpe_function; break; case MAC_INTERNALIZE_PIPE_LABEL: mpc->mpc_ops->mpo_internalize_pipe_label = mpe->mpe_function; break; case MAC_INTERNALIZE_SOCKET_LABEL: mpc->mpc_ops->mpo_internalize_socket_label = mpe->mpe_function; break; case MAC_INTERNALIZE_VNODE_LABEL: mpc->mpc_ops->mpo_internalize_vnode_label = mpe->mpe_function; break; case MAC_CREATE_DEVFS_DEVICE: mpc->mpc_ops->mpo_create_devfs_device = mpe->mpe_function; break; case MAC_CREATE_DEVFS_DIRECTORY: mpc->mpc_ops->mpo_create_devfs_directory = mpe->mpe_function; break; case MAC_CREATE_DEVFS_SYMLINK: mpc->mpc_ops->mpo_create_devfs_symlink = mpe->mpe_function; break; case MAC_CREATE_DEVFS_VNODE: mpc->mpc_ops->mpo_create_devfs_vnode = mpe->mpe_function; break; case MAC_CREATE_MOUNT: mpc->mpc_ops->mpo_create_mount = mpe->mpe_function; break; case MAC_CREATE_ROOT_MOUNT: mpc->mpc_ops->mpo_create_root_mount = mpe->mpe_function; break; case MAC_RELABEL_VNODE: mpc->mpc_ops->mpo_relabel_vnode = mpe->mpe_function; break; case MAC_UPDATE_DEVFSDIRENT: mpc->mpc_ops->mpo_update_devfsdirent = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_DEVFS: mpc->mpc_ops->mpo_associate_vnode_devfs = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_EXTATTR: mpc->mpc_ops->mpo_associate_vnode_extattr = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_SINGLELABEL: mpc->mpc_ops->mpo_associate_vnode_singlelabel = mpe->mpe_function; break; case MAC_CREATE_VNODE_EXTATTR: mpc->mpc_ops->mpo_create_vnode_extattr = mpe->mpe_function; break; case MAC_SETLABEL_VNODE_EXTATTR: mpc->mpc_ops->mpo_setlabel_vnode_extattr = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_SOCKET: mpc->mpc_ops->mpo_create_mbuf_from_socket = mpe->mpe_function; break; case MAC_CREATE_PIPE: mpc->mpc_ops->mpo_create_pipe = mpe->mpe_function; break; case MAC_CREATE_SOCKET: mpc->mpc_ops->mpo_create_socket = mpe->mpe_function; break; case MAC_CREATE_SOCKET_FROM_SOCKET: mpc->mpc_ops->mpo_create_socket_from_socket = mpe->mpe_function; break; case MAC_RELABEL_PIPE: mpc->mpc_ops->mpo_relabel_pipe = mpe->mpe_function; break; case MAC_RELABEL_SOCKET: mpc->mpc_ops->mpo_relabel_socket = mpe->mpe_function; break; case MAC_SET_SOCKET_PEER_FROM_MBUF: mpc->mpc_ops->mpo_set_socket_peer_from_mbuf = mpe->mpe_function; break; case MAC_SET_SOCKET_PEER_FROM_SOCKET: mpc->mpc_ops->mpo_set_socket_peer_from_socket = mpe->mpe_function; break; case MAC_CREATE_BPFDESC: mpc->mpc_ops->mpo_create_bpfdesc = mpe->mpe_function; break; case MAC_CREATE_DATAGRAM_FROM_IPQ: mpc->mpc_ops->mpo_create_datagram_from_ipq = mpe->mpe_function; break; case MAC_CREATE_FRAGMENT: mpc->mpc_ops->mpo_create_fragment = mpe->mpe_function; break; case MAC_CREATE_IFNET: mpc->mpc_ops->mpo_create_ifnet = mpe->mpe_function; break; case MAC_CREATE_IPQ: mpc->mpc_ops->mpo_create_ipq = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_MBUF: mpc->mpc_ops->mpo_create_mbuf_from_mbuf = mpe->mpe_function; break; case MAC_CREATE_MBUF_LINKLAYER: mpc->mpc_ops->mpo_create_mbuf_linklayer = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_BPFDESC: mpc->mpc_ops->mpo_create_mbuf_from_bpfdesc = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_IFNET: mpc->mpc_ops->mpo_create_mbuf_from_ifnet = mpe->mpe_function; break; case MAC_CREATE_MBUF_MULTICAST_ENCAP: mpc->mpc_ops->mpo_create_mbuf_multicast_encap = mpe->mpe_function; break; case MAC_CREATE_MBUF_NETLAYER: mpc->mpc_ops->mpo_create_mbuf_netlayer = mpe->mpe_function; break; case MAC_FRAGMENT_MATCH: mpc->mpc_ops->mpo_fragment_match = mpe->mpe_function; break; case MAC_RELABEL_IFNET: mpc->mpc_ops->mpo_relabel_ifnet = mpe->mpe_function; break; case MAC_UPDATE_IPQ: mpc->mpc_ops->mpo_update_ipq = mpe->mpe_function; break; case MAC_CREATE_CRED: mpc->mpc_ops->mpo_create_cred = mpe->mpe_function; break; case MAC_EXECVE_TRANSITION: mpc->mpc_ops->mpo_execve_transition = mpe->mpe_function; break; case MAC_EXECVE_WILL_TRANSITION: mpc->mpc_ops->mpo_execve_will_transition = mpe->mpe_function; break; case MAC_CREATE_PROC0: mpc->mpc_ops->mpo_create_proc0 = mpe->mpe_function; break; case MAC_CREATE_PROC1: mpc->mpc_ops->mpo_create_proc1 = mpe->mpe_function; break; case MAC_RELABEL_CRED: mpc->mpc_ops->mpo_relabel_cred = mpe->mpe_function; break; case MAC_THREAD_USERRET: mpc->mpc_ops->mpo_thread_userret = mpe->mpe_function; break; case MAC_CHECK_BPFDESC_RECEIVE: mpc->mpc_ops->mpo_check_bpfdesc_receive = mpe->mpe_function; break; case MAC_CHECK_CRED_RELABEL: mpc->mpc_ops->mpo_check_cred_relabel = mpe->mpe_function; break; case MAC_CHECK_CRED_VISIBLE: mpc->mpc_ops->mpo_check_cred_visible = mpe->mpe_function; break; case MAC_CHECK_IFNET_RELABEL: mpc->mpc_ops->mpo_check_ifnet_relabel = mpe->mpe_function; break; case MAC_CHECK_IFNET_TRANSMIT: mpc->mpc_ops->mpo_check_ifnet_transmit = mpe->mpe_function; break; case MAC_CHECK_MOUNT_STAT: mpc->mpc_ops->mpo_check_mount_stat = mpe->mpe_function; break; case MAC_CHECK_PIPE_IOCTL: mpc->mpc_ops->mpo_check_pipe_ioctl = mpe->mpe_function; break; case MAC_CHECK_PIPE_POLL: mpc->mpc_ops->mpo_check_pipe_poll = mpe->mpe_function; break; case MAC_CHECK_PIPE_READ: mpc->mpc_ops->mpo_check_pipe_read = mpe->mpe_function; break; case MAC_CHECK_PIPE_RELABEL: mpc->mpc_ops->mpo_check_pipe_relabel = mpe->mpe_function; break; case MAC_CHECK_PIPE_STAT: mpc->mpc_ops->mpo_check_pipe_stat = mpe->mpe_function; break; case MAC_CHECK_PIPE_WRITE: mpc->mpc_ops->mpo_check_pipe_write = mpe->mpe_function; break; case MAC_CHECK_PROC_DEBUG: mpc->mpc_ops->mpo_check_proc_debug = mpe->mpe_function; break; case MAC_CHECK_PROC_SCHED: mpc->mpc_ops->mpo_check_proc_sched = mpe->mpe_function; break; case MAC_CHECK_PROC_SIGNAL: mpc->mpc_ops->mpo_check_proc_signal = mpe->mpe_function; break; case MAC_CHECK_SOCKET_BIND: mpc->mpc_ops->mpo_check_socket_bind = mpe->mpe_function; break; case MAC_CHECK_SOCKET_CONNECT: mpc->mpc_ops->mpo_check_socket_connect = mpe->mpe_function; break; case MAC_CHECK_SOCKET_DELIVER: mpc->mpc_ops->mpo_check_socket_deliver = mpe->mpe_function; break; case MAC_CHECK_SOCKET_LISTEN: mpc->mpc_ops->mpo_check_socket_listen = mpe->mpe_function; break; case MAC_CHECK_SOCKET_RECEIVE: mpc->mpc_ops->mpo_check_socket_receive = mpe->mpe_function; break; case MAC_CHECK_SOCKET_RELABEL: mpc->mpc_ops->mpo_check_socket_relabel = mpe->mpe_function; break; case MAC_CHECK_SOCKET_SEND: mpc->mpc_ops->mpo_check_socket_send = mpe->mpe_function; break; case MAC_CHECK_SOCKET_VISIBLE: mpc->mpc_ops->mpo_check_socket_visible = mpe->mpe_function; break; case MAC_CHECK_SYSTEM_REBOOT: mpc->mpc_ops->mpo_check_system_reboot = mpe->mpe_function; break; case MAC_CHECK_SYSTEM_SWAPON: mpc->mpc_ops->mpo_check_system_swapon = mpe->mpe_function; break; + case MAC_CHECK_SYSTEM_SYSCTL: + mpc->mpc_ops->mpo_check_system_sysctl = + mpe->mpe_function; + break; case MAC_CHECK_VNODE_ACCESS: mpc->mpc_ops->mpo_check_vnode_access = mpe->mpe_function; break; case MAC_CHECK_VNODE_CHDIR: mpc->mpc_ops->mpo_check_vnode_chdir = mpe->mpe_function; break; case MAC_CHECK_VNODE_CHROOT: mpc->mpc_ops->mpo_check_vnode_chroot = mpe->mpe_function; break; case MAC_CHECK_VNODE_CREATE: mpc->mpc_ops->mpo_check_vnode_create = mpe->mpe_function; break; case MAC_CHECK_VNODE_DELETE: mpc->mpc_ops->mpo_check_vnode_delete = mpe->mpe_function; break; case MAC_CHECK_VNODE_DELETEACL: mpc->mpc_ops->mpo_check_vnode_deleteacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_EXEC: mpc->mpc_ops->mpo_check_vnode_exec = mpe->mpe_function; break; case MAC_CHECK_VNODE_GETACL: mpc->mpc_ops->mpo_check_vnode_getacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_GETEXTATTR: mpc->mpc_ops->mpo_check_vnode_getextattr = mpe->mpe_function; break; case MAC_CHECK_VNODE_LINK: mpc->mpc_ops->mpo_check_vnode_link = mpe->mpe_function; break; case MAC_CHECK_VNODE_LOOKUP: mpc->mpc_ops->mpo_check_vnode_lookup = mpe->mpe_function; break; case MAC_CHECK_VNODE_MMAP: mpc->mpc_ops->mpo_check_vnode_mmap = mpe->mpe_function; break; case MAC_CHECK_VNODE_MMAP_DOWNGRADE: mpc->mpc_ops->mpo_check_vnode_mmap_downgrade = mpe->mpe_function; break; case MAC_CHECK_VNODE_MPROTECT: mpc->mpc_ops->mpo_check_vnode_mprotect = mpe->mpe_function; break; case MAC_CHECK_VNODE_OPEN: mpc->mpc_ops->mpo_check_vnode_open = mpe->mpe_function; break; case MAC_CHECK_VNODE_POLL: mpc->mpc_ops->mpo_check_vnode_poll = mpe->mpe_function; break; case MAC_CHECK_VNODE_READ: mpc->mpc_ops->mpo_check_vnode_read = mpe->mpe_function; break; case MAC_CHECK_VNODE_READDIR: mpc->mpc_ops->mpo_check_vnode_readdir = mpe->mpe_function; break; case MAC_CHECK_VNODE_READLINK: mpc->mpc_ops->mpo_check_vnode_readlink = mpe->mpe_function; break; case MAC_CHECK_VNODE_RELABEL: mpc->mpc_ops->mpo_check_vnode_relabel = mpe->mpe_function; break; case MAC_CHECK_VNODE_RENAME_FROM: mpc->mpc_ops->mpo_check_vnode_rename_from = mpe->mpe_function; break; case MAC_CHECK_VNODE_RENAME_TO: mpc->mpc_ops->mpo_check_vnode_rename_to = mpe->mpe_function; break; case MAC_CHECK_VNODE_REVOKE: mpc->mpc_ops->mpo_check_vnode_revoke = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETACL: mpc->mpc_ops->mpo_check_vnode_setacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETEXTATTR: mpc->mpc_ops->mpo_check_vnode_setextattr = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETFLAGS: mpc->mpc_ops->mpo_check_vnode_setflags = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETMODE: mpc->mpc_ops->mpo_check_vnode_setmode = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETOWNER: mpc->mpc_ops->mpo_check_vnode_setowner = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETUTIMES: mpc->mpc_ops->mpo_check_vnode_setutimes = mpe->mpe_function; break; case MAC_CHECK_VNODE_STAT: mpc->mpc_ops->mpo_check_vnode_stat = mpe->mpe_function; break; case MAC_CHECK_VNODE_WRITE: mpc->mpc_ops->mpo_check_vnode_write = mpe->mpe_function; break; /* default: printf("MAC policy `%s': unknown operation %d\n", mpc->mpc_name, mpe->mpe_constant); return (EINVAL); */ } } MAC_POLICY_LIST_LOCK(); if (mac_policy_list_busy > 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (EBUSY); } LIST_FOREACH(tmpc, &mac_policy_list, mpc_list) { if (strcmp(tmpc->mpc_name, mpc->mpc_name) == 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (EEXIST); } } if (mpc->mpc_field_off != NULL) { slot = ffs(mac_policy_offsets_free); if (slot == 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (ENOMEM); } slot--; mac_policy_offsets_free &= ~(1 << slot); *mpc->mpc_field_off = slot; } mpc->mpc_runtime_flags |= MPC_RUNTIME_FLAG_REGISTERED; LIST_INSERT_HEAD(&mac_policy_list, mpc, mpc_list); /* Per-policy initialization. */ if (mpc->mpc_ops->mpo_init != NULL) (*(mpc->mpc_ops->mpo_init))(mpc); MAC_POLICY_LIST_UNLOCK(); printf("Security policy loaded: %s (%s)\n", mpc->mpc_fullname, mpc->mpc_name); return (0); } 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_LIST_LOCK(); if ((mpc->mpc_runtime_flags & MPC_RUNTIME_FLAG_REGISTERED) == 0) { MAC_POLICY_LIST_UNLOCK(); return (0); } #if 0 /* * Don't allow unloading modules with private data. */ if (mpc->mpc_field_off != NULL) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } #endif /* * Only allow the unload to proceed if the module is unloadable * by its own definition. */ if ((mpc->mpc_loadtime_flags & MPC_LOADTIME_FLAG_UNLOADOK) == 0) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } /* * Right now, we EBUSY if the list is in use. In the future, * for reliability reasons, we might want to sleep and wakeup * later to try again. */ if (mac_policy_list_busy > 0) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } if (mpc->mpc_ops->mpo_destroy != NULL) (*(mpc->mpc_ops->mpo_destroy))(mpc); LIST_REMOVE(mpc, mpc_list); MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; mpc->mpc_runtime_flags &= ~MPC_RUNTIME_FLAG_REGISTERED; printf("Security policy unload: %s (%s)\n", mpc->mpc_fullname, mpc->mpc_name); return (0); } /* * Define an error value precedence, and given two arguments, selects the * value with the higher precedence. */ static int 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); } static void mac_init_label(struct label *label) { bzero(label, sizeof(*label)); label->l_flags = MAC_FLAG_INITIALIZED; } static void mac_destroy_label(struct label *label) { KASSERT(label->l_flags & MAC_FLAG_INITIALIZED, ("destroying uninitialized label")); bzero(label, sizeof(*label)); /* implicit: label->l_flags &= ~MAC_FLAG_INITIALIZED; */ } void mac_init_bpfdesc(struct bpf_d *bpf_d) { mac_init_label(&bpf_d->bd_label); MAC_PERFORM(init_bpfdesc_label, &bpf_d->bd_label); #ifdef MAC_DEBUG atomic_add_int(&nmacbpfdescs, 1); #endif } static void mac_init_cred_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_cred_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmaccreds, 1); #endif } void mac_init_cred(struct ucred *cred) { mac_init_cred_label(&cred->cr_label); } void mac_init_devfsdirent(struct devfs_dirent *de) { mac_init_label(&de->de_label); MAC_PERFORM(init_devfsdirent_label, &de->de_label); #ifdef MAC_DEBUG atomic_add_int(&nmacdevfsdirents, 1); #endif } static void mac_init_ifnet_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_ifnet_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacifnets, 1); #endif } void mac_init_ifnet(struct ifnet *ifp) { mac_init_ifnet_label(&ifp->if_label); } void mac_init_ipq(struct ipq *ipq) { mac_init_label(&ipq->ipq_label); MAC_PERFORM(init_ipq_label, &ipq->ipq_label); #ifdef MAC_DEBUG atomic_add_int(&nmacipqs, 1); #endif } int mac_init_mbuf(struct mbuf *m, int flag) { int error; KASSERT(m->m_flags & M_PKTHDR, ("mac_init_mbuf on non-header mbuf")); mac_init_label(&m->m_pkthdr.label); MAC_CHECK(init_mbuf_label, &m->m_pkthdr.label, flag); if (error) { MAC_PERFORM(destroy_mbuf_label, &m->m_pkthdr.label); mac_destroy_label(&m->m_pkthdr.label); } #ifdef MAC_DEBUG if (error == 0) atomic_add_int(&nmacmbufs, 1); #endif return (error); } void mac_init_mount(struct mount *mp) { mac_init_label(&mp->mnt_mntlabel); mac_init_label(&mp->mnt_fslabel); MAC_PERFORM(init_mount_label, &mp->mnt_mntlabel); MAC_PERFORM(init_mount_fs_label, &mp->mnt_fslabel); #ifdef MAC_DEBUG atomic_add_int(&nmacmounts, 1); #endif } static void mac_init_pipe_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_pipe_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacpipes, 1); #endif } void mac_init_pipe(struct pipe *pipe) { struct label *label; label = malloc(sizeof(struct label), M_MACPIPELABEL, M_ZERO|M_WAITOK); pipe->pipe_label = label; pipe->pipe_peer->pipe_label = label; mac_init_pipe_label(label); } static int mac_init_socket_label(struct label *label, int flag) { int error; mac_init_label(label); MAC_CHECK(init_socket_label, label, flag); if (error) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); } #ifdef MAC_DEBUG if (error == 0) atomic_add_int(&nmacsockets, 1); #endif return (error); } static int mac_init_socket_peer_label(struct label *label, int flag) { int error; mac_init_label(label); MAC_CHECK(init_socket_peer_label, label, flag); if (error) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); } return (error); } int mac_init_socket(struct socket *socket, int flag) { int error; error = mac_init_socket_label(&socket->so_label, flag); if (error) return (error); error = mac_init_socket_peer_label(&socket->so_peerlabel, flag); if (error) mac_destroy_socket_label(&socket->so_label); return (error); } void mac_init_vnode_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_vnode_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacvnodes, 1); #endif } void mac_init_vnode(struct vnode *vp) { mac_init_vnode_label(&vp->v_label); } void mac_destroy_bpfdesc(struct bpf_d *bpf_d) { MAC_PERFORM(destroy_bpfdesc_label, &bpf_d->bd_label); mac_destroy_label(&bpf_d->bd_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacbpfdescs, 1); #endif } static void mac_destroy_cred_label(struct label *label) { MAC_PERFORM(destroy_cred_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmaccreds, 1); #endif } void mac_destroy_cred(struct ucred *cred) { mac_destroy_cred_label(&cred->cr_label); } void mac_destroy_devfsdirent(struct devfs_dirent *de) { MAC_PERFORM(destroy_devfsdirent_label, &de->de_label); mac_destroy_label(&de->de_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacdevfsdirents, 1); #endif } static void mac_destroy_ifnet_label(struct label *label) { MAC_PERFORM(destroy_ifnet_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacifnets, 1); #endif } void mac_destroy_ifnet(struct ifnet *ifp) { mac_destroy_ifnet_label(&ifp->if_label); } void mac_destroy_ipq(struct ipq *ipq) { MAC_PERFORM(destroy_ipq_label, &ipq->ipq_label); mac_destroy_label(&ipq->ipq_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacipqs, 1); #endif } void mac_destroy_mbuf(struct mbuf *m) { MAC_PERFORM(destroy_mbuf_label, &m->m_pkthdr.label); mac_destroy_label(&m->m_pkthdr.label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacmbufs, 1); #endif } void mac_destroy_mount(struct mount *mp) { MAC_PERFORM(destroy_mount_label, &mp->mnt_mntlabel); MAC_PERFORM(destroy_mount_fs_label, &mp->mnt_fslabel); mac_destroy_label(&mp->mnt_fslabel); mac_destroy_label(&mp->mnt_mntlabel); #ifdef MAC_DEBUG atomic_subtract_int(&nmacmounts, 1); #endif } static void mac_destroy_pipe_label(struct label *label) { MAC_PERFORM(destroy_pipe_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacpipes, 1); #endif } void mac_destroy_pipe(struct pipe *pipe) { mac_destroy_pipe_label(pipe->pipe_label); free(pipe->pipe_label, M_MACPIPELABEL); } static void mac_destroy_socket_label(struct label *label) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacsockets, 1); #endif } static void mac_destroy_socket_peer_label(struct label *label) { MAC_PERFORM(destroy_socket_peer_label, label); mac_destroy_label(label); } void mac_destroy_socket(struct socket *socket) { mac_destroy_socket_label(&socket->so_label); mac_destroy_socket_peer_label(&socket->so_peerlabel); } void mac_destroy_vnode_label(struct label *label) { MAC_PERFORM(destroy_vnode_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacvnodes, 1); #endif } void mac_destroy_vnode(struct vnode *vp) { mac_destroy_vnode_label(&vp->v_label); } static void mac_copy_pipe_label(struct label *src, struct label *dest) { MAC_PERFORM(copy_pipe_label, src, dest); } void mac_copy_vnode_label(struct label *src, struct label *dest) { MAC_PERFORM(copy_vnode_label, src, dest); } static int mac_check_structmac_consistent(struct mac *mac) { if (mac->m_buflen > MAC_MAX_LABEL_BUF_LEN) return (EINVAL); return (0); } static int mac_externalize_cred_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(cred_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_ifnet_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(ifnet_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_pipe_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(pipe_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_socket_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(socket_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_socket_peer_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(socket_peer_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_vnode_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(vnode_label, label, elements, outbuf, outbuflen); return (error); } static int mac_internalize_cred_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(cred_label, label, string); return (error); } static int mac_internalize_ifnet_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(ifnet_label, label, string); return (error); } static int mac_internalize_pipe_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(pipe_label, label, string); return (error); } static int mac_internalize_socket_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(socket_label, label, string); return (error); } static int mac_internalize_vnode_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(vnode_label, label, string); return (error); } /* * Initialize MAC label for the first kernel process, from which other * kernel processes and threads are spawned. */ void mac_create_proc0(struct ucred *cred) { MAC_PERFORM(create_proc0, cred); } /* * Initialize MAC label for the first userland process, from which other * userland processes and threads are spawned. */ void mac_create_proc1(struct ucred *cred) { MAC_PERFORM(create_proc1, cred); } void mac_thread_userret(struct thread *td) { MAC_PERFORM(thread_userret, td); } /* * 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_create_cred(struct ucred *parent_cred, struct ucred *child_cred) { MAC_PERFORM(create_cred, parent_cred, child_cred); } void mac_update_devfsdirent(struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(update_devfsdirent, de, &de->de_label, vp, &vp->v_label); } void mac_associate_vnode_devfs(struct mount *mp, struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(associate_vnode_devfs, mp, &mp->mnt_fslabel, de, &de->de_label, vp, &vp->v_label); } int mac_associate_vnode_extattr(struct mount *mp, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_associate_vnode_extattr"); MAC_CHECK(associate_vnode_extattr, mp, &mp->mnt_fslabel, vp, &vp->v_label); return (error); } void mac_associate_vnode_singlelabel(struct mount *mp, struct vnode *vp) { MAC_PERFORM(associate_vnode_singlelabel, mp, &mp->mnt_fslabel, vp, &vp->v_label); } int mac_create_vnode_extattr(struct ucred *cred, struct mount *mp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_create_vnode_extattr"); ASSERT_VOP_LOCKED(vp, "mac_create_vnode_extattr"); error = VOP_OPENEXTATTR(vp, cred, curthread); if (error == EOPNOTSUPP) { /* XXX: Optionally abort if transactions not supported. */ if (ea_warn_once == 0) { printf("Warning: transactions not supported " "in EA write.\n"); ea_warn_once = 1; } } else if (error) return (error); MAC_CHECK(create_vnode_extattr, cred, mp, &mp->mnt_fslabel, 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; /* XXX */ return (error); } static int mac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *intlabel) { int error; ASSERT_VOP_LOCKED(vp, "mac_setlabel_vnode_extattr"); error = VOP_OPENEXTATTR(vp, cred, curthread); if (error == EOPNOTSUPP) { /* XXX: Optionally abort if transactions not supported. */ if (ea_warn_once == 0) { printf("Warning: transactions not supported " "in EA write.\n"); ea_warn_once = 1; } } else if (error) return (error); MAC_CHECK(setlabel_vnode_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; /* XXX */ return (error); } void mac_execve_transition(struct ucred *old, struct ucred *new, struct vnode *vp) { ASSERT_VOP_LOCKED(vp, "mac_execve_transition"); MAC_PERFORM(execve_transition, old, new, vp, &vp->v_label); } int mac_execve_will_transition(struct ucred *old, struct vnode *vp) { int result; result = 0; MAC_BOOLEAN(execve_will_transition, ||, old, vp, &vp->v_label); return (result); } int mac_check_vnode_access(struct ucred *cred, struct vnode *vp, int flags) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_access"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_access, cred, vp, &vp->v_label, flags); return (error); } int mac_check_vnode_chdir(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_chdir"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_chdir, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_chroot(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_chroot"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_chroot, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_create(struct ucred *cred, struct vnode *dvp, struct componentname *cnp, struct vattr *vap) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_create"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_create, cred, dvp, &dvp->v_label, cnp, vap); return (error); } int mac_check_vnode_delete(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_delete"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_delete"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_delete, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp, acl_type_t type) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_deleteacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_deleteacl, cred, vp, &vp->v_label, type); return (error); } int mac_check_vnode_exec(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_exec"); if (!mac_enforce_process && !mac_enforce_fs) return (0); MAC_CHECK(check_vnode_exec, cred, vp, &vp->v_label); return (error); } int mac_check_vnode_getacl(struct ucred *cred, struct vnode *vp, acl_type_t type) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_getacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_getacl, cred, vp, &vp->v_label, type); return (error); } int mac_check_vnode_getextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_getextattr"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_getextattr, cred, vp, &vp->v_label, attrnamespace, name, uio); return (error); } int mac_check_vnode_link(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_link"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_link"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_link, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_lookup(struct ucred *cred, struct vnode *dvp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_lookup"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_lookup, cred, dvp, &dvp->v_label, cnp); return (error); } int mac_check_vnode_mmap(struct ucred *cred, struct vnode *vp, int prot) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mmap"); if (!mac_enforce_fs || !mac_enforce_vm) return (0); MAC_CHECK(check_vnode_mmap, cred, vp, &vp->v_label, prot); return (error); } void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot) { int result = *prot; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mmap_downgrade"); if (!mac_enforce_fs || !mac_enforce_vm) return; MAC_PERFORM(check_vnode_mmap_downgrade, cred, vp, &vp->v_label, &result); *prot = result; } int mac_check_vnode_mprotect(struct ucred *cred, struct vnode *vp, int prot) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mprotect"); if (!mac_enforce_fs || !mac_enforce_vm) return (0); MAC_CHECK(check_vnode_mprotect, cred, vp, &vp->v_label, prot); return (error); } int mac_check_vnode_open(struct ucred *cred, struct vnode *vp, mode_t acc_mode) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_open"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_open, cred, vp, &vp->v_label, acc_mode); return (error); } int mac_check_vnode_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_poll"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_poll, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_read"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_read, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_readdir(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_readdir"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_readdir, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_readlink(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_readlink"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_readlink, cred, vp, &vp->v_label); return (error); } static int mac_check_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *newlabel) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_relabel"); MAC_CHECK(check_vnode_relabel, cred, vp, &vp->v_label, newlabel); return (error); } int mac_check_vnode_rename_from(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_rename_from"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_rename_from"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_rename_from, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp, struct vnode *vp, int samedir, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_rename_to"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_rename_to"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_rename_to, cred, dvp, &dvp->v_label, vp, vp != NULL ? &vp->v_label : NULL, samedir, cnp); return (error); } int mac_check_vnode_revoke(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_revoke"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_revoke, cred, vp, &vp->v_label); return (error); } int mac_check_vnode_setacl(struct ucred *cred, struct vnode *vp, acl_type_t type, struct acl *acl) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setacl, cred, vp, &vp->v_label, type, acl); return (error); } int mac_check_vnode_setextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setextattr"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setextattr, cred, vp, &vp->v_label, attrnamespace, name, uio); return (error); } int mac_check_vnode_setflags(struct ucred *cred, struct vnode *vp, u_long flags) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setflags"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setflags, cred, vp, &vp->v_label, flags); return (error); } int mac_check_vnode_setmode(struct ucred *cred, struct vnode *vp, mode_t mode) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setmode"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setmode, cred, vp, &vp->v_label, mode); return (error); } int mac_check_vnode_setowner(struct ucred *cred, struct vnode *vp, uid_t uid, gid_t gid) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setowner"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setowner, cred, vp, &vp->v_label, uid, gid); return (error); } int mac_check_vnode_setutimes(struct ucred *cred, struct vnode *vp, struct timespec atime, struct timespec mtime) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setutimes"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setutimes, cred, vp, &vp->v_label, atime, mtime); return (error); } int mac_check_vnode_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_stat"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_stat, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_write"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_write, active_cred, file_cred, vp, &vp->v_label); return (error); } /* * 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. */ static void mac_cred_mmapped_drop_perms(struct thread *td, struct ucred *cred) { /* XXX freeze all other threads */ mac_cred_mmapped_drop_perms_recurse(td, cred, &td->td_proc->p_vmspace->vm_map); /* XXX allow other threads to continue */ } 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_cred_mmapped_drop_perms_recurse(struct thread *td, struct ucred *cred, struct vm_map *map) { struct vm_map_entry *vme; int result; vm_prot_t revokeperms; vm_object_t object; vm_ooffset_t offset; struct vnode *vp; if (!mac_mmap_revocation) return; vm_map_lock_read(map); for (vme = map->header.next; vme != &map->header; vme = vme->next) { if (vme->eflags & MAP_ENTRY_IS_SUB_MAP) { mac_cred_mmapped_drop_perms_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; while (object->backing_object != NULL) { object = object->backing_object; offset += object->backing_object_offset; } /* * 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, td); result = vme->max_protection; mac_check_vnode_mmap_downgrade(cred, vp, &result); VOP_UNLOCK(vp, 0, td); /* * 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)); vm_map_lock_upgrade(map); /* * 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); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); vm_object_page_clean(object, OFF_TO_IDX(offset), OFF_TO_IDX(offset + vme->end - vme->start + PAGE_MASK), OBJPC_SYNC); VOP_UNLOCK(vp, 0, td); 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_lock_downgrade(map); } vm_map_unlock_read(map); } /* * 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. */ static void mac_relabel_cred(struct ucred *cred, struct label *newlabel) { MAC_PERFORM(relabel_cred, cred, newlabel); } void mac_relabel_vnode(struct ucred *cred, struct vnode *vp, struct label *newlabel) { MAC_PERFORM(relabel_vnode, cred, vp, &vp->v_label, newlabel); } void mac_create_ifnet(struct ifnet *ifnet) { MAC_PERFORM(create_ifnet, ifnet, &ifnet->if_label); } void mac_create_bpfdesc(struct ucred *cred, struct bpf_d *bpf_d) { MAC_PERFORM(create_bpfdesc, cred, bpf_d, &bpf_d->bd_label); } void mac_create_socket(struct ucred *cred, struct socket *socket) { MAC_PERFORM(create_socket, cred, socket, &socket->so_label); } void mac_create_pipe(struct ucred *cred, struct pipe *pipe) { MAC_PERFORM(create_pipe, cred, pipe, pipe->pipe_label); } void mac_create_socket_from_socket(struct socket *oldsocket, struct socket *newsocket) { MAC_PERFORM(create_socket_from_socket, oldsocket, &oldsocket->so_label, newsocket, &newsocket->so_label); } static void mac_relabel_socket(struct ucred *cred, struct socket *socket, struct label *newlabel) { MAC_PERFORM(relabel_socket, cred, socket, &socket->so_label, newlabel); } static void mac_relabel_pipe(struct ucred *cred, struct pipe *pipe, struct label *newlabel) { MAC_PERFORM(relabel_pipe, cred, pipe, pipe->pipe_label, newlabel); } void mac_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct socket *socket) { MAC_PERFORM(set_socket_peer_from_mbuf, mbuf, &mbuf->m_pkthdr.label, socket, &socket->so_peerlabel); } void mac_set_socket_peer_from_socket(struct socket *oldsocket, struct socket *newsocket) { MAC_PERFORM(set_socket_peer_from_socket, oldsocket, &oldsocket->so_label, newsocket, &newsocket->so_peerlabel); } void mac_create_datagram_from_ipq(struct ipq *ipq, struct mbuf *datagram) { MAC_PERFORM(create_datagram_from_ipq, ipq, &ipq->ipq_label, datagram, &datagram->m_pkthdr.label); } void mac_create_fragment(struct mbuf *datagram, struct mbuf *fragment) { MAC_PERFORM(create_fragment, datagram, &datagram->m_pkthdr.label, fragment, &fragment->m_pkthdr.label); } void mac_create_ipq(struct mbuf *fragment, struct ipq *ipq) { MAC_PERFORM(create_ipq, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); } void mac_create_mbuf_from_mbuf(struct mbuf *oldmbuf, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_from_mbuf, oldmbuf, &oldmbuf->m_pkthdr.label, newmbuf, &newmbuf->m_pkthdr.label); } void mac_create_mbuf_from_bpfdesc(struct bpf_d *bpf_d, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_bpfdesc, bpf_d, &bpf_d->bd_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_linklayer(struct ifnet *ifnet, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_linklayer, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_from_ifnet(struct ifnet *ifnet, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_ifnet, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_multicast_encap(struct mbuf *oldmbuf, struct ifnet *ifnet, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_multicast_encap, oldmbuf, &oldmbuf->m_pkthdr.label, ifnet, &ifnet->if_label, newmbuf, &newmbuf->m_pkthdr.label); } void mac_create_mbuf_netlayer(struct mbuf *oldmbuf, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_netlayer, oldmbuf, &oldmbuf->m_pkthdr.label, newmbuf, &newmbuf->m_pkthdr.label); } int mac_fragment_match(struct mbuf *fragment, struct ipq *ipq) { int result; result = 1; MAC_BOOLEAN(fragment_match, &&, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); return (result); } void mac_update_ipq(struct mbuf *fragment, struct ipq *ipq) { MAC_PERFORM(update_ipq, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); } void mac_create_mbuf_from_socket(struct socket *socket, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_socket, socket, &socket->so_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mount(struct ucred *cred, struct mount *mp) { MAC_PERFORM(create_mount, cred, mp, &mp->mnt_mntlabel, &mp->mnt_fslabel); } void mac_create_root_mount(struct ucred *cred, struct mount *mp) { MAC_PERFORM(create_root_mount, cred, mp, &mp->mnt_mntlabel, &mp->mnt_fslabel); } int mac_check_bpfdesc_receive(struct bpf_d *bpf_d, struct ifnet *ifnet) { int error; if (!mac_enforce_network) return (0); MAC_CHECK(check_bpfdesc_receive, bpf_d, &bpf_d->bd_label, ifnet, &ifnet->if_label); return (error); } static int mac_check_cred_relabel(struct ucred *cred, struct label *newlabel) { int error; MAC_CHECK(check_cred_relabel, cred, newlabel); return (error); } int mac_check_cred_visible(struct ucred *u1, struct ucred *u2) { int error; if (!mac_enforce_process) return (0); MAC_CHECK(check_cred_visible, u1, u2); return (error); } int mac_check_ifnet_transmit(struct ifnet *ifnet, struct mbuf *mbuf) { int error; if (!mac_enforce_network) return (0); KASSERT(mbuf->m_flags & M_PKTHDR, ("packet has no pkthdr")); if (!(mbuf->m_pkthdr.label.l_flags & MAC_FLAG_INITIALIZED)) if_printf(ifnet, "not initialized\n"); MAC_CHECK(check_ifnet_transmit, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); return (error); } int mac_check_mount_stat(struct ucred *cred, struct mount *mount) { int error; if (!mac_enforce_fs) return (0); MAC_CHECK(check_mount_stat, cred, mount, &mount->mnt_mntlabel); return (error); } int mac_check_pipe_ioctl(struct ucred *cred, struct pipe *pipe, unsigned long cmd, void *data) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_ioctl, cred, pipe, pipe->pipe_label, cmd, data); return (error); } int mac_check_pipe_poll(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_poll, cred, pipe, pipe->pipe_label); return (error); } int mac_check_pipe_read(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_read, cred, pipe, pipe->pipe_label); return (error); } static int mac_check_pipe_relabel(struct ucred *cred, struct pipe *pipe, struct label *newlabel) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_relabel, cred, pipe, pipe->pipe_label, newlabel); return (error); } int mac_check_pipe_stat(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_stat, cred, pipe, pipe->pipe_label); return (error); } int mac_check_pipe_write(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_write, cred, pipe, pipe->pipe_label); return (error); } int mac_check_proc_debug(struct ucred *cred, struct proc *proc) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_debug, cred, proc); return (error); } int mac_check_proc_sched(struct ucred *cred, struct proc *proc) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_sched, cred, proc); return (error); } int mac_check_proc_signal(struct ucred *cred, struct proc *proc, int signum) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_signal, cred, proc, signum); return (error); } int mac_check_socket_bind(struct ucred *ucred, struct socket *socket, struct sockaddr *sockaddr) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_bind, ucred, socket, &socket->so_label, sockaddr); return (error); } int mac_check_socket_connect(struct ucred *cred, struct socket *socket, struct sockaddr *sockaddr) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_connect, cred, socket, &socket->so_label, sockaddr); return (error); } int mac_check_socket_deliver(struct socket *socket, struct mbuf *mbuf) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_deliver, socket, &socket->so_label, mbuf, &mbuf->m_pkthdr.label); return (error); } int mac_check_socket_listen(struct ucred *cred, struct socket *socket) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_listen, cred, socket, &socket->so_label); return (error); } int mac_check_socket_receive(struct ucred *cred, struct socket *so) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_receive, cred, so, &so->so_label); return (error); } static int mac_check_socket_relabel(struct ucred *cred, struct socket *socket, struct label *newlabel) { int error; MAC_CHECK(check_socket_relabel, cred, socket, &socket->so_label, newlabel); return (error); } int mac_check_socket_send(struct ucred *cred, struct socket *so) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_send, cred, so, &so->so_label); return (error); } int mac_check_socket_visible(struct ucred *cred, struct socket *socket) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_visible, cred, socket, &socket->so_label); return (error); } int mac_check_system_reboot(struct ucred *cred, int howto) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_system_reboot"); if (!mac_enforce_reboot) return (0); MAC_CHECK(check_system_reboot, cred, howto); return (error); } int mac_check_system_swapon(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_system_swapon"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_system_swapon, cred, vp, &vp->v_label); + return (error); +} + +int +mac_check_system_sysctl(struct ucred *cred, int *name, u_int namelen, + void *old, size_t *oldlenp, int inkernel, void *new, size_t newlen) +{ + int error; + + /* + * XXXMAC: We're very much like to assert the SYSCTL_LOCK here, + * but since it's not exported from kern_sysctl.c, we can't. + */ + if (!mac_enforce_sysctl) + return (0); + + MAC_CHECK(check_system_sysctl, cred, name, namelen, old, oldlenp, + inkernel, new, newlen); + return (error); } int mac_ioctl_ifnet_get(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet) { char *elements, *buffer; struct mac mac; int error; 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); error = mac_externalize_ifnet_label(&ifnet->if_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } int mac_ioctl_ifnet_set(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet) { struct label intlabel; struct mac mac; char *buffer; int error; 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); } mac_init_ifnet_label(&intlabel); error = mac_internalize_ifnet_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_ifnet_label(&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 = suser_cred(cred, 0); if (error) { mac_destroy_ifnet_label(&intlabel); return (error); } MAC_CHECK(check_ifnet_relabel, cred, ifnet, &ifnet->if_label, &intlabel); if (error) { mac_destroy_ifnet_label(&intlabel); return (error); } MAC_PERFORM(relabel_ifnet, cred, ifnet, &ifnet->if_label, &intlabel); mac_destroy_ifnet_label(&intlabel); return (0); } void mac_create_devfs_vnode(struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(create_devfs_vnode, de, &de->de_label, vp, &vp->v_label); } void mac_create_devfs_device(dev_t dev, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_device, dev, de, &de->de_label); } void mac_create_devfs_symlink(struct ucred *cred, struct devfs_dirent *dd, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_symlink, cred, dd, &dd->de_label, de, &de->de_label); } void mac_create_devfs_directory(char *dirname, int dirnamelen, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_directory, dirname, dirnamelen, de, &de->de_label); } int mac_setsockopt_label_set(struct ucred *cred, struct socket *so, struct mac *mac) { struct label intlabel; char *buffer; int 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); } mac_init_socket_label(&intlabel, M_WAITOK); error = mac_internalize_socket_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_socket_label(&intlabel); return (error); } mac_check_socket_relabel(cred, so, &intlabel); if (error) { mac_destroy_socket_label(&intlabel); return (error); } mac_relabel_socket(cred, so, &intlabel); mac_destroy_socket_label(&intlabel); return (0); } int mac_pipe_label_set(struct ucred *cred, struct pipe *pipe, struct label *label) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); error = mac_check_pipe_relabel(cred, pipe, label); if (error) return (error); mac_relabel_pipe(cred, pipe, label); return (0); } int mac_getsockopt_label_get(struct ucred *cred, struct socket *so, struct mac *mac) { char *buffer, *elements; int 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_externalize_socket_label(&so->so_label, elements, buffer, mac->m_buflen, M_WAITOK); 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_get(struct ucred *cred, struct socket *so, struct mac *mac) { char *elements, *buffer; int 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_externalize_socket_peer_label(&so->so_peerlabel, elements, buffer, mac->m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac->m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * 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_setlabel_vnode_extattr(ap->a_cred, vp, intlabel); if (error) return (error); mac_relabel_vnode(ap->a_cred, vp, intlabel); return (0); } static 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_check_vnode_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); } int __mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) { char *elements, *buffer; struct mac mac; struct proc *tproc; struct ucred *tcred; int error; error = copyin(SCARG(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_externalize_cred_label(&tcred->cr_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); crfree(tcred); return (error); } /* * MPSAFE */ int __mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) { char *elements, *buffer; struct mac mac; int error; error = copyin(uap->mac_p, &mac, sizeof(mac)); if (error) return (error); error = mac_check_structmac_consistent(&mac); if (error) return (error); elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL); if (error) { free(elements, M_MACTEMP); return (error); } buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); error = mac_externalize_cred_label(&td->td_ucred->cr_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) { struct ucred *newcred, *oldcred; struct label intlabel; struct proc *p; struct mac mac; char *buffer; int error; 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); } mac_init_cred_label(&intlabel); error = mac_internalize_cred_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_cred_label(&intlabel); return (error); } newcred = crget(); p = td->td_proc; PROC_LOCK(p); oldcred = p->p_ucred; error = mac_check_cred_relabel(oldcred, &intlabel); if (error) { PROC_UNLOCK(p); crfree(newcred); goto out; } setsugid(p); crcopy(newcred, oldcred); mac_relabel_cred(newcred, &intlabel); p->p_ucred = newcred; /* * Grab additional reference for use while revoking mmaps, prior * to releasing the proc lock and sharing the cred. */ crhold(newcred); PROC_UNLOCK(p); if (mac_enforce_vm) { mtx_lock(&Giant); mac_cred_mmapped_drop_perms(td, newcred); mtx_unlock(&Giant); } crfree(newcred); /* Free revocation reference. */ crfree(oldcred); out: mac_destroy_cred_label(&intlabel); return (error); } /* * MPSAFE */ int __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) { char *elements, *buffer; struct label intlabel; struct file *fp; struct mac mac; struct vnode *vp; struct pipe *pipe; 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); mtx_lock(&Giant); /* VFS */ error = fget(td, SCARG(uap, fd), &fp); if (error) goto out; label_type = fp->f_type; switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: vp = (struct vnode *)fp->f_data; mac_init_vnode_label(&intlabel); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); mac_copy_vnode_label(&vp->v_label, &intlabel); VOP_UNLOCK(vp, 0, td); break; case DTYPE_PIPE: pipe = (struct pipe *)fp->f_data; mac_init_pipe_label(&intlabel); PIPE_LOCK(pipe); mac_copy_pipe_label(pipe->pipe_label, &intlabel); PIPE_UNLOCK(pipe); break; default: error = EINVAL; fdrop(fp, td); goto out; } fdrop(fp, td); switch (label_type) { case DTYPE_FIFO: case DTYPE_VNODE: if (error == 0) error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); mac_destroy_vnode_label(&intlabel); break; case DTYPE_PIPE: error = mac_externalize_pipe_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); mac_destroy_pipe_label(&intlabel); break; default: panic("__mac_get_fd: corrupted label_type"); } if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_get_file(struct thread *td, struct __mac_get_file_args *uap) { char *elements, *buffer; struct nameidata nd; struct label intlabel; struct mac mac; int 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); mtx_lock(&Giant); /* VFS */ NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_USERSPACE, uap->path_p, td); error = namei(&nd); if (error) goto out; mac_init_vnode_label(&intlabel); mac_copy_vnode_label(&nd.ni_vp->v_label, &intlabel); error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); NDFREE(&nd, 0); mac_destroy_vnode_label(&intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_get_link(struct thread *td, struct __mac_get_link_args *uap) { char *elements, *buffer; struct nameidata nd; struct label intlabel; struct mac mac; int 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); mtx_lock(&Giant); /* VFS */ NDINIT(&nd, LOOKUP, LOCKLEAF | NOFOLLOW, UIO_USERSPACE, uap->path_p, td); error = namei(&nd); if (error) goto out; mac_init_vnode_label(&intlabel); mac_copy_vnode_label(&nd.ni_vp->v_label, &intlabel); error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); NDFREE(&nd, 0); mac_destroy_vnode_label(&intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) { struct label intlabel; struct pipe *pipe; 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); } mtx_lock(&Giant); /* VFS */ error = fget(td, SCARG(uap, fd), &fp); if (error) goto out; switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); if (error) { mac_destroy_vnode_label(&intlabel); break; } vp = (struct vnode *)fp->f_data; error = vn_start_write(vp, &mp, V_WAIT | PCATCH); if (error != 0) { mac_destroy_vnode_label(&intlabel); break; } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); error = vn_setlabel(vp, &intlabel, td->td_ucred); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); mac_destroy_vnode_label(&intlabel); break; case DTYPE_PIPE: mac_init_pipe_label(&intlabel); error = mac_internalize_pipe_label(&intlabel, buffer); if (error == 0) { pipe = (struct pipe *)fp->f_data; PIPE_LOCK(pipe); error = mac_pipe_label_set(td->td_ucred, pipe, &intlabel); PIPE_UNLOCK(pipe); } mac_destroy_pipe_label(&intlabel); break; default: error = EINVAL; } fdrop(fp, td); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_file(struct thread *td, struct __mac_set_file_args *uap) { struct label intlabel; struct nameidata nd; struct mount *mp; struct mac mac; char *buffer; int 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); } mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_vnode_label(&intlabel); return (error); } mtx_lock(&Giant); /* VFS */ 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); mtx_unlock(&Giant); /* VFS */ mac_destroy_vnode_label(&intlabel); return (error); } /* * MPSAFE */ int __mac_set_link(struct thread *td, struct __mac_set_link_args *uap) { struct label intlabel; struct nameidata nd; struct mount *mp; struct mac mac; char *buffer; int 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); } mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_vnode_label(&intlabel); return (error); } mtx_lock(&Giant); /* VFS */ 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); mtx_unlock(&Giant); /* VFS */ mac_destroy_vnode_label(&intlabel); return (error); } /* * MPSAFE */ int 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(SCARG(uap, policy), target, sizeof(target), NULL); if (error) return (error); error = ENOSYS; MAC_POLICY_LIST_BUSY(); 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, SCARG(uap, call), SCARG(uap, arg)); goto out; } } out: MAC_POLICY_LIST_UNBUSY(); return (error); } 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); #else /* !MAC */ int __mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) { return (ENOSYS); } int __mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) { return (ENOSYS); } int __mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) { return (ENOSYS); } int __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) { return (ENOSYS); } int __mac_get_file(struct thread *td, struct __mac_get_file_args *uap) { return (ENOSYS); } int __mac_get_link(struct thread *td, struct __mac_get_link_args *uap) { return (ENOSYS); } int __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) { return (ENOSYS); } int __mac_set_file(struct thread *td, struct __mac_set_file_args *uap) { return (ENOSYS); } int __mac_set_link(struct thread *td, struct __mac_set_link_args *uap) { return (ENOSYS); } int mac_syscall(struct thread *td, struct mac_syscall_args *uap) { return (ENOSYS); } #endif Index: head/sys/security/mac/mac_framework.h =================================================================== --- head/sys/security/mac/mac_framework.h (revision 106024) +++ head/sys/security/mac/mac_framework.h (revision 106025) @@ -1,375 +1,378 @@ /*- * Copyright (c) 1999, 2000, 2001, 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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$ */ /* * Userland/kernel interface for Mandatory Access Control. * * The POSIX.1e implementation page may be reached at: * http://www.trustedbsd.org/ */ #ifndef _SYS_MAC_H #define _SYS_MAC_H #include #ifndef _POSIX_MAC #define _POSIX_MAC #endif /* * XXXMAC: The single MAC extended attribute will be deprecated once * compound EA writes on a single target file can be performed cleanly * with UFS2. */ #define FREEBSD_MAC_EXTATTR_NAME "freebsd.mac" #define FREEBSD_MAC_EXTATTR_NAMESPACE EXTATTR_NAMESPACE_SYSTEM /* * MAC framework-related constants and limits. */ #define MAC_MAX_POLICY_NAME 32 #define MAC_MAX_LABEL_ELEMENT_NAME 32 #define MAC_MAX_LABEL_ELEMENT_DATA 4096 #define MAC_MAX_LABEL_BUF_LEN 8192 struct mac { size_t m_buflen; char *m_string; }; typedef struct mac *mac_t; #ifndef _KERNEL /* * Location of the userland MAC framework configuration file. mac.conf * binds policy names to shared libraries that understand those policies, * as well as setting defaults for MAC-aware applications. */ #define MAC_CONFFILE "/etc/mac.conf" /* * Extended non-POSIX.1e interfaces that offer additional services * available from the userland and kernel MAC frameworks. */ int mac_free(mac_t _label); int mac_from_text(mac_t *_label, const char *_text); int mac_get_fd(int _fd, mac_t _label); int mac_get_file(const char *_path, mac_t _label); int mac_get_link(const char *_path, mac_t _label); int mac_get_pid(pid_t _pid, mac_t _label); int mac_get_proc(mac_t _label); int mac_is_present(const char *_policyname); int mac_prepare(mac_t *_label, char *_elements); int mac_prepare_file_label(mac_t *_label); int mac_prepare_ifnet_label(mac_t *_label); int mac_prepare_process_label(mac_t *_label); int mac_set_fd(int _fildes, const mac_t _label); int mac_set_file(const char *_path, mac_t _label); int mac_set_link(const char *_path, mac_t _label); int mac_set_proc(const mac_t _label); int mac_syscall(const char *_policyname, int _call, void *_arg); int mac_to_text(mac_t mac, char **_text); #endif /* !_KERNEL */ /* * XXXMAC: For compatibility until the labels on disk are changed. We * will enable the definitions in various policy include files once * these can be disabled. */ #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]; }; struct mac_biba { int mb_flags; struct mac_biba_element mb_single; struct mac_biba_element mb_rangelow, mb_rangehigh; }; #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]; }; struct mac_mls { int mm_flags; struct mac_mls_element mm_single; struct mac_mls_element mm_rangelow, mm_rangehigh; }; struct mac_sebsd { uint32_t ms_psid; }; struct oldmac { int m_macflags; struct mac_biba m_biba; struct mac_mls m_mls; struct mac_sebsd m_sebsd; }; #ifdef _KERNEL /* * Kernel functions to manage and evaluate labels. */ struct bpf_d; struct componentname; struct devfs_dirent; struct ifnet; struct ifreq; struct ipq; struct mbuf; struct mount; struct proc; struct sockaddr; struct socket; struct pipe; struct thread; struct timespec; struct ucred; struct uio; struct vattr; struct vnode; #include /* XXX acl_type_t */ struct vop_refreshlabel_args; struct vop_setlabel_args; /* * Label operations. */ void mac_init_bpfdesc(struct bpf_d *); void mac_init_cred(struct ucred *); void mac_init_devfsdirent(struct devfs_dirent *); void mac_init_ifnet(struct ifnet *); void mac_init_ipq(struct ipq *); int mac_init_socket(struct socket *, int flag); void mac_init_pipe(struct pipe *); int mac_init_mbuf(struct mbuf *m, int flag); void mac_init_mount(struct mount *); void mac_init_vnode(struct vnode *); void mac_init_vnode_label(struct label *); void mac_copy_vnode_label(struct label *, struct label *label); void mac_destroy_bpfdesc(struct bpf_d *); void mac_destroy_cred(struct ucred *); void mac_destroy_devfsdirent(struct devfs_dirent *); void mac_destroy_ifnet(struct ifnet *); void mac_destroy_ipq(struct ipq *); void mac_destroy_socket(struct socket *); void mac_destroy_pipe(struct pipe *); void mac_destroy_mbuf(struct mbuf *); void mac_destroy_mount(struct mount *); void mac_destroy_vnode(struct vnode *); void mac_destroy_vnode_label(struct label *); /* * Labeling event operations: file system objects, and things that * look a lot like file system objects. */ void mac_associate_vnode_devfs(struct mount *mp, struct devfs_dirent *de, struct vnode *vp); int mac_associate_vnode_extattr(struct mount *mp, struct vnode *vp); void mac_associate_vnode_singlelabel(struct mount *mp, struct vnode *vp); void mac_create_devfs_device(dev_t dev, struct devfs_dirent *de); void mac_create_devfs_directory(char *dirname, int dirnamelen, struct devfs_dirent *de); void mac_create_devfs_symlink(struct ucred *cred, struct devfs_dirent *dd, struct devfs_dirent *de); void mac_create_devfs_vnode(struct devfs_dirent *de, struct vnode *vp); int mac_create_vnode_extattr(struct ucred *cred, struct mount *mp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp); void mac_create_mount(struct ucred *cred, struct mount *mp); void mac_create_root_mount(struct ucred *cred, struct mount *mp); void mac_relabel_vnode(struct ucred *cred, struct vnode *vp, struct label *newlabel); void mac_update_devfsdirent(struct devfs_dirent *de, struct vnode *vp); /* * Labeling event operations: IPC objects. */ void mac_create_mbuf_from_socket(struct socket *so, struct mbuf *m); void mac_create_socket(struct ucred *cred, struct socket *socket); void mac_create_socket_from_socket(struct socket *oldsocket, struct socket *newsocket); void mac_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct socket *socket); void mac_set_socket_peer_from_socket(struct socket *oldsocket, struct socket *newsocket); void mac_create_pipe(struct ucred *cred, struct pipe *pipe); /* * Labeling event operations: network objects. */ void mac_create_bpfdesc(struct ucred *cred, struct bpf_d *bpf_d); void mac_create_ifnet(struct ifnet *ifp); void mac_create_ipq(struct mbuf *fragment, struct ipq *ipq); void mac_create_datagram_from_ipq(struct ipq *ipq, struct mbuf *datagram); void mac_create_fragment(struct mbuf *datagram, struct mbuf *fragment); void mac_create_mbuf_from_mbuf(struct mbuf *oldmbuf, struct mbuf *newmbuf); void mac_create_mbuf_linklayer(struct ifnet *ifnet, struct mbuf *m); void mac_create_mbuf_from_bpfdesc(struct bpf_d *bpf_d, struct mbuf *m); void mac_create_mbuf_from_ifnet(struct ifnet *ifnet, struct mbuf *m); void mac_create_mbuf_multicast_encap(struct mbuf *oldmbuf, struct ifnet *ifnet, struct mbuf *newmbuf); void mac_create_mbuf_netlayer(struct mbuf *oldmbuf, struct mbuf *newmbuf); int mac_fragment_match(struct mbuf *fragment, struct ipq *ipq); void mac_update_ipq(struct mbuf *fragment, struct ipq *ipq); /* * Labeling event operations: processes. */ void mac_create_cred(struct ucred *cred_parent, struct ucred *cred_child); void mac_execve_transition(struct ucred *old, struct ucred *new, struct vnode *vp); int mac_execve_will_transition(struct ucred *old, struct vnode *vp); void mac_create_proc0(struct ucred *cred); void mac_create_proc1(struct ucred *cred); void mac_thread_userret(struct thread *td); /* Access control checks. */ int mac_check_bpfdesc_receive(struct bpf_d *bpf_d, struct ifnet *ifnet); int mac_check_cred_visible(struct ucred *u1, struct ucred *u2); int mac_check_ifnet_transmit(struct ifnet *ifnet, struct mbuf *m); int mac_check_mount_stat(struct ucred *cred, struct mount *mp); int mac_check_pipe_ioctl(struct ucred *cred, struct pipe *pipe, unsigned long cmd, void *data); int mac_check_pipe_poll(struct ucred *cred, struct pipe *pipe); int mac_check_pipe_read(struct ucred *cred, struct pipe *pipe); int mac_check_pipe_stat(struct ucred *cred, struct pipe *pipe); int mac_check_pipe_write(struct ucred *cred, struct pipe *pipe); int mac_check_proc_debug(struct ucred *cred, struct proc *proc); int mac_check_proc_sched(struct ucred *cred, struct proc *proc); int mac_check_proc_signal(struct ucred *cred, struct proc *proc, int signum); int mac_check_socket_bind(struct ucred *cred, struct socket *so, struct sockaddr *sockaddr); int mac_check_socket_connect(struct ucred *cred, struct socket *so, struct sockaddr *sockaddr); int mac_check_socket_deliver(struct socket *so, struct mbuf *m); int mac_check_socket_listen(struct ucred *cred, struct socket *so); int mac_check_socket_receive(struct ucred *cred, struct socket *so); int mac_check_socket_send(struct ucred *cred, struct socket *so); int mac_check_socket_visible(struct ucred *cred, struct socket *so); int mac_check_system_reboot(struct ucred *cred, int howto); int mac_check_system_swapon(struct ucred *cred, struct vnode *vp); +int mac_check_system_sysctl(struct ucred *cred, int *name, + u_int namelen, void *old, size_t *oldlenp, int inkernel, + void *new, size_t newlen); int mac_check_vnode_access(struct ucred *cred, struct vnode *vp, int flags); int mac_check_vnode_chdir(struct ucred *cred, struct vnode *dvp); int mac_check_vnode_chroot(struct ucred *cred, struct vnode *dvp); int mac_check_vnode_create(struct ucred *cred, struct vnode *dvp, struct componentname *cnp, struct vattr *vap); int mac_check_vnode_delete(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp); int mac_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp, acl_type_t type); int mac_check_vnode_exec(struct ucred *cred, struct vnode *vp); int mac_check_vnode_getacl(struct ucred *cred, struct vnode *vp, acl_type_t type); int mac_check_vnode_getextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio); int mac_check_vnode_link(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp); int mac_check_vnode_lookup(struct ucred *cred, struct vnode *dvp, struct componentname *cnp); int mac_check_vnode_mmap(struct ucred *cred, struct vnode *vp, int prot); int mac_check_vnode_mprotect(struct ucred *cred, struct vnode *vp, int prot); int mac_check_vnode_open(struct ucred *cred, struct vnode *vp, mode_t acc_mode); int mac_check_vnode_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp); int mac_check_vnode_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp); int mac_check_vnode_readdir(struct ucred *cred, struct vnode *vp); int mac_check_vnode_readlink(struct ucred *cred, struct vnode *vp); int mac_check_vnode_rename_from(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp); int mac_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp, struct vnode *vp, int samedir, struct componentname *cnp); int mac_check_vnode_revoke(struct ucred *cred, struct vnode *vp); int mac_check_vnode_setacl(struct ucred *cred, struct vnode *vp, acl_type_t type, struct acl *acl); int mac_check_vnode_setextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio); int mac_check_vnode_setflags(struct ucred *cred, struct vnode *vp, u_long flags); int mac_check_vnode_setmode(struct ucred *cred, struct vnode *vp, mode_t mode); int mac_check_vnode_setowner(struct ucred *cred, struct vnode *vp, uid_t uid, gid_t gid); int mac_check_vnode_setutimes(struct ucred *cred, struct vnode *vp, struct timespec atime, struct timespec mtime); int mac_check_vnode_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp); int mac_check_vnode_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp); int mac_getsockopt_label_get(struct ucred *cred, struct socket *so, struct mac *extmac); int mac_getsockopt_peerlabel_get(struct ucred *cred, struct socket *so, struct mac *extmac); int mac_ioctl_ifnet_get(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet); int mac_ioctl_ifnet_set(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet); int mac_setsockopt_label_set(struct ucred *cred, struct socket *so, struct mac *extmac); int mac_pipe_label_set(struct ucred *cred, struct pipe *pipe, struct label *label); /* * Calls to help various file systems implement labeling functionality * using their existing EA implementation. */ int vop_stdsetlabel_ea(struct vop_setlabel_args *ap); #endif /* _KERNEL */ #endif /* !_SYS_MAC_H */ Index: head/sys/security/mac/mac_internal.h =================================================================== --- head/sys/security/mac/mac_internal.h (revision 106024) +++ head/sys/security/mac/mac_internal.h (revision 106025) @@ -1,4010 +1,4038 @@ /*- * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson * Copyright (c) 2001 Ilmar S. Habibulin * Copyright (c) 2001, 2002 Networks Associates Technology, 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 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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. * * Framework for extensible kernel access control. Kernel and userland * interface to the framework, policy registration and composition. */ #include "opt_mac.h" #include "opt_devfs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MAC /* * Declare that the kernel provides MAC support, version 1. 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, 1); SYSCTL_DECL(_security); SYSCTL_NODE(_security, OID_AUTO, mac, CTLFLAG_RW, 0, "TrustedBSD MAC policy controls"); #if MAC_MAX_POLICIES > 32 #error "MAC_MAX_POLICIES too large" #endif static unsigned int mac_max_policies = MAC_MAX_POLICIES; static unsigned int mac_policy_offsets_free = (1 << MAC_MAX_POLICIES) - 1; SYSCTL_UINT(_security_mac, OID_AUTO, max_policies, CTLFLAG_RD, &mac_max_policies, 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; /* * Warn about EA transactions only the first time they happen. * Weak coherency, no locking. */ static int ea_warn_once = 0; static int mac_enforce_fs = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_fs, CTLFLAG_RW, &mac_enforce_fs, 0, "Enforce MAC policy on file system objects"); TUNABLE_INT("security.mac.enforce_fs", &mac_enforce_fs); static int mac_enforce_network = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_network, CTLFLAG_RW, &mac_enforce_network, 0, "Enforce MAC policy on network packets"); TUNABLE_INT("security.mac.enforce_network", &mac_enforce_network); static int mac_enforce_pipe = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_pipe, CTLFLAG_RW, &mac_enforce_pipe, 0, "Enforce MAC policy on pipe operations"); TUNABLE_INT("security.mac.enforce_pipe", &mac_enforce_pipe); static int mac_enforce_process = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_process, CTLFLAG_RW, &mac_enforce_process, 0, "Enforce MAC policy on inter-process operations"); TUNABLE_INT("security.mac.enforce_process", &mac_enforce_process); static int mac_enforce_reboot = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_reboot, CTLFLAG_RW, &mac_enforce_reboot, 0, "Enforce MAC policy for reboot operations"); TUNABLE_INT("security.mac.enforce_reboot", &mac_enforce_reboot); static int mac_enforce_socket = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_socket, CTLFLAG_RW, &mac_enforce_socket, 0, "Enforce MAC policy on socket operations"); TUNABLE_INT("security.mac.enforce_socket", &mac_enforce_socket); +static int mac_enforce_sysctl = 1; +SYSCTL_INT(_security_mac, OID_AUTO, enforce_sysctl, CTLFLAG_RW, + &mac_enforce_sysctl, 0, "Enforce MAC policy on sysctl operations"); +TUNABLE_INT("security.mac.enforce_sysctl", &mac_enforce_sysctl); + static int mac_enforce_vm = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_vm, CTLFLAG_RW, &mac_enforce_vm, 0, "Enforce MAC policy on vm operations"); TUNABLE_INT("security.mac.enforce_vm", &mac_enforce_vm); static int mac_cache_fslabel_in_vnode = 1; SYSCTL_INT(_security_mac, OID_AUTO, cache_fslabel_in_vnode, CTLFLAG_RW, &mac_cache_fslabel_in_vnode, 0, "Cache mount fslabel in vnode"); TUNABLE_INT("security.mac.cache_fslabel_in_vnode", &mac_cache_fslabel_in_vnode); 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"); #ifdef MAC_DEBUG SYSCTL_NODE(_security_mac, OID_AUTO, debug, CTLFLAG_RW, 0, "TrustedBSD MAC debug info"); static int mac_debug_label_fallback = 0; SYSCTL_INT(_security_mac_debug, OID_AUTO, label_fallback, CTLFLAG_RW, &mac_debug_label_fallback, 0, "Filesystems should fall back to fs label" "when label is corrupted."); TUNABLE_INT("security.mac.debug_label_fallback", &mac_debug_label_fallback); SYSCTL_NODE(_security_mac_debug, OID_AUTO, counters, CTLFLAG_RW, 0, "TrustedBSD MAC object counters"); static unsigned int nmacmbufs, nmaccreds, nmacifnets, nmacbpfdescs, nmacsockets, nmacmounts, nmactemp, nmacvnodes, nmacdevfsdirents, nmacipqs, nmacpipes; SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, mbufs, CTLFLAG_RD, &nmacmbufs, 0, "number of mbufs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, creds, CTLFLAG_RD, &nmaccreds, 0, "number of ucreds in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ifnets, CTLFLAG_RD, &nmacifnets, 0, "number of ifnets in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ipqs, CTLFLAG_RD, &nmacipqs, 0, "number of ipqs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, bpfdescs, CTLFLAG_RD, &nmacbpfdescs, 0, "number of bpfdescs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, sockets, CTLFLAG_RD, &nmacsockets, 0, "number of sockets in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, pipes, CTLFLAG_RD, &nmacpipes, 0, "number of pipes in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, mounts, CTLFLAG_RD, &nmacmounts, 0, "number of mounts in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, temp, CTLFLAG_RD, &nmactemp, 0, "number of temporary labels in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, vnodes, CTLFLAG_RD, &nmacvnodes, 0, "number of vnodes in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, devfsdirents, CTLFLAG_RD, &nmacdevfsdirents, 0, "number of devfs dirents inuse"); #endif static int error_select(int error1, int error2); static int mac_policy_register(struct mac_policy_conf *mpc); static int mac_policy_unregister(struct mac_policy_conf *mpc); static void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot); static void mac_cred_mmapped_drop_perms_recurse(struct thread *td, struct ucred *cred, struct vm_map *map); static void mac_destroy_socket_label(struct label *label); static int mac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *intlabel); MALLOC_DEFINE(M_MACOPVEC, "macopvec", "MAC policy operation vector"); MALLOC_DEFINE(M_MACPIPELABEL, "macpipelabel", "MAC labels for pipes"); MALLOC_DEFINE(M_MACTEMP, "mactemp", "MAC temporary label storage"); /* * mac_policy_list_lock protects the consistency of 'mac_policy_list', * the linked list of attached policy modules. Read-only consumers of * the list must acquire a shared lock for the duration of their use; * writers must acquire an exclusive lock. Note that for compound * operations, locks should be held for the entire compound operation, * and that this is not yet done for relabel requests. */ static struct mtx mac_policy_list_lock; static LIST_HEAD(, mac_policy_conf) mac_policy_list; static int mac_policy_list_busy; #define MAC_POLICY_LIST_LOCKINIT() mtx_init(&mac_policy_list_lock, \ "mac_policy_list_lock", NULL, MTX_DEF); #define MAC_POLICY_LIST_LOCK() mtx_lock(&mac_policy_list_lock); #define MAC_POLICY_LIST_UNLOCK() mtx_unlock(&mac_policy_list_lock); #define MAC_POLICY_LIST_BUSY() do { \ MAC_POLICY_LIST_LOCK(); \ mac_policy_list_busy++; \ MAC_POLICY_LIST_UNLOCK(); \ } while (0) #define MAC_POLICY_LIST_UNBUSY() do { \ MAC_POLICY_LIST_LOCK(); \ mac_policy_list_busy--; \ if (mac_policy_list_busy < 0) \ panic("Extra mac_policy_list_busy--"); \ MAC_POLICY_LIST_UNLOCK(); \ } while (0) /* * MAC_CHECK performs the designated check by walking the policy * module list and checking with each as to how it feels about the * request. Note that it returns its value via 'error' in the scope * of the caller. */ #define MAC_CHECK(check, args...) do { \ struct mac_policy_conf *mpc; \ \ error = 0; \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## check != NULL) \ error = error_select( \ mpc->mpc_ops->mpo_ ## check (args), \ error); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) /* * MAC_BOOLEAN performs the designated boolean composition by walking * the module list, invoking each instance of the operation, and * combining the results using the passed C operator. Note that it * returns its value via 'result' in the scope of the caller, which * should be initialized by the caller in a meaningful way to get * a meaningful result. */ #define MAC_BOOLEAN(operation, composition, args...) do { \ struct mac_policy_conf *mpc; \ \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ result = result composition \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) #define MAC_EXTERNALIZE(type, label, elementlist, outbuf, \ outbuflen) do { \ char *curptr, *curptr_start, *element_name, *element_temp; \ size_t left, left_start, len; \ int claimed, first, first_start, ignorenotfound; \ \ error = 0; \ element_temp = elementlist; \ curptr = outbuf; \ curptr[0] = '\0'; \ left = outbuflen; \ first = 1; \ while ((element_name = strsep(&element_temp, ",")) != NULL) { \ curptr_start = curptr; \ left_start = left; \ first_start = first; \ if (element_name[0] == '?') { \ element_name++; \ ignorenotfound = 1; \ } else \ ignorenotfound = 0; \ claimed = 0; \ if (first) { \ len = snprintf(curptr, left, "%s/", \ element_name); \ first = 0; \ } else \ len = snprintf(curptr, left, ",%s/", \ element_name); \ if (len >= left) { \ error = EINVAL; /* XXXMAC: E2BIG */ \ break; \ } \ curptr += len; \ left -= len; \ \ MAC_CHECK(externalize_ ## type, label, element_name, \ curptr, left, &len, &claimed); \ if (error) \ break; \ if (claimed == 1) { \ if (len >= outbuflen) { \ error = EINVAL; /* XXXMAC: E2BIG */ \ break; \ } \ curptr += len; \ left -= len; \ } else if (claimed == 0 && ignorenotfound) { \ /* \ * Revert addition of the label element \ * name. \ */ \ curptr = curptr_start; \ *curptr = '\0'; \ left = left_start; \ first = first_start; \ } else { \ error = EINVAL; /* XXXMAC: ENOLABEL */ \ break; \ } \ } \ } while (0) #define MAC_INTERNALIZE(type, label, instring) do { \ char *element, *element_name, *element_data; \ int claimed; \ \ error = 0; \ element = instring; \ while ((element_name = strsep(&element, ",")) != NULL) { \ element_data = element_name; \ element_name = strsep(&element_data, "/"); \ if (element_data == NULL) { \ error = EINVAL; \ break; \ } \ claimed = 0; \ MAC_CHECK(internalize_ ## type, label, element_name, \ element_data, &claimed); \ if (error) \ break; \ if (claimed != 1) { \ /* XXXMAC: Another error here? */ \ error = EINVAL; \ break; \ } \ } \ } while (0) /* * MAC_PERFORM performs the designated operation by walking the policy * module list and invoking that operation for each policy. */ #define MAC_PERFORM(operation, args...) do { \ struct mac_policy_conf *mpc; \ \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) /* * Initialize the MAC subsystem, including appropriate SMP locks. */ static void mac_init(void) { LIST_INIT(&mac_policy_list); MAC_POLICY_LIST_LOCKINIT(); } /* * 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; } /* * 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; 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: break; } return (error); } static int mac_policy_register(struct mac_policy_conf *mpc) { struct mac_policy_conf *tmpc; struct mac_policy_op_entry *mpe; int slot; MALLOC(mpc->mpc_ops, struct mac_policy_ops *, sizeof(*mpc->mpc_ops), M_MACOPVEC, M_WAITOK | M_ZERO); for (mpe = mpc->mpc_entries; mpe->mpe_constant != MAC_OP_LAST; mpe++) { switch (mpe->mpe_constant) { case MAC_OP_LAST: /* * Doesn't actually happen, but this allows checking * that all enumerated values are handled. */ break; case MAC_DESTROY: mpc->mpc_ops->mpo_destroy = mpe->mpe_function; break; case MAC_INIT: mpc->mpc_ops->mpo_init = mpe->mpe_function; break; case MAC_SYSCALL: mpc->mpc_ops->mpo_syscall = mpe->mpe_function; break; case MAC_INIT_BPFDESC_LABEL: mpc->mpc_ops->mpo_init_bpfdesc_label = mpe->mpe_function; break; case MAC_INIT_CRED_LABEL: mpc->mpc_ops->mpo_init_cred_label = mpe->mpe_function; break; case MAC_INIT_DEVFSDIRENT_LABEL: mpc->mpc_ops->mpo_init_devfsdirent_label = mpe->mpe_function; break; case MAC_INIT_IFNET_LABEL: mpc->mpc_ops->mpo_init_ifnet_label = mpe->mpe_function; break; case MAC_INIT_IPQ_LABEL: mpc->mpc_ops->mpo_init_ipq_label = mpe->mpe_function; break; case MAC_INIT_MBUF_LABEL: mpc->mpc_ops->mpo_init_mbuf_label = mpe->mpe_function; break; case MAC_INIT_MOUNT_LABEL: mpc->mpc_ops->mpo_init_mount_label = mpe->mpe_function; break; case MAC_INIT_MOUNT_FS_LABEL: mpc->mpc_ops->mpo_init_mount_fs_label = mpe->mpe_function; break; case MAC_INIT_PIPE_LABEL: mpc->mpc_ops->mpo_init_pipe_label = mpe->mpe_function; break; case MAC_INIT_SOCKET_LABEL: mpc->mpc_ops->mpo_init_socket_label = mpe->mpe_function; break; case MAC_INIT_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_init_socket_peer_label = mpe->mpe_function; break; case MAC_INIT_VNODE_LABEL: mpc->mpc_ops->mpo_init_vnode_label = mpe->mpe_function; break; case MAC_DESTROY_BPFDESC_LABEL: mpc->mpc_ops->mpo_destroy_bpfdesc_label = mpe->mpe_function; break; case MAC_DESTROY_CRED_LABEL: mpc->mpc_ops->mpo_destroy_cred_label = mpe->mpe_function; break; case MAC_DESTROY_DEVFSDIRENT_LABEL: mpc->mpc_ops->mpo_destroy_devfsdirent_label = mpe->mpe_function; break; case MAC_DESTROY_IFNET_LABEL: mpc->mpc_ops->mpo_destroy_ifnet_label = mpe->mpe_function; break; case MAC_DESTROY_IPQ_LABEL: mpc->mpc_ops->mpo_destroy_ipq_label = mpe->mpe_function; break; case MAC_DESTROY_MBUF_LABEL: mpc->mpc_ops->mpo_destroy_mbuf_label = mpe->mpe_function; break; case MAC_DESTROY_MOUNT_LABEL: mpc->mpc_ops->mpo_destroy_mount_label = mpe->mpe_function; break; case MAC_DESTROY_MOUNT_FS_LABEL: mpc->mpc_ops->mpo_destroy_mount_fs_label = mpe->mpe_function; break; case MAC_DESTROY_PIPE_LABEL: mpc->mpc_ops->mpo_destroy_pipe_label = mpe->mpe_function; break; case MAC_DESTROY_SOCKET_LABEL: mpc->mpc_ops->mpo_destroy_socket_label = mpe->mpe_function; break; case MAC_DESTROY_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_destroy_socket_peer_label = mpe->mpe_function; break; case MAC_DESTROY_VNODE_LABEL: mpc->mpc_ops->mpo_destroy_vnode_label = mpe->mpe_function; break; case MAC_COPY_PIPE_LABEL: mpc->mpc_ops->mpo_copy_pipe_label = mpe->mpe_function; break; case MAC_COPY_VNODE_LABEL: mpc->mpc_ops->mpo_copy_vnode_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_CRED_LABEL: mpc->mpc_ops->mpo_externalize_cred_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_IFNET_LABEL: mpc->mpc_ops->mpo_externalize_ifnet_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_PIPE_LABEL: mpc->mpc_ops->mpo_externalize_pipe_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_SOCKET_LABEL: mpc->mpc_ops->mpo_externalize_socket_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_externalize_socket_peer_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_VNODE_LABEL: mpc->mpc_ops->mpo_externalize_vnode_label = mpe->mpe_function; break; case MAC_INTERNALIZE_CRED_LABEL: mpc->mpc_ops->mpo_internalize_cred_label = mpe->mpe_function; break; case MAC_INTERNALIZE_IFNET_LABEL: mpc->mpc_ops->mpo_internalize_ifnet_label = mpe->mpe_function; break; case MAC_INTERNALIZE_PIPE_LABEL: mpc->mpc_ops->mpo_internalize_pipe_label = mpe->mpe_function; break; case MAC_INTERNALIZE_SOCKET_LABEL: mpc->mpc_ops->mpo_internalize_socket_label = mpe->mpe_function; break; case MAC_INTERNALIZE_VNODE_LABEL: mpc->mpc_ops->mpo_internalize_vnode_label = mpe->mpe_function; break; case MAC_CREATE_DEVFS_DEVICE: mpc->mpc_ops->mpo_create_devfs_device = mpe->mpe_function; break; case MAC_CREATE_DEVFS_DIRECTORY: mpc->mpc_ops->mpo_create_devfs_directory = mpe->mpe_function; break; case MAC_CREATE_DEVFS_SYMLINK: mpc->mpc_ops->mpo_create_devfs_symlink = mpe->mpe_function; break; case MAC_CREATE_DEVFS_VNODE: mpc->mpc_ops->mpo_create_devfs_vnode = mpe->mpe_function; break; case MAC_CREATE_MOUNT: mpc->mpc_ops->mpo_create_mount = mpe->mpe_function; break; case MAC_CREATE_ROOT_MOUNT: mpc->mpc_ops->mpo_create_root_mount = mpe->mpe_function; break; case MAC_RELABEL_VNODE: mpc->mpc_ops->mpo_relabel_vnode = mpe->mpe_function; break; case MAC_UPDATE_DEVFSDIRENT: mpc->mpc_ops->mpo_update_devfsdirent = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_DEVFS: mpc->mpc_ops->mpo_associate_vnode_devfs = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_EXTATTR: mpc->mpc_ops->mpo_associate_vnode_extattr = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_SINGLELABEL: mpc->mpc_ops->mpo_associate_vnode_singlelabel = mpe->mpe_function; break; case MAC_CREATE_VNODE_EXTATTR: mpc->mpc_ops->mpo_create_vnode_extattr = mpe->mpe_function; break; case MAC_SETLABEL_VNODE_EXTATTR: mpc->mpc_ops->mpo_setlabel_vnode_extattr = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_SOCKET: mpc->mpc_ops->mpo_create_mbuf_from_socket = mpe->mpe_function; break; case MAC_CREATE_PIPE: mpc->mpc_ops->mpo_create_pipe = mpe->mpe_function; break; case MAC_CREATE_SOCKET: mpc->mpc_ops->mpo_create_socket = mpe->mpe_function; break; case MAC_CREATE_SOCKET_FROM_SOCKET: mpc->mpc_ops->mpo_create_socket_from_socket = mpe->mpe_function; break; case MAC_RELABEL_PIPE: mpc->mpc_ops->mpo_relabel_pipe = mpe->mpe_function; break; case MAC_RELABEL_SOCKET: mpc->mpc_ops->mpo_relabel_socket = mpe->mpe_function; break; case MAC_SET_SOCKET_PEER_FROM_MBUF: mpc->mpc_ops->mpo_set_socket_peer_from_mbuf = mpe->mpe_function; break; case MAC_SET_SOCKET_PEER_FROM_SOCKET: mpc->mpc_ops->mpo_set_socket_peer_from_socket = mpe->mpe_function; break; case MAC_CREATE_BPFDESC: mpc->mpc_ops->mpo_create_bpfdesc = mpe->mpe_function; break; case MAC_CREATE_DATAGRAM_FROM_IPQ: mpc->mpc_ops->mpo_create_datagram_from_ipq = mpe->mpe_function; break; case MAC_CREATE_FRAGMENT: mpc->mpc_ops->mpo_create_fragment = mpe->mpe_function; break; case MAC_CREATE_IFNET: mpc->mpc_ops->mpo_create_ifnet = mpe->mpe_function; break; case MAC_CREATE_IPQ: mpc->mpc_ops->mpo_create_ipq = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_MBUF: mpc->mpc_ops->mpo_create_mbuf_from_mbuf = mpe->mpe_function; break; case MAC_CREATE_MBUF_LINKLAYER: mpc->mpc_ops->mpo_create_mbuf_linklayer = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_BPFDESC: mpc->mpc_ops->mpo_create_mbuf_from_bpfdesc = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_IFNET: mpc->mpc_ops->mpo_create_mbuf_from_ifnet = mpe->mpe_function; break; case MAC_CREATE_MBUF_MULTICAST_ENCAP: mpc->mpc_ops->mpo_create_mbuf_multicast_encap = mpe->mpe_function; break; case MAC_CREATE_MBUF_NETLAYER: mpc->mpc_ops->mpo_create_mbuf_netlayer = mpe->mpe_function; break; case MAC_FRAGMENT_MATCH: mpc->mpc_ops->mpo_fragment_match = mpe->mpe_function; break; case MAC_RELABEL_IFNET: mpc->mpc_ops->mpo_relabel_ifnet = mpe->mpe_function; break; case MAC_UPDATE_IPQ: mpc->mpc_ops->mpo_update_ipq = mpe->mpe_function; break; case MAC_CREATE_CRED: mpc->mpc_ops->mpo_create_cred = mpe->mpe_function; break; case MAC_EXECVE_TRANSITION: mpc->mpc_ops->mpo_execve_transition = mpe->mpe_function; break; case MAC_EXECVE_WILL_TRANSITION: mpc->mpc_ops->mpo_execve_will_transition = mpe->mpe_function; break; case MAC_CREATE_PROC0: mpc->mpc_ops->mpo_create_proc0 = mpe->mpe_function; break; case MAC_CREATE_PROC1: mpc->mpc_ops->mpo_create_proc1 = mpe->mpe_function; break; case MAC_RELABEL_CRED: mpc->mpc_ops->mpo_relabel_cred = mpe->mpe_function; break; case MAC_THREAD_USERRET: mpc->mpc_ops->mpo_thread_userret = mpe->mpe_function; break; case MAC_CHECK_BPFDESC_RECEIVE: mpc->mpc_ops->mpo_check_bpfdesc_receive = mpe->mpe_function; break; case MAC_CHECK_CRED_RELABEL: mpc->mpc_ops->mpo_check_cred_relabel = mpe->mpe_function; break; case MAC_CHECK_CRED_VISIBLE: mpc->mpc_ops->mpo_check_cred_visible = mpe->mpe_function; break; case MAC_CHECK_IFNET_RELABEL: mpc->mpc_ops->mpo_check_ifnet_relabel = mpe->mpe_function; break; case MAC_CHECK_IFNET_TRANSMIT: mpc->mpc_ops->mpo_check_ifnet_transmit = mpe->mpe_function; break; case MAC_CHECK_MOUNT_STAT: mpc->mpc_ops->mpo_check_mount_stat = mpe->mpe_function; break; case MAC_CHECK_PIPE_IOCTL: mpc->mpc_ops->mpo_check_pipe_ioctl = mpe->mpe_function; break; case MAC_CHECK_PIPE_POLL: mpc->mpc_ops->mpo_check_pipe_poll = mpe->mpe_function; break; case MAC_CHECK_PIPE_READ: mpc->mpc_ops->mpo_check_pipe_read = mpe->mpe_function; break; case MAC_CHECK_PIPE_RELABEL: mpc->mpc_ops->mpo_check_pipe_relabel = mpe->mpe_function; break; case MAC_CHECK_PIPE_STAT: mpc->mpc_ops->mpo_check_pipe_stat = mpe->mpe_function; break; case MAC_CHECK_PIPE_WRITE: mpc->mpc_ops->mpo_check_pipe_write = mpe->mpe_function; break; case MAC_CHECK_PROC_DEBUG: mpc->mpc_ops->mpo_check_proc_debug = mpe->mpe_function; break; case MAC_CHECK_PROC_SCHED: mpc->mpc_ops->mpo_check_proc_sched = mpe->mpe_function; break; case MAC_CHECK_PROC_SIGNAL: mpc->mpc_ops->mpo_check_proc_signal = mpe->mpe_function; break; case MAC_CHECK_SOCKET_BIND: mpc->mpc_ops->mpo_check_socket_bind = mpe->mpe_function; break; case MAC_CHECK_SOCKET_CONNECT: mpc->mpc_ops->mpo_check_socket_connect = mpe->mpe_function; break; case MAC_CHECK_SOCKET_DELIVER: mpc->mpc_ops->mpo_check_socket_deliver = mpe->mpe_function; break; case MAC_CHECK_SOCKET_LISTEN: mpc->mpc_ops->mpo_check_socket_listen = mpe->mpe_function; break; case MAC_CHECK_SOCKET_RECEIVE: mpc->mpc_ops->mpo_check_socket_receive = mpe->mpe_function; break; case MAC_CHECK_SOCKET_RELABEL: mpc->mpc_ops->mpo_check_socket_relabel = mpe->mpe_function; break; case MAC_CHECK_SOCKET_SEND: mpc->mpc_ops->mpo_check_socket_send = mpe->mpe_function; break; case MAC_CHECK_SOCKET_VISIBLE: mpc->mpc_ops->mpo_check_socket_visible = mpe->mpe_function; break; case MAC_CHECK_SYSTEM_REBOOT: mpc->mpc_ops->mpo_check_system_reboot = mpe->mpe_function; break; case MAC_CHECK_SYSTEM_SWAPON: mpc->mpc_ops->mpo_check_system_swapon = mpe->mpe_function; break; + case MAC_CHECK_SYSTEM_SYSCTL: + mpc->mpc_ops->mpo_check_system_sysctl = + mpe->mpe_function; + break; case MAC_CHECK_VNODE_ACCESS: mpc->mpc_ops->mpo_check_vnode_access = mpe->mpe_function; break; case MAC_CHECK_VNODE_CHDIR: mpc->mpc_ops->mpo_check_vnode_chdir = mpe->mpe_function; break; case MAC_CHECK_VNODE_CHROOT: mpc->mpc_ops->mpo_check_vnode_chroot = mpe->mpe_function; break; case MAC_CHECK_VNODE_CREATE: mpc->mpc_ops->mpo_check_vnode_create = mpe->mpe_function; break; case MAC_CHECK_VNODE_DELETE: mpc->mpc_ops->mpo_check_vnode_delete = mpe->mpe_function; break; case MAC_CHECK_VNODE_DELETEACL: mpc->mpc_ops->mpo_check_vnode_deleteacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_EXEC: mpc->mpc_ops->mpo_check_vnode_exec = mpe->mpe_function; break; case MAC_CHECK_VNODE_GETACL: mpc->mpc_ops->mpo_check_vnode_getacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_GETEXTATTR: mpc->mpc_ops->mpo_check_vnode_getextattr = mpe->mpe_function; break; case MAC_CHECK_VNODE_LINK: mpc->mpc_ops->mpo_check_vnode_link = mpe->mpe_function; break; case MAC_CHECK_VNODE_LOOKUP: mpc->mpc_ops->mpo_check_vnode_lookup = mpe->mpe_function; break; case MAC_CHECK_VNODE_MMAP: mpc->mpc_ops->mpo_check_vnode_mmap = mpe->mpe_function; break; case MAC_CHECK_VNODE_MMAP_DOWNGRADE: mpc->mpc_ops->mpo_check_vnode_mmap_downgrade = mpe->mpe_function; break; case MAC_CHECK_VNODE_MPROTECT: mpc->mpc_ops->mpo_check_vnode_mprotect = mpe->mpe_function; break; case MAC_CHECK_VNODE_OPEN: mpc->mpc_ops->mpo_check_vnode_open = mpe->mpe_function; break; case MAC_CHECK_VNODE_POLL: mpc->mpc_ops->mpo_check_vnode_poll = mpe->mpe_function; break; case MAC_CHECK_VNODE_READ: mpc->mpc_ops->mpo_check_vnode_read = mpe->mpe_function; break; case MAC_CHECK_VNODE_READDIR: mpc->mpc_ops->mpo_check_vnode_readdir = mpe->mpe_function; break; case MAC_CHECK_VNODE_READLINK: mpc->mpc_ops->mpo_check_vnode_readlink = mpe->mpe_function; break; case MAC_CHECK_VNODE_RELABEL: mpc->mpc_ops->mpo_check_vnode_relabel = mpe->mpe_function; break; case MAC_CHECK_VNODE_RENAME_FROM: mpc->mpc_ops->mpo_check_vnode_rename_from = mpe->mpe_function; break; case MAC_CHECK_VNODE_RENAME_TO: mpc->mpc_ops->mpo_check_vnode_rename_to = mpe->mpe_function; break; case MAC_CHECK_VNODE_REVOKE: mpc->mpc_ops->mpo_check_vnode_revoke = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETACL: mpc->mpc_ops->mpo_check_vnode_setacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETEXTATTR: mpc->mpc_ops->mpo_check_vnode_setextattr = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETFLAGS: mpc->mpc_ops->mpo_check_vnode_setflags = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETMODE: mpc->mpc_ops->mpo_check_vnode_setmode = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETOWNER: mpc->mpc_ops->mpo_check_vnode_setowner = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETUTIMES: mpc->mpc_ops->mpo_check_vnode_setutimes = mpe->mpe_function; break; case MAC_CHECK_VNODE_STAT: mpc->mpc_ops->mpo_check_vnode_stat = mpe->mpe_function; break; case MAC_CHECK_VNODE_WRITE: mpc->mpc_ops->mpo_check_vnode_write = mpe->mpe_function; break; /* default: printf("MAC policy `%s': unknown operation %d\n", mpc->mpc_name, mpe->mpe_constant); return (EINVAL); */ } } MAC_POLICY_LIST_LOCK(); if (mac_policy_list_busy > 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (EBUSY); } LIST_FOREACH(tmpc, &mac_policy_list, mpc_list) { if (strcmp(tmpc->mpc_name, mpc->mpc_name) == 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (EEXIST); } } if (mpc->mpc_field_off != NULL) { slot = ffs(mac_policy_offsets_free); if (slot == 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (ENOMEM); } slot--; mac_policy_offsets_free &= ~(1 << slot); *mpc->mpc_field_off = slot; } mpc->mpc_runtime_flags |= MPC_RUNTIME_FLAG_REGISTERED; LIST_INSERT_HEAD(&mac_policy_list, mpc, mpc_list); /* Per-policy initialization. */ if (mpc->mpc_ops->mpo_init != NULL) (*(mpc->mpc_ops->mpo_init))(mpc); MAC_POLICY_LIST_UNLOCK(); printf("Security policy loaded: %s (%s)\n", mpc->mpc_fullname, mpc->mpc_name); return (0); } 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_LIST_LOCK(); if ((mpc->mpc_runtime_flags & MPC_RUNTIME_FLAG_REGISTERED) == 0) { MAC_POLICY_LIST_UNLOCK(); return (0); } #if 0 /* * Don't allow unloading modules with private data. */ if (mpc->mpc_field_off != NULL) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } #endif /* * Only allow the unload to proceed if the module is unloadable * by its own definition. */ if ((mpc->mpc_loadtime_flags & MPC_LOADTIME_FLAG_UNLOADOK) == 0) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } /* * Right now, we EBUSY if the list is in use. In the future, * for reliability reasons, we might want to sleep and wakeup * later to try again. */ if (mac_policy_list_busy > 0) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } if (mpc->mpc_ops->mpo_destroy != NULL) (*(mpc->mpc_ops->mpo_destroy))(mpc); LIST_REMOVE(mpc, mpc_list); MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; mpc->mpc_runtime_flags &= ~MPC_RUNTIME_FLAG_REGISTERED; printf("Security policy unload: %s (%s)\n", mpc->mpc_fullname, mpc->mpc_name); return (0); } /* * Define an error value precedence, and given two arguments, selects the * value with the higher precedence. */ static int 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); } static void mac_init_label(struct label *label) { bzero(label, sizeof(*label)); label->l_flags = MAC_FLAG_INITIALIZED; } static void mac_destroy_label(struct label *label) { KASSERT(label->l_flags & MAC_FLAG_INITIALIZED, ("destroying uninitialized label")); bzero(label, sizeof(*label)); /* implicit: label->l_flags &= ~MAC_FLAG_INITIALIZED; */ } void mac_init_bpfdesc(struct bpf_d *bpf_d) { mac_init_label(&bpf_d->bd_label); MAC_PERFORM(init_bpfdesc_label, &bpf_d->bd_label); #ifdef MAC_DEBUG atomic_add_int(&nmacbpfdescs, 1); #endif } static void mac_init_cred_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_cred_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmaccreds, 1); #endif } void mac_init_cred(struct ucred *cred) { mac_init_cred_label(&cred->cr_label); } void mac_init_devfsdirent(struct devfs_dirent *de) { mac_init_label(&de->de_label); MAC_PERFORM(init_devfsdirent_label, &de->de_label); #ifdef MAC_DEBUG atomic_add_int(&nmacdevfsdirents, 1); #endif } static void mac_init_ifnet_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_ifnet_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacifnets, 1); #endif } void mac_init_ifnet(struct ifnet *ifp) { mac_init_ifnet_label(&ifp->if_label); } void mac_init_ipq(struct ipq *ipq) { mac_init_label(&ipq->ipq_label); MAC_PERFORM(init_ipq_label, &ipq->ipq_label); #ifdef MAC_DEBUG atomic_add_int(&nmacipqs, 1); #endif } int mac_init_mbuf(struct mbuf *m, int flag) { int error; KASSERT(m->m_flags & M_PKTHDR, ("mac_init_mbuf on non-header mbuf")); mac_init_label(&m->m_pkthdr.label); MAC_CHECK(init_mbuf_label, &m->m_pkthdr.label, flag); if (error) { MAC_PERFORM(destroy_mbuf_label, &m->m_pkthdr.label); mac_destroy_label(&m->m_pkthdr.label); } #ifdef MAC_DEBUG if (error == 0) atomic_add_int(&nmacmbufs, 1); #endif return (error); } void mac_init_mount(struct mount *mp) { mac_init_label(&mp->mnt_mntlabel); mac_init_label(&mp->mnt_fslabel); MAC_PERFORM(init_mount_label, &mp->mnt_mntlabel); MAC_PERFORM(init_mount_fs_label, &mp->mnt_fslabel); #ifdef MAC_DEBUG atomic_add_int(&nmacmounts, 1); #endif } static void mac_init_pipe_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_pipe_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacpipes, 1); #endif } void mac_init_pipe(struct pipe *pipe) { struct label *label; label = malloc(sizeof(struct label), M_MACPIPELABEL, M_ZERO|M_WAITOK); pipe->pipe_label = label; pipe->pipe_peer->pipe_label = label; mac_init_pipe_label(label); } static int mac_init_socket_label(struct label *label, int flag) { int error; mac_init_label(label); MAC_CHECK(init_socket_label, label, flag); if (error) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); } #ifdef MAC_DEBUG if (error == 0) atomic_add_int(&nmacsockets, 1); #endif return (error); } static int mac_init_socket_peer_label(struct label *label, int flag) { int error; mac_init_label(label); MAC_CHECK(init_socket_peer_label, label, flag); if (error) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); } return (error); } int mac_init_socket(struct socket *socket, int flag) { int error; error = mac_init_socket_label(&socket->so_label, flag); if (error) return (error); error = mac_init_socket_peer_label(&socket->so_peerlabel, flag); if (error) mac_destroy_socket_label(&socket->so_label); return (error); } void mac_init_vnode_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_vnode_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacvnodes, 1); #endif } void mac_init_vnode(struct vnode *vp) { mac_init_vnode_label(&vp->v_label); } void mac_destroy_bpfdesc(struct bpf_d *bpf_d) { MAC_PERFORM(destroy_bpfdesc_label, &bpf_d->bd_label); mac_destroy_label(&bpf_d->bd_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacbpfdescs, 1); #endif } static void mac_destroy_cred_label(struct label *label) { MAC_PERFORM(destroy_cred_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmaccreds, 1); #endif } void mac_destroy_cred(struct ucred *cred) { mac_destroy_cred_label(&cred->cr_label); } void mac_destroy_devfsdirent(struct devfs_dirent *de) { MAC_PERFORM(destroy_devfsdirent_label, &de->de_label); mac_destroy_label(&de->de_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacdevfsdirents, 1); #endif } static void mac_destroy_ifnet_label(struct label *label) { MAC_PERFORM(destroy_ifnet_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacifnets, 1); #endif } void mac_destroy_ifnet(struct ifnet *ifp) { mac_destroy_ifnet_label(&ifp->if_label); } void mac_destroy_ipq(struct ipq *ipq) { MAC_PERFORM(destroy_ipq_label, &ipq->ipq_label); mac_destroy_label(&ipq->ipq_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacipqs, 1); #endif } void mac_destroy_mbuf(struct mbuf *m) { MAC_PERFORM(destroy_mbuf_label, &m->m_pkthdr.label); mac_destroy_label(&m->m_pkthdr.label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacmbufs, 1); #endif } void mac_destroy_mount(struct mount *mp) { MAC_PERFORM(destroy_mount_label, &mp->mnt_mntlabel); MAC_PERFORM(destroy_mount_fs_label, &mp->mnt_fslabel); mac_destroy_label(&mp->mnt_fslabel); mac_destroy_label(&mp->mnt_mntlabel); #ifdef MAC_DEBUG atomic_subtract_int(&nmacmounts, 1); #endif } static void mac_destroy_pipe_label(struct label *label) { MAC_PERFORM(destroy_pipe_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacpipes, 1); #endif } void mac_destroy_pipe(struct pipe *pipe) { mac_destroy_pipe_label(pipe->pipe_label); free(pipe->pipe_label, M_MACPIPELABEL); } static void mac_destroy_socket_label(struct label *label) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacsockets, 1); #endif } static void mac_destroy_socket_peer_label(struct label *label) { MAC_PERFORM(destroy_socket_peer_label, label); mac_destroy_label(label); } void mac_destroy_socket(struct socket *socket) { mac_destroy_socket_label(&socket->so_label); mac_destroy_socket_peer_label(&socket->so_peerlabel); } void mac_destroy_vnode_label(struct label *label) { MAC_PERFORM(destroy_vnode_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacvnodes, 1); #endif } void mac_destroy_vnode(struct vnode *vp) { mac_destroy_vnode_label(&vp->v_label); } static void mac_copy_pipe_label(struct label *src, struct label *dest) { MAC_PERFORM(copy_pipe_label, src, dest); } void mac_copy_vnode_label(struct label *src, struct label *dest) { MAC_PERFORM(copy_vnode_label, src, dest); } static int mac_check_structmac_consistent(struct mac *mac) { if (mac->m_buflen > MAC_MAX_LABEL_BUF_LEN) return (EINVAL); return (0); } static int mac_externalize_cred_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(cred_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_ifnet_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(ifnet_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_pipe_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(pipe_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_socket_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(socket_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_socket_peer_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(socket_peer_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_vnode_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(vnode_label, label, elements, outbuf, outbuflen); return (error); } static int mac_internalize_cred_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(cred_label, label, string); return (error); } static int mac_internalize_ifnet_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(ifnet_label, label, string); return (error); } static int mac_internalize_pipe_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(pipe_label, label, string); return (error); } static int mac_internalize_socket_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(socket_label, label, string); return (error); } static int mac_internalize_vnode_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(vnode_label, label, string); return (error); } /* * Initialize MAC label for the first kernel process, from which other * kernel processes and threads are spawned. */ void mac_create_proc0(struct ucred *cred) { MAC_PERFORM(create_proc0, cred); } /* * Initialize MAC label for the first userland process, from which other * userland processes and threads are spawned. */ void mac_create_proc1(struct ucred *cred) { MAC_PERFORM(create_proc1, cred); } void mac_thread_userret(struct thread *td) { MAC_PERFORM(thread_userret, td); } /* * 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_create_cred(struct ucred *parent_cred, struct ucred *child_cred) { MAC_PERFORM(create_cred, parent_cred, child_cred); } void mac_update_devfsdirent(struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(update_devfsdirent, de, &de->de_label, vp, &vp->v_label); } void mac_associate_vnode_devfs(struct mount *mp, struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(associate_vnode_devfs, mp, &mp->mnt_fslabel, de, &de->de_label, vp, &vp->v_label); } int mac_associate_vnode_extattr(struct mount *mp, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_associate_vnode_extattr"); MAC_CHECK(associate_vnode_extattr, mp, &mp->mnt_fslabel, vp, &vp->v_label); return (error); } void mac_associate_vnode_singlelabel(struct mount *mp, struct vnode *vp) { MAC_PERFORM(associate_vnode_singlelabel, mp, &mp->mnt_fslabel, vp, &vp->v_label); } int mac_create_vnode_extattr(struct ucred *cred, struct mount *mp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_create_vnode_extattr"); ASSERT_VOP_LOCKED(vp, "mac_create_vnode_extattr"); error = VOP_OPENEXTATTR(vp, cred, curthread); if (error == EOPNOTSUPP) { /* XXX: Optionally abort if transactions not supported. */ if (ea_warn_once == 0) { printf("Warning: transactions not supported " "in EA write.\n"); ea_warn_once = 1; } } else if (error) return (error); MAC_CHECK(create_vnode_extattr, cred, mp, &mp->mnt_fslabel, 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; /* XXX */ return (error); } static int mac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *intlabel) { int error; ASSERT_VOP_LOCKED(vp, "mac_setlabel_vnode_extattr"); error = VOP_OPENEXTATTR(vp, cred, curthread); if (error == EOPNOTSUPP) { /* XXX: Optionally abort if transactions not supported. */ if (ea_warn_once == 0) { printf("Warning: transactions not supported " "in EA write.\n"); ea_warn_once = 1; } } else if (error) return (error); MAC_CHECK(setlabel_vnode_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; /* XXX */ return (error); } void mac_execve_transition(struct ucred *old, struct ucred *new, struct vnode *vp) { ASSERT_VOP_LOCKED(vp, "mac_execve_transition"); MAC_PERFORM(execve_transition, old, new, vp, &vp->v_label); } int mac_execve_will_transition(struct ucred *old, struct vnode *vp) { int result; result = 0; MAC_BOOLEAN(execve_will_transition, ||, old, vp, &vp->v_label); return (result); } int mac_check_vnode_access(struct ucred *cred, struct vnode *vp, int flags) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_access"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_access, cred, vp, &vp->v_label, flags); return (error); } int mac_check_vnode_chdir(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_chdir"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_chdir, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_chroot(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_chroot"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_chroot, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_create(struct ucred *cred, struct vnode *dvp, struct componentname *cnp, struct vattr *vap) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_create"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_create, cred, dvp, &dvp->v_label, cnp, vap); return (error); } int mac_check_vnode_delete(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_delete"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_delete"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_delete, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp, acl_type_t type) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_deleteacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_deleteacl, cred, vp, &vp->v_label, type); return (error); } int mac_check_vnode_exec(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_exec"); if (!mac_enforce_process && !mac_enforce_fs) return (0); MAC_CHECK(check_vnode_exec, cred, vp, &vp->v_label); return (error); } int mac_check_vnode_getacl(struct ucred *cred, struct vnode *vp, acl_type_t type) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_getacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_getacl, cred, vp, &vp->v_label, type); return (error); } int mac_check_vnode_getextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_getextattr"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_getextattr, cred, vp, &vp->v_label, attrnamespace, name, uio); return (error); } int mac_check_vnode_link(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_link"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_link"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_link, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_lookup(struct ucred *cred, struct vnode *dvp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_lookup"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_lookup, cred, dvp, &dvp->v_label, cnp); return (error); } int mac_check_vnode_mmap(struct ucred *cred, struct vnode *vp, int prot) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mmap"); if (!mac_enforce_fs || !mac_enforce_vm) return (0); MAC_CHECK(check_vnode_mmap, cred, vp, &vp->v_label, prot); return (error); } void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot) { int result = *prot; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mmap_downgrade"); if (!mac_enforce_fs || !mac_enforce_vm) return; MAC_PERFORM(check_vnode_mmap_downgrade, cred, vp, &vp->v_label, &result); *prot = result; } int mac_check_vnode_mprotect(struct ucred *cred, struct vnode *vp, int prot) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mprotect"); if (!mac_enforce_fs || !mac_enforce_vm) return (0); MAC_CHECK(check_vnode_mprotect, cred, vp, &vp->v_label, prot); return (error); } int mac_check_vnode_open(struct ucred *cred, struct vnode *vp, mode_t acc_mode) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_open"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_open, cred, vp, &vp->v_label, acc_mode); return (error); } int mac_check_vnode_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_poll"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_poll, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_read"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_read, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_readdir(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_readdir"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_readdir, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_readlink(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_readlink"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_readlink, cred, vp, &vp->v_label); return (error); } static int mac_check_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *newlabel) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_relabel"); MAC_CHECK(check_vnode_relabel, cred, vp, &vp->v_label, newlabel); return (error); } int mac_check_vnode_rename_from(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_rename_from"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_rename_from"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_rename_from, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp, struct vnode *vp, int samedir, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_rename_to"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_rename_to"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_rename_to, cred, dvp, &dvp->v_label, vp, vp != NULL ? &vp->v_label : NULL, samedir, cnp); return (error); } int mac_check_vnode_revoke(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_revoke"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_revoke, cred, vp, &vp->v_label); return (error); } int mac_check_vnode_setacl(struct ucred *cred, struct vnode *vp, acl_type_t type, struct acl *acl) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setacl, cred, vp, &vp->v_label, type, acl); return (error); } int mac_check_vnode_setextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setextattr"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setextattr, cred, vp, &vp->v_label, attrnamespace, name, uio); return (error); } int mac_check_vnode_setflags(struct ucred *cred, struct vnode *vp, u_long flags) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setflags"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setflags, cred, vp, &vp->v_label, flags); return (error); } int mac_check_vnode_setmode(struct ucred *cred, struct vnode *vp, mode_t mode) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setmode"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setmode, cred, vp, &vp->v_label, mode); return (error); } int mac_check_vnode_setowner(struct ucred *cred, struct vnode *vp, uid_t uid, gid_t gid) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setowner"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setowner, cred, vp, &vp->v_label, uid, gid); return (error); } int mac_check_vnode_setutimes(struct ucred *cred, struct vnode *vp, struct timespec atime, struct timespec mtime) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setutimes"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setutimes, cred, vp, &vp->v_label, atime, mtime); return (error); } int mac_check_vnode_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_stat"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_stat, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_write"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_write, active_cred, file_cred, vp, &vp->v_label); return (error); } /* * 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. */ static void mac_cred_mmapped_drop_perms(struct thread *td, struct ucred *cred) { /* XXX freeze all other threads */ mac_cred_mmapped_drop_perms_recurse(td, cred, &td->td_proc->p_vmspace->vm_map); /* XXX allow other threads to continue */ } 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_cred_mmapped_drop_perms_recurse(struct thread *td, struct ucred *cred, struct vm_map *map) { struct vm_map_entry *vme; int result; vm_prot_t revokeperms; vm_object_t object; vm_ooffset_t offset; struct vnode *vp; if (!mac_mmap_revocation) return; vm_map_lock_read(map); for (vme = map->header.next; vme != &map->header; vme = vme->next) { if (vme->eflags & MAP_ENTRY_IS_SUB_MAP) { mac_cred_mmapped_drop_perms_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; while (object->backing_object != NULL) { object = object->backing_object; offset += object->backing_object_offset; } /* * 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, td); result = vme->max_protection; mac_check_vnode_mmap_downgrade(cred, vp, &result); VOP_UNLOCK(vp, 0, td); /* * 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)); vm_map_lock_upgrade(map); /* * 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); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); vm_object_page_clean(object, OFF_TO_IDX(offset), OFF_TO_IDX(offset + vme->end - vme->start + PAGE_MASK), OBJPC_SYNC); VOP_UNLOCK(vp, 0, td); 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_lock_downgrade(map); } vm_map_unlock_read(map); } /* * 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. */ static void mac_relabel_cred(struct ucred *cred, struct label *newlabel) { MAC_PERFORM(relabel_cred, cred, newlabel); } void mac_relabel_vnode(struct ucred *cred, struct vnode *vp, struct label *newlabel) { MAC_PERFORM(relabel_vnode, cred, vp, &vp->v_label, newlabel); } void mac_create_ifnet(struct ifnet *ifnet) { MAC_PERFORM(create_ifnet, ifnet, &ifnet->if_label); } void mac_create_bpfdesc(struct ucred *cred, struct bpf_d *bpf_d) { MAC_PERFORM(create_bpfdesc, cred, bpf_d, &bpf_d->bd_label); } void mac_create_socket(struct ucred *cred, struct socket *socket) { MAC_PERFORM(create_socket, cred, socket, &socket->so_label); } void mac_create_pipe(struct ucred *cred, struct pipe *pipe) { MAC_PERFORM(create_pipe, cred, pipe, pipe->pipe_label); } void mac_create_socket_from_socket(struct socket *oldsocket, struct socket *newsocket) { MAC_PERFORM(create_socket_from_socket, oldsocket, &oldsocket->so_label, newsocket, &newsocket->so_label); } static void mac_relabel_socket(struct ucred *cred, struct socket *socket, struct label *newlabel) { MAC_PERFORM(relabel_socket, cred, socket, &socket->so_label, newlabel); } static void mac_relabel_pipe(struct ucred *cred, struct pipe *pipe, struct label *newlabel) { MAC_PERFORM(relabel_pipe, cred, pipe, pipe->pipe_label, newlabel); } void mac_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct socket *socket) { MAC_PERFORM(set_socket_peer_from_mbuf, mbuf, &mbuf->m_pkthdr.label, socket, &socket->so_peerlabel); } void mac_set_socket_peer_from_socket(struct socket *oldsocket, struct socket *newsocket) { MAC_PERFORM(set_socket_peer_from_socket, oldsocket, &oldsocket->so_label, newsocket, &newsocket->so_peerlabel); } void mac_create_datagram_from_ipq(struct ipq *ipq, struct mbuf *datagram) { MAC_PERFORM(create_datagram_from_ipq, ipq, &ipq->ipq_label, datagram, &datagram->m_pkthdr.label); } void mac_create_fragment(struct mbuf *datagram, struct mbuf *fragment) { MAC_PERFORM(create_fragment, datagram, &datagram->m_pkthdr.label, fragment, &fragment->m_pkthdr.label); } void mac_create_ipq(struct mbuf *fragment, struct ipq *ipq) { MAC_PERFORM(create_ipq, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); } void mac_create_mbuf_from_mbuf(struct mbuf *oldmbuf, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_from_mbuf, oldmbuf, &oldmbuf->m_pkthdr.label, newmbuf, &newmbuf->m_pkthdr.label); } void mac_create_mbuf_from_bpfdesc(struct bpf_d *bpf_d, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_bpfdesc, bpf_d, &bpf_d->bd_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_linklayer(struct ifnet *ifnet, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_linklayer, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_from_ifnet(struct ifnet *ifnet, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_ifnet, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_multicast_encap(struct mbuf *oldmbuf, struct ifnet *ifnet, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_multicast_encap, oldmbuf, &oldmbuf->m_pkthdr.label, ifnet, &ifnet->if_label, newmbuf, &newmbuf->m_pkthdr.label); } void mac_create_mbuf_netlayer(struct mbuf *oldmbuf, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_netlayer, oldmbuf, &oldmbuf->m_pkthdr.label, newmbuf, &newmbuf->m_pkthdr.label); } int mac_fragment_match(struct mbuf *fragment, struct ipq *ipq) { int result; result = 1; MAC_BOOLEAN(fragment_match, &&, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); return (result); } void mac_update_ipq(struct mbuf *fragment, struct ipq *ipq) { MAC_PERFORM(update_ipq, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); } void mac_create_mbuf_from_socket(struct socket *socket, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_socket, socket, &socket->so_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mount(struct ucred *cred, struct mount *mp) { MAC_PERFORM(create_mount, cred, mp, &mp->mnt_mntlabel, &mp->mnt_fslabel); } void mac_create_root_mount(struct ucred *cred, struct mount *mp) { MAC_PERFORM(create_root_mount, cred, mp, &mp->mnt_mntlabel, &mp->mnt_fslabel); } int mac_check_bpfdesc_receive(struct bpf_d *bpf_d, struct ifnet *ifnet) { int error; if (!mac_enforce_network) return (0); MAC_CHECK(check_bpfdesc_receive, bpf_d, &bpf_d->bd_label, ifnet, &ifnet->if_label); return (error); } static int mac_check_cred_relabel(struct ucred *cred, struct label *newlabel) { int error; MAC_CHECK(check_cred_relabel, cred, newlabel); return (error); } int mac_check_cred_visible(struct ucred *u1, struct ucred *u2) { int error; if (!mac_enforce_process) return (0); MAC_CHECK(check_cred_visible, u1, u2); return (error); } int mac_check_ifnet_transmit(struct ifnet *ifnet, struct mbuf *mbuf) { int error; if (!mac_enforce_network) return (0); KASSERT(mbuf->m_flags & M_PKTHDR, ("packet has no pkthdr")); if (!(mbuf->m_pkthdr.label.l_flags & MAC_FLAG_INITIALIZED)) if_printf(ifnet, "not initialized\n"); MAC_CHECK(check_ifnet_transmit, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); return (error); } int mac_check_mount_stat(struct ucred *cred, struct mount *mount) { int error; if (!mac_enforce_fs) return (0); MAC_CHECK(check_mount_stat, cred, mount, &mount->mnt_mntlabel); return (error); } int mac_check_pipe_ioctl(struct ucred *cred, struct pipe *pipe, unsigned long cmd, void *data) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_ioctl, cred, pipe, pipe->pipe_label, cmd, data); return (error); } int mac_check_pipe_poll(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_poll, cred, pipe, pipe->pipe_label); return (error); } int mac_check_pipe_read(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_read, cred, pipe, pipe->pipe_label); return (error); } static int mac_check_pipe_relabel(struct ucred *cred, struct pipe *pipe, struct label *newlabel) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_relabel, cred, pipe, pipe->pipe_label, newlabel); return (error); } int mac_check_pipe_stat(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_stat, cred, pipe, pipe->pipe_label); return (error); } int mac_check_pipe_write(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_write, cred, pipe, pipe->pipe_label); return (error); } int mac_check_proc_debug(struct ucred *cred, struct proc *proc) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_debug, cred, proc); return (error); } int mac_check_proc_sched(struct ucred *cred, struct proc *proc) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_sched, cred, proc); return (error); } int mac_check_proc_signal(struct ucred *cred, struct proc *proc, int signum) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_signal, cred, proc, signum); return (error); } int mac_check_socket_bind(struct ucred *ucred, struct socket *socket, struct sockaddr *sockaddr) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_bind, ucred, socket, &socket->so_label, sockaddr); return (error); } int mac_check_socket_connect(struct ucred *cred, struct socket *socket, struct sockaddr *sockaddr) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_connect, cred, socket, &socket->so_label, sockaddr); return (error); } int mac_check_socket_deliver(struct socket *socket, struct mbuf *mbuf) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_deliver, socket, &socket->so_label, mbuf, &mbuf->m_pkthdr.label); return (error); } int mac_check_socket_listen(struct ucred *cred, struct socket *socket) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_listen, cred, socket, &socket->so_label); return (error); } int mac_check_socket_receive(struct ucred *cred, struct socket *so) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_receive, cred, so, &so->so_label); return (error); } static int mac_check_socket_relabel(struct ucred *cred, struct socket *socket, struct label *newlabel) { int error; MAC_CHECK(check_socket_relabel, cred, socket, &socket->so_label, newlabel); return (error); } int mac_check_socket_send(struct ucred *cred, struct socket *so) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_send, cred, so, &so->so_label); return (error); } int mac_check_socket_visible(struct ucred *cred, struct socket *socket) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_visible, cred, socket, &socket->so_label); return (error); } int mac_check_system_reboot(struct ucred *cred, int howto) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_system_reboot"); if (!mac_enforce_reboot) return (0); MAC_CHECK(check_system_reboot, cred, howto); return (error); } int mac_check_system_swapon(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_system_swapon"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_system_swapon, cred, vp, &vp->v_label); + return (error); +} + +int +mac_check_system_sysctl(struct ucred *cred, int *name, u_int namelen, + void *old, size_t *oldlenp, int inkernel, void *new, size_t newlen) +{ + int error; + + /* + * XXXMAC: We're very much like to assert the SYSCTL_LOCK here, + * but since it's not exported from kern_sysctl.c, we can't. + */ + if (!mac_enforce_sysctl) + return (0); + + MAC_CHECK(check_system_sysctl, cred, name, namelen, old, oldlenp, + inkernel, new, newlen); + return (error); } int mac_ioctl_ifnet_get(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet) { char *elements, *buffer; struct mac mac; int error; 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); error = mac_externalize_ifnet_label(&ifnet->if_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } int mac_ioctl_ifnet_set(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet) { struct label intlabel; struct mac mac; char *buffer; int error; 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); } mac_init_ifnet_label(&intlabel); error = mac_internalize_ifnet_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_ifnet_label(&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 = suser_cred(cred, 0); if (error) { mac_destroy_ifnet_label(&intlabel); return (error); } MAC_CHECK(check_ifnet_relabel, cred, ifnet, &ifnet->if_label, &intlabel); if (error) { mac_destroy_ifnet_label(&intlabel); return (error); } MAC_PERFORM(relabel_ifnet, cred, ifnet, &ifnet->if_label, &intlabel); mac_destroy_ifnet_label(&intlabel); return (0); } void mac_create_devfs_vnode(struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(create_devfs_vnode, de, &de->de_label, vp, &vp->v_label); } void mac_create_devfs_device(dev_t dev, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_device, dev, de, &de->de_label); } void mac_create_devfs_symlink(struct ucred *cred, struct devfs_dirent *dd, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_symlink, cred, dd, &dd->de_label, de, &de->de_label); } void mac_create_devfs_directory(char *dirname, int dirnamelen, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_directory, dirname, dirnamelen, de, &de->de_label); } int mac_setsockopt_label_set(struct ucred *cred, struct socket *so, struct mac *mac) { struct label intlabel; char *buffer; int 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); } mac_init_socket_label(&intlabel, M_WAITOK); error = mac_internalize_socket_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_socket_label(&intlabel); return (error); } mac_check_socket_relabel(cred, so, &intlabel); if (error) { mac_destroy_socket_label(&intlabel); return (error); } mac_relabel_socket(cred, so, &intlabel); mac_destroy_socket_label(&intlabel); return (0); } int mac_pipe_label_set(struct ucred *cred, struct pipe *pipe, struct label *label) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); error = mac_check_pipe_relabel(cred, pipe, label); if (error) return (error); mac_relabel_pipe(cred, pipe, label); return (0); } int mac_getsockopt_label_get(struct ucred *cred, struct socket *so, struct mac *mac) { char *buffer, *elements; int 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_externalize_socket_label(&so->so_label, elements, buffer, mac->m_buflen, M_WAITOK); 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_get(struct ucred *cred, struct socket *so, struct mac *mac) { char *elements, *buffer; int 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_externalize_socket_peer_label(&so->so_peerlabel, elements, buffer, mac->m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac->m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * 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_setlabel_vnode_extattr(ap->a_cred, vp, intlabel); if (error) return (error); mac_relabel_vnode(ap->a_cred, vp, intlabel); return (0); } static 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_check_vnode_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); } int __mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) { char *elements, *buffer; struct mac mac; struct proc *tproc; struct ucred *tcred; int error; error = copyin(SCARG(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_externalize_cred_label(&tcred->cr_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); crfree(tcred); return (error); } /* * MPSAFE */ int __mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) { char *elements, *buffer; struct mac mac; int error; error = copyin(uap->mac_p, &mac, sizeof(mac)); if (error) return (error); error = mac_check_structmac_consistent(&mac); if (error) return (error); elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL); if (error) { free(elements, M_MACTEMP); return (error); } buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); error = mac_externalize_cred_label(&td->td_ucred->cr_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) { struct ucred *newcred, *oldcred; struct label intlabel; struct proc *p; struct mac mac; char *buffer; int error; 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); } mac_init_cred_label(&intlabel); error = mac_internalize_cred_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_cred_label(&intlabel); return (error); } newcred = crget(); p = td->td_proc; PROC_LOCK(p); oldcred = p->p_ucred; error = mac_check_cred_relabel(oldcred, &intlabel); if (error) { PROC_UNLOCK(p); crfree(newcred); goto out; } setsugid(p); crcopy(newcred, oldcred); mac_relabel_cred(newcred, &intlabel); p->p_ucred = newcred; /* * Grab additional reference for use while revoking mmaps, prior * to releasing the proc lock and sharing the cred. */ crhold(newcred); PROC_UNLOCK(p); if (mac_enforce_vm) { mtx_lock(&Giant); mac_cred_mmapped_drop_perms(td, newcred); mtx_unlock(&Giant); } crfree(newcred); /* Free revocation reference. */ crfree(oldcred); out: mac_destroy_cred_label(&intlabel); return (error); } /* * MPSAFE */ int __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) { char *elements, *buffer; struct label intlabel; struct file *fp; struct mac mac; struct vnode *vp; struct pipe *pipe; 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); mtx_lock(&Giant); /* VFS */ error = fget(td, SCARG(uap, fd), &fp); if (error) goto out; label_type = fp->f_type; switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: vp = (struct vnode *)fp->f_data; mac_init_vnode_label(&intlabel); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); mac_copy_vnode_label(&vp->v_label, &intlabel); VOP_UNLOCK(vp, 0, td); break; case DTYPE_PIPE: pipe = (struct pipe *)fp->f_data; mac_init_pipe_label(&intlabel); PIPE_LOCK(pipe); mac_copy_pipe_label(pipe->pipe_label, &intlabel); PIPE_UNLOCK(pipe); break; default: error = EINVAL; fdrop(fp, td); goto out; } fdrop(fp, td); switch (label_type) { case DTYPE_FIFO: case DTYPE_VNODE: if (error == 0) error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); mac_destroy_vnode_label(&intlabel); break; case DTYPE_PIPE: error = mac_externalize_pipe_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); mac_destroy_pipe_label(&intlabel); break; default: panic("__mac_get_fd: corrupted label_type"); } if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_get_file(struct thread *td, struct __mac_get_file_args *uap) { char *elements, *buffer; struct nameidata nd; struct label intlabel; struct mac mac; int 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); mtx_lock(&Giant); /* VFS */ NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_USERSPACE, uap->path_p, td); error = namei(&nd); if (error) goto out; mac_init_vnode_label(&intlabel); mac_copy_vnode_label(&nd.ni_vp->v_label, &intlabel); error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); NDFREE(&nd, 0); mac_destroy_vnode_label(&intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_get_link(struct thread *td, struct __mac_get_link_args *uap) { char *elements, *buffer; struct nameidata nd; struct label intlabel; struct mac mac; int 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); mtx_lock(&Giant); /* VFS */ NDINIT(&nd, LOOKUP, LOCKLEAF | NOFOLLOW, UIO_USERSPACE, uap->path_p, td); error = namei(&nd); if (error) goto out; mac_init_vnode_label(&intlabel); mac_copy_vnode_label(&nd.ni_vp->v_label, &intlabel); error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); NDFREE(&nd, 0); mac_destroy_vnode_label(&intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) { struct label intlabel; struct pipe *pipe; 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); } mtx_lock(&Giant); /* VFS */ error = fget(td, SCARG(uap, fd), &fp); if (error) goto out; switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); if (error) { mac_destroy_vnode_label(&intlabel); break; } vp = (struct vnode *)fp->f_data; error = vn_start_write(vp, &mp, V_WAIT | PCATCH); if (error != 0) { mac_destroy_vnode_label(&intlabel); break; } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); error = vn_setlabel(vp, &intlabel, td->td_ucred); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); mac_destroy_vnode_label(&intlabel); break; case DTYPE_PIPE: mac_init_pipe_label(&intlabel); error = mac_internalize_pipe_label(&intlabel, buffer); if (error == 0) { pipe = (struct pipe *)fp->f_data; PIPE_LOCK(pipe); error = mac_pipe_label_set(td->td_ucred, pipe, &intlabel); PIPE_UNLOCK(pipe); } mac_destroy_pipe_label(&intlabel); break; default: error = EINVAL; } fdrop(fp, td); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_file(struct thread *td, struct __mac_set_file_args *uap) { struct label intlabel; struct nameidata nd; struct mount *mp; struct mac mac; char *buffer; int 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); } mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_vnode_label(&intlabel); return (error); } mtx_lock(&Giant); /* VFS */ 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); mtx_unlock(&Giant); /* VFS */ mac_destroy_vnode_label(&intlabel); return (error); } /* * MPSAFE */ int __mac_set_link(struct thread *td, struct __mac_set_link_args *uap) { struct label intlabel; struct nameidata nd; struct mount *mp; struct mac mac; char *buffer; int 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); } mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_vnode_label(&intlabel); return (error); } mtx_lock(&Giant); /* VFS */ 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); mtx_unlock(&Giant); /* VFS */ mac_destroy_vnode_label(&intlabel); return (error); } /* * MPSAFE */ int 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(SCARG(uap, policy), target, sizeof(target), NULL); if (error) return (error); error = ENOSYS; MAC_POLICY_LIST_BUSY(); 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, SCARG(uap, call), SCARG(uap, arg)); goto out; } } out: MAC_POLICY_LIST_UNBUSY(); return (error); } 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); #else /* !MAC */ int __mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) { return (ENOSYS); } int __mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) { return (ENOSYS); } int __mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) { return (ENOSYS); } int __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) { return (ENOSYS); } int __mac_get_file(struct thread *td, struct __mac_get_file_args *uap) { return (ENOSYS); } int __mac_get_link(struct thread *td, struct __mac_get_link_args *uap) { return (ENOSYS); } int __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) { return (ENOSYS); } int __mac_set_file(struct thread *td, struct __mac_set_file_args *uap) { return (ENOSYS); } int __mac_set_link(struct thread *td, struct __mac_set_link_args *uap) { return (ENOSYS); } int mac_syscall(struct thread *td, struct mac_syscall_args *uap) { return (ENOSYS); } #endif Index: head/sys/security/mac/mac_net.c =================================================================== --- head/sys/security/mac/mac_net.c (revision 106024) +++ head/sys/security/mac/mac_net.c (revision 106025) @@ -1,4010 +1,4038 @@ /*- * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson * Copyright (c) 2001 Ilmar S. Habibulin * Copyright (c) 2001, 2002 Networks Associates Technology, 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 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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. * * Framework for extensible kernel access control. Kernel and userland * interface to the framework, policy registration and composition. */ #include "opt_mac.h" #include "opt_devfs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MAC /* * Declare that the kernel provides MAC support, version 1. 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, 1); SYSCTL_DECL(_security); SYSCTL_NODE(_security, OID_AUTO, mac, CTLFLAG_RW, 0, "TrustedBSD MAC policy controls"); #if MAC_MAX_POLICIES > 32 #error "MAC_MAX_POLICIES too large" #endif static unsigned int mac_max_policies = MAC_MAX_POLICIES; static unsigned int mac_policy_offsets_free = (1 << MAC_MAX_POLICIES) - 1; SYSCTL_UINT(_security_mac, OID_AUTO, max_policies, CTLFLAG_RD, &mac_max_policies, 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; /* * Warn about EA transactions only the first time they happen. * Weak coherency, no locking. */ static int ea_warn_once = 0; static int mac_enforce_fs = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_fs, CTLFLAG_RW, &mac_enforce_fs, 0, "Enforce MAC policy on file system objects"); TUNABLE_INT("security.mac.enforce_fs", &mac_enforce_fs); static int mac_enforce_network = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_network, CTLFLAG_RW, &mac_enforce_network, 0, "Enforce MAC policy on network packets"); TUNABLE_INT("security.mac.enforce_network", &mac_enforce_network); static int mac_enforce_pipe = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_pipe, CTLFLAG_RW, &mac_enforce_pipe, 0, "Enforce MAC policy on pipe operations"); TUNABLE_INT("security.mac.enforce_pipe", &mac_enforce_pipe); static int mac_enforce_process = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_process, CTLFLAG_RW, &mac_enforce_process, 0, "Enforce MAC policy on inter-process operations"); TUNABLE_INT("security.mac.enforce_process", &mac_enforce_process); static int mac_enforce_reboot = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_reboot, CTLFLAG_RW, &mac_enforce_reboot, 0, "Enforce MAC policy for reboot operations"); TUNABLE_INT("security.mac.enforce_reboot", &mac_enforce_reboot); static int mac_enforce_socket = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_socket, CTLFLAG_RW, &mac_enforce_socket, 0, "Enforce MAC policy on socket operations"); TUNABLE_INT("security.mac.enforce_socket", &mac_enforce_socket); +static int mac_enforce_sysctl = 1; +SYSCTL_INT(_security_mac, OID_AUTO, enforce_sysctl, CTLFLAG_RW, + &mac_enforce_sysctl, 0, "Enforce MAC policy on sysctl operations"); +TUNABLE_INT("security.mac.enforce_sysctl", &mac_enforce_sysctl); + static int mac_enforce_vm = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_vm, CTLFLAG_RW, &mac_enforce_vm, 0, "Enforce MAC policy on vm operations"); TUNABLE_INT("security.mac.enforce_vm", &mac_enforce_vm); static int mac_cache_fslabel_in_vnode = 1; SYSCTL_INT(_security_mac, OID_AUTO, cache_fslabel_in_vnode, CTLFLAG_RW, &mac_cache_fslabel_in_vnode, 0, "Cache mount fslabel in vnode"); TUNABLE_INT("security.mac.cache_fslabel_in_vnode", &mac_cache_fslabel_in_vnode); 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"); #ifdef MAC_DEBUG SYSCTL_NODE(_security_mac, OID_AUTO, debug, CTLFLAG_RW, 0, "TrustedBSD MAC debug info"); static int mac_debug_label_fallback = 0; SYSCTL_INT(_security_mac_debug, OID_AUTO, label_fallback, CTLFLAG_RW, &mac_debug_label_fallback, 0, "Filesystems should fall back to fs label" "when label is corrupted."); TUNABLE_INT("security.mac.debug_label_fallback", &mac_debug_label_fallback); SYSCTL_NODE(_security_mac_debug, OID_AUTO, counters, CTLFLAG_RW, 0, "TrustedBSD MAC object counters"); static unsigned int nmacmbufs, nmaccreds, nmacifnets, nmacbpfdescs, nmacsockets, nmacmounts, nmactemp, nmacvnodes, nmacdevfsdirents, nmacipqs, nmacpipes; SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, mbufs, CTLFLAG_RD, &nmacmbufs, 0, "number of mbufs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, creds, CTLFLAG_RD, &nmaccreds, 0, "number of ucreds in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ifnets, CTLFLAG_RD, &nmacifnets, 0, "number of ifnets in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ipqs, CTLFLAG_RD, &nmacipqs, 0, "number of ipqs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, bpfdescs, CTLFLAG_RD, &nmacbpfdescs, 0, "number of bpfdescs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, sockets, CTLFLAG_RD, &nmacsockets, 0, "number of sockets in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, pipes, CTLFLAG_RD, &nmacpipes, 0, "number of pipes in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, mounts, CTLFLAG_RD, &nmacmounts, 0, "number of mounts in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, temp, CTLFLAG_RD, &nmactemp, 0, "number of temporary labels in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, vnodes, CTLFLAG_RD, &nmacvnodes, 0, "number of vnodes in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, devfsdirents, CTLFLAG_RD, &nmacdevfsdirents, 0, "number of devfs dirents inuse"); #endif static int error_select(int error1, int error2); static int mac_policy_register(struct mac_policy_conf *mpc); static int mac_policy_unregister(struct mac_policy_conf *mpc); static void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot); static void mac_cred_mmapped_drop_perms_recurse(struct thread *td, struct ucred *cred, struct vm_map *map); static void mac_destroy_socket_label(struct label *label); static int mac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *intlabel); MALLOC_DEFINE(M_MACOPVEC, "macopvec", "MAC policy operation vector"); MALLOC_DEFINE(M_MACPIPELABEL, "macpipelabel", "MAC labels for pipes"); MALLOC_DEFINE(M_MACTEMP, "mactemp", "MAC temporary label storage"); /* * mac_policy_list_lock protects the consistency of 'mac_policy_list', * the linked list of attached policy modules. Read-only consumers of * the list must acquire a shared lock for the duration of their use; * writers must acquire an exclusive lock. Note that for compound * operations, locks should be held for the entire compound operation, * and that this is not yet done for relabel requests. */ static struct mtx mac_policy_list_lock; static LIST_HEAD(, mac_policy_conf) mac_policy_list; static int mac_policy_list_busy; #define MAC_POLICY_LIST_LOCKINIT() mtx_init(&mac_policy_list_lock, \ "mac_policy_list_lock", NULL, MTX_DEF); #define MAC_POLICY_LIST_LOCK() mtx_lock(&mac_policy_list_lock); #define MAC_POLICY_LIST_UNLOCK() mtx_unlock(&mac_policy_list_lock); #define MAC_POLICY_LIST_BUSY() do { \ MAC_POLICY_LIST_LOCK(); \ mac_policy_list_busy++; \ MAC_POLICY_LIST_UNLOCK(); \ } while (0) #define MAC_POLICY_LIST_UNBUSY() do { \ MAC_POLICY_LIST_LOCK(); \ mac_policy_list_busy--; \ if (mac_policy_list_busy < 0) \ panic("Extra mac_policy_list_busy--"); \ MAC_POLICY_LIST_UNLOCK(); \ } while (0) /* * MAC_CHECK performs the designated check by walking the policy * module list and checking with each as to how it feels about the * request. Note that it returns its value via 'error' in the scope * of the caller. */ #define MAC_CHECK(check, args...) do { \ struct mac_policy_conf *mpc; \ \ error = 0; \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## check != NULL) \ error = error_select( \ mpc->mpc_ops->mpo_ ## check (args), \ error); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) /* * MAC_BOOLEAN performs the designated boolean composition by walking * the module list, invoking each instance of the operation, and * combining the results using the passed C operator. Note that it * returns its value via 'result' in the scope of the caller, which * should be initialized by the caller in a meaningful way to get * a meaningful result. */ #define MAC_BOOLEAN(operation, composition, args...) do { \ struct mac_policy_conf *mpc; \ \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ result = result composition \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) #define MAC_EXTERNALIZE(type, label, elementlist, outbuf, \ outbuflen) do { \ char *curptr, *curptr_start, *element_name, *element_temp; \ size_t left, left_start, len; \ int claimed, first, first_start, ignorenotfound; \ \ error = 0; \ element_temp = elementlist; \ curptr = outbuf; \ curptr[0] = '\0'; \ left = outbuflen; \ first = 1; \ while ((element_name = strsep(&element_temp, ",")) != NULL) { \ curptr_start = curptr; \ left_start = left; \ first_start = first; \ if (element_name[0] == '?') { \ element_name++; \ ignorenotfound = 1; \ } else \ ignorenotfound = 0; \ claimed = 0; \ if (first) { \ len = snprintf(curptr, left, "%s/", \ element_name); \ first = 0; \ } else \ len = snprintf(curptr, left, ",%s/", \ element_name); \ if (len >= left) { \ error = EINVAL; /* XXXMAC: E2BIG */ \ break; \ } \ curptr += len; \ left -= len; \ \ MAC_CHECK(externalize_ ## type, label, element_name, \ curptr, left, &len, &claimed); \ if (error) \ break; \ if (claimed == 1) { \ if (len >= outbuflen) { \ error = EINVAL; /* XXXMAC: E2BIG */ \ break; \ } \ curptr += len; \ left -= len; \ } else if (claimed == 0 && ignorenotfound) { \ /* \ * Revert addition of the label element \ * name. \ */ \ curptr = curptr_start; \ *curptr = '\0'; \ left = left_start; \ first = first_start; \ } else { \ error = EINVAL; /* XXXMAC: ENOLABEL */ \ break; \ } \ } \ } while (0) #define MAC_INTERNALIZE(type, label, instring) do { \ char *element, *element_name, *element_data; \ int claimed; \ \ error = 0; \ element = instring; \ while ((element_name = strsep(&element, ",")) != NULL) { \ element_data = element_name; \ element_name = strsep(&element_data, "/"); \ if (element_data == NULL) { \ error = EINVAL; \ break; \ } \ claimed = 0; \ MAC_CHECK(internalize_ ## type, label, element_name, \ element_data, &claimed); \ if (error) \ break; \ if (claimed != 1) { \ /* XXXMAC: Another error here? */ \ error = EINVAL; \ break; \ } \ } \ } while (0) /* * MAC_PERFORM performs the designated operation by walking the policy * module list and invoking that operation for each policy. */ #define MAC_PERFORM(operation, args...) do { \ struct mac_policy_conf *mpc; \ \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) /* * Initialize the MAC subsystem, including appropriate SMP locks. */ static void mac_init(void) { LIST_INIT(&mac_policy_list); MAC_POLICY_LIST_LOCKINIT(); } /* * 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; } /* * 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; 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: break; } return (error); } static int mac_policy_register(struct mac_policy_conf *mpc) { struct mac_policy_conf *tmpc; struct mac_policy_op_entry *mpe; int slot; MALLOC(mpc->mpc_ops, struct mac_policy_ops *, sizeof(*mpc->mpc_ops), M_MACOPVEC, M_WAITOK | M_ZERO); for (mpe = mpc->mpc_entries; mpe->mpe_constant != MAC_OP_LAST; mpe++) { switch (mpe->mpe_constant) { case MAC_OP_LAST: /* * Doesn't actually happen, but this allows checking * that all enumerated values are handled. */ break; case MAC_DESTROY: mpc->mpc_ops->mpo_destroy = mpe->mpe_function; break; case MAC_INIT: mpc->mpc_ops->mpo_init = mpe->mpe_function; break; case MAC_SYSCALL: mpc->mpc_ops->mpo_syscall = mpe->mpe_function; break; case MAC_INIT_BPFDESC_LABEL: mpc->mpc_ops->mpo_init_bpfdesc_label = mpe->mpe_function; break; case MAC_INIT_CRED_LABEL: mpc->mpc_ops->mpo_init_cred_label = mpe->mpe_function; break; case MAC_INIT_DEVFSDIRENT_LABEL: mpc->mpc_ops->mpo_init_devfsdirent_label = mpe->mpe_function; break; case MAC_INIT_IFNET_LABEL: mpc->mpc_ops->mpo_init_ifnet_label = mpe->mpe_function; break; case MAC_INIT_IPQ_LABEL: mpc->mpc_ops->mpo_init_ipq_label = mpe->mpe_function; break; case MAC_INIT_MBUF_LABEL: mpc->mpc_ops->mpo_init_mbuf_label = mpe->mpe_function; break; case MAC_INIT_MOUNT_LABEL: mpc->mpc_ops->mpo_init_mount_label = mpe->mpe_function; break; case MAC_INIT_MOUNT_FS_LABEL: mpc->mpc_ops->mpo_init_mount_fs_label = mpe->mpe_function; break; case MAC_INIT_PIPE_LABEL: mpc->mpc_ops->mpo_init_pipe_label = mpe->mpe_function; break; case MAC_INIT_SOCKET_LABEL: mpc->mpc_ops->mpo_init_socket_label = mpe->mpe_function; break; case MAC_INIT_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_init_socket_peer_label = mpe->mpe_function; break; case MAC_INIT_VNODE_LABEL: mpc->mpc_ops->mpo_init_vnode_label = mpe->mpe_function; break; case MAC_DESTROY_BPFDESC_LABEL: mpc->mpc_ops->mpo_destroy_bpfdesc_label = mpe->mpe_function; break; case MAC_DESTROY_CRED_LABEL: mpc->mpc_ops->mpo_destroy_cred_label = mpe->mpe_function; break; case MAC_DESTROY_DEVFSDIRENT_LABEL: mpc->mpc_ops->mpo_destroy_devfsdirent_label = mpe->mpe_function; break; case MAC_DESTROY_IFNET_LABEL: mpc->mpc_ops->mpo_destroy_ifnet_label = mpe->mpe_function; break; case MAC_DESTROY_IPQ_LABEL: mpc->mpc_ops->mpo_destroy_ipq_label = mpe->mpe_function; break; case MAC_DESTROY_MBUF_LABEL: mpc->mpc_ops->mpo_destroy_mbuf_label = mpe->mpe_function; break; case MAC_DESTROY_MOUNT_LABEL: mpc->mpc_ops->mpo_destroy_mount_label = mpe->mpe_function; break; case MAC_DESTROY_MOUNT_FS_LABEL: mpc->mpc_ops->mpo_destroy_mount_fs_label = mpe->mpe_function; break; case MAC_DESTROY_PIPE_LABEL: mpc->mpc_ops->mpo_destroy_pipe_label = mpe->mpe_function; break; case MAC_DESTROY_SOCKET_LABEL: mpc->mpc_ops->mpo_destroy_socket_label = mpe->mpe_function; break; case MAC_DESTROY_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_destroy_socket_peer_label = mpe->mpe_function; break; case MAC_DESTROY_VNODE_LABEL: mpc->mpc_ops->mpo_destroy_vnode_label = mpe->mpe_function; break; case MAC_COPY_PIPE_LABEL: mpc->mpc_ops->mpo_copy_pipe_label = mpe->mpe_function; break; case MAC_COPY_VNODE_LABEL: mpc->mpc_ops->mpo_copy_vnode_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_CRED_LABEL: mpc->mpc_ops->mpo_externalize_cred_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_IFNET_LABEL: mpc->mpc_ops->mpo_externalize_ifnet_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_PIPE_LABEL: mpc->mpc_ops->mpo_externalize_pipe_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_SOCKET_LABEL: mpc->mpc_ops->mpo_externalize_socket_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_externalize_socket_peer_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_VNODE_LABEL: mpc->mpc_ops->mpo_externalize_vnode_label = mpe->mpe_function; break; case MAC_INTERNALIZE_CRED_LABEL: mpc->mpc_ops->mpo_internalize_cred_label = mpe->mpe_function; break; case MAC_INTERNALIZE_IFNET_LABEL: mpc->mpc_ops->mpo_internalize_ifnet_label = mpe->mpe_function; break; case MAC_INTERNALIZE_PIPE_LABEL: mpc->mpc_ops->mpo_internalize_pipe_label = mpe->mpe_function; break; case MAC_INTERNALIZE_SOCKET_LABEL: mpc->mpc_ops->mpo_internalize_socket_label = mpe->mpe_function; break; case MAC_INTERNALIZE_VNODE_LABEL: mpc->mpc_ops->mpo_internalize_vnode_label = mpe->mpe_function; break; case MAC_CREATE_DEVFS_DEVICE: mpc->mpc_ops->mpo_create_devfs_device = mpe->mpe_function; break; case MAC_CREATE_DEVFS_DIRECTORY: mpc->mpc_ops->mpo_create_devfs_directory = mpe->mpe_function; break; case MAC_CREATE_DEVFS_SYMLINK: mpc->mpc_ops->mpo_create_devfs_symlink = mpe->mpe_function; break; case MAC_CREATE_DEVFS_VNODE: mpc->mpc_ops->mpo_create_devfs_vnode = mpe->mpe_function; break; case MAC_CREATE_MOUNT: mpc->mpc_ops->mpo_create_mount = mpe->mpe_function; break; case MAC_CREATE_ROOT_MOUNT: mpc->mpc_ops->mpo_create_root_mount = mpe->mpe_function; break; case MAC_RELABEL_VNODE: mpc->mpc_ops->mpo_relabel_vnode = mpe->mpe_function; break; case MAC_UPDATE_DEVFSDIRENT: mpc->mpc_ops->mpo_update_devfsdirent = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_DEVFS: mpc->mpc_ops->mpo_associate_vnode_devfs = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_EXTATTR: mpc->mpc_ops->mpo_associate_vnode_extattr = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_SINGLELABEL: mpc->mpc_ops->mpo_associate_vnode_singlelabel = mpe->mpe_function; break; case MAC_CREATE_VNODE_EXTATTR: mpc->mpc_ops->mpo_create_vnode_extattr = mpe->mpe_function; break; case MAC_SETLABEL_VNODE_EXTATTR: mpc->mpc_ops->mpo_setlabel_vnode_extattr = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_SOCKET: mpc->mpc_ops->mpo_create_mbuf_from_socket = mpe->mpe_function; break; case MAC_CREATE_PIPE: mpc->mpc_ops->mpo_create_pipe = mpe->mpe_function; break; case MAC_CREATE_SOCKET: mpc->mpc_ops->mpo_create_socket = mpe->mpe_function; break; case MAC_CREATE_SOCKET_FROM_SOCKET: mpc->mpc_ops->mpo_create_socket_from_socket = mpe->mpe_function; break; case MAC_RELABEL_PIPE: mpc->mpc_ops->mpo_relabel_pipe = mpe->mpe_function; break; case MAC_RELABEL_SOCKET: mpc->mpc_ops->mpo_relabel_socket = mpe->mpe_function; break; case MAC_SET_SOCKET_PEER_FROM_MBUF: mpc->mpc_ops->mpo_set_socket_peer_from_mbuf = mpe->mpe_function; break; case MAC_SET_SOCKET_PEER_FROM_SOCKET: mpc->mpc_ops->mpo_set_socket_peer_from_socket = mpe->mpe_function; break; case MAC_CREATE_BPFDESC: mpc->mpc_ops->mpo_create_bpfdesc = mpe->mpe_function; break; case MAC_CREATE_DATAGRAM_FROM_IPQ: mpc->mpc_ops->mpo_create_datagram_from_ipq = mpe->mpe_function; break; case MAC_CREATE_FRAGMENT: mpc->mpc_ops->mpo_create_fragment = mpe->mpe_function; break; case MAC_CREATE_IFNET: mpc->mpc_ops->mpo_create_ifnet = mpe->mpe_function; break; case MAC_CREATE_IPQ: mpc->mpc_ops->mpo_create_ipq = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_MBUF: mpc->mpc_ops->mpo_create_mbuf_from_mbuf = mpe->mpe_function; break; case MAC_CREATE_MBUF_LINKLAYER: mpc->mpc_ops->mpo_create_mbuf_linklayer = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_BPFDESC: mpc->mpc_ops->mpo_create_mbuf_from_bpfdesc = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_IFNET: mpc->mpc_ops->mpo_create_mbuf_from_ifnet = mpe->mpe_function; break; case MAC_CREATE_MBUF_MULTICAST_ENCAP: mpc->mpc_ops->mpo_create_mbuf_multicast_encap = mpe->mpe_function; break; case MAC_CREATE_MBUF_NETLAYER: mpc->mpc_ops->mpo_create_mbuf_netlayer = mpe->mpe_function; break; case MAC_FRAGMENT_MATCH: mpc->mpc_ops->mpo_fragment_match = mpe->mpe_function; break; case MAC_RELABEL_IFNET: mpc->mpc_ops->mpo_relabel_ifnet = mpe->mpe_function; break; case MAC_UPDATE_IPQ: mpc->mpc_ops->mpo_update_ipq = mpe->mpe_function; break; case MAC_CREATE_CRED: mpc->mpc_ops->mpo_create_cred = mpe->mpe_function; break; case MAC_EXECVE_TRANSITION: mpc->mpc_ops->mpo_execve_transition = mpe->mpe_function; break; case MAC_EXECVE_WILL_TRANSITION: mpc->mpc_ops->mpo_execve_will_transition = mpe->mpe_function; break; case MAC_CREATE_PROC0: mpc->mpc_ops->mpo_create_proc0 = mpe->mpe_function; break; case MAC_CREATE_PROC1: mpc->mpc_ops->mpo_create_proc1 = mpe->mpe_function; break; case MAC_RELABEL_CRED: mpc->mpc_ops->mpo_relabel_cred = mpe->mpe_function; break; case MAC_THREAD_USERRET: mpc->mpc_ops->mpo_thread_userret = mpe->mpe_function; break; case MAC_CHECK_BPFDESC_RECEIVE: mpc->mpc_ops->mpo_check_bpfdesc_receive = mpe->mpe_function; break; case MAC_CHECK_CRED_RELABEL: mpc->mpc_ops->mpo_check_cred_relabel = mpe->mpe_function; break; case MAC_CHECK_CRED_VISIBLE: mpc->mpc_ops->mpo_check_cred_visible = mpe->mpe_function; break; case MAC_CHECK_IFNET_RELABEL: mpc->mpc_ops->mpo_check_ifnet_relabel = mpe->mpe_function; break; case MAC_CHECK_IFNET_TRANSMIT: mpc->mpc_ops->mpo_check_ifnet_transmit = mpe->mpe_function; break; case MAC_CHECK_MOUNT_STAT: mpc->mpc_ops->mpo_check_mount_stat = mpe->mpe_function; break; case MAC_CHECK_PIPE_IOCTL: mpc->mpc_ops->mpo_check_pipe_ioctl = mpe->mpe_function; break; case MAC_CHECK_PIPE_POLL: mpc->mpc_ops->mpo_check_pipe_poll = mpe->mpe_function; break; case MAC_CHECK_PIPE_READ: mpc->mpc_ops->mpo_check_pipe_read = mpe->mpe_function; break; case MAC_CHECK_PIPE_RELABEL: mpc->mpc_ops->mpo_check_pipe_relabel = mpe->mpe_function; break; case MAC_CHECK_PIPE_STAT: mpc->mpc_ops->mpo_check_pipe_stat = mpe->mpe_function; break; case MAC_CHECK_PIPE_WRITE: mpc->mpc_ops->mpo_check_pipe_write = mpe->mpe_function; break; case MAC_CHECK_PROC_DEBUG: mpc->mpc_ops->mpo_check_proc_debug = mpe->mpe_function; break; case MAC_CHECK_PROC_SCHED: mpc->mpc_ops->mpo_check_proc_sched = mpe->mpe_function; break; case MAC_CHECK_PROC_SIGNAL: mpc->mpc_ops->mpo_check_proc_signal = mpe->mpe_function; break; case MAC_CHECK_SOCKET_BIND: mpc->mpc_ops->mpo_check_socket_bind = mpe->mpe_function; break; case MAC_CHECK_SOCKET_CONNECT: mpc->mpc_ops->mpo_check_socket_connect = mpe->mpe_function; break; case MAC_CHECK_SOCKET_DELIVER: mpc->mpc_ops->mpo_check_socket_deliver = mpe->mpe_function; break; case MAC_CHECK_SOCKET_LISTEN: mpc->mpc_ops->mpo_check_socket_listen = mpe->mpe_function; break; case MAC_CHECK_SOCKET_RECEIVE: mpc->mpc_ops->mpo_check_socket_receive = mpe->mpe_function; break; case MAC_CHECK_SOCKET_RELABEL: mpc->mpc_ops->mpo_check_socket_relabel = mpe->mpe_function; break; case MAC_CHECK_SOCKET_SEND: mpc->mpc_ops->mpo_check_socket_send = mpe->mpe_function; break; case MAC_CHECK_SOCKET_VISIBLE: mpc->mpc_ops->mpo_check_socket_visible = mpe->mpe_function; break; case MAC_CHECK_SYSTEM_REBOOT: mpc->mpc_ops->mpo_check_system_reboot = mpe->mpe_function; break; case MAC_CHECK_SYSTEM_SWAPON: mpc->mpc_ops->mpo_check_system_swapon = mpe->mpe_function; break; + case MAC_CHECK_SYSTEM_SYSCTL: + mpc->mpc_ops->mpo_check_system_sysctl = + mpe->mpe_function; + break; case MAC_CHECK_VNODE_ACCESS: mpc->mpc_ops->mpo_check_vnode_access = mpe->mpe_function; break; case MAC_CHECK_VNODE_CHDIR: mpc->mpc_ops->mpo_check_vnode_chdir = mpe->mpe_function; break; case MAC_CHECK_VNODE_CHROOT: mpc->mpc_ops->mpo_check_vnode_chroot = mpe->mpe_function; break; case MAC_CHECK_VNODE_CREATE: mpc->mpc_ops->mpo_check_vnode_create = mpe->mpe_function; break; case MAC_CHECK_VNODE_DELETE: mpc->mpc_ops->mpo_check_vnode_delete = mpe->mpe_function; break; case MAC_CHECK_VNODE_DELETEACL: mpc->mpc_ops->mpo_check_vnode_deleteacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_EXEC: mpc->mpc_ops->mpo_check_vnode_exec = mpe->mpe_function; break; case MAC_CHECK_VNODE_GETACL: mpc->mpc_ops->mpo_check_vnode_getacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_GETEXTATTR: mpc->mpc_ops->mpo_check_vnode_getextattr = mpe->mpe_function; break; case MAC_CHECK_VNODE_LINK: mpc->mpc_ops->mpo_check_vnode_link = mpe->mpe_function; break; case MAC_CHECK_VNODE_LOOKUP: mpc->mpc_ops->mpo_check_vnode_lookup = mpe->mpe_function; break; case MAC_CHECK_VNODE_MMAP: mpc->mpc_ops->mpo_check_vnode_mmap = mpe->mpe_function; break; case MAC_CHECK_VNODE_MMAP_DOWNGRADE: mpc->mpc_ops->mpo_check_vnode_mmap_downgrade = mpe->mpe_function; break; case MAC_CHECK_VNODE_MPROTECT: mpc->mpc_ops->mpo_check_vnode_mprotect = mpe->mpe_function; break; case MAC_CHECK_VNODE_OPEN: mpc->mpc_ops->mpo_check_vnode_open = mpe->mpe_function; break; case MAC_CHECK_VNODE_POLL: mpc->mpc_ops->mpo_check_vnode_poll = mpe->mpe_function; break; case MAC_CHECK_VNODE_READ: mpc->mpc_ops->mpo_check_vnode_read = mpe->mpe_function; break; case MAC_CHECK_VNODE_READDIR: mpc->mpc_ops->mpo_check_vnode_readdir = mpe->mpe_function; break; case MAC_CHECK_VNODE_READLINK: mpc->mpc_ops->mpo_check_vnode_readlink = mpe->mpe_function; break; case MAC_CHECK_VNODE_RELABEL: mpc->mpc_ops->mpo_check_vnode_relabel = mpe->mpe_function; break; case MAC_CHECK_VNODE_RENAME_FROM: mpc->mpc_ops->mpo_check_vnode_rename_from = mpe->mpe_function; break; case MAC_CHECK_VNODE_RENAME_TO: mpc->mpc_ops->mpo_check_vnode_rename_to = mpe->mpe_function; break; case MAC_CHECK_VNODE_REVOKE: mpc->mpc_ops->mpo_check_vnode_revoke = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETACL: mpc->mpc_ops->mpo_check_vnode_setacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETEXTATTR: mpc->mpc_ops->mpo_check_vnode_setextattr = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETFLAGS: mpc->mpc_ops->mpo_check_vnode_setflags = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETMODE: mpc->mpc_ops->mpo_check_vnode_setmode = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETOWNER: mpc->mpc_ops->mpo_check_vnode_setowner = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETUTIMES: mpc->mpc_ops->mpo_check_vnode_setutimes = mpe->mpe_function; break; case MAC_CHECK_VNODE_STAT: mpc->mpc_ops->mpo_check_vnode_stat = mpe->mpe_function; break; case MAC_CHECK_VNODE_WRITE: mpc->mpc_ops->mpo_check_vnode_write = mpe->mpe_function; break; /* default: printf("MAC policy `%s': unknown operation %d\n", mpc->mpc_name, mpe->mpe_constant); return (EINVAL); */ } } MAC_POLICY_LIST_LOCK(); if (mac_policy_list_busy > 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (EBUSY); } LIST_FOREACH(tmpc, &mac_policy_list, mpc_list) { if (strcmp(tmpc->mpc_name, mpc->mpc_name) == 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (EEXIST); } } if (mpc->mpc_field_off != NULL) { slot = ffs(mac_policy_offsets_free); if (slot == 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (ENOMEM); } slot--; mac_policy_offsets_free &= ~(1 << slot); *mpc->mpc_field_off = slot; } mpc->mpc_runtime_flags |= MPC_RUNTIME_FLAG_REGISTERED; LIST_INSERT_HEAD(&mac_policy_list, mpc, mpc_list); /* Per-policy initialization. */ if (mpc->mpc_ops->mpo_init != NULL) (*(mpc->mpc_ops->mpo_init))(mpc); MAC_POLICY_LIST_UNLOCK(); printf("Security policy loaded: %s (%s)\n", mpc->mpc_fullname, mpc->mpc_name); return (0); } 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_LIST_LOCK(); if ((mpc->mpc_runtime_flags & MPC_RUNTIME_FLAG_REGISTERED) == 0) { MAC_POLICY_LIST_UNLOCK(); return (0); } #if 0 /* * Don't allow unloading modules with private data. */ if (mpc->mpc_field_off != NULL) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } #endif /* * Only allow the unload to proceed if the module is unloadable * by its own definition. */ if ((mpc->mpc_loadtime_flags & MPC_LOADTIME_FLAG_UNLOADOK) == 0) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } /* * Right now, we EBUSY if the list is in use. In the future, * for reliability reasons, we might want to sleep and wakeup * later to try again. */ if (mac_policy_list_busy > 0) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } if (mpc->mpc_ops->mpo_destroy != NULL) (*(mpc->mpc_ops->mpo_destroy))(mpc); LIST_REMOVE(mpc, mpc_list); MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; mpc->mpc_runtime_flags &= ~MPC_RUNTIME_FLAG_REGISTERED; printf("Security policy unload: %s (%s)\n", mpc->mpc_fullname, mpc->mpc_name); return (0); } /* * Define an error value precedence, and given two arguments, selects the * value with the higher precedence. */ static int 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); } static void mac_init_label(struct label *label) { bzero(label, sizeof(*label)); label->l_flags = MAC_FLAG_INITIALIZED; } static void mac_destroy_label(struct label *label) { KASSERT(label->l_flags & MAC_FLAG_INITIALIZED, ("destroying uninitialized label")); bzero(label, sizeof(*label)); /* implicit: label->l_flags &= ~MAC_FLAG_INITIALIZED; */ } void mac_init_bpfdesc(struct bpf_d *bpf_d) { mac_init_label(&bpf_d->bd_label); MAC_PERFORM(init_bpfdesc_label, &bpf_d->bd_label); #ifdef MAC_DEBUG atomic_add_int(&nmacbpfdescs, 1); #endif } static void mac_init_cred_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_cred_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmaccreds, 1); #endif } void mac_init_cred(struct ucred *cred) { mac_init_cred_label(&cred->cr_label); } void mac_init_devfsdirent(struct devfs_dirent *de) { mac_init_label(&de->de_label); MAC_PERFORM(init_devfsdirent_label, &de->de_label); #ifdef MAC_DEBUG atomic_add_int(&nmacdevfsdirents, 1); #endif } static void mac_init_ifnet_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_ifnet_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacifnets, 1); #endif } void mac_init_ifnet(struct ifnet *ifp) { mac_init_ifnet_label(&ifp->if_label); } void mac_init_ipq(struct ipq *ipq) { mac_init_label(&ipq->ipq_label); MAC_PERFORM(init_ipq_label, &ipq->ipq_label); #ifdef MAC_DEBUG atomic_add_int(&nmacipqs, 1); #endif } int mac_init_mbuf(struct mbuf *m, int flag) { int error; KASSERT(m->m_flags & M_PKTHDR, ("mac_init_mbuf on non-header mbuf")); mac_init_label(&m->m_pkthdr.label); MAC_CHECK(init_mbuf_label, &m->m_pkthdr.label, flag); if (error) { MAC_PERFORM(destroy_mbuf_label, &m->m_pkthdr.label); mac_destroy_label(&m->m_pkthdr.label); } #ifdef MAC_DEBUG if (error == 0) atomic_add_int(&nmacmbufs, 1); #endif return (error); } void mac_init_mount(struct mount *mp) { mac_init_label(&mp->mnt_mntlabel); mac_init_label(&mp->mnt_fslabel); MAC_PERFORM(init_mount_label, &mp->mnt_mntlabel); MAC_PERFORM(init_mount_fs_label, &mp->mnt_fslabel); #ifdef MAC_DEBUG atomic_add_int(&nmacmounts, 1); #endif } static void mac_init_pipe_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_pipe_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacpipes, 1); #endif } void mac_init_pipe(struct pipe *pipe) { struct label *label; label = malloc(sizeof(struct label), M_MACPIPELABEL, M_ZERO|M_WAITOK); pipe->pipe_label = label; pipe->pipe_peer->pipe_label = label; mac_init_pipe_label(label); } static int mac_init_socket_label(struct label *label, int flag) { int error; mac_init_label(label); MAC_CHECK(init_socket_label, label, flag); if (error) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); } #ifdef MAC_DEBUG if (error == 0) atomic_add_int(&nmacsockets, 1); #endif return (error); } static int mac_init_socket_peer_label(struct label *label, int flag) { int error; mac_init_label(label); MAC_CHECK(init_socket_peer_label, label, flag); if (error) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); } return (error); } int mac_init_socket(struct socket *socket, int flag) { int error; error = mac_init_socket_label(&socket->so_label, flag); if (error) return (error); error = mac_init_socket_peer_label(&socket->so_peerlabel, flag); if (error) mac_destroy_socket_label(&socket->so_label); return (error); } void mac_init_vnode_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_vnode_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacvnodes, 1); #endif } void mac_init_vnode(struct vnode *vp) { mac_init_vnode_label(&vp->v_label); } void mac_destroy_bpfdesc(struct bpf_d *bpf_d) { MAC_PERFORM(destroy_bpfdesc_label, &bpf_d->bd_label); mac_destroy_label(&bpf_d->bd_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacbpfdescs, 1); #endif } static void mac_destroy_cred_label(struct label *label) { MAC_PERFORM(destroy_cred_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmaccreds, 1); #endif } void mac_destroy_cred(struct ucred *cred) { mac_destroy_cred_label(&cred->cr_label); } void mac_destroy_devfsdirent(struct devfs_dirent *de) { MAC_PERFORM(destroy_devfsdirent_label, &de->de_label); mac_destroy_label(&de->de_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacdevfsdirents, 1); #endif } static void mac_destroy_ifnet_label(struct label *label) { MAC_PERFORM(destroy_ifnet_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacifnets, 1); #endif } void mac_destroy_ifnet(struct ifnet *ifp) { mac_destroy_ifnet_label(&ifp->if_label); } void mac_destroy_ipq(struct ipq *ipq) { MAC_PERFORM(destroy_ipq_label, &ipq->ipq_label); mac_destroy_label(&ipq->ipq_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacipqs, 1); #endif } void mac_destroy_mbuf(struct mbuf *m) { MAC_PERFORM(destroy_mbuf_label, &m->m_pkthdr.label); mac_destroy_label(&m->m_pkthdr.label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacmbufs, 1); #endif } void mac_destroy_mount(struct mount *mp) { MAC_PERFORM(destroy_mount_label, &mp->mnt_mntlabel); MAC_PERFORM(destroy_mount_fs_label, &mp->mnt_fslabel); mac_destroy_label(&mp->mnt_fslabel); mac_destroy_label(&mp->mnt_mntlabel); #ifdef MAC_DEBUG atomic_subtract_int(&nmacmounts, 1); #endif } static void mac_destroy_pipe_label(struct label *label) { MAC_PERFORM(destroy_pipe_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacpipes, 1); #endif } void mac_destroy_pipe(struct pipe *pipe) { mac_destroy_pipe_label(pipe->pipe_label); free(pipe->pipe_label, M_MACPIPELABEL); } static void mac_destroy_socket_label(struct label *label) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacsockets, 1); #endif } static void mac_destroy_socket_peer_label(struct label *label) { MAC_PERFORM(destroy_socket_peer_label, label); mac_destroy_label(label); } void mac_destroy_socket(struct socket *socket) { mac_destroy_socket_label(&socket->so_label); mac_destroy_socket_peer_label(&socket->so_peerlabel); } void mac_destroy_vnode_label(struct label *label) { MAC_PERFORM(destroy_vnode_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacvnodes, 1); #endif } void mac_destroy_vnode(struct vnode *vp) { mac_destroy_vnode_label(&vp->v_label); } static void mac_copy_pipe_label(struct label *src, struct label *dest) { MAC_PERFORM(copy_pipe_label, src, dest); } void mac_copy_vnode_label(struct label *src, struct label *dest) { MAC_PERFORM(copy_vnode_label, src, dest); } static int mac_check_structmac_consistent(struct mac *mac) { if (mac->m_buflen > MAC_MAX_LABEL_BUF_LEN) return (EINVAL); return (0); } static int mac_externalize_cred_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(cred_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_ifnet_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(ifnet_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_pipe_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(pipe_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_socket_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(socket_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_socket_peer_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(socket_peer_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_vnode_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(vnode_label, label, elements, outbuf, outbuflen); return (error); } static int mac_internalize_cred_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(cred_label, label, string); return (error); } static int mac_internalize_ifnet_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(ifnet_label, label, string); return (error); } static int mac_internalize_pipe_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(pipe_label, label, string); return (error); } static int mac_internalize_socket_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(socket_label, label, string); return (error); } static int mac_internalize_vnode_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(vnode_label, label, string); return (error); } /* * Initialize MAC label for the first kernel process, from which other * kernel processes and threads are spawned. */ void mac_create_proc0(struct ucred *cred) { MAC_PERFORM(create_proc0, cred); } /* * Initialize MAC label for the first userland process, from which other * userland processes and threads are spawned. */ void mac_create_proc1(struct ucred *cred) { MAC_PERFORM(create_proc1, cred); } void mac_thread_userret(struct thread *td) { MAC_PERFORM(thread_userret, td); } /* * 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_create_cred(struct ucred *parent_cred, struct ucred *child_cred) { MAC_PERFORM(create_cred, parent_cred, child_cred); } void mac_update_devfsdirent(struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(update_devfsdirent, de, &de->de_label, vp, &vp->v_label); } void mac_associate_vnode_devfs(struct mount *mp, struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(associate_vnode_devfs, mp, &mp->mnt_fslabel, de, &de->de_label, vp, &vp->v_label); } int mac_associate_vnode_extattr(struct mount *mp, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_associate_vnode_extattr"); MAC_CHECK(associate_vnode_extattr, mp, &mp->mnt_fslabel, vp, &vp->v_label); return (error); } void mac_associate_vnode_singlelabel(struct mount *mp, struct vnode *vp) { MAC_PERFORM(associate_vnode_singlelabel, mp, &mp->mnt_fslabel, vp, &vp->v_label); } int mac_create_vnode_extattr(struct ucred *cred, struct mount *mp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_create_vnode_extattr"); ASSERT_VOP_LOCKED(vp, "mac_create_vnode_extattr"); error = VOP_OPENEXTATTR(vp, cred, curthread); if (error == EOPNOTSUPP) { /* XXX: Optionally abort if transactions not supported. */ if (ea_warn_once == 0) { printf("Warning: transactions not supported " "in EA write.\n"); ea_warn_once = 1; } } else if (error) return (error); MAC_CHECK(create_vnode_extattr, cred, mp, &mp->mnt_fslabel, 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; /* XXX */ return (error); } static int mac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *intlabel) { int error; ASSERT_VOP_LOCKED(vp, "mac_setlabel_vnode_extattr"); error = VOP_OPENEXTATTR(vp, cred, curthread); if (error == EOPNOTSUPP) { /* XXX: Optionally abort if transactions not supported. */ if (ea_warn_once == 0) { printf("Warning: transactions not supported " "in EA write.\n"); ea_warn_once = 1; } } else if (error) return (error); MAC_CHECK(setlabel_vnode_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; /* XXX */ return (error); } void mac_execve_transition(struct ucred *old, struct ucred *new, struct vnode *vp) { ASSERT_VOP_LOCKED(vp, "mac_execve_transition"); MAC_PERFORM(execve_transition, old, new, vp, &vp->v_label); } int mac_execve_will_transition(struct ucred *old, struct vnode *vp) { int result; result = 0; MAC_BOOLEAN(execve_will_transition, ||, old, vp, &vp->v_label); return (result); } int mac_check_vnode_access(struct ucred *cred, struct vnode *vp, int flags) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_access"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_access, cred, vp, &vp->v_label, flags); return (error); } int mac_check_vnode_chdir(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_chdir"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_chdir, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_chroot(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_chroot"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_chroot, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_create(struct ucred *cred, struct vnode *dvp, struct componentname *cnp, struct vattr *vap) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_create"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_create, cred, dvp, &dvp->v_label, cnp, vap); return (error); } int mac_check_vnode_delete(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_delete"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_delete"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_delete, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp, acl_type_t type) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_deleteacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_deleteacl, cred, vp, &vp->v_label, type); return (error); } int mac_check_vnode_exec(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_exec"); if (!mac_enforce_process && !mac_enforce_fs) return (0); MAC_CHECK(check_vnode_exec, cred, vp, &vp->v_label); return (error); } int mac_check_vnode_getacl(struct ucred *cred, struct vnode *vp, acl_type_t type) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_getacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_getacl, cred, vp, &vp->v_label, type); return (error); } int mac_check_vnode_getextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_getextattr"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_getextattr, cred, vp, &vp->v_label, attrnamespace, name, uio); return (error); } int mac_check_vnode_link(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_link"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_link"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_link, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_lookup(struct ucred *cred, struct vnode *dvp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_lookup"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_lookup, cred, dvp, &dvp->v_label, cnp); return (error); } int mac_check_vnode_mmap(struct ucred *cred, struct vnode *vp, int prot) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mmap"); if (!mac_enforce_fs || !mac_enforce_vm) return (0); MAC_CHECK(check_vnode_mmap, cred, vp, &vp->v_label, prot); return (error); } void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot) { int result = *prot; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mmap_downgrade"); if (!mac_enforce_fs || !mac_enforce_vm) return; MAC_PERFORM(check_vnode_mmap_downgrade, cred, vp, &vp->v_label, &result); *prot = result; } int mac_check_vnode_mprotect(struct ucred *cred, struct vnode *vp, int prot) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mprotect"); if (!mac_enforce_fs || !mac_enforce_vm) return (0); MAC_CHECK(check_vnode_mprotect, cred, vp, &vp->v_label, prot); return (error); } int mac_check_vnode_open(struct ucred *cred, struct vnode *vp, mode_t acc_mode) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_open"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_open, cred, vp, &vp->v_label, acc_mode); return (error); } int mac_check_vnode_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_poll"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_poll, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_read"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_read, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_readdir(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_readdir"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_readdir, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_readlink(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_readlink"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_readlink, cred, vp, &vp->v_label); return (error); } static int mac_check_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *newlabel) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_relabel"); MAC_CHECK(check_vnode_relabel, cred, vp, &vp->v_label, newlabel); return (error); } int mac_check_vnode_rename_from(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_rename_from"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_rename_from"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_rename_from, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp, struct vnode *vp, int samedir, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_rename_to"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_rename_to"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_rename_to, cred, dvp, &dvp->v_label, vp, vp != NULL ? &vp->v_label : NULL, samedir, cnp); return (error); } int mac_check_vnode_revoke(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_revoke"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_revoke, cred, vp, &vp->v_label); return (error); } int mac_check_vnode_setacl(struct ucred *cred, struct vnode *vp, acl_type_t type, struct acl *acl) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setacl, cred, vp, &vp->v_label, type, acl); return (error); } int mac_check_vnode_setextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setextattr"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setextattr, cred, vp, &vp->v_label, attrnamespace, name, uio); return (error); } int mac_check_vnode_setflags(struct ucred *cred, struct vnode *vp, u_long flags) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setflags"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setflags, cred, vp, &vp->v_label, flags); return (error); } int mac_check_vnode_setmode(struct ucred *cred, struct vnode *vp, mode_t mode) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setmode"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setmode, cred, vp, &vp->v_label, mode); return (error); } int mac_check_vnode_setowner(struct ucred *cred, struct vnode *vp, uid_t uid, gid_t gid) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setowner"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setowner, cred, vp, &vp->v_label, uid, gid); return (error); } int mac_check_vnode_setutimes(struct ucred *cred, struct vnode *vp, struct timespec atime, struct timespec mtime) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setutimes"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setutimes, cred, vp, &vp->v_label, atime, mtime); return (error); } int mac_check_vnode_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_stat"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_stat, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_write"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_write, active_cred, file_cred, vp, &vp->v_label); return (error); } /* * 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. */ static void mac_cred_mmapped_drop_perms(struct thread *td, struct ucred *cred) { /* XXX freeze all other threads */ mac_cred_mmapped_drop_perms_recurse(td, cred, &td->td_proc->p_vmspace->vm_map); /* XXX allow other threads to continue */ } 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_cred_mmapped_drop_perms_recurse(struct thread *td, struct ucred *cred, struct vm_map *map) { struct vm_map_entry *vme; int result; vm_prot_t revokeperms; vm_object_t object; vm_ooffset_t offset; struct vnode *vp; if (!mac_mmap_revocation) return; vm_map_lock_read(map); for (vme = map->header.next; vme != &map->header; vme = vme->next) { if (vme->eflags & MAP_ENTRY_IS_SUB_MAP) { mac_cred_mmapped_drop_perms_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; while (object->backing_object != NULL) { object = object->backing_object; offset += object->backing_object_offset; } /* * 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, td); result = vme->max_protection; mac_check_vnode_mmap_downgrade(cred, vp, &result); VOP_UNLOCK(vp, 0, td); /* * 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)); vm_map_lock_upgrade(map); /* * 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); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); vm_object_page_clean(object, OFF_TO_IDX(offset), OFF_TO_IDX(offset + vme->end - vme->start + PAGE_MASK), OBJPC_SYNC); VOP_UNLOCK(vp, 0, td); 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_lock_downgrade(map); } vm_map_unlock_read(map); } /* * 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. */ static void mac_relabel_cred(struct ucred *cred, struct label *newlabel) { MAC_PERFORM(relabel_cred, cred, newlabel); } void mac_relabel_vnode(struct ucred *cred, struct vnode *vp, struct label *newlabel) { MAC_PERFORM(relabel_vnode, cred, vp, &vp->v_label, newlabel); } void mac_create_ifnet(struct ifnet *ifnet) { MAC_PERFORM(create_ifnet, ifnet, &ifnet->if_label); } void mac_create_bpfdesc(struct ucred *cred, struct bpf_d *bpf_d) { MAC_PERFORM(create_bpfdesc, cred, bpf_d, &bpf_d->bd_label); } void mac_create_socket(struct ucred *cred, struct socket *socket) { MAC_PERFORM(create_socket, cred, socket, &socket->so_label); } void mac_create_pipe(struct ucred *cred, struct pipe *pipe) { MAC_PERFORM(create_pipe, cred, pipe, pipe->pipe_label); } void mac_create_socket_from_socket(struct socket *oldsocket, struct socket *newsocket) { MAC_PERFORM(create_socket_from_socket, oldsocket, &oldsocket->so_label, newsocket, &newsocket->so_label); } static void mac_relabel_socket(struct ucred *cred, struct socket *socket, struct label *newlabel) { MAC_PERFORM(relabel_socket, cred, socket, &socket->so_label, newlabel); } static void mac_relabel_pipe(struct ucred *cred, struct pipe *pipe, struct label *newlabel) { MAC_PERFORM(relabel_pipe, cred, pipe, pipe->pipe_label, newlabel); } void mac_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct socket *socket) { MAC_PERFORM(set_socket_peer_from_mbuf, mbuf, &mbuf->m_pkthdr.label, socket, &socket->so_peerlabel); } void mac_set_socket_peer_from_socket(struct socket *oldsocket, struct socket *newsocket) { MAC_PERFORM(set_socket_peer_from_socket, oldsocket, &oldsocket->so_label, newsocket, &newsocket->so_peerlabel); } void mac_create_datagram_from_ipq(struct ipq *ipq, struct mbuf *datagram) { MAC_PERFORM(create_datagram_from_ipq, ipq, &ipq->ipq_label, datagram, &datagram->m_pkthdr.label); } void mac_create_fragment(struct mbuf *datagram, struct mbuf *fragment) { MAC_PERFORM(create_fragment, datagram, &datagram->m_pkthdr.label, fragment, &fragment->m_pkthdr.label); } void mac_create_ipq(struct mbuf *fragment, struct ipq *ipq) { MAC_PERFORM(create_ipq, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); } void mac_create_mbuf_from_mbuf(struct mbuf *oldmbuf, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_from_mbuf, oldmbuf, &oldmbuf->m_pkthdr.label, newmbuf, &newmbuf->m_pkthdr.label); } void mac_create_mbuf_from_bpfdesc(struct bpf_d *bpf_d, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_bpfdesc, bpf_d, &bpf_d->bd_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_linklayer(struct ifnet *ifnet, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_linklayer, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_from_ifnet(struct ifnet *ifnet, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_ifnet, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_multicast_encap(struct mbuf *oldmbuf, struct ifnet *ifnet, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_multicast_encap, oldmbuf, &oldmbuf->m_pkthdr.label, ifnet, &ifnet->if_label, newmbuf, &newmbuf->m_pkthdr.label); } void mac_create_mbuf_netlayer(struct mbuf *oldmbuf, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_netlayer, oldmbuf, &oldmbuf->m_pkthdr.label, newmbuf, &newmbuf->m_pkthdr.label); } int mac_fragment_match(struct mbuf *fragment, struct ipq *ipq) { int result; result = 1; MAC_BOOLEAN(fragment_match, &&, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); return (result); } void mac_update_ipq(struct mbuf *fragment, struct ipq *ipq) { MAC_PERFORM(update_ipq, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); } void mac_create_mbuf_from_socket(struct socket *socket, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_socket, socket, &socket->so_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mount(struct ucred *cred, struct mount *mp) { MAC_PERFORM(create_mount, cred, mp, &mp->mnt_mntlabel, &mp->mnt_fslabel); } void mac_create_root_mount(struct ucred *cred, struct mount *mp) { MAC_PERFORM(create_root_mount, cred, mp, &mp->mnt_mntlabel, &mp->mnt_fslabel); } int mac_check_bpfdesc_receive(struct bpf_d *bpf_d, struct ifnet *ifnet) { int error; if (!mac_enforce_network) return (0); MAC_CHECK(check_bpfdesc_receive, bpf_d, &bpf_d->bd_label, ifnet, &ifnet->if_label); return (error); } static int mac_check_cred_relabel(struct ucred *cred, struct label *newlabel) { int error; MAC_CHECK(check_cred_relabel, cred, newlabel); return (error); } int mac_check_cred_visible(struct ucred *u1, struct ucred *u2) { int error; if (!mac_enforce_process) return (0); MAC_CHECK(check_cred_visible, u1, u2); return (error); } int mac_check_ifnet_transmit(struct ifnet *ifnet, struct mbuf *mbuf) { int error; if (!mac_enforce_network) return (0); KASSERT(mbuf->m_flags & M_PKTHDR, ("packet has no pkthdr")); if (!(mbuf->m_pkthdr.label.l_flags & MAC_FLAG_INITIALIZED)) if_printf(ifnet, "not initialized\n"); MAC_CHECK(check_ifnet_transmit, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); return (error); } int mac_check_mount_stat(struct ucred *cred, struct mount *mount) { int error; if (!mac_enforce_fs) return (0); MAC_CHECK(check_mount_stat, cred, mount, &mount->mnt_mntlabel); return (error); } int mac_check_pipe_ioctl(struct ucred *cred, struct pipe *pipe, unsigned long cmd, void *data) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_ioctl, cred, pipe, pipe->pipe_label, cmd, data); return (error); } int mac_check_pipe_poll(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_poll, cred, pipe, pipe->pipe_label); return (error); } int mac_check_pipe_read(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_read, cred, pipe, pipe->pipe_label); return (error); } static int mac_check_pipe_relabel(struct ucred *cred, struct pipe *pipe, struct label *newlabel) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_relabel, cred, pipe, pipe->pipe_label, newlabel); return (error); } int mac_check_pipe_stat(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_stat, cred, pipe, pipe->pipe_label); return (error); } int mac_check_pipe_write(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_write, cred, pipe, pipe->pipe_label); return (error); } int mac_check_proc_debug(struct ucred *cred, struct proc *proc) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_debug, cred, proc); return (error); } int mac_check_proc_sched(struct ucred *cred, struct proc *proc) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_sched, cred, proc); return (error); } int mac_check_proc_signal(struct ucred *cred, struct proc *proc, int signum) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_signal, cred, proc, signum); return (error); } int mac_check_socket_bind(struct ucred *ucred, struct socket *socket, struct sockaddr *sockaddr) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_bind, ucred, socket, &socket->so_label, sockaddr); return (error); } int mac_check_socket_connect(struct ucred *cred, struct socket *socket, struct sockaddr *sockaddr) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_connect, cred, socket, &socket->so_label, sockaddr); return (error); } int mac_check_socket_deliver(struct socket *socket, struct mbuf *mbuf) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_deliver, socket, &socket->so_label, mbuf, &mbuf->m_pkthdr.label); return (error); } int mac_check_socket_listen(struct ucred *cred, struct socket *socket) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_listen, cred, socket, &socket->so_label); return (error); } int mac_check_socket_receive(struct ucred *cred, struct socket *so) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_receive, cred, so, &so->so_label); return (error); } static int mac_check_socket_relabel(struct ucred *cred, struct socket *socket, struct label *newlabel) { int error; MAC_CHECK(check_socket_relabel, cred, socket, &socket->so_label, newlabel); return (error); } int mac_check_socket_send(struct ucred *cred, struct socket *so) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_send, cred, so, &so->so_label); return (error); } int mac_check_socket_visible(struct ucred *cred, struct socket *socket) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_visible, cred, socket, &socket->so_label); return (error); } int mac_check_system_reboot(struct ucred *cred, int howto) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_system_reboot"); if (!mac_enforce_reboot) return (0); MAC_CHECK(check_system_reboot, cred, howto); return (error); } int mac_check_system_swapon(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_system_swapon"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_system_swapon, cred, vp, &vp->v_label); + return (error); +} + +int +mac_check_system_sysctl(struct ucred *cred, int *name, u_int namelen, + void *old, size_t *oldlenp, int inkernel, void *new, size_t newlen) +{ + int error; + + /* + * XXXMAC: We're very much like to assert the SYSCTL_LOCK here, + * but since it's not exported from kern_sysctl.c, we can't. + */ + if (!mac_enforce_sysctl) + return (0); + + MAC_CHECK(check_system_sysctl, cred, name, namelen, old, oldlenp, + inkernel, new, newlen); + return (error); } int mac_ioctl_ifnet_get(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet) { char *elements, *buffer; struct mac mac; int error; 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); error = mac_externalize_ifnet_label(&ifnet->if_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } int mac_ioctl_ifnet_set(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet) { struct label intlabel; struct mac mac; char *buffer; int error; 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); } mac_init_ifnet_label(&intlabel); error = mac_internalize_ifnet_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_ifnet_label(&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 = suser_cred(cred, 0); if (error) { mac_destroy_ifnet_label(&intlabel); return (error); } MAC_CHECK(check_ifnet_relabel, cred, ifnet, &ifnet->if_label, &intlabel); if (error) { mac_destroy_ifnet_label(&intlabel); return (error); } MAC_PERFORM(relabel_ifnet, cred, ifnet, &ifnet->if_label, &intlabel); mac_destroy_ifnet_label(&intlabel); return (0); } void mac_create_devfs_vnode(struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(create_devfs_vnode, de, &de->de_label, vp, &vp->v_label); } void mac_create_devfs_device(dev_t dev, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_device, dev, de, &de->de_label); } void mac_create_devfs_symlink(struct ucred *cred, struct devfs_dirent *dd, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_symlink, cred, dd, &dd->de_label, de, &de->de_label); } void mac_create_devfs_directory(char *dirname, int dirnamelen, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_directory, dirname, dirnamelen, de, &de->de_label); } int mac_setsockopt_label_set(struct ucred *cred, struct socket *so, struct mac *mac) { struct label intlabel; char *buffer; int 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); } mac_init_socket_label(&intlabel, M_WAITOK); error = mac_internalize_socket_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_socket_label(&intlabel); return (error); } mac_check_socket_relabel(cred, so, &intlabel); if (error) { mac_destroy_socket_label(&intlabel); return (error); } mac_relabel_socket(cred, so, &intlabel); mac_destroy_socket_label(&intlabel); return (0); } int mac_pipe_label_set(struct ucred *cred, struct pipe *pipe, struct label *label) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); error = mac_check_pipe_relabel(cred, pipe, label); if (error) return (error); mac_relabel_pipe(cred, pipe, label); return (0); } int mac_getsockopt_label_get(struct ucred *cred, struct socket *so, struct mac *mac) { char *buffer, *elements; int 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_externalize_socket_label(&so->so_label, elements, buffer, mac->m_buflen, M_WAITOK); 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_get(struct ucred *cred, struct socket *so, struct mac *mac) { char *elements, *buffer; int 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_externalize_socket_peer_label(&so->so_peerlabel, elements, buffer, mac->m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac->m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * 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_setlabel_vnode_extattr(ap->a_cred, vp, intlabel); if (error) return (error); mac_relabel_vnode(ap->a_cred, vp, intlabel); return (0); } static 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_check_vnode_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); } int __mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) { char *elements, *buffer; struct mac mac; struct proc *tproc; struct ucred *tcred; int error; error = copyin(SCARG(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_externalize_cred_label(&tcred->cr_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); crfree(tcred); return (error); } /* * MPSAFE */ int __mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) { char *elements, *buffer; struct mac mac; int error; error = copyin(uap->mac_p, &mac, sizeof(mac)); if (error) return (error); error = mac_check_structmac_consistent(&mac); if (error) return (error); elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL); if (error) { free(elements, M_MACTEMP); return (error); } buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); error = mac_externalize_cred_label(&td->td_ucred->cr_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) { struct ucred *newcred, *oldcred; struct label intlabel; struct proc *p; struct mac mac; char *buffer; int error; 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); } mac_init_cred_label(&intlabel); error = mac_internalize_cred_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_cred_label(&intlabel); return (error); } newcred = crget(); p = td->td_proc; PROC_LOCK(p); oldcred = p->p_ucred; error = mac_check_cred_relabel(oldcred, &intlabel); if (error) { PROC_UNLOCK(p); crfree(newcred); goto out; } setsugid(p); crcopy(newcred, oldcred); mac_relabel_cred(newcred, &intlabel); p->p_ucred = newcred; /* * Grab additional reference for use while revoking mmaps, prior * to releasing the proc lock and sharing the cred. */ crhold(newcred); PROC_UNLOCK(p); if (mac_enforce_vm) { mtx_lock(&Giant); mac_cred_mmapped_drop_perms(td, newcred); mtx_unlock(&Giant); } crfree(newcred); /* Free revocation reference. */ crfree(oldcred); out: mac_destroy_cred_label(&intlabel); return (error); } /* * MPSAFE */ int __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) { char *elements, *buffer; struct label intlabel; struct file *fp; struct mac mac; struct vnode *vp; struct pipe *pipe; 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); mtx_lock(&Giant); /* VFS */ error = fget(td, SCARG(uap, fd), &fp); if (error) goto out; label_type = fp->f_type; switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: vp = (struct vnode *)fp->f_data; mac_init_vnode_label(&intlabel); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); mac_copy_vnode_label(&vp->v_label, &intlabel); VOP_UNLOCK(vp, 0, td); break; case DTYPE_PIPE: pipe = (struct pipe *)fp->f_data; mac_init_pipe_label(&intlabel); PIPE_LOCK(pipe); mac_copy_pipe_label(pipe->pipe_label, &intlabel); PIPE_UNLOCK(pipe); break; default: error = EINVAL; fdrop(fp, td); goto out; } fdrop(fp, td); switch (label_type) { case DTYPE_FIFO: case DTYPE_VNODE: if (error == 0) error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); mac_destroy_vnode_label(&intlabel); break; case DTYPE_PIPE: error = mac_externalize_pipe_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); mac_destroy_pipe_label(&intlabel); break; default: panic("__mac_get_fd: corrupted label_type"); } if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_get_file(struct thread *td, struct __mac_get_file_args *uap) { char *elements, *buffer; struct nameidata nd; struct label intlabel; struct mac mac; int 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); mtx_lock(&Giant); /* VFS */ NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_USERSPACE, uap->path_p, td); error = namei(&nd); if (error) goto out; mac_init_vnode_label(&intlabel); mac_copy_vnode_label(&nd.ni_vp->v_label, &intlabel); error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); NDFREE(&nd, 0); mac_destroy_vnode_label(&intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_get_link(struct thread *td, struct __mac_get_link_args *uap) { char *elements, *buffer; struct nameidata nd; struct label intlabel; struct mac mac; int 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); mtx_lock(&Giant); /* VFS */ NDINIT(&nd, LOOKUP, LOCKLEAF | NOFOLLOW, UIO_USERSPACE, uap->path_p, td); error = namei(&nd); if (error) goto out; mac_init_vnode_label(&intlabel); mac_copy_vnode_label(&nd.ni_vp->v_label, &intlabel); error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); NDFREE(&nd, 0); mac_destroy_vnode_label(&intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) { struct label intlabel; struct pipe *pipe; 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); } mtx_lock(&Giant); /* VFS */ error = fget(td, SCARG(uap, fd), &fp); if (error) goto out; switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); if (error) { mac_destroy_vnode_label(&intlabel); break; } vp = (struct vnode *)fp->f_data; error = vn_start_write(vp, &mp, V_WAIT | PCATCH); if (error != 0) { mac_destroy_vnode_label(&intlabel); break; } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); error = vn_setlabel(vp, &intlabel, td->td_ucred); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); mac_destroy_vnode_label(&intlabel); break; case DTYPE_PIPE: mac_init_pipe_label(&intlabel); error = mac_internalize_pipe_label(&intlabel, buffer); if (error == 0) { pipe = (struct pipe *)fp->f_data; PIPE_LOCK(pipe); error = mac_pipe_label_set(td->td_ucred, pipe, &intlabel); PIPE_UNLOCK(pipe); } mac_destroy_pipe_label(&intlabel); break; default: error = EINVAL; } fdrop(fp, td); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_file(struct thread *td, struct __mac_set_file_args *uap) { struct label intlabel; struct nameidata nd; struct mount *mp; struct mac mac; char *buffer; int 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); } mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_vnode_label(&intlabel); return (error); } mtx_lock(&Giant); /* VFS */ 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); mtx_unlock(&Giant); /* VFS */ mac_destroy_vnode_label(&intlabel); return (error); } /* * MPSAFE */ int __mac_set_link(struct thread *td, struct __mac_set_link_args *uap) { struct label intlabel; struct nameidata nd; struct mount *mp; struct mac mac; char *buffer; int 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); } mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_vnode_label(&intlabel); return (error); } mtx_lock(&Giant); /* VFS */ 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); mtx_unlock(&Giant); /* VFS */ mac_destroy_vnode_label(&intlabel); return (error); } /* * MPSAFE */ int 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(SCARG(uap, policy), target, sizeof(target), NULL); if (error) return (error); error = ENOSYS; MAC_POLICY_LIST_BUSY(); 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, SCARG(uap, call), SCARG(uap, arg)); goto out; } } out: MAC_POLICY_LIST_UNBUSY(); return (error); } 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); #else /* !MAC */ int __mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) { return (ENOSYS); } int __mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) { return (ENOSYS); } int __mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) { return (ENOSYS); } int __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) { return (ENOSYS); } int __mac_get_file(struct thread *td, struct __mac_get_file_args *uap) { return (ENOSYS); } int __mac_get_link(struct thread *td, struct __mac_get_link_args *uap) { return (ENOSYS); } int __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) { return (ENOSYS); } int __mac_set_file(struct thread *td, struct __mac_set_file_args *uap) { return (ENOSYS); } int __mac_set_link(struct thread *td, struct __mac_set_link_args *uap) { return (ENOSYS); } int mac_syscall(struct thread *td, struct mac_syscall_args *uap) { return (ENOSYS); } #endif Index: head/sys/security/mac/mac_pipe.c =================================================================== --- head/sys/security/mac/mac_pipe.c (revision 106024) +++ head/sys/security/mac/mac_pipe.c (revision 106025) @@ -1,4010 +1,4038 @@ /*- * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson * Copyright (c) 2001 Ilmar S. Habibulin * Copyright (c) 2001, 2002 Networks Associates Technology, 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 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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. * * Framework for extensible kernel access control. Kernel and userland * interface to the framework, policy registration and composition. */ #include "opt_mac.h" #include "opt_devfs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MAC /* * Declare that the kernel provides MAC support, version 1. 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, 1); SYSCTL_DECL(_security); SYSCTL_NODE(_security, OID_AUTO, mac, CTLFLAG_RW, 0, "TrustedBSD MAC policy controls"); #if MAC_MAX_POLICIES > 32 #error "MAC_MAX_POLICIES too large" #endif static unsigned int mac_max_policies = MAC_MAX_POLICIES; static unsigned int mac_policy_offsets_free = (1 << MAC_MAX_POLICIES) - 1; SYSCTL_UINT(_security_mac, OID_AUTO, max_policies, CTLFLAG_RD, &mac_max_policies, 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; /* * Warn about EA transactions only the first time they happen. * Weak coherency, no locking. */ static int ea_warn_once = 0; static int mac_enforce_fs = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_fs, CTLFLAG_RW, &mac_enforce_fs, 0, "Enforce MAC policy on file system objects"); TUNABLE_INT("security.mac.enforce_fs", &mac_enforce_fs); static int mac_enforce_network = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_network, CTLFLAG_RW, &mac_enforce_network, 0, "Enforce MAC policy on network packets"); TUNABLE_INT("security.mac.enforce_network", &mac_enforce_network); static int mac_enforce_pipe = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_pipe, CTLFLAG_RW, &mac_enforce_pipe, 0, "Enforce MAC policy on pipe operations"); TUNABLE_INT("security.mac.enforce_pipe", &mac_enforce_pipe); static int mac_enforce_process = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_process, CTLFLAG_RW, &mac_enforce_process, 0, "Enforce MAC policy on inter-process operations"); TUNABLE_INT("security.mac.enforce_process", &mac_enforce_process); static int mac_enforce_reboot = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_reboot, CTLFLAG_RW, &mac_enforce_reboot, 0, "Enforce MAC policy for reboot operations"); TUNABLE_INT("security.mac.enforce_reboot", &mac_enforce_reboot); static int mac_enforce_socket = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_socket, CTLFLAG_RW, &mac_enforce_socket, 0, "Enforce MAC policy on socket operations"); TUNABLE_INT("security.mac.enforce_socket", &mac_enforce_socket); +static int mac_enforce_sysctl = 1; +SYSCTL_INT(_security_mac, OID_AUTO, enforce_sysctl, CTLFLAG_RW, + &mac_enforce_sysctl, 0, "Enforce MAC policy on sysctl operations"); +TUNABLE_INT("security.mac.enforce_sysctl", &mac_enforce_sysctl); + static int mac_enforce_vm = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_vm, CTLFLAG_RW, &mac_enforce_vm, 0, "Enforce MAC policy on vm operations"); TUNABLE_INT("security.mac.enforce_vm", &mac_enforce_vm); static int mac_cache_fslabel_in_vnode = 1; SYSCTL_INT(_security_mac, OID_AUTO, cache_fslabel_in_vnode, CTLFLAG_RW, &mac_cache_fslabel_in_vnode, 0, "Cache mount fslabel in vnode"); TUNABLE_INT("security.mac.cache_fslabel_in_vnode", &mac_cache_fslabel_in_vnode); 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"); #ifdef MAC_DEBUG SYSCTL_NODE(_security_mac, OID_AUTO, debug, CTLFLAG_RW, 0, "TrustedBSD MAC debug info"); static int mac_debug_label_fallback = 0; SYSCTL_INT(_security_mac_debug, OID_AUTO, label_fallback, CTLFLAG_RW, &mac_debug_label_fallback, 0, "Filesystems should fall back to fs label" "when label is corrupted."); TUNABLE_INT("security.mac.debug_label_fallback", &mac_debug_label_fallback); SYSCTL_NODE(_security_mac_debug, OID_AUTO, counters, CTLFLAG_RW, 0, "TrustedBSD MAC object counters"); static unsigned int nmacmbufs, nmaccreds, nmacifnets, nmacbpfdescs, nmacsockets, nmacmounts, nmactemp, nmacvnodes, nmacdevfsdirents, nmacipqs, nmacpipes; SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, mbufs, CTLFLAG_RD, &nmacmbufs, 0, "number of mbufs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, creds, CTLFLAG_RD, &nmaccreds, 0, "number of ucreds in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ifnets, CTLFLAG_RD, &nmacifnets, 0, "number of ifnets in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ipqs, CTLFLAG_RD, &nmacipqs, 0, "number of ipqs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, bpfdescs, CTLFLAG_RD, &nmacbpfdescs, 0, "number of bpfdescs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, sockets, CTLFLAG_RD, &nmacsockets, 0, "number of sockets in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, pipes, CTLFLAG_RD, &nmacpipes, 0, "number of pipes in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, mounts, CTLFLAG_RD, &nmacmounts, 0, "number of mounts in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, temp, CTLFLAG_RD, &nmactemp, 0, "number of temporary labels in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, vnodes, CTLFLAG_RD, &nmacvnodes, 0, "number of vnodes in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, devfsdirents, CTLFLAG_RD, &nmacdevfsdirents, 0, "number of devfs dirents inuse"); #endif static int error_select(int error1, int error2); static int mac_policy_register(struct mac_policy_conf *mpc); static int mac_policy_unregister(struct mac_policy_conf *mpc); static void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot); static void mac_cred_mmapped_drop_perms_recurse(struct thread *td, struct ucred *cred, struct vm_map *map); static void mac_destroy_socket_label(struct label *label); static int mac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *intlabel); MALLOC_DEFINE(M_MACOPVEC, "macopvec", "MAC policy operation vector"); MALLOC_DEFINE(M_MACPIPELABEL, "macpipelabel", "MAC labels for pipes"); MALLOC_DEFINE(M_MACTEMP, "mactemp", "MAC temporary label storage"); /* * mac_policy_list_lock protects the consistency of 'mac_policy_list', * the linked list of attached policy modules. Read-only consumers of * the list must acquire a shared lock for the duration of their use; * writers must acquire an exclusive lock. Note that for compound * operations, locks should be held for the entire compound operation, * and that this is not yet done for relabel requests. */ static struct mtx mac_policy_list_lock; static LIST_HEAD(, mac_policy_conf) mac_policy_list; static int mac_policy_list_busy; #define MAC_POLICY_LIST_LOCKINIT() mtx_init(&mac_policy_list_lock, \ "mac_policy_list_lock", NULL, MTX_DEF); #define MAC_POLICY_LIST_LOCK() mtx_lock(&mac_policy_list_lock); #define MAC_POLICY_LIST_UNLOCK() mtx_unlock(&mac_policy_list_lock); #define MAC_POLICY_LIST_BUSY() do { \ MAC_POLICY_LIST_LOCK(); \ mac_policy_list_busy++; \ MAC_POLICY_LIST_UNLOCK(); \ } while (0) #define MAC_POLICY_LIST_UNBUSY() do { \ MAC_POLICY_LIST_LOCK(); \ mac_policy_list_busy--; \ if (mac_policy_list_busy < 0) \ panic("Extra mac_policy_list_busy--"); \ MAC_POLICY_LIST_UNLOCK(); \ } while (0) /* * MAC_CHECK performs the designated check by walking the policy * module list and checking with each as to how it feels about the * request. Note that it returns its value via 'error' in the scope * of the caller. */ #define MAC_CHECK(check, args...) do { \ struct mac_policy_conf *mpc; \ \ error = 0; \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## check != NULL) \ error = error_select( \ mpc->mpc_ops->mpo_ ## check (args), \ error); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) /* * MAC_BOOLEAN performs the designated boolean composition by walking * the module list, invoking each instance of the operation, and * combining the results using the passed C operator. Note that it * returns its value via 'result' in the scope of the caller, which * should be initialized by the caller in a meaningful way to get * a meaningful result. */ #define MAC_BOOLEAN(operation, composition, args...) do { \ struct mac_policy_conf *mpc; \ \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ result = result composition \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) #define MAC_EXTERNALIZE(type, label, elementlist, outbuf, \ outbuflen) do { \ char *curptr, *curptr_start, *element_name, *element_temp; \ size_t left, left_start, len; \ int claimed, first, first_start, ignorenotfound; \ \ error = 0; \ element_temp = elementlist; \ curptr = outbuf; \ curptr[0] = '\0'; \ left = outbuflen; \ first = 1; \ while ((element_name = strsep(&element_temp, ",")) != NULL) { \ curptr_start = curptr; \ left_start = left; \ first_start = first; \ if (element_name[0] == '?') { \ element_name++; \ ignorenotfound = 1; \ } else \ ignorenotfound = 0; \ claimed = 0; \ if (first) { \ len = snprintf(curptr, left, "%s/", \ element_name); \ first = 0; \ } else \ len = snprintf(curptr, left, ",%s/", \ element_name); \ if (len >= left) { \ error = EINVAL; /* XXXMAC: E2BIG */ \ break; \ } \ curptr += len; \ left -= len; \ \ MAC_CHECK(externalize_ ## type, label, element_name, \ curptr, left, &len, &claimed); \ if (error) \ break; \ if (claimed == 1) { \ if (len >= outbuflen) { \ error = EINVAL; /* XXXMAC: E2BIG */ \ break; \ } \ curptr += len; \ left -= len; \ } else if (claimed == 0 && ignorenotfound) { \ /* \ * Revert addition of the label element \ * name. \ */ \ curptr = curptr_start; \ *curptr = '\0'; \ left = left_start; \ first = first_start; \ } else { \ error = EINVAL; /* XXXMAC: ENOLABEL */ \ break; \ } \ } \ } while (0) #define MAC_INTERNALIZE(type, label, instring) do { \ char *element, *element_name, *element_data; \ int claimed; \ \ error = 0; \ element = instring; \ while ((element_name = strsep(&element, ",")) != NULL) { \ element_data = element_name; \ element_name = strsep(&element_data, "/"); \ if (element_data == NULL) { \ error = EINVAL; \ break; \ } \ claimed = 0; \ MAC_CHECK(internalize_ ## type, label, element_name, \ element_data, &claimed); \ if (error) \ break; \ if (claimed != 1) { \ /* XXXMAC: Another error here? */ \ error = EINVAL; \ break; \ } \ } \ } while (0) /* * MAC_PERFORM performs the designated operation by walking the policy * module list and invoking that operation for each policy. */ #define MAC_PERFORM(operation, args...) do { \ struct mac_policy_conf *mpc; \ \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) /* * Initialize the MAC subsystem, including appropriate SMP locks. */ static void mac_init(void) { LIST_INIT(&mac_policy_list); MAC_POLICY_LIST_LOCKINIT(); } /* * 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; } /* * 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; 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: break; } return (error); } static int mac_policy_register(struct mac_policy_conf *mpc) { struct mac_policy_conf *tmpc; struct mac_policy_op_entry *mpe; int slot; MALLOC(mpc->mpc_ops, struct mac_policy_ops *, sizeof(*mpc->mpc_ops), M_MACOPVEC, M_WAITOK | M_ZERO); for (mpe = mpc->mpc_entries; mpe->mpe_constant != MAC_OP_LAST; mpe++) { switch (mpe->mpe_constant) { case MAC_OP_LAST: /* * Doesn't actually happen, but this allows checking * that all enumerated values are handled. */ break; case MAC_DESTROY: mpc->mpc_ops->mpo_destroy = mpe->mpe_function; break; case MAC_INIT: mpc->mpc_ops->mpo_init = mpe->mpe_function; break; case MAC_SYSCALL: mpc->mpc_ops->mpo_syscall = mpe->mpe_function; break; case MAC_INIT_BPFDESC_LABEL: mpc->mpc_ops->mpo_init_bpfdesc_label = mpe->mpe_function; break; case MAC_INIT_CRED_LABEL: mpc->mpc_ops->mpo_init_cred_label = mpe->mpe_function; break; case MAC_INIT_DEVFSDIRENT_LABEL: mpc->mpc_ops->mpo_init_devfsdirent_label = mpe->mpe_function; break; case MAC_INIT_IFNET_LABEL: mpc->mpc_ops->mpo_init_ifnet_label = mpe->mpe_function; break; case MAC_INIT_IPQ_LABEL: mpc->mpc_ops->mpo_init_ipq_label = mpe->mpe_function; break; case MAC_INIT_MBUF_LABEL: mpc->mpc_ops->mpo_init_mbuf_label = mpe->mpe_function; break; case MAC_INIT_MOUNT_LABEL: mpc->mpc_ops->mpo_init_mount_label = mpe->mpe_function; break; case MAC_INIT_MOUNT_FS_LABEL: mpc->mpc_ops->mpo_init_mount_fs_label = mpe->mpe_function; break; case MAC_INIT_PIPE_LABEL: mpc->mpc_ops->mpo_init_pipe_label = mpe->mpe_function; break; case MAC_INIT_SOCKET_LABEL: mpc->mpc_ops->mpo_init_socket_label = mpe->mpe_function; break; case MAC_INIT_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_init_socket_peer_label = mpe->mpe_function; break; case MAC_INIT_VNODE_LABEL: mpc->mpc_ops->mpo_init_vnode_label = mpe->mpe_function; break; case MAC_DESTROY_BPFDESC_LABEL: mpc->mpc_ops->mpo_destroy_bpfdesc_label = mpe->mpe_function; break; case MAC_DESTROY_CRED_LABEL: mpc->mpc_ops->mpo_destroy_cred_label = mpe->mpe_function; break; case MAC_DESTROY_DEVFSDIRENT_LABEL: mpc->mpc_ops->mpo_destroy_devfsdirent_label = mpe->mpe_function; break; case MAC_DESTROY_IFNET_LABEL: mpc->mpc_ops->mpo_destroy_ifnet_label = mpe->mpe_function; break; case MAC_DESTROY_IPQ_LABEL: mpc->mpc_ops->mpo_destroy_ipq_label = mpe->mpe_function; break; case MAC_DESTROY_MBUF_LABEL: mpc->mpc_ops->mpo_destroy_mbuf_label = mpe->mpe_function; break; case MAC_DESTROY_MOUNT_LABEL: mpc->mpc_ops->mpo_destroy_mount_label = mpe->mpe_function; break; case MAC_DESTROY_MOUNT_FS_LABEL: mpc->mpc_ops->mpo_destroy_mount_fs_label = mpe->mpe_function; break; case MAC_DESTROY_PIPE_LABEL: mpc->mpc_ops->mpo_destroy_pipe_label = mpe->mpe_function; break; case MAC_DESTROY_SOCKET_LABEL: mpc->mpc_ops->mpo_destroy_socket_label = mpe->mpe_function; break; case MAC_DESTROY_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_destroy_socket_peer_label = mpe->mpe_function; break; case MAC_DESTROY_VNODE_LABEL: mpc->mpc_ops->mpo_destroy_vnode_label = mpe->mpe_function; break; case MAC_COPY_PIPE_LABEL: mpc->mpc_ops->mpo_copy_pipe_label = mpe->mpe_function; break; case MAC_COPY_VNODE_LABEL: mpc->mpc_ops->mpo_copy_vnode_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_CRED_LABEL: mpc->mpc_ops->mpo_externalize_cred_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_IFNET_LABEL: mpc->mpc_ops->mpo_externalize_ifnet_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_PIPE_LABEL: mpc->mpc_ops->mpo_externalize_pipe_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_SOCKET_LABEL: mpc->mpc_ops->mpo_externalize_socket_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_externalize_socket_peer_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_VNODE_LABEL: mpc->mpc_ops->mpo_externalize_vnode_label = mpe->mpe_function; break; case MAC_INTERNALIZE_CRED_LABEL: mpc->mpc_ops->mpo_internalize_cred_label = mpe->mpe_function; break; case MAC_INTERNALIZE_IFNET_LABEL: mpc->mpc_ops->mpo_internalize_ifnet_label = mpe->mpe_function; break; case MAC_INTERNALIZE_PIPE_LABEL: mpc->mpc_ops->mpo_internalize_pipe_label = mpe->mpe_function; break; case MAC_INTERNALIZE_SOCKET_LABEL: mpc->mpc_ops->mpo_internalize_socket_label = mpe->mpe_function; break; case MAC_INTERNALIZE_VNODE_LABEL: mpc->mpc_ops->mpo_internalize_vnode_label = mpe->mpe_function; break; case MAC_CREATE_DEVFS_DEVICE: mpc->mpc_ops->mpo_create_devfs_device = mpe->mpe_function; break; case MAC_CREATE_DEVFS_DIRECTORY: mpc->mpc_ops->mpo_create_devfs_directory = mpe->mpe_function; break; case MAC_CREATE_DEVFS_SYMLINK: mpc->mpc_ops->mpo_create_devfs_symlink = mpe->mpe_function; break; case MAC_CREATE_DEVFS_VNODE: mpc->mpc_ops->mpo_create_devfs_vnode = mpe->mpe_function; break; case MAC_CREATE_MOUNT: mpc->mpc_ops->mpo_create_mount = mpe->mpe_function; break; case MAC_CREATE_ROOT_MOUNT: mpc->mpc_ops->mpo_create_root_mount = mpe->mpe_function; break; case MAC_RELABEL_VNODE: mpc->mpc_ops->mpo_relabel_vnode = mpe->mpe_function; break; case MAC_UPDATE_DEVFSDIRENT: mpc->mpc_ops->mpo_update_devfsdirent = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_DEVFS: mpc->mpc_ops->mpo_associate_vnode_devfs = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_EXTATTR: mpc->mpc_ops->mpo_associate_vnode_extattr = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_SINGLELABEL: mpc->mpc_ops->mpo_associate_vnode_singlelabel = mpe->mpe_function; break; case MAC_CREATE_VNODE_EXTATTR: mpc->mpc_ops->mpo_create_vnode_extattr = mpe->mpe_function; break; case MAC_SETLABEL_VNODE_EXTATTR: mpc->mpc_ops->mpo_setlabel_vnode_extattr = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_SOCKET: mpc->mpc_ops->mpo_create_mbuf_from_socket = mpe->mpe_function; break; case MAC_CREATE_PIPE: mpc->mpc_ops->mpo_create_pipe = mpe->mpe_function; break; case MAC_CREATE_SOCKET: mpc->mpc_ops->mpo_create_socket = mpe->mpe_function; break; case MAC_CREATE_SOCKET_FROM_SOCKET: mpc->mpc_ops->mpo_create_socket_from_socket = mpe->mpe_function; break; case MAC_RELABEL_PIPE: mpc->mpc_ops->mpo_relabel_pipe = mpe->mpe_function; break; case MAC_RELABEL_SOCKET: mpc->mpc_ops->mpo_relabel_socket = mpe->mpe_function; break; case MAC_SET_SOCKET_PEER_FROM_MBUF: mpc->mpc_ops->mpo_set_socket_peer_from_mbuf = mpe->mpe_function; break; case MAC_SET_SOCKET_PEER_FROM_SOCKET: mpc->mpc_ops->mpo_set_socket_peer_from_socket = mpe->mpe_function; break; case MAC_CREATE_BPFDESC: mpc->mpc_ops->mpo_create_bpfdesc = mpe->mpe_function; break; case MAC_CREATE_DATAGRAM_FROM_IPQ: mpc->mpc_ops->mpo_create_datagram_from_ipq = mpe->mpe_function; break; case MAC_CREATE_FRAGMENT: mpc->mpc_ops->mpo_create_fragment = mpe->mpe_function; break; case MAC_CREATE_IFNET: mpc->mpc_ops->mpo_create_ifnet = mpe->mpe_function; break; case MAC_CREATE_IPQ: mpc->mpc_ops->mpo_create_ipq = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_MBUF: mpc->mpc_ops->mpo_create_mbuf_from_mbuf = mpe->mpe_function; break; case MAC_CREATE_MBUF_LINKLAYER: mpc->mpc_ops->mpo_create_mbuf_linklayer = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_BPFDESC: mpc->mpc_ops->mpo_create_mbuf_from_bpfdesc = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_IFNET: mpc->mpc_ops->mpo_create_mbuf_from_ifnet = mpe->mpe_function; break; case MAC_CREATE_MBUF_MULTICAST_ENCAP: mpc->mpc_ops->mpo_create_mbuf_multicast_encap = mpe->mpe_function; break; case MAC_CREATE_MBUF_NETLAYER: mpc->mpc_ops->mpo_create_mbuf_netlayer = mpe->mpe_function; break; case MAC_FRAGMENT_MATCH: mpc->mpc_ops->mpo_fragment_match = mpe->mpe_function; break; case MAC_RELABEL_IFNET: mpc->mpc_ops->mpo_relabel_ifnet = mpe->mpe_function; break; case MAC_UPDATE_IPQ: mpc->mpc_ops->mpo_update_ipq = mpe->mpe_function; break; case MAC_CREATE_CRED: mpc->mpc_ops->mpo_create_cred = mpe->mpe_function; break; case MAC_EXECVE_TRANSITION: mpc->mpc_ops->mpo_execve_transition = mpe->mpe_function; break; case MAC_EXECVE_WILL_TRANSITION: mpc->mpc_ops->mpo_execve_will_transition = mpe->mpe_function; break; case MAC_CREATE_PROC0: mpc->mpc_ops->mpo_create_proc0 = mpe->mpe_function; break; case MAC_CREATE_PROC1: mpc->mpc_ops->mpo_create_proc1 = mpe->mpe_function; break; case MAC_RELABEL_CRED: mpc->mpc_ops->mpo_relabel_cred = mpe->mpe_function; break; case MAC_THREAD_USERRET: mpc->mpc_ops->mpo_thread_userret = mpe->mpe_function; break; case MAC_CHECK_BPFDESC_RECEIVE: mpc->mpc_ops->mpo_check_bpfdesc_receive = mpe->mpe_function; break; case MAC_CHECK_CRED_RELABEL: mpc->mpc_ops->mpo_check_cred_relabel = mpe->mpe_function; break; case MAC_CHECK_CRED_VISIBLE: mpc->mpc_ops->mpo_check_cred_visible = mpe->mpe_function; break; case MAC_CHECK_IFNET_RELABEL: mpc->mpc_ops->mpo_check_ifnet_relabel = mpe->mpe_function; break; case MAC_CHECK_IFNET_TRANSMIT: mpc->mpc_ops->mpo_check_ifnet_transmit = mpe->mpe_function; break; case MAC_CHECK_MOUNT_STAT: mpc->mpc_ops->mpo_check_mount_stat = mpe->mpe_function; break; case MAC_CHECK_PIPE_IOCTL: mpc->mpc_ops->mpo_check_pipe_ioctl = mpe->mpe_function; break; case MAC_CHECK_PIPE_POLL: mpc->mpc_ops->mpo_check_pipe_poll = mpe->mpe_function; break; case MAC_CHECK_PIPE_READ: mpc->mpc_ops->mpo_check_pipe_read = mpe->mpe_function; break; case MAC_CHECK_PIPE_RELABEL: mpc->mpc_ops->mpo_check_pipe_relabel = mpe->mpe_function; break; case MAC_CHECK_PIPE_STAT: mpc->mpc_ops->mpo_check_pipe_stat = mpe->mpe_function; break; case MAC_CHECK_PIPE_WRITE: mpc->mpc_ops->mpo_check_pipe_write = mpe->mpe_function; break; case MAC_CHECK_PROC_DEBUG: mpc->mpc_ops->mpo_check_proc_debug = mpe->mpe_function; break; case MAC_CHECK_PROC_SCHED: mpc->mpc_ops->mpo_check_proc_sched = mpe->mpe_function; break; case MAC_CHECK_PROC_SIGNAL: mpc->mpc_ops->mpo_check_proc_signal = mpe->mpe_function; break; case MAC_CHECK_SOCKET_BIND: mpc->mpc_ops->mpo_check_socket_bind = mpe->mpe_function; break; case MAC_CHECK_SOCKET_CONNECT: mpc->mpc_ops->mpo_check_socket_connect = mpe->mpe_function; break; case MAC_CHECK_SOCKET_DELIVER: mpc->mpc_ops->mpo_check_socket_deliver = mpe->mpe_function; break; case MAC_CHECK_SOCKET_LISTEN: mpc->mpc_ops->mpo_check_socket_listen = mpe->mpe_function; break; case MAC_CHECK_SOCKET_RECEIVE: mpc->mpc_ops->mpo_check_socket_receive = mpe->mpe_function; break; case MAC_CHECK_SOCKET_RELABEL: mpc->mpc_ops->mpo_check_socket_relabel = mpe->mpe_function; break; case MAC_CHECK_SOCKET_SEND: mpc->mpc_ops->mpo_check_socket_send = mpe->mpe_function; break; case MAC_CHECK_SOCKET_VISIBLE: mpc->mpc_ops->mpo_check_socket_visible = mpe->mpe_function; break; case MAC_CHECK_SYSTEM_REBOOT: mpc->mpc_ops->mpo_check_system_reboot = mpe->mpe_function; break; case MAC_CHECK_SYSTEM_SWAPON: mpc->mpc_ops->mpo_check_system_swapon = mpe->mpe_function; break; + case MAC_CHECK_SYSTEM_SYSCTL: + mpc->mpc_ops->mpo_check_system_sysctl = + mpe->mpe_function; + break; case MAC_CHECK_VNODE_ACCESS: mpc->mpc_ops->mpo_check_vnode_access = mpe->mpe_function; break; case MAC_CHECK_VNODE_CHDIR: mpc->mpc_ops->mpo_check_vnode_chdir = mpe->mpe_function; break; case MAC_CHECK_VNODE_CHROOT: mpc->mpc_ops->mpo_check_vnode_chroot = mpe->mpe_function; break; case MAC_CHECK_VNODE_CREATE: mpc->mpc_ops->mpo_check_vnode_create = mpe->mpe_function; break; case MAC_CHECK_VNODE_DELETE: mpc->mpc_ops->mpo_check_vnode_delete = mpe->mpe_function; break; case MAC_CHECK_VNODE_DELETEACL: mpc->mpc_ops->mpo_check_vnode_deleteacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_EXEC: mpc->mpc_ops->mpo_check_vnode_exec = mpe->mpe_function; break; case MAC_CHECK_VNODE_GETACL: mpc->mpc_ops->mpo_check_vnode_getacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_GETEXTATTR: mpc->mpc_ops->mpo_check_vnode_getextattr = mpe->mpe_function; break; case MAC_CHECK_VNODE_LINK: mpc->mpc_ops->mpo_check_vnode_link = mpe->mpe_function; break; case MAC_CHECK_VNODE_LOOKUP: mpc->mpc_ops->mpo_check_vnode_lookup = mpe->mpe_function; break; case MAC_CHECK_VNODE_MMAP: mpc->mpc_ops->mpo_check_vnode_mmap = mpe->mpe_function; break; case MAC_CHECK_VNODE_MMAP_DOWNGRADE: mpc->mpc_ops->mpo_check_vnode_mmap_downgrade = mpe->mpe_function; break; case MAC_CHECK_VNODE_MPROTECT: mpc->mpc_ops->mpo_check_vnode_mprotect = mpe->mpe_function; break; case MAC_CHECK_VNODE_OPEN: mpc->mpc_ops->mpo_check_vnode_open = mpe->mpe_function; break; case MAC_CHECK_VNODE_POLL: mpc->mpc_ops->mpo_check_vnode_poll = mpe->mpe_function; break; case MAC_CHECK_VNODE_READ: mpc->mpc_ops->mpo_check_vnode_read = mpe->mpe_function; break; case MAC_CHECK_VNODE_READDIR: mpc->mpc_ops->mpo_check_vnode_readdir = mpe->mpe_function; break; case MAC_CHECK_VNODE_READLINK: mpc->mpc_ops->mpo_check_vnode_readlink = mpe->mpe_function; break; case MAC_CHECK_VNODE_RELABEL: mpc->mpc_ops->mpo_check_vnode_relabel = mpe->mpe_function; break; case MAC_CHECK_VNODE_RENAME_FROM: mpc->mpc_ops->mpo_check_vnode_rename_from = mpe->mpe_function; break; case MAC_CHECK_VNODE_RENAME_TO: mpc->mpc_ops->mpo_check_vnode_rename_to = mpe->mpe_function; break; case MAC_CHECK_VNODE_REVOKE: mpc->mpc_ops->mpo_check_vnode_revoke = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETACL: mpc->mpc_ops->mpo_check_vnode_setacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETEXTATTR: mpc->mpc_ops->mpo_check_vnode_setextattr = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETFLAGS: mpc->mpc_ops->mpo_check_vnode_setflags = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETMODE: mpc->mpc_ops->mpo_check_vnode_setmode = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETOWNER: mpc->mpc_ops->mpo_check_vnode_setowner = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETUTIMES: mpc->mpc_ops->mpo_check_vnode_setutimes = mpe->mpe_function; break; case MAC_CHECK_VNODE_STAT: mpc->mpc_ops->mpo_check_vnode_stat = mpe->mpe_function; break; case MAC_CHECK_VNODE_WRITE: mpc->mpc_ops->mpo_check_vnode_write = mpe->mpe_function; break; /* default: printf("MAC policy `%s': unknown operation %d\n", mpc->mpc_name, mpe->mpe_constant); return (EINVAL); */ } } MAC_POLICY_LIST_LOCK(); if (mac_policy_list_busy > 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (EBUSY); } LIST_FOREACH(tmpc, &mac_policy_list, mpc_list) { if (strcmp(tmpc->mpc_name, mpc->mpc_name) == 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (EEXIST); } } if (mpc->mpc_field_off != NULL) { slot = ffs(mac_policy_offsets_free); if (slot == 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (ENOMEM); } slot--; mac_policy_offsets_free &= ~(1 << slot); *mpc->mpc_field_off = slot; } mpc->mpc_runtime_flags |= MPC_RUNTIME_FLAG_REGISTERED; LIST_INSERT_HEAD(&mac_policy_list, mpc, mpc_list); /* Per-policy initialization. */ if (mpc->mpc_ops->mpo_init != NULL) (*(mpc->mpc_ops->mpo_init))(mpc); MAC_POLICY_LIST_UNLOCK(); printf("Security policy loaded: %s (%s)\n", mpc->mpc_fullname, mpc->mpc_name); return (0); } 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_LIST_LOCK(); if ((mpc->mpc_runtime_flags & MPC_RUNTIME_FLAG_REGISTERED) == 0) { MAC_POLICY_LIST_UNLOCK(); return (0); } #if 0 /* * Don't allow unloading modules with private data. */ if (mpc->mpc_field_off != NULL) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } #endif /* * Only allow the unload to proceed if the module is unloadable * by its own definition. */ if ((mpc->mpc_loadtime_flags & MPC_LOADTIME_FLAG_UNLOADOK) == 0) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } /* * Right now, we EBUSY if the list is in use. In the future, * for reliability reasons, we might want to sleep and wakeup * later to try again. */ if (mac_policy_list_busy > 0) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } if (mpc->mpc_ops->mpo_destroy != NULL) (*(mpc->mpc_ops->mpo_destroy))(mpc); LIST_REMOVE(mpc, mpc_list); MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; mpc->mpc_runtime_flags &= ~MPC_RUNTIME_FLAG_REGISTERED; printf("Security policy unload: %s (%s)\n", mpc->mpc_fullname, mpc->mpc_name); return (0); } /* * Define an error value precedence, and given two arguments, selects the * value with the higher precedence. */ static int 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); } static void mac_init_label(struct label *label) { bzero(label, sizeof(*label)); label->l_flags = MAC_FLAG_INITIALIZED; } static void mac_destroy_label(struct label *label) { KASSERT(label->l_flags & MAC_FLAG_INITIALIZED, ("destroying uninitialized label")); bzero(label, sizeof(*label)); /* implicit: label->l_flags &= ~MAC_FLAG_INITIALIZED; */ } void mac_init_bpfdesc(struct bpf_d *bpf_d) { mac_init_label(&bpf_d->bd_label); MAC_PERFORM(init_bpfdesc_label, &bpf_d->bd_label); #ifdef MAC_DEBUG atomic_add_int(&nmacbpfdescs, 1); #endif } static void mac_init_cred_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_cred_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmaccreds, 1); #endif } void mac_init_cred(struct ucred *cred) { mac_init_cred_label(&cred->cr_label); } void mac_init_devfsdirent(struct devfs_dirent *de) { mac_init_label(&de->de_label); MAC_PERFORM(init_devfsdirent_label, &de->de_label); #ifdef MAC_DEBUG atomic_add_int(&nmacdevfsdirents, 1); #endif } static void mac_init_ifnet_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_ifnet_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacifnets, 1); #endif } void mac_init_ifnet(struct ifnet *ifp) { mac_init_ifnet_label(&ifp->if_label); } void mac_init_ipq(struct ipq *ipq) { mac_init_label(&ipq->ipq_label); MAC_PERFORM(init_ipq_label, &ipq->ipq_label); #ifdef MAC_DEBUG atomic_add_int(&nmacipqs, 1); #endif } int mac_init_mbuf(struct mbuf *m, int flag) { int error; KASSERT(m->m_flags & M_PKTHDR, ("mac_init_mbuf on non-header mbuf")); mac_init_label(&m->m_pkthdr.label); MAC_CHECK(init_mbuf_label, &m->m_pkthdr.label, flag); if (error) { MAC_PERFORM(destroy_mbuf_label, &m->m_pkthdr.label); mac_destroy_label(&m->m_pkthdr.label); } #ifdef MAC_DEBUG if (error == 0) atomic_add_int(&nmacmbufs, 1); #endif return (error); } void mac_init_mount(struct mount *mp) { mac_init_label(&mp->mnt_mntlabel); mac_init_label(&mp->mnt_fslabel); MAC_PERFORM(init_mount_label, &mp->mnt_mntlabel); MAC_PERFORM(init_mount_fs_label, &mp->mnt_fslabel); #ifdef MAC_DEBUG atomic_add_int(&nmacmounts, 1); #endif } static void mac_init_pipe_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_pipe_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacpipes, 1); #endif } void mac_init_pipe(struct pipe *pipe) { struct label *label; label = malloc(sizeof(struct label), M_MACPIPELABEL, M_ZERO|M_WAITOK); pipe->pipe_label = label; pipe->pipe_peer->pipe_label = label; mac_init_pipe_label(label); } static int mac_init_socket_label(struct label *label, int flag) { int error; mac_init_label(label); MAC_CHECK(init_socket_label, label, flag); if (error) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); } #ifdef MAC_DEBUG if (error == 0) atomic_add_int(&nmacsockets, 1); #endif return (error); } static int mac_init_socket_peer_label(struct label *label, int flag) { int error; mac_init_label(label); MAC_CHECK(init_socket_peer_label, label, flag); if (error) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); } return (error); } int mac_init_socket(struct socket *socket, int flag) { int error; error = mac_init_socket_label(&socket->so_label, flag); if (error) return (error); error = mac_init_socket_peer_label(&socket->so_peerlabel, flag); if (error) mac_destroy_socket_label(&socket->so_label); return (error); } void mac_init_vnode_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_vnode_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacvnodes, 1); #endif } void mac_init_vnode(struct vnode *vp) { mac_init_vnode_label(&vp->v_label); } void mac_destroy_bpfdesc(struct bpf_d *bpf_d) { MAC_PERFORM(destroy_bpfdesc_label, &bpf_d->bd_label); mac_destroy_label(&bpf_d->bd_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacbpfdescs, 1); #endif } static void mac_destroy_cred_label(struct label *label) { MAC_PERFORM(destroy_cred_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmaccreds, 1); #endif } void mac_destroy_cred(struct ucred *cred) { mac_destroy_cred_label(&cred->cr_label); } void mac_destroy_devfsdirent(struct devfs_dirent *de) { MAC_PERFORM(destroy_devfsdirent_label, &de->de_label); mac_destroy_label(&de->de_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacdevfsdirents, 1); #endif } static void mac_destroy_ifnet_label(struct label *label) { MAC_PERFORM(destroy_ifnet_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacifnets, 1); #endif } void mac_destroy_ifnet(struct ifnet *ifp) { mac_destroy_ifnet_label(&ifp->if_label); } void mac_destroy_ipq(struct ipq *ipq) { MAC_PERFORM(destroy_ipq_label, &ipq->ipq_label); mac_destroy_label(&ipq->ipq_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacipqs, 1); #endif } void mac_destroy_mbuf(struct mbuf *m) { MAC_PERFORM(destroy_mbuf_label, &m->m_pkthdr.label); mac_destroy_label(&m->m_pkthdr.label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacmbufs, 1); #endif } void mac_destroy_mount(struct mount *mp) { MAC_PERFORM(destroy_mount_label, &mp->mnt_mntlabel); MAC_PERFORM(destroy_mount_fs_label, &mp->mnt_fslabel); mac_destroy_label(&mp->mnt_fslabel); mac_destroy_label(&mp->mnt_mntlabel); #ifdef MAC_DEBUG atomic_subtract_int(&nmacmounts, 1); #endif } static void mac_destroy_pipe_label(struct label *label) { MAC_PERFORM(destroy_pipe_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacpipes, 1); #endif } void mac_destroy_pipe(struct pipe *pipe) { mac_destroy_pipe_label(pipe->pipe_label); free(pipe->pipe_label, M_MACPIPELABEL); } static void mac_destroy_socket_label(struct label *label) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacsockets, 1); #endif } static void mac_destroy_socket_peer_label(struct label *label) { MAC_PERFORM(destroy_socket_peer_label, label); mac_destroy_label(label); } void mac_destroy_socket(struct socket *socket) { mac_destroy_socket_label(&socket->so_label); mac_destroy_socket_peer_label(&socket->so_peerlabel); } void mac_destroy_vnode_label(struct label *label) { MAC_PERFORM(destroy_vnode_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacvnodes, 1); #endif } void mac_destroy_vnode(struct vnode *vp) { mac_destroy_vnode_label(&vp->v_label); } static void mac_copy_pipe_label(struct label *src, struct label *dest) { MAC_PERFORM(copy_pipe_label, src, dest); } void mac_copy_vnode_label(struct label *src, struct label *dest) { MAC_PERFORM(copy_vnode_label, src, dest); } static int mac_check_structmac_consistent(struct mac *mac) { if (mac->m_buflen > MAC_MAX_LABEL_BUF_LEN) return (EINVAL); return (0); } static int mac_externalize_cred_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(cred_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_ifnet_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(ifnet_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_pipe_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(pipe_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_socket_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(socket_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_socket_peer_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(socket_peer_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_vnode_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(vnode_label, label, elements, outbuf, outbuflen); return (error); } static int mac_internalize_cred_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(cred_label, label, string); return (error); } static int mac_internalize_ifnet_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(ifnet_label, label, string); return (error); } static int mac_internalize_pipe_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(pipe_label, label, string); return (error); } static int mac_internalize_socket_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(socket_label, label, string); return (error); } static int mac_internalize_vnode_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(vnode_label, label, string); return (error); } /* * Initialize MAC label for the first kernel process, from which other * kernel processes and threads are spawned. */ void mac_create_proc0(struct ucred *cred) { MAC_PERFORM(create_proc0, cred); } /* * Initialize MAC label for the first userland process, from which other * userland processes and threads are spawned. */ void mac_create_proc1(struct ucred *cred) { MAC_PERFORM(create_proc1, cred); } void mac_thread_userret(struct thread *td) { MAC_PERFORM(thread_userret, td); } /* * 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_create_cred(struct ucred *parent_cred, struct ucred *child_cred) { MAC_PERFORM(create_cred, parent_cred, child_cred); } void mac_update_devfsdirent(struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(update_devfsdirent, de, &de->de_label, vp, &vp->v_label); } void mac_associate_vnode_devfs(struct mount *mp, struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(associate_vnode_devfs, mp, &mp->mnt_fslabel, de, &de->de_label, vp, &vp->v_label); } int mac_associate_vnode_extattr(struct mount *mp, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_associate_vnode_extattr"); MAC_CHECK(associate_vnode_extattr, mp, &mp->mnt_fslabel, vp, &vp->v_label); return (error); } void mac_associate_vnode_singlelabel(struct mount *mp, struct vnode *vp) { MAC_PERFORM(associate_vnode_singlelabel, mp, &mp->mnt_fslabel, vp, &vp->v_label); } int mac_create_vnode_extattr(struct ucred *cred, struct mount *mp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_create_vnode_extattr"); ASSERT_VOP_LOCKED(vp, "mac_create_vnode_extattr"); error = VOP_OPENEXTATTR(vp, cred, curthread); if (error == EOPNOTSUPP) { /* XXX: Optionally abort if transactions not supported. */ if (ea_warn_once == 0) { printf("Warning: transactions not supported " "in EA write.\n"); ea_warn_once = 1; } } else if (error) return (error); MAC_CHECK(create_vnode_extattr, cred, mp, &mp->mnt_fslabel, 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; /* XXX */ return (error); } static int mac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *intlabel) { int error; ASSERT_VOP_LOCKED(vp, "mac_setlabel_vnode_extattr"); error = VOP_OPENEXTATTR(vp, cred, curthread); if (error == EOPNOTSUPP) { /* XXX: Optionally abort if transactions not supported. */ if (ea_warn_once == 0) { printf("Warning: transactions not supported " "in EA write.\n"); ea_warn_once = 1; } } else if (error) return (error); MAC_CHECK(setlabel_vnode_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; /* XXX */ return (error); } void mac_execve_transition(struct ucred *old, struct ucred *new, struct vnode *vp) { ASSERT_VOP_LOCKED(vp, "mac_execve_transition"); MAC_PERFORM(execve_transition, old, new, vp, &vp->v_label); } int mac_execve_will_transition(struct ucred *old, struct vnode *vp) { int result; result = 0; MAC_BOOLEAN(execve_will_transition, ||, old, vp, &vp->v_label); return (result); } int mac_check_vnode_access(struct ucred *cred, struct vnode *vp, int flags) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_access"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_access, cred, vp, &vp->v_label, flags); return (error); } int mac_check_vnode_chdir(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_chdir"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_chdir, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_chroot(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_chroot"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_chroot, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_create(struct ucred *cred, struct vnode *dvp, struct componentname *cnp, struct vattr *vap) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_create"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_create, cred, dvp, &dvp->v_label, cnp, vap); return (error); } int mac_check_vnode_delete(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_delete"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_delete"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_delete, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp, acl_type_t type) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_deleteacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_deleteacl, cred, vp, &vp->v_label, type); return (error); } int mac_check_vnode_exec(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_exec"); if (!mac_enforce_process && !mac_enforce_fs) return (0); MAC_CHECK(check_vnode_exec, cred, vp, &vp->v_label); return (error); } int mac_check_vnode_getacl(struct ucred *cred, struct vnode *vp, acl_type_t type) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_getacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_getacl, cred, vp, &vp->v_label, type); return (error); } int mac_check_vnode_getextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_getextattr"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_getextattr, cred, vp, &vp->v_label, attrnamespace, name, uio); return (error); } int mac_check_vnode_link(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_link"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_link"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_link, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_lookup(struct ucred *cred, struct vnode *dvp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_lookup"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_lookup, cred, dvp, &dvp->v_label, cnp); return (error); } int mac_check_vnode_mmap(struct ucred *cred, struct vnode *vp, int prot) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mmap"); if (!mac_enforce_fs || !mac_enforce_vm) return (0); MAC_CHECK(check_vnode_mmap, cred, vp, &vp->v_label, prot); return (error); } void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot) { int result = *prot; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mmap_downgrade"); if (!mac_enforce_fs || !mac_enforce_vm) return; MAC_PERFORM(check_vnode_mmap_downgrade, cred, vp, &vp->v_label, &result); *prot = result; } int mac_check_vnode_mprotect(struct ucred *cred, struct vnode *vp, int prot) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mprotect"); if (!mac_enforce_fs || !mac_enforce_vm) return (0); MAC_CHECK(check_vnode_mprotect, cred, vp, &vp->v_label, prot); return (error); } int mac_check_vnode_open(struct ucred *cred, struct vnode *vp, mode_t acc_mode) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_open"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_open, cred, vp, &vp->v_label, acc_mode); return (error); } int mac_check_vnode_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_poll"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_poll, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_read"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_read, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_readdir(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_readdir"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_readdir, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_readlink(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_readlink"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_readlink, cred, vp, &vp->v_label); return (error); } static int mac_check_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *newlabel) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_relabel"); MAC_CHECK(check_vnode_relabel, cred, vp, &vp->v_label, newlabel); return (error); } int mac_check_vnode_rename_from(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_rename_from"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_rename_from"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_rename_from, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp, struct vnode *vp, int samedir, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_rename_to"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_rename_to"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_rename_to, cred, dvp, &dvp->v_label, vp, vp != NULL ? &vp->v_label : NULL, samedir, cnp); return (error); } int mac_check_vnode_revoke(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_revoke"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_revoke, cred, vp, &vp->v_label); return (error); } int mac_check_vnode_setacl(struct ucred *cred, struct vnode *vp, acl_type_t type, struct acl *acl) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setacl, cred, vp, &vp->v_label, type, acl); return (error); } int mac_check_vnode_setextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setextattr"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setextattr, cred, vp, &vp->v_label, attrnamespace, name, uio); return (error); } int mac_check_vnode_setflags(struct ucred *cred, struct vnode *vp, u_long flags) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setflags"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setflags, cred, vp, &vp->v_label, flags); return (error); } int mac_check_vnode_setmode(struct ucred *cred, struct vnode *vp, mode_t mode) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setmode"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setmode, cred, vp, &vp->v_label, mode); return (error); } int mac_check_vnode_setowner(struct ucred *cred, struct vnode *vp, uid_t uid, gid_t gid) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setowner"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setowner, cred, vp, &vp->v_label, uid, gid); return (error); } int mac_check_vnode_setutimes(struct ucred *cred, struct vnode *vp, struct timespec atime, struct timespec mtime) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setutimes"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setutimes, cred, vp, &vp->v_label, atime, mtime); return (error); } int mac_check_vnode_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_stat"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_stat, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_write"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_write, active_cred, file_cred, vp, &vp->v_label); return (error); } /* * 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. */ static void mac_cred_mmapped_drop_perms(struct thread *td, struct ucred *cred) { /* XXX freeze all other threads */ mac_cred_mmapped_drop_perms_recurse(td, cred, &td->td_proc->p_vmspace->vm_map); /* XXX allow other threads to continue */ } 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_cred_mmapped_drop_perms_recurse(struct thread *td, struct ucred *cred, struct vm_map *map) { struct vm_map_entry *vme; int result; vm_prot_t revokeperms; vm_object_t object; vm_ooffset_t offset; struct vnode *vp; if (!mac_mmap_revocation) return; vm_map_lock_read(map); for (vme = map->header.next; vme != &map->header; vme = vme->next) { if (vme->eflags & MAP_ENTRY_IS_SUB_MAP) { mac_cred_mmapped_drop_perms_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; while (object->backing_object != NULL) { object = object->backing_object; offset += object->backing_object_offset; } /* * 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, td); result = vme->max_protection; mac_check_vnode_mmap_downgrade(cred, vp, &result); VOP_UNLOCK(vp, 0, td); /* * 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)); vm_map_lock_upgrade(map); /* * 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); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); vm_object_page_clean(object, OFF_TO_IDX(offset), OFF_TO_IDX(offset + vme->end - vme->start + PAGE_MASK), OBJPC_SYNC); VOP_UNLOCK(vp, 0, td); 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_lock_downgrade(map); } vm_map_unlock_read(map); } /* * 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. */ static void mac_relabel_cred(struct ucred *cred, struct label *newlabel) { MAC_PERFORM(relabel_cred, cred, newlabel); } void mac_relabel_vnode(struct ucred *cred, struct vnode *vp, struct label *newlabel) { MAC_PERFORM(relabel_vnode, cred, vp, &vp->v_label, newlabel); } void mac_create_ifnet(struct ifnet *ifnet) { MAC_PERFORM(create_ifnet, ifnet, &ifnet->if_label); } void mac_create_bpfdesc(struct ucred *cred, struct bpf_d *bpf_d) { MAC_PERFORM(create_bpfdesc, cred, bpf_d, &bpf_d->bd_label); } void mac_create_socket(struct ucred *cred, struct socket *socket) { MAC_PERFORM(create_socket, cred, socket, &socket->so_label); } void mac_create_pipe(struct ucred *cred, struct pipe *pipe) { MAC_PERFORM(create_pipe, cred, pipe, pipe->pipe_label); } void mac_create_socket_from_socket(struct socket *oldsocket, struct socket *newsocket) { MAC_PERFORM(create_socket_from_socket, oldsocket, &oldsocket->so_label, newsocket, &newsocket->so_label); } static void mac_relabel_socket(struct ucred *cred, struct socket *socket, struct label *newlabel) { MAC_PERFORM(relabel_socket, cred, socket, &socket->so_label, newlabel); } static void mac_relabel_pipe(struct ucred *cred, struct pipe *pipe, struct label *newlabel) { MAC_PERFORM(relabel_pipe, cred, pipe, pipe->pipe_label, newlabel); } void mac_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct socket *socket) { MAC_PERFORM(set_socket_peer_from_mbuf, mbuf, &mbuf->m_pkthdr.label, socket, &socket->so_peerlabel); } void mac_set_socket_peer_from_socket(struct socket *oldsocket, struct socket *newsocket) { MAC_PERFORM(set_socket_peer_from_socket, oldsocket, &oldsocket->so_label, newsocket, &newsocket->so_peerlabel); } void mac_create_datagram_from_ipq(struct ipq *ipq, struct mbuf *datagram) { MAC_PERFORM(create_datagram_from_ipq, ipq, &ipq->ipq_label, datagram, &datagram->m_pkthdr.label); } void mac_create_fragment(struct mbuf *datagram, struct mbuf *fragment) { MAC_PERFORM(create_fragment, datagram, &datagram->m_pkthdr.label, fragment, &fragment->m_pkthdr.label); } void mac_create_ipq(struct mbuf *fragment, struct ipq *ipq) { MAC_PERFORM(create_ipq, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); } void mac_create_mbuf_from_mbuf(struct mbuf *oldmbuf, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_from_mbuf, oldmbuf, &oldmbuf->m_pkthdr.label, newmbuf, &newmbuf->m_pkthdr.label); } void mac_create_mbuf_from_bpfdesc(struct bpf_d *bpf_d, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_bpfdesc, bpf_d, &bpf_d->bd_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_linklayer(struct ifnet *ifnet, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_linklayer, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_from_ifnet(struct ifnet *ifnet, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_ifnet, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_multicast_encap(struct mbuf *oldmbuf, struct ifnet *ifnet, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_multicast_encap, oldmbuf, &oldmbuf->m_pkthdr.label, ifnet, &ifnet->if_label, newmbuf, &newmbuf->m_pkthdr.label); } void mac_create_mbuf_netlayer(struct mbuf *oldmbuf, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_netlayer, oldmbuf, &oldmbuf->m_pkthdr.label, newmbuf, &newmbuf->m_pkthdr.label); } int mac_fragment_match(struct mbuf *fragment, struct ipq *ipq) { int result; result = 1; MAC_BOOLEAN(fragment_match, &&, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); return (result); } void mac_update_ipq(struct mbuf *fragment, struct ipq *ipq) { MAC_PERFORM(update_ipq, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); } void mac_create_mbuf_from_socket(struct socket *socket, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_socket, socket, &socket->so_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mount(struct ucred *cred, struct mount *mp) { MAC_PERFORM(create_mount, cred, mp, &mp->mnt_mntlabel, &mp->mnt_fslabel); } void mac_create_root_mount(struct ucred *cred, struct mount *mp) { MAC_PERFORM(create_root_mount, cred, mp, &mp->mnt_mntlabel, &mp->mnt_fslabel); } int mac_check_bpfdesc_receive(struct bpf_d *bpf_d, struct ifnet *ifnet) { int error; if (!mac_enforce_network) return (0); MAC_CHECK(check_bpfdesc_receive, bpf_d, &bpf_d->bd_label, ifnet, &ifnet->if_label); return (error); } static int mac_check_cred_relabel(struct ucred *cred, struct label *newlabel) { int error; MAC_CHECK(check_cred_relabel, cred, newlabel); return (error); } int mac_check_cred_visible(struct ucred *u1, struct ucred *u2) { int error; if (!mac_enforce_process) return (0); MAC_CHECK(check_cred_visible, u1, u2); return (error); } int mac_check_ifnet_transmit(struct ifnet *ifnet, struct mbuf *mbuf) { int error; if (!mac_enforce_network) return (0); KASSERT(mbuf->m_flags & M_PKTHDR, ("packet has no pkthdr")); if (!(mbuf->m_pkthdr.label.l_flags & MAC_FLAG_INITIALIZED)) if_printf(ifnet, "not initialized\n"); MAC_CHECK(check_ifnet_transmit, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); return (error); } int mac_check_mount_stat(struct ucred *cred, struct mount *mount) { int error; if (!mac_enforce_fs) return (0); MAC_CHECK(check_mount_stat, cred, mount, &mount->mnt_mntlabel); return (error); } int mac_check_pipe_ioctl(struct ucred *cred, struct pipe *pipe, unsigned long cmd, void *data) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_ioctl, cred, pipe, pipe->pipe_label, cmd, data); return (error); } int mac_check_pipe_poll(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_poll, cred, pipe, pipe->pipe_label); return (error); } int mac_check_pipe_read(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_read, cred, pipe, pipe->pipe_label); return (error); } static int mac_check_pipe_relabel(struct ucred *cred, struct pipe *pipe, struct label *newlabel) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_relabel, cred, pipe, pipe->pipe_label, newlabel); return (error); } int mac_check_pipe_stat(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_stat, cred, pipe, pipe->pipe_label); return (error); } int mac_check_pipe_write(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_write, cred, pipe, pipe->pipe_label); return (error); } int mac_check_proc_debug(struct ucred *cred, struct proc *proc) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_debug, cred, proc); return (error); } int mac_check_proc_sched(struct ucred *cred, struct proc *proc) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_sched, cred, proc); return (error); } int mac_check_proc_signal(struct ucred *cred, struct proc *proc, int signum) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_signal, cred, proc, signum); return (error); } int mac_check_socket_bind(struct ucred *ucred, struct socket *socket, struct sockaddr *sockaddr) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_bind, ucred, socket, &socket->so_label, sockaddr); return (error); } int mac_check_socket_connect(struct ucred *cred, struct socket *socket, struct sockaddr *sockaddr) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_connect, cred, socket, &socket->so_label, sockaddr); return (error); } int mac_check_socket_deliver(struct socket *socket, struct mbuf *mbuf) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_deliver, socket, &socket->so_label, mbuf, &mbuf->m_pkthdr.label); return (error); } int mac_check_socket_listen(struct ucred *cred, struct socket *socket) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_listen, cred, socket, &socket->so_label); return (error); } int mac_check_socket_receive(struct ucred *cred, struct socket *so) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_receive, cred, so, &so->so_label); return (error); } static int mac_check_socket_relabel(struct ucred *cred, struct socket *socket, struct label *newlabel) { int error; MAC_CHECK(check_socket_relabel, cred, socket, &socket->so_label, newlabel); return (error); } int mac_check_socket_send(struct ucred *cred, struct socket *so) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_send, cred, so, &so->so_label); return (error); } int mac_check_socket_visible(struct ucred *cred, struct socket *socket) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_visible, cred, socket, &socket->so_label); return (error); } int mac_check_system_reboot(struct ucred *cred, int howto) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_system_reboot"); if (!mac_enforce_reboot) return (0); MAC_CHECK(check_system_reboot, cred, howto); return (error); } int mac_check_system_swapon(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_system_swapon"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_system_swapon, cred, vp, &vp->v_label); + return (error); +} + +int +mac_check_system_sysctl(struct ucred *cred, int *name, u_int namelen, + void *old, size_t *oldlenp, int inkernel, void *new, size_t newlen) +{ + int error; + + /* + * XXXMAC: We're very much like to assert the SYSCTL_LOCK here, + * but since it's not exported from kern_sysctl.c, we can't. + */ + if (!mac_enforce_sysctl) + return (0); + + MAC_CHECK(check_system_sysctl, cred, name, namelen, old, oldlenp, + inkernel, new, newlen); + return (error); } int mac_ioctl_ifnet_get(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet) { char *elements, *buffer; struct mac mac; int error; 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); error = mac_externalize_ifnet_label(&ifnet->if_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } int mac_ioctl_ifnet_set(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet) { struct label intlabel; struct mac mac; char *buffer; int error; 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); } mac_init_ifnet_label(&intlabel); error = mac_internalize_ifnet_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_ifnet_label(&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 = suser_cred(cred, 0); if (error) { mac_destroy_ifnet_label(&intlabel); return (error); } MAC_CHECK(check_ifnet_relabel, cred, ifnet, &ifnet->if_label, &intlabel); if (error) { mac_destroy_ifnet_label(&intlabel); return (error); } MAC_PERFORM(relabel_ifnet, cred, ifnet, &ifnet->if_label, &intlabel); mac_destroy_ifnet_label(&intlabel); return (0); } void mac_create_devfs_vnode(struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(create_devfs_vnode, de, &de->de_label, vp, &vp->v_label); } void mac_create_devfs_device(dev_t dev, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_device, dev, de, &de->de_label); } void mac_create_devfs_symlink(struct ucred *cred, struct devfs_dirent *dd, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_symlink, cred, dd, &dd->de_label, de, &de->de_label); } void mac_create_devfs_directory(char *dirname, int dirnamelen, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_directory, dirname, dirnamelen, de, &de->de_label); } int mac_setsockopt_label_set(struct ucred *cred, struct socket *so, struct mac *mac) { struct label intlabel; char *buffer; int 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); } mac_init_socket_label(&intlabel, M_WAITOK); error = mac_internalize_socket_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_socket_label(&intlabel); return (error); } mac_check_socket_relabel(cred, so, &intlabel); if (error) { mac_destroy_socket_label(&intlabel); return (error); } mac_relabel_socket(cred, so, &intlabel); mac_destroy_socket_label(&intlabel); return (0); } int mac_pipe_label_set(struct ucred *cred, struct pipe *pipe, struct label *label) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); error = mac_check_pipe_relabel(cred, pipe, label); if (error) return (error); mac_relabel_pipe(cred, pipe, label); return (0); } int mac_getsockopt_label_get(struct ucred *cred, struct socket *so, struct mac *mac) { char *buffer, *elements; int 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_externalize_socket_label(&so->so_label, elements, buffer, mac->m_buflen, M_WAITOK); 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_get(struct ucred *cred, struct socket *so, struct mac *mac) { char *elements, *buffer; int 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_externalize_socket_peer_label(&so->so_peerlabel, elements, buffer, mac->m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac->m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * 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_setlabel_vnode_extattr(ap->a_cred, vp, intlabel); if (error) return (error); mac_relabel_vnode(ap->a_cred, vp, intlabel); return (0); } static 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_check_vnode_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); } int __mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) { char *elements, *buffer; struct mac mac; struct proc *tproc; struct ucred *tcred; int error; error = copyin(SCARG(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_externalize_cred_label(&tcred->cr_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); crfree(tcred); return (error); } /* * MPSAFE */ int __mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) { char *elements, *buffer; struct mac mac; int error; error = copyin(uap->mac_p, &mac, sizeof(mac)); if (error) return (error); error = mac_check_structmac_consistent(&mac); if (error) return (error); elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL); if (error) { free(elements, M_MACTEMP); return (error); } buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); error = mac_externalize_cred_label(&td->td_ucred->cr_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) { struct ucred *newcred, *oldcred; struct label intlabel; struct proc *p; struct mac mac; char *buffer; int error; 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); } mac_init_cred_label(&intlabel); error = mac_internalize_cred_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_cred_label(&intlabel); return (error); } newcred = crget(); p = td->td_proc; PROC_LOCK(p); oldcred = p->p_ucred; error = mac_check_cred_relabel(oldcred, &intlabel); if (error) { PROC_UNLOCK(p); crfree(newcred); goto out; } setsugid(p); crcopy(newcred, oldcred); mac_relabel_cred(newcred, &intlabel); p->p_ucred = newcred; /* * Grab additional reference for use while revoking mmaps, prior * to releasing the proc lock and sharing the cred. */ crhold(newcred); PROC_UNLOCK(p); if (mac_enforce_vm) { mtx_lock(&Giant); mac_cred_mmapped_drop_perms(td, newcred); mtx_unlock(&Giant); } crfree(newcred); /* Free revocation reference. */ crfree(oldcred); out: mac_destroy_cred_label(&intlabel); return (error); } /* * MPSAFE */ int __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) { char *elements, *buffer; struct label intlabel; struct file *fp; struct mac mac; struct vnode *vp; struct pipe *pipe; 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); mtx_lock(&Giant); /* VFS */ error = fget(td, SCARG(uap, fd), &fp); if (error) goto out; label_type = fp->f_type; switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: vp = (struct vnode *)fp->f_data; mac_init_vnode_label(&intlabel); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); mac_copy_vnode_label(&vp->v_label, &intlabel); VOP_UNLOCK(vp, 0, td); break; case DTYPE_PIPE: pipe = (struct pipe *)fp->f_data; mac_init_pipe_label(&intlabel); PIPE_LOCK(pipe); mac_copy_pipe_label(pipe->pipe_label, &intlabel); PIPE_UNLOCK(pipe); break; default: error = EINVAL; fdrop(fp, td); goto out; } fdrop(fp, td); switch (label_type) { case DTYPE_FIFO: case DTYPE_VNODE: if (error == 0) error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); mac_destroy_vnode_label(&intlabel); break; case DTYPE_PIPE: error = mac_externalize_pipe_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); mac_destroy_pipe_label(&intlabel); break; default: panic("__mac_get_fd: corrupted label_type"); } if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_get_file(struct thread *td, struct __mac_get_file_args *uap) { char *elements, *buffer; struct nameidata nd; struct label intlabel; struct mac mac; int 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); mtx_lock(&Giant); /* VFS */ NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_USERSPACE, uap->path_p, td); error = namei(&nd); if (error) goto out; mac_init_vnode_label(&intlabel); mac_copy_vnode_label(&nd.ni_vp->v_label, &intlabel); error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); NDFREE(&nd, 0); mac_destroy_vnode_label(&intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_get_link(struct thread *td, struct __mac_get_link_args *uap) { char *elements, *buffer; struct nameidata nd; struct label intlabel; struct mac mac; int 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); mtx_lock(&Giant); /* VFS */ NDINIT(&nd, LOOKUP, LOCKLEAF | NOFOLLOW, UIO_USERSPACE, uap->path_p, td); error = namei(&nd); if (error) goto out; mac_init_vnode_label(&intlabel); mac_copy_vnode_label(&nd.ni_vp->v_label, &intlabel); error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); NDFREE(&nd, 0); mac_destroy_vnode_label(&intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) { struct label intlabel; struct pipe *pipe; 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); } mtx_lock(&Giant); /* VFS */ error = fget(td, SCARG(uap, fd), &fp); if (error) goto out; switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); if (error) { mac_destroy_vnode_label(&intlabel); break; } vp = (struct vnode *)fp->f_data; error = vn_start_write(vp, &mp, V_WAIT | PCATCH); if (error != 0) { mac_destroy_vnode_label(&intlabel); break; } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); error = vn_setlabel(vp, &intlabel, td->td_ucred); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); mac_destroy_vnode_label(&intlabel); break; case DTYPE_PIPE: mac_init_pipe_label(&intlabel); error = mac_internalize_pipe_label(&intlabel, buffer); if (error == 0) { pipe = (struct pipe *)fp->f_data; PIPE_LOCK(pipe); error = mac_pipe_label_set(td->td_ucred, pipe, &intlabel); PIPE_UNLOCK(pipe); } mac_destroy_pipe_label(&intlabel); break; default: error = EINVAL; } fdrop(fp, td); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_file(struct thread *td, struct __mac_set_file_args *uap) { struct label intlabel; struct nameidata nd; struct mount *mp; struct mac mac; char *buffer; int 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); } mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_vnode_label(&intlabel); return (error); } mtx_lock(&Giant); /* VFS */ 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); mtx_unlock(&Giant); /* VFS */ mac_destroy_vnode_label(&intlabel); return (error); } /* * MPSAFE */ int __mac_set_link(struct thread *td, struct __mac_set_link_args *uap) { struct label intlabel; struct nameidata nd; struct mount *mp; struct mac mac; char *buffer; int 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); } mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_vnode_label(&intlabel); return (error); } mtx_lock(&Giant); /* VFS */ 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); mtx_unlock(&Giant); /* VFS */ mac_destroy_vnode_label(&intlabel); return (error); } /* * MPSAFE */ int 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(SCARG(uap, policy), target, sizeof(target), NULL); if (error) return (error); error = ENOSYS; MAC_POLICY_LIST_BUSY(); 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, SCARG(uap, call), SCARG(uap, arg)); goto out; } } out: MAC_POLICY_LIST_UNBUSY(); return (error); } 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); #else /* !MAC */ int __mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) { return (ENOSYS); } int __mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) { return (ENOSYS); } int __mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) { return (ENOSYS); } int __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) { return (ENOSYS); } int __mac_get_file(struct thread *td, struct __mac_get_file_args *uap) { return (ENOSYS); } int __mac_get_link(struct thread *td, struct __mac_get_link_args *uap) { return (ENOSYS); } int __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) { return (ENOSYS); } int __mac_set_file(struct thread *td, struct __mac_set_file_args *uap) { return (ENOSYS); } int __mac_set_link(struct thread *td, struct __mac_set_link_args *uap) { return (ENOSYS); } int mac_syscall(struct thread *td, struct mac_syscall_args *uap) { return (ENOSYS); } #endif Index: head/sys/security/mac/mac_policy.h =================================================================== --- head/sys/security/mac/mac_policy.h (revision 106024) +++ head/sys/security/mac/mac_policy.h (revision 106025) @@ -1,587 +1,591 @@ /*- * Copyright (c) 1999, 2000, 2001, 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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 _SYS_MAC_POLICY_H #define _SYS_MAC_POLICY_H /*- * 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. */ struct mac_policy_conf; struct mac_policy_ops { /* * Policy module operations. */ void (*mpo_destroy)(struct mac_policy_conf *mpc); void (*mpo_init)(struct mac_policy_conf *mpc); /* * General policy-directed security system call so that policies * may implement new services without reserving explicit * system call numbers. */ int (*mpo_syscall)(struct thread *td, int call, void *arg); /* * Label operations. */ void (*mpo_init_bpfdesc_label)(struct label *label); void (*mpo_init_cred_label)(struct label *label); void (*mpo_init_devfsdirent_label)(struct label *label); void (*mpo_init_ifnet_label)(struct label *label); void (*mpo_init_ipq_label)(struct label *label); int (*mpo_init_mbuf_label)(struct label *label, int flag); void (*mpo_init_mount_label)(struct label *label); void (*mpo_init_mount_fs_label)(struct label *label); int (*mpo_init_socket_label)(struct label *label, int flag); int (*mpo_init_socket_peer_label)(struct label *label, int flag); void (*mpo_init_pipe_label)(struct label *label); void (*mpo_init_vnode_label)(struct label *label); void (*mpo_destroy_bpfdesc_label)(struct label *label); void (*mpo_destroy_cred_label)(struct label *label); void (*mpo_destroy_devfsdirent_label)(struct label *label); void (*mpo_destroy_ifnet_label)(struct label *label); void (*mpo_destroy_ipq_label)(struct label *label); void (*mpo_destroy_mbuf_label)(struct label *label); void (*mpo_destroy_mount_label)(struct label *label); void (*mpo_destroy_mount_fs_label)(struct label *label); void (*mpo_destroy_socket_label)(struct label *label); void (*mpo_destroy_socket_peer_label)(struct label *label); void (*mpo_destroy_pipe_label)(struct label *label); void (*mpo_destroy_vnode_label)(struct label *label); void (*mpo_copy_pipe_label)(struct label *src, struct label *dest); void (*mpo_copy_vnode_label)(struct label *src, struct label *dest); int (*mpo_externalize_cred_label)(struct label *label, char *element_name, char *buffer, size_t buflen, size_t *len, int *claimed); int (*mpo_externalize_ifnet_label)(struct label *label, char *element_name, char *buffer, size_t buflen, size_t *len, int *claimed); int (*mpo_externalize_pipe_label)(struct label *label, char *element_name, char *buffer, size_t buflen, size_t *len, int *claimed); int (*mpo_externalize_socket_label)(struct label *label, char *element_name, char *buffer, size_t buflen, size_t *len, int *claimed); int (*mpo_externalize_socket_peer_label)(struct label *label, char *element_name, char *buffer, size_t buflen, size_t *len, int *claimed); int (*mpo_externalize_vnode_label)(struct label *label, char *element_name, char *buffer, size_t buflen, size_t *len, int *claimed); int (*mpo_internalize_cred_label)(struct label *label, char *element_name, char *element_data, int *claimed); int (*mpo_internalize_ifnet_label)(struct label *label, char *element_name, char *element_data, int *claimed); int (*mpo_internalize_pipe_label)(struct label *label, char *element_name, char *element_data, int *claimed); int (*mpo_internalize_socket_label)(struct label *label, char *element_name, char *element_data, int *claimed); int (*mpo_internalize_vnode_label)(struct label *label, char *element_name, char *element_data, int *claimed); /* * Labeling event operations: file system objects, and things that * look a lot like file system objects. */ void (*mpo_associate_vnode_devfs)(struct mount *mp, struct label *fslabel, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vlabel); int (*mpo_associate_vnode_extattr)(struct mount *mp, struct label *fslabel, struct vnode *vp, struct label *vlabel); void (*mpo_associate_vnode_singlelabel)(struct mount *mp, struct label *fslabel, struct vnode *vp, struct label *vlabel); void (*mpo_create_devfs_device)(dev_t dev, struct devfs_dirent *de, struct label *label); void (*mpo_create_devfs_directory)(char *dirname, int dirnamelen, struct devfs_dirent *de, struct label *label); void (*mpo_create_devfs_symlink)(struct ucred *cred, struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de, struct label *delabel); void (*mpo_create_devfs_vnode)(struct devfs_dirent *de, struct label *direntlabel, struct vnode *vp, struct label *vnodelabel); int (*mpo_create_vnode_extattr)(struct ucred *cred, struct mount *mp, struct label *fslabel, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *vlabel, struct componentname *cnp); void (*mpo_create_mount)(struct ucred *cred, struct mount *mp, struct label *mntlabel, struct label *fslabel); void (*mpo_create_root_mount)(struct ucred *cred, struct mount *mp, struct label *mountlabel, struct label *fslabel); void (*mpo_relabel_vnode)(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, struct label *label); int (*mpo_setlabel_vnode_extattr)(struct ucred *cred, struct vnode *vp, struct label *vlabel, struct label *intlabel); void (*mpo_update_devfsdirent)(struct devfs_dirent *devfs_dirent, struct label *direntlabel, struct vnode *vp, struct label *vnodelabel); /* * Labeling event operations: IPC objects. */ void (*mpo_create_mbuf_from_socket)(struct socket *so, struct label *socketlabel, struct mbuf *m, struct label *mbuflabel); void (*mpo_create_socket)(struct ucred *cred, struct socket *so, struct label *socketlabel); void (*mpo_create_socket_from_socket)(struct socket *oldsocket, struct label *oldsocketlabel, struct socket *newsocket, struct label *newsocketlabel); void (*mpo_relabel_socket)(struct ucred *cred, struct socket *so, struct label *oldlabel, struct label *newlabel); void (*mpo_relabel_pipe)(struct ucred *cred, struct pipe *pipe, struct label *oldlabel, struct label *newlabel); void (*mpo_set_socket_peer_from_mbuf)(struct mbuf *mbuf, struct label *mbuflabel, struct socket *so, struct label *socketpeerlabel); void (*mpo_set_socket_peer_from_socket)(struct socket *oldsocket, struct label *oldsocketlabel, struct socket *newsocket, struct label *newsocketpeerlabel); void (*mpo_create_pipe)(struct ucred *cred, struct pipe *pipe, struct label *pipelabel); /* * Labeling event operations: network objects. */ void (*mpo_create_bpfdesc)(struct ucred *cred, struct bpf_d *bpf_d, struct label *bpflabel); void (*mpo_create_ifnet)(struct ifnet *ifnet, struct label *ifnetlabel); void (*mpo_create_ipq)(struct mbuf *fragment, struct label *fragmentlabel, struct ipq *ipq, struct label *ipqlabel); void (*mpo_create_datagram_from_ipq) (struct ipq *ipq, struct label *ipqlabel, struct mbuf *datagram, struct label *datagramlabel); void (*mpo_create_fragment)(struct mbuf *datagram, struct label *datagramlabel, struct mbuf *fragment, struct label *fragmentlabel); void (*mpo_create_mbuf_from_mbuf)(struct mbuf *oldmbuf, struct label *oldlabel, struct mbuf *newmbuf, struct label *newlabel); void (*mpo_create_mbuf_linklayer)(struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *mbuf, struct label *mbuflabel); void (*mpo_create_mbuf_from_bpfdesc)(struct bpf_d *bpf_d, struct label *bpflabel, struct mbuf *mbuf, struct label *mbuflabel); void (*mpo_create_mbuf_from_ifnet)(struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *mbuf, struct label *mbuflabel); void (*mpo_create_mbuf_multicast_encap)(struct mbuf *oldmbuf, struct label *oldmbuflabel, struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *newmbuf, struct label *newmbuflabel); void (*mpo_create_mbuf_netlayer)(struct mbuf *oldmbuf, struct label *oldmbuflabel, struct mbuf *newmbuf, struct label *newmbuflabel); int (*mpo_fragment_match)(struct mbuf *fragment, struct label *fragmentlabel, struct ipq *ipq, struct label *ipqlabel); void (*mpo_relabel_ifnet)(struct ucred *cred, struct ifnet *ifnet, struct label *ifnetlabel, struct label *newlabel); void (*mpo_update_ipq)(struct mbuf *fragment, struct label *fragmentlabel, struct ipq *ipq, struct label *ipqlabel); /* * Labeling event operations: processes. */ void (*mpo_create_cred)(struct ucred *parent_cred, struct ucred *child_cred); void (*mpo_execve_transition)(struct ucred *old, struct ucred *new, struct vnode *vp, struct label *vnodelabel); int (*mpo_execve_will_transition)(struct ucred *old, struct vnode *vp, struct label *vnodelabel); void (*mpo_create_proc0)(struct ucred *cred); void (*mpo_create_proc1)(struct ucred *cred); void (*mpo_relabel_cred)(struct ucred *cred, struct label *newlabel); void (*mpo_thread_userret)(struct thread *thread); /* * Access control checks. */ int (*mpo_check_bpfdesc_receive)(struct bpf_d *bpf_d, struct label *bpflabel, struct ifnet *ifnet, struct label *ifnetlabel); int (*mpo_check_cred_relabel)(struct ucred *cred, struct label *newlabel); int (*mpo_check_cred_visible)(struct ucred *u1, struct ucred *u2); int (*mpo_check_ifnet_relabel)(struct ucred *cred, struct ifnet *ifnet, struct label *ifnetlabel, struct label *newlabel); int (*mpo_check_ifnet_transmit)(struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *m, struct label *mbuflabel); int (*mpo_check_mount_stat)(struct ucred *cred, struct mount *mp, struct label *mntlabel); int (*mpo_check_pipe_ioctl)(struct ucred *cred, struct pipe *pipe, struct label *pipelabel, unsigned long cmd, void *data); int (*mpo_check_pipe_poll)(struct ucred *cred, struct pipe *pipe, struct label *pipelabel); int (*mpo_check_pipe_read)(struct ucred *cred, struct pipe *pipe, struct label *pipelabel); int (*mpo_check_pipe_relabel)(struct ucred *cred, struct pipe *pipe, struct label *pipelabel, struct label *newlabel); int (*mpo_check_pipe_stat)(struct ucred *cred, struct pipe *pipe, struct label *pipelabel); int (*mpo_check_pipe_write)(struct ucred *cred, struct pipe *pipe, struct label *pipelabel); int (*mpo_check_proc_debug)(struct ucred *cred, struct proc *proc); int (*mpo_check_proc_sched)(struct ucred *cred, struct proc *proc); int (*mpo_check_proc_signal)(struct ucred *cred, struct proc *proc, int signum); int (*mpo_check_socket_bind)(struct ucred *cred, struct socket *so, struct label *socketlabel, struct sockaddr *sockaddr); int (*mpo_check_socket_connect)(struct ucred *cred, struct socket *so, struct label *socketlabel, struct sockaddr *sockaddr); int (*mpo_check_socket_deliver)(struct socket *so, struct label *socketlabel, struct mbuf *m, struct label *mbuflabel); int (*mpo_check_socket_listen)(struct ucred *cred, struct socket *so, struct label *socketlabel); int (*mpo_check_socket_receive)(struct ucred *cred, struct socket *so, struct label *socketlabel); int (*mpo_check_socket_relabel)(struct ucred *cred, struct socket *so, struct label *socketlabel, struct label *newlabel); int (*mpo_check_socket_send)(struct ucred *cred, struct socket *so, struct label *socketlabel); int (*mpo_check_socket_visible)(struct ucred *cred, struct socket *so, struct label *socketlabel); int (*mpo_check_system_reboot)(struct ucred *cred, int howto); int (*mpo_check_system_swapon)(struct ucred *cred, struct vnode *vp, struct label *label); + int (*mpo_check_system_sysctl)(struct ucred *cred, int *name, + u_int namelen, void *old, size_t *oldlenp, int inkernel, + void *new, size_t newlen); int (*mpo_check_vnode_access)(struct ucred *cred, struct vnode *vp, struct label *label, int flags); int (*mpo_check_vnode_chdir)(struct ucred *cred, struct vnode *dvp, struct label *dlabel); int (*mpo_check_vnode_chroot)(struct ucred *cred, struct vnode *dvp, struct label *dlabel); int (*mpo_check_vnode_create)(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct componentname *cnp, struct vattr *vap); int (*mpo_check_vnode_delete)(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, void *label, struct componentname *cnp); int (*mpo_check_vnode_deleteacl)(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type); int (*mpo_check_vnode_exec)(struct ucred *cred, struct vnode *vp, struct label *label); int (*mpo_check_vnode_getacl)(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type); int (*mpo_check_vnode_getextattr)(struct ucred *cred, struct vnode *vp, struct label *label, int attrnamespace, const char *name, struct uio *uio); int (*mpo_check_vnode_link)(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp); int (*mpo_check_vnode_lookup)(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct componentname *cnp); int (*mpo_check_vnode_mmap)(struct ucred *cred, struct vnode *vp, struct label *label, int prot); void (*mpo_check_vnode_mmap_downgrade)(struct ucred *cred, struct vnode *vp, struct label *label, int *prot); int (*mpo_check_vnode_mprotect)(struct ucred *cred, struct vnode *vp, struct label *label, int prot); int (*mpo_check_vnode_open)(struct ucred *cred, struct vnode *vp, struct label *label, mode_t acc_mode); int (*mpo_check_vnode_poll)(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label); int (*mpo_check_vnode_read)(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label); int (*mpo_check_vnode_readdir)(struct ucred *cred, struct vnode *dvp, struct label *dlabel); int (*mpo_check_vnode_readlink)(struct ucred *cred, struct vnode *vp, struct label *label); int (*mpo_check_vnode_relabel)(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, struct label *newlabel); int (*mpo_check_vnode_rename_from)(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp); int (*mpo_check_vnode_rename_to)(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, int samedir, struct componentname *cnp); int (*mpo_check_vnode_revoke)(struct ucred *cred, struct vnode *vp, struct label *label); int (*mpo_check_vnode_setacl)(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type, struct acl *acl); int (*mpo_check_vnode_setextattr)(struct ucred *cred, struct vnode *vp, struct label *label, int attrnamespace, const char *name, struct uio *uio); int (*mpo_check_vnode_setflags)(struct ucred *cred, struct vnode *vp, struct label *label, u_long flags); int (*mpo_check_vnode_setmode)(struct ucred *cred, struct vnode *vp, struct label *label, mode_t mode); int (*mpo_check_vnode_setowner)(struct ucred *cred, struct vnode *vp, struct label *label, uid_t uid, gid_t gid); int (*mpo_check_vnode_setutimes)(struct ucred *cred, struct vnode *vp, struct label *label, struct timespec atime, struct timespec mtime); int (*mpo_check_vnode_stat)(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label); int (*mpo_check_vnode_write)(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label); }; typedef const void *macop_t; enum mac_op_constant { MAC_OP_LAST, MAC_DESTROY, MAC_INIT, MAC_SYSCALL, MAC_INIT_BPFDESC_LABEL, MAC_INIT_CRED_LABEL, MAC_INIT_DEVFSDIRENT_LABEL, MAC_INIT_IFNET_LABEL, MAC_INIT_IPQ_LABEL, MAC_INIT_MBUF_LABEL, MAC_INIT_MOUNT_LABEL, MAC_INIT_MOUNT_FS_LABEL, MAC_INIT_PIPE_LABEL, MAC_INIT_SOCKET_LABEL, MAC_INIT_SOCKET_PEER_LABEL, MAC_INIT_VNODE_LABEL, MAC_DESTROY_BPFDESC_LABEL, MAC_DESTROY_CRED_LABEL, MAC_DESTROY_DEVFSDIRENT_LABEL, MAC_DESTROY_IFNET_LABEL, MAC_DESTROY_IPQ_LABEL, MAC_DESTROY_MBUF_LABEL, MAC_DESTROY_MOUNT_LABEL, MAC_DESTROY_MOUNT_FS_LABEL, MAC_DESTROY_PIPE_LABEL, MAC_DESTROY_SOCKET_LABEL, MAC_DESTROY_SOCKET_PEER_LABEL, MAC_DESTROY_VNODE_LABEL, MAC_COPY_PIPE_LABEL, MAC_COPY_VNODE_LABEL, MAC_EXTERNALIZE_CRED_LABEL, MAC_EXTERNALIZE_IFNET_LABEL, MAC_EXTERNALIZE_PIPE_LABEL, MAC_EXTERNALIZE_SOCKET_LABEL, MAC_EXTERNALIZE_SOCKET_PEER_LABEL, MAC_EXTERNALIZE_VNODE_LABEL, MAC_INTERNALIZE_CRED_LABEL, MAC_INTERNALIZE_IFNET_LABEL, MAC_INTERNALIZE_PIPE_LABEL, MAC_INTERNALIZE_SOCKET_LABEL, MAC_INTERNALIZE_VNODE_LABEL, MAC_CREATE_DEVFS_DEVICE, MAC_CREATE_DEVFS_DIRECTORY, MAC_CREATE_DEVFS_SYMLINK, MAC_CREATE_DEVFS_VNODE, MAC_CREATE_MOUNT, MAC_CREATE_ROOT_MOUNT, MAC_RELABEL_VNODE, MAC_UPDATE_DEVFSDIRENT, MAC_ASSOCIATE_VNODE_DEVFS, MAC_ASSOCIATE_VNODE_EXTATTR, MAC_ASSOCIATE_VNODE_SINGLELABEL, MAC_CREATE_VNODE_EXTATTR, MAC_SETLABEL_VNODE_EXTATTR, MAC_CREATE_MBUF_FROM_SOCKET, MAC_CREATE_PIPE, MAC_CREATE_SOCKET, MAC_CREATE_SOCKET_FROM_SOCKET, MAC_RELABEL_PIPE, MAC_RELABEL_SOCKET, MAC_SET_SOCKET_PEER_FROM_MBUF, MAC_SET_SOCKET_PEER_FROM_SOCKET, MAC_CREATE_BPFDESC, MAC_CREATE_DATAGRAM_FROM_IPQ, MAC_CREATE_IFNET, MAC_CREATE_IPQ, MAC_CREATE_FRAGMENT, MAC_CREATE_MBUF_FROM_MBUF, MAC_CREATE_MBUF_LINKLAYER, MAC_CREATE_MBUF_FROM_BPFDESC, MAC_CREATE_MBUF_FROM_IFNET, MAC_CREATE_MBUF_MULTICAST_ENCAP, MAC_CREATE_MBUF_NETLAYER, MAC_FRAGMENT_MATCH, MAC_RELABEL_IFNET, MAC_UPDATE_IPQ, MAC_CREATE_CRED, MAC_EXECVE_TRANSITION, MAC_EXECVE_WILL_TRANSITION, MAC_CREATE_PROC0, MAC_CREATE_PROC1, MAC_RELABEL_CRED, MAC_THREAD_USERRET, MAC_CHECK_BPFDESC_RECEIVE, MAC_CHECK_CRED_RELABEL, MAC_CHECK_CRED_VISIBLE, MAC_CHECK_IFNET_RELABEL, MAC_CHECK_IFNET_TRANSMIT, MAC_CHECK_MOUNT_STAT, MAC_CHECK_PIPE_IOCTL, MAC_CHECK_PIPE_POLL, MAC_CHECK_PIPE_READ, MAC_CHECK_PIPE_RELABEL, MAC_CHECK_PIPE_STAT, MAC_CHECK_PIPE_WRITE, MAC_CHECK_PROC_DEBUG, MAC_CHECK_PROC_SCHED, MAC_CHECK_PROC_SIGNAL, MAC_CHECK_SOCKET_BIND, MAC_CHECK_SOCKET_CONNECT, MAC_CHECK_SOCKET_DELIVER, MAC_CHECK_SOCKET_LISTEN, MAC_CHECK_SOCKET_RECEIVE, MAC_CHECK_SOCKET_RELABEL, MAC_CHECK_SOCKET_SEND, MAC_CHECK_SOCKET_VISIBLE, MAC_CHECK_SYSTEM_REBOOT, MAC_CHECK_SYSTEM_SWAPON, + MAC_CHECK_SYSTEM_SYSCTL, MAC_CHECK_VNODE_ACCESS, MAC_CHECK_VNODE_CHDIR, MAC_CHECK_VNODE_CHROOT, MAC_CHECK_VNODE_CREATE, MAC_CHECK_VNODE_DELETE, MAC_CHECK_VNODE_DELETEACL, MAC_CHECK_VNODE_EXEC, MAC_CHECK_VNODE_GETACL, MAC_CHECK_VNODE_GETEXTATTR, MAC_CHECK_VNODE_LINK, MAC_CHECK_VNODE_LOOKUP, MAC_CHECK_VNODE_MMAP, MAC_CHECK_VNODE_MMAP_DOWNGRADE, MAC_CHECK_VNODE_MPROTECT, MAC_CHECK_VNODE_OPEN, MAC_CHECK_VNODE_POLL, MAC_CHECK_VNODE_READ, MAC_CHECK_VNODE_READDIR, MAC_CHECK_VNODE_READLINK, MAC_CHECK_VNODE_RELABEL, MAC_CHECK_VNODE_RENAME_FROM, MAC_CHECK_VNODE_RENAME_TO, MAC_CHECK_VNODE_REVOKE, MAC_CHECK_VNODE_SETACL, MAC_CHECK_VNODE_SETEXTATTR, MAC_CHECK_VNODE_SETFLAGS, MAC_CHECK_VNODE_SETMODE, MAC_CHECK_VNODE_SETOWNER, MAC_CHECK_VNODE_SETUTIMES, MAC_CHECK_VNODE_STAT, MAC_CHECK_VNODE_WRITE, }; struct mac_policy_op_entry { enum mac_op_constant mpe_constant; /* what this hook implements */ macop_t mpe_function; /* hook's implementation */ }; struct mac_policy_conf { char *mpc_name; /* policy name */ char *mpc_fullname; /* policy full name */ struct mac_policy_ops *mpc_ops; /* policy operations */ struct mac_policy_op_entry *mpc_entries; /* ops to fill in */ int mpc_loadtime_flags; /* flags */ int *mpc_field_off; /* security field */ int mpc_runtime_flags; /* flags */ 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 #define MAC_POLICY_SET(mpents, mpname, mpfullname, mpflags, privdata_wanted) \ static struct mac_policy_conf mpname##_mac_policy_conf = { \ #mpname, \ mpfullname, \ NULL, \ mpents, \ mpflags, \ privdata_wanted, \ 0, \ }; \ static moduledata_t mpname##_mod = { \ #mpname, \ mac_policy_modevent, \ &mpname##_mac_policy_conf \ }; \ MODULE_DEPEND(mpname, kernel_mac_support, 1, 1, 1); \ DECLARE_MODULE(mpname, mpname##_mod, SI_SUB_MAC_POLICY, \ SI_ORDER_MIDDLE) int mac_policy_modevent(module_t mod, int type, void *data); #define LABEL_TO_SLOT(l, s) (l)->l_perpolicy[s] #endif /* !_SYS_MAC_POLICY_H */ Index: head/sys/security/mac/mac_process.c =================================================================== --- head/sys/security/mac/mac_process.c (revision 106024) +++ head/sys/security/mac/mac_process.c (revision 106025) @@ -1,4010 +1,4038 @@ /*- * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson * Copyright (c) 2001 Ilmar S. Habibulin * Copyright (c) 2001, 2002 Networks Associates Technology, 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 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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. * * Framework for extensible kernel access control. Kernel and userland * interface to the framework, policy registration and composition. */ #include "opt_mac.h" #include "opt_devfs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MAC /* * Declare that the kernel provides MAC support, version 1. 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, 1); SYSCTL_DECL(_security); SYSCTL_NODE(_security, OID_AUTO, mac, CTLFLAG_RW, 0, "TrustedBSD MAC policy controls"); #if MAC_MAX_POLICIES > 32 #error "MAC_MAX_POLICIES too large" #endif static unsigned int mac_max_policies = MAC_MAX_POLICIES; static unsigned int mac_policy_offsets_free = (1 << MAC_MAX_POLICIES) - 1; SYSCTL_UINT(_security_mac, OID_AUTO, max_policies, CTLFLAG_RD, &mac_max_policies, 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; /* * Warn about EA transactions only the first time they happen. * Weak coherency, no locking. */ static int ea_warn_once = 0; static int mac_enforce_fs = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_fs, CTLFLAG_RW, &mac_enforce_fs, 0, "Enforce MAC policy on file system objects"); TUNABLE_INT("security.mac.enforce_fs", &mac_enforce_fs); static int mac_enforce_network = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_network, CTLFLAG_RW, &mac_enforce_network, 0, "Enforce MAC policy on network packets"); TUNABLE_INT("security.mac.enforce_network", &mac_enforce_network); static int mac_enforce_pipe = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_pipe, CTLFLAG_RW, &mac_enforce_pipe, 0, "Enforce MAC policy on pipe operations"); TUNABLE_INT("security.mac.enforce_pipe", &mac_enforce_pipe); static int mac_enforce_process = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_process, CTLFLAG_RW, &mac_enforce_process, 0, "Enforce MAC policy on inter-process operations"); TUNABLE_INT("security.mac.enforce_process", &mac_enforce_process); static int mac_enforce_reboot = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_reboot, CTLFLAG_RW, &mac_enforce_reboot, 0, "Enforce MAC policy for reboot operations"); TUNABLE_INT("security.mac.enforce_reboot", &mac_enforce_reboot); static int mac_enforce_socket = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_socket, CTLFLAG_RW, &mac_enforce_socket, 0, "Enforce MAC policy on socket operations"); TUNABLE_INT("security.mac.enforce_socket", &mac_enforce_socket); +static int mac_enforce_sysctl = 1; +SYSCTL_INT(_security_mac, OID_AUTO, enforce_sysctl, CTLFLAG_RW, + &mac_enforce_sysctl, 0, "Enforce MAC policy on sysctl operations"); +TUNABLE_INT("security.mac.enforce_sysctl", &mac_enforce_sysctl); + static int mac_enforce_vm = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_vm, CTLFLAG_RW, &mac_enforce_vm, 0, "Enforce MAC policy on vm operations"); TUNABLE_INT("security.mac.enforce_vm", &mac_enforce_vm); static int mac_cache_fslabel_in_vnode = 1; SYSCTL_INT(_security_mac, OID_AUTO, cache_fslabel_in_vnode, CTLFLAG_RW, &mac_cache_fslabel_in_vnode, 0, "Cache mount fslabel in vnode"); TUNABLE_INT("security.mac.cache_fslabel_in_vnode", &mac_cache_fslabel_in_vnode); 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"); #ifdef MAC_DEBUG SYSCTL_NODE(_security_mac, OID_AUTO, debug, CTLFLAG_RW, 0, "TrustedBSD MAC debug info"); static int mac_debug_label_fallback = 0; SYSCTL_INT(_security_mac_debug, OID_AUTO, label_fallback, CTLFLAG_RW, &mac_debug_label_fallback, 0, "Filesystems should fall back to fs label" "when label is corrupted."); TUNABLE_INT("security.mac.debug_label_fallback", &mac_debug_label_fallback); SYSCTL_NODE(_security_mac_debug, OID_AUTO, counters, CTLFLAG_RW, 0, "TrustedBSD MAC object counters"); static unsigned int nmacmbufs, nmaccreds, nmacifnets, nmacbpfdescs, nmacsockets, nmacmounts, nmactemp, nmacvnodes, nmacdevfsdirents, nmacipqs, nmacpipes; SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, mbufs, CTLFLAG_RD, &nmacmbufs, 0, "number of mbufs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, creds, CTLFLAG_RD, &nmaccreds, 0, "number of ucreds in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ifnets, CTLFLAG_RD, &nmacifnets, 0, "number of ifnets in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ipqs, CTLFLAG_RD, &nmacipqs, 0, "number of ipqs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, bpfdescs, CTLFLAG_RD, &nmacbpfdescs, 0, "number of bpfdescs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, sockets, CTLFLAG_RD, &nmacsockets, 0, "number of sockets in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, pipes, CTLFLAG_RD, &nmacpipes, 0, "number of pipes in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, mounts, CTLFLAG_RD, &nmacmounts, 0, "number of mounts in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, temp, CTLFLAG_RD, &nmactemp, 0, "number of temporary labels in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, vnodes, CTLFLAG_RD, &nmacvnodes, 0, "number of vnodes in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, devfsdirents, CTLFLAG_RD, &nmacdevfsdirents, 0, "number of devfs dirents inuse"); #endif static int error_select(int error1, int error2); static int mac_policy_register(struct mac_policy_conf *mpc); static int mac_policy_unregister(struct mac_policy_conf *mpc); static void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot); static void mac_cred_mmapped_drop_perms_recurse(struct thread *td, struct ucred *cred, struct vm_map *map); static void mac_destroy_socket_label(struct label *label); static int mac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *intlabel); MALLOC_DEFINE(M_MACOPVEC, "macopvec", "MAC policy operation vector"); MALLOC_DEFINE(M_MACPIPELABEL, "macpipelabel", "MAC labels for pipes"); MALLOC_DEFINE(M_MACTEMP, "mactemp", "MAC temporary label storage"); /* * mac_policy_list_lock protects the consistency of 'mac_policy_list', * the linked list of attached policy modules. Read-only consumers of * the list must acquire a shared lock for the duration of their use; * writers must acquire an exclusive lock. Note that for compound * operations, locks should be held for the entire compound operation, * and that this is not yet done for relabel requests. */ static struct mtx mac_policy_list_lock; static LIST_HEAD(, mac_policy_conf) mac_policy_list; static int mac_policy_list_busy; #define MAC_POLICY_LIST_LOCKINIT() mtx_init(&mac_policy_list_lock, \ "mac_policy_list_lock", NULL, MTX_DEF); #define MAC_POLICY_LIST_LOCK() mtx_lock(&mac_policy_list_lock); #define MAC_POLICY_LIST_UNLOCK() mtx_unlock(&mac_policy_list_lock); #define MAC_POLICY_LIST_BUSY() do { \ MAC_POLICY_LIST_LOCK(); \ mac_policy_list_busy++; \ MAC_POLICY_LIST_UNLOCK(); \ } while (0) #define MAC_POLICY_LIST_UNBUSY() do { \ MAC_POLICY_LIST_LOCK(); \ mac_policy_list_busy--; \ if (mac_policy_list_busy < 0) \ panic("Extra mac_policy_list_busy--"); \ MAC_POLICY_LIST_UNLOCK(); \ } while (0) /* * MAC_CHECK performs the designated check by walking the policy * module list and checking with each as to how it feels about the * request. Note that it returns its value via 'error' in the scope * of the caller. */ #define MAC_CHECK(check, args...) do { \ struct mac_policy_conf *mpc; \ \ error = 0; \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## check != NULL) \ error = error_select( \ mpc->mpc_ops->mpo_ ## check (args), \ error); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) /* * MAC_BOOLEAN performs the designated boolean composition by walking * the module list, invoking each instance of the operation, and * combining the results using the passed C operator. Note that it * returns its value via 'result' in the scope of the caller, which * should be initialized by the caller in a meaningful way to get * a meaningful result. */ #define MAC_BOOLEAN(operation, composition, args...) do { \ struct mac_policy_conf *mpc; \ \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ result = result composition \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) #define MAC_EXTERNALIZE(type, label, elementlist, outbuf, \ outbuflen) do { \ char *curptr, *curptr_start, *element_name, *element_temp; \ size_t left, left_start, len; \ int claimed, first, first_start, ignorenotfound; \ \ error = 0; \ element_temp = elementlist; \ curptr = outbuf; \ curptr[0] = '\0'; \ left = outbuflen; \ first = 1; \ while ((element_name = strsep(&element_temp, ",")) != NULL) { \ curptr_start = curptr; \ left_start = left; \ first_start = first; \ if (element_name[0] == '?') { \ element_name++; \ ignorenotfound = 1; \ } else \ ignorenotfound = 0; \ claimed = 0; \ if (first) { \ len = snprintf(curptr, left, "%s/", \ element_name); \ first = 0; \ } else \ len = snprintf(curptr, left, ",%s/", \ element_name); \ if (len >= left) { \ error = EINVAL; /* XXXMAC: E2BIG */ \ break; \ } \ curptr += len; \ left -= len; \ \ MAC_CHECK(externalize_ ## type, label, element_name, \ curptr, left, &len, &claimed); \ if (error) \ break; \ if (claimed == 1) { \ if (len >= outbuflen) { \ error = EINVAL; /* XXXMAC: E2BIG */ \ break; \ } \ curptr += len; \ left -= len; \ } else if (claimed == 0 && ignorenotfound) { \ /* \ * Revert addition of the label element \ * name. \ */ \ curptr = curptr_start; \ *curptr = '\0'; \ left = left_start; \ first = first_start; \ } else { \ error = EINVAL; /* XXXMAC: ENOLABEL */ \ break; \ } \ } \ } while (0) #define MAC_INTERNALIZE(type, label, instring) do { \ char *element, *element_name, *element_data; \ int claimed; \ \ error = 0; \ element = instring; \ while ((element_name = strsep(&element, ",")) != NULL) { \ element_data = element_name; \ element_name = strsep(&element_data, "/"); \ if (element_data == NULL) { \ error = EINVAL; \ break; \ } \ claimed = 0; \ MAC_CHECK(internalize_ ## type, label, element_name, \ element_data, &claimed); \ if (error) \ break; \ if (claimed != 1) { \ /* XXXMAC: Another error here? */ \ error = EINVAL; \ break; \ } \ } \ } while (0) /* * MAC_PERFORM performs the designated operation by walking the policy * module list and invoking that operation for each policy. */ #define MAC_PERFORM(operation, args...) do { \ struct mac_policy_conf *mpc; \ \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) /* * Initialize the MAC subsystem, including appropriate SMP locks. */ static void mac_init(void) { LIST_INIT(&mac_policy_list); MAC_POLICY_LIST_LOCKINIT(); } /* * 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; } /* * 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; 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: break; } return (error); } static int mac_policy_register(struct mac_policy_conf *mpc) { struct mac_policy_conf *tmpc; struct mac_policy_op_entry *mpe; int slot; MALLOC(mpc->mpc_ops, struct mac_policy_ops *, sizeof(*mpc->mpc_ops), M_MACOPVEC, M_WAITOK | M_ZERO); for (mpe = mpc->mpc_entries; mpe->mpe_constant != MAC_OP_LAST; mpe++) { switch (mpe->mpe_constant) { case MAC_OP_LAST: /* * Doesn't actually happen, but this allows checking * that all enumerated values are handled. */ break; case MAC_DESTROY: mpc->mpc_ops->mpo_destroy = mpe->mpe_function; break; case MAC_INIT: mpc->mpc_ops->mpo_init = mpe->mpe_function; break; case MAC_SYSCALL: mpc->mpc_ops->mpo_syscall = mpe->mpe_function; break; case MAC_INIT_BPFDESC_LABEL: mpc->mpc_ops->mpo_init_bpfdesc_label = mpe->mpe_function; break; case MAC_INIT_CRED_LABEL: mpc->mpc_ops->mpo_init_cred_label = mpe->mpe_function; break; case MAC_INIT_DEVFSDIRENT_LABEL: mpc->mpc_ops->mpo_init_devfsdirent_label = mpe->mpe_function; break; case MAC_INIT_IFNET_LABEL: mpc->mpc_ops->mpo_init_ifnet_label = mpe->mpe_function; break; case MAC_INIT_IPQ_LABEL: mpc->mpc_ops->mpo_init_ipq_label = mpe->mpe_function; break; case MAC_INIT_MBUF_LABEL: mpc->mpc_ops->mpo_init_mbuf_label = mpe->mpe_function; break; case MAC_INIT_MOUNT_LABEL: mpc->mpc_ops->mpo_init_mount_label = mpe->mpe_function; break; case MAC_INIT_MOUNT_FS_LABEL: mpc->mpc_ops->mpo_init_mount_fs_label = mpe->mpe_function; break; case MAC_INIT_PIPE_LABEL: mpc->mpc_ops->mpo_init_pipe_label = mpe->mpe_function; break; case MAC_INIT_SOCKET_LABEL: mpc->mpc_ops->mpo_init_socket_label = mpe->mpe_function; break; case MAC_INIT_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_init_socket_peer_label = mpe->mpe_function; break; case MAC_INIT_VNODE_LABEL: mpc->mpc_ops->mpo_init_vnode_label = mpe->mpe_function; break; case MAC_DESTROY_BPFDESC_LABEL: mpc->mpc_ops->mpo_destroy_bpfdesc_label = mpe->mpe_function; break; case MAC_DESTROY_CRED_LABEL: mpc->mpc_ops->mpo_destroy_cred_label = mpe->mpe_function; break; case MAC_DESTROY_DEVFSDIRENT_LABEL: mpc->mpc_ops->mpo_destroy_devfsdirent_label = mpe->mpe_function; break; case MAC_DESTROY_IFNET_LABEL: mpc->mpc_ops->mpo_destroy_ifnet_label = mpe->mpe_function; break; case MAC_DESTROY_IPQ_LABEL: mpc->mpc_ops->mpo_destroy_ipq_label = mpe->mpe_function; break; case MAC_DESTROY_MBUF_LABEL: mpc->mpc_ops->mpo_destroy_mbuf_label = mpe->mpe_function; break; case MAC_DESTROY_MOUNT_LABEL: mpc->mpc_ops->mpo_destroy_mount_label = mpe->mpe_function; break; case MAC_DESTROY_MOUNT_FS_LABEL: mpc->mpc_ops->mpo_destroy_mount_fs_label = mpe->mpe_function; break; case MAC_DESTROY_PIPE_LABEL: mpc->mpc_ops->mpo_destroy_pipe_label = mpe->mpe_function; break; case MAC_DESTROY_SOCKET_LABEL: mpc->mpc_ops->mpo_destroy_socket_label = mpe->mpe_function; break; case MAC_DESTROY_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_destroy_socket_peer_label = mpe->mpe_function; break; case MAC_DESTROY_VNODE_LABEL: mpc->mpc_ops->mpo_destroy_vnode_label = mpe->mpe_function; break; case MAC_COPY_PIPE_LABEL: mpc->mpc_ops->mpo_copy_pipe_label = mpe->mpe_function; break; case MAC_COPY_VNODE_LABEL: mpc->mpc_ops->mpo_copy_vnode_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_CRED_LABEL: mpc->mpc_ops->mpo_externalize_cred_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_IFNET_LABEL: mpc->mpc_ops->mpo_externalize_ifnet_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_PIPE_LABEL: mpc->mpc_ops->mpo_externalize_pipe_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_SOCKET_LABEL: mpc->mpc_ops->mpo_externalize_socket_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_externalize_socket_peer_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_VNODE_LABEL: mpc->mpc_ops->mpo_externalize_vnode_label = mpe->mpe_function; break; case MAC_INTERNALIZE_CRED_LABEL: mpc->mpc_ops->mpo_internalize_cred_label = mpe->mpe_function; break; case MAC_INTERNALIZE_IFNET_LABEL: mpc->mpc_ops->mpo_internalize_ifnet_label = mpe->mpe_function; break; case MAC_INTERNALIZE_PIPE_LABEL: mpc->mpc_ops->mpo_internalize_pipe_label = mpe->mpe_function; break; case MAC_INTERNALIZE_SOCKET_LABEL: mpc->mpc_ops->mpo_internalize_socket_label = mpe->mpe_function; break; case MAC_INTERNALIZE_VNODE_LABEL: mpc->mpc_ops->mpo_internalize_vnode_label = mpe->mpe_function; break; case MAC_CREATE_DEVFS_DEVICE: mpc->mpc_ops->mpo_create_devfs_device = mpe->mpe_function; break; case MAC_CREATE_DEVFS_DIRECTORY: mpc->mpc_ops->mpo_create_devfs_directory = mpe->mpe_function; break; case MAC_CREATE_DEVFS_SYMLINK: mpc->mpc_ops->mpo_create_devfs_symlink = mpe->mpe_function; break; case MAC_CREATE_DEVFS_VNODE: mpc->mpc_ops->mpo_create_devfs_vnode = mpe->mpe_function; break; case MAC_CREATE_MOUNT: mpc->mpc_ops->mpo_create_mount = mpe->mpe_function; break; case MAC_CREATE_ROOT_MOUNT: mpc->mpc_ops->mpo_create_root_mount = mpe->mpe_function; break; case MAC_RELABEL_VNODE: mpc->mpc_ops->mpo_relabel_vnode = mpe->mpe_function; break; case MAC_UPDATE_DEVFSDIRENT: mpc->mpc_ops->mpo_update_devfsdirent = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_DEVFS: mpc->mpc_ops->mpo_associate_vnode_devfs = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_EXTATTR: mpc->mpc_ops->mpo_associate_vnode_extattr = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_SINGLELABEL: mpc->mpc_ops->mpo_associate_vnode_singlelabel = mpe->mpe_function; break; case MAC_CREATE_VNODE_EXTATTR: mpc->mpc_ops->mpo_create_vnode_extattr = mpe->mpe_function; break; case MAC_SETLABEL_VNODE_EXTATTR: mpc->mpc_ops->mpo_setlabel_vnode_extattr = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_SOCKET: mpc->mpc_ops->mpo_create_mbuf_from_socket = mpe->mpe_function; break; case MAC_CREATE_PIPE: mpc->mpc_ops->mpo_create_pipe = mpe->mpe_function; break; case MAC_CREATE_SOCKET: mpc->mpc_ops->mpo_create_socket = mpe->mpe_function; break; case MAC_CREATE_SOCKET_FROM_SOCKET: mpc->mpc_ops->mpo_create_socket_from_socket = mpe->mpe_function; break; case MAC_RELABEL_PIPE: mpc->mpc_ops->mpo_relabel_pipe = mpe->mpe_function; break; case MAC_RELABEL_SOCKET: mpc->mpc_ops->mpo_relabel_socket = mpe->mpe_function; break; case MAC_SET_SOCKET_PEER_FROM_MBUF: mpc->mpc_ops->mpo_set_socket_peer_from_mbuf = mpe->mpe_function; break; case MAC_SET_SOCKET_PEER_FROM_SOCKET: mpc->mpc_ops->mpo_set_socket_peer_from_socket = mpe->mpe_function; break; case MAC_CREATE_BPFDESC: mpc->mpc_ops->mpo_create_bpfdesc = mpe->mpe_function; break; case MAC_CREATE_DATAGRAM_FROM_IPQ: mpc->mpc_ops->mpo_create_datagram_from_ipq = mpe->mpe_function; break; case MAC_CREATE_FRAGMENT: mpc->mpc_ops->mpo_create_fragment = mpe->mpe_function; break; case MAC_CREATE_IFNET: mpc->mpc_ops->mpo_create_ifnet = mpe->mpe_function; break; case MAC_CREATE_IPQ: mpc->mpc_ops->mpo_create_ipq = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_MBUF: mpc->mpc_ops->mpo_create_mbuf_from_mbuf = mpe->mpe_function; break; case MAC_CREATE_MBUF_LINKLAYER: mpc->mpc_ops->mpo_create_mbuf_linklayer = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_BPFDESC: mpc->mpc_ops->mpo_create_mbuf_from_bpfdesc = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_IFNET: mpc->mpc_ops->mpo_create_mbuf_from_ifnet = mpe->mpe_function; break; case MAC_CREATE_MBUF_MULTICAST_ENCAP: mpc->mpc_ops->mpo_create_mbuf_multicast_encap = mpe->mpe_function; break; case MAC_CREATE_MBUF_NETLAYER: mpc->mpc_ops->mpo_create_mbuf_netlayer = mpe->mpe_function; break; case MAC_FRAGMENT_MATCH: mpc->mpc_ops->mpo_fragment_match = mpe->mpe_function; break; case MAC_RELABEL_IFNET: mpc->mpc_ops->mpo_relabel_ifnet = mpe->mpe_function; break; case MAC_UPDATE_IPQ: mpc->mpc_ops->mpo_update_ipq = mpe->mpe_function; break; case MAC_CREATE_CRED: mpc->mpc_ops->mpo_create_cred = mpe->mpe_function; break; case MAC_EXECVE_TRANSITION: mpc->mpc_ops->mpo_execve_transition = mpe->mpe_function; break; case MAC_EXECVE_WILL_TRANSITION: mpc->mpc_ops->mpo_execve_will_transition = mpe->mpe_function; break; case MAC_CREATE_PROC0: mpc->mpc_ops->mpo_create_proc0 = mpe->mpe_function; break; case MAC_CREATE_PROC1: mpc->mpc_ops->mpo_create_proc1 = mpe->mpe_function; break; case MAC_RELABEL_CRED: mpc->mpc_ops->mpo_relabel_cred = mpe->mpe_function; break; case MAC_THREAD_USERRET: mpc->mpc_ops->mpo_thread_userret = mpe->mpe_function; break; case MAC_CHECK_BPFDESC_RECEIVE: mpc->mpc_ops->mpo_check_bpfdesc_receive = mpe->mpe_function; break; case MAC_CHECK_CRED_RELABEL: mpc->mpc_ops->mpo_check_cred_relabel = mpe->mpe_function; break; case MAC_CHECK_CRED_VISIBLE: mpc->mpc_ops->mpo_check_cred_visible = mpe->mpe_function; break; case MAC_CHECK_IFNET_RELABEL: mpc->mpc_ops->mpo_check_ifnet_relabel = mpe->mpe_function; break; case MAC_CHECK_IFNET_TRANSMIT: mpc->mpc_ops->mpo_check_ifnet_transmit = mpe->mpe_function; break; case MAC_CHECK_MOUNT_STAT: mpc->mpc_ops->mpo_check_mount_stat = mpe->mpe_function; break; case MAC_CHECK_PIPE_IOCTL: mpc->mpc_ops->mpo_check_pipe_ioctl = mpe->mpe_function; break; case MAC_CHECK_PIPE_POLL: mpc->mpc_ops->mpo_check_pipe_poll = mpe->mpe_function; break; case MAC_CHECK_PIPE_READ: mpc->mpc_ops->mpo_check_pipe_read = mpe->mpe_function; break; case MAC_CHECK_PIPE_RELABEL: mpc->mpc_ops->mpo_check_pipe_relabel = mpe->mpe_function; break; case MAC_CHECK_PIPE_STAT: mpc->mpc_ops->mpo_check_pipe_stat = mpe->mpe_function; break; case MAC_CHECK_PIPE_WRITE: mpc->mpc_ops->mpo_check_pipe_write = mpe->mpe_function; break; case MAC_CHECK_PROC_DEBUG: mpc->mpc_ops->mpo_check_proc_debug = mpe->mpe_function; break; case MAC_CHECK_PROC_SCHED: mpc->mpc_ops->mpo_check_proc_sched = mpe->mpe_function; break; case MAC_CHECK_PROC_SIGNAL: mpc->mpc_ops->mpo_check_proc_signal = mpe->mpe_function; break; case MAC_CHECK_SOCKET_BIND: mpc->mpc_ops->mpo_check_socket_bind = mpe->mpe_function; break; case MAC_CHECK_SOCKET_CONNECT: mpc->mpc_ops->mpo_check_socket_connect = mpe->mpe_function; break; case MAC_CHECK_SOCKET_DELIVER: mpc->mpc_ops->mpo_check_socket_deliver = mpe->mpe_function; break; case MAC_CHECK_SOCKET_LISTEN: mpc->mpc_ops->mpo_check_socket_listen = mpe->mpe_function; break; case MAC_CHECK_SOCKET_RECEIVE: mpc->mpc_ops->mpo_check_socket_receive = mpe->mpe_function; break; case MAC_CHECK_SOCKET_RELABEL: mpc->mpc_ops->mpo_check_socket_relabel = mpe->mpe_function; break; case MAC_CHECK_SOCKET_SEND: mpc->mpc_ops->mpo_check_socket_send = mpe->mpe_function; break; case MAC_CHECK_SOCKET_VISIBLE: mpc->mpc_ops->mpo_check_socket_visible = mpe->mpe_function; break; case MAC_CHECK_SYSTEM_REBOOT: mpc->mpc_ops->mpo_check_system_reboot = mpe->mpe_function; break; case MAC_CHECK_SYSTEM_SWAPON: mpc->mpc_ops->mpo_check_system_swapon = mpe->mpe_function; break; + case MAC_CHECK_SYSTEM_SYSCTL: + mpc->mpc_ops->mpo_check_system_sysctl = + mpe->mpe_function; + break; case MAC_CHECK_VNODE_ACCESS: mpc->mpc_ops->mpo_check_vnode_access = mpe->mpe_function; break; case MAC_CHECK_VNODE_CHDIR: mpc->mpc_ops->mpo_check_vnode_chdir = mpe->mpe_function; break; case MAC_CHECK_VNODE_CHROOT: mpc->mpc_ops->mpo_check_vnode_chroot = mpe->mpe_function; break; case MAC_CHECK_VNODE_CREATE: mpc->mpc_ops->mpo_check_vnode_create = mpe->mpe_function; break; case MAC_CHECK_VNODE_DELETE: mpc->mpc_ops->mpo_check_vnode_delete = mpe->mpe_function; break; case MAC_CHECK_VNODE_DELETEACL: mpc->mpc_ops->mpo_check_vnode_deleteacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_EXEC: mpc->mpc_ops->mpo_check_vnode_exec = mpe->mpe_function; break; case MAC_CHECK_VNODE_GETACL: mpc->mpc_ops->mpo_check_vnode_getacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_GETEXTATTR: mpc->mpc_ops->mpo_check_vnode_getextattr = mpe->mpe_function; break; case MAC_CHECK_VNODE_LINK: mpc->mpc_ops->mpo_check_vnode_link = mpe->mpe_function; break; case MAC_CHECK_VNODE_LOOKUP: mpc->mpc_ops->mpo_check_vnode_lookup = mpe->mpe_function; break; case MAC_CHECK_VNODE_MMAP: mpc->mpc_ops->mpo_check_vnode_mmap = mpe->mpe_function; break; case MAC_CHECK_VNODE_MMAP_DOWNGRADE: mpc->mpc_ops->mpo_check_vnode_mmap_downgrade = mpe->mpe_function; break; case MAC_CHECK_VNODE_MPROTECT: mpc->mpc_ops->mpo_check_vnode_mprotect = mpe->mpe_function; break; case MAC_CHECK_VNODE_OPEN: mpc->mpc_ops->mpo_check_vnode_open = mpe->mpe_function; break; case MAC_CHECK_VNODE_POLL: mpc->mpc_ops->mpo_check_vnode_poll = mpe->mpe_function; break; case MAC_CHECK_VNODE_READ: mpc->mpc_ops->mpo_check_vnode_read = mpe->mpe_function; break; case MAC_CHECK_VNODE_READDIR: mpc->mpc_ops->mpo_check_vnode_readdir = mpe->mpe_function; break; case MAC_CHECK_VNODE_READLINK: mpc->mpc_ops->mpo_check_vnode_readlink = mpe->mpe_function; break; case MAC_CHECK_VNODE_RELABEL: mpc->mpc_ops->mpo_check_vnode_relabel = mpe->mpe_function; break; case MAC_CHECK_VNODE_RENAME_FROM: mpc->mpc_ops->mpo_check_vnode_rename_from = mpe->mpe_function; break; case MAC_CHECK_VNODE_RENAME_TO: mpc->mpc_ops->mpo_check_vnode_rename_to = mpe->mpe_function; break; case MAC_CHECK_VNODE_REVOKE: mpc->mpc_ops->mpo_check_vnode_revoke = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETACL: mpc->mpc_ops->mpo_check_vnode_setacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETEXTATTR: mpc->mpc_ops->mpo_check_vnode_setextattr = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETFLAGS: mpc->mpc_ops->mpo_check_vnode_setflags = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETMODE: mpc->mpc_ops->mpo_check_vnode_setmode = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETOWNER: mpc->mpc_ops->mpo_check_vnode_setowner = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETUTIMES: mpc->mpc_ops->mpo_check_vnode_setutimes = mpe->mpe_function; break; case MAC_CHECK_VNODE_STAT: mpc->mpc_ops->mpo_check_vnode_stat = mpe->mpe_function; break; case MAC_CHECK_VNODE_WRITE: mpc->mpc_ops->mpo_check_vnode_write = mpe->mpe_function; break; /* default: printf("MAC policy `%s': unknown operation %d\n", mpc->mpc_name, mpe->mpe_constant); return (EINVAL); */ } } MAC_POLICY_LIST_LOCK(); if (mac_policy_list_busy > 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (EBUSY); } LIST_FOREACH(tmpc, &mac_policy_list, mpc_list) { if (strcmp(tmpc->mpc_name, mpc->mpc_name) == 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (EEXIST); } } if (mpc->mpc_field_off != NULL) { slot = ffs(mac_policy_offsets_free); if (slot == 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (ENOMEM); } slot--; mac_policy_offsets_free &= ~(1 << slot); *mpc->mpc_field_off = slot; } mpc->mpc_runtime_flags |= MPC_RUNTIME_FLAG_REGISTERED; LIST_INSERT_HEAD(&mac_policy_list, mpc, mpc_list); /* Per-policy initialization. */ if (mpc->mpc_ops->mpo_init != NULL) (*(mpc->mpc_ops->mpo_init))(mpc); MAC_POLICY_LIST_UNLOCK(); printf("Security policy loaded: %s (%s)\n", mpc->mpc_fullname, mpc->mpc_name); return (0); } 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_LIST_LOCK(); if ((mpc->mpc_runtime_flags & MPC_RUNTIME_FLAG_REGISTERED) == 0) { MAC_POLICY_LIST_UNLOCK(); return (0); } #if 0 /* * Don't allow unloading modules with private data. */ if (mpc->mpc_field_off != NULL) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } #endif /* * Only allow the unload to proceed if the module is unloadable * by its own definition. */ if ((mpc->mpc_loadtime_flags & MPC_LOADTIME_FLAG_UNLOADOK) == 0) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } /* * Right now, we EBUSY if the list is in use. In the future, * for reliability reasons, we might want to sleep and wakeup * later to try again. */ if (mac_policy_list_busy > 0) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } if (mpc->mpc_ops->mpo_destroy != NULL) (*(mpc->mpc_ops->mpo_destroy))(mpc); LIST_REMOVE(mpc, mpc_list); MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; mpc->mpc_runtime_flags &= ~MPC_RUNTIME_FLAG_REGISTERED; printf("Security policy unload: %s (%s)\n", mpc->mpc_fullname, mpc->mpc_name); return (0); } /* * Define an error value precedence, and given two arguments, selects the * value with the higher precedence. */ static int 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); } static void mac_init_label(struct label *label) { bzero(label, sizeof(*label)); label->l_flags = MAC_FLAG_INITIALIZED; } static void mac_destroy_label(struct label *label) { KASSERT(label->l_flags & MAC_FLAG_INITIALIZED, ("destroying uninitialized label")); bzero(label, sizeof(*label)); /* implicit: label->l_flags &= ~MAC_FLAG_INITIALIZED; */ } void mac_init_bpfdesc(struct bpf_d *bpf_d) { mac_init_label(&bpf_d->bd_label); MAC_PERFORM(init_bpfdesc_label, &bpf_d->bd_label); #ifdef MAC_DEBUG atomic_add_int(&nmacbpfdescs, 1); #endif } static void mac_init_cred_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_cred_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmaccreds, 1); #endif } void mac_init_cred(struct ucred *cred) { mac_init_cred_label(&cred->cr_label); } void mac_init_devfsdirent(struct devfs_dirent *de) { mac_init_label(&de->de_label); MAC_PERFORM(init_devfsdirent_label, &de->de_label); #ifdef MAC_DEBUG atomic_add_int(&nmacdevfsdirents, 1); #endif } static void mac_init_ifnet_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_ifnet_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacifnets, 1); #endif } void mac_init_ifnet(struct ifnet *ifp) { mac_init_ifnet_label(&ifp->if_label); } void mac_init_ipq(struct ipq *ipq) { mac_init_label(&ipq->ipq_label); MAC_PERFORM(init_ipq_label, &ipq->ipq_label); #ifdef MAC_DEBUG atomic_add_int(&nmacipqs, 1); #endif } int mac_init_mbuf(struct mbuf *m, int flag) { int error; KASSERT(m->m_flags & M_PKTHDR, ("mac_init_mbuf on non-header mbuf")); mac_init_label(&m->m_pkthdr.label); MAC_CHECK(init_mbuf_label, &m->m_pkthdr.label, flag); if (error) { MAC_PERFORM(destroy_mbuf_label, &m->m_pkthdr.label); mac_destroy_label(&m->m_pkthdr.label); } #ifdef MAC_DEBUG if (error == 0) atomic_add_int(&nmacmbufs, 1); #endif return (error); } void mac_init_mount(struct mount *mp) { mac_init_label(&mp->mnt_mntlabel); mac_init_label(&mp->mnt_fslabel); MAC_PERFORM(init_mount_label, &mp->mnt_mntlabel); MAC_PERFORM(init_mount_fs_label, &mp->mnt_fslabel); #ifdef MAC_DEBUG atomic_add_int(&nmacmounts, 1); #endif } static void mac_init_pipe_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_pipe_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacpipes, 1); #endif } void mac_init_pipe(struct pipe *pipe) { struct label *label; label = malloc(sizeof(struct label), M_MACPIPELABEL, M_ZERO|M_WAITOK); pipe->pipe_label = label; pipe->pipe_peer->pipe_label = label; mac_init_pipe_label(label); } static int mac_init_socket_label(struct label *label, int flag) { int error; mac_init_label(label); MAC_CHECK(init_socket_label, label, flag); if (error) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); } #ifdef MAC_DEBUG if (error == 0) atomic_add_int(&nmacsockets, 1); #endif return (error); } static int mac_init_socket_peer_label(struct label *label, int flag) { int error; mac_init_label(label); MAC_CHECK(init_socket_peer_label, label, flag); if (error) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); } return (error); } int mac_init_socket(struct socket *socket, int flag) { int error; error = mac_init_socket_label(&socket->so_label, flag); if (error) return (error); error = mac_init_socket_peer_label(&socket->so_peerlabel, flag); if (error) mac_destroy_socket_label(&socket->so_label); return (error); } void mac_init_vnode_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_vnode_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacvnodes, 1); #endif } void mac_init_vnode(struct vnode *vp) { mac_init_vnode_label(&vp->v_label); } void mac_destroy_bpfdesc(struct bpf_d *bpf_d) { MAC_PERFORM(destroy_bpfdesc_label, &bpf_d->bd_label); mac_destroy_label(&bpf_d->bd_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacbpfdescs, 1); #endif } static void mac_destroy_cred_label(struct label *label) { MAC_PERFORM(destroy_cred_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmaccreds, 1); #endif } void mac_destroy_cred(struct ucred *cred) { mac_destroy_cred_label(&cred->cr_label); } void mac_destroy_devfsdirent(struct devfs_dirent *de) { MAC_PERFORM(destroy_devfsdirent_label, &de->de_label); mac_destroy_label(&de->de_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacdevfsdirents, 1); #endif } static void mac_destroy_ifnet_label(struct label *label) { MAC_PERFORM(destroy_ifnet_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacifnets, 1); #endif } void mac_destroy_ifnet(struct ifnet *ifp) { mac_destroy_ifnet_label(&ifp->if_label); } void mac_destroy_ipq(struct ipq *ipq) { MAC_PERFORM(destroy_ipq_label, &ipq->ipq_label); mac_destroy_label(&ipq->ipq_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacipqs, 1); #endif } void mac_destroy_mbuf(struct mbuf *m) { MAC_PERFORM(destroy_mbuf_label, &m->m_pkthdr.label); mac_destroy_label(&m->m_pkthdr.label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacmbufs, 1); #endif } void mac_destroy_mount(struct mount *mp) { MAC_PERFORM(destroy_mount_label, &mp->mnt_mntlabel); MAC_PERFORM(destroy_mount_fs_label, &mp->mnt_fslabel); mac_destroy_label(&mp->mnt_fslabel); mac_destroy_label(&mp->mnt_mntlabel); #ifdef MAC_DEBUG atomic_subtract_int(&nmacmounts, 1); #endif } static void mac_destroy_pipe_label(struct label *label) { MAC_PERFORM(destroy_pipe_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacpipes, 1); #endif } void mac_destroy_pipe(struct pipe *pipe) { mac_destroy_pipe_label(pipe->pipe_label); free(pipe->pipe_label, M_MACPIPELABEL); } static void mac_destroy_socket_label(struct label *label) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacsockets, 1); #endif } static void mac_destroy_socket_peer_label(struct label *label) { MAC_PERFORM(destroy_socket_peer_label, label); mac_destroy_label(label); } void mac_destroy_socket(struct socket *socket) { mac_destroy_socket_label(&socket->so_label); mac_destroy_socket_peer_label(&socket->so_peerlabel); } void mac_destroy_vnode_label(struct label *label) { MAC_PERFORM(destroy_vnode_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacvnodes, 1); #endif } void mac_destroy_vnode(struct vnode *vp) { mac_destroy_vnode_label(&vp->v_label); } static void mac_copy_pipe_label(struct label *src, struct label *dest) { MAC_PERFORM(copy_pipe_label, src, dest); } void mac_copy_vnode_label(struct label *src, struct label *dest) { MAC_PERFORM(copy_vnode_label, src, dest); } static int mac_check_structmac_consistent(struct mac *mac) { if (mac->m_buflen > MAC_MAX_LABEL_BUF_LEN) return (EINVAL); return (0); } static int mac_externalize_cred_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(cred_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_ifnet_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(ifnet_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_pipe_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(pipe_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_socket_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(socket_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_socket_peer_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(socket_peer_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_vnode_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(vnode_label, label, elements, outbuf, outbuflen); return (error); } static int mac_internalize_cred_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(cred_label, label, string); return (error); } static int mac_internalize_ifnet_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(ifnet_label, label, string); return (error); } static int mac_internalize_pipe_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(pipe_label, label, string); return (error); } static int mac_internalize_socket_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(socket_label, label, string); return (error); } static int mac_internalize_vnode_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(vnode_label, label, string); return (error); } /* * Initialize MAC label for the first kernel process, from which other * kernel processes and threads are spawned. */ void mac_create_proc0(struct ucred *cred) { MAC_PERFORM(create_proc0, cred); } /* * Initialize MAC label for the first userland process, from which other * userland processes and threads are spawned. */ void mac_create_proc1(struct ucred *cred) { MAC_PERFORM(create_proc1, cred); } void mac_thread_userret(struct thread *td) { MAC_PERFORM(thread_userret, td); } /* * 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_create_cred(struct ucred *parent_cred, struct ucred *child_cred) { MAC_PERFORM(create_cred, parent_cred, child_cred); } void mac_update_devfsdirent(struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(update_devfsdirent, de, &de->de_label, vp, &vp->v_label); } void mac_associate_vnode_devfs(struct mount *mp, struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(associate_vnode_devfs, mp, &mp->mnt_fslabel, de, &de->de_label, vp, &vp->v_label); } int mac_associate_vnode_extattr(struct mount *mp, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_associate_vnode_extattr"); MAC_CHECK(associate_vnode_extattr, mp, &mp->mnt_fslabel, vp, &vp->v_label); return (error); } void mac_associate_vnode_singlelabel(struct mount *mp, struct vnode *vp) { MAC_PERFORM(associate_vnode_singlelabel, mp, &mp->mnt_fslabel, vp, &vp->v_label); } int mac_create_vnode_extattr(struct ucred *cred, struct mount *mp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_create_vnode_extattr"); ASSERT_VOP_LOCKED(vp, "mac_create_vnode_extattr"); error = VOP_OPENEXTATTR(vp, cred, curthread); if (error == EOPNOTSUPP) { /* XXX: Optionally abort if transactions not supported. */ if (ea_warn_once == 0) { printf("Warning: transactions not supported " "in EA write.\n"); ea_warn_once = 1; } } else if (error) return (error); MAC_CHECK(create_vnode_extattr, cred, mp, &mp->mnt_fslabel, 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; /* XXX */ return (error); } static int mac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *intlabel) { int error; ASSERT_VOP_LOCKED(vp, "mac_setlabel_vnode_extattr"); error = VOP_OPENEXTATTR(vp, cred, curthread); if (error == EOPNOTSUPP) { /* XXX: Optionally abort if transactions not supported. */ if (ea_warn_once == 0) { printf("Warning: transactions not supported " "in EA write.\n"); ea_warn_once = 1; } } else if (error) return (error); MAC_CHECK(setlabel_vnode_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; /* XXX */ return (error); } void mac_execve_transition(struct ucred *old, struct ucred *new, struct vnode *vp) { ASSERT_VOP_LOCKED(vp, "mac_execve_transition"); MAC_PERFORM(execve_transition, old, new, vp, &vp->v_label); } int mac_execve_will_transition(struct ucred *old, struct vnode *vp) { int result; result = 0; MAC_BOOLEAN(execve_will_transition, ||, old, vp, &vp->v_label); return (result); } int mac_check_vnode_access(struct ucred *cred, struct vnode *vp, int flags) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_access"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_access, cred, vp, &vp->v_label, flags); return (error); } int mac_check_vnode_chdir(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_chdir"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_chdir, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_chroot(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_chroot"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_chroot, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_create(struct ucred *cred, struct vnode *dvp, struct componentname *cnp, struct vattr *vap) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_create"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_create, cred, dvp, &dvp->v_label, cnp, vap); return (error); } int mac_check_vnode_delete(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_delete"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_delete"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_delete, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp, acl_type_t type) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_deleteacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_deleteacl, cred, vp, &vp->v_label, type); return (error); } int mac_check_vnode_exec(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_exec"); if (!mac_enforce_process && !mac_enforce_fs) return (0); MAC_CHECK(check_vnode_exec, cred, vp, &vp->v_label); return (error); } int mac_check_vnode_getacl(struct ucred *cred, struct vnode *vp, acl_type_t type) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_getacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_getacl, cred, vp, &vp->v_label, type); return (error); } int mac_check_vnode_getextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_getextattr"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_getextattr, cred, vp, &vp->v_label, attrnamespace, name, uio); return (error); } int mac_check_vnode_link(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_link"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_link"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_link, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_lookup(struct ucred *cred, struct vnode *dvp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_lookup"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_lookup, cred, dvp, &dvp->v_label, cnp); return (error); } int mac_check_vnode_mmap(struct ucred *cred, struct vnode *vp, int prot) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mmap"); if (!mac_enforce_fs || !mac_enforce_vm) return (0); MAC_CHECK(check_vnode_mmap, cred, vp, &vp->v_label, prot); return (error); } void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot) { int result = *prot; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mmap_downgrade"); if (!mac_enforce_fs || !mac_enforce_vm) return; MAC_PERFORM(check_vnode_mmap_downgrade, cred, vp, &vp->v_label, &result); *prot = result; } int mac_check_vnode_mprotect(struct ucred *cred, struct vnode *vp, int prot) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mprotect"); if (!mac_enforce_fs || !mac_enforce_vm) return (0); MAC_CHECK(check_vnode_mprotect, cred, vp, &vp->v_label, prot); return (error); } int mac_check_vnode_open(struct ucred *cred, struct vnode *vp, mode_t acc_mode) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_open"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_open, cred, vp, &vp->v_label, acc_mode); return (error); } int mac_check_vnode_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_poll"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_poll, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_read"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_read, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_readdir(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_readdir"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_readdir, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_readlink(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_readlink"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_readlink, cred, vp, &vp->v_label); return (error); } static int mac_check_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *newlabel) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_relabel"); MAC_CHECK(check_vnode_relabel, cred, vp, &vp->v_label, newlabel); return (error); } int mac_check_vnode_rename_from(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_rename_from"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_rename_from"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_rename_from, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp, struct vnode *vp, int samedir, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_rename_to"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_rename_to"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_rename_to, cred, dvp, &dvp->v_label, vp, vp != NULL ? &vp->v_label : NULL, samedir, cnp); return (error); } int mac_check_vnode_revoke(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_revoke"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_revoke, cred, vp, &vp->v_label); return (error); } int mac_check_vnode_setacl(struct ucred *cred, struct vnode *vp, acl_type_t type, struct acl *acl) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setacl, cred, vp, &vp->v_label, type, acl); return (error); } int mac_check_vnode_setextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setextattr"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setextattr, cred, vp, &vp->v_label, attrnamespace, name, uio); return (error); } int mac_check_vnode_setflags(struct ucred *cred, struct vnode *vp, u_long flags) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setflags"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setflags, cred, vp, &vp->v_label, flags); return (error); } int mac_check_vnode_setmode(struct ucred *cred, struct vnode *vp, mode_t mode) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setmode"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setmode, cred, vp, &vp->v_label, mode); return (error); } int mac_check_vnode_setowner(struct ucred *cred, struct vnode *vp, uid_t uid, gid_t gid) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setowner"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setowner, cred, vp, &vp->v_label, uid, gid); return (error); } int mac_check_vnode_setutimes(struct ucred *cred, struct vnode *vp, struct timespec atime, struct timespec mtime) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setutimes"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setutimes, cred, vp, &vp->v_label, atime, mtime); return (error); } int mac_check_vnode_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_stat"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_stat, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_write"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_write, active_cred, file_cred, vp, &vp->v_label); return (error); } /* * 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. */ static void mac_cred_mmapped_drop_perms(struct thread *td, struct ucred *cred) { /* XXX freeze all other threads */ mac_cred_mmapped_drop_perms_recurse(td, cred, &td->td_proc->p_vmspace->vm_map); /* XXX allow other threads to continue */ } 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_cred_mmapped_drop_perms_recurse(struct thread *td, struct ucred *cred, struct vm_map *map) { struct vm_map_entry *vme; int result; vm_prot_t revokeperms; vm_object_t object; vm_ooffset_t offset; struct vnode *vp; if (!mac_mmap_revocation) return; vm_map_lock_read(map); for (vme = map->header.next; vme != &map->header; vme = vme->next) { if (vme->eflags & MAP_ENTRY_IS_SUB_MAP) { mac_cred_mmapped_drop_perms_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; while (object->backing_object != NULL) { object = object->backing_object; offset += object->backing_object_offset; } /* * 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, td); result = vme->max_protection; mac_check_vnode_mmap_downgrade(cred, vp, &result); VOP_UNLOCK(vp, 0, td); /* * 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)); vm_map_lock_upgrade(map); /* * 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); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); vm_object_page_clean(object, OFF_TO_IDX(offset), OFF_TO_IDX(offset + vme->end - vme->start + PAGE_MASK), OBJPC_SYNC); VOP_UNLOCK(vp, 0, td); 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_lock_downgrade(map); } vm_map_unlock_read(map); } /* * 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. */ static void mac_relabel_cred(struct ucred *cred, struct label *newlabel) { MAC_PERFORM(relabel_cred, cred, newlabel); } void mac_relabel_vnode(struct ucred *cred, struct vnode *vp, struct label *newlabel) { MAC_PERFORM(relabel_vnode, cred, vp, &vp->v_label, newlabel); } void mac_create_ifnet(struct ifnet *ifnet) { MAC_PERFORM(create_ifnet, ifnet, &ifnet->if_label); } void mac_create_bpfdesc(struct ucred *cred, struct bpf_d *bpf_d) { MAC_PERFORM(create_bpfdesc, cred, bpf_d, &bpf_d->bd_label); } void mac_create_socket(struct ucred *cred, struct socket *socket) { MAC_PERFORM(create_socket, cred, socket, &socket->so_label); } void mac_create_pipe(struct ucred *cred, struct pipe *pipe) { MAC_PERFORM(create_pipe, cred, pipe, pipe->pipe_label); } void mac_create_socket_from_socket(struct socket *oldsocket, struct socket *newsocket) { MAC_PERFORM(create_socket_from_socket, oldsocket, &oldsocket->so_label, newsocket, &newsocket->so_label); } static void mac_relabel_socket(struct ucred *cred, struct socket *socket, struct label *newlabel) { MAC_PERFORM(relabel_socket, cred, socket, &socket->so_label, newlabel); } static void mac_relabel_pipe(struct ucred *cred, struct pipe *pipe, struct label *newlabel) { MAC_PERFORM(relabel_pipe, cred, pipe, pipe->pipe_label, newlabel); } void mac_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct socket *socket) { MAC_PERFORM(set_socket_peer_from_mbuf, mbuf, &mbuf->m_pkthdr.label, socket, &socket->so_peerlabel); } void mac_set_socket_peer_from_socket(struct socket *oldsocket, struct socket *newsocket) { MAC_PERFORM(set_socket_peer_from_socket, oldsocket, &oldsocket->so_label, newsocket, &newsocket->so_peerlabel); } void mac_create_datagram_from_ipq(struct ipq *ipq, struct mbuf *datagram) { MAC_PERFORM(create_datagram_from_ipq, ipq, &ipq->ipq_label, datagram, &datagram->m_pkthdr.label); } void mac_create_fragment(struct mbuf *datagram, struct mbuf *fragment) { MAC_PERFORM(create_fragment, datagram, &datagram->m_pkthdr.label, fragment, &fragment->m_pkthdr.label); } void mac_create_ipq(struct mbuf *fragment, struct ipq *ipq) { MAC_PERFORM(create_ipq, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); } void mac_create_mbuf_from_mbuf(struct mbuf *oldmbuf, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_from_mbuf, oldmbuf, &oldmbuf->m_pkthdr.label, newmbuf, &newmbuf->m_pkthdr.label); } void mac_create_mbuf_from_bpfdesc(struct bpf_d *bpf_d, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_bpfdesc, bpf_d, &bpf_d->bd_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_linklayer(struct ifnet *ifnet, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_linklayer, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_from_ifnet(struct ifnet *ifnet, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_ifnet, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_multicast_encap(struct mbuf *oldmbuf, struct ifnet *ifnet, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_multicast_encap, oldmbuf, &oldmbuf->m_pkthdr.label, ifnet, &ifnet->if_label, newmbuf, &newmbuf->m_pkthdr.label); } void mac_create_mbuf_netlayer(struct mbuf *oldmbuf, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_netlayer, oldmbuf, &oldmbuf->m_pkthdr.label, newmbuf, &newmbuf->m_pkthdr.label); } int mac_fragment_match(struct mbuf *fragment, struct ipq *ipq) { int result; result = 1; MAC_BOOLEAN(fragment_match, &&, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); return (result); } void mac_update_ipq(struct mbuf *fragment, struct ipq *ipq) { MAC_PERFORM(update_ipq, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); } void mac_create_mbuf_from_socket(struct socket *socket, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_socket, socket, &socket->so_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mount(struct ucred *cred, struct mount *mp) { MAC_PERFORM(create_mount, cred, mp, &mp->mnt_mntlabel, &mp->mnt_fslabel); } void mac_create_root_mount(struct ucred *cred, struct mount *mp) { MAC_PERFORM(create_root_mount, cred, mp, &mp->mnt_mntlabel, &mp->mnt_fslabel); } int mac_check_bpfdesc_receive(struct bpf_d *bpf_d, struct ifnet *ifnet) { int error; if (!mac_enforce_network) return (0); MAC_CHECK(check_bpfdesc_receive, bpf_d, &bpf_d->bd_label, ifnet, &ifnet->if_label); return (error); } static int mac_check_cred_relabel(struct ucred *cred, struct label *newlabel) { int error; MAC_CHECK(check_cred_relabel, cred, newlabel); return (error); } int mac_check_cred_visible(struct ucred *u1, struct ucred *u2) { int error; if (!mac_enforce_process) return (0); MAC_CHECK(check_cred_visible, u1, u2); return (error); } int mac_check_ifnet_transmit(struct ifnet *ifnet, struct mbuf *mbuf) { int error; if (!mac_enforce_network) return (0); KASSERT(mbuf->m_flags & M_PKTHDR, ("packet has no pkthdr")); if (!(mbuf->m_pkthdr.label.l_flags & MAC_FLAG_INITIALIZED)) if_printf(ifnet, "not initialized\n"); MAC_CHECK(check_ifnet_transmit, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); return (error); } int mac_check_mount_stat(struct ucred *cred, struct mount *mount) { int error; if (!mac_enforce_fs) return (0); MAC_CHECK(check_mount_stat, cred, mount, &mount->mnt_mntlabel); return (error); } int mac_check_pipe_ioctl(struct ucred *cred, struct pipe *pipe, unsigned long cmd, void *data) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_ioctl, cred, pipe, pipe->pipe_label, cmd, data); return (error); } int mac_check_pipe_poll(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_poll, cred, pipe, pipe->pipe_label); return (error); } int mac_check_pipe_read(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_read, cred, pipe, pipe->pipe_label); return (error); } static int mac_check_pipe_relabel(struct ucred *cred, struct pipe *pipe, struct label *newlabel) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_relabel, cred, pipe, pipe->pipe_label, newlabel); return (error); } int mac_check_pipe_stat(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_stat, cred, pipe, pipe->pipe_label); return (error); } int mac_check_pipe_write(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_write, cred, pipe, pipe->pipe_label); return (error); } int mac_check_proc_debug(struct ucred *cred, struct proc *proc) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_debug, cred, proc); return (error); } int mac_check_proc_sched(struct ucred *cred, struct proc *proc) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_sched, cred, proc); return (error); } int mac_check_proc_signal(struct ucred *cred, struct proc *proc, int signum) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_signal, cred, proc, signum); return (error); } int mac_check_socket_bind(struct ucred *ucred, struct socket *socket, struct sockaddr *sockaddr) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_bind, ucred, socket, &socket->so_label, sockaddr); return (error); } int mac_check_socket_connect(struct ucred *cred, struct socket *socket, struct sockaddr *sockaddr) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_connect, cred, socket, &socket->so_label, sockaddr); return (error); } int mac_check_socket_deliver(struct socket *socket, struct mbuf *mbuf) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_deliver, socket, &socket->so_label, mbuf, &mbuf->m_pkthdr.label); return (error); } int mac_check_socket_listen(struct ucred *cred, struct socket *socket) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_listen, cred, socket, &socket->so_label); return (error); } int mac_check_socket_receive(struct ucred *cred, struct socket *so) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_receive, cred, so, &so->so_label); return (error); } static int mac_check_socket_relabel(struct ucred *cred, struct socket *socket, struct label *newlabel) { int error; MAC_CHECK(check_socket_relabel, cred, socket, &socket->so_label, newlabel); return (error); } int mac_check_socket_send(struct ucred *cred, struct socket *so) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_send, cred, so, &so->so_label); return (error); } int mac_check_socket_visible(struct ucred *cred, struct socket *socket) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_visible, cred, socket, &socket->so_label); return (error); } int mac_check_system_reboot(struct ucred *cred, int howto) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_system_reboot"); if (!mac_enforce_reboot) return (0); MAC_CHECK(check_system_reboot, cred, howto); return (error); } int mac_check_system_swapon(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_system_swapon"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_system_swapon, cred, vp, &vp->v_label); + return (error); +} + +int +mac_check_system_sysctl(struct ucred *cred, int *name, u_int namelen, + void *old, size_t *oldlenp, int inkernel, void *new, size_t newlen) +{ + int error; + + /* + * XXXMAC: We're very much like to assert the SYSCTL_LOCK here, + * but since it's not exported from kern_sysctl.c, we can't. + */ + if (!mac_enforce_sysctl) + return (0); + + MAC_CHECK(check_system_sysctl, cred, name, namelen, old, oldlenp, + inkernel, new, newlen); + return (error); } int mac_ioctl_ifnet_get(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet) { char *elements, *buffer; struct mac mac; int error; 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); error = mac_externalize_ifnet_label(&ifnet->if_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } int mac_ioctl_ifnet_set(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet) { struct label intlabel; struct mac mac; char *buffer; int error; 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); } mac_init_ifnet_label(&intlabel); error = mac_internalize_ifnet_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_ifnet_label(&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 = suser_cred(cred, 0); if (error) { mac_destroy_ifnet_label(&intlabel); return (error); } MAC_CHECK(check_ifnet_relabel, cred, ifnet, &ifnet->if_label, &intlabel); if (error) { mac_destroy_ifnet_label(&intlabel); return (error); } MAC_PERFORM(relabel_ifnet, cred, ifnet, &ifnet->if_label, &intlabel); mac_destroy_ifnet_label(&intlabel); return (0); } void mac_create_devfs_vnode(struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(create_devfs_vnode, de, &de->de_label, vp, &vp->v_label); } void mac_create_devfs_device(dev_t dev, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_device, dev, de, &de->de_label); } void mac_create_devfs_symlink(struct ucred *cred, struct devfs_dirent *dd, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_symlink, cred, dd, &dd->de_label, de, &de->de_label); } void mac_create_devfs_directory(char *dirname, int dirnamelen, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_directory, dirname, dirnamelen, de, &de->de_label); } int mac_setsockopt_label_set(struct ucred *cred, struct socket *so, struct mac *mac) { struct label intlabel; char *buffer; int 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); } mac_init_socket_label(&intlabel, M_WAITOK); error = mac_internalize_socket_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_socket_label(&intlabel); return (error); } mac_check_socket_relabel(cred, so, &intlabel); if (error) { mac_destroy_socket_label(&intlabel); return (error); } mac_relabel_socket(cred, so, &intlabel); mac_destroy_socket_label(&intlabel); return (0); } int mac_pipe_label_set(struct ucred *cred, struct pipe *pipe, struct label *label) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); error = mac_check_pipe_relabel(cred, pipe, label); if (error) return (error); mac_relabel_pipe(cred, pipe, label); return (0); } int mac_getsockopt_label_get(struct ucred *cred, struct socket *so, struct mac *mac) { char *buffer, *elements; int 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_externalize_socket_label(&so->so_label, elements, buffer, mac->m_buflen, M_WAITOK); 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_get(struct ucred *cred, struct socket *so, struct mac *mac) { char *elements, *buffer; int 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_externalize_socket_peer_label(&so->so_peerlabel, elements, buffer, mac->m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac->m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * 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_setlabel_vnode_extattr(ap->a_cred, vp, intlabel); if (error) return (error); mac_relabel_vnode(ap->a_cred, vp, intlabel); return (0); } static 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_check_vnode_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); } int __mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) { char *elements, *buffer; struct mac mac; struct proc *tproc; struct ucred *tcred; int error; error = copyin(SCARG(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_externalize_cred_label(&tcred->cr_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); crfree(tcred); return (error); } /* * MPSAFE */ int __mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) { char *elements, *buffer; struct mac mac; int error; error = copyin(uap->mac_p, &mac, sizeof(mac)); if (error) return (error); error = mac_check_structmac_consistent(&mac); if (error) return (error); elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL); if (error) { free(elements, M_MACTEMP); return (error); } buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); error = mac_externalize_cred_label(&td->td_ucred->cr_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) { struct ucred *newcred, *oldcred; struct label intlabel; struct proc *p; struct mac mac; char *buffer; int error; 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); } mac_init_cred_label(&intlabel); error = mac_internalize_cred_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_cred_label(&intlabel); return (error); } newcred = crget(); p = td->td_proc; PROC_LOCK(p); oldcred = p->p_ucred; error = mac_check_cred_relabel(oldcred, &intlabel); if (error) { PROC_UNLOCK(p); crfree(newcred); goto out; } setsugid(p); crcopy(newcred, oldcred); mac_relabel_cred(newcred, &intlabel); p->p_ucred = newcred; /* * Grab additional reference for use while revoking mmaps, prior * to releasing the proc lock and sharing the cred. */ crhold(newcred); PROC_UNLOCK(p); if (mac_enforce_vm) { mtx_lock(&Giant); mac_cred_mmapped_drop_perms(td, newcred); mtx_unlock(&Giant); } crfree(newcred); /* Free revocation reference. */ crfree(oldcred); out: mac_destroy_cred_label(&intlabel); return (error); } /* * MPSAFE */ int __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) { char *elements, *buffer; struct label intlabel; struct file *fp; struct mac mac; struct vnode *vp; struct pipe *pipe; 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); mtx_lock(&Giant); /* VFS */ error = fget(td, SCARG(uap, fd), &fp); if (error) goto out; label_type = fp->f_type; switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: vp = (struct vnode *)fp->f_data; mac_init_vnode_label(&intlabel); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); mac_copy_vnode_label(&vp->v_label, &intlabel); VOP_UNLOCK(vp, 0, td); break; case DTYPE_PIPE: pipe = (struct pipe *)fp->f_data; mac_init_pipe_label(&intlabel); PIPE_LOCK(pipe); mac_copy_pipe_label(pipe->pipe_label, &intlabel); PIPE_UNLOCK(pipe); break; default: error = EINVAL; fdrop(fp, td); goto out; } fdrop(fp, td); switch (label_type) { case DTYPE_FIFO: case DTYPE_VNODE: if (error == 0) error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); mac_destroy_vnode_label(&intlabel); break; case DTYPE_PIPE: error = mac_externalize_pipe_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); mac_destroy_pipe_label(&intlabel); break; default: panic("__mac_get_fd: corrupted label_type"); } if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_get_file(struct thread *td, struct __mac_get_file_args *uap) { char *elements, *buffer; struct nameidata nd; struct label intlabel; struct mac mac; int 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); mtx_lock(&Giant); /* VFS */ NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_USERSPACE, uap->path_p, td); error = namei(&nd); if (error) goto out; mac_init_vnode_label(&intlabel); mac_copy_vnode_label(&nd.ni_vp->v_label, &intlabel); error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); NDFREE(&nd, 0); mac_destroy_vnode_label(&intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_get_link(struct thread *td, struct __mac_get_link_args *uap) { char *elements, *buffer; struct nameidata nd; struct label intlabel; struct mac mac; int 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); mtx_lock(&Giant); /* VFS */ NDINIT(&nd, LOOKUP, LOCKLEAF | NOFOLLOW, UIO_USERSPACE, uap->path_p, td); error = namei(&nd); if (error) goto out; mac_init_vnode_label(&intlabel); mac_copy_vnode_label(&nd.ni_vp->v_label, &intlabel); error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); NDFREE(&nd, 0); mac_destroy_vnode_label(&intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) { struct label intlabel; struct pipe *pipe; 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); } mtx_lock(&Giant); /* VFS */ error = fget(td, SCARG(uap, fd), &fp); if (error) goto out; switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); if (error) { mac_destroy_vnode_label(&intlabel); break; } vp = (struct vnode *)fp->f_data; error = vn_start_write(vp, &mp, V_WAIT | PCATCH); if (error != 0) { mac_destroy_vnode_label(&intlabel); break; } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); error = vn_setlabel(vp, &intlabel, td->td_ucred); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); mac_destroy_vnode_label(&intlabel); break; case DTYPE_PIPE: mac_init_pipe_label(&intlabel); error = mac_internalize_pipe_label(&intlabel, buffer); if (error == 0) { pipe = (struct pipe *)fp->f_data; PIPE_LOCK(pipe); error = mac_pipe_label_set(td->td_ucred, pipe, &intlabel); PIPE_UNLOCK(pipe); } mac_destroy_pipe_label(&intlabel); break; default: error = EINVAL; } fdrop(fp, td); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_file(struct thread *td, struct __mac_set_file_args *uap) { struct label intlabel; struct nameidata nd; struct mount *mp; struct mac mac; char *buffer; int 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); } mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_vnode_label(&intlabel); return (error); } mtx_lock(&Giant); /* VFS */ 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); mtx_unlock(&Giant); /* VFS */ mac_destroy_vnode_label(&intlabel); return (error); } /* * MPSAFE */ int __mac_set_link(struct thread *td, struct __mac_set_link_args *uap) { struct label intlabel; struct nameidata nd; struct mount *mp; struct mac mac; char *buffer; int 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); } mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_vnode_label(&intlabel); return (error); } mtx_lock(&Giant); /* VFS */ 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); mtx_unlock(&Giant); /* VFS */ mac_destroy_vnode_label(&intlabel); return (error); } /* * MPSAFE */ int 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(SCARG(uap, policy), target, sizeof(target), NULL); if (error) return (error); error = ENOSYS; MAC_POLICY_LIST_BUSY(); 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, SCARG(uap, call), SCARG(uap, arg)); goto out; } } out: MAC_POLICY_LIST_UNBUSY(); return (error); } 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); #else /* !MAC */ int __mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) { return (ENOSYS); } int __mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) { return (ENOSYS); } int __mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) { return (ENOSYS); } int __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) { return (ENOSYS); } int __mac_get_file(struct thread *td, struct __mac_get_file_args *uap) { return (ENOSYS); } int __mac_get_link(struct thread *td, struct __mac_get_link_args *uap) { return (ENOSYS); } int __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) { return (ENOSYS); } int __mac_set_file(struct thread *td, struct __mac_set_file_args *uap) { return (ENOSYS); } int __mac_set_link(struct thread *td, struct __mac_set_link_args *uap) { return (ENOSYS); } int mac_syscall(struct thread *td, struct mac_syscall_args *uap) { return (ENOSYS); } #endif Index: head/sys/security/mac/mac_syscalls.c =================================================================== --- head/sys/security/mac/mac_syscalls.c (revision 106024) +++ head/sys/security/mac/mac_syscalls.c (revision 106025) @@ -1,4010 +1,4038 @@ /*- * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson * Copyright (c) 2001 Ilmar S. Habibulin * Copyright (c) 2001, 2002 Networks Associates Technology, 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 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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. * * Framework for extensible kernel access control. Kernel and userland * interface to the framework, policy registration and composition. */ #include "opt_mac.h" #include "opt_devfs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MAC /* * Declare that the kernel provides MAC support, version 1. 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, 1); SYSCTL_DECL(_security); SYSCTL_NODE(_security, OID_AUTO, mac, CTLFLAG_RW, 0, "TrustedBSD MAC policy controls"); #if MAC_MAX_POLICIES > 32 #error "MAC_MAX_POLICIES too large" #endif static unsigned int mac_max_policies = MAC_MAX_POLICIES; static unsigned int mac_policy_offsets_free = (1 << MAC_MAX_POLICIES) - 1; SYSCTL_UINT(_security_mac, OID_AUTO, max_policies, CTLFLAG_RD, &mac_max_policies, 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; /* * Warn about EA transactions only the first time they happen. * Weak coherency, no locking. */ static int ea_warn_once = 0; static int mac_enforce_fs = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_fs, CTLFLAG_RW, &mac_enforce_fs, 0, "Enforce MAC policy on file system objects"); TUNABLE_INT("security.mac.enforce_fs", &mac_enforce_fs); static int mac_enforce_network = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_network, CTLFLAG_RW, &mac_enforce_network, 0, "Enforce MAC policy on network packets"); TUNABLE_INT("security.mac.enforce_network", &mac_enforce_network); static int mac_enforce_pipe = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_pipe, CTLFLAG_RW, &mac_enforce_pipe, 0, "Enforce MAC policy on pipe operations"); TUNABLE_INT("security.mac.enforce_pipe", &mac_enforce_pipe); static int mac_enforce_process = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_process, CTLFLAG_RW, &mac_enforce_process, 0, "Enforce MAC policy on inter-process operations"); TUNABLE_INT("security.mac.enforce_process", &mac_enforce_process); static int mac_enforce_reboot = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_reboot, CTLFLAG_RW, &mac_enforce_reboot, 0, "Enforce MAC policy for reboot operations"); TUNABLE_INT("security.mac.enforce_reboot", &mac_enforce_reboot); static int mac_enforce_socket = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_socket, CTLFLAG_RW, &mac_enforce_socket, 0, "Enforce MAC policy on socket operations"); TUNABLE_INT("security.mac.enforce_socket", &mac_enforce_socket); +static int mac_enforce_sysctl = 1; +SYSCTL_INT(_security_mac, OID_AUTO, enforce_sysctl, CTLFLAG_RW, + &mac_enforce_sysctl, 0, "Enforce MAC policy on sysctl operations"); +TUNABLE_INT("security.mac.enforce_sysctl", &mac_enforce_sysctl); + static int mac_enforce_vm = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_vm, CTLFLAG_RW, &mac_enforce_vm, 0, "Enforce MAC policy on vm operations"); TUNABLE_INT("security.mac.enforce_vm", &mac_enforce_vm); static int mac_cache_fslabel_in_vnode = 1; SYSCTL_INT(_security_mac, OID_AUTO, cache_fslabel_in_vnode, CTLFLAG_RW, &mac_cache_fslabel_in_vnode, 0, "Cache mount fslabel in vnode"); TUNABLE_INT("security.mac.cache_fslabel_in_vnode", &mac_cache_fslabel_in_vnode); 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"); #ifdef MAC_DEBUG SYSCTL_NODE(_security_mac, OID_AUTO, debug, CTLFLAG_RW, 0, "TrustedBSD MAC debug info"); static int mac_debug_label_fallback = 0; SYSCTL_INT(_security_mac_debug, OID_AUTO, label_fallback, CTLFLAG_RW, &mac_debug_label_fallback, 0, "Filesystems should fall back to fs label" "when label is corrupted."); TUNABLE_INT("security.mac.debug_label_fallback", &mac_debug_label_fallback); SYSCTL_NODE(_security_mac_debug, OID_AUTO, counters, CTLFLAG_RW, 0, "TrustedBSD MAC object counters"); static unsigned int nmacmbufs, nmaccreds, nmacifnets, nmacbpfdescs, nmacsockets, nmacmounts, nmactemp, nmacvnodes, nmacdevfsdirents, nmacipqs, nmacpipes; SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, mbufs, CTLFLAG_RD, &nmacmbufs, 0, "number of mbufs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, creds, CTLFLAG_RD, &nmaccreds, 0, "number of ucreds in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ifnets, CTLFLAG_RD, &nmacifnets, 0, "number of ifnets in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ipqs, CTLFLAG_RD, &nmacipqs, 0, "number of ipqs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, bpfdescs, CTLFLAG_RD, &nmacbpfdescs, 0, "number of bpfdescs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, sockets, CTLFLAG_RD, &nmacsockets, 0, "number of sockets in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, pipes, CTLFLAG_RD, &nmacpipes, 0, "number of pipes in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, mounts, CTLFLAG_RD, &nmacmounts, 0, "number of mounts in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, temp, CTLFLAG_RD, &nmactemp, 0, "number of temporary labels in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, vnodes, CTLFLAG_RD, &nmacvnodes, 0, "number of vnodes in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, devfsdirents, CTLFLAG_RD, &nmacdevfsdirents, 0, "number of devfs dirents inuse"); #endif static int error_select(int error1, int error2); static int mac_policy_register(struct mac_policy_conf *mpc); static int mac_policy_unregister(struct mac_policy_conf *mpc); static void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot); static void mac_cred_mmapped_drop_perms_recurse(struct thread *td, struct ucred *cred, struct vm_map *map); static void mac_destroy_socket_label(struct label *label); static int mac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *intlabel); MALLOC_DEFINE(M_MACOPVEC, "macopvec", "MAC policy operation vector"); MALLOC_DEFINE(M_MACPIPELABEL, "macpipelabel", "MAC labels for pipes"); MALLOC_DEFINE(M_MACTEMP, "mactemp", "MAC temporary label storage"); /* * mac_policy_list_lock protects the consistency of 'mac_policy_list', * the linked list of attached policy modules. Read-only consumers of * the list must acquire a shared lock for the duration of their use; * writers must acquire an exclusive lock. Note that for compound * operations, locks should be held for the entire compound operation, * and that this is not yet done for relabel requests. */ static struct mtx mac_policy_list_lock; static LIST_HEAD(, mac_policy_conf) mac_policy_list; static int mac_policy_list_busy; #define MAC_POLICY_LIST_LOCKINIT() mtx_init(&mac_policy_list_lock, \ "mac_policy_list_lock", NULL, MTX_DEF); #define MAC_POLICY_LIST_LOCK() mtx_lock(&mac_policy_list_lock); #define MAC_POLICY_LIST_UNLOCK() mtx_unlock(&mac_policy_list_lock); #define MAC_POLICY_LIST_BUSY() do { \ MAC_POLICY_LIST_LOCK(); \ mac_policy_list_busy++; \ MAC_POLICY_LIST_UNLOCK(); \ } while (0) #define MAC_POLICY_LIST_UNBUSY() do { \ MAC_POLICY_LIST_LOCK(); \ mac_policy_list_busy--; \ if (mac_policy_list_busy < 0) \ panic("Extra mac_policy_list_busy--"); \ MAC_POLICY_LIST_UNLOCK(); \ } while (0) /* * MAC_CHECK performs the designated check by walking the policy * module list and checking with each as to how it feels about the * request. Note that it returns its value via 'error' in the scope * of the caller. */ #define MAC_CHECK(check, args...) do { \ struct mac_policy_conf *mpc; \ \ error = 0; \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## check != NULL) \ error = error_select( \ mpc->mpc_ops->mpo_ ## check (args), \ error); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) /* * MAC_BOOLEAN performs the designated boolean composition by walking * the module list, invoking each instance of the operation, and * combining the results using the passed C operator. Note that it * returns its value via 'result' in the scope of the caller, which * should be initialized by the caller in a meaningful way to get * a meaningful result. */ #define MAC_BOOLEAN(operation, composition, args...) do { \ struct mac_policy_conf *mpc; \ \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ result = result composition \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) #define MAC_EXTERNALIZE(type, label, elementlist, outbuf, \ outbuflen) do { \ char *curptr, *curptr_start, *element_name, *element_temp; \ size_t left, left_start, len; \ int claimed, first, first_start, ignorenotfound; \ \ error = 0; \ element_temp = elementlist; \ curptr = outbuf; \ curptr[0] = '\0'; \ left = outbuflen; \ first = 1; \ while ((element_name = strsep(&element_temp, ",")) != NULL) { \ curptr_start = curptr; \ left_start = left; \ first_start = first; \ if (element_name[0] == '?') { \ element_name++; \ ignorenotfound = 1; \ } else \ ignorenotfound = 0; \ claimed = 0; \ if (first) { \ len = snprintf(curptr, left, "%s/", \ element_name); \ first = 0; \ } else \ len = snprintf(curptr, left, ",%s/", \ element_name); \ if (len >= left) { \ error = EINVAL; /* XXXMAC: E2BIG */ \ break; \ } \ curptr += len; \ left -= len; \ \ MAC_CHECK(externalize_ ## type, label, element_name, \ curptr, left, &len, &claimed); \ if (error) \ break; \ if (claimed == 1) { \ if (len >= outbuflen) { \ error = EINVAL; /* XXXMAC: E2BIG */ \ break; \ } \ curptr += len; \ left -= len; \ } else if (claimed == 0 && ignorenotfound) { \ /* \ * Revert addition of the label element \ * name. \ */ \ curptr = curptr_start; \ *curptr = '\0'; \ left = left_start; \ first = first_start; \ } else { \ error = EINVAL; /* XXXMAC: ENOLABEL */ \ break; \ } \ } \ } while (0) #define MAC_INTERNALIZE(type, label, instring) do { \ char *element, *element_name, *element_data; \ int claimed; \ \ error = 0; \ element = instring; \ while ((element_name = strsep(&element, ",")) != NULL) { \ element_data = element_name; \ element_name = strsep(&element_data, "/"); \ if (element_data == NULL) { \ error = EINVAL; \ break; \ } \ claimed = 0; \ MAC_CHECK(internalize_ ## type, label, element_name, \ element_data, &claimed); \ if (error) \ break; \ if (claimed != 1) { \ /* XXXMAC: Another error here? */ \ error = EINVAL; \ break; \ } \ } \ } while (0) /* * MAC_PERFORM performs the designated operation by walking the policy * module list and invoking that operation for each policy. */ #define MAC_PERFORM(operation, args...) do { \ struct mac_policy_conf *mpc; \ \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) /* * Initialize the MAC subsystem, including appropriate SMP locks. */ static void mac_init(void) { LIST_INIT(&mac_policy_list); MAC_POLICY_LIST_LOCKINIT(); } /* * 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; } /* * 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; 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: break; } return (error); } static int mac_policy_register(struct mac_policy_conf *mpc) { struct mac_policy_conf *tmpc; struct mac_policy_op_entry *mpe; int slot; MALLOC(mpc->mpc_ops, struct mac_policy_ops *, sizeof(*mpc->mpc_ops), M_MACOPVEC, M_WAITOK | M_ZERO); for (mpe = mpc->mpc_entries; mpe->mpe_constant != MAC_OP_LAST; mpe++) { switch (mpe->mpe_constant) { case MAC_OP_LAST: /* * Doesn't actually happen, but this allows checking * that all enumerated values are handled. */ break; case MAC_DESTROY: mpc->mpc_ops->mpo_destroy = mpe->mpe_function; break; case MAC_INIT: mpc->mpc_ops->mpo_init = mpe->mpe_function; break; case MAC_SYSCALL: mpc->mpc_ops->mpo_syscall = mpe->mpe_function; break; case MAC_INIT_BPFDESC_LABEL: mpc->mpc_ops->mpo_init_bpfdesc_label = mpe->mpe_function; break; case MAC_INIT_CRED_LABEL: mpc->mpc_ops->mpo_init_cred_label = mpe->mpe_function; break; case MAC_INIT_DEVFSDIRENT_LABEL: mpc->mpc_ops->mpo_init_devfsdirent_label = mpe->mpe_function; break; case MAC_INIT_IFNET_LABEL: mpc->mpc_ops->mpo_init_ifnet_label = mpe->mpe_function; break; case MAC_INIT_IPQ_LABEL: mpc->mpc_ops->mpo_init_ipq_label = mpe->mpe_function; break; case MAC_INIT_MBUF_LABEL: mpc->mpc_ops->mpo_init_mbuf_label = mpe->mpe_function; break; case MAC_INIT_MOUNT_LABEL: mpc->mpc_ops->mpo_init_mount_label = mpe->mpe_function; break; case MAC_INIT_MOUNT_FS_LABEL: mpc->mpc_ops->mpo_init_mount_fs_label = mpe->mpe_function; break; case MAC_INIT_PIPE_LABEL: mpc->mpc_ops->mpo_init_pipe_label = mpe->mpe_function; break; case MAC_INIT_SOCKET_LABEL: mpc->mpc_ops->mpo_init_socket_label = mpe->mpe_function; break; case MAC_INIT_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_init_socket_peer_label = mpe->mpe_function; break; case MAC_INIT_VNODE_LABEL: mpc->mpc_ops->mpo_init_vnode_label = mpe->mpe_function; break; case MAC_DESTROY_BPFDESC_LABEL: mpc->mpc_ops->mpo_destroy_bpfdesc_label = mpe->mpe_function; break; case MAC_DESTROY_CRED_LABEL: mpc->mpc_ops->mpo_destroy_cred_label = mpe->mpe_function; break; case MAC_DESTROY_DEVFSDIRENT_LABEL: mpc->mpc_ops->mpo_destroy_devfsdirent_label = mpe->mpe_function; break; case MAC_DESTROY_IFNET_LABEL: mpc->mpc_ops->mpo_destroy_ifnet_label = mpe->mpe_function; break; case MAC_DESTROY_IPQ_LABEL: mpc->mpc_ops->mpo_destroy_ipq_label = mpe->mpe_function; break; case MAC_DESTROY_MBUF_LABEL: mpc->mpc_ops->mpo_destroy_mbuf_label = mpe->mpe_function; break; case MAC_DESTROY_MOUNT_LABEL: mpc->mpc_ops->mpo_destroy_mount_label = mpe->mpe_function; break; case MAC_DESTROY_MOUNT_FS_LABEL: mpc->mpc_ops->mpo_destroy_mount_fs_label = mpe->mpe_function; break; case MAC_DESTROY_PIPE_LABEL: mpc->mpc_ops->mpo_destroy_pipe_label = mpe->mpe_function; break; case MAC_DESTROY_SOCKET_LABEL: mpc->mpc_ops->mpo_destroy_socket_label = mpe->mpe_function; break; case MAC_DESTROY_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_destroy_socket_peer_label = mpe->mpe_function; break; case MAC_DESTROY_VNODE_LABEL: mpc->mpc_ops->mpo_destroy_vnode_label = mpe->mpe_function; break; case MAC_COPY_PIPE_LABEL: mpc->mpc_ops->mpo_copy_pipe_label = mpe->mpe_function; break; case MAC_COPY_VNODE_LABEL: mpc->mpc_ops->mpo_copy_vnode_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_CRED_LABEL: mpc->mpc_ops->mpo_externalize_cred_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_IFNET_LABEL: mpc->mpc_ops->mpo_externalize_ifnet_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_PIPE_LABEL: mpc->mpc_ops->mpo_externalize_pipe_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_SOCKET_LABEL: mpc->mpc_ops->mpo_externalize_socket_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_externalize_socket_peer_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_VNODE_LABEL: mpc->mpc_ops->mpo_externalize_vnode_label = mpe->mpe_function; break; case MAC_INTERNALIZE_CRED_LABEL: mpc->mpc_ops->mpo_internalize_cred_label = mpe->mpe_function; break; case MAC_INTERNALIZE_IFNET_LABEL: mpc->mpc_ops->mpo_internalize_ifnet_label = mpe->mpe_function; break; case MAC_INTERNALIZE_PIPE_LABEL: mpc->mpc_ops->mpo_internalize_pipe_label = mpe->mpe_function; break; case MAC_INTERNALIZE_SOCKET_LABEL: mpc->mpc_ops->mpo_internalize_socket_label = mpe->mpe_function; break; case MAC_INTERNALIZE_VNODE_LABEL: mpc->mpc_ops->mpo_internalize_vnode_label = mpe->mpe_function; break; case MAC_CREATE_DEVFS_DEVICE: mpc->mpc_ops->mpo_create_devfs_device = mpe->mpe_function; break; case MAC_CREATE_DEVFS_DIRECTORY: mpc->mpc_ops->mpo_create_devfs_directory = mpe->mpe_function; break; case MAC_CREATE_DEVFS_SYMLINK: mpc->mpc_ops->mpo_create_devfs_symlink = mpe->mpe_function; break; case MAC_CREATE_DEVFS_VNODE: mpc->mpc_ops->mpo_create_devfs_vnode = mpe->mpe_function; break; case MAC_CREATE_MOUNT: mpc->mpc_ops->mpo_create_mount = mpe->mpe_function; break; case MAC_CREATE_ROOT_MOUNT: mpc->mpc_ops->mpo_create_root_mount = mpe->mpe_function; break; case MAC_RELABEL_VNODE: mpc->mpc_ops->mpo_relabel_vnode = mpe->mpe_function; break; case MAC_UPDATE_DEVFSDIRENT: mpc->mpc_ops->mpo_update_devfsdirent = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_DEVFS: mpc->mpc_ops->mpo_associate_vnode_devfs = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_EXTATTR: mpc->mpc_ops->mpo_associate_vnode_extattr = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_SINGLELABEL: mpc->mpc_ops->mpo_associate_vnode_singlelabel = mpe->mpe_function; break; case MAC_CREATE_VNODE_EXTATTR: mpc->mpc_ops->mpo_create_vnode_extattr = mpe->mpe_function; break; case MAC_SETLABEL_VNODE_EXTATTR: mpc->mpc_ops->mpo_setlabel_vnode_extattr = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_SOCKET: mpc->mpc_ops->mpo_create_mbuf_from_socket = mpe->mpe_function; break; case MAC_CREATE_PIPE: mpc->mpc_ops->mpo_create_pipe = mpe->mpe_function; break; case MAC_CREATE_SOCKET: mpc->mpc_ops->mpo_create_socket = mpe->mpe_function; break; case MAC_CREATE_SOCKET_FROM_SOCKET: mpc->mpc_ops->mpo_create_socket_from_socket = mpe->mpe_function; break; case MAC_RELABEL_PIPE: mpc->mpc_ops->mpo_relabel_pipe = mpe->mpe_function; break; case MAC_RELABEL_SOCKET: mpc->mpc_ops->mpo_relabel_socket = mpe->mpe_function; break; case MAC_SET_SOCKET_PEER_FROM_MBUF: mpc->mpc_ops->mpo_set_socket_peer_from_mbuf = mpe->mpe_function; break; case MAC_SET_SOCKET_PEER_FROM_SOCKET: mpc->mpc_ops->mpo_set_socket_peer_from_socket = mpe->mpe_function; break; case MAC_CREATE_BPFDESC: mpc->mpc_ops->mpo_create_bpfdesc = mpe->mpe_function; break; case MAC_CREATE_DATAGRAM_FROM_IPQ: mpc->mpc_ops->mpo_create_datagram_from_ipq = mpe->mpe_function; break; case MAC_CREATE_FRAGMENT: mpc->mpc_ops->mpo_create_fragment = mpe->mpe_function; break; case MAC_CREATE_IFNET: mpc->mpc_ops->mpo_create_ifnet = mpe->mpe_function; break; case MAC_CREATE_IPQ: mpc->mpc_ops->mpo_create_ipq = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_MBUF: mpc->mpc_ops->mpo_create_mbuf_from_mbuf = mpe->mpe_function; break; case MAC_CREATE_MBUF_LINKLAYER: mpc->mpc_ops->mpo_create_mbuf_linklayer = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_BPFDESC: mpc->mpc_ops->mpo_create_mbuf_from_bpfdesc = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_IFNET: mpc->mpc_ops->mpo_create_mbuf_from_ifnet = mpe->mpe_function; break; case MAC_CREATE_MBUF_MULTICAST_ENCAP: mpc->mpc_ops->mpo_create_mbuf_multicast_encap = mpe->mpe_function; break; case MAC_CREATE_MBUF_NETLAYER: mpc->mpc_ops->mpo_create_mbuf_netlayer = mpe->mpe_function; break; case MAC_FRAGMENT_MATCH: mpc->mpc_ops->mpo_fragment_match = mpe->mpe_function; break; case MAC_RELABEL_IFNET: mpc->mpc_ops->mpo_relabel_ifnet = mpe->mpe_function; break; case MAC_UPDATE_IPQ: mpc->mpc_ops->mpo_update_ipq = mpe->mpe_function; break; case MAC_CREATE_CRED: mpc->mpc_ops->mpo_create_cred = mpe->mpe_function; break; case MAC_EXECVE_TRANSITION: mpc->mpc_ops->mpo_execve_transition = mpe->mpe_function; break; case MAC_EXECVE_WILL_TRANSITION: mpc->mpc_ops->mpo_execve_will_transition = mpe->mpe_function; break; case MAC_CREATE_PROC0: mpc->mpc_ops->mpo_create_proc0 = mpe->mpe_function; break; case MAC_CREATE_PROC1: mpc->mpc_ops->mpo_create_proc1 = mpe->mpe_function; break; case MAC_RELABEL_CRED: mpc->mpc_ops->mpo_relabel_cred = mpe->mpe_function; break; case MAC_THREAD_USERRET: mpc->mpc_ops->mpo_thread_userret = mpe->mpe_function; break; case MAC_CHECK_BPFDESC_RECEIVE: mpc->mpc_ops->mpo_check_bpfdesc_receive = mpe->mpe_function; break; case MAC_CHECK_CRED_RELABEL: mpc->mpc_ops->mpo_check_cred_relabel = mpe->mpe_function; break; case MAC_CHECK_CRED_VISIBLE: mpc->mpc_ops->mpo_check_cred_visible = mpe->mpe_function; break; case MAC_CHECK_IFNET_RELABEL: mpc->mpc_ops->mpo_check_ifnet_relabel = mpe->mpe_function; break; case MAC_CHECK_IFNET_TRANSMIT: mpc->mpc_ops->mpo_check_ifnet_transmit = mpe->mpe_function; break; case MAC_CHECK_MOUNT_STAT: mpc->mpc_ops->mpo_check_mount_stat = mpe->mpe_function; break; case MAC_CHECK_PIPE_IOCTL: mpc->mpc_ops->mpo_check_pipe_ioctl = mpe->mpe_function; break; case MAC_CHECK_PIPE_POLL: mpc->mpc_ops->mpo_check_pipe_poll = mpe->mpe_function; break; case MAC_CHECK_PIPE_READ: mpc->mpc_ops->mpo_check_pipe_read = mpe->mpe_function; break; case MAC_CHECK_PIPE_RELABEL: mpc->mpc_ops->mpo_check_pipe_relabel = mpe->mpe_function; break; case MAC_CHECK_PIPE_STAT: mpc->mpc_ops->mpo_check_pipe_stat = mpe->mpe_function; break; case MAC_CHECK_PIPE_WRITE: mpc->mpc_ops->mpo_check_pipe_write = mpe->mpe_function; break; case MAC_CHECK_PROC_DEBUG: mpc->mpc_ops->mpo_check_proc_debug = mpe->mpe_function; break; case MAC_CHECK_PROC_SCHED: mpc->mpc_ops->mpo_check_proc_sched = mpe->mpe_function; break; case MAC_CHECK_PROC_SIGNAL: mpc->mpc_ops->mpo_check_proc_signal = mpe->mpe_function; break; case MAC_CHECK_SOCKET_BIND: mpc->mpc_ops->mpo_check_socket_bind = mpe->mpe_function; break; case MAC_CHECK_SOCKET_CONNECT: mpc->mpc_ops->mpo_check_socket_connect = mpe->mpe_function; break; case MAC_CHECK_SOCKET_DELIVER: mpc->mpc_ops->mpo_check_socket_deliver = mpe->mpe_function; break; case MAC_CHECK_SOCKET_LISTEN: mpc->mpc_ops->mpo_check_socket_listen = mpe->mpe_function; break; case MAC_CHECK_SOCKET_RECEIVE: mpc->mpc_ops->mpo_check_socket_receive = mpe->mpe_function; break; case MAC_CHECK_SOCKET_RELABEL: mpc->mpc_ops->mpo_check_socket_relabel = mpe->mpe_function; break; case MAC_CHECK_SOCKET_SEND: mpc->mpc_ops->mpo_check_socket_send = mpe->mpe_function; break; case MAC_CHECK_SOCKET_VISIBLE: mpc->mpc_ops->mpo_check_socket_visible = mpe->mpe_function; break; case MAC_CHECK_SYSTEM_REBOOT: mpc->mpc_ops->mpo_check_system_reboot = mpe->mpe_function; break; case MAC_CHECK_SYSTEM_SWAPON: mpc->mpc_ops->mpo_check_system_swapon = mpe->mpe_function; break; + case MAC_CHECK_SYSTEM_SYSCTL: + mpc->mpc_ops->mpo_check_system_sysctl = + mpe->mpe_function; + break; case MAC_CHECK_VNODE_ACCESS: mpc->mpc_ops->mpo_check_vnode_access = mpe->mpe_function; break; case MAC_CHECK_VNODE_CHDIR: mpc->mpc_ops->mpo_check_vnode_chdir = mpe->mpe_function; break; case MAC_CHECK_VNODE_CHROOT: mpc->mpc_ops->mpo_check_vnode_chroot = mpe->mpe_function; break; case MAC_CHECK_VNODE_CREATE: mpc->mpc_ops->mpo_check_vnode_create = mpe->mpe_function; break; case MAC_CHECK_VNODE_DELETE: mpc->mpc_ops->mpo_check_vnode_delete = mpe->mpe_function; break; case MAC_CHECK_VNODE_DELETEACL: mpc->mpc_ops->mpo_check_vnode_deleteacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_EXEC: mpc->mpc_ops->mpo_check_vnode_exec = mpe->mpe_function; break; case MAC_CHECK_VNODE_GETACL: mpc->mpc_ops->mpo_check_vnode_getacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_GETEXTATTR: mpc->mpc_ops->mpo_check_vnode_getextattr = mpe->mpe_function; break; case MAC_CHECK_VNODE_LINK: mpc->mpc_ops->mpo_check_vnode_link = mpe->mpe_function; break; case MAC_CHECK_VNODE_LOOKUP: mpc->mpc_ops->mpo_check_vnode_lookup = mpe->mpe_function; break; case MAC_CHECK_VNODE_MMAP: mpc->mpc_ops->mpo_check_vnode_mmap = mpe->mpe_function; break; case MAC_CHECK_VNODE_MMAP_DOWNGRADE: mpc->mpc_ops->mpo_check_vnode_mmap_downgrade = mpe->mpe_function; break; case MAC_CHECK_VNODE_MPROTECT: mpc->mpc_ops->mpo_check_vnode_mprotect = mpe->mpe_function; break; case MAC_CHECK_VNODE_OPEN: mpc->mpc_ops->mpo_check_vnode_open = mpe->mpe_function; break; case MAC_CHECK_VNODE_POLL: mpc->mpc_ops->mpo_check_vnode_poll = mpe->mpe_function; break; case MAC_CHECK_VNODE_READ: mpc->mpc_ops->mpo_check_vnode_read = mpe->mpe_function; break; case MAC_CHECK_VNODE_READDIR: mpc->mpc_ops->mpo_check_vnode_readdir = mpe->mpe_function; break; case MAC_CHECK_VNODE_READLINK: mpc->mpc_ops->mpo_check_vnode_readlink = mpe->mpe_function; break; case MAC_CHECK_VNODE_RELABEL: mpc->mpc_ops->mpo_check_vnode_relabel = mpe->mpe_function; break; case MAC_CHECK_VNODE_RENAME_FROM: mpc->mpc_ops->mpo_check_vnode_rename_from = mpe->mpe_function; break; case MAC_CHECK_VNODE_RENAME_TO: mpc->mpc_ops->mpo_check_vnode_rename_to = mpe->mpe_function; break; case MAC_CHECK_VNODE_REVOKE: mpc->mpc_ops->mpo_check_vnode_revoke = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETACL: mpc->mpc_ops->mpo_check_vnode_setacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETEXTATTR: mpc->mpc_ops->mpo_check_vnode_setextattr = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETFLAGS: mpc->mpc_ops->mpo_check_vnode_setflags = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETMODE: mpc->mpc_ops->mpo_check_vnode_setmode = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETOWNER: mpc->mpc_ops->mpo_check_vnode_setowner = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETUTIMES: mpc->mpc_ops->mpo_check_vnode_setutimes = mpe->mpe_function; break; case MAC_CHECK_VNODE_STAT: mpc->mpc_ops->mpo_check_vnode_stat = mpe->mpe_function; break; case MAC_CHECK_VNODE_WRITE: mpc->mpc_ops->mpo_check_vnode_write = mpe->mpe_function; break; /* default: printf("MAC policy `%s': unknown operation %d\n", mpc->mpc_name, mpe->mpe_constant); return (EINVAL); */ } } MAC_POLICY_LIST_LOCK(); if (mac_policy_list_busy > 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (EBUSY); } LIST_FOREACH(tmpc, &mac_policy_list, mpc_list) { if (strcmp(tmpc->mpc_name, mpc->mpc_name) == 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (EEXIST); } } if (mpc->mpc_field_off != NULL) { slot = ffs(mac_policy_offsets_free); if (slot == 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (ENOMEM); } slot--; mac_policy_offsets_free &= ~(1 << slot); *mpc->mpc_field_off = slot; } mpc->mpc_runtime_flags |= MPC_RUNTIME_FLAG_REGISTERED; LIST_INSERT_HEAD(&mac_policy_list, mpc, mpc_list); /* Per-policy initialization. */ if (mpc->mpc_ops->mpo_init != NULL) (*(mpc->mpc_ops->mpo_init))(mpc); MAC_POLICY_LIST_UNLOCK(); printf("Security policy loaded: %s (%s)\n", mpc->mpc_fullname, mpc->mpc_name); return (0); } 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_LIST_LOCK(); if ((mpc->mpc_runtime_flags & MPC_RUNTIME_FLAG_REGISTERED) == 0) { MAC_POLICY_LIST_UNLOCK(); return (0); } #if 0 /* * Don't allow unloading modules with private data. */ if (mpc->mpc_field_off != NULL) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } #endif /* * Only allow the unload to proceed if the module is unloadable * by its own definition. */ if ((mpc->mpc_loadtime_flags & MPC_LOADTIME_FLAG_UNLOADOK) == 0) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } /* * Right now, we EBUSY if the list is in use. In the future, * for reliability reasons, we might want to sleep and wakeup * later to try again. */ if (mac_policy_list_busy > 0) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } if (mpc->mpc_ops->mpo_destroy != NULL) (*(mpc->mpc_ops->mpo_destroy))(mpc); LIST_REMOVE(mpc, mpc_list); MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; mpc->mpc_runtime_flags &= ~MPC_RUNTIME_FLAG_REGISTERED; printf("Security policy unload: %s (%s)\n", mpc->mpc_fullname, mpc->mpc_name); return (0); } /* * Define an error value precedence, and given two arguments, selects the * value with the higher precedence. */ static int 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); } static void mac_init_label(struct label *label) { bzero(label, sizeof(*label)); label->l_flags = MAC_FLAG_INITIALIZED; } static void mac_destroy_label(struct label *label) { KASSERT(label->l_flags & MAC_FLAG_INITIALIZED, ("destroying uninitialized label")); bzero(label, sizeof(*label)); /* implicit: label->l_flags &= ~MAC_FLAG_INITIALIZED; */ } void mac_init_bpfdesc(struct bpf_d *bpf_d) { mac_init_label(&bpf_d->bd_label); MAC_PERFORM(init_bpfdesc_label, &bpf_d->bd_label); #ifdef MAC_DEBUG atomic_add_int(&nmacbpfdescs, 1); #endif } static void mac_init_cred_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_cred_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmaccreds, 1); #endif } void mac_init_cred(struct ucred *cred) { mac_init_cred_label(&cred->cr_label); } void mac_init_devfsdirent(struct devfs_dirent *de) { mac_init_label(&de->de_label); MAC_PERFORM(init_devfsdirent_label, &de->de_label); #ifdef MAC_DEBUG atomic_add_int(&nmacdevfsdirents, 1); #endif } static void mac_init_ifnet_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_ifnet_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacifnets, 1); #endif } void mac_init_ifnet(struct ifnet *ifp) { mac_init_ifnet_label(&ifp->if_label); } void mac_init_ipq(struct ipq *ipq) { mac_init_label(&ipq->ipq_label); MAC_PERFORM(init_ipq_label, &ipq->ipq_label); #ifdef MAC_DEBUG atomic_add_int(&nmacipqs, 1); #endif } int mac_init_mbuf(struct mbuf *m, int flag) { int error; KASSERT(m->m_flags & M_PKTHDR, ("mac_init_mbuf on non-header mbuf")); mac_init_label(&m->m_pkthdr.label); MAC_CHECK(init_mbuf_label, &m->m_pkthdr.label, flag); if (error) { MAC_PERFORM(destroy_mbuf_label, &m->m_pkthdr.label); mac_destroy_label(&m->m_pkthdr.label); } #ifdef MAC_DEBUG if (error == 0) atomic_add_int(&nmacmbufs, 1); #endif return (error); } void mac_init_mount(struct mount *mp) { mac_init_label(&mp->mnt_mntlabel); mac_init_label(&mp->mnt_fslabel); MAC_PERFORM(init_mount_label, &mp->mnt_mntlabel); MAC_PERFORM(init_mount_fs_label, &mp->mnt_fslabel); #ifdef MAC_DEBUG atomic_add_int(&nmacmounts, 1); #endif } static void mac_init_pipe_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_pipe_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacpipes, 1); #endif } void mac_init_pipe(struct pipe *pipe) { struct label *label; label = malloc(sizeof(struct label), M_MACPIPELABEL, M_ZERO|M_WAITOK); pipe->pipe_label = label; pipe->pipe_peer->pipe_label = label; mac_init_pipe_label(label); } static int mac_init_socket_label(struct label *label, int flag) { int error; mac_init_label(label); MAC_CHECK(init_socket_label, label, flag); if (error) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); } #ifdef MAC_DEBUG if (error == 0) atomic_add_int(&nmacsockets, 1); #endif return (error); } static int mac_init_socket_peer_label(struct label *label, int flag) { int error; mac_init_label(label); MAC_CHECK(init_socket_peer_label, label, flag); if (error) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); } return (error); } int mac_init_socket(struct socket *socket, int flag) { int error; error = mac_init_socket_label(&socket->so_label, flag); if (error) return (error); error = mac_init_socket_peer_label(&socket->so_peerlabel, flag); if (error) mac_destroy_socket_label(&socket->so_label); return (error); } void mac_init_vnode_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_vnode_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacvnodes, 1); #endif } void mac_init_vnode(struct vnode *vp) { mac_init_vnode_label(&vp->v_label); } void mac_destroy_bpfdesc(struct bpf_d *bpf_d) { MAC_PERFORM(destroy_bpfdesc_label, &bpf_d->bd_label); mac_destroy_label(&bpf_d->bd_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacbpfdescs, 1); #endif } static void mac_destroy_cred_label(struct label *label) { MAC_PERFORM(destroy_cred_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmaccreds, 1); #endif } void mac_destroy_cred(struct ucred *cred) { mac_destroy_cred_label(&cred->cr_label); } void mac_destroy_devfsdirent(struct devfs_dirent *de) { MAC_PERFORM(destroy_devfsdirent_label, &de->de_label); mac_destroy_label(&de->de_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacdevfsdirents, 1); #endif } static void mac_destroy_ifnet_label(struct label *label) { MAC_PERFORM(destroy_ifnet_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacifnets, 1); #endif } void mac_destroy_ifnet(struct ifnet *ifp) { mac_destroy_ifnet_label(&ifp->if_label); } void mac_destroy_ipq(struct ipq *ipq) { MAC_PERFORM(destroy_ipq_label, &ipq->ipq_label); mac_destroy_label(&ipq->ipq_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacipqs, 1); #endif } void mac_destroy_mbuf(struct mbuf *m) { MAC_PERFORM(destroy_mbuf_label, &m->m_pkthdr.label); mac_destroy_label(&m->m_pkthdr.label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacmbufs, 1); #endif } void mac_destroy_mount(struct mount *mp) { MAC_PERFORM(destroy_mount_label, &mp->mnt_mntlabel); MAC_PERFORM(destroy_mount_fs_label, &mp->mnt_fslabel); mac_destroy_label(&mp->mnt_fslabel); mac_destroy_label(&mp->mnt_mntlabel); #ifdef MAC_DEBUG atomic_subtract_int(&nmacmounts, 1); #endif } static void mac_destroy_pipe_label(struct label *label) { MAC_PERFORM(destroy_pipe_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacpipes, 1); #endif } void mac_destroy_pipe(struct pipe *pipe) { mac_destroy_pipe_label(pipe->pipe_label); free(pipe->pipe_label, M_MACPIPELABEL); } static void mac_destroy_socket_label(struct label *label) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacsockets, 1); #endif } static void mac_destroy_socket_peer_label(struct label *label) { MAC_PERFORM(destroy_socket_peer_label, label); mac_destroy_label(label); } void mac_destroy_socket(struct socket *socket) { mac_destroy_socket_label(&socket->so_label); mac_destroy_socket_peer_label(&socket->so_peerlabel); } void mac_destroy_vnode_label(struct label *label) { MAC_PERFORM(destroy_vnode_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacvnodes, 1); #endif } void mac_destroy_vnode(struct vnode *vp) { mac_destroy_vnode_label(&vp->v_label); } static void mac_copy_pipe_label(struct label *src, struct label *dest) { MAC_PERFORM(copy_pipe_label, src, dest); } void mac_copy_vnode_label(struct label *src, struct label *dest) { MAC_PERFORM(copy_vnode_label, src, dest); } static int mac_check_structmac_consistent(struct mac *mac) { if (mac->m_buflen > MAC_MAX_LABEL_BUF_LEN) return (EINVAL); return (0); } static int mac_externalize_cred_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(cred_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_ifnet_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(ifnet_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_pipe_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(pipe_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_socket_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(socket_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_socket_peer_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(socket_peer_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_vnode_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(vnode_label, label, elements, outbuf, outbuflen); return (error); } static int mac_internalize_cred_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(cred_label, label, string); return (error); } static int mac_internalize_ifnet_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(ifnet_label, label, string); return (error); } static int mac_internalize_pipe_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(pipe_label, label, string); return (error); } static int mac_internalize_socket_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(socket_label, label, string); return (error); } static int mac_internalize_vnode_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(vnode_label, label, string); return (error); } /* * Initialize MAC label for the first kernel process, from which other * kernel processes and threads are spawned. */ void mac_create_proc0(struct ucred *cred) { MAC_PERFORM(create_proc0, cred); } /* * Initialize MAC label for the first userland process, from which other * userland processes and threads are spawned. */ void mac_create_proc1(struct ucred *cred) { MAC_PERFORM(create_proc1, cred); } void mac_thread_userret(struct thread *td) { MAC_PERFORM(thread_userret, td); } /* * 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_create_cred(struct ucred *parent_cred, struct ucred *child_cred) { MAC_PERFORM(create_cred, parent_cred, child_cred); } void mac_update_devfsdirent(struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(update_devfsdirent, de, &de->de_label, vp, &vp->v_label); } void mac_associate_vnode_devfs(struct mount *mp, struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(associate_vnode_devfs, mp, &mp->mnt_fslabel, de, &de->de_label, vp, &vp->v_label); } int mac_associate_vnode_extattr(struct mount *mp, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_associate_vnode_extattr"); MAC_CHECK(associate_vnode_extattr, mp, &mp->mnt_fslabel, vp, &vp->v_label); return (error); } void mac_associate_vnode_singlelabel(struct mount *mp, struct vnode *vp) { MAC_PERFORM(associate_vnode_singlelabel, mp, &mp->mnt_fslabel, vp, &vp->v_label); } int mac_create_vnode_extattr(struct ucred *cred, struct mount *mp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_create_vnode_extattr"); ASSERT_VOP_LOCKED(vp, "mac_create_vnode_extattr"); error = VOP_OPENEXTATTR(vp, cred, curthread); if (error == EOPNOTSUPP) { /* XXX: Optionally abort if transactions not supported. */ if (ea_warn_once == 0) { printf("Warning: transactions not supported " "in EA write.\n"); ea_warn_once = 1; } } else if (error) return (error); MAC_CHECK(create_vnode_extattr, cred, mp, &mp->mnt_fslabel, 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; /* XXX */ return (error); } static int mac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *intlabel) { int error; ASSERT_VOP_LOCKED(vp, "mac_setlabel_vnode_extattr"); error = VOP_OPENEXTATTR(vp, cred, curthread); if (error == EOPNOTSUPP) { /* XXX: Optionally abort if transactions not supported. */ if (ea_warn_once == 0) { printf("Warning: transactions not supported " "in EA write.\n"); ea_warn_once = 1; } } else if (error) return (error); MAC_CHECK(setlabel_vnode_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; /* XXX */ return (error); } void mac_execve_transition(struct ucred *old, struct ucred *new, struct vnode *vp) { ASSERT_VOP_LOCKED(vp, "mac_execve_transition"); MAC_PERFORM(execve_transition, old, new, vp, &vp->v_label); } int mac_execve_will_transition(struct ucred *old, struct vnode *vp) { int result; result = 0; MAC_BOOLEAN(execve_will_transition, ||, old, vp, &vp->v_label); return (result); } int mac_check_vnode_access(struct ucred *cred, struct vnode *vp, int flags) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_access"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_access, cred, vp, &vp->v_label, flags); return (error); } int mac_check_vnode_chdir(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_chdir"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_chdir, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_chroot(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_chroot"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_chroot, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_create(struct ucred *cred, struct vnode *dvp, struct componentname *cnp, struct vattr *vap) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_create"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_create, cred, dvp, &dvp->v_label, cnp, vap); return (error); } int mac_check_vnode_delete(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_delete"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_delete"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_delete, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp, acl_type_t type) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_deleteacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_deleteacl, cred, vp, &vp->v_label, type); return (error); } int mac_check_vnode_exec(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_exec"); if (!mac_enforce_process && !mac_enforce_fs) return (0); MAC_CHECK(check_vnode_exec, cred, vp, &vp->v_label); return (error); } int mac_check_vnode_getacl(struct ucred *cred, struct vnode *vp, acl_type_t type) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_getacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_getacl, cred, vp, &vp->v_label, type); return (error); } int mac_check_vnode_getextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_getextattr"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_getextattr, cred, vp, &vp->v_label, attrnamespace, name, uio); return (error); } int mac_check_vnode_link(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_link"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_link"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_link, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_lookup(struct ucred *cred, struct vnode *dvp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_lookup"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_lookup, cred, dvp, &dvp->v_label, cnp); return (error); } int mac_check_vnode_mmap(struct ucred *cred, struct vnode *vp, int prot) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mmap"); if (!mac_enforce_fs || !mac_enforce_vm) return (0); MAC_CHECK(check_vnode_mmap, cred, vp, &vp->v_label, prot); return (error); } void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot) { int result = *prot; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mmap_downgrade"); if (!mac_enforce_fs || !mac_enforce_vm) return; MAC_PERFORM(check_vnode_mmap_downgrade, cred, vp, &vp->v_label, &result); *prot = result; } int mac_check_vnode_mprotect(struct ucred *cred, struct vnode *vp, int prot) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mprotect"); if (!mac_enforce_fs || !mac_enforce_vm) return (0); MAC_CHECK(check_vnode_mprotect, cred, vp, &vp->v_label, prot); return (error); } int mac_check_vnode_open(struct ucred *cred, struct vnode *vp, mode_t acc_mode) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_open"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_open, cred, vp, &vp->v_label, acc_mode); return (error); } int mac_check_vnode_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_poll"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_poll, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_read"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_read, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_readdir(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_readdir"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_readdir, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_readlink(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_readlink"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_readlink, cred, vp, &vp->v_label); return (error); } static int mac_check_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *newlabel) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_relabel"); MAC_CHECK(check_vnode_relabel, cred, vp, &vp->v_label, newlabel); return (error); } int mac_check_vnode_rename_from(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_rename_from"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_rename_from"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_rename_from, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp, struct vnode *vp, int samedir, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_rename_to"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_rename_to"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_rename_to, cred, dvp, &dvp->v_label, vp, vp != NULL ? &vp->v_label : NULL, samedir, cnp); return (error); } int mac_check_vnode_revoke(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_revoke"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_revoke, cred, vp, &vp->v_label); return (error); } int mac_check_vnode_setacl(struct ucred *cred, struct vnode *vp, acl_type_t type, struct acl *acl) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setacl, cred, vp, &vp->v_label, type, acl); return (error); } int mac_check_vnode_setextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setextattr"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setextattr, cred, vp, &vp->v_label, attrnamespace, name, uio); return (error); } int mac_check_vnode_setflags(struct ucred *cred, struct vnode *vp, u_long flags) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setflags"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setflags, cred, vp, &vp->v_label, flags); return (error); } int mac_check_vnode_setmode(struct ucred *cred, struct vnode *vp, mode_t mode) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setmode"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setmode, cred, vp, &vp->v_label, mode); return (error); } int mac_check_vnode_setowner(struct ucred *cred, struct vnode *vp, uid_t uid, gid_t gid) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setowner"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setowner, cred, vp, &vp->v_label, uid, gid); return (error); } int mac_check_vnode_setutimes(struct ucred *cred, struct vnode *vp, struct timespec atime, struct timespec mtime) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setutimes"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setutimes, cred, vp, &vp->v_label, atime, mtime); return (error); } int mac_check_vnode_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_stat"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_stat, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_write"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_write, active_cred, file_cred, vp, &vp->v_label); return (error); } /* * 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. */ static void mac_cred_mmapped_drop_perms(struct thread *td, struct ucred *cred) { /* XXX freeze all other threads */ mac_cred_mmapped_drop_perms_recurse(td, cred, &td->td_proc->p_vmspace->vm_map); /* XXX allow other threads to continue */ } 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_cred_mmapped_drop_perms_recurse(struct thread *td, struct ucred *cred, struct vm_map *map) { struct vm_map_entry *vme; int result; vm_prot_t revokeperms; vm_object_t object; vm_ooffset_t offset; struct vnode *vp; if (!mac_mmap_revocation) return; vm_map_lock_read(map); for (vme = map->header.next; vme != &map->header; vme = vme->next) { if (vme->eflags & MAP_ENTRY_IS_SUB_MAP) { mac_cred_mmapped_drop_perms_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; while (object->backing_object != NULL) { object = object->backing_object; offset += object->backing_object_offset; } /* * 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, td); result = vme->max_protection; mac_check_vnode_mmap_downgrade(cred, vp, &result); VOP_UNLOCK(vp, 0, td); /* * 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)); vm_map_lock_upgrade(map); /* * 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); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); vm_object_page_clean(object, OFF_TO_IDX(offset), OFF_TO_IDX(offset + vme->end - vme->start + PAGE_MASK), OBJPC_SYNC); VOP_UNLOCK(vp, 0, td); 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_lock_downgrade(map); } vm_map_unlock_read(map); } /* * 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. */ static void mac_relabel_cred(struct ucred *cred, struct label *newlabel) { MAC_PERFORM(relabel_cred, cred, newlabel); } void mac_relabel_vnode(struct ucred *cred, struct vnode *vp, struct label *newlabel) { MAC_PERFORM(relabel_vnode, cred, vp, &vp->v_label, newlabel); } void mac_create_ifnet(struct ifnet *ifnet) { MAC_PERFORM(create_ifnet, ifnet, &ifnet->if_label); } void mac_create_bpfdesc(struct ucred *cred, struct bpf_d *bpf_d) { MAC_PERFORM(create_bpfdesc, cred, bpf_d, &bpf_d->bd_label); } void mac_create_socket(struct ucred *cred, struct socket *socket) { MAC_PERFORM(create_socket, cred, socket, &socket->so_label); } void mac_create_pipe(struct ucred *cred, struct pipe *pipe) { MAC_PERFORM(create_pipe, cred, pipe, pipe->pipe_label); } void mac_create_socket_from_socket(struct socket *oldsocket, struct socket *newsocket) { MAC_PERFORM(create_socket_from_socket, oldsocket, &oldsocket->so_label, newsocket, &newsocket->so_label); } static void mac_relabel_socket(struct ucred *cred, struct socket *socket, struct label *newlabel) { MAC_PERFORM(relabel_socket, cred, socket, &socket->so_label, newlabel); } static void mac_relabel_pipe(struct ucred *cred, struct pipe *pipe, struct label *newlabel) { MAC_PERFORM(relabel_pipe, cred, pipe, pipe->pipe_label, newlabel); } void mac_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct socket *socket) { MAC_PERFORM(set_socket_peer_from_mbuf, mbuf, &mbuf->m_pkthdr.label, socket, &socket->so_peerlabel); } void mac_set_socket_peer_from_socket(struct socket *oldsocket, struct socket *newsocket) { MAC_PERFORM(set_socket_peer_from_socket, oldsocket, &oldsocket->so_label, newsocket, &newsocket->so_peerlabel); } void mac_create_datagram_from_ipq(struct ipq *ipq, struct mbuf *datagram) { MAC_PERFORM(create_datagram_from_ipq, ipq, &ipq->ipq_label, datagram, &datagram->m_pkthdr.label); } void mac_create_fragment(struct mbuf *datagram, struct mbuf *fragment) { MAC_PERFORM(create_fragment, datagram, &datagram->m_pkthdr.label, fragment, &fragment->m_pkthdr.label); } void mac_create_ipq(struct mbuf *fragment, struct ipq *ipq) { MAC_PERFORM(create_ipq, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); } void mac_create_mbuf_from_mbuf(struct mbuf *oldmbuf, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_from_mbuf, oldmbuf, &oldmbuf->m_pkthdr.label, newmbuf, &newmbuf->m_pkthdr.label); } void mac_create_mbuf_from_bpfdesc(struct bpf_d *bpf_d, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_bpfdesc, bpf_d, &bpf_d->bd_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_linklayer(struct ifnet *ifnet, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_linklayer, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_from_ifnet(struct ifnet *ifnet, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_ifnet, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_multicast_encap(struct mbuf *oldmbuf, struct ifnet *ifnet, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_multicast_encap, oldmbuf, &oldmbuf->m_pkthdr.label, ifnet, &ifnet->if_label, newmbuf, &newmbuf->m_pkthdr.label); } void mac_create_mbuf_netlayer(struct mbuf *oldmbuf, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_netlayer, oldmbuf, &oldmbuf->m_pkthdr.label, newmbuf, &newmbuf->m_pkthdr.label); } int mac_fragment_match(struct mbuf *fragment, struct ipq *ipq) { int result; result = 1; MAC_BOOLEAN(fragment_match, &&, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); return (result); } void mac_update_ipq(struct mbuf *fragment, struct ipq *ipq) { MAC_PERFORM(update_ipq, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); } void mac_create_mbuf_from_socket(struct socket *socket, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_socket, socket, &socket->so_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mount(struct ucred *cred, struct mount *mp) { MAC_PERFORM(create_mount, cred, mp, &mp->mnt_mntlabel, &mp->mnt_fslabel); } void mac_create_root_mount(struct ucred *cred, struct mount *mp) { MAC_PERFORM(create_root_mount, cred, mp, &mp->mnt_mntlabel, &mp->mnt_fslabel); } int mac_check_bpfdesc_receive(struct bpf_d *bpf_d, struct ifnet *ifnet) { int error; if (!mac_enforce_network) return (0); MAC_CHECK(check_bpfdesc_receive, bpf_d, &bpf_d->bd_label, ifnet, &ifnet->if_label); return (error); } static int mac_check_cred_relabel(struct ucred *cred, struct label *newlabel) { int error; MAC_CHECK(check_cred_relabel, cred, newlabel); return (error); } int mac_check_cred_visible(struct ucred *u1, struct ucred *u2) { int error; if (!mac_enforce_process) return (0); MAC_CHECK(check_cred_visible, u1, u2); return (error); } int mac_check_ifnet_transmit(struct ifnet *ifnet, struct mbuf *mbuf) { int error; if (!mac_enforce_network) return (0); KASSERT(mbuf->m_flags & M_PKTHDR, ("packet has no pkthdr")); if (!(mbuf->m_pkthdr.label.l_flags & MAC_FLAG_INITIALIZED)) if_printf(ifnet, "not initialized\n"); MAC_CHECK(check_ifnet_transmit, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); return (error); } int mac_check_mount_stat(struct ucred *cred, struct mount *mount) { int error; if (!mac_enforce_fs) return (0); MAC_CHECK(check_mount_stat, cred, mount, &mount->mnt_mntlabel); return (error); } int mac_check_pipe_ioctl(struct ucred *cred, struct pipe *pipe, unsigned long cmd, void *data) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_ioctl, cred, pipe, pipe->pipe_label, cmd, data); return (error); } int mac_check_pipe_poll(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_poll, cred, pipe, pipe->pipe_label); return (error); } int mac_check_pipe_read(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_read, cred, pipe, pipe->pipe_label); return (error); } static int mac_check_pipe_relabel(struct ucred *cred, struct pipe *pipe, struct label *newlabel) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_relabel, cred, pipe, pipe->pipe_label, newlabel); return (error); } int mac_check_pipe_stat(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_stat, cred, pipe, pipe->pipe_label); return (error); } int mac_check_pipe_write(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_write, cred, pipe, pipe->pipe_label); return (error); } int mac_check_proc_debug(struct ucred *cred, struct proc *proc) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_debug, cred, proc); return (error); } int mac_check_proc_sched(struct ucred *cred, struct proc *proc) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_sched, cred, proc); return (error); } int mac_check_proc_signal(struct ucred *cred, struct proc *proc, int signum) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_signal, cred, proc, signum); return (error); } int mac_check_socket_bind(struct ucred *ucred, struct socket *socket, struct sockaddr *sockaddr) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_bind, ucred, socket, &socket->so_label, sockaddr); return (error); } int mac_check_socket_connect(struct ucred *cred, struct socket *socket, struct sockaddr *sockaddr) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_connect, cred, socket, &socket->so_label, sockaddr); return (error); } int mac_check_socket_deliver(struct socket *socket, struct mbuf *mbuf) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_deliver, socket, &socket->so_label, mbuf, &mbuf->m_pkthdr.label); return (error); } int mac_check_socket_listen(struct ucred *cred, struct socket *socket) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_listen, cred, socket, &socket->so_label); return (error); } int mac_check_socket_receive(struct ucred *cred, struct socket *so) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_receive, cred, so, &so->so_label); return (error); } static int mac_check_socket_relabel(struct ucred *cred, struct socket *socket, struct label *newlabel) { int error; MAC_CHECK(check_socket_relabel, cred, socket, &socket->so_label, newlabel); return (error); } int mac_check_socket_send(struct ucred *cred, struct socket *so) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_send, cred, so, &so->so_label); return (error); } int mac_check_socket_visible(struct ucred *cred, struct socket *socket) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_visible, cred, socket, &socket->so_label); return (error); } int mac_check_system_reboot(struct ucred *cred, int howto) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_system_reboot"); if (!mac_enforce_reboot) return (0); MAC_CHECK(check_system_reboot, cred, howto); return (error); } int mac_check_system_swapon(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_system_swapon"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_system_swapon, cred, vp, &vp->v_label); + return (error); +} + +int +mac_check_system_sysctl(struct ucred *cred, int *name, u_int namelen, + void *old, size_t *oldlenp, int inkernel, void *new, size_t newlen) +{ + int error; + + /* + * XXXMAC: We're very much like to assert the SYSCTL_LOCK here, + * but since it's not exported from kern_sysctl.c, we can't. + */ + if (!mac_enforce_sysctl) + return (0); + + MAC_CHECK(check_system_sysctl, cred, name, namelen, old, oldlenp, + inkernel, new, newlen); + return (error); } int mac_ioctl_ifnet_get(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet) { char *elements, *buffer; struct mac mac; int error; 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); error = mac_externalize_ifnet_label(&ifnet->if_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } int mac_ioctl_ifnet_set(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet) { struct label intlabel; struct mac mac; char *buffer; int error; 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); } mac_init_ifnet_label(&intlabel); error = mac_internalize_ifnet_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_ifnet_label(&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 = suser_cred(cred, 0); if (error) { mac_destroy_ifnet_label(&intlabel); return (error); } MAC_CHECK(check_ifnet_relabel, cred, ifnet, &ifnet->if_label, &intlabel); if (error) { mac_destroy_ifnet_label(&intlabel); return (error); } MAC_PERFORM(relabel_ifnet, cred, ifnet, &ifnet->if_label, &intlabel); mac_destroy_ifnet_label(&intlabel); return (0); } void mac_create_devfs_vnode(struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(create_devfs_vnode, de, &de->de_label, vp, &vp->v_label); } void mac_create_devfs_device(dev_t dev, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_device, dev, de, &de->de_label); } void mac_create_devfs_symlink(struct ucred *cred, struct devfs_dirent *dd, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_symlink, cred, dd, &dd->de_label, de, &de->de_label); } void mac_create_devfs_directory(char *dirname, int dirnamelen, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_directory, dirname, dirnamelen, de, &de->de_label); } int mac_setsockopt_label_set(struct ucred *cred, struct socket *so, struct mac *mac) { struct label intlabel; char *buffer; int 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); } mac_init_socket_label(&intlabel, M_WAITOK); error = mac_internalize_socket_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_socket_label(&intlabel); return (error); } mac_check_socket_relabel(cred, so, &intlabel); if (error) { mac_destroy_socket_label(&intlabel); return (error); } mac_relabel_socket(cred, so, &intlabel); mac_destroy_socket_label(&intlabel); return (0); } int mac_pipe_label_set(struct ucred *cred, struct pipe *pipe, struct label *label) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); error = mac_check_pipe_relabel(cred, pipe, label); if (error) return (error); mac_relabel_pipe(cred, pipe, label); return (0); } int mac_getsockopt_label_get(struct ucred *cred, struct socket *so, struct mac *mac) { char *buffer, *elements; int 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_externalize_socket_label(&so->so_label, elements, buffer, mac->m_buflen, M_WAITOK); 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_get(struct ucred *cred, struct socket *so, struct mac *mac) { char *elements, *buffer; int 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_externalize_socket_peer_label(&so->so_peerlabel, elements, buffer, mac->m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac->m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * 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_setlabel_vnode_extattr(ap->a_cred, vp, intlabel); if (error) return (error); mac_relabel_vnode(ap->a_cred, vp, intlabel); return (0); } static 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_check_vnode_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); } int __mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) { char *elements, *buffer; struct mac mac; struct proc *tproc; struct ucred *tcred; int error; error = copyin(SCARG(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_externalize_cred_label(&tcred->cr_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); crfree(tcred); return (error); } /* * MPSAFE */ int __mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) { char *elements, *buffer; struct mac mac; int error; error = copyin(uap->mac_p, &mac, sizeof(mac)); if (error) return (error); error = mac_check_structmac_consistent(&mac); if (error) return (error); elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL); if (error) { free(elements, M_MACTEMP); return (error); } buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); error = mac_externalize_cred_label(&td->td_ucred->cr_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) { struct ucred *newcred, *oldcred; struct label intlabel; struct proc *p; struct mac mac; char *buffer; int error; 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); } mac_init_cred_label(&intlabel); error = mac_internalize_cred_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_cred_label(&intlabel); return (error); } newcred = crget(); p = td->td_proc; PROC_LOCK(p); oldcred = p->p_ucred; error = mac_check_cred_relabel(oldcred, &intlabel); if (error) { PROC_UNLOCK(p); crfree(newcred); goto out; } setsugid(p); crcopy(newcred, oldcred); mac_relabel_cred(newcred, &intlabel); p->p_ucred = newcred; /* * Grab additional reference for use while revoking mmaps, prior * to releasing the proc lock and sharing the cred. */ crhold(newcred); PROC_UNLOCK(p); if (mac_enforce_vm) { mtx_lock(&Giant); mac_cred_mmapped_drop_perms(td, newcred); mtx_unlock(&Giant); } crfree(newcred); /* Free revocation reference. */ crfree(oldcred); out: mac_destroy_cred_label(&intlabel); return (error); } /* * MPSAFE */ int __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) { char *elements, *buffer; struct label intlabel; struct file *fp; struct mac mac; struct vnode *vp; struct pipe *pipe; 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); mtx_lock(&Giant); /* VFS */ error = fget(td, SCARG(uap, fd), &fp); if (error) goto out; label_type = fp->f_type; switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: vp = (struct vnode *)fp->f_data; mac_init_vnode_label(&intlabel); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); mac_copy_vnode_label(&vp->v_label, &intlabel); VOP_UNLOCK(vp, 0, td); break; case DTYPE_PIPE: pipe = (struct pipe *)fp->f_data; mac_init_pipe_label(&intlabel); PIPE_LOCK(pipe); mac_copy_pipe_label(pipe->pipe_label, &intlabel); PIPE_UNLOCK(pipe); break; default: error = EINVAL; fdrop(fp, td); goto out; } fdrop(fp, td); switch (label_type) { case DTYPE_FIFO: case DTYPE_VNODE: if (error == 0) error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); mac_destroy_vnode_label(&intlabel); break; case DTYPE_PIPE: error = mac_externalize_pipe_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); mac_destroy_pipe_label(&intlabel); break; default: panic("__mac_get_fd: corrupted label_type"); } if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_get_file(struct thread *td, struct __mac_get_file_args *uap) { char *elements, *buffer; struct nameidata nd; struct label intlabel; struct mac mac; int 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); mtx_lock(&Giant); /* VFS */ NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_USERSPACE, uap->path_p, td); error = namei(&nd); if (error) goto out; mac_init_vnode_label(&intlabel); mac_copy_vnode_label(&nd.ni_vp->v_label, &intlabel); error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); NDFREE(&nd, 0); mac_destroy_vnode_label(&intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_get_link(struct thread *td, struct __mac_get_link_args *uap) { char *elements, *buffer; struct nameidata nd; struct label intlabel; struct mac mac; int 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); mtx_lock(&Giant); /* VFS */ NDINIT(&nd, LOOKUP, LOCKLEAF | NOFOLLOW, UIO_USERSPACE, uap->path_p, td); error = namei(&nd); if (error) goto out; mac_init_vnode_label(&intlabel); mac_copy_vnode_label(&nd.ni_vp->v_label, &intlabel); error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); NDFREE(&nd, 0); mac_destroy_vnode_label(&intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) { struct label intlabel; struct pipe *pipe; 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); } mtx_lock(&Giant); /* VFS */ error = fget(td, SCARG(uap, fd), &fp); if (error) goto out; switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); if (error) { mac_destroy_vnode_label(&intlabel); break; } vp = (struct vnode *)fp->f_data; error = vn_start_write(vp, &mp, V_WAIT | PCATCH); if (error != 0) { mac_destroy_vnode_label(&intlabel); break; } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); error = vn_setlabel(vp, &intlabel, td->td_ucred); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); mac_destroy_vnode_label(&intlabel); break; case DTYPE_PIPE: mac_init_pipe_label(&intlabel); error = mac_internalize_pipe_label(&intlabel, buffer); if (error == 0) { pipe = (struct pipe *)fp->f_data; PIPE_LOCK(pipe); error = mac_pipe_label_set(td->td_ucred, pipe, &intlabel); PIPE_UNLOCK(pipe); } mac_destroy_pipe_label(&intlabel); break; default: error = EINVAL; } fdrop(fp, td); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_file(struct thread *td, struct __mac_set_file_args *uap) { struct label intlabel; struct nameidata nd; struct mount *mp; struct mac mac; char *buffer; int 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); } mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_vnode_label(&intlabel); return (error); } mtx_lock(&Giant); /* VFS */ 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); mtx_unlock(&Giant); /* VFS */ mac_destroy_vnode_label(&intlabel); return (error); } /* * MPSAFE */ int __mac_set_link(struct thread *td, struct __mac_set_link_args *uap) { struct label intlabel; struct nameidata nd; struct mount *mp; struct mac mac; char *buffer; int 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); } mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_vnode_label(&intlabel); return (error); } mtx_lock(&Giant); /* VFS */ 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); mtx_unlock(&Giant); /* VFS */ mac_destroy_vnode_label(&intlabel); return (error); } /* * MPSAFE */ int 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(SCARG(uap, policy), target, sizeof(target), NULL); if (error) return (error); error = ENOSYS; MAC_POLICY_LIST_BUSY(); 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, SCARG(uap, call), SCARG(uap, arg)); goto out; } } out: MAC_POLICY_LIST_UNBUSY(); return (error); } 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); #else /* !MAC */ int __mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) { return (ENOSYS); } int __mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) { return (ENOSYS); } int __mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) { return (ENOSYS); } int __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) { return (ENOSYS); } int __mac_get_file(struct thread *td, struct __mac_get_file_args *uap) { return (ENOSYS); } int __mac_get_link(struct thread *td, struct __mac_get_link_args *uap) { return (ENOSYS); } int __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) { return (ENOSYS); } int __mac_set_file(struct thread *td, struct __mac_set_file_args *uap) { return (ENOSYS); } int __mac_set_link(struct thread *td, struct __mac_set_link_args *uap) { return (ENOSYS); } int mac_syscall(struct thread *td, struct mac_syscall_args *uap) { return (ENOSYS); } #endif Index: head/sys/security/mac/mac_system.c =================================================================== --- head/sys/security/mac/mac_system.c (revision 106024) +++ head/sys/security/mac/mac_system.c (revision 106025) @@ -1,4010 +1,4038 @@ /*- * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson * Copyright (c) 2001 Ilmar S. Habibulin * Copyright (c) 2001, 2002 Networks Associates Technology, 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 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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. * * Framework for extensible kernel access control. Kernel and userland * interface to the framework, policy registration and composition. */ #include "opt_mac.h" #include "opt_devfs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MAC /* * Declare that the kernel provides MAC support, version 1. 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, 1); SYSCTL_DECL(_security); SYSCTL_NODE(_security, OID_AUTO, mac, CTLFLAG_RW, 0, "TrustedBSD MAC policy controls"); #if MAC_MAX_POLICIES > 32 #error "MAC_MAX_POLICIES too large" #endif static unsigned int mac_max_policies = MAC_MAX_POLICIES; static unsigned int mac_policy_offsets_free = (1 << MAC_MAX_POLICIES) - 1; SYSCTL_UINT(_security_mac, OID_AUTO, max_policies, CTLFLAG_RD, &mac_max_policies, 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; /* * Warn about EA transactions only the first time they happen. * Weak coherency, no locking. */ static int ea_warn_once = 0; static int mac_enforce_fs = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_fs, CTLFLAG_RW, &mac_enforce_fs, 0, "Enforce MAC policy on file system objects"); TUNABLE_INT("security.mac.enforce_fs", &mac_enforce_fs); static int mac_enforce_network = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_network, CTLFLAG_RW, &mac_enforce_network, 0, "Enforce MAC policy on network packets"); TUNABLE_INT("security.mac.enforce_network", &mac_enforce_network); static int mac_enforce_pipe = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_pipe, CTLFLAG_RW, &mac_enforce_pipe, 0, "Enforce MAC policy on pipe operations"); TUNABLE_INT("security.mac.enforce_pipe", &mac_enforce_pipe); static int mac_enforce_process = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_process, CTLFLAG_RW, &mac_enforce_process, 0, "Enforce MAC policy on inter-process operations"); TUNABLE_INT("security.mac.enforce_process", &mac_enforce_process); static int mac_enforce_reboot = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_reboot, CTLFLAG_RW, &mac_enforce_reboot, 0, "Enforce MAC policy for reboot operations"); TUNABLE_INT("security.mac.enforce_reboot", &mac_enforce_reboot); static int mac_enforce_socket = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_socket, CTLFLAG_RW, &mac_enforce_socket, 0, "Enforce MAC policy on socket operations"); TUNABLE_INT("security.mac.enforce_socket", &mac_enforce_socket); +static int mac_enforce_sysctl = 1; +SYSCTL_INT(_security_mac, OID_AUTO, enforce_sysctl, CTLFLAG_RW, + &mac_enforce_sysctl, 0, "Enforce MAC policy on sysctl operations"); +TUNABLE_INT("security.mac.enforce_sysctl", &mac_enforce_sysctl); + static int mac_enforce_vm = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_vm, CTLFLAG_RW, &mac_enforce_vm, 0, "Enforce MAC policy on vm operations"); TUNABLE_INT("security.mac.enforce_vm", &mac_enforce_vm); static int mac_cache_fslabel_in_vnode = 1; SYSCTL_INT(_security_mac, OID_AUTO, cache_fslabel_in_vnode, CTLFLAG_RW, &mac_cache_fslabel_in_vnode, 0, "Cache mount fslabel in vnode"); TUNABLE_INT("security.mac.cache_fslabel_in_vnode", &mac_cache_fslabel_in_vnode); 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"); #ifdef MAC_DEBUG SYSCTL_NODE(_security_mac, OID_AUTO, debug, CTLFLAG_RW, 0, "TrustedBSD MAC debug info"); static int mac_debug_label_fallback = 0; SYSCTL_INT(_security_mac_debug, OID_AUTO, label_fallback, CTLFLAG_RW, &mac_debug_label_fallback, 0, "Filesystems should fall back to fs label" "when label is corrupted."); TUNABLE_INT("security.mac.debug_label_fallback", &mac_debug_label_fallback); SYSCTL_NODE(_security_mac_debug, OID_AUTO, counters, CTLFLAG_RW, 0, "TrustedBSD MAC object counters"); static unsigned int nmacmbufs, nmaccreds, nmacifnets, nmacbpfdescs, nmacsockets, nmacmounts, nmactemp, nmacvnodes, nmacdevfsdirents, nmacipqs, nmacpipes; SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, mbufs, CTLFLAG_RD, &nmacmbufs, 0, "number of mbufs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, creds, CTLFLAG_RD, &nmaccreds, 0, "number of ucreds in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ifnets, CTLFLAG_RD, &nmacifnets, 0, "number of ifnets in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ipqs, CTLFLAG_RD, &nmacipqs, 0, "number of ipqs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, bpfdescs, CTLFLAG_RD, &nmacbpfdescs, 0, "number of bpfdescs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, sockets, CTLFLAG_RD, &nmacsockets, 0, "number of sockets in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, pipes, CTLFLAG_RD, &nmacpipes, 0, "number of pipes in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, mounts, CTLFLAG_RD, &nmacmounts, 0, "number of mounts in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, temp, CTLFLAG_RD, &nmactemp, 0, "number of temporary labels in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, vnodes, CTLFLAG_RD, &nmacvnodes, 0, "number of vnodes in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, devfsdirents, CTLFLAG_RD, &nmacdevfsdirents, 0, "number of devfs dirents inuse"); #endif static int error_select(int error1, int error2); static int mac_policy_register(struct mac_policy_conf *mpc); static int mac_policy_unregister(struct mac_policy_conf *mpc); static void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot); static void mac_cred_mmapped_drop_perms_recurse(struct thread *td, struct ucred *cred, struct vm_map *map); static void mac_destroy_socket_label(struct label *label); static int mac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *intlabel); MALLOC_DEFINE(M_MACOPVEC, "macopvec", "MAC policy operation vector"); MALLOC_DEFINE(M_MACPIPELABEL, "macpipelabel", "MAC labels for pipes"); MALLOC_DEFINE(M_MACTEMP, "mactemp", "MAC temporary label storage"); /* * mac_policy_list_lock protects the consistency of 'mac_policy_list', * the linked list of attached policy modules. Read-only consumers of * the list must acquire a shared lock for the duration of their use; * writers must acquire an exclusive lock. Note that for compound * operations, locks should be held for the entire compound operation, * and that this is not yet done for relabel requests. */ static struct mtx mac_policy_list_lock; static LIST_HEAD(, mac_policy_conf) mac_policy_list; static int mac_policy_list_busy; #define MAC_POLICY_LIST_LOCKINIT() mtx_init(&mac_policy_list_lock, \ "mac_policy_list_lock", NULL, MTX_DEF); #define MAC_POLICY_LIST_LOCK() mtx_lock(&mac_policy_list_lock); #define MAC_POLICY_LIST_UNLOCK() mtx_unlock(&mac_policy_list_lock); #define MAC_POLICY_LIST_BUSY() do { \ MAC_POLICY_LIST_LOCK(); \ mac_policy_list_busy++; \ MAC_POLICY_LIST_UNLOCK(); \ } while (0) #define MAC_POLICY_LIST_UNBUSY() do { \ MAC_POLICY_LIST_LOCK(); \ mac_policy_list_busy--; \ if (mac_policy_list_busy < 0) \ panic("Extra mac_policy_list_busy--"); \ MAC_POLICY_LIST_UNLOCK(); \ } while (0) /* * MAC_CHECK performs the designated check by walking the policy * module list and checking with each as to how it feels about the * request. Note that it returns its value via 'error' in the scope * of the caller. */ #define MAC_CHECK(check, args...) do { \ struct mac_policy_conf *mpc; \ \ error = 0; \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## check != NULL) \ error = error_select( \ mpc->mpc_ops->mpo_ ## check (args), \ error); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) /* * MAC_BOOLEAN performs the designated boolean composition by walking * the module list, invoking each instance of the operation, and * combining the results using the passed C operator. Note that it * returns its value via 'result' in the scope of the caller, which * should be initialized by the caller in a meaningful way to get * a meaningful result. */ #define MAC_BOOLEAN(operation, composition, args...) do { \ struct mac_policy_conf *mpc; \ \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ result = result composition \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) #define MAC_EXTERNALIZE(type, label, elementlist, outbuf, \ outbuflen) do { \ char *curptr, *curptr_start, *element_name, *element_temp; \ size_t left, left_start, len; \ int claimed, first, first_start, ignorenotfound; \ \ error = 0; \ element_temp = elementlist; \ curptr = outbuf; \ curptr[0] = '\0'; \ left = outbuflen; \ first = 1; \ while ((element_name = strsep(&element_temp, ",")) != NULL) { \ curptr_start = curptr; \ left_start = left; \ first_start = first; \ if (element_name[0] == '?') { \ element_name++; \ ignorenotfound = 1; \ } else \ ignorenotfound = 0; \ claimed = 0; \ if (first) { \ len = snprintf(curptr, left, "%s/", \ element_name); \ first = 0; \ } else \ len = snprintf(curptr, left, ",%s/", \ element_name); \ if (len >= left) { \ error = EINVAL; /* XXXMAC: E2BIG */ \ break; \ } \ curptr += len; \ left -= len; \ \ MAC_CHECK(externalize_ ## type, label, element_name, \ curptr, left, &len, &claimed); \ if (error) \ break; \ if (claimed == 1) { \ if (len >= outbuflen) { \ error = EINVAL; /* XXXMAC: E2BIG */ \ break; \ } \ curptr += len; \ left -= len; \ } else if (claimed == 0 && ignorenotfound) { \ /* \ * Revert addition of the label element \ * name. \ */ \ curptr = curptr_start; \ *curptr = '\0'; \ left = left_start; \ first = first_start; \ } else { \ error = EINVAL; /* XXXMAC: ENOLABEL */ \ break; \ } \ } \ } while (0) #define MAC_INTERNALIZE(type, label, instring) do { \ char *element, *element_name, *element_data; \ int claimed; \ \ error = 0; \ element = instring; \ while ((element_name = strsep(&element, ",")) != NULL) { \ element_data = element_name; \ element_name = strsep(&element_data, "/"); \ if (element_data == NULL) { \ error = EINVAL; \ break; \ } \ claimed = 0; \ MAC_CHECK(internalize_ ## type, label, element_name, \ element_data, &claimed); \ if (error) \ break; \ if (claimed != 1) { \ /* XXXMAC: Another error here? */ \ error = EINVAL; \ break; \ } \ } \ } while (0) /* * MAC_PERFORM performs the designated operation by walking the policy * module list and invoking that operation for each policy. */ #define MAC_PERFORM(operation, args...) do { \ struct mac_policy_conf *mpc; \ \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) /* * Initialize the MAC subsystem, including appropriate SMP locks. */ static void mac_init(void) { LIST_INIT(&mac_policy_list); MAC_POLICY_LIST_LOCKINIT(); } /* * 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; } /* * 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; 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: break; } return (error); } static int mac_policy_register(struct mac_policy_conf *mpc) { struct mac_policy_conf *tmpc; struct mac_policy_op_entry *mpe; int slot; MALLOC(mpc->mpc_ops, struct mac_policy_ops *, sizeof(*mpc->mpc_ops), M_MACOPVEC, M_WAITOK | M_ZERO); for (mpe = mpc->mpc_entries; mpe->mpe_constant != MAC_OP_LAST; mpe++) { switch (mpe->mpe_constant) { case MAC_OP_LAST: /* * Doesn't actually happen, but this allows checking * that all enumerated values are handled. */ break; case MAC_DESTROY: mpc->mpc_ops->mpo_destroy = mpe->mpe_function; break; case MAC_INIT: mpc->mpc_ops->mpo_init = mpe->mpe_function; break; case MAC_SYSCALL: mpc->mpc_ops->mpo_syscall = mpe->mpe_function; break; case MAC_INIT_BPFDESC_LABEL: mpc->mpc_ops->mpo_init_bpfdesc_label = mpe->mpe_function; break; case MAC_INIT_CRED_LABEL: mpc->mpc_ops->mpo_init_cred_label = mpe->mpe_function; break; case MAC_INIT_DEVFSDIRENT_LABEL: mpc->mpc_ops->mpo_init_devfsdirent_label = mpe->mpe_function; break; case MAC_INIT_IFNET_LABEL: mpc->mpc_ops->mpo_init_ifnet_label = mpe->mpe_function; break; case MAC_INIT_IPQ_LABEL: mpc->mpc_ops->mpo_init_ipq_label = mpe->mpe_function; break; case MAC_INIT_MBUF_LABEL: mpc->mpc_ops->mpo_init_mbuf_label = mpe->mpe_function; break; case MAC_INIT_MOUNT_LABEL: mpc->mpc_ops->mpo_init_mount_label = mpe->mpe_function; break; case MAC_INIT_MOUNT_FS_LABEL: mpc->mpc_ops->mpo_init_mount_fs_label = mpe->mpe_function; break; case MAC_INIT_PIPE_LABEL: mpc->mpc_ops->mpo_init_pipe_label = mpe->mpe_function; break; case MAC_INIT_SOCKET_LABEL: mpc->mpc_ops->mpo_init_socket_label = mpe->mpe_function; break; case MAC_INIT_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_init_socket_peer_label = mpe->mpe_function; break; case MAC_INIT_VNODE_LABEL: mpc->mpc_ops->mpo_init_vnode_label = mpe->mpe_function; break; case MAC_DESTROY_BPFDESC_LABEL: mpc->mpc_ops->mpo_destroy_bpfdesc_label = mpe->mpe_function; break; case MAC_DESTROY_CRED_LABEL: mpc->mpc_ops->mpo_destroy_cred_label = mpe->mpe_function; break; case MAC_DESTROY_DEVFSDIRENT_LABEL: mpc->mpc_ops->mpo_destroy_devfsdirent_label = mpe->mpe_function; break; case MAC_DESTROY_IFNET_LABEL: mpc->mpc_ops->mpo_destroy_ifnet_label = mpe->mpe_function; break; case MAC_DESTROY_IPQ_LABEL: mpc->mpc_ops->mpo_destroy_ipq_label = mpe->mpe_function; break; case MAC_DESTROY_MBUF_LABEL: mpc->mpc_ops->mpo_destroy_mbuf_label = mpe->mpe_function; break; case MAC_DESTROY_MOUNT_LABEL: mpc->mpc_ops->mpo_destroy_mount_label = mpe->mpe_function; break; case MAC_DESTROY_MOUNT_FS_LABEL: mpc->mpc_ops->mpo_destroy_mount_fs_label = mpe->mpe_function; break; case MAC_DESTROY_PIPE_LABEL: mpc->mpc_ops->mpo_destroy_pipe_label = mpe->mpe_function; break; case MAC_DESTROY_SOCKET_LABEL: mpc->mpc_ops->mpo_destroy_socket_label = mpe->mpe_function; break; case MAC_DESTROY_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_destroy_socket_peer_label = mpe->mpe_function; break; case MAC_DESTROY_VNODE_LABEL: mpc->mpc_ops->mpo_destroy_vnode_label = mpe->mpe_function; break; case MAC_COPY_PIPE_LABEL: mpc->mpc_ops->mpo_copy_pipe_label = mpe->mpe_function; break; case MAC_COPY_VNODE_LABEL: mpc->mpc_ops->mpo_copy_vnode_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_CRED_LABEL: mpc->mpc_ops->mpo_externalize_cred_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_IFNET_LABEL: mpc->mpc_ops->mpo_externalize_ifnet_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_PIPE_LABEL: mpc->mpc_ops->mpo_externalize_pipe_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_SOCKET_LABEL: mpc->mpc_ops->mpo_externalize_socket_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_externalize_socket_peer_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_VNODE_LABEL: mpc->mpc_ops->mpo_externalize_vnode_label = mpe->mpe_function; break; case MAC_INTERNALIZE_CRED_LABEL: mpc->mpc_ops->mpo_internalize_cred_label = mpe->mpe_function; break; case MAC_INTERNALIZE_IFNET_LABEL: mpc->mpc_ops->mpo_internalize_ifnet_label = mpe->mpe_function; break; case MAC_INTERNALIZE_PIPE_LABEL: mpc->mpc_ops->mpo_internalize_pipe_label = mpe->mpe_function; break; case MAC_INTERNALIZE_SOCKET_LABEL: mpc->mpc_ops->mpo_internalize_socket_label = mpe->mpe_function; break; case MAC_INTERNALIZE_VNODE_LABEL: mpc->mpc_ops->mpo_internalize_vnode_label = mpe->mpe_function; break; case MAC_CREATE_DEVFS_DEVICE: mpc->mpc_ops->mpo_create_devfs_device = mpe->mpe_function; break; case MAC_CREATE_DEVFS_DIRECTORY: mpc->mpc_ops->mpo_create_devfs_directory = mpe->mpe_function; break; case MAC_CREATE_DEVFS_SYMLINK: mpc->mpc_ops->mpo_create_devfs_symlink = mpe->mpe_function; break; case MAC_CREATE_DEVFS_VNODE: mpc->mpc_ops->mpo_create_devfs_vnode = mpe->mpe_function; break; case MAC_CREATE_MOUNT: mpc->mpc_ops->mpo_create_mount = mpe->mpe_function; break; case MAC_CREATE_ROOT_MOUNT: mpc->mpc_ops->mpo_create_root_mount = mpe->mpe_function; break; case MAC_RELABEL_VNODE: mpc->mpc_ops->mpo_relabel_vnode = mpe->mpe_function; break; case MAC_UPDATE_DEVFSDIRENT: mpc->mpc_ops->mpo_update_devfsdirent = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_DEVFS: mpc->mpc_ops->mpo_associate_vnode_devfs = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_EXTATTR: mpc->mpc_ops->mpo_associate_vnode_extattr = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_SINGLELABEL: mpc->mpc_ops->mpo_associate_vnode_singlelabel = mpe->mpe_function; break; case MAC_CREATE_VNODE_EXTATTR: mpc->mpc_ops->mpo_create_vnode_extattr = mpe->mpe_function; break; case MAC_SETLABEL_VNODE_EXTATTR: mpc->mpc_ops->mpo_setlabel_vnode_extattr = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_SOCKET: mpc->mpc_ops->mpo_create_mbuf_from_socket = mpe->mpe_function; break; case MAC_CREATE_PIPE: mpc->mpc_ops->mpo_create_pipe = mpe->mpe_function; break; case MAC_CREATE_SOCKET: mpc->mpc_ops->mpo_create_socket = mpe->mpe_function; break; case MAC_CREATE_SOCKET_FROM_SOCKET: mpc->mpc_ops->mpo_create_socket_from_socket = mpe->mpe_function; break; case MAC_RELABEL_PIPE: mpc->mpc_ops->mpo_relabel_pipe = mpe->mpe_function; break; case MAC_RELABEL_SOCKET: mpc->mpc_ops->mpo_relabel_socket = mpe->mpe_function; break; case MAC_SET_SOCKET_PEER_FROM_MBUF: mpc->mpc_ops->mpo_set_socket_peer_from_mbuf = mpe->mpe_function; break; case MAC_SET_SOCKET_PEER_FROM_SOCKET: mpc->mpc_ops->mpo_set_socket_peer_from_socket = mpe->mpe_function; break; case MAC_CREATE_BPFDESC: mpc->mpc_ops->mpo_create_bpfdesc = mpe->mpe_function; break; case MAC_CREATE_DATAGRAM_FROM_IPQ: mpc->mpc_ops->mpo_create_datagram_from_ipq = mpe->mpe_function; break; case MAC_CREATE_FRAGMENT: mpc->mpc_ops->mpo_create_fragment = mpe->mpe_function; break; case MAC_CREATE_IFNET: mpc->mpc_ops->mpo_create_ifnet = mpe->mpe_function; break; case MAC_CREATE_IPQ: mpc->mpc_ops->mpo_create_ipq = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_MBUF: mpc->mpc_ops->mpo_create_mbuf_from_mbuf = mpe->mpe_function; break; case MAC_CREATE_MBUF_LINKLAYER: mpc->mpc_ops->mpo_create_mbuf_linklayer = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_BPFDESC: mpc->mpc_ops->mpo_create_mbuf_from_bpfdesc = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_IFNET: mpc->mpc_ops->mpo_create_mbuf_from_ifnet = mpe->mpe_function; break; case MAC_CREATE_MBUF_MULTICAST_ENCAP: mpc->mpc_ops->mpo_create_mbuf_multicast_encap = mpe->mpe_function; break; case MAC_CREATE_MBUF_NETLAYER: mpc->mpc_ops->mpo_create_mbuf_netlayer = mpe->mpe_function; break; case MAC_FRAGMENT_MATCH: mpc->mpc_ops->mpo_fragment_match = mpe->mpe_function; break; case MAC_RELABEL_IFNET: mpc->mpc_ops->mpo_relabel_ifnet = mpe->mpe_function; break; case MAC_UPDATE_IPQ: mpc->mpc_ops->mpo_update_ipq = mpe->mpe_function; break; case MAC_CREATE_CRED: mpc->mpc_ops->mpo_create_cred = mpe->mpe_function; break; case MAC_EXECVE_TRANSITION: mpc->mpc_ops->mpo_execve_transition = mpe->mpe_function; break; case MAC_EXECVE_WILL_TRANSITION: mpc->mpc_ops->mpo_execve_will_transition = mpe->mpe_function; break; case MAC_CREATE_PROC0: mpc->mpc_ops->mpo_create_proc0 = mpe->mpe_function; break; case MAC_CREATE_PROC1: mpc->mpc_ops->mpo_create_proc1 = mpe->mpe_function; break; case MAC_RELABEL_CRED: mpc->mpc_ops->mpo_relabel_cred = mpe->mpe_function; break; case MAC_THREAD_USERRET: mpc->mpc_ops->mpo_thread_userret = mpe->mpe_function; break; case MAC_CHECK_BPFDESC_RECEIVE: mpc->mpc_ops->mpo_check_bpfdesc_receive = mpe->mpe_function; break; case MAC_CHECK_CRED_RELABEL: mpc->mpc_ops->mpo_check_cred_relabel = mpe->mpe_function; break; case MAC_CHECK_CRED_VISIBLE: mpc->mpc_ops->mpo_check_cred_visible = mpe->mpe_function; break; case MAC_CHECK_IFNET_RELABEL: mpc->mpc_ops->mpo_check_ifnet_relabel = mpe->mpe_function; break; case MAC_CHECK_IFNET_TRANSMIT: mpc->mpc_ops->mpo_check_ifnet_transmit = mpe->mpe_function; break; case MAC_CHECK_MOUNT_STAT: mpc->mpc_ops->mpo_check_mount_stat = mpe->mpe_function; break; case MAC_CHECK_PIPE_IOCTL: mpc->mpc_ops->mpo_check_pipe_ioctl = mpe->mpe_function; break; case MAC_CHECK_PIPE_POLL: mpc->mpc_ops->mpo_check_pipe_poll = mpe->mpe_function; break; case MAC_CHECK_PIPE_READ: mpc->mpc_ops->mpo_check_pipe_read = mpe->mpe_function; break; case MAC_CHECK_PIPE_RELABEL: mpc->mpc_ops->mpo_check_pipe_relabel = mpe->mpe_function; break; case MAC_CHECK_PIPE_STAT: mpc->mpc_ops->mpo_check_pipe_stat = mpe->mpe_function; break; case MAC_CHECK_PIPE_WRITE: mpc->mpc_ops->mpo_check_pipe_write = mpe->mpe_function; break; case MAC_CHECK_PROC_DEBUG: mpc->mpc_ops->mpo_check_proc_debug = mpe->mpe_function; break; case MAC_CHECK_PROC_SCHED: mpc->mpc_ops->mpo_check_proc_sched = mpe->mpe_function; break; case MAC_CHECK_PROC_SIGNAL: mpc->mpc_ops->mpo_check_proc_signal = mpe->mpe_function; break; case MAC_CHECK_SOCKET_BIND: mpc->mpc_ops->mpo_check_socket_bind = mpe->mpe_function; break; case MAC_CHECK_SOCKET_CONNECT: mpc->mpc_ops->mpo_check_socket_connect = mpe->mpe_function; break; case MAC_CHECK_SOCKET_DELIVER: mpc->mpc_ops->mpo_check_socket_deliver = mpe->mpe_function; break; case MAC_CHECK_SOCKET_LISTEN: mpc->mpc_ops->mpo_check_socket_listen = mpe->mpe_function; break; case MAC_CHECK_SOCKET_RECEIVE: mpc->mpc_ops->mpo_check_socket_receive = mpe->mpe_function; break; case MAC_CHECK_SOCKET_RELABEL: mpc->mpc_ops->mpo_check_socket_relabel = mpe->mpe_function; break; case MAC_CHECK_SOCKET_SEND: mpc->mpc_ops->mpo_check_socket_send = mpe->mpe_function; break; case MAC_CHECK_SOCKET_VISIBLE: mpc->mpc_ops->mpo_check_socket_visible = mpe->mpe_function; break; case MAC_CHECK_SYSTEM_REBOOT: mpc->mpc_ops->mpo_check_system_reboot = mpe->mpe_function; break; case MAC_CHECK_SYSTEM_SWAPON: mpc->mpc_ops->mpo_check_system_swapon = mpe->mpe_function; break; + case MAC_CHECK_SYSTEM_SYSCTL: + mpc->mpc_ops->mpo_check_system_sysctl = + mpe->mpe_function; + break; case MAC_CHECK_VNODE_ACCESS: mpc->mpc_ops->mpo_check_vnode_access = mpe->mpe_function; break; case MAC_CHECK_VNODE_CHDIR: mpc->mpc_ops->mpo_check_vnode_chdir = mpe->mpe_function; break; case MAC_CHECK_VNODE_CHROOT: mpc->mpc_ops->mpo_check_vnode_chroot = mpe->mpe_function; break; case MAC_CHECK_VNODE_CREATE: mpc->mpc_ops->mpo_check_vnode_create = mpe->mpe_function; break; case MAC_CHECK_VNODE_DELETE: mpc->mpc_ops->mpo_check_vnode_delete = mpe->mpe_function; break; case MAC_CHECK_VNODE_DELETEACL: mpc->mpc_ops->mpo_check_vnode_deleteacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_EXEC: mpc->mpc_ops->mpo_check_vnode_exec = mpe->mpe_function; break; case MAC_CHECK_VNODE_GETACL: mpc->mpc_ops->mpo_check_vnode_getacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_GETEXTATTR: mpc->mpc_ops->mpo_check_vnode_getextattr = mpe->mpe_function; break; case MAC_CHECK_VNODE_LINK: mpc->mpc_ops->mpo_check_vnode_link = mpe->mpe_function; break; case MAC_CHECK_VNODE_LOOKUP: mpc->mpc_ops->mpo_check_vnode_lookup = mpe->mpe_function; break; case MAC_CHECK_VNODE_MMAP: mpc->mpc_ops->mpo_check_vnode_mmap = mpe->mpe_function; break; case MAC_CHECK_VNODE_MMAP_DOWNGRADE: mpc->mpc_ops->mpo_check_vnode_mmap_downgrade = mpe->mpe_function; break; case MAC_CHECK_VNODE_MPROTECT: mpc->mpc_ops->mpo_check_vnode_mprotect = mpe->mpe_function; break; case MAC_CHECK_VNODE_OPEN: mpc->mpc_ops->mpo_check_vnode_open = mpe->mpe_function; break; case MAC_CHECK_VNODE_POLL: mpc->mpc_ops->mpo_check_vnode_poll = mpe->mpe_function; break; case MAC_CHECK_VNODE_READ: mpc->mpc_ops->mpo_check_vnode_read = mpe->mpe_function; break; case MAC_CHECK_VNODE_READDIR: mpc->mpc_ops->mpo_check_vnode_readdir = mpe->mpe_function; break; case MAC_CHECK_VNODE_READLINK: mpc->mpc_ops->mpo_check_vnode_readlink = mpe->mpe_function; break; case MAC_CHECK_VNODE_RELABEL: mpc->mpc_ops->mpo_check_vnode_relabel = mpe->mpe_function; break; case MAC_CHECK_VNODE_RENAME_FROM: mpc->mpc_ops->mpo_check_vnode_rename_from = mpe->mpe_function; break; case MAC_CHECK_VNODE_RENAME_TO: mpc->mpc_ops->mpo_check_vnode_rename_to = mpe->mpe_function; break; case MAC_CHECK_VNODE_REVOKE: mpc->mpc_ops->mpo_check_vnode_revoke = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETACL: mpc->mpc_ops->mpo_check_vnode_setacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETEXTATTR: mpc->mpc_ops->mpo_check_vnode_setextattr = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETFLAGS: mpc->mpc_ops->mpo_check_vnode_setflags = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETMODE: mpc->mpc_ops->mpo_check_vnode_setmode = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETOWNER: mpc->mpc_ops->mpo_check_vnode_setowner = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETUTIMES: mpc->mpc_ops->mpo_check_vnode_setutimes = mpe->mpe_function; break; case MAC_CHECK_VNODE_STAT: mpc->mpc_ops->mpo_check_vnode_stat = mpe->mpe_function; break; case MAC_CHECK_VNODE_WRITE: mpc->mpc_ops->mpo_check_vnode_write = mpe->mpe_function; break; /* default: printf("MAC policy `%s': unknown operation %d\n", mpc->mpc_name, mpe->mpe_constant); return (EINVAL); */ } } MAC_POLICY_LIST_LOCK(); if (mac_policy_list_busy > 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (EBUSY); } LIST_FOREACH(tmpc, &mac_policy_list, mpc_list) { if (strcmp(tmpc->mpc_name, mpc->mpc_name) == 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (EEXIST); } } if (mpc->mpc_field_off != NULL) { slot = ffs(mac_policy_offsets_free); if (slot == 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (ENOMEM); } slot--; mac_policy_offsets_free &= ~(1 << slot); *mpc->mpc_field_off = slot; } mpc->mpc_runtime_flags |= MPC_RUNTIME_FLAG_REGISTERED; LIST_INSERT_HEAD(&mac_policy_list, mpc, mpc_list); /* Per-policy initialization. */ if (mpc->mpc_ops->mpo_init != NULL) (*(mpc->mpc_ops->mpo_init))(mpc); MAC_POLICY_LIST_UNLOCK(); printf("Security policy loaded: %s (%s)\n", mpc->mpc_fullname, mpc->mpc_name); return (0); } 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_LIST_LOCK(); if ((mpc->mpc_runtime_flags & MPC_RUNTIME_FLAG_REGISTERED) == 0) { MAC_POLICY_LIST_UNLOCK(); return (0); } #if 0 /* * Don't allow unloading modules with private data. */ if (mpc->mpc_field_off != NULL) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } #endif /* * Only allow the unload to proceed if the module is unloadable * by its own definition. */ if ((mpc->mpc_loadtime_flags & MPC_LOADTIME_FLAG_UNLOADOK) == 0) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } /* * Right now, we EBUSY if the list is in use. In the future, * for reliability reasons, we might want to sleep and wakeup * later to try again. */ if (mac_policy_list_busy > 0) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } if (mpc->mpc_ops->mpo_destroy != NULL) (*(mpc->mpc_ops->mpo_destroy))(mpc); LIST_REMOVE(mpc, mpc_list); MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; mpc->mpc_runtime_flags &= ~MPC_RUNTIME_FLAG_REGISTERED; printf("Security policy unload: %s (%s)\n", mpc->mpc_fullname, mpc->mpc_name); return (0); } /* * Define an error value precedence, and given two arguments, selects the * value with the higher precedence. */ static int 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); } static void mac_init_label(struct label *label) { bzero(label, sizeof(*label)); label->l_flags = MAC_FLAG_INITIALIZED; } static void mac_destroy_label(struct label *label) { KASSERT(label->l_flags & MAC_FLAG_INITIALIZED, ("destroying uninitialized label")); bzero(label, sizeof(*label)); /* implicit: label->l_flags &= ~MAC_FLAG_INITIALIZED; */ } void mac_init_bpfdesc(struct bpf_d *bpf_d) { mac_init_label(&bpf_d->bd_label); MAC_PERFORM(init_bpfdesc_label, &bpf_d->bd_label); #ifdef MAC_DEBUG atomic_add_int(&nmacbpfdescs, 1); #endif } static void mac_init_cred_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_cred_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmaccreds, 1); #endif } void mac_init_cred(struct ucred *cred) { mac_init_cred_label(&cred->cr_label); } void mac_init_devfsdirent(struct devfs_dirent *de) { mac_init_label(&de->de_label); MAC_PERFORM(init_devfsdirent_label, &de->de_label); #ifdef MAC_DEBUG atomic_add_int(&nmacdevfsdirents, 1); #endif } static void mac_init_ifnet_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_ifnet_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacifnets, 1); #endif } void mac_init_ifnet(struct ifnet *ifp) { mac_init_ifnet_label(&ifp->if_label); } void mac_init_ipq(struct ipq *ipq) { mac_init_label(&ipq->ipq_label); MAC_PERFORM(init_ipq_label, &ipq->ipq_label); #ifdef MAC_DEBUG atomic_add_int(&nmacipqs, 1); #endif } int mac_init_mbuf(struct mbuf *m, int flag) { int error; KASSERT(m->m_flags & M_PKTHDR, ("mac_init_mbuf on non-header mbuf")); mac_init_label(&m->m_pkthdr.label); MAC_CHECK(init_mbuf_label, &m->m_pkthdr.label, flag); if (error) { MAC_PERFORM(destroy_mbuf_label, &m->m_pkthdr.label); mac_destroy_label(&m->m_pkthdr.label); } #ifdef MAC_DEBUG if (error == 0) atomic_add_int(&nmacmbufs, 1); #endif return (error); } void mac_init_mount(struct mount *mp) { mac_init_label(&mp->mnt_mntlabel); mac_init_label(&mp->mnt_fslabel); MAC_PERFORM(init_mount_label, &mp->mnt_mntlabel); MAC_PERFORM(init_mount_fs_label, &mp->mnt_fslabel); #ifdef MAC_DEBUG atomic_add_int(&nmacmounts, 1); #endif } static void mac_init_pipe_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_pipe_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacpipes, 1); #endif } void mac_init_pipe(struct pipe *pipe) { struct label *label; label = malloc(sizeof(struct label), M_MACPIPELABEL, M_ZERO|M_WAITOK); pipe->pipe_label = label; pipe->pipe_peer->pipe_label = label; mac_init_pipe_label(label); } static int mac_init_socket_label(struct label *label, int flag) { int error; mac_init_label(label); MAC_CHECK(init_socket_label, label, flag); if (error) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); } #ifdef MAC_DEBUG if (error == 0) atomic_add_int(&nmacsockets, 1); #endif return (error); } static int mac_init_socket_peer_label(struct label *label, int flag) { int error; mac_init_label(label); MAC_CHECK(init_socket_peer_label, label, flag); if (error) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); } return (error); } int mac_init_socket(struct socket *socket, int flag) { int error; error = mac_init_socket_label(&socket->so_label, flag); if (error) return (error); error = mac_init_socket_peer_label(&socket->so_peerlabel, flag); if (error) mac_destroy_socket_label(&socket->so_label); return (error); } void mac_init_vnode_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_vnode_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacvnodes, 1); #endif } void mac_init_vnode(struct vnode *vp) { mac_init_vnode_label(&vp->v_label); } void mac_destroy_bpfdesc(struct bpf_d *bpf_d) { MAC_PERFORM(destroy_bpfdesc_label, &bpf_d->bd_label); mac_destroy_label(&bpf_d->bd_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacbpfdescs, 1); #endif } static void mac_destroy_cred_label(struct label *label) { MAC_PERFORM(destroy_cred_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmaccreds, 1); #endif } void mac_destroy_cred(struct ucred *cred) { mac_destroy_cred_label(&cred->cr_label); } void mac_destroy_devfsdirent(struct devfs_dirent *de) { MAC_PERFORM(destroy_devfsdirent_label, &de->de_label); mac_destroy_label(&de->de_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacdevfsdirents, 1); #endif } static void mac_destroy_ifnet_label(struct label *label) { MAC_PERFORM(destroy_ifnet_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacifnets, 1); #endif } void mac_destroy_ifnet(struct ifnet *ifp) { mac_destroy_ifnet_label(&ifp->if_label); } void mac_destroy_ipq(struct ipq *ipq) { MAC_PERFORM(destroy_ipq_label, &ipq->ipq_label); mac_destroy_label(&ipq->ipq_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacipqs, 1); #endif } void mac_destroy_mbuf(struct mbuf *m) { MAC_PERFORM(destroy_mbuf_label, &m->m_pkthdr.label); mac_destroy_label(&m->m_pkthdr.label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacmbufs, 1); #endif } void mac_destroy_mount(struct mount *mp) { MAC_PERFORM(destroy_mount_label, &mp->mnt_mntlabel); MAC_PERFORM(destroy_mount_fs_label, &mp->mnt_fslabel); mac_destroy_label(&mp->mnt_fslabel); mac_destroy_label(&mp->mnt_mntlabel); #ifdef MAC_DEBUG atomic_subtract_int(&nmacmounts, 1); #endif } static void mac_destroy_pipe_label(struct label *label) { MAC_PERFORM(destroy_pipe_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacpipes, 1); #endif } void mac_destroy_pipe(struct pipe *pipe) { mac_destroy_pipe_label(pipe->pipe_label); free(pipe->pipe_label, M_MACPIPELABEL); } static void mac_destroy_socket_label(struct label *label) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacsockets, 1); #endif } static void mac_destroy_socket_peer_label(struct label *label) { MAC_PERFORM(destroy_socket_peer_label, label); mac_destroy_label(label); } void mac_destroy_socket(struct socket *socket) { mac_destroy_socket_label(&socket->so_label); mac_destroy_socket_peer_label(&socket->so_peerlabel); } void mac_destroy_vnode_label(struct label *label) { MAC_PERFORM(destroy_vnode_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacvnodes, 1); #endif } void mac_destroy_vnode(struct vnode *vp) { mac_destroy_vnode_label(&vp->v_label); } static void mac_copy_pipe_label(struct label *src, struct label *dest) { MAC_PERFORM(copy_pipe_label, src, dest); } void mac_copy_vnode_label(struct label *src, struct label *dest) { MAC_PERFORM(copy_vnode_label, src, dest); } static int mac_check_structmac_consistent(struct mac *mac) { if (mac->m_buflen > MAC_MAX_LABEL_BUF_LEN) return (EINVAL); return (0); } static int mac_externalize_cred_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(cred_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_ifnet_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(ifnet_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_pipe_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(pipe_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_socket_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(socket_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_socket_peer_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(socket_peer_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_vnode_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(vnode_label, label, elements, outbuf, outbuflen); return (error); } static int mac_internalize_cred_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(cred_label, label, string); return (error); } static int mac_internalize_ifnet_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(ifnet_label, label, string); return (error); } static int mac_internalize_pipe_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(pipe_label, label, string); return (error); } static int mac_internalize_socket_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(socket_label, label, string); return (error); } static int mac_internalize_vnode_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(vnode_label, label, string); return (error); } /* * Initialize MAC label for the first kernel process, from which other * kernel processes and threads are spawned. */ void mac_create_proc0(struct ucred *cred) { MAC_PERFORM(create_proc0, cred); } /* * Initialize MAC label for the first userland process, from which other * userland processes and threads are spawned. */ void mac_create_proc1(struct ucred *cred) { MAC_PERFORM(create_proc1, cred); } void mac_thread_userret(struct thread *td) { MAC_PERFORM(thread_userret, td); } /* * 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_create_cred(struct ucred *parent_cred, struct ucred *child_cred) { MAC_PERFORM(create_cred, parent_cred, child_cred); } void mac_update_devfsdirent(struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(update_devfsdirent, de, &de->de_label, vp, &vp->v_label); } void mac_associate_vnode_devfs(struct mount *mp, struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(associate_vnode_devfs, mp, &mp->mnt_fslabel, de, &de->de_label, vp, &vp->v_label); } int mac_associate_vnode_extattr(struct mount *mp, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_associate_vnode_extattr"); MAC_CHECK(associate_vnode_extattr, mp, &mp->mnt_fslabel, vp, &vp->v_label); return (error); } void mac_associate_vnode_singlelabel(struct mount *mp, struct vnode *vp) { MAC_PERFORM(associate_vnode_singlelabel, mp, &mp->mnt_fslabel, vp, &vp->v_label); } int mac_create_vnode_extattr(struct ucred *cred, struct mount *mp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_create_vnode_extattr"); ASSERT_VOP_LOCKED(vp, "mac_create_vnode_extattr"); error = VOP_OPENEXTATTR(vp, cred, curthread); if (error == EOPNOTSUPP) { /* XXX: Optionally abort if transactions not supported. */ if (ea_warn_once == 0) { printf("Warning: transactions not supported " "in EA write.\n"); ea_warn_once = 1; } } else if (error) return (error); MAC_CHECK(create_vnode_extattr, cred, mp, &mp->mnt_fslabel, 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; /* XXX */ return (error); } static int mac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *intlabel) { int error; ASSERT_VOP_LOCKED(vp, "mac_setlabel_vnode_extattr"); error = VOP_OPENEXTATTR(vp, cred, curthread); if (error == EOPNOTSUPP) { /* XXX: Optionally abort if transactions not supported. */ if (ea_warn_once == 0) { printf("Warning: transactions not supported " "in EA write.\n"); ea_warn_once = 1; } } else if (error) return (error); MAC_CHECK(setlabel_vnode_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; /* XXX */ return (error); } void mac_execve_transition(struct ucred *old, struct ucred *new, struct vnode *vp) { ASSERT_VOP_LOCKED(vp, "mac_execve_transition"); MAC_PERFORM(execve_transition, old, new, vp, &vp->v_label); } int mac_execve_will_transition(struct ucred *old, struct vnode *vp) { int result; result = 0; MAC_BOOLEAN(execve_will_transition, ||, old, vp, &vp->v_label); return (result); } int mac_check_vnode_access(struct ucred *cred, struct vnode *vp, int flags) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_access"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_access, cred, vp, &vp->v_label, flags); return (error); } int mac_check_vnode_chdir(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_chdir"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_chdir, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_chroot(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_chroot"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_chroot, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_create(struct ucred *cred, struct vnode *dvp, struct componentname *cnp, struct vattr *vap) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_create"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_create, cred, dvp, &dvp->v_label, cnp, vap); return (error); } int mac_check_vnode_delete(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_delete"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_delete"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_delete, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp, acl_type_t type) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_deleteacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_deleteacl, cred, vp, &vp->v_label, type); return (error); } int mac_check_vnode_exec(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_exec"); if (!mac_enforce_process && !mac_enforce_fs) return (0); MAC_CHECK(check_vnode_exec, cred, vp, &vp->v_label); return (error); } int mac_check_vnode_getacl(struct ucred *cred, struct vnode *vp, acl_type_t type) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_getacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_getacl, cred, vp, &vp->v_label, type); return (error); } int mac_check_vnode_getextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_getextattr"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_getextattr, cred, vp, &vp->v_label, attrnamespace, name, uio); return (error); } int mac_check_vnode_link(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_link"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_link"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_link, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_lookup(struct ucred *cred, struct vnode *dvp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_lookup"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_lookup, cred, dvp, &dvp->v_label, cnp); return (error); } int mac_check_vnode_mmap(struct ucred *cred, struct vnode *vp, int prot) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mmap"); if (!mac_enforce_fs || !mac_enforce_vm) return (0); MAC_CHECK(check_vnode_mmap, cred, vp, &vp->v_label, prot); return (error); } void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot) { int result = *prot; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mmap_downgrade"); if (!mac_enforce_fs || !mac_enforce_vm) return; MAC_PERFORM(check_vnode_mmap_downgrade, cred, vp, &vp->v_label, &result); *prot = result; } int mac_check_vnode_mprotect(struct ucred *cred, struct vnode *vp, int prot) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mprotect"); if (!mac_enforce_fs || !mac_enforce_vm) return (0); MAC_CHECK(check_vnode_mprotect, cred, vp, &vp->v_label, prot); return (error); } int mac_check_vnode_open(struct ucred *cred, struct vnode *vp, mode_t acc_mode) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_open"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_open, cred, vp, &vp->v_label, acc_mode); return (error); } int mac_check_vnode_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_poll"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_poll, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_read"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_read, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_readdir(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_readdir"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_readdir, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_readlink(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_readlink"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_readlink, cred, vp, &vp->v_label); return (error); } static int mac_check_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *newlabel) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_relabel"); MAC_CHECK(check_vnode_relabel, cred, vp, &vp->v_label, newlabel); return (error); } int mac_check_vnode_rename_from(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_rename_from"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_rename_from"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_rename_from, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp, struct vnode *vp, int samedir, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_rename_to"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_rename_to"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_rename_to, cred, dvp, &dvp->v_label, vp, vp != NULL ? &vp->v_label : NULL, samedir, cnp); return (error); } int mac_check_vnode_revoke(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_revoke"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_revoke, cred, vp, &vp->v_label); return (error); } int mac_check_vnode_setacl(struct ucred *cred, struct vnode *vp, acl_type_t type, struct acl *acl) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setacl, cred, vp, &vp->v_label, type, acl); return (error); } int mac_check_vnode_setextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setextattr"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setextattr, cred, vp, &vp->v_label, attrnamespace, name, uio); return (error); } int mac_check_vnode_setflags(struct ucred *cred, struct vnode *vp, u_long flags) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setflags"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setflags, cred, vp, &vp->v_label, flags); return (error); } int mac_check_vnode_setmode(struct ucred *cred, struct vnode *vp, mode_t mode) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setmode"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setmode, cred, vp, &vp->v_label, mode); return (error); } int mac_check_vnode_setowner(struct ucred *cred, struct vnode *vp, uid_t uid, gid_t gid) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setowner"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setowner, cred, vp, &vp->v_label, uid, gid); return (error); } int mac_check_vnode_setutimes(struct ucred *cred, struct vnode *vp, struct timespec atime, struct timespec mtime) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setutimes"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setutimes, cred, vp, &vp->v_label, atime, mtime); return (error); } int mac_check_vnode_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_stat"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_stat, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_write"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_write, active_cred, file_cred, vp, &vp->v_label); return (error); } /* * 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. */ static void mac_cred_mmapped_drop_perms(struct thread *td, struct ucred *cred) { /* XXX freeze all other threads */ mac_cred_mmapped_drop_perms_recurse(td, cred, &td->td_proc->p_vmspace->vm_map); /* XXX allow other threads to continue */ } 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_cred_mmapped_drop_perms_recurse(struct thread *td, struct ucred *cred, struct vm_map *map) { struct vm_map_entry *vme; int result; vm_prot_t revokeperms; vm_object_t object; vm_ooffset_t offset; struct vnode *vp; if (!mac_mmap_revocation) return; vm_map_lock_read(map); for (vme = map->header.next; vme != &map->header; vme = vme->next) { if (vme->eflags & MAP_ENTRY_IS_SUB_MAP) { mac_cred_mmapped_drop_perms_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; while (object->backing_object != NULL) { object = object->backing_object; offset += object->backing_object_offset; } /* * 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, td); result = vme->max_protection; mac_check_vnode_mmap_downgrade(cred, vp, &result); VOP_UNLOCK(vp, 0, td); /* * 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)); vm_map_lock_upgrade(map); /* * 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); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); vm_object_page_clean(object, OFF_TO_IDX(offset), OFF_TO_IDX(offset + vme->end - vme->start + PAGE_MASK), OBJPC_SYNC); VOP_UNLOCK(vp, 0, td); 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_lock_downgrade(map); } vm_map_unlock_read(map); } /* * 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. */ static void mac_relabel_cred(struct ucred *cred, struct label *newlabel) { MAC_PERFORM(relabel_cred, cred, newlabel); } void mac_relabel_vnode(struct ucred *cred, struct vnode *vp, struct label *newlabel) { MAC_PERFORM(relabel_vnode, cred, vp, &vp->v_label, newlabel); } void mac_create_ifnet(struct ifnet *ifnet) { MAC_PERFORM(create_ifnet, ifnet, &ifnet->if_label); } void mac_create_bpfdesc(struct ucred *cred, struct bpf_d *bpf_d) { MAC_PERFORM(create_bpfdesc, cred, bpf_d, &bpf_d->bd_label); } void mac_create_socket(struct ucred *cred, struct socket *socket) { MAC_PERFORM(create_socket, cred, socket, &socket->so_label); } void mac_create_pipe(struct ucred *cred, struct pipe *pipe) { MAC_PERFORM(create_pipe, cred, pipe, pipe->pipe_label); } void mac_create_socket_from_socket(struct socket *oldsocket, struct socket *newsocket) { MAC_PERFORM(create_socket_from_socket, oldsocket, &oldsocket->so_label, newsocket, &newsocket->so_label); } static void mac_relabel_socket(struct ucred *cred, struct socket *socket, struct label *newlabel) { MAC_PERFORM(relabel_socket, cred, socket, &socket->so_label, newlabel); } static void mac_relabel_pipe(struct ucred *cred, struct pipe *pipe, struct label *newlabel) { MAC_PERFORM(relabel_pipe, cred, pipe, pipe->pipe_label, newlabel); } void mac_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct socket *socket) { MAC_PERFORM(set_socket_peer_from_mbuf, mbuf, &mbuf->m_pkthdr.label, socket, &socket->so_peerlabel); } void mac_set_socket_peer_from_socket(struct socket *oldsocket, struct socket *newsocket) { MAC_PERFORM(set_socket_peer_from_socket, oldsocket, &oldsocket->so_label, newsocket, &newsocket->so_peerlabel); } void mac_create_datagram_from_ipq(struct ipq *ipq, struct mbuf *datagram) { MAC_PERFORM(create_datagram_from_ipq, ipq, &ipq->ipq_label, datagram, &datagram->m_pkthdr.label); } void mac_create_fragment(struct mbuf *datagram, struct mbuf *fragment) { MAC_PERFORM(create_fragment, datagram, &datagram->m_pkthdr.label, fragment, &fragment->m_pkthdr.label); } void mac_create_ipq(struct mbuf *fragment, struct ipq *ipq) { MAC_PERFORM(create_ipq, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); } void mac_create_mbuf_from_mbuf(struct mbuf *oldmbuf, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_from_mbuf, oldmbuf, &oldmbuf->m_pkthdr.label, newmbuf, &newmbuf->m_pkthdr.label); } void mac_create_mbuf_from_bpfdesc(struct bpf_d *bpf_d, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_bpfdesc, bpf_d, &bpf_d->bd_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_linklayer(struct ifnet *ifnet, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_linklayer, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_from_ifnet(struct ifnet *ifnet, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_ifnet, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_multicast_encap(struct mbuf *oldmbuf, struct ifnet *ifnet, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_multicast_encap, oldmbuf, &oldmbuf->m_pkthdr.label, ifnet, &ifnet->if_label, newmbuf, &newmbuf->m_pkthdr.label); } void mac_create_mbuf_netlayer(struct mbuf *oldmbuf, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_netlayer, oldmbuf, &oldmbuf->m_pkthdr.label, newmbuf, &newmbuf->m_pkthdr.label); } int mac_fragment_match(struct mbuf *fragment, struct ipq *ipq) { int result; result = 1; MAC_BOOLEAN(fragment_match, &&, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); return (result); } void mac_update_ipq(struct mbuf *fragment, struct ipq *ipq) { MAC_PERFORM(update_ipq, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); } void mac_create_mbuf_from_socket(struct socket *socket, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_socket, socket, &socket->so_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mount(struct ucred *cred, struct mount *mp) { MAC_PERFORM(create_mount, cred, mp, &mp->mnt_mntlabel, &mp->mnt_fslabel); } void mac_create_root_mount(struct ucred *cred, struct mount *mp) { MAC_PERFORM(create_root_mount, cred, mp, &mp->mnt_mntlabel, &mp->mnt_fslabel); } int mac_check_bpfdesc_receive(struct bpf_d *bpf_d, struct ifnet *ifnet) { int error; if (!mac_enforce_network) return (0); MAC_CHECK(check_bpfdesc_receive, bpf_d, &bpf_d->bd_label, ifnet, &ifnet->if_label); return (error); } static int mac_check_cred_relabel(struct ucred *cred, struct label *newlabel) { int error; MAC_CHECK(check_cred_relabel, cred, newlabel); return (error); } int mac_check_cred_visible(struct ucred *u1, struct ucred *u2) { int error; if (!mac_enforce_process) return (0); MAC_CHECK(check_cred_visible, u1, u2); return (error); } int mac_check_ifnet_transmit(struct ifnet *ifnet, struct mbuf *mbuf) { int error; if (!mac_enforce_network) return (0); KASSERT(mbuf->m_flags & M_PKTHDR, ("packet has no pkthdr")); if (!(mbuf->m_pkthdr.label.l_flags & MAC_FLAG_INITIALIZED)) if_printf(ifnet, "not initialized\n"); MAC_CHECK(check_ifnet_transmit, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); return (error); } int mac_check_mount_stat(struct ucred *cred, struct mount *mount) { int error; if (!mac_enforce_fs) return (0); MAC_CHECK(check_mount_stat, cred, mount, &mount->mnt_mntlabel); return (error); } int mac_check_pipe_ioctl(struct ucred *cred, struct pipe *pipe, unsigned long cmd, void *data) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_ioctl, cred, pipe, pipe->pipe_label, cmd, data); return (error); } int mac_check_pipe_poll(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_poll, cred, pipe, pipe->pipe_label); return (error); } int mac_check_pipe_read(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_read, cred, pipe, pipe->pipe_label); return (error); } static int mac_check_pipe_relabel(struct ucred *cred, struct pipe *pipe, struct label *newlabel) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_relabel, cred, pipe, pipe->pipe_label, newlabel); return (error); } int mac_check_pipe_stat(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_stat, cred, pipe, pipe->pipe_label); return (error); } int mac_check_pipe_write(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_write, cred, pipe, pipe->pipe_label); return (error); } int mac_check_proc_debug(struct ucred *cred, struct proc *proc) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_debug, cred, proc); return (error); } int mac_check_proc_sched(struct ucred *cred, struct proc *proc) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_sched, cred, proc); return (error); } int mac_check_proc_signal(struct ucred *cred, struct proc *proc, int signum) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_signal, cred, proc, signum); return (error); } int mac_check_socket_bind(struct ucred *ucred, struct socket *socket, struct sockaddr *sockaddr) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_bind, ucred, socket, &socket->so_label, sockaddr); return (error); } int mac_check_socket_connect(struct ucred *cred, struct socket *socket, struct sockaddr *sockaddr) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_connect, cred, socket, &socket->so_label, sockaddr); return (error); } int mac_check_socket_deliver(struct socket *socket, struct mbuf *mbuf) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_deliver, socket, &socket->so_label, mbuf, &mbuf->m_pkthdr.label); return (error); } int mac_check_socket_listen(struct ucred *cred, struct socket *socket) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_listen, cred, socket, &socket->so_label); return (error); } int mac_check_socket_receive(struct ucred *cred, struct socket *so) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_receive, cred, so, &so->so_label); return (error); } static int mac_check_socket_relabel(struct ucred *cred, struct socket *socket, struct label *newlabel) { int error; MAC_CHECK(check_socket_relabel, cred, socket, &socket->so_label, newlabel); return (error); } int mac_check_socket_send(struct ucred *cred, struct socket *so) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_send, cred, so, &so->so_label); return (error); } int mac_check_socket_visible(struct ucred *cred, struct socket *socket) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_visible, cred, socket, &socket->so_label); return (error); } int mac_check_system_reboot(struct ucred *cred, int howto) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_system_reboot"); if (!mac_enforce_reboot) return (0); MAC_CHECK(check_system_reboot, cred, howto); return (error); } int mac_check_system_swapon(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_system_swapon"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_system_swapon, cred, vp, &vp->v_label); + return (error); +} + +int +mac_check_system_sysctl(struct ucred *cred, int *name, u_int namelen, + void *old, size_t *oldlenp, int inkernel, void *new, size_t newlen) +{ + int error; + + /* + * XXXMAC: We're very much like to assert the SYSCTL_LOCK here, + * but since it's not exported from kern_sysctl.c, we can't. + */ + if (!mac_enforce_sysctl) + return (0); + + MAC_CHECK(check_system_sysctl, cred, name, namelen, old, oldlenp, + inkernel, new, newlen); + return (error); } int mac_ioctl_ifnet_get(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet) { char *elements, *buffer; struct mac mac; int error; 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); error = mac_externalize_ifnet_label(&ifnet->if_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } int mac_ioctl_ifnet_set(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet) { struct label intlabel; struct mac mac; char *buffer; int error; 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); } mac_init_ifnet_label(&intlabel); error = mac_internalize_ifnet_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_ifnet_label(&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 = suser_cred(cred, 0); if (error) { mac_destroy_ifnet_label(&intlabel); return (error); } MAC_CHECK(check_ifnet_relabel, cred, ifnet, &ifnet->if_label, &intlabel); if (error) { mac_destroy_ifnet_label(&intlabel); return (error); } MAC_PERFORM(relabel_ifnet, cred, ifnet, &ifnet->if_label, &intlabel); mac_destroy_ifnet_label(&intlabel); return (0); } void mac_create_devfs_vnode(struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(create_devfs_vnode, de, &de->de_label, vp, &vp->v_label); } void mac_create_devfs_device(dev_t dev, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_device, dev, de, &de->de_label); } void mac_create_devfs_symlink(struct ucred *cred, struct devfs_dirent *dd, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_symlink, cred, dd, &dd->de_label, de, &de->de_label); } void mac_create_devfs_directory(char *dirname, int dirnamelen, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_directory, dirname, dirnamelen, de, &de->de_label); } int mac_setsockopt_label_set(struct ucred *cred, struct socket *so, struct mac *mac) { struct label intlabel; char *buffer; int 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); } mac_init_socket_label(&intlabel, M_WAITOK); error = mac_internalize_socket_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_socket_label(&intlabel); return (error); } mac_check_socket_relabel(cred, so, &intlabel); if (error) { mac_destroy_socket_label(&intlabel); return (error); } mac_relabel_socket(cred, so, &intlabel); mac_destroy_socket_label(&intlabel); return (0); } int mac_pipe_label_set(struct ucred *cred, struct pipe *pipe, struct label *label) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); error = mac_check_pipe_relabel(cred, pipe, label); if (error) return (error); mac_relabel_pipe(cred, pipe, label); return (0); } int mac_getsockopt_label_get(struct ucred *cred, struct socket *so, struct mac *mac) { char *buffer, *elements; int 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_externalize_socket_label(&so->so_label, elements, buffer, mac->m_buflen, M_WAITOK); 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_get(struct ucred *cred, struct socket *so, struct mac *mac) { char *elements, *buffer; int 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_externalize_socket_peer_label(&so->so_peerlabel, elements, buffer, mac->m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac->m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * 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_setlabel_vnode_extattr(ap->a_cred, vp, intlabel); if (error) return (error); mac_relabel_vnode(ap->a_cred, vp, intlabel); return (0); } static 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_check_vnode_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); } int __mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) { char *elements, *buffer; struct mac mac; struct proc *tproc; struct ucred *tcred; int error; error = copyin(SCARG(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_externalize_cred_label(&tcred->cr_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); crfree(tcred); return (error); } /* * MPSAFE */ int __mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) { char *elements, *buffer; struct mac mac; int error; error = copyin(uap->mac_p, &mac, sizeof(mac)); if (error) return (error); error = mac_check_structmac_consistent(&mac); if (error) return (error); elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL); if (error) { free(elements, M_MACTEMP); return (error); } buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); error = mac_externalize_cred_label(&td->td_ucred->cr_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) { struct ucred *newcred, *oldcred; struct label intlabel; struct proc *p; struct mac mac; char *buffer; int error; 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); } mac_init_cred_label(&intlabel); error = mac_internalize_cred_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_cred_label(&intlabel); return (error); } newcred = crget(); p = td->td_proc; PROC_LOCK(p); oldcred = p->p_ucred; error = mac_check_cred_relabel(oldcred, &intlabel); if (error) { PROC_UNLOCK(p); crfree(newcred); goto out; } setsugid(p); crcopy(newcred, oldcred); mac_relabel_cred(newcred, &intlabel); p->p_ucred = newcred; /* * Grab additional reference for use while revoking mmaps, prior * to releasing the proc lock and sharing the cred. */ crhold(newcred); PROC_UNLOCK(p); if (mac_enforce_vm) { mtx_lock(&Giant); mac_cred_mmapped_drop_perms(td, newcred); mtx_unlock(&Giant); } crfree(newcred); /* Free revocation reference. */ crfree(oldcred); out: mac_destroy_cred_label(&intlabel); return (error); } /* * MPSAFE */ int __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) { char *elements, *buffer; struct label intlabel; struct file *fp; struct mac mac; struct vnode *vp; struct pipe *pipe; 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); mtx_lock(&Giant); /* VFS */ error = fget(td, SCARG(uap, fd), &fp); if (error) goto out; label_type = fp->f_type; switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: vp = (struct vnode *)fp->f_data; mac_init_vnode_label(&intlabel); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); mac_copy_vnode_label(&vp->v_label, &intlabel); VOP_UNLOCK(vp, 0, td); break; case DTYPE_PIPE: pipe = (struct pipe *)fp->f_data; mac_init_pipe_label(&intlabel); PIPE_LOCK(pipe); mac_copy_pipe_label(pipe->pipe_label, &intlabel); PIPE_UNLOCK(pipe); break; default: error = EINVAL; fdrop(fp, td); goto out; } fdrop(fp, td); switch (label_type) { case DTYPE_FIFO: case DTYPE_VNODE: if (error == 0) error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); mac_destroy_vnode_label(&intlabel); break; case DTYPE_PIPE: error = mac_externalize_pipe_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); mac_destroy_pipe_label(&intlabel); break; default: panic("__mac_get_fd: corrupted label_type"); } if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_get_file(struct thread *td, struct __mac_get_file_args *uap) { char *elements, *buffer; struct nameidata nd; struct label intlabel; struct mac mac; int 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); mtx_lock(&Giant); /* VFS */ NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_USERSPACE, uap->path_p, td); error = namei(&nd); if (error) goto out; mac_init_vnode_label(&intlabel); mac_copy_vnode_label(&nd.ni_vp->v_label, &intlabel); error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); NDFREE(&nd, 0); mac_destroy_vnode_label(&intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_get_link(struct thread *td, struct __mac_get_link_args *uap) { char *elements, *buffer; struct nameidata nd; struct label intlabel; struct mac mac; int 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); mtx_lock(&Giant); /* VFS */ NDINIT(&nd, LOOKUP, LOCKLEAF | NOFOLLOW, UIO_USERSPACE, uap->path_p, td); error = namei(&nd); if (error) goto out; mac_init_vnode_label(&intlabel); mac_copy_vnode_label(&nd.ni_vp->v_label, &intlabel); error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); NDFREE(&nd, 0); mac_destroy_vnode_label(&intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) { struct label intlabel; struct pipe *pipe; 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); } mtx_lock(&Giant); /* VFS */ error = fget(td, SCARG(uap, fd), &fp); if (error) goto out; switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); if (error) { mac_destroy_vnode_label(&intlabel); break; } vp = (struct vnode *)fp->f_data; error = vn_start_write(vp, &mp, V_WAIT | PCATCH); if (error != 0) { mac_destroy_vnode_label(&intlabel); break; } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); error = vn_setlabel(vp, &intlabel, td->td_ucred); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); mac_destroy_vnode_label(&intlabel); break; case DTYPE_PIPE: mac_init_pipe_label(&intlabel); error = mac_internalize_pipe_label(&intlabel, buffer); if (error == 0) { pipe = (struct pipe *)fp->f_data; PIPE_LOCK(pipe); error = mac_pipe_label_set(td->td_ucred, pipe, &intlabel); PIPE_UNLOCK(pipe); } mac_destroy_pipe_label(&intlabel); break; default: error = EINVAL; } fdrop(fp, td); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_file(struct thread *td, struct __mac_set_file_args *uap) { struct label intlabel; struct nameidata nd; struct mount *mp; struct mac mac; char *buffer; int 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); } mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_vnode_label(&intlabel); return (error); } mtx_lock(&Giant); /* VFS */ 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); mtx_unlock(&Giant); /* VFS */ mac_destroy_vnode_label(&intlabel); return (error); } /* * MPSAFE */ int __mac_set_link(struct thread *td, struct __mac_set_link_args *uap) { struct label intlabel; struct nameidata nd; struct mount *mp; struct mac mac; char *buffer; int 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); } mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_vnode_label(&intlabel); return (error); } mtx_lock(&Giant); /* VFS */ 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); mtx_unlock(&Giant); /* VFS */ mac_destroy_vnode_label(&intlabel); return (error); } /* * MPSAFE */ int 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(SCARG(uap, policy), target, sizeof(target), NULL); if (error) return (error); error = ENOSYS; MAC_POLICY_LIST_BUSY(); 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, SCARG(uap, call), SCARG(uap, arg)); goto out; } } out: MAC_POLICY_LIST_UNBUSY(); return (error); } 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); #else /* !MAC */ int __mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) { return (ENOSYS); } int __mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) { return (ENOSYS); } int __mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) { return (ENOSYS); } int __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) { return (ENOSYS); } int __mac_get_file(struct thread *td, struct __mac_get_file_args *uap) { return (ENOSYS); } int __mac_get_link(struct thread *td, struct __mac_get_link_args *uap) { return (ENOSYS); } int __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) { return (ENOSYS); } int __mac_set_file(struct thread *td, struct __mac_set_file_args *uap) { return (ENOSYS); } int __mac_set_link(struct thread *td, struct __mac_set_link_args *uap) { return (ENOSYS); } int mac_syscall(struct thread *td, struct mac_syscall_args *uap) { return (ENOSYS); } #endif Index: head/sys/security/mac/mac_vfs.c =================================================================== --- head/sys/security/mac/mac_vfs.c (revision 106024) +++ head/sys/security/mac/mac_vfs.c (revision 106025) @@ -1,4010 +1,4038 @@ /*- * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson * Copyright (c) 2001 Ilmar S. Habibulin * Copyright (c) 2001, 2002 Networks Associates Technology, 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 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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. * * Framework for extensible kernel access control. Kernel and userland * interface to the framework, policy registration and composition. */ #include "opt_mac.h" #include "opt_devfs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MAC /* * Declare that the kernel provides MAC support, version 1. 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, 1); SYSCTL_DECL(_security); SYSCTL_NODE(_security, OID_AUTO, mac, CTLFLAG_RW, 0, "TrustedBSD MAC policy controls"); #if MAC_MAX_POLICIES > 32 #error "MAC_MAX_POLICIES too large" #endif static unsigned int mac_max_policies = MAC_MAX_POLICIES; static unsigned int mac_policy_offsets_free = (1 << MAC_MAX_POLICIES) - 1; SYSCTL_UINT(_security_mac, OID_AUTO, max_policies, CTLFLAG_RD, &mac_max_policies, 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; /* * Warn about EA transactions only the first time they happen. * Weak coherency, no locking. */ static int ea_warn_once = 0; static int mac_enforce_fs = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_fs, CTLFLAG_RW, &mac_enforce_fs, 0, "Enforce MAC policy on file system objects"); TUNABLE_INT("security.mac.enforce_fs", &mac_enforce_fs); static int mac_enforce_network = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_network, CTLFLAG_RW, &mac_enforce_network, 0, "Enforce MAC policy on network packets"); TUNABLE_INT("security.mac.enforce_network", &mac_enforce_network); static int mac_enforce_pipe = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_pipe, CTLFLAG_RW, &mac_enforce_pipe, 0, "Enforce MAC policy on pipe operations"); TUNABLE_INT("security.mac.enforce_pipe", &mac_enforce_pipe); static int mac_enforce_process = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_process, CTLFLAG_RW, &mac_enforce_process, 0, "Enforce MAC policy on inter-process operations"); TUNABLE_INT("security.mac.enforce_process", &mac_enforce_process); static int mac_enforce_reboot = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_reboot, CTLFLAG_RW, &mac_enforce_reboot, 0, "Enforce MAC policy for reboot operations"); TUNABLE_INT("security.mac.enforce_reboot", &mac_enforce_reboot); static int mac_enforce_socket = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_socket, CTLFLAG_RW, &mac_enforce_socket, 0, "Enforce MAC policy on socket operations"); TUNABLE_INT("security.mac.enforce_socket", &mac_enforce_socket); +static int mac_enforce_sysctl = 1; +SYSCTL_INT(_security_mac, OID_AUTO, enforce_sysctl, CTLFLAG_RW, + &mac_enforce_sysctl, 0, "Enforce MAC policy on sysctl operations"); +TUNABLE_INT("security.mac.enforce_sysctl", &mac_enforce_sysctl); + static int mac_enforce_vm = 1; SYSCTL_INT(_security_mac, OID_AUTO, enforce_vm, CTLFLAG_RW, &mac_enforce_vm, 0, "Enforce MAC policy on vm operations"); TUNABLE_INT("security.mac.enforce_vm", &mac_enforce_vm); static int mac_cache_fslabel_in_vnode = 1; SYSCTL_INT(_security_mac, OID_AUTO, cache_fslabel_in_vnode, CTLFLAG_RW, &mac_cache_fslabel_in_vnode, 0, "Cache mount fslabel in vnode"); TUNABLE_INT("security.mac.cache_fslabel_in_vnode", &mac_cache_fslabel_in_vnode); 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"); #ifdef MAC_DEBUG SYSCTL_NODE(_security_mac, OID_AUTO, debug, CTLFLAG_RW, 0, "TrustedBSD MAC debug info"); static int mac_debug_label_fallback = 0; SYSCTL_INT(_security_mac_debug, OID_AUTO, label_fallback, CTLFLAG_RW, &mac_debug_label_fallback, 0, "Filesystems should fall back to fs label" "when label is corrupted."); TUNABLE_INT("security.mac.debug_label_fallback", &mac_debug_label_fallback); SYSCTL_NODE(_security_mac_debug, OID_AUTO, counters, CTLFLAG_RW, 0, "TrustedBSD MAC object counters"); static unsigned int nmacmbufs, nmaccreds, nmacifnets, nmacbpfdescs, nmacsockets, nmacmounts, nmactemp, nmacvnodes, nmacdevfsdirents, nmacipqs, nmacpipes; SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, mbufs, CTLFLAG_RD, &nmacmbufs, 0, "number of mbufs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, creds, CTLFLAG_RD, &nmaccreds, 0, "number of ucreds in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ifnets, CTLFLAG_RD, &nmacifnets, 0, "number of ifnets in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ipqs, CTLFLAG_RD, &nmacipqs, 0, "number of ipqs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, bpfdescs, CTLFLAG_RD, &nmacbpfdescs, 0, "number of bpfdescs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, sockets, CTLFLAG_RD, &nmacsockets, 0, "number of sockets in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, pipes, CTLFLAG_RD, &nmacpipes, 0, "number of pipes in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, mounts, CTLFLAG_RD, &nmacmounts, 0, "number of mounts in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, temp, CTLFLAG_RD, &nmactemp, 0, "number of temporary labels in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, vnodes, CTLFLAG_RD, &nmacvnodes, 0, "number of vnodes in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, devfsdirents, CTLFLAG_RD, &nmacdevfsdirents, 0, "number of devfs dirents inuse"); #endif static int error_select(int error1, int error2); static int mac_policy_register(struct mac_policy_conf *mpc); static int mac_policy_unregister(struct mac_policy_conf *mpc); static void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot); static void mac_cred_mmapped_drop_perms_recurse(struct thread *td, struct ucred *cred, struct vm_map *map); static void mac_destroy_socket_label(struct label *label); static int mac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *intlabel); MALLOC_DEFINE(M_MACOPVEC, "macopvec", "MAC policy operation vector"); MALLOC_DEFINE(M_MACPIPELABEL, "macpipelabel", "MAC labels for pipes"); MALLOC_DEFINE(M_MACTEMP, "mactemp", "MAC temporary label storage"); /* * mac_policy_list_lock protects the consistency of 'mac_policy_list', * the linked list of attached policy modules. Read-only consumers of * the list must acquire a shared lock for the duration of their use; * writers must acquire an exclusive lock. Note that for compound * operations, locks should be held for the entire compound operation, * and that this is not yet done for relabel requests. */ static struct mtx mac_policy_list_lock; static LIST_HEAD(, mac_policy_conf) mac_policy_list; static int mac_policy_list_busy; #define MAC_POLICY_LIST_LOCKINIT() mtx_init(&mac_policy_list_lock, \ "mac_policy_list_lock", NULL, MTX_DEF); #define MAC_POLICY_LIST_LOCK() mtx_lock(&mac_policy_list_lock); #define MAC_POLICY_LIST_UNLOCK() mtx_unlock(&mac_policy_list_lock); #define MAC_POLICY_LIST_BUSY() do { \ MAC_POLICY_LIST_LOCK(); \ mac_policy_list_busy++; \ MAC_POLICY_LIST_UNLOCK(); \ } while (0) #define MAC_POLICY_LIST_UNBUSY() do { \ MAC_POLICY_LIST_LOCK(); \ mac_policy_list_busy--; \ if (mac_policy_list_busy < 0) \ panic("Extra mac_policy_list_busy--"); \ MAC_POLICY_LIST_UNLOCK(); \ } while (0) /* * MAC_CHECK performs the designated check by walking the policy * module list and checking with each as to how it feels about the * request. Note that it returns its value via 'error' in the scope * of the caller. */ #define MAC_CHECK(check, args...) do { \ struct mac_policy_conf *mpc; \ \ error = 0; \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## check != NULL) \ error = error_select( \ mpc->mpc_ops->mpo_ ## check (args), \ error); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) /* * MAC_BOOLEAN performs the designated boolean composition by walking * the module list, invoking each instance of the operation, and * combining the results using the passed C operator. Note that it * returns its value via 'result' in the scope of the caller, which * should be initialized by the caller in a meaningful way to get * a meaningful result. */ #define MAC_BOOLEAN(operation, composition, args...) do { \ struct mac_policy_conf *mpc; \ \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ result = result composition \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) #define MAC_EXTERNALIZE(type, label, elementlist, outbuf, \ outbuflen) do { \ char *curptr, *curptr_start, *element_name, *element_temp; \ size_t left, left_start, len; \ int claimed, first, first_start, ignorenotfound; \ \ error = 0; \ element_temp = elementlist; \ curptr = outbuf; \ curptr[0] = '\0'; \ left = outbuflen; \ first = 1; \ while ((element_name = strsep(&element_temp, ",")) != NULL) { \ curptr_start = curptr; \ left_start = left; \ first_start = first; \ if (element_name[0] == '?') { \ element_name++; \ ignorenotfound = 1; \ } else \ ignorenotfound = 0; \ claimed = 0; \ if (first) { \ len = snprintf(curptr, left, "%s/", \ element_name); \ first = 0; \ } else \ len = snprintf(curptr, left, ",%s/", \ element_name); \ if (len >= left) { \ error = EINVAL; /* XXXMAC: E2BIG */ \ break; \ } \ curptr += len; \ left -= len; \ \ MAC_CHECK(externalize_ ## type, label, element_name, \ curptr, left, &len, &claimed); \ if (error) \ break; \ if (claimed == 1) { \ if (len >= outbuflen) { \ error = EINVAL; /* XXXMAC: E2BIG */ \ break; \ } \ curptr += len; \ left -= len; \ } else if (claimed == 0 && ignorenotfound) { \ /* \ * Revert addition of the label element \ * name. \ */ \ curptr = curptr_start; \ *curptr = '\0'; \ left = left_start; \ first = first_start; \ } else { \ error = EINVAL; /* XXXMAC: ENOLABEL */ \ break; \ } \ } \ } while (0) #define MAC_INTERNALIZE(type, label, instring) do { \ char *element, *element_name, *element_data; \ int claimed; \ \ error = 0; \ element = instring; \ while ((element_name = strsep(&element, ",")) != NULL) { \ element_data = element_name; \ element_name = strsep(&element_data, "/"); \ if (element_data == NULL) { \ error = EINVAL; \ break; \ } \ claimed = 0; \ MAC_CHECK(internalize_ ## type, label, element_name, \ element_data, &claimed); \ if (error) \ break; \ if (claimed != 1) { \ /* XXXMAC: Another error here? */ \ error = EINVAL; \ break; \ } \ } \ } while (0) /* * MAC_PERFORM performs the designated operation by walking the policy * module list and invoking that operation for each policy. */ #define MAC_PERFORM(operation, args...) do { \ struct mac_policy_conf *mpc; \ \ MAC_POLICY_LIST_BUSY(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ MAC_POLICY_LIST_UNBUSY(); \ } while (0) /* * Initialize the MAC subsystem, including appropriate SMP locks. */ static void mac_init(void) { LIST_INIT(&mac_policy_list); MAC_POLICY_LIST_LOCKINIT(); } /* * 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; } /* * 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; 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: break; } return (error); } static int mac_policy_register(struct mac_policy_conf *mpc) { struct mac_policy_conf *tmpc; struct mac_policy_op_entry *mpe; int slot; MALLOC(mpc->mpc_ops, struct mac_policy_ops *, sizeof(*mpc->mpc_ops), M_MACOPVEC, M_WAITOK | M_ZERO); for (mpe = mpc->mpc_entries; mpe->mpe_constant != MAC_OP_LAST; mpe++) { switch (mpe->mpe_constant) { case MAC_OP_LAST: /* * Doesn't actually happen, but this allows checking * that all enumerated values are handled. */ break; case MAC_DESTROY: mpc->mpc_ops->mpo_destroy = mpe->mpe_function; break; case MAC_INIT: mpc->mpc_ops->mpo_init = mpe->mpe_function; break; case MAC_SYSCALL: mpc->mpc_ops->mpo_syscall = mpe->mpe_function; break; case MAC_INIT_BPFDESC_LABEL: mpc->mpc_ops->mpo_init_bpfdesc_label = mpe->mpe_function; break; case MAC_INIT_CRED_LABEL: mpc->mpc_ops->mpo_init_cred_label = mpe->mpe_function; break; case MAC_INIT_DEVFSDIRENT_LABEL: mpc->mpc_ops->mpo_init_devfsdirent_label = mpe->mpe_function; break; case MAC_INIT_IFNET_LABEL: mpc->mpc_ops->mpo_init_ifnet_label = mpe->mpe_function; break; case MAC_INIT_IPQ_LABEL: mpc->mpc_ops->mpo_init_ipq_label = mpe->mpe_function; break; case MAC_INIT_MBUF_LABEL: mpc->mpc_ops->mpo_init_mbuf_label = mpe->mpe_function; break; case MAC_INIT_MOUNT_LABEL: mpc->mpc_ops->mpo_init_mount_label = mpe->mpe_function; break; case MAC_INIT_MOUNT_FS_LABEL: mpc->mpc_ops->mpo_init_mount_fs_label = mpe->mpe_function; break; case MAC_INIT_PIPE_LABEL: mpc->mpc_ops->mpo_init_pipe_label = mpe->mpe_function; break; case MAC_INIT_SOCKET_LABEL: mpc->mpc_ops->mpo_init_socket_label = mpe->mpe_function; break; case MAC_INIT_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_init_socket_peer_label = mpe->mpe_function; break; case MAC_INIT_VNODE_LABEL: mpc->mpc_ops->mpo_init_vnode_label = mpe->mpe_function; break; case MAC_DESTROY_BPFDESC_LABEL: mpc->mpc_ops->mpo_destroy_bpfdesc_label = mpe->mpe_function; break; case MAC_DESTROY_CRED_LABEL: mpc->mpc_ops->mpo_destroy_cred_label = mpe->mpe_function; break; case MAC_DESTROY_DEVFSDIRENT_LABEL: mpc->mpc_ops->mpo_destroy_devfsdirent_label = mpe->mpe_function; break; case MAC_DESTROY_IFNET_LABEL: mpc->mpc_ops->mpo_destroy_ifnet_label = mpe->mpe_function; break; case MAC_DESTROY_IPQ_LABEL: mpc->mpc_ops->mpo_destroy_ipq_label = mpe->mpe_function; break; case MAC_DESTROY_MBUF_LABEL: mpc->mpc_ops->mpo_destroy_mbuf_label = mpe->mpe_function; break; case MAC_DESTROY_MOUNT_LABEL: mpc->mpc_ops->mpo_destroy_mount_label = mpe->mpe_function; break; case MAC_DESTROY_MOUNT_FS_LABEL: mpc->mpc_ops->mpo_destroy_mount_fs_label = mpe->mpe_function; break; case MAC_DESTROY_PIPE_LABEL: mpc->mpc_ops->mpo_destroy_pipe_label = mpe->mpe_function; break; case MAC_DESTROY_SOCKET_LABEL: mpc->mpc_ops->mpo_destroy_socket_label = mpe->mpe_function; break; case MAC_DESTROY_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_destroy_socket_peer_label = mpe->mpe_function; break; case MAC_DESTROY_VNODE_LABEL: mpc->mpc_ops->mpo_destroy_vnode_label = mpe->mpe_function; break; case MAC_COPY_PIPE_LABEL: mpc->mpc_ops->mpo_copy_pipe_label = mpe->mpe_function; break; case MAC_COPY_VNODE_LABEL: mpc->mpc_ops->mpo_copy_vnode_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_CRED_LABEL: mpc->mpc_ops->mpo_externalize_cred_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_IFNET_LABEL: mpc->mpc_ops->mpo_externalize_ifnet_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_PIPE_LABEL: mpc->mpc_ops->mpo_externalize_pipe_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_SOCKET_LABEL: mpc->mpc_ops->mpo_externalize_socket_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_SOCKET_PEER_LABEL: mpc->mpc_ops->mpo_externalize_socket_peer_label = mpe->mpe_function; break; case MAC_EXTERNALIZE_VNODE_LABEL: mpc->mpc_ops->mpo_externalize_vnode_label = mpe->mpe_function; break; case MAC_INTERNALIZE_CRED_LABEL: mpc->mpc_ops->mpo_internalize_cred_label = mpe->mpe_function; break; case MAC_INTERNALIZE_IFNET_LABEL: mpc->mpc_ops->mpo_internalize_ifnet_label = mpe->mpe_function; break; case MAC_INTERNALIZE_PIPE_LABEL: mpc->mpc_ops->mpo_internalize_pipe_label = mpe->mpe_function; break; case MAC_INTERNALIZE_SOCKET_LABEL: mpc->mpc_ops->mpo_internalize_socket_label = mpe->mpe_function; break; case MAC_INTERNALIZE_VNODE_LABEL: mpc->mpc_ops->mpo_internalize_vnode_label = mpe->mpe_function; break; case MAC_CREATE_DEVFS_DEVICE: mpc->mpc_ops->mpo_create_devfs_device = mpe->mpe_function; break; case MAC_CREATE_DEVFS_DIRECTORY: mpc->mpc_ops->mpo_create_devfs_directory = mpe->mpe_function; break; case MAC_CREATE_DEVFS_SYMLINK: mpc->mpc_ops->mpo_create_devfs_symlink = mpe->mpe_function; break; case MAC_CREATE_DEVFS_VNODE: mpc->mpc_ops->mpo_create_devfs_vnode = mpe->mpe_function; break; case MAC_CREATE_MOUNT: mpc->mpc_ops->mpo_create_mount = mpe->mpe_function; break; case MAC_CREATE_ROOT_MOUNT: mpc->mpc_ops->mpo_create_root_mount = mpe->mpe_function; break; case MAC_RELABEL_VNODE: mpc->mpc_ops->mpo_relabel_vnode = mpe->mpe_function; break; case MAC_UPDATE_DEVFSDIRENT: mpc->mpc_ops->mpo_update_devfsdirent = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_DEVFS: mpc->mpc_ops->mpo_associate_vnode_devfs = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_EXTATTR: mpc->mpc_ops->mpo_associate_vnode_extattr = mpe->mpe_function; break; case MAC_ASSOCIATE_VNODE_SINGLELABEL: mpc->mpc_ops->mpo_associate_vnode_singlelabel = mpe->mpe_function; break; case MAC_CREATE_VNODE_EXTATTR: mpc->mpc_ops->mpo_create_vnode_extattr = mpe->mpe_function; break; case MAC_SETLABEL_VNODE_EXTATTR: mpc->mpc_ops->mpo_setlabel_vnode_extattr = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_SOCKET: mpc->mpc_ops->mpo_create_mbuf_from_socket = mpe->mpe_function; break; case MAC_CREATE_PIPE: mpc->mpc_ops->mpo_create_pipe = mpe->mpe_function; break; case MAC_CREATE_SOCKET: mpc->mpc_ops->mpo_create_socket = mpe->mpe_function; break; case MAC_CREATE_SOCKET_FROM_SOCKET: mpc->mpc_ops->mpo_create_socket_from_socket = mpe->mpe_function; break; case MAC_RELABEL_PIPE: mpc->mpc_ops->mpo_relabel_pipe = mpe->mpe_function; break; case MAC_RELABEL_SOCKET: mpc->mpc_ops->mpo_relabel_socket = mpe->mpe_function; break; case MAC_SET_SOCKET_PEER_FROM_MBUF: mpc->mpc_ops->mpo_set_socket_peer_from_mbuf = mpe->mpe_function; break; case MAC_SET_SOCKET_PEER_FROM_SOCKET: mpc->mpc_ops->mpo_set_socket_peer_from_socket = mpe->mpe_function; break; case MAC_CREATE_BPFDESC: mpc->mpc_ops->mpo_create_bpfdesc = mpe->mpe_function; break; case MAC_CREATE_DATAGRAM_FROM_IPQ: mpc->mpc_ops->mpo_create_datagram_from_ipq = mpe->mpe_function; break; case MAC_CREATE_FRAGMENT: mpc->mpc_ops->mpo_create_fragment = mpe->mpe_function; break; case MAC_CREATE_IFNET: mpc->mpc_ops->mpo_create_ifnet = mpe->mpe_function; break; case MAC_CREATE_IPQ: mpc->mpc_ops->mpo_create_ipq = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_MBUF: mpc->mpc_ops->mpo_create_mbuf_from_mbuf = mpe->mpe_function; break; case MAC_CREATE_MBUF_LINKLAYER: mpc->mpc_ops->mpo_create_mbuf_linklayer = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_BPFDESC: mpc->mpc_ops->mpo_create_mbuf_from_bpfdesc = mpe->mpe_function; break; case MAC_CREATE_MBUF_FROM_IFNET: mpc->mpc_ops->mpo_create_mbuf_from_ifnet = mpe->mpe_function; break; case MAC_CREATE_MBUF_MULTICAST_ENCAP: mpc->mpc_ops->mpo_create_mbuf_multicast_encap = mpe->mpe_function; break; case MAC_CREATE_MBUF_NETLAYER: mpc->mpc_ops->mpo_create_mbuf_netlayer = mpe->mpe_function; break; case MAC_FRAGMENT_MATCH: mpc->mpc_ops->mpo_fragment_match = mpe->mpe_function; break; case MAC_RELABEL_IFNET: mpc->mpc_ops->mpo_relabel_ifnet = mpe->mpe_function; break; case MAC_UPDATE_IPQ: mpc->mpc_ops->mpo_update_ipq = mpe->mpe_function; break; case MAC_CREATE_CRED: mpc->mpc_ops->mpo_create_cred = mpe->mpe_function; break; case MAC_EXECVE_TRANSITION: mpc->mpc_ops->mpo_execve_transition = mpe->mpe_function; break; case MAC_EXECVE_WILL_TRANSITION: mpc->mpc_ops->mpo_execve_will_transition = mpe->mpe_function; break; case MAC_CREATE_PROC0: mpc->mpc_ops->mpo_create_proc0 = mpe->mpe_function; break; case MAC_CREATE_PROC1: mpc->mpc_ops->mpo_create_proc1 = mpe->mpe_function; break; case MAC_RELABEL_CRED: mpc->mpc_ops->mpo_relabel_cred = mpe->mpe_function; break; case MAC_THREAD_USERRET: mpc->mpc_ops->mpo_thread_userret = mpe->mpe_function; break; case MAC_CHECK_BPFDESC_RECEIVE: mpc->mpc_ops->mpo_check_bpfdesc_receive = mpe->mpe_function; break; case MAC_CHECK_CRED_RELABEL: mpc->mpc_ops->mpo_check_cred_relabel = mpe->mpe_function; break; case MAC_CHECK_CRED_VISIBLE: mpc->mpc_ops->mpo_check_cred_visible = mpe->mpe_function; break; case MAC_CHECK_IFNET_RELABEL: mpc->mpc_ops->mpo_check_ifnet_relabel = mpe->mpe_function; break; case MAC_CHECK_IFNET_TRANSMIT: mpc->mpc_ops->mpo_check_ifnet_transmit = mpe->mpe_function; break; case MAC_CHECK_MOUNT_STAT: mpc->mpc_ops->mpo_check_mount_stat = mpe->mpe_function; break; case MAC_CHECK_PIPE_IOCTL: mpc->mpc_ops->mpo_check_pipe_ioctl = mpe->mpe_function; break; case MAC_CHECK_PIPE_POLL: mpc->mpc_ops->mpo_check_pipe_poll = mpe->mpe_function; break; case MAC_CHECK_PIPE_READ: mpc->mpc_ops->mpo_check_pipe_read = mpe->mpe_function; break; case MAC_CHECK_PIPE_RELABEL: mpc->mpc_ops->mpo_check_pipe_relabel = mpe->mpe_function; break; case MAC_CHECK_PIPE_STAT: mpc->mpc_ops->mpo_check_pipe_stat = mpe->mpe_function; break; case MAC_CHECK_PIPE_WRITE: mpc->mpc_ops->mpo_check_pipe_write = mpe->mpe_function; break; case MAC_CHECK_PROC_DEBUG: mpc->mpc_ops->mpo_check_proc_debug = mpe->mpe_function; break; case MAC_CHECK_PROC_SCHED: mpc->mpc_ops->mpo_check_proc_sched = mpe->mpe_function; break; case MAC_CHECK_PROC_SIGNAL: mpc->mpc_ops->mpo_check_proc_signal = mpe->mpe_function; break; case MAC_CHECK_SOCKET_BIND: mpc->mpc_ops->mpo_check_socket_bind = mpe->mpe_function; break; case MAC_CHECK_SOCKET_CONNECT: mpc->mpc_ops->mpo_check_socket_connect = mpe->mpe_function; break; case MAC_CHECK_SOCKET_DELIVER: mpc->mpc_ops->mpo_check_socket_deliver = mpe->mpe_function; break; case MAC_CHECK_SOCKET_LISTEN: mpc->mpc_ops->mpo_check_socket_listen = mpe->mpe_function; break; case MAC_CHECK_SOCKET_RECEIVE: mpc->mpc_ops->mpo_check_socket_receive = mpe->mpe_function; break; case MAC_CHECK_SOCKET_RELABEL: mpc->mpc_ops->mpo_check_socket_relabel = mpe->mpe_function; break; case MAC_CHECK_SOCKET_SEND: mpc->mpc_ops->mpo_check_socket_send = mpe->mpe_function; break; case MAC_CHECK_SOCKET_VISIBLE: mpc->mpc_ops->mpo_check_socket_visible = mpe->mpe_function; break; case MAC_CHECK_SYSTEM_REBOOT: mpc->mpc_ops->mpo_check_system_reboot = mpe->mpe_function; break; case MAC_CHECK_SYSTEM_SWAPON: mpc->mpc_ops->mpo_check_system_swapon = mpe->mpe_function; break; + case MAC_CHECK_SYSTEM_SYSCTL: + mpc->mpc_ops->mpo_check_system_sysctl = + mpe->mpe_function; + break; case MAC_CHECK_VNODE_ACCESS: mpc->mpc_ops->mpo_check_vnode_access = mpe->mpe_function; break; case MAC_CHECK_VNODE_CHDIR: mpc->mpc_ops->mpo_check_vnode_chdir = mpe->mpe_function; break; case MAC_CHECK_VNODE_CHROOT: mpc->mpc_ops->mpo_check_vnode_chroot = mpe->mpe_function; break; case MAC_CHECK_VNODE_CREATE: mpc->mpc_ops->mpo_check_vnode_create = mpe->mpe_function; break; case MAC_CHECK_VNODE_DELETE: mpc->mpc_ops->mpo_check_vnode_delete = mpe->mpe_function; break; case MAC_CHECK_VNODE_DELETEACL: mpc->mpc_ops->mpo_check_vnode_deleteacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_EXEC: mpc->mpc_ops->mpo_check_vnode_exec = mpe->mpe_function; break; case MAC_CHECK_VNODE_GETACL: mpc->mpc_ops->mpo_check_vnode_getacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_GETEXTATTR: mpc->mpc_ops->mpo_check_vnode_getextattr = mpe->mpe_function; break; case MAC_CHECK_VNODE_LINK: mpc->mpc_ops->mpo_check_vnode_link = mpe->mpe_function; break; case MAC_CHECK_VNODE_LOOKUP: mpc->mpc_ops->mpo_check_vnode_lookup = mpe->mpe_function; break; case MAC_CHECK_VNODE_MMAP: mpc->mpc_ops->mpo_check_vnode_mmap = mpe->mpe_function; break; case MAC_CHECK_VNODE_MMAP_DOWNGRADE: mpc->mpc_ops->mpo_check_vnode_mmap_downgrade = mpe->mpe_function; break; case MAC_CHECK_VNODE_MPROTECT: mpc->mpc_ops->mpo_check_vnode_mprotect = mpe->mpe_function; break; case MAC_CHECK_VNODE_OPEN: mpc->mpc_ops->mpo_check_vnode_open = mpe->mpe_function; break; case MAC_CHECK_VNODE_POLL: mpc->mpc_ops->mpo_check_vnode_poll = mpe->mpe_function; break; case MAC_CHECK_VNODE_READ: mpc->mpc_ops->mpo_check_vnode_read = mpe->mpe_function; break; case MAC_CHECK_VNODE_READDIR: mpc->mpc_ops->mpo_check_vnode_readdir = mpe->mpe_function; break; case MAC_CHECK_VNODE_READLINK: mpc->mpc_ops->mpo_check_vnode_readlink = mpe->mpe_function; break; case MAC_CHECK_VNODE_RELABEL: mpc->mpc_ops->mpo_check_vnode_relabel = mpe->mpe_function; break; case MAC_CHECK_VNODE_RENAME_FROM: mpc->mpc_ops->mpo_check_vnode_rename_from = mpe->mpe_function; break; case MAC_CHECK_VNODE_RENAME_TO: mpc->mpc_ops->mpo_check_vnode_rename_to = mpe->mpe_function; break; case MAC_CHECK_VNODE_REVOKE: mpc->mpc_ops->mpo_check_vnode_revoke = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETACL: mpc->mpc_ops->mpo_check_vnode_setacl = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETEXTATTR: mpc->mpc_ops->mpo_check_vnode_setextattr = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETFLAGS: mpc->mpc_ops->mpo_check_vnode_setflags = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETMODE: mpc->mpc_ops->mpo_check_vnode_setmode = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETOWNER: mpc->mpc_ops->mpo_check_vnode_setowner = mpe->mpe_function; break; case MAC_CHECK_VNODE_SETUTIMES: mpc->mpc_ops->mpo_check_vnode_setutimes = mpe->mpe_function; break; case MAC_CHECK_VNODE_STAT: mpc->mpc_ops->mpo_check_vnode_stat = mpe->mpe_function; break; case MAC_CHECK_VNODE_WRITE: mpc->mpc_ops->mpo_check_vnode_write = mpe->mpe_function; break; /* default: printf("MAC policy `%s': unknown operation %d\n", mpc->mpc_name, mpe->mpe_constant); return (EINVAL); */ } } MAC_POLICY_LIST_LOCK(); if (mac_policy_list_busy > 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (EBUSY); } LIST_FOREACH(tmpc, &mac_policy_list, mpc_list) { if (strcmp(tmpc->mpc_name, mpc->mpc_name) == 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (EEXIST); } } if (mpc->mpc_field_off != NULL) { slot = ffs(mac_policy_offsets_free); if (slot == 0) { MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; return (ENOMEM); } slot--; mac_policy_offsets_free &= ~(1 << slot); *mpc->mpc_field_off = slot; } mpc->mpc_runtime_flags |= MPC_RUNTIME_FLAG_REGISTERED; LIST_INSERT_HEAD(&mac_policy_list, mpc, mpc_list); /* Per-policy initialization. */ if (mpc->mpc_ops->mpo_init != NULL) (*(mpc->mpc_ops->mpo_init))(mpc); MAC_POLICY_LIST_UNLOCK(); printf("Security policy loaded: %s (%s)\n", mpc->mpc_fullname, mpc->mpc_name); return (0); } 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_LIST_LOCK(); if ((mpc->mpc_runtime_flags & MPC_RUNTIME_FLAG_REGISTERED) == 0) { MAC_POLICY_LIST_UNLOCK(); return (0); } #if 0 /* * Don't allow unloading modules with private data. */ if (mpc->mpc_field_off != NULL) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } #endif /* * Only allow the unload to proceed if the module is unloadable * by its own definition. */ if ((mpc->mpc_loadtime_flags & MPC_LOADTIME_FLAG_UNLOADOK) == 0) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } /* * Right now, we EBUSY if the list is in use. In the future, * for reliability reasons, we might want to sleep and wakeup * later to try again. */ if (mac_policy_list_busy > 0) { MAC_POLICY_LIST_UNLOCK(); return (EBUSY); } if (mpc->mpc_ops->mpo_destroy != NULL) (*(mpc->mpc_ops->mpo_destroy))(mpc); LIST_REMOVE(mpc, mpc_list); MAC_POLICY_LIST_UNLOCK(); FREE(mpc->mpc_ops, M_MACOPVEC); mpc->mpc_ops = NULL; mpc->mpc_runtime_flags &= ~MPC_RUNTIME_FLAG_REGISTERED; printf("Security policy unload: %s (%s)\n", mpc->mpc_fullname, mpc->mpc_name); return (0); } /* * Define an error value precedence, and given two arguments, selects the * value with the higher precedence. */ static int 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); } static void mac_init_label(struct label *label) { bzero(label, sizeof(*label)); label->l_flags = MAC_FLAG_INITIALIZED; } static void mac_destroy_label(struct label *label) { KASSERT(label->l_flags & MAC_FLAG_INITIALIZED, ("destroying uninitialized label")); bzero(label, sizeof(*label)); /* implicit: label->l_flags &= ~MAC_FLAG_INITIALIZED; */ } void mac_init_bpfdesc(struct bpf_d *bpf_d) { mac_init_label(&bpf_d->bd_label); MAC_PERFORM(init_bpfdesc_label, &bpf_d->bd_label); #ifdef MAC_DEBUG atomic_add_int(&nmacbpfdescs, 1); #endif } static void mac_init_cred_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_cred_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmaccreds, 1); #endif } void mac_init_cred(struct ucred *cred) { mac_init_cred_label(&cred->cr_label); } void mac_init_devfsdirent(struct devfs_dirent *de) { mac_init_label(&de->de_label); MAC_PERFORM(init_devfsdirent_label, &de->de_label); #ifdef MAC_DEBUG atomic_add_int(&nmacdevfsdirents, 1); #endif } static void mac_init_ifnet_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_ifnet_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacifnets, 1); #endif } void mac_init_ifnet(struct ifnet *ifp) { mac_init_ifnet_label(&ifp->if_label); } void mac_init_ipq(struct ipq *ipq) { mac_init_label(&ipq->ipq_label); MAC_PERFORM(init_ipq_label, &ipq->ipq_label); #ifdef MAC_DEBUG atomic_add_int(&nmacipqs, 1); #endif } int mac_init_mbuf(struct mbuf *m, int flag) { int error; KASSERT(m->m_flags & M_PKTHDR, ("mac_init_mbuf on non-header mbuf")); mac_init_label(&m->m_pkthdr.label); MAC_CHECK(init_mbuf_label, &m->m_pkthdr.label, flag); if (error) { MAC_PERFORM(destroy_mbuf_label, &m->m_pkthdr.label); mac_destroy_label(&m->m_pkthdr.label); } #ifdef MAC_DEBUG if (error == 0) atomic_add_int(&nmacmbufs, 1); #endif return (error); } void mac_init_mount(struct mount *mp) { mac_init_label(&mp->mnt_mntlabel); mac_init_label(&mp->mnt_fslabel); MAC_PERFORM(init_mount_label, &mp->mnt_mntlabel); MAC_PERFORM(init_mount_fs_label, &mp->mnt_fslabel); #ifdef MAC_DEBUG atomic_add_int(&nmacmounts, 1); #endif } static void mac_init_pipe_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_pipe_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacpipes, 1); #endif } void mac_init_pipe(struct pipe *pipe) { struct label *label; label = malloc(sizeof(struct label), M_MACPIPELABEL, M_ZERO|M_WAITOK); pipe->pipe_label = label; pipe->pipe_peer->pipe_label = label; mac_init_pipe_label(label); } static int mac_init_socket_label(struct label *label, int flag) { int error; mac_init_label(label); MAC_CHECK(init_socket_label, label, flag); if (error) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); } #ifdef MAC_DEBUG if (error == 0) atomic_add_int(&nmacsockets, 1); #endif return (error); } static int mac_init_socket_peer_label(struct label *label, int flag) { int error; mac_init_label(label); MAC_CHECK(init_socket_peer_label, label, flag); if (error) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); } return (error); } int mac_init_socket(struct socket *socket, int flag) { int error; error = mac_init_socket_label(&socket->so_label, flag); if (error) return (error); error = mac_init_socket_peer_label(&socket->so_peerlabel, flag); if (error) mac_destroy_socket_label(&socket->so_label); return (error); } void mac_init_vnode_label(struct label *label) { mac_init_label(label); MAC_PERFORM(init_vnode_label, label); #ifdef MAC_DEBUG atomic_add_int(&nmacvnodes, 1); #endif } void mac_init_vnode(struct vnode *vp) { mac_init_vnode_label(&vp->v_label); } void mac_destroy_bpfdesc(struct bpf_d *bpf_d) { MAC_PERFORM(destroy_bpfdesc_label, &bpf_d->bd_label); mac_destroy_label(&bpf_d->bd_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacbpfdescs, 1); #endif } static void mac_destroy_cred_label(struct label *label) { MAC_PERFORM(destroy_cred_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmaccreds, 1); #endif } void mac_destroy_cred(struct ucred *cred) { mac_destroy_cred_label(&cred->cr_label); } void mac_destroy_devfsdirent(struct devfs_dirent *de) { MAC_PERFORM(destroy_devfsdirent_label, &de->de_label); mac_destroy_label(&de->de_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacdevfsdirents, 1); #endif } static void mac_destroy_ifnet_label(struct label *label) { MAC_PERFORM(destroy_ifnet_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacifnets, 1); #endif } void mac_destroy_ifnet(struct ifnet *ifp) { mac_destroy_ifnet_label(&ifp->if_label); } void mac_destroy_ipq(struct ipq *ipq) { MAC_PERFORM(destroy_ipq_label, &ipq->ipq_label); mac_destroy_label(&ipq->ipq_label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacipqs, 1); #endif } void mac_destroy_mbuf(struct mbuf *m) { MAC_PERFORM(destroy_mbuf_label, &m->m_pkthdr.label); mac_destroy_label(&m->m_pkthdr.label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacmbufs, 1); #endif } void mac_destroy_mount(struct mount *mp) { MAC_PERFORM(destroy_mount_label, &mp->mnt_mntlabel); MAC_PERFORM(destroy_mount_fs_label, &mp->mnt_fslabel); mac_destroy_label(&mp->mnt_fslabel); mac_destroy_label(&mp->mnt_mntlabel); #ifdef MAC_DEBUG atomic_subtract_int(&nmacmounts, 1); #endif } static void mac_destroy_pipe_label(struct label *label) { MAC_PERFORM(destroy_pipe_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacpipes, 1); #endif } void mac_destroy_pipe(struct pipe *pipe) { mac_destroy_pipe_label(pipe->pipe_label); free(pipe->pipe_label, M_MACPIPELABEL); } static void mac_destroy_socket_label(struct label *label) { MAC_PERFORM(destroy_socket_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacsockets, 1); #endif } static void mac_destroy_socket_peer_label(struct label *label) { MAC_PERFORM(destroy_socket_peer_label, label); mac_destroy_label(label); } void mac_destroy_socket(struct socket *socket) { mac_destroy_socket_label(&socket->so_label); mac_destroy_socket_peer_label(&socket->so_peerlabel); } void mac_destroy_vnode_label(struct label *label) { MAC_PERFORM(destroy_vnode_label, label); mac_destroy_label(label); #ifdef MAC_DEBUG atomic_subtract_int(&nmacvnodes, 1); #endif } void mac_destroy_vnode(struct vnode *vp) { mac_destroy_vnode_label(&vp->v_label); } static void mac_copy_pipe_label(struct label *src, struct label *dest) { MAC_PERFORM(copy_pipe_label, src, dest); } void mac_copy_vnode_label(struct label *src, struct label *dest) { MAC_PERFORM(copy_vnode_label, src, dest); } static int mac_check_structmac_consistent(struct mac *mac) { if (mac->m_buflen > MAC_MAX_LABEL_BUF_LEN) return (EINVAL); return (0); } static int mac_externalize_cred_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(cred_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_ifnet_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(ifnet_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_pipe_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(pipe_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_socket_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(socket_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_socket_peer_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(socket_peer_label, label, elements, outbuf, outbuflen); return (error); } static int mac_externalize_vnode_label(struct label *label, char *elements, char *outbuf, size_t outbuflen, int flags) { int error; MAC_EXTERNALIZE(vnode_label, label, elements, outbuf, outbuflen); return (error); } static int mac_internalize_cred_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(cred_label, label, string); return (error); } static int mac_internalize_ifnet_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(ifnet_label, label, string); return (error); } static int mac_internalize_pipe_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(pipe_label, label, string); return (error); } static int mac_internalize_socket_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(socket_label, label, string); return (error); } static int mac_internalize_vnode_label(struct label *label, char *string) { int error; MAC_INTERNALIZE(vnode_label, label, string); return (error); } /* * Initialize MAC label for the first kernel process, from which other * kernel processes and threads are spawned. */ void mac_create_proc0(struct ucred *cred) { MAC_PERFORM(create_proc0, cred); } /* * Initialize MAC label for the first userland process, from which other * userland processes and threads are spawned. */ void mac_create_proc1(struct ucred *cred) { MAC_PERFORM(create_proc1, cred); } void mac_thread_userret(struct thread *td) { MAC_PERFORM(thread_userret, td); } /* * 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_create_cred(struct ucred *parent_cred, struct ucred *child_cred) { MAC_PERFORM(create_cred, parent_cred, child_cred); } void mac_update_devfsdirent(struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(update_devfsdirent, de, &de->de_label, vp, &vp->v_label); } void mac_associate_vnode_devfs(struct mount *mp, struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(associate_vnode_devfs, mp, &mp->mnt_fslabel, de, &de->de_label, vp, &vp->v_label); } int mac_associate_vnode_extattr(struct mount *mp, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_associate_vnode_extattr"); MAC_CHECK(associate_vnode_extattr, mp, &mp->mnt_fslabel, vp, &vp->v_label); return (error); } void mac_associate_vnode_singlelabel(struct mount *mp, struct vnode *vp) { MAC_PERFORM(associate_vnode_singlelabel, mp, &mp->mnt_fslabel, vp, &vp->v_label); } int mac_create_vnode_extattr(struct ucred *cred, struct mount *mp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_create_vnode_extattr"); ASSERT_VOP_LOCKED(vp, "mac_create_vnode_extattr"); error = VOP_OPENEXTATTR(vp, cred, curthread); if (error == EOPNOTSUPP) { /* XXX: Optionally abort if transactions not supported. */ if (ea_warn_once == 0) { printf("Warning: transactions not supported " "in EA write.\n"); ea_warn_once = 1; } } else if (error) return (error); MAC_CHECK(create_vnode_extattr, cred, mp, &mp->mnt_fslabel, 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; /* XXX */ return (error); } static int mac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *intlabel) { int error; ASSERT_VOP_LOCKED(vp, "mac_setlabel_vnode_extattr"); error = VOP_OPENEXTATTR(vp, cred, curthread); if (error == EOPNOTSUPP) { /* XXX: Optionally abort if transactions not supported. */ if (ea_warn_once == 0) { printf("Warning: transactions not supported " "in EA write.\n"); ea_warn_once = 1; } } else if (error) return (error); MAC_CHECK(setlabel_vnode_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; /* XXX */ return (error); } void mac_execve_transition(struct ucred *old, struct ucred *new, struct vnode *vp) { ASSERT_VOP_LOCKED(vp, "mac_execve_transition"); MAC_PERFORM(execve_transition, old, new, vp, &vp->v_label); } int mac_execve_will_transition(struct ucred *old, struct vnode *vp) { int result; result = 0; MAC_BOOLEAN(execve_will_transition, ||, old, vp, &vp->v_label); return (result); } int mac_check_vnode_access(struct ucred *cred, struct vnode *vp, int flags) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_access"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_access, cred, vp, &vp->v_label, flags); return (error); } int mac_check_vnode_chdir(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_chdir"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_chdir, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_chroot(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_chroot"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_chroot, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_create(struct ucred *cred, struct vnode *dvp, struct componentname *cnp, struct vattr *vap) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_create"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_create, cred, dvp, &dvp->v_label, cnp, vap); return (error); } int mac_check_vnode_delete(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_delete"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_delete"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_delete, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp, acl_type_t type) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_deleteacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_deleteacl, cred, vp, &vp->v_label, type); return (error); } int mac_check_vnode_exec(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_exec"); if (!mac_enforce_process && !mac_enforce_fs) return (0); MAC_CHECK(check_vnode_exec, cred, vp, &vp->v_label); return (error); } int mac_check_vnode_getacl(struct ucred *cred, struct vnode *vp, acl_type_t type) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_getacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_getacl, cred, vp, &vp->v_label, type); return (error); } int mac_check_vnode_getextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_getextattr"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_getextattr, cred, vp, &vp->v_label, attrnamespace, name, uio); return (error); } int mac_check_vnode_link(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_link"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_link"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_link, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_lookup(struct ucred *cred, struct vnode *dvp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_lookup"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_lookup, cred, dvp, &dvp->v_label, cnp); return (error); } int mac_check_vnode_mmap(struct ucred *cred, struct vnode *vp, int prot) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mmap"); if (!mac_enforce_fs || !mac_enforce_vm) return (0); MAC_CHECK(check_vnode_mmap, cred, vp, &vp->v_label, prot); return (error); } void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot) { int result = *prot; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mmap_downgrade"); if (!mac_enforce_fs || !mac_enforce_vm) return; MAC_PERFORM(check_vnode_mmap_downgrade, cred, vp, &vp->v_label, &result); *prot = result; } int mac_check_vnode_mprotect(struct ucred *cred, struct vnode *vp, int prot) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_mprotect"); if (!mac_enforce_fs || !mac_enforce_vm) return (0); MAC_CHECK(check_vnode_mprotect, cred, vp, &vp->v_label, prot); return (error); } int mac_check_vnode_open(struct ucred *cred, struct vnode *vp, mode_t acc_mode) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_open"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_open, cred, vp, &vp->v_label, acc_mode); return (error); } int mac_check_vnode_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_poll"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_poll, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_read"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_read, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_readdir(struct ucred *cred, struct vnode *dvp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_readdir"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_readdir, cred, dvp, &dvp->v_label); return (error); } int mac_check_vnode_readlink(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_readlink"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_readlink, cred, vp, &vp->v_label); return (error); } static int mac_check_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *newlabel) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_relabel"); MAC_CHECK(check_vnode_relabel, cred, vp, &vp->v_label, newlabel); return (error); } int mac_check_vnode_rename_from(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_rename_from"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_rename_from"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_rename_from, cred, dvp, &dvp->v_label, vp, &vp->v_label, cnp); return (error); } int mac_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp, struct vnode *vp, int samedir, struct componentname *cnp) { int error; ASSERT_VOP_LOCKED(dvp, "mac_check_vnode_rename_to"); ASSERT_VOP_LOCKED(vp, "mac_check_vnode_rename_to"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_rename_to, cred, dvp, &dvp->v_label, vp, vp != NULL ? &vp->v_label : NULL, samedir, cnp); return (error); } int mac_check_vnode_revoke(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_revoke"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_revoke, cred, vp, &vp->v_label); return (error); } int mac_check_vnode_setacl(struct ucred *cred, struct vnode *vp, acl_type_t type, struct acl *acl) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setacl"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setacl, cred, vp, &vp->v_label, type, acl); return (error); } int mac_check_vnode_setextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setextattr"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setextattr, cred, vp, &vp->v_label, attrnamespace, name, uio); return (error); } int mac_check_vnode_setflags(struct ucred *cred, struct vnode *vp, u_long flags) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setflags"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setflags, cred, vp, &vp->v_label, flags); return (error); } int mac_check_vnode_setmode(struct ucred *cred, struct vnode *vp, mode_t mode) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setmode"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setmode, cred, vp, &vp->v_label, mode); return (error); } int mac_check_vnode_setowner(struct ucred *cred, struct vnode *vp, uid_t uid, gid_t gid) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setowner"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setowner, cred, vp, &vp->v_label, uid, gid); return (error); } int mac_check_vnode_setutimes(struct ucred *cred, struct vnode *vp, struct timespec atime, struct timespec mtime) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_setutimes"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_setutimes, cred, vp, &vp->v_label, atime, mtime); return (error); } int mac_check_vnode_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_stat"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_stat, active_cred, file_cred, vp, &vp->v_label); return (error); } int mac_check_vnode_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_vnode_write"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_vnode_write, active_cred, file_cred, vp, &vp->v_label); return (error); } /* * 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. */ static void mac_cred_mmapped_drop_perms(struct thread *td, struct ucred *cred) { /* XXX freeze all other threads */ mac_cred_mmapped_drop_perms_recurse(td, cred, &td->td_proc->p_vmspace->vm_map); /* XXX allow other threads to continue */ } 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_cred_mmapped_drop_perms_recurse(struct thread *td, struct ucred *cred, struct vm_map *map) { struct vm_map_entry *vme; int result; vm_prot_t revokeperms; vm_object_t object; vm_ooffset_t offset; struct vnode *vp; if (!mac_mmap_revocation) return; vm_map_lock_read(map); for (vme = map->header.next; vme != &map->header; vme = vme->next) { if (vme->eflags & MAP_ENTRY_IS_SUB_MAP) { mac_cred_mmapped_drop_perms_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; while (object->backing_object != NULL) { object = object->backing_object; offset += object->backing_object_offset; } /* * 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, td); result = vme->max_protection; mac_check_vnode_mmap_downgrade(cred, vp, &result); VOP_UNLOCK(vp, 0, td); /* * 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)); vm_map_lock_upgrade(map); /* * 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); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); vm_object_page_clean(object, OFF_TO_IDX(offset), OFF_TO_IDX(offset + vme->end - vme->start + PAGE_MASK), OBJPC_SYNC); VOP_UNLOCK(vp, 0, td); 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_lock_downgrade(map); } vm_map_unlock_read(map); } /* * 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. */ static void mac_relabel_cred(struct ucred *cred, struct label *newlabel) { MAC_PERFORM(relabel_cred, cred, newlabel); } void mac_relabel_vnode(struct ucred *cred, struct vnode *vp, struct label *newlabel) { MAC_PERFORM(relabel_vnode, cred, vp, &vp->v_label, newlabel); } void mac_create_ifnet(struct ifnet *ifnet) { MAC_PERFORM(create_ifnet, ifnet, &ifnet->if_label); } void mac_create_bpfdesc(struct ucred *cred, struct bpf_d *bpf_d) { MAC_PERFORM(create_bpfdesc, cred, bpf_d, &bpf_d->bd_label); } void mac_create_socket(struct ucred *cred, struct socket *socket) { MAC_PERFORM(create_socket, cred, socket, &socket->so_label); } void mac_create_pipe(struct ucred *cred, struct pipe *pipe) { MAC_PERFORM(create_pipe, cred, pipe, pipe->pipe_label); } void mac_create_socket_from_socket(struct socket *oldsocket, struct socket *newsocket) { MAC_PERFORM(create_socket_from_socket, oldsocket, &oldsocket->so_label, newsocket, &newsocket->so_label); } static void mac_relabel_socket(struct ucred *cred, struct socket *socket, struct label *newlabel) { MAC_PERFORM(relabel_socket, cred, socket, &socket->so_label, newlabel); } static void mac_relabel_pipe(struct ucred *cred, struct pipe *pipe, struct label *newlabel) { MAC_PERFORM(relabel_pipe, cred, pipe, pipe->pipe_label, newlabel); } void mac_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct socket *socket) { MAC_PERFORM(set_socket_peer_from_mbuf, mbuf, &mbuf->m_pkthdr.label, socket, &socket->so_peerlabel); } void mac_set_socket_peer_from_socket(struct socket *oldsocket, struct socket *newsocket) { MAC_PERFORM(set_socket_peer_from_socket, oldsocket, &oldsocket->so_label, newsocket, &newsocket->so_peerlabel); } void mac_create_datagram_from_ipq(struct ipq *ipq, struct mbuf *datagram) { MAC_PERFORM(create_datagram_from_ipq, ipq, &ipq->ipq_label, datagram, &datagram->m_pkthdr.label); } void mac_create_fragment(struct mbuf *datagram, struct mbuf *fragment) { MAC_PERFORM(create_fragment, datagram, &datagram->m_pkthdr.label, fragment, &fragment->m_pkthdr.label); } void mac_create_ipq(struct mbuf *fragment, struct ipq *ipq) { MAC_PERFORM(create_ipq, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); } void mac_create_mbuf_from_mbuf(struct mbuf *oldmbuf, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_from_mbuf, oldmbuf, &oldmbuf->m_pkthdr.label, newmbuf, &newmbuf->m_pkthdr.label); } void mac_create_mbuf_from_bpfdesc(struct bpf_d *bpf_d, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_bpfdesc, bpf_d, &bpf_d->bd_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_linklayer(struct ifnet *ifnet, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_linklayer, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_from_ifnet(struct ifnet *ifnet, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_ifnet, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mbuf_multicast_encap(struct mbuf *oldmbuf, struct ifnet *ifnet, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_multicast_encap, oldmbuf, &oldmbuf->m_pkthdr.label, ifnet, &ifnet->if_label, newmbuf, &newmbuf->m_pkthdr.label); } void mac_create_mbuf_netlayer(struct mbuf *oldmbuf, struct mbuf *newmbuf) { MAC_PERFORM(create_mbuf_netlayer, oldmbuf, &oldmbuf->m_pkthdr.label, newmbuf, &newmbuf->m_pkthdr.label); } int mac_fragment_match(struct mbuf *fragment, struct ipq *ipq) { int result; result = 1; MAC_BOOLEAN(fragment_match, &&, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); return (result); } void mac_update_ipq(struct mbuf *fragment, struct ipq *ipq) { MAC_PERFORM(update_ipq, fragment, &fragment->m_pkthdr.label, ipq, &ipq->ipq_label); } void mac_create_mbuf_from_socket(struct socket *socket, struct mbuf *mbuf) { MAC_PERFORM(create_mbuf_from_socket, socket, &socket->so_label, mbuf, &mbuf->m_pkthdr.label); } void mac_create_mount(struct ucred *cred, struct mount *mp) { MAC_PERFORM(create_mount, cred, mp, &mp->mnt_mntlabel, &mp->mnt_fslabel); } void mac_create_root_mount(struct ucred *cred, struct mount *mp) { MAC_PERFORM(create_root_mount, cred, mp, &mp->mnt_mntlabel, &mp->mnt_fslabel); } int mac_check_bpfdesc_receive(struct bpf_d *bpf_d, struct ifnet *ifnet) { int error; if (!mac_enforce_network) return (0); MAC_CHECK(check_bpfdesc_receive, bpf_d, &bpf_d->bd_label, ifnet, &ifnet->if_label); return (error); } static int mac_check_cred_relabel(struct ucred *cred, struct label *newlabel) { int error; MAC_CHECK(check_cred_relabel, cred, newlabel); return (error); } int mac_check_cred_visible(struct ucred *u1, struct ucred *u2) { int error; if (!mac_enforce_process) return (0); MAC_CHECK(check_cred_visible, u1, u2); return (error); } int mac_check_ifnet_transmit(struct ifnet *ifnet, struct mbuf *mbuf) { int error; if (!mac_enforce_network) return (0); KASSERT(mbuf->m_flags & M_PKTHDR, ("packet has no pkthdr")); if (!(mbuf->m_pkthdr.label.l_flags & MAC_FLAG_INITIALIZED)) if_printf(ifnet, "not initialized\n"); MAC_CHECK(check_ifnet_transmit, ifnet, &ifnet->if_label, mbuf, &mbuf->m_pkthdr.label); return (error); } int mac_check_mount_stat(struct ucred *cred, struct mount *mount) { int error; if (!mac_enforce_fs) return (0); MAC_CHECK(check_mount_stat, cred, mount, &mount->mnt_mntlabel); return (error); } int mac_check_pipe_ioctl(struct ucred *cred, struct pipe *pipe, unsigned long cmd, void *data) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_ioctl, cred, pipe, pipe->pipe_label, cmd, data); return (error); } int mac_check_pipe_poll(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_poll, cred, pipe, pipe->pipe_label); return (error); } int mac_check_pipe_read(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_read, cred, pipe, pipe->pipe_label); return (error); } static int mac_check_pipe_relabel(struct ucred *cred, struct pipe *pipe, struct label *newlabel) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_relabel, cred, pipe, pipe->pipe_label, newlabel); return (error); } int mac_check_pipe_stat(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_stat, cred, pipe, pipe->pipe_label); return (error); } int mac_check_pipe_write(struct ucred *cred, struct pipe *pipe) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); if (!mac_enforce_pipe) return (0); MAC_CHECK(check_pipe_write, cred, pipe, pipe->pipe_label); return (error); } int mac_check_proc_debug(struct ucred *cred, struct proc *proc) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_debug, cred, proc); return (error); } int mac_check_proc_sched(struct ucred *cred, struct proc *proc) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_sched, cred, proc); return (error); } int mac_check_proc_signal(struct ucred *cred, struct proc *proc, int signum) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); if (!mac_enforce_process) return (0); MAC_CHECK(check_proc_signal, cred, proc, signum); return (error); } int mac_check_socket_bind(struct ucred *ucred, struct socket *socket, struct sockaddr *sockaddr) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_bind, ucred, socket, &socket->so_label, sockaddr); return (error); } int mac_check_socket_connect(struct ucred *cred, struct socket *socket, struct sockaddr *sockaddr) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_connect, cred, socket, &socket->so_label, sockaddr); return (error); } int mac_check_socket_deliver(struct socket *socket, struct mbuf *mbuf) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_deliver, socket, &socket->so_label, mbuf, &mbuf->m_pkthdr.label); return (error); } int mac_check_socket_listen(struct ucred *cred, struct socket *socket) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_listen, cred, socket, &socket->so_label); return (error); } int mac_check_socket_receive(struct ucred *cred, struct socket *so) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_receive, cred, so, &so->so_label); return (error); } static int mac_check_socket_relabel(struct ucred *cred, struct socket *socket, struct label *newlabel) { int error; MAC_CHECK(check_socket_relabel, cred, socket, &socket->so_label, newlabel); return (error); } int mac_check_socket_send(struct ucred *cred, struct socket *so) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_send, cred, so, &so->so_label); return (error); } int mac_check_socket_visible(struct ucred *cred, struct socket *socket) { int error; if (!mac_enforce_socket) return (0); MAC_CHECK(check_socket_visible, cred, socket, &socket->so_label); return (error); } int mac_check_system_reboot(struct ucred *cred, int howto) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_system_reboot"); if (!mac_enforce_reboot) return (0); MAC_CHECK(check_system_reboot, cred, howto); return (error); } int mac_check_system_swapon(struct ucred *cred, struct vnode *vp) { int error; ASSERT_VOP_LOCKED(vp, "mac_check_system_swapon"); if (!mac_enforce_fs) return (0); MAC_CHECK(check_system_swapon, cred, vp, &vp->v_label); + return (error); +} + +int +mac_check_system_sysctl(struct ucred *cred, int *name, u_int namelen, + void *old, size_t *oldlenp, int inkernel, void *new, size_t newlen) +{ + int error; + + /* + * XXXMAC: We're very much like to assert the SYSCTL_LOCK here, + * but since it's not exported from kern_sysctl.c, we can't. + */ + if (!mac_enforce_sysctl) + return (0); + + MAC_CHECK(check_system_sysctl, cred, name, namelen, old, oldlenp, + inkernel, new, newlen); + return (error); } int mac_ioctl_ifnet_get(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet) { char *elements, *buffer; struct mac mac; int error; 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); error = mac_externalize_ifnet_label(&ifnet->if_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } int mac_ioctl_ifnet_set(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet) { struct label intlabel; struct mac mac; char *buffer; int error; 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); } mac_init_ifnet_label(&intlabel); error = mac_internalize_ifnet_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_ifnet_label(&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 = suser_cred(cred, 0); if (error) { mac_destroy_ifnet_label(&intlabel); return (error); } MAC_CHECK(check_ifnet_relabel, cred, ifnet, &ifnet->if_label, &intlabel); if (error) { mac_destroy_ifnet_label(&intlabel); return (error); } MAC_PERFORM(relabel_ifnet, cred, ifnet, &ifnet->if_label, &intlabel); mac_destroy_ifnet_label(&intlabel); return (0); } void mac_create_devfs_vnode(struct devfs_dirent *de, struct vnode *vp) { MAC_PERFORM(create_devfs_vnode, de, &de->de_label, vp, &vp->v_label); } void mac_create_devfs_device(dev_t dev, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_device, dev, de, &de->de_label); } void mac_create_devfs_symlink(struct ucred *cred, struct devfs_dirent *dd, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_symlink, cred, dd, &dd->de_label, de, &de->de_label); } void mac_create_devfs_directory(char *dirname, int dirnamelen, struct devfs_dirent *de) { MAC_PERFORM(create_devfs_directory, dirname, dirnamelen, de, &de->de_label); } int mac_setsockopt_label_set(struct ucred *cred, struct socket *so, struct mac *mac) { struct label intlabel; char *buffer; int 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); } mac_init_socket_label(&intlabel, M_WAITOK); error = mac_internalize_socket_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_socket_label(&intlabel); return (error); } mac_check_socket_relabel(cred, so, &intlabel); if (error) { mac_destroy_socket_label(&intlabel); return (error); } mac_relabel_socket(cred, so, &intlabel); mac_destroy_socket_label(&intlabel); return (0); } int mac_pipe_label_set(struct ucred *cred, struct pipe *pipe, struct label *label) { int error; PIPE_LOCK_ASSERT(pipe, MA_OWNED); error = mac_check_pipe_relabel(cred, pipe, label); if (error) return (error); mac_relabel_pipe(cred, pipe, label); return (0); } int mac_getsockopt_label_get(struct ucred *cred, struct socket *so, struct mac *mac) { char *buffer, *elements; int 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_externalize_socket_label(&so->so_label, elements, buffer, mac->m_buflen, M_WAITOK); 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_get(struct ucred *cred, struct socket *so, struct mac *mac) { char *elements, *buffer; int 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_externalize_socket_peer_label(&so->so_peerlabel, elements, buffer, mac->m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac->m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * 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_setlabel_vnode_extattr(ap->a_cred, vp, intlabel); if (error) return (error); mac_relabel_vnode(ap->a_cred, vp, intlabel); return (0); } static 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_check_vnode_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); } int __mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) { char *elements, *buffer; struct mac mac; struct proc *tproc; struct ucred *tcred; int error; error = copyin(SCARG(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_externalize_cred_label(&tcred->cr_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); crfree(tcred); return (error); } /* * MPSAFE */ int __mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) { char *elements, *buffer; struct mac mac; int error; error = copyin(uap->mac_p, &mac, sizeof(mac)); if (error) return (error); error = mac_check_structmac_consistent(&mac); if (error) return (error); elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL); if (error) { free(elements, M_MACTEMP); return (error); } buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); error = mac_externalize_cred_label(&td->td_ucred->cr_label, elements, buffer, mac.m_buflen, M_WAITOK); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) { struct ucred *newcred, *oldcred; struct label intlabel; struct proc *p; struct mac mac; char *buffer; int error; 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); } mac_init_cred_label(&intlabel); error = mac_internalize_cred_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_cred_label(&intlabel); return (error); } newcred = crget(); p = td->td_proc; PROC_LOCK(p); oldcred = p->p_ucred; error = mac_check_cred_relabel(oldcred, &intlabel); if (error) { PROC_UNLOCK(p); crfree(newcred); goto out; } setsugid(p); crcopy(newcred, oldcred); mac_relabel_cred(newcred, &intlabel); p->p_ucred = newcred; /* * Grab additional reference for use while revoking mmaps, prior * to releasing the proc lock and sharing the cred. */ crhold(newcred); PROC_UNLOCK(p); if (mac_enforce_vm) { mtx_lock(&Giant); mac_cred_mmapped_drop_perms(td, newcred); mtx_unlock(&Giant); } crfree(newcred); /* Free revocation reference. */ crfree(oldcred); out: mac_destroy_cred_label(&intlabel); return (error); } /* * MPSAFE */ int __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) { char *elements, *buffer; struct label intlabel; struct file *fp; struct mac mac; struct vnode *vp; struct pipe *pipe; 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); mtx_lock(&Giant); /* VFS */ error = fget(td, SCARG(uap, fd), &fp); if (error) goto out; label_type = fp->f_type; switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: vp = (struct vnode *)fp->f_data; mac_init_vnode_label(&intlabel); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); mac_copy_vnode_label(&vp->v_label, &intlabel); VOP_UNLOCK(vp, 0, td); break; case DTYPE_PIPE: pipe = (struct pipe *)fp->f_data; mac_init_pipe_label(&intlabel); PIPE_LOCK(pipe); mac_copy_pipe_label(pipe->pipe_label, &intlabel); PIPE_UNLOCK(pipe); break; default: error = EINVAL; fdrop(fp, td); goto out; } fdrop(fp, td); switch (label_type) { case DTYPE_FIFO: case DTYPE_VNODE: if (error == 0) error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); mac_destroy_vnode_label(&intlabel); break; case DTYPE_PIPE: error = mac_externalize_pipe_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); mac_destroy_pipe_label(&intlabel); break; default: panic("__mac_get_fd: corrupted label_type"); } if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_get_file(struct thread *td, struct __mac_get_file_args *uap) { char *elements, *buffer; struct nameidata nd; struct label intlabel; struct mac mac; int 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); mtx_lock(&Giant); /* VFS */ NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_USERSPACE, uap->path_p, td); error = namei(&nd); if (error) goto out; mac_init_vnode_label(&intlabel); mac_copy_vnode_label(&nd.ni_vp->v_label, &intlabel); error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); NDFREE(&nd, 0); mac_destroy_vnode_label(&intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_get_link(struct thread *td, struct __mac_get_link_args *uap) { char *elements, *buffer; struct nameidata nd; struct label intlabel; struct mac mac; int 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); mtx_lock(&Giant); /* VFS */ NDINIT(&nd, LOOKUP, LOCKLEAF | NOFOLLOW, UIO_USERSPACE, uap->path_p, td); error = namei(&nd); if (error) goto out; mac_init_vnode_label(&intlabel); mac_copy_vnode_label(&nd.ni_vp->v_label, &intlabel); error = mac_externalize_vnode_label(&intlabel, elements, buffer, mac.m_buflen, M_WAITOK); NDFREE(&nd, 0); mac_destroy_vnode_label(&intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) { struct label intlabel; struct pipe *pipe; 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); } mtx_lock(&Giant); /* VFS */ error = fget(td, SCARG(uap, fd), &fp); if (error) goto out; switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); if (error) { mac_destroy_vnode_label(&intlabel); break; } vp = (struct vnode *)fp->f_data; error = vn_start_write(vp, &mp, V_WAIT | PCATCH); if (error != 0) { mac_destroy_vnode_label(&intlabel); break; } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); error = vn_setlabel(vp, &intlabel, td->td_ucred); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); mac_destroy_vnode_label(&intlabel); break; case DTYPE_PIPE: mac_init_pipe_label(&intlabel); error = mac_internalize_pipe_label(&intlabel, buffer); if (error == 0) { pipe = (struct pipe *)fp->f_data; PIPE_LOCK(pipe); error = mac_pipe_label_set(td->td_ucred, pipe, &intlabel); PIPE_UNLOCK(pipe); } mac_destroy_pipe_label(&intlabel); break; default: error = EINVAL; } fdrop(fp, td); out: mtx_unlock(&Giant); /* VFS */ free(buffer, M_MACTEMP); return (error); } /* * MPSAFE */ int __mac_set_file(struct thread *td, struct __mac_set_file_args *uap) { struct label intlabel; struct nameidata nd; struct mount *mp; struct mac mac; char *buffer; int 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); } mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_vnode_label(&intlabel); return (error); } mtx_lock(&Giant); /* VFS */ 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); mtx_unlock(&Giant); /* VFS */ mac_destroy_vnode_label(&intlabel); return (error); } /* * MPSAFE */ int __mac_set_link(struct thread *td, struct __mac_set_link_args *uap) { struct label intlabel; struct nameidata nd; struct mount *mp; struct mac mac; char *buffer; int 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); } mac_init_vnode_label(&intlabel); error = mac_internalize_vnode_label(&intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_destroy_vnode_label(&intlabel); return (error); } mtx_lock(&Giant); /* VFS */ 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); mtx_unlock(&Giant); /* VFS */ mac_destroy_vnode_label(&intlabel); return (error); } /* * MPSAFE */ int 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(SCARG(uap, policy), target, sizeof(target), NULL); if (error) return (error); error = ENOSYS; MAC_POLICY_LIST_BUSY(); 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, SCARG(uap, call), SCARG(uap, arg)); goto out; } } out: MAC_POLICY_LIST_UNBUSY(); return (error); } 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); #else /* !MAC */ int __mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) { return (ENOSYS); } int __mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) { return (ENOSYS); } int __mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) { return (ENOSYS); } int __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) { return (ENOSYS); } int __mac_get_file(struct thread *td, struct __mac_get_file_args *uap) { return (ENOSYS); } int __mac_get_link(struct thread *td, struct __mac_get_link_args *uap) { return (ENOSYS); } int __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) { return (ENOSYS); } int __mac_set_file(struct thread *td, struct __mac_set_file_args *uap) { return (ENOSYS); } int __mac_set_link(struct thread *td, struct __mac_set_link_args *uap) { return (ENOSYS); } int mac_syscall(struct thread *td, struct mac_syscall_args *uap) { return (ENOSYS); } #endif Index: head/sys/sys/mac.h =================================================================== --- head/sys/sys/mac.h (revision 106024) +++ head/sys/sys/mac.h (revision 106025) @@ -1,375 +1,378 @@ /*- * Copyright (c) 1999, 2000, 2001, 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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$ */ /* * Userland/kernel interface for Mandatory Access Control. * * The POSIX.1e implementation page may be reached at: * http://www.trustedbsd.org/ */ #ifndef _SYS_MAC_H #define _SYS_MAC_H #include #ifndef _POSIX_MAC #define _POSIX_MAC #endif /* * XXXMAC: The single MAC extended attribute will be deprecated once * compound EA writes on a single target file can be performed cleanly * with UFS2. */ #define FREEBSD_MAC_EXTATTR_NAME "freebsd.mac" #define FREEBSD_MAC_EXTATTR_NAMESPACE EXTATTR_NAMESPACE_SYSTEM /* * MAC framework-related constants and limits. */ #define MAC_MAX_POLICY_NAME 32 #define MAC_MAX_LABEL_ELEMENT_NAME 32 #define MAC_MAX_LABEL_ELEMENT_DATA 4096 #define MAC_MAX_LABEL_BUF_LEN 8192 struct mac { size_t m_buflen; char *m_string; }; typedef struct mac *mac_t; #ifndef _KERNEL /* * Location of the userland MAC framework configuration file. mac.conf * binds policy names to shared libraries that understand those policies, * as well as setting defaults for MAC-aware applications. */ #define MAC_CONFFILE "/etc/mac.conf" /* * Extended non-POSIX.1e interfaces that offer additional services * available from the userland and kernel MAC frameworks. */ int mac_free(mac_t _label); int mac_from_text(mac_t *_label, const char *_text); int mac_get_fd(int _fd, mac_t _label); int mac_get_file(const char *_path, mac_t _label); int mac_get_link(const char *_path, mac_t _label); int mac_get_pid(pid_t _pid, mac_t _label); int mac_get_proc(mac_t _label); int mac_is_present(const char *_policyname); int mac_prepare(mac_t *_label, char *_elements); int mac_prepare_file_label(mac_t *_label); int mac_prepare_ifnet_label(mac_t *_label); int mac_prepare_process_label(mac_t *_label); int mac_set_fd(int _fildes, const mac_t _label); int mac_set_file(const char *_path, mac_t _label); int mac_set_link(const char *_path, mac_t _label); int mac_set_proc(const mac_t _label); int mac_syscall(const char *_policyname, int _call, void *_arg); int mac_to_text(mac_t mac, char **_text); #endif /* !_KERNEL */ /* * XXXMAC: For compatibility until the labels on disk are changed. We * will enable the definitions in various policy include files once * these can be disabled. */ #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]; }; struct mac_biba { int mb_flags; struct mac_biba_element mb_single; struct mac_biba_element mb_rangelow, mb_rangehigh; }; #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]; }; struct mac_mls { int mm_flags; struct mac_mls_element mm_single; struct mac_mls_element mm_rangelow, mm_rangehigh; }; struct mac_sebsd { uint32_t ms_psid; }; struct oldmac { int m_macflags; struct mac_biba m_biba; struct mac_mls m_mls; struct mac_sebsd m_sebsd; }; #ifdef _KERNEL /* * Kernel functions to manage and evaluate labels. */ struct bpf_d; struct componentname; struct devfs_dirent; struct ifnet; struct ifreq; struct ipq; struct mbuf; struct mount; struct proc; struct sockaddr; struct socket; struct pipe; struct thread; struct timespec; struct ucred; struct uio; struct vattr; struct vnode; #include /* XXX acl_type_t */ struct vop_refreshlabel_args; struct vop_setlabel_args; /* * Label operations. */ void mac_init_bpfdesc(struct bpf_d *); void mac_init_cred(struct ucred *); void mac_init_devfsdirent(struct devfs_dirent *); void mac_init_ifnet(struct ifnet *); void mac_init_ipq(struct ipq *); int mac_init_socket(struct socket *, int flag); void mac_init_pipe(struct pipe *); int mac_init_mbuf(struct mbuf *m, int flag); void mac_init_mount(struct mount *); void mac_init_vnode(struct vnode *); void mac_init_vnode_label(struct label *); void mac_copy_vnode_label(struct label *, struct label *label); void mac_destroy_bpfdesc(struct bpf_d *); void mac_destroy_cred(struct ucred *); void mac_destroy_devfsdirent(struct devfs_dirent *); void mac_destroy_ifnet(struct ifnet *); void mac_destroy_ipq(struct ipq *); void mac_destroy_socket(struct socket *); void mac_destroy_pipe(struct pipe *); void mac_destroy_mbuf(struct mbuf *); void mac_destroy_mount(struct mount *); void mac_destroy_vnode(struct vnode *); void mac_destroy_vnode_label(struct label *); /* * Labeling event operations: file system objects, and things that * look a lot like file system objects. */ void mac_associate_vnode_devfs(struct mount *mp, struct devfs_dirent *de, struct vnode *vp); int mac_associate_vnode_extattr(struct mount *mp, struct vnode *vp); void mac_associate_vnode_singlelabel(struct mount *mp, struct vnode *vp); void mac_create_devfs_device(dev_t dev, struct devfs_dirent *de); void mac_create_devfs_directory(char *dirname, int dirnamelen, struct devfs_dirent *de); void mac_create_devfs_symlink(struct ucred *cred, struct devfs_dirent *dd, struct devfs_dirent *de); void mac_create_devfs_vnode(struct devfs_dirent *de, struct vnode *vp); int mac_create_vnode_extattr(struct ucred *cred, struct mount *mp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp); void mac_create_mount(struct ucred *cred, struct mount *mp); void mac_create_root_mount(struct ucred *cred, struct mount *mp); void mac_relabel_vnode(struct ucred *cred, struct vnode *vp, struct label *newlabel); void mac_update_devfsdirent(struct devfs_dirent *de, struct vnode *vp); /* * Labeling event operations: IPC objects. */ void mac_create_mbuf_from_socket(struct socket *so, struct mbuf *m); void mac_create_socket(struct ucred *cred, struct socket *socket); void mac_create_socket_from_socket(struct socket *oldsocket, struct socket *newsocket); void mac_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct socket *socket); void mac_set_socket_peer_from_socket(struct socket *oldsocket, struct socket *newsocket); void mac_create_pipe(struct ucred *cred, struct pipe *pipe); /* * Labeling event operations: network objects. */ void mac_create_bpfdesc(struct ucred *cred, struct bpf_d *bpf_d); void mac_create_ifnet(struct ifnet *ifp); void mac_create_ipq(struct mbuf *fragment, struct ipq *ipq); void mac_create_datagram_from_ipq(struct ipq *ipq, struct mbuf *datagram); void mac_create_fragment(struct mbuf *datagram, struct mbuf *fragment); void mac_create_mbuf_from_mbuf(struct mbuf *oldmbuf, struct mbuf *newmbuf); void mac_create_mbuf_linklayer(struct ifnet *ifnet, struct mbuf *m); void mac_create_mbuf_from_bpfdesc(struct bpf_d *bpf_d, struct mbuf *m); void mac_create_mbuf_from_ifnet(struct ifnet *ifnet, struct mbuf *m); void mac_create_mbuf_multicast_encap(struct mbuf *oldmbuf, struct ifnet *ifnet, struct mbuf *newmbuf); void mac_create_mbuf_netlayer(struct mbuf *oldmbuf, struct mbuf *newmbuf); int mac_fragment_match(struct mbuf *fragment, struct ipq *ipq); void mac_update_ipq(struct mbuf *fragment, struct ipq *ipq); /* * Labeling event operations: processes. */ void mac_create_cred(struct ucred *cred_parent, struct ucred *cred_child); void mac_execve_transition(struct ucred *old, struct ucred *new, struct vnode *vp); int mac_execve_will_transition(struct ucred *old, struct vnode *vp); void mac_create_proc0(struct ucred *cred); void mac_create_proc1(struct ucred *cred); void mac_thread_userret(struct thread *td); /* Access control checks. */ int mac_check_bpfdesc_receive(struct bpf_d *bpf_d, struct ifnet *ifnet); int mac_check_cred_visible(struct ucred *u1, struct ucred *u2); int mac_check_ifnet_transmit(struct ifnet *ifnet, struct mbuf *m); int mac_check_mount_stat(struct ucred *cred, struct mount *mp); int mac_check_pipe_ioctl(struct ucred *cred, struct pipe *pipe, unsigned long cmd, void *data); int mac_check_pipe_poll(struct ucred *cred, struct pipe *pipe); int mac_check_pipe_read(struct ucred *cred, struct pipe *pipe); int mac_check_pipe_stat(struct ucred *cred, struct pipe *pipe); int mac_check_pipe_write(struct ucred *cred, struct pipe *pipe); int mac_check_proc_debug(struct ucred *cred, struct proc *proc); int mac_check_proc_sched(struct ucred *cred, struct proc *proc); int mac_check_proc_signal(struct ucred *cred, struct proc *proc, int signum); int mac_check_socket_bind(struct ucred *cred, struct socket *so, struct sockaddr *sockaddr); int mac_check_socket_connect(struct ucred *cred, struct socket *so, struct sockaddr *sockaddr); int mac_check_socket_deliver(struct socket *so, struct mbuf *m); int mac_check_socket_listen(struct ucred *cred, struct socket *so); int mac_check_socket_receive(struct ucred *cred, struct socket *so); int mac_check_socket_send(struct ucred *cred, struct socket *so); int mac_check_socket_visible(struct ucred *cred, struct socket *so); int mac_check_system_reboot(struct ucred *cred, int howto); int mac_check_system_swapon(struct ucred *cred, struct vnode *vp); +int mac_check_system_sysctl(struct ucred *cred, int *name, + u_int namelen, void *old, size_t *oldlenp, int inkernel, + void *new, size_t newlen); int mac_check_vnode_access(struct ucred *cred, struct vnode *vp, int flags); int mac_check_vnode_chdir(struct ucred *cred, struct vnode *dvp); int mac_check_vnode_chroot(struct ucred *cred, struct vnode *dvp); int mac_check_vnode_create(struct ucred *cred, struct vnode *dvp, struct componentname *cnp, struct vattr *vap); int mac_check_vnode_delete(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp); int mac_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp, acl_type_t type); int mac_check_vnode_exec(struct ucred *cred, struct vnode *vp); int mac_check_vnode_getacl(struct ucred *cred, struct vnode *vp, acl_type_t type); int mac_check_vnode_getextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio); int mac_check_vnode_link(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp); int mac_check_vnode_lookup(struct ucred *cred, struct vnode *dvp, struct componentname *cnp); int mac_check_vnode_mmap(struct ucred *cred, struct vnode *vp, int prot); int mac_check_vnode_mprotect(struct ucred *cred, struct vnode *vp, int prot); int mac_check_vnode_open(struct ucred *cred, struct vnode *vp, mode_t acc_mode); int mac_check_vnode_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp); int mac_check_vnode_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp); int mac_check_vnode_readdir(struct ucred *cred, struct vnode *vp); int mac_check_vnode_readlink(struct ucred *cred, struct vnode *vp); int mac_check_vnode_rename_from(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp); int mac_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp, struct vnode *vp, int samedir, struct componentname *cnp); int mac_check_vnode_revoke(struct ucred *cred, struct vnode *vp); int mac_check_vnode_setacl(struct ucred *cred, struct vnode *vp, acl_type_t type, struct acl *acl); int mac_check_vnode_setextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio); int mac_check_vnode_setflags(struct ucred *cred, struct vnode *vp, u_long flags); int mac_check_vnode_setmode(struct ucred *cred, struct vnode *vp, mode_t mode); int mac_check_vnode_setowner(struct ucred *cred, struct vnode *vp, uid_t uid, gid_t gid); int mac_check_vnode_setutimes(struct ucred *cred, struct vnode *vp, struct timespec atime, struct timespec mtime); int mac_check_vnode_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp); int mac_check_vnode_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp); int mac_getsockopt_label_get(struct ucred *cred, struct socket *so, struct mac *extmac); int mac_getsockopt_peerlabel_get(struct ucred *cred, struct socket *so, struct mac *extmac); int mac_ioctl_ifnet_get(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet); int mac_ioctl_ifnet_set(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet); int mac_setsockopt_label_set(struct ucred *cred, struct socket *so, struct mac *extmac); int mac_pipe_label_set(struct ucred *cred, struct pipe *pipe, struct label *label); /* * Calls to help various file systems implement labeling functionality * using their existing EA implementation. */ int vop_stdsetlabel_ea(struct vop_setlabel_args *ap); #endif /* _KERNEL */ #endif /* !_SYS_MAC_H */ Index: head/sys/sys/mac_policy.h =================================================================== --- head/sys/sys/mac_policy.h (revision 106024) +++ head/sys/sys/mac_policy.h (revision 106025) @@ -1,587 +1,591 @@ /*- * Copyright (c) 1999, 2000, 2001, 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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 _SYS_MAC_POLICY_H #define _SYS_MAC_POLICY_H /*- * 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. */ struct mac_policy_conf; struct mac_policy_ops { /* * Policy module operations. */ void (*mpo_destroy)(struct mac_policy_conf *mpc); void (*mpo_init)(struct mac_policy_conf *mpc); /* * General policy-directed security system call so that policies * may implement new services without reserving explicit * system call numbers. */ int (*mpo_syscall)(struct thread *td, int call, void *arg); /* * Label operations. */ void (*mpo_init_bpfdesc_label)(struct label *label); void (*mpo_init_cred_label)(struct label *label); void (*mpo_init_devfsdirent_label)(struct label *label); void (*mpo_init_ifnet_label)(struct label *label); void (*mpo_init_ipq_label)(struct label *label); int (*mpo_init_mbuf_label)(struct label *label, int flag); void (*mpo_init_mount_label)(struct label *label); void (*mpo_init_mount_fs_label)(struct label *label); int (*mpo_init_socket_label)(struct label *label, int flag); int (*mpo_init_socket_peer_label)(struct label *label, int flag); void (*mpo_init_pipe_label)(struct label *label); void (*mpo_init_vnode_label)(struct label *label); void (*mpo_destroy_bpfdesc_label)(struct label *label); void (*mpo_destroy_cred_label)(struct label *label); void (*mpo_destroy_devfsdirent_label)(struct label *label); void (*mpo_destroy_ifnet_label)(struct label *label); void (*mpo_destroy_ipq_label)(struct label *label); void (*mpo_destroy_mbuf_label)(struct label *label); void (*mpo_destroy_mount_label)(struct label *label); void (*mpo_destroy_mount_fs_label)(struct label *label); void (*mpo_destroy_socket_label)(struct label *label); void (*mpo_destroy_socket_peer_label)(struct label *label); void (*mpo_destroy_pipe_label)(struct label *label); void (*mpo_destroy_vnode_label)(struct label *label); void (*mpo_copy_pipe_label)(struct label *src, struct label *dest); void (*mpo_copy_vnode_label)(struct label *src, struct label *dest); int (*mpo_externalize_cred_label)(struct label *label, char *element_name, char *buffer, size_t buflen, size_t *len, int *claimed); int (*mpo_externalize_ifnet_label)(struct label *label, char *element_name, char *buffer, size_t buflen, size_t *len, int *claimed); int (*mpo_externalize_pipe_label)(struct label *label, char *element_name, char *buffer, size_t buflen, size_t *len, int *claimed); int (*mpo_externalize_socket_label)(struct label *label, char *element_name, char *buffer, size_t buflen, size_t *len, int *claimed); int (*mpo_externalize_socket_peer_label)(struct label *label, char *element_name, char *buffer, size_t buflen, size_t *len, int *claimed); int (*mpo_externalize_vnode_label)(struct label *label, char *element_name, char *buffer, size_t buflen, size_t *len, int *claimed); int (*mpo_internalize_cred_label)(struct label *label, char *element_name, char *element_data, int *claimed); int (*mpo_internalize_ifnet_label)(struct label *label, char *element_name, char *element_data, int *claimed); int (*mpo_internalize_pipe_label)(struct label *label, char *element_name, char *element_data, int *claimed); int (*mpo_internalize_socket_label)(struct label *label, char *element_name, char *element_data, int *claimed); int (*mpo_internalize_vnode_label)(struct label *label, char *element_name, char *element_data, int *claimed); /* * Labeling event operations: file system objects, and things that * look a lot like file system objects. */ void (*mpo_associate_vnode_devfs)(struct mount *mp, struct label *fslabel, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vlabel); int (*mpo_associate_vnode_extattr)(struct mount *mp, struct label *fslabel, struct vnode *vp, struct label *vlabel); void (*mpo_associate_vnode_singlelabel)(struct mount *mp, struct label *fslabel, struct vnode *vp, struct label *vlabel); void (*mpo_create_devfs_device)(dev_t dev, struct devfs_dirent *de, struct label *label); void (*mpo_create_devfs_directory)(char *dirname, int dirnamelen, struct devfs_dirent *de, struct label *label); void (*mpo_create_devfs_symlink)(struct ucred *cred, struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de, struct label *delabel); void (*mpo_create_devfs_vnode)(struct devfs_dirent *de, struct label *direntlabel, struct vnode *vp, struct label *vnodelabel); int (*mpo_create_vnode_extattr)(struct ucred *cred, struct mount *mp, struct label *fslabel, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *vlabel, struct componentname *cnp); void (*mpo_create_mount)(struct ucred *cred, struct mount *mp, struct label *mntlabel, struct label *fslabel); void (*mpo_create_root_mount)(struct ucred *cred, struct mount *mp, struct label *mountlabel, struct label *fslabel); void (*mpo_relabel_vnode)(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, struct label *label); int (*mpo_setlabel_vnode_extattr)(struct ucred *cred, struct vnode *vp, struct label *vlabel, struct label *intlabel); void (*mpo_update_devfsdirent)(struct devfs_dirent *devfs_dirent, struct label *direntlabel, struct vnode *vp, struct label *vnodelabel); /* * Labeling event operations: IPC objects. */ void (*mpo_create_mbuf_from_socket)(struct socket *so, struct label *socketlabel, struct mbuf *m, struct label *mbuflabel); void (*mpo_create_socket)(struct ucred *cred, struct socket *so, struct label *socketlabel); void (*mpo_create_socket_from_socket)(struct socket *oldsocket, struct label *oldsocketlabel, struct socket *newsocket, struct label *newsocketlabel); void (*mpo_relabel_socket)(struct ucred *cred, struct socket *so, struct label *oldlabel, struct label *newlabel); void (*mpo_relabel_pipe)(struct ucred *cred, struct pipe *pipe, struct label *oldlabel, struct label *newlabel); void (*mpo_set_socket_peer_from_mbuf)(struct mbuf *mbuf, struct label *mbuflabel, struct socket *so, struct label *socketpeerlabel); void (*mpo_set_socket_peer_from_socket)(struct socket *oldsocket, struct label *oldsocketlabel, struct socket *newsocket, struct label *newsocketpeerlabel); void (*mpo_create_pipe)(struct ucred *cred, struct pipe *pipe, struct label *pipelabel); /* * Labeling event operations: network objects. */ void (*mpo_create_bpfdesc)(struct ucred *cred, struct bpf_d *bpf_d, struct label *bpflabel); void (*mpo_create_ifnet)(struct ifnet *ifnet, struct label *ifnetlabel); void (*mpo_create_ipq)(struct mbuf *fragment, struct label *fragmentlabel, struct ipq *ipq, struct label *ipqlabel); void (*mpo_create_datagram_from_ipq) (struct ipq *ipq, struct label *ipqlabel, struct mbuf *datagram, struct label *datagramlabel); void (*mpo_create_fragment)(struct mbuf *datagram, struct label *datagramlabel, struct mbuf *fragment, struct label *fragmentlabel); void (*mpo_create_mbuf_from_mbuf)(struct mbuf *oldmbuf, struct label *oldlabel, struct mbuf *newmbuf, struct label *newlabel); void (*mpo_create_mbuf_linklayer)(struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *mbuf, struct label *mbuflabel); void (*mpo_create_mbuf_from_bpfdesc)(struct bpf_d *bpf_d, struct label *bpflabel, struct mbuf *mbuf, struct label *mbuflabel); void (*mpo_create_mbuf_from_ifnet)(struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *mbuf, struct label *mbuflabel); void (*mpo_create_mbuf_multicast_encap)(struct mbuf *oldmbuf, struct label *oldmbuflabel, struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *newmbuf, struct label *newmbuflabel); void (*mpo_create_mbuf_netlayer)(struct mbuf *oldmbuf, struct label *oldmbuflabel, struct mbuf *newmbuf, struct label *newmbuflabel); int (*mpo_fragment_match)(struct mbuf *fragment, struct label *fragmentlabel, struct ipq *ipq, struct label *ipqlabel); void (*mpo_relabel_ifnet)(struct ucred *cred, struct ifnet *ifnet, struct label *ifnetlabel, struct label *newlabel); void (*mpo_update_ipq)(struct mbuf *fragment, struct label *fragmentlabel, struct ipq *ipq, struct label *ipqlabel); /* * Labeling event operations: processes. */ void (*mpo_create_cred)(struct ucred *parent_cred, struct ucred *child_cred); void (*mpo_execve_transition)(struct ucred *old, struct ucred *new, struct vnode *vp, struct label *vnodelabel); int (*mpo_execve_will_transition)(struct ucred *old, struct vnode *vp, struct label *vnodelabel); void (*mpo_create_proc0)(struct ucred *cred); void (*mpo_create_proc1)(struct ucred *cred); void (*mpo_relabel_cred)(struct ucred *cred, struct label *newlabel); void (*mpo_thread_userret)(struct thread *thread); /* * Access control checks. */ int (*mpo_check_bpfdesc_receive)(struct bpf_d *bpf_d, struct label *bpflabel, struct ifnet *ifnet, struct label *ifnetlabel); int (*mpo_check_cred_relabel)(struct ucred *cred, struct label *newlabel); int (*mpo_check_cred_visible)(struct ucred *u1, struct ucred *u2); int (*mpo_check_ifnet_relabel)(struct ucred *cred, struct ifnet *ifnet, struct label *ifnetlabel, struct label *newlabel); int (*mpo_check_ifnet_transmit)(struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *m, struct label *mbuflabel); int (*mpo_check_mount_stat)(struct ucred *cred, struct mount *mp, struct label *mntlabel); int (*mpo_check_pipe_ioctl)(struct ucred *cred, struct pipe *pipe, struct label *pipelabel, unsigned long cmd, void *data); int (*mpo_check_pipe_poll)(struct ucred *cred, struct pipe *pipe, struct label *pipelabel); int (*mpo_check_pipe_read)(struct ucred *cred, struct pipe *pipe, struct label *pipelabel); int (*mpo_check_pipe_relabel)(struct ucred *cred, struct pipe *pipe, struct label *pipelabel, struct label *newlabel); int (*mpo_check_pipe_stat)(struct ucred *cred, struct pipe *pipe, struct label *pipelabel); int (*mpo_check_pipe_write)(struct ucred *cred, struct pipe *pipe, struct label *pipelabel); int (*mpo_check_proc_debug)(struct ucred *cred, struct proc *proc); int (*mpo_check_proc_sched)(struct ucred *cred, struct proc *proc); int (*mpo_check_proc_signal)(struct ucred *cred, struct proc *proc, int signum); int (*mpo_check_socket_bind)(struct ucred *cred, struct socket *so, struct label *socketlabel, struct sockaddr *sockaddr); int (*mpo_check_socket_connect)(struct ucred *cred, struct socket *so, struct label *socketlabel, struct sockaddr *sockaddr); int (*mpo_check_socket_deliver)(struct socket *so, struct label *socketlabel, struct mbuf *m, struct label *mbuflabel); int (*mpo_check_socket_listen)(struct ucred *cred, struct socket *so, struct label *socketlabel); int (*mpo_check_socket_receive)(struct ucred *cred, struct socket *so, struct label *socketlabel); int (*mpo_check_socket_relabel)(struct ucred *cred, struct socket *so, struct label *socketlabel, struct label *newlabel); int (*mpo_check_socket_send)(struct ucred *cred, struct socket *so, struct label *socketlabel); int (*mpo_check_socket_visible)(struct ucred *cred, struct socket *so, struct label *socketlabel); int (*mpo_check_system_reboot)(struct ucred *cred, int howto); int (*mpo_check_system_swapon)(struct ucred *cred, struct vnode *vp, struct label *label); + int (*mpo_check_system_sysctl)(struct ucred *cred, int *name, + u_int namelen, void *old, size_t *oldlenp, int inkernel, + void *new, size_t newlen); int (*mpo_check_vnode_access)(struct ucred *cred, struct vnode *vp, struct label *label, int flags); int (*mpo_check_vnode_chdir)(struct ucred *cred, struct vnode *dvp, struct label *dlabel); int (*mpo_check_vnode_chroot)(struct ucred *cred, struct vnode *dvp, struct label *dlabel); int (*mpo_check_vnode_create)(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct componentname *cnp, struct vattr *vap); int (*mpo_check_vnode_delete)(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, void *label, struct componentname *cnp); int (*mpo_check_vnode_deleteacl)(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type); int (*mpo_check_vnode_exec)(struct ucred *cred, struct vnode *vp, struct label *label); int (*mpo_check_vnode_getacl)(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type); int (*mpo_check_vnode_getextattr)(struct ucred *cred, struct vnode *vp, struct label *label, int attrnamespace, const char *name, struct uio *uio); int (*mpo_check_vnode_link)(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp); int (*mpo_check_vnode_lookup)(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct componentname *cnp); int (*mpo_check_vnode_mmap)(struct ucred *cred, struct vnode *vp, struct label *label, int prot); void (*mpo_check_vnode_mmap_downgrade)(struct ucred *cred, struct vnode *vp, struct label *label, int *prot); int (*mpo_check_vnode_mprotect)(struct ucred *cred, struct vnode *vp, struct label *label, int prot); int (*mpo_check_vnode_open)(struct ucred *cred, struct vnode *vp, struct label *label, mode_t acc_mode); int (*mpo_check_vnode_poll)(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label); int (*mpo_check_vnode_read)(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label); int (*mpo_check_vnode_readdir)(struct ucred *cred, struct vnode *dvp, struct label *dlabel); int (*mpo_check_vnode_readlink)(struct ucred *cred, struct vnode *vp, struct label *label); int (*mpo_check_vnode_relabel)(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, struct label *newlabel); int (*mpo_check_vnode_rename_from)(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp); int (*mpo_check_vnode_rename_to)(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, int samedir, struct componentname *cnp); int (*mpo_check_vnode_revoke)(struct ucred *cred, struct vnode *vp, struct label *label); int (*mpo_check_vnode_setacl)(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type, struct acl *acl); int (*mpo_check_vnode_setextattr)(struct ucred *cred, struct vnode *vp, struct label *label, int attrnamespace, const char *name, struct uio *uio); int (*mpo_check_vnode_setflags)(struct ucred *cred, struct vnode *vp, struct label *label, u_long flags); int (*mpo_check_vnode_setmode)(struct ucred *cred, struct vnode *vp, struct label *label, mode_t mode); int (*mpo_check_vnode_setowner)(struct ucred *cred, struct vnode *vp, struct label *label, uid_t uid, gid_t gid); int (*mpo_check_vnode_setutimes)(struct ucred *cred, struct vnode *vp, struct label *label, struct timespec atime, struct timespec mtime); int (*mpo_check_vnode_stat)(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label); int (*mpo_check_vnode_write)(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label); }; typedef const void *macop_t; enum mac_op_constant { MAC_OP_LAST, MAC_DESTROY, MAC_INIT, MAC_SYSCALL, MAC_INIT_BPFDESC_LABEL, MAC_INIT_CRED_LABEL, MAC_INIT_DEVFSDIRENT_LABEL, MAC_INIT_IFNET_LABEL, MAC_INIT_IPQ_LABEL, MAC_INIT_MBUF_LABEL, MAC_INIT_MOUNT_LABEL, MAC_INIT_MOUNT_FS_LABEL, MAC_INIT_PIPE_LABEL, MAC_INIT_SOCKET_LABEL, MAC_INIT_SOCKET_PEER_LABEL, MAC_INIT_VNODE_LABEL, MAC_DESTROY_BPFDESC_LABEL, MAC_DESTROY_CRED_LABEL, MAC_DESTROY_DEVFSDIRENT_LABEL, MAC_DESTROY_IFNET_LABEL, MAC_DESTROY_IPQ_LABEL, MAC_DESTROY_MBUF_LABEL, MAC_DESTROY_MOUNT_LABEL, MAC_DESTROY_MOUNT_FS_LABEL, MAC_DESTROY_PIPE_LABEL, MAC_DESTROY_SOCKET_LABEL, MAC_DESTROY_SOCKET_PEER_LABEL, MAC_DESTROY_VNODE_LABEL, MAC_COPY_PIPE_LABEL, MAC_COPY_VNODE_LABEL, MAC_EXTERNALIZE_CRED_LABEL, MAC_EXTERNALIZE_IFNET_LABEL, MAC_EXTERNALIZE_PIPE_LABEL, MAC_EXTERNALIZE_SOCKET_LABEL, MAC_EXTERNALIZE_SOCKET_PEER_LABEL, MAC_EXTERNALIZE_VNODE_LABEL, MAC_INTERNALIZE_CRED_LABEL, MAC_INTERNALIZE_IFNET_LABEL, MAC_INTERNALIZE_PIPE_LABEL, MAC_INTERNALIZE_SOCKET_LABEL, MAC_INTERNALIZE_VNODE_LABEL, MAC_CREATE_DEVFS_DEVICE, MAC_CREATE_DEVFS_DIRECTORY, MAC_CREATE_DEVFS_SYMLINK, MAC_CREATE_DEVFS_VNODE, MAC_CREATE_MOUNT, MAC_CREATE_ROOT_MOUNT, MAC_RELABEL_VNODE, MAC_UPDATE_DEVFSDIRENT, MAC_ASSOCIATE_VNODE_DEVFS, MAC_ASSOCIATE_VNODE_EXTATTR, MAC_ASSOCIATE_VNODE_SINGLELABEL, MAC_CREATE_VNODE_EXTATTR, MAC_SETLABEL_VNODE_EXTATTR, MAC_CREATE_MBUF_FROM_SOCKET, MAC_CREATE_PIPE, MAC_CREATE_SOCKET, MAC_CREATE_SOCKET_FROM_SOCKET, MAC_RELABEL_PIPE, MAC_RELABEL_SOCKET, MAC_SET_SOCKET_PEER_FROM_MBUF, MAC_SET_SOCKET_PEER_FROM_SOCKET, MAC_CREATE_BPFDESC, MAC_CREATE_DATAGRAM_FROM_IPQ, MAC_CREATE_IFNET, MAC_CREATE_IPQ, MAC_CREATE_FRAGMENT, MAC_CREATE_MBUF_FROM_MBUF, MAC_CREATE_MBUF_LINKLAYER, MAC_CREATE_MBUF_FROM_BPFDESC, MAC_CREATE_MBUF_FROM_IFNET, MAC_CREATE_MBUF_MULTICAST_ENCAP, MAC_CREATE_MBUF_NETLAYER, MAC_FRAGMENT_MATCH, MAC_RELABEL_IFNET, MAC_UPDATE_IPQ, MAC_CREATE_CRED, MAC_EXECVE_TRANSITION, MAC_EXECVE_WILL_TRANSITION, MAC_CREATE_PROC0, MAC_CREATE_PROC1, MAC_RELABEL_CRED, MAC_THREAD_USERRET, MAC_CHECK_BPFDESC_RECEIVE, MAC_CHECK_CRED_RELABEL, MAC_CHECK_CRED_VISIBLE, MAC_CHECK_IFNET_RELABEL, MAC_CHECK_IFNET_TRANSMIT, MAC_CHECK_MOUNT_STAT, MAC_CHECK_PIPE_IOCTL, MAC_CHECK_PIPE_POLL, MAC_CHECK_PIPE_READ, MAC_CHECK_PIPE_RELABEL, MAC_CHECK_PIPE_STAT, MAC_CHECK_PIPE_WRITE, MAC_CHECK_PROC_DEBUG, MAC_CHECK_PROC_SCHED, MAC_CHECK_PROC_SIGNAL, MAC_CHECK_SOCKET_BIND, MAC_CHECK_SOCKET_CONNECT, MAC_CHECK_SOCKET_DELIVER, MAC_CHECK_SOCKET_LISTEN, MAC_CHECK_SOCKET_RECEIVE, MAC_CHECK_SOCKET_RELABEL, MAC_CHECK_SOCKET_SEND, MAC_CHECK_SOCKET_VISIBLE, MAC_CHECK_SYSTEM_REBOOT, MAC_CHECK_SYSTEM_SWAPON, + MAC_CHECK_SYSTEM_SYSCTL, MAC_CHECK_VNODE_ACCESS, MAC_CHECK_VNODE_CHDIR, MAC_CHECK_VNODE_CHROOT, MAC_CHECK_VNODE_CREATE, MAC_CHECK_VNODE_DELETE, MAC_CHECK_VNODE_DELETEACL, MAC_CHECK_VNODE_EXEC, MAC_CHECK_VNODE_GETACL, MAC_CHECK_VNODE_GETEXTATTR, MAC_CHECK_VNODE_LINK, MAC_CHECK_VNODE_LOOKUP, MAC_CHECK_VNODE_MMAP, MAC_CHECK_VNODE_MMAP_DOWNGRADE, MAC_CHECK_VNODE_MPROTECT, MAC_CHECK_VNODE_OPEN, MAC_CHECK_VNODE_POLL, MAC_CHECK_VNODE_READ, MAC_CHECK_VNODE_READDIR, MAC_CHECK_VNODE_READLINK, MAC_CHECK_VNODE_RELABEL, MAC_CHECK_VNODE_RENAME_FROM, MAC_CHECK_VNODE_RENAME_TO, MAC_CHECK_VNODE_REVOKE, MAC_CHECK_VNODE_SETACL, MAC_CHECK_VNODE_SETEXTATTR, MAC_CHECK_VNODE_SETFLAGS, MAC_CHECK_VNODE_SETMODE, MAC_CHECK_VNODE_SETOWNER, MAC_CHECK_VNODE_SETUTIMES, MAC_CHECK_VNODE_STAT, MAC_CHECK_VNODE_WRITE, }; struct mac_policy_op_entry { enum mac_op_constant mpe_constant; /* what this hook implements */ macop_t mpe_function; /* hook's implementation */ }; struct mac_policy_conf { char *mpc_name; /* policy name */ char *mpc_fullname; /* policy full name */ struct mac_policy_ops *mpc_ops; /* policy operations */ struct mac_policy_op_entry *mpc_entries; /* ops to fill in */ int mpc_loadtime_flags; /* flags */ int *mpc_field_off; /* security field */ int mpc_runtime_flags; /* flags */ 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 #define MAC_POLICY_SET(mpents, mpname, mpfullname, mpflags, privdata_wanted) \ static struct mac_policy_conf mpname##_mac_policy_conf = { \ #mpname, \ mpfullname, \ NULL, \ mpents, \ mpflags, \ privdata_wanted, \ 0, \ }; \ static moduledata_t mpname##_mod = { \ #mpname, \ mac_policy_modevent, \ &mpname##_mac_policy_conf \ }; \ MODULE_DEPEND(mpname, kernel_mac_support, 1, 1, 1); \ DECLARE_MODULE(mpname, mpname##_mod, SI_SUB_MAC_POLICY, \ SI_ORDER_MIDDLE) int mac_policy_modevent(module_t mod, int type, void *data); #define LABEL_TO_SLOT(l, s) (l)->l_perpolicy[s] #endif /* !_SYS_MAC_POLICY_H */