sidtab.c revision 12b29f34558b9b45a2c6eabd4f3c6be939a3980f
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 "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
89static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force)
90{
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
102	if (force && cur && sid == cur->sid && cur->context.len)
103		return &cur->context;
104
105	if (cur == NULL || sid != cur->sid || cur->context.len) {
106		/* 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
119struct 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
129int 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
154static 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++;
194		if (context->len)
195			printk(KERN_INFO
196		       "SELinux:  Context %s is not valid (left unmapped).\n",
197			       context->str);
198		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
234	printk(KERN_DEBUG "%s:  %d entries and %d/%d buckets used, longest "
235	       "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}
283