netif.c (6886B)
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 32struct sel_netif { 33 struct list_head list; 34 struct netif_security_struct nsec; 35 struct rcu_head rcu_head; 36}; 37 38static u32 sel_netif_total; 39static DEFINE_SPINLOCK(sel_netif_lock); 40static 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 */ 52static 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 */ 67static inline struct sel_netif *sel_netif_find(const struct net *ns, 68 int ifindex) 69{ 70 int 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 */ 90static int sel_netif_insert(struct sel_netif *netif) 91{ 92 int 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 */ 112static 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 */ 132static 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(&selinux_state, 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 168out: 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 */ 191int 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 */ 217static 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 */ 237void 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 249static 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 260static struct notifier_block sel_netif_netdev_notifier = { 261 .notifier_call = sel_netif_netdev_notifier_handler, 262}; 263 264static __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