xref: /openbmc/linux/security/selinux/ss/mls.c (revision 0bd2af46839ad6262d25714a6ec0365db9d6b98f)
1 /*
2  * Implementation of the multi-level security (MLS) policy.
3  *
4  * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
5  */
6 /*
7  * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
8  *
9  *	Support for enhanced MLS infrastructure.
10  *
11  * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
12  */
13 /*
14  * Updated: Hewlett-Packard <paul.moore@hp.com>
15  *
16  *      Added support to import/export the MLS label
17  *
18  * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
19  */
20 
21 #include <linux/kernel.h>
22 #include <linux/slab.h>
23 #include <linux/string.h>
24 #include <linux/errno.h>
25 #include "sidtab.h"
26 #include "mls.h"
27 #include "policydb.h"
28 #include "services.h"
29 
30 /*
31  * Return the length in bytes for the MLS fields of the
32  * security context string representation of `context'.
33  */
34 int mls_compute_context_len(struct context * context)
35 {
36 	int i, l, len, range;
37 	struct ebitmap_node *node;
38 
39 	if (!selinux_mls_enabled)
40 		return 0;
41 
42 	len = 1; /* for the beginning ":" */
43 	for (l = 0; l < 2; l++) {
44 		range = 0;
45 		len += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
46 
47 		ebitmap_for_each_bit(&context->range.level[l].cat, node, i) {
48 			if (ebitmap_node_get_bit(node, i)) {
49 				if (range) {
50 					range++;
51 					continue;
52 				}
53 
54 				len += strlen(policydb.p_cat_val_to_name[i]) + 1;
55 				range++;
56 			} else {
57 				if (range > 1)
58 					len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1;
59 				range = 0;
60 			}
61 		}
62 		/* Handle case where last category is the end of range */
63 		if (range > 1)
64 			len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1;
65 
66 		if (l == 0) {
67 			if (mls_level_eq(&context->range.level[0],
68 			                 &context->range.level[1]))
69 				break;
70 			else
71 				len++;
72 		}
73 	}
74 
75 	return len;
76 }
77 
78 /*
79  * Write the security context string representation of
80  * the MLS fields of `context' into the string `*scontext'.
81  * Update `*scontext' to point to the end of the MLS fields.
82  */
83 void mls_sid_to_context(struct context *context,
84                         char **scontext)
85 {
86 	char *scontextp;
87 	int i, l, range, wrote_sep;
88 	struct ebitmap_node *node;
89 
90 	if (!selinux_mls_enabled)
91 		return;
92 
93 	scontextp = *scontext;
94 
95 	*scontextp = ':';
96 	scontextp++;
97 
98 	for (l = 0; l < 2; l++) {
99 		range = 0;
100 		wrote_sep = 0;
101 		strcpy(scontextp,
102 		       policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
103 		scontextp += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
104 
105 		/* categories */
106 		ebitmap_for_each_bit(&context->range.level[l].cat, node, i) {
107 			if (ebitmap_node_get_bit(node, i)) {
108 				if (range) {
109 					range++;
110 					continue;
111 				}
112 
113 				if (!wrote_sep) {
114 					*scontextp++ = ':';
115 					wrote_sep = 1;
116 				} else
117 					*scontextp++ = ',';
118 				strcpy(scontextp, policydb.p_cat_val_to_name[i]);
119 				scontextp += strlen(policydb.p_cat_val_to_name[i]);
120 				range++;
121 			} else {
122 				if (range > 1) {
123 					if (range > 2)
124 						*scontextp++ = '.';
125 					else
126 						*scontextp++ = ',';
127 
128 					strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]);
129 					scontextp += strlen(policydb.p_cat_val_to_name[i - 1]);
130 				}
131 				range = 0;
132 			}
133 		}
134 
135 		/* Handle case where last category is the end of range */
136 		if (range > 1) {
137 			if (range > 2)
138 				*scontextp++ = '.';
139 			else
140 				*scontextp++ = ',';
141 
142 			strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]);
143 			scontextp += strlen(policydb.p_cat_val_to_name[i - 1]);
144 		}
145 
146 		if (l == 0) {
147 			if (mls_level_eq(&context->range.level[0],
148 			                 &context->range.level[1]))
149 				break;
150 			else {
151 				*scontextp = '-';
152 				scontextp++;
153 			}
154 		}
155 	}
156 
157 	*scontext = scontextp;
158 	return;
159 }
160 
161 /*
162  * Return 1 if the MLS fields in the security context
163  * structure `c' are valid.  Return 0 otherwise.
164  */
165 int mls_context_isvalid(struct policydb *p, struct context *c)
166 {
167 	struct level_datum *levdatum;
168 	struct user_datum *usrdatum;
169 	struct ebitmap_node *node;
170 	int i, l;
171 
172 	if (!selinux_mls_enabled)
173 		return 1;
174 
175 	/*
176 	 * MLS range validity checks: high must dominate low, low level must
177 	 * be valid (category set <-> sensitivity check), and high level must
178 	 * be valid (category set <-> sensitivity check)
179 	 */
180 	if (!mls_level_dom(&c->range.level[1], &c->range.level[0]))
181 		/* High does not dominate low. */
182 		return 0;
183 
184 	for (l = 0; l < 2; l++) {
185 		if (!c->range.level[l].sens || c->range.level[l].sens > p->p_levels.nprim)
186 			return 0;
187 		levdatum = hashtab_search(p->p_levels.table,
188 			p->p_sens_val_to_name[c->range.level[l].sens - 1]);
189 		if (!levdatum)
190 			return 0;
191 
192 		ebitmap_for_each_bit(&c->range.level[l].cat, node, i) {
193 			if (ebitmap_node_get_bit(node, i)) {
194 				if (i > p->p_cats.nprim)
195 					return 0;
196 				if (!ebitmap_get_bit(&levdatum->level->cat, i))
197 					/*
198 					 * Category may not be associated with
199 					 * sensitivity in low level.
200 					 */
201 					return 0;
202 			}
203 		}
204 	}
205 
206 	if (c->role == OBJECT_R_VAL)
207 		return 1;
208 
209 	/*
210 	 * User must be authorized for the MLS range.
211 	 */
212 	if (!c->user || c->user > p->p_users.nprim)
213 		return 0;
214 	usrdatum = p->user_val_to_struct[c->user - 1];
215 	if (!mls_range_contains(usrdatum->range, c->range))
216 		return 0; /* user may not be associated with range */
217 
218 	return 1;
219 }
220 
221 /*
222  * Set the MLS fields in the security context structure
223  * `context' based on the string representation in
224  * the string `*scontext'.  Update `*scontext' to
225  * point to the end of the string representation of
226  * the MLS fields.
227  *
228  * This function modifies the string in place, inserting
229  * NULL characters to terminate the MLS fields.
230  *
231  * If a def_sid is provided and no MLS field is present,
232  * copy the MLS field of the associated default context.
233  * Used for upgraded to MLS systems where objects may lack
234  * MLS fields.
235  *
236  * Policy read-lock must be held for sidtab lookup.
237  *
238  */
239 int mls_context_to_sid(char oldc,
240 		       char **scontext,
241 		       struct context *context,
242 		       struct sidtab *s,
243 		       u32 def_sid)
244 {
245 
246 	char delim;
247 	char *scontextp, *p, *rngptr;
248 	struct level_datum *levdatum;
249 	struct cat_datum *catdatum, *rngdatum;
250 	int l, rc = -EINVAL;
251 
252 	if (!selinux_mls_enabled) {
253 		if (def_sid != SECSID_NULL && oldc)
254 			*scontext += strlen(*scontext)+1;
255 		return 0;
256 	}
257 
258 	/*
259 	 * No MLS component to the security context, try and map to
260 	 * default if provided.
261 	 */
262 	if (!oldc) {
263 		struct context *defcon;
264 
265 		if (def_sid == SECSID_NULL)
266 			goto out;
267 
268 		defcon = sidtab_search(s, def_sid);
269 		if (!defcon)
270 			goto out;
271 
272 		rc = mls_copy_context(context, defcon);
273 		goto out;
274 	}
275 
276 	/* Extract low sensitivity. */
277 	scontextp = p = *scontext;
278 	while (*p && *p != ':' && *p != '-')
279 		p++;
280 
281 	delim = *p;
282 	if (delim != 0)
283 		*p++ = 0;
284 
285 	for (l = 0; l < 2; l++) {
286 		levdatum = hashtab_search(policydb.p_levels.table, scontextp);
287 		if (!levdatum) {
288 			rc = -EINVAL;
289 			goto out;
290 		}
291 
292 		context->range.level[l].sens = levdatum->level->sens;
293 
294 		if (delim == ':') {
295 			/* Extract category set. */
296 			while (1) {
297 				scontextp = p;
298 				while (*p && *p != ',' && *p != '-')
299 					p++;
300 				delim = *p;
301 				if (delim != 0)
302 					*p++ = 0;
303 
304 				/* Separate into range if exists */
305 				if ((rngptr = strchr(scontextp, '.')) != NULL) {
306 					/* Remove '.' */
307 					*rngptr++ = 0;
308 				}
309 
310 				catdatum = hashtab_search(policydb.p_cats.table,
311 				                          scontextp);
312 				if (!catdatum) {
313 					rc = -EINVAL;
314 					goto out;
315 				}
316 
317 				rc = ebitmap_set_bit(&context->range.level[l].cat,
318 				                     catdatum->value - 1, 1);
319 				if (rc)
320 					goto out;
321 
322 				/* If range, set all categories in range */
323 				if (rngptr) {
324 					int i;
325 
326 					rngdatum = hashtab_search(policydb.p_cats.table, rngptr);
327 					if (!rngdatum) {
328 						rc = -EINVAL;
329 						goto out;
330 					}
331 
332 					if (catdatum->value >= rngdatum->value) {
333 						rc = -EINVAL;
334 						goto out;
335 					}
336 
337 					for (i = catdatum->value; i < rngdatum->value; i++) {
338 						rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
339 						if (rc)
340 							goto out;
341 					}
342 				}
343 
344 				if (delim != ',')
345 					break;
346 			}
347 		}
348 		if (delim == '-') {
349 			/* Extract high sensitivity. */
350 			scontextp = p;
351 			while (*p && *p != ':')
352 				p++;
353 
354 			delim = *p;
355 			if (delim != 0)
356 				*p++ = 0;
357 		} else
358 			break;
359 	}
360 
361 	if (l == 0) {
362 		context->range.level[1].sens = context->range.level[0].sens;
363 		rc = ebitmap_cpy(&context->range.level[1].cat,
364 				 &context->range.level[0].cat);
365 		if (rc)
366 			goto out;
367 	}
368 	*scontext = ++p;
369 	rc = 0;
370 out:
371 	return rc;
372 }
373 
374 /*
375  * Set the MLS fields in the security context structure
376  * `context' based on the string representation in
377  * the string `str'.  This function will allocate temporary memory with the
378  * given constraints of gfp_mask.
379  */
380 int mls_from_string(char *str, struct context *context, gfp_t gfp_mask)
381 {
382 	char *tmpstr, *freestr;
383 	int rc;
384 
385 	if (!selinux_mls_enabled)
386 		return -EINVAL;
387 
388 	/* we need freestr because mls_context_to_sid will change
389 	   the value of tmpstr */
390 	tmpstr = freestr = kstrdup(str, gfp_mask);
391 	if (!tmpstr) {
392 		rc = -ENOMEM;
393 	} else {
394 		rc = mls_context_to_sid(':', &tmpstr, context,
395 		                        NULL, SECSID_NULL);
396 		kfree(freestr);
397 	}
398 
399 	return rc;
400 }
401 
402 /*
403  * Copies the effective MLS range from `src' into `dst'.
404  */
405 static inline int mls_scopy_context(struct context *dst,
406                                     struct context *src)
407 {
408 	int l, rc = 0;
409 
410 	/* Copy the MLS range from the source context */
411 	for (l = 0; l < 2; l++) {
412 		dst->range.level[l].sens = src->range.level[0].sens;
413 		rc = ebitmap_cpy(&dst->range.level[l].cat,
414 				 &src->range.level[0].cat);
415 		if (rc)
416 			break;
417 	}
418 
419 	return rc;
420 }
421 
422 /*
423  * Copies the MLS range `range' into `context'.
424  */
425 static inline int mls_range_set(struct context *context,
426                                 struct mls_range *range)
427 {
428 	int l, rc = 0;
429 
430 	/* Copy the MLS range into the  context */
431 	for (l = 0; l < 2; l++) {
432 		context->range.level[l].sens = range->level[l].sens;
433 		rc = ebitmap_cpy(&context->range.level[l].cat,
434 				 &range->level[l].cat);
435 		if (rc)
436 			break;
437 	}
438 
439 	return rc;
440 }
441 
442 int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
443                          struct context *usercon)
444 {
445 	if (selinux_mls_enabled) {
446 		struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
447 		struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
448 		struct mls_level *user_low = &(user->range.level[0]);
449 		struct mls_level *user_clr = &(user->range.level[1]);
450 		struct mls_level *user_def = &(user->dfltlevel);
451 		struct mls_level *usercon_sen = &(usercon->range.level[0]);
452 		struct mls_level *usercon_clr = &(usercon->range.level[1]);
453 
454 		/* Honor the user's default level if we can */
455 		if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) {
456 			*usercon_sen = *user_def;
457 		} else if (mls_level_between(fromcon_sen, user_def, user_clr)) {
458 			*usercon_sen = *fromcon_sen;
459 		} else if (mls_level_between(fromcon_clr, user_low, user_def)) {
460 			*usercon_sen = *user_low;
461 		} else
462 			return -EINVAL;
463 
464 		/* Lower the clearance of available contexts
465 		   if the clearance of "fromcon" is lower than
466 		   that of the user's default clearance (but
467 		   only if the "fromcon" clearance dominates
468 		   the user's computed sensitivity level) */
469 		if (mls_level_dom(user_clr, fromcon_clr)) {
470 			*usercon_clr = *fromcon_clr;
471 		} else if (mls_level_dom(fromcon_clr, user_clr)) {
472 			*usercon_clr = *user_clr;
473 		} else
474 			return -EINVAL;
475 	}
476 
477 	return 0;
478 }
479 
480 /*
481  * Convert the MLS fields in the security context
482  * structure `c' from the values specified in the
483  * policy `oldp' to the values specified in the policy `newp'.
484  */
485 int mls_convert_context(struct policydb *oldp,
486 			struct policydb *newp,
487 			struct context *c)
488 {
489 	struct level_datum *levdatum;
490 	struct cat_datum *catdatum;
491 	struct ebitmap bitmap;
492 	struct ebitmap_node *node;
493 	int l, i;
494 
495 	if (!selinux_mls_enabled)
496 		return 0;
497 
498 	for (l = 0; l < 2; l++) {
499 		levdatum = hashtab_search(newp->p_levels.table,
500 			oldp->p_sens_val_to_name[c->range.level[l].sens - 1]);
501 
502 		if (!levdatum)
503 			return -EINVAL;
504 		c->range.level[l].sens = levdatum->level->sens;
505 
506 		ebitmap_init(&bitmap);
507 		ebitmap_for_each_bit(&c->range.level[l].cat, node, i) {
508 			if (ebitmap_node_get_bit(node, i)) {
509 				int rc;
510 
511 				catdatum = hashtab_search(newp->p_cats.table,
512 				         	oldp->p_cat_val_to_name[i]);
513 				if (!catdatum)
514 					return -EINVAL;
515 				rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
516 				if (rc)
517 					return rc;
518 			}
519 		}
520 		ebitmap_destroy(&c->range.level[l].cat);
521 		c->range.level[l].cat = bitmap;
522 	}
523 
524 	return 0;
525 }
526 
527 int mls_compute_sid(struct context *scontext,
528 		    struct context *tcontext,
529 		    u16 tclass,
530 		    u32 specified,
531 		    struct context *newcontext)
532 {
533 	struct range_trans *rtr;
534 
535 	if (!selinux_mls_enabled)
536 		return 0;
537 
538 	switch (specified) {
539 	case AVTAB_TRANSITION:
540 		/* Look for a range transition rule. */
541 		for (rtr = policydb.range_tr; rtr; rtr = rtr->next) {
542 			if (rtr->source_type == scontext->type &&
543 			    rtr->target_type == tcontext->type &&
544 			    rtr->target_class == tclass) {
545 				/* Set the range from the rule */
546 				return mls_range_set(newcontext,
547 				                     &rtr->target_range);
548 			}
549 		}
550 		/* Fallthrough */
551 	case AVTAB_CHANGE:
552 		if (tclass == SECCLASS_PROCESS)
553 			/* Use the process MLS attributes. */
554 			return mls_copy_context(newcontext, scontext);
555 		else
556 			/* Use the process effective MLS attributes. */
557 			return mls_scopy_context(newcontext, scontext);
558 	case AVTAB_MEMBER:
559 		/* Only polyinstantiate the MLS attributes if
560 		   the type is being polyinstantiated */
561 		if (newcontext->type != tcontext->type) {
562 			/* Use the process effective MLS attributes. */
563 			return mls_scopy_context(newcontext, scontext);
564 		} else {
565 			/* Use the related object MLS attributes. */
566 			return mls_copy_context(newcontext, tcontext);
567 		}
568 	default:
569 		return -EINVAL;
570 	}
571 	return -EINVAL;
572 }
573 
574 /**
575  * mls_export_lvl - Export the MLS sensitivity levels
576  * @context: the security context
577  * @low: the low sensitivity level
578  * @high: the high sensitivity level
579  *
580  * Description:
581  * Given the security context copy the low MLS sensitivity level into lvl_low
582  * and the high sensitivity level in lvl_high.  The MLS levels are only
583  * exported if the pointers are not NULL, if they are NULL then that level is
584  * not exported.
585  *
586  */
587 void mls_export_lvl(const struct context *context, u32 *low, u32 *high)
588 {
589 	if (!selinux_mls_enabled)
590 		return;
591 
592 	if (low != NULL)
593 		*low = context->range.level[0].sens - 1;
594 	if (high != NULL)
595 		*high = context->range.level[1].sens - 1;
596 }
597 
598 /**
599  * mls_import_lvl - Import the MLS sensitivity levels
600  * @context: the security context
601  * @low: the low sensitivity level
602  * @high: the high sensitivity level
603  *
604  * Description:
605  * Given the security context and the two sensitivty levels, set the MLS levels
606  * in the context according the two given as parameters.  Returns zero on
607  * success, negative values on failure.
608  *
609  */
610 void mls_import_lvl(struct context *context, u32 low, u32 high)
611 {
612 	if (!selinux_mls_enabled)
613 		return;
614 
615 	context->range.level[0].sens = low + 1;
616 	context->range.level[1].sens = high + 1;
617 }
618 
619 /**
620  * mls_export_cat - Export the MLS categories
621  * @context: the security context
622  * @low: the low category
623  * @low_len: length of the cat_low bitmap in bytes
624  * @high: the high category
625  * @high_len: length of the cat_high bitmap in bytes
626  *
627  * Description:
628  * Given the security context export the low MLS category bitmap into cat_low
629  * and the high category bitmap into cat_high.  The MLS categories are only
630  * exported if the pointers are not NULL, if they are NULL then that level is
631  * not exported.  The caller is responsibile for freeing the memory when
632  * finished.  Returns zero on success, negative values on failure.
633  *
634  */
635 int mls_export_cat(const struct context *context,
636 		   unsigned char **low,
637 		   size_t *low_len,
638 		   unsigned char **high,
639 		   size_t *high_len)
640 {
641 	int rc = -EPERM;
642 
643 	if (!selinux_mls_enabled) {
644 		*low = NULL;
645 		*low_len = 0;
646 		*high = NULL;
647 		*high_len = 0;
648 		return 0;
649 	}
650 
651 	if (low != NULL) {
652 		rc = ebitmap_export(&context->range.level[0].cat,
653 				    low,
654 				    low_len);
655 		if (rc != 0)
656 			goto export_cat_failure;
657 	}
658 	if (high != NULL) {
659 		rc = ebitmap_export(&context->range.level[1].cat,
660 				    high,
661 				    high_len);
662 		if (rc != 0)
663 			goto export_cat_failure;
664 	}
665 
666 	return 0;
667 
668 export_cat_failure:
669 	if (low != NULL) {
670 		kfree(*low);
671 		*low = NULL;
672 		*low_len = 0;
673 	}
674 	if (high != NULL) {
675 		kfree(*high);
676 		*high = NULL;
677 		*high_len = 0;
678 	}
679 	return rc;
680 }
681 
682 /**
683  * mls_import_cat - Import the MLS categories
684  * @context: the security context
685  * @low: the low category
686  * @low_len: length of the cat_low bitmap in bytes
687  * @high: the high category
688  * @high_len: length of the cat_high bitmap in bytes
689  *
690  * Description:
691  * Given the security context and the two category bitmap strings import the
692  * categories into the security context.  The MLS categories are only imported
693  * if the pointers are not NULL, if they are NULL they are skipped.  Returns
694  * zero on success, negative values on failure.
695  *
696  */
697 int mls_import_cat(struct context *context,
698 		   const unsigned char *low,
699 		   size_t low_len,
700 		   const unsigned char *high,
701 		   size_t high_len)
702 {
703 	int rc = -EPERM;
704 
705 	if (!selinux_mls_enabled)
706 		return 0;
707 
708 	if (low != NULL) {
709 		rc = ebitmap_import(low,
710 				    low_len,
711 				    &context->range.level[0].cat);
712 		if (rc != 0)
713 			goto import_cat_failure;
714 	}
715 	if (high != NULL) {
716 		if (high == low)
717 			rc = ebitmap_cpy(&context->range.level[1].cat,
718 					 &context->range.level[0].cat);
719 		else
720 			rc = ebitmap_import(high,
721 					    high_len,
722 					    &context->range.level[1].cat);
723 		if (rc != 0)
724 			goto import_cat_failure;
725 	}
726 
727 	return 0;
728 
729 import_cat_failure:
730 	ebitmap_destroy(&context->range.level[0].cat);
731 	ebitmap_destroy(&context->range.level[1].cat);
732 	return rc;
733 }
734