]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 LT |
2 | /* |
3 | * Implementation of the SID table type. | |
4 | * | |
7efbb60b | 5 | * Author : Stephen Smalley, <sds@tycho.nsa.gov> |
1da177e4 LT |
6 | */ |
7 | #include <linux/kernel.h> | |
8 | #include <linux/slab.h> | |
9 | #include <linux/spinlock.h> | |
10 | #include <linux/errno.h> | |
1da177e4 LT |
11 | #include "flask.h" |
12 | #include "security.h" | |
13 | #include "sidtab.h" | |
14 | ||
15 | #define SIDTAB_HASH(sid) \ | |
16 | (sid & SIDTAB_HASH_MASK) | |
17 | ||
1da177e4 LT |
18 | int sidtab_init(struct sidtab *s) |
19 | { | |
20 | int i; | |
21 | ||
b380f783 | 22 | s->htable = kmalloc_array(SIDTAB_SIZE, sizeof(*s->htable), GFP_ATOMIC); |
1da177e4 LT |
23 | if (!s->htable) |
24 | return -ENOMEM; | |
25 | for (i = 0; i < SIDTAB_SIZE; i++) | |
26 | s->htable[i] = NULL; | |
27 | s->nel = 0; | |
28 | s->next_sid = 1; | |
29 | s->shutdown = 0; | |
bdd581c1 | 30 | spin_lock_init(&s->lock); |
1da177e4 LT |
31 | return 0; |
32 | } | |
33 | ||
34 | int sidtab_insert(struct sidtab *s, u32 sid, struct context *context) | |
35 | { | |
46be14d2 | 36 | int hvalue; |
1da177e4 LT |
37 | struct sidtab_node *prev, *cur, *newnode; |
38 | ||
46be14d2 ME |
39 | if (!s) |
40 | return -ENOMEM; | |
1da177e4 LT |
41 | |
42 | hvalue = SIDTAB_HASH(sid); | |
43 | prev = NULL; | |
44 | cur = s->htable[hvalue]; | |
dbc74c65 | 45 | while (cur && sid > cur->sid) { |
1da177e4 LT |
46 | prev = cur; |
47 | cur = cur->next; | |
48 | } | |
49 | ||
46be14d2 ME |
50 | if (cur && sid == cur->sid) |
51 | return -EEXIST; | |
1da177e4 LT |
52 | |
53 | newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC); | |
46be14d2 ME |
54 | if (!newnode) |
55 | return -ENOMEM; | |
56 | ||
1da177e4 LT |
57 | newnode->sid = sid; |
58 | if (context_cpy(&newnode->context, context)) { | |
59 | kfree(newnode); | |
46be14d2 | 60 | return -ENOMEM; |
1da177e4 LT |
61 | } |
62 | ||
63 | if (prev) { | |
64 | newnode->next = prev->next; | |
65 | wmb(); | |
66 | prev->next = newnode; | |
67 | } else { | |
68 | newnode->next = s->htable[hvalue]; | |
69 | wmb(); | |
70 | s->htable[hvalue] = newnode; | |
71 | } | |
72 | ||
73 | s->nel++; | |
74 | if (sid >= s->next_sid) | |
75 | s->next_sid = sid + 1; | |
46be14d2 | 76 | return 0; |
1da177e4 LT |
77 | } |
78 | ||
12b29f34 | 79 | static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force) |
1da177e4 LT |
80 | { |
81 | int hvalue; | |
82 | struct sidtab_node *cur; | |
83 | ||
84 | if (!s) | |
85 | return NULL; | |
86 | ||
87 | hvalue = SIDTAB_HASH(sid); | |
88 | cur = s->htable[hvalue]; | |
dbc74c65 | 89 | while (cur && sid > cur->sid) |
1da177e4 LT |
90 | cur = cur->next; |
91 | ||
12b29f34 SS |
92 | if (force && cur && sid == cur->sid && cur->context.len) |
93 | return &cur->context; | |
94 | ||
8ee4586c | 95 | if (!cur || sid != cur->sid || cur->context.len) { |
1da177e4 LT |
96 | /* Remap invalid SIDs to the unlabeled SID. */ |
97 | sid = SECINITSID_UNLABELED; | |
98 | hvalue = SIDTAB_HASH(sid); | |
99 | cur = s->htable[hvalue]; | |
dbc74c65 | 100 | while (cur && sid > cur->sid) |
1da177e4 LT |
101 | cur = cur->next; |
102 | if (!cur || sid != cur->sid) | |
103 | return NULL; | |
104 | } | |
105 | ||
106 | return &cur->context; | |
107 | } | |
108 | ||
12b29f34 SS |
109 | struct context *sidtab_search(struct sidtab *s, u32 sid) |
110 | { | |
111 | return sidtab_search_core(s, sid, 0); | |
112 | } | |
113 | ||
114 | struct context *sidtab_search_force(struct sidtab *s, u32 sid) | |
115 | { | |
116 | return sidtab_search_core(s, sid, 1); | |
117 | } | |
118 | ||
1da177e4 LT |
119 | int sidtab_map(struct sidtab *s, |
120 | int (*apply) (u32 sid, | |
121 | struct context *context, | |
122 | void *args), | |
123 | void *args) | |
124 | { | |
125 | int i, rc = 0; | |
126 | struct sidtab_node *cur; | |
127 | ||
128 | if (!s) | |
129 | goto out; | |
130 | ||
131 | for (i = 0; i < SIDTAB_SIZE; i++) { | |
132 | cur = s->htable[i]; | |
dbc74c65 | 133 | while (cur) { |
1da177e4 LT |
134 | rc = apply(cur->sid, &cur->context, args); |
135 | if (rc) | |
136 | goto out; | |
137 | cur = cur->next; | |
138 | } | |
139 | } | |
140 | out: | |
141 | return rc; | |
142 | } | |
143 | ||
73ff5fc0 EP |
144 | static void sidtab_update_cache(struct sidtab *s, struct sidtab_node *n, int loc) |
145 | { | |
146 | BUG_ON(loc >= SIDTAB_CACHE_LEN); | |
147 | ||
148 | while (loc > 0) { | |
149 | s->cache[loc] = s->cache[loc - 1]; | |
150 | loc--; | |
151 | } | |
152 | s->cache[0] = n; | |
153 | } | |
154 | ||
1da177e4 LT |
155 | static inline u32 sidtab_search_context(struct sidtab *s, |
156 | struct context *context) | |
157 | { | |
158 | int i; | |
159 | struct sidtab_node *cur; | |
160 | ||
161 | for (i = 0; i < SIDTAB_SIZE; i++) { | |
162 | cur = s->htable[i]; | |
dbc74c65 | 163 | while (cur) { |
73ff5fc0 EP |
164 | if (context_cmp(&cur->context, context)) { |
165 | sidtab_update_cache(s, cur, SIDTAB_CACHE_LEN - 1); | |
1da177e4 | 166 | return cur->sid; |
73ff5fc0 | 167 | } |
1da177e4 LT |
168 | cur = cur->next; |
169 | } | |
170 | } | |
171 | return 0; | |
172 | } | |
173 | ||
73ff5fc0 EP |
174 | static inline u32 sidtab_search_cache(struct sidtab *s, struct context *context) |
175 | { | |
176 | int i; | |
177 | struct sidtab_node *node; | |
178 | ||
179 | for (i = 0; i < SIDTAB_CACHE_LEN; i++) { | |
180 | node = s->cache[i]; | |
181 | if (unlikely(!node)) | |
182 | return 0; | |
183 | if (context_cmp(&node->context, context)) { | |
184 | sidtab_update_cache(s, node, i); | |
185 | return node->sid; | |
186 | } | |
187 | } | |
188 | return 0; | |
189 | } | |
190 | ||
1da177e4 LT |
191 | int sidtab_context_to_sid(struct sidtab *s, |
192 | struct context *context, | |
193 | u32 *out_sid) | |
194 | { | |
195 | u32 sid; | |
196 | int ret = 0; | |
197 | unsigned long flags; | |
198 | ||
199 | *out_sid = SECSID_NULL; | |
200 | ||
73ff5fc0 EP |
201 | sid = sidtab_search_cache(s, context); |
202 | if (!sid) | |
203 | sid = sidtab_search_context(s, context); | |
1da177e4 | 204 | if (!sid) { |
bdd581c1 | 205 | spin_lock_irqsave(&s->lock, flags); |
1da177e4 LT |
206 | /* Rescan now that we hold the lock. */ |
207 | sid = sidtab_search_context(s, context); | |
208 | if (sid) | |
209 | goto unlock_out; | |
210 | /* No SID exists for the context. Allocate a new one. */ | |
211 | if (s->next_sid == UINT_MAX || s->shutdown) { | |
212 | ret = -ENOMEM; | |
213 | goto unlock_out; | |
214 | } | |
215 | sid = s->next_sid++; | |
12b29f34 SS |
216 | if (context->len) |
217 | printk(KERN_INFO | |
218 | "SELinux: Context %s is not valid (left unmapped).\n", | |
219 | context->str); | |
1da177e4 LT |
220 | ret = sidtab_insert(s, sid, context); |
221 | if (ret) | |
222 | s->next_sid--; | |
223 | unlock_out: | |
bdd581c1 | 224 | spin_unlock_irqrestore(&s->lock, flags); |
1da177e4 LT |
225 | } |
226 | ||
227 | if (ret) | |
228 | return ret; | |
229 | ||
230 | *out_sid = sid; | |
231 | return 0; | |
232 | } | |
233 | ||
234 | void sidtab_hash_eval(struct sidtab *h, char *tag) | |
235 | { | |
236 | int i, chain_len, slots_used, max_chain_len; | |
237 | struct sidtab_node *cur; | |
238 | ||
239 | slots_used = 0; | |
240 | max_chain_len = 0; | |
241 | for (i = 0; i < SIDTAB_SIZE; i++) { | |
242 | cur = h->htable[i]; | |
243 | if (cur) { | |
244 | slots_used++; | |
245 | chain_len = 0; | |
246 | while (cur) { | |
247 | chain_len++; | |
248 | cur = cur->next; | |
249 | } | |
250 | ||
251 | if (chain_len > max_chain_len) | |
252 | max_chain_len = chain_len; | |
253 | } | |
254 | } | |
255 | ||
fadcdb45 | 256 | printk(KERN_DEBUG "%s: %d entries and %d/%d buckets used, longest " |
1da177e4 LT |
257 | "chain length %d\n", tag, h->nel, slots_used, SIDTAB_SIZE, |
258 | max_chain_len); | |
259 | } | |
260 | ||
261 | void sidtab_destroy(struct sidtab *s) | |
262 | { | |
263 | int i; | |
264 | struct sidtab_node *cur, *temp; | |
265 | ||
266 | if (!s) | |
267 | return; | |
268 | ||
269 | for (i = 0; i < SIDTAB_SIZE; i++) { | |
270 | cur = s->htable[i]; | |
dbc74c65 | 271 | while (cur) { |
1da177e4 LT |
272 | temp = cur; |
273 | cur = cur->next; | |
274 | context_destroy(&temp->context); | |
275 | kfree(temp); | |
276 | } | |
277 | s->htable[i] = NULL; | |
278 | } | |
279 | kfree(s->htable); | |
280 | s->htable = NULL; | |
281 | s->nel = 0; | |
282 | s->next_sid = 1; | |
283 | } | |
284 | ||
285 | void sidtab_set(struct sidtab *dst, struct sidtab *src) | |
286 | { | |
287 | unsigned long flags; | |
73ff5fc0 | 288 | int i; |
1da177e4 | 289 | |
bdd581c1 | 290 | spin_lock_irqsave(&src->lock, flags); |
1da177e4 LT |
291 | dst->htable = src->htable; |
292 | dst->nel = src->nel; | |
293 | dst->next_sid = src->next_sid; | |
294 | dst->shutdown = 0; | |
73ff5fc0 EP |
295 | for (i = 0; i < SIDTAB_CACHE_LEN; i++) |
296 | dst->cache[i] = NULL; | |
bdd581c1 | 297 | spin_unlock_irqrestore(&src->lock, flags); |
1da177e4 LT |
298 | } |
299 | ||
300 | void sidtab_shutdown(struct sidtab *s) | |
301 | { | |
302 | unsigned long flags; | |
303 | ||
bdd581c1 | 304 | spin_lock_irqsave(&s->lock, flags); |
1da177e4 | 305 | s->shutdown = 1; |
bdd581c1 | 306 | spin_unlock_irqrestore(&s->lock, flags); |
1da177e4 | 307 | } |