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