// SPDX-License-Identifier: GPL-2.0-only1/*2* Network port table3*4* SELinux must keep a mapping of network ports to labels/SIDs. This5* mapping is maintained as part of the normal policy but a fast cache is6* needed to reduce the lookup overhead.7*8* Author: Paul Moore <[email protected]>9*10* This code is heavily based on the "netif" concept originally developed by11* James Morris <[email protected]>12* (see security/selinux/netif.c for more information)13*/1415/*16* (c) Copyright Hewlett-Packard Development Company, L.P., 200817*/1819#include <linux/types.h>20#include <linux/rcupdate.h>21#include <linux/list.h>22#include <linux/slab.h>23#include <linux/spinlock.h>24#include <linux/in.h>25#include <linux/in6.h>26#include <linux/ip.h>27#include <linux/ipv6.h>28#include <net/ip.h>29#include <net/ipv6.h>3031#include "initcalls.h"32#include "netport.h"33#include "objsec.h"3435#define SEL_NETPORT_HASH_SIZE 25636#define SEL_NETPORT_HASH_BKT_LIMIT 163738struct sel_netport_bkt {39int size;40struct list_head list;41};4243struct sel_netport {44struct netport_security_struct psec;4546struct list_head list;47struct rcu_head rcu;48};4950static DEFINE_SPINLOCK(sel_netport_lock);51static struct sel_netport_bkt sel_netport_hash[SEL_NETPORT_HASH_SIZE];5253/**54* sel_netport_hashfn - Hashing function for the port table55* @pnum: port number56*57* Description:58* This is the hashing function for the port table, it returns the bucket59* number for the given port.60*61*/62static unsigned int sel_netport_hashfn(u16 pnum)63{64return (pnum & (SEL_NETPORT_HASH_SIZE - 1));65}6667/**68* sel_netport_find - Search for a port record69* @protocol: protocol70* @pnum: port71*72* Description:73* Search the network port table and return the matching record. If an entry74* can not be found in the table return NULL.75*76*/77static struct sel_netport *sel_netport_find(u8 protocol, u16 pnum)78{79unsigned int idx;80struct sel_netport *port;8182idx = sel_netport_hashfn(pnum);83list_for_each_entry_rcu(port, &sel_netport_hash[idx].list, list)84if (port->psec.port == pnum && port->psec.protocol == protocol)85return port;8687return NULL;88}8990/**91* sel_netport_insert - Insert a new port into the table92* @port: the new port record93*94* Description:95* Add a new port record to the network address hash table.96*97*/98static void sel_netport_insert(struct sel_netport *port)99{100unsigned int idx;101102/* we need to impose a limit on the growth of the hash table so check103* this bucket to make sure it is within the specified bounds */104idx = sel_netport_hashfn(port->psec.port);105list_add_rcu(&port->list, &sel_netport_hash[idx].list);106if (sel_netport_hash[idx].size == SEL_NETPORT_HASH_BKT_LIMIT) {107struct sel_netport *tail;108tail = list_entry(109rcu_dereference_protected(110list_tail_rcu(&sel_netport_hash[idx].list),111lockdep_is_held(&sel_netport_lock)),112struct sel_netport, list);113list_del_rcu(&tail->list);114kfree_rcu(tail, rcu);115} else116sel_netport_hash[idx].size++;117}118119/**120* sel_netport_sid_slow - Lookup the SID of a network address using the policy121* @protocol: protocol122* @pnum: port123* @sid: port SID124*125* Description:126* This function determines the SID of a network port by querying the security127* policy. The result is added to the network port table to speedup future128* queries. Returns zero on success, negative values on failure.129*130*/131static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid)132{133int ret;134struct sel_netport *port;135struct sel_netport *new;136137spin_lock_bh(&sel_netport_lock);138port = sel_netport_find(protocol, pnum);139if (port != NULL) {140*sid = port->psec.sid;141spin_unlock_bh(&sel_netport_lock);142return 0;143}144145ret = security_port_sid(protocol, pnum, sid);146if (ret != 0)147goto out;148149/* If this memory allocation fails still return 0. The SID150* is valid, it just won't be added to the cache.151*/152new = kmalloc(sizeof(*new), GFP_ATOMIC);153if (new) {154new->psec.port = pnum;155new->psec.protocol = protocol;156new->psec.sid = *sid;157sel_netport_insert(new);158}159160out:161spin_unlock_bh(&sel_netport_lock);162if (unlikely(ret))163pr_warn("SELinux: failure in %s(), unable to determine network port label\n",164__func__);165return ret;166}167168/**169* sel_netport_sid - Lookup the SID of a network port170* @protocol: protocol171* @pnum: port172* @sid: port SID173*174* Description:175* This function determines the SID of a network port using the fastest method176* possible. First the port table is queried, but if an entry can't be found177* then the policy is queried and the result is added to the table to speedup178* future queries. Returns zero on success, negative values on failure.179*180*/181int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid)182{183struct sel_netport *port;184185rcu_read_lock();186port = sel_netport_find(protocol, pnum);187if (likely(port != NULL)) {188*sid = port->psec.sid;189rcu_read_unlock();190return 0;191}192rcu_read_unlock();193194return sel_netport_sid_slow(protocol, pnum, sid);195}196197/**198* sel_netport_flush - Flush the entire network port table199*200* Description:201* Remove all entries from the network address table.202*203*/204void sel_netport_flush(void)205{206unsigned int idx;207struct sel_netport *port, *port_tmp;208209spin_lock_bh(&sel_netport_lock);210for (idx = 0; idx < SEL_NETPORT_HASH_SIZE; idx++) {211list_for_each_entry_safe(port, port_tmp,212&sel_netport_hash[idx].list, list) {213list_del_rcu(&port->list);214kfree_rcu(port, rcu);215}216sel_netport_hash[idx].size = 0;217}218spin_unlock_bh(&sel_netport_lock);219}220221int __init sel_netport_init(void)222{223int iter;224225if (!selinux_enabled_boot)226return 0;227228for (iter = 0; iter < SEL_NETPORT_HASH_SIZE; iter++) {229INIT_LIST_HEAD(&sel_netport_hash[iter].list);230sel_netport_hash[iter].size = 0;231}232233return 0;234}235236237