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_positive_bit(&p->attr_type_map[tgt - 1], tnode, i) {
115			avtab_key.target_type = i + 1;
116			rc = bounds_insert_rule(handle, avtab, global, other,
117						&avtab_key, &datum);
118			if (rc) goto exit;
119		}
120	}
121
122exit:
123	return rc;
124}
125
126static int bounds_expand_cond_rules(sepol_handle_t *handle, policydb_t *p,
127				    cond_av_list_t *cur, avtab_t *avtab,
128				    avtab_t *global, avtab_t *other,
129				    uint32_t parent)
130{
131	int rc = 0;
132
133	for (; cur; cur = cur->next) {
134		avtab_ptr_t n = cur->node;
135		rc = bounds_expand_rule(handle, p, avtab, global, other, parent,
136					n->key.source_type, n->key.target_type,
137					n->key.target_class, n->datum.data);
138		if (rc) goto exit;
139	}
140
141exit:
142	return rc;
143}
144
145struct bounds_expand_args {
146	sepol_handle_t *handle;
147	policydb_t *p;
148	avtab_t *avtab;
149	uint32_t parent;
150};
151
152static int bounds_expand_rule_callback(avtab_key_t *k, avtab_datum_t *d,
153				       void *args)
154{
155	struct bounds_expand_args *a = (struct bounds_expand_args *)args;
156
157	if (!(k->specified & AVTAB_ALLOWED))
158		return 0;
159
160	return bounds_expand_rule(a->handle, a->p, a->avtab, NULL, NULL,
161				  a->parent, k->source_type, k->target_type,
162				  k->target_class, d->data);
163}
164
165struct bounds_cond_info {
166	avtab_t true_avtab;
167	avtab_t false_avtab;
168	cond_list_t *cond_list;
169	struct bounds_cond_info *next;
170};
171
172static void bounds_destroy_cond_info(struct bounds_cond_info *cur)
173{
174	struct bounds_cond_info *next;
175
176	for (; cur; cur = next) {
177		next = cur->next;
178		avtab_destroy(&cur->true_avtab);
179		avtab_destroy(&cur->false_avtab);
180		cur->next = NULL;
181		free(cur);
182	}
183}
184
185static int bounds_expand_parent_rules(sepol_handle_t *handle, policydb_t *p,
186				      avtab_t *global_avtab,
187				      struct bounds_cond_info **cond_info,
188				      uint32_t parent)
189{
190	int rc = 0;
191	struct bounds_expand_args args;
192	cond_list_t *cur;
193
194	avtab_init(global_avtab);
195	rc = avtab_alloc(global_avtab, BOUNDS_AVTAB_SIZE);
196	if (rc) goto oom;
197
198	args.handle = handle;
199	args.p = p;
200	args.avtab = global_avtab;
201	args.parent = parent;
202	rc = avtab_map(&p->te_avtab, bounds_expand_rule_callback, &args);
203	if (rc) goto exit;
204
205	*cond_info = NULL;
206	for (cur = p->cond_list; cur; cur = cur->next) {
207		struct bounds_cond_info *ci;
208		ci = malloc(sizeof(struct bounds_cond_info));
209		if (!ci) goto oom;
210		avtab_init(&ci->true_avtab);
211		avtab_init(&ci->false_avtab);
212		ci->cond_list = cur;
213		ci->next = *cond_info;
214		*cond_info = ci;
215		if (cur->true_list) {
216			rc = avtab_alloc(&ci->true_avtab, BOUNDS_AVTAB_SIZE);
217			if (rc) goto oom;
218			rc = bounds_expand_cond_rules(handle, p, cur->true_list,
219						      &ci->true_avtab, NULL,
220						      NULL, parent);
221			if (rc) goto exit;
222		}
223		if (cur->false_list) {
224			rc = avtab_alloc(&ci->false_avtab, BOUNDS_AVTAB_SIZE);
225			if (rc) goto oom;
226			rc = bounds_expand_cond_rules(handle, p, cur->false_list,
227						      &ci->false_avtab,
228						      global_avtab,
229						      &ci->true_avtab, parent);
230			if (rc) goto exit;
231		}
232	}
233
234	return 0;
235
236oom:
237	ERR(handle, "Insufficient memory");
238
239exit:
240	ERR(handle,"Failed to expand parent rules");
241	avtab_destroy(global_avtab);
242	bounds_destroy_cond_info(*cond_info);
243	*cond_info = NULL;
244	return rc;
245}
246
247static int bounds_not_covered(avtab_t *global_avtab, avtab_t *cur_avtab,
248			      avtab_key_t *avtab_key, uint32_t data)
249{
250	avtab_datum_t *datum = avtab_search(cur_avtab, avtab_key);
251	if (datum)
252		data &= ~datum->data;
253	if (global_avtab && data) {
254		datum = avtab_search(global_avtab, avtab_key);
255		if (datum)
256			data &= ~datum->data;
257	}
258
259	return data;
260}
261
262static int bounds_add_bad(sepol_handle_t *handle, uint32_t src, uint32_t tgt,
263			  uint32_t class, uint32_t data, avtab_ptr_t *bad)
264{
265	struct avtab_node *new = malloc(sizeof(struct avtab_node));
266	if (new == NULL) {
267		ERR(handle, "Insufficient memory");
268		return SEPOL_ENOMEM;
269	}
270	memset(new, 0, sizeof(struct avtab_node));
271	new->key.source_type = src;
272	new->key.target_type = tgt;
273	new->key.target_class = class;
274	new->datum.data = data;
275	new->next = *bad;
276	*bad = new;
277
278	return 0;
279}
280
281static int bounds_check_rule(sepol_handle_t *handle, policydb_t *p,
282			     avtab_t *global_avtab, avtab_t *cur_avtab,
283			     uint32_t child, uint32_t parent, uint32_t src,
284			     uint32_t tgt, uint32_t class, uint32_t data,
285			     avtab_ptr_t *bad, int *numbad)
286{
287	int rc = 0;
288	avtab_key_t avtab_key;
289	type_datum_t *td;
290	ebitmap_node_t *tnode;
291	unsigned int i;
292	uint32_t d;
293
294	avtab_key.specified = AVTAB_ALLOWED;
295	avtab_key.target_class = class;
296
297	if (ebitmap_get_bit(&p->attr_type_map[src - 1], child - 1)) {
298		avtab_key.source_type = parent;
299		ebitmap_for_each_positive_bit(&p->attr_type_map[tgt - 1], tnode, i) {
300			td = p->type_val_to_struct[i];
301			if (td && td->bounds) {
302				avtab_key.target_type = td->bounds;
303				d = bounds_not_covered(global_avtab, cur_avtab,
304						       &avtab_key, data);
305			} else {
306				avtab_key.target_type = i + 1;
307				d = bounds_not_covered(global_avtab, cur_avtab,
308						       &avtab_key, data);
309			}
310			if (d) {
311				(*numbad)++;
312				rc = bounds_add_bad(handle, child, i+1, class, d, bad);
313				if (rc) goto exit;
314			}
315		}
316	}
317
318exit:
319	return rc;
320}
321
322static int bounds_check_cond_rules(sepol_handle_t *handle, policydb_t *p,
323				   avtab_t *global_avtab, avtab_t *cond_avtab,
324				   cond_av_list_t *rules, uint32_t child,
325				   uint32_t parent, avtab_ptr_t *bad,
326				   int *numbad)
327{
328	int rc = 0;
329	cond_av_list_t *cur;
330
331	for (cur = rules; cur; cur = cur->next) {
332		avtab_ptr_t ap = cur->node;
333		avtab_key_t *key = &ap->key;
334		avtab_datum_t *datum = &ap->datum;
335		if (!(key->specified & AVTAB_ALLOWED))
336			continue;
337		rc = bounds_check_rule(handle, p, global_avtab, cond_avtab,
338				       child, parent, key->source_type,
339				       key->target_type, key->target_class,
340				       datum->data, bad, numbad);
341		if (rc) goto exit;
342	}
343
344exit:
345	return rc;
346}
347
348struct bounds_check_args {
349	sepol_handle_t *handle;
350	policydb_t *p;
351	avtab_t *cur_avtab;
352	uint32_t child;
353	uint32_t parent;
354	avtab_ptr_t bad;
355	int numbad;
356};
357
358static int bounds_check_rule_callback(avtab_key_t *k, avtab_datum_t *d,
359				      void *args)
360{
361	struct bounds_check_args *a = (struct bounds_check_args *)args;
362
363	if (!(k->specified & AVTAB_ALLOWED))
364		return 0;
365
366	return bounds_check_rule(a->handle, a->p, NULL, a->cur_avtab, a->child,
367				 a->parent, k->source_type, k->target_type,
368				 k->target_class, d->data, &a->bad, &a->numbad);
369}
370
371static int bounds_check_child_rules(sepol_handle_t *handle, policydb_t *p,
372				    avtab_t *global_avtab,
373				    struct bounds_cond_info *cond_info,
374				    uint32_t child, uint32_t parent,
375				    avtab_ptr_t *bad, int *numbad)
376{
377	int rc;
378	struct bounds_check_args args;
379	struct bounds_cond_info *cur;
380
381	args.handle = handle;
382	args.p = p;
383	args.cur_avtab = global_avtab;
384	args.child = child;
385	args.parent = parent;
386	args.bad = NULL;
387	args.numbad = 0;
388	rc = avtab_map(&p->te_avtab, bounds_check_rule_callback, &args);
389	if (rc) goto exit;
390
391	for (cur = cond_info; cur; cur = cur->next) {
392		cond_list_t *node = cur->cond_list;
393		rc = bounds_check_cond_rules(handle, p, global_avtab,
394					     &cur->true_avtab,
395					     node->true_list, child, parent,
396					     &args.bad, &args.numbad);
397		if (rc) goto exit;
398
399		rc = bounds_check_cond_rules(handle, p, global_avtab,
400					     &cur->false_avtab,
401					     node->false_list, child, parent,
402					     &args.bad, &args.numbad);
403		if (rc) goto exit;
404	}
405
406	*numbad += args.numbad;
407	*bad = args.bad;
408
409exit:
410	return rc;
411}
412
413int bounds_check_type(sepol_handle_t *handle, policydb_t *p, uint32_t child,
414		      uint32_t parent, avtab_ptr_t *bad, int *numbad)
415{
416	int rc = 0;
417	avtab_t global_avtab;
418	struct bounds_cond_info *cond_info = NULL;
419
420	rc = bounds_expand_parent_rules(handle, p, &global_avtab, &cond_info, parent);
421	if (rc) goto exit;
422
423	rc = bounds_check_child_rules(handle, p, &global_avtab, cond_info,
424				      child, parent, bad, numbad);
425
426	bounds_destroy_cond_info(cond_info);
427	avtab_destroy(&global_avtab);
428
429exit:
430	return rc;
431}
432
433struct bounds_args {
434	sepol_handle_t *handle;
435	policydb_t *p;
436	int numbad;
437};
438
439static void bounds_report(sepol_handle_t *handle, policydb_t *p, uint32_t child,
440			  uint32_t parent, avtab_ptr_t cur)
441{
442	ERR(handle, "Child type %s exceeds bounds of parent %s in the following rules:",
443	    p->p_type_val_to_name[child - 1],
444	    p->p_type_val_to_name[parent - 1]);
445	for (; cur; cur = cur->next) {
446		ERR(handle, "    %s %s : %s { %s }",
447		    p->p_type_val_to_name[cur->key.source_type - 1],
448		    p->p_type_val_to_name[cur->key.target_type - 1],
449		    p->p_class_val_to_name[cur->key.target_class - 1],
450		    sepol_av_to_string(p, cur->key.target_class,
451				       cur->datum.data));
452	}
453}
454
455void bounds_destroy_bad(avtab_ptr_t cur)
456{
457	avtab_ptr_t next;
458
459	for (; cur; cur = next) {
460		next = cur->next;
461		cur->next = NULL;
462		free(cur);
463	}
464}
465
466static int bounds_check_type_callback(hashtab_key_t k __attribute__ ((unused)),
467				      hashtab_datum_t d, void *args)
468{
469	int rc = 0;
470	struct bounds_args *a = (struct bounds_args *)args;
471	type_datum_t *t = (type_datum_t *)d;
472	avtab_ptr_t bad = NULL;
473
474	if (t->bounds) {
475		rc = bounds_check_type(a->handle, a->p, t->s.value, t->bounds,
476				       &bad, &a->numbad);
477		if (bad) {
478			bounds_report(a->handle, a->p, t->s.value, t->bounds,
479				      bad);
480			bounds_destroy_bad(bad);
481		}
482	}
483
484	return rc;
485}
486
487int bounds_check_types(sepol_handle_t *handle, policydb_t *p)
488{
489	int rc;
490	struct bounds_args args;
491
492	args.handle = handle;
493	args.p = p;
494	args.numbad = 0;
495
496	rc = hashtab_map(p->p_types.table, bounds_check_type_callback, &args);
497	if (rc) goto exit;
498
499	if (args.numbad > 0) {
500		ERR(handle, "%d errors found during type bounds check",
501		    args.numbad);
502		rc = SEPOL_ERR;
503	}
504
505exit:
506	return rc;
507}
508
509/* The role bounds is defined as: a child role cannot have a type that
510 * its parent doesn't have.
511 */
512static int bounds_check_role_callback(hashtab_key_t k,
513				      hashtab_datum_t d, void *args)
514{
515	struct bounds_args *a = (struct bounds_args *)args;
516	role_datum_t *r = (role_datum_t *) d;
517	role_datum_t *rp = NULL;
518
519	if (!r->bounds)
520		return 0;
521
522	rp = a->p->role_val_to_struct[r->bounds - 1];
523
524	if (rp && !ebitmap_contains(&rp->types.types, &r->types.types)) {
525		ERR(a->handle, "Role bounds violation, %s exceeds %s",
526		    (char *)k, a->p->p_role_val_to_name[rp->s.value - 1]);
527		a->numbad++;
528	}
529
530	return 0;
531}
532
533int bounds_check_roles(sepol_handle_t *handle, policydb_t *p)
534{
535	struct bounds_args args;
536
537	args.handle = handle;
538	args.p = p;
539	args.numbad = 0;
540
541	hashtab_map(p->p_roles.table, bounds_check_role_callback, &args);
542
543	if (args.numbad > 0) {
544		ERR(handle, "%d errors found during role bounds check",
545		    args.numbad);
546		return SEPOL_ERR;
547	}
548
549	return 0;
550}
551
552/* The user bounds is defined as: a child user cannot have a role that
553 * its parent doesn't have.
554 */
555static int bounds_check_user_callback(hashtab_key_t k,
556				      hashtab_datum_t d, void *args)
557{
558	struct bounds_args *a = (struct bounds_args *)args;
559	user_datum_t *u = (user_datum_t *) d;
560	user_datum_t *up = NULL;
561
562	if (!u->bounds)
563		return 0;
564
565	up = a->p->user_val_to_struct[u->bounds - 1];
566
567	if (up && !ebitmap_contains(&up->roles.roles, &u->roles.roles)) {
568		ERR(a->handle, "User bounds violation, %s exceeds %s",
569		    (char *) k, a->p->p_user_val_to_name[up->s.value - 1]);
570		a->numbad++;
571	}
572
573	return 0;
574}
575
576int bounds_check_users(sepol_handle_t *handle, policydb_t *p)
577{
578	struct bounds_args args;
579
580	args.handle = handle;
581	args.p = p;
582	args.numbad = 0;
583
584	hashtab_map(p->p_users.table, bounds_check_user_callback, &args);
585
586	if (args.numbad > 0) {
587		ERR(handle, "%d errors found during user bounds check",
588		    args.numbad);
589		return SEPOL_ERR;
590	}
591
592	return 0;
593}
594
595#define add_hierarchy_callback_template(prefix)				\
596	int hierarchy_add_##prefix##_callback(hashtab_key_t k __attribute__ ((unused)), \
597					    hashtab_datum_t d, void *args) \
598{								\
599	struct bounds_args *a = (struct bounds_args *)args;		\
600	sepol_handle_t *handle = a->handle;				\
601	policydb_t *p = a->p;						\
602	prefix##_datum_t *datum = (prefix##_datum_t *)d;		\
603	prefix##_datum_t *parent;					\
604	char *parent_name, *datum_name, *tmp;				\
605									\
606	if (!datum->bounds) {						\
607		datum_name = p->p_##prefix##_val_to_name[datum->s.value - 1]; \
608									\
609		tmp = strrchr(datum_name, '.');				\
610		/* no '.' means it has no parent */			\
611		if (!tmp) return 0;					\
612									\
613		parent_name = strdup(datum_name);			\
614		if (!parent_name) {					\
615			ERR(handle, "Insufficient memory");		\
616			return SEPOL_ENOMEM;				\
617		}							\
618		parent_name[tmp - datum_name] = '\0';			\
619									\
620		parent = hashtab_search(p->p_##prefix##s.table, parent_name); \
621		if (!parent) {						\
622			/* Orphan type/role/user */			\
623			ERR(handle, "%s doesn't exist, %s is an orphan",\
624			    parent_name,				\
625			    p->p_##prefix##_val_to_name[datum->s.value - 1]); \
626			free(parent_name);				\
627			a->numbad++;					\
628			return 0;					\
629		}							\
630		datum->bounds = parent->s.value;			\
631		free(parent_name);					\
632	}								\
633									\
634	return 0;							\
635}								\
636
637static add_hierarchy_callback_template(type)
638static add_hierarchy_callback_template(role)
639static add_hierarchy_callback_template(user)
640
641int hierarchy_add_bounds(sepol_handle_t *handle, policydb_t *p)
642{
643	int rc = 0;
644	struct bounds_args args;
645
646	args.handle = handle;
647	args.p = p;
648	args.numbad = 0;
649
650	rc = hashtab_map(p->p_users.table, hierarchy_add_user_callback, &args);
651	if (rc) goto exit;
652
653	rc = hashtab_map(p->p_roles.table, hierarchy_add_role_callback, &args);
654	if (rc) goto exit;
655
656	rc = hashtab_map(p->p_types.table, hierarchy_add_type_callback, &args);
657	if (rc) goto exit;
658
659	if (args.numbad > 0) {
660		ERR(handle, "%d errors found while adding hierarchies",
661		    args.numbad);
662		rc = SEPOL_ERR;
663	}
664
665exit:
666	return rc;
667}
668
669int hierarchy_check_constraints(sepol_handle_t * handle, policydb_t * p)
670{
671	int rc = 0;
672	int violation = 0;
673
674	rc = hierarchy_add_bounds(handle, p);
675	if (rc) goto exit;
676
677	rc = bounds_check_users(handle, p);
678	if (rc)
679		violation = 1;
680
681	rc = bounds_check_roles(handle, p);
682	if (rc)
683		violation = 1;
684
685	rc = bounds_check_types(handle, p);
686	if (rc) {
687		if (rc == SEPOL_ERR)
688			violation = 1;
689		else
690			goto exit;
691	}
692
693	if (violation)
694		rc = SEPOL_ERR;
695
696exit:
697	return rc;
698}
699