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 DEFINE_SPINLOCK(sel_netif_lock); |
40 | static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE]; |
41 | |
42 | /** |
43 | * sel_netif_hashfn - Hashing function for the interface table |
44 | * @ns: the network namespace |
45 | * @ifindex: the network interface |
46 | * |
47 | * Description: |
48 | * This is the hashing function for the network interface table, it returns the |
49 | * bucket number for the given interface. |
50 | * |
51 | */ |
52 | static inline u32 sel_netif_hashfn(const struct net *ns, int ifindex) |
53 | { |
54 | return (((uintptr_t)ns + ifindex) & (SEL_NETIF_HASH_SIZE - 1)); |
55 | } |
56 | |
57 | /** |
58 | * sel_netif_find - Search for an interface record |
59 | * @ns: the network namespace |
60 | * @ifindex: the network interface |
61 | * |
62 | * Description: |
63 | * Search the network interface table and return the record matching @ifindex. |
64 | * If an entry can not be found in the table return NULL. |
65 | * |
66 | */ |
67 | static inline struct sel_netif *sel_netif_find(const struct net *ns, |
68 | int ifindex) |
69 | { |
70 | u32 idx = sel_netif_hashfn(ns, ifindex); |
71 | struct sel_netif *netif; |
72 | |
73 | list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list) |
74 | if (net_eq(net1: netif->nsec.ns, net2: ns) && |
75 | netif->nsec.ifindex == ifindex) |
76 | return netif; |
77 | |
78 | return NULL; |
79 | } |
80 | |
81 | /** |
82 | * sel_netif_insert - Insert a new interface into the table |
83 | * @netif: the new interface record |
84 | * |
85 | * Description: |
86 | * Add a new interface record to the network interface hash table. Returns |
87 | * zero on success, negative values on failure. |
88 | * |
89 | */ |
90 | static int sel_netif_insert(struct sel_netif *netif) |
91 | { |
92 | u32 idx; |
93 | |
94 | if (sel_netif_total >= SEL_NETIF_HASH_MAX) |
95 | return -ENOSPC; |
96 | |
97 | idx = sel_netif_hashfn(ns: netif->nsec.ns, ifindex: netif->nsec.ifindex); |
98 | list_add_rcu(new: &netif->list, head: &sel_netif_hash[idx]); |
99 | sel_netif_total++; |
100 | |
101 | return 0; |
102 | } |
103 | |
104 | /** |
105 | * sel_netif_destroy - Remove an interface record from the table |
106 | * @netif: the existing interface record |
107 | * |
108 | * Description: |
109 | * Remove an existing interface record from the network interface table. |
110 | * |
111 | */ |
112 | static void sel_netif_destroy(struct sel_netif *netif) |
113 | { |
114 | list_del_rcu(entry: &netif->list); |
115 | sel_netif_total--; |
116 | kfree_rcu(netif, rcu_head); |
117 | } |
118 | |
119 | /** |
120 | * sel_netif_sid_slow - Lookup the SID of a network interface using the policy |
121 | * @ns: the network namespace |
122 | * @ifindex: the network interface |
123 | * @sid: interface SID |
124 | * |
125 | * Description: |
126 | * This function determines the SID of a network interface by querying the |
127 | * security policy. The result is added to the network interface table to |
128 | * speedup future queries. Returns zero on success, negative values on |
129 | * failure. |
130 | * |
131 | */ |
132 | static int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid) |
133 | { |
134 | int ret = 0; |
135 | struct sel_netif *netif; |
136 | struct sel_netif *new; |
137 | struct net_device *dev; |
138 | |
139 | /* NOTE: we always use init's network namespace since we don't |
140 | * currently support containers */ |
141 | |
142 | dev = dev_get_by_index(net: ns, ifindex); |
143 | if (unlikely(dev == NULL)) { |
144 | pr_warn("SELinux: failure in %s(), invalid network interface (%d)\n" , |
145 | __func__, ifindex); |
146 | return -ENOENT; |
147 | } |
148 | |
149 | spin_lock_bh(lock: &sel_netif_lock); |
150 | netif = sel_netif_find(ns, ifindex); |
151 | if (netif != NULL) { |
152 | *sid = netif->nsec.sid; |
153 | goto out; |
154 | } |
155 | |
156 | ret = security_netif_sid(dev->name, sid); |
157 | if (ret != 0) |
158 | goto out; |
159 | new = kzalloc(size: sizeof(*new), GFP_ATOMIC); |
160 | if (new) { |
161 | new->nsec.ns = ns; |
162 | new->nsec.ifindex = ifindex; |
163 | new->nsec.sid = *sid; |
164 | if (sel_netif_insert(netif: new)) |
165 | kfree(objp: new); |
166 | } |
167 | |
168 | out: |
169 | spin_unlock_bh(lock: &sel_netif_lock); |
170 | dev_put(dev); |
171 | if (unlikely(ret)) |
172 | pr_warn("SELinux: failure in %s(), unable to determine network interface label (%d)\n" , |
173 | __func__, ifindex); |
174 | return ret; |
175 | } |
176 | |
177 | /** |
178 | * sel_netif_sid - Lookup the SID of a network interface |
179 | * @ns: the network namespace |
180 | * @ifindex: the network interface |
181 | * @sid: interface SID |
182 | * |
183 | * Description: |
184 | * This function determines the SID of a network interface using the fastest |
185 | * method possible. First the interface table is queried, but if an entry |
186 | * can't be found then the policy is queried and the result is added to the |
187 | * table to speedup future queries. Returns zero on success, negative values |
188 | * on failure. |
189 | * |
190 | */ |
191 | int sel_netif_sid(struct net *ns, int ifindex, u32 *sid) |
192 | { |
193 | struct sel_netif *netif; |
194 | |
195 | rcu_read_lock(); |
196 | netif = sel_netif_find(ns, ifindex); |
197 | if (likely(netif != NULL)) { |
198 | *sid = netif->nsec.sid; |
199 | rcu_read_unlock(); |
200 | return 0; |
201 | } |
202 | rcu_read_unlock(); |
203 | |
204 | return sel_netif_sid_slow(ns, ifindex, sid); |
205 | } |
206 | |
207 | /** |
208 | * sel_netif_kill - Remove an entry from the network interface table |
209 | * @ns: the network namespace |
210 | * @ifindex: the network interface |
211 | * |
212 | * Description: |
213 | * This function removes the entry matching @ifindex from the network interface |
214 | * table if it exists. |
215 | * |
216 | */ |
217 | static void sel_netif_kill(const struct net *ns, int ifindex) |
218 | { |
219 | struct sel_netif *netif; |
220 | |
221 | rcu_read_lock(); |
222 | spin_lock_bh(lock: &sel_netif_lock); |
223 | netif = sel_netif_find(ns, ifindex); |
224 | if (netif) |
225 | sel_netif_destroy(netif); |
226 | spin_unlock_bh(lock: &sel_netif_lock); |
227 | rcu_read_unlock(); |
228 | } |
229 | |
230 | /** |
231 | * sel_netif_flush - Flush the entire network interface table |
232 | * |
233 | * Description: |
234 | * Remove all entries from the network interface table. |
235 | * |
236 | */ |
237 | void sel_netif_flush(void) |
238 | { |
239 | int idx; |
240 | struct sel_netif *netif; |
241 | |
242 | spin_lock_bh(lock: &sel_netif_lock); |
243 | for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++) |
244 | list_for_each_entry(netif, &sel_netif_hash[idx], list) |
245 | sel_netif_destroy(netif); |
246 | spin_unlock_bh(lock: &sel_netif_lock); |
247 | } |
248 | |
249 | static int sel_netif_netdev_notifier_handler(struct notifier_block *this, |
250 | unsigned long event, void *ptr) |
251 | { |
252 | struct net_device *dev = netdev_notifier_info_to_dev(info: ptr); |
253 | |
254 | if (event == NETDEV_DOWN) |
255 | sel_netif_kill(ns: dev_net(dev), ifindex: dev->ifindex); |
256 | |
257 | return NOTIFY_DONE; |
258 | } |
259 | |
260 | static struct notifier_block sel_netif_netdev_notifier = { |
261 | .notifier_call = sel_netif_netdev_notifier_handler, |
262 | }; |
263 | |
264 | static __init int sel_netif_init(void) |
265 | { |
266 | int i; |
267 | |
268 | if (!selinux_enabled_boot) |
269 | return 0; |
270 | |
271 | for (i = 0; i < SEL_NETIF_HASH_SIZE; i++) |
272 | INIT_LIST_HEAD(list: &sel_netif_hash[i]); |
273 | |
274 | register_netdevice_notifier(nb: &sel_netif_netdev_notifier); |
275 | |
276 | return 0; |
277 | } |
278 | |
279 | __initcall(sel_netif_init); |
280 | |
281 | |