Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/security/selinux/netif.c
26378 views
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 <[email protected]>
9
*
10
* Copyright (C) 2003 Red Hat, Inc., James Morris <[email protected]>
11
* Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
12
* Paul Moore <[email protected]>
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(netif->nsec.ns, 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(netif->nsec.ns, netif->nsec.ifindex);
98
list_add_rcu(&netif->list, &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(&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(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(&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
160
/* If this memory allocation fails still return 0. The SID
161
* is valid, it just won't be added to the cache.
162
*/
163
new = kmalloc(sizeof(*new), GFP_ATOMIC);
164
if (new) {
165
new->nsec.ns = ns;
166
new->nsec.ifindex = ifindex;
167
new->nsec.sid = *sid;
168
if (sel_netif_insert(new))
169
kfree(new);
170
}
171
172
out:
173
spin_unlock_bh(&sel_netif_lock);
174
dev_put(dev);
175
if (unlikely(ret))
176
pr_warn("SELinux: failure in %s(), unable to determine network interface label (%d)\n",
177
__func__, ifindex);
178
return ret;
179
}
180
181
/**
182
* sel_netif_sid - Lookup the SID of a network interface
183
* @ns: the network namespace
184
* @ifindex: the network interface
185
* @sid: interface SID
186
*
187
* Description:
188
* This function determines the SID of a network interface using the fastest
189
* method possible. First the interface table is queried, but if an entry
190
* can't be found then the policy is queried and the result is added to the
191
* table to speedup future queries. Returns zero on success, negative values
192
* on failure.
193
*
194
*/
195
int sel_netif_sid(struct net *ns, int ifindex, u32 *sid)
196
{
197
struct sel_netif *netif;
198
199
rcu_read_lock();
200
netif = sel_netif_find(ns, ifindex);
201
if (likely(netif != NULL)) {
202
*sid = netif->nsec.sid;
203
rcu_read_unlock();
204
return 0;
205
}
206
rcu_read_unlock();
207
208
return sel_netif_sid_slow(ns, ifindex, sid);
209
}
210
211
/**
212
* sel_netif_kill - Remove an entry from the network interface table
213
* @ns: the network namespace
214
* @ifindex: the network interface
215
*
216
* Description:
217
* This function removes the entry matching @ifindex from the network interface
218
* table if it exists.
219
*
220
*/
221
static void sel_netif_kill(const struct net *ns, int ifindex)
222
{
223
struct sel_netif *netif;
224
225
rcu_read_lock();
226
spin_lock_bh(&sel_netif_lock);
227
netif = sel_netif_find(ns, ifindex);
228
if (netif)
229
sel_netif_destroy(netif);
230
spin_unlock_bh(&sel_netif_lock);
231
rcu_read_unlock();
232
}
233
234
/**
235
* sel_netif_flush - Flush the entire network interface table
236
*
237
* Description:
238
* Remove all entries from the network interface table.
239
*
240
*/
241
void sel_netif_flush(void)
242
{
243
int idx;
244
struct sel_netif *netif;
245
246
spin_lock_bh(&sel_netif_lock);
247
for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++)
248
list_for_each_entry(netif, &sel_netif_hash[idx], list)
249
sel_netif_destroy(netif);
250
spin_unlock_bh(&sel_netif_lock);
251
}
252
253
static int sel_netif_netdev_notifier_handler(struct notifier_block *this,
254
unsigned long event, void *ptr)
255
{
256
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
257
258
if (event == NETDEV_DOWN)
259
sel_netif_kill(dev_net(dev), dev->ifindex);
260
261
return NOTIFY_DONE;
262
}
263
264
static struct notifier_block sel_netif_netdev_notifier = {
265
.notifier_call = sel_netif_netdev_notifier_handler,
266
};
267
268
static __init int sel_netif_init(void)
269
{
270
int i;
271
272
if (!selinux_enabled_boot)
273
return 0;
274
275
for (i = 0; i < SEL_NETIF_HASH_SIZE; i++)
276
INIT_LIST_HEAD(&sel_netif_hash[i]);
277
278
register_netdevice_notifier(&sel_netif_netdev_notifier);
279
280
return 0;
281
}
282
283
__initcall(sel_netif_init);
284
285
286