// SPDX-License-Identifier: GPL-2.0-only1/*2* Network node table3*4* SELinux must keep a mapping of network nodes to labels/SIDs. This5* mapping is maintained as part of the normal policy but a fast cache is6* needed to reduce the lookup overhead since most of these queries happen on7* a per-packet basis.8*9* Author: Paul Moore <[email protected]>10*11* This code is heavily based on the "netif" concept originally developed by12* James Morris <[email protected]>13* (see security/selinux/netif.c for more information)14*/1516/*17* (c) Copyright Hewlett-Packard Development Company, L.P., 200718*/1920#include <linux/types.h>21#include <linux/rcupdate.h>22#include <linux/list.h>23#include <linux/slab.h>24#include <linux/spinlock.h>25#include <linux/in.h>26#include <linux/in6.h>27#include <linux/ip.h>28#include <linux/ipv6.h>29#include <net/ip.h>30#include <net/ipv6.h>3132#include "initcalls.h"33#include "netnode.h"34#include "objsec.h"3536#define SEL_NETNODE_HASH_SIZE 25637#define SEL_NETNODE_HASH_BKT_LIMIT 163839struct sel_netnode_bkt {40unsigned int size;41struct list_head list;42};4344struct sel_netnode {45struct netnode_security_struct nsec;4647struct list_head list;48struct rcu_head rcu;49};5051/* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason52* for this is that I suspect most users will not make heavy use of both53* address families at the same time so one table will usually end up wasted,54* if this becomes a problem we can always add a hash table for each address55* family later */5657static DEFINE_SPINLOCK(sel_netnode_lock);58static struct sel_netnode_bkt sel_netnode_hash[SEL_NETNODE_HASH_SIZE];5960/**61* sel_netnode_hashfn_ipv4 - IPv4 hashing function for the node table62* @addr: IPv4 address63*64* Description:65* This is the IPv4 hashing function for the node interface table, it returns66* the bucket number for the given IP address.67*68*/69static unsigned int sel_netnode_hashfn_ipv4(__be32 addr)70{71/* at some point we should determine if the mismatch in byte order72* affects the hash function dramatically */73return (addr & (SEL_NETNODE_HASH_SIZE - 1));74}7576/**77* sel_netnode_hashfn_ipv6 - IPv6 hashing function for the node table78* @addr: IPv6 address79*80* Description:81* This is the IPv6 hashing function for the node interface table, it returns82* the bucket number for the given IP address.83*84*/85static unsigned int sel_netnode_hashfn_ipv6(const struct in6_addr *addr)86{87/* just hash the least significant 32 bits to keep things fast (they88* are the most likely to be different anyway), we can revisit this89* later if needed */90return (addr->s6_addr32[3] & (SEL_NETNODE_HASH_SIZE - 1));91}9293/**94* sel_netnode_find - Search for a node record95* @addr: IP address96* @family: address family97*98* Description:99* Search the network node table and return the record matching @addr. If an100* entry can not be found in the table return NULL.101*102*/103static struct sel_netnode *sel_netnode_find(const void *addr, u16 family)104{105unsigned int idx;106struct sel_netnode *node;107108switch (family) {109case PF_INET:110idx = sel_netnode_hashfn_ipv4(*(const __be32 *)addr);111break;112case PF_INET6:113idx = sel_netnode_hashfn_ipv6(addr);114break;115default:116BUG();117return NULL;118}119120list_for_each_entry_rcu(node, &sel_netnode_hash[idx].list, list)121if (node->nsec.family == family)122switch (family) {123case PF_INET:124if (node->nsec.addr.ipv4 == *(const __be32 *)addr)125return node;126break;127case PF_INET6:128if (ipv6_addr_equal(&node->nsec.addr.ipv6,129addr))130return node;131break;132}133134return NULL;135}136137/**138* sel_netnode_insert - Insert a new node into the table139* @node: the new node record140*141* Description:142* Add a new node record to the network address hash table.143*144*/145static void sel_netnode_insert(struct sel_netnode *node)146{147unsigned int idx;148149switch (node->nsec.family) {150case PF_INET:151idx = sel_netnode_hashfn_ipv4(node->nsec.addr.ipv4);152break;153case PF_INET6:154idx = sel_netnode_hashfn_ipv6(&node->nsec.addr.ipv6);155break;156default:157BUG();158return;159}160161/* we need to impose a limit on the growth of the hash table so check162* this bucket to make sure it is within the specified bounds */163list_add_rcu(&node->list, &sel_netnode_hash[idx].list);164if (sel_netnode_hash[idx].size == SEL_NETNODE_HASH_BKT_LIMIT) {165struct sel_netnode *tail;166tail = list_entry(167rcu_dereference_protected(168list_tail_rcu(&sel_netnode_hash[idx].list),169lockdep_is_held(&sel_netnode_lock)),170struct sel_netnode, list);171list_del_rcu(&tail->list);172kfree_rcu(tail, rcu);173} else174sel_netnode_hash[idx].size++;175}176177/**178* sel_netnode_sid_slow - Lookup the SID of a network address using the policy179* @addr: the IP address180* @family: the address family181* @sid: node SID182*183* Description:184* This function determines the SID of a network address by querying the185* security policy. The result is added to the network address table to186* speedup future queries. Returns zero on success, negative values on187* failure.188*189*/190static int sel_netnode_sid_slow(const void *addr, u16 family, u32 *sid)191{192int ret;193struct sel_netnode *node;194struct sel_netnode *new;195196spin_lock_bh(&sel_netnode_lock);197node = sel_netnode_find(addr, family);198if (node != NULL) {199*sid = node->nsec.sid;200spin_unlock_bh(&sel_netnode_lock);201return 0;202}203204/* If this memory allocation fails still return 0. The SID205* is valid, it just won't be added to the cache.206*/207new = kmalloc(sizeof(*new), GFP_ATOMIC);208switch (family) {209case PF_INET:210ret = security_node_sid(PF_INET,211addr, sizeof(struct in_addr), sid);212if (new)213new->nsec.addr.ipv4 = *(const __be32 *)addr;214break;215case PF_INET6:216ret = security_node_sid(PF_INET6,217addr, sizeof(struct in6_addr), sid);218if (new)219new->nsec.addr.ipv6 = *(const struct in6_addr *)addr;220break;221default:222BUG();223ret = -EINVAL;224}225if (ret == 0 && new) {226new->nsec.family = family;227new->nsec.sid = *sid;228sel_netnode_insert(new);229} else230kfree(new);231232spin_unlock_bh(&sel_netnode_lock);233if (unlikely(ret))234pr_warn("SELinux: failure in %s(), unable to determine network node label\n",235__func__);236return ret;237}238239/**240* sel_netnode_sid - Lookup the SID of a network address241* @addr: the IP address242* @family: the address family243* @sid: node SID244*245* Description:246* This function determines the SID of a network address using the fastest247* method possible. First the address table is queried, but if an entry248* can't be found then the policy is queried and the result is added to the249* table to speedup future queries. Returns zero on success, negative values250* on failure.251*252*/253int sel_netnode_sid(const void *addr, u16 family, u32 *sid)254{255struct sel_netnode *node;256257rcu_read_lock();258node = sel_netnode_find(addr, family);259if (likely(node != NULL)) {260*sid = node->nsec.sid;261rcu_read_unlock();262return 0;263}264rcu_read_unlock();265266return sel_netnode_sid_slow(addr, family, sid);267}268269/**270* sel_netnode_flush - Flush the entire network address table271*272* Description:273* Remove all entries from the network address table.274*275*/276void sel_netnode_flush(void)277{278unsigned int idx;279struct sel_netnode *node, *node_tmp;280281spin_lock_bh(&sel_netnode_lock);282for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++) {283list_for_each_entry_safe(node, node_tmp,284&sel_netnode_hash[idx].list, list) {285list_del_rcu(&node->list);286kfree_rcu(node, rcu);287}288sel_netnode_hash[idx].size = 0;289}290spin_unlock_bh(&sel_netnode_lock);291}292293int __init sel_netnode_init(void)294{295int iter;296297if (!selinux_enabled_boot)298return 0;299300for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++) {301INIT_LIST_HEAD(&sel_netnode_hash[iter].list);302sel_netnode_hash[iter].size = 0;303}304305return 0;306}307308309