xref: /openbmc/linux/security/selinux/ss/sidtab.c (revision 96de0e252cedffad61b3cb5e05662c591898e69a)
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 
21 int 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 
37 int 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;
85 out:
86 	return rc;
87 }
88 
89 struct context *sidtab_search(struct sidtab *s, u32 sid)
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 (cur == NULL || sid != cur->sid) {
103 		/* Remap invalid SIDs to the unlabeled SID. */
104 		sid = SECINITSID_UNLABELED;
105 		hvalue = SIDTAB_HASH(sid);
106 		cur = s->htable[hvalue];
107 		while (cur != NULL && sid > cur->sid)
108 			cur = cur->next;
109 		if (!cur || sid != cur->sid)
110 			return NULL;
111 	}
112 
113 	return &cur->context;
114 }
115 
116 int sidtab_map(struct sidtab *s,
117 	       int (*apply) (u32 sid,
118 			     struct context *context,
119 			     void *args),
120 	       void *args)
121 {
122 	int i, rc = 0;
123 	struct sidtab_node *cur;
124 
125 	if (!s)
126 		goto out;
127 
128 	for (i = 0; i < SIDTAB_SIZE; i++) {
129 		cur = s->htable[i];
130 		while (cur != NULL) {
131 			rc = apply(cur->sid, &cur->context, args);
132 			if (rc)
133 				goto out;
134 			cur = cur->next;
135 		}
136 	}
137 out:
138 	return rc;
139 }
140 
141 void sidtab_map_remove_on_error(struct sidtab *s,
142 				int (*apply) (u32 sid,
143 					      struct context *context,
144 					      void *args),
145 				void *args)
146 {
147 	int i, ret;
148 	struct sidtab_node *last, *cur, *temp;
149 
150 	if (!s)
151 		return;
152 
153 	for (i = 0; i < SIDTAB_SIZE; i++) {
154 		last = NULL;
155 		cur = s->htable[i];
156 		while (cur != NULL) {
157 			ret = apply(cur->sid, &cur->context, args);
158 			if (ret) {
159 				if (last) {
160 					last->next = cur->next;
161 				} else {
162 					s->htable[i] = cur->next;
163 				}
164 
165 				temp = cur;
166 				cur = cur->next;
167 				context_destroy(&temp->context);
168 				kfree(temp);
169 				s->nel--;
170 			} else {
171 				last = cur;
172 				cur = cur->next;
173 			}
174 		}
175 	}
176 
177 	return;
178 }
179 
180 static inline u32 sidtab_search_context(struct sidtab *s,
181 						  struct context *context)
182 {
183 	int i;
184 	struct sidtab_node *cur;
185 
186 	for (i = 0; i < SIDTAB_SIZE; i++) {
187 		cur = s->htable[i];
188 		while (cur != NULL) {
189 			if (context_cmp(&cur->context, context))
190 				return cur->sid;
191 			cur = cur->next;
192 		}
193 	}
194 	return 0;
195 }
196 
197 int sidtab_context_to_sid(struct sidtab *s,
198 			  struct context *context,
199 			  u32 *out_sid)
200 {
201 	u32 sid;
202 	int ret = 0;
203 	unsigned long flags;
204 
205 	*out_sid = SECSID_NULL;
206 
207 	sid = sidtab_search_context(s, context);
208 	if (!sid) {
209 		SIDTAB_LOCK(s, flags);
210 		/* Rescan now that we hold the lock. */
211 		sid = sidtab_search_context(s, context);
212 		if (sid)
213 			goto unlock_out;
214 		/* No SID exists for the context.  Allocate a new one. */
215 		if (s->next_sid == UINT_MAX || s->shutdown) {
216 			ret = -ENOMEM;
217 			goto unlock_out;
218 		}
219 		sid = s->next_sid++;
220 		ret = sidtab_insert(s, sid, context);
221 		if (ret)
222 			s->next_sid--;
223 unlock_out:
224 		SIDTAB_UNLOCK(s, flags);
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 
256 	printk(KERN_DEBUG "%s:  %d entries and %d/%d buckets used, longest "
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];
271 		while (cur != NULL) {
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;
288 
289 	SIDTAB_LOCK(src, flags);
290 	dst->htable = src->htable;
291 	dst->nel = src->nel;
292 	dst->next_sid = src->next_sid;
293 	dst->shutdown = 0;
294 	SIDTAB_UNLOCK(src, flags);
295 }
296 
297 void sidtab_shutdown(struct sidtab *s)
298 {
299 	unsigned long flags;
300 
301 	SIDTAB_LOCK(s, flags);
302 	s->shutdown = 1;
303 	SIDTAB_UNLOCK(s, flags);
304 }
305