• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Author: Ondrej Mosnacek <omosnacek@gmail.com>
3  *
4  * Copyright (C) 2019 Red Hat Inc.
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2.1 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 /*
22  * Binary policy optimization.
23  *
24  * Defines the policydb_optimize() function, which finds and removes
25  * redundant rules from the binary policy to reduce its size and potentially
26  * improve rule matching times. Only rules that are already covered by a
27  * more general rule are removed. The resulting policy is functionally
28  * equivalent to the original one.
29  */
30 
31 #include <sepol/policydb/policydb.h>
32 #include <sepol/policydb/conditional.h>
33 
34 #define TYPE_VEC_INIT_SIZE 16
35 
36 struct type_vec {
37 	uint32_t *types;
38 	unsigned int count, capacity;
39 };
40 
type_vec_init(struct type_vec * v)41 static int type_vec_init(struct type_vec *v)
42 {
43 	v->capacity = TYPE_VEC_INIT_SIZE;
44 	v->count = 0;
45 	v->types = malloc(v->capacity * sizeof(*v->types));
46 	if (!v->types)
47 		return -1;
48 	return 0;
49 }
50 
type_vec_destroy(struct type_vec * v)51 static void type_vec_destroy(struct type_vec *v)
52 {
53 	free(v->types);
54 }
55 
type_vec_append(struct type_vec * v,uint32_t type)56 static int type_vec_append(struct type_vec *v, uint32_t type)
57 {
58 	if (v->capacity == v->count) {
59 		unsigned int new_capacity = v->capacity * 2;
60 		uint32_t *new_types = realloc(v->types,
61 					      new_capacity * sizeof(*v->types));
62 		if (!new_types)
63 			return -1;
64 
65 		v->types = new_types;
66 		v->capacity = new_capacity;
67 	}
68 
69 	v->types[v->count++] = type;
70 	return 0;
71 }
72 
type_vec_contains(const struct type_vec * v,uint32_t type)73 static int type_vec_contains(const struct type_vec *v, uint32_t type)
74 {
75 	unsigned int s = 0, e = v->count;
76 
77 	while (s != e) {
78 		unsigned int mid = (s + e) / 2;
79 
80 		if (v->types[mid] == type)
81 			return 1;
82 
83 		if (v->types[mid] < type)
84 			s = mid + 1;
85 		else
86 			e = mid;
87 	}
88 	return 0;
89 }
90 
91 /* builds map: type/attribute -> {all attributes that are a superset of it} */
build_type_map(const policydb_t * p)92 static struct type_vec *build_type_map(const policydb_t *p)
93 {
94 	unsigned int i, k;
95 	ebitmap_node_t *n;
96 	struct type_vec *map = malloc(p->p_types.nprim * sizeof(*map));
97 	if (!map)
98 		return NULL;
99 
100 	for (i = 0; i < p->p_types.nprim; i++) {
101 		if (type_vec_init(&map[i]))
102 			goto err;
103 
104 		if (p->type_val_to_struct[i]->flavor != TYPE_ATTRIB) {
105 			ebitmap_for_each_positive_bit(&p->type_attr_map[i],
106 						      n, k) {
107 				if (type_vec_append(&map[i], k))
108 					goto err;
109 			}
110 		} else {
111 			ebitmap_t *types_i = &p->attr_type_map[i];
112 
113 			for (k = 0; k < p->p_types.nprim; k++) {
114 				ebitmap_t *types_k = &p->attr_type_map[k];
115 
116 				if (p->type_val_to_struct[k]->flavor != TYPE_ATTRIB)
117 					continue;
118 
119 				if (ebitmap_contains(types_k, types_i)) {
120 					if (type_vec_append(&map[i], k))
121 						goto err;
122 				}
123 			}
124 		}
125 	}
126 	return map;
127 err:
128 	for (k = 0; k <= i; k++)
129 		type_vec_destroy(&map[k]);
130 	free(map);
131 	return NULL;
132 }
133 
destroy_type_map(const policydb_t * p,struct type_vec * type_map)134 static void destroy_type_map(const policydb_t *p, struct type_vec *type_map)
135 {
136 	unsigned int i;
137 	for (i = 0; i < p->p_types.nprim; i++)
138 		type_vec_destroy(&type_map[i]);
139 	free(type_map);
140 }
141 
process_xperms(uint32_t * p1,const uint32_t * p2)142 static int process_xperms(uint32_t *p1, const uint32_t *p2)
143 {
144 	size_t i;
145 	int ret = 1;
146 
147 	for (i = 0; i < EXTENDED_PERMS_LEN; i++) {
148 		p1[i] &= ~p2[i];
149 		if (p1[i] != 0)
150 			ret = 0;
151 	}
152 	return ret;
153 }
154 
process_avtab_datum(uint16_t specified,avtab_datum_t * d1,const avtab_datum_t * d2)155 static int process_avtab_datum(uint16_t specified,
156 			       avtab_datum_t *d1, const avtab_datum_t *d2)
157 {
158 	/* inverse logic needed for AUDITDENY rules */
159 	if (specified & AVTAB_AUDITDENY)
160 		return (d1->data |= ~d2->data) == UINT32_C(0xFFFFFFFF);
161 
162 	if (specified & AVTAB_AV)
163 		return (d1->data &= ~d2->data) == 0;
164 
165 	if (specified & AVTAB_XPERMS) {
166 		avtab_extended_perms_t *x1 = d1->xperms;
167 		const avtab_extended_perms_t *x2 = d2->xperms;
168 
169 		if (x1->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
170 			if (x2->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
171 				if (x1->driver != x2->driver)
172 					return 0;
173 				return process_xperms(x1->perms, x2->perms);
174 			}
175 			if (x2->specified == AVTAB_XPERMS_IOCTLDRIVER)
176 				return xperm_test(x1->driver, x2->perms);
177 		} else if (x1->specified == AVTAB_XPERMS_IOCTLDRIVER) {
178 			if (x2->specified == AVTAB_XPERMS_IOCTLFUNCTION)
179 				return 0;
180 
181 			if (x2->specified == AVTAB_XPERMS_IOCTLDRIVER)
182 				return process_xperms(x1->perms, x2->perms);
183 		}
184 		return 0;
185 	}
186 	return 0;
187 }
188 
189 /* checks if avtab contains a rule that covers the given rule */
is_avrule_redundant(avtab_ptr_t entry,avtab_t * tab,const struct type_vec * type_map,unsigned char not_cond)190 static int is_avrule_redundant(avtab_ptr_t entry, avtab_t *tab,
191 			       const struct type_vec *type_map,
192 			       unsigned char not_cond)
193 {
194 	unsigned int i, k, s_idx, t_idx;
195 	uint32_t st, tt;
196 	avtab_datum_t *d1, *d2;
197 	avtab_key_t key;
198 
199 	/* we only care about AV rules */
200 	if (!(entry->key.specified & (AVTAB_AV|AVTAB_XPERMS)))
201 		return 0;
202 
203 	s_idx = entry->key.source_type - 1;
204 	t_idx = entry->key.target_type - 1;
205 
206 	key.target_class = entry->key.target_class;
207 	key.specified    = entry->key.specified;
208 
209 	d1 = &entry->datum;
210 
211 	for (i = 0; i < type_map[s_idx].count; i++) {
212 		st = type_map[s_idx].types[i];
213 		key.source_type = st + 1;
214 
215 		for (k = 0; k < type_map[t_idx].count; k++) {
216 			tt = type_map[t_idx].types[k];
217 
218 			if (not_cond && s_idx == st && t_idx == tt)
219 				continue;
220 
221 			key.target_type = tt + 1;
222 
223 			d2 = avtab_search(tab, &key);
224 			if (!d2)
225 				continue;
226 
227 			if (process_avtab_datum(key.specified, d1, d2))
228 				return 1;
229 		}
230 	}
231 	return 0;
232 }
233 
is_type_attr(policydb_t * p,unsigned int id)234 static int is_type_attr(policydb_t *p, unsigned int id)
235 {
236 	return p->type_val_to_struct[id]->flavor == TYPE_ATTRIB;
237 }
238 
is_avrule_with_attr(avtab_ptr_t entry,policydb_t * p)239 static int is_avrule_with_attr(avtab_ptr_t entry, policydb_t *p)
240 {
241 	unsigned int s_idx = entry->key.source_type - 1;
242 	unsigned int t_idx = entry->key.target_type - 1;
243 
244 	return is_type_attr(p, s_idx) || is_type_attr(p, t_idx);
245 }
246 
247 /* checks if conditional list contains a rule that covers the given rule */
is_cond_rule_redundant(avtab_ptr_t e1,cond_av_list_t * list,const struct type_vec * type_map)248 static int is_cond_rule_redundant(avtab_ptr_t e1, cond_av_list_t *list,
249 				  const struct type_vec *type_map)
250 {
251 	unsigned int s1, t1, c1, k1, s2, t2, c2, k2;
252 
253 	/* we only care about AV rules */
254 	if (!(e1->key.specified & (AVTAB_AV|AVTAB_XPERMS)))
255 		return 0;
256 
257 	s1 = e1->key.source_type - 1;
258 	t1 = e1->key.target_type - 1;
259 	c1 = e1->key.target_class;
260 	k1 = e1->key.specified;
261 
262 	for (; list; list = list->next) {
263 		avtab_ptr_t e2 = list->node;
264 
265 		s2 = e2->key.source_type - 1;
266 		t2 = e2->key.target_type - 1;
267 		c2 = e2->key.target_class;
268 		k2 = e2->key.specified;
269 
270 		if (k1 != k2 || c1 != c2)
271 			continue;
272 
273 		if (s1 == s2 && t1 == t2)
274 			continue;
275 		if (!type_vec_contains(&type_map[s1], s2))
276 			continue;
277 		if (!type_vec_contains(&type_map[t1], t2))
278 			continue;
279 
280 		if (process_avtab_datum(k1, &e1->datum, &e2->datum))
281 			return 1;
282 	}
283 	return 0;
284 }
285 
optimize_avtab(policydb_t * p,const struct type_vec * type_map)286 static void optimize_avtab(policydb_t *p, const struct type_vec *type_map)
287 {
288 	avtab_t *tab = &p->te_avtab;
289 	unsigned int i;
290 	avtab_ptr_t *cur;
291 
292 	for (i = 0; i < tab->nslot; i++) {
293 		cur = &tab->htable[i];
294 		while (*cur) {
295 			if (is_avrule_redundant(*cur, tab, type_map, 1)) {
296 				/* redundant rule -> remove it */
297 				avtab_ptr_t tmp = *cur;
298 
299 				*cur = tmp->next;
300 				if (tmp->key.specified & AVTAB_XPERMS)
301 					free(tmp->datum.xperms);
302 				free(tmp);
303 
304 				tab->nel--;
305 			} else {
306 				/* rule not redundant -> move to next rule */
307 				cur = &(*cur)->next;
308 			}
309 		}
310 	}
311 }
312 
313 /* find redundant rules in (*cond) and put them into (*del) */
optimize_cond_av_list(cond_av_list_t ** cond,cond_av_list_t ** del,policydb_t * p,const struct type_vec * type_map)314 static void optimize_cond_av_list(cond_av_list_t **cond, cond_av_list_t **del,
315 				  policydb_t *p, const struct type_vec *type_map)
316 {
317 	cond_av_list_t **listp = cond;
318 	cond_av_list_t *pcov = NULL;
319 	cond_av_list_t **pcov_cur;
320 
321 	/*
322 	 * Separate out all "potentially covering" rules (src or tgt is an attr)
323 	 * and move them to the end of the list. This is needed to avoid
324 	 * polynomial complexity when almost all rules are expanded.
325 	 */
326 	while (*cond) {
327 		if (is_avrule_with_attr((*cond)->node, p)) {
328 			cond_av_list_t *tmp = *cond;
329 
330 			*cond = tmp->next;
331 			tmp->next = pcov;
332 			pcov = tmp;
333 		} else {
334 			cond = &(*cond)->next;
335 		}
336 	}
337 	/* link the "potentially covering" rules to the end of the list */
338 	*cond = pcov;
339 
340 	/* now go through the list and find the redundant rules */
341 	cond = listp;
342 	pcov_cur = &pcov;
343 	while (*cond) {
344 		/* needed because pcov itself may get deleted */
345 		if (*cond == pcov)
346 			pcov_cur = cond;
347 		/*
348 		 * First check if covered by an unconditional rule, then also
349 		 * check if covered by another rule in the same list.
350 		 */
351 		if (is_avrule_redundant((*cond)->node, &p->te_avtab, type_map, 0) ||
352 		    is_cond_rule_redundant((*cond)->node, *pcov_cur, type_map)) {
353 			cond_av_list_t *tmp = *cond;
354 
355 			*cond = tmp->next;
356 			tmp->next = *del;
357 			*del = tmp;
358 		} else {
359 			cond = &(*cond)->next;
360 		}
361 	}
362 }
363 
optimize_cond_avtab(policydb_t * p,const struct type_vec * type_map)364 static void optimize_cond_avtab(policydb_t *p, const struct type_vec *type_map)
365 {
366 	avtab_t *tab = &p->te_cond_avtab;
367 	unsigned int i;
368 	avtab_ptr_t *cur;
369 	cond_node_t **cond;
370 	cond_av_list_t **avcond, *del = NULL;
371 
372 	/* First go through all conditionals and collect redundant rules. */
373 	cond = &p->cond_list;
374 	while (*cond) {
375 		optimize_cond_av_list(&(*cond)->true_list,  &del, p, type_map);
376 		optimize_cond_av_list(&(*cond)->false_list, &del, p, type_map);
377 		/* TODO: maybe also check for rules present in both lists */
378 
379 		/* nothing left in both lists -> remove the whole conditional */
380 		if (!(*cond)->true_list && !(*cond)->false_list) {
381 			cond_node_t *cond_tmp = *cond;
382 
383 			*cond = cond_tmp->next;
384 			cond_node_destroy(cond_tmp);
385 			free(cond_tmp);
386 		} else {
387 			cond = &(*cond)->next;
388 		}
389 	}
390 
391 	if (!del)
392 		return;
393 
394 	/*
395 	 * Now go through the whole cond_avtab and remove all rules that are
396 	 * found in the 'del' list.
397 	 */
398 	for (i = 0; i < tab->nslot; i++) {
399 		cur = &tab->htable[i];
400 		while (*cur) {
401 			int redundant = 0;
402 			avcond = &del;
403 			while (*avcond) {
404 				if ((*avcond)->node == *cur) {
405 					cond_av_list_t *cond_tmp = *avcond;
406 
407 					*avcond = cond_tmp->next;
408 					free(cond_tmp);
409 					redundant = 1;
410 					break;
411 				} else {
412 					avcond = &(*avcond)->next;
413 				}
414 			}
415 			if (redundant) {
416 				avtab_ptr_t tmp = *cur;
417 
418 				*cur = tmp->next;
419 				if (tmp->key.specified & AVTAB_XPERMS)
420 					free(tmp->datum.xperms);
421 				free(tmp);
422 
423 				tab->nel--;
424 			} else {
425 				cur = &(*cur)->next;
426 			}
427 		}
428 	}
429 }
430 
policydb_optimize(policydb_t * p)431 int policydb_optimize(policydb_t *p)
432 {
433 	struct type_vec *type_map;
434 
435 	if (p->policy_type != POLICY_KERN)
436 		return -1;
437 
438 	type_map = build_type_map(p);
439 	if (!type_map)
440 		return -1;
441 
442 	optimize_avtab(p, type_map);
443 	optimize_cond_avtab(p, type_map);
444 
445 	destroy_type_map(p, type_map);
446 	return 0;
447 }
448