blob: ba354164049191026f0efbcc06c0568049686b02 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070010#include "flask.h"
11#include "security.h"
12#include "sidtab.h"
13
14#define SIDTAB_HASH(sid) \
15(sid & SIDTAB_HASH_MASK)
16
17#define INIT_SIDTAB_LOCK(s) spin_lock_init(&s->lock)
18#define SIDTAB_LOCK(s, x) spin_lock_irqsave(&s->lock, x)
19#define SIDTAB_UNLOCK(s, x) spin_unlock_irqrestore(&s->lock, x)
20
21int sidtab_init(struct sidtab *s)
22{
23 int i;
24
25 s->htable = kmalloc(sizeof(*(s->htable)) * SIDTAB_SIZE, GFP_ATOMIC);
26 if (!s->htable)
27 return -ENOMEM;
28 for (i = 0; i < SIDTAB_SIZE; i++)
29 s->htable[i] = NULL;
30 s->nel = 0;
31 s->next_sid = 1;
32 s->shutdown = 0;
33 INIT_SIDTAB_LOCK(s);
34 return 0;
35}
36
37int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
38{
39 int hvalue, rc = 0;
40 struct sidtab_node *prev, *cur, *newnode;
41
42 if (!s) {
43 rc = -ENOMEM;
44 goto out;
45 }
46
47 hvalue = SIDTAB_HASH(sid);
48 prev = NULL;
49 cur = s->htable[hvalue];
50 while (cur != NULL && sid > cur->sid) {
51 prev = cur;
52 cur = cur->next;
53 }
54
55 if (cur && sid == cur->sid) {
56 rc = -EEXIST;
57 goto out;
58 }
59
60 newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC);
61 if (newnode == NULL) {
62 rc = -ENOMEM;
63 goto out;
64 }
65 newnode->sid = sid;
66 if (context_cpy(&newnode->context, context)) {
67 kfree(newnode);
68 rc = -ENOMEM;
69 goto out;
70 }
71
72 if (prev) {
73 newnode->next = prev->next;
74 wmb();
75 prev->next = newnode;
76 } else {
77 newnode->next = s->htable[hvalue];
78 wmb();
79 s->htable[hvalue] = newnode;
80 }
81
82 s->nel++;
83 if (sid >= s->next_sid)
84 s->next_sid = sid + 1;
85out:
86 return rc;
87}
88
Stephen Smalley12b29f32008-05-07 13:03:20 -040089static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force)
Linus Torvalds1da177e2005-04-16 15:20:36 -070090{
91 int hvalue;
92 struct sidtab_node *cur;
93
94 if (!s)
95 return NULL;
96
97 hvalue = SIDTAB_HASH(sid);
98 cur = s->htable[hvalue];
99 while (cur != NULL && sid > cur->sid)
100 cur = cur->next;
101
Stephen Smalley12b29f32008-05-07 13:03:20 -0400102 if (force && cur && sid == cur->sid && cur->context.len)
103 return &cur->context;
104
105 if (cur == NULL || sid != cur->sid || cur->context.len) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 /* Remap invalid SIDs to the unlabeled SID. */
107 sid = SECINITSID_UNLABELED;
108 hvalue = SIDTAB_HASH(sid);
109 cur = s->htable[hvalue];
110 while (cur != NULL && sid > cur->sid)
111 cur = cur->next;
112 if (!cur || sid != cur->sid)
113 return NULL;
114 }
115
116 return &cur->context;
117}
118
Stephen Smalley12b29f32008-05-07 13:03:20 -0400119struct context *sidtab_search(struct sidtab *s, u32 sid)
120{
121 return sidtab_search_core(s, sid, 0);
122}
123
124struct context *sidtab_search_force(struct sidtab *s, u32 sid)
125{
126 return sidtab_search_core(s, sid, 1);
127}
128
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129int sidtab_map(struct sidtab *s,
130 int (*apply) (u32 sid,
131 struct context *context,
132 void *args),
133 void *args)
134{
135 int i, rc = 0;
136 struct sidtab_node *cur;
137
138 if (!s)
139 goto out;
140
141 for (i = 0; i < SIDTAB_SIZE; i++) {
142 cur = s->htable[i];
143 while (cur != NULL) {
144 rc = apply(cur->sid, &cur->context, args);
145 if (rc)
146 goto out;
147 cur = cur->next;
148 }
149 }
150out:
151 return rc;
152}
153
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154static inline u32 sidtab_search_context(struct sidtab *s,
155 struct context *context)
156{
157 int i;
158 struct sidtab_node *cur;
159
160 for (i = 0; i < SIDTAB_SIZE; i++) {
161 cur = s->htable[i];
162 while (cur != NULL) {
163 if (context_cmp(&cur->context, context))
164 return cur->sid;
165 cur = cur->next;
166 }
167 }
168 return 0;
169}
170
171int sidtab_context_to_sid(struct sidtab *s,
172 struct context *context,
173 u32 *out_sid)
174{
175 u32 sid;
176 int ret = 0;
177 unsigned long flags;
178
179 *out_sid = SECSID_NULL;
180
181 sid = sidtab_search_context(s, context);
182 if (!sid) {
183 SIDTAB_LOCK(s, flags);
184 /* Rescan now that we hold the lock. */
185 sid = sidtab_search_context(s, context);
186 if (sid)
187 goto unlock_out;
188 /* No SID exists for the context. Allocate a new one. */
189 if (s->next_sid == UINT_MAX || s->shutdown) {
190 ret = -ENOMEM;
191 goto unlock_out;
192 }
193 sid = s->next_sid++;
Stephen Smalley12b29f32008-05-07 13:03:20 -0400194 if (context->len)
195 printk(KERN_INFO
196 "SELinux: Context %s is not valid (left unmapped).\n",
197 context->str);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 ret = sidtab_insert(s, sid, context);
199 if (ret)
200 s->next_sid--;
201unlock_out:
202 SIDTAB_UNLOCK(s, flags);
203 }
204
205 if (ret)
206 return ret;
207
208 *out_sid = sid;
209 return 0;
210}
211
212void sidtab_hash_eval(struct sidtab *h, char *tag)
213{
214 int i, chain_len, slots_used, max_chain_len;
215 struct sidtab_node *cur;
216
217 slots_used = 0;
218 max_chain_len = 0;
219 for (i = 0; i < SIDTAB_SIZE; i++) {
220 cur = h->htable[i];
221 if (cur) {
222 slots_used++;
223 chain_len = 0;
224 while (cur) {
225 chain_len++;
226 cur = cur->next;
227 }
228
229 if (chain_len > max_chain_len)
230 max_chain_len = chain_len;
231 }
232 }
233
Eric Parisfadcdb42007-02-22 18:11:31 -0500234 printk(KERN_DEBUG "%s: %d entries and %d/%d buckets used, longest "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 "chain length %d\n", tag, h->nel, slots_used, SIDTAB_SIZE,
236 max_chain_len);
237}
238
239void sidtab_destroy(struct sidtab *s)
240{
241 int i;
242 struct sidtab_node *cur, *temp;
243
244 if (!s)
245 return;
246
247 for (i = 0; i < SIDTAB_SIZE; i++) {
248 cur = s->htable[i];
249 while (cur != NULL) {
250 temp = cur;
251 cur = cur->next;
252 context_destroy(&temp->context);
253 kfree(temp);
254 }
255 s->htable[i] = NULL;
256 }
257 kfree(s->htable);
258 s->htable = NULL;
259 s->nel = 0;
260 s->next_sid = 1;
261}
262
263void sidtab_set(struct sidtab *dst, struct sidtab *src)
264{
265 unsigned long flags;
266
267 SIDTAB_LOCK(src, flags);
268 dst->htable = src->htable;
269 dst->nel = src->nel;
270 dst->next_sid = src->next_sid;
271 dst->shutdown = 0;
272 SIDTAB_UNLOCK(src, flags);
273}
274
275void sidtab_shutdown(struct sidtab *s)
276{
277 unsigned long flags;
278
279 SIDTAB_LOCK(s, flags);
280 s->shutdown = 1;
281 SIDTAB_UNLOCK(s, flags);
282}