Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/security/selinux/ibpkey.c
26378 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Pkey table
4
*
5
* SELinux must keep a mapping of Infinband PKEYs 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.
8
*
9
* This code is heavily based on the "netif" and "netport" concept originally
10
* developed by
11
* James Morris <[email protected]> and
12
* Paul Moore <[email protected]>
13
* (see security/selinux/netif.c and security/selinux/netport.c for more
14
* information)
15
*/
16
17
/*
18
* (c) Mellanox Technologies, 2016
19
*/
20
21
#include <linux/types.h>
22
#include <linux/rcupdate.h>
23
#include <linux/list.h>
24
#include <linux/spinlock.h>
25
26
#include "ibpkey.h"
27
#include "objsec.h"
28
29
#define SEL_PKEY_HASH_SIZE 256
30
#define SEL_PKEY_HASH_BKT_LIMIT 16
31
32
struct sel_ib_pkey_bkt {
33
int size;
34
struct list_head list;
35
};
36
37
struct sel_ib_pkey {
38
struct pkey_security_struct psec;
39
struct list_head list;
40
struct rcu_head rcu;
41
};
42
43
static DEFINE_SPINLOCK(sel_ib_pkey_lock);
44
static struct sel_ib_pkey_bkt sel_ib_pkey_hash[SEL_PKEY_HASH_SIZE];
45
46
/**
47
* sel_ib_pkey_hashfn - Hashing function for the pkey table
48
* @pkey: pkey number
49
*
50
* Description:
51
* This is the hashing function for the pkey table, it returns the bucket
52
* number for the given pkey.
53
*
54
*/
55
static unsigned int sel_ib_pkey_hashfn(u16 pkey)
56
{
57
return (pkey & (SEL_PKEY_HASH_SIZE - 1));
58
}
59
60
/**
61
* sel_ib_pkey_find - Search for a pkey record
62
* @subnet_prefix: subnet_prefix
63
* @pkey_num: pkey_num
64
*
65
* Description:
66
* Search the pkey table and return the matching record. If an entry
67
* can not be found in the table return NULL.
68
*
69
*/
70
static struct sel_ib_pkey *sel_ib_pkey_find(u64 subnet_prefix, u16 pkey_num)
71
{
72
unsigned int idx;
73
struct sel_ib_pkey *pkey;
74
75
idx = sel_ib_pkey_hashfn(pkey_num);
76
list_for_each_entry_rcu(pkey, &sel_ib_pkey_hash[idx].list, list) {
77
if (pkey->psec.pkey == pkey_num &&
78
pkey->psec.subnet_prefix == subnet_prefix)
79
return pkey;
80
}
81
82
return NULL;
83
}
84
85
/**
86
* sel_ib_pkey_insert - Insert a new pkey into the table
87
* @pkey: the new pkey record
88
*
89
* Description:
90
* Add a new pkey record to the hash table.
91
*
92
*/
93
static void sel_ib_pkey_insert(struct sel_ib_pkey *pkey)
94
{
95
unsigned int idx;
96
97
/* we need to impose a limit on the growth of the hash table so check
98
* this bucket to make sure it is within the specified bounds
99
*/
100
idx = sel_ib_pkey_hashfn(pkey->psec.pkey);
101
list_add_rcu(&pkey->list, &sel_ib_pkey_hash[idx].list);
102
if (sel_ib_pkey_hash[idx].size == SEL_PKEY_HASH_BKT_LIMIT) {
103
struct sel_ib_pkey *tail;
104
105
tail = list_entry(
106
rcu_dereference_protected(
107
list_tail_rcu(&sel_ib_pkey_hash[idx].list),
108
lockdep_is_held(&sel_ib_pkey_lock)),
109
struct sel_ib_pkey, list);
110
list_del_rcu(&tail->list);
111
kfree_rcu(tail, rcu);
112
} else {
113
sel_ib_pkey_hash[idx].size++;
114
}
115
}
116
117
/**
118
* sel_ib_pkey_sid_slow - Lookup the SID of a pkey using the policy
119
* @subnet_prefix: subnet prefix
120
* @pkey_num: pkey number
121
* @sid: pkey SID
122
*
123
* Description:
124
* This function determines the SID of a pkey by querying the security
125
* policy. The result is added to the pkey table to speedup future
126
* queries. Returns zero on success, negative values on failure.
127
*
128
*/
129
static int sel_ib_pkey_sid_slow(u64 subnet_prefix, u16 pkey_num, u32 *sid)
130
{
131
int ret;
132
struct sel_ib_pkey *pkey;
133
struct sel_ib_pkey *new;
134
unsigned long flags;
135
136
spin_lock_irqsave(&sel_ib_pkey_lock, flags);
137
pkey = sel_ib_pkey_find(subnet_prefix, pkey_num);
138
if (pkey) {
139
*sid = pkey->psec.sid;
140
spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
141
return 0;
142
}
143
144
ret = security_ib_pkey_sid(subnet_prefix, pkey_num,
145
sid);
146
if (ret)
147
goto out;
148
149
new = kmalloc(sizeof(*new), GFP_ATOMIC);
150
if (!new) {
151
/* If this memory allocation fails still return 0. The SID
152
* is valid, it just won't be added to the cache.
153
*/
154
goto out;
155
}
156
157
new->psec.subnet_prefix = subnet_prefix;
158
new->psec.pkey = pkey_num;
159
new->psec.sid = *sid;
160
sel_ib_pkey_insert(new);
161
162
out:
163
spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
164
return ret;
165
}
166
167
/**
168
* sel_ib_pkey_sid - Lookup the SID of a PKEY
169
* @subnet_prefix: subnet_prefix
170
* @pkey_num: pkey number
171
* @sid: pkey SID
172
*
173
* Description:
174
* This function determines the SID of a PKEY using the fastest method
175
* possible. First the pkey table is queried, but if an entry can't be found
176
* then the policy is queried and the result is added to the table to speedup
177
* future queries. Returns zero on success, negative values on failure.
178
*
179
*/
180
int sel_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *sid)
181
{
182
struct sel_ib_pkey *pkey;
183
184
rcu_read_lock();
185
pkey = sel_ib_pkey_find(subnet_prefix, pkey_num);
186
if (likely(pkey)) {
187
*sid = pkey->psec.sid;
188
rcu_read_unlock();
189
return 0;
190
}
191
rcu_read_unlock();
192
193
return sel_ib_pkey_sid_slow(subnet_prefix, pkey_num, sid);
194
}
195
196
/**
197
* sel_ib_pkey_flush - Flush the entire pkey table
198
*
199
* Description:
200
* Remove all entries from the pkey table
201
*
202
*/
203
void sel_ib_pkey_flush(void)
204
{
205
unsigned int idx;
206
struct sel_ib_pkey *pkey, *pkey_tmp;
207
unsigned long flags;
208
209
spin_lock_irqsave(&sel_ib_pkey_lock, flags);
210
for (idx = 0; idx < SEL_PKEY_HASH_SIZE; idx++) {
211
list_for_each_entry_safe(pkey, pkey_tmp,
212
&sel_ib_pkey_hash[idx].list, list) {
213
list_del_rcu(&pkey->list);
214
kfree_rcu(pkey, rcu);
215
}
216
sel_ib_pkey_hash[idx].size = 0;
217
}
218
spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
219
}
220
221
static __init int sel_ib_pkey_init(void)
222
{
223
int iter;
224
225
if (!selinux_enabled_boot)
226
return 0;
227
228
for (iter = 0; iter < SEL_PKEY_HASH_SIZE; iter++) {
229
INIT_LIST_HEAD(&sel_ib_pkey_hash[iter].list);
230
sel_ib_pkey_hash[iter].size = 0;
231
}
232
233
return 0;
234
}
235
236
subsys_initcall(sel_ib_pkey_init);
237
238