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