Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/security/selinux/netnode.c
49086 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Network node table
4
*
5
* SELinux must keep a mapping of network nodes to labels/SIDs. This
6
* mapping is maintained as part of the normal policy but a fast cache is
7
* needed to reduce the lookup overhead since most of these queries happen on
8
* a per-packet basis.
9
*
10
* Author: Paul Moore <[email protected]>
11
*
12
* This code is heavily based on the "netif" concept originally developed by
13
* James Morris <[email protected]>
14
* (see security/selinux/netif.c for more information)
15
*/
16
17
/*
18
* (c) Copyright Hewlett-Packard Development Company, L.P., 2007
19
*/
20
21
#include <linux/types.h>
22
#include <linux/rcupdate.h>
23
#include <linux/list.h>
24
#include <linux/slab.h>
25
#include <linux/spinlock.h>
26
#include <linux/in.h>
27
#include <linux/in6.h>
28
#include <linux/ip.h>
29
#include <linux/ipv6.h>
30
#include <net/ip.h>
31
#include <net/ipv6.h>
32
33
#include "initcalls.h"
34
#include "netnode.h"
35
#include "objsec.h"
36
37
#define SEL_NETNODE_HASH_SIZE 256
38
#define SEL_NETNODE_HASH_BKT_LIMIT 16
39
40
struct sel_netnode_bkt {
41
unsigned int size;
42
struct list_head list;
43
};
44
45
struct sel_netnode {
46
struct netnode_security_struct nsec;
47
48
struct list_head list;
49
struct rcu_head rcu;
50
};
51
52
/* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason
53
* for this is that I suspect most users will not make heavy use of both
54
* address families at the same time so one table will usually end up wasted,
55
* if this becomes a problem we can always add a hash table for each address
56
* family later */
57
58
static DEFINE_SPINLOCK(sel_netnode_lock);
59
static struct sel_netnode_bkt sel_netnode_hash[SEL_NETNODE_HASH_SIZE];
60
61
/**
62
* sel_netnode_hashfn_ipv4 - IPv4 hashing function for the node table
63
* @addr: IPv4 address
64
*
65
* Description:
66
* This is the IPv4 hashing function for the node interface table, it returns
67
* the bucket number for the given IP address.
68
*
69
*/
70
static unsigned int sel_netnode_hashfn_ipv4(__be32 addr)
71
{
72
/* at some point we should determine if the mismatch in byte order
73
* affects the hash function dramatically */
74
return (addr & (SEL_NETNODE_HASH_SIZE - 1));
75
}
76
77
/**
78
* sel_netnode_hashfn_ipv6 - IPv6 hashing function for the node table
79
* @addr: IPv6 address
80
*
81
* Description:
82
* This is the IPv6 hashing function for the node interface table, it returns
83
* the bucket number for the given IP address.
84
*
85
*/
86
static unsigned int sel_netnode_hashfn_ipv6(const struct in6_addr *addr)
87
{
88
/* just hash the least significant 32 bits to keep things fast (they
89
* are the most likely to be different anyway), we can revisit this
90
* later if needed */
91
return (addr->s6_addr32[3] & (SEL_NETNODE_HASH_SIZE - 1));
92
}
93
94
/**
95
* sel_netnode_find - Search for a node record
96
* @addr: IP address
97
* @family: address family
98
*
99
* Description:
100
* Search the network node table and return the record matching @addr. If an
101
* entry can not be found in the table return NULL.
102
*
103
*/
104
static struct sel_netnode *sel_netnode_find(const void *addr, u16 family)
105
{
106
unsigned int idx;
107
struct sel_netnode *node;
108
109
switch (family) {
110
case PF_INET:
111
idx = sel_netnode_hashfn_ipv4(*(const __be32 *)addr);
112
break;
113
case PF_INET6:
114
idx = sel_netnode_hashfn_ipv6(addr);
115
break;
116
default:
117
BUG();
118
return NULL;
119
}
120
121
list_for_each_entry_rcu(node, &sel_netnode_hash[idx].list, list)
122
if (node->nsec.family == family)
123
switch (family) {
124
case PF_INET:
125
if (node->nsec.addr.ipv4 == *(const __be32 *)addr)
126
return node;
127
break;
128
case PF_INET6:
129
if (ipv6_addr_equal(&node->nsec.addr.ipv6,
130
addr))
131
return node;
132
break;
133
}
134
135
return NULL;
136
}
137
138
/**
139
* sel_netnode_insert - Insert a new node into the table
140
* @node: the new node record
141
*
142
* Description:
143
* Add a new node record to the network address hash table.
144
*
145
*/
146
static void sel_netnode_insert(struct sel_netnode *node)
147
{
148
unsigned int idx;
149
150
switch (node->nsec.family) {
151
case PF_INET:
152
idx = sel_netnode_hashfn_ipv4(node->nsec.addr.ipv4);
153
break;
154
case PF_INET6:
155
idx = sel_netnode_hashfn_ipv6(&node->nsec.addr.ipv6);
156
break;
157
default:
158
BUG();
159
return;
160
}
161
162
/* we need to impose a limit on the growth of the hash table so check
163
* this bucket to make sure it is within the specified bounds */
164
list_add_rcu(&node->list, &sel_netnode_hash[idx].list);
165
if (sel_netnode_hash[idx].size == SEL_NETNODE_HASH_BKT_LIMIT) {
166
struct sel_netnode *tail;
167
tail = list_entry(
168
rcu_dereference_protected(
169
list_tail_rcu(&sel_netnode_hash[idx].list),
170
lockdep_is_held(&sel_netnode_lock)),
171
struct sel_netnode, list);
172
list_del_rcu(&tail->list);
173
kfree_rcu(tail, rcu);
174
} else
175
sel_netnode_hash[idx].size++;
176
}
177
178
/**
179
* sel_netnode_sid_slow - Lookup the SID of a network address using the policy
180
* @addr: the IP address
181
* @family: the address family
182
* @sid: node SID
183
*
184
* Description:
185
* This function determines the SID of a network address by querying the
186
* security policy. The result is added to the network address table to
187
* speedup future queries. Returns zero on success, negative values on
188
* failure.
189
*
190
*/
191
static int sel_netnode_sid_slow(const void *addr, u16 family, u32 *sid)
192
{
193
int ret;
194
struct sel_netnode *node;
195
struct sel_netnode *new;
196
197
spin_lock_bh(&sel_netnode_lock);
198
node = sel_netnode_find(addr, family);
199
if (node != NULL) {
200
*sid = node->nsec.sid;
201
spin_unlock_bh(&sel_netnode_lock);
202
return 0;
203
}
204
205
/* If this memory allocation fails still return 0. The SID
206
* is valid, it just won't be added to the cache.
207
*/
208
new = kmalloc(sizeof(*new), GFP_ATOMIC);
209
switch (family) {
210
case PF_INET:
211
ret = security_node_sid(PF_INET,
212
addr, sizeof(struct in_addr), sid);
213
if (new)
214
new->nsec.addr.ipv4 = *(const __be32 *)addr;
215
break;
216
case PF_INET6:
217
ret = security_node_sid(PF_INET6,
218
addr, sizeof(struct in6_addr), sid);
219
if (new)
220
new->nsec.addr.ipv6 = *(const struct in6_addr *)addr;
221
break;
222
default:
223
BUG();
224
ret = -EINVAL;
225
}
226
if (ret == 0 && new) {
227
new->nsec.family = family;
228
new->nsec.sid = *sid;
229
sel_netnode_insert(new);
230
} else
231
kfree(new);
232
233
spin_unlock_bh(&sel_netnode_lock);
234
if (unlikely(ret))
235
pr_warn("SELinux: failure in %s(), unable to determine network node label\n",
236
__func__);
237
return ret;
238
}
239
240
/**
241
* sel_netnode_sid - Lookup the SID of a network address
242
* @addr: the IP address
243
* @family: the address family
244
* @sid: node SID
245
*
246
* Description:
247
* This function determines the SID of a network address using the fastest
248
* method possible. First the address table is queried, but if an entry
249
* can't be found then the policy is queried and the result is added to the
250
* table to speedup future queries. Returns zero on success, negative values
251
* on failure.
252
*
253
*/
254
int sel_netnode_sid(const void *addr, u16 family, u32 *sid)
255
{
256
struct sel_netnode *node;
257
258
rcu_read_lock();
259
node = sel_netnode_find(addr, family);
260
if (likely(node != NULL)) {
261
*sid = node->nsec.sid;
262
rcu_read_unlock();
263
return 0;
264
}
265
rcu_read_unlock();
266
267
return sel_netnode_sid_slow(addr, family, sid);
268
}
269
270
/**
271
* sel_netnode_flush - Flush the entire network address table
272
*
273
* Description:
274
* Remove all entries from the network address table.
275
*
276
*/
277
void sel_netnode_flush(void)
278
{
279
unsigned int idx;
280
struct sel_netnode *node, *node_tmp;
281
282
spin_lock_bh(&sel_netnode_lock);
283
for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++) {
284
list_for_each_entry_safe(node, node_tmp,
285
&sel_netnode_hash[idx].list, list) {
286
list_del_rcu(&node->list);
287
kfree_rcu(node, rcu);
288
}
289
sel_netnode_hash[idx].size = 0;
290
}
291
spin_unlock_bh(&sel_netnode_lock);
292
}
293
294
int __init sel_netnode_init(void)
295
{
296
int iter;
297
298
if (!selinux_enabled_boot)
299
return 0;
300
301
for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++) {
302
INIT_LIST_HEAD(&sel_netnode_hash[iter].list);
303
sel_netnode_hash[iter].size = 0;
304
}
305
306
return 0;
307
}
308
309