11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Network interface table. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Network interfaces (devices) do not have a security field, so we 51da177e4SLinus Torvalds * maintain a table associating each interface with a SID. 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * Author: James Morris <jmorris@redhat.com> 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> 10e8bfdb9dSPaul Moore * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. 11e8bfdb9dSPaul Moore * Paul Moore <paul.moore@hp.com> 121da177e4SLinus Torvalds * 131da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 141da177e4SLinus Torvalds * it under the terms of the GNU General Public License version 2, 151da177e4SLinus Torvalds * as published by the Free Software Foundation. 161da177e4SLinus Torvalds */ 171da177e4SLinus Torvalds #include <linux/init.h> 181da177e4SLinus Torvalds #include <linux/types.h> 191da177e4SLinus Torvalds #include <linux/stddef.h> 201da177e4SLinus Torvalds #include <linux/kernel.h> 211da177e4SLinus Torvalds #include <linux/list.h> 221da177e4SLinus Torvalds #include <linux/notifier.h> 231da177e4SLinus Torvalds #include <linux/netdevice.h> 241da177e4SLinus Torvalds #include <linux/rcupdate.h> 25e9dc8653SEric W. Biederman #include <net/net_namespace.h> 261da177e4SLinus Torvalds 271da177e4SLinus Torvalds #include "security.h" 281da177e4SLinus Torvalds #include "objsec.h" 291da177e4SLinus Torvalds #include "netif.h" 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds #define SEL_NETIF_HASH_SIZE 64 321da177e4SLinus Torvalds #define SEL_NETIF_HASH_MAX 1024 331da177e4SLinus Torvalds 34338366cbSEric Paris struct sel_netif { 351da177e4SLinus Torvalds struct list_head list; 361da177e4SLinus Torvalds struct netif_security_struct nsec; 371da177e4SLinus Torvalds struct rcu_head rcu_head; 381da177e4SLinus Torvalds }; 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds static u32 sel_netif_total; 411da177e4SLinus Torvalds static LIST_HEAD(sel_netif_list); 421da177e4SLinus Torvalds static DEFINE_SPINLOCK(sel_netif_lock); 431da177e4SLinus Torvalds static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE]; 441da177e4SLinus Torvalds 45e8bfdb9dSPaul Moore /** 46e8bfdb9dSPaul Moore * sel_netif_hashfn - Hashing function for the interface table 47e8bfdb9dSPaul Moore * @ifindex: the network interface 48e8bfdb9dSPaul Moore * 49e8bfdb9dSPaul Moore * Description: 50e8bfdb9dSPaul Moore * This is the hashing function for the network interface table, it returns the 51e8bfdb9dSPaul Moore * bucket number for the given interface. 52e8bfdb9dSPaul Moore * 531da177e4SLinus Torvalds */ 54e8bfdb9dSPaul Moore static inline u32 sel_netif_hashfn(int ifindex) 551da177e4SLinus Torvalds { 56e8bfdb9dSPaul Moore return (ifindex & (SEL_NETIF_HASH_SIZE - 1)); 571da177e4SLinus Torvalds } 58e8bfdb9dSPaul Moore 59e8bfdb9dSPaul Moore /** 60e8bfdb9dSPaul Moore * sel_netif_find - Search for an interface record 61e8bfdb9dSPaul Moore * @ifindex: the network interface 62e8bfdb9dSPaul Moore * 63e8bfdb9dSPaul Moore * Description: 64e8bfdb9dSPaul Moore * Search the network interface table and return the record matching @ifindex. 65e8bfdb9dSPaul Moore * If an entry can not be found in the table return NULL. 66e8bfdb9dSPaul Moore * 67e8bfdb9dSPaul Moore */ 68e8bfdb9dSPaul Moore static inline struct sel_netif *sel_netif_find(int ifindex) 69e8bfdb9dSPaul Moore { 70e8bfdb9dSPaul Moore int idx = sel_netif_hashfn(ifindex); 71e8bfdb9dSPaul Moore struct sel_netif *netif; 72e8bfdb9dSPaul Moore 73e8bfdb9dSPaul Moore list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list) 74e8bfdb9dSPaul Moore /* all of the devices should normally fit in the hash, so we 75e8bfdb9dSPaul Moore * optimize for that case */ 76e8bfdb9dSPaul Moore if (likely(netif->nsec.ifindex == ifindex)) 77e8bfdb9dSPaul Moore return netif; 78e8bfdb9dSPaul Moore 791da177e4SLinus Torvalds return NULL; 801da177e4SLinus Torvalds } 811da177e4SLinus Torvalds 82e8bfdb9dSPaul Moore /** 83e8bfdb9dSPaul Moore * sel_netif_insert - Insert a new interface into the table 84e8bfdb9dSPaul Moore * @netif: the new interface record 85e8bfdb9dSPaul Moore * 86e8bfdb9dSPaul Moore * Description: 87e8bfdb9dSPaul Moore * Add a new interface record to the network interface hash table. Returns 88e8bfdb9dSPaul Moore * zero on success, negative values on failure. 89e8bfdb9dSPaul Moore * 90e8bfdb9dSPaul Moore */ 911da177e4SLinus Torvalds static int sel_netif_insert(struct sel_netif *netif) 921da177e4SLinus Torvalds { 93e8bfdb9dSPaul Moore int idx; 941da177e4SLinus Torvalds 95e8bfdb9dSPaul Moore if (sel_netif_total >= SEL_NETIF_HASH_MAX) 96e8bfdb9dSPaul Moore return -ENOSPC; 971da177e4SLinus Torvalds 98e8bfdb9dSPaul Moore idx = sel_netif_hashfn(netif->nsec.ifindex); 991da177e4SLinus Torvalds list_add_rcu(&netif->list, &sel_netif_hash[idx]); 1001da177e4SLinus Torvalds sel_netif_total++; 101e8bfdb9dSPaul Moore 102e8bfdb9dSPaul Moore return 0; 1031da177e4SLinus Torvalds } 1041da177e4SLinus Torvalds 105e8bfdb9dSPaul Moore /** 106e8bfdb9dSPaul Moore * sel_netif_free - Frees an interface entry 107e8bfdb9dSPaul Moore * @p: the entry's RCU field 108e8bfdb9dSPaul Moore * 109e8bfdb9dSPaul Moore * Description: 110e8bfdb9dSPaul Moore * This function is designed to be used as a callback to the call_rcu() 111e8bfdb9dSPaul Moore * function so that memory allocated to a hash table interface entry can be 112e8bfdb9dSPaul Moore * released safely. 113e8bfdb9dSPaul Moore * 114e8bfdb9dSPaul Moore */ 1151da177e4SLinus Torvalds static void sel_netif_free(struct rcu_head *p) 1161da177e4SLinus Torvalds { 1171da177e4SLinus Torvalds struct sel_netif *netif = container_of(p, struct sel_netif, rcu_head); 1181da177e4SLinus Torvalds kfree(netif); 1191da177e4SLinus Torvalds } 1201da177e4SLinus Torvalds 121e8bfdb9dSPaul Moore /** 122e8bfdb9dSPaul Moore * sel_netif_destroy - Remove an interface record from the table 123e8bfdb9dSPaul Moore * @netif: the existing interface record 124e8bfdb9dSPaul Moore * 125e8bfdb9dSPaul Moore * Description: 126e8bfdb9dSPaul Moore * Remove an existing interface record from the network interface table. 127e8bfdb9dSPaul Moore * 128e8bfdb9dSPaul Moore */ 1291da177e4SLinus Torvalds static void sel_netif_destroy(struct sel_netif *netif) 1301da177e4SLinus Torvalds { 1311da177e4SLinus Torvalds list_del_rcu(&netif->list); 1321da177e4SLinus Torvalds sel_netif_total--; 1331da177e4SLinus Torvalds call_rcu(&netif->rcu_head, sel_netif_free); 1341da177e4SLinus Torvalds } 1351da177e4SLinus Torvalds 136e8bfdb9dSPaul Moore /** 137e8bfdb9dSPaul Moore * sel_netif_sid_slow - Lookup the SID of a network interface using the policy 138e8bfdb9dSPaul Moore * @ifindex: the network interface 139e8bfdb9dSPaul Moore * @sid: interface SID 140e8bfdb9dSPaul Moore * 141e8bfdb9dSPaul Moore * Description: 142e8bfdb9dSPaul Moore * This function determines the SID of a network interface by quering the 143e8bfdb9dSPaul Moore * security policy. The result is added to the network interface table to 144e8bfdb9dSPaul Moore * speedup future queries. Returns zero on success, negative values on 145e8bfdb9dSPaul Moore * failure. 146e8bfdb9dSPaul Moore * 147e8bfdb9dSPaul Moore */ 148e8bfdb9dSPaul Moore static int sel_netif_sid_slow(int ifindex, u32 *sid) 1491da177e4SLinus Torvalds { 1501da177e4SLinus Torvalds int ret; 151e8bfdb9dSPaul Moore struct sel_netif *netif; 152e8bfdb9dSPaul Moore struct sel_netif *new = NULL; 153e8bfdb9dSPaul Moore struct net_device *dev; 1541da177e4SLinus Torvalds 155e8bfdb9dSPaul Moore /* NOTE: we always use init's network namespace since we don't 156e8bfdb9dSPaul Moore * currently support containers */ 1571da177e4SLinus Torvalds 158e8bfdb9dSPaul Moore dev = dev_get_by_index(&init_net, ifindex); 15971f1cb05SPaul Moore if (unlikely(dev == NULL)) { 16071f1cb05SPaul Moore printk(KERN_WARNING 16171f1cb05SPaul Moore "SELinux: failure in sel_netif_sid_slow()," 16271f1cb05SPaul Moore " invalid network interface (%d)\n", ifindex); 163e8bfdb9dSPaul Moore return -ENOENT; 16471f1cb05SPaul Moore } 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds spin_lock_bh(&sel_netif_lock); 167e8bfdb9dSPaul Moore netif = sel_netif_find(ifindex); 168e8bfdb9dSPaul Moore if (netif != NULL) { 169e8bfdb9dSPaul Moore *sid = netif->nsec.sid; 170e8bfdb9dSPaul Moore ret = 0; 1711da177e4SLinus Torvalds goto out; 1721da177e4SLinus Torvalds } 173e8bfdb9dSPaul Moore new = kzalloc(sizeof(*new), GFP_ATOMIC); 174e8bfdb9dSPaul Moore if (new == NULL) { 175e8bfdb9dSPaul Moore ret = -ENOMEM; 176e8bfdb9dSPaul Moore goto out; 177e8bfdb9dSPaul Moore } 178e8bfdb9dSPaul Moore ret = security_netif_sid(dev->name, &new->nsec.sid); 179e8bfdb9dSPaul Moore if (ret != 0) 180e8bfdb9dSPaul Moore goto out; 181e8bfdb9dSPaul Moore new->nsec.ifindex = ifindex; 1821da177e4SLinus Torvalds ret = sel_netif_insert(new); 183e8bfdb9dSPaul Moore if (ret != 0) 1841da177e4SLinus Torvalds goto out; 185e8bfdb9dSPaul Moore *sid = new->nsec.sid; 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds out: 188e8bfdb9dSPaul Moore spin_unlock_bh(&sel_netif_lock); 189e8bfdb9dSPaul Moore dev_put(dev); 19071f1cb05SPaul Moore if (unlikely(ret)) { 19171f1cb05SPaul Moore printk(KERN_WARNING 19271f1cb05SPaul Moore "SELinux: failure in sel_netif_sid_slow()," 19371f1cb05SPaul Moore " unable to determine network interface label (%d)\n", 19471f1cb05SPaul Moore ifindex); 195e8bfdb9dSPaul Moore kfree(new); 19671f1cb05SPaul Moore } 1971da177e4SLinus Torvalds return ret; 1981da177e4SLinus Torvalds } 1991da177e4SLinus Torvalds 200e8bfdb9dSPaul Moore /** 201e8bfdb9dSPaul Moore * sel_netif_sid - Lookup the SID of a network interface 202e8bfdb9dSPaul Moore * @ifindex: the network interface 203e8bfdb9dSPaul Moore * @sid: interface SID 204e8bfdb9dSPaul Moore * 205e8bfdb9dSPaul Moore * Description: 206e8bfdb9dSPaul Moore * This function determines the SID of a network interface using the fastest 207e8bfdb9dSPaul Moore * method possible. First the interface table is queried, but if an entry 208e8bfdb9dSPaul Moore * can't be found then the policy is queried and the result is added to the 209e8bfdb9dSPaul Moore * table to speedup future queries. Returns zero on success, negative values 210e8bfdb9dSPaul Moore * on failure. 211e8bfdb9dSPaul Moore * 212e8bfdb9dSPaul Moore */ 213e8bfdb9dSPaul Moore int sel_netif_sid(int ifindex, u32 *sid) 2141da177e4SLinus Torvalds { 2151da177e4SLinus Torvalds struct sel_netif *netif; 2161da177e4SLinus Torvalds 2171da177e4SLinus Torvalds rcu_read_lock(); 218e8bfdb9dSPaul Moore netif = sel_netif_find(ifindex); 219e8bfdb9dSPaul Moore if (likely(netif != NULL)) { 220e8bfdb9dSPaul Moore *sid = netif->nsec.sid; 2211da177e4SLinus Torvalds rcu_read_unlock(); 222e8bfdb9dSPaul Moore return 0; 2231da177e4SLinus Torvalds } 2241da177e4SLinus Torvalds rcu_read_unlock(); 225e8bfdb9dSPaul Moore 226e8bfdb9dSPaul Moore return sel_netif_sid_slow(ifindex, sid); 2271da177e4SLinus Torvalds } 2281da177e4SLinus Torvalds 229e8bfdb9dSPaul Moore /** 230e8bfdb9dSPaul Moore * sel_netif_kill - Remove an entry from the network interface table 231e8bfdb9dSPaul Moore * @ifindex: the network interface 232e8bfdb9dSPaul Moore * 233e8bfdb9dSPaul Moore * Description: 234e8bfdb9dSPaul Moore * This function removes the entry matching @ifindex from the network interface 235e8bfdb9dSPaul Moore * table if it exists. 236e8bfdb9dSPaul Moore * 237e8bfdb9dSPaul Moore */ 238e8bfdb9dSPaul Moore static void sel_netif_kill(int ifindex) 2391da177e4SLinus Torvalds { 2401da177e4SLinus Torvalds struct sel_netif *netif; 2411da177e4SLinus Torvalds 242*61844250SPaul E. McKenney rcu_read_lock(); 2431da177e4SLinus Torvalds spin_lock_bh(&sel_netif_lock); 244e8bfdb9dSPaul Moore netif = sel_netif_find(ifindex); 2451da177e4SLinus Torvalds if (netif) 2461da177e4SLinus Torvalds sel_netif_destroy(netif); 2471da177e4SLinus Torvalds spin_unlock_bh(&sel_netif_lock); 248*61844250SPaul E. McKenney rcu_read_unlock(); 2491da177e4SLinus Torvalds } 2501da177e4SLinus Torvalds 251e8bfdb9dSPaul Moore /** 252e8bfdb9dSPaul Moore * sel_netif_flush - Flush the entire network interface table 253e8bfdb9dSPaul Moore * 254e8bfdb9dSPaul Moore * Description: 255e8bfdb9dSPaul Moore * Remove all entries from the network interface table. 256e8bfdb9dSPaul Moore * 257e8bfdb9dSPaul Moore */ 2581da177e4SLinus Torvalds static void sel_netif_flush(void) 2591da177e4SLinus Torvalds { 2601da177e4SLinus Torvalds int idx; 2611da177e4SLinus Torvalds struct sel_netif *netif; 2621da177e4SLinus Torvalds 263e8bfdb9dSPaul Moore spin_lock_bh(&sel_netif_lock); 264e8bfdb9dSPaul Moore for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++) 2651da177e4SLinus Torvalds list_for_each_entry(netif, &sel_netif_hash[idx], list) 2661da177e4SLinus Torvalds sel_netif_destroy(netif); 2671da177e4SLinus Torvalds spin_unlock_bh(&sel_netif_lock); 2681da177e4SLinus Torvalds } 2691da177e4SLinus Torvalds 2701da177e4SLinus Torvalds static int sel_netif_avc_callback(u32 event, u32 ssid, u32 tsid, 2711da177e4SLinus Torvalds u16 class, u32 perms, u32 *retained) 2721da177e4SLinus Torvalds { 2731da177e4SLinus Torvalds if (event == AVC_CALLBACK_RESET) { 2741da177e4SLinus Torvalds sel_netif_flush(); 2751da177e4SLinus Torvalds synchronize_net(); 2761da177e4SLinus Torvalds } 2771da177e4SLinus Torvalds return 0; 2781da177e4SLinus Torvalds } 2791da177e4SLinus Torvalds 2801da177e4SLinus Torvalds static int sel_netif_netdev_notifier_handler(struct notifier_block *this, 2811da177e4SLinus Torvalds unsigned long event, void *ptr) 2821da177e4SLinus Torvalds { 2831da177e4SLinus Torvalds struct net_device *dev = ptr; 2841da177e4SLinus Torvalds 285c346dca1SYOSHIFUJI Hideaki if (dev_net(dev) != &init_net) 286e9dc8653SEric W. Biederman return NOTIFY_DONE; 287e9dc8653SEric W. Biederman 2881da177e4SLinus Torvalds if (event == NETDEV_DOWN) 289e8bfdb9dSPaul Moore sel_netif_kill(dev->ifindex); 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds return NOTIFY_DONE; 2921da177e4SLinus Torvalds } 2931da177e4SLinus Torvalds 2941da177e4SLinus Torvalds static struct notifier_block sel_netif_netdev_notifier = { 2951da177e4SLinus Torvalds .notifier_call = sel_netif_netdev_notifier_handler, 2961da177e4SLinus Torvalds }; 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds static __init int sel_netif_init(void) 2991da177e4SLinus Torvalds { 300e8bfdb9dSPaul Moore int i, err; 3011da177e4SLinus Torvalds 3021da177e4SLinus Torvalds if (!selinux_enabled) 303e8bfdb9dSPaul Moore return 0; 3041da177e4SLinus Torvalds 3051da177e4SLinus Torvalds for (i = 0; i < SEL_NETIF_HASH_SIZE; i++) 3061da177e4SLinus Torvalds INIT_LIST_HEAD(&sel_netif_hash[i]); 3071da177e4SLinus Torvalds 3081da177e4SLinus Torvalds register_netdevice_notifier(&sel_netif_netdev_notifier); 3091da177e4SLinus Torvalds 3101da177e4SLinus Torvalds err = avc_add_callback(sel_netif_avc_callback, AVC_CALLBACK_RESET, 3111da177e4SLinus Torvalds SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0); 3121da177e4SLinus Torvalds if (err) 3131da177e4SLinus Torvalds panic("avc_add_callback() failed, error %d\n", err); 3141da177e4SLinus Torvalds 3151da177e4SLinus Torvalds return err; 3161da177e4SLinus Torvalds } 3171da177e4SLinus Torvalds 3181da177e4SLinus Torvalds __initcall(sel_netif_init); 3191da177e4SLinus Torvalds 320