mls.c revision 255e72915d4cbddceb435e13d81601755714e9f3
1/* Author : Stephen Smalley, <sds@epoch.ncsc.mil> */
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/policydb/policydb.h>
31#include <sepol/policydb/services.h>
32#include <sepol/policydb/flask.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
266	if (!p->mls)
267		return 1;
268
269	/*
270	 * MLS range validity checks: high must dominate low, low level must
271	 * be valid (category set <-> sensitivity check), and high level must
272	 * be valid (category set <-> sensitivity check)
273	 */
274	if (!mls_level_dom(&c->range.level[1], &c->range.level[0]))
275		/* High does not dominate low. */
276		return 0;
277
278	for (l = 0; l < 2; l++) {
279		if (!c->range.level[l].sens
280		    || c->range.level[l].sens > p->p_levels.nprim)
281			return 0;
282		levdatum = (level_datum_t *) hashtab_search(p->p_levels.table,
283							    p->
284							    p_sens_val_to_name
285							    [c->range.level[l].
286							     sens - 1]);
287		if (!levdatum)
288			return 0;
289
290		ebitmap_for_each_bit(&c->range.level[l].cat, cnode, i) {
291			if (ebitmap_node_get_bit(cnode, i)) {
292				if (i > p->p_cats.nprim)
293					return 0;
294				if (!ebitmap_get_bit(&levdatum->level->cat, i))
295					/*
296					 * Category may not be associated with
297					 * sensitivity in low level.
298					 */
299					return 0;
300			}
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 (!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				   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				    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, 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_bit(&c->range.level[l].cat, cnode, i) {
581			if (ebitmap_node_get_bit(cnode, i)) {
582				int rc;
583
584				catdatum =
585				    (cat_datum_t *) hashtab_search(newp->p_cats.
586								   table,
587								   oldp->
588								   p_cat_val_to_name
589								   [i]);
590				if (!catdatum)
591					return -EINVAL;
592				rc = ebitmap_set_bit(&bitmap,
593						     catdatum->s.value - 1, 1);
594				if (rc)
595					return rc;
596			}
597		}
598		ebitmap_destroy(&c->range.level[l].cat);
599		c->range.level[l].cat = bitmap;
600	}
601
602	return 0;
603}
604
605int mls_compute_sid(policydb_t * policydb,
606		    context_struct_t * scontext,
607		    context_struct_t * tcontext,
608		    sepol_security_class_t tclass,
609		    uint32_t specified, context_struct_t * newcontext)
610{
611	range_trans_t *rtr;
612	if (!policydb->mls)
613		return 0;
614
615	switch (specified) {
616	case AVTAB_TRANSITION:
617		/* Look for a range transition rule. */
618		for (rtr = policydb->range_tr; rtr; rtr = rtr->next) {
619			if (rtr->source_type == scontext->type &&
620			    rtr->target_type == tcontext->type &&
621			    rtr->target_class == tclass) {
622				/* Set the range from the rule */
623				return mls_range_set(newcontext,
624						     &rtr->target_range);
625			}
626		}
627		/* Fallthrough */
628	case AVTAB_CHANGE:
629		if (tclass == SECCLASS_PROCESS)
630			/* Use the process MLS attributes. */
631			return mls_copy_context(newcontext, scontext);
632		else
633			/* Use the process effective MLS attributes. */
634			return mls_scopy_context(newcontext, scontext);
635	case AVTAB_MEMBER:
636		/* Only polyinstantiate the MLS attributes if
637		   the type is being polyinstantiated */
638		if (newcontext->type != tcontext->type) {
639			/* Use the process effective MLS attributes. */
640			return mls_scopy_context(newcontext, scontext);
641		} else {
642			/* Use the related object MLS attributes. */
643			return mls_copy_context(newcontext, tcontext);
644		}
645	default:
646		return -EINVAL;
647	}
648	return -EINVAL;
649}
650
651int sepol_mls_contains(sepol_handle_t * handle,
652		       sepol_policydb_t * policydb,
653		       const char *mls1, const char *mls2, int *response)
654{
655
656	context_struct_t *ctx1 = NULL, *ctx2 = NULL;
657	ctx1 = malloc(sizeof(context_struct_t));
658	ctx2 = malloc(sizeof(context_struct_t));
659	if (ctx1 == NULL || ctx2 == NULL)
660		goto omem;
661	context_init(ctx1);
662	context_init(ctx2);
663
664	if (mls_from_string(handle, &policydb->p, mls1, ctx1) < 0)
665		goto err;
666
667	if (mls_from_string(handle, &policydb->p, mls2, ctx2) < 0)
668		goto err;
669
670	*response = mls_range_contains(ctx1->range, ctx2->range);
671	context_destroy(ctx1);
672	context_destroy(ctx2);
673	free(ctx1);
674	free(ctx2);
675	return STATUS_SUCCESS;
676
677      omem:
678	ERR(handle, "out of memory");
679
680      err:
681	ERR(handle, "could not check if mls context %s contains %s",
682	    mls1, mls2);
683	context_destroy(ctx1);
684	context_destroy(ctx2);
685	free(ctx1);
686	free(ctx2);
687	return STATUS_ERR;
688}
689
690int sepol_mls_check(sepol_handle_t * handle,
691		    sepol_policydb_t * policydb, const char *mls)
692{
693
694	int ret;
695	context_struct_t *con = malloc(sizeof(context_struct_t));
696	if (!con) {
697		ERR(handle, "out of memory, could not check if "
698		    "mls context %s is valid", mls);
699		return STATUS_ERR;
700	}
701	context_init(con);
702
703	ret = mls_from_string(handle, &policydb->p, mls, con);
704	context_destroy(con);
705	free(con);
706	return ret;
707}
708
709void mls_semantic_cat_init(mls_semantic_cat_t * c)
710{
711	memset(c, 0, sizeof(mls_semantic_cat_t));
712}
713
714void mls_semantic_cat_destroy(mls_semantic_cat_t * c __attribute__ ((unused)))
715{
716	/* it's currently a simple struct - really nothing to destroy */
717	return;
718}
719
720void mls_semantic_level_init(mls_semantic_level_t * l)
721{
722	memset(l, 0, sizeof(mls_semantic_level_t));
723}
724
725void mls_semantic_level_destroy(mls_semantic_level_t * l)
726{
727	mls_semantic_cat_t *cur, *next;
728
729	if (l == NULL)
730		return;
731
732	next = l->cat;
733	while (next) {
734		cur = next;
735		next = cur->next;
736		mls_semantic_cat_destroy(cur);
737		free(cur);
738	}
739}
740
741int mls_semantic_level_cpy(mls_semantic_level_t * dst,
742			   mls_semantic_level_t * src)
743{
744	mls_semantic_cat_t *cat, *newcat, *lnewcat = NULL;
745
746	mls_semantic_level_init(dst);
747	dst->sens = src->sens;
748	cat = src->cat;
749	while (cat) {
750		newcat =
751		    (mls_semantic_cat_t *) malloc(sizeof(mls_semantic_cat_t));
752		if (!newcat)
753			goto err;
754
755		mls_semantic_cat_init(newcat);
756		if (lnewcat)
757			lnewcat->next = newcat;
758		else
759			dst->cat = newcat;
760
761		newcat->low = cat->low;
762		newcat->high = cat->high;
763
764		lnewcat = newcat;
765		cat = cat->next;
766	}
767	return 0;
768
769      err:
770	mls_semantic_level_destroy(dst);
771	return -1;
772}
773
774void mls_semantic_range_init(mls_semantic_range_t * r)
775{
776	mls_semantic_level_init(&r->level[0]);
777	mls_semantic_level_init(&r->level[1]);
778}
779
780void mls_semantic_range_destroy(mls_semantic_range_t * r)
781{
782	mls_semantic_level_destroy(&r->level[0]);
783	mls_semantic_level_destroy(&r->level[1]);
784}
785
786int mls_semantic_range_cpy(mls_semantic_range_t * dst,
787			   mls_semantic_range_t * src)
788{
789	if (mls_semantic_level_cpy(&dst->level[0], &src->level[0]) < 0)
790		return -1;
791
792	if (mls_semantic_level_cpy(&dst->level[1], &src->level[1]) < 0) {
793		mls_semantic_level_destroy(&dst->level[0]);
794		return -1;
795	}
796
797	return 0;
798}
799