18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Network port table 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * SELinux must keep a mapping of network ports to labels/SIDs. This 68c2ecf20Sopenharmony_ci * mapping is maintained as part of the normal policy but a fast cache is 78c2ecf20Sopenharmony_ci * needed to reduce the lookup overhead. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Author: Paul Moore <paul@paul-moore.com> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This code is heavily based on the "netif" concept originally developed by 128c2ecf20Sopenharmony_ci * James Morris <jmorris@redhat.com> 138c2ecf20Sopenharmony_ci * (see security/selinux/netif.c for more information) 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* 178c2ecf20Sopenharmony_ci * (c) Copyright Hewlett-Packard Development Company, L.P., 2008 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/types.h> 218c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 228c2ecf20Sopenharmony_ci#include <linux/list.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 258c2ecf20Sopenharmony_ci#include <linux/in.h> 268c2ecf20Sopenharmony_ci#include <linux/in6.h> 278c2ecf20Sopenharmony_ci#include <linux/ip.h> 288c2ecf20Sopenharmony_ci#include <linux/ipv6.h> 298c2ecf20Sopenharmony_ci#include <net/ip.h> 308c2ecf20Sopenharmony_ci#include <net/ipv6.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include "netport.h" 338c2ecf20Sopenharmony_ci#include "objsec.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define SEL_NETPORT_HASH_SIZE 256 368c2ecf20Sopenharmony_ci#define SEL_NETPORT_HASH_BKT_LIMIT 16 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct sel_netport_bkt { 398c2ecf20Sopenharmony_ci int size; 408c2ecf20Sopenharmony_ci struct list_head list; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct sel_netport { 448c2ecf20Sopenharmony_ci struct netport_security_struct psec; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci struct list_head list; 478c2ecf20Sopenharmony_ci struct rcu_head rcu; 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason 518c2ecf20Sopenharmony_ci * for this is that I suspect most users will not make heavy use of both 528c2ecf20Sopenharmony_ci * address families at the same time so one table will usually end up wasted, 538c2ecf20Sopenharmony_ci * if this becomes a problem we can always add a hash table for each address 548c2ecf20Sopenharmony_ci * family later */ 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic LIST_HEAD(sel_netport_list); 578c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(sel_netport_lock); 588c2ecf20Sopenharmony_cistatic struct sel_netport_bkt sel_netport_hash[SEL_NETPORT_HASH_SIZE]; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/** 618c2ecf20Sopenharmony_ci * sel_netport_hashfn - Hashing function for the port table 628c2ecf20Sopenharmony_ci * @pnum: port number 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * Description: 658c2ecf20Sopenharmony_ci * This is the hashing function for the port table, it returns the bucket 668c2ecf20Sopenharmony_ci * number for the given port. 678c2ecf20Sopenharmony_ci * 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_cistatic unsigned int sel_netport_hashfn(u16 pnum) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci return (pnum & (SEL_NETPORT_HASH_SIZE - 1)); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/** 758c2ecf20Sopenharmony_ci * sel_netport_find - Search for a port record 768c2ecf20Sopenharmony_ci * @protocol: protocol 778c2ecf20Sopenharmony_ci * @port: pnum 788c2ecf20Sopenharmony_ci * 798c2ecf20Sopenharmony_ci * Description: 808c2ecf20Sopenharmony_ci * Search the network port table and return the matching record. If an entry 818c2ecf20Sopenharmony_ci * can not be found in the table return NULL. 828c2ecf20Sopenharmony_ci * 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_cistatic struct sel_netport *sel_netport_find(u8 protocol, u16 pnum) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci unsigned int idx; 878c2ecf20Sopenharmony_ci struct sel_netport *port; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci idx = sel_netport_hashfn(pnum); 908c2ecf20Sopenharmony_ci list_for_each_entry_rcu(port, &sel_netport_hash[idx].list, list) 918c2ecf20Sopenharmony_ci if (port->psec.port == pnum && port->psec.protocol == protocol) 928c2ecf20Sopenharmony_ci return port; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return NULL; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/** 988c2ecf20Sopenharmony_ci * sel_netport_insert - Insert a new port into the table 998c2ecf20Sopenharmony_ci * @port: the new port record 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * Description: 1028c2ecf20Sopenharmony_ci * Add a new port record to the network address hash table. 1038c2ecf20Sopenharmony_ci * 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_cistatic void sel_netport_insert(struct sel_netport *port) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci unsigned int idx; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* we need to impose a limit on the growth of the hash table so check 1108c2ecf20Sopenharmony_ci * this bucket to make sure it is within the specified bounds */ 1118c2ecf20Sopenharmony_ci idx = sel_netport_hashfn(port->psec.port); 1128c2ecf20Sopenharmony_ci list_add_rcu(&port->list, &sel_netport_hash[idx].list); 1138c2ecf20Sopenharmony_ci if (sel_netport_hash[idx].size == SEL_NETPORT_HASH_BKT_LIMIT) { 1148c2ecf20Sopenharmony_ci struct sel_netport *tail; 1158c2ecf20Sopenharmony_ci tail = list_entry( 1168c2ecf20Sopenharmony_ci rcu_dereference_protected( 1178c2ecf20Sopenharmony_ci sel_netport_hash[idx].list.prev, 1188c2ecf20Sopenharmony_ci lockdep_is_held(&sel_netport_lock)), 1198c2ecf20Sopenharmony_ci struct sel_netport, list); 1208c2ecf20Sopenharmony_ci list_del_rcu(&tail->list); 1218c2ecf20Sopenharmony_ci kfree_rcu(tail, rcu); 1228c2ecf20Sopenharmony_ci } else 1238c2ecf20Sopenharmony_ci sel_netport_hash[idx].size++; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/** 1278c2ecf20Sopenharmony_ci * sel_netport_sid_slow - Lookup the SID of a network address using the policy 1288c2ecf20Sopenharmony_ci * @protocol: protocol 1298c2ecf20Sopenharmony_ci * @pnum: port 1308c2ecf20Sopenharmony_ci * @sid: port SID 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci * Description: 1338c2ecf20Sopenharmony_ci * This function determines the SID of a network port by querying the security 1348c2ecf20Sopenharmony_ci * policy. The result is added to the network port table to speedup future 1358c2ecf20Sopenharmony_ci * queries. Returns zero on success, negative values on failure. 1368c2ecf20Sopenharmony_ci * 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_cistatic int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci int ret; 1418c2ecf20Sopenharmony_ci struct sel_netport *port; 1428c2ecf20Sopenharmony_ci struct sel_netport *new; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci spin_lock_bh(&sel_netport_lock); 1458c2ecf20Sopenharmony_ci port = sel_netport_find(protocol, pnum); 1468c2ecf20Sopenharmony_ci if (port != NULL) { 1478c2ecf20Sopenharmony_ci *sid = port->psec.sid; 1488c2ecf20Sopenharmony_ci spin_unlock_bh(&sel_netport_lock); 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci ret = security_port_sid(&selinux_state, protocol, pnum, sid); 1538c2ecf20Sopenharmony_ci if (ret != 0) 1548c2ecf20Sopenharmony_ci goto out; 1558c2ecf20Sopenharmony_ci new = kzalloc(sizeof(*new), GFP_ATOMIC); 1568c2ecf20Sopenharmony_ci if (new) { 1578c2ecf20Sopenharmony_ci new->psec.port = pnum; 1588c2ecf20Sopenharmony_ci new->psec.protocol = protocol; 1598c2ecf20Sopenharmony_ci new->psec.sid = *sid; 1608c2ecf20Sopenharmony_ci sel_netport_insert(new); 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ciout: 1648c2ecf20Sopenharmony_ci spin_unlock_bh(&sel_netport_lock); 1658c2ecf20Sopenharmony_ci if (unlikely(ret)) 1668c2ecf20Sopenharmony_ci pr_warn("SELinux: failure in %s(), unable to determine network port label\n", 1678c2ecf20Sopenharmony_ci __func__); 1688c2ecf20Sopenharmony_ci return ret; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/** 1728c2ecf20Sopenharmony_ci * sel_netport_sid - Lookup the SID of a network port 1738c2ecf20Sopenharmony_ci * @protocol: protocol 1748c2ecf20Sopenharmony_ci * @pnum: port 1758c2ecf20Sopenharmony_ci * @sid: port SID 1768c2ecf20Sopenharmony_ci * 1778c2ecf20Sopenharmony_ci * Description: 1788c2ecf20Sopenharmony_ci * This function determines the SID of a network port using the fastest method 1798c2ecf20Sopenharmony_ci * possible. First the port table is queried, but if an entry can't be found 1808c2ecf20Sopenharmony_ci * then the policy is queried and the result is added to the table to speedup 1818c2ecf20Sopenharmony_ci * future queries. Returns zero on success, negative values on failure. 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ciint sel_netport_sid(u8 protocol, u16 pnum, u32 *sid) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct sel_netport *port; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci rcu_read_lock(); 1898c2ecf20Sopenharmony_ci port = sel_netport_find(protocol, pnum); 1908c2ecf20Sopenharmony_ci if (port != NULL) { 1918c2ecf20Sopenharmony_ci *sid = port->psec.sid; 1928c2ecf20Sopenharmony_ci rcu_read_unlock(); 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci rcu_read_unlock(); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return sel_netport_sid_slow(protocol, pnum, sid); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci/** 2018c2ecf20Sopenharmony_ci * sel_netport_flush - Flush the entire network port table 2028c2ecf20Sopenharmony_ci * 2038c2ecf20Sopenharmony_ci * Description: 2048c2ecf20Sopenharmony_ci * Remove all entries from the network address table. 2058c2ecf20Sopenharmony_ci * 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_civoid sel_netport_flush(void) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci unsigned int idx; 2108c2ecf20Sopenharmony_ci struct sel_netport *port, *port_tmp; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci spin_lock_bh(&sel_netport_lock); 2138c2ecf20Sopenharmony_ci for (idx = 0; idx < SEL_NETPORT_HASH_SIZE; idx++) { 2148c2ecf20Sopenharmony_ci list_for_each_entry_safe(port, port_tmp, 2158c2ecf20Sopenharmony_ci &sel_netport_hash[idx].list, list) { 2168c2ecf20Sopenharmony_ci list_del_rcu(&port->list); 2178c2ecf20Sopenharmony_ci kfree_rcu(port, rcu); 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci sel_netport_hash[idx].size = 0; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci spin_unlock_bh(&sel_netport_lock); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic __init int sel_netport_init(void) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci int iter; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (!selinux_enabled_boot) 2298c2ecf20Sopenharmony_ci return 0; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci for (iter = 0; iter < SEL_NETPORT_HASH_SIZE; iter++) { 2328c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sel_netport_hash[iter].list); 2338c2ecf20Sopenharmony_ci sel_netport_hash[iter].size = 0; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return 0; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci__initcall(sel_netport_init); 240