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