xref: /openbmc/linux/security/selinux/ss/mls.c (revision e1cd7b80)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Implementation of the multi-level security (MLS) policy.
4  *
5  * Author : Stephen Smalley, <sds@tycho.nsa.gov>
6  */
7 /*
8  * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
9  *
10  *	Support for enhanced MLS infrastructure.
11  *
12  * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
13  */
14 /*
15  * Updated: Hewlett-Packard <paul@paul-moore.com>
16  *
17  *      Added support to import/export the MLS label from NetLabel
18  *
19  * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
20  */
21 
22 #include <linux/kernel.h>
23 #include <linux/slab.h>
24 #include <linux/string.h>
25 #include <linux/errno.h>
26 #include <net/netlabel.h>
27 #include "sidtab.h"
28 #include "mls.h"
29 #include "policydb.h"
30 #include "services.h"
31 
32 /*
33  * Return the length in bytes for the MLS fields of the
34  * security context string representation of `context'.
35  */
36 int mls_compute_context_len(struct policydb *p, struct context *context)
37 {
38 	int i, l, len, head, prev;
39 	char *nm;
40 	struct ebitmap *e;
41 	struct ebitmap_node *node;
42 
43 	if (!p->mls_enabled)
44 		return 0;
45 
46 	len = 1; /* for the beginning ":" */
47 	for (l = 0; l < 2; l++) {
48 		int index_sens = context->range.level[l].sens;
49 		len += strlen(sym_name(p, SYM_LEVELS, index_sens - 1));
50 
51 		/* categories */
52 		head = -2;
53 		prev = -2;
54 		e = &context->range.level[l].cat;
55 		ebitmap_for_each_positive_bit(e, node, i) {
56 			if (i - prev > 1) {
57 				/* one or more negative bits are skipped */
58 				if (head != prev) {
59 					nm = sym_name(p, SYM_CATS, prev);
60 					len += strlen(nm) + 1;
61 				}
62 				nm = sym_name(p, SYM_CATS, i);
63 				len += strlen(nm) + 1;
64 				head = i;
65 			}
66 			prev = i;
67 		}
68 		if (prev != head) {
69 			nm = sym_name(p, SYM_CATS, prev);
70 			len += strlen(nm) + 1;
71 		}
72 		if (l == 0) {
73 			if (mls_level_eq(&context->range.level[0],
74 					 &context->range.level[1]))
75 				break;
76 			else
77 				len++;
78 		}
79 	}
80 
81 	return len;
82 }
83 
84 /*
85  * Write the security context string representation of
86  * the MLS fields of `context' into the string `*scontext'.
87  * Update `*scontext' to point to the end of the MLS fields.
88  */
89 void mls_sid_to_context(struct policydb *p,
90 			struct context *context,
91 			char **scontext)
92 {
93 	char *scontextp, *nm;
94 	int i, l, head, prev;
95 	struct ebitmap *e;
96 	struct ebitmap_node *node;
97 
98 	if (!p->mls_enabled)
99 		return;
100 
101 	scontextp = *scontext;
102 
103 	*scontextp = ':';
104 	scontextp++;
105 
106 	for (l = 0; l < 2; l++) {
107 		strcpy(scontextp, sym_name(p, SYM_LEVELS,
108 					   context->range.level[l].sens - 1));
109 		scontextp += strlen(scontextp);
110 
111 		/* categories */
112 		head = -2;
113 		prev = -2;
114 		e = &context->range.level[l].cat;
115 		ebitmap_for_each_positive_bit(e, node, i) {
116 			if (i - prev > 1) {
117 				/* one or more negative bits are skipped */
118 				if (prev != head) {
119 					if (prev - head > 1)
120 						*scontextp++ = '.';
121 					else
122 						*scontextp++ = ',';
123 					nm = sym_name(p, SYM_CATS, prev);
124 					strcpy(scontextp, nm);
125 					scontextp += strlen(nm);
126 				}
127 				if (prev < 0)
128 					*scontextp++ = ':';
129 				else
130 					*scontextp++ = ',';
131 				nm = sym_name(p, SYM_CATS, i);
132 				strcpy(scontextp, nm);
133 				scontextp += strlen(nm);
134 				head = i;
135 			}
136 			prev = i;
137 		}
138 
139 		if (prev != head) {
140 			if (prev - head > 1)
141 				*scontextp++ = '.';
142 			else
143 				*scontextp++ = ',';
144 			nm = sym_name(p, SYM_CATS, prev);
145 			strcpy(scontextp, nm);
146 			scontextp += strlen(nm);
147 		}
148 
149 		if (l == 0) {
150 			if (mls_level_eq(&context->range.level[0],
151 					 &context->range.level[1]))
152 				break;
153 			else
154 				*scontextp++ = '-';
155 		}
156 	}
157 
158 	*scontext = scontextp;
159 	return;
160 }
161 
162 int mls_level_isvalid(struct policydb *p, struct mls_level *l)
163 {
164 	struct level_datum *levdatum;
165 
166 	if (!l->sens || l->sens > p->p_levels.nprim)
167 		return 0;
168 	levdatum = hashtab_search(p->p_levels.table,
169 				  sym_name(p, SYM_LEVELS, l->sens - 1));
170 	if (!levdatum)
171 		return 0;
172 
173 	/*
174 	 * Return 1 iff all the bits set in l->cat are also be set in
175 	 * levdatum->level->cat and no bit in l->cat is larger than
176 	 * p->p_cats.nprim.
177 	 */
178 	return ebitmap_contains(&levdatum->level->cat, &l->cat,
179 				p->p_cats.nprim);
180 }
181 
182 int mls_range_isvalid(struct policydb *p, struct mls_range *r)
183 {
184 	return (mls_level_isvalid(p, &r->level[0]) &&
185 		mls_level_isvalid(p, &r->level[1]) &&
186 		mls_level_dom(&r->level[1], &r->level[0]));
187 }
188 
189 /*
190  * Return 1 if the MLS fields in the security context
191  * structure `c' are valid.  Return 0 otherwise.
192  */
193 int mls_context_isvalid(struct policydb *p, struct context *c)
194 {
195 	struct user_datum *usrdatum;
196 
197 	if (!p->mls_enabled)
198 		return 1;
199 
200 	if (!mls_range_isvalid(p, &c->range))
201 		return 0;
202 
203 	if (c->role == OBJECT_R_VAL)
204 		return 1;
205 
206 	/*
207 	 * User must be authorized for the MLS range.
208 	 */
209 	if (!c->user || c->user > p->p_users.nprim)
210 		return 0;
211 	usrdatum = p->user_val_to_struct[c->user - 1];
212 	if (!mls_range_contains(usrdatum->range, c->range))
213 		return 0; /* user may not be associated with range */
214 
215 	return 1;
216 }
217 
218 /*
219  * Set the MLS fields in the security context structure
220  * `context' based on the string representation in
221  * the string `scontext'.
222  *
223  * This function modifies the string in place, inserting
224  * NULL characters to terminate the MLS fields.
225  *
226  * If a def_sid is provided and no MLS field is present,
227  * copy the MLS field of the associated default context.
228  * Used for upgraded to MLS systems where objects may lack
229  * MLS fields.
230  *
231  * Policy read-lock must be held for sidtab lookup.
232  *
233  */
234 int mls_context_to_sid(struct policydb *pol,
235 		       char oldc,
236 		       char *scontext,
237 		       struct context *context,
238 		       struct sidtab *s,
239 		       u32 def_sid)
240 {
241 	char *sensitivity, *cur_cat, *next_cat, *rngptr;
242 	struct level_datum *levdatum;
243 	struct cat_datum *catdatum, *rngdatum;
244 	int l, rc, i;
245 	char *rangep[2];
246 
247 	if (!pol->mls_enabled) {
248 		/*
249 		 * With no MLS, only return -EINVAL if there is a MLS field
250 		 * and it did not come from an xattr.
251 		 */
252 		if (oldc && def_sid == SECSID_NULL)
253 			return -EINVAL;
254 		return 0;
255 	}
256 
257 	/*
258 	 * No MLS component to the security context, try and map to
259 	 * default if provided.
260 	 */
261 	if (!oldc) {
262 		struct context *defcon;
263 
264 		if (def_sid == SECSID_NULL)
265 			return -EINVAL;
266 
267 		defcon = sidtab_search(s, def_sid);
268 		if (!defcon)
269 			return -EINVAL;
270 
271 		return mls_context_cpy(context, defcon);
272 	}
273 
274 	/*
275 	 * If we're dealing with a range, figure out where the two parts
276 	 * of the range begin.
277 	 */
278 	rangep[0] = scontext;
279 	rangep[1] = strchr(scontext, '-');
280 	if (rangep[1]) {
281 		rangep[1][0] = '\0';
282 		rangep[1]++;
283 	}
284 
285 	/* For each part of the range: */
286 	for (l = 0; l < 2; l++) {
287 		/* Split sensitivity and category set. */
288 		sensitivity = rangep[l];
289 		if (sensitivity == NULL)
290 			break;
291 		next_cat = strchr(sensitivity, ':');
292 		if (next_cat)
293 			*(next_cat++) = '\0';
294 
295 		/* Parse sensitivity. */
296 		levdatum = hashtab_search(pol->p_levels.table, sensitivity);
297 		if (!levdatum)
298 			return -EINVAL;
299 		context->range.level[l].sens = levdatum->level->sens;
300 
301 		/* Extract category set. */
302 		while (next_cat != NULL) {
303 			cur_cat = next_cat;
304 			next_cat = strchr(next_cat, ',');
305 			if (next_cat != NULL)
306 				*(next_cat++) = '\0';
307 
308 			/* Separate into range if exists */
309 			rngptr = strchr(cur_cat, '.');
310 			if (rngptr != NULL) {
311 				/* Remove '.' */
312 				*rngptr++ = '\0';
313 			}
314 
315 			catdatum = hashtab_search(pol->p_cats.table, cur_cat);
316 			if (!catdatum)
317 				return -EINVAL;
318 
319 			rc = ebitmap_set_bit(&context->range.level[l].cat,
320 					     catdatum->value - 1, 1);
321 			if (rc)
322 				return rc;
323 
324 			/* If range, set all categories in range */
325 			if (rngptr == NULL)
326 				continue;
327 
328 			rngdatum = hashtab_search(pol->p_cats.table, rngptr);
329 			if (!rngdatum)
330 				return -EINVAL;
331 
332 			if (catdatum->value >= rngdatum->value)
333 				return -EINVAL;
334 
335 			for (i = catdatum->value; i < rngdatum->value; i++) {
336 				rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
337 				if (rc)
338 					return rc;
339 			}
340 		}
341 	}
342 
343 	/* If we didn't see a '-', the range start is also the range end. */
344 	if (rangep[1] == NULL) {
345 		context->range.level[1].sens = context->range.level[0].sens;
346 		rc = ebitmap_cpy(&context->range.level[1].cat,
347 				 &context->range.level[0].cat);
348 		if (rc)
349 			return rc;
350 	}
351 
352 	return 0;
353 }
354 
355 /*
356  * Set the MLS fields in the security context structure
357  * `context' based on the string representation in
358  * the string `str'.  This function will allocate temporary memory with the
359  * given constraints of gfp_mask.
360  */
361 int mls_from_string(struct policydb *p, char *str, struct context *context,
362 		    gfp_t gfp_mask)
363 {
364 	char *tmpstr;
365 	int rc;
366 
367 	if (!p->mls_enabled)
368 		return -EINVAL;
369 
370 	tmpstr = kstrdup(str, gfp_mask);
371 	if (!tmpstr) {
372 		rc = -ENOMEM;
373 	} else {
374 		rc = mls_context_to_sid(p, ':', tmpstr, context,
375 					NULL, SECSID_NULL);
376 		kfree(tmpstr);
377 	}
378 
379 	return rc;
380 }
381 
382 /*
383  * Copies the MLS range `range' into `context'.
384  */
385 int mls_range_set(struct context *context,
386 				struct mls_range *range)
387 {
388 	int l, rc = 0;
389 
390 	/* Copy the MLS range into the  context */
391 	for (l = 0; l < 2; l++) {
392 		context->range.level[l].sens = range->level[l].sens;
393 		rc = ebitmap_cpy(&context->range.level[l].cat,
394 				 &range->level[l].cat);
395 		if (rc)
396 			break;
397 	}
398 
399 	return rc;
400 }
401 
402 int mls_setup_user_range(struct policydb *p,
403 			 struct context *fromcon, struct user_datum *user,
404 			 struct context *usercon)
405 {
406 	if (p->mls_enabled) {
407 		struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
408 		struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
409 		struct mls_level *user_low = &(user->range.level[0]);
410 		struct mls_level *user_clr = &(user->range.level[1]);
411 		struct mls_level *user_def = &(user->dfltlevel);
412 		struct mls_level *usercon_sen = &(usercon->range.level[0]);
413 		struct mls_level *usercon_clr = &(usercon->range.level[1]);
414 
415 		/* Honor the user's default level if we can */
416 		if (mls_level_between(user_def, fromcon_sen, fromcon_clr))
417 			*usercon_sen = *user_def;
418 		else if (mls_level_between(fromcon_sen, user_def, user_clr))
419 			*usercon_sen = *fromcon_sen;
420 		else if (mls_level_between(fromcon_clr, user_low, user_def))
421 			*usercon_sen = *user_low;
422 		else
423 			return -EINVAL;
424 
425 		/* Lower the clearance of available contexts
426 		   if the clearance of "fromcon" is lower than
427 		   that of the user's default clearance (but
428 		   only if the "fromcon" clearance dominates
429 		   the user's computed sensitivity level) */
430 		if (mls_level_dom(user_clr, fromcon_clr))
431 			*usercon_clr = *fromcon_clr;
432 		else if (mls_level_dom(fromcon_clr, user_clr))
433 			*usercon_clr = *user_clr;
434 		else
435 			return -EINVAL;
436 	}
437 
438 	return 0;
439 }
440 
441 /*
442  * Convert the MLS fields in the security context
443  * structure `oldc' from the values specified in the
444  * policy `oldp' to the values specified in the policy `newp',
445  * storing the resulting context in `newc'.
446  */
447 int mls_convert_context(struct policydb *oldp,
448 			struct policydb *newp,
449 			struct context *oldc,
450 			struct context *newc)
451 {
452 	struct level_datum *levdatum;
453 	struct cat_datum *catdatum;
454 	struct ebitmap_node *node;
455 	int l, i;
456 
457 	if (!oldp->mls_enabled || !newp->mls_enabled)
458 		return 0;
459 
460 	for (l = 0; l < 2; l++) {
461 		levdatum = hashtab_search(newp->p_levels.table,
462 					  sym_name(oldp, SYM_LEVELS,
463 						   oldc->range.level[l].sens - 1));
464 
465 		if (!levdatum)
466 			return -EINVAL;
467 		newc->range.level[l].sens = levdatum->level->sens;
468 
469 		ebitmap_for_each_positive_bit(&oldc->range.level[l].cat,
470 					      node, i) {
471 			int rc;
472 
473 			catdatum = hashtab_search(newp->p_cats.table,
474 						  sym_name(oldp, SYM_CATS, i));
475 			if (!catdatum)
476 				return -EINVAL;
477 			rc = ebitmap_set_bit(&newc->range.level[l].cat,
478 					     catdatum->value - 1, 1);
479 			if (rc)
480 				return rc;
481 		}
482 	}
483 
484 	return 0;
485 }
486 
487 int mls_compute_sid(struct policydb *p,
488 		    struct context *scontext,
489 		    struct context *tcontext,
490 		    u16 tclass,
491 		    u32 specified,
492 		    struct context *newcontext,
493 		    bool sock)
494 {
495 	struct range_trans rtr;
496 	struct mls_range *r;
497 	struct class_datum *cladatum;
498 	int default_range = 0;
499 
500 	if (!p->mls_enabled)
501 		return 0;
502 
503 	switch (specified) {
504 	case AVTAB_TRANSITION:
505 		/* Look for a range transition rule. */
506 		rtr.source_type = scontext->type;
507 		rtr.target_type = tcontext->type;
508 		rtr.target_class = tclass;
509 		r = hashtab_search(p->range_tr, &rtr);
510 		if (r)
511 			return mls_range_set(newcontext, r);
512 
513 		if (tclass && tclass <= p->p_classes.nprim) {
514 			cladatum = p->class_val_to_struct[tclass - 1];
515 			if (cladatum)
516 				default_range = cladatum->default_range;
517 		}
518 
519 		switch (default_range) {
520 		case DEFAULT_SOURCE_LOW:
521 			return mls_context_cpy_low(newcontext, scontext);
522 		case DEFAULT_SOURCE_HIGH:
523 			return mls_context_cpy_high(newcontext, scontext);
524 		case DEFAULT_SOURCE_LOW_HIGH:
525 			return mls_context_cpy(newcontext, scontext);
526 		case DEFAULT_TARGET_LOW:
527 			return mls_context_cpy_low(newcontext, tcontext);
528 		case DEFAULT_TARGET_HIGH:
529 			return mls_context_cpy_high(newcontext, tcontext);
530 		case DEFAULT_TARGET_LOW_HIGH:
531 			return mls_context_cpy(newcontext, tcontext);
532 		case DEFAULT_GLBLUB:
533 			return mls_context_glblub(newcontext,
534 						  scontext, tcontext);
535 		}
536 
537 		/* Fallthrough */
538 	case AVTAB_CHANGE:
539 		if ((tclass == p->process_class) || (sock == true))
540 			/* Use the process MLS attributes. */
541 			return mls_context_cpy(newcontext, scontext);
542 		else
543 			/* Use the process effective MLS attributes. */
544 			return mls_context_cpy_low(newcontext, scontext);
545 	case AVTAB_MEMBER:
546 		/* Use the process effective MLS attributes. */
547 		return mls_context_cpy_low(newcontext, scontext);
548 
549 	/* fall through */
550 	}
551 	return -EINVAL;
552 }
553 
554 #ifdef CONFIG_NETLABEL
555 /**
556  * mls_export_netlbl_lvl - Export the MLS sensitivity levels to NetLabel
557  * @context: the security context
558  * @secattr: the NetLabel security attributes
559  *
560  * Description:
561  * Given the security context copy the low MLS sensitivity level into the
562  * NetLabel MLS sensitivity level field.
563  *
564  */
565 void mls_export_netlbl_lvl(struct policydb *p,
566 			   struct context *context,
567 			   struct netlbl_lsm_secattr *secattr)
568 {
569 	if (!p->mls_enabled)
570 		return;
571 
572 	secattr->attr.mls.lvl = context->range.level[0].sens - 1;
573 	secattr->flags |= NETLBL_SECATTR_MLS_LVL;
574 }
575 
576 /**
577  * mls_import_netlbl_lvl - Import the NetLabel MLS sensitivity levels
578  * @context: the security context
579  * @secattr: the NetLabel security attributes
580  *
581  * Description:
582  * Given the security context and the NetLabel security attributes, copy the
583  * NetLabel MLS sensitivity level into the context.
584  *
585  */
586 void mls_import_netlbl_lvl(struct policydb *p,
587 			   struct context *context,
588 			   struct netlbl_lsm_secattr *secattr)
589 {
590 	if (!p->mls_enabled)
591 		return;
592 
593 	context->range.level[0].sens = secattr->attr.mls.lvl + 1;
594 	context->range.level[1].sens = context->range.level[0].sens;
595 }
596 
597 /**
598  * mls_export_netlbl_cat - Export the MLS categories to NetLabel
599  * @context: the security context
600  * @secattr: the NetLabel security attributes
601  *
602  * Description:
603  * Given the security context copy the low MLS categories into the NetLabel
604  * MLS category field.  Returns zero on success, negative values on failure.
605  *
606  */
607 int mls_export_netlbl_cat(struct policydb *p,
608 			  struct context *context,
609 			  struct netlbl_lsm_secattr *secattr)
610 {
611 	int rc;
612 
613 	if (!p->mls_enabled)
614 		return 0;
615 
616 	rc = ebitmap_netlbl_export(&context->range.level[0].cat,
617 				   &secattr->attr.mls.cat);
618 	if (rc == 0 && secattr->attr.mls.cat != NULL)
619 		secattr->flags |= NETLBL_SECATTR_MLS_CAT;
620 
621 	return rc;
622 }
623 
624 /**
625  * mls_import_netlbl_cat - Import the MLS categories from NetLabel
626  * @context: the security context
627  * @secattr: the NetLabel security attributes
628  *
629  * Description:
630  * Copy the NetLabel security attributes into the SELinux context; since the
631  * NetLabel security attribute only contains a single MLS category use it for
632  * both the low and high categories of the context.  Returns zero on success,
633  * negative values on failure.
634  *
635  */
636 int mls_import_netlbl_cat(struct policydb *p,
637 			  struct context *context,
638 			  struct netlbl_lsm_secattr *secattr)
639 {
640 	int rc;
641 
642 	if (!p->mls_enabled)
643 		return 0;
644 
645 	rc = ebitmap_netlbl_import(&context->range.level[0].cat,
646 				   secattr->attr.mls.cat);
647 	if (rc)
648 		goto import_netlbl_cat_failure;
649 	memcpy(&context->range.level[1].cat, &context->range.level[0].cat,
650 	       sizeof(context->range.level[0].cat));
651 
652 	return 0;
653 
654 import_netlbl_cat_failure:
655 	ebitmap_destroy(&context->range.level[0].cat);
656 	return rc;
657 }
658 #endif /* CONFIG_NETLABEL */
659