18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Network interface table. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Network interfaces (devices) do not have a security field, so we 68c2ecf20Sopenharmony_ci * maintain a table associating each interface with a SID. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Author: James Morris <jmorris@redhat.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> 118c2ecf20Sopenharmony_ci * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. 128c2ecf20Sopenharmony_ci * Paul Moore <paul@paul-moore.com> 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/types.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/stddef.h> 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/list.h> 208c2ecf20Sopenharmony_ci#include <linux/notifier.h> 218c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 228c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 238c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "security.h" 268c2ecf20Sopenharmony_ci#include "objsec.h" 278c2ecf20Sopenharmony_ci#include "netif.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define SEL_NETIF_HASH_SIZE 64 308c2ecf20Sopenharmony_ci#define SEL_NETIF_HASH_MAX 1024 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct sel_netif { 338c2ecf20Sopenharmony_ci struct list_head list; 348c2ecf20Sopenharmony_ci struct netif_security_struct nsec; 358c2ecf20Sopenharmony_ci struct rcu_head rcu_head; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic u32 sel_netif_total; 398c2ecf20Sopenharmony_cistatic LIST_HEAD(sel_netif_list); 408c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(sel_netif_lock); 418c2ecf20Sopenharmony_cistatic struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE]; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/** 448c2ecf20Sopenharmony_ci * sel_netif_hashfn - Hashing function for the interface table 458c2ecf20Sopenharmony_ci * @ns: the network namespace 468c2ecf20Sopenharmony_ci * @ifindex: the network interface 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * Description: 498c2ecf20Sopenharmony_ci * This is the hashing function for the network interface table, it returns the 508c2ecf20Sopenharmony_ci * bucket number for the given interface. 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_cistatic inline u32 sel_netif_hashfn(const struct net *ns, int ifindex) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci return (((uintptr_t)ns + ifindex) & (SEL_NETIF_HASH_SIZE - 1)); 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/** 598c2ecf20Sopenharmony_ci * sel_netif_find - Search for an interface record 608c2ecf20Sopenharmony_ci * @ns: the network namespace 618c2ecf20Sopenharmony_ci * @ifindex: the network interface 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * Description: 648c2ecf20Sopenharmony_ci * Search the network interface table and return the record matching @ifindex. 658c2ecf20Sopenharmony_ci * If an entry can not be found in the table return NULL. 668c2ecf20Sopenharmony_ci * 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_cistatic inline struct sel_netif *sel_netif_find(const struct net *ns, 698c2ecf20Sopenharmony_ci int ifindex) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci int idx = sel_netif_hashfn(ns, ifindex); 728c2ecf20Sopenharmony_ci struct sel_netif *netif; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list) 758c2ecf20Sopenharmony_ci if (net_eq(netif->nsec.ns, ns) && 768c2ecf20Sopenharmony_ci netif->nsec.ifindex == ifindex) 778c2ecf20Sopenharmony_ci return netif; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci return NULL; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/** 838c2ecf20Sopenharmony_ci * sel_netif_insert - Insert a new interface into the table 848c2ecf20Sopenharmony_ci * @netif: the new interface record 858c2ecf20Sopenharmony_ci * 868c2ecf20Sopenharmony_ci * Description: 878c2ecf20Sopenharmony_ci * Add a new interface record to the network interface hash table. Returns 888c2ecf20Sopenharmony_ci * zero on success, negative values on failure. 898c2ecf20Sopenharmony_ci * 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_cistatic int sel_netif_insert(struct sel_netif *netif) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci int idx; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (sel_netif_total >= SEL_NETIF_HASH_MAX) 968c2ecf20Sopenharmony_ci return -ENOSPC; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci idx = sel_netif_hashfn(netif->nsec.ns, netif->nsec.ifindex); 998c2ecf20Sopenharmony_ci list_add_rcu(&netif->list, &sel_netif_hash[idx]); 1008c2ecf20Sopenharmony_ci sel_netif_total++; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/** 1068c2ecf20Sopenharmony_ci * sel_netif_destroy - Remove an interface record from the table 1078c2ecf20Sopenharmony_ci * @netif: the existing interface record 1088c2ecf20Sopenharmony_ci * 1098c2ecf20Sopenharmony_ci * Description: 1108c2ecf20Sopenharmony_ci * Remove an existing interface record from the network interface table. 1118c2ecf20Sopenharmony_ci * 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_cistatic void sel_netif_destroy(struct sel_netif *netif) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci list_del_rcu(&netif->list); 1168c2ecf20Sopenharmony_ci sel_netif_total--; 1178c2ecf20Sopenharmony_ci kfree_rcu(netif, rcu_head); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/** 1218c2ecf20Sopenharmony_ci * sel_netif_sid_slow - Lookup the SID of a network interface using the policy 1228c2ecf20Sopenharmony_ci * @ns: the network namespace 1238c2ecf20Sopenharmony_ci * @ifindex: the network interface 1248c2ecf20Sopenharmony_ci * @sid: interface SID 1258c2ecf20Sopenharmony_ci * 1268c2ecf20Sopenharmony_ci * Description: 1278c2ecf20Sopenharmony_ci * This function determines the SID of a network interface by querying the 1288c2ecf20Sopenharmony_ci * security policy. The result is added to the network interface table to 1298c2ecf20Sopenharmony_ci * speedup future queries. Returns zero on success, negative values on 1308c2ecf20Sopenharmony_ci * failure. 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_cistatic int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci int ret = 0; 1368c2ecf20Sopenharmony_ci struct sel_netif *netif; 1378c2ecf20Sopenharmony_ci struct sel_netif *new; 1388c2ecf20Sopenharmony_ci struct net_device *dev; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* NOTE: we always use init's network namespace since we don't 1418c2ecf20Sopenharmony_ci * currently support containers */ 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci dev = dev_get_by_index(ns, ifindex); 1448c2ecf20Sopenharmony_ci if (unlikely(dev == NULL)) { 1458c2ecf20Sopenharmony_ci pr_warn("SELinux: failure in %s(), invalid network interface (%d)\n", 1468c2ecf20Sopenharmony_ci __func__, ifindex); 1478c2ecf20Sopenharmony_ci return -ENOENT; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci spin_lock_bh(&sel_netif_lock); 1518c2ecf20Sopenharmony_ci netif = sel_netif_find(ns, ifindex); 1528c2ecf20Sopenharmony_ci if (netif != NULL) { 1538c2ecf20Sopenharmony_ci *sid = netif->nsec.sid; 1548c2ecf20Sopenharmony_ci goto out; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci ret = security_netif_sid(&selinux_state, dev->name, sid); 1588c2ecf20Sopenharmony_ci if (ret != 0) 1598c2ecf20Sopenharmony_ci goto out; 1608c2ecf20Sopenharmony_ci new = kzalloc(sizeof(*new), GFP_ATOMIC); 1618c2ecf20Sopenharmony_ci if (new) { 1628c2ecf20Sopenharmony_ci new->nsec.ns = ns; 1638c2ecf20Sopenharmony_ci new->nsec.ifindex = ifindex; 1648c2ecf20Sopenharmony_ci new->nsec.sid = *sid; 1658c2ecf20Sopenharmony_ci if (sel_netif_insert(new)) 1668c2ecf20Sopenharmony_ci kfree(new); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ciout: 1708c2ecf20Sopenharmony_ci spin_unlock_bh(&sel_netif_lock); 1718c2ecf20Sopenharmony_ci dev_put(dev); 1728c2ecf20Sopenharmony_ci if (unlikely(ret)) 1738c2ecf20Sopenharmony_ci pr_warn("SELinux: failure in %s(), unable to determine network interface label (%d)\n", 1748c2ecf20Sopenharmony_ci __func__, ifindex); 1758c2ecf20Sopenharmony_ci return ret; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/** 1798c2ecf20Sopenharmony_ci * sel_netif_sid - Lookup the SID of a network interface 1808c2ecf20Sopenharmony_ci * @ns: the network namespace 1818c2ecf20Sopenharmony_ci * @ifindex: the network interface 1828c2ecf20Sopenharmony_ci * @sid: interface SID 1838c2ecf20Sopenharmony_ci * 1848c2ecf20Sopenharmony_ci * Description: 1858c2ecf20Sopenharmony_ci * This function determines the SID of a network interface using the fastest 1868c2ecf20Sopenharmony_ci * method possible. First the interface table is queried, but if an entry 1878c2ecf20Sopenharmony_ci * can't be found then the policy is queried and the result is added to the 1888c2ecf20Sopenharmony_ci * table to speedup future queries. Returns zero on success, negative values 1898c2ecf20Sopenharmony_ci * on failure. 1908c2ecf20Sopenharmony_ci * 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ciint sel_netif_sid(struct net *ns, int ifindex, u32 *sid) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct sel_netif *netif; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci rcu_read_lock(); 1978c2ecf20Sopenharmony_ci netif = sel_netif_find(ns, ifindex); 1988c2ecf20Sopenharmony_ci if (likely(netif != NULL)) { 1998c2ecf20Sopenharmony_ci *sid = netif->nsec.sid; 2008c2ecf20Sopenharmony_ci rcu_read_unlock(); 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci rcu_read_unlock(); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return sel_netif_sid_slow(ns, ifindex, sid); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci/** 2098c2ecf20Sopenharmony_ci * sel_netif_kill - Remove an entry from the network interface table 2108c2ecf20Sopenharmony_ci * @ns: the network namespace 2118c2ecf20Sopenharmony_ci * @ifindex: the network interface 2128c2ecf20Sopenharmony_ci * 2138c2ecf20Sopenharmony_ci * Description: 2148c2ecf20Sopenharmony_ci * This function removes the entry matching @ifindex from the network interface 2158c2ecf20Sopenharmony_ci * table if it exists. 2168c2ecf20Sopenharmony_ci * 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_cistatic void sel_netif_kill(const struct net *ns, int ifindex) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct sel_netif *netif; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci rcu_read_lock(); 2238c2ecf20Sopenharmony_ci spin_lock_bh(&sel_netif_lock); 2248c2ecf20Sopenharmony_ci netif = sel_netif_find(ns, ifindex); 2258c2ecf20Sopenharmony_ci if (netif) 2268c2ecf20Sopenharmony_ci sel_netif_destroy(netif); 2278c2ecf20Sopenharmony_ci spin_unlock_bh(&sel_netif_lock); 2288c2ecf20Sopenharmony_ci rcu_read_unlock(); 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci/** 2328c2ecf20Sopenharmony_ci * sel_netif_flush - Flush the entire network interface table 2338c2ecf20Sopenharmony_ci * 2348c2ecf20Sopenharmony_ci * Description: 2358c2ecf20Sopenharmony_ci * Remove all entries from the network interface table. 2368c2ecf20Sopenharmony_ci * 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_civoid sel_netif_flush(void) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci int idx; 2418c2ecf20Sopenharmony_ci struct sel_netif *netif; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci spin_lock_bh(&sel_netif_lock); 2448c2ecf20Sopenharmony_ci for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++) 2458c2ecf20Sopenharmony_ci list_for_each_entry(netif, &sel_netif_hash[idx], list) 2468c2ecf20Sopenharmony_ci sel_netif_destroy(netif); 2478c2ecf20Sopenharmony_ci spin_unlock_bh(&sel_netif_lock); 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic int sel_netif_netdev_notifier_handler(struct notifier_block *this, 2518c2ecf20Sopenharmony_ci unsigned long event, void *ptr) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (event == NETDEV_DOWN) 2568c2ecf20Sopenharmony_ci sel_netif_kill(dev_net(dev), dev->ifindex); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return NOTIFY_DONE; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic struct notifier_block sel_netif_netdev_notifier = { 2628c2ecf20Sopenharmony_ci .notifier_call = sel_netif_netdev_notifier_handler, 2638c2ecf20Sopenharmony_ci}; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic __init int sel_netif_init(void) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci int i; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (!selinux_enabled_boot) 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci for (i = 0; i < SEL_NETIF_HASH_SIZE; i++) 2738c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sel_netif_hash[i]); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci register_netdevice_notifier(&sel_netif_netdev_notifier); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return 0; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci__initcall(sel_netif_init); 2818c2ecf20Sopenharmony_ci 282