1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Network interface table. 4 * 5 * Network interfaces (devices) do not have a security field, so we 6 * maintain a table associating each interface with a SID. 7 * 8 * Author: James Morris <jmorris@redhat.com> 9 * 10 * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> 11 * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. 12 * Paul Moore <paul@paul-moore.com> 13 */ 14 #include <linux/init.h> 15 #include <linux/types.h> 16 #include <linux/slab.h> 17 #include <linux/stddef.h> 18 #include <linux/kernel.h> 19 #include <linux/list.h> 20 #include <linux/notifier.h> 21 #include <linux/netdevice.h> 22 #include <linux/rcupdate.h> 23 #include <net/net_namespace.h> 24 25 #include "security.h" 26 #include "objsec.h" 27 #include "netif.h" 28 29 #define SEL_NETIF_HASH_SIZE 64 30 #define SEL_NETIF_HASH_MAX 1024 31 32 struct sel_netif { 33 struct list_head list; 34 struct netif_security_struct nsec; 35 struct rcu_head rcu_head; 36 }; 37 38 static u32 sel_netif_total; 39 static DEFINE_SPINLOCK(sel_netif_lock); 40 static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE]; 41 42 /** 43 * sel_netif_hashfn - Hashing function for the interface table 44 * @ns: the network namespace 45 * @ifindex: the network interface 46 * 47 * Description: 48 * This is the hashing function for the network interface table, it returns the 49 * bucket number for the given interface. 50 * 51 */ 52 static inline u32 sel_netif_hashfn(const struct net *ns, int ifindex) 53 { 54 return (((uintptr_t)ns + ifindex) & (SEL_NETIF_HASH_SIZE - 1)); 55 } 56 57 /** 58 * sel_netif_find - Search for an interface record 59 * @ns: the network namespace 60 * @ifindex: the network interface 61 * 62 * Description: 63 * Search the network interface table and return the record matching @ifindex. 64 * If an entry can not be found in the table return NULL. 65 * 66 */ 67 static inline struct sel_netif *sel_netif_find(const struct net *ns, 68 int ifindex) 69 { 70 u32 idx = sel_netif_hashfn(ns, ifindex); 71 struct sel_netif *netif; 72 73 list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list) 74 if (net_eq(netif->nsec.ns, ns) && 75 netif->nsec.ifindex == ifindex) 76 return netif; 77 78 return NULL; 79 } 80 81 /** 82 * sel_netif_insert - Insert a new interface into the table 83 * @netif: the new interface record 84 * 85 * Description: 86 * Add a new interface record to the network interface hash table. Returns 87 * zero on success, negative values on failure. 88 * 89 */ 90 static int sel_netif_insert(struct sel_netif *netif) 91 { 92 u32 idx; 93 94 if (sel_netif_total >= SEL_NETIF_HASH_MAX) 95 return -ENOSPC; 96 97 idx = sel_netif_hashfn(netif->nsec.ns, netif->nsec.ifindex); 98 list_add_rcu(&netif->list, &sel_netif_hash[idx]); 99 sel_netif_total++; 100 101 return 0; 102 } 103 104 /** 105 * sel_netif_destroy - Remove an interface record from the table 106 * @netif: the existing interface record 107 * 108 * Description: 109 * Remove an existing interface record from the network interface table. 110 * 111 */ 112 static void sel_netif_destroy(struct sel_netif *netif) 113 { 114 list_del_rcu(&netif->list); 115 sel_netif_total--; 116 kfree_rcu(netif, rcu_head); 117 } 118 119 /** 120 * sel_netif_sid_slow - Lookup the SID of a network interface using the policy 121 * @ns: the network namespace 122 * @ifindex: the network interface 123 * @sid: interface SID 124 * 125 * Description: 126 * This function determines the SID of a network interface by querying the 127 * security policy. The result is added to the network interface table to 128 * speedup future queries. Returns zero on success, negative values on 129 * failure. 130 * 131 */ 132 static int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid) 133 { 134 int ret = 0; 135 struct sel_netif *netif; 136 struct sel_netif *new; 137 struct net_device *dev; 138 139 /* NOTE: we always use init's network namespace since we don't 140 * currently support containers */ 141 142 dev = dev_get_by_index(ns, ifindex); 143 if (unlikely(dev == NULL)) { 144 pr_warn("SELinux: failure in %s(), invalid network interface (%d)\n", 145 __func__, ifindex); 146 return -ENOENT; 147 } 148 149 spin_lock_bh(&sel_netif_lock); 150 netif = sel_netif_find(ns, ifindex); 151 if (netif != NULL) { 152 *sid = netif->nsec.sid; 153 goto out; 154 } 155 156 ret = security_netif_sid(dev->name, sid); 157 if (ret != 0) 158 goto out; 159 new = kzalloc(sizeof(*new), GFP_ATOMIC); 160 if (new) { 161 new->nsec.ns = ns; 162 new->nsec.ifindex = ifindex; 163 new->nsec.sid = *sid; 164 if (sel_netif_insert(new)) 165 kfree(new); 166 } 167 168 out: 169 spin_unlock_bh(&sel_netif_lock); 170 dev_put(dev); 171 if (unlikely(ret)) 172 pr_warn("SELinux: failure in %s(), unable to determine network interface label (%d)\n", 173 __func__, ifindex); 174 return ret; 175 } 176 177 /** 178 * sel_netif_sid - Lookup the SID of a network interface 179 * @ns: the network namespace 180 * @ifindex: the network interface 181 * @sid: interface SID 182 * 183 * Description: 184 * This function determines the SID of a network interface using the fastest 185 * method possible. First the interface table is queried, but if an entry 186 * can't be found then the policy is queried and the result is added to the 187 * table to speedup future queries. Returns zero on success, negative values 188 * on failure. 189 * 190 */ 191 int sel_netif_sid(struct net *ns, int ifindex, u32 *sid) 192 { 193 struct sel_netif *netif; 194 195 rcu_read_lock(); 196 netif = sel_netif_find(ns, ifindex); 197 if (likely(netif != NULL)) { 198 *sid = netif->nsec.sid; 199 rcu_read_unlock(); 200 return 0; 201 } 202 rcu_read_unlock(); 203 204 return sel_netif_sid_slow(ns, ifindex, sid); 205 } 206 207 /** 208 * sel_netif_kill - Remove an entry from the network interface table 209 * @ns: the network namespace 210 * @ifindex: the network interface 211 * 212 * Description: 213 * This function removes the entry matching @ifindex from the network interface 214 * table if it exists. 215 * 216 */ 217 static void sel_netif_kill(const struct net *ns, int ifindex) 218 { 219 struct sel_netif *netif; 220 221 rcu_read_lock(); 222 spin_lock_bh(&sel_netif_lock); 223 netif = sel_netif_find(ns, ifindex); 224 if (netif) 225 sel_netif_destroy(netif); 226 spin_unlock_bh(&sel_netif_lock); 227 rcu_read_unlock(); 228 } 229 230 /** 231 * sel_netif_flush - Flush the entire network interface table 232 * 233 * Description: 234 * Remove all entries from the network interface table. 235 * 236 */ 237 void sel_netif_flush(void) 238 { 239 int idx; 240 struct sel_netif *netif; 241 242 spin_lock_bh(&sel_netif_lock); 243 for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++) 244 list_for_each_entry(netif, &sel_netif_hash[idx], list) 245 sel_netif_destroy(netif); 246 spin_unlock_bh(&sel_netif_lock); 247 } 248 249 static int sel_netif_netdev_notifier_handler(struct notifier_block *this, 250 unsigned long event, void *ptr) 251 { 252 struct net_device *dev = netdev_notifier_info_to_dev(ptr); 253 254 if (event == NETDEV_DOWN) 255 sel_netif_kill(dev_net(dev), dev->ifindex); 256 257 return NOTIFY_DONE; 258 } 259 260 static struct notifier_block sel_netif_netdev_notifier = { 261 .notifier_call = sel_netif_netdev_notifier_handler, 262 }; 263 264 static __init int sel_netif_init(void) 265 { 266 int i; 267 268 if (!selinux_enabled_boot) 269 return 0; 270 271 for (i = 0; i < SEL_NETIF_HASH_SIZE; i++) 272 INIT_LIST_HEAD(&sel_netif_hash[i]); 273 274 register_netdevice_notifier(&sel_netif_netdev_notifier); 275 276 return 0; 277 } 278 279 __initcall(sel_netif_init); 280 281
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.