162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Network interface table.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Network interfaces (devices) do not have a security field, so we
662306a36Sopenharmony_ci * maintain a table associating each interface with a SID.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Author: James Morris <jmorris@redhat.com>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
1162306a36Sopenharmony_ci * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
1262306a36Sopenharmony_ci *		      Paul Moore <paul@paul-moore.com>
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/types.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/stddef.h>
1862306a36Sopenharmony_ci#include <linux/kernel.h>
1962306a36Sopenharmony_ci#include <linux/list.h>
2062306a36Sopenharmony_ci#include <linux/notifier.h>
2162306a36Sopenharmony_ci#include <linux/netdevice.h>
2262306a36Sopenharmony_ci#include <linux/rcupdate.h>
2362306a36Sopenharmony_ci#include <net/net_namespace.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "security.h"
2662306a36Sopenharmony_ci#include "objsec.h"
2762306a36Sopenharmony_ci#include "netif.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define SEL_NETIF_HASH_SIZE	64
3062306a36Sopenharmony_ci#define SEL_NETIF_HASH_MAX	1024
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct sel_netif {
3362306a36Sopenharmony_ci	struct list_head list;
3462306a36Sopenharmony_ci	struct netif_security_struct nsec;
3562306a36Sopenharmony_ci	struct rcu_head rcu_head;
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic u32 sel_netif_total;
3962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(sel_netif_lock);
4062306a36Sopenharmony_cistatic struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE];
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/**
4362306a36Sopenharmony_ci * sel_netif_hashfn - Hashing function for the interface table
4462306a36Sopenharmony_ci * @ns: the network namespace
4562306a36Sopenharmony_ci * @ifindex: the network interface
4662306a36Sopenharmony_ci *
4762306a36Sopenharmony_ci * Description:
4862306a36Sopenharmony_ci * This is the hashing function for the network interface table, it returns the
4962306a36Sopenharmony_ci * bucket number for the given interface.
5062306a36Sopenharmony_ci *
5162306a36Sopenharmony_ci */
5262306a36Sopenharmony_cistatic inline u32 sel_netif_hashfn(const struct net *ns, int ifindex)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	return (((uintptr_t)ns + ifindex) & (SEL_NETIF_HASH_SIZE - 1));
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/**
5862306a36Sopenharmony_ci * sel_netif_find - Search for an interface record
5962306a36Sopenharmony_ci * @ns: the network namespace
6062306a36Sopenharmony_ci * @ifindex: the network interface
6162306a36Sopenharmony_ci *
6262306a36Sopenharmony_ci * Description:
6362306a36Sopenharmony_ci * Search the network interface table and return the record matching @ifindex.
6462306a36Sopenharmony_ci * If an entry can not be found in the table return NULL.
6562306a36Sopenharmony_ci *
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_cistatic inline struct sel_netif *sel_netif_find(const struct net *ns,
6862306a36Sopenharmony_ci					       int ifindex)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	u32 idx = sel_netif_hashfn(ns, ifindex);
7162306a36Sopenharmony_ci	struct sel_netif *netif;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list)
7462306a36Sopenharmony_ci		if (net_eq(netif->nsec.ns, ns) &&
7562306a36Sopenharmony_ci		    netif->nsec.ifindex == ifindex)
7662306a36Sopenharmony_ci			return netif;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return NULL;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/**
8262306a36Sopenharmony_ci * sel_netif_insert - Insert a new interface into the table
8362306a36Sopenharmony_ci * @netif: the new interface record
8462306a36Sopenharmony_ci *
8562306a36Sopenharmony_ci * Description:
8662306a36Sopenharmony_ci * Add a new interface record to the network interface hash table.  Returns
8762306a36Sopenharmony_ci * zero on success, negative values on failure.
8862306a36Sopenharmony_ci *
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_cistatic int sel_netif_insert(struct sel_netif *netif)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	u32 idx;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (sel_netif_total >= SEL_NETIF_HASH_MAX)
9562306a36Sopenharmony_ci		return -ENOSPC;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	idx = sel_netif_hashfn(netif->nsec.ns, netif->nsec.ifindex);
9862306a36Sopenharmony_ci	list_add_rcu(&netif->list, &sel_netif_hash[idx]);
9962306a36Sopenharmony_ci	sel_netif_total++;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return 0;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/**
10562306a36Sopenharmony_ci * sel_netif_destroy - Remove an interface record from the table
10662306a36Sopenharmony_ci * @netif: the existing interface record
10762306a36Sopenharmony_ci *
10862306a36Sopenharmony_ci * Description:
10962306a36Sopenharmony_ci * Remove an existing interface record from the network interface table.
11062306a36Sopenharmony_ci *
11162306a36Sopenharmony_ci */
11262306a36Sopenharmony_cistatic void sel_netif_destroy(struct sel_netif *netif)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	list_del_rcu(&netif->list);
11562306a36Sopenharmony_ci	sel_netif_total--;
11662306a36Sopenharmony_ci	kfree_rcu(netif, rcu_head);
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/**
12062306a36Sopenharmony_ci * sel_netif_sid_slow - Lookup the SID of a network interface using the policy
12162306a36Sopenharmony_ci * @ns: the network namespace
12262306a36Sopenharmony_ci * @ifindex: the network interface
12362306a36Sopenharmony_ci * @sid: interface SID
12462306a36Sopenharmony_ci *
12562306a36Sopenharmony_ci * Description:
12662306a36Sopenharmony_ci * This function determines the SID of a network interface by querying the
12762306a36Sopenharmony_ci * security policy.  The result is added to the network interface table to
12862306a36Sopenharmony_ci * speedup future queries.  Returns zero on success, negative values on
12962306a36Sopenharmony_ci * failure.
13062306a36Sopenharmony_ci *
13162306a36Sopenharmony_ci */
13262306a36Sopenharmony_cistatic int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	int ret = 0;
13562306a36Sopenharmony_ci	struct sel_netif *netif;
13662306a36Sopenharmony_ci	struct sel_netif *new;
13762306a36Sopenharmony_ci	struct net_device *dev;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	/* NOTE: we always use init's network namespace since we don't
14062306a36Sopenharmony_ci	 * currently support containers */
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	dev = dev_get_by_index(ns, ifindex);
14362306a36Sopenharmony_ci	if (unlikely(dev == NULL)) {
14462306a36Sopenharmony_ci		pr_warn("SELinux: failure in %s(), invalid network interface (%d)\n",
14562306a36Sopenharmony_ci			__func__, ifindex);
14662306a36Sopenharmony_ci		return -ENOENT;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	spin_lock_bh(&sel_netif_lock);
15062306a36Sopenharmony_ci	netif = sel_netif_find(ns, ifindex);
15162306a36Sopenharmony_ci	if (netif != NULL) {
15262306a36Sopenharmony_ci		*sid = netif->nsec.sid;
15362306a36Sopenharmony_ci		goto out;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	ret = security_netif_sid(dev->name, sid);
15762306a36Sopenharmony_ci	if (ret != 0)
15862306a36Sopenharmony_ci		goto out;
15962306a36Sopenharmony_ci	new = kzalloc(sizeof(*new), GFP_ATOMIC);
16062306a36Sopenharmony_ci	if (new) {
16162306a36Sopenharmony_ci		new->nsec.ns = ns;
16262306a36Sopenharmony_ci		new->nsec.ifindex = ifindex;
16362306a36Sopenharmony_ci		new->nsec.sid = *sid;
16462306a36Sopenharmony_ci		if (sel_netif_insert(new))
16562306a36Sopenharmony_ci			kfree(new);
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ciout:
16962306a36Sopenharmony_ci	spin_unlock_bh(&sel_netif_lock);
17062306a36Sopenharmony_ci	dev_put(dev);
17162306a36Sopenharmony_ci	if (unlikely(ret))
17262306a36Sopenharmony_ci		pr_warn("SELinux: failure in %s(), unable to determine network interface label (%d)\n",
17362306a36Sopenharmony_ci			__func__, ifindex);
17462306a36Sopenharmony_ci	return ret;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci/**
17862306a36Sopenharmony_ci * sel_netif_sid - Lookup the SID of a network interface
17962306a36Sopenharmony_ci * @ns: the network namespace
18062306a36Sopenharmony_ci * @ifindex: the network interface
18162306a36Sopenharmony_ci * @sid: interface SID
18262306a36Sopenharmony_ci *
18362306a36Sopenharmony_ci * Description:
18462306a36Sopenharmony_ci * This function determines the SID of a network interface using the fastest
18562306a36Sopenharmony_ci * method possible.  First the interface table is queried, but if an entry
18662306a36Sopenharmony_ci * can't be found then the policy is queried and the result is added to the
18762306a36Sopenharmony_ci * table to speedup future queries.  Returns zero on success, negative values
18862306a36Sopenharmony_ci * on failure.
18962306a36Sopenharmony_ci *
19062306a36Sopenharmony_ci */
19162306a36Sopenharmony_ciint sel_netif_sid(struct net *ns, int ifindex, u32 *sid)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	struct sel_netif *netif;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	rcu_read_lock();
19662306a36Sopenharmony_ci	netif = sel_netif_find(ns, ifindex);
19762306a36Sopenharmony_ci	if (likely(netif != NULL)) {
19862306a36Sopenharmony_ci		*sid = netif->nsec.sid;
19962306a36Sopenharmony_ci		rcu_read_unlock();
20062306a36Sopenharmony_ci		return 0;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci	rcu_read_unlock();
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return sel_netif_sid_slow(ns, ifindex, sid);
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci/**
20862306a36Sopenharmony_ci * sel_netif_kill - Remove an entry from the network interface table
20962306a36Sopenharmony_ci * @ns: the network namespace
21062306a36Sopenharmony_ci * @ifindex: the network interface
21162306a36Sopenharmony_ci *
21262306a36Sopenharmony_ci * Description:
21362306a36Sopenharmony_ci * This function removes the entry matching @ifindex from the network interface
21462306a36Sopenharmony_ci * table if it exists.
21562306a36Sopenharmony_ci *
21662306a36Sopenharmony_ci */
21762306a36Sopenharmony_cistatic void sel_netif_kill(const struct net *ns, int ifindex)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct sel_netif *netif;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	rcu_read_lock();
22262306a36Sopenharmony_ci	spin_lock_bh(&sel_netif_lock);
22362306a36Sopenharmony_ci	netif = sel_netif_find(ns, ifindex);
22462306a36Sopenharmony_ci	if (netif)
22562306a36Sopenharmony_ci		sel_netif_destroy(netif);
22662306a36Sopenharmony_ci	spin_unlock_bh(&sel_netif_lock);
22762306a36Sopenharmony_ci	rcu_read_unlock();
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/**
23162306a36Sopenharmony_ci * sel_netif_flush - Flush the entire network interface table
23262306a36Sopenharmony_ci *
23362306a36Sopenharmony_ci * Description:
23462306a36Sopenharmony_ci * Remove all entries from the network interface table.
23562306a36Sopenharmony_ci *
23662306a36Sopenharmony_ci */
23762306a36Sopenharmony_civoid sel_netif_flush(void)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	int idx;
24062306a36Sopenharmony_ci	struct sel_netif *netif;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	spin_lock_bh(&sel_netif_lock);
24362306a36Sopenharmony_ci	for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++)
24462306a36Sopenharmony_ci		list_for_each_entry(netif, &sel_netif_hash[idx], list)
24562306a36Sopenharmony_ci			sel_netif_destroy(netif);
24662306a36Sopenharmony_ci	spin_unlock_bh(&sel_netif_lock);
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic int sel_netif_netdev_notifier_handler(struct notifier_block *this,
25062306a36Sopenharmony_ci					     unsigned long event, void *ptr)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (event == NETDEV_DOWN)
25562306a36Sopenharmony_ci		sel_netif_kill(dev_net(dev), dev->ifindex);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	return NOTIFY_DONE;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic struct notifier_block sel_netif_netdev_notifier = {
26162306a36Sopenharmony_ci	.notifier_call = sel_netif_netdev_notifier_handler,
26262306a36Sopenharmony_ci};
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic __init int sel_netif_init(void)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	int i;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (!selinux_enabled_boot)
26962306a36Sopenharmony_ci		return 0;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	for (i = 0; i < SEL_NETIF_HASH_SIZE; i++)
27262306a36Sopenharmony_ci		INIT_LIST_HEAD(&sel_netif_hash[i]);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	register_netdevice_notifier(&sel_netif_netdev_notifier);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	return 0;
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci__initcall(sel_netif_init);
28062306a36Sopenharmony_ci
281