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