// 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 "netport.h"32#include "objsec.h"3334#define SEL_NETPORT_HASH_SIZE 25635#define SEL_NETPORT_HASH_BKT_LIMIT 163637struct sel_netport_bkt {38int size;39struct list_head list;40};4142struct sel_netport {43struct netport_security_struct psec;4445struct list_head list;46struct rcu_head rcu;47};4849static DEFINE_SPINLOCK(sel_netport_lock);50static struct sel_netport_bkt sel_netport_hash[SEL_NETPORT_HASH_SIZE];5152/**53* sel_netport_hashfn - Hashing function for the port table54* @pnum: port number55*56* Description:57* This is the hashing function for the port table, it returns the bucket58* number for the given port.59*60*/61static unsigned int sel_netport_hashfn(u16 pnum)62{63return (pnum & (SEL_NETPORT_HASH_SIZE - 1));64}6566/**67* sel_netport_find - Search for a port record68* @protocol: protocol69* @pnum: port70*71* Description:72* Search the network port table and return the matching record. If an entry73* can not be found in the table return NULL.74*75*/76static struct sel_netport *sel_netport_find(u8 protocol, u16 pnum)77{78unsigned int idx;79struct sel_netport *port;8081idx = sel_netport_hashfn(pnum);82list_for_each_entry_rcu(port, &sel_netport_hash[idx].list, list)83if (port->psec.port == pnum && port->psec.protocol == protocol)84return port;8586return NULL;87}8889/**90* sel_netport_insert - Insert a new port into the table91* @port: the new port record92*93* Description:94* Add a new port record to the network address hash table.95*96*/97static void sel_netport_insert(struct sel_netport *port)98{99unsigned int idx;100101/* we need to impose a limit on the growth of the hash table so check102* this bucket to make sure it is within the specified bounds */103idx = sel_netport_hashfn(port->psec.port);104list_add_rcu(&port->list, &sel_netport_hash[idx].list);105if (sel_netport_hash[idx].size == SEL_NETPORT_HASH_BKT_LIMIT) {106struct sel_netport *tail;107tail = list_entry(108rcu_dereference_protected(109list_tail_rcu(&sel_netport_hash[idx].list),110lockdep_is_held(&sel_netport_lock)),111struct sel_netport, list);112list_del_rcu(&tail->list);113kfree_rcu(tail, rcu);114} else115sel_netport_hash[idx].size++;116}117118/**119* sel_netport_sid_slow - Lookup the SID of a network address using the policy120* @protocol: protocol121* @pnum: port122* @sid: port SID123*124* Description:125* This function determines the SID of a network port by querying the security126* policy. The result is added to the network port table to speedup future127* queries. Returns zero on success, negative values on failure.128*129*/130static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid)131{132int ret;133struct sel_netport *port;134struct sel_netport *new;135136spin_lock_bh(&sel_netport_lock);137port = sel_netport_find(protocol, pnum);138if (port != NULL) {139*sid = port->psec.sid;140spin_unlock_bh(&sel_netport_lock);141return 0;142}143144ret = security_port_sid(protocol, pnum, sid);145if (ret != 0)146goto out;147148/* If this memory allocation fails still return 0. The SID149* is valid, it just won't be added to the cache.150*/151new = kmalloc(sizeof(*new), GFP_ATOMIC);152if (new) {153new->psec.port = pnum;154new->psec.protocol = protocol;155new->psec.sid = *sid;156sel_netport_insert(new);157}158159out:160spin_unlock_bh(&sel_netport_lock);161if (unlikely(ret))162pr_warn("SELinux: failure in %s(), unable to determine network port label\n",163__func__);164return ret;165}166167/**168* sel_netport_sid - Lookup the SID of a network port169* @protocol: protocol170* @pnum: port171* @sid: port SID172*173* Description:174* This function determines the SID of a network port using the fastest method175* possible. First the port table is queried, but if an entry can't be found176* then the policy is queried and the result is added to the table to speedup177* future queries. Returns zero on success, negative values on failure.178*179*/180int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid)181{182struct sel_netport *port;183184rcu_read_lock();185port = sel_netport_find(protocol, pnum);186if (likely(port != NULL)) {187*sid = port->psec.sid;188rcu_read_unlock();189return 0;190}191rcu_read_unlock();192193return sel_netport_sid_slow(protocol, pnum, sid);194}195196/**197* sel_netport_flush - Flush the entire network port table198*199* Description:200* Remove all entries from the network address table.201*202*/203void sel_netport_flush(void)204{205unsigned int idx;206struct sel_netport *port, *port_tmp;207208spin_lock_bh(&sel_netport_lock);209for (idx = 0; idx < SEL_NETPORT_HASH_SIZE; idx++) {210list_for_each_entry_safe(port, port_tmp,211&sel_netport_hash[idx].list, list) {212list_del_rcu(&port->list);213kfree_rcu(port, rcu);214}215sel_netport_hash[idx].size = 0;216}217spin_unlock_bh(&sel_netport_lock);218}219220static __init int sel_netport_init(void)221{222int iter;223224if (!selinux_enabled_boot)225return 0;226227for (iter = 0; iter < SEL_NETPORT_HASH_SIZE; iter++) {228INIT_LIST_HEAD(&sel_netport_hash[iter].list);229sel_netport_hash[iter].size = 0;230}231232return 0;233}234235__initcall(sel_netport_init);236237238