diff --git a/sys/net/if.c b/sys/net/if.c index 96093d0a2aa3..a6cf6d050875 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -1,4895 +1,4907 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2010 Bjoern A. Zeeb * Copyright (c) 1980, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of 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. * * @(#)if.c 8.5 (Berkeley) 1/9/95 * $FreeBSD$ */ #include "opt_bpf.h" #include "opt_inet6.h" #include "opt_inet.h" #include "opt_ddb.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 #ifdef DDB #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(INET) || defined(INET6) #include #include #include #include #include #ifdef INET #include #include #endif /* INET */ #ifdef INET6 #include #include #endif /* INET6 */ #endif /* INET || INET6 */ #include /* * Consumers of struct ifreq such as tcpdump assume no pad between ifr_name * and ifr_ifru when it is used in SIOCGIFCONF. */ _Static_assert(sizeof(((struct ifreq *)0)->ifr_name) == offsetof(struct ifreq, ifr_ifru), "gap between ifr_name and ifr_ifru"); __read_mostly epoch_t net_epoch_preempt; #ifdef COMPAT_FREEBSD32 #include #include struct ifreq_buffer32 { uint32_t length; /* (size_t) */ uint32_t buffer; /* (void *) */ }; /* * Interface request structure used for socket * ioctl's. All interface ioctl's must have parameter * definitions which begin with ifr_name. The * remainder may be interface specific. */ struct ifreq32 { char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */ union { struct sockaddr ifru_addr; struct sockaddr ifru_dstaddr; struct sockaddr ifru_broadaddr; struct ifreq_buffer32 ifru_buffer; short ifru_flags[2]; short ifru_index; int ifru_jid; int ifru_metric; int ifru_mtu; int ifru_phys; int ifru_media; uint32_t ifru_data; int ifru_cap[2]; u_int ifru_fib; u_char ifru_vlan_pcp; } ifr_ifru; }; CTASSERT(sizeof(struct ifreq) == sizeof(struct ifreq32)); CTASSERT(__offsetof(struct ifreq, ifr_ifru) == __offsetof(struct ifreq32, ifr_ifru)); struct ifconf32 { int32_t ifc_len; union { uint32_t ifcu_buf; uint32_t ifcu_req; } ifc_ifcu; }; #define SIOCGIFCONF32 _IOWR('i', 36, struct ifconf32) struct ifdrv32 { char ifd_name[IFNAMSIZ]; uint32_t ifd_cmd; uint32_t ifd_len; uint32_t ifd_data; }; #define SIOCSDRVSPEC32 _IOC_NEWTYPE(SIOCSDRVSPEC, struct ifdrv32) #define SIOCGDRVSPEC32 _IOC_NEWTYPE(SIOCGDRVSPEC, struct ifdrv32) struct ifgroupreq32 { char ifgr_name[IFNAMSIZ]; u_int ifgr_len; union { char ifgru_group[IFNAMSIZ]; uint32_t ifgru_groups; } ifgr_ifgru; }; #define SIOCAIFGROUP32 _IOC_NEWTYPE(SIOCAIFGROUP, struct ifgroupreq32) #define SIOCGIFGROUP32 _IOC_NEWTYPE(SIOCGIFGROUP, struct ifgroupreq32) #define SIOCDIFGROUP32 _IOC_NEWTYPE(SIOCDIFGROUP, struct ifgroupreq32) #define SIOCGIFGMEMB32 _IOC_NEWTYPE(SIOCGIFGMEMB, struct ifgroupreq32) struct ifmediareq32 { char ifm_name[IFNAMSIZ]; int ifm_current; int ifm_mask; int ifm_status; int ifm_active; int ifm_count; uint32_t ifm_ulist; /* (int *) */ }; #define SIOCGIFMEDIA32 _IOC_NEWTYPE(SIOCGIFMEDIA, struct ifmediareq32) #define SIOCGIFXMEDIA32 _IOC_NEWTYPE(SIOCGIFXMEDIA, struct ifmediareq32) #endif /* COMPAT_FREEBSD32 */ union ifreq_union { struct ifreq ifr; #ifdef COMPAT_FREEBSD32 struct ifreq32 ifr32; #endif }; SYSCTL_NODE(_net, PF_LINK, link, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "Link layers"); SYSCTL_NODE(_net_link, 0, generic, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "Generic link-management"); SYSCTL_INT(_net_link, OID_AUTO, ifqmaxlen, CTLFLAG_RDTUN, &ifqmaxlen, 0, "max send queue size"); /* Log link state change events */ static int log_link_state_change = 1; SYSCTL_INT(_net_link, OID_AUTO, log_link_state_change, CTLFLAG_RW, &log_link_state_change, 0, "log interface link state change events"); /* Log promiscuous mode change events */ static int log_promisc_mode_change = 1; SYSCTL_INT(_net_link, OID_AUTO, log_promisc_mode_change, CTLFLAG_RDTUN, &log_promisc_mode_change, 1, "log promiscuous mode change events"); /* Interface description */ static unsigned int ifdescr_maxlen = 1024; SYSCTL_UINT(_net, OID_AUTO, ifdescr_maxlen, CTLFLAG_RW, &ifdescr_maxlen, 0, "administrative maximum length for interface description"); static MALLOC_DEFINE(M_IFDESCR, "ifdescr", "ifnet descriptions"); /* global sx for non-critical path ifdescr */ static struct sx ifdescr_sx; SX_SYSINIT(ifdescr_sx, &ifdescr_sx, "ifnet descr"); void (*ng_ether_link_state_p)(struct ifnet *ifp, int state); void (*lagg_linkstate_p)(struct ifnet *ifp, int state); /* These are external hooks for CARP. */ void (*carp_linkstate_p)(struct ifnet *ifp); void (*carp_demote_adj_p)(int, char *); int (*carp_master_p)(struct ifaddr *); #if defined(INET) || defined(INET6) int (*carp_forus_p)(struct ifnet *ifp, u_char *dhost); int (*carp_output_p)(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *sa); int (*carp_ioctl_p)(struct ifreq *, u_long, struct thread *); int (*carp_attach_p)(struct ifaddr *, int); void (*carp_detach_p)(struct ifaddr *, bool); #endif #ifdef INET int (*carp_iamatch_p)(struct ifaddr *, uint8_t **); #endif #ifdef INET6 struct ifaddr *(*carp_iamatch6_p)(struct ifnet *ifp, struct in6_addr *taddr6); caddr_t (*carp_macmatch6_p)(struct ifnet *ifp, struct mbuf *m, const struct in6_addr *taddr); #endif struct mbuf *(*tbr_dequeue_ptr)(struct ifaltq *, int) = NULL; /* * XXX: Style; these should be sorted alphabetically, and unprototyped * static functions should be prototyped. Currently they are sorted by * declaration order. */ static void if_attachdomain(void *); static void if_attachdomain1(struct ifnet *); static int ifconf(u_long, caddr_t); static void if_input_default(struct ifnet *, struct mbuf *); static int if_requestencap_default(struct ifnet *, struct if_encap_req *); static void if_route(struct ifnet *, int flag, int fam); static int if_setflag(struct ifnet *, int, int, int *, int); static int if_transmit_default(struct ifnet *ifp, struct mbuf *m); static void if_unroute(struct ifnet *, int flag, int fam); static int if_delmulti_locked(struct ifnet *, struct ifmultiaddr *, int); static void do_link_state_change(void *, int); static int if_getgroup(struct ifgroupreq *, struct ifnet *); static int if_getgroupmembers(struct ifgroupreq *); static void if_delgroups(struct ifnet *); static void if_attach_internal(struct ifnet *, bool); static int if_detach_internal(struct ifnet *, bool); static void if_siocaddmulti(void *, int); static void if_link_ifnet(struct ifnet *); static bool if_unlink_ifnet(struct ifnet *, bool); #ifdef VIMAGE static int if_vmove(struct ifnet *, struct vnet *); #endif #ifdef INET6 /* * XXX: declare here to avoid to include many inet6 related files.. * should be more generalized? */ extern void nd6_setmtu(struct ifnet *); #endif /* ipsec helper hooks */ VNET_DEFINE(struct hhook_head *, ipsec_hhh_in[HHOOK_IPSEC_COUNT]); VNET_DEFINE(struct hhook_head *, ipsec_hhh_out[HHOOK_IPSEC_COUNT]); int ifqmaxlen = IFQ_MAXLEN; VNET_DEFINE(struct ifnethead, ifnet); /* depend on static init XXX */ VNET_DEFINE(struct ifgrouphead, ifg_head); /* Table of ifnet by index. */ static int if_index; static int if_indexlim = 8; static struct ifindex_entry { struct ifnet *ife_ifnet; uint16_t ife_gencnt; } *ifindex_table; SYSCTL_NODE(_net_link_generic, IFMIB_SYSTEM, system, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "Variables global to all interfaces"); static int sysctl_ifcount(SYSCTL_HANDLER_ARGS) { int rv = 0; IFNET_RLOCK(); for (int i = 1; i <= if_index; i++) if (ifindex_table[i].ife_ifnet != NULL && ifindex_table[i].ife_ifnet->if_vnet == curvnet) rv = i; IFNET_RUNLOCK(); return (sysctl_handle_int(oidp, &rv, 0, req)); } SYSCTL_PROC(_net_link_generic_system, IFMIB_IFCOUNT, ifcount, CTLTYPE_INT | CTLFLAG_VNET | CTLFLAG_RD, NULL, 0, sysctl_ifcount, "I", "Maximum known interface index"); /* * The global network interface list (V_ifnet) and related state (such as * if_index, if_indexlim, and ifindex_table) are protected by an sxlock. * This may be acquired to stabilise the list, or we may rely on NET_EPOCH. */ struct sx ifnet_sxlock; SX_SYSINIT_FLAGS(ifnet_sx, &ifnet_sxlock, "ifnet_sx", SX_RECURSE); struct sx ifnet_detach_sxlock; SX_SYSINIT_FLAGS(ifnet_detach, &ifnet_detach_sxlock, "ifnet_detach_sx", SX_RECURSE); #ifdef VIMAGE #define VNET_IS_SHUTTING_DOWN(_vnet) \ ((_vnet)->vnet_shutdown && (_vnet)->vnet_state < SI_SUB_VNET_DONE) #endif static if_com_alloc_t *if_com_alloc[256]; static if_com_free_t *if_com_free[256]; static MALLOC_DEFINE(M_IFNET, "ifnet", "interface internals"); MALLOC_DEFINE(M_IFADDR, "ifaddr", "interface address"); MALLOC_DEFINE(M_IFMADDR, "ether_multi", "link-level multicast address"); struct ifnet * ifnet_byindex(u_int idx) { struct ifnet *ifp; NET_EPOCH_ASSERT(); if (__predict_false(idx > if_index)) return (NULL); ifp = ck_pr_load_ptr(&ifindex_table[idx].ife_ifnet); if (curvnet != NULL && ifp != NULL && ifp->if_vnet != curvnet) ifp = NULL; return (ifp); } struct ifnet * ifnet_byindex_ref(u_int idx) { struct ifnet *ifp; ifp = ifnet_byindex(idx); if (ifp == NULL || (ifp->if_flags & IFF_DYING)) return (NULL); if (!if_try_ref(ifp)) return (NULL); return (ifp); } struct ifnet * ifnet_byindexgen(uint16_t idx, uint16_t gen) { struct ifnet *ifp; NET_EPOCH_ASSERT(); if (__predict_false(idx > if_index)) return (NULL); ifp = ck_pr_load_ptr(&ifindex_table[idx].ife_ifnet); if (ifindex_table[idx].ife_gencnt == gen) return (ifp); else return (NULL); } /* * Network interface utility routines. * * Routines with ifa_ifwith* names take sockaddr *'s as * parameters. */ static void if_init_idxtable(void *arg __unused) { ifindex_table = malloc(if_indexlim * sizeof(*ifindex_table), M_IFNET, M_WAITOK | M_ZERO); } SYSINIT(if_init, SI_SUB_INIT_IF, SI_ORDER_SECOND, if_init_idxtable, NULL); static void vnet_if_init(const void *unused __unused) { CK_STAILQ_INIT(&V_ifnet); CK_STAILQ_INIT(&V_ifg_head); vnet_if_clone_init(); } VNET_SYSINIT(vnet_if_init, SI_SUB_INIT_IF, SI_ORDER_SECOND, vnet_if_init, NULL); static void if_link_ifnet(struct ifnet *ifp) { IFNET_WLOCK(); CK_STAILQ_INSERT_TAIL(&V_ifnet, ifp, if_link); #ifdef VIMAGE curvnet->vnet_ifcnt++; #endif IFNET_WUNLOCK(); } static bool if_unlink_ifnet(struct ifnet *ifp, bool vmove) { struct ifnet *iter; int found = 0; IFNET_WLOCK(); CK_STAILQ_FOREACH(iter, &V_ifnet, if_link) if (iter == ifp) { CK_STAILQ_REMOVE(&V_ifnet, ifp, ifnet, if_link); if (!vmove) ifp->if_flags |= IFF_DYING; found = 1; break; } #ifdef VIMAGE curvnet->vnet_ifcnt--; #endif IFNET_WUNLOCK(); return (found); } #ifdef VIMAGE static void vnet_if_return(const void *unused __unused) { struct ifnet *ifp, *nifp; struct ifnet **pending; int found __diagused; int i; i = 0; /* * We need to protect our access to the V_ifnet tailq. Ordinarily we'd * enter NET_EPOCH, but that's not possible, because if_vmove() calls * if_detach_internal(), which waits for NET_EPOCH callbacks to * complete. We can't do that from within NET_EPOCH. * * However, we can also use the IFNET_xLOCK, which is the V_ifnet * read/write lock. We cannot hold the lock as we call if_vmove() * though, as that presents LOR w.r.t ifnet_sx, in_multi_sx and iflib * ctx lock. */ IFNET_WLOCK(); pending = malloc(sizeof(struct ifnet *) * curvnet->vnet_ifcnt, M_IFNET, M_WAITOK | M_ZERO); /* Return all inherited interfaces to their parent vnets. */ CK_STAILQ_FOREACH_SAFE(ifp, &V_ifnet, if_link, nifp) { if (ifp->if_home_vnet != ifp->if_vnet) { found = if_unlink_ifnet(ifp, true); MPASS(found); pending[i++] = ifp; } } IFNET_WUNLOCK(); for (int j = 0; j < i; j++) { sx_xlock(&ifnet_detach_sxlock); if_vmove(pending[j], pending[j]->if_home_vnet); sx_xunlock(&ifnet_detach_sxlock); } free(pending, M_IFNET); } VNET_SYSUNINIT(vnet_if_return, SI_SUB_VNET_DONE, SI_ORDER_ANY, vnet_if_return, NULL); #endif /* * Allocate a struct ifnet and an index for an interface. A layer 2 * common structure will also be allocated if an allocation routine is * registered for the passed type. */ static struct ifnet * if_alloc_domain(u_char type, int numa_domain) { struct ifnet *ifp; u_short idx; KASSERT(numa_domain <= IF_NODOM, ("numa_domain too large")); if (numa_domain == IF_NODOM) ifp = malloc(sizeof(struct ifnet), M_IFNET, M_WAITOK | M_ZERO); else ifp = malloc_domainset(sizeof(struct ifnet), M_IFNET, DOMAINSET_PREF(numa_domain), M_WAITOK | M_ZERO); ifp->if_type = type; ifp->if_alloctype = type; ifp->if_numa_domain = numa_domain; #ifdef VIMAGE ifp->if_vnet = curvnet; #endif if (if_com_alloc[type] != NULL) { ifp->if_l2com = if_com_alloc[type](type, ifp); KASSERT(ifp->if_l2com, ("%s: if_com_alloc[%u] failed", __func__, type)); } IF_ADDR_LOCK_INIT(ifp); TASK_INIT(&ifp->if_linktask, 0, do_link_state_change, ifp); TASK_INIT(&ifp->if_addmultitask, 0, if_siocaddmulti, ifp); ifp->if_afdata_initialized = 0; IF_AFDATA_LOCK_INIT(ifp); CK_STAILQ_INIT(&ifp->if_addrhead); CK_STAILQ_INIT(&ifp->if_multiaddrs); CK_STAILQ_INIT(&ifp->if_groups); #ifdef MAC mac_ifnet_init(ifp); #endif ifq_init(&ifp->if_snd, ifp); refcount_init(&ifp->if_refcount, 1); /* Index reference. */ for (int i = 0; i < IFCOUNTERS; i++) ifp->if_counters[i] = counter_u64_alloc(M_WAITOK); ifp->if_get_counter = if_get_counter_default; ifp->if_pcp = IFNET_PCP_NONE; /* Allocate an ifindex array entry. */ IFNET_WLOCK(); /* * Try to find an empty slot below if_index. If we fail, take the * next slot. */ for (idx = 1; idx <= if_index; idx++) { if (ifindex_table[idx].ife_ifnet == NULL) break; } /* Catch if_index overflow. */ if (idx >= if_indexlim) { struct ifindex_entry *new, *old; int newlim; newlim = if_indexlim * 2; new = malloc(newlim * sizeof(*new), M_IFNET, M_WAITOK | M_ZERO); memcpy(new, ifindex_table, if_indexlim * sizeof(*new)); old = ifindex_table; ck_pr_store_ptr(&ifindex_table, new); if_indexlim = newlim; epoch_wait_preempt(net_epoch_preempt); free(old, M_IFNET); } if (idx > if_index) if_index = idx; ifp->if_index = idx; ifp->if_idxgen = ifindex_table[idx].ife_gencnt; ck_pr_store_ptr(&ifindex_table[idx].ife_ifnet, ifp); IFNET_WUNLOCK(); return (ifp); } struct ifnet * if_alloc_dev(u_char type, device_t dev) { int numa_domain; if (dev == NULL || bus_get_domain(dev, &numa_domain) != 0) return (if_alloc_domain(type, IF_NODOM)); return (if_alloc_domain(type, numa_domain)); } struct ifnet * if_alloc(u_char type) { return (if_alloc_domain(type, IF_NODOM)); } /* * Do the actual work of freeing a struct ifnet, and layer 2 common * structure. This call is made when the network epoch guarantees * us that nobody holds a pointer to the interface. */ static void if_free_deferred(epoch_context_t ctx) { struct ifnet *ifp = __containerof(ctx, struct ifnet, if_epoch_ctx); KASSERT((ifp->if_flags & IFF_DYING), ("%s: interface not dying", __func__)); if (if_com_free[ifp->if_alloctype] != NULL) if_com_free[ifp->if_alloctype](ifp->if_l2com, ifp->if_alloctype); #ifdef MAC mac_ifnet_destroy(ifp); #endif /* MAC */ IF_AFDATA_DESTROY(ifp); IF_ADDR_LOCK_DESTROY(ifp); ifq_delete(&ifp->if_snd); for (int i = 0; i < IFCOUNTERS; i++) counter_u64_free(ifp->if_counters[i]); if_freedescr(ifp->if_description); free(ifp->if_hw_addr, M_IFADDR); free(ifp, M_IFNET); } /* * Deregister an interface and free the associated storage. */ void if_free(struct ifnet *ifp) { ifp->if_flags |= IFF_DYING; /* XXX: Locking */ /* * XXXGL: An interface index is really an alias to ifp pointer. * Why would we clear the alias now, and not in the deferred * context? Indeed there is nothing wrong with some network * thread obtaining ifp via ifnet_byindex() inside the network * epoch and then dereferencing ifp while we perform if_free(), * and after if_free() finished, too. * * This early index freeing was important back when ifindex was * virtualized and interface would outlive the vnet. */ IFNET_WLOCK(); MPASS(ifindex_table[ifp->if_index].ife_ifnet == ifp); ck_pr_store_ptr(&ifindex_table[ifp->if_index].ife_ifnet, NULL); ifindex_table[ifp->if_index].ife_gencnt++; while (if_index > 0 && ifindex_table[if_index].ife_ifnet == NULL) if_index--; IFNET_WUNLOCK(); if (refcount_release(&ifp->if_refcount)) NET_EPOCH_CALL(if_free_deferred, &ifp->if_epoch_ctx); } /* * Interfaces to keep an ifnet type-stable despite the possibility of the * driver calling if_free(). If there are additional references, we defer * freeing the underlying data structure. */ void if_ref(struct ifnet *ifp) { u_int old __diagused; /* We don't assert the ifnet list lock here, but arguably should. */ old = refcount_acquire(&ifp->if_refcount); KASSERT(old > 0, ("%s: ifp %p has 0 refs", __func__, ifp)); } bool if_try_ref(struct ifnet *ifp) { NET_EPOCH_ASSERT(); return (refcount_acquire_if_not_zero(&ifp->if_refcount)); } void if_rele(struct ifnet *ifp) { if (!refcount_release(&ifp->if_refcount)) return; NET_EPOCH_CALL(if_free_deferred, &ifp->if_epoch_ctx); } void ifq_init(struct ifaltq *ifq, struct ifnet *ifp) { mtx_init(&ifq->ifq_mtx, ifp->if_xname, "if send queue", MTX_DEF); if (ifq->ifq_maxlen == 0) ifq->ifq_maxlen = ifqmaxlen; ifq->altq_type = 0; ifq->altq_disc = NULL; ifq->altq_flags &= ALTQF_CANTCHANGE; ifq->altq_tbr = NULL; ifq->altq_ifp = ifp; } void ifq_delete(struct ifaltq *ifq) { mtx_destroy(&ifq->ifq_mtx); } /* * Perform generic interface initialization tasks and attach the interface * to the list of "active" interfaces. If vmove flag is set on entry * to if_attach_internal(), perform only a limited subset of initialization * tasks, given that we are moving from one vnet to another an ifnet which * has already been fully initialized. * * Note that if_detach_internal() removes group membership unconditionally * even when vmove flag is set, and if_attach_internal() adds only IFG_ALL. * Thus, when if_vmove() is applied to a cloned interface, group membership * is lost while a cloned one always joins a group whose name is * ifc->ifc_name. To recover this after if_detach_internal() and * if_attach_internal(), the cloner should be specified to * if_attach_internal() via ifc. If it is non-NULL, if_attach_internal() * attempts to join a group whose name is ifc->ifc_name. * * XXX: * - The decision to return void and thus require this function to * succeed is questionable. * - We should probably do more sanity checking. For instance we don't * do anything to insure if_xname is unique or non-empty. */ void if_attach(struct ifnet *ifp) { if_attach_internal(ifp, false); } /* * Compute the least common TSO limit. */ void if_hw_tsomax_common(if_t ifp, struct ifnet_hw_tsomax *pmax) { /* * 1) If there is no limit currently, take the limit from * the network adapter. * * 2) If the network adapter has a limit below the current * limit, apply it. */ if (pmax->tsomaxbytes == 0 || (ifp->if_hw_tsomax != 0 && ifp->if_hw_tsomax < pmax->tsomaxbytes)) { pmax->tsomaxbytes = ifp->if_hw_tsomax; } if (pmax->tsomaxsegcount == 0 || (ifp->if_hw_tsomaxsegcount != 0 && ifp->if_hw_tsomaxsegcount < pmax->tsomaxsegcount)) { pmax->tsomaxsegcount = ifp->if_hw_tsomaxsegcount; } if (pmax->tsomaxsegsize == 0 || (ifp->if_hw_tsomaxsegsize != 0 && ifp->if_hw_tsomaxsegsize < pmax->tsomaxsegsize)) { pmax->tsomaxsegsize = ifp->if_hw_tsomaxsegsize; } } /* * Update TSO limit of a network adapter. * * Returns zero if no change. Else non-zero. */ int if_hw_tsomax_update(if_t ifp, struct ifnet_hw_tsomax *pmax) { int retval = 0; if (ifp->if_hw_tsomax != pmax->tsomaxbytes) { ifp->if_hw_tsomax = pmax->tsomaxbytes; retval++; } if (ifp->if_hw_tsomaxsegsize != pmax->tsomaxsegsize) { ifp->if_hw_tsomaxsegsize = pmax->tsomaxsegsize; retval++; } if (ifp->if_hw_tsomaxsegcount != pmax->tsomaxsegcount) { ifp->if_hw_tsomaxsegcount = pmax->tsomaxsegcount; retval++; } return (retval); } static void if_attach_internal(struct ifnet *ifp, bool vmove) { unsigned socksize, ifasize; int namelen, masklen; struct sockaddr_dl *sdl; struct ifaddr *ifa; MPASS(ifindex_table[ifp->if_index].ife_ifnet == ifp); #ifdef VIMAGE ifp->if_vnet = curvnet; if (ifp->if_home_vnet == NULL) ifp->if_home_vnet = curvnet; #endif if_addgroup(ifp, IFG_ALL); #ifdef VIMAGE /* Restore group membership for cloned interface. */ if (vmove) if_clone_restoregroup(ifp); #endif getmicrotime(&ifp->if_lastchange); ifp->if_epoch = time_uptime; KASSERT((ifp->if_transmit == NULL && ifp->if_qflush == NULL) || (ifp->if_transmit != NULL && ifp->if_qflush != NULL), ("transmit and qflush must both either be set or both be NULL")); if (ifp->if_transmit == NULL) { ifp->if_transmit = if_transmit_default; ifp->if_qflush = if_qflush; } if (ifp->if_input == NULL) ifp->if_input = if_input_default; if (ifp->if_requestencap == NULL) ifp->if_requestencap = if_requestencap_default; if (!vmove) { #ifdef MAC mac_ifnet_create(ifp); #endif /* * Create a Link Level name for this device. */ namelen = strlen(ifp->if_xname); /* * Always save enough space for any possiable name so we * can do a rename in place later. */ masklen = offsetof(struct sockaddr_dl, sdl_data[0]) + IFNAMSIZ; socksize = masklen + ifp->if_addrlen; if (socksize < sizeof(*sdl)) socksize = sizeof(*sdl); socksize = roundup2(socksize, sizeof(long)); ifasize = sizeof(*ifa) + 2 * socksize; ifa = ifa_alloc(ifasize, M_WAITOK); sdl = (struct sockaddr_dl *)(ifa + 1); sdl->sdl_len = socksize; sdl->sdl_family = AF_LINK; bcopy(ifp->if_xname, sdl->sdl_data, namelen); sdl->sdl_nlen = namelen; sdl->sdl_index = ifp->if_index; sdl->sdl_type = ifp->if_type; ifp->if_addr = ifa; ifa->ifa_ifp = ifp; ifa->ifa_addr = (struct sockaddr *)sdl; sdl = (struct sockaddr_dl *)(socksize + (caddr_t)sdl); ifa->ifa_netmask = (struct sockaddr *)sdl; sdl->sdl_len = masklen; while (namelen != 0) sdl->sdl_data[--namelen] = 0xff; CK_STAILQ_INSERT_HEAD(&ifp->if_addrhead, ifa, ifa_link); /* Reliably crash if used uninitialized. */ ifp->if_broadcastaddr = NULL; if (ifp->if_type == IFT_ETHER) { ifp->if_hw_addr = malloc(ifp->if_addrlen, M_IFADDR, M_WAITOK | M_ZERO); } #if defined(INET) || defined(INET6) /* Use defaults for TSO, if nothing is set */ if (ifp->if_hw_tsomax == 0 && ifp->if_hw_tsomaxsegcount == 0 && ifp->if_hw_tsomaxsegsize == 0) { /* * The TSO defaults needs to be such that an * NFS mbuf list of 35 mbufs totalling just * below 64K works and that a chain of mbufs * can be defragged into at most 32 segments: */ ifp->if_hw_tsomax = min(IP_MAXPACKET, (32 * MCLBYTES) - (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN)); ifp->if_hw_tsomaxsegcount = 35; ifp->if_hw_tsomaxsegsize = 2048; /* 2K */ /* XXX some drivers set IFCAP_TSO after ethernet attach */ if (ifp->if_capabilities & IFCAP_TSO) { if_printf(ifp, "Using defaults for TSO: %u/%u/%u\n", ifp->if_hw_tsomax, ifp->if_hw_tsomaxsegcount, ifp->if_hw_tsomaxsegsize); } } #endif } #ifdef VIMAGE else { /* * Update the interface index in the link layer address * of the interface. */ for (ifa = ifp->if_addr; ifa != NULL; ifa = CK_STAILQ_NEXT(ifa, ifa_link)) { if (ifa->ifa_addr->sa_family == AF_LINK) { sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_index = ifp->if_index; } } } #endif if_link_ifnet(ifp); if (domain_init_status >= 2) if_attachdomain1(ifp); EVENTHANDLER_INVOKE(ifnet_arrival_event, ifp); if (IS_DEFAULT_VNET(curvnet)) devctl_notify("IFNET", ifp->if_xname, "ATTACH", NULL); } static void if_epochalloc(void *dummy __unused) { net_epoch_preempt = epoch_alloc("Net preemptible", EPOCH_PREEMPT); } SYSINIT(ifepochalloc, SI_SUB_EPOCH, SI_ORDER_ANY, if_epochalloc, NULL); static void if_attachdomain(void *dummy) { struct ifnet *ifp; CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) if_attachdomain1(ifp); } SYSINIT(domainifattach, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_SECOND, if_attachdomain, NULL); static void if_attachdomain1(struct ifnet *ifp) { struct domain *dp; /* * Since dp->dom_ifattach calls malloc() with M_WAITOK, we * cannot lock ifp->if_afdata initialization, entirely. */ IF_AFDATA_LOCK(ifp); if (ifp->if_afdata_initialized >= domain_init_status) { IF_AFDATA_UNLOCK(ifp); log(LOG_WARNING, "%s called more than once on %s\n", __func__, ifp->if_xname); return; } ifp->if_afdata_initialized = domain_init_status; IF_AFDATA_UNLOCK(ifp); /* address family dependent data region */ bzero(ifp->if_afdata, sizeof(ifp->if_afdata)); SLIST_FOREACH(dp, &domains, dom_next) { if (dp->dom_ifattach) ifp->if_afdata[dp->dom_family] = (*dp->dom_ifattach)(ifp); } } /* * Remove any unicast or broadcast network addresses from an interface. */ void if_purgeaddrs(struct ifnet *ifp) { struct ifaddr *ifa; #ifdef INET6 /* * Need to leave multicast addresses of proxy NDP llentries * before in6_purgeifaddr() because the llentries are keys * for in6_multi objects of proxy NDP entries. * in6_purgeifaddr()s clean up llentries including proxy NDPs * then we would lose the keys if they are called earlier. */ in6_purge_proxy_ndp(ifp); #endif while (1) { struct epoch_tracker et; NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_LINK) break; } NET_EPOCH_EXIT(et); if (ifa == NULL) break; #ifdef INET /* XXX: Ugly!! ad hoc just for INET */ if (ifa->ifa_addr->sa_family == AF_INET) { struct ifaliasreq ifr; bzero(&ifr, sizeof(ifr)); ifr.ifra_addr = *ifa->ifa_addr; if (ifa->ifa_dstaddr) ifr.ifra_broadaddr = *ifa->ifa_dstaddr; if (in_control(NULL, SIOCDIFADDR, (caddr_t)&ifr, ifp, NULL) == 0) continue; } #endif /* INET */ #ifdef INET6 if (ifa->ifa_addr->sa_family == AF_INET6) { in6_purgeifaddr((struct in6_ifaddr *)ifa); /* ifp_addrhead is already updated */ continue; } #endif /* INET6 */ IF_ADDR_WLOCK(ifp); CK_STAILQ_REMOVE(&ifp->if_addrhead, ifa, ifaddr, ifa_link); IF_ADDR_WUNLOCK(ifp); ifa_free(ifa); } } /* * Remove any multicast network addresses from an interface when an ifnet * is going away. */ static void if_purgemaddrs(struct ifnet *ifp) { struct ifmultiaddr *ifma; IF_ADDR_WLOCK(ifp); while (!CK_STAILQ_EMPTY(&ifp->if_multiaddrs)) { ifma = CK_STAILQ_FIRST(&ifp->if_multiaddrs); CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifmultiaddr, ifma_link); if_delmulti_locked(ifp, ifma, 1); } IF_ADDR_WUNLOCK(ifp); } /* * Detach an interface, removing it from the list of "active" interfaces. * If vmove flag is set on entry to if_detach_internal(), perform only a * limited subset of cleanup tasks, given that we are moving an ifnet from * one vnet to another, where it must be fully operational. * * XXXRW: There are some significant questions about event ordering, and * how to prevent things from starting to use the interface during detach. */ void if_detach(struct ifnet *ifp) { bool found; CURVNET_SET_QUIET(ifp->if_vnet); found = if_unlink_ifnet(ifp, false); if (found) { sx_xlock(&ifnet_detach_sxlock); if_detach_internal(ifp, false); sx_xunlock(&ifnet_detach_sxlock); } CURVNET_RESTORE(); } /* * The vmove flag, if set, indicates that we are called from a callpath * that is moving an interface to a different vnet instance. * * The shutdown flag, if set, indicates that we are called in the * process of shutting down a vnet instance. Currently only the * vnet_if_return SYSUNINIT function sets it. Note: we can be called * on a vnet instance shutdown without this flag being set, e.g., when * the cloned interfaces are destoyed as first thing of teardown. */ static int if_detach_internal(struct ifnet *ifp, bool vmove) { struct ifaddr *ifa; int i; struct domain *dp; #ifdef VIMAGE bool shutdown; shutdown = VNET_IS_SHUTTING_DOWN(ifp->if_vnet); #endif /* * At this point we know the interface still was on the ifnet list * and we removed it so we are in a stable state. */ epoch_wait_preempt(net_epoch_preempt); /* * Ensure all pending EPOCH(9) callbacks have been executed. This * fixes issues about late destruction of multicast options * which lead to leave group calls, which in turn access the * belonging ifnet structure: */ NET_EPOCH_DRAIN_CALLBACKS(); /* * In any case (destroy or vmove) detach us from the groups * and remove/wait for pending events on the taskq. * XXX-BZ in theory an interface could still enqueue a taskq change? */ if_delgroups(ifp); taskqueue_drain(taskqueue_swi, &ifp->if_linktask); taskqueue_drain(taskqueue_swi, &ifp->if_addmultitask); if_down(ifp); #ifdef VIMAGE /* * On VNET shutdown abort here as the stack teardown will do all * the work top-down for us. */ if (shutdown) { /* Give interface users the chance to clean up. */ EVENTHANDLER_INVOKE(ifnet_departure_event, ifp); /* * In case of a vmove we are done here without error. * If we would signal an error it would lead to the same * abort as if we did not find the ifnet anymore. * if_detach() calls us in void context and does not care * about an early abort notification, so life is splendid :) */ goto finish_vnet_shutdown; } #endif /* * At this point we are not tearing down a VNET and are either * going to destroy or vmove the interface and have to cleanup * accordingly. */ /* * Remove routes and flush queues. */ #ifdef ALTQ if (ALTQ_IS_ENABLED(&ifp->if_snd)) altq_disable(&ifp->if_snd); if (ALTQ_IS_ATTACHED(&ifp->if_snd)) altq_detach(&ifp->if_snd); #endif if_purgeaddrs(ifp); #ifdef INET in_ifdetach(ifp); #endif #ifdef INET6 /* * Remove all IPv6 kernel structs related to ifp. This should be done * before removing routing entries below, since IPv6 interface direct * routes are expected to be removed by the IPv6-specific kernel API. * Otherwise, the kernel will detect some inconsistency and bark it. */ in6_ifdetach(ifp); #endif if_purgemaddrs(ifp); EVENTHANDLER_INVOKE(ifnet_departure_event, ifp); if (IS_DEFAULT_VNET(curvnet)) devctl_notify("IFNET", ifp->if_xname, "DETACH", NULL); if (!vmove) { /* * Prevent further calls into the device driver via ifnet. */ if_dead(ifp); /* * Clean up all addresses. */ IF_ADDR_WLOCK(ifp); if (!CK_STAILQ_EMPTY(&ifp->if_addrhead)) { ifa = CK_STAILQ_FIRST(&ifp->if_addrhead); CK_STAILQ_REMOVE(&ifp->if_addrhead, ifa, ifaddr, ifa_link); IF_ADDR_WUNLOCK(ifp); ifa_free(ifa); } else IF_ADDR_WUNLOCK(ifp); } rt_flushifroutes(ifp); #ifdef VIMAGE finish_vnet_shutdown: #endif /* * We cannot hold the lock over dom_ifdetach calls as they might * sleep, for example trying to drain a callout, thus open up the * theoretical race with re-attaching. */ IF_AFDATA_LOCK(ifp); i = ifp->if_afdata_initialized; ifp->if_afdata_initialized = 0; IF_AFDATA_UNLOCK(ifp); if (i == 0) return (0); SLIST_FOREACH(dp, &domains, dom_next) { if (dp->dom_ifdetach && ifp->if_afdata[dp->dom_family]) { (*dp->dom_ifdetach)(ifp, ifp->if_afdata[dp->dom_family]); ifp->if_afdata[dp->dom_family] = NULL; } } return (0); } #ifdef VIMAGE /* * if_vmove() performs a limited version of if_detach() in current * vnet and if_attach()es the ifnet to the vnet specified as 2nd arg. */ static int if_vmove(struct ifnet *ifp, struct vnet *new_vnet) { #ifdef DEV_BPF u_int bif_dlt, bif_hdrlen; #endif int rc; #ifdef DEV_BPF /* * if_detach_internal() will call the eventhandler to notify * interface departure. That will detach if_bpf. We need to * safe the dlt and hdrlen so we can re-attach it later. */ bpf_get_bp_params(ifp->if_bpf, &bif_dlt, &bif_hdrlen); #endif /* * Detach from current vnet, but preserve LLADDR info, do not * mark as dead etc. so that the ifnet can be reattached later. * If we cannot find it, we lost the race to someone else. */ rc = if_detach_internal(ifp, true); if (rc != 0) return (rc); /* * Perform interface-specific reassignment tasks, if provided by * the driver. */ if (ifp->if_reassign != NULL) ifp->if_reassign(ifp, new_vnet, NULL); /* * Switch to the context of the target vnet. */ CURVNET_SET_QUIET(new_vnet); if_attach_internal(ifp, true); #ifdef DEV_BPF if (ifp->if_bpf == NULL) bpfattach(ifp, bif_dlt, bif_hdrlen); #endif CURVNET_RESTORE(); return (0); } /* * Move an ifnet to or from another child prison/vnet, specified by the jail id. */ static int if_vmove_loan(struct thread *td, struct ifnet *ifp, char *ifname, int jid) { struct prison *pr; struct ifnet *difp; int error; bool found __diagused; bool shutdown; MPASS(ifindex_table[ifp->if_index].ife_ifnet == ifp); /* Try to find the prison within our visibility. */ sx_slock(&allprison_lock); pr = prison_find_child(td->td_ucred->cr_prison, jid); sx_sunlock(&allprison_lock); if (pr == NULL) return (ENXIO); prison_hold_locked(pr); mtx_unlock(&pr->pr_mtx); /* Do not try to move the iface from and to the same prison. */ if (pr->pr_vnet == ifp->if_vnet) { prison_free(pr); return (EEXIST); } /* Make sure the named iface does not exists in the dst. prison/vnet. */ /* XXX Lock interfaces to avoid races. */ CURVNET_SET_QUIET(pr->pr_vnet); difp = ifunit(ifname); if (difp != NULL) { CURVNET_RESTORE(); prison_free(pr); return (EEXIST); } sx_xlock(&ifnet_detach_sxlock); /* Make sure the VNET is stable. */ shutdown = VNET_IS_SHUTTING_DOWN(ifp->if_vnet); if (shutdown) { sx_xunlock(&ifnet_detach_sxlock); CURVNET_RESTORE(); prison_free(pr); return (EBUSY); } CURVNET_RESTORE(); found = if_unlink_ifnet(ifp, true); if (! found) { sx_xunlock(&ifnet_detach_sxlock); CURVNET_RESTORE(); prison_free(pr); return (ENODEV); } /* Move the interface into the child jail/vnet. */ error = if_vmove(ifp, pr->pr_vnet); /* Report the new if_xname back to the userland on success. */ if (error == 0) sprintf(ifname, "%s", ifp->if_xname); sx_xunlock(&ifnet_detach_sxlock); prison_free(pr); return (error); } static int if_vmove_reclaim(struct thread *td, char *ifname, int jid) { struct prison *pr; struct vnet *vnet_dst; struct ifnet *ifp; int error, found __diagused; bool shutdown; /* Try to find the prison within our visibility. */ sx_slock(&allprison_lock); pr = prison_find_child(td->td_ucred->cr_prison, jid); sx_sunlock(&allprison_lock); if (pr == NULL) return (ENXIO); prison_hold_locked(pr); mtx_unlock(&pr->pr_mtx); /* Make sure the named iface exists in the source prison/vnet. */ CURVNET_SET(pr->pr_vnet); ifp = ifunit(ifname); /* XXX Lock to avoid races. */ if (ifp == NULL) { CURVNET_RESTORE(); prison_free(pr); return (ENXIO); } /* Do not try to move the iface from and to the same prison. */ vnet_dst = TD_TO_VNET(td); if (vnet_dst == ifp->if_vnet) { CURVNET_RESTORE(); prison_free(pr); return (EEXIST); } /* Make sure the VNET is stable. */ shutdown = VNET_IS_SHUTTING_DOWN(ifp->if_vnet); if (shutdown) { CURVNET_RESTORE(); prison_free(pr); return (EBUSY); } /* Get interface back from child jail/vnet. */ found = if_unlink_ifnet(ifp, true); MPASS(found); sx_xlock(&ifnet_detach_sxlock); error = if_vmove(ifp, vnet_dst); sx_xunlock(&ifnet_detach_sxlock); CURVNET_RESTORE(); /* Report the new if_xname back to the userland on success. */ if (error == 0) sprintf(ifname, "%s", ifp->if_xname); prison_free(pr); return (error); } #endif /* VIMAGE */ /* * Add a group to an interface */ int if_addgroup(struct ifnet *ifp, const char *groupname) { struct ifg_list *ifgl; struct ifg_group *ifg = NULL; struct ifg_member *ifgm; int new = 0; if (groupname[0] && groupname[strlen(groupname) - 1] >= '0' && groupname[strlen(groupname) - 1] <= '9') return (EINVAL); IFNET_WLOCK(); CK_STAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) if (!strcmp(ifgl->ifgl_group->ifg_group, groupname)) { IFNET_WUNLOCK(); return (EEXIST); } if ((ifgl = malloc(sizeof(*ifgl), M_TEMP, M_NOWAIT)) == NULL) { IFNET_WUNLOCK(); return (ENOMEM); } if ((ifgm = malloc(sizeof(*ifgm), M_TEMP, M_NOWAIT)) == NULL) { free(ifgl, M_TEMP); IFNET_WUNLOCK(); return (ENOMEM); } CK_STAILQ_FOREACH(ifg, &V_ifg_head, ifg_next) if (!strcmp(ifg->ifg_group, groupname)) break; if (ifg == NULL) { if ((ifg = malloc(sizeof(*ifg), M_TEMP, M_NOWAIT)) == NULL) { free(ifgl, M_TEMP); free(ifgm, M_TEMP); IFNET_WUNLOCK(); return (ENOMEM); } strlcpy(ifg->ifg_group, groupname, sizeof(ifg->ifg_group)); ifg->ifg_refcnt = 0; CK_STAILQ_INIT(&ifg->ifg_members); CK_STAILQ_INSERT_TAIL(&V_ifg_head, ifg, ifg_next); new = 1; } ifg->ifg_refcnt++; ifgl->ifgl_group = ifg; ifgm->ifgm_ifp = ifp; IF_ADDR_WLOCK(ifp); CK_STAILQ_INSERT_TAIL(&ifg->ifg_members, ifgm, ifgm_next); CK_STAILQ_INSERT_TAIL(&ifp->if_groups, ifgl, ifgl_next); IF_ADDR_WUNLOCK(ifp); IFNET_WUNLOCK(); if (new) EVENTHANDLER_INVOKE(group_attach_event, ifg); EVENTHANDLER_INVOKE(group_change_event, groupname); return (0); } /* * Helper function to remove a group out of an interface. Expects the global * ifnet lock to be write-locked, and drops it before returning. */ static void _if_delgroup_locked(struct ifnet *ifp, struct ifg_list *ifgl, const char *groupname) { struct ifg_member *ifgm; bool freeifgl; IFNET_WLOCK_ASSERT(); IF_ADDR_WLOCK(ifp); CK_STAILQ_REMOVE(&ifp->if_groups, ifgl, ifg_list, ifgl_next); IF_ADDR_WUNLOCK(ifp); CK_STAILQ_FOREACH(ifgm, &ifgl->ifgl_group->ifg_members, ifgm_next) { if (ifgm->ifgm_ifp == ifp) { CK_STAILQ_REMOVE(&ifgl->ifgl_group->ifg_members, ifgm, ifg_member, ifgm_next); break; } } if (--ifgl->ifgl_group->ifg_refcnt == 0) { CK_STAILQ_REMOVE(&V_ifg_head, ifgl->ifgl_group, ifg_group, ifg_next); freeifgl = true; } else { freeifgl = false; } IFNET_WUNLOCK(); epoch_wait_preempt(net_epoch_preempt); EVENTHANDLER_INVOKE(group_change_event, groupname); if (freeifgl) { EVENTHANDLER_INVOKE(group_detach_event, ifgl->ifgl_group); free(ifgl->ifgl_group, M_TEMP); } free(ifgm, M_TEMP); free(ifgl, M_TEMP); } /* * Remove a group from an interface */ int if_delgroup(struct ifnet *ifp, const char *groupname) { struct ifg_list *ifgl; IFNET_WLOCK(); CK_STAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) if (strcmp(ifgl->ifgl_group->ifg_group, groupname) == 0) break; if (ifgl == NULL) { IFNET_WUNLOCK(); return (ENOENT); } _if_delgroup_locked(ifp, ifgl, groupname); return (0); } /* * Remove an interface from all groups */ static void if_delgroups(struct ifnet *ifp) { struct ifg_list *ifgl; char groupname[IFNAMSIZ]; IFNET_WLOCK(); while ((ifgl = CK_STAILQ_FIRST(&ifp->if_groups)) != NULL) { strlcpy(groupname, ifgl->ifgl_group->ifg_group, IFNAMSIZ); _if_delgroup_locked(ifp, ifgl, groupname); IFNET_WLOCK(); } IFNET_WUNLOCK(); } /* * Stores all groups from an interface in memory pointed to by ifgr. */ static int if_getgroup(struct ifgroupreq *ifgr, struct ifnet *ifp) { int len, error; struct ifg_list *ifgl; struct ifg_req ifgrq, *ifgp; NET_EPOCH_ASSERT(); if (ifgr->ifgr_len == 0) { CK_STAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) ifgr->ifgr_len += sizeof(struct ifg_req); return (0); } len = ifgr->ifgr_len; ifgp = ifgr->ifgr_groups; /* XXX: wire */ CK_STAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) { if (len < sizeof(ifgrq)) return (EINVAL); bzero(&ifgrq, sizeof ifgrq); strlcpy(ifgrq.ifgrq_group, ifgl->ifgl_group->ifg_group, sizeof(ifgrq.ifgrq_group)); if ((error = copyout(&ifgrq, ifgp, sizeof(struct ifg_req)))) return (error); len -= sizeof(ifgrq); ifgp++; } return (0); } /* * Stores all members of a group in memory pointed to by igfr */ static int if_getgroupmembers(struct ifgroupreq *ifgr) { struct ifg_group *ifg; struct ifg_member *ifgm; struct ifg_req ifgrq, *ifgp; int len, error; IFNET_RLOCK(); CK_STAILQ_FOREACH(ifg, &V_ifg_head, ifg_next) if (strcmp(ifg->ifg_group, ifgr->ifgr_name) == 0) break; if (ifg == NULL) { IFNET_RUNLOCK(); return (ENOENT); } if (ifgr->ifgr_len == 0) { CK_STAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next) ifgr->ifgr_len += sizeof(ifgrq); IFNET_RUNLOCK(); return (0); } len = ifgr->ifgr_len; ifgp = ifgr->ifgr_groups; CK_STAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next) { if (len < sizeof(ifgrq)) { IFNET_RUNLOCK(); return (EINVAL); } bzero(&ifgrq, sizeof ifgrq); strlcpy(ifgrq.ifgrq_member, ifgm->ifgm_ifp->if_xname, sizeof(ifgrq.ifgrq_member)); if ((error = copyout(&ifgrq, ifgp, sizeof(struct ifg_req)))) { IFNET_RUNLOCK(); return (error); } len -= sizeof(ifgrq); ifgp++; } IFNET_RUNLOCK(); return (0); } /* * Return counter values from counter(9)s stored in ifnet. */ uint64_t if_get_counter_default(struct ifnet *ifp, ift_counter cnt) { KASSERT(cnt < IFCOUNTERS, ("%s: invalid cnt %d", __func__, cnt)); return (counter_u64_fetch(ifp->if_counters[cnt])); } /* * Increase an ifnet counter. Usually used for counters shared * between the stack and a driver, but function supports them all. */ void if_inc_counter(struct ifnet *ifp, ift_counter cnt, int64_t inc) { KASSERT(cnt < IFCOUNTERS, ("%s: invalid cnt %d", __func__, cnt)); counter_u64_add(ifp->if_counters[cnt], inc); } /* * Copy data from ifnet to userland API structure if_data. */ void if_data_copy(struct ifnet *ifp, struct if_data *ifd) { ifd->ifi_type = ifp->if_type; ifd->ifi_physical = 0; ifd->ifi_addrlen = ifp->if_addrlen; ifd->ifi_hdrlen = ifp->if_hdrlen; ifd->ifi_link_state = ifp->if_link_state; ifd->ifi_vhid = 0; ifd->ifi_datalen = sizeof(struct if_data); ifd->ifi_mtu = ifp->if_mtu; ifd->ifi_metric = ifp->if_metric; ifd->ifi_baudrate = ifp->if_baudrate; ifd->ifi_hwassist = ifp->if_hwassist; ifd->ifi_epoch = ifp->if_epoch; ifd->ifi_lastchange = ifp->if_lastchange; ifd->ifi_ipackets = ifp->if_get_counter(ifp, IFCOUNTER_IPACKETS); ifd->ifi_ierrors = ifp->if_get_counter(ifp, IFCOUNTER_IERRORS); ifd->ifi_opackets = ifp->if_get_counter(ifp, IFCOUNTER_OPACKETS); ifd->ifi_oerrors = ifp->if_get_counter(ifp, IFCOUNTER_OERRORS); ifd->ifi_collisions = ifp->if_get_counter(ifp, IFCOUNTER_COLLISIONS); ifd->ifi_ibytes = ifp->if_get_counter(ifp, IFCOUNTER_IBYTES); ifd->ifi_obytes = ifp->if_get_counter(ifp, IFCOUNTER_OBYTES); ifd->ifi_imcasts = ifp->if_get_counter(ifp, IFCOUNTER_IMCASTS); ifd->ifi_omcasts = ifp->if_get_counter(ifp, IFCOUNTER_OMCASTS); ifd->ifi_iqdrops = ifp->if_get_counter(ifp, IFCOUNTER_IQDROPS); ifd->ifi_oqdrops = ifp->if_get_counter(ifp, IFCOUNTER_OQDROPS); ifd->ifi_noproto = ifp->if_get_counter(ifp, IFCOUNTER_NOPROTO); } /* * Initialization, destruction and refcounting functions for ifaddrs. */ struct ifaddr * ifa_alloc(size_t size, int flags) { struct ifaddr *ifa; KASSERT(size >= sizeof(struct ifaddr), ("%s: invalid size %zu", __func__, size)); ifa = malloc(size, M_IFADDR, M_ZERO | flags); if (ifa == NULL) return (NULL); if ((ifa->ifa_opackets = counter_u64_alloc(flags)) == NULL) goto fail; if ((ifa->ifa_ipackets = counter_u64_alloc(flags)) == NULL) goto fail; if ((ifa->ifa_obytes = counter_u64_alloc(flags)) == NULL) goto fail; if ((ifa->ifa_ibytes = counter_u64_alloc(flags)) == NULL) goto fail; refcount_init(&ifa->ifa_refcnt, 1); return (ifa); fail: /* free(NULL) is okay */ counter_u64_free(ifa->ifa_opackets); counter_u64_free(ifa->ifa_ipackets); counter_u64_free(ifa->ifa_obytes); counter_u64_free(ifa->ifa_ibytes); free(ifa, M_IFADDR); return (NULL); } void ifa_ref(struct ifaddr *ifa) { u_int old __diagused; old = refcount_acquire(&ifa->ifa_refcnt); KASSERT(old > 0, ("%s: ifa %p has 0 refs", __func__, ifa)); } int ifa_try_ref(struct ifaddr *ifa) { NET_EPOCH_ASSERT(); return (refcount_acquire_if_not_zero(&ifa->ifa_refcnt)); } static void ifa_destroy(epoch_context_t ctx) { struct ifaddr *ifa; ifa = __containerof(ctx, struct ifaddr, ifa_epoch_ctx); counter_u64_free(ifa->ifa_opackets); counter_u64_free(ifa->ifa_ipackets); counter_u64_free(ifa->ifa_obytes); counter_u64_free(ifa->ifa_ibytes); free(ifa, M_IFADDR); } void ifa_free(struct ifaddr *ifa) { if (refcount_release(&ifa->ifa_refcnt)) NET_EPOCH_CALL(ifa_destroy, &ifa->ifa_epoch_ctx); } /* * XXX: Because sockaddr_dl has deeper structure than the sockaddr * structs used to represent other address families, it is necessary * to perform a different comparison. */ #define sa_dl_equal(a1, a2) \ ((((const struct sockaddr_dl *)(a1))->sdl_len == \ ((const struct sockaddr_dl *)(a2))->sdl_len) && \ (bcmp(CLLADDR((const struct sockaddr_dl *)(a1)), \ CLLADDR((const struct sockaddr_dl *)(a2)), \ ((const struct sockaddr_dl *)(a1))->sdl_alen) == 0)) /* * Locate an interface based on a complete address. */ /*ARGSUSED*/ struct ifaddr * ifa_ifwithaddr(const struct sockaddr *addr) { struct ifnet *ifp; struct ifaddr *ifa; NET_EPOCH_ASSERT(); CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != addr->sa_family) continue; if (sa_equal(addr, ifa->ifa_addr)) { goto done; } /* IP6 doesn't have broadcast */ if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr && ifa->ifa_broadaddr->sa_len != 0 && sa_equal(ifa->ifa_broadaddr, addr)) { goto done; } } } ifa = NULL; done: return (ifa); } int ifa_ifwithaddr_check(const struct sockaddr *addr) { struct epoch_tracker et; int rc; NET_EPOCH_ENTER(et); rc = (ifa_ifwithaddr(addr) != NULL); NET_EPOCH_EXIT(et); return (rc); } /* * Locate an interface based on the broadcast address. */ /* ARGSUSED */ struct ifaddr * ifa_ifwithbroadaddr(const struct sockaddr *addr, int fibnum) { struct ifnet *ifp; struct ifaddr *ifa; NET_EPOCH_ASSERT(); CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { if ((fibnum != RT_ALL_FIBS) && (ifp->if_fib != fibnum)) continue; CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != addr->sa_family) continue; if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr && ifa->ifa_broadaddr->sa_len != 0 && sa_equal(ifa->ifa_broadaddr, addr)) { goto done; } } } ifa = NULL; done: return (ifa); } /* * Locate the point to point interface with a given destination address. */ /*ARGSUSED*/ struct ifaddr * ifa_ifwithdstaddr(const struct sockaddr *addr, int fibnum) { struct ifnet *ifp; struct ifaddr *ifa; NET_EPOCH_ASSERT(); CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { if ((ifp->if_flags & IFF_POINTOPOINT) == 0) continue; if ((fibnum != RT_ALL_FIBS) && (ifp->if_fib != fibnum)) continue; CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != addr->sa_family) continue; if (ifa->ifa_dstaddr != NULL && sa_equal(addr, ifa->ifa_dstaddr)) { goto done; } } } ifa = NULL; done: return (ifa); } /* * Find an interface on a specific network. If many, choice * is most specific found. */ struct ifaddr * ifa_ifwithnet(const struct sockaddr *addr, int ignore_ptp, int fibnum) { struct ifnet *ifp; struct ifaddr *ifa; struct ifaddr *ifa_maybe = NULL; u_int af = addr->sa_family; const char *addr_data = addr->sa_data, *cplim; NET_EPOCH_ASSERT(); /* * AF_LINK addresses can be looked up directly by their index number, * so do that if we can. */ if (af == AF_LINK) { ifp = ifnet_byindex( ((const struct sockaddr_dl *)addr)->sdl_index); return (ifp ? ifp->if_addr : NULL); } /* * Scan though each interface, looking for ones that have addresses * in this address family and the requested fib. */ CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { if ((fibnum != RT_ALL_FIBS) && (ifp->if_fib != fibnum)) continue; CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { const char *cp, *cp2, *cp3; if (ifa->ifa_addr->sa_family != af) next: continue; if (af == AF_INET && ifp->if_flags & IFF_POINTOPOINT && !ignore_ptp) { /* * This is a bit broken as it doesn't * take into account that the remote end may * be a single node in the network we are * looking for. * The trouble is that we don't know the * netmask for the remote end. */ if (ifa->ifa_dstaddr != NULL && sa_equal(addr, ifa->ifa_dstaddr)) { goto done; } } else { /* * Scan all the bits in the ifa's address. * If a bit dissagrees with what we are * looking for, mask it with the netmask * to see if it really matters. * (A byte at a time) */ if (ifa->ifa_netmask == 0) continue; cp = addr_data; cp2 = ifa->ifa_addr->sa_data; cp3 = ifa->ifa_netmask->sa_data; cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask; while (cp3 < cplim) if ((*cp++ ^ *cp2++) & *cp3++) goto next; /* next address! */ /* * If the netmask of what we just found * is more specific than what we had before * (if we had one), or if the virtual status * of new prefix is better than of the old one, * then remember the new one before continuing * to search for an even better one. */ if (ifa_maybe == NULL || ifa_preferred(ifa_maybe, ifa) || rn_refines((caddr_t)ifa->ifa_netmask, (caddr_t)ifa_maybe->ifa_netmask)) { ifa_maybe = ifa; } } } } ifa = ifa_maybe; ifa_maybe = NULL; done: return (ifa); } /* * Find an interface address specific to an interface best matching * a given address. */ struct ifaddr * ifaof_ifpforaddr(const struct sockaddr *addr, struct ifnet *ifp) { struct ifaddr *ifa; const char *cp, *cp2, *cp3; char *cplim; struct ifaddr *ifa_maybe = NULL; u_int af = addr->sa_family; if (af >= AF_MAX) return (NULL); NET_EPOCH_ASSERT(); CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != af) continue; if (ifa_maybe == NULL) ifa_maybe = ifa; if (ifa->ifa_netmask == 0) { if (sa_equal(addr, ifa->ifa_addr) || (ifa->ifa_dstaddr && sa_equal(addr, ifa->ifa_dstaddr))) goto done; continue; } if (ifp->if_flags & IFF_POINTOPOINT) { if (sa_equal(addr, ifa->ifa_dstaddr)) goto done; } else { cp = addr->sa_data; cp2 = ifa->ifa_addr->sa_data; cp3 = ifa->ifa_netmask->sa_data; cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask; for (; cp3 < cplim; cp3++) if ((*cp++ ^ *cp2++) & *cp3) break; if (cp3 == cplim) goto done; } } ifa = ifa_maybe; done: return (ifa); } /* * See whether new ifa is better than current one: * 1) A non-virtual one is preferred over virtual. * 2) A virtual in master state preferred over any other state. * * Used in several address selecting functions. */ int ifa_preferred(struct ifaddr *cur, struct ifaddr *next) { return (cur->ifa_carp && (!next->ifa_carp || ((*carp_master_p)(next) && !(*carp_master_p)(cur)))); } struct sockaddr_dl * link_alloc_sdl(size_t size, int flags) { return (malloc(size, M_TEMP, flags)); } void link_free_sdl(struct sockaddr *sa) { free(sa, M_TEMP); } /* * Fills in given sdl with interface basic info. * Returns pointer to filled sdl. */ struct sockaddr_dl * link_init_sdl(struct ifnet *ifp, struct sockaddr *paddr, u_char iftype) { struct sockaddr_dl *sdl; sdl = (struct sockaddr_dl *)paddr; memset(sdl, 0, sizeof(struct sockaddr_dl)); sdl->sdl_len = sizeof(struct sockaddr_dl); sdl->sdl_family = AF_LINK; sdl->sdl_index = ifp->if_index; sdl->sdl_type = iftype; return (sdl); } /* * Mark an interface down and notify protocols of * the transition. */ static void if_unroute(struct ifnet *ifp, int flag, int fam) { KASSERT(flag == IFF_UP, ("if_unroute: flag != IFF_UP")); ifp->if_flags &= ~flag; getmicrotime(&ifp->if_lastchange); ifp->if_qflush(ifp); if (ifp->if_carp) (*carp_linkstate_p)(ifp); rt_ifmsg(ifp, IFF_UP); } /* * Mark an interface up and notify protocols of * the transition. */ static void if_route(struct ifnet *ifp, int flag, int fam) { KASSERT(flag == IFF_UP, ("if_route: flag != IFF_UP")); ifp->if_flags |= flag; getmicrotime(&ifp->if_lastchange); if (ifp->if_carp) (*carp_linkstate_p)(ifp); rt_ifmsg(ifp, IFF_UP); #ifdef INET6 in6_if_up(ifp); #endif } void (*vlan_link_state_p)(struct ifnet *); /* XXX: private from if_vlan */ void (*vlan_trunk_cap_p)(struct ifnet *); /* XXX: private from if_vlan */ struct ifnet *(*vlan_trunkdev_p)(struct ifnet *); struct ifnet *(*vlan_devat_p)(struct ifnet *, uint16_t); int (*vlan_tag_p)(struct ifnet *, uint16_t *); int (*vlan_pcp_p)(struct ifnet *, uint16_t *); int (*vlan_setcookie_p)(struct ifnet *, void *); void *(*vlan_cookie_p)(struct ifnet *); /* * Handle a change in the interface link state. To avoid LORs * between driver lock and upper layer locks, as well as possible * recursions, we post event to taskqueue, and all job * is done in static do_link_state_change(). */ void if_link_state_change(struct ifnet *ifp, int link_state) { /* Return if state hasn't changed. */ if (ifp->if_link_state == link_state) return; ifp->if_link_state = link_state; /* XXXGL: reference ifp? */ taskqueue_enqueue(taskqueue_swi, &ifp->if_linktask); } static void do_link_state_change(void *arg, int pending) { struct ifnet *ifp; int link_state; ifp = arg; link_state = ifp->if_link_state; CURVNET_SET(ifp->if_vnet); rt_ifmsg(ifp, 0); if (ifp->if_vlantrunk != NULL) (*vlan_link_state_p)(ifp); if ((ifp->if_type == IFT_ETHER || ifp->if_type == IFT_L2VLAN) && ifp->if_l2com != NULL) (*ng_ether_link_state_p)(ifp, link_state); if (ifp->if_carp) (*carp_linkstate_p)(ifp); if (ifp->if_bridge) ifp->if_bridge_linkstate(ifp); if (ifp->if_lagg) (*lagg_linkstate_p)(ifp, link_state); if (IS_DEFAULT_VNET(curvnet)) devctl_notify("IFNET", ifp->if_xname, (link_state == LINK_STATE_UP) ? "LINK_UP" : "LINK_DOWN", NULL); if (pending > 1) if_printf(ifp, "%d link states coalesced\n", pending); if (log_link_state_change) if_printf(ifp, "link state changed to %s\n", (link_state == LINK_STATE_UP) ? "UP" : "DOWN" ); EVENTHANDLER_INVOKE(ifnet_link_event, ifp, link_state); CURVNET_RESTORE(); } /* * Mark an interface down and notify protocols of * the transition. */ void if_down(struct ifnet *ifp) { EVENTHANDLER_INVOKE(ifnet_event, ifp, IFNET_EVENT_DOWN); if_unroute(ifp, IFF_UP, AF_UNSPEC); } /* * Mark an interface up and notify protocols of * the transition. */ void if_up(struct ifnet *ifp) { if_route(ifp, IFF_UP, AF_UNSPEC); EVENTHANDLER_INVOKE(ifnet_event, ifp, IFNET_EVENT_UP); } /* * Flush an interface queue. */ void if_qflush(struct ifnet *ifp) { struct mbuf *m, *n; struct ifaltq *ifq; ifq = &ifp->if_snd; IFQ_LOCK(ifq); #ifdef ALTQ if (ALTQ_IS_ENABLED(ifq)) ALTQ_PURGE(ifq); #endif n = ifq->ifq_head; while ((m = n) != NULL) { n = m->m_nextpkt; m_freem(m); } ifq->ifq_head = 0; ifq->ifq_tail = 0; ifq->ifq_len = 0; IFQ_UNLOCK(ifq); } /* * Map interface name to interface structure pointer, with or without * returning a reference. */ struct ifnet * ifunit_ref(const char *name) { struct epoch_tracker et; struct ifnet *ifp; NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { if (strncmp(name, ifp->if_xname, IFNAMSIZ) == 0 && !(ifp->if_flags & IFF_DYING)) break; } if (ifp != NULL) { if_ref(ifp); MPASS(ifindex_table[ifp->if_index].ife_ifnet == ifp); } NET_EPOCH_EXIT(et); return (ifp); } struct ifnet * ifunit(const char *name) { struct epoch_tracker et; struct ifnet *ifp; NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { if (strncmp(name, ifp->if_xname, IFNAMSIZ) == 0) break; } NET_EPOCH_EXIT(et); return (ifp); } void * ifr_buffer_get_buffer(void *data) { union ifreq_union *ifrup; ifrup = data; #ifdef COMPAT_FREEBSD32 if (SV_CURPROC_FLAG(SV_ILP32)) return ((void *)(uintptr_t) ifrup->ifr32.ifr_ifru.ifru_buffer.buffer); #endif return (ifrup->ifr.ifr_ifru.ifru_buffer.buffer); } static void ifr_buffer_set_buffer_null(void *data) { union ifreq_union *ifrup; ifrup = data; #ifdef COMPAT_FREEBSD32 if (SV_CURPROC_FLAG(SV_ILP32)) ifrup->ifr32.ifr_ifru.ifru_buffer.buffer = 0; else #endif ifrup->ifr.ifr_ifru.ifru_buffer.buffer = NULL; } size_t ifr_buffer_get_length(void *data) { union ifreq_union *ifrup; ifrup = data; #ifdef COMPAT_FREEBSD32 if (SV_CURPROC_FLAG(SV_ILP32)) return (ifrup->ifr32.ifr_ifru.ifru_buffer.length); #endif return (ifrup->ifr.ifr_ifru.ifru_buffer.length); } static void ifr_buffer_set_length(void *data, size_t len) { union ifreq_union *ifrup; ifrup = data; #ifdef COMPAT_FREEBSD32 if (SV_CURPROC_FLAG(SV_ILP32)) ifrup->ifr32.ifr_ifru.ifru_buffer.length = len; else #endif ifrup->ifr.ifr_ifru.ifru_buffer.length = len; } void * ifr_data_get_ptr(void *ifrp) { union ifreq_union *ifrup; ifrup = ifrp; #ifdef COMPAT_FREEBSD32 if (SV_CURPROC_FLAG(SV_ILP32)) return ((void *)(uintptr_t) ifrup->ifr32.ifr_ifru.ifru_data); #endif return (ifrup->ifr.ifr_ifru.ifru_data); } struct ifcap_nv_bit_name { uint64_t cap_bit; const char *cap_name; }; #define CAPNV(x) {.cap_bit = IFCAP_##x, \ .cap_name = __CONCAT(IFCAP_, __CONCAT(x, _NAME)) } const struct ifcap_nv_bit_name ifcap_nv_bit_names[] = { CAPNV(RXCSUM), CAPNV(TXCSUM), CAPNV(NETCONS), CAPNV(VLAN_MTU), CAPNV(VLAN_HWTAGGING), CAPNV(JUMBO_MTU), CAPNV(POLLING), CAPNV(VLAN_HWCSUM), CAPNV(TSO4), CAPNV(TSO6), CAPNV(LRO), CAPNV(WOL_UCAST), CAPNV(WOL_MCAST), CAPNV(WOL_MAGIC), CAPNV(TOE4), CAPNV(TOE6), CAPNV(VLAN_HWFILTER), CAPNV(VLAN_HWTSO), CAPNV(LINKSTATE), CAPNV(NETMAP), CAPNV(RXCSUM_IPV6), CAPNV(TXCSUM_IPV6), CAPNV(HWSTATS), CAPNV(TXRTLMT), CAPNV(HWRXTSTMP), CAPNV(MEXTPG), CAPNV(TXTLS4), CAPNV(TXTLS6), CAPNV(VXLAN_HWCSUM), CAPNV(VXLAN_HWTSO), CAPNV(TXTLS_RTLMT), {0, NULL} }; #define CAP2NV(x) {.cap_bit = IFCAP2_##x, \ .cap_name = __CONCAT(IFCAP2_, __CONCAT(x, _NAME)) } const struct ifcap_nv_bit_name ifcap2_nv_bit_names[] = { CAP2NV(RXTLS4), CAP2NV(RXTLS6), {0, NULL} }; #undef CAPNV #undef CAP2NV int if_capnv_to_capint(const nvlist_t *nv, int *old_cap, const struct ifcap_nv_bit_name *nn, bool all) { int i, res; res = 0; for (i = 0; nn[i].cap_name != NULL; i++) { if (nvlist_exists_bool(nv, nn[i].cap_name)) { if (all || nvlist_get_bool(nv, nn[i].cap_name)) res |= nn[i].cap_bit; } else { res |= *old_cap & nn[i].cap_bit; } } return (res); } void if_capint_to_capnv(nvlist_t *nv, const struct ifcap_nv_bit_name *nn, int ifr_cap, int ifr_req) { int i; for (i = 0; nn[i].cap_name != NULL; i++) { if ((nn[i].cap_bit & ifr_cap) != 0) { nvlist_add_bool(nv, nn[i].cap_name, (nn[i].cap_bit & ifr_req) != 0); } } } /* * Hardware specific interface ioctls. */ int ifhwioctl(u_long cmd, struct ifnet *ifp, caddr_t data, struct thread *td) { struct ifreq *ifr; int error = 0, do_ifup = 0; int new_flags, temp_flags; size_t namelen, onamelen; size_t descrlen, nvbuflen; char *descrbuf; char new_name[IFNAMSIZ]; char old_name[IFNAMSIZ], strbuf[IFNAMSIZ + 8]; struct ifaddr *ifa; struct sockaddr_dl *sdl; void *buf; nvlist_t *nvcap; struct siocsifcapnv_driver_data drv_ioctl_data; ifr = (struct ifreq *)data; switch (cmd) { case SIOCGIFINDEX: ifr->ifr_index = ifp->if_index; break; case SIOCGIFFLAGS: temp_flags = ifp->if_flags | ifp->if_drv_flags; ifr->ifr_flags = temp_flags & 0xffff; ifr->ifr_flagshigh = temp_flags >> 16; break; case SIOCGIFCAP: ifr->ifr_reqcap = ifp->if_capabilities; ifr->ifr_curcap = ifp->if_capenable; break; case SIOCGIFCAPNV: if ((ifp->if_capabilities & IFCAP_NV) == 0) { error = EINVAL; break; } buf = NULL; nvcap = nvlist_create(0); for (;;) { if_capint_to_capnv(nvcap, ifcap_nv_bit_names, ifp->if_capabilities, ifp->if_capenable); if_capint_to_capnv(nvcap, ifcap2_nv_bit_names, ifp->if_capabilities2, ifp->if_capenable2); error = (*ifp->if_ioctl)(ifp, SIOCGIFCAPNV, __DECONST(caddr_t, nvcap)); if (error != 0) { if_printf(ifp, "SIOCGIFCAPNV driver mistake: nvlist error %d\n", error); break; } buf = nvlist_pack(nvcap, &nvbuflen); if (buf == NULL) { error = nvlist_error(nvcap); if (error == 0) error = EDOOFUS; break; } if (nvbuflen > ifr->ifr_cap_nv.buf_length) { ifr->ifr_cap_nv.length = nvbuflen; ifr->ifr_cap_nv.buffer = NULL; error = EFBIG; break; } ifr->ifr_cap_nv.length = nvbuflen; error = copyout(buf, ifr->ifr_cap_nv.buffer, nvbuflen); break; } free(buf, M_NVLIST); nvlist_destroy(nvcap); break; case SIOCGIFDATA: { struct if_data ifd; /* Ensure uninitialised padding is not leaked. */ memset(&ifd, 0, sizeof(ifd)); if_data_copy(ifp, &ifd); error = copyout(&ifd, ifr_data_get_ptr(ifr), sizeof(ifd)); break; } #ifdef MAC case SIOCGIFMAC: error = mac_ifnet_ioctl_get(td->td_ucred, ifr, ifp); break; #endif case SIOCGIFMETRIC: ifr->ifr_metric = ifp->if_metric; break; case SIOCGIFMTU: ifr->ifr_mtu = ifp->if_mtu; break; case SIOCGIFPHYS: /* XXXGL: did this ever worked? */ ifr->ifr_phys = 0; break; case SIOCGIFDESCR: error = 0; sx_slock(&ifdescr_sx); if (ifp->if_description == NULL) error = ENOMSG; else { /* space for terminating nul */ descrlen = strlen(ifp->if_description) + 1; if (ifr_buffer_get_length(ifr) < descrlen) ifr_buffer_set_buffer_null(ifr); else error = copyout(ifp->if_description, ifr_buffer_get_buffer(ifr), descrlen); ifr_buffer_set_length(ifr, descrlen); } sx_sunlock(&ifdescr_sx); break; case SIOCSIFDESCR: error = priv_check(td, PRIV_NET_SETIFDESCR); if (error) return (error); /* * Copy only (length-1) bytes to make sure that * if_description is always nul terminated. The * length parameter is supposed to count the * terminating nul in. */ if (ifr_buffer_get_length(ifr) > ifdescr_maxlen) return (ENAMETOOLONG); else if (ifr_buffer_get_length(ifr) == 0) descrbuf = NULL; else { descrbuf = if_allocdescr(ifr_buffer_get_length(ifr), M_WAITOK); error = copyin(ifr_buffer_get_buffer(ifr), descrbuf, ifr_buffer_get_length(ifr) - 1); if (error) { if_freedescr(descrbuf); break; } } if_setdescr(ifp, descrbuf); getmicrotime(&ifp->if_lastchange); break; case SIOCGIFFIB: ifr->ifr_fib = ifp->if_fib; break; case SIOCSIFFIB: error = priv_check(td, PRIV_NET_SETIFFIB); if (error) return (error); if (ifr->ifr_fib >= rt_numfibs) return (EINVAL); ifp->if_fib = ifr->ifr_fib; break; case SIOCSIFFLAGS: error = priv_check(td, PRIV_NET_SETIFFLAGS); if (error) return (error); /* * Currently, no driver owned flags pass the IFF_CANTCHANGE * check, so we don't need special handling here yet. */ new_flags = (ifr->ifr_flags & 0xffff) | (ifr->ifr_flagshigh << 16); if (ifp->if_flags & IFF_UP && (new_flags & IFF_UP) == 0) { if_down(ifp); } else if (new_flags & IFF_UP && (ifp->if_flags & IFF_UP) == 0) { do_ifup = 1; } /* See if permanently promiscuous mode bit is about to flip */ if ((ifp->if_flags ^ new_flags) & IFF_PPROMISC) { if (new_flags & IFF_PPROMISC) ifp->if_flags |= IFF_PROMISC; else if (ifp->if_pcount == 0) ifp->if_flags &= ~IFF_PROMISC; if (log_promisc_mode_change) if_printf(ifp, "permanently promiscuous mode %s\n", ((new_flags & IFF_PPROMISC) ? "enabled" : "disabled")); } ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) | (new_flags &~ IFF_CANTCHANGE); if (ifp->if_ioctl) { (void) (*ifp->if_ioctl)(ifp, cmd, data); } if (do_ifup) if_up(ifp); getmicrotime(&ifp->if_lastchange); break; case SIOCSIFCAP: error = priv_check(td, PRIV_NET_SETIFCAP); if (error != 0) return (error); if (ifp->if_ioctl == NULL) return (EOPNOTSUPP); if (ifr->ifr_reqcap & ~ifp->if_capabilities) return (EINVAL); error = (*ifp->if_ioctl)(ifp, cmd, data); if (error == 0) getmicrotime(&ifp->if_lastchange); break; case SIOCSIFCAPNV: error = priv_check(td, PRIV_NET_SETIFCAP); if (error != 0) return (error); if (ifp->if_ioctl == NULL) return (EOPNOTSUPP); if ((ifp->if_capabilities & IFCAP_NV) == 0) return (EINVAL); if (ifr->ifr_cap_nv.length > IFR_CAP_NV_MAXBUFSIZE) return (EINVAL); nvcap = NULL; buf = malloc(ifr->ifr_cap_nv.length, M_TEMP, M_WAITOK); for (;;) { error = copyin(ifr->ifr_cap_nv.buffer, buf, ifr->ifr_cap_nv.length); if (error != 0) break; nvcap = nvlist_unpack(buf, ifr->ifr_cap_nv.length, 0); if (nvcap == NULL) { error = EINVAL; break; } drv_ioctl_data.reqcap = if_capnv_to_capint(nvcap, &ifp->if_capenable, ifcap_nv_bit_names, false); if ((drv_ioctl_data.reqcap & ~ifp->if_capabilities) != 0) { error = EINVAL; break; } drv_ioctl_data.reqcap2 = if_capnv_to_capint(nvcap, &ifp->if_capenable2, ifcap2_nv_bit_names, false); if ((drv_ioctl_data.reqcap2 & ~ifp->if_capabilities2) != 0) { error = EINVAL; break; } drv_ioctl_data.nvcap = nvcap; error = (*ifp->if_ioctl)(ifp, SIOCSIFCAPNV, (caddr_t)&drv_ioctl_data); break; } nvlist_destroy(nvcap); free(buf, M_TEMP); if (error == 0) getmicrotime(&ifp->if_lastchange); break; #ifdef MAC case SIOCSIFMAC: error = mac_ifnet_ioctl_set(td->td_ucred, ifr, ifp); break; #endif case SIOCSIFNAME: error = priv_check(td, PRIV_NET_SETIFNAME); if (error) return (error); error = copyinstr(ifr_data_get_ptr(ifr), new_name, IFNAMSIZ, NULL); if (error != 0) return (error); if (new_name[0] == '\0') return (EINVAL); if (strcmp(new_name, ifp->if_xname) == 0) break; if (ifunit(new_name) != NULL) return (EEXIST); /* * XXX: Locking. Nothing else seems to lock if_flags, * and there are numerous other races with the * ifunit() checks not being atomic with namespace * changes (renames, vmoves, if_attach, etc). */ ifp->if_flags |= IFF_RENAMING; EVENTHANDLER_INVOKE(ifnet_departure_event, ifp); if_printf(ifp, "changing name to '%s'\n", new_name); IF_ADDR_WLOCK(ifp); strlcpy(old_name, ifp->if_xname, sizeof(old_name)); strlcpy(ifp->if_xname, new_name, sizeof(ifp->if_xname)); ifa = ifp->if_addr; sdl = (struct sockaddr_dl *)ifa->ifa_addr; namelen = strlen(new_name); onamelen = sdl->sdl_nlen; /* * Move the address if needed. This is safe because we * allocate space for a name of length IFNAMSIZ when we * create this in if_attach(). */ if (namelen != onamelen) { bcopy(sdl->sdl_data + onamelen, sdl->sdl_data + namelen, sdl->sdl_alen); } bcopy(new_name, sdl->sdl_data, namelen); sdl->sdl_nlen = namelen; sdl = (struct sockaddr_dl *)ifa->ifa_netmask; bzero(sdl->sdl_data, onamelen); while (namelen != 0) sdl->sdl_data[--namelen] = 0xff; IF_ADDR_WUNLOCK(ifp); EVENTHANDLER_INVOKE(ifnet_arrival_event, ifp); ifp->if_flags &= ~IFF_RENAMING; snprintf(strbuf, sizeof(strbuf), "name=%s", new_name); devctl_notify("IFNET", old_name, "RENAME", strbuf); break; #ifdef VIMAGE case SIOCSIFVNET: error = priv_check(td, PRIV_NET_SETIFVNET); if (error) return (error); error = if_vmove_loan(td, ifp, ifr->ifr_name, ifr->ifr_jid); break; #endif case SIOCSIFMETRIC: error = priv_check(td, PRIV_NET_SETIFMETRIC); if (error) return (error); ifp->if_metric = ifr->ifr_metric; getmicrotime(&ifp->if_lastchange); break; case SIOCSIFPHYS: error = priv_check(td, PRIV_NET_SETIFPHYS); if (error) return (error); if (ifp->if_ioctl == NULL) return (EOPNOTSUPP); error = (*ifp->if_ioctl)(ifp, cmd, data); if (error == 0) getmicrotime(&ifp->if_lastchange); break; case SIOCSIFMTU: { u_long oldmtu = ifp->if_mtu; error = priv_check(td, PRIV_NET_SETIFMTU); if (error) return (error); if (ifr->ifr_mtu < IF_MINMTU || ifr->ifr_mtu > IF_MAXMTU) return (EINVAL); if (ifp->if_ioctl == NULL) return (EOPNOTSUPP); /* Disallow MTU changes on bridge member interfaces. */ if (ifp->if_bridge) return (EOPNOTSUPP); error = (*ifp->if_ioctl)(ifp, cmd, data); if (error == 0) { getmicrotime(&ifp->if_lastchange); rt_ifmsg(ifp, 0); #ifdef INET DEBUGNET_NOTIFY_MTU(ifp); #endif } /* * If the link MTU changed, do network layer specific procedure. */ if (ifp->if_mtu != oldmtu) { #ifdef INET6 nd6_setmtu(ifp); #endif rt_updatemtu(ifp); } break; } case SIOCADDMULTI: case SIOCDELMULTI: if (cmd == SIOCADDMULTI) error = priv_check(td, PRIV_NET_ADDMULTI); else error = priv_check(td, PRIV_NET_DELMULTI); if (error) return (error); /* Don't allow group membership on non-multicast interfaces. */ if ((ifp->if_flags & IFF_MULTICAST) == 0) return (EOPNOTSUPP); /* Don't let users screw up protocols' entries. */ if (ifr->ifr_addr.sa_family != AF_LINK) return (EINVAL); if (cmd == SIOCADDMULTI) { struct epoch_tracker et; struct ifmultiaddr *ifma; /* * Userland is only permitted to join groups once * via the if_addmulti() KPI, because it cannot hold * struct ifmultiaddr * between calls. It may also * lose a race while we check if the membership * already exists. */ NET_EPOCH_ENTER(et); ifma = if_findmulti(ifp, &ifr->ifr_addr); NET_EPOCH_EXIT(et); if (ifma != NULL) error = EADDRINUSE; else error = if_addmulti(ifp, &ifr->ifr_addr, &ifma); } else { error = if_delmulti(ifp, &ifr->ifr_addr); } if (error == 0) getmicrotime(&ifp->if_lastchange); break; case SIOCSIFPHYADDR: case SIOCDIFPHYADDR: #ifdef INET6 case SIOCSIFPHYADDR_IN6: #endif case SIOCSIFMEDIA: case SIOCSIFGENERIC: error = priv_check(td, PRIV_NET_HWIOCTL); if (error) return (error); if (ifp->if_ioctl == NULL) return (EOPNOTSUPP); error = (*ifp->if_ioctl)(ifp, cmd, data); if (error == 0) getmicrotime(&ifp->if_lastchange); break; case SIOCGIFSTATUS: case SIOCGIFPSRCADDR: case SIOCGIFPDSTADDR: case SIOCGIFMEDIA: case SIOCGIFXMEDIA: case SIOCGIFGENERIC: case SIOCGIFRSSKEY: case SIOCGIFRSSHASH: case SIOCGIFDOWNREASON: if (ifp->if_ioctl == NULL) return (EOPNOTSUPP); error = (*ifp->if_ioctl)(ifp, cmd, data); break; case SIOCSIFLLADDR: error = priv_check(td, PRIV_NET_SETLLADDR); if (error) return (error); error = if_setlladdr(ifp, ifr->ifr_addr.sa_data, ifr->ifr_addr.sa_len); break; case SIOCGHWADDR: error = if_gethwaddr(ifp, ifr); break; case SIOCAIFGROUP: error = priv_check(td, PRIV_NET_ADDIFGROUP); if (error) return (error); error = if_addgroup(ifp, ((struct ifgroupreq *)data)->ifgr_group); if (error != 0) return (error); break; case SIOCGIFGROUP: { struct epoch_tracker et; NET_EPOCH_ENTER(et); error = if_getgroup((struct ifgroupreq *)data, ifp); NET_EPOCH_EXIT(et); break; } case SIOCDIFGROUP: error = priv_check(td, PRIV_NET_DELIFGROUP); if (error) return (error); error = if_delgroup(ifp, ((struct ifgroupreq *)data)->ifgr_group); if (error != 0) return (error); break; default: error = ENOIOCTL; break; } return (error); } /* * Interface ioctls. */ int ifioctl(struct socket *so, u_long cmd, caddr_t data, struct thread *td) { #ifdef COMPAT_FREEBSD32 union { struct ifconf ifc; struct ifdrv ifd; struct ifgroupreq ifgr; struct ifmediareq ifmr; } thunk; u_long saved_cmd; struct ifconf32 *ifc32; struct ifdrv32 *ifd32; struct ifgroupreq32 *ifgr32; struct ifmediareq32 *ifmr32; #endif struct ifnet *ifp; struct ifreq *ifr; int error; int oif_flags; #ifdef VIMAGE bool shutdown; #endif CURVNET_SET(so->so_vnet); #ifdef VIMAGE /* Make sure the VNET is stable. */ shutdown = VNET_IS_SHUTTING_DOWN(so->so_vnet); if (shutdown) { CURVNET_RESTORE(); return (EBUSY); } #endif #ifdef COMPAT_FREEBSD32 saved_cmd = cmd; switch (cmd) { case SIOCGIFCONF32: ifc32 = (struct ifconf32 *)data; thunk.ifc.ifc_len = ifc32->ifc_len; thunk.ifc.ifc_buf = PTRIN(ifc32->ifc_buf); data = (caddr_t)&thunk.ifc; cmd = SIOCGIFCONF; break; case SIOCGDRVSPEC32: case SIOCSDRVSPEC32: ifd32 = (struct ifdrv32 *)data; memcpy(thunk.ifd.ifd_name, ifd32->ifd_name, sizeof(thunk.ifd.ifd_name)); thunk.ifd.ifd_cmd = ifd32->ifd_cmd; thunk.ifd.ifd_len = ifd32->ifd_len; thunk.ifd.ifd_data = PTRIN(ifd32->ifd_data); data = (caddr_t)&thunk.ifd; cmd = _IOC_NEWTYPE(cmd, struct ifdrv); break; case SIOCAIFGROUP32: case SIOCGIFGROUP32: case SIOCDIFGROUP32: case SIOCGIFGMEMB32: ifgr32 = (struct ifgroupreq32 *)data; memcpy(thunk.ifgr.ifgr_name, ifgr32->ifgr_name, sizeof(thunk.ifgr.ifgr_name)); thunk.ifgr.ifgr_len = ifgr32->ifgr_len; switch (cmd) { case SIOCAIFGROUP32: case SIOCDIFGROUP32: memcpy(thunk.ifgr.ifgr_group, ifgr32->ifgr_group, sizeof(thunk.ifgr.ifgr_group)); break; case SIOCGIFGROUP32: case SIOCGIFGMEMB32: thunk.ifgr.ifgr_groups = PTRIN(ifgr32->ifgr_groups); break; } data = (caddr_t)&thunk.ifgr; cmd = _IOC_NEWTYPE(cmd, struct ifgroupreq); break; case SIOCGIFMEDIA32: case SIOCGIFXMEDIA32: ifmr32 = (struct ifmediareq32 *)data; memcpy(thunk.ifmr.ifm_name, ifmr32->ifm_name, sizeof(thunk.ifmr.ifm_name)); thunk.ifmr.ifm_current = ifmr32->ifm_current; thunk.ifmr.ifm_mask = ifmr32->ifm_mask; thunk.ifmr.ifm_status = ifmr32->ifm_status; thunk.ifmr.ifm_active = ifmr32->ifm_active; thunk.ifmr.ifm_count = ifmr32->ifm_count; thunk.ifmr.ifm_ulist = PTRIN(ifmr32->ifm_ulist); data = (caddr_t)&thunk.ifmr; cmd = _IOC_NEWTYPE(cmd, struct ifmediareq); break; } #endif switch (cmd) { case SIOCGIFCONF: error = ifconf(cmd, data); goto out_noref; } ifr = (struct ifreq *)data; switch (cmd) { #ifdef VIMAGE case SIOCSIFRVNET: error = priv_check(td, PRIV_NET_SETIFVNET); if (error == 0) error = if_vmove_reclaim(td, ifr->ifr_name, ifr->ifr_jid); goto out_noref; #endif case SIOCIFCREATE: case SIOCIFCREATE2: error = priv_check(td, PRIV_NET_IFCREATE); if (error == 0) error = if_clone_create(ifr->ifr_name, sizeof(ifr->ifr_name), cmd == SIOCIFCREATE2 ? ifr_data_get_ptr(ifr) : NULL); goto out_noref; case SIOCIFDESTROY: error = priv_check(td, PRIV_NET_IFDESTROY); if (error == 0) { sx_xlock(&ifnet_detach_sxlock); error = if_clone_destroy(ifr->ifr_name); sx_xunlock(&ifnet_detach_sxlock); } goto out_noref; case SIOCIFGCLONERS: error = if_clone_list((struct if_clonereq *)data); goto out_noref; case SIOCGIFGMEMB: error = if_getgroupmembers((struct ifgroupreq *)data); goto out_noref; #if defined(INET) || defined(INET6) case SIOCSVH: case SIOCGVH: if (carp_ioctl_p == NULL) error = EPROTONOSUPPORT; else error = (*carp_ioctl_p)(ifr, cmd, td); goto out_noref; #endif } ifp = ifunit_ref(ifr->ifr_name); if (ifp == NULL) { error = ENXIO; goto out_noref; } error = ifhwioctl(cmd, ifp, data, td); if (error != ENOIOCTL) goto out_ref; oif_flags = ifp->if_flags; if (so->so_proto == NULL) { error = EOPNOTSUPP; goto out_ref; } /* * Pass the request on to the socket control method, and if the * latter returns EOPNOTSUPP, directly to the interface. * * Make an exception for the legacy SIOCSIF* requests. Drivers * trust SIOCSIFADDR et al to come from an already privileged * layer, and do not perform any credentials checks or input * validation. */ error = so->so_proto->pr_control(so, cmd, data, ifp, td); if (error == EOPNOTSUPP && ifp != NULL && ifp->if_ioctl != NULL && cmd != SIOCSIFADDR && cmd != SIOCSIFBRDADDR && cmd != SIOCSIFDSTADDR && cmd != SIOCSIFNETMASK) error = (*ifp->if_ioctl)(ifp, cmd, data); if ((oif_flags ^ ifp->if_flags) & IFF_UP) { #ifdef INET6 if (ifp->if_flags & IFF_UP) in6_if_up(ifp); #endif } out_ref: if_rele(ifp); out_noref: CURVNET_RESTORE(); #ifdef COMPAT_FREEBSD32 if (error != 0) return (error); switch (saved_cmd) { case SIOCGIFCONF32: ifc32->ifc_len = thunk.ifc.ifc_len; break; case SIOCGDRVSPEC32: /* * SIOCGDRVSPEC is IOWR, but nothing actually touches * the struct so just assert that ifd_len (the only * field it might make sense to update) hasn't * changed. */ KASSERT(thunk.ifd.ifd_len == ifd32->ifd_len, ("ifd_len was updated %u -> %zu", ifd32->ifd_len, thunk.ifd.ifd_len)); break; case SIOCGIFGROUP32: case SIOCGIFGMEMB32: ifgr32->ifgr_len = thunk.ifgr.ifgr_len; break; case SIOCGIFMEDIA32: case SIOCGIFXMEDIA32: ifmr32->ifm_current = thunk.ifmr.ifm_current; ifmr32->ifm_mask = thunk.ifmr.ifm_mask; ifmr32->ifm_status = thunk.ifmr.ifm_status; ifmr32->ifm_active = thunk.ifmr.ifm_active; ifmr32->ifm_count = thunk.ifmr.ifm_count; break; } #endif return (error); } /* * The code common to handling reference counted flags, * e.g., in ifpromisc() and if_allmulti(). * The "pflag" argument can specify a permanent mode flag to check, * such as IFF_PPROMISC for promiscuous mode; should be 0 if none. * * Only to be used on stack-owned flags, not driver-owned flags. */ static int if_setflag(struct ifnet *ifp, int flag, int pflag, int *refcount, int onswitch) { struct ifreq ifr; int error; int oldflags, oldcount; /* Sanity checks to catch programming errors */ KASSERT((flag & (IFF_DRV_OACTIVE|IFF_DRV_RUNNING)) == 0, ("%s: setting driver-owned flag %d", __func__, flag)); if (onswitch) KASSERT(*refcount >= 0, ("%s: increment negative refcount %d for flag %d", __func__, *refcount, flag)); else KASSERT(*refcount > 0, ("%s: decrement non-positive refcount %d for flag %d", __func__, *refcount, flag)); /* In case this mode is permanent, just touch refcount */ if (ifp->if_flags & pflag) { *refcount += onswitch ? 1 : -1; return (0); } /* Save ifnet parameters for if_ioctl() may fail */ oldcount = *refcount; oldflags = ifp->if_flags; /* * See if we aren't the only and touching refcount is enough. * Actually toggle interface flag if we are the first or last. */ if (onswitch) { if ((*refcount)++) return (0); ifp->if_flags |= flag; } else { if (--(*refcount)) return (0); ifp->if_flags &= ~flag; } /* Call down the driver since we've changed interface flags */ if (ifp->if_ioctl == NULL) { error = EOPNOTSUPP; goto recover; } ifr.ifr_flags = ifp->if_flags & 0xffff; ifr.ifr_flagshigh = ifp->if_flags >> 16; error = (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); if (error) goto recover; /* Notify userland that interface flags have changed */ rt_ifmsg(ifp, flag); return (0); recover: /* Recover after driver error */ *refcount = oldcount; ifp->if_flags = oldflags; return (error); } /* * Set/clear promiscuous mode on interface ifp based on the truth value * of pswitch. The calls are reference counted so that only the first * "on" request actually has an effect, as does the final "off" request. * Results are undefined if the "off" and "on" requests are not matched. */ int ifpromisc(struct ifnet *ifp, int pswitch) { int error; int oldflags = ifp->if_flags; error = if_setflag(ifp, IFF_PROMISC, IFF_PPROMISC, &ifp->if_pcount, pswitch); /* If promiscuous mode status has changed, log a message */ if (error == 0 && ((ifp->if_flags ^ oldflags) & IFF_PROMISC) && log_promisc_mode_change) if_printf(ifp, "promiscuous mode %s\n", (ifp->if_flags & IFF_PROMISC) ? "enabled" : "disabled"); return (error); } /* * Return interface configuration * of system. List may be used * in later ioctl's (above) to get * other information. */ /*ARGSUSED*/ static int ifconf(u_long cmd, caddr_t data) { struct ifconf *ifc = (struct ifconf *)data; struct ifnet *ifp; struct ifaddr *ifa; struct ifreq ifr; struct sbuf *sb; int error, full = 0, valid_len, max_len; /* Limit initial buffer size to maxphys to avoid DoS from userspace. */ max_len = maxphys - 1; /* Prevent hostile input from being able to crash the system */ if (ifc->ifc_len <= 0) return (EINVAL); again: if (ifc->ifc_len <= max_len) { max_len = ifc->ifc_len; full = 1; } sb = sbuf_new(NULL, NULL, max_len + 1, SBUF_FIXEDLEN); max_len = 0; valid_len = 0; IFNET_RLOCK(); CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { struct epoch_tracker et; int addrs; /* * Zero the ifr to make sure we don't disclose the contents * of the stack. */ memset(&ifr, 0, sizeof(ifr)); if (strlcpy(ifr.ifr_name, ifp->if_xname, sizeof(ifr.ifr_name)) >= sizeof(ifr.ifr_name)) { sbuf_delete(sb); IFNET_RUNLOCK(); return (ENAMETOOLONG); } addrs = 0; NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { struct sockaddr *sa = ifa->ifa_addr; if (prison_if(curthread->td_ucred, sa) != 0) continue; addrs++; if (sa->sa_len <= sizeof(*sa)) { if (sa->sa_len < sizeof(*sa)) { memset(&ifr.ifr_ifru.ifru_addr, 0, sizeof(ifr.ifr_ifru.ifru_addr)); memcpy(&ifr.ifr_ifru.ifru_addr, sa, sa->sa_len); } else ifr.ifr_ifru.ifru_addr = *sa; sbuf_bcat(sb, &ifr, sizeof(ifr)); max_len += sizeof(ifr); } else { sbuf_bcat(sb, &ifr, offsetof(struct ifreq, ifr_addr)); max_len += offsetof(struct ifreq, ifr_addr); sbuf_bcat(sb, sa, sa->sa_len); max_len += sa->sa_len; } if (sbuf_error(sb) == 0) valid_len = sbuf_len(sb); } NET_EPOCH_EXIT(et); if (addrs == 0) { sbuf_bcat(sb, &ifr, sizeof(ifr)); max_len += sizeof(ifr); if (sbuf_error(sb) == 0) valid_len = sbuf_len(sb); } } IFNET_RUNLOCK(); /* * If we didn't allocate enough space (uncommon), try again. If * we have already allocated as much space as we are allowed, * return what we've got. */ if (valid_len != max_len && !full) { sbuf_delete(sb); goto again; } ifc->ifc_len = valid_len; sbuf_finish(sb); error = copyout(sbuf_data(sb), ifc->ifc_req, ifc->ifc_len); sbuf_delete(sb); return (error); } /* * Just like ifpromisc(), but for all-multicast-reception mode. */ int if_allmulti(struct ifnet *ifp, int onswitch) { return (if_setflag(ifp, IFF_ALLMULTI, 0, &ifp->if_amcount, onswitch)); } struct ifmultiaddr * if_findmulti(struct ifnet *ifp, const struct sockaddr *sa) { struct ifmultiaddr *ifma; IF_ADDR_LOCK_ASSERT(ifp); CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (sa->sa_family == AF_LINK) { if (sa_dl_equal(ifma->ifma_addr, sa)) break; } else { if (sa_equal(ifma->ifma_addr, sa)) break; } } return ifma; } /* * Allocate a new ifmultiaddr and initialize based on passed arguments. We * make copies of passed sockaddrs. The ifmultiaddr will not be added to * the ifnet multicast address list here, so the caller must do that and * other setup work (such as notifying the device driver). The reference * count is initialized to 1. */ static struct ifmultiaddr * if_allocmulti(struct ifnet *ifp, struct sockaddr *sa, struct sockaddr *llsa, int mflags) { struct ifmultiaddr *ifma; struct sockaddr *dupsa; ifma = malloc(sizeof *ifma, M_IFMADDR, mflags | M_ZERO); if (ifma == NULL) return (NULL); dupsa = malloc(sa->sa_len, M_IFMADDR, mflags); if (dupsa == NULL) { free(ifma, M_IFMADDR); return (NULL); } bcopy(sa, dupsa, sa->sa_len); ifma->ifma_addr = dupsa; ifma->ifma_ifp = ifp; ifma->ifma_refcount = 1; ifma->ifma_protospec = NULL; if (llsa == NULL) { ifma->ifma_lladdr = NULL; return (ifma); } dupsa = malloc(llsa->sa_len, M_IFMADDR, mflags); if (dupsa == NULL) { free(ifma->ifma_addr, M_IFMADDR); free(ifma, M_IFMADDR); return (NULL); } bcopy(llsa, dupsa, llsa->sa_len); ifma->ifma_lladdr = dupsa; return (ifma); } /* * if_freemulti: free ifmultiaddr structure and possibly attached related * addresses. The caller is responsible for implementing reference * counting, notifying the driver, handling routing messages, and releasing * any dependent link layer state. */ #ifdef MCAST_VERBOSE extern void kdb_backtrace(void); #endif static void if_freemulti_internal(struct ifmultiaddr *ifma) { KASSERT(ifma->ifma_refcount == 0, ("if_freemulti: refcount %d", ifma->ifma_refcount)); if (ifma->ifma_lladdr != NULL) free(ifma->ifma_lladdr, M_IFMADDR); #ifdef MCAST_VERBOSE kdb_backtrace(); printf("%s freeing ifma: %p\n", __func__, ifma); #endif free(ifma->ifma_addr, M_IFMADDR); free(ifma, M_IFMADDR); } static void if_destroymulti(epoch_context_t ctx) { struct ifmultiaddr *ifma; ifma = __containerof(ctx, struct ifmultiaddr, ifma_epoch_ctx); if_freemulti_internal(ifma); } void if_freemulti(struct ifmultiaddr *ifma) { KASSERT(ifma->ifma_refcount == 0, ("if_freemulti_epoch: refcount %d", ifma->ifma_refcount)); NET_EPOCH_CALL(if_destroymulti, &ifma->ifma_epoch_ctx); } /* * Register an additional multicast address with a network interface. * * - If the address is already present, bump the reference count on the * address and return. * - If the address is not link-layer, look up a link layer address. * - Allocate address structures for one or both addresses, and attach to the * multicast address list on the interface. If automatically adding a link * layer address, the protocol address will own a reference to the link * layer address, to be freed when it is freed. * - Notify the network device driver of an addition to the multicast address * list. * * 'sa' points to caller-owned memory with the desired multicast address. * * 'retifma' will be used to return a pointer to the resulting multicast * address reference, if desired. */ int if_addmulti(struct ifnet *ifp, struct sockaddr *sa, struct ifmultiaddr **retifma) { struct ifmultiaddr *ifma, *ll_ifma; struct sockaddr *llsa; struct sockaddr_dl sdl; int error; #ifdef INET IN_MULTI_LIST_UNLOCK_ASSERT(); #endif #ifdef INET6 IN6_MULTI_LIST_UNLOCK_ASSERT(); #endif /* * If the address is already present, return a new reference to it; * otherwise, allocate storage and set up a new address. */ IF_ADDR_WLOCK(ifp); ifma = if_findmulti(ifp, sa); if (ifma != NULL) { ifma->ifma_refcount++; if (retifma != NULL) *retifma = ifma; IF_ADDR_WUNLOCK(ifp); return (0); } /* * The address isn't already present; resolve the protocol address * into a link layer address, and then look that up, bump its * refcount or allocate an ifma for that also. * Most link layer resolving functions returns address data which * fits inside default sockaddr_dl structure. However callback * can allocate another sockaddr structure, in that case we need to * free it later. */ llsa = NULL; ll_ifma = NULL; if (ifp->if_resolvemulti != NULL) { /* Provide called function with buffer size information */ sdl.sdl_len = sizeof(sdl); llsa = (struct sockaddr *)&sdl; error = ifp->if_resolvemulti(ifp, &llsa, sa); if (error) goto unlock_out; } /* * Allocate the new address. Don't hook it up yet, as we may also * need to allocate a link layer multicast address. */ ifma = if_allocmulti(ifp, sa, llsa, M_NOWAIT); if (ifma == NULL) { error = ENOMEM; goto free_llsa_out; } /* * If a link layer address is found, we'll need to see if it's * already present in the address list, or allocate is as well. * When this block finishes, the link layer address will be on the * list. */ if (llsa != NULL) { ll_ifma = if_findmulti(ifp, llsa); if (ll_ifma == NULL) { ll_ifma = if_allocmulti(ifp, llsa, NULL, M_NOWAIT); if (ll_ifma == NULL) { --ifma->ifma_refcount; if_freemulti(ifma); error = ENOMEM; goto free_llsa_out; } ll_ifma->ifma_flags |= IFMA_F_ENQUEUED; CK_STAILQ_INSERT_HEAD(&ifp->if_multiaddrs, ll_ifma, ifma_link); } else ll_ifma->ifma_refcount++; ifma->ifma_llifma = ll_ifma; } /* * We now have a new multicast address, ifma, and possibly a new or * referenced link layer address. Add the primary address to the * ifnet address list. */ ifma->ifma_flags |= IFMA_F_ENQUEUED; CK_STAILQ_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link); if (retifma != NULL) *retifma = ifma; /* * Must generate the message while holding the lock so that 'ifma' * pointer is still valid. */ rt_newmaddrmsg(RTM_NEWMADDR, ifma); IF_ADDR_WUNLOCK(ifp); /* * We are certain we have added something, so call down to the * interface to let them know about it. */ if (ifp->if_ioctl != NULL) { if (THREAD_CAN_SLEEP()) (void )(*ifp->if_ioctl)(ifp, SIOCADDMULTI, 0); else taskqueue_enqueue(taskqueue_swi, &ifp->if_addmultitask); } if ((llsa != NULL) && (llsa != (struct sockaddr *)&sdl)) link_free_sdl(llsa); return (0); free_llsa_out: if ((llsa != NULL) && (llsa != (struct sockaddr *)&sdl)) link_free_sdl(llsa); unlock_out: IF_ADDR_WUNLOCK(ifp); return (error); } static void if_siocaddmulti(void *arg, int pending) { struct ifnet *ifp; ifp = arg; #ifdef DIAGNOSTIC if (pending > 1) if_printf(ifp, "%d SIOCADDMULTI coalesced\n", pending); #endif CURVNET_SET(ifp->if_vnet); (void )(*ifp->if_ioctl)(ifp, SIOCADDMULTI, 0); CURVNET_RESTORE(); } /* * Delete a multicast group membership by network-layer group address. * * Returns ENOENT if the entry could not be found. If ifp no longer * exists, results are undefined. This entry point should only be used * from subsystems which do appropriate locking to hold ifp for the * duration of the call. * Network-layer protocol domains must use if_delmulti_ifma(). */ int if_delmulti(struct ifnet *ifp, struct sockaddr *sa) { struct ifmultiaddr *ifma; int lastref; KASSERT(ifp, ("%s: NULL ifp", __func__)); IF_ADDR_WLOCK(ifp); lastref = 0; ifma = if_findmulti(ifp, sa); if (ifma != NULL) lastref = if_delmulti_locked(ifp, ifma, 0); IF_ADDR_WUNLOCK(ifp); if (ifma == NULL) return (ENOENT); if (lastref && ifp->if_ioctl != NULL) { (void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, 0); } return (0); } /* * Delete all multicast group membership for an interface. * Should be used to quickly flush all multicast filters. */ void if_delallmulti(struct ifnet *ifp) { struct ifmultiaddr *ifma; struct ifmultiaddr *next; IF_ADDR_WLOCK(ifp); CK_STAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) if_delmulti_locked(ifp, ifma, 0); IF_ADDR_WUNLOCK(ifp); } void if_delmulti_ifma(struct ifmultiaddr *ifma) { if_delmulti_ifma_flags(ifma, 0); } /* * Delete a multicast group membership by group membership pointer. * Network-layer protocol domains must use this routine. * * It is safe to call this routine if the ifp disappeared. */ void if_delmulti_ifma_flags(struct ifmultiaddr *ifma, int flags) { struct ifnet *ifp; int lastref; MCDPRINTF("%s freeing ifma: %p\n", __func__, ifma); #ifdef INET IN_MULTI_LIST_UNLOCK_ASSERT(); #endif ifp = ifma->ifma_ifp; #ifdef DIAGNOSTIC if (ifp == NULL) { printf("%s: ifma_ifp seems to be detached\n", __func__); } else { struct epoch_tracker et; struct ifnet *oifp; NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(oifp, &V_ifnet, if_link) if (ifp == oifp) break; NET_EPOCH_EXIT(et); if (ifp != oifp) ifp = NULL; } #endif /* * If and only if the ifnet instance exists: Acquire the address lock. */ if (ifp != NULL) IF_ADDR_WLOCK(ifp); lastref = if_delmulti_locked(ifp, ifma, flags); if (ifp != NULL) { /* * If and only if the ifnet instance exists: * Release the address lock. * If the group was left: update the hardware hash filter. */ IF_ADDR_WUNLOCK(ifp); if (lastref && ifp->if_ioctl != NULL) { (void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, 0); } } } /* * Perform deletion of network-layer and/or link-layer multicast address. * * Return 0 if the reference count was decremented. * Return 1 if the final reference was released, indicating that the * hardware hash filter should be reprogrammed. */ static int if_delmulti_locked(struct ifnet *ifp, struct ifmultiaddr *ifma, int detaching) { struct ifmultiaddr *ll_ifma; if (ifp != NULL && ifma->ifma_ifp != NULL) { KASSERT(ifma->ifma_ifp == ifp, ("%s: inconsistent ifp %p", __func__, ifp)); IF_ADDR_WLOCK_ASSERT(ifp); } ifp = ifma->ifma_ifp; MCDPRINTF("%s freeing %p from %s \n", __func__, ifma, ifp ? ifp->if_xname : ""); /* * If the ifnet is detaching, null out references to ifnet, * so that upper protocol layers will notice, and not attempt * to obtain locks for an ifnet which no longer exists. The * routing socket announcement must happen before the ifnet * instance is detached from the system. */ if (detaching) { #ifdef DIAGNOSTIC printf("%s: detaching ifnet instance %p\n", __func__, ifp); #endif /* * ifp may already be nulled out if we are being reentered * to delete the ll_ifma. */ if (ifp != NULL) { rt_newmaddrmsg(RTM_DELMADDR, ifma); ifma->ifma_ifp = NULL; } } if (--ifma->ifma_refcount > 0) return 0; if (ifp != NULL && detaching == 0 && (ifma->ifma_flags & IFMA_F_ENQUEUED)) { CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifmultiaddr, ifma_link); ifma->ifma_flags &= ~IFMA_F_ENQUEUED; } /* * If this ifma is a network-layer ifma, a link-layer ifma may * have been associated with it. Release it first if so. */ ll_ifma = ifma->ifma_llifma; if (ll_ifma != NULL) { KASSERT(ifma->ifma_lladdr != NULL, ("%s: llifma w/o lladdr", __func__)); if (detaching) ll_ifma->ifma_ifp = NULL; /* XXX */ if (--ll_ifma->ifma_refcount == 0) { if (ifp != NULL) { if (ll_ifma->ifma_flags & IFMA_F_ENQUEUED) { CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ll_ifma, ifmultiaddr, ifma_link); ll_ifma->ifma_flags &= ~IFMA_F_ENQUEUED; } } if_freemulti(ll_ifma); } } #ifdef INVARIANTS if (ifp) { struct ifmultiaddr *ifmatmp; CK_STAILQ_FOREACH(ifmatmp, &ifp->if_multiaddrs, ifma_link) MPASS(ifma != ifmatmp); } #endif if_freemulti(ifma); /* * The last reference to this instance of struct ifmultiaddr * was released; the hardware should be notified of this change. */ return 1; } /* * Set the link layer address on an interface. * * At this time we only support certain types of interfaces, * and we don't allow the length of the address to change. * * Set noinline to be dtrace-friendly */ __noinline int if_setlladdr(struct ifnet *ifp, const u_char *lladdr, int len) { struct sockaddr_dl *sdl; struct ifaddr *ifa; struct ifreq ifr; ifa = ifp->if_addr; if (ifa == NULL) return (EINVAL); sdl = (struct sockaddr_dl *)ifa->ifa_addr; if (sdl == NULL) return (EINVAL); if (len != sdl->sdl_alen) /* don't allow length to change */ return (EINVAL); switch (ifp->if_type) { case IFT_ETHER: case IFT_XETHER: case IFT_L2VLAN: case IFT_BRIDGE: case IFT_IEEE8023ADLAG: bcopy(lladdr, LLADDR(sdl), len); break; default: return (ENODEV); } /* * If the interface is already up, we need * to re-init it in order to reprogram its * address filter. */ if ((ifp->if_flags & IFF_UP) != 0) { if (ifp->if_ioctl) { ifp->if_flags &= ~IFF_UP; ifr.ifr_flags = ifp->if_flags & 0xffff; ifr.ifr_flagshigh = ifp->if_flags >> 16; (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); ifp->if_flags |= IFF_UP; ifr.ifr_flags = ifp->if_flags & 0xffff; ifr.ifr_flagshigh = ifp->if_flags >> 16; (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); } } EVENTHANDLER_INVOKE(iflladdr_event, ifp); return (0); } /* * Compat function for handling basic encapsulation requests. * Not converted stacks (FDDI, IB, ..) supports traditional * output model: ARP (and other similar L2 protocols) are handled * inside output routine, arpresolve/nd6_resolve() returns MAC * address instead of full prepend. * * This function creates calculated header==MAC for IPv4/IPv6 and * returns EAFNOSUPPORT (which is then handled in ARP code) for other * address families. */ static int if_requestencap_default(struct ifnet *ifp, struct if_encap_req *req) { if (req->rtype != IFENCAP_LL) return (EOPNOTSUPP); if (req->bufsize < req->lladdr_len) return (ENOMEM); switch (req->family) { case AF_INET: case AF_INET6: break; default: return (EAFNOSUPPORT); } /* Copy lladdr to storage as is */ memmove(req->buf, req->lladdr, req->lladdr_len); req->bufsize = req->lladdr_len; req->lladdr_off = 0; return (0); } /* * Tunnel interfaces can nest, also they may cause infinite recursion * calls when misconfigured. We'll prevent this by detecting loops. * High nesting level may cause stack exhaustion. We'll prevent this * by introducing upper limit. * * Return 0, if tunnel nesting count is equal or less than limit. */ int if_tunnel_check_nesting(struct ifnet *ifp, struct mbuf *m, uint32_t cookie, int limit) { struct m_tag *mtag; int count; count = 1; mtag = NULL; while ((mtag = m_tag_locate(m, cookie, 0, mtag)) != NULL) { if (*(struct ifnet **)(mtag + 1) == ifp) { log(LOG_NOTICE, "%s: loop detected\n", if_name(ifp)); return (EIO); } count++; } if (count > limit) { log(LOG_NOTICE, "%s: if_output recursively called too many times(%d)\n", if_name(ifp), count); return (EIO); } mtag = m_tag_alloc(cookie, 0, sizeof(struct ifnet *), M_NOWAIT); if (mtag == NULL) return (ENOMEM); *(struct ifnet **)(mtag + 1) = ifp; m_tag_prepend(m, mtag); return (0); } /* * Get the link layer address that was read from the hardware at attach. * * This is only set by Ethernet NICs (IFT_ETHER), but laggX interfaces re-type * their component interfaces as IFT_IEEE8023ADLAG. */ int if_gethwaddr(struct ifnet *ifp, struct ifreq *ifr) { if (ifp->if_hw_addr == NULL) return (ENODEV); switch (ifp->if_type) { case IFT_ETHER: case IFT_IEEE8023ADLAG: bcopy(ifp->if_hw_addr, ifr->ifr_addr.sa_data, ifp->if_addrlen); return (0); default: return (ENODEV); } } /* * The name argument must be a pointer to storage which will last as * long as the interface does. For physical devices, the result of * device_get_name(dev) is a good choice and for pseudo-devices a * static string works well. */ void if_initname(struct ifnet *ifp, const char *name, int unit) { ifp->if_dname = name; ifp->if_dunit = unit; if (unit != IF_DUNIT_NONE) snprintf(ifp->if_xname, IFNAMSIZ, "%s%d", name, unit); else strlcpy(ifp->if_xname, name, IFNAMSIZ); } static int if_vlog(struct ifnet *ifp, int pri, const char *fmt, va_list ap) { char if_fmt[256]; snprintf(if_fmt, sizeof(if_fmt), "%s: %s", ifp->if_xname, fmt); vlog(pri, if_fmt, ap); return (0); } int if_printf(struct ifnet *ifp, const char *fmt, ...) { va_list ap; va_start(ap, fmt); if_vlog(ifp, LOG_INFO, fmt, ap); va_end(ap); return (0); } int if_log(struct ifnet *ifp, int pri, const char *fmt, ...) { va_list ap; va_start(ap, fmt); if_vlog(ifp, pri, fmt, ap); va_end(ap); return (0); } void if_start(struct ifnet *ifp) { (*(ifp)->if_start)(ifp); } /* * Backwards compatibility interface for drivers * that have not implemented it */ static int if_transmit_default(struct ifnet *ifp, struct mbuf *m) { int error; IFQ_HANDOFF(ifp, m, error); return (error); } static void if_input_default(struct ifnet *ifp __unused, struct mbuf *m) { m_freem(m); } int if_handoff(struct ifqueue *ifq, struct mbuf *m, struct ifnet *ifp, int adjust) { int active = 0; IF_LOCK(ifq); if (_IF_QFULL(ifq)) { IF_UNLOCK(ifq); if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1); m_freem(m); return (0); } if (ifp != NULL) { if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len + adjust); if (m->m_flags & (M_BCAST|M_MCAST)) if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1); active = ifp->if_drv_flags & IFF_DRV_OACTIVE; } _IF_ENQUEUE(ifq, m); IF_UNLOCK(ifq); if (ifp != NULL && !active) (*(ifp)->if_start)(ifp); return (1); } void if_register_com_alloc(u_char type, if_com_alloc_t *a, if_com_free_t *f) { KASSERT(if_com_alloc[type] == NULL, ("if_register_com_alloc: %d already registered", type)); KASSERT(if_com_free[type] == NULL, ("if_register_com_alloc: %d free already registered", type)); if_com_alloc[type] = a; if_com_free[type] = f; } void if_deregister_com_alloc(u_char type) { KASSERT(if_com_alloc[type] != NULL, ("if_deregister_com_alloc: %d not registered", type)); KASSERT(if_com_free[type] != NULL, ("if_deregister_com_alloc: %d free not registered", type)); /* * Ensure all pending EPOCH(9) callbacks have been executed. This * fixes issues about late invocation of if_destroy(), which leads * to memory leak from if_com_alloc[type] allocated if_l2com. */ NET_EPOCH_DRAIN_CALLBACKS(); if_com_alloc[type] = NULL; if_com_free[type] = NULL; } /* API for driver access to network stack owned ifnet.*/ uint64_t if_setbaudrate(struct ifnet *ifp, uint64_t baudrate) { uint64_t oldbrate; oldbrate = ifp->if_baudrate; ifp->if_baudrate = baudrate; return (oldbrate); } uint64_t if_getbaudrate(const if_t ifp) { return (((struct ifnet *)ifp)->if_baudrate); } int if_setcapabilities(if_t ifp, int capabilities) { ((struct ifnet *)ifp)->if_capabilities = capabilities; return (0); } int if_setcapabilitiesbit(if_t ifp, int setbit, int clearbit) { ((struct ifnet *)ifp)->if_capabilities &= ~clearbit; ((struct ifnet *)ifp)->if_capabilities |= setbit; return (0); } int if_getcapabilities(const if_t ifp) { return ((struct ifnet *)ifp)->if_capabilities; } int if_setcapenable(if_t ifp, int capabilities) { ((struct ifnet *)ifp)->if_capenable = capabilities; return (0); } int if_setcapenablebit(if_t ifp, int setcap, int clearcap) { if(clearcap) ((struct ifnet *)ifp)->if_capenable &= ~clearcap; if(setcap) ((struct ifnet *)ifp)->if_capenable |= setcap; return (0); } const char * if_getdname(const if_t ifp) { return ((struct ifnet *)ifp)->if_dname; } void if_setdname(if_t ifp, const char *dname) { ((struct ifnet *)ifp)->if_dname = dname; } const char * if_name(if_t ifp) { return ((struct ifnet *)ifp)->if_xname; } int if_setname(if_t ifp, const char *name) { if (strlen(name) > sizeof(ifp->if_xname) - 1) return (ENAMETOOLONG); strlcpy(ifp->if_xname, name, sizeof(ifp->if_xname)); return (0); } int if_togglecapenable(if_t ifp, int togglecap) { ((struct ifnet *)ifp)->if_capenable ^= togglecap; return (0); } int if_getcapenable(const if_t ifp) { return ((struct ifnet *)ifp)->if_capenable; } int if_getdunit(const if_t ifp) { return ((struct ifnet *)ifp)->if_dunit; } int if_getindex(const if_t ifp) { return ((struct ifnet *)ifp)->if_index; } void if_setdescr(if_t ifp, char *descrbuf) { sx_xlock(&ifdescr_sx); char *odescrbuf = ifp->if_description; ifp->if_description = descrbuf; sx_xunlock(&ifdescr_sx); if_freedescr(odescrbuf); } char * if_allocdescr(size_t sz, int malloc_flag) { malloc_flag &= (M_WAITOK | M_NOWAIT); return (malloc(sz, M_IFDESCR, M_ZERO | malloc_flag)); } void if_freedescr(char *descrbuf) { free(descrbuf, M_IFDESCR); } int if_getalloctype(const if_t ifp) { return ((struct ifnet *)ifp)->if_alloctype; } /* * This is largely undesirable because it ties ifnet to a device, but does * provide flexiblity for an embedded product vendor. Should be used with * the understanding that it violates the interface boundaries, and should be * a last resort only. */ int if_setdev(if_t ifp, void *dev) { return (0); } int if_setdrvflagbits(if_t ifp, int set_flags, int clear_flags) { ((struct ifnet *)ifp)->if_drv_flags &= ~clear_flags; ((struct ifnet *)ifp)->if_drv_flags |= set_flags; return (0); } int if_getdrvflags(const if_t ifp) { return ((struct ifnet *)ifp)->if_drv_flags; } int if_setdrvflags(if_t ifp, int flags) { ((struct ifnet *)ifp)->if_drv_flags = flags; return (0); } int if_setflags(if_t ifp, int flags) { ifp->if_flags = flags; return (0); } int if_setflagbits(if_t ifp, int set, int clear) { ((struct ifnet *)ifp)->if_flags &= ~clear; ((struct ifnet *)ifp)->if_flags |= set; return (0); } int if_getflags(const if_t ifp) { return ((struct ifnet *)ifp)->if_flags; } int if_clearhwassist(if_t ifp) { ((struct ifnet *)ifp)->if_hwassist = 0; return (0); } int if_sethwassistbits(if_t ifp, int toset, int toclear) { ((struct ifnet *)ifp)->if_hwassist &= ~toclear; ((struct ifnet *)ifp)->if_hwassist |= toset; return (0); } int if_sethwassist(if_t ifp, int hwassist_bit) { ((struct ifnet *)ifp)->if_hwassist = hwassist_bit; return (0); } int if_gethwassist(const if_t ifp) { return ((struct ifnet *)ifp)->if_hwassist; } int if_togglehwassist(if_t ifp, int toggle_bits) { ((struct ifnet *)ifp)->if_hwassist ^= toggle_bits; return (0); } int if_setmtu(if_t ifp, int mtu) { ((struct ifnet *)ifp)->if_mtu = mtu; return (0); } int if_getmtu(const if_t ifp) { return ((struct ifnet *)ifp)->if_mtu; } int if_getmtu_family(const if_t ifp, int family) { struct domain *dp; SLIST_FOREACH(dp, &domains, dom_next) { if (dp->dom_family == family && dp->dom_ifmtu != NULL) return (dp->dom_ifmtu((struct ifnet *)ifp)); } return (((struct ifnet *)ifp)->if_mtu); } /* * Methods for drivers to access interface unicast and multicast * link level addresses. Driver shall not know 'struct ifaddr' neither * 'struct ifmultiaddr'. */ u_int if_lladdr_count(if_t ifp) { struct epoch_tracker et; struct ifaddr *ifa; u_int count; count = 0; NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) if (ifa->ifa_addr->sa_family == AF_LINK) count++; NET_EPOCH_EXIT(et); return (count); } u_int if_foreach_lladdr(if_t ifp, iflladdr_cb_t cb, void *cb_arg) { struct epoch_tracker et; struct ifaddr *ifa; u_int count; MPASS(cb); count = 0; NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_LINK) continue; count += (*cb)(cb_arg, (struct sockaddr_dl *)ifa->ifa_addr, count); } NET_EPOCH_EXIT(et); return (count); } u_int if_llmaddr_count(if_t ifp) { struct epoch_tracker et; struct ifmultiaddr *ifma; int count; count = 0; NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) if (ifma->ifma_addr->sa_family == AF_LINK) count++; NET_EPOCH_EXIT(et); return (count); } u_int if_foreach_llmaddr(if_t ifp, iflladdr_cb_t cb, void *cb_arg) { struct epoch_tracker et; struct ifmultiaddr *ifma; u_int count; MPASS(cb); count = 0; NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; count += (*cb)(cb_arg, (struct sockaddr_dl *)ifma->ifma_addr, count); } NET_EPOCH_EXIT(et); return (count); } u_int if_foreach_addr_type(if_t ifp, int type, if_addr_cb_t cb, void *cb_arg) { struct epoch_tracker et; struct ifaddr *ifa; u_int count; MPASS(cb); count = 0; NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != type) continue; count += (*cb)(cb_arg, ifa, count); } NET_EPOCH_EXIT(et); return (count); } int if_setsoftc(if_t ifp, void *softc) { ((struct ifnet *)ifp)->if_softc = softc; return (0); } void * if_getsoftc(const if_t ifp) { return ((struct ifnet *)ifp)->if_softc; } void if_setrcvif(struct mbuf *m, if_t ifp) { MPASS((m->m_pkthdr.csum_flags & CSUM_SND_TAG) == 0); m->m_pkthdr.rcvif = (struct ifnet *)ifp; } void if_setvtag(struct mbuf *m, uint16_t tag) { m->m_pkthdr.ether_vtag = tag; } uint16_t if_getvtag(struct mbuf *m) { return (m->m_pkthdr.ether_vtag); } int if_sendq_empty(if_t ifp) { return IFQ_DRV_IS_EMPTY(&((struct ifnet *)ifp)->if_snd); } struct ifaddr * if_getifaddr(const if_t ifp) { return ((struct ifnet *)ifp)->if_addr; } int if_getamcount(const if_t ifp) { return ((struct ifnet *)ifp)->if_amcount; } int if_setsendqready(if_t ifp) { IFQ_SET_READY(&((struct ifnet *)ifp)->if_snd); return (0); } int if_setsendqlen(if_t ifp, int tx_desc_count) { IFQ_SET_MAXLEN(&((struct ifnet *)ifp)->if_snd, tx_desc_count); ((struct ifnet *)ifp)->if_snd.ifq_drv_maxlen = tx_desc_count; return (0); } int if_vlantrunkinuse(if_t ifp) { return ((struct ifnet *)ifp)->if_vlantrunk != NULL?1:0; } int if_init(if_t ifp, void *ctx) { (*((struct ifnet *)ifp)->if_init)(ctx); return (0); } int if_input(if_t ifp, struct mbuf* sendmp) { (*((struct ifnet *)ifp)->if_input)((struct ifnet *)ifp, sendmp); return (0); } int if_transmit(if_t ifp, struct mbuf *m) { (*((struct ifnet *)ifp)->if_transmit)((struct ifnet *)ifp, m); return (0); } struct mbuf * if_dequeue(if_t ifp) { struct mbuf *m; IFQ_DRV_DEQUEUE(&((struct ifnet *)ifp)->if_snd, m); return (m); } int if_sendq_prepend(if_t ifp, struct mbuf *m) { IFQ_DRV_PREPEND(&((struct ifnet *)ifp)->if_snd, m); return (0); } int if_setifheaderlen(if_t ifp, int len) { ((struct ifnet *)ifp)->if_hdrlen = len; return (0); } caddr_t if_getlladdr(const if_t ifp) { return (IF_LLADDR((struct ifnet *)ifp)); } void * if_gethandle(u_char type) { return (if_alloc(type)); } void if_bpfmtap(if_t ifh, struct mbuf *m) { struct ifnet *ifp = (struct ifnet *)ifh; BPF_MTAP(ifp, m); } void if_etherbpfmtap(if_t ifh, struct mbuf *m) { struct ifnet *ifp = (struct ifnet *)ifh; ETHER_BPF_MTAP(ifp, m); } void if_vlancap(if_t ifh) { struct ifnet *ifp = (struct ifnet *)ifh; VLAN_CAPABILITIES(ifp); } int if_sethwtsomax(if_t ifp, u_int if_hw_tsomax) { ((struct ifnet *)ifp)->if_hw_tsomax = if_hw_tsomax; return (0); } int if_sethwtsomaxsegcount(if_t ifp, u_int if_hw_tsomaxsegcount) { ((struct ifnet *)ifp)->if_hw_tsomaxsegcount = if_hw_tsomaxsegcount; return (0); } int if_sethwtsomaxsegsize(if_t ifp, u_int if_hw_tsomaxsegsize) { ((struct ifnet *)ifp)->if_hw_tsomaxsegsize = if_hw_tsomaxsegsize; return (0); } u_int if_gethwtsomax(const if_t ifp) { return (((struct ifnet *)ifp)->if_hw_tsomax); } u_int if_gethwtsomaxsegcount(const if_t ifp) { return (((struct ifnet *)ifp)->if_hw_tsomaxsegcount); } u_int if_gethwtsomaxsegsize(const if_t ifp) { return (((struct ifnet *)ifp)->if_hw_tsomaxsegsize); } void if_setinitfn(if_t ifp, if_init_fn_t init_fn) { ((struct ifnet *)ifp)->if_init = init_fn; } void if_setinputfn(if_t ifp, if_input_fn_t input_fn) { ((struct ifnet *)ifp)->if_input = input_fn; } void if_setioctlfn(if_t ifp, if_ioctl_fn_t ioctl_fn) { ((struct ifnet *)ifp)->if_ioctl = (void *)ioctl_fn; } void if_setoutputfn(if_t ifp, if_output_fn_t output_fn) { ((struct ifnet *)ifp)->if_output = output_fn; } void if_setstartfn(if_t ifp, if_start_fn_t start_fn) { ((struct ifnet *)ifp)->if_start = (void *)start_fn; } void if_settransmitfn(if_t ifp, if_transmit_fn_t start_fn) { ((struct ifnet *)ifp)->if_transmit = start_fn; } void if_setqflushfn(if_t ifp, if_qflush_fn_t flush_fn) { ((struct ifnet *)ifp)->if_qflush = flush_fn; } void if_setsndtagallocfn(if_t ifp, if_snd_tag_alloc_t alloc_fn) { ((struct ifnet *)ifp)->if_snd_tag_alloc = alloc_fn; } void if_setgetcounterfn(if_t ifp, if_get_counter_t fn) { ifp->if_get_counter = fn; } void if_setdebugnet_methods(if_t ifp, struct debugnet_methods *m) { ifp->if_debugnet_methods = m; } +struct label * +if_getmaclabel(if_t ifp) +{ + return (ifp->if_label); +} + +void +if_setmaclabel(if_t ifp, struct label *label) +{ + ifp->if_label = label; +} + int if_gettype(if_t ifp) { return (ifp->if_type); } #ifdef DDB static void if_show_ifnet(struct ifnet *ifp) { if (ifp == NULL) return; db_printf("%s:\n", ifp->if_xname); #define IF_DB_PRINTF(f, e) db_printf(" %s = " f "\n", #e, ifp->e); IF_DB_PRINTF("%s", if_dname); IF_DB_PRINTF("%d", if_dunit); IF_DB_PRINTF("%s", if_description); IF_DB_PRINTF("%u", if_index); IF_DB_PRINTF("%d", if_idxgen); IF_DB_PRINTF("%u", if_refcount); IF_DB_PRINTF("%p", if_softc); IF_DB_PRINTF("%p", if_l2com); IF_DB_PRINTF("%p", if_llsoftc); IF_DB_PRINTF("%d", if_amcount); IF_DB_PRINTF("%p", if_addr); IF_DB_PRINTF("%p", if_broadcastaddr); IF_DB_PRINTF("%p", if_afdata); IF_DB_PRINTF("%d", if_afdata_initialized); IF_DB_PRINTF("%u", if_fib); IF_DB_PRINTF("%p", if_vnet); IF_DB_PRINTF("%p", if_home_vnet); IF_DB_PRINTF("%p", if_vlantrunk); IF_DB_PRINTF("%p", if_bpf); IF_DB_PRINTF("%u", if_pcount); IF_DB_PRINTF("%p", if_bridge); IF_DB_PRINTF("%p", if_lagg); IF_DB_PRINTF("%p", if_pf_kif); IF_DB_PRINTF("%p", if_carp); IF_DB_PRINTF("%p", if_label); IF_DB_PRINTF("%p", if_netmap); IF_DB_PRINTF("0x%08x", if_flags); IF_DB_PRINTF("0x%08x", if_drv_flags); IF_DB_PRINTF("0x%08x", if_capabilities); IF_DB_PRINTF("0x%08x", if_capenable); IF_DB_PRINTF("%p", if_snd.ifq_head); IF_DB_PRINTF("%p", if_snd.ifq_tail); IF_DB_PRINTF("%d", if_snd.ifq_len); IF_DB_PRINTF("%d", if_snd.ifq_maxlen); IF_DB_PRINTF("%p", if_snd.ifq_drv_head); IF_DB_PRINTF("%p", if_snd.ifq_drv_tail); IF_DB_PRINTF("%d", if_snd.ifq_drv_len); IF_DB_PRINTF("%d", if_snd.ifq_drv_maxlen); IF_DB_PRINTF("%d", if_snd.altq_type); IF_DB_PRINTF("%x", if_snd.altq_flags); #undef IF_DB_PRINTF } DB_SHOW_COMMAND(ifnet, db_show_ifnet) { if (!have_addr) { db_printf("usage: show ifnet \n"); return; } if_show_ifnet((struct ifnet *)addr); } DB_SHOW_ALL_COMMAND(ifnets, db_show_all_ifnets) { struct ifnet *ifp; u_short idx; for (idx = 1; idx <= if_index; idx++) { ifp = ifindex_table[idx].ife_ifnet; if (ifp == NULL) continue; db_printf( "%20s ifp=%p\n", ifp->if_xname, ifp); if (db_pager_quit) break; } } #endif /* DDB */ diff --git a/sys/net/if_var.h b/sys/net/if_var.h index 4c54d26a921d..e9e6086bfa89 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -1,683 +1,685 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of 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. * * From: @(#)if.h 8.1 (Berkeley) 6/10/93 * $FreeBSD$ */ #ifndef _NET_IF_VAR_H_ #define _NET_IF_VAR_H_ /* * Structures defining a network interface, providing a packet * transport mechanism (ala level 0 of the PUP protocols). * * Each interface accepts output datagrams of a specified maximum * length, and provides higher level routines with input datagrams * received from its medium. * * Output occurs when the routine if_output is called, with three parameters: * (*ifp->if_output)(ifp, m, dst, ro) * Here m is the mbuf chain to be sent and dst is the destination address. * The output routine encapsulates the supplied datagram if necessary, * and then transmits it on its medium. * * On input, each interface unwraps the data received by it, and either * places it on the input queue of an internetwork datagram routine * and posts the associated software interrupt, or passes the datagram to a raw * packet input routine. * * Routines exist for locating interfaces by their addresses * or for locating an interface on a certain network, as well as more general * routing and gateway routines maintaining information used to locate * interfaces. These routines live in the files if.c and route.c */ struct rtentry; /* ifa_rtrequest */ struct socket; struct carp_if; struct carp_softc; struct ifvlantrunk; struct route; /* if_output */ struct vnet; struct ifmedia; struct netmap_adapter; struct debugnet_methods; #ifdef _KERNEL #include #include /* ifqueue only? */ #include #include #endif /* _KERNEL */ #include #include #include #include /* XXX */ #include /* struct ifqueue */ #include /* XXX */ #include /* XXX */ #include /* if_link_task */ #define IF_DUNIT_NONE -1 #include CK_STAILQ_HEAD(ifnethead, ifnet); /* we use TAILQs so that the order of */ CK_STAILQ_HEAD(ifaddrhead, ifaddr); /* instantiation is preserved in the list */ CK_STAILQ_HEAD(ifmultihead, ifmultiaddr); CK_STAILQ_HEAD(ifgrouphead, ifg_group); #ifdef _KERNEL VNET_DECLARE(struct pfil_head *, link_pfil_head); #define V_link_pfil_head VNET(link_pfil_head) #define PFIL_ETHER_NAME "ethernet" #define HHOOK_IPSEC_INET 0 #define HHOOK_IPSEC_INET6 1 #define HHOOK_IPSEC_COUNT 2 VNET_DECLARE(struct hhook_head *, ipsec_hhh_in[HHOOK_IPSEC_COUNT]); VNET_DECLARE(struct hhook_head *, ipsec_hhh_out[HHOOK_IPSEC_COUNT]); #define V_ipsec_hhh_in VNET(ipsec_hhh_in) #define V_ipsec_hhh_out VNET(ipsec_hhh_out) #endif /* _KERNEL */ typedef enum { IFCOUNTER_IPACKETS = 0, IFCOUNTER_IERRORS, IFCOUNTER_OPACKETS, IFCOUNTER_OERRORS, IFCOUNTER_COLLISIONS, IFCOUNTER_IBYTES, IFCOUNTER_OBYTES, IFCOUNTER_IMCASTS, IFCOUNTER_OMCASTS, IFCOUNTER_IQDROPS, IFCOUNTER_OQDROPS, IFCOUNTER_NOPROTO, IFCOUNTERS /* Array size. */ } ift_counter; typedef void (*if_start_fn_t)(if_t); typedef int (*if_ioctl_fn_t)(if_t, u_long, caddr_t); typedef void (*if_init_fn_t)(void *); typedef void (*if_input_fn_t)(struct ifnet *, struct mbuf *); typedef int (*if_output_fn_t) (struct ifnet *, struct mbuf *, const struct sockaddr *, struct route *); typedef void (*if_qflush_fn_t)(if_t); typedef int (*if_transmit_fn_t)(if_t, struct mbuf *); typedef uint64_t (*if_get_counter_t)(if_t, ift_counter); struct ifnet_hw_tsomax { u_int tsomaxbytes; /* TSO total burst length limit in bytes */ u_int tsomaxsegcount; /* TSO maximum segment count */ u_int tsomaxsegsize; /* TSO maximum segment size in bytes */ }; /* Interface encap request types */ typedef enum { IFENCAP_LL = 1 /* pre-calculate link-layer header */ } ife_type; /* * The structure below allows to request various pre-calculated L2/L3 headers * for different media. Requests varies by type (rtype field). * * IFENCAP_LL type: pre-calculates link header based on address family * and destination lladdr. * * Input data fields: * buf: pointer to destination buffer * bufsize: buffer size * flags: IFENCAP_FLAG_BROADCAST if destination is broadcast * family: address family defined by AF_ constant. * lladdr: pointer to link-layer address * lladdr_len: length of link-layer address * hdata: pointer to L3 header (optional, used for ARP requests). * Output data fields: * buf: encap data is stored here * bufsize: resulting encap length is stored here * lladdr_off: offset of link-layer address from encap hdr start * hdata: L3 header may be altered if necessary */ struct if_encap_req { u_char *buf; /* Destination buffer (w) */ size_t bufsize; /* size of provided buffer (r) */ ife_type rtype; /* request type (r) */ uint32_t flags; /* Request flags (r) */ int family; /* Address family AF_* (r) */ int lladdr_off; /* offset from header start (w) */ int lladdr_len; /* lladdr length (r) */ char *lladdr; /* link-level address pointer (r) */ char *hdata; /* Upper layer header data (rw) */ }; #define IFENCAP_FLAG_BROADCAST 0x02 /* Destination is broadcast */ /* * Network interface send tag support. The storage of "struct * m_snd_tag" comes from the network driver and it is free to allocate * as much additional space as it wants for its own use. */ struct ktls_session; struct m_snd_tag; #define IF_SND_TAG_TYPE_RATE_LIMIT 0 #define IF_SND_TAG_TYPE_UNLIMITED 1 #define IF_SND_TAG_TYPE_TLS 2 #define IF_SND_TAG_TYPE_TLS_RATE_LIMIT 3 #define IF_SND_TAG_TYPE_TLS_RX 4 #define IF_SND_TAG_TYPE_MAX 5 struct if_snd_tag_alloc_header { uint32_t type; /* send tag type, see IF_SND_TAG_XXX */ uint32_t flowid; /* mbuf hash value */ uint32_t flowtype; /* mbuf hash type */ uint8_t numa_domain; /* numa domain of associated inp */ }; struct if_snd_tag_alloc_rate_limit { struct if_snd_tag_alloc_header hdr; uint64_t max_rate; /* in bytes/s */ uint32_t flags; /* M_NOWAIT or M_WAITOK */ uint32_t reserved; /* alignment */ }; struct if_snd_tag_alloc_tls { struct if_snd_tag_alloc_header hdr; struct inpcb *inp; const struct ktls_session *tls; }; struct if_snd_tag_alloc_tls_rx { struct if_snd_tag_alloc_header hdr; struct inpcb *inp; const struct ktls_session *tls; uint16_t vlan_id; /* valid if non-zero */ }; struct if_snd_tag_alloc_tls_rate_limit { struct if_snd_tag_alloc_header hdr; struct inpcb *inp; const struct ktls_session *tls; uint64_t max_rate; /* in bytes/s */ }; struct if_snd_tag_rate_limit_params { uint64_t max_rate; /* in bytes/s */ uint32_t queue_level; /* 0 (empty) .. 65535 (full) */ #define IF_SND_QUEUE_LEVEL_MIN 0 #define IF_SND_QUEUE_LEVEL_MAX 65535 uint32_t flags; /* M_NOWAIT or M_WAITOK */ }; struct if_snd_tag_modify_tls_rx { /* TCP sequence number of TLS header in host endian format */ uint32_t tls_hdr_tcp_sn; /* * TLS record length, including all headers, data and trailers. * If the tls_rec_length is zero, it means HW encryption resumed. */ uint32_t tls_rec_length; /* TLS sequence number in host endian format */ uint64_t tls_seq_number; }; union if_snd_tag_alloc_params { struct if_snd_tag_alloc_header hdr; struct if_snd_tag_alloc_rate_limit rate_limit; struct if_snd_tag_alloc_rate_limit unlimited; struct if_snd_tag_alloc_tls tls; struct if_snd_tag_alloc_tls_rx tls_rx; struct if_snd_tag_alloc_tls_rate_limit tls_rate_limit; }; union if_snd_tag_modify_params { struct if_snd_tag_rate_limit_params rate_limit; struct if_snd_tag_rate_limit_params unlimited; struct if_snd_tag_rate_limit_params tls_rate_limit; struct if_snd_tag_modify_tls_rx tls_rx; }; union if_snd_tag_query_params { struct if_snd_tag_rate_limit_params rate_limit; struct if_snd_tag_rate_limit_params unlimited; struct if_snd_tag_rate_limit_params tls_rate_limit; }; typedef int (if_snd_tag_alloc_t)(struct ifnet *, union if_snd_tag_alloc_params *, struct m_snd_tag **); typedef int (if_snd_tag_modify_t)(struct m_snd_tag *, union if_snd_tag_modify_params *); typedef int (if_snd_tag_query_t)(struct m_snd_tag *, union if_snd_tag_query_params *); typedef void (if_snd_tag_free_t)(struct m_snd_tag *); typedef struct m_snd_tag *(if_next_send_tag_t)(struct m_snd_tag *); struct if_snd_tag_sw { if_snd_tag_modify_t *snd_tag_modify; if_snd_tag_query_t *snd_tag_query; if_snd_tag_free_t *snd_tag_free; if_next_send_tag_t *next_snd_tag; u_int type; /* One of IF_SND_TAG_TYPE_*. */ }; /* Query return flags */ #define RT_NOSUPPORT 0x00000000 /* Not supported */ #define RT_IS_INDIRECT 0x00000001 /* * Interface like a lagg, select * the actual interface for * capabilities. */ #define RT_IS_SELECTABLE 0x00000002 /* * No rate table, you select * rates and the first * number_of_rates are created. */ #define RT_IS_FIXED_TABLE 0x00000004 /* A fixed table is attached */ #define RT_IS_UNUSABLE 0x00000008 /* It is not usable for this */ #define RT_IS_SETUP_REQ 0x00000010 /* The interface setup must be called before use */ struct if_ratelimit_query_results { const uint64_t *rate_table; /* Pointer to table if present */ uint32_t flags; /* Flags indicating results */ uint32_t max_flows; /* Max flows using, 0=unlimited */ uint32_t number_of_rates; /* How many unique rates can be created */ uint32_t min_segment_burst; /* The amount the adapter bursts at each send */ }; typedef void (if_ratelimit_query_t)(struct ifnet *, struct if_ratelimit_query_results *); typedef int (if_ratelimit_setup_t)(struct ifnet *, uint64_t, uint32_t); #define IF_NODOM 255 /* * Locks for address lists on the network interface. */ #define IF_ADDR_LOCK_INIT(if) mtx_init(&(if)->if_addr_lock, "if_addr_lock", NULL, MTX_DEF) #define IF_ADDR_LOCK_DESTROY(if) mtx_destroy(&(if)->if_addr_lock) #define IF_ADDR_WLOCK(if) mtx_lock(&(if)->if_addr_lock) #define IF_ADDR_WUNLOCK(if) mtx_unlock(&(if)->if_addr_lock) #define IF_ADDR_LOCK_ASSERT(if) MPASS(in_epoch(net_epoch_preempt) || mtx_owned(&(if)->if_addr_lock)) #define IF_ADDR_WLOCK_ASSERT(if) mtx_assert(&(if)->if_addr_lock, MA_OWNED) #ifdef _KERNEL /* interface link layer address change event */ typedef void (*iflladdr_event_handler_t)(void *, struct ifnet *); EVENTHANDLER_DECLARE(iflladdr_event, iflladdr_event_handler_t); /* interface address change event */ typedef void (*ifaddr_event_handler_t)(void *, struct ifnet *); EVENTHANDLER_DECLARE(ifaddr_event, ifaddr_event_handler_t); typedef void (*ifaddr_event_ext_handler_t)(void *, struct ifnet *, struct ifaddr *, int); EVENTHANDLER_DECLARE(ifaddr_event_ext, ifaddr_event_ext_handler_t); #define IFADDR_EVENT_ADD 0 #define IFADDR_EVENT_DEL 1 /* new interface arrival event */ typedef void (*ifnet_arrival_event_handler_t)(void *, struct ifnet *); EVENTHANDLER_DECLARE(ifnet_arrival_event, ifnet_arrival_event_handler_t); /* interface departure event */ typedef void (*ifnet_departure_event_handler_t)(void *, struct ifnet *); EVENTHANDLER_DECLARE(ifnet_departure_event, ifnet_departure_event_handler_t); /* Interface link state change event */ typedef void (*ifnet_link_event_handler_t)(void *, struct ifnet *, int); EVENTHANDLER_DECLARE(ifnet_link_event, ifnet_link_event_handler_t); /* Interface up/down event */ #define IFNET_EVENT_UP 0 #define IFNET_EVENT_DOWN 1 #define IFNET_EVENT_PCP 2 /* priority code point, PCP */ #define IFNET_EVENT_UPDATE_BAUDRATE 3 typedef void (*ifnet_event_fn)(void *, struct ifnet *ifp, int event); EVENTHANDLER_DECLARE(ifnet_event, ifnet_event_fn); /* * interface groups */ struct ifg_group { char ifg_group[IFNAMSIZ]; u_int ifg_refcnt; void *ifg_pf_kif; CK_STAILQ_HEAD(, ifg_member) ifg_members; /* (CK_) */ CK_STAILQ_ENTRY(ifg_group) ifg_next; /* (CK_) */ }; struct ifg_member { CK_STAILQ_ENTRY(ifg_member) ifgm_next; /* (CK_) */ struct ifnet *ifgm_ifp; }; struct ifg_list { struct ifg_group *ifgl_group; CK_STAILQ_ENTRY(ifg_list) ifgl_next; /* (CK_) */ }; #ifdef _SYS_EVENTHANDLER_H_ /* group attach event */ typedef void (*group_attach_event_handler_t)(void *, struct ifg_group *); EVENTHANDLER_DECLARE(group_attach_event, group_attach_event_handler_t); /* group detach event */ typedef void (*group_detach_event_handler_t)(void *, struct ifg_group *); EVENTHANDLER_DECLARE(group_detach_event, group_detach_event_handler_t); /* group change event */ typedef void (*group_change_event_handler_t)(void *, const char *); EVENTHANDLER_DECLARE(group_change_event, group_change_event_handler_t); #endif /* _SYS_EVENTHANDLER_H_ */ #define IF_AFDATA_LOCK_INIT(ifp) \ mtx_init(&(ifp)->if_afdata_lock, "if_afdata", NULL, MTX_DEF) #define IF_AFDATA_WLOCK(ifp) mtx_lock(&(ifp)->if_afdata_lock) #define IF_AFDATA_WUNLOCK(ifp) mtx_unlock(&(ifp)->if_afdata_lock) #define IF_AFDATA_LOCK(ifp) IF_AFDATA_WLOCK(ifp) #define IF_AFDATA_UNLOCK(ifp) IF_AFDATA_WUNLOCK(ifp) #define IF_AFDATA_TRYLOCK(ifp) mtx_trylock(&(ifp)->if_afdata_lock) #define IF_AFDATA_DESTROY(ifp) mtx_destroy(&(ifp)->if_afdata_lock) #define IF_AFDATA_LOCK_ASSERT(ifp) MPASS(in_epoch(net_epoch_preempt) || mtx_owned(&(ifp)->if_afdata_lock)) #define IF_AFDATA_WLOCK_ASSERT(ifp) mtx_assert(&(ifp)->if_afdata_lock, MA_OWNED) #define IF_AFDATA_UNLOCK_ASSERT(ifp) mtx_assert(&(ifp)->if_afdata_lock, MA_NOTOWNED) /* * 72 was chosen below because it is the size of a TCP/IP * header (40) + the minimum mss (32). */ #define IF_MINMTU 72 #define IF_MAXMTU 65535 #define TOEDEV(ifp) ((ifp)->if_llsoftc) /* * The ifaddr structure contains information about one address * of an interface. They are maintained by the different address families, * are allocated and attached when an address is set, and are linked * together so all addresses for an interface can be located. * * NOTE: a 'struct ifaddr' is always at the beginning of a larger * chunk of malloc'ed memory, where we store the three addresses * (ifa_addr, ifa_dstaddr and ifa_netmask) referenced here. */ struct ifaddr { struct sockaddr *ifa_addr; /* address of interface */ struct sockaddr *ifa_dstaddr; /* other end of p-to-p link */ #define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ struct sockaddr *ifa_netmask; /* used to determine subnet */ struct ifnet *ifa_ifp; /* back-pointer to interface */ struct carp_softc *ifa_carp; /* pointer to CARP data */ CK_STAILQ_ENTRY(ifaddr) ifa_link; /* queue macro glue */ u_short ifa_flags; /* mostly rt_flags for cloning */ #define IFA_ROUTE RTF_UP /* route installed */ #define IFA_RTSELF RTF_HOST /* loopback route to self installed */ u_int ifa_refcnt; /* references to this structure */ counter_u64_t ifa_ipackets; counter_u64_t ifa_opackets; counter_u64_t ifa_ibytes; counter_u64_t ifa_obytes; struct epoch_context ifa_epoch_ctx; }; struct ifaddr * ifa_alloc(size_t size, int flags); void ifa_free(struct ifaddr *ifa); void ifa_ref(struct ifaddr *ifa); int __result_use_check ifa_try_ref(struct ifaddr *ifa); /* * Multicast address structure. This is analogous to the ifaddr * structure except that it keeps track of multicast addresses. */ #define IFMA_F_ENQUEUED 0x1 struct ifmultiaddr { CK_STAILQ_ENTRY(ifmultiaddr) ifma_link; /* queue macro glue */ struct sockaddr *ifma_addr; /* address this membership is for */ struct sockaddr *ifma_lladdr; /* link-layer translation, if any */ struct ifnet *ifma_ifp; /* back-pointer to interface */ u_int ifma_refcount; /* reference count */ int ifma_flags; void *ifma_protospec; /* protocol-specific state, if any */ struct ifmultiaddr *ifma_llifma; /* pointer to ifma for ifma_lladdr */ struct epoch_context ifma_epoch_ctx; }; extern struct sx ifnet_sxlock; #define IFNET_WLOCK() sx_xlock(&ifnet_sxlock) #define IFNET_WUNLOCK() sx_xunlock(&ifnet_sxlock) #define IFNET_RLOCK_ASSERT() sx_assert(&ifnet_sxlock, SA_SLOCKED) #define IFNET_WLOCK_ASSERT() sx_assert(&ifnet_sxlock, SA_XLOCKED) #define IFNET_RLOCK() sx_slock(&ifnet_sxlock) #define IFNET_RUNLOCK() sx_sunlock(&ifnet_sxlock) /* * Look up an ifnet given its index. The returned value protected from * being freed by the network epoch. The _ref variant also acquires a * reference that must be freed using if_rele(). */ struct ifnet *ifnet_byindex(u_int); struct ifnet *ifnet_byindex_ref(u_int); /* * ifnet_byindexgen() looks up ifnet by index and generation count, * attempting to restore a weak pointer that had been stored across * the epoch. */ struct ifnet *ifnet_byindexgen(uint16_t idx, uint16_t gen); VNET_DECLARE(struct ifnethead, ifnet); VNET_DECLARE(struct ifgrouphead, ifg_head); VNET_DECLARE(struct ifnet *, loif); /* first loopback interface */ #define V_ifnet VNET(ifnet) #define V_ifg_head VNET(ifg_head) #define V_loif VNET(loif) #ifdef MCAST_VERBOSE #define MCDPRINTF printf #else #define MCDPRINTF(...) #endif int if_addgroup(struct ifnet *, const char *); int if_delgroup(struct ifnet *, const char *); int if_addmulti(struct ifnet *, struct sockaddr *, struct ifmultiaddr **); int if_allmulti(struct ifnet *, int); struct ifnet* if_alloc(u_char); struct ifnet* if_alloc_dev(u_char, device_t dev); void if_attach(struct ifnet *); void if_dead(struct ifnet *); int if_delmulti(struct ifnet *, struct sockaddr *); void if_delmulti_ifma(struct ifmultiaddr *); void if_delmulti_ifma_flags(struct ifmultiaddr *, int flags); void if_detach(struct ifnet *); void if_purgeaddrs(struct ifnet *); void if_delallmulti(struct ifnet *); void if_down(struct ifnet *); struct ifmultiaddr * if_findmulti(struct ifnet *, const struct sockaddr *); void if_freemulti(struct ifmultiaddr *ifma); void if_free(struct ifnet *); void if_initname(struct ifnet *, const char *, int); void if_link_state_change(struct ifnet *, int); int if_printf(struct ifnet *, const char *, ...) __printflike(2, 3); int if_log(struct ifnet *, int, const char *, ...) __printflike(3, 4); void if_ref(struct ifnet *); void if_rele(struct ifnet *); bool __result_use_check if_try_ref(struct ifnet *); int if_setlladdr(struct ifnet *, const u_char *, int); int if_tunnel_check_nesting(struct ifnet *, struct mbuf *, uint32_t, int); void if_up(struct ifnet *); int ifioctl(struct socket *, u_long, caddr_t, struct thread *); int ifpromisc(struct ifnet *, int); struct ifnet *ifunit(const char *); struct ifnet *ifunit_ref(const char *); int ifa_add_loopback_route(struct ifaddr *, struct sockaddr *); int ifa_del_loopback_route(struct ifaddr *, struct sockaddr *); int ifa_switch_loopback_route(struct ifaddr *, struct sockaddr *); struct ifaddr *ifa_ifwithaddr(const struct sockaddr *); int ifa_ifwithaddr_check(const struct sockaddr *); struct ifaddr *ifa_ifwithbroadaddr(const struct sockaddr *, int); struct ifaddr *ifa_ifwithdstaddr(const struct sockaddr *, int); struct ifaddr *ifa_ifwithnet(const struct sockaddr *, int, int); struct ifaddr *ifa_ifwithroute(int, const struct sockaddr *, const struct sockaddr *, u_int); struct ifaddr *ifaof_ifpforaddr(const struct sockaddr *, struct ifnet *); int ifa_preferred(struct ifaddr *, struct ifaddr *); int if_simloop(struct ifnet *ifp, struct mbuf *m, int af, int hlen); typedef void *if_com_alloc_t(u_char type, struct ifnet *ifp); typedef void if_com_free_t(void *com, u_char type); void if_register_com_alloc(u_char type, if_com_alloc_t *a, if_com_free_t *f); void if_deregister_com_alloc(u_char type); void if_data_copy(struct ifnet *, struct if_data *); uint64_t if_get_counter_default(struct ifnet *, ift_counter); void if_inc_counter(struct ifnet *, ift_counter, int64_t); #define IF_LLADDR(ifp) \ LLADDR((struct sockaddr_dl *)((ifp)->if_addr->ifa_addr)) uint64_t if_setbaudrate(if_t ifp, uint64_t baudrate); uint64_t if_getbaudrate(const if_t ifp); int if_setcapabilities(if_t ifp, int capabilities); int if_setcapabilitiesbit(if_t ifp, int setbit, int clearbit); int if_getcapabilities(const if_t ifp); int if_togglecapenable(if_t ifp, int togglecap); int if_setcapenable(if_t ifp, int capenable); int if_setcapenablebit(if_t ifp, int setcap, int clearcap); int if_getcapenable(const if_t ifp); int if_getdunit(const if_t ifp); int if_getindex(const if_t ifp); const char *if_getdname(const if_t ifp); void if_setdname(if_t ifp, const char *name); const char *if_name(if_t ifp); int if_setname(if_t ifp, const char *name); void if_setdescr(if_t ifp, char *descrbuf); char *if_allocdescr(size_t sz, int malloc_flag); void if_freedescr(char *descrbuf); int if_getalloctype(const if_t ifp); int if_gettype(const if_t ifp); int if_setdev(if_t ifp, void *dev); int if_setdrvflagbits(if_t ifp, int if_setflags, int clear_flags); int if_getdrvflags(const if_t ifp); int if_setdrvflags(if_t ifp, int flags); int if_clearhwassist(if_t ifp); int if_sethwassistbits(if_t ifp, int toset, int toclear); int if_sethwassist(if_t ifp, int hwassist_bit); int if_gethwassist(const if_t ifp); int if_togglehwassist(if_t ifp, int toggle_bits); int if_setsoftc(if_t ifp, void *softc); void *if_getsoftc(if_t ifp); int if_setflags(if_t ifp, int flags); int if_gethwaddr(const if_t ifp, struct ifreq *); int if_setmtu(if_t ifp, int mtu); int if_getmtu(const if_t ifp); int if_getmtu_family(const if_t ifp, int family); int if_setflagbits(if_t ifp, int set, int clear); int if_getflags(const if_t ifp); int if_sendq_empty(if_t ifp); int if_setsendqready(if_t ifp); int if_setsendqlen(if_t ifp, int tx_desc_count); int if_sethwtsomax(if_t ifp, u_int if_hw_tsomax); int if_sethwtsomaxsegcount(if_t ifp, u_int if_hw_tsomaxsegcount); int if_sethwtsomaxsegsize(if_t ifp, u_int if_hw_tsomaxsegsize); u_int if_gethwtsomax(const if_t ifp); u_int if_gethwtsomaxsegcount(const if_t ifp); u_int if_gethwtsomaxsegsize(const if_t ifp); int if_input(if_t ifp, struct mbuf* sendmp); int if_sendq_prepend(if_t ifp, struct mbuf *m); struct mbuf *if_dequeue(if_t ifp); int if_setifheaderlen(if_t ifp, int len); void if_setrcvif(struct mbuf *m, if_t ifp); void if_setvtag(struct mbuf *m, u_int16_t tag); u_int16_t if_getvtag(struct mbuf *m); int if_vlantrunkinuse(if_t ifp); caddr_t if_getlladdr(const if_t ifp); void *if_gethandle(u_char); void if_bpfmtap(if_t ifp, struct mbuf *m); void if_etherbpfmtap(if_t ifp, struct mbuf *m); void if_vlancap(if_t ifp); int if_transmit(if_t ifp, struct mbuf *m); int if_init(if_t ifp, void *ctx); +struct label *if_getmaclabel(if_t ifp); +void if_setmaclabel(if_t ifp, struct label *label); /* * Traversing through interface address lists. */ struct sockaddr_dl; typedef u_int iflladdr_cb_t(void *, struct sockaddr_dl *, u_int); u_int if_foreach_lladdr(if_t, iflladdr_cb_t, void *); u_int if_foreach_llmaddr(if_t, iflladdr_cb_t, void *); u_int if_lladdr_count(if_t); u_int if_llmaddr_count(if_t); int if_getamcount(const if_t ifp); struct ifaddr * if_getifaddr(const if_t ifp); typedef u_int if_addr_cb_t(void *, struct ifaddr *, u_int); u_int if_foreach_addr_type(if_t ifp, int type, if_addr_cb_t cb, void *cb_arg); /* Functions */ void if_setinitfn(if_t ifp, if_init_fn_t); void if_setinputfn(if_t ifp, if_input_fn_t); void if_setioctlfn(if_t ifp, if_ioctl_fn_t); void if_setoutputfn(if_t ifp, int(*) (if_t, struct mbuf *, const struct sockaddr *, struct route *)); void if_setstartfn(if_t ifp, void (*)(if_t)); void if_settransmitfn(if_t ifp, if_transmit_fn_t); void if_setqflushfn(if_t ifp, if_qflush_fn_t); void if_setgetcounterfn(if_t ifp, if_get_counter_t); void if_setsndtagallocfn(if_t ifp, if_snd_tag_alloc_t); void if_setdebugnet_methods(struct ifnet *, struct debugnet_methods *); /* TSO */ void if_hw_tsomax_common(if_t ifp, struct ifnet_hw_tsomax *); int if_hw_tsomax_update(if_t ifp, struct ifnet_hw_tsomax *); /* accessors for struct ifreq */ void *ifr_data_get_ptr(void *ifrp); void *ifr_buffer_get_buffer(void *data); size_t ifr_buffer_get_length(void *data); int ifhwioctl(u_long, struct ifnet *, caddr_t, struct thread *); #ifdef DEVICE_POLLING enum poll_cmd { POLL_ONLY, POLL_AND_CHECK_STATUS }; typedef int poll_handler_t(if_t ifp, enum poll_cmd cmd, int count); int ether_poll_register(poll_handler_t *h, if_t ifp); int ether_poll_deregister(if_t ifp); #endif /* DEVICE_POLLING */ #endif /* _KERNEL */ #include /* XXX: temporary until drivers converted. */ #include /* XXXAO: temporary unconditional include */ #endif /* !_NET_IF_VAR_H_ */ diff --git a/sys/security/mac/mac_inet.c b/sys/security/mac/mac_inet.c index 2b6a70fdf1bf..dd77a6825204 100644 --- a/sys/security/mac/mac_inet.c +++ b/sys/security/mac/mac_inet.c @@ -1,509 +1,509 @@ /*- * Copyright (c) 1999-2002, 2007, 2009, 2019 Robert N. M. Watson * Copyright (c) 2001 Ilmar S. Habibulin * Copyright (c) 2001-2004 Networks Associates Technology, Inc. * Copyright (c) 2006 SPARTA, Inc. * Copyright (c) 2008 Apple Inc. * All rights reserved. * * This software was developed by Robert Watson and Ilmar Habibulin for the * TrustedBSD Project. * * This software was developed for the FreeBSD Project in part by Network * Associates Laboratories, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), * as part of the DARPA CHATS research program. * * This software was enhanced by SPARTA ISSO under SPAWAR contract * N66001-04-C-6019 ("SEFOS"). * * This software was developed at the University of Cambridge Computer * Laboratory with support from a grant from Google, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct label * mac_inpcb_label_alloc(int flag) { struct label *label; int error; label = mac_labelzone_alloc(flag); if (label == NULL) return (NULL); if (flag & M_WAITOK) MAC_POLICY_CHECK(inpcb_init_label, label, flag); else MAC_POLICY_CHECK_NOSLEEP(inpcb_init_label, label, flag); if (error) { MAC_POLICY_PERFORM_NOSLEEP(inpcb_destroy_label, label); mac_labelzone_free(label); return (NULL); } return (label); } int mac_inpcb_init(struct inpcb *inp, int flag) { if (mac_labeled & MPC_OBJECT_INPCB) { inp->inp_label = mac_inpcb_label_alloc(flag); if (inp->inp_label == NULL) return (ENOMEM); } else inp->inp_label = NULL; return (0); } static struct label * mac_ipq_label_alloc(int flag) { struct label *label; int error; label = mac_labelzone_alloc(flag); if (label == NULL) return (NULL); if (flag & M_WAITOK) MAC_POLICY_CHECK(ipq_init_label, label, flag); else MAC_POLICY_CHECK_NOSLEEP(ipq_init_label, label, flag); if (error) { MAC_POLICY_PERFORM_NOSLEEP(ipq_destroy_label, label); mac_labelzone_free(label); return (NULL); } return (label); } int mac_ipq_init(struct ipq *q, int flag) { if (mac_labeled & MPC_OBJECT_IPQ) { q->ipq_label = mac_ipq_label_alloc(flag); if (q->ipq_label == NULL) return (ENOMEM); } else q->ipq_label = NULL; return (0); } static void mac_inpcb_label_free(struct label *label) { MAC_POLICY_PERFORM_NOSLEEP(inpcb_destroy_label, label); mac_labelzone_free(label); } void mac_inpcb_destroy(struct inpcb *inp) { if (inp->inp_label != NULL) { mac_inpcb_label_free(inp->inp_label); inp->inp_label = NULL; } } static void mac_ipq_label_free(struct label *label) { MAC_POLICY_PERFORM_NOSLEEP(ipq_destroy_label, label); mac_labelzone_free(label); } void mac_ipq_destroy(struct ipq *q) { if (q->ipq_label != NULL) { mac_ipq_label_free(q->ipq_label); q->ipq_label = NULL; } } void mac_inpcb_create(struct socket *so, struct inpcb *inp) { MAC_POLICY_PERFORM_NOSLEEP(inpcb_create, so, so->so_label, inp, inp->inp_label); } void mac_ipq_reassemble(struct ipq *q, struct mbuf *m) { struct label *label; if (mac_policy_count == 0) return; label = mac_mbuf_to_label(m); MAC_POLICY_PERFORM_NOSLEEP(ipq_reassemble, q, q->ipq_label, m, label); } void mac_netinet_fragment(struct mbuf *m, struct mbuf *frag) { struct label *mlabel, *fraglabel; if (mac_policy_count == 0) return; mlabel = mac_mbuf_to_label(m); fraglabel = mac_mbuf_to_label(frag); MAC_POLICY_PERFORM_NOSLEEP(netinet_fragment, m, mlabel, frag, fraglabel); } void mac_ipq_create(struct mbuf *m, struct ipq *q) { struct label *label; if (mac_policy_count == 0) return; label = mac_mbuf_to_label(m); MAC_POLICY_PERFORM_NOSLEEP(ipq_create, m, label, q, q->ipq_label); } void mac_inpcb_create_mbuf(struct inpcb *inp, struct mbuf *m) { struct label *mlabel; INP_LOCK_ASSERT(inp); if (mac_policy_count == 0) return; mlabel = mac_mbuf_to_label(m); MAC_POLICY_PERFORM_NOSLEEP(inpcb_create_mbuf, inp, inp->inp_label, m, mlabel); } int mac_ipq_match(struct mbuf *m, struct ipq *q) { struct label *label; int result; if (mac_policy_count == 0) return (1); label = mac_mbuf_to_label(m); result = 1; MAC_POLICY_BOOLEAN_NOSLEEP(ipq_match, &&, m, label, q, q->ipq_label); return (result); } void mac_netinet_arp_send(struct ifnet *ifp, struct mbuf *m) { struct label *mlabel; int locked; if (mac_policy_count == 0) return; mlabel = mac_mbuf_to_label(m); MAC_IFNET_LOCK(ifp, locked); - MAC_POLICY_PERFORM_NOSLEEP(netinet_arp_send, ifp, ifp->if_label, m, - mlabel); + MAC_POLICY_PERFORM_NOSLEEP(netinet_arp_send, ifp, if_getmaclabel(ifp), + m, mlabel); MAC_IFNET_UNLOCK(ifp, locked); } void mac_netinet_icmp_reply(struct mbuf *mrecv, struct mbuf *msend) { struct label *mrecvlabel, *msendlabel; if (mac_policy_count == 0) return; mrecvlabel = mac_mbuf_to_label(mrecv); msendlabel = mac_mbuf_to_label(msend); MAC_POLICY_PERFORM_NOSLEEP(netinet_icmp_reply, mrecv, mrecvlabel, msend, msendlabel); } void mac_netinet_icmp_replyinplace(struct mbuf *m) { struct label *label; if (mac_policy_count == 0) return; label = mac_mbuf_to_label(m); MAC_POLICY_PERFORM_NOSLEEP(netinet_icmp_replyinplace, m, label); } void mac_netinet_igmp_send(struct ifnet *ifp, struct mbuf *m) { struct label *mlabel; int locked; if (mac_policy_count == 0) return; mlabel = mac_mbuf_to_label(m); MAC_IFNET_LOCK(ifp, locked); - MAC_POLICY_PERFORM_NOSLEEP(netinet_igmp_send, ifp, ifp->if_label, m, - mlabel); + MAC_POLICY_PERFORM_NOSLEEP(netinet_igmp_send, ifp, if_getmaclabel(ifp), + m, mlabel); MAC_IFNET_UNLOCK(ifp, locked); } void mac_netinet_tcp_reply(struct mbuf *m) { struct label *label; if (mac_policy_count == 0) return; label = mac_mbuf_to_label(m); MAC_POLICY_PERFORM_NOSLEEP(netinet_tcp_reply, m, label); } void mac_ipq_update(struct mbuf *m, struct ipq *q) { struct label *label; if (mac_policy_count == 0) return; label = mac_mbuf_to_label(m); MAC_POLICY_PERFORM_NOSLEEP(ipq_update, m, label, q, q->ipq_label); } MAC_CHECK_PROBE_DEFINE2(inpcb_check_deliver, "struct inpcb *", "struct mbuf *"); int mac_inpcb_check_deliver(struct inpcb *inp, struct mbuf *m) { struct label *label; int error; M_ASSERTPKTHDR(m); if (mac_policy_count == 0) return (0); label = mac_mbuf_to_label(m); MAC_POLICY_CHECK_NOSLEEP(inpcb_check_deliver, inp, inp->inp_label, m, label); MAC_CHECK_PROBE2(inpcb_check_deliver, error, inp, m); return (error); } MAC_CHECK_PROBE_DEFINE2(inpcb_check_visible, "struct ucred *", "struct inpcb *"); int mac_inpcb_check_visible(struct ucred *cred, struct inpcb *inp) { int error; INP_LOCK_ASSERT(inp); MAC_POLICY_CHECK_NOSLEEP(inpcb_check_visible, cred, inp, inp->inp_label); MAC_CHECK_PROBE2(inpcb_check_visible, error, cred, inp); return (error); } void mac_inpcb_sosetlabel(struct socket *so, struct inpcb *inp) { INP_WLOCK_ASSERT(inp); SOCK_LOCK_ASSERT(so); MAC_POLICY_PERFORM_NOSLEEP(inpcb_sosetlabel, so, so->so_label, inp, inp->inp_label); } void mac_netinet_firewall_reply(struct mbuf *mrecv, struct mbuf *msend) { struct label *mrecvlabel, *msendlabel; M_ASSERTPKTHDR(mrecv); M_ASSERTPKTHDR(msend); if (mac_policy_count == 0) return; mrecvlabel = mac_mbuf_to_label(mrecv); msendlabel = mac_mbuf_to_label(msend); MAC_POLICY_PERFORM_NOSLEEP(netinet_firewall_reply, mrecv, mrecvlabel, msend, msendlabel); } void mac_netinet_firewall_send(struct mbuf *m) { struct label *label; M_ASSERTPKTHDR(m); if (mac_policy_count == 0) return; label = mac_mbuf_to_label(m); MAC_POLICY_PERFORM_NOSLEEP(netinet_firewall_send, m, label); } /* * These functions really should be referencing the syncache structure * instead of the label. However, due to some of the complexities associated * with exposing this syncache structure we operate directly on its label * pointer. This should be OK since we aren't making any access control * decisions within this code directly, we are merely allocating and copying * label storage so we can properly initialize mbuf labels for any packets * the syncache code might create. */ void mac_syncache_destroy(struct label **label) { if (*label != NULL) { MAC_POLICY_PERFORM_NOSLEEP(syncache_destroy_label, *label); mac_labelzone_free(*label); *label = NULL; } } int mac_syncache_init(struct label **label) { int error; if (mac_labeled & MPC_OBJECT_SYNCACHE) { *label = mac_labelzone_alloc(M_NOWAIT); if (*label == NULL) return (ENOMEM); /* * Since we are holding the inpcb locks the policy can not * allocate policy specific label storage using M_WAITOK. So * we need to do a MAC_CHECK instead of the typical * MAC_PERFORM so we can propagate allocation failures back * to the syncache code. */ MAC_POLICY_CHECK_NOSLEEP(syncache_init_label, *label, M_NOWAIT); if (error) { MAC_POLICY_PERFORM_NOSLEEP(syncache_destroy_label, *label); mac_labelzone_free(*label); } return (error); } else *label = NULL; return (0); } void mac_syncache_create(struct label *label, struct inpcb *inp) { INP_LOCK_ASSERT(inp); MAC_POLICY_PERFORM_NOSLEEP(syncache_create, label, inp); } void mac_syncache_create_mbuf(struct label *sc_label, struct mbuf *m) { struct label *mlabel; M_ASSERTPKTHDR(m); if (mac_policy_count == 0) return; mlabel = mac_mbuf_to_label(m); MAC_POLICY_PERFORM_NOSLEEP(syncache_create_mbuf, sc_label, m, mlabel); } diff --git a/sys/security/mac/mac_inet6.c b/sys/security/mac/mac_inet6.c index a080a74b17a3..cb0812bab785 100644 --- a/sys/security/mac/mac_inet6.c +++ b/sys/security/mac/mac_inet6.c @@ -1,188 +1,188 @@ /*- * Copyright (c) 2007-2009 Robert N. M. Watson * All rights reserved. * * This software was developed by Robert Watson for the TrustedBSD Project. * * This software was developed at the University of Cambridge Computer * Laboratory with support from a grant from Google, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct label * mac_ip6q_label_alloc(int flag) { struct label *label; int error; label = mac_labelzone_alloc(flag); if (label == NULL) return (NULL); if (flag & M_WAITOK) MAC_POLICY_CHECK(ip6q_init_label, label, flag); else MAC_POLICY_CHECK_NOSLEEP(ip6q_init_label, label, flag); if (error) { MAC_POLICY_PERFORM_NOSLEEP(ip6q_destroy_label, label); mac_labelzone_free(label); return (NULL); } return (label); } int mac_ip6q_init(struct ip6q *q6, int flag) { if (mac_labeled & MPC_OBJECT_IP6Q) { q6->ip6q_label = mac_ip6q_label_alloc(flag); if (q6->ip6q_label == NULL) return (ENOMEM); } else q6->ip6q_label = NULL; return (0); } static void mac_ip6q_label_free(struct label *label) { MAC_POLICY_PERFORM_NOSLEEP(ip6q_destroy_label, label); mac_labelzone_free(label); } void mac_ip6q_destroy(struct ip6q *q6) { if (q6->ip6q_label != NULL) { mac_ip6q_label_free(q6->ip6q_label); q6->ip6q_label = NULL; } } void mac_ip6q_reassemble(struct ip6q *q6, struct mbuf *m) { struct label *label; if (mac_policy_count == 0) return; label = mac_mbuf_to_label(m); MAC_POLICY_PERFORM_NOSLEEP(ip6q_reassemble, q6, q6->ip6q_label, m, label); } void mac_ip6q_create(struct mbuf *m, struct ip6q *q6) { struct label *label; if (mac_policy_count == 0) return; label = mac_mbuf_to_label(m); MAC_POLICY_PERFORM_NOSLEEP(ip6q_create, m, label, q6, q6->ip6q_label); } int mac_ip6q_match(struct mbuf *m, struct ip6q *q6) { struct label *label; int result; if (mac_policy_count == 0) return (1); label = mac_mbuf_to_label(m); result = 1; MAC_POLICY_BOOLEAN_NOSLEEP(ip6q_match, &&, m, label, q6, q6->ip6q_label); return (result); } void mac_ip6q_update(struct mbuf *m, struct ip6q *q6) { struct label *label; if (mac_policy_count == 0) return; label = mac_mbuf_to_label(m); MAC_POLICY_PERFORM_NOSLEEP(ip6q_update, m, label, q6, q6->ip6q_label); } void mac_netinet6_nd6_send(struct ifnet *ifp, struct mbuf *m) { struct label *mlabel; if (mac_policy_count == 0) return; mlabel = mac_mbuf_to_label(m); - MAC_POLICY_PERFORM_NOSLEEP(netinet6_nd6_send, ifp, ifp->if_label, m, + MAC_POLICY_PERFORM_NOSLEEP(netinet6_nd6_send, ifp, if_getmaclabel(ifp), m, mlabel); } diff --git a/sys/security/mac/mac_net.c b/sys/security/mac/mac_net.c index 372619c7b583..c21918c99e3e 100644 --- a/sys/security/mac/mac_net.c +++ b/sys/security/mac/mac_net.c @@ -1,502 +1,502 @@ /*- * Copyright (c) 1999-2002, 2009, 2019 Robert N. M. Watson * Copyright (c) 2001 Ilmar S. Habibulin * Copyright (c) 2001-2004 Networks Associates Technology, Inc. * Copyright (c) 2006 SPARTA, Inc. * Copyright (c) 2008 Apple Inc. * All rights reserved. * * This software was developed by Robert Watson and Ilmar Habibulin for the * TrustedBSD Project. * * This software was enhanced by SPARTA ISSO under SPAWAR contract * N66001-04-C-6019 ("SEFOS"). * * This software was developed for the FreeBSD Project in part by Network * Associates Laboratories, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), * as part of the DARPA CHATS research program. * * This software was developed at the University of Cambridge Computer * Laboratory with support from a grant from Google, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * XXXRW: struct ifnet locking is incomplete in the network code, so we use * our own global mutex for struct ifnet. Non-ideal, but should help in the * SMP environment. * * This lock is acquired only if a loaded policy is using ifnet labeling. * This should not ever change during a MAC policy check, itself, but could * change during setup/return from a check, so we have to condition unlock on * previous lock. */ struct mtx mac_ifnet_mtx; MTX_SYSINIT(mac_ifnet_mtx, &mac_ifnet_mtx, "mac_ifnet", MTX_DEF); /* * Retrieve the label associated with an mbuf by searching for the tag. * Depending on the value of mac_labelmbufs, it's possible that a label will * not be present, in which case NULL is returned. Policies must handle the * possibility of an mbuf not having label storage if they do not enforce * early loading. */ struct label * mac_mbuf_to_label(struct mbuf *m) { struct m_tag *tag; struct label *label; if (m == NULL) return (NULL); tag = m_tag_find(m, PACKET_TAG_MACLABEL, NULL); if (tag == NULL) return (NULL); label = (struct label *)(tag+1); return (label); } static struct label * mac_bpfdesc_label_alloc(void) { struct label *label; label = mac_labelzone_alloc(M_WAITOK); MAC_POLICY_PERFORM(bpfdesc_init_label, label); return (label); } void mac_bpfdesc_init(struct bpf_d *d) { if (mac_labeled & MPC_OBJECT_BPFDESC) d->bd_label = mac_bpfdesc_label_alloc(); else d->bd_label = NULL; } static struct label * mac_ifnet_label_alloc(void) { struct label *label; label = mac_labelzone_alloc(M_WAITOK); MAC_POLICY_PERFORM(ifnet_init_label, label); return (label); } void mac_ifnet_init(struct ifnet *ifp) { if (mac_labeled & MPC_OBJECT_IFNET) - ifp->if_label = mac_ifnet_label_alloc(); + if_setmaclabel(ifp, mac_ifnet_label_alloc()); else - ifp->if_label = NULL; + if_setmaclabel(ifp, NULL); } int mac_mbuf_tag_init(struct m_tag *tag, int flag) { struct label *label; int error; label = (struct label *) (tag + 1); mac_init_label(label); if (flag & M_WAITOK) MAC_POLICY_CHECK(mbuf_init_label, label, flag); else MAC_POLICY_CHECK_NOSLEEP(mbuf_init_label, label, flag); if (error) { MAC_POLICY_PERFORM_NOSLEEP(mbuf_destroy_label, label); mac_destroy_label(label); } return (error); } int mac_mbuf_init(struct mbuf *m, int flag) { struct m_tag *tag; int error; M_ASSERTPKTHDR(m); if (mac_labeled & MPC_OBJECT_MBUF) { tag = m_tag_get(PACKET_TAG_MACLABEL, sizeof(struct label), flag); if (tag == NULL) return (ENOMEM); error = mac_mbuf_tag_init(tag, flag); if (error) { m_tag_free(tag); return (error); } m_tag_prepend(m, tag); } return (0); } static void mac_bpfdesc_label_free(struct label *label) { MAC_POLICY_PERFORM_NOSLEEP(bpfdesc_destroy_label, label); mac_labelzone_free(label); } void mac_bpfdesc_destroy(struct bpf_d *d) { if (d->bd_label != NULL) { mac_bpfdesc_label_free(d->bd_label); d->bd_label = NULL; } } static void mac_ifnet_label_free(struct label *label) { MAC_POLICY_PERFORM_NOSLEEP(ifnet_destroy_label, label); mac_labelzone_free(label); } void mac_ifnet_destroy(struct ifnet *ifp) { - - if (ifp->if_label != NULL) { - mac_ifnet_label_free(ifp->if_label); - ifp->if_label = NULL; + struct label *label = if_getmaclabel(ifp); + if (label != NULL) { + mac_ifnet_label_free(label); + if_setmaclabel(ifp, NULL); } } void mac_mbuf_tag_destroy(struct m_tag *tag) { struct label *label; label = (struct label *)(tag+1); MAC_POLICY_PERFORM_NOSLEEP(mbuf_destroy_label, label); mac_destroy_label(label); } /* * mac_mbuf_tag_copy is called when an mbuf header is duplicated, in which * case the labels must also be duplicated. */ void mac_mbuf_tag_copy(struct m_tag *src, struct m_tag *dest) { struct label *src_label, *dest_label; src_label = (struct label *)(src+1); dest_label = (struct label *)(dest+1); /* * mac_mbuf_tag_init() is called on the target tag in m_tag_copy(), * so we don't need to call it here. */ MAC_POLICY_PERFORM_NOSLEEP(mbuf_copy_label, src_label, dest_label); } void mac_mbuf_copy(struct mbuf *m_from, struct mbuf *m_to) { struct label *src_label, *dest_label; if (mac_policy_count == 0) return; src_label = mac_mbuf_to_label(m_from); dest_label = mac_mbuf_to_label(m_to); MAC_POLICY_PERFORM_NOSLEEP(mbuf_copy_label, src_label, dest_label); } static void mac_ifnet_copy_label(struct label *src, struct label *dest) { MAC_POLICY_PERFORM_NOSLEEP(ifnet_copy_label, src, dest); } static int mac_ifnet_externalize_label(struct label *label, char *elements, char *outbuf, size_t outbuflen) { int error; MAC_POLICY_EXTERNALIZE(ifnet, label, elements, outbuf, outbuflen); return (error); } static int mac_ifnet_internalize_label(struct label *label, char *string) { int error; MAC_POLICY_INTERNALIZE(ifnet, label, string); return (error); } void mac_ifnet_create(struct ifnet *ifp) { int locked; if (mac_policy_count == 0) return; MAC_IFNET_LOCK(ifp, locked); - MAC_POLICY_PERFORM_NOSLEEP(ifnet_create, ifp, ifp->if_label); + MAC_POLICY_PERFORM_NOSLEEP(ifnet_create, ifp, if_getmaclabel(ifp)); MAC_IFNET_UNLOCK(ifp, locked); } void mac_bpfdesc_create(struct ucred *cred, struct bpf_d *d) { MAC_POLICY_PERFORM_NOSLEEP(bpfdesc_create, cred, d, d->bd_label); } void mac_bpfdesc_create_mbuf(struct bpf_d *d, struct mbuf *m) { struct label *label; /* Assume reader lock is enough. */ BPFD_LOCK_ASSERT(d); if (mac_policy_count == 0) return; label = mac_mbuf_to_label(m); MAC_POLICY_PERFORM_NOSLEEP(bpfdesc_create_mbuf, d, d->bd_label, m, label); } void mac_ifnet_create_mbuf_impl(struct ifnet *ifp, struct mbuf *m) { struct label *label; int locked; label = mac_mbuf_to_label(m); MAC_IFNET_LOCK(ifp, locked); - MAC_POLICY_PERFORM_NOSLEEP(ifnet_create_mbuf, ifp, ifp->if_label, m, + MAC_POLICY_PERFORM_NOSLEEP(ifnet_create_mbuf, ifp, if_getmaclabel(ifp), m, label); MAC_IFNET_UNLOCK(ifp, locked); } MAC_CHECK_PROBE_DEFINE2(bpfdesc_check_receive, "struct bpf_d *", "struct ifnet *"); int mac_bpfdesc_check_receive(struct bpf_d *d, struct ifnet *ifp) { int error, locked; /* Assume reader lock is enough. */ BPFD_LOCK_ASSERT(d); if (mac_policy_count == 0) return (0); MAC_IFNET_LOCK(ifp, locked); MAC_POLICY_CHECK_NOSLEEP(bpfdesc_check_receive, d, d->bd_label, ifp, - ifp->if_label); + if_getmaclabel(ifp)); MAC_CHECK_PROBE2(bpfdesc_check_receive, error, d, ifp); MAC_IFNET_UNLOCK(ifp, locked); return (error); } MAC_CHECK_PROBE_DEFINE2(ifnet_check_transmit, "struct ifnet *", "struct mbuf *"); int mac_ifnet_check_transmit_impl(struct ifnet *ifp, struct mbuf *m) { struct label *label; int error, locked; M_ASSERTPKTHDR(m); label = mac_mbuf_to_label(m); MAC_IFNET_LOCK(ifp, locked); - MAC_POLICY_CHECK_NOSLEEP(ifnet_check_transmit, ifp, ifp->if_label, m, + MAC_POLICY_CHECK_NOSLEEP(ifnet_check_transmit, ifp, if_getmaclabel(ifp), m, label); MAC_CHECK_PROBE2(ifnet_check_transmit, error, ifp, m); MAC_IFNET_UNLOCK(ifp, locked); return (error); } int mac_ifnet_ioctl_get(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifp) { char *elements, *buffer; struct label *intlabel; struct mac mac; int error, locked; if (!(mac_labeled & MPC_OBJECT_IFNET)) return (EINVAL); error = copyin(ifr_data_get_ptr(ifr), &mac, sizeof(mac)); if (error) return (error); error = mac_check_structmac_consistent(&mac); if (error) return (error); elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL); if (error) { free(elements, M_MACTEMP); return (error); } buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); intlabel = mac_ifnet_label_alloc(); MAC_IFNET_LOCK(ifp, locked); - mac_ifnet_copy_label(ifp->if_label, intlabel); + mac_ifnet_copy_label(if_getmaclabel(ifp), intlabel); MAC_IFNET_UNLOCK(ifp, locked); error = mac_ifnet_externalize_label(intlabel, elements, buffer, mac.m_buflen); mac_ifnet_label_free(intlabel); if (error == 0) error = copyout(buffer, mac.m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } int mac_ifnet_ioctl_set(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifp) { struct label *intlabel; struct mac mac; char *buffer; int error, locked; if (!(mac_labeled & MPC_OBJECT_IFNET)) return (EINVAL); error = copyin(ifr_data_get_ptr(ifr), &mac, sizeof(mac)); if (error) return (error); error = mac_check_structmac_consistent(&mac); if (error) return (error); buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); error = copyinstr(mac.m_string, buffer, mac.m_buflen, NULL); if (error) { free(buffer, M_MACTEMP); return (error); } intlabel = mac_ifnet_label_alloc(); error = mac_ifnet_internalize_label(intlabel, buffer); free(buffer, M_MACTEMP); if (error) { mac_ifnet_label_free(intlabel); return (error); } /* * XXX: Note that this is a redundant privilege check, since policies * impose this check themselves if required by the policy * Eventually, this should go away. */ error = priv_check_cred(cred, PRIV_NET_SETIFMAC); if (error) { mac_ifnet_label_free(intlabel); return (error); } MAC_IFNET_LOCK(ifp, locked); MAC_POLICY_CHECK_NOSLEEP(ifnet_check_relabel, cred, ifp, - ifp->if_label, intlabel); + if_getmaclabel(ifp), intlabel); if (error) { MAC_IFNET_UNLOCK(ifp, locked); mac_ifnet_label_free(intlabel); return (error); } - MAC_POLICY_PERFORM_NOSLEEP(ifnet_relabel, cred, ifp, ifp->if_label, + MAC_POLICY_PERFORM_NOSLEEP(ifnet_relabel, cred, ifp, if_getmaclabel(ifp), intlabel); MAC_IFNET_UNLOCK(ifp, locked); mac_ifnet_label_free(intlabel); return (0); } diff --git a/sys/security/mac_biba/mac_biba.c b/sys/security/mac_biba/mac_biba.c index 08df65cc289d..d011f7e19a56 100644 --- a/sys/security/mac_biba/mac_biba.c +++ b/sys/security/mac_biba/mac_biba.c @@ -1,3799 +1,3799 @@ /*- * Copyright (c) 1999-2002, 2007-2011 Robert N. M. Watson * Copyright (c) 2001-2005 McAfee, Inc. * Copyright (c) 2006 SPARTA, Inc. * All rights reserved. * * This software was developed by Robert Watson for the TrustedBSD Project. * * This software was developed for the FreeBSD Project in part by McAfee * Research, the Security Research Division of McAfee, Inc. under * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA * CHATS research program. * * This software was enhanced by SPARTA ISSO under SPAWAR contract * N66001-04-C-6019 ("SEFOS"). * * This software was developed at the University of Cambridge Computer * Laboratory with support from a grant from Google, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Developed by the TrustedBSD Project. * * Biba fixed label mandatory integrity policy. */ #include #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 #include #include #include #include #include SYSCTL_DECL(_security_mac); static SYSCTL_NODE(_security_mac, OID_AUTO, biba, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "TrustedBSD mac_biba policy controls"); static int biba_label_size = sizeof(struct mac_biba); SYSCTL_INT(_security_mac_biba, OID_AUTO, label_size, CTLFLAG_RD, &biba_label_size, 0, "Size of struct mac_biba"); static int biba_enabled = 1; SYSCTL_INT(_security_mac_biba, OID_AUTO, enabled, CTLFLAG_RWTUN, &biba_enabled, 0, "Enforce MAC/Biba policy"); static int destroyed_not_inited; SYSCTL_INT(_security_mac_biba, OID_AUTO, destroyed_not_inited, CTLFLAG_RD, &destroyed_not_inited, 0, "Count of labels destroyed but not inited"); static int trust_all_interfaces = 0; SYSCTL_INT(_security_mac_biba, OID_AUTO, trust_all_interfaces, CTLFLAG_RDTUN, &trust_all_interfaces, 0, "Consider all interfaces 'trusted' by MAC/Biba"); static char trusted_interfaces[128]; SYSCTL_STRING(_security_mac_biba, OID_AUTO, trusted_interfaces, CTLFLAG_RDTUN, trusted_interfaces, 0, "Interfaces considered 'trusted' by MAC/Biba"); static int max_compartments = MAC_BIBA_MAX_COMPARTMENTS; SYSCTL_INT(_security_mac_biba, OID_AUTO, max_compartments, CTLFLAG_RD, &max_compartments, 0, "Maximum supported compartments"); static int ptys_equal = 0; SYSCTL_INT(_security_mac_biba, OID_AUTO, ptys_equal, CTLFLAG_RWTUN, &ptys_equal, 0, "Label pty devices as biba/equal on create"); static int interfaces_equal = 1; SYSCTL_INT(_security_mac_biba, OID_AUTO, interfaces_equal, CTLFLAG_RWTUN, &interfaces_equal, 0, "Label network interfaces as biba/equal on create"); static int revocation_enabled = 0; SYSCTL_INT(_security_mac_biba, OID_AUTO, revocation_enabled, CTLFLAG_RWTUN, &revocation_enabled, 0, "Revoke access to objects on relabel"); static int biba_slot; #define SLOT(l) ((struct mac_biba *)mac_label_get((l), biba_slot)) #define SLOT_SET(l, val) mac_label_set((l), biba_slot, (uintptr_t)(val)) static uma_zone_t zone_biba; static __inline int biba_bit_set_empty(u_char *set) { int i; for (i = 0; i < MAC_BIBA_MAX_COMPARTMENTS >> 3; i++) if (set[i] != 0) return (0); return (1); } static struct mac_biba * biba_alloc(int flag) { return (uma_zalloc(zone_biba, flag | M_ZERO)); } static void biba_free(struct mac_biba *mb) { if (mb != NULL) uma_zfree(zone_biba, mb); else atomic_add_int(&destroyed_not_inited, 1); } static int biba_atmostflags(struct mac_biba *mb, int flags) { if ((mb->mb_flags & flags) != mb->mb_flags) return (EINVAL); return (0); } static int biba_dominate_element(struct mac_biba_element *a, struct mac_biba_element *b) { int bit; switch (a->mbe_type) { case MAC_BIBA_TYPE_EQUAL: case MAC_BIBA_TYPE_HIGH: return (1); case MAC_BIBA_TYPE_LOW: switch (b->mbe_type) { case MAC_BIBA_TYPE_GRADE: case MAC_BIBA_TYPE_HIGH: return (0); case MAC_BIBA_TYPE_EQUAL: case MAC_BIBA_TYPE_LOW: return (1); default: panic("biba_dominate_element: b->mbe_type invalid"); } case MAC_BIBA_TYPE_GRADE: switch (b->mbe_type) { case MAC_BIBA_TYPE_EQUAL: case MAC_BIBA_TYPE_LOW: return (1); case MAC_BIBA_TYPE_HIGH: return (0); case MAC_BIBA_TYPE_GRADE: for (bit = 1; bit <= MAC_BIBA_MAX_COMPARTMENTS; bit++) if (!MAC_BIBA_BIT_TEST(bit, a->mbe_compartments) && MAC_BIBA_BIT_TEST(bit, b->mbe_compartments)) return (0); return (a->mbe_grade >= b->mbe_grade); default: panic("biba_dominate_element: b->mbe_type invalid"); } default: panic("biba_dominate_element: a->mbe_type invalid"); } return (0); } static int biba_subject_dominate_high(struct mac_biba *mb) { struct mac_biba_element *element; KASSERT((mb->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("biba_effective_in_range: mb not effective")); element = &mb->mb_effective; return (element->mbe_type == MAC_BIBA_TYPE_EQUAL || element->mbe_type == MAC_BIBA_TYPE_HIGH); } static int biba_range_in_range(struct mac_biba *rangea, struct mac_biba *rangeb) { return (biba_dominate_element(&rangeb->mb_rangehigh, &rangea->mb_rangehigh) && biba_dominate_element(&rangea->mb_rangelow, &rangeb->mb_rangelow)); } static int biba_effective_in_range(struct mac_biba *effective, struct mac_biba *range) { KASSERT((effective->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("biba_effective_in_range: a not effective")); KASSERT((range->mb_flags & MAC_BIBA_FLAG_RANGE) != 0, ("biba_effective_in_range: b not range")); return (biba_dominate_element(&range->mb_rangehigh, &effective->mb_effective) && biba_dominate_element(&effective->mb_effective, &range->mb_rangelow)); return (1); } static int biba_dominate_effective(struct mac_biba *a, struct mac_biba *b) { KASSERT((a->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("biba_dominate_effective: a not effective")); KASSERT((b->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("biba_dominate_effective: b not effective")); return (biba_dominate_element(&a->mb_effective, &b->mb_effective)); } static int biba_equal_element(struct mac_biba_element *a, struct mac_biba_element *b) { if (a->mbe_type == MAC_BIBA_TYPE_EQUAL || b->mbe_type == MAC_BIBA_TYPE_EQUAL) return (1); return (a->mbe_type == b->mbe_type && a->mbe_grade == b->mbe_grade); } static int biba_equal_effective(struct mac_biba *a, struct mac_biba *b) { KASSERT((a->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("biba_equal_effective: a not effective")); KASSERT((b->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("biba_equal_effective: b not effective")); return (biba_equal_element(&a->mb_effective, &b->mb_effective)); } static int biba_contains_equal(struct mac_biba *mb) { if (mb->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { if (mb->mb_effective.mbe_type == MAC_BIBA_TYPE_EQUAL) return (1); } if (mb->mb_flags & MAC_BIBA_FLAG_RANGE) { if (mb->mb_rangelow.mbe_type == MAC_BIBA_TYPE_EQUAL) return (1); if (mb->mb_rangehigh.mbe_type == MAC_BIBA_TYPE_EQUAL) return (1); } return (0); } static int biba_subject_privileged(struct mac_biba *mb) { KASSERT((mb->mb_flags & MAC_BIBA_FLAGS_BOTH) == MAC_BIBA_FLAGS_BOTH, ("biba_subject_privileged: subject doesn't have both labels")); /* If the effective is EQUAL, it's ok. */ if (mb->mb_effective.mbe_type == MAC_BIBA_TYPE_EQUAL) return (0); /* If either range endpoint is EQUAL, it's ok. */ if (mb->mb_rangelow.mbe_type == MAC_BIBA_TYPE_EQUAL || mb->mb_rangehigh.mbe_type == MAC_BIBA_TYPE_EQUAL) return (0); /* If the range is low-high, it's ok. */ if (mb->mb_rangelow.mbe_type == MAC_BIBA_TYPE_LOW && mb->mb_rangehigh.mbe_type == MAC_BIBA_TYPE_HIGH) return (0); /* It's not ok. */ return (EPERM); } static int biba_high_effective(struct mac_biba *mb) { KASSERT((mb->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("biba_equal_effective: mb not effective")); return (mb->mb_effective.mbe_type == MAC_BIBA_TYPE_HIGH); } static int biba_valid(struct mac_biba *mb) { if (mb->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { switch (mb->mb_effective.mbe_type) { case MAC_BIBA_TYPE_GRADE: break; case MAC_BIBA_TYPE_EQUAL: case MAC_BIBA_TYPE_HIGH: case MAC_BIBA_TYPE_LOW: if (mb->mb_effective.mbe_grade != 0 || !MAC_BIBA_BIT_SET_EMPTY( mb->mb_effective.mbe_compartments)) return (EINVAL); break; default: return (EINVAL); } } else { if (mb->mb_effective.mbe_type != MAC_BIBA_TYPE_UNDEF) return (EINVAL); } if (mb->mb_flags & MAC_BIBA_FLAG_RANGE) { switch (mb->mb_rangelow.mbe_type) { case MAC_BIBA_TYPE_GRADE: break; case MAC_BIBA_TYPE_EQUAL: case MAC_BIBA_TYPE_HIGH: case MAC_BIBA_TYPE_LOW: if (mb->mb_rangelow.mbe_grade != 0 || !MAC_BIBA_BIT_SET_EMPTY( mb->mb_rangelow.mbe_compartments)) return (EINVAL); break; default: return (EINVAL); } switch (mb->mb_rangehigh.mbe_type) { case MAC_BIBA_TYPE_GRADE: break; case MAC_BIBA_TYPE_EQUAL: case MAC_BIBA_TYPE_HIGH: case MAC_BIBA_TYPE_LOW: if (mb->mb_rangehigh.mbe_grade != 0 || !MAC_BIBA_BIT_SET_EMPTY( mb->mb_rangehigh.mbe_compartments)) return (EINVAL); break; default: return (EINVAL); } if (!biba_dominate_element(&mb->mb_rangehigh, &mb->mb_rangelow)) return (EINVAL); } else { if (mb->mb_rangelow.mbe_type != MAC_BIBA_TYPE_UNDEF || mb->mb_rangehigh.mbe_type != MAC_BIBA_TYPE_UNDEF) return (EINVAL); } return (0); } static void biba_set_range(struct mac_biba *mb, u_short typelow, u_short gradelow, u_char *compartmentslow, u_short typehigh, u_short gradehigh, u_char *compartmentshigh) { mb->mb_rangelow.mbe_type = typelow; mb->mb_rangelow.mbe_grade = gradelow; if (compartmentslow != NULL) memcpy(mb->mb_rangelow.mbe_compartments, compartmentslow, sizeof(mb->mb_rangelow.mbe_compartments)); mb->mb_rangehigh.mbe_type = typehigh; mb->mb_rangehigh.mbe_grade = gradehigh; if (compartmentshigh != NULL) memcpy(mb->mb_rangehigh.mbe_compartments, compartmentshigh, sizeof(mb->mb_rangehigh.mbe_compartments)); mb->mb_flags |= MAC_BIBA_FLAG_RANGE; } static void biba_set_effective(struct mac_biba *mb, u_short type, u_short grade, u_char *compartments) { mb->mb_effective.mbe_type = type; mb->mb_effective.mbe_grade = grade; if (compartments != NULL) memcpy(mb->mb_effective.mbe_compartments, compartments, sizeof(mb->mb_effective.mbe_compartments)); mb->mb_flags |= MAC_BIBA_FLAG_EFFECTIVE; } static void biba_copy_range(struct mac_biba *labelfrom, struct mac_biba *labelto) { KASSERT((labelfrom->mb_flags & MAC_BIBA_FLAG_RANGE) != 0, ("biba_copy_range: labelfrom not range")); labelto->mb_rangelow = labelfrom->mb_rangelow; labelto->mb_rangehigh = labelfrom->mb_rangehigh; labelto->mb_flags |= MAC_BIBA_FLAG_RANGE; } static void biba_copy_effective(struct mac_biba *labelfrom, struct mac_biba *labelto) { KASSERT((labelfrom->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("biba_copy_effective: labelfrom not effective")); labelto->mb_effective = labelfrom->mb_effective; labelto->mb_flags |= MAC_BIBA_FLAG_EFFECTIVE; } static void biba_copy(struct mac_biba *source, struct mac_biba *dest) { if (source->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) biba_copy_effective(source, dest); if (source->mb_flags & MAC_BIBA_FLAG_RANGE) biba_copy_range(source, dest); } /* * Policy module operations. */ static void biba_init(struct mac_policy_conf *conf) { zone_biba = uma_zcreate("mac_biba", sizeof(struct mac_biba), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); } /* * Label operations. */ static void biba_init_label(struct label *label) { SLOT_SET(label, biba_alloc(M_WAITOK)); } static int biba_init_label_waitcheck(struct label *label, int flag) { SLOT_SET(label, biba_alloc(flag)); if (SLOT(label) == NULL) return (ENOMEM); return (0); } static void biba_destroy_label(struct label *label) { biba_free(SLOT(label)); SLOT_SET(label, NULL); } /* * biba_element_to_string() accepts an sbuf and Biba element. It converts * the Biba element to a string and stores the result in the sbuf; if there * isn't space in the sbuf, -1 is returned. */ static int biba_element_to_string(struct sbuf *sb, struct mac_biba_element *element) { int i, first; switch (element->mbe_type) { case MAC_BIBA_TYPE_HIGH: return (sbuf_printf(sb, "high")); case MAC_BIBA_TYPE_LOW: return (sbuf_printf(sb, "low")); case MAC_BIBA_TYPE_EQUAL: return (sbuf_printf(sb, "equal")); case MAC_BIBA_TYPE_GRADE: if (sbuf_printf(sb, "%d", element->mbe_grade) == -1) return (-1); first = 1; for (i = 1; i <= MAC_BIBA_MAX_COMPARTMENTS; i++) { if (MAC_BIBA_BIT_TEST(i, element->mbe_compartments)) { if (first) { if (sbuf_putc(sb, ':') == -1) return (-1); if (sbuf_printf(sb, "%d", i) == -1) return (-1); first = 0; } else { if (sbuf_printf(sb, "+%d", i) == -1) return (-1); } } } return (0); default: panic("biba_element_to_string: invalid type (%d)", element->mbe_type); } } /* * biba_to_string() converts a Biba label to a string, and places the results * in the passed sbuf. It returns 0 on success, or EINVAL if there isn't * room in the sbuf. Note: the sbuf will be modified even in a failure case, * so the caller may need to revert the sbuf by restoring the offset if * that's undesired. */ static int biba_to_string(struct sbuf *sb, struct mac_biba *mb) { if (mb->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { if (biba_element_to_string(sb, &mb->mb_effective) == -1) return (EINVAL); } if (mb->mb_flags & MAC_BIBA_FLAG_RANGE) { if (sbuf_putc(sb, '(') == -1) return (EINVAL); if (biba_element_to_string(sb, &mb->mb_rangelow) == -1) return (EINVAL); if (sbuf_putc(sb, '-') == -1) return (EINVAL); if (biba_element_to_string(sb, &mb->mb_rangehigh) == -1) return (EINVAL); if (sbuf_putc(sb, ')') == -1) return (EINVAL); } return (0); } static int biba_externalize_label(struct label *label, char *element_name, struct sbuf *sb, int *claimed) { struct mac_biba *mb; if (strcmp(MAC_BIBA_LABEL_NAME, element_name) != 0) return (0); (*claimed)++; mb = SLOT(label); return (biba_to_string(sb, mb)); } static int biba_parse_element(struct mac_biba_element *element, char *string) { char *compartment, *end, *grade; int value; if (strcmp(string, "high") == 0 || strcmp(string, "hi") == 0) { element->mbe_type = MAC_BIBA_TYPE_HIGH; element->mbe_grade = MAC_BIBA_TYPE_UNDEF; } else if (strcmp(string, "low") == 0 || strcmp(string, "lo") == 0) { element->mbe_type = MAC_BIBA_TYPE_LOW; element->mbe_grade = MAC_BIBA_TYPE_UNDEF; } else if (strcmp(string, "equal") == 0 || strcmp(string, "eq") == 0) { element->mbe_type = MAC_BIBA_TYPE_EQUAL; element->mbe_grade = MAC_BIBA_TYPE_UNDEF; } else { element->mbe_type = MAC_BIBA_TYPE_GRADE; /* * Numeric grade piece of the element. */ grade = strsep(&string, ":"); value = strtol(grade, &end, 10); if (end == grade || *end != '\0') return (EINVAL); if (value < 0 || value > 65535) return (EINVAL); element->mbe_grade = value; /* * Optional compartment piece of the element. If none are * included, we assume that the label has no compartments. */ if (string == NULL) return (0); if (*string == '\0') return (0); while ((compartment = strsep(&string, "+")) != NULL) { value = strtol(compartment, &end, 10); if (compartment == end || *end != '\0') return (EINVAL); if (value < 1 || value > MAC_BIBA_MAX_COMPARTMENTS) return (EINVAL); MAC_BIBA_BIT_SET(value, element->mbe_compartments); } } return (0); } /* * Note: destructively consumes the string, make a local copy before calling * if that's a problem. */ static int biba_parse(struct mac_biba *mb, char *string) { char *rangehigh, *rangelow, *effective; int error; effective = strsep(&string, "("); if (*effective == '\0') effective = NULL; if (string != NULL) { rangelow = strsep(&string, "-"); if (string == NULL) return (EINVAL); rangehigh = strsep(&string, ")"); if (string == NULL) return (EINVAL); if (*string != '\0') return (EINVAL); } else { rangelow = NULL; rangehigh = NULL; } KASSERT((rangelow != NULL && rangehigh != NULL) || (rangelow == NULL && rangehigh == NULL), ("biba_parse: range mismatch")); bzero(mb, sizeof(*mb)); if (effective != NULL) { error = biba_parse_element(&mb->mb_effective, effective); if (error) return (error); mb->mb_flags |= MAC_BIBA_FLAG_EFFECTIVE; } if (rangelow != NULL) { error = biba_parse_element(&mb->mb_rangelow, rangelow); if (error) return (error); error = biba_parse_element(&mb->mb_rangehigh, rangehigh); if (error) return (error); mb->mb_flags |= MAC_BIBA_FLAG_RANGE; } error = biba_valid(mb); if (error) return (error); return (0); } static int biba_internalize_label(struct label *label, char *element_name, char *element_data, int *claimed) { struct mac_biba *mb, mb_temp; int error; if (strcmp(MAC_BIBA_LABEL_NAME, element_name) != 0) return (0); (*claimed)++; error = biba_parse(&mb_temp, element_data); if (error) return (error); mb = SLOT(label); *mb = mb_temp; return (0); } static void biba_copy_label(struct label *src, struct label *dest) { *SLOT(dest) = *SLOT(src); } /* * Object-specific entry point implementations are sorted alphabetically by * object type name and then by operation. */ static int biba_bpfdesc_check_receive(struct bpf_d *d, struct label *dlabel, struct ifnet *ifp, struct label *ifplabel) { struct mac_biba *a, *b; if (!biba_enabled) return (0); a = SLOT(dlabel); b = SLOT(ifplabel); if (biba_equal_effective(a, b)) return (0); return (EACCES); } static void biba_bpfdesc_create(struct ucred *cred, struct bpf_d *d, struct label *dlabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(dlabel); biba_copy_effective(source, dest); } static void biba_bpfdesc_create_mbuf(struct bpf_d *d, struct label *dlabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *source, *dest; source = SLOT(dlabel); dest = SLOT(mlabel); biba_copy_effective(source, dest); } static void biba_cred_associate_nfsd(struct ucred *cred) { struct mac_biba *label; label = SLOT(cred->cr_label); biba_set_effective(label, MAC_BIBA_TYPE_LOW, 0, NULL); biba_set_range(label, MAC_BIBA_TYPE_LOW, 0, NULL, MAC_BIBA_TYPE_HIGH, 0, NULL); } static int biba_cred_check_relabel(struct ucred *cred, struct label *newlabel) { struct mac_biba *subj, *new; int error; subj = SLOT(cred->cr_label); new = SLOT(newlabel); /* * If there is a Biba label update for the credential, it may * be an update of the effective, range, or both. */ error = biba_atmostflags(new, MAC_BIBA_FLAGS_BOTH); if (error) return (error); /* * If the Biba label is to be changed, authorize as appropriate. */ if (new->mb_flags & MAC_BIBA_FLAGS_BOTH) { /* * If the change request modifies both the Biba label * effective and range, check that the new effective will be * in the new range. */ if ((new->mb_flags & MAC_BIBA_FLAGS_BOTH) == MAC_BIBA_FLAGS_BOTH && !biba_effective_in_range(new, new)) return (EINVAL); /* * To change the Biba effective label on a credential, the * new effective label must be in the current range. */ if (new->mb_flags & MAC_BIBA_FLAG_EFFECTIVE && !biba_effective_in_range(new, subj)) return (EPERM); /* * To change the Biba range on a credential, the new range * label must be in the current range. */ if (new->mb_flags & MAC_BIBA_FLAG_RANGE && !biba_range_in_range(new, subj)) return (EPERM); /* * To have EQUAL in any component of the new credential Biba * label, the subject must already have EQUAL in their label. */ if (biba_contains_equal(new)) { error = biba_subject_privileged(subj); if (error) return (error); } } return (0); } static int biba_cred_check_visible(struct ucred *u1, struct ucred *u2) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(u1->cr_label); obj = SLOT(u2->cr_label); /* XXX: range */ if (!biba_dominate_effective(obj, subj)) return (ESRCH); return (0); } static void biba_cred_create_init(struct ucred *cred) { struct mac_biba *dest; dest = SLOT(cred->cr_label); biba_set_effective(dest, MAC_BIBA_TYPE_HIGH, 0, NULL); biba_set_range(dest, MAC_BIBA_TYPE_LOW, 0, NULL, MAC_BIBA_TYPE_HIGH, 0, NULL); } static void biba_cred_create_swapper(struct ucred *cred) { struct mac_biba *dest; dest = SLOT(cred->cr_label); biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); biba_set_range(dest, MAC_BIBA_TYPE_LOW, 0, NULL, MAC_BIBA_TYPE_HIGH, 0, NULL); } static void biba_cred_relabel(struct ucred *cred, struct label *newlabel) { struct mac_biba *source, *dest; source = SLOT(newlabel); dest = SLOT(cred->cr_label); biba_copy(source, dest); } static void biba_devfs_create_device(struct ucred *cred, struct mount *mp, struct cdev *dev, struct devfs_dirent *de, struct label *delabel) { struct mac_biba *mb; const char *dn; int biba_type; mb = SLOT(delabel); dn = devtoname(dev); if (strcmp(dn, "null") == 0 || strcmp(dn, "zero") == 0 || strcmp(dn, "random") == 0 || strncmp(dn, "fd/", strlen("fd/")) == 0) biba_type = MAC_BIBA_TYPE_EQUAL; else if (ptys_equal && (strncmp(dn, "ttyp", strlen("ttyp")) == 0 || strncmp(dn, "pts/", strlen("pts/")) == 0 || strncmp(dn, "ptyp", strlen("ptyp")) == 0)) biba_type = MAC_BIBA_TYPE_EQUAL; else biba_type = MAC_BIBA_TYPE_HIGH; biba_set_effective(mb, biba_type, 0, NULL); } static void biba_devfs_create_directory(struct mount *mp, char *dirname, int dirnamelen, struct devfs_dirent *de, struct label *delabel) { struct mac_biba *mb; mb = SLOT(delabel); biba_set_effective(mb, MAC_BIBA_TYPE_HIGH, 0, NULL); } static void biba_devfs_create_symlink(struct ucred *cred, struct mount *mp, struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de, struct label *delabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(delabel); biba_copy_effective(source, dest); } static void biba_devfs_update(struct mount *mp, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vplabel) { struct mac_biba *source, *dest; source = SLOT(vplabel); dest = SLOT(delabel); biba_copy(source, dest); } static void biba_devfs_vnode_associate(struct mount *mp, struct label *mntlabel, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vplabel) { struct mac_biba *source, *dest; source = SLOT(delabel); dest = SLOT(vplabel); biba_copy_effective(source, dest); } static int biba_ifnet_check_relabel(struct ucred *cred, struct ifnet *ifp, struct label *ifplabel, struct label *newlabel) { struct mac_biba *subj, *new; int error; subj = SLOT(cred->cr_label); new = SLOT(newlabel); /* * If there is a Biba label update for the interface, it may be an * update of the effective, range, or both. */ error = biba_atmostflags(new, MAC_BIBA_FLAGS_BOTH); if (error) return (error); /* * Relabling network interfaces requires Biba privilege. */ error = biba_subject_privileged(subj); if (error) return (error); return (0); } static int biba_ifnet_check_transmit(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *p, *i; if (!biba_enabled) return (0); p = SLOT(mlabel); i = SLOT(ifplabel); return (biba_effective_in_range(p, i) ? 0 : EACCES); } static void biba_ifnet_create(struct ifnet *ifp, struct label *ifplabel) { char tifname[IFNAMSIZ], *p, *q; char tiflist[sizeof(trusted_interfaces)]; struct mac_biba *dest; int len, type; dest = SLOT(ifplabel); - if (ifp->if_type == IFT_LOOP || interfaces_equal != 0) { + if (if_gettype(ifp) == IFT_LOOP || interfaces_equal != 0) { type = MAC_BIBA_TYPE_EQUAL; goto set; } if (trust_all_interfaces) { type = MAC_BIBA_TYPE_HIGH; goto set; } type = MAC_BIBA_TYPE_LOW; if (trusted_interfaces[0] == '\0' || !strvalid(trusted_interfaces, sizeof(trusted_interfaces))) goto set; bzero(tiflist, sizeof(tiflist)); for (p = trusted_interfaces, q = tiflist; *p != '\0'; p++, q++) if(*p != ' ' && *p != '\t') *q = *p; for (p = q = tiflist;; p++) { if (*p == ',' || *p == '\0') { len = p - q; if (len < IFNAMSIZ) { bzero(tifname, sizeof(tifname)); bcopy(q, tifname, len); - if (strcmp(tifname, ifp->if_xname) == 0) { + if (strcmp(tifname, if_name(ifp)) == 0) { type = MAC_BIBA_TYPE_HIGH; break; } } else { *p = '\0'; printf("mac_biba warning: interface name " "\"%s\" is too long (must be < %d)\n", q, IFNAMSIZ); } if (*p == '\0') break; q = p + 1; } } set: biba_set_effective(dest, type, 0, NULL); biba_set_range(dest, type, 0, NULL, type, 0, NULL); } static void biba_ifnet_create_mbuf(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *source, *dest; source = SLOT(ifplabel); dest = SLOT(mlabel); biba_copy_effective(source, dest); } static void biba_ifnet_relabel(struct ucred *cred, struct ifnet *ifp, struct label *ifplabel, struct label *newlabel) { struct mac_biba *source, *dest; source = SLOT(newlabel); dest = SLOT(ifplabel); biba_copy(source, dest); } static int biba_inpcb_check_deliver(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *p, *i; if (!biba_enabled) return (0); p = SLOT(mlabel); i = SLOT(inplabel); return (biba_equal_effective(p, i) ? 0 : EACCES); } static int biba_inpcb_check_visible(struct ucred *cred, struct inpcb *inp, struct label *inplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(inplabel); if (!biba_dominate_effective(obj, subj)) return (ENOENT); return (0); } static void biba_inpcb_create(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { struct mac_biba *source, *dest; source = SLOT(solabel); dest = SLOT(inplabel); SOCK_LOCK(so); biba_copy_effective(source, dest); SOCK_UNLOCK(so); } static void biba_inpcb_create_mbuf(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *source, *dest; source = SLOT(inplabel); dest = SLOT(mlabel); biba_copy_effective(source, dest); } static void biba_inpcb_sosetlabel(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { struct mac_biba *source, *dest; SOCK_LOCK_ASSERT(so); source = SLOT(solabel); dest = SLOT(inplabel); biba_copy(source, dest); } static void biba_ip6q_create(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { struct mac_biba *source, *dest; source = SLOT(mlabel); dest = SLOT(q6label); biba_copy_effective(source, dest); } static int biba_ip6q_match(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { struct mac_biba *a, *b; a = SLOT(q6label); b = SLOT(mlabel); return (biba_equal_effective(a, b)); } static void biba_ip6q_reassemble(struct ip6q *q6, struct label *q6label, struct mbuf *m, struct label *mlabel) { struct mac_biba *source, *dest; source = SLOT(q6label); dest = SLOT(mlabel); /* Just use the head, since we require them all to match. */ biba_copy_effective(source, dest); } static void biba_ip6q_update(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { /* NOOP: we only accept matching labels, so no need to update */ } static void biba_ipq_create(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { struct mac_biba *source, *dest; source = SLOT(mlabel); dest = SLOT(qlabel); biba_copy_effective(source, dest); } static int biba_ipq_match(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { struct mac_biba *a, *b; a = SLOT(qlabel); b = SLOT(mlabel); return (biba_equal_effective(a, b)); } static void biba_ipq_reassemble(struct ipq *q, struct label *qlabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *source, *dest; source = SLOT(qlabel); dest = SLOT(mlabel); /* Just use the head, since we require them all to match. */ biba_copy_effective(source, dest); } static void biba_ipq_update(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { /* NOOP: we only accept matching labels, so no need to update */ } static int biba_kld_check_load(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; int error; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); error = biba_subject_privileged(subj); if (error) return (error); obj = SLOT(vplabel); if (!biba_high_effective(obj)) return (EACCES); return (0); } static int biba_mount_check_stat(struct ucred *cred, struct mount *mp, struct label *mplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(mplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static void biba_mount_create(struct ucred *cred, struct mount *mp, struct label *mplabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(mplabel); biba_copy_effective(source, dest); } static void biba_netinet_arp_send(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *dest; dest = SLOT(mlabel); biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); } static void biba_netinet_firewall_reply(struct mbuf *mrecv, struct label *mrecvlabel, struct mbuf *msend, struct label *msendlabel) { struct mac_biba *source, *dest; source = SLOT(mrecvlabel); dest = SLOT(msendlabel); biba_copy_effective(source, dest); } static void biba_netinet_firewall_send(struct mbuf *m, struct label *mlabel) { struct mac_biba *dest; dest = SLOT(mlabel); /* XXX: where is the label for the firewall really coming from? */ biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); } static void biba_netinet_fragment(struct mbuf *m, struct label *mlabel, struct mbuf *frag, struct label *fraglabel) { struct mac_biba *source, *dest; source = SLOT(mlabel); dest = SLOT(fraglabel); biba_copy_effective(source, dest); } static void biba_netinet_icmp_reply(struct mbuf *mrecv, struct label *mrecvlabel, struct mbuf *msend, struct label *msendlabel) { struct mac_biba *source, *dest; source = SLOT(mrecvlabel); dest = SLOT(msendlabel); biba_copy_effective(source, dest); } static void biba_netinet_igmp_send(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *dest; dest = SLOT(mlabel); biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); } static void biba_netinet6_nd6_send(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *dest; dest = SLOT(mlabel); biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); } static int biba_pipe_check_ioctl(struct ucred *cred, struct pipepair *pp, struct label *pplabel, unsigned long cmd, void /* caddr_t */ *data) { if(!biba_enabled) return (0); /* XXX: This will be implemented soon... */ return (0); } static int biba_pipe_check_poll(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_pipe_check_read(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_pipe_check_relabel(struct ucred *cred, struct pipepair *pp, struct label *pplabel, struct label *newlabel) { struct mac_biba *subj, *obj, *new; int error; new = SLOT(newlabel); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); /* * If there is a Biba label update for a pipe, it must be a effective * update. */ error = biba_atmostflags(new, MAC_BIBA_FLAG_EFFECTIVE); if (error) return (error); /* * To perform a relabel of a pipe (Biba label or not), Biba must * authorize the relabel. */ if (!biba_effective_in_range(obj, subj)) return (EPERM); /* * If the Biba label is to be changed, authorize as appropriate. */ if (new->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { /* * To change the Biba label on a pipe, the new pipe label * must be in the subject range. */ if (!biba_effective_in_range(new, subj)) return (EPERM); /* * To change the Biba label on a pipe to be EQUAL, the * subject must have appropriate privilege. */ if (biba_contains_equal(new)) { error = biba_subject_privileged(subj); if (error) return (error); } } return (0); } static int biba_pipe_check_stat(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_pipe_check_write(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static void biba_pipe_create(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(pplabel); biba_copy_effective(source, dest); } static void biba_pipe_relabel(struct ucred *cred, struct pipepair *pp, struct label *pplabel, struct label *newlabel) { struct mac_biba *source, *dest; source = SLOT(newlabel); dest = SLOT(pplabel); biba_copy(source, dest); } static int biba_posixsem_check_openunlink(struct ucred *cred, struct ksem *ks, struct label *kslabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(kslabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_posixsem_check_setmode(struct ucred *cred, struct ksem *ks, struct label *kslabel, mode_t mode) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(kslabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_posixsem_check_setowner(struct ucred *cred, struct ksem *ks, struct label *kslabel, uid_t uid, gid_t gid) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(kslabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_posixsem_check_write(struct ucred *active_cred, struct ucred *file_cred, struct ksem *ks, struct label *kslabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(kslabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_posixsem_check_rdonly(struct ucred *active_cred, struct ucred *file_cred, struct ksem *ks, struct label *kslabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(kslabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static void biba_posixsem_create(struct ucred *cred, struct ksem *ks, struct label *kslabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(kslabel); biba_copy_effective(source, dest); } static int biba_posixshm_check_mmap(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, int prot, int flags) { struct mac_biba *subj, *obj; if (!biba_enabled || !revocation_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) { if (!biba_dominate_effective(obj, subj)) return (EACCES); } if (((prot & VM_PROT_WRITE) != 0) && ((flags & MAP_SHARED) != 0)) { if (!biba_dominate_effective(subj, obj)) return (EACCES); } return (0); } static int biba_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, accmode_t accmode) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (accmode & (VREAD | VEXEC | VSTAT_PERMS)) { if (!biba_dominate_effective(obj, subj)) return (EACCES); } if (accmode & VMODIFY_PERMS) { if (!biba_dominate_effective(subj, obj)) return (EACCES); } return (0); } static int biba_posixshm_check_read(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *vp, struct label *shmlabel) { struct mac_biba *subj, *obj; if (!biba_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(shmlabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, mode_t mode) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_posixshm_check_setowner(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, uid_t uid, gid_t gid) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_posixshm_check_stat(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *shmfd, struct label *shmlabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(shmlabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_posixshm_check_truncate(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *shmfd, struct label *shmlabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(shmlabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_posixshm_check_unlink(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_posixshm_check_write(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *vp, struct label *shmlabel) { struct mac_biba *subj, *obj; if (!biba_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(shmlabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static void biba_posixshm_create(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(shmlabel); biba_copy_effective(source, dest); } /* * Some system privileges are allowed regardless of integrity grade; others * are allowed only when running with privilege with respect to the Biba * policy as they might otherwise allow bypassing of the integrity policy. */ static int biba_priv_check(struct ucred *cred, int priv) { struct mac_biba *subj; int error; if (!biba_enabled) return (0); /* * Exempt only specific privileges from the Biba integrity policy. */ switch (priv) { case PRIV_KTRACE: case PRIV_MSGBUF: /* * Allow processes to manipulate basic process audit properties, and * to submit audit records. */ case PRIV_AUDIT_GETAUDIT: case PRIV_AUDIT_SETAUDIT: case PRIV_AUDIT_SUBMIT: /* * Allow processes to manipulate their regular UNIX credentials. */ case PRIV_CRED_SETUID: case PRIV_CRED_SETEUID: case PRIV_CRED_SETGID: case PRIV_CRED_SETEGID: case PRIV_CRED_SETGROUPS: case PRIV_CRED_SETREUID: case PRIV_CRED_SETREGID: case PRIV_CRED_SETRESUID: case PRIV_CRED_SETRESGID: /* * Allow processes to perform system monitoring. */ case PRIV_SEEOTHERGIDS: case PRIV_SEEOTHERUIDS: break; /* * Allow access to general process debugging facilities. We * separately control debugging based on MAC label. */ case PRIV_DEBUG_DIFFCRED: case PRIV_DEBUG_SUGID: case PRIV_DEBUG_UNPRIV: /* * Allow manipulating jails. */ case PRIV_JAIL_ATTACH: /* * Allow privilege with respect to the Partition policy, but not the * Privs policy. */ case PRIV_MAC_PARTITION: /* * Allow privilege with respect to process resource limits and login * context. */ case PRIV_PROC_LIMIT: case PRIV_PROC_SETLOGIN: case PRIV_PROC_SETRLIMIT: /* * Allow System V and POSIX IPC privileges. */ case PRIV_IPC_READ: case PRIV_IPC_WRITE: case PRIV_IPC_ADMIN: case PRIV_IPC_MSGSIZE: case PRIV_MQ_ADMIN: /* * Allow certain scheduler manipulations -- possibly this should be * controlled by more fine-grained policy, as potentially low * integrity processes can deny CPU to higher integrity ones. */ case PRIV_SCHED_DIFFCRED: case PRIV_SCHED_SETPRIORITY: case PRIV_SCHED_RTPRIO: case PRIV_SCHED_SETPOLICY: case PRIV_SCHED_SET: case PRIV_SCHED_SETPARAM: case PRIV_SCHED_IDPRIO: /* * More IPC privileges. */ case PRIV_SEM_WRITE: /* * Allow signaling privileges subject to integrity policy. */ case PRIV_SIGNAL_DIFFCRED: case PRIV_SIGNAL_SUGID: /* * Allow access to only limited sysctls from lower integrity levels; * piggy-back on the Jail definition. */ case PRIV_SYSCTL_WRITEJAIL: /* * Allow TTY-based privileges, subject to general device access using * labels on TTY device nodes, but not console privilege. */ case PRIV_TTY_DRAINWAIT: case PRIV_TTY_DTRWAIT: case PRIV_TTY_EXCLUSIVE: case PRIV_TTY_STI: case PRIV_TTY_SETA: /* * Grant most VFS privileges, as almost all are in practice bounded * by more specific checks using labels. */ case PRIV_VFS_READ: case PRIV_VFS_WRITE: case PRIV_VFS_ADMIN: case PRIV_VFS_EXEC: case PRIV_VFS_LOOKUP: case PRIV_VFS_CHFLAGS_DEV: case PRIV_VFS_CHOWN: case PRIV_VFS_CHROOT: case PRIV_VFS_RETAINSUGID: case PRIV_VFS_EXCEEDQUOTA: case PRIV_VFS_FCHROOT: case PRIV_VFS_FHOPEN: case PRIV_VFS_FHSTATFS: case PRIV_VFS_GENERATION: case PRIV_VFS_GETFH: case PRIV_VFS_GETQUOTA: case PRIV_VFS_LINK: case PRIV_VFS_MOUNT: case PRIV_VFS_MOUNT_OWNER: case PRIV_VFS_MOUNT_PERM: case PRIV_VFS_MOUNT_SUIDDIR: case PRIV_VFS_MOUNT_NONUSER: case PRIV_VFS_SETGID: case PRIV_VFS_STICKYFILE: case PRIV_VFS_SYSFLAGS: case PRIV_VFS_UNMOUNT: /* * Allow VM privileges; it would be nice if these were subject to * resource limits. */ case PRIV_VM_MADV_PROTECT: case PRIV_VM_MLOCK: case PRIV_VM_MUNLOCK: case PRIV_VM_SWAP_NOQUOTA: case PRIV_VM_SWAP_NORLIMIT: /* * Allow some but not all network privileges. In general, dont allow * reconfiguring the network stack, just normal use. */ case PRIV_NETINET_RESERVEDPORT: case PRIV_NETINET_RAW: case PRIV_NETINET_REUSEPORT: break; /* * All remaining system privileges are allow only if the process * holds privilege with respect to the Biba policy. */ default: subj = SLOT(cred->cr_label); error = biba_subject_privileged(subj); if (error) return (error); } return (0); } static int biba_proc_check_debug(struct ucred *cred, struct proc *p) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(p->p_ucred->cr_label); /* XXX: range checks */ if (!biba_dominate_effective(obj, subj)) return (ESRCH); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_proc_check_sched(struct ucred *cred, struct proc *p) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(p->p_ucred->cr_label); /* XXX: range checks */ if (!biba_dominate_effective(obj, subj)) return (ESRCH); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_proc_check_signal(struct ucred *cred, struct proc *p, int signum) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(p->p_ucred->cr_label); /* XXX: range checks */ if (!biba_dominate_effective(obj, subj)) return (ESRCH); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_socket_check_deliver(struct socket *so, struct label *solabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *p, *s; int error; if (!biba_enabled) return (0); p = SLOT(mlabel); s = SLOT(solabel); SOCK_LOCK(so); error = biba_equal_effective(p, s) ? 0 : EACCES; SOCK_UNLOCK(so); return (error); } static int biba_socket_check_relabel(struct ucred *cred, struct socket *so, struct label *solabel, struct label *newlabel) { struct mac_biba *subj, *obj, *new; int error; SOCK_LOCK_ASSERT(so); new = SLOT(newlabel); subj = SLOT(cred->cr_label); obj = SLOT(solabel); /* * If there is a Biba label update for the socket, it may be an * update of effective. */ error = biba_atmostflags(new, MAC_BIBA_FLAG_EFFECTIVE); if (error) return (error); /* * To relabel a socket, the old socket effective must be in the * subject range. */ if (!biba_effective_in_range(obj, subj)) return (EPERM); /* * If the Biba label is to be changed, authorize as appropriate. */ if (new->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { /* * To relabel a socket, the new socket effective must be in * the subject range. */ if (!biba_effective_in_range(new, subj)) return (EPERM); /* * To change the Biba label on the socket to contain EQUAL, * the subject must have appropriate privilege. */ if (biba_contains_equal(new)) { error = biba_subject_privileged(subj); if (error) return (error); } } return (0); } static int biba_socket_check_visible(struct ucred *cred, struct socket *so, struct label *solabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(solabel); SOCK_LOCK(so); if (!biba_dominate_effective(obj, subj)) { SOCK_UNLOCK(so); return (ENOENT); } SOCK_UNLOCK(so); return (0); } static void biba_socket_create(struct ucred *cred, struct socket *so, struct label *solabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(solabel); biba_copy_effective(source, dest); } static void biba_socket_create_mbuf(struct socket *so, struct label *solabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *source, *dest; source = SLOT(solabel); dest = SLOT(mlabel); SOCK_LOCK(so); biba_copy_effective(source, dest); SOCK_UNLOCK(so); } static void biba_socket_newconn(struct socket *oldso, struct label *oldsolabel, struct socket *newso, struct label *newsolabel) { struct mac_biba source, *dest; SOCK_LOCK(oldso); source = *SLOT(oldsolabel); SOCK_UNLOCK(oldso); dest = SLOT(newsolabel); SOCK_LOCK(newso); biba_copy_effective(&source, dest); SOCK_UNLOCK(newso); } static void biba_socket_relabel(struct ucred *cred, struct socket *so, struct label *solabel, struct label *newlabel) { struct mac_biba *source, *dest; SOCK_LOCK_ASSERT(so); source = SLOT(newlabel); dest = SLOT(solabel); biba_copy(source, dest); } static void biba_socketpeer_set_from_mbuf(struct mbuf *m, struct label *mlabel, struct socket *so, struct label *sopeerlabel) { struct mac_biba *source, *dest; source = SLOT(mlabel); dest = SLOT(sopeerlabel); SOCK_LOCK(so); biba_copy_effective(source, dest); SOCK_UNLOCK(so); } static void biba_socketpeer_set_from_socket(struct socket *oldso, struct label *oldsolabel, struct socket *newso, struct label *newsopeerlabel) { struct mac_biba source, *dest; SOCK_LOCK(oldso); source = *SLOT(oldsolabel); SOCK_UNLOCK(oldso); dest = SLOT(newsopeerlabel); SOCK_LOCK(newso); biba_copy_effective(&source, dest); SOCK_UNLOCK(newso); } static void biba_syncache_create(struct label *label, struct inpcb *inp) { struct mac_biba *source, *dest; source = SLOT(inp->inp_label); dest = SLOT(label); biba_copy_effective(source, dest); } static void biba_syncache_create_mbuf(struct label *sc_label, struct mbuf *m, struct label *mlabel) { struct mac_biba *source, *dest; source = SLOT(sc_label); dest = SLOT(mlabel); biba_copy_effective(source, dest); } static int biba_system_check_acct(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; int error; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); error = biba_subject_privileged(subj); if (error) return (error); if (vplabel == NULL) return (0); obj = SLOT(vplabel); if (!biba_high_effective(obj)) return (EACCES); return (0); } static int biba_system_check_auditctl(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; int error; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); error = biba_subject_privileged(subj); if (error) return (error); if (vplabel == NULL) return (0); obj = SLOT(vplabel); if (!biba_high_effective(obj)) return (EACCES); return (0); } static int biba_system_check_auditon(struct ucred *cred, int cmd) { struct mac_biba *subj; int error; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); error = biba_subject_privileged(subj); if (error) return (error); return (0); } static int biba_system_check_swapoff(struct ucred *cred, struct vnode *vp, struct label *label) { struct mac_biba *subj; int error; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); error = biba_subject_privileged(subj); if (error) return (error); return (0); } static int biba_system_check_swapon(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; int error; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); error = biba_subject_privileged(subj); if (error) return (error); if (!biba_high_effective(obj)) return (EACCES); return (0); } static int biba_system_check_sysctl(struct ucred *cred, struct sysctl_oid *oidp, void *arg1, int arg2, struct sysctl_req *req) { struct mac_biba *subj; int error; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); /* * Treat sysctl variables without CTLFLAG_ANYBODY flag as biba/high, * but also require privilege to change them. */ if (req->newptr != NULL && (oidp->oid_kind & CTLFLAG_ANYBODY) == 0) { if (!biba_subject_dominate_high(subj)) return (EACCES); error = biba_subject_privileged(subj); if (error) return (error); } return (0); } static void biba_sysvmsg_cleanup(struct label *msglabel) { bzero(SLOT(msglabel), sizeof(struct mac_biba)); } static void biba_sysvmsg_create(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqlabel, struct msg *msgptr, struct label *msglabel) { struct mac_biba *source, *dest; /* Ignore the msgq label */ source = SLOT(cred->cr_label); dest = SLOT(msglabel); biba_copy_effective(source, dest); } static int biba_sysvmsq_check_msgrcv(struct ucred *cred, struct msg *msgptr, struct label *msglabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msglabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_sysvmsq_check_msgrmid(struct ucred *cred, struct msg *msgptr, struct label *msglabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msglabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_sysvmsq_check_msqget(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_sysvmsq_check_msqsnd(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_sysvmsq_check_msqrcv(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_sysvmsq_check_msqctl(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel, int cmd) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); switch(cmd) { case IPC_RMID: case IPC_SET: if (!biba_dominate_effective(subj, obj)) return (EACCES); break; case IPC_STAT: if (!biba_dominate_effective(obj, subj)) return (EACCES); break; default: return (EACCES); } return (0); } static void biba_sysvmsq_cleanup(struct label *msqlabel) { bzero(SLOT(msqlabel), sizeof(struct mac_biba)); } static void biba_sysvmsq_create(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqlabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(msqlabel); biba_copy_effective(source, dest); } static int biba_sysvsem_check_semctl(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel, int cmd) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(semaklabel); switch(cmd) { case IPC_RMID: case IPC_SET: case SETVAL: case SETALL: if (!biba_dominate_effective(subj, obj)) return (EACCES); break; case IPC_STAT: case GETVAL: case GETPID: case GETNCNT: case GETZCNT: case GETALL: if (!biba_dominate_effective(obj, subj)) return (EACCES); break; default: return (EACCES); } return (0); } static int biba_sysvsem_check_semget(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(semaklabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_sysvsem_check_semop(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel, size_t accesstype) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(semaklabel); if (accesstype & SEM_R) if (!biba_dominate_effective(obj, subj)) return (EACCES); if (accesstype & SEM_A) if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static void biba_sysvsem_cleanup(struct label *semalabel) { bzero(SLOT(semalabel), sizeof(struct mac_biba)); } static void biba_sysvsem_create(struct ucred *cred, struct semid_kernel *semakptr, struct label *semalabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(semalabel); biba_copy_effective(source, dest); } static int biba_sysvshm_check_shmat(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int shmflg) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmseglabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); if ((shmflg & SHM_RDONLY) == 0) { if (!biba_dominate_effective(subj, obj)) return (EACCES); } return (0); } static int biba_sysvshm_check_shmctl(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int cmd) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmseglabel); switch(cmd) { case IPC_RMID: case IPC_SET: if (!biba_dominate_effective(subj, obj)) return (EACCES); break; case IPC_STAT: case SHM_STAT: if (!biba_dominate_effective(obj, subj)) return (EACCES); break; default: return (EACCES); } return (0); } static int biba_sysvshm_check_shmget(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int shmflg) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmseglabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static void biba_sysvshm_cleanup(struct label *shmlabel) { bzero(SLOT(shmlabel), sizeof(struct mac_biba)); } static void biba_sysvshm_create(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmlabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(shmlabel); biba_copy_effective(source, dest); } static int biba_vnode_associate_extattr(struct mount *mp, struct label *mplabel, struct vnode *vp, struct label *vplabel) { struct mac_biba mb_temp, *source, *dest; int buflen, error; source = SLOT(mplabel); dest = SLOT(vplabel); buflen = sizeof(mb_temp); bzero(&mb_temp, buflen); error = vn_extattr_get(vp, IO_NODELOCKED, MAC_BIBA_EXTATTR_NAMESPACE, MAC_BIBA_EXTATTR_NAME, &buflen, (char *) &mb_temp, curthread); if (error == ENOATTR || error == EOPNOTSUPP) { /* Fall back to the mntlabel. */ biba_copy_effective(source, dest); return (0); } else if (error) return (error); if (buflen != sizeof(mb_temp)) { printf("biba_vnode_associate_extattr: bad size %d\n", buflen); return (EPERM); } if (biba_valid(&mb_temp) != 0) { printf("biba_vnode_associate_extattr: invalid\n"); return (EPERM); } if ((mb_temp.mb_flags & MAC_BIBA_FLAGS_BOTH) != MAC_BIBA_FLAG_EFFECTIVE) { printf("biba_vnode_associate_extattr: not effective\n"); return (EPERM); } biba_copy_effective(&mb_temp, dest); return (0); } static void biba_vnode_associate_singlelabel(struct mount *mp, struct label *mplabel, struct vnode *vp, struct label *vplabel) { struct mac_biba *source, *dest; source = SLOT(mplabel); dest = SLOT(vplabel); biba_copy_effective(source, dest); } static int biba_vnode_check_chdir(struct ucred *cred, struct vnode *dvp, struct label *dvplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_chroot(struct ucred *cred, struct vnode *dvp, struct label *dvplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_create(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct componentname *cnp, struct vattr *vap) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_deleteacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_deleteextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_exec(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct image_params *imgp, struct label *execlabel) { struct mac_biba *subj, *obj, *exec; int error; if (execlabel != NULL) { /* * We currently don't permit labels to be changed at * exec-time as part of Biba, so disallow non-NULL Biba label * elements in the execlabel. */ exec = SLOT(execlabel); error = biba_atmostflags(exec, 0); if (error) return (error); } if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_getacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_getextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_link(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_listextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_lookup(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct componentname *cnp) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_mmap(struct ucred *cred, struct vnode *vp, struct label *vplabel, int prot, int flags) { struct mac_biba *subj, *obj; /* * Rely on the use of open()-time protections to handle * non-revocation cases. */ if (!biba_enabled || !revocation_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) { if (!biba_dominate_effective(obj, subj)) return (EACCES); } if (((prot & VM_PROT_WRITE) != 0) && ((flags & MAP_SHARED) != 0)) { if (!biba_dominate_effective(subj, obj)) return (EACCES); } return (0); } static int biba_vnode_check_open(struct ucred *cred, struct vnode *vp, struct label *vplabel, accmode_t accmode) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); /* XXX privilege override for admin? */ if (accmode & (VREAD | VEXEC | VSTAT_PERMS)) { if (!biba_dominate_effective(obj, subj)) return (EACCES); } if (accmode & VMODIFY_PERMS) { if (!biba_dominate_effective(subj, obj)) return (EACCES); } return (0); } static int biba_vnode_check_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; if (!biba_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; if (!biba_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_readdir(struct ucred *cred, struct vnode *dvp, struct label *dvplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_readlink(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_relabel(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *newlabel) { struct mac_biba *old, *new, *subj; int error; old = SLOT(vplabel); new = SLOT(newlabel); subj = SLOT(cred->cr_label); /* * If there is a Biba label update for the vnode, it must be a * effective label. */ error = biba_atmostflags(new, MAC_BIBA_FLAG_EFFECTIVE); if (error) return (error); /* * To perform a relabel of the vnode (Biba label or not), Biba must * authorize the relabel. */ if (!biba_effective_in_range(old, subj)) return (EPERM); /* * If the Biba label is to be changed, authorize as appropriate. */ if (new->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { /* * To change the Biba label on a vnode, the new vnode label * must be in the subject range. */ if (!biba_effective_in_range(new, subj)) return (EPERM); /* * To change the Biba label on the vnode to be EQUAL, the * subject must have appropriate privilege. */ if (biba_contains_equal(new)) { error = biba_subject_privileged(subj); if (error) return (error); } } return (0); } static int biba_vnode_check_rename_from(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, int samedir, struct componentname *cnp) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); if (vp != NULL) { obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); } return (0); } static int biba_vnode_check_revoke(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_setacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type, struct acl *acl) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_setextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); /* XXX: protect the MAC EA in a special way? */ return (0); } static int biba_vnode_check_setflags(struct ucred *cred, struct vnode *vp, struct label *vplabel, u_long flags) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_setmode(struct ucred *cred, struct vnode *vp, struct label *vplabel, mode_t mode) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_setowner(struct ucred *cred, struct vnode *vp, struct label *vplabel, uid_t uid, gid_t gid) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_setutimes(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct timespec atime, struct timespec mtime) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_unlink(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; if (!biba_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_create_extattr(struct ucred *cred, struct mount *mp, struct label *mplabel, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_biba *source, *dest, mb_temp; size_t buflen; int error; buflen = sizeof(mb_temp); bzero(&mb_temp, buflen); source = SLOT(cred->cr_label); dest = SLOT(vplabel); biba_copy_effective(source, &mb_temp); error = vn_extattr_set(vp, IO_NODELOCKED, MAC_BIBA_EXTATTR_NAMESPACE, MAC_BIBA_EXTATTR_NAME, buflen, (char *) &mb_temp, curthread); if (error == 0) biba_copy_effective(source, dest); return (error); } static void biba_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *newlabel) { struct mac_biba *source, *dest; source = SLOT(newlabel); dest = SLOT(vplabel); biba_copy(source, dest); } static int biba_vnode_setlabel_extattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *intlabel) { struct mac_biba *source, mb_temp; size_t buflen; int error; buflen = sizeof(mb_temp); bzero(&mb_temp, buflen); source = SLOT(intlabel); if ((source->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) == 0) return (0); biba_copy_effective(source, &mb_temp); error = vn_extattr_set(vp, IO_NODELOCKED, MAC_BIBA_EXTATTR_NAMESPACE, MAC_BIBA_EXTATTR_NAME, buflen, (char *) &mb_temp, curthread); return (error); } static struct mac_policy_ops mac_biba_ops = { .mpo_init = biba_init, .mpo_bpfdesc_check_receive = biba_bpfdesc_check_receive, .mpo_bpfdesc_create = biba_bpfdesc_create, .mpo_bpfdesc_create_mbuf = biba_bpfdesc_create_mbuf, .mpo_bpfdesc_destroy_label = biba_destroy_label, .mpo_bpfdesc_init_label = biba_init_label, .mpo_cred_associate_nfsd = biba_cred_associate_nfsd, .mpo_cred_check_relabel = biba_cred_check_relabel, .mpo_cred_check_visible = biba_cred_check_visible, .mpo_cred_copy_label = biba_copy_label, .mpo_cred_create_init = biba_cred_create_init, .mpo_cred_create_swapper = biba_cred_create_swapper, .mpo_cred_destroy_label = biba_destroy_label, .mpo_cred_externalize_label = biba_externalize_label, .mpo_cred_init_label = biba_init_label, .mpo_cred_internalize_label = biba_internalize_label, .mpo_cred_relabel = biba_cred_relabel, .mpo_devfs_create_device = biba_devfs_create_device, .mpo_devfs_create_directory = biba_devfs_create_directory, .mpo_devfs_create_symlink = biba_devfs_create_symlink, .mpo_devfs_destroy_label = biba_destroy_label, .mpo_devfs_init_label = biba_init_label, .mpo_devfs_update = biba_devfs_update, .mpo_devfs_vnode_associate = biba_devfs_vnode_associate, .mpo_ifnet_check_relabel = biba_ifnet_check_relabel, .mpo_ifnet_check_transmit = biba_ifnet_check_transmit, .mpo_ifnet_copy_label = biba_copy_label, .mpo_ifnet_create = biba_ifnet_create, .mpo_ifnet_create_mbuf = biba_ifnet_create_mbuf, .mpo_ifnet_destroy_label = biba_destroy_label, .mpo_ifnet_externalize_label = biba_externalize_label, .mpo_ifnet_init_label = biba_init_label, .mpo_ifnet_internalize_label = biba_internalize_label, .mpo_ifnet_relabel = biba_ifnet_relabel, .mpo_inpcb_check_deliver = biba_inpcb_check_deliver, .mpo_inpcb_check_visible = biba_inpcb_check_visible, .mpo_inpcb_create = biba_inpcb_create, .mpo_inpcb_create_mbuf = biba_inpcb_create_mbuf, .mpo_inpcb_destroy_label = biba_destroy_label, .mpo_inpcb_init_label = biba_init_label_waitcheck, .mpo_inpcb_sosetlabel = biba_inpcb_sosetlabel, .mpo_ip6q_create = biba_ip6q_create, .mpo_ip6q_destroy_label = biba_destroy_label, .mpo_ip6q_init_label = biba_init_label_waitcheck, .mpo_ip6q_match = biba_ip6q_match, .mpo_ip6q_reassemble = biba_ip6q_reassemble, .mpo_ip6q_update = biba_ip6q_update, .mpo_ipq_create = biba_ipq_create, .mpo_ipq_destroy_label = biba_destroy_label, .mpo_ipq_init_label = biba_init_label_waitcheck, .mpo_ipq_match = biba_ipq_match, .mpo_ipq_reassemble = biba_ipq_reassemble, .mpo_ipq_update = biba_ipq_update, .mpo_kld_check_load = biba_kld_check_load, .mpo_mbuf_copy_label = biba_copy_label, .mpo_mbuf_destroy_label = biba_destroy_label, .mpo_mbuf_init_label = biba_init_label_waitcheck, .mpo_mount_check_stat = biba_mount_check_stat, .mpo_mount_create = biba_mount_create, .mpo_mount_destroy_label = biba_destroy_label, .mpo_mount_init_label = biba_init_label, .mpo_netinet_arp_send = biba_netinet_arp_send, .mpo_netinet_firewall_reply = biba_netinet_firewall_reply, .mpo_netinet_firewall_send = biba_netinet_firewall_send, .mpo_netinet_fragment = biba_netinet_fragment, .mpo_netinet_icmp_reply = biba_netinet_icmp_reply, .mpo_netinet_igmp_send = biba_netinet_igmp_send, .mpo_netinet6_nd6_send = biba_netinet6_nd6_send, .mpo_pipe_check_ioctl = biba_pipe_check_ioctl, .mpo_pipe_check_poll = biba_pipe_check_poll, .mpo_pipe_check_read = biba_pipe_check_read, .mpo_pipe_check_relabel = biba_pipe_check_relabel, .mpo_pipe_check_stat = biba_pipe_check_stat, .mpo_pipe_check_write = biba_pipe_check_write, .mpo_pipe_copy_label = biba_copy_label, .mpo_pipe_create = biba_pipe_create, .mpo_pipe_destroy_label = biba_destroy_label, .mpo_pipe_externalize_label = biba_externalize_label, .mpo_pipe_init_label = biba_init_label, .mpo_pipe_internalize_label = biba_internalize_label, .mpo_pipe_relabel = biba_pipe_relabel, .mpo_posixsem_check_getvalue = biba_posixsem_check_rdonly, .mpo_posixsem_check_open = biba_posixsem_check_openunlink, .mpo_posixsem_check_post = biba_posixsem_check_write, .mpo_posixsem_check_setmode = biba_posixsem_check_setmode, .mpo_posixsem_check_setowner = biba_posixsem_check_setowner, .mpo_posixsem_check_stat = biba_posixsem_check_rdonly, .mpo_posixsem_check_unlink = biba_posixsem_check_openunlink, .mpo_posixsem_check_wait = biba_posixsem_check_write, .mpo_posixsem_create = biba_posixsem_create, .mpo_posixsem_destroy_label = biba_destroy_label, .mpo_posixsem_init_label = biba_init_label, .mpo_posixshm_check_mmap = biba_posixshm_check_mmap, .mpo_posixshm_check_open = biba_posixshm_check_open, .mpo_posixshm_check_read = biba_posixshm_check_read, .mpo_posixshm_check_setmode = biba_posixshm_check_setmode, .mpo_posixshm_check_setowner = biba_posixshm_check_setowner, .mpo_posixshm_check_stat = biba_posixshm_check_stat, .mpo_posixshm_check_truncate = biba_posixshm_check_truncate, .mpo_posixshm_check_unlink = biba_posixshm_check_unlink, .mpo_posixshm_check_write = biba_posixshm_check_write, .mpo_posixshm_create = biba_posixshm_create, .mpo_posixshm_destroy_label = biba_destroy_label, .mpo_posixshm_init_label = biba_init_label, .mpo_priv_check = biba_priv_check, .mpo_proc_check_debug = biba_proc_check_debug, .mpo_proc_check_sched = biba_proc_check_sched, .mpo_proc_check_signal = biba_proc_check_signal, .mpo_socket_check_deliver = biba_socket_check_deliver, .mpo_socket_check_relabel = biba_socket_check_relabel, .mpo_socket_check_visible = biba_socket_check_visible, .mpo_socket_copy_label = biba_copy_label, .mpo_socket_create = biba_socket_create, .mpo_socket_create_mbuf = biba_socket_create_mbuf, .mpo_socket_destroy_label = biba_destroy_label, .mpo_socket_externalize_label = biba_externalize_label, .mpo_socket_init_label = biba_init_label_waitcheck, .mpo_socket_internalize_label = biba_internalize_label, .mpo_socket_newconn = biba_socket_newconn, .mpo_socket_relabel = biba_socket_relabel, .mpo_socketpeer_destroy_label = biba_destroy_label, .mpo_socketpeer_externalize_label = biba_externalize_label, .mpo_socketpeer_init_label = biba_init_label_waitcheck, .mpo_socketpeer_set_from_mbuf = biba_socketpeer_set_from_mbuf, .mpo_socketpeer_set_from_socket = biba_socketpeer_set_from_socket, .mpo_syncache_create = biba_syncache_create, .mpo_syncache_create_mbuf = biba_syncache_create_mbuf, .mpo_syncache_destroy_label = biba_destroy_label, .mpo_syncache_init_label = biba_init_label_waitcheck, .mpo_system_check_acct = biba_system_check_acct, .mpo_system_check_auditctl = biba_system_check_auditctl, .mpo_system_check_auditon = biba_system_check_auditon, .mpo_system_check_swapoff = biba_system_check_swapoff, .mpo_system_check_swapon = biba_system_check_swapon, .mpo_system_check_sysctl = biba_system_check_sysctl, .mpo_sysvmsg_cleanup = biba_sysvmsg_cleanup, .mpo_sysvmsg_create = biba_sysvmsg_create, .mpo_sysvmsg_destroy_label = biba_destroy_label, .mpo_sysvmsg_init_label = biba_init_label, .mpo_sysvmsq_check_msgrcv = biba_sysvmsq_check_msgrcv, .mpo_sysvmsq_check_msgrmid = biba_sysvmsq_check_msgrmid, .mpo_sysvmsq_check_msqget = biba_sysvmsq_check_msqget, .mpo_sysvmsq_check_msqsnd = biba_sysvmsq_check_msqsnd, .mpo_sysvmsq_check_msqrcv = biba_sysvmsq_check_msqrcv, .mpo_sysvmsq_check_msqctl = biba_sysvmsq_check_msqctl, .mpo_sysvmsq_cleanup = biba_sysvmsq_cleanup, .mpo_sysvmsq_create = biba_sysvmsq_create, .mpo_sysvmsq_destroy_label = biba_destroy_label, .mpo_sysvmsq_init_label = biba_init_label, .mpo_sysvsem_check_semctl = biba_sysvsem_check_semctl, .mpo_sysvsem_check_semget = biba_sysvsem_check_semget, .mpo_sysvsem_check_semop = biba_sysvsem_check_semop, .mpo_sysvsem_cleanup = biba_sysvsem_cleanup, .mpo_sysvsem_create = biba_sysvsem_create, .mpo_sysvsem_destroy_label = biba_destroy_label, .mpo_sysvsem_init_label = biba_init_label, .mpo_sysvshm_check_shmat = biba_sysvshm_check_shmat, .mpo_sysvshm_check_shmctl = biba_sysvshm_check_shmctl, .mpo_sysvshm_check_shmget = biba_sysvshm_check_shmget, .mpo_sysvshm_cleanup = biba_sysvshm_cleanup, .mpo_sysvshm_create = biba_sysvshm_create, .mpo_sysvshm_destroy_label = biba_destroy_label, .mpo_sysvshm_init_label = biba_init_label, .mpo_vnode_associate_extattr = biba_vnode_associate_extattr, .mpo_vnode_associate_singlelabel = biba_vnode_associate_singlelabel, .mpo_vnode_check_access = biba_vnode_check_open, .mpo_vnode_check_chdir = biba_vnode_check_chdir, .mpo_vnode_check_chroot = biba_vnode_check_chroot, .mpo_vnode_check_create = biba_vnode_check_create, .mpo_vnode_check_deleteacl = biba_vnode_check_deleteacl, .mpo_vnode_check_deleteextattr = biba_vnode_check_deleteextattr, .mpo_vnode_check_exec = biba_vnode_check_exec, .mpo_vnode_check_getacl = biba_vnode_check_getacl, .mpo_vnode_check_getextattr = biba_vnode_check_getextattr, .mpo_vnode_check_link = biba_vnode_check_link, .mpo_vnode_check_listextattr = biba_vnode_check_listextattr, .mpo_vnode_check_lookup = biba_vnode_check_lookup, .mpo_vnode_check_mmap = biba_vnode_check_mmap, .mpo_vnode_check_open = biba_vnode_check_open, .mpo_vnode_check_poll = biba_vnode_check_poll, .mpo_vnode_check_read = biba_vnode_check_read, .mpo_vnode_check_readdir = biba_vnode_check_readdir, .mpo_vnode_check_readlink = biba_vnode_check_readlink, .mpo_vnode_check_relabel = biba_vnode_check_relabel, .mpo_vnode_check_rename_from = biba_vnode_check_rename_from, .mpo_vnode_check_rename_to = biba_vnode_check_rename_to, .mpo_vnode_check_revoke = biba_vnode_check_revoke, .mpo_vnode_check_setacl = biba_vnode_check_setacl, .mpo_vnode_check_setextattr = biba_vnode_check_setextattr, .mpo_vnode_check_setflags = biba_vnode_check_setflags, .mpo_vnode_check_setmode = biba_vnode_check_setmode, .mpo_vnode_check_setowner = biba_vnode_check_setowner, .mpo_vnode_check_setutimes = biba_vnode_check_setutimes, .mpo_vnode_check_stat = biba_vnode_check_stat, .mpo_vnode_check_unlink = biba_vnode_check_unlink, .mpo_vnode_check_write = biba_vnode_check_write, .mpo_vnode_create_extattr = biba_vnode_create_extattr, .mpo_vnode_copy_label = biba_copy_label, .mpo_vnode_destroy_label = biba_destroy_label, .mpo_vnode_externalize_label = biba_externalize_label, .mpo_vnode_init_label = biba_init_label, .mpo_vnode_internalize_label = biba_internalize_label, .mpo_vnode_relabel = biba_vnode_relabel, .mpo_vnode_setlabel_extattr = biba_vnode_setlabel_extattr, }; MAC_POLICY_SET(&mac_biba_ops, mac_biba, "TrustedBSD MAC/Biba", MPC_LOADTIME_FLAG_NOTLATE, &biba_slot); diff --git a/sys/security/mac_ifoff/mac_ifoff.c b/sys/security/mac_ifoff/mac_ifoff.c index a19ddd34b22b..b52a70d3c7bf 100644 --- a/sys/security/mac_ifoff/mac_ifoff.c +++ b/sys/security/mac_ifoff/mac_ifoff.c @@ -1,173 +1,173 @@ /*- * Copyright (c) 1999-2002, 2007 Robert N. M. Watson * Copyright (c) 2001-2002 Networks Associates Technology, Inc. * Copyright (c) 2006 SPARTA, Inc. * All rights reserved. * * This software was developed by Robert Watson for the TrustedBSD Project. * * This software was developed for the FreeBSD Project in part by Network * Associates Laboratories, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), * as part of the DARPA CHATS research program. * * This software was enhanced by SPARTA ISSO under SPAWAR contract * N66001-04-C-6019 ("SEFOS"). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Developed by the TrustedBSD Project. * * Limit access to interfaces until they are specifically administratively * enabled. Prevents protocol stack-driven packet leakage in unsafe * environments. */ #include #include #include #include #include #include #include #include #include #include #include SYSCTL_DECL(_security_mac); static SYSCTL_NODE(_security_mac, OID_AUTO, ifoff, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "TrustedBSD mac_ifoff policy controls"); static int ifoff_enabled = 1; SYSCTL_INT(_security_mac_ifoff, OID_AUTO, enabled, CTLFLAG_RWTUN, &ifoff_enabled, 0, "Enforce ifoff policy"); static int ifoff_lo_enabled = 1; SYSCTL_INT(_security_mac_ifoff, OID_AUTO, lo_enabled, CTLFLAG_RWTUN, &ifoff_lo_enabled, 0, "Enable loopback interfaces"); static int ifoff_other_enabled = 0; SYSCTL_INT(_security_mac_ifoff, OID_AUTO, other_enabled, CTLFLAG_RWTUN, &ifoff_other_enabled, 0, "Enable other interfaces"); static int ifoff_bpfrecv_enabled = 0; SYSCTL_INT(_security_mac_ifoff, OID_AUTO, bpfrecv_enabled, CTLFLAG_RWTUN, &ifoff_bpfrecv_enabled, 0, "Enable BPF reception even when interface " "is disabled"); static int ifnet_check_outgoing(struct ifnet *ifp) { if (!ifoff_enabled) return (0); - if (ifoff_lo_enabled && ifp->if_type == IFT_LOOP) + if (ifoff_lo_enabled && if_gettype(ifp) == IFT_LOOP) return (0); - if (ifoff_other_enabled && ifp->if_type != IFT_LOOP) + if (ifoff_other_enabled && if_gettype(ifp) != IFT_LOOP) return (0); return (EPERM); } static int ifnet_check_incoming(struct ifnet *ifp, int viabpf) { if (!ifoff_enabled) return (0); - if (ifoff_lo_enabled && ifp->if_type == IFT_LOOP) + if (ifoff_lo_enabled && if_gettype(ifp) == IFT_LOOP) return (0); - if (ifoff_other_enabled && ifp->if_type != IFT_LOOP) + if (ifoff_other_enabled && if_gettype(ifp) != IFT_LOOP) return (0); if (viabpf && ifoff_bpfrecv_enabled) return (0); return (EPERM); } /* * Object-specific entry point implementations are sorted alphabetically by * object type and then by operation. */ static int ifoff_bpfdesc_check_receive(struct bpf_d *d, struct label *dlabel, struct ifnet *ifp, struct label *ifplabel) { return (ifnet_check_incoming(ifp, 1)); } static int ifoff_ifnet_check_transmit(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { return (ifnet_check_outgoing(ifp)); } static int ifoff_inpcb_check_deliver(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { M_ASSERTPKTHDR(m); if (m->m_pkthdr.rcvif != NULL) return (ifnet_check_incoming(m->m_pkthdr.rcvif, 0)); return (0); } static int ifoff_socket_check_deliver(struct socket *so, struct label *solabel, struct mbuf *m, struct label *mlabel) { M_ASSERTPKTHDR(m); if (m->m_pkthdr.rcvif != NULL) return (ifnet_check_incoming(m->m_pkthdr.rcvif, 0)); return (0); } static struct mac_policy_ops ifoff_ops = { .mpo_bpfdesc_check_receive = ifoff_bpfdesc_check_receive, .mpo_ifnet_check_transmit = ifoff_ifnet_check_transmit, .mpo_inpcb_check_deliver = ifoff_inpcb_check_deliver, .mpo_socket_check_deliver = ifoff_socket_check_deliver, }; MAC_POLICY_SET(&ifoff_ops, mac_ifoff, "TrustedBSD MAC/ifoff", MPC_LOADTIME_FLAG_UNLOADOK, NULL); diff --git a/sys/security/mac_lomac/mac_lomac.c b/sys/security/mac_lomac/mac_lomac.c index dffd06d964a2..2384b590d1c8 100644 --- a/sys/security/mac_lomac/mac_lomac.c +++ b/sys/security/mac_lomac/mac_lomac.c @@ -1,3071 +1,3071 @@ /*- * Copyright (c) 1999-2002, 2007-2009 Robert N. M. Watson * Copyright (c) 2001-2005 Networks Associates Technology, Inc. * Copyright (c) 2006 SPARTA, Inc. * All rights reserved. * * This software was developed by Robert Watson for the TrustedBSD Project. * * This software was developed for the FreeBSD Project in part by NAI Labs, * the Security Research Division of Network Associates, Inc. under * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA * CHATS research program. * * This software was enhanced by SPARTA ISSO under SPAWAR contract * N66001-04-C-6019 ("SEFOS"). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Developed by the TrustedBSD Project. * * Low-watermark floating label mandatory integrity policy. */ #include #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 #include #include #include #include struct mac_lomac_proc { struct mac_lomac mac_lomac; struct mtx mtx; }; SYSCTL_DECL(_security_mac); static SYSCTL_NODE(_security_mac, OID_AUTO, lomac, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "TrustedBSD mac_lomac policy controls"); static int lomac_label_size = sizeof(struct mac_lomac); SYSCTL_INT(_security_mac_lomac, OID_AUTO, label_size, CTLFLAG_RD, &lomac_label_size, 0, "Size of struct mac_lomac"); static int lomac_enabled = 1; SYSCTL_INT(_security_mac_lomac, OID_AUTO, enabled, CTLFLAG_RWTUN, &lomac_enabled, 0, "Enforce MAC/LOMAC policy"); static int destroyed_not_inited; SYSCTL_INT(_security_mac_lomac, OID_AUTO, destroyed_not_inited, CTLFLAG_RD, &destroyed_not_inited, 0, "Count of labels destroyed but not inited"); static int trust_all_interfaces = 0; SYSCTL_INT(_security_mac_lomac, OID_AUTO, trust_all_interfaces, CTLFLAG_RDTUN, &trust_all_interfaces, 0, "Consider all interfaces 'trusted' by MAC/LOMAC"); static char trusted_interfaces[128]; SYSCTL_STRING(_security_mac_lomac, OID_AUTO, trusted_interfaces, CTLFLAG_RDTUN, trusted_interfaces, 0, "Interfaces considered 'trusted' by MAC/LOMAC"); static int ptys_equal = 0; SYSCTL_INT(_security_mac_lomac, OID_AUTO, ptys_equal, CTLFLAG_RWTUN, &ptys_equal, 0, "Label pty devices as lomac/equal on create"); static int revocation_enabled = 1; SYSCTL_INT(_security_mac_lomac, OID_AUTO, revocation_enabled, CTLFLAG_RWTUN, &revocation_enabled, 0, "Revoke access to objects on relabel"); static int lomac_slot; #define SLOT(l) ((struct mac_lomac *)mac_label_get((l), lomac_slot)) #define SLOT_SET(l, val) mac_label_set((l), lomac_slot, (uintptr_t)(val)) #define PSLOT(l) ((struct mac_lomac_proc *) \ mac_label_get((l), lomac_slot)) #define PSLOT_SET(l, val) mac_label_set((l), lomac_slot, (uintptr_t)(val)) static MALLOC_DEFINE(M_LOMAC, "mac_lomac_label", "MAC/LOMAC labels"); static struct mac_lomac * lomac_alloc(int flag) { struct mac_lomac *ml; ml = malloc(sizeof(*ml), M_LOMAC, M_ZERO | flag); return (ml); } static void lomac_free(struct mac_lomac *ml) { if (ml != NULL) free(ml, M_LOMAC); else atomic_add_int(&destroyed_not_inited, 1); } static int lomac_atmostflags(struct mac_lomac *ml, int flags) { if ((ml->ml_flags & flags) != ml->ml_flags) return (EINVAL); return (0); } static int lomac_dominate_element(struct mac_lomac_element *a, struct mac_lomac_element *b) { switch (a->mle_type) { case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_HIGH: return (1); case MAC_LOMAC_TYPE_LOW: switch (b->mle_type) { case MAC_LOMAC_TYPE_GRADE: case MAC_LOMAC_TYPE_HIGH: return (0); case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_LOW: return (1); default: panic("lomac_dominate_element: b->mle_type invalid"); } case MAC_LOMAC_TYPE_GRADE: switch (b->mle_type) { case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_LOW: return (1); case MAC_LOMAC_TYPE_HIGH: return (0); case MAC_LOMAC_TYPE_GRADE: return (a->mle_grade >= b->mle_grade); default: panic("lomac_dominate_element: b->mle_type invalid"); } default: panic("lomac_dominate_element: a->mle_type invalid"); } } static int lomac_range_in_range(struct mac_lomac *rangea, struct mac_lomac *rangeb) { return (lomac_dominate_element(&rangeb->ml_rangehigh, &rangea->ml_rangehigh) && lomac_dominate_element(&rangea->ml_rangelow, &rangeb->ml_rangelow)); } static int lomac_single_in_range(struct mac_lomac *single, struct mac_lomac *range) { KASSERT((single->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("lomac_single_in_range: a not single")); KASSERT((range->ml_flags & MAC_LOMAC_FLAG_RANGE) != 0, ("lomac_single_in_range: b not range")); return (lomac_dominate_element(&range->ml_rangehigh, &single->ml_single) && lomac_dominate_element(&single->ml_single, &range->ml_rangelow)); } static int lomac_auxsingle_in_range(struct mac_lomac *single, struct mac_lomac *range) { KASSERT((single->ml_flags & MAC_LOMAC_FLAG_AUX) != 0, ("lomac_single_in_range: a not auxsingle")); KASSERT((range->ml_flags & MAC_LOMAC_FLAG_RANGE) != 0, ("lomac_single_in_range: b not range")); return (lomac_dominate_element(&range->ml_rangehigh, &single->ml_auxsingle) && lomac_dominate_element(&single->ml_auxsingle, &range->ml_rangelow)); } static int lomac_dominate_single(struct mac_lomac *a, struct mac_lomac *b) { KASSERT((a->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("lomac_dominate_single: a not single")); KASSERT((b->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("lomac_dominate_single: b not single")); return (lomac_dominate_element(&a->ml_single, &b->ml_single)); } static int lomac_subject_dominate(struct mac_lomac *a, struct mac_lomac *b) { KASSERT((~a->ml_flags & (MAC_LOMAC_FLAG_SINGLE | MAC_LOMAC_FLAG_RANGE)) == 0, ("lomac_dominate_single: a not subject")); KASSERT((b->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("lomac_dominate_single: b not single")); return (lomac_dominate_element(&a->ml_rangehigh, &b->ml_single)); } static int lomac_equal_element(struct mac_lomac_element *a, struct mac_lomac_element *b) { if (a->mle_type == MAC_LOMAC_TYPE_EQUAL || b->mle_type == MAC_LOMAC_TYPE_EQUAL) return (1); return (a->mle_type == b->mle_type && a->mle_grade == b->mle_grade); } static int lomac_equal_single(struct mac_lomac *a, struct mac_lomac *b) { KASSERT((a->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("lomac_equal_single: a not single")); KASSERT((b->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("lomac_equal_single: b not single")); return (lomac_equal_element(&a->ml_single, &b->ml_single)); } static int lomac_contains_equal(struct mac_lomac *ml) { if (ml->ml_flags & MAC_LOMAC_FLAG_SINGLE) if (ml->ml_single.mle_type == MAC_LOMAC_TYPE_EQUAL) return (1); if (ml->ml_flags & MAC_LOMAC_FLAG_AUX) if (ml->ml_auxsingle.mle_type == MAC_LOMAC_TYPE_EQUAL) return (1); if (ml->ml_flags & MAC_LOMAC_FLAG_RANGE) { if (ml->ml_rangelow.mle_type == MAC_LOMAC_TYPE_EQUAL) return (1); if (ml->ml_rangehigh.mle_type == MAC_LOMAC_TYPE_EQUAL) return (1); } return (0); } static int lomac_subject_privileged(struct mac_lomac *ml) { KASSERT((ml->ml_flags & MAC_LOMAC_FLAGS_BOTH) == MAC_LOMAC_FLAGS_BOTH, ("lomac_subject_privileged: subject doesn't have both labels")); /* If the single is EQUAL, it's ok. */ if (ml->ml_single.mle_type == MAC_LOMAC_TYPE_EQUAL) return (0); /* If either range endpoint is EQUAL, it's ok. */ if (ml->ml_rangelow.mle_type == MAC_LOMAC_TYPE_EQUAL || ml->ml_rangehigh.mle_type == MAC_LOMAC_TYPE_EQUAL) return (0); /* If the range is low-high, it's ok. */ if (ml->ml_rangelow.mle_type == MAC_LOMAC_TYPE_LOW && ml->ml_rangehigh.mle_type == MAC_LOMAC_TYPE_HIGH) return (0); /* It's not ok. */ return (EPERM); } static int lomac_high_single(struct mac_lomac *ml) { KASSERT((ml->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("lomac_high_single: mac_lomac not single")); return (ml->ml_single.mle_type == MAC_LOMAC_TYPE_HIGH); } static int lomac_valid(struct mac_lomac *ml) { if (ml->ml_flags & MAC_LOMAC_FLAG_SINGLE) { switch (ml->ml_single.mle_type) { case MAC_LOMAC_TYPE_GRADE: case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_HIGH: case MAC_LOMAC_TYPE_LOW: break; default: return (EINVAL); } } else { if (ml->ml_single.mle_type != MAC_LOMAC_TYPE_UNDEF) return (EINVAL); } if (ml->ml_flags & MAC_LOMAC_FLAG_AUX) { switch (ml->ml_auxsingle.mle_type) { case MAC_LOMAC_TYPE_GRADE: case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_HIGH: case MAC_LOMAC_TYPE_LOW: break; default: return (EINVAL); } } else { if (ml->ml_auxsingle.mle_type != MAC_LOMAC_TYPE_UNDEF) return (EINVAL); } if (ml->ml_flags & MAC_LOMAC_FLAG_RANGE) { switch (ml->ml_rangelow.mle_type) { case MAC_LOMAC_TYPE_GRADE: case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_HIGH: case MAC_LOMAC_TYPE_LOW: break; default: return (EINVAL); } switch (ml->ml_rangehigh.mle_type) { case MAC_LOMAC_TYPE_GRADE: case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_HIGH: case MAC_LOMAC_TYPE_LOW: break; default: return (EINVAL); } if (!lomac_dominate_element(&ml->ml_rangehigh, &ml->ml_rangelow)) return (EINVAL); } else { if (ml->ml_rangelow.mle_type != MAC_LOMAC_TYPE_UNDEF || ml->ml_rangehigh.mle_type != MAC_LOMAC_TYPE_UNDEF) return (EINVAL); } return (0); } static void lomac_set_range(struct mac_lomac *ml, u_short typelow, u_short gradelow, u_short typehigh, u_short gradehigh) { ml->ml_rangelow.mle_type = typelow; ml->ml_rangelow.mle_grade = gradelow; ml->ml_rangehigh.mle_type = typehigh; ml->ml_rangehigh.mle_grade = gradehigh; ml->ml_flags |= MAC_LOMAC_FLAG_RANGE; } static void lomac_set_single(struct mac_lomac *ml, u_short type, u_short grade) { ml->ml_single.mle_type = type; ml->ml_single.mle_grade = grade; ml->ml_flags |= MAC_LOMAC_FLAG_SINGLE; } static void lomac_copy_range(struct mac_lomac *labelfrom, struct mac_lomac *labelto) { KASSERT((labelfrom->ml_flags & MAC_LOMAC_FLAG_RANGE) != 0, ("lomac_copy_range: labelfrom not range")); labelto->ml_rangelow = labelfrom->ml_rangelow; labelto->ml_rangehigh = labelfrom->ml_rangehigh; labelto->ml_flags |= MAC_LOMAC_FLAG_RANGE; } static void lomac_copy_single(struct mac_lomac *labelfrom, struct mac_lomac *labelto) { KASSERT((labelfrom->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("lomac_copy_single: labelfrom not single")); labelto->ml_single = labelfrom->ml_single; labelto->ml_flags |= MAC_LOMAC_FLAG_SINGLE; } static void lomac_copy_auxsingle(struct mac_lomac *labelfrom, struct mac_lomac *labelto) { KASSERT((labelfrom->ml_flags & MAC_LOMAC_FLAG_AUX) != 0, ("lomac_copy_auxsingle: labelfrom not auxsingle")); labelto->ml_auxsingle = labelfrom->ml_auxsingle; labelto->ml_flags |= MAC_LOMAC_FLAG_AUX; } static void lomac_copy(struct mac_lomac *source, struct mac_lomac *dest) { if (source->ml_flags & MAC_LOMAC_FLAG_SINGLE) lomac_copy_single(source, dest); if (source->ml_flags & MAC_LOMAC_FLAG_AUX) lomac_copy_auxsingle(source, dest); if (source->ml_flags & MAC_LOMAC_FLAG_RANGE) lomac_copy_range(source, dest); } static int lomac_to_string(struct sbuf *sb, struct mac_lomac *ml); static int maybe_demote(struct mac_lomac *subjlabel, struct mac_lomac *objlabel, const char *actionname, const char *objname, struct vnode *vp) { struct sbuf subjlabel_sb, subjtext_sb, objlabel_sb; char *subjlabeltext, *objlabeltext, *subjtext; struct mac_lomac cached_subjlabel; struct mac_lomac_proc *subj; struct vattr va; struct proc *p; pid_t pgid; subj = PSLOT(curthread->td_proc->p_label); p = curthread->td_proc; mtx_lock(&subj->mtx); if (subj->mac_lomac.ml_flags & MAC_LOMAC_FLAG_UPDATE) { /* * Check to see if the pending demotion would be more or less * severe than this one, and keep the more severe. This can * only happen for a multi-threaded application. */ if (lomac_dominate_single(objlabel, &subj->mac_lomac)) { mtx_unlock(&subj->mtx); return (0); } } bzero(&subj->mac_lomac, sizeof(subj->mac_lomac)); /* * Always demote the single label. */ lomac_copy_single(objlabel, &subj->mac_lomac); /* * Start with the original range, then minimize each side of the * range to the point of not dominating the object. The high side * will always be demoted, of course. */ lomac_copy_range(subjlabel, &subj->mac_lomac); if (!lomac_dominate_element(&objlabel->ml_single, &subj->mac_lomac.ml_rangelow)) subj->mac_lomac.ml_rangelow = objlabel->ml_single; subj->mac_lomac.ml_rangehigh = objlabel->ml_single; subj->mac_lomac.ml_flags |= MAC_LOMAC_FLAG_UPDATE; ast_sched(curthread, TDA_MAC); /* * Avoid memory allocation while holding a mutex; cache the label. */ lomac_copy_single(&subj->mac_lomac, &cached_subjlabel); mtx_unlock(&subj->mtx); sbuf_new(&subjlabel_sb, NULL, 0, SBUF_AUTOEXTEND); lomac_to_string(&subjlabel_sb, subjlabel); sbuf_finish(&subjlabel_sb); subjlabeltext = sbuf_data(&subjlabel_sb); sbuf_new(&subjtext_sb, NULL, 0, SBUF_AUTOEXTEND); lomac_to_string(&subjtext_sb, &subj->mac_lomac); sbuf_finish(&subjtext_sb); subjtext = sbuf_data(&subjtext_sb); sbuf_new(&objlabel_sb, NULL, 0, SBUF_AUTOEXTEND); lomac_to_string(&objlabel_sb, objlabel); sbuf_finish(&objlabel_sb); objlabeltext = sbuf_data(&objlabel_sb); pgid = p->p_pgrp->pg_id; /* XXX could be stale? */ if (vp != NULL && VOP_GETATTR(vp, &va, curthread->td_ucred) == 0) { log(LOG_INFO, "LOMAC: level-%s subject p%dg%du%d:%s demoted to" " level %s after %s a level-%s %s (inode=%ju, " "mountpount=%s)\n", subjlabeltext, p->p_pid, pgid, curthread->td_ucred->cr_uid, p->p_comm, subjtext, actionname, objlabeltext, objname, (uintmax_t)va.va_fileid, vp->v_mount->mnt_stat.f_mntonname); } else { log(LOG_INFO, "LOMAC: level-%s subject p%dg%du%d:%s demoted to" " level %s after %s a level-%s %s\n", subjlabeltext, p->p_pid, pgid, curthread->td_ucred->cr_uid, p->p_comm, subjtext, actionname, objlabeltext, objname); } sbuf_delete(&subjlabel_sb); sbuf_delete(&subjtext_sb); sbuf_delete(&objlabel_sb); return (0); } /* * Relabel "to" to "from" only if "from" is a valid label (contains at least * a single), as for a relabel operation which may or may not involve a * relevant label. */ static void try_relabel(struct mac_lomac *from, struct mac_lomac *to) { if (from->ml_flags & MAC_LOMAC_FLAG_SINGLE) { bzero(to, sizeof(*to)); lomac_copy(from, to); } } static void ast_mac(struct thread *td, int tda __unused) { mac_thread_userret(td); } /* * Policy module operations. */ static void lomac_init(struct mac_policy_conf *conf __unused) { ast_register(TDA_MAC, ASTR_ASTF_REQUIRED, 0, ast_mac); } static void lomac_fini(struct mac_policy_conf *conf __unused) { ast_deregister(TDA_MAC); } /* * Label operations. */ static void lomac_init_label(struct label *label) { SLOT_SET(label, lomac_alloc(M_WAITOK)); } static int lomac_init_label_waitcheck(struct label *label, int flag) { SLOT_SET(label, lomac_alloc(flag)); if (SLOT(label) == NULL) return (ENOMEM); return (0); } static void lomac_destroy_label(struct label *label) { lomac_free(SLOT(label)); SLOT_SET(label, NULL); } static int lomac_element_to_string(struct sbuf *sb, struct mac_lomac_element *element) { switch (element->mle_type) { case MAC_LOMAC_TYPE_HIGH: return (sbuf_printf(sb, "high")); case MAC_LOMAC_TYPE_LOW: return (sbuf_printf(sb, "low")); case MAC_LOMAC_TYPE_EQUAL: return (sbuf_printf(sb, "equal")); case MAC_LOMAC_TYPE_GRADE: return (sbuf_printf(sb, "%d", element->mle_grade)); default: panic("lomac_element_to_string: invalid type (%d)", element->mle_type); } } static int lomac_to_string(struct sbuf *sb, struct mac_lomac *ml) { if (ml->ml_flags & MAC_LOMAC_FLAG_SINGLE) { if (lomac_element_to_string(sb, &ml->ml_single) == -1) return (EINVAL); } if (ml->ml_flags & MAC_LOMAC_FLAG_AUX) { if (sbuf_putc(sb, '[') == -1) return (EINVAL); if (lomac_element_to_string(sb, &ml->ml_auxsingle) == -1) return (EINVAL); if (sbuf_putc(sb, ']') == -1) return (EINVAL); } if (ml->ml_flags & MAC_LOMAC_FLAG_RANGE) { if (sbuf_putc(sb, '(') == -1) return (EINVAL); if (lomac_element_to_string(sb, &ml->ml_rangelow) == -1) return (EINVAL); if (sbuf_putc(sb, '-') == -1) return (EINVAL); if (lomac_element_to_string(sb, &ml->ml_rangehigh) == -1) return (EINVAL); if (sbuf_putc(sb, ')') == -1) return (EINVAL); } return (0); } static int lomac_externalize_label(struct label *label, char *element_name, struct sbuf *sb, int *claimed) { struct mac_lomac *ml; if (strcmp(MAC_LOMAC_LABEL_NAME, element_name) != 0) return (0); (*claimed)++; ml = SLOT(label); return (lomac_to_string(sb, ml)); } static int lomac_parse_element(struct mac_lomac_element *element, char *string) { if (strcmp(string, "high") == 0 || strcmp(string, "hi") == 0) { element->mle_type = MAC_LOMAC_TYPE_HIGH; element->mle_grade = MAC_LOMAC_TYPE_UNDEF; } else if (strcmp(string, "low") == 0 || strcmp(string, "lo") == 0) { element->mle_type = MAC_LOMAC_TYPE_LOW; element->mle_grade = MAC_LOMAC_TYPE_UNDEF; } else if (strcmp(string, "equal") == 0 || strcmp(string, "eq") == 0) { element->mle_type = MAC_LOMAC_TYPE_EQUAL; element->mle_grade = MAC_LOMAC_TYPE_UNDEF; } else { char *p0, *p1; int d; p0 = string; d = strtol(p0, &p1, 10); if (d < 0 || d > 65535) return (EINVAL); element->mle_type = MAC_LOMAC_TYPE_GRADE; element->mle_grade = d; if (p1 == p0 || *p1 != '\0') return (EINVAL); } return (0); } /* * Note: destructively consumes the string, make a local copy before calling * if that's a problem. */ static int lomac_parse(struct mac_lomac *ml, char *string) { char *range, *rangeend, *rangehigh, *rangelow, *single, *auxsingle, *auxsingleend; int error; /* Do we have a range? */ single = string; range = strchr(string, '('); if (range == single) single = NULL; auxsingle = strchr(string, '['); if (auxsingle == single) single = NULL; if (range != NULL && auxsingle != NULL) return (EINVAL); rangelow = rangehigh = NULL; if (range != NULL) { /* Nul terminate the end of the single string. */ *range = '\0'; range++; rangelow = range; rangehigh = strchr(rangelow, '-'); if (rangehigh == NULL) return (EINVAL); rangehigh++; if (*rangelow == '\0' || *rangehigh == '\0') return (EINVAL); rangeend = strchr(rangehigh, ')'); if (rangeend == NULL) return (EINVAL); if (*(rangeend + 1) != '\0') return (EINVAL); /* Nul terminate the ends of the ranges. */ *(rangehigh - 1) = '\0'; *rangeend = '\0'; } KASSERT((rangelow != NULL && rangehigh != NULL) || (rangelow == NULL && rangehigh == NULL), ("lomac_internalize_label: range mismatch")); if (auxsingle != NULL) { /* Nul terminate the end of the single string. */ *auxsingle = '\0'; auxsingle++; auxsingleend = strchr(auxsingle, ']'); if (auxsingleend == NULL) return (EINVAL); if (*(auxsingleend + 1) != '\0') return (EINVAL); /* Nul terminate the end of the auxsingle. */ *auxsingleend = '\0'; } bzero(ml, sizeof(*ml)); if (single != NULL) { error = lomac_parse_element(&ml->ml_single, single); if (error) return (error); ml->ml_flags |= MAC_LOMAC_FLAG_SINGLE; } if (auxsingle != NULL) { error = lomac_parse_element(&ml->ml_auxsingle, auxsingle); if (error) return (error); ml->ml_flags |= MAC_LOMAC_FLAG_AUX; } if (rangelow != NULL) { error = lomac_parse_element(&ml->ml_rangelow, rangelow); if (error) return (error); error = lomac_parse_element(&ml->ml_rangehigh, rangehigh); if (error) return (error); ml->ml_flags |= MAC_LOMAC_FLAG_RANGE; } error = lomac_valid(ml); if (error) return (error); return (0); } static int lomac_internalize_label(struct label *label, char *element_name, char *element_data, int *claimed) { struct mac_lomac *ml, ml_temp; int error; if (strcmp(MAC_LOMAC_LABEL_NAME, element_name) != 0) return (0); (*claimed)++; error = lomac_parse(&ml_temp, element_data); if (error) return (error); ml = SLOT(label); *ml = ml_temp; return (0); } static void lomac_copy_label(struct label *src, struct label *dest) { *SLOT(dest) = *SLOT(src); } /* * Object-specific entry point implementations are sorted alphabetically by * object type name and then by operation. */ static int lomac_bpfdesc_check_receive(struct bpf_d *d, struct label *dlabel, struct ifnet *ifp, struct label *ifplabel) { struct mac_lomac *a, *b; if (!lomac_enabled) return (0); a = SLOT(dlabel); b = SLOT(ifplabel); if (lomac_equal_single(a, b)) return (0); return (EACCES); } static void lomac_bpfdesc_create(struct ucred *cred, struct bpf_d *d, struct label *dlabel) { struct mac_lomac *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(dlabel); lomac_copy_single(source, dest); } static void lomac_bpfdesc_create_mbuf(struct bpf_d *d, struct label *dlabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *source, *dest; source = SLOT(dlabel); dest = SLOT(mlabel); lomac_copy_single(source, dest); } static int lomac_cred_check_relabel(struct ucred *cred, struct label *newlabel) { struct mac_lomac *subj, *new; int error; subj = SLOT(cred->cr_label); new = SLOT(newlabel); /* * If there is a LOMAC label update for the credential, it may be an * update of the single, range, or both. */ error = lomac_atmostflags(new, MAC_LOMAC_FLAGS_BOTH); if (error) return (error); /* * If the LOMAC label is to be changed, authorize as appropriate. */ if (new->ml_flags & MAC_LOMAC_FLAGS_BOTH) { /* * Fill in the missing parts from the previous label. */ if ((new->ml_flags & MAC_LOMAC_FLAG_SINGLE) == 0) lomac_copy_single(subj, new); if ((new->ml_flags & MAC_LOMAC_FLAG_RANGE) == 0) lomac_copy_range(subj, new); /* * To change the LOMAC range on a credential, the new range * label must be in the current range. */ if (!lomac_range_in_range(new, subj)) return (EPERM); /* * To change the LOMAC single label on a credential, the new * single label must be in the new range. Implicitly from * the previous check, the new single is in the old range. */ if (!lomac_single_in_range(new, new)) return (EPERM); /* * To have EQUAL in any component of the new credential LOMAC * label, the subject must already have EQUAL in their label. */ if (lomac_contains_equal(new)) { error = lomac_subject_privileged(subj); if (error) return (error); } /* * XXXMAC: Additional consistency tests regarding the single * and range of the new label might be performed here. */ } return (0); } static int lomac_cred_check_visible(struct ucred *cr1, struct ucred *cr2) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cr1->cr_label); obj = SLOT(cr2->cr_label); /* XXX: range */ if (!lomac_dominate_single(obj, subj)) return (ESRCH); return (0); } static void lomac_cred_create_init(struct ucred *cred) { struct mac_lomac *dest; dest = SLOT(cred->cr_label); lomac_set_single(dest, MAC_LOMAC_TYPE_HIGH, 0); lomac_set_range(dest, MAC_LOMAC_TYPE_LOW, 0, MAC_LOMAC_TYPE_HIGH, 0); } static void lomac_cred_create_swapper(struct ucred *cred) { struct mac_lomac *dest; dest = SLOT(cred->cr_label); lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); lomac_set_range(dest, MAC_LOMAC_TYPE_LOW, 0, MAC_LOMAC_TYPE_HIGH, 0); } static void lomac_cred_relabel(struct ucred *cred, struct label *newlabel) { struct mac_lomac *source, *dest; source = SLOT(newlabel); dest = SLOT(cred->cr_label); try_relabel(source, dest); } static void lomac_devfs_create_device(struct ucred *cred, struct mount *mp, struct cdev *dev, struct devfs_dirent *de, struct label *delabel) { struct mac_lomac *ml; const char *dn; int lomac_type; ml = SLOT(delabel); dn = devtoname(dev); if (strcmp(dn, "null") == 0 || strcmp(dn, "zero") == 0 || strcmp(dn, "random") == 0 || strncmp(dn, "fd/", strlen("fd/")) == 0 || strncmp(dn, "ttyv", strlen("ttyv")) == 0) lomac_type = MAC_LOMAC_TYPE_EQUAL; else if (ptys_equal && (strncmp(dn, "ttyp", strlen("ttyp")) == 0 || strncmp(dn, "pts/", strlen("pts/")) == 0 || strncmp(dn, "ptyp", strlen("ptyp")) == 0)) lomac_type = MAC_LOMAC_TYPE_EQUAL; else lomac_type = MAC_LOMAC_TYPE_HIGH; lomac_set_single(ml, lomac_type, 0); } static void lomac_devfs_create_directory(struct mount *mp, char *dirname, int dirnamelen, struct devfs_dirent *de, struct label *delabel) { struct mac_lomac *ml; ml = SLOT(delabel); lomac_set_single(ml, MAC_LOMAC_TYPE_HIGH, 0); } static void lomac_devfs_create_symlink(struct ucred *cred, struct mount *mp, struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de, struct label *delabel) { struct mac_lomac *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(delabel); lomac_copy_single(source, dest); } static void lomac_devfs_update(struct mount *mp, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vplabel) { struct mac_lomac *source, *dest; source = SLOT(vplabel); dest = SLOT(delabel); lomac_copy(source, dest); } static void lomac_devfs_vnode_associate(struct mount *mp, struct label *mplabel, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vplabel) { struct mac_lomac *source, *dest; source = SLOT(delabel); dest = SLOT(vplabel); lomac_copy_single(source, dest); } static int lomac_ifnet_check_relabel(struct ucred *cred, struct ifnet *ifp, struct label *ifplabel, struct label *newlabel) { struct mac_lomac *subj, *new; int error; subj = SLOT(cred->cr_label); new = SLOT(newlabel); /* * If there is a LOMAC label update for the interface, it may be an * update of the single, range, or both. */ error = lomac_atmostflags(new, MAC_LOMAC_FLAGS_BOTH); if (error) return (error); /* * Relabling network interfaces requires LOMAC privilege. */ error = lomac_subject_privileged(subj); if (error) return (error); /* * If the LOMAC label is to be changed, authorize as appropriate. */ if (new->ml_flags & MAC_LOMAC_FLAGS_BOTH) { /* * Fill in the missing parts from the previous label. */ if ((new->ml_flags & MAC_LOMAC_FLAG_SINGLE) == 0) lomac_copy_single(subj, new); if ((new->ml_flags & MAC_LOMAC_FLAG_RANGE) == 0) lomac_copy_range(subj, new); /* * Rely on the traditional superuser status for the LOMAC * interface relabel requirements. XXXMAC: This will go * away. * * XXXRW: This is also redundant to a higher layer check. */ error = priv_check_cred(cred, PRIV_NET_SETIFMAC); if (error) return (EPERM); /* * XXXMAC: Additional consistency tests regarding the single * and the range of the new label might be performed here. */ } return (0); } static int lomac_ifnet_check_transmit(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *p, *i; if (!lomac_enabled) return (0); p = SLOT(mlabel); i = SLOT(ifplabel); return (lomac_single_in_range(p, i) ? 0 : EACCES); } static void lomac_ifnet_create(struct ifnet *ifp, struct label *ifplabel) { char tifname[IFNAMSIZ], *p, *q; char tiflist[sizeof(trusted_interfaces)]; struct mac_lomac *dest; int len, grade; dest = SLOT(ifplabel); - if (ifp->if_type == IFT_LOOP) { + if (if_gettype(ifp) == IFT_LOOP) { grade = MAC_LOMAC_TYPE_EQUAL; goto set; } if (trust_all_interfaces) { grade = MAC_LOMAC_TYPE_HIGH; goto set; } grade = MAC_LOMAC_TYPE_LOW; if (trusted_interfaces[0] == '\0' || !strvalid(trusted_interfaces, sizeof(trusted_interfaces))) goto set; bzero(tiflist, sizeof(tiflist)); for (p = trusted_interfaces, q = tiflist; *p != '\0'; p++, q++) if(*p != ' ' && *p != '\t') *q = *p; for (p = q = tiflist;; p++) { if (*p == ',' || *p == '\0') { len = p - q; if (len < IFNAMSIZ) { bzero(tifname, sizeof(tifname)); bcopy(q, tifname, len); - if (strcmp(tifname, ifp->if_xname) == 0) { + if (strcmp(tifname, if_name(ifp)) == 0) { grade = MAC_LOMAC_TYPE_HIGH; break; } } else { *p = '\0'; printf("MAC/LOMAC warning: interface name " "\"%s\" is too long (must be < %d)\n", q, IFNAMSIZ); } if (*p == '\0') break; q = p + 1; } } set: lomac_set_single(dest, grade, 0); lomac_set_range(dest, grade, 0, grade, 0); } static void lomac_ifnet_create_mbuf(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *source, *dest; source = SLOT(ifplabel); dest = SLOT(mlabel); lomac_copy_single(source, dest); } static void lomac_ifnet_relabel(struct ucred *cred, struct ifnet *ifp, struct label *ifplabel, struct label *newlabel) { struct mac_lomac *source, *dest; source = SLOT(newlabel); dest = SLOT(ifplabel); try_relabel(source, dest); } static int lomac_inpcb_check_deliver(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *p, *i; if (!lomac_enabled) return (0); p = SLOT(mlabel); i = SLOT(inplabel); return (lomac_equal_single(p, i) ? 0 : EACCES); } static int lomac_inpcb_check_visible(struct ucred *cred, struct inpcb *inp, struct label *inplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(inplabel); if (!lomac_dominate_single(obj, subj)) return (ENOENT); return (0); } static void lomac_inpcb_create(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { struct mac_lomac *source, *dest; source = SLOT(solabel); dest = SLOT(inplabel); lomac_copy_single(source, dest); } static void lomac_inpcb_create_mbuf(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *source, *dest; source = SLOT(inplabel); dest = SLOT(mlabel); lomac_copy_single(source, dest); } static void lomac_inpcb_sosetlabel(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { struct mac_lomac *source, *dest; SOCK_LOCK_ASSERT(so); source = SLOT(solabel); dest = SLOT(inplabel); lomac_copy_single(source, dest); } static void lomac_ip6q_create(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { struct mac_lomac *source, *dest; source = SLOT(mlabel); dest = SLOT(q6label); lomac_copy_single(source, dest); } static int lomac_ip6q_match(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { struct mac_lomac *a, *b; a = SLOT(q6label); b = SLOT(mlabel); return (lomac_equal_single(a, b)); } static void lomac_ip6q_reassemble(struct ip6q *q6, struct label *q6label, struct mbuf *m, struct label *mlabel) { struct mac_lomac *source, *dest; source = SLOT(q6label); dest = SLOT(mlabel); /* Just use the head, since we require them all to match. */ lomac_copy_single(source, dest); } static void lomac_ip6q_update(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { /* NOOP: we only accept matching labels, so no need to update */ } static void lomac_ipq_create(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { struct mac_lomac *source, *dest; source = SLOT(mlabel); dest = SLOT(qlabel); lomac_copy_single(source, dest); } static int lomac_ipq_match(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { struct mac_lomac *a, *b; a = SLOT(qlabel); b = SLOT(mlabel); return (lomac_equal_single(a, b)); } static void lomac_ipq_reassemble(struct ipq *q, struct label *qlabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *source, *dest; source = SLOT(qlabel); dest = SLOT(mlabel); /* Just use the head, since we require them all to match. */ lomac_copy_single(source, dest); } static void lomac_ipq_update(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { /* NOOP: we only accept matching labels, so no need to update */ } static int lomac_kld_check_load(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (lomac_subject_privileged(subj)) return (EPERM); if (!lomac_high_single(obj)) return (EACCES); return (0); } static void lomac_mount_create(struct ucred *cred, struct mount *mp, struct label *mplabel) { struct mac_lomac *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(mplabel); lomac_copy_single(source, dest); } static void lomac_netinet_arp_send(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *dest; dest = SLOT(mlabel); lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); } static void lomac_netinet_firewall_reply(struct mbuf *mrecv, struct label *mrecvlabel, struct mbuf *msend, struct label *msendlabel) { struct mac_lomac *source, *dest; source = SLOT(mrecvlabel); dest = SLOT(msendlabel); lomac_copy_single(source, dest); } static void lomac_netinet_firewall_send(struct mbuf *m, struct label *mlabel) { struct mac_lomac *dest; dest = SLOT(mlabel); /* XXX: where is the label for the firewall really coming from? */ lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); } static void lomac_netinet_fragment(struct mbuf *m, struct label *mlabel, struct mbuf *frag, struct label *fraglabel) { struct mac_lomac *source, *dest; source = SLOT(mlabel); dest = SLOT(fraglabel); lomac_copy_single(source, dest); } static void lomac_netinet_icmp_reply(struct mbuf *mrecv, struct label *mrecvlabel, struct mbuf *msend, struct label *msendlabel) { struct mac_lomac *source, *dest; source = SLOT(mrecvlabel); dest = SLOT(msendlabel); lomac_copy_single(source, dest); } static void lomac_netinet_igmp_send(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *dest; dest = SLOT(mlabel); lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); } static void lomac_netinet6_nd6_send(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *dest; dest = SLOT(mlabel); lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); } static int lomac_pipe_check_ioctl(struct ucred *cred, struct pipepair *pp, struct label *pplabel, unsigned long cmd, void /* caddr_t */ *data) { if (!lomac_enabled) return (0); /* XXX: This will be implemented soon... */ return (0); } static int lomac_pipe_check_read(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!lomac_dominate_single(obj, subj)) return (maybe_demote(subj, obj, "reading", "pipe", NULL)); return (0); } static int lomac_pipe_check_relabel(struct ucred *cred, struct pipepair *pp, struct label *pplabel, struct label *newlabel) { struct mac_lomac *subj, *obj, *new; int error; new = SLOT(newlabel); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); /* * If there is a LOMAC label update for a pipe, it must be a single * update. */ error = lomac_atmostflags(new, MAC_LOMAC_FLAG_SINGLE); if (error) return (error); /* * To perform a relabel of a pipe (LOMAC label or not), LOMAC must * authorize the relabel. */ if (!lomac_single_in_range(obj, subj)) return (EPERM); /* * If the LOMAC label is to be changed, authorize as appropriate. */ if (new->ml_flags & MAC_LOMAC_FLAG_SINGLE) { /* * To change the LOMAC label on a pipe, the new pipe label * must be in the subject range. */ if (!lomac_single_in_range(new, subj)) return (EPERM); /* * To change the LOMAC label on a pipe to be EQUAL, the * subject must have appropriate privilege. */ if (lomac_contains_equal(new)) { error = lomac_subject_privileged(subj); if (error) return (error); } } return (0); } static int lomac_pipe_check_write(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static void lomac_pipe_create(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_lomac *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(pplabel); lomac_copy_single(source, dest); } static void lomac_pipe_relabel(struct ucred *cred, struct pipepair *pp, struct label *pplabel, struct label *newlabel) { struct mac_lomac *source, *dest; source = SLOT(newlabel); dest = SLOT(pplabel); try_relabel(source, dest); } /* * Some system privileges are allowed regardless of integrity grade; others * are allowed only when running with privilege with respect to the LOMAC * policy as they might otherwise allow bypassing of the integrity policy. */ static int lomac_priv_check(struct ucred *cred, int priv) { struct mac_lomac *subj; int error; if (!lomac_enabled) return (0); /* * Exempt only specific privileges from the LOMAC integrity policy. */ switch (priv) { case PRIV_KTRACE: case PRIV_MSGBUF: /* * Allow processes to manipulate basic process audit properties, and * to submit audit records. */ case PRIV_AUDIT_GETAUDIT: case PRIV_AUDIT_SETAUDIT: case PRIV_AUDIT_SUBMIT: /* * Allow processes to manipulate their regular UNIX credentials. */ case PRIV_CRED_SETUID: case PRIV_CRED_SETEUID: case PRIV_CRED_SETGID: case PRIV_CRED_SETEGID: case PRIV_CRED_SETGROUPS: case PRIV_CRED_SETREUID: case PRIV_CRED_SETREGID: case PRIV_CRED_SETRESUID: case PRIV_CRED_SETRESGID: /* * Allow processes to perform system monitoring. */ case PRIV_SEEOTHERGIDS: case PRIV_SEEOTHERUIDS: break; /* * Allow access to general process debugging facilities. We * separately control debugging based on MAC label. */ case PRIV_DEBUG_DIFFCRED: case PRIV_DEBUG_SUGID: case PRIV_DEBUG_UNPRIV: /* * Allow manipulating jails. */ case PRIV_JAIL_ATTACH: /* * Allow privilege with respect to the Partition policy, but not the * Privs policy. */ case PRIV_MAC_PARTITION: /* * Allow privilege with respect to process resource limits and login * context. */ case PRIV_PROC_LIMIT: case PRIV_PROC_SETLOGIN: case PRIV_PROC_SETRLIMIT: /* * Allow System V and POSIX IPC privileges. */ case PRIV_IPC_READ: case PRIV_IPC_WRITE: case PRIV_IPC_ADMIN: case PRIV_IPC_MSGSIZE: case PRIV_MQ_ADMIN: /* * Allow certain scheduler manipulations -- possibly this should be * controlled by more fine-grained policy, as potentially low * integrity processes can deny CPU to higher integrity ones. */ case PRIV_SCHED_DIFFCRED: case PRIV_SCHED_SETPRIORITY: case PRIV_SCHED_RTPRIO: case PRIV_SCHED_SETPOLICY: case PRIV_SCHED_SET: case PRIV_SCHED_SETPARAM: case PRIV_SCHED_IDPRIO: /* * More IPC privileges. */ case PRIV_SEM_WRITE: /* * Allow signaling privileges subject to integrity policy. */ case PRIV_SIGNAL_DIFFCRED: case PRIV_SIGNAL_SUGID: /* * Allow access to only limited sysctls from lower integrity levels; * piggy-back on the Jail definition. */ case PRIV_SYSCTL_WRITEJAIL: /* * Allow TTY-based privileges, subject to general device access using * labels on TTY device nodes, but not console privilege. */ case PRIV_TTY_DRAINWAIT: case PRIV_TTY_DTRWAIT: case PRIV_TTY_EXCLUSIVE: case PRIV_TTY_STI: case PRIV_TTY_SETA: /* * Grant most VFS privileges, as almost all are in practice bounded * by more specific checks using labels. */ case PRIV_VFS_READ: case PRIV_VFS_WRITE: case PRIV_VFS_ADMIN: case PRIV_VFS_EXEC: case PRIV_VFS_LOOKUP: case PRIV_VFS_CHFLAGS_DEV: case PRIV_VFS_CHOWN: case PRIV_VFS_CHROOT: case PRIV_VFS_RETAINSUGID: case PRIV_VFS_EXCEEDQUOTA: case PRIV_VFS_FCHROOT: case PRIV_VFS_FHOPEN: case PRIV_VFS_FHSTATFS: case PRIV_VFS_GENERATION: case PRIV_VFS_GETFH: case PRIV_VFS_GETQUOTA: case PRIV_VFS_LINK: case PRIV_VFS_MOUNT: case PRIV_VFS_MOUNT_OWNER: case PRIV_VFS_MOUNT_PERM: case PRIV_VFS_MOUNT_SUIDDIR: case PRIV_VFS_MOUNT_NONUSER: case PRIV_VFS_SETGID: case PRIV_VFS_STICKYFILE: case PRIV_VFS_SYSFLAGS: case PRIV_VFS_UNMOUNT: /* * Allow VM privileges; it would be nice if these were subject to * resource limits. */ case PRIV_VM_MADV_PROTECT: case PRIV_VM_MLOCK: case PRIV_VM_MUNLOCK: case PRIV_VM_SWAP_NOQUOTA: case PRIV_VM_SWAP_NORLIMIT: /* * Allow some but not all network privileges. In general, dont allow * reconfiguring the network stack, just normal use. */ case PRIV_NETINET_RESERVEDPORT: case PRIV_NETINET_RAW: case PRIV_NETINET_REUSEPORT: break; /* * All remaining system privileges are allow only if the process * holds privilege with respect to the LOMAC policy. */ default: subj = SLOT(cred->cr_label); error = lomac_subject_privileged(subj); if (error) return (error); } return (0); } static int lomac_proc_check_debug(struct ucred *cred, struct proc *p) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(p->p_ucred->cr_label); /* XXX: range checks */ if (!lomac_dominate_single(obj, subj)) return (ESRCH); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_proc_check_sched(struct ucred *cred, struct proc *p) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(p->p_ucred->cr_label); /* XXX: range checks */ if (!lomac_dominate_single(obj, subj)) return (ESRCH); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_proc_check_signal(struct ucred *cred, struct proc *p, int signum) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(p->p_ucred->cr_label); /* XXX: range checks */ if (!lomac_dominate_single(obj, subj)) return (ESRCH); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static void lomac_proc_destroy_label(struct label *label) { mtx_destroy(&PSLOT(label)->mtx); free(PSLOT(label), M_LOMAC); PSLOT_SET(label, NULL); } static void lomac_proc_init_label(struct label *label) { PSLOT_SET(label, malloc(sizeof(struct mac_lomac_proc), M_LOMAC, M_ZERO | M_WAITOK)); mtx_init(&PSLOT(label)->mtx, "MAC/Lomac proc lock", NULL, MTX_DEF); } static int lomac_socket_check_deliver(struct socket *so, struct label *solabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *p, *s; int error; if (!lomac_enabled) return (0); p = SLOT(mlabel); s = SLOT(solabel); SOCK_LOCK(so); error = lomac_equal_single(p, s) ? 0 : EACCES; SOCK_UNLOCK(so); return (error); } static int lomac_socket_check_relabel(struct ucred *cred, struct socket *so, struct label *solabel, struct label *newlabel) { struct mac_lomac *subj, *obj, *new; int error; SOCK_LOCK_ASSERT(so); new = SLOT(newlabel); subj = SLOT(cred->cr_label); obj = SLOT(solabel); /* * If there is a LOMAC label update for the socket, it may be an * update of single. */ error = lomac_atmostflags(new, MAC_LOMAC_FLAG_SINGLE); if (error) return (error); /* * To relabel a socket, the old socket single must be in the subject * range. */ if (!lomac_single_in_range(obj, subj)) return (EPERM); /* * If the LOMAC label is to be changed, authorize as appropriate. */ if (new->ml_flags & MAC_LOMAC_FLAG_SINGLE) { /* * To relabel a socket, the new socket single must be in the * subject range. */ if (!lomac_single_in_range(new, subj)) return (EPERM); /* * To change the LOMAC label on the socket to contain EQUAL, * the subject must have appropriate privilege. */ if (lomac_contains_equal(new)) { error = lomac_subject_privileged(subj); if (error) return (error); } } return (0); } static int lomac_socket_check_visible(struct ucred *cred, struct socket *so, struct label *solabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(solabel); SOCK_LOCK(so); if (!lomac_dominate_single(obj, subj)) { SOCK_UNLOCK(so); return (ENOENT); } SOCK_UNLOCK(so); return (0); } static void lomac_socket_create(struct ucred *cred, struct socket *so, struct label *solabel) { struct mac_lomac *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(solabel); lomac_copy_single(source, dest); } static void lomac_socket_create_mbuf(struct socket *so, struct label *solabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *source, *dest; source = SLOT(solabel); dest = SLOT(mlabel); SOCK_LOCK(so); lomac_copy_single(source, dest); SOCK_UNLOCK(so); } static void lomac_socket_newconn(struct socket *oldso, struct label *oldsolabel, struct socket *newso, struct label *newsolabel) { struct mac_lomac source, *dest; SOCK_LOCK(oldso); source = *SLOT(oldsolabel); SOCK_UNLOCK(oldso); dest = SLOT(newsolabel); SOCK_LOCK(newso); lomac_copy_single(&source, dest); SOCK_UNLOCK(newso); } static void lomac_socket_relabel(struct ucred *cred, struct socket *so, struct label *solabel, struct label *newlabel) { struct mac_lomac *source, *dest; SOCK_LOCK_ASSERT(so); source = SLOT(newlabel); dest = SLOT(solabel); try_relabel(source, dest); } static void lomac_socketpeer_set_from_mbuf(struct mbuf *m, struct label *mlabel, struct socket *so, struct label *sopeerlabel) { struct mac_lomac *source, *dest; source = SLOT(mlabel); dest = SLOT(sopeerlabel); SOCK_LOCK(so); lomac_copy_single(source, dest); SOCK_UNLOCK(so); } static void lomac_socketpeer_set_from_socket(struct socket *oldso, struct label *oldsolabel, struct socket *newso, struct label *newsopeerlabel) { struct mac_lomac source, *dest; SOCK_LOCK(oldso); source = *SLOT(oldsolabel); SOCK_UNLOCK(oldso); dest = SLOT(newsopeerlabel); SOCK_LOCK(newso); lomac_copy_single(&source, dest); SOCK_UNLOCK(newso); } static void lomac_syncache_create(struct label *label, struct inpcb *inp) { struct mac_lomac *source, *dest; source = SLOT(inp->inp_label); dest = SLOT(label); lomac_copy(source, dest); } static void lomac_syncache_create_mbuf(struct label *sc_label, struct mbuf *m, struct label *mlabel) { struct mac_lomac *source, *dest; source = SLOT(sc_label); dest = SLOT(mlabel); lomac_copy(source, dest); } static int lomac_system_check_acct(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (lomac_subject_privileged(subj)) return (EPERM); if (!lomac_high_single(obj)) return (EACCES); return (0); } static int lomac_system_check_auditctl(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (lomac_subject_privileged(subj)) return (EPERM); if (!lomac_high_single(obj)) return (EACCES); return (0); } static int lomac_system_check_swapoff(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_lomac *subj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); if (lomac_subject_privileged(subj)) return (EPERM); return (0); } static int lomac_system_check_swapon(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (lomac_subject_privileged(subj)) return (EPERM); if (!lomac_high_single(obj)) return (EACCES); return (0); } static int lomac_system_check_sysctl(struct ucred *cred, struct sysctl_oid *oidp, void *arg1, int arg2, struct sysctl_req *req) { struct mac_lomac *subj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); /* * Treat sysctl variables without CTLFLAG_ANYBODY flag as lomac/high, * but also require privilege to change them. */ if (req->newptr != NULL && (oidp->oid_kind & CTLFLAG_ANYBODY) == 0) { #ifdef notdef if (!lomac_subject_dominate_high(subj)) return (EACCES); #endif if (lomac_subject_privileged(subj)) return (EPERM); } return (0); } static void lomac_thread_userret(struct thread *td) { struct proc *p = td->td_proc; struct mac_lomac_proc *subj = PSLOT(p->p_label); struct ucred *newcred, *oldcred; int dodrop; mtx_lock(&subj->mtx); if (subj->mac_lomac.ml_flags & MAC_LOMAC_FLAG_UPDATE) { dodrop = 0; mtx_unlock(&subj->mtx); newcred = crget(); PROC_LOCK(p); mtx_lock(&subj->mtx); /* * Check if we lost the race while allocating the cred. */ if ((subj->mac_lomac.ml_flags & MAC_LOMAC_FLAG_UPDATE) == 0) { crfree(newcred); goto out; } oldcred = p->p_ucred; crcopy(newcred, oldcred); crhold(newcred); lomac_copy(&subj->mac_lomac, SLOT(newcred->cr_label)); proc_set_cred(p, newcred); crfree(oldcred); dodrop = 1; out: mtx_unlock(&subj->mtx); PROC_UNLOCK(p); if (dodrop) mac_proc_vm_revoke(curthread); } else { mtx_unlock(&subj->mtx); } } static int lomac_vnode_associate_extattr(struct mount *mp, struct label *mplabel, struct vnode *vp, struct label *vplabel) { struct mac_lomac ml_temp, *source, *dest; int buflen, error; source = SLOT(mplabel); dest = SLOT(vplabel); buflen = sizeof(ml_temp); bzero(&ml_temp, buflen); error = vn_extattr_get(vp, IO_NODELOCKED, MAC_LOMAC_EXTATTR_NAMESPACE, MAC_LOMAC_EXTATTR_NAME, &buflen, (char *)&ml_temp, curthread); if (error == ENOATTR || error == EOPNOTSUPP) { /* Fall back to the mntlabel. */ lomac_copy_single(source, dest); return (0); } else if (error) return (error); if (buflen != sizeof(ml_temp)) { if (buflen != sizeof(ml_temp) - sizeof(ml_temp.ml_auxsingle)) { printf("lomac_vnode_associate_extattr: bad size %d\n", buflen); return (EPERM); } bzero(&ml_temp.ml_auxsingle, sizeof(ml_temp.ml_auxsingle)); buflen = sizeof(ml_temp); (void)vn_extattr_set(vp, IO_NODELOCKED, MAC_LOMAC_EXTATTR_NAMESPACE, MAC_LOMAC_EXTATTR_NAME, buflen, (char *)&ml_temp, curthread); } if (lomac_valid(&ml_temp) != 0) { printf("lomac_vnode_associate_extattr: invalid\n"); return (EPERM); } if ((ml_temp.ml_flags & MAC_LOMAC_FLAGS_BOTH) != MAC_LOMAC_FLAG_SINGLE) { printf("lomac_vnode_associate_extattr: not single\n"); return (EPERM); } lomac_copy_single(&ml_temp, dest); return (0); } static void lomac_vnode_associate_singlelabel(struct mount *mp, struct label *mplabel, struct vnode *vp, struct label *vplabel) { struct mac_lomac *source, *dest; source = SLOT(mplabel); dest = SLOT(vplabel); lomac_copy_single(source, dest); } static int lomac_vnode_check_create(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct componentname *cnp, struct vattr *vap) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); if (obj->ml_flags & MAC_LOMAC_FLAG_AUX && !lomac_dominate_element(&subj->ml_single, &obj->ml_auxsingle)) return (EACCES); return (0); } static int lomac_vnode_check_deleteacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_link(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_mmap(struct ucred *cred, struct vnode *vp, struct label *vplabel, int prot, int flags) { struct mac_lomac *subj, *obj; /* * Rely on the use of open()-time protections to handle * non-revocation cases. */ if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (((prot & VM_PROT_WRITE) != 0) && ((flags & MAP_SHARED) != 0)) { if (!lomac_subject_dominate(subj, obj)) return (EACCES); } if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) { if (!lomac_dominate_single(obj, subj)) return (maybe_demote(subj, obj, "mapping", "file", vp)); } return (0); } static void lomac_vnode_check_mmap_downgrade(struct ucred *cred, struct vnode *vp, struct label *vplabel, /* XXX vm_prot_t */ int *prot) { struct mac_lomac *subj, *obj; /* * Rely on the use of open()-time protections to handle * non-revocation cases. */ if (!lomac_enabled || !revocation_enabled) return; subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) *prot &= ~VM_PROT_WRITE; } static int lomac_vnode_check_open(struct ucred *cred, struct vnode *vp, struct label *vplabel, accmode_t accmode) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); /* XXX privilege override for admin? */ if (accmode & VMODIFY_PERMS) { if (!lomac_subject_dominate(subj, obj)) return (EACCES); } return (0); } static int lomac_vnode_check_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!lomac_dominate_single(obj, subj)) return (maybe_demote(subj, obj, "reading", "file", vp)); return (0); } static int lomac_vnode_check_relabel(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *newlabel) { struct mac_lomac *old, *new, *subj; int error; old = SLOT(vplabel); new = SLOT(newlabel); subj = SLOT(cred->cr_label); /* * If there is a LOMAC label update for the vnode, it must be a * single label, with an optional explicit auxiliary single. */ error = lomac_atmostflags(new, MAC_LOMAC_FLAG_SINGLE | MAC_LOMAC_FLAG_AUX); if (error) return (error); /* * To perform a relabel of the vnode (LOMAC label or not), LOMAC must * authorize the relabel. */ if (!lomac_single_in_range(old, subj)) return (EPERM); /* * If the LOMAC label is to be changed, authorize as appropriate. */ if (new->ml_flags & MAC_LOMAC_FLAG_SINGLE) { /* * To change the LOMAC label on a vnode, the new vnode label * must be in the subject range. */ if (!lomac_single_in_range(new, subj)) return (EPERM); /* * To change the LOMAC label on the vnode to be EQUAL, the * subject must have appropriate privilege. */ if (lomac_contains_equal(new)) { error = lomac_subject_privileged(subj); if (error) return (error); } } if (new->ml_flags & MAC_LOMAC_FLAG_AUX) { /* * Fill in the missing parts from the previous label. */ if ((new->ml_flags & MAC_LOMAC_FLAG_SINGLE) == 0) lomac_copy_single(subj, new); /* * To change the auxiliary LOMAC label on a vnode, the new * vnode label must be in the subject range. */ if (!lomac_auxsingle_in_range(new, subj)) return (EPERM); /* * To change the auxiliary LOMAC label on the vnode to be * EQUAL, the subject must have appropriate privilege. */ if (lomac_contains_equal(new)) { error = lomac_subject_privileged(subj); if (error) return (error); } } return (0); } static int lomac_vnode_check_rename_from(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, int samedir, struct componentname *cnp) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); if (vp != NULL) { obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); } return (0); } static int lomac_vnode_check_revoke(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_setacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type, struct acl *acl) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_setextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); /* XXX: protect the MAC EA in a special way? */ return (0); } static int lomac_vnode_check_setflags(struct ucred *cred, struct vnode *vp, struct label *vplabel, u_long flags) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_setmode(struct ucred *cred, struct vnode *vp, struct label *vplabel, mode_t mode) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_setowner(struct ucred *cred, struct vnode *vp, struct label *vplabel, uid_t uid, gid_t gid) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_setutimes(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct timespec atime, struct timespec mtime) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_unlink(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_create_extattr(struct ucred *cred, struct mount *mp, struct label *mplabel, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_lomac *source, *dest, *dir, temp; size_t buflen; int error; buflen = sizeof(temp); bzero(&temp, buflen); source = SLOT(cred->cr_label); dest = SLOT(vplabel); dir = SLOT(dvplabel); if (dir->ml_flags & MAC_LOMAC_FLAG_AUX) { lomac_copy_auxsingle(dir, &temp); lomac_set_single(&temp, dir->ml_auxsingle.mle_type, dir->ml_auxsingle.mle_grade); } else { lomac_copy_single(source, &temp); } error = vn_extattr_set(vp, IO_NODELOCKED, MAC_LOMAC_EXTATTR_NAMESPACE, MAC_LOMAC_EXTATTR_NAME, buflen, (char *)&temp, curthread); if (error == 0) lomac_copy(&temp, dest); return (error); } static void lomac_vnode_execve_transition(struct ucred *old, struct ucred *new, struct vnode *vp, struct label *vplabel, struct label *interpvplabel, struct image_params *imgp, struct label *execlabel) { struct mac_lomac *source, *dest, *obj, *robj; source = SLOT(old->cr_label); dest = SLOT(new->cr_label); obj = SLOT(vplabel); robj = interpvplabel != NULL ? SLOT(interpvplabel) : obj; lomac_copy(source, dest); /* * If there's an auxiliary label on the real object, respect it and * assume that this level should be assumed immediately if a higher * level is currently in place. */ if (robj->ml_flags & MAC_LOMAC_FLAG_AUX && !lomac_dominate_element(&robj->ml_auxsingle, &dest->ml_single) && lomac_auxsingle_in_range(robj, dest)) lomac_set_single(dest, robj->ml_auxsingle.mle_type, robj->ml_auxsingle.mle_grade); /* * Restructuring to use the execve transitioning mechanism instead of * the normal demotion mechanism here would be difficult, so just * copy the label over and perform standard demotion. This is also * non-optimal because it will result in the intermediate label "new" * being created and immediately recycled. */ if (lomac_enabled && revocation_enabled && !lomac_dominate_single(obj, source)) (void)maybe_demote(source, obj, "executing", "file", vp); } static int lomac_vnode_execve_will_transition(struct ucred *old, struct vnode *vp, struct label *vplabel, struct label *interpvplabel, struct image_params *imgp, struct label *execlabel) { struct mac_lomac *subj, *obj, *robj; if (!lomac_enabled || !revocation_enabled) return (0); subj = SLOT(old->cr_label); obj = SLOT(vplabel); robj = interpvplabel != NULL ? SLOT(interpvplabel) : obj; return ((robj->ml_flags & MAC_LOMAC_FLAG_AUX && !lomac_dominate_element(&robj->ml_auxsingle, &subj->ml_single) && lomac_auxsingle_in_range(robj, subj)) || !lomac_dominate_single(obj, subj)); } static void lomac_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *newlabel) { struct mac_lomac *source, *dest; source = SLOT(newlabel); dest = SLOT(vplabel); try_relabel(source, dest); } static int lomac_vnode_setlabel_extattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *intlabel) { struct mac_lomac *source, temp; size_t buflen; int error; buflen = sizeof(temp); bzero(&temp, buflen); source = SLOT(intlabel); if ((source->ml_flags & MAC_LOMAC_FLAG_SINGLE) == 0) return (0); lomac_copy_single(source, &temp); error = vn_extattr_set(vp, IO_NODELOCKED, MAC_LOMAC_EXTATTR_NAMESPACE, MAC_LOMAC_EXTATTR_NAME, buflen, (char *)&temp, curthread); return (error); } static struct mac_policy_ops lomac_ops = { .mpo_init = lomac_init, .mpo_destroy = lomac_fini, .mpo_bpfdesc_check_receive = lomac_bpfdesc_check_receive, .mpo_bpfdesc_create = lomac_bpfdesc_create, .mpo_bpfdesc_create_mbuf = lomac_bpfdesc_create_mbuf, .mpo_bpfdesc_destroy_label = lomac_destroy_label, .mpo_bpfdesc_init_label = lomac_init_label, .mpo_cred_check_relabel = lomac_cred_check_relabel, .mpo_cred_check_visible = lomac_cred_check_visible, .mpo_cred_copy_label = lomac_copy_label, .mpo_cred_create_swapper = lomac_cred_create_swapper, .mpo_cred_create_init = lomac_cred_create_init, .mpo_cred_destroy_label = lomac_destroy_label, .mpo_cred_externalize_label = lomac_externalize_label, .mpo_cred_init_label = lomac_init_label, .mpo_cred_internalize_label = lomac_internalize_label, .mpo_cred_relabel = lomac_cred_relabel, .mpo_devfs_create_device = lomac_devfs_create_device, .mpo_devfs_create_directory = lomac_devfs_create_directory, .mpo_devfs_create_symlink = lomac_devfs_create_symlink, .mpo_devfs_destroy_label = lomac_destroy_label, .mpo_devfs_init_label = lomac_init_label, .mpo_devfs_update = lomac_devfs_update, .mpo_devfs_vnode_associate = lomac_devfs_vnode_associate, .mpo_ifnet_check_relabel = lomac_ifnet_check_relabel, .mpo_ifnet_check_transmit = lomac_ifnet_check_transmit, .mpo_ifnet_copy_label = lomac_copy_label, .mpo_ifnet_create = lomac_ifnet_create, .mpo_ifnet_create_mbuf = lomac_ifnet_create_mbuf, .mpo_ifnet_destroy_label = lomac_destroy_label, .mpo_ifnet_externalize_label = lomac_externalize_label, .mpo_ifnet_init_label = lomac_init_label, .mpo_ifnet_internalize_label = lomac_internalize_label, .mpo_ifnet_relabel = lomac_ifnet_relabel, .mpo_syncache_create = lomac_syncache_create, .mpo_syncache_destroy_label = lomac_destroy_label, .mpo_syncache_init_label = lomac_init_label_waitcheck, .mpo_inpcb_check_deliver = lomac_inpcb_check_deliver, .mpo_inpcb_check_visible = lomac_inpcb_check_visible, .mpo_inpcb_create = lomac_inpcb_create, .mpo_inpcb_create_mbuf = lomac_inpcb_create_mbuf, .mpo_inpcb_destroy_label = lomac_destroy_label, .mpo_inpcb_init_label = lomac_init_label_waitcheck, .mpo_inpcb_sosetlabel = lomac_inpcb_sosetlabel, .mpo_ip6q_create = lomac_ip6q_create, .mpo_ip6q_destroy_label = lomac_destroy_label, .mpo_ip6q_init_label = lomac_init_label_waitcheck, .mpo_ip6q_match = lomac_ip6q_match, .mpo_ip6q_reassemble = lomac_ip6q_reassemble, .mpo_ip6q_update = lomac_ip6q_update, .mpo_ipq_create = lomac_ipq_create, .mpo_ipq_destroy_label = lomac_destroy_label, .mpo_ipq_init_label = lomac_init_label_waitcheck, .mpo_ipq_match = lomac_ipq_match, .mpo_ipq_reassemble = lomac_ipq_reassemble, .mpo_ipq_update = lomac_ipq_update, .mpo_kld_check_load = lomac_kld_check_load, .mpo_mbuf_copy_label = lomac_copy_label, .mpo_mbuf_destroy_label = lomac_destroy_label, .mpo_mbuf_init_label = lomac_init_label_waitcheck, .mpo_mount_create = lomac_mount_create, .mpo_mount_destroy_label = lomac_destroy_label, .mpo_mount_init_label = lomac_init_label, .mpo_netinet_arp_send = lomac_netinet_arp_send, .mpo_netinet_firewall_reply = lomac_netinet_firewall_reply, .mpo_netinet_firewall_send = lomac_netinet_firewall_send, .mpo_netinet_fragment = lomac_netinet_fragment, .mpo_netinet_icmp_reply = lomac_netinet_icmp_reply, .mpo_netinet_igmp_send = lomac_netinet_igmp_send, .mpo_netinet6_nd6_send = lomac_netinet6_nd6_send, .mpo_pipe_check_ioctl = lomac_pipe_check_ioctl, .mpo_pipe_check_read = lomac_pipe_check_read, .mpo_pipe_check_relabel = lomac_pipe_check_relabel, .mpo_pipe_check_write = lomac_pipe_check_write, .mpo_pipe_copy_label = lomac_copy_label, .mpo_pipe_create = lomac_pipe_create, .mpo_pipe_destroy_label = lomac_destroy_label, .mpo_pipe_externalize_label = lomac_externalize_label, .mpo_pipe_init_label = lomac_init_label, .mpo_pipe_internalize_label = lomac_internalize_label, .mpo_pipe_relabel = lomac_pipe_relabel, .mpo_priv_check = lomac_priv_check, .mpo_proc_check_debug = lomac_proc_check_debug, .mpo_proc_check_sched = lomac_proc_check_sched, .mpo_proc_check_signal = lomac_proc_check_signal, .mpo_proc_destroy_label = lomac_proc_destroy_label, .mpo_proc_init_label = lomac_proc_init_label, .mpo_socket_check_deliver = lomac_socket_check_deliver, .mpo_socket_check_relabel = lomac_socket_check_relabel, .mpo_socket_check_visible = lomac_socket_check_visible, .mpo_socket_copy_label = lomac_copy_label, .mpo_socket_create = lomac_socket_create, .mpo_socket_create_mbuf = lomac_socket_create_mbuf, .mpo_socket_destroy_label = lomac_destroy_label, .mpo_socket_externalize_label = lomac_externalize_label, .mpo_socket_init_label = lomac_init_label_waitcheck, .mpo_socket_internalize_label = lomac_internalize_label, .mpo_socket_newconn = lomac_socket_newconn, .mpo_socket_relabel = lomac_socket_relabel, .mpo_socketpeer_destroy_label = lomac_destroy_label, .mpo_socketpeer_externalize_label = lomac_externalize_label, .mpo_socketpeer_init_label = lomac_init_label_waitcheck, .mpo_socketpeer_set_from_mbuf = lomac_socketpeer_set_from_mbuf, .mpo_socketpeer_set_from_socket = lomac_socketpeer_set_from_socket, .mpo_syncache_create_mbuf = lomac_syncache_create_mbuf, .mpo_system_check_acct = lomac_system_check_acct, .mpo_system_check_auditctl = lomac_system_check_auditctl, .mpo_system_check_swapoff = lomac_system_check_swapoff, .mpo_system_check_swapon = lomac_system_check_swapon, .mpo_system_check_sysctl = lomac_system_check_sysctl, .mpo_thread_userret = lomac_thread_userret, .mpo_vnode_associate_extattr = lomac_vnode_associate_extattr, .mpo_vnode_associate_singlelabel = lomac_vnode_associate_singlelabel, .mpo_vnode_check_access = lomac_vnode_check_open, .mpo_vnode_check_create = lomac_vnode_check_create, .mpo_vnode_check_deleteacl = lomac_vnode_check_deleteacl, .mpo_vnode_check_link = lomac_vnode_check_link, .mpo_vnode_check_mmap = lomac_vnode_check_mmap, .mpo_vnode_check_mmap_downgrade = lomac_vnode_check_mmap_downgrade, .mpo_vnode_check_open = lomac_vnode_check_open, .mpo_vnode_check_read = lomac_vnode_check_read, .mpo_vnode_check_relabel = lomac_vnode_check_relabel, .mpo_vnode_check_rename_from = lomac_vnode_check_rename_from, .mpo_vnode_check_rename_to = lomac_vnode_check_rename_to, .mpo_vnode_check_revoke = lomac_vnode_check_revoke, .mpo_vnode_check_setacl = lomac_vnode_check_setacl, .mpo_vnode_check_setextattr = lomac_vnode_check_setextattr, .mpo_vnode_check_setflags = lomac_vnode_check_setflags, .mpo_vnode_check_setmode = lomac_vnode_check_setmode, .mpo_vnode_check_setowner = lomac_vnode_check_setowner, .mpo_vnode_check_setutimes = lomac_vnode_check_setutimes, .mpo_vnode_check_unlink = lomac_vnode_check_unlink, .mpo_vnode_check_write = lomac_vnode_check_write, .mpo_vnode_copy_label = lomac_copy_label, .mpo_vnode_create_extattr = lomac_vnode_create_extattr, .mpo_vnode_destroy_label = lomac_destroy_label, .mpo_vnode_execve_transition = lomac_vnode_execve_transition, .mpo_vnode_execve_will_transition = lomac_vnode_execve_will_transition, .mpo_vnode_externalize_label = lomac_externalize_label, .mpo_vnode_init_label = lomac_init_label, .mpo_vnode_internalize_label = lomac_internalize_label, .mpo_vnode_relabel = lomac_vnode_relabel, .mpo_vnode_setlabel_extattr = lomac_vnode_setlabel_extattr, }; MAC_POLICY_SET(&lomac_ops, mac_lomac, "TrustedBSD MAC/LOMAC", MPC_LOADTIME_FLAG_NOTLATE, &lomac_slot); diff --git a/sys/security/mac_mls/mac_mls.c b/sys/security/mac_mls/mac_mls.c index da9ed8a3e141..94d907efc7f1 100644 --- a/sys/security/mac_mls/mac_mls.c +++ b/sys/security/mac_mls/mac_mls.c @@ -1,3423 +1,3423 @@ /*- * Copyright (c) 1999-2002, 2007-2011 Robert N. M. Watson * Copyright (c) 2001-2005 McAfee, Inc. * Copyright (c) 2006 SPARTA, Inc. * All rights reserved. * * This software was developed by Robert Watson for the TrustedBSD Project. * * This software was developed for the FreeBSD Project in part by McAfee * Research, the Security Research Division of McAfee, Inc. under * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA * CHATS research program. * * This software was enhanced by SPARTA ISSO under SPAWAR contract * N66001-04-C-6019 ("SEFOS"). * * This software was developed at the University of Cambridge Computer * Laboratory with support from a grant from Google, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Developed by the TrustedBSD Project. * * MLS fixed label mandatory confidentiality policy. */ #include #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 #include #include #include #include #include #include SYSCTL_DECL(_security_mac); static SYSCTL_NODE(_security_mac, OID_AUTO, mls, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "TrustedBSD mac_mls policy controls"); static int mls_label_size = sizeof(struct mac_mls); SYSCTL_INT(_security_mac_mls, OID_AUTO, label_size, CTLFLAG_RD, &mls_label_size, 0, "Size of struct mac_mls"); static int mls_enabled = 1; SYSCTL_INT(_security_mac_mls, OID_AUTO, enabled, CTLFLAG_RWTUN, &mls_enabled, 0, "Enforce MAC/MLS policy"); static int destroyed_not_inited; SYSCTL_INT(_security_mac_mls, OID_AUTO, destroyed_not_inited, CTLFLAG_RD, &destroyed_not_inited, 0, "Count of labels destroyed but not inited"); static int ptys_equal = 0; SYSCTL_INT(_security_mac_mls, OID_AUTO, ptys_equal, CTLFLAG_RWTUN, &ptys_equal, 0, "Label pty devices as mls/equal on create"); static int revocation_enabled = 0; SYSCTL_INT(_security_mac_mls, OID_AUTO, revocation_enabled, CTLFLAG_RWTUN, &revocation_enabled, 0, "Revoke access to objects on relabel"); static int max_compartments = MAC_MLS_MAX_COMPARTMENTS; SYSCTL_INT(_security_mac_mls, OID_AUTO, max_compartments, CTLFLAG_RD, &max_compartments, 0, "Maximum compartments the policy supports"); static int mls_slot; #define SLOT(l) ((struct mac_mls *)mac_label_get((l), mls_slot)) #define SLOT_SET(l, val) mac_label_set((l), mls_slot, (uintptr_t)(val)) static uma_zone_t zone_mls; static __inline int mls_bit_set_empty(u_char *set) { int i; for (i = 0; i < MAC_MLS_MAX_COMPARTMENTS >> 3; i++) if (set[i] != 0) return (0); return (1); } static struct mac_mls * mls_alloc(int flag) { return (uma_zalloc(zone_mls, flag | M_ZERO)); } static void mls_free(struct mac_mls *mm) { if (mm != NULL) uma_zfree(zone_mls, mm); else atomic_add_int(&destroyed_not_inited, 1); } static int mls_atmostflags(struct mac_mls *mm, int flags) { if ((mm->mm_flags & flags) != mm->mm_flags) return (EINVAL); return (0); } static int mls_dominate_element(struct mac_mls_element *a, struct mac_mls_element *b) { int bit; switch (a->mme_type) { case MAC_MLS_TYPE_EQUAL: case MAC_MLS_TYPE_HIGH: return (1); case MAC_MLS_TYPE_LOW: switch (b->mme_type) { case MAC_MLS_TYPE_LEVEL: case MAC_MLS_TYPE_HIGH: return (0); case MAC_MLS_TYPE_EQUAL: case MAC_MLS_TYPE_LOW: return (1); default: panic("mls_dominate_element: b->mme_type invalid"); } case MAC_MLS_TYPE_LEVEL: switch (b->mme_type) { case MAC_MLS_TYPE_EQUAL: case MAC_MLS_TYPE_LOW: return (1); case MAC_MLS_TYPE_HIGH: return (0); case MAC_MLS_TYPE_LEVEL: for (bit = 1; bit <= MAC_MLS_MAX_COMPARTMENTS; bit++) if (!MAC_MLS_BIT_TEST(bit, a->mme_compartments) && MAC_MLS_BIT_TEST(bit, b->mme_compartments)) return (0); return (a->mme_level >= b->mme_level); default: panic("mls_dominate_element: b->mme_type invalid"); } default: panic("mls_dominate_element: a->mme_type invalid"); } return (0); } static int mls_range_in_range(struct mac_mls *rangea, struct mac_mls *rangeb) { return (mls_dominate_element(&rangeb->mm_rangehigh, &rangea->mm_rangehigh) && mls_dominate_element(&rangea->mm_rangelow, &rangeb->mm_rangelow)); } static int mls_effective_in_range(struct mac_mls *effective, struct mac_mls *range) { KASSERT((effective->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, ("mls_effective_in_range: a not effective")); KASSERT((range->mm_flags & MAC_MLS_FLAG_RANGE) != 0, ("mls_effective_in_range: b not range")); return (mls_dominate_element(&range->mm_rangehigh, &effective->mm_effective) && mls_dominate_element(&effective->mm_effective, &range->mm_rangelow)); return (1); } static int mls_dominate_effective(struct mac_mls *a, struct mac_mls *b) { KASSERT((a->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, ("mls_dominate_effective: a not effective")); KASSERT((b->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, ("mls_dominate_effective: b not effective")); return (mls_dominate_element(&a->mm_effective, &b->mm_effective)); } static int mls_equal_element(struct mac_mls_element *a, struct mac_mls_element *b) { if (a->mme_type == MAC_MLS_TYPE_EQUAL || b->mme_type == MAC_MLS_TYPE_EQUAL) return (1); return (a->mme_type == b->mme_type && a->mme_level == b->mme_level); } static int mls_equal_effective(struct mac_mls *a, struct mac_mls *b) { KASSERT((a->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, ("mls_equal_effective: a not effective")); KASSERT((b->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, ("mls_equal_effective: b not effective")); return (mls_equal_element(&a->mm_effective, &b->mm_effective)); } static int mls_contains_equal(struct mac_mls *mm) { if (mm->mm_flags & MAC_MLS_FLAG_EFFECTIVE) if (mm->mm_effective.mme_type == MAC_MLS_TYPE_EQUAL) return (1); if (mm->mm_flags & MAC_MLS_FLAG_RANGE) { if (mm->mm_rangelow.mme_type == MAC_MLS_TYPE_EQUAL) return (1); if (mm->mm_rangehigh.mme_type == MAC_MLS_TYPE_EQUAL) return (1); } return (0); } static int mls_subject_privileged(struct mac_mls *mm) { KASSERT((mm->mm_flags & MAC_MLS_FLAGS_BOTH) == MAC_MLS_FLAGS_BOTH, ("mls_subject_privileged: subject doesn't have both labels")); /* If the effective is EQUAL, it's ok. */ if (mm->mm_effective.mme_type == MAC_MLS_TYPE_EQUAL) return (0); /* If either range endpoint is EQUAL, it's ok. */ if (mm->mm_rangelow.mme_type == MAC_MLS_TYPE_EQUAL || mm->mm_rangehigh.mme_type == MAC_MLS_TYPE_EQUAL) return (0); /* If the range is low-high, it's ok. */ if (mm->mm_rangelow.mme_type == MAC_MLS_TYPE_LOW && mm->mm_rangehigh.mme_type == MAC_MLS_TYPE_HIGH) return (0); /* It's not ok. */ return (EPERM); } static int mls_valid(struct mac_mls *mm) { if (mm->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { switch (mm->mm_effective.mme_type) { case MAC_MLS_TYPE_LEVEL: break; case MAC_MLS_TYPE_EQUAL: case MAC_MLS_TYPE_HIGH: case MAC_MLS_TYPE_LOW: if (mm->mm_effective.mme_level != 0 || !MAC_MLS_BIT_SET_EMPTY( mm->mm_effective.mme_compartments)) return (EINVAL); break; default: return (EINVAL); } } else { if (mm->mm_effective.mme_type != MAC_MLS_TYPE_UNDEF) return (EINVAL); } if (mm->mm_flags & MAC_MLS_FLAG_RANGE) { switch (mm->mm_rangelow.mme_type) { case MAC_MLS_TYPE_LEVEL: break; case MAC_MLS_TYPE_EQUAL: case MAC_MLS_TYPE_HIGH: case MAC_MLS_TYPE_LOW: if (mm->mm_rangelow.mme_level != 0 || !MAC_MLS_BIT_SET_EMPTY( mm->mm_rangelow.mme_compartments)) return (EINVAL); break; default: return (EINVAL); } switch (mm->mm_rangehigh.mme_type) { case MAC_MLS_TYPE_LEVEL: break; case MAC_MLS_TYPE_EQUAL: case MAC_MLS_TYPE_HIGH: case MAC_MLS_TYPE_LOW: if (mm->mm_rangehigh.mme_level != 0 || !MAC_MLS_BIT_SET_EMPTY( mm->mm_rangehigh.mme_compartments)) return (EINVAL); break; default: return (EINVAL); } if (!mls_dominate_element(&mm->mm_rangehigh, &mm->mm_rangelow)) return (EINVAL); } else { if (mm->mm_rangelow.mme_type != MAC_MLS_TYPE_UNDEF || mm->mm_rangehigh.mme_type != MAC_MLS_TYPE_UNDEF) return (EINVAL); } return (0); } static void mls_set_range(struct mac_mls *mm, u_short typelow, u_short levellow, u_char *compartmentslow, u_short typehigh, u_short levelhigh, u_char *compartmentshigh) { mm->mm_rangelow.mme_type = typelow; mm->mm_rangelow.mme_level = levellow; if (compartmentslow != NULL) memcpy(mm->mm_rangelow.mme_compartments, compartmentslow, sizeof(mm->mm_rangelow.mme_compartments)); mm->mm_rangehigh.mme_type = typehigh; mm->mm_rangehigh.mme_level = levelhigh; if (compartmentshigh != NULL) memcpy(mm->mm_rangehigh.mme_compartments, compartmentshigh, sizeof(mm->mm_rangehigh.mme_compartments)); mm->mm_flags |= MAC_MLS_FLAG_RANGE; } static void mls_set_effective(struct mac_mls *mm, u_short type, u_short level, u_char *compartments) { mm->mm_effective.mme_type = type; mm->mm_effective.mme_level = level; if (compartments != NULL) memcpy(mm->mm_effective.mme_compartments, compartments, sizeof(mm->mm_effective.mme_compartments)); mm->mm_flags |= MAC_MLS_FLAG_EFFECTIVE; } static void mls_copy_range(struct mac_mls *labelfrom, struct mac_mls *labelto) { KASSERT((labelfrom->mm_flags & MAC_MLS_FLAG_RANGE) != 0, ("mls_copy_range: labelfrom not range")); labelto->mm_rangelow = labelfrom->mm_rangelow; labelto->mm_rangehigh = labelfrom->mm_rangehigh; labelto->mm_flags |= MAC_MLS_FLAG_RANGE; } static void mls_copy_effective(struct mac_mls *labelfrom, struct mac_mls *labelto) { KASSERT((labelfrom->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, ("mls_copy_effective: labelfrom not effective")); labelto->mm_effective = labelfrom->mm_effective; labelto->mm_flags |= MAC_MLS_FLAG_EFFECTIVE; } static void mls_copy(struct mac_mls *source, struct mac_mls *dest) { if (source->mm_flags & MAC_MLS_FLAG_EFFECTIVE) mls_copy_effective(source, dest); if (source->mm_flags & MAC_MLS_FLAG_RANGE) mls_copy_range(source, dest); } /* * Policy module operations. */ static void mls_init(struct mac_policy_conf *conf) { zone_mls = uma_zcreate("mac_mls", sizeof(struct mac_mls), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); } /* * Label operations. */ static void mls_init_label(struct label *label) { SLOT_SET(label, mls_alloc(M_WAITOK)); } static int mls_init_label_waitcheck(struct label *label, int flag) { SLOT_SET(label, mls_alloc(flag)); if (SLOT(label) == NULL) return (ENOMEM); return (0); } static void mls_destroy_label(struct label *label) { mls_free(SLOT(label)); SLOT_SET(label, NULL); } /* * mls_element_to_string() accepts an sbuf and MLS element. It converts the * MLS element to a string and stores the result in the sbuf; if there isn't * space in the sbuf, -1 is returned. */ static int mls_element_to_string(struct sbuf *sb, struct mac_mls_element *element) { int i, first; switch (element->mme_type) { case MAC_MLS_TYPE_HIGH: return (sbuf_printf(sb, "high")); case MAC_MLS_TYPE_LOW: return (sbuf_printf(sb, "low")); case MAC_MLS_TYPE_EQUAL: return (sbuf_printf(sb, "equal")); case MAC_MLS_TYPE_LEVEL: if (sbuf_printf(sb, "%d", element->mme_level) == -1) return (-1); first = 1; for (i = 1; i <= MAC_MLS_MAX_COMPARTMENTS; i++) { if (MAC_MLS_BIT_TEST(i, element->mme_compartments)) { if (first) { if (sbuf_putc(sb, ':') == -1) return (-1); if (sbuf_printf(sb, "%d", i) == -1) return (-1); first = 0; } else { if (sbuf_printf(sb, "+%d", i) == -1) return (-1); } } } return (0); default: panic("mls_element_to_string: invalid type (%d)", element->mme_type); } } /* * mls_to_string() converts an MLS label to a string, and places the results * in the passed sbuf. It returns 0 on success, or EINVAL if there isn't * room in the sbuf. Note: the sbuf will be modified even in a failure case, * so the caller may need to revert the sbuf by restoring the offset if * that's undesired. */ static int mls_to_string(struct sbuf *sb, struct mac_mls *mm) { if (mm->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { if (mls_element_to_string(sb, &mm->mm_effective) == -1) return (EINVAL); } if (mm->mm_flags & MAC_MLS_FLAG_RANGE) { if (sbuf_putc(sb, '(') == -1) return (EINVAL); if (mls_element_to_string(sb, &mm->mm_rangelow) == -1) return (EINVAL); if (sbuf_putc(sb, '-') == -1) return (EINVAL); if (mls_element_to_string(sb, &mm->mm_rangehigh) == -1) return (EINVAL); if (sbuf_putc(sb, ')') == -1) return (EINVAL); } return (0); } static int mls_externalize_label(struct label *label, char *element_name, struct sbuf *sb, int *claimed) { struct mac_mls *mm; if (strcmp(MAC_MLS_LABEL_NAME, element_name) != 0) return (0); (*claimed)++; mm = SLOT(label); return (mls_to_string(sb, mm)); } static int mls_parse_element(struct mac_mls_element *element, char *string) { char *compartment, *end, *level; int value; if (strcmp(string, "high") == 0 || strcmp(string, "hi") == 0) { element->mme_type = MAC_MLS_TYPE_HIGH; element->mme_level = MAC_MLS_TYPE_UNDEF; } else if (strcmp(string, "low") == 0 || strcmp(string, "lo") == 0) { element->mme_type = MAC_MLS_TYPE_LOW; element->mme_level = MAC_MLS_TYPE_UNDEF; } else if (strcmp(string, "equal") == 0 || strcmp(string, "eq") == 0) { element->mme_type = MAC_MLS_TYPE_EQUAL; element->mme_level = MAC_MLS_TYPE_UNDEF; } else { element->mme_type = MAC_MLS_TYPE_LEVEL; /* * Numeric level piece of the element. */ level = strsep(&string, ":"); value = strtol(level, &end, 10); if (end == level || *end != '\0') return (EINVAL); if (value < 0 || value > 65535) return (EINVAL); element->mme_level = value; /* * Optional compartment piece of the element. If none are * included, we assume that the label has no compartments. */ if (string == NULL) return (0); if (*string == '\0') return (0); while ((compartment = strsep(&string, "+")) != NULL) { value = strtol(compartment, &end, 10); if (compartment == end || *end != '\0') return (EINVAL); if (value < 1 || value > MAC_MLS_MAX_COMPARTMENTS) return (EINVAL); MAC_MLS_BIT_SET(value, element->mme_compartments); } } return (0); } /* * Note: destructively consumes the string, make a local copy before calling * if that's a problem. */ static int mls_parse(struct mac_mls *mm, char *string) { char *rangehigh, *rangelow, *effective; int error; effective = strsep(&string, "("); if (*effective == '\0') effective = NULL; if (string != NULL) { rangelow = strsep(&string, "-"); if (string == NULL) return (EINVAL); rangehigh = strsep(&string, ")"); if (string == NULL) return (EINVAL); if (*string != '\0') return (EINVAL); } else { rangelow = NULL; rangehigh = NULL; } KASSERT((rangelow != NULL && rangehigh != NULL) || (rangelow == NULL && rangehigh == NULL), ("mls_parse: range mismatch")); bzero(mm, sizeof(*mm)); if (effective != NULL) { error = mls_parse_element(&mm->mm_effective, effective); if (error) return (error); mm->mm_flags |= MAC_MLS_FLAG_EFFECTIVE; } if (rangelow != NULL) { error = mls_parse_element(&mm->mm_rangelow, rangelow); if (error) return (error); error = mls_parse_element(&mm->mm_rangehigh, rangehigh); if (error) return (error); mm->mm_flags |= MAC_MLS_FLAG_RANGE; } error = mls_valid(mm); if (error) return (error); return (0); } static int mls_internalize_label(struct label *label, char *element_name, char *element_data, int *claimed) { struct mac_mls *mm, mm_temp; int error; if (strcmp(MAC_MLS_LABEL_NAME, element_name) != 0) return (0); (*claimed)++; error = mls_parse(&mm_temp, element_data); if (error) return (error); mm = SLOT(label); *mm = mm_temp; return (0); } static void mls_copy_label(struct label *src, struct label *dest) { *SLOT(dest) = *SLOT(src); } /* * Object-specific entry point implementations are sorted alphabetically by * object type name and then by operation. */ static int mls_bpfdesc_check_receive(struct bpf_d *d, struct label *dlabel, struct ifnet *ifp, struct label *ifplabel) { struct mac_mls *a, *b; if (!mls_enabled) return (0); a = SLOT(dlabel); b = SLOT(ifplabel); if (mls_equal_effective(a, b)) return (0); return (EACCES); } static void mls_bpfdesc_create(struct ucred *cred, struct bpf_d *d, struct label *dlabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(dlabel); mls_copy_effective(source, dest); } static void mls_bpfdesc_create_mbuf(struct bpf_d *d, struct label *dlabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *source, *dest; source = SLOT(dlabel); dest = SLOT(mlabel); mls_copy_effective(source, dest); } static void mls_cred_associate_nfsd(struct ucred *cred) { struct mac_mls *label; label = SLOT(cred->cr_label); mls_set_effective(label, MAC_MLS_TYPE_LOW, 0, NULL); mls_set_range(label, MAC_MLS_TYPE_LOW, 0, NULL, MAC_MLS_TYPE_HIGH, 0, NULL); } static int mls_cred_check_relabel(struct ucred *cred, struct label *newlabel) { struct mac_mls *subj, *new; int error; subj = SLOT(cred->cr_label); new = SLOT(newlabel); /* * If there is an MLS label update for the credential, it may be an * update of effective, range, or both. */ error = mls_atmostflags(new, MAC_MLS_FLAGS_BOTH); if (error) return (error); /* * If the MLS label is to be changed, authorize as appropriate. */ if (new->mm_flags & MAC_MLS_FLAGS_BOTH) { /* * If the change request modifies both the MLS label * effective and range, check that the new effective will be * in the new range. */ if ((new->mm_flags & MAC_MLS_FLAGS_BOTH) == MAC_MLS_FLAGS_BOTH && !mls_effective_in_range(new, new)) return (EINVAL); /* * To change the MLS effective label on a credential, the new * effective label must be in the current range. */ if (new->mm_flags & MAC_MLS_FLAG_EFFECTIVE && !mls_effective_in_range(new, subj)) return (EPERM); /* * To change the MLS range label on a credential, the new * range must be in the current range. */ if (new->mm_flags & MAC_MLS_FLAG_RANGE && !mls_range_in_range(new, subj)) return (EPERM); /* * To have EQUAL in any component of the new credential MLS * label, the subject must already have EQUAL in their label. */ if (mls_contains_equal(new)) { error = mls_subject_privileged(subj); if (error) return (error); } } return (0); } static int mls_cred_check_visible(struct ucred *cr1, struct ucred *cr2) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cr1->cr_label); obj = SLOT(cr2->cr_label); /* XXX: range */ if (!mls_dominate_effective(subj, obj)) return (ESRCH); return (0); } static void mls_cred_create_init(struct ucred *cred) { struct mac_mls *dest; dest = SLOT(cred->cr_label); mls_set_effective(dest, MAC_MLS_TYPE_LOW, 0, NULL); mls_set_range(dest, MAC_MLS_TYPE_LOW, 0, NULL, MAC_MLS_TYPE_HIGH, 0, NULL); } static void mls_cred_create_swapper(struct ucred *cred) { struct mac_mls *dest; dest = SLOT(cred->cr_label); mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); mls_set_range(dest, MAC_MLS_TYPE_LOW, 0, NULL, MAC_MLS_TYPE_HIGH, 0, NULL); } static void mls_cred_relabel(struct ucred *cred, struct label *newlabel) { struct mac_mls *source, *dest; source = SLOT(newlabel); dest = SLOT(cred->cr_label); mls_copy(source, dest); } static void mls_devfs_create_device(struct ucred *cred, struct mount *mp, struct cdev *dev, struct devfs_dirent *de, struct label *delabel) { struct mac_mls *mm; const char *dn; int mls_type; mm = SLOT(delabel); dn = devtoname(dev); if (strcmp(dn, "null") == 0 || strcmp(dn, "zero") == 0 || strcmp(dn, "random") == 0 || strncmp(dn, "fd/", strlen("fd/")) == 0) mls_type = MAC_MLS_TYPE_EQUAL; else if (strcmp(dn, "kmem") == 0 || strcmp(dn, "mem") == 0) mls_type = MAC_MLS_TYPE_HIGH; else if (ptys_equal && (strncmp(dn, "ttyp", strlen("ttyp")) == 0 || strncmp(dn, "pts/", strlen("pts/")) == 0 || strncmp(dn, "ptyp", strlen("ptyp")) == 0)) mls_type = MAC_MLS_TYPE_EQUAL; else mls_type = MAC_MLS_TYPE_LOW; mls_set_effective(mm, mls_type, 0, NULL); } static void mls_devfs_create_directory(struct mount *mp, char *dirname, int dirnamelen, struct devfs_dirent *de, struct label *delabel) { struct mac_mls *mm; mm = SLOT(delabel); mls_set_effective(mm, MAC_MLS_TYPE_LOW, 0, NULL); } static void mls_devfs_create_symlink(struct ucred *cred, struct mount *mp, struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de, struct label *delabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(delabel); mls_copy_effective(source, dest); } static void mls_devfs_update(struct mount *mp, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vplabel) { struct mac_mls *source, *dest; source = SLOT(vplabel); dest = SLOT(delabel); mls_copy_effective(source, dest); } static void mls_devfs_vnode_associate(struct mount *mp, struct label *mplabel, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vplabel) { struct mac_mls *source, *dest; source = SLOT(delabel); dest = SLOT(vplabel); mls_copy_effective(source, dest); } static int mls_ifnet_check_relabel(struct ucred *cred, struct ifnet *ifp, struct label *ifplabel, struct label *newlabel) { struct mac_mls *subj, *new; int error; subj = SLOT(cred->cr_label); new = SLOT(newlabel); /* * If there is an MLS label update for the interface, it may be an * update of effective, range, or both. */ error = mls_atmostflags(new, MAC_MLS_FLAGS_BOTH); if (error) return (error); /* * Relabeling network interfaces requires MLS privilege. */ return (mls_subject_privileged(subj)); } static int mls_ifnet_check_transmit(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *p, *i; if (!mls_enabled) return (0); p = SLOT(mlabel); i = SLOT(ifplabel); return (mls_effective_in_range(p, i) ? 0 : EACCES); } static void mls_ifnet_create(struct ifnet *ifp, struct label *ifplabel) { struct mac_mls *dest; int type; dest = SLOT(ifplabel); - if (ifp->if_type == IFT_LOOP) + if (if_gettype(ifp) == IFT_LOOP) type = MAC_MLS_TYPE_EQUAL; else type = MAC_MLS_TYPE_LOW; mls_set_effective(dest, type, 0, NULL); mls_set_range(dest, type, 0, NULL, type, 0, NULL); } static void mls_ifnet_create_mbuf(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *source, *dest; source = SLOT(ifplabel); dest = SLOT(mlabel); mls_copy_effective(source, dest); } static void mls_ifnet_relabel(struct ucred *cred, struct ifnet *ifp, struct label *ifplabel, struct label *newlabel) { struct mac_mls *source, *dest; source = SLOT(newlabel); dest = SLOT(ifplabel); mls_copy(source, dest); } static int mls_inpcb_check_deliver(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *p, *i; if (!mls_enabled) return (0); p = SLOT(mlabel); i = SLOT(inplabel); return (mls_equal_effective(p, i) ? 0 : EACCES); } static int mls_inpcb_check_visible(struct ucred *cred, struct inpcb *inp, struct label *inplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(inplabel); if (!mls_dominate_effective(subj, obj)) return (ENOENT); return (0); } static void mls_inpcb_create(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { struct mac_mls *source, *dest; source = SLOT(solabel); dest = SLOT(inplabel); mls_copy_effective(source, dest); } static void mls_inpcb_create_mbuf(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *source, *dest; source = SLOT(inplabel); dest = SLOT(mlabel); mls_copy_effective(source, dest); } static void mls_inpcb_sosetlabel(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { struct mac_mls *source, *dest; SOCK_LOCK_ASSERT(so); source = SLOT(solabel); dest = SLOT(inplabel); mls_copy(source, dest); } static void mls_ip6q_create(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { struct mac_mls *source, *dest; source = SLOT(mlabel); dest = SLOT(q6label); mls_copy_effective(source, dest); } static int mls_ip6q_match(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { struct mac_mls *a, *b; a = SLOT(q6label); b = SLOT(mlabel); return (mls_equal_effective(a, b)); } static void mls_ip6q_reassemble(struct ip6q *q6, struct label *q6label, struct mbuf *m, struct label *mlabel) { struct mac_mls *source, *dest; source = SLOT(q6label); dest = SLOT(mlabel); /* Just use the head, since we require them all to match. */ mls_copy_effective(source, dest); } static void mls_ip6q_update(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { /* NOOP: we only accept matching labels, so no need to update */ } static void mls_ipq_create(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { struct mac_mls *source, *dest; source = SLOT(mlabel); dest = SLOT(qlabel); mls_copy_effective(source, dest); } static int mls_ipq_match(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { struct mac_mls *a, *b; a = SLOT(qlabel); b = SLOT(mlabel); return (mls_equal_effective(a, b)); } static void mls_ipq_reassemble(struct ipq *q, struct label *qlabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *source, *dest; source = SLOT(qlabel); dest = SLOT(mlabel); /* Just use the head, since we require them all to match. */ mls_copy_effective(source, dest); } static void mls_ipq_update(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { /* NOOP: we only accept matching labels, so no need to update */ } static int mls_mount_check_stat(struct ucred *cred, struct mount *mp, struct label *mntlabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(mntlabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static void mls_mount_create(struct ucred *cred, struct mount *mp, struct label *mplabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(mplabel); mls_copy_effective(source, dest); } static void mls_netinet_arp_send(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *dest; dest = SLOT(mlabel); mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); } static void mls_netinet_firewall_reply(struct mbuf *mrecv, struct label *mrecvlabel, struct mbuf *msend, struct label *msendlabel) { struct mac_mls *source, *dest; source = SLOT(mrecvlabel); dest = SLOT(msendlabel); mls_copy_effective(source, dest); } static void mls_netinet_firewall_send(struct mbuf *m, struct label *mlabel) { struct mac_mls *dest; dest = SLOT(mlabel); /* XXX: where is the label for the firewall really coming from? */ mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); } static void mls_netinet_fragment(struct mbuf *m, struct label *mlabel, struct mbuf *frag, struct label *fraglabel) { struct mac_mls *source, *dest; source = SLOT(mlabel); dest = SLOT(fraglabel); mls_copy_effective(source, dest); } static void mls_netinet_icmp_reply(struct mbuf *mrecv, struct label *mrecvlabel, struct mbuf *msend, struct label *msendlabel) { struct mac_mls *source, *dest; source = SLOT(mrecvlabel); dest = SLOT(msendlabel); mls_copy_effective(source, dest); } static void mls_netinet_igmp_send(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *dest; dest = SLOT(mlabel); mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); } static void mls_netinet6_nd6_send(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *dest; dest = SLOT(mlabel); mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); } static int mls_pipe_check_ioctl(struct ucred *cred, struct pipepair *pp, struct label *pplabel, unsigned long cmd, void /* caddr_t */ *data) { if (!mls_enabled) return (0); /* XXX: This will be implemented soon... */ return (0); } static int mls_pipe_check_poll(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_pipe_check_read(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_pipe_check_relabel(struct ucred *cred, struct pipepair *pp, struct label *pplabel, struct label *newlabel) { struct mac_mls *subj, *obj, *new; int error; new = SLOT(newlabel); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); /* * If there is an MLS label update for a pipe, it must be a effective * update. */ error = mls_atmostflags(new, MAC_MLS_FLAG_EFFECTIVE); if (error) return (error); /* * To perform a relabel of a pipe (MLS label or not), MLS must * authorize the relabel. */ if (!mls_effective_in_range(obj, subj)) return (EPERM); /* * If the MLS label is to be changed, authorize as appropriate. */ if (new->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { /* * To change the MLS label on a pipe, the new pipe label must * be in the subject range. */ if (!mls_effective_in_range(new, subj)) return (EPERM); /* * To change the MLS label on a pipe to be EQUAL, the subject * must have appropriate privilege. */ if (mls_contains_equal(new)) { error = mls_subject_privileged(subj); if (error) return (error); } } return (0); } static int mls_pipe_check_stat(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_pipe_check_write(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static void mls_pipe_create(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(pplabel); mls_copy_effective(source, dest); } static void mls_pipe_relabel(struct ucred *cred, struct pipepair *pp, struct label *pplabel, struct label *newlabel) { struct mac_mls *source, *dest; source = SLOT(newlabel); dest = SLOT(pplabel); mls_copy(source, dest); } static int mls_posixsem_check_openunlink(struct ucred *cred, struct ksem *ks, struct label *kslabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(kslabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_posixsem_check_rdonly(struct ucred *active_cred, struct ucred *file_cred, struct ksem *ks, struct label *kslabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(kslabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_posixsem_check_setmode(struct ucred *cred, struct ksem *ks, struct label *shmlabel, mode_t mode) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_posixsem_check_setowner(struct ucred *cred, struct ksem *ks, struct label *shmlabel, uid_t uid, gid_t gid) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_posixsem_check_write(struct ucred *active_cred, struct ucred *file_cred, struct ksem *ks, struct label *kslabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(kslabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static void mls_posixsem_create(struct ucred *cred, struct ksem *ks, struct label *kslabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(kslabel); mls_copy_effective(source, dest); } static int mls_posixshm_check_mmap(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, int prot, int flags) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) { if (!mls_dominate_effective(subj, obj)) return (EACCES); } if (((prot & VM_PROT_WRITE) != 0) && ((flags & MAP_SHARED) != 0)) { if (!mls_dominate_effective(obj, subj)) return (EACCES); } return (0); } static int mls_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, accmode_t accmode) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (accmode & (VREAD | VEXEC | VSTAT_PERMS)) { if (!mls_dominate_effective(subj, obj)) return (EACCES); } if (accmode & VMODIFY_PERMS) { if (!mls_dominate_effective(obj, subj)) return (EACCES); } return (0); } static int mls_posixshm_check_read(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *shm, struct label *shmlabel) { struct mac_mls *subj, *obj; if (!mls_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(shmlabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, mode_t mode) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_posixshm_check_setowner(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, uid_t uid, gid_t gid) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_posixshm_check_stat(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *shmfd, struct label *shmlabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(shmlabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_posixshm_check_truncate(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *shmfd, struct label *shmlabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(shmlabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_posixshm_check_unlink(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_posixshm_check_write(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *shm, struct label *shmlabel) { struct mac_mls *subj, *obj; if (!mls_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(shmlabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static void mls_posixshm_create(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(shmlabel); mls_copy_effective(source, dest); } static int mls_proc_check_debug(struct ucred *cred, struct proc *p) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(p->p_ucred->cr_label); /* XXX: range checks */ if (!mls_dominate_effective(subj, obj)) return (ESRCH); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_proc_check_sched(struct ucred *cred, struct proc *p) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(p->p_ucred->cr_label); /* XXX: range checks */ if (!mls_dominate_effective(subj, obj)) return (ESRCH); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_proc_check_signal(struct ucred *cred, struct proc *p, int signum) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(p->p_ucred->cr_label); /* XXX: range checks */ if (!mls_dominate_effective(subj, obj)) return (ESRCH); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_socket_check_deliver(struct socket *so, struct label *solabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *p, *s; int error; if (!mls_enabled) return (0); p = SLOT(mlabel); s = SLOT(solabel); SOCK_LOCK(so); error = mls_equal_effective(p, s) ? 0 : EACCES; SOCK_UNLOCK(so); return (error); } static int mls_socket_check_relabel(struct ucred *cred, struct socket *so, struct label *solabel, struct label *newlabel) { struct mac_mls *subj, *obj, *new; int error; SOCK_LOCK_ASSERT(so); new = SLOT(newlabel); subj = SLOT(cred->cr_label); obj = SLOT(solabel); /* * If there is an MLS label update for the socket, it may be an * update of effective. */ error = mls_atmostflags(new, MAC_MLS_FLAG_EFFECTIVE); if (error) return (error); /* * To relabel a socket, the old socket effective must be in the * subject range. */ if (!mls_effective_in_range(obj, subj)) return (EPERM); /* * If the MLS label is to be changed, authorize as appropriate. */ if (new->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { /* * To relabel a socket, the new socket effective must be in * the subject range. */ if (!mls_effective_in_range(new, subj)) return (EPERM); /* * To change the MLS label on the socket to contain EQUAL, * the subject must have appropriate privilege. */ if (mls_contains_equal(new)) { error = mls_subject_privileged(subj); if (error) return (error); } } return (0); } static int mls_socket_check_visible(struct ucred *cred, struct socket *so, struct label *solabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(solabel); SOCK_LOCK(so); if (!mls_dominate_effective(subj, obj)) { SOCK_UNLOCK(so); return (ENOENT); } SOCK_UNLOCK(so); return (0); } static void mls_socket_create(struct ucred *cred, struct socket *so, struct label *solabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(solabel); mls_copy_effective(source, dest); } static void mls_socket_create_mbuf(struct socket *so, struct label *solabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *source, *dest; source = SLOT(solabel); dest = SLOT(mlabel); SOCK_LOCK(so); mls_copy_effective(source, dest); SOCK_UNLOCK(so); } static void mls_socket_newconn(struct socket *oldso, struct label *oldsolabel, struct socket *newso, struct label *newsolabel) { struct mac_mls source, *dest; SOCK_LOCK(oldso); source = *SLOT(oldsolabel); SOCK_UNLOCK(oldso); dest = SLOT(newsolabel); SOCK_LOCK(newso); mls_copy_effective(&source, dest); SOCK_UNLOCK(newso); } static void mls_socket_relabel(struct ucred *cred, struct socket *so, struct label *solabel, struct label *newlabel) { struct mac_mls *source, *dest; SOCK_LOCK_ASSERT(so); source = SLOT(newlabel); dest = SLOT(solabel); mls_copy(source, dest); } static void mls_socketpeer_set_from_mbuf(struct mbuf *m, struct label *mlabel, struct socket *so, struct label *sopeerlabel) { struct mac_mls *source, *dest; source = SLOT(mlabel); dest = SLOT(sopeerlabel); SOCK_LOCK(so); mls_copy_effective(source, dest); SOCK_UNLOCK(so); } static void mls_socketpeer_set_from_socket(struct socket *oldso, struct label *oldsolabel, struct socket *newso, struct label *newsopeerlabel) { struct mac_mls source, *dest; SOCK_LOCK(oldso); source = *SLOT(oldsolabel); SOCK_UNLOCK(oldso); dest = SLOT(newsopeerlabel); SOCK_LOCK(newso); mls_copy_effective(&source, dest); SOCK_UNLOCK(newso); } static void mls_syncache_create(struct label *label, struct inpcb *inp) { struct mac_mls *source, *dest; source = SLOT(inp->inp_label); dest = SLOT(label); mls_copy_effective(source, dest); } static void mls_syncache_create_mbuf(struct label *sc_label, struct mbuf *m, struct label *mlabel) { struct mac_mls *source, *dest; source = SLOT(sc_label); dest = SLOT(mlabel); mls_copy_effective(source, dest); } static int mls_system_check_acct(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); if (vplabel == NULL) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj) || !mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_system_check_auditctl(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj) || !mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_system_check_swapon(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj) || !mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static void mls_sysvmsg_cleanup(struct label *msglabel) { bzero(SLOT(msglabel), sizeof(struct mac_mls)); } static void mls_sysvmsg_create(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqlabel, struct msg *msgptr, struct label *msglabel) { struct mac_mls *source, *dest; /* Ignore the msgq label. */ source = SLOT(cred->cr_label); dest = SLOT(msglabel); mls_copy_effective(source, dest); } static int mls_sysvmsq_check_msgrcv(struct ucred *cred, struct msg *msgptr, struct label *msglabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msglabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_sysvmsq_check_msgrmid(struct ucred *cred, struct msg *msgptr, struct label *msglabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msglabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_sysvmsq_check_msqget(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_sysvmsq_check_msqsnd(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_sysvmsq_check_msqrcv(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_sysvmsq_check_msqctl(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel, int cmd) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); switch(cmd) { case IPC_RMID: case IPC_SET: if (!mls_dominate_effective(obj, subj)) return (EACCES); break; case IPC_STAT: if (!mls_dominate_effective(subj, obj)) return (EACCES); break; default: return (EACCES); } return (0); } static void mls_sysvmsq_cleanup(struct label *msqlabel) { bzero(SLOT(msqlabel), sizeof(struct mac_mls)); } static void mls_sysvmsq_create(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqlabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(msqlabel); mls_copy_effective(source, dest); } static int mls_sysvsem_check_semctl(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel, int cmd) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(semaklabel); switch(cmd) { case IPC_RMID: case IPC_SET: case SETVAL: case SETALL: if (!mls_dominate_effective(obj, subj)) return (EACCES); break; case IPC_STAT: case GETVAL: case GETPID: case GETNCNT: case GETZCNT: case GETALL: if (!mls_dominate_effective(subj, obj)) return (EACCES); break; default: return (EACCES); } return (0); } static int mls_sysvsem_check_semget(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(semaklabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_sysvsem_check_semop(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel, size_t accesstype) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(semaklabel); if( accesstype & SEM_R ) if (!mls_dominate_effective(subj, obj)) return (EACCES); if( accesstype & SEM_A ) if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static void mls_sysvsem_cleanup(struct label *semalabel) { bzero(SLOT(semalabel), sizeof(struct mac_mls)); } static void mls_sysvsem_create(struct ucred *cred, struct semid_kernel *semakptr, struct label *semalabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(semalabel); mls_copy_effective(source, dest); } static int mls_sysvshm_check_shmat(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int shmflg) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmseglabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); if ((shmflg & SHM_RDONLY) == 0) { if (!mls_dominate_effective(obj, subj)) return (EACCES); } return (0); } static int mls_sysvshm_check_shmctl(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int cmd) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmseglabel); switch(cmd) { case IPC_RMID: case IPC_SET: if (!mls_dominate_effective(obj, subj)) return (EACCES); break; case IPC_STAT: case SHM_STAT: if (!mls_dominate_effective(subj, obj)) return (EACCES); break; default: return (EACCES); } return (0); } static int mls_sysvshm_check_shmget(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int shmflg) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmseglabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static void mls_sysvshm_cleanup(struct label *shmlabel) { bzero(SLOT(shmlabel), sizeof(struct mac_mls)); } static void mls_sysvshm_create(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmlabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(shmlabel); mls_copy_effective(source, dest); } static int mls_vnode_associate_extattr(struct mount *mp, struct label *mplabel, struct vnode *vp, struct label *vplabel) { struct mac_mls mm_temp, *source, *dest; int buflen, error; source = SLOT(mplabel); dest = SLOT(vplabel); buflen = sizeof(mm_temp); bzero(&mm_temp, buflen); error = vn_extattr_get(vp, IO_NODELOCKED, MAC_MLS_EXTATTR_NAMESPACE, MAC_MLS_EXTATTR_NAME, &buflen, (char *) &mm_temp, curthread); if (error == ENOATTR || error == EOPNOTSUPP) { /* Fall back to the mntlabel. */ mls_copy_effective(source, dest); return (0); } else if (error) return (error); if (buflen != sizeof(mm_temp)) { printf("mls_vnode_associate_extattr: bad size %d\n", buflen); return (EPERM); } if (mls_valid(&mm_temp) != 0) { printf("mls_vnode_associate_extattr: invalid\n"); return (EPERM); } if ((mm_temp.mm_flags & MAC_MLS_FLAGS_BOTH) != MAC_MLS_FLAG_EFFECTIVE) { printf("mls_associated_vnode_extattr: not effective\n"); return (EPERM); } mls_copy_effective(&mm_temp, dest); return (0); } static void mls_vnode_associate_singlelabel(struct mount *mp, struct label *mplabel, struct vnode *vp, struct label *vplabel) { struct mac_mls *source, *dest; source = SLOT(mplabel); dest = SLOT(vplabel); mls_copy_effective(source, dest); } static int mls_vnode_check_chdir(struct ucred *cred, struct vnode *dvp, struct label *dvplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_chroot(struct ucred *cred, struct vnode *dvp, struct label *dvplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_create(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct componentname *cnp, struct vattr *vap) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_deleteacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_deleteextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_exec(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct image_params *imgp, struct label *execlabel) { struct mac_mls *subj, *obj, *exec; int error; if (execlabel != NULL) { /* * We currently don't permit labels to be changed at * exec-time as part of MLS, so disallow non-NULL MLS label * elements in the execlabel. */ exec = SLOT(execlabel); error = mls_atmostflags(exec, 0); if (error) return (error); } if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_getacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_getextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_link(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_listextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_lookup(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct componentname *cnp) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_mmap(struct ucred *cred, struct vnode *vp, struct label *vplabel, int prot, int flags) { struct mac_mls *subj, *obj; /* * Rely on the use of open()-time protections to handle * non-revocation cases. */ if (!mls_enabled || !revocation_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) { if (!mls_dominate_effective(subj, obj)) return (EACCES); } if (((prot & VM_PROT_WRITE) != 0) && ((flags & MAP_SHARED) != 0)) { if (!mls_dominate_effective(obj, subj)) return (EACCES); } return (0); } static int mls_vnode_check_open(struct ucred *cred, struct vnode *vp, struct label *vplabel, accmode_t accmode) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); /* XXX privilege override for admin? */ if (accmode & (VREAD | VEXEC | VSTAT_PERMS)) { if (!mls_dominate_effective(subj, obj)) return (EACCES); } if (accmode & VMODIFY_PERMS) { if (!mls_dominate_effective(obj, subj)) return (EACCES); } return (0); } static int mls_vnode_check_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_mls *subj, *obj; if (!mls_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_mls *subj, *obj; if (!mls_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_readdir(struct ucred *cred, struct vnode *dvp, struct label *dvplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_readlink(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_relabel(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *newlabel) { struct mac_mls *old, *new, *subj; int error; old = SLOT(vplabel); new = SLOT(newlabel); subj = SLOT(cred->cr_label); /* * If there is an MLS label update for the vnode, it must be a * effective label. */ error = mls_atmostflags(new, MAC_MLS_FLAG_EFFECTIVE); if (error) return (error); /* * To perform a relabel of the vnode (MLS label or not), MLS must * authorize the relabel. */ if (!mls_effective_in_range(old, subj)) return (EPERM); /* * If the MLS label is to be changed, authorize as appropriate. */ if (new->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { /* * To change the MLS label on a vnode, the new vnode label * must be in the subject range. */ if (!mls_effective_in_range(new, subj)) return (EPERM); /* * To change the MLS label on the vnode to be EQUAL, the * subject must have appropriate privilege. */ if (mls_contains_equal(new)) { error = mls_subject_privileged(subj); if (error) return (error); } } return (0); } static int mls_vnode_check_rename_from(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, int samedir, struct componentname *cnp) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); if (vp != NULL) { obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); } return (0); } static int mls_vnode_check_revoke(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_setacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type, struct acl *acl) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_setextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); /* XXX: protect the MAC EA in a special way? */ return (0); } static int mls_vnode_check_setflags(struct ucred *cred, struct vnode *vp, struct label *vplabel, u_long flags) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_setmode(struct ucred *cred, struct vnode *vp, struct label *vplabel, mode_t mode) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_setowner(struct ucred *cred, struct vnode *vp, struct label *vplabel, uid_t uid, gid_t gid) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_setutimes(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct timespec atime, struct timespec mtime) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_unlink(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_mls *subj, *obj; if (!mls_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_create_extattr(struct ucred *cred, struct mount *mp, struct label *mplabel, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_mls *source, *dest, mm_temp; size_t buflen; int error; buflen = sizeof(mm_temp); bzero(&mm_temp, buflen); source = SLOT(cred->cr_label); dest = SLOT(vplabel); mls_copy_effective(source, &mm_temp); error = vn_extattr_set(vp, IO_NODELOCKED, MAC_MLS_EXTATTR_NAMESPACE, MAC_MLS_EXTATTR_NAME, buflen, (char *) &mm_temp, curthread); if (error == 0) mls_copy_effective(source, dest); return (error); } static void mls_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *label) { struct mac_mls *source, *dest; source = SLOT(label); dest = SLOT(vplabel); mls_copy(source, dest); } static int mls_vnode_setlabel_extattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *intlabel) { struct mac_mls *source, mm_temp; size_t buflen; int error; buflen = sizeof(mm_temp); bzero(&mm_temp, buflen); source = SLOT(intlabel); if ((source->mm_flags & MAC_MLS_FLAG_EFFECTIVE) == 0) return (0); mls_copy_effective(source, &mm_temp); error = vn_extattr_set(vp, IO_NODELOCKED, MAC_MLS_EXTATTR_NAMESPACE, MAC_MLS_EXTATTR_NAME, buflen, (char *) &mm_temp, curthread); return (error); } static struct mac_policy_ops mls_ops = { .mpo_init = mls_init, .mpo_bpfdesc_check_receive = mls_bpfdesc_check_receive, .mpo_bpfdesc_create = mls_bpfdesc_create, .mpo_bpfdesc_create_mbuf = mls_bpfdesc_create_mbuf, .mpo_bpfdesc_destroy_label = mls_destroy_label, .mpo_bpfdesc_init_label = mls_init_label, .mpo_cred_associate_nfsd = mls_cred_associate_nfsd, .mpo_cred_check_relabel = mls_cred_check_relabel, .mpo_cred_check_visible = mls_cred_check_visible, .mpo_cred_copy_label = mls_copy_label, .mpo_cred_create_init = mls_cred_create_init, .mpo_cred_create_swapper = mls_cred_create_swapper, .mpo_cred_destroy_label = mls_destroy_label, .mpo_cred_externalize_label = mls_externalize_label, .mpo_cred_init_label = mls_init_label, .mpo_cred_internalize_label = mls_internalize_label, .mpo_cred_relabel = mls_cred_relabel, .mpo_devfs_create_device = mls_devfs_create_device, .mpo_devfs_create_directory = mls_devfs_create_directory, .mpo_devfs_create_symlink = mls_devfs_create_symlink, .mpo_devfs_destroy_label = mls_destroy_label, .mpo_devfs_init_label = mls_init_label, .mpo_devfs_update = mls_devfs_update, .mpo_devfs_vnode_associate = mls_devfs_vnode_associate, .mpo_ifnet_check_relabel = mls_ifnet_check_relabel, .mpo_ifnet_check_transmit = mls_ifnet_check_transmit, .mpo_ifnet_copy_label = mls_copy_label, .mpo_ifnet_create = mls_ifnet_create, .mpo_ifnet_create_mbuf = mls_ifnet_create_mbuf, .mpo_ifnet_destroy_label = mls_destroy_label, .mpo_ifnet_externalize_label = mls_externalize_label, .mpo_ifnet_init_label = mls_init_label, .mpo_ifnet_internalize_label = mls_internalize_label, .mpo_ifnet_relabel = mls_ifnet_relabel, .mpo_inpcb_check_deliver = mls_inpcb_check_deliver, .mpo_inpcb_check_visible = mls_inpcb_check_visible, .mpo_inpcb_create = mls_inpcb_create, .mpo_inpcb_create_mbuf = mls_inpcb_create_mbuf, .mpo_inpcb_destroy_label = mls_destroy_label, .mpo_inpcb_init_label = mls_init_label_waitcheck, .mpo_inpcb_sosetlabel = mls_inpcb_sosetlabel, .mpo_ip6q_create = mls_ip6q_create, .mpo_ip6q_destroy_label = mls_destroy_label, .mpo_ip6q_init_label = mls_init_label_waitcheck, .mpo_ip6q_match = mls_ip6q_match, .mpo_ip6q_reassemble = mls_ip6q_reassemble, .mpo_ip6q_update = mls_ip6q_update, .mpo_ipq_create = mls_ipq_create, .mpo_ipq_destroy_label = mls_destroy_label, .mpo_ipq_init_label = mls_init_label_waitcheck, .mpo_ipq_match = mls_ipq_match, .mpo_ipq_reassemble = mls_ipq_reassemble, .mpo_ipq_update = mls_ipq_update, .mpo_mbuf_copy_label = mls_copy_label, .mpo_mbuf_destroy_label = mls_destroy_label, .mpo_mbuf_init_label = mls_init_label_waitcheck, .mpo_mount_check_stat = mls_mount_check_stat, .mpo_mount_create = mls_mount_create, .mpo_mount_destroy_label = mls_destroy_label, .mpo_mount_init_label = mls_init_label, .mpo_netinet_arp_send = mls_netinet_arp_send, .mpo_netinet_firewall_reply = mls_netinet_firewall_reply, .mpo_netinet_firewall_send = mls_netinet_firewall_send, .mpo_netinet_fragment = mls_netinet_fragment, .mpo_netinet_icmp_reply = mls_netinet_icmp_reply, .mpo_netinet_igmp_send = mls_netinet_igmp_send, .mpo_netinet6_nd6_send = mls_netinet6_nd6_send, .mpo_pipe_check_ioctl = mls_pipe_check_ioctl, .mpo_pipe_check_poll = mls_pipe_check_poll, .mpo_pipe_check_read = mls_pipe_check_read, .mpo_pipe_check_relabel = mls_pipe_check_relabel, .mpo_pipe_check_stat = mls_pipe_check_stat, .mpo_pipe_check_write = mls_pipe_check_write, .mpo_pipe_copy_label = mls_copy_label, .mpo_pipe_create = mls_pipe_create, .mpo_pipe_destroy_label = mls_destroy_label, .mpo_pipe_externalize_label = mls_externalize_label, .mpo_pipe_init_label = mls_init_label, .mpo_pipe_internalize_label = mls_internalize_label, .mpo_pipe_relabel = mls_pipe_relabel, .mpo_posixsem_check_getvalue = mls_posixsem_check_rdonly, .mpo_posixsem_check_open = mls_posixsem_check_openunlink, .mpo_posixsem_check_post = mls_posixsem_check_write, .mpo_posixsem_check_setmode = mls_posixsem_check_setmode, .mpo_posixsem_check_setowner = mls_posixsem_check_setowner, .mpo_posixsem_check_stat = mls_posixsem_check_rdonly, .mpo_posixsem_check_unlink = mls_posixsem_check_openunlink, .mpo_posixsem_check_wait = mls_posixsem_check_write, .mpo_posixsem_create = mls_posixsem_create, .mpo_posixsem_destroy_label = mls_destroy_label, .mpo_posixsem_init_label = mls_init_label, .mpo_posixshm_check_mmap = mls_posixshm_check_mmap, .mpo_posixshm_check_open = mls_posixshm_check_open, .mpo_posixshm_check_read = mls_posixshm_check_read, .mpo_posixshm_check_setmode = mls_posixshm_check_setmode, .mpo_posixshm_check_setowner = mls_posixshm_check_setowner, .mpo_posixshm_check_stat = mls_posixshm_check_stat, .mpo_posixshm_check_truncate = mls_posixshm_check_truncate, .mpo_posixshm_check_unlink = mls_posixshm_check_unlink, .mpo_posixshm_check_write = mls_posixshm_check_write, .mpo_posixshm_create = mls_posixshm_create, .mpo_posixshm_destroy_label = mls_destroy_label, .mpo_posixshm_init_label = mls_init_label, .mpo_proc_check_debug = mls_proc_check_debug, .mpo_proc_check_sched = mls_proc_check_sched, .mpo_proc_check_signal = mls_proc_check_signal, .mpo_socket_check_deliver = mls_socket_check_deliver, .mpo_socket_check_relabel = mls_socket_check_relabel, .mpo_socket_check_visible = mls_socket_check_visible, .mpo_socket_copy_label = mls_copy_label, .mpo_socket_create = mls_socket_create, .mpo_socket_create_mbuf = mls_socket_create_mbuf, .mpo_socket_destroy_label = mls_destroy_label, .mpo_socket_externalize_label = mls_externalize_label, .mpo_socket_init_label = mls_init_label_waitcheck, .mpo_socket_internalize_label = mls_internalize_label, .mpo_socket_newconn = mls_socket_newconn, .mpo_socket_relabel = mls_socket_relabel, .mpo_socketpeer_destroy_label = mls_destroy_label, .mpo_socketpeer_externalize_label = mls_externalize_label, .mpo_socketpeer_init_label = mls_init_label_waitcheck, .mpo_socketpeer_set_from_mbuf = mls_socketpeer_set_from_mbuf, .mpo_socketpeer_set_from_socket = mls_socketpeer_set_from_socket, .mpo_syncache_create = mls_syncache_create, .mpo_syncache_create_mbuf = mls_syncache_create_mbuf, .mpo_syncache_destroy_label = mls_destroy_label, .mpo_syncache_init_label = mls_init_label_waitcheck, .mpo_sysvmsg_cleanup = mls_sysvmsg_cleanup, .mpo_sysvmsg_create = mls_sysvmsg_create, .mpo_sysvmsg_destroy_label = mls_destroy_label, .mpo_sysvmsg_init_label = mls_init_label, .mpo_sysvmsq_check_msgrcv = mls_sysvmsq_check_msgrcv, .mpo_sysvmsq_check_msgrmid = mls_sysvmsq_check_msgrmid, .mpo_sysvmsq_check_msqget = mls_sysvmsq_check_msqget, .mpo_sysvmsq_check_msqsnd = mls_sysvmsq_check_msqsnd, .mpo_sysvmsq_check_msqrcv = mls_sysvmsq_check_msqrcv, .mpo_sysvmsq_check_msqctl = mls_sysvmsq_check_msqctl, .mpo_sysvmsq_cleanup = mls_sysvmsq_cleanup, .mpo_sysvmsq_destroy_label = mls_destroy_label, .mpo_sysvmsq_init_label = mls_init_label, .mpo_sysvmsq_create = mls_sysvmsq_create, .mpo_sysvsem_check_semctl = mls_sysvsem_check_semctl, .mpo_sysvsem_check_semget = mls_sysvsem_check_semget, .mpo_sysvsem_check_semop = mls_sysvsem_check_semop, .mpo_sysvsem_cleanup = mls_sysvsem_cleanup, .mpo_sysvsem_create = mls_sysvsem_create, .mpo_sysvsem_destroy_label = mls_destroy_label, .mpo_sysvsem_init_label = mls_init_label, .mpo_sysvshm_check_shmat = mls_sysvshm_check_shmat, .mpo_sysvshm_check_shmctl = mls_sysvshm_check_shmctl, .mpo_sysvshm_check_shmget = mls_sysvshm_check_shmget, .mpo_sysvshm_cleanup = mls_sysvshm_cleanup, .mpo_sysvshm_create = mls_sysvshm_create, .mpo_sysvshm_destroy_label = mls_destroy_label, .mpo_sysvshm_init_label = mls_init_label, .mpo_system_check_acct = mls_system_check_acct, .mpo_system_check_auditctl = mls_system_check_auditctl, .mpo_system_check_swapon = mls_system_check_swapon, .mpo_vnode_associate_extattr = mls_vnode_associate_extattr, .mpo_vnode_associate_singlelabel = mls_vnode_associate_singlelabel, .mpo_vnode_check_access = mls_vnode_check_open, .mpo_vnode_check_chdir = mls_vnode_check_chdir, .mpo_vnode_check_chroot = mls_vnode_check_chroot, .mpo_vnode_check_create = mls_vnode_check_create, .mpo_vnode_check_deleteacl = mls_vnode_check_deleteacl, .mpo_vnode_check_deleteextattr = mls_vnode_check_deleteextattr, .mpo_vnode_check_exec = mls_vnode_check_exec, .mpo_vnode_check_getacl = mls_vnode_check_getacl, .mpo_vnode_check_getextattr = mls_vnode_check_getextattr, .mpo_vnode_check_link = mls_vnode_check_link, .mpo_vnode_check_listextattr = mls_vnode_check_listextattr, .mpo_vnode_check_lookup = mls_vnode_check_lookup, .mpo_vnode_check_mmap = mls_vnode_check_mmap, .mpo_vnode_check_open = mls_vnode_check_open, .mpo_vnode_check_poll = mls_vnode_check_poll, .mpo_vnode_check_read = mls_vnode_check_read, .mpo_vnode_check_readdir = mls_vnode_check_readdir, .mpo_vnode_check_readlink = mls_vnode_check_readlink, .mpo_vnode_check_relabel = mls_vnode_check_relabel, .mpo_vnode_check_rename_from = mls_vnode_check_rename_from, .mpo_vnode_check_rename_to = mls_vnode_check_rename_to, .mpo_vnode_check_revoke = mls_vnode_check_revoke, .mpo_vnode_check_setacl = mls_vnode_check_setacl, .mpo_vnode_check_setextattr = mls_vnode_check_setextattr, .mpo_vnode_check_setflags = mls_vnode_check_setflags, .mpo_vnode_check_setmode = mls_vnode_check_setmode, .mpo_vnode_check_setowner = mls_vnode_check_setowner, .mpo_vnode_check_setutimes = mls_vnode_check_setutimes, .mpo_vnode_check_stat = mls_vnode_check_stat, .mpo_vnode_check_unlink = mls_vnode_check_unlink, .mpo_vnode_check_write = mls_vnode_check_write, .mpo_vnode_copy_label = mls_copy_label, .mpo_vnode_create_extattr = mls_vnode_create_extattr, .mpo_vnode_destroy_label = mls_destroy_label, .mpo_vnode_externalize_label = mls_externalize_label, .mpo_vnode_init_label = mls_init_label, .mpo_vnode_internalize_label = mls_internalize_label, .mpo_vnode_relabel = mls_vnode_relabel, .mpo_vnode_setlabel_extattr = mls_vnode_setlabel_extattr, }; MAC_POLICY_SET(&mls_ops, mac_mls, "TrustedBSD MAC/MLS", MPC_LOADTIME_FLAG_NOTLATE, &mls_slot);