18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Network interface table.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Network interfaces (devices) do not have a security field, so we
68c2ecf20Sopenharmony_ci * maintain a table associating each interface with a SID.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Author: James Morris <jmorris@redhat.com>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
118c2ecf20Sopenharmony_ci * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
128c2ecf20Sopenharmony_ci *		      Paul Moore <paul@paul-moore.com>
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci#include <linux/init.h>
158c2ecf20Sopenharmony_ci#include <linux/types.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/stddef.h>
188c2ecf20Sopenharmony_ci#include <linux/kernel.h>
198c2ecf20Sopenharmony_ci#include <linux/list.h>
208c2ecf20Sopenharmony_ci#include <linux/notifier.h>
218c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
228c2ecf20Sopenharmony_ci#include <linux/rcupdate.h>
238c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "security.h"
268c2ecf20Sopenharmony_ci#include "objsec.h"
278c2ecf20Sopenharmony_ci#include "netif.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define SEL_NETIF_HASH_SIZE	64
308c2ecf20Sopenharmony_ci#define SEL_NETIF_HASH_MAX	1024
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct sel_netif {
338c2ecf20Sopenharmony_ci	struct list_head list;
348c2ecf20Sopenharmony_ci	struct netif_security_struct nsec;
358c2ecf20Sopenharmony_ci	struct rcu_head rcu_head;
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic u32 sel_netif_total;
398c2ecf20Sopenharmony_cistatic LIST_HEAD(sel_netif_list);
408c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(sel_netif_lock);
418c2ecf20Sopenharmony_cistatic struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE];
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/**
448c2ecf20Sopenharmony_ci * sel_netif_hashfn - Hashing function for the interface table
458c2ecf20Sopenharmony_ci * @ns: the network namespace
468c2ecf20Sopenharmony_ci * @ifindex: the network interface
478c2ecf20Sopenharmony_ci *
488c2ecf20Sopenharmony_ci * Description:
498c2ecf20Sopenharmony_ci * This is the hashing function for the network interface table, it returns the
508c2ecf20Sopenharmony_ci * bucket number for the given interface.
518c2ecf20Sopenharmony_ci *
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_cistatic inline u32 sel_netif_hashfn(const struct net *ns, int ifindex)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	return (((uintptr_t)ns + ifindex) & (SEL_NETIF_HASH_SIZE - 1));
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/**
598c2ecf20Sopenharmony_ci * sel_netif_find - Search for an interface record
608c2ecf20Sopenharmony_ci * @ns: the network namespace
618c2ecf20Sopenharmony_ci * @ifindex: the network interface
628c2ecf20Sopenharmony_ci *
638c2ecf20Sopenharmony_ci * Description:
648c2ecf20Sopenharmony_ci * Search the network interface table and return the record matching @ifindex.
658c2ecf20Sopenharmony_ci * If an entry can not be found in the table return NULL.
668c2ecf20Sopenharmony_ci *
678c2ecf20Sopenharmony_ci */
688c2ecf20Sopenharmony_cistatic inline struct sel_netif *sel_netif_find(const struct net *ns,
698c2ecf20Sopenharmony_ci					       int ifindex)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	int idx = sel_netif_hashfn(ns, ifindex);
728c2ecf20Sopenharmony_ci	struct sel_netif *netif;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list)
758c2ecf20Sopenharmony_ci		if (net_eq(netif->nsec.ns, ns) &&
768c2ecf20Sopenharmony_ci		    netif->nsec.ifindex == ifindex)
778c2ecf20Sopenharmony_ci			return netif;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	return NULL;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/**
838c2ecf20Sopenharmony_ci * sel_netif_insert - Insert a new interface into the table
848c2ecf20Sopenharmony_ci * @netif: the new interface record
858c2ecf20Sopenharmony_ci *
868c2ecf20Sopenharmony_ci * Description:
878c2ecf20Sopenharmony_ci * Add a new interface record to the network interface hash table.  Returns
888c2ecf20Sopenharmony_ci * zero on success, negative values on failure.
898c2ecf20Sopenharmony_ci *
908c2ecf20Sopenharmony_ci */
918c2ecf20Sopenharmony_cistatic int sel_netif_insert(struct sel_netif *netif)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	int idx;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (sel_netif_total >= SEL_NETIF_HASH_MAX)
968c2ecf20Sopenharmony_ci		return -ENOSPC;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	idx = sel_netif_hashfn(netif->nsec.ns, netif->nsec.ifindex);
998c2ecf20Sopenharmony_ci	list_add_rcu(&netif->list, &sel_netif_hash[idx]);
1008c2ecf20Sopenharmony_ci	sel_netif_total++;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return 0;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/**
1068c2ecf20Sopenharmony_ci * sel_netif_destroy - Remove an interface record from the table
1078c2ecf20Sopenharmony_ci * @netif: the existing interface record
1088c2ecf20Sopenharmony_ci *
1098c2ecf20Sopenharmony_ci * Description:
1108c2ecf20Sopenharmony_ci * Remove an existing interface record from the network interface table.
1118c2ecf20Sopenharmony_ci *
1128c2ecf20Sopenharmony_ci */
1138c2ecf20Sopenharmony_cistatic void sel_netif_destroy(struct sel_netif *netif)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	list_del_rcu(&netif->list);
1168c2ecf20Sopenharmony_ci	sel_netif_total--;
1178c2ecf20Sopenharmony_ci	kfree_rcu(netif, rcu_head);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci/**
1218c2ecf20Sopenharmony_ci * sel_netif_sid_slow - Lookup the SID of a network interface using the policy
1228c2ecf20Sopenharmony_ci * @ns: the network namespace
1238c2ecf20Sopenharmony_ci * @ifindex: the network interface
1248c2ecf20Sopenharmony_ci * @sid: interface SID
1258c2ecf20Sopenharmony_ci *
1268c2ecf20Sopenharmony_ci * Description:
1278c2ecf20Sopenharmony_ci * This function determines the SID of a network interface by querying the
1288c2ecf20Sopenharmony_ci * security policy.  The result is added to the network interface table to
1298c2ecf20Sopenharmony_ci * speedup future queries.  Returns zero on success, negative values on
1308c2ecf20Sopenharmony_ci * failure.
1318c2ecf20Sopenharmony_ci *
1328c2ecf20Sopenharmony_ci */
1338c2ecf20Sopenharmony_cistatic int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	int ret = 0;
1368c2ecf20Sopenharmony_ci	struct sel_netif *netif;
1378c2ecf20Sopenharmony_ci	struct sel_netif *new;
1388c2ecf20Sopenharmony_ci	struct net_device *dev;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* NOTE: we always use init's network namespace since we don't
1418c2ecf20Sopenharmony_ci	 * currently support containers */
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	dev = dev_get_by_index(ns, ifindex);
1448c2ecf20Sopenharmony_ci	if (unlikely(dev == NULL)) {
1458c2ecf20Sopenharmony_ci		pr_warn("SELinux: failure in %s(), invalid network interface (%d)\n",
1468c2ecf20Sopenharmony_ci			__func__, ifindex);
1478c2ecf20Sopenharmony_ci		return -ENOENT;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	spin_lock_bh(&sel_netif_lock);
1518c2ecf20Sopenharmony_ci	netif = sel_netif_find(ns, ifindex);
1528c2ecf20Sopenharmony_ci	if (netif != NULL) {
1538c2ecf20Sopenharmony_ci		*sid = netif->nsec.sid;
1548c2ecf20Sopenharmony_ci		goto out;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	ret = security_netif_sid(&selinux_state, dev->name, sid);
1588c2ecf20Sopenharmony_ci	if (ret != 0)
1598c2ecf20Sopenharmony_ci		goto out;
1608c2ecf20Sopenharmony_ci	new = kzalloc(sizeof(*new), GFP_ATOMIC);
1618c2ecf20Sopenharmony_ci	if (new) {
1628c2ecf20Sopenharmony_ci		new->nsec.ns = ns;
1638c2ecf20Sopenharmony_ci		new->nsec.ifindex = ifindex;
1648c2ecf20Sopenharmony_ci		new->nsec.sid = *sid;
1658c2ecf20Sopenharmony_ci		if (sel_netif_insert(new))
1668c2ecf20Sopenharmony_ci			kfree(new);
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ciout:
1708c2ecf20Sopenharmony_ci	spin_unlock_bh(&sel_netif_lock);
1718c2ecf20Sopenharmony_ci	dev_put(dev);
1728c2ecf20Sopenharmony_ci	if (unlikely(ret))
1738c2ecf20Sopenharmony_ci		pr_warn("SELinux: failure in %s(), unable to determine network interface label (%d)\n",
1748c2ecf20Sopenharmony_ci			__func__, ifindex);
1758c2ecf20Sopenharmony_ci	return ret;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/**
1798c2ecf20Sopenharmony_ci * sel_netif_sid - Lookup the SID of a network interface
1808c2ecf20Sopenharmony_ci * @ns: the network namespace
1818c2ecf20Sopenharmony_ci * @ifindex: the network interface
1828c2ecf20Sopenharmony_ci * @sid: interface SID
1838c2ecf20Sopenharmony_ci *
1848c2ecf20Sopenharmony_ci * Description:
1858c2ecf20Sopenharmony_ci * This function determines the SID of a network interface using the fastest
1868c2ecf20Sopenharmony_ci * method possible.  First the interface table is queried, but if an entry
1878c2ecf20Sopenharmony_ci * can't be found then the policy is queried and the result is added to the
1888c2ecf20Sopenharmony_ci * table to speedup future queries.  Returns zero on success, negative values
1898c2ecf20Sopenharmony_ci * on failure.
1908c2ecf20Sopenharmony_ci *
1918c2ecf20Sopenharmony_ci */
1928c2ecf20Sopenharmony_ciint sel_netif_sid(struct net *ns, int ifindex, u32 *sid)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct sel_netif *netif;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	rcu_read_lock();
1978c2ecf20Sopenharmony_ci	netif = sel_netif_find(ns, ifindex);
1988c2ecf20Sopenharmony_ci	if (likely(netif != NULL)) {
1998c2ecf20Sopenharmony_ci		*sid = netif->nsec.sid;
2008c2ecf20Sopenharmony_ci		rcu_read_unlock();
2018c2ecf20Sopenharmony_ci		return 0;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci	rcu_read_unlock();
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return sel_netif_sid_slow(ns, ifindex, sid);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci/**
2098c2ecf20Sopenharmony_ci * sel_netif_kill - Remove an entry from the network interface table
2108c2ecf20Sopenharmony_ci * @ns: the network namespace
2118c2ecf20Sopenharmony_ci * @ifindex: the network interface
2128c2ecf20Sopenharmony_ci *
2138c2ecf20Sopenharmony_ci * Description:
2148c2ecf20Sopenharmony_ci * This function removes the entry matching @ifindex from the network interface
2158c2ecf20Sopenharmony_ci * table if it exists.
2168c2ecf20Sopenharmony_ci *
2178c2ecf20Sopenharmony_ci */
2188c2ecf20Sopenharmony_cistatic void sel_netif_kill(const struct net *ns, int ifindex)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	struct sel_netif *netif;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	rcu_read_lock();
2238c2ecf20Sopenharmony_ci	spin_lock_bh(&sel_netif_lock);
2248c2ecf20Sopenharmony_ci	netif = sel_netif_find(ns, ifindex);
2258c2ecf20Sopenharmony_ci	if (netif)
2268c2ecf20Sopenharmony_ci		sel_netif_destroy(netif);
2278c2ecf20Sopenharmony_ci	spin_unlock_bh(&sel_netif_lock);
2288c2ecf20Sopenharmony_ci	rcu_read_unlock();
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci/**
2328c2ecf20Sopenharmony_ci * sel_netif_flush - Flush the entire network interface table
2338c2ecf20Sopenharmony_ci *
2348c2ecf20Sopenharmony_ci * Description:
2358c2ecf20Sopenharmony_ci * Remove all entries from the network interface table.
2368c2ecf20Sopenharmony_ci *
2378c2ecf20Sopenharmony_ci */
2388c2ecf20Sopenharmony_civoid sel_netif_flush(void)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	int idx;
2418c2ecf20Sopenharmony_ci	struct sel_netif *netif;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	spin_lock_bh(&sel_netif_lock);
2448c2ecf20Sopenharmony_ci	for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++)
2458c2ecf20Sopenharmony_ci		list_for_each_entry(netif, &sel_netif_hash[idx], list)
2468c2ecf20Sopenharmony_ci			sel_netif_destroy(netif);
2478c2ecf20Sopenharmony_ci	spin_unlock_bh(&sel_netif_lock);
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic int sel_netif_netdev_notifier_handler(struct notifier_block *this,
2518c2ecf20Sopenharmony_ci					     unsigned long event, void *ptr)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (event == NETDEV_DOWN)
2568c2ecf20Sopenharmony_ci		sel_netif_kill(dev_net(dev), dev->ifindex);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic struct notifier_block sel_netif_netdev_notifier = {
2628c2ecf20Sopenharmony_ci	.notifier_call = sel_netif_netdev_notifier_handler,
2638c2ecf20Sopenharmony_ci};
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic __init int sel_netif_init(void)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	int i;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (!selinux_enabled_boot)
2708c2ecf20Sopenharmony_ci		return 0;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	for (i = 0; i < SEL_NETIF_HASH_SIZE; i++)
2738c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&sel_netif_hash[i]);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	register_netdevice_notifier(&sel_netif_netdev_notifier);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	return 0;
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci__initcall(sel_netif_init);
2818c2ecf20Sopenharmony_ci
282