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