1/* Authors: Joshua Brindle <jbrindle@tresys.com>
2 * 	    Jason Tang <jtang@tresys.com>
3 *
4 * Updates: KaiGai Kohei <kaigai@ak.jp.nec.com>
5 *          adds checks based on newer boundary facility.
6 *
7 * A set of utility functions that aid policy decision when dealing
8 * with hierarchal namespaces.
9 *
10 * Copyright (C) 2005 Tresys Technology, LLC
11 *
12 * Copyright (c) 2008 NEC Corporation
13 *
14 *  This library is free software; you can redistribute it and/or
15 *  modify it under the terms of the GNU Lesser General Public
16 *  License as published by the Free Software Foundation; either
17 *  version 2.1 of the License, or (at your option) any later version.
18 *
19 *  This library is distributed in the hope that it will be useful,
20 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22 *  Lesser General Public License for more details.
23 *
24 *  You should have received a copy of the GNU Lesser General Public
25 *  License along with this library; if not, write to the Free Software
26 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
27 */
28
29#include <string.h>
30#include <stdlib.h>
31#include <assert.h>
32#include <sepol/policydb/policydb.h>
33#include <sepol/policydb/conditional.h>
34#include <sepol/policydb/hierarchy.h>
35#include <sepol/policydb/expand.h>
36#include <sepol/policydb/util.h>
37
38#include "debug.h"
39
40#define BOUNDS_AVTAB_SIZE 1024
41
42static int bounds_insert_helper(sepol_handle_t *handle, avtab_t *avtab,
43				avtab_key_t *avtab_key, avtab_datum_t *datum)
44{
45	int rc = avtab_insert(avtab, avtab_key, datum);
46	if (rc) {
47		if (rc == SEPOL_ENOMEM)
48			ERR(handle, "Insufficient memory");
49		else
50			ERR(handle, "Unexpected error (%d)", rc);
51	}
52	return rc;
53}
54
55
56static int bounds_insert_rule(sepol_handle_t *handle, avtab_t *avtab,
57			      avtab_t *global, avtab_t *other,
58			      avtab_key_t *avtab_key, avtab_datum_t *datum)
59{
60	int rc = 0;
61	avtab_datum_t *dup = avtab_search(avtab, avtab_key);
62
63	if (!dup) {
64		rc = bounds_insert_helper(handle, avtab, avtab_key, datum);
65		if (rc) goto exit;
66	} else {
67		dup->data |= datum->data;
68	}
69
70	if (other) {
71		/* Search the other conditional avtab for the key and
72		 * add any common permissions to the global avtab
73		 */
74		uint32_t data = 0;
75		dup = avtab_search(other, avtab_key);
76		if (dup) {
77			data = dup->data & datum->data;
78			if (data) {
79				dup = avtab_search(global, avtab_key);
80				if (!dup) {
81					avtab_datum_t d;
82					d.data = data;
83					rc = bounds_insert_helper(handle, global,
84								  avtab_key, &d);
85					if (rc) goto exit;
86				} else {
87					dup->data |= data;
88				}
89			}
90		}
91	}
92
93exit:
94	return rc;
95}
96
97static int bounds_expand_rule(sepol_handle_t *handle, policydb_t *p,
98			      avtab_t *avtab, avtab_t *global, avtab_t *other,
99			      uint32_t parent, uint32_t src, uint32_t tgt,
100			      uint32_t class, uint32_t data)
101{
102	int rc = 0;
103	avtab_key_t avtab_key;
104	avtab_datum_t datum;
105	ebitmap_node_t *tnode;
106	unsigned int i;
107
108	avtab_key.specified = AVTAB_ALLOWED;
109	avtab_key.target_class = class;
110	datum.data = data;
111
112	if (ebitmap_get_bit(&p->attr_type_map[src - 1], parent - 1)) {
113		avtab_key.source_type = parent;
114		ebitmap_for_each_bit(&p->attr_type_map[tgt - 1], tnode, i) {
115			if (!ebitmap_node_get_bit(tnode, i))
116				continue;
117			avtab_key.target_type = i + 1;
118			rc = bounds_insert_rule(handle, avtab, global, other,
119						&avtab_key, &datum);
120			if (rc) goto exit;
121		}
122	}
123
124exit:
125	return rc;
126}
127
128static int bounds_expand_cond_rules(sepol_handle_t *handle, policydb_t *p,
129				    cond_av_list_t *cur, avtab_t *avtab,
130				    avtab_t *global, avtab_t *other,
131				    uint32_t parent)
132{
133	int rc = 0;
134
135	for (; cur; cur = cur->next) {
136		avtab_ptr_t n = cur->node;
137		rc = bounds_expand_rule(handle, p, avtab, global, other, parent,
138					n->key.source_type, n->key.target_type,
139					n->key.target_class, n->datum.data);
140		if (rc) goto exit;
141	}
142
143exit:
144	return rc;
145}
146
147struct bounds_expand_args {
148	sepol_handle_t *handle;
149	policydb_t *p;
150	avtab_t *avtab;
151	uint32_t parent;
152};
153
154static int bounds_expand_rule_callback(avtab_key_t *k, avtab_datum_t *d,
155				       void *args)
156{
157	struct bounds_expand_args *a = (struct bounds_expand_args *)args;
158
159	if (!(k->specified & AVTAB_ALLOWED))
160		return 0;
161
162	return bounds_expand_rule(a->handle, a->p, a->avtab, NULL, NULL,
163				  a->parent, k->source_type, k->target_type,
164				  k->target_class, d->data);
165}
166
167struct bounds_cond_info {
168	avtab_t true_avtab;
169	avtab_t false_avtab;
170	cond_list_t *cond_list;
171	struct bounds_cond_info *next;
172};
173
174static void bounds_destroy_cond_info(struct bounds_cond_info *cur)
175{
176	struct bounds_cond_info *next;
177
178	for (; cur; cur = next) {
179		next = cur->next;
180		avtab_destroy(&cur->true_avtab);
181		avtab_destroy(&cur->false_avtab);
182		cur->next = NULL;
183		free(cur);
184	}
185}
186
187static int bounds_expand_parent_rules(sepol_handle_t *handle, policydb_t *p,
188				      avtab_t *global_avtab,
189				      struct bounds_cond_info **cond_info,
190				      uint32_t parent)
191{
192	int rc = 0;
193	struct bounds_expand_args args;
194	cond_list_t *cur;
195
196	avtab_init(global_avtab);
197	rc = avtab_alloc(global_avtab, BOUNDS_AVTAB_SIZE);
198	if (rc) goto oom;
199
200	args.handle = handle;
201	args.p = p;
202	args.avtab = global_avtab;
203	args.parent = parent;
204	rc = avtab_map(&p->te_avtab, bounds_expand_rule_callback, &args);
205	if (rc) goto exit;
206
207	*cond_info = NULL;
208	for (cur = p->cond_list; cur; cur = cur->next) {
209		struct bounds_cond_info *ci;
210		ci = malloc(sizeof(struct bounds_cond_info));
211		if (!ci) goto oom;
212		avtab_init(&ci->true_avtab);
213		avtab_init(&ci->false_avtab);
214		ci->cond_list = cur;
215		ci->next = *cond_info;
216		*cond_info = ci;
217		if (cur->true_list) {
218			rc = avtab_alloc(&ci->true_avtab, BOUNDS_AVTAB_SIZE);
219			if (rc) goto oom;
220			rc = bounds_expand_cond_rules(handle, p, cur->true_list,
221						      &ci->true_avtab, NULL,
222						      NULL, parent);
223			if (rc) goto exit;
224		}
225		if (cur->false_list) {
226			rc = avtab_alloc(&ci->false_avtab, BOUNDS_AVTAB_SIZE);
227			if (rc) goto oom;
228			rc = bounds_expand_cond_rules(handle, p, cur->false_list,
229						      &ci->false_avtab,
230						      global_avtab,
231						      &ci->true_avtab, parent);
232			if (rc) goto exit;
233		}
234	}
235
236	return 0;
237
238oom:
239	ERR(handle, "Insufficient memory");
240
241exit:
242	ERR(handle,"Failed to expand parent rules\n");
243	avtab_destroy(global_avtab);
244	bounds_destroy_cond_info(*cond_info);
245	*cond_info = NULL;
246	return rc;
247}
248
249static int bounds_not_covered(avtab_t *global_avtab, avtab_t *cur_avtab,
250			      avtab_key_t *avtab_key, uint32_t data)
251{
252	avtab_datum_t *datum = avtab_search(cur_avtab, avtab_key);
253	if (datum)
254		data &= ~datum->data;
255	if (global_avtab && data) {
256		datum = avtab_search(global_avtab, avtab_key);
257		if (datum)
258			data &= ~datum->data;
259	}
260
261	return data;
262}
263
264static int bounds_add_bad(sepol_handle_t *handle, uint32_t src, uint32_t tgt,
265			  uint32_t class, uint32_t data, avtab_ptr_t *bad)
266{
267	struct avtab_node *new = malloc(sizeof(struct avtab_node));
268	if (new == NULL) {
269		ERR(handle, "Insufficient memory");
270		return SEPOL_ENOMEM;
271	}
272	memset(new, 0, sizeof(struct avtab_node));
273	new->key.source_type = src;
274	new->key.target_type = tgt;
275	new->key.target_class = class;
276	new->datum.data = data;
277	new->next = *bad;
278	*bad = new;
279
280	return 0;
281}
282
283static int bounds_check_rule(sepol_handle_t *handle, policydb_t *p,
284			     avtab_t *global_avtab, avtab_t *cur_avtab,
285			     uint32_t child, uint32_t parent, uint32_t src,
286			     uint32_t tgt, uint32_t class, uint32_t data,
287			     avtab_ptr_t *bad, int *numbad)
288{
289	int rc = 0;
290	avtab_key_t avtab_key;
291	type_datum_t *td;
292	ebitmap_node_t *tnode;
293	unsigned int i;
294	uint32_t d;
295
296	avtab_key.specified = AVTAB_ALLOWED;
297	avtab_key.target_class = class;
298
299	if (ebitmap_get_bit(&p->attr_type_map[src - 1], child - 1)) {
300		avtab_key.source_type = parent;
301		ebitmap_for_each_bit(&p->attr_type_map[tgt - 1], tnode, i) {
302			if (!ebitmap_node_get_bit(tnode, i))
303				continue;
304			td = p->type_val_to_struct[i];
305			if (td && td->bounds) {
306				avtab_key.target_type = td->bounds;
307				d = bounds_not_covered(global_avtab, cur_avtab,
308						       &avtab_key, data);
309			} else {
310				avtab_key.target_type = i + 1;
311				d = bounds_not_covered(global_avtab, cur_avtab,
312						       &avtab_key, data);
313			}
314			if (d) {
315				(*numbad)++;
316				rc = bounds_add_bad(handle, child, i+1, class, d, bad);
317				if (rc) goto exit;
318			}
319		}
320	}
321
322exit:
323	return rc;
324}
325
326static int bounds_check_cond_rules(sepol_handle_t *handle, policydb_t *p,
327				   avtab_t *global_avtab, avtab_t *cond_avtab,
328				   cond_av_list_t *rules, uint32_t child,
329				   uint32_t parent, avtab_ptr_t *bad,
330				   int *numbad)
331{
332	int rc = 0;
333	cond_av_list_t *cur;
334
335	for (cur = rules; cur; cur = cur->next) {
336		avtab_ptr_t ap = cur->node;
337		avtab_key_t *key = &ap->key;
338		avtab_datum_t *datum = &ap->datum;
339		if (!(key->specified & AVTAB_ALLOWED))
340			continue;
341		rc = bounds_check_rule(handle, p, global_avtab, cond_avtab,
342				       child, parent, key->source_type,
343				       key->target_type, key->target_class,
344				       datum->data, bad, numbad);
345		if (rc) goto exit;
346	}
347
348exit:
349	return rc;
350}
351
352struct bounds_check_args {
353	sepol_handle_t *handle;
354	policydb_t *p;
355	avtab_t *cur_avtab;
356	uint32_t child;
357	uint32_t parent;
358	avtab_ptr_t bad;
359	int numbad;
360};
361
362static int bounds_check_rule_callback(avtab_key_t *k, avtab_datum_t *d,
363				      void *args)
364{
365	struct bounds_check_args *a = (struct bounds_check_args *)args;
366
367	if (!(k->specified & AVTAB_ALLOWED))
368		return 0;
369
370	return bounds_check_rule(a->handle, a->p, NULL, a->cur_avtab, a->child,
371				 a->parent, k->source_type, k->target_type,
372				 k->target_class, d->data, &a->bad, &a->numbad);
373}
374
375static int bounds_check_child_rules(sepol_handle_t *handle, policydb_t *p,
376				    avtab_t *global_avtab,
377				    struct bounds_cond_info *cond_info,
378				    uint32_t child, uint32_t parent,
379				    avtab_ptr_t *bad, int *numbad)
380{
381	int rc;
382	struct bounds_check_args args;
383	struct bounds_cond_info *cur;
384
385	args.handle = handle;
386	args.p = p;
387	args.cur_avtab = global_avtab;
388	args.child = child;
389	args.parent = parent;
390	args.bad = NULL;
391	args.numbad = 0;
392	rc = avtab_map(&p->te_avtab, bounds_check_rule_callback, &args);
393	if (rc) goto exit;
394
395	for (cur = cond_info; cur; cur = cur->next) {
396		cond_list_t *node = cur->cond_list;
397		rc = bounds_check_cond_rules(handle, p, global_avtab,
398					     &cur->true_avtab,
399					     node->true_list, child, parent,
400					     &args.bad, &args.numbad);
401		if (rc) goto exit;
402
403		rc = bounds_check_cond_rules(handle, p, global_avtab,
404					     &cur->false_avtab,
405					     node->false_list, child, parent,
406					     &args.bad, &args.numbad);
407		if (rc) goto exit;
408	}
409
410	*numbad += args.numbad;
411	*bad = args.bad;
412
413exit:
414	return rc;
415}
416
417int bounds_check_type(sepol_handle_t *handle, policydb_t *p, uint32_t child,
418		      uint32_t parent, avtab_ptr_t *bad, int *numbad)
419{
420	int rc = 0;
421	avtab_t global_avtab;
422	struct bounds_cond_info *cond_info = NULL;
423
424	rc = bounds_expand_parent_rules(handle, p, &global_avtab, &cond_info, parent);
425	if (rc) goto exit;
426
427	rc = bounds_check_child_rules(handle, p, &global_avtab, cond_info,
428				      child, parent, bad, numbad);
429
430	bounds_destroy_cond_info(cond_info);
431	avtab_destroy(&global_avtab);
432
433exit:
434	return rc;
435}
436
437struct bounds_args {
438	sepol_handle_t *handle;
439	policydb_t *p;
440	int numbad;
441};
442
443static void bounds_report(sepol_handle_t *handle, policydb_t *p, uint32_t child,
444			  uint32_t parent, avtab_ptr_t cur)
445{
446	ERR(handle, "Child type %s exceeds bounds of parent %s in the following rules:",
447	    p->p_type_val_to_name[child - 1],
448	    p->p_type_val_to_name[parent - 1]);
449	for (; cur; cur = cur->next) {
450		ERR(handle, "    %s %s : %s { %s }",
451		    p->p_type_val_to_name[cur->key.source_type - 1],
452		    p->p_type_val_to_name[cur->key.target_type - 1],
453		    p->p_class_val_to_name[cur->key.target_class - 1],
454		    sepol_av_to_string(p, cur->key.target_class,
455				       cur->datum.data));
456	}
457}
458
459void bounds_destroy_bad(avtab_ptr_t cur)
460{
461	avtab_ptr_t next;
462
463	for (; cur; cur = next) {
464		next = cur->next;
465		cur->next = NULL;
466		free(cur);
467	}
468}
469
470static int bounds_check_type_callback(hashtab_key_t k __attribute__ ((unused)),
471				      hashtab_datum_t d, void *args)
472{
473	int rc = 0;
474	struct bounds_args *a = (struct bounds_args *)args;
475	type_datum_t *t = (type_datum_t *)d;
476	avtab_ptr_t bad = NULL;
477
478	if (t->bounds) {
479		rc = bounds_check_type(a->handle, a->p, t->s.value, t->bounds,
480				       &bad, &a->numbad);
481		if (bad) {
482			bounds_report(a->handle, a->p, t->s.value, t->bounds,
483				      bad);
484			bounds_destroy_bad(bad);
485		}
486	}
487
488	return rc;
489}
490
491int bounds_check_types(sepol_handle_t *handle, policydb_t *p)
492{
493	int rc;
494	struct bounds_args args;
495
496	args.handle = handle;
497	args.p = p;
498	args.numbad = 0;
499
500	rc = hashtab_map(p->p_types.table, bounds_check_type_callback, &args);
501	if (rc) goto exit;
502
503	if (args.numbad > 0) {
504		ERR(handle, "%d errors found during type bounds check",
505		    args.numbad);
506		rc = SEPOL_ERR;
507	}
508
509exit:
510	return rc;
511}
512
513/* The role bounds is defined as: a child role cannot have a type that
514 * its parent doesn't have.
515 */
516static int bounds_check_role_callback(hashtab_key_t k,
517				      hashtab_datum_t d, void *args)
518{
519	struct bounds_args *a = (struct bounds_args *)args;
520	role_datum_t *r = (role_datum_t *) d;
521	role_datum_t *rp = NULL;
522
523	if (!r->bounds)
524		return 0;
525
526	rp = a->p->role_val_to_struct[r->bounds - 1];
527
528	if (rp && !ebitmap_contains(&rp->types.types, &r->types.types)) {
529		ERR(a->handle, "Role bounds violation, %s exceeds %s",
530		    (char *)k, a->p->p_role_val_to_name[rp->s.value - 1]);
531		a->numbad++;
532	}
533
534	return 0;
535}
536
537int bounds_check_roles(sepol_handle_t *handle, policydb_t *p)
538{
539	struct bounds_args args;
540
541	args.handle = handle;
542	args.p = p;
543	args.numbad = 0;
544
545	hashtab_map(p->p_roles.table, bounds_check_role_callback, &args);
546
547	if (args.numbad > 0) {
548		ERR(handle, "%d errors found during role bounds check",
549		    args.numbad);
550		return SEPOL_ERR;
551	}
552
553	return 0;
554}
555
556/* The user bounds is defined as: a child user cannot have a role that
557 * its parent doesn't have.
558 */
559static int bounds_check_user_callback(hashtab_key_t k,
560				      hashtab_datum_t d, void *args)
561{
562	struct bounds_args *a = (struct bounds_args *)args;
563	user_datum_t *u = (user_datum_t *) d;
564	user_datum_t *up = NULL;
565
566	if (!u->bounds)
567		return 0;
568
569	up = a->p->user_val_to_struct[u->bounds - 1];
570
571	if (up && !ebitmap_contains(&up->roles.roles, &u->roles.roles)) {
572		ERR(a->handle, "User bounds violation, %s exceeds %s",
573		    (char *) k, a->p->p_user_val_to_name[up->s.value - 1]);
574		a->numbad++;
575	}
576
577	return 0;
578}
579
580int bounds_check_users(sepol_handle_t *handle, policydb_t *p)
581{
582	struct bounds_args args;
583
584	args.handle = handle;
585	args.p = p;
586	args.numbad = 0;
587
588	hashtab_map(p->p_users.table, bounds_check_user_callback, &args);
589
590	if (args.numbad > 0) {
591		ERR(handle, "%d errors found during user bounds check",
592		    args.numbad);
593		return SEPOL_ERR;
594	}
595
596	return 0;
597}
598
599#define add_hierarchy_callback_template(prefix)				\
600	int hierarchy_add_##prefix##_callback(hashtab_key_t k __attribute__ ((unused)), \
601					    hashtab_datum_t d, void *args) \
602{								\
603	struct bounds_args *a = (struct bounds_args *)args;		\
604	sepol_handle_t *handle = a->handle;				\
605	policydb_t *p = a->p;						\
606	prefix##_datum_t *datum = (prefix##_datum_t *)d;		\
607	prefix##_datum_t *parent;					\
608	char *parent_name, *datum_name, *tmp;				\
609									\
610	if (!datum->bounds) {						\
611		datum_name = p->p_##prefix##_val_to_name[datum->s.value - 1]; \
612									\
613		tmp = strrchr(datum_name, '.');				\
614		/* no '.' means it has no parent */			\
615		if (!tmp) return 0;					\
616									\
617		parent_name = strdup(datum_name);			\
618		if (!parent_name) {					\
619			ERR(handle, "Insufficient memory");		\
620			return SEPOL_ENOMEM;				\
621		}							\
622		parent_name[tmp - datum_name] = '\0';			\
623									\
624		parent = hashtab_search(p->p_##prefix##s.table, parent_name); \
625		if (!parent) {						\
626			/* Orphan type/role/user */			\
627			ERR(handle, "%s doesn't exist, %s is an orphan",\
628			    parent_name,				\
629			    p->p_##prefix##_val_to_name[datum->s.value - 1]); \
630			free(parent_name);				\
631			a->numbad++;					\
632			return 0;					\
633		}							\
634		datum->bounds = parent->s.value;			\
635		free(parent_name);					\
636	}								\
637									\
638	return 0;							\
639}								\
640
641static add_hierarchy_callback_template(type)
642static add_hierarchy_callback_template(role)
643static add_hierarchy_callback_template(user)
644
645int hierarchy_add_bounds(sepol_handle_t *handle, policydb_t *p)
646{
647	int rc = 0;
648	struct bounds_args args;
649
650	args.handle = handle;
651	args.p = p;
652	args.numbad = 0;
653
654	rc = hashtab_map(p->p_users.table, hierarchy_add_user_callback, &args);
655	if (rc) goto exit;
656
657	rc = hashtab_map(p->p_roles.table, hierarchy_add_role_callback, &args);
658	if (rc) goto exit;
659
660	rc = hashtab_map(p->p_types.table, hierarchy_add_type_callback, &args);
661	if (rc) goto exit;
662
663	if (args.numbad > 0) {
664		ERR(handle, "%d errors found while adding hierarchies",
665		    args.numbad);
666		rc = SEPOL_ERR;
667	}
668
669exit:
670	return rc;
671}
672
673int hierarchy_check_constraints(sepol_handle_t * handle, policydb_t * p)
674{
675	int rc = 0;
676	int violation = 0;
677
678	rc = hierarchy_add_bounds(handle, p);
679	if (rc) goto exit;
680
681	rc = bounds_check_users(handle, p);
682	if (rc)
683		violation = 1;
684
685	rc = bounds_check_roles(handle, p);
686	if (rc)
687		violation = 1;
688
689	rc = bounds_check_types(handle, p);
690	if (rc) {
691		if (rc == SEPOL_ERR)
692			violation = 1;
693		else
694			goto exit;
695	}
696
697	if (violation)
698		rc = SEPOL_ERR;
699
700exit:
701	return rc;
702}
703