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