// SPDX-License-Identifier: GPL-2.0-only1/*2* Network interface table.3*4* Network interfaces (devices) do not have a security field, so we5* maintain a table associating each interface with a SID.6*7* Author: James Morris <[email protected]>8*9* Copyright (C) 2003 Red Hat, Inc., James Morris <[email protected]>10* Copyright (C) 2007 Hewlett-Packard Development Company, L.P.11* Paul Moore <[email protected]>12*/13#include <linux/init.h>14#include <linux/types.h>15#include <linux/slab.h>16#include <linux/stddef.h>17#include <linux/kernel.h>18#include <linux/list.h>19#include <linux/notifier.h>20#include <linux/netdevice.h>21#include <linux/rcupdate.h>22#include <net/net_namespace.h>2324#include "security.h"25#include "objsec.h"26#include "netif.h"2728#define SEL_NETIF_HASH_SIZE 6429#define SEL_NETIF_HASH_MAX 10243031struct sel_netif {32struct list_head list;33struct netif_security_struct nsec;34struct rcu_head rcu_head;35};3637static u32 sel_netif_total;38static DEFINE_SPINLOCK(sel_netif_lock);39static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE];4041/**42* sel_netif_hashfn - Hashing function for the interface table43* @ns: the network namespace44* @ifindex: the network interface45*46* Description:47* This is the hashing function for the network interface table, it returns the48* bucket number for the given interface.49*50*/51static inline u32 sel_netif_hashfn(const struct net *ns, int ifindex)52{53return (((uintptr_t)ns + ifindex) & (SEL_NETIF_HASH_SIZE - 1));54}5556/**57* sel_netif_find - Search for an interface record58* @ns: the network namespace59* @ifindex: the network interface60*61* Description:62* Search the network interface table and return the record matching @ifindex.63* If an entry can not be found in the table return NULL.64*65*/66static inline struct sel_netif *sel_netif_find(const struct net *ns,67int ifindex)68{69u32 idx = sel_netif_hashfn(ns, ifindex);70struct sel_netif *netif;7172list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list)73if (net_eq(netif->nsec.ns, ns) &&74netif->nsec.ifindex == ifindex)75return netif;7677return NULL;78}7980/**81* sel_netif_insert - Insert a new interface into the table82* @netif: the new interface record83*84* Description:85* Add a new interface record to the network interface hash table. Returns86* zero on success, negative values on failure.87*88*/89static int sel_netif_insert(struct sel_netif *netif)90{91u32 idx;9293if (sel_netif_total >= SEL_NETIF_HASH_MAX)94return -ENOSPC;9596idx = sel_netif_hashfn(netif->nsec.ns, netif->nsec.ifindex);97list_add_rcu(&netif->list, &sel_netif_hash[idx]);98sel_netif_total++;99100return 0;101}102103/**104* sel_netif_destroy - Remove an interface record from the table105* @netif: the existing interface record106*107* Description:108* Remove an existing interface record from the network interface table.109*110*/111static void sel_netif_destroy(struct sel_netif *netif)112{113list_del_rcu(&netif->list);114sel_netif_total--;115kfree_rcu(netif, rcu_head);116}117118/**119* sel_netif_sid_slow - Lookup the SID of a network interface using the policy120* @ns: the network namespace121* @ifindex: the network interface122* @sid: interface SID123*124* Description:125* This function determines the SID of a network interface by querying the126* security policy. The result is added to the network interface table to127* speedup future queries. Returns zero on success, negative values on128* failure.129*130*/131static int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid)132{133int ret = 0;134struct sel_netif *netif;135struct sel_netif *new;136struct net_device *dev;137138/* NOTE: we always use init's network namespace since we don't139* currently support containers */140141dev = dev_get_by_index(ns, ifindex);142if (unlikely(dev == NULL)) {143pr_warn("SELinux: failure in %s(), invalid network interface (%d)\n",144__func__, ifindex);145return -ENOENT;146}147148spin_lock_bh(&sel_netif_lock);149netif = sel_netif_find(ns, ifindex);150if (netif != NULL) {151*sid = netif->nsec.sid;152goto out;153}154155ret = security_netif_sid(dev->name, sid);156if (ret != 0)157goto out;158159/* If this memory allocation fails still return 0. The SID160* is valid, it just won't be added to the cache.161*/162new = kmalloc(sizeof(*new), GFP_ATOMIC);163if (new) {164new->nsec.ns = ns;165new->nsec.ifindex = ifindex;166new->nsec.sid = *sid;167if (sel_netif_insert(new))168kfree(new);169}170171out:172spin_unlock_bh(&sel_netif_lock);173dev_put(dev);174if (unlikely(ret))175pr_warn("SELinux: failure in %s(), unable to determine network interface label (%d)\n",176__func__, ifindex);177return ret;178}179180/**181* sel_netif_sid - Lookup the SID of a network interface182* @ns: the network namespace183* @ifindex: the network interface184* @sid: interface SID185*186* Description:187* This function determines the SID of a network interface using the fastest188* method possible. First the interface table is queried, but if an entry189* can't be found then the policy is queried and the result is added to the190* table to speedup future queries. Returns zero on success, negative values191* on failure.192*193*/194int sel_netif_sid(struct net *ns, int ifindex, u32 *sid)195{196struct sel_netif *netif;197198rcu_read_lock();199netif = sel_netif_find(ns, ifindex);200if (likely(netif != NULL)) {201*sid = netif->nsec.sid;202rcu_read_unlock();203return 0;204}205rcu_read_unlock();206207return sel_netif_sid_slow(ns, ifindex, sid);208}209210/**211* sel_netif_kill - Remove an entry from the network interface table212* @ns: the network namespace213* @ifindex: the network interface214*215* Description:216* This function removes the entry matching @ifindex from the network interface217* table if it exists.218*219*/220static void sel_netif_kill(const struct net *ns, int ifindex)221{222struct sel_netif *netif;223224rcu_read_lock();225spin_lock_bh(&sel_netif_lock);226netif = sel_netif_find(ns, ifindex);227if (netif)228sel_netif_destroy(netif);229spin_unlock_bh(&sel_netif_lock);230rcu_read_unlock();231}232233/**234* sel_netif_flush - Flush the entire network interface table235*236* Description:237* Remove all entries from the network interface table.238*239*/240void sel_netif_flush(void)241{242int idx;243struct sel_netif *netif;244245spin_lock_bh(&sel_netif_lock);246for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++)247list_for_each_entry(netif, &sel_netif_hash[idx], list)248sel_netif_destroy(netif);249spin_unlock_bh(&sel_netif_lock);250}251252static int sel_netif_netdev_notifier_handler(struct notifier_block *this,253unsigned long event, void *ptr)254{255struct net_device *dev = netdev_notifier_info_to_dev(ptr);256257if (event == NETDEV_DOWN)258sel_netif_kill(dev_net(dev), dev->ifindex);259260return NOTIFY_DONE;261}262263static struct notifier_block sel_netif_netdev_notifier = {264.notifier_call = sel_netif_netdev_notifier_handler,265};266267static __init int sel_netif_init(void)268{269int i;270271if (!selinux_enabled_boot)272return 0;273274for (i = 0; i < SEL_NETIF_HASH_SIZE; i++)275INIT_LIST_HEAD(&sel_netif_hash[i]);276277register_netdevice_notifier(&sel_netif_netdev_notifier);278279return 0;280}281282__initcall(sel_netif_init);283284285286