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