xref: /third_party/selinux/libsepol/src/mls.c (revision 6cd6a6ac)
1/* Author : Stephen Smalley, <sds@tycho.nsa.gov> */
2/*
3 * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
4 *
5 *	Support for enhanced MLS infrastructure.
6 *
7 * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
8 *
9 *  This library is free software; you can redistribute it and/or
10 *  modify it under the terms of the GNU Lesser General Public
11 *  License as published by the Free Software Foundation; either
12 *  version 2.1 of the License, or (at your option) any later version.
13 *
14 *  This library is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 *  Lesser General Public License for more details.
18 *
19 *  You should have received a copy of the GNU Lesser General Public
20 *  License along with this library; if not, write to the Free Software
21 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22 */
23
24/* FLASK */
25
26/*
27 * Implementation of the multi-level security (MLS) policy.
28 */
29
30#include <sepol/context.h>
31#include <sepol/policydb/policydb.h>
32#include <sepol/policydb/services.h>
33#include <sepol/policydb/context.h>
34
35#include <stdlib.h>
36
37#include "handle.h"
38#include "debug.h"
39#include "private.h"
40#include "mls.h"
41
42int mls_to_string(sepol_handle_t * handle,
43		  const policydb_t * policydb,
44		  const context_struct_t * mls, char **str)
45{
46
47	char *ptr = NULL, *ptr2 = NULL;
48
49	/* Temporary buffer - length + NULL terminator */
50	int len = mls_compute_context_len(policydb, mls) + 1;
51
52	ptr = (char *)malloc(len);
53	if (ptr == NULL)
54		goto omem;
55
56	/* Final string w/ ':' cut off */
57	ptr2 = (char *)malloc(len - 1);
58	if (ptr2 == NULL)
59		goto omem;
60
61	mls_sid_to_context(policydb, mls, &ptr);
62	ptr -= len - 1;
63	strcpy(ptr2, ptr + 1);
64
65	free(ptr);
66	*str = ptr2;
67	return STATUS_SUCCESS;
68
69      omem:
70	ERR(handle, "out of memory, could not convert mls context to string");
71
72	free(ptr);
73	free(ptr2);
74	return STATUS_ERR;
75
76}
77
78int mls_from_string(sepol_handle_t * handle,
79		    const policydb_t * policydb,
80		    const char *str, context_struct_t * mls)
81{
82
83	char *tmp = strdup(str);
84	char *tmp_cp = tmp;
85	if (!tmp)
86		goto omem;
87
88	if (mls_context_to_sid(policydb, '$', &tmp_cp, mls) < 0) {
89		ERR(handle, "invalid MLS context %s", str);
90		free(tmp);
91		goto err;
92	}
93
94	free(tmp);
95	return STATUS_SUCCESS;
96
97      omem:
98	ERR(handle, "out of memory");
99
100      err:
101	ERR(handle, "could not construct mls context structure");
102	return STATUS_ERR;
103}
104
105/*
106 * Return the length in bytes for the MLS fields of the
107 * security context string representation of `context'.
108 */
109int mls_compute_context_len(const policydb_t * policydb,
110			    const context_struct_t * context)
111{
112
113	unsigned int i, l, len, range;
114	ebitmap_node_t *cnode;
115
116	if (!policydb->mls)
117		return 0;
118
119	len = 1;		/* for the beginning ":" */
120	for (l = 0; l < 2; l++) {
121		range = 0;
122		len +=
123		    strlen(policydb->
124			   p_sens_val_to_name[context->range.level[l].sens -
125					      1]);
126
127		ebitmap_for_each_bit(&context->range.level[l].cat, cnode, i) {
128			if (ebitmap_node_get_bit(cnode, i)) {
129				if (range) {
130					range++;
131					continue;
132				}
133
134				len +=
135				    strlen(policydb->p_cat_val_to_name[i]) + 1;
136				range++;
137			} else {
138				if (range > 1)
139					len +=
140					    strlen(policydb->
141						   p_cat_val_to_name[i - 1]) +
142					    1;
143				range = 0;
144			}
145		}
146		/* Handle case where last category is the end of range */
147		if (range > 1)
148			len += strlen(policydb->p_cat_val_to_name[i - 1]) + 1;
149
150		if (l == 0) {
151			if (mls_level_eq(&context->range.level[0],
152					 &context->range.level[1]))
153				break;
154			else
155				len++;
156		}
157	}
158
159	return len;
160}
161
162/*
163 * Write the security context string representation of
164 * the MLS fields of `context' into the string `*scontext'.
165 * Update `*scontext' to point to the end of the MLS fields.
166 */
167void mls_sid_to_context(const policydb_t * policydb,
168			const context_struct_t * context, char **scontext)
169{
170
171	char *scontextp;
172	unsigned int i, l, range, wrote_sep;
173	ebitmap_node_t *cnode;
174
175	if (!policydb->mls)
176		return;
177
178	scontextp = *scontext;
179
180	*scontextp = ':';
181	scontextp++;
182
183	for (l = 0; l < 2; l++) {
184		range = 0;
185		wrote_sep = 0;
186		strcpy(scontextp,
187		       policydb->p_sens_val_to_name[context->range.level[l].
188						    sens - 1]);
189		scontextp +=
190		    strlen(policydb->
191			   p_sens_val_to_name[context->range.level[l].sens -
192					      1]);
193		/* categories */
194		ebitmap_for_each_bit(&context->range.level[l].cat, cnode, i) {
195			if (ebitmap_node_get_bit(cnode, i)) {
196				if (range) {
197					range++;
198					continue;
199				}
200
201				if (!wrote_sep) {
202					*scontextp++ = ':';
203					wrote_sep = 1;
204				} else
205					*scontextp++ = ',';
206				strcpy(scontextp,
207				       policydb->p_cat_val_to_name[i]);
208				scontextp +=
209				    strlen(policydb->p_cat_val_to_name[i]);
210				range++;
211			} else {
212				if (range > 1) {
213					if (range > 2)
214						*scontextp++ = '.';
215					else
216						*scontextp++ = ',';
217
218					strcpy(scontextp,
219					       policydb->p_cat_val_to_name[i -
220									   1]);
221					scontextp +=
222					    strlen(policydb->
223						   p_cat_val_to_name[i - 1]);
224				}
225				range = 0;
226			}
227		}
228		/* Handle case where last category is the end of range */
229		if (range > 1) {
230			if (range > 2)
231				*scontextp++ = '.';
232			else
233				*scontextp++ = ',';
234
235			strcpy(scontextp, policydb->p_cat_val_to_name[i - 1]);
236			scontextp += strlen(policydb->p_cat_val_to_name[i - 1]);
237		}
238
239		if (l == 0) {
240			if (mls_level_eq(&context->range.level[0],
241					 &context->range.level[1]))
242				break;
243			else {
244				*scontextp = '-';
245				scontextp++;
246			}
247		}
248	}
249
250	*scontext = scontextp;
251	return;
252}
253
254/*
255 * Return 1 if the MLS fields in the security context
256 * structure `c' are valid.  Return 0 otherwise.
257 */
258int mls_context_isvalid(const policydb_t * p, const context_struct_t * c)
259{
260
261	level_datum_t *levdatum;
262	user_datum_t *usrdatum;
263	unsigned int i, l;
264	ebitmap_node_t *cnode;
265	hashtab_key_t key;
266
267	if (!p->mls)
268		return 1;
269
270	/*
271	 * MLS range validity checks: high must dominate low, low level must
272	 * be valid (category set <-> sensitivity check), and high level must
273	 * be valid (category set <-> sensitivity check)
274	 */
275	if (!mls_level_dom(&c->range.level[1], &c->range.level[0]))
276		/* High does not dominate low. */
277		return 0;
278
279	for (l = 0; l < 2; l++) {
280		if (!c->range.level[l].sens
281		    || c->range.level[l].sens > p->p_levels.nprim)
282			return 0;
283
284		key = p->p_sens_val_to_name[c->range.level[l].sens - 1];
285		if (!key)
286			return 0;
287
288		levdatum = (level_datum_t *) hashtab_search(p->p_levels.table, key);
289		if (!levdatum)
290			return 0;
291
292		ebitmap_for_each_positive_bit(&c->range.level[l].cat, cnode, i) {
293			if (i > p->p_cats.nprim)
294				return 0;
295			if (!ebitmap_get_bit(&levdatum->level->cat, i))
296				/*
297				 * Category may not be associated with
298				 * sensitivity in low level.
299				 */
300				return 0;
301		}
302	}
303
304	if (c->role == OBJECT_R_VAL)
305		return 1;
306
307	/*
308	 * User must be authorized for the MLS range.
309	 */
310	if (!c->user || c->user > p->p_users.nprim)
311		return 0;
312	usrdatum = p->user_val_to_struct[c->user - 1];
313	if (!usrdatum || !mls_range_contains(usrdatum->exp_range, c->range))
314		return 0;	/* user may not be associated with range */
315
316	return 1;
317}
318
319/*
320 * Set the MLS fields in the security context structure
321 * `context' based on the string representation in
322 * the string `*scontext'.  Update `*scontext' to
323 * point to the end of the string representation of
324 * the MLS fields.
325 *
326 * This function modifies the string in place, inserting
327 * NULL characters to terminate the MLS fields.
328 */
329int mls_context_to_sid(const policydb_t * policydb,
330		       char oldc, char **scontext, context_struct_t * context)
331{
332
333	char delim;
334	char *scontextp, *p, *rngptr;
335	level_datum_t *levdatum;
336	cat_datum_t *catdatum, *rngdatum;
337	unsigned int l;
338
339	if (!policydb->mls)
340		return 0;
341
342	/* No MLS component to the security context */
343	if (!oldc)
344		goto err;
345
346	/* Extract low sensitivity. */
347	scontextp = p = *scontext;
348	while (*p && *p != ':' && *p != '-')
349		p++;
350
351	delim = *p;
352	if (delim != 0)
353		*p++ = 0;
354
355	for (l = 0; l < 2; l++) {
356		levdatum =
357		    (level_datum_t *) hashtab_search(policydb->p_levels.table,
358						     (hashtab_key_t) scontextp);
359
360		if (!levdatum)
361			goto err;
362
363		context->range.level[l].sens = levdatum->level->sens;
364
365		if (delim == ':') {
366			/* Extract category set. */
367			while (1) {
368				scontextp = p;
369				while (*p && *p != ',' && *p != '-')
370					p++;
371				delim = *p;
372				if (delim != 0)
373					*p++ = 0;
374
375				/* Separate into range if exists */
376				if ((rngptr = strchr(scontextp, '.')) != NULL) {
377					/* Remove '.' */
378					*rngptr++ = 0;
379				}
380
381				catdatum =
382				    (cat_datum_t *) hashtab_search(policydb->
383								   p_cats.table,
384								   (hashtab_key_t)
385								   scontextp);
386				if (!catdatum)
387					goto err;
388
389				if (ebitmap_set_bit
390				    (&context->range.level[l].cat,
391				     catdatum->s.value - 1, 1))
392					goto err;
393
394				/* If range, set all categories in range */
395				if (rngptr) {
396					unsigned int i;
397
398					rngdatum = (cat_datum_t *)
399					    hashtab_search(policydb->p_cats.
400							   table,
401							   (hashtab_key_t)
402							   rngptr);
403					if (!rngdatum)
404						goto err;
405
406					if (catdatum->s.value >=
407					    rngdatum->s.value)
408						goto err;
409
410					for (i = catdatum->s.value;
411					     i < rngdatum->s.value; i++) {
412						if (ebitmap_set_bit
413						    (&context->range.level[l].
414						     cat, i, 1))
415							goto err;
416					}
417				}
418
419				if (delim != ',')
420					break;
421			}
422		}
423		if (delim == '-') {
424			/* Extract high sensitivity. */
425			scontextp = p;
426			while (*p && *p != ':')
427				p++;
428
429			delim = *p;
430			if (delim != 0)
431				*p++ = 0;
432		} else
433			break;
434	}
435
436	/* High level is missing, copy low level */
437	if (l == 0) {
438		if (mls_level_cpy(&context->range.level[1],
439				  &context->range.level[0]) < 0)
440			goto err;
441	}
442	*scontext = ++p;
443
444	return STATUS_SUCCESS;
445
446      err:
447	return STATUS_ERR;
448}
449
450/*
451 * Copies the MLS range from `src' into `dst'.
452 */
453static inline int mls_copy_context(context_struct_t * dst,
454				   const context_struct_t * src)
455{
456	int l, rc = 0;
457
458	/* Copy the MLS range from the source context */
459	for (l = 0; l < 2; l++) {
460		dst->range.level[l].sens = src->range.level[l].sens;
461		rc = ebitmap_cpy(&dst->range.level[l].cat,
462				 &src->range.level[l].cat);
463		if (rc)
464			break;
465	}
466
467	return rc;
468}
469
470/*
471 * Copies the effective MLS range from `src' into `dst'.
472 */
473static inline int mls_scopy_context(context_struct_t * dst,
474				    const context_struct_t * src)
475{
476	int l, rc = 0;
477
478	/* Copy the MLS range from the source context */
479	for (l = 0; l < 2; l++) {
480		dst->range.level[l].sens = src->range.level[0].sens;
481		rc = ebitmap_cpy(&dst->range.level[l].cat,
482				 &src->range.level[0].cat);
483		if (rc)
484			break;
485	}
486
487	return rc;
488}
489
490/*
491 * Copies the MLS range `range' into `context'.
492 */
493static inline int mls_range_set(context_struct_t * context, const mls_range_t * range)
494{
495	int l, rc = 0;
496
497	/* Copy the MLS range into the  context */
498	for (l = 0; l < 2; l++) {
499		context->range.level[l].sens = range->level[l].sens;
500		rc = ebitmap_cpy(&context->range.level[l].cat,
501				 &range->level[l].cat);
502		if (rc)
503			break;
504	}
505
506	return rc;
507}
508
509int mls_setup_user_range(context_struct_t * fromcon, user_datum_t * user,
510			 context_struct_t * usercon, int mls)
511{
512	if (mls) {
513		mls_level_t *fromcon_sen = &(fromcon->range.level[0]);
514		mls_level_t *fromcon_clr = &(fromcon->range.level[1]);
515		mls_level_t *user_low = &(user->exp_range.level[0]);
516		mls_level_t *user_clr = &(user->exp_range.level[1]);
517		mls_level_t *user_def = &(user->exp_dfltlevel);
518		mls_level_t *usercon_sen = &(usercon->range.level[0]);
519		mls_level_t *usercon_clr = &(usercon->range.level[1]);
520
521		/* Honor the user's default level if we can */
522		if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) {
523			*usercon_sen = *user_def;
524		} else if (mls_level_between(fromcon_sen, user_def, user_clr)) {
525			*usercon_sen = *fromcon_sen;
526		} else if (mls_level_between(fromcon_clr, user_low, user_def)) {
527			*usercon_sen = *user_low;
528		} else
529			return -EINVAL;
530
531		/* Lower the clearance of available contexts
532		   if the clearance of "fromcon" is lower than
533		   that of the user's default clearance (but
534		   only if the "fromcon" clearance dominates
535		   the user's computed sensitivity level) */
536		if (mls_level_dom(user_clr, fromcon_clr)) {
537			*usercon_clr = *fromcon_clr;
538		} else if (mls_level_dom(fromcon_clr, user_clr)) {
539			*usercon_clr = *user_clr;
540		} else
541			return -EINVAL;
542	}
543
544	return 0;
545}
546
547/*
548 * Convert the MLS fields in the security context
549 * structure `c' from the values specified in the
550 * policy `oldp' to the values specified in the policy `newp'.
551 */
552int mls_convert_context(policydb_t * oldp,
553			policydb_t * newp, context_struct_t * c)
554{
555	level_datum_t *levdatum;
556	cat_datum_t *catdatum;
557	ebitmap_t bitmap;
558	unsigned int l, i;
559	ebitmap_node_t *cnode;
560
561	if (!oldp->mls)
562		return 0;
563
564	for (l = 0; l < 2; l++) {
565		levdatum =
566		    (level_datum_t *) hashtab_search(newp->p_levels.table,
567						     oldp->
568						     p_sens_val_to_name[c->
569									range.
570									level
571									[l].
572									sens -
573									1]);
574
575		if (!levdatum)
576			return -EINVAL;
577		c->range.level[l].sens = levdatum->level->sens;
578
579		ebitmap_init(&bitmap);
580		ebitmap_for_each_positive_bit(&c->range.level[l].cat, cnode, i) {
581			int rc;
582
583			catdatum =
584			    (cat_datum_t *) hashtab_search(newp->p_cats.
585							   table,
586							   oldp->
587							   p_cat_val_to_name
588							   [i]);
589			if (!catdatum)
590				return -EINVAL;
591			rc = ebitmap_set_bit(&bitmap,
592					     catdatum->s.value - 1, 1);
593			if (rc)
594				return rc;
595		}
596		ebitmap_destroy(&c->range.level[l].cat);
597		c->range.level[l].cat = bitmap;
598	}
599
600	return 0;
601}
602
603int mls_compute_sid(policydb_t * policydb,
604		    const context_struct_t * scontext,
605		    const context_struct_t * tcontext,
606		    sepol_security_class_t tclass,
607		    uint32_t specified, context_struct_t * newcontext)
608{
609	range_trans_t rtr;
610	struct mls_range *r;
611	struct class_datum *cladatum;
612	int default_range = 0;
613
614	if (!policydb->mls)
615		return 0;
616
617	switch (specified) {
618	case AVTAB_TRANSITION:
619		/* Look for a range transition rule. */
620		rtr.source_type = scontext->type;
621		rtr.target_type = tcontext->type;
622		rtr.target_class = tclass;
623		r = hashtab_search(policydb->range_tr, (hashtab_key_t) &rtr);
624		if (r)
625			return mls_range_set(newcontext, r);
626
627		if (tclass && tclass <= policydb->p_classes.nprim) {
628			cladatum = policydb->class_val_to_struct[tclass - 1];
629			if (cladatum)
630				default_range = cladatum->default_range;
631		}
632
633		switch (default_range) {
634		case DEFAULT_SOURCE_LOW:
635			return mls_context_cpy_low(newcontext, scontext);
636		case DEFAULT_SOURCE_HIGH:
637			return mls_context_cpy_high(newcontext, scontext);
638		case DEFAULT_SOURCE_LOW_HIGH:
639			return mls_context_cpy(newcontext, scontext);
640		case DEFAULT_TARGET_LOW:
641			return mls_context_cpy_low(newcontext, tcontext);
642		case DEFAULT_TARGET_HIGH:
643			return mls_context_cpy_high(newcontext, tcontext);
644		case DEFAULT_TARGET_LOW_HIGH:
645			return mls_context_cpy(newcontext, tcontext);
646		case DEFAULT_GLBLUB:
647			return mls_context_glblub(newcontext, scontext, tcontext);
648		}
649
650		/* Fallthrough */
651	case AVTAB_CHANGE:
652		if (tclass == policydb->process_class)
653			/* Use the process MLS attributes. */
654			return mls_copy_context(newcontext, scontext);
655		else
656			/* Use the process effective MLS attributes. */
657			return mls_scopy_context(newcontext, scontext);
658	case AVTAB_MEMBER:
659		/* Use the process effective MLS attributes. */
660		return mls_context_cpy_low(newcontext, scontext);
661	default:
662		return -EINVAL;
663	}
664	return -EINVAL;
665}
666
667int sepol_mls_contains(sepol_handle_t * handle,
668		       const sepol_policydb_t * policydb,
669		       const char *mls1, const char *mls2, int *response)
670{
671
672	context_struct_t *ctx1 = NULL, *ctx2 = NULL;
673	ctx1 = malloc(sizeof(context_struct_t));
674	ctx2 = malloc(sizeof(context_struct_t));
675	if (ctx1 == NULL || ctx2 == NULL)
676		goto omem;
677	context_init(ctx1);
678	context_init(ctx2);
679
680	if (mls_from_string(handle, &policydb->p, mls1, ctx1) < 0)
681		goto err;
682
683	if (mls_from_string(handle, &policydb->p, mls2, ctx2) < 0)
684		goto err;
685
686	*response = mls_range_contains(ctx1->range, ctx2->range);
687	context_destroy(ctx1);
688	context_destroy(ctx2);
689	free(ctx1);
690	free(ctx2);
691	return STATUS_SUCCESS;
692
693      omem:
694	ERR(handle, "out of memory");
695
696      err:
697	ERR(handle, "could not check if mls context %s contains %s",
698	    mls1, mls2);
699	context_destroy(ctx1);
700	context_destroy(ctx2);
701	free(ctx1);
702	free(ctx2);
703	return STATUS_ERR;
704}
705
706int sepol_mls_check(sepol_handle_t * handle,
707		    const sepol_policydb_t * policydb, const char *mls)
708{
709
710	int ret;
711	context_struct_t *con = malloc(sizeof(context_struct_t));
712	if (!con) {
713		ERR(handle, "out of memory, could not check if "
714		    "mls context %s is valid", mls);
715		return STATUS_ERR;
716	}
717	context_init(con);
718
719	ret = mls_from_string(handle, &policydb->p, mls, con);
720	context_destroy(con);
721	free(con);
722	return ret;
723}
724
725void mls_semantic_cat_init(mls_semantic_cat_t * c)
726{
727	memset(c, 0, sizeof(mls_semantic_cat_t));
728}
729
730void mls_semantic_cat_destroy(mls_semantic_cat_t * c __attribute__ ((unused)))
731{
732	/* it's currently a simple struct - really nothing to destroy */
733	return;
734}
735
736void mls_semantic_level_init(mls_semantic_level_t * l)
737{
738	memset(l, 0, sizeof(mls_semantic_level_t));
739}
740
741void mls_semantic_level_destroy(mls_semantic_level_t * l)
742{
743	mls_semantic_cat_t *cur, *next;
744
745	if (l == NULL)
746		return;
747
748	next = l->cat;
749	while (next) {
750		cur = next;
751		next = cur->next;
752		mls_semantic_cat_destroy(cur);
753		free(cur);
754	}
755}
756
757int mls_semantic_level_cpy(mls_semantic_level_t * dst,
758			   const mls_semantic_level_t * src)
759{
760	const mls_semantic_cat_t *cat;
761	mls_semantic_cat_t *newcat, *lnewcat = NULL;
762
763	mls_semantic_level_init(dst);
764	dst->sens = src->sens;
765	cat = src->cat;
766	while (cat) {
767		newcat =
768		    (mls_semantic_cat_t *) malloc(sizeof(mls_semantic_cat_t));
769		if (!newcat)
770			goto err;
771
772		mls_semantic_cat_init(newcat);
773		if (lnewcat)
774			lnewcat->next = newcat;
775		else
776			dst->cat = newcat;
777
778		newcat->low = cat->low;
779		newcat->high = cat->high;
780
781		lnewcat = newcat;
782		cat = cat->next;
783	}
784	return 0;
785
786      err:
787	mls_semantic_level_destroy(dst);
788	return -1;
789}
790
791void mls_semantic_range_init(mls_semantic_range_t * r)
792{
793	mls_semantic_level_init(&r->level[0]);
794	mls_semantic_level_init(&r->level[1]);
795}
796
797void mls_semantic_range_destroy(mls_semantic_range_t * r)
798{
799	mls_semantic_level_destroy(&r->level[0]);
800	mls_semantic_level_destroy(&r->level[1]);
801}
802
803int mls_semantic_range_cpy(mls_semantic_range_t * dst,
804			   const mls_semantic_range_t * src)
805{
806	if (mls_semantic_level_cpy(&dst->level[0], &src->level[0]) < 0)
807		return -1;
808
809	if (mls_semantic_level_cpy(&dst->level[1], &src->level[1]) < 0) {
810		mls_semantic_level_destroy(&dst->level[0]);
811		return -1;
812	}
813
814	return 0;
815}
816