xref: /openbmc/linux/security/selinux/ss/mls.c (revision 901181b7)
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 = symtab_search(&p->p_levels,
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 = symtab_search(&pol->p_levels, 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 = symtab_search(&pol->p_cats, 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 = symtab_search(&pol->p_cats, 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 		char *name = sym_name(oldp, SYM_LEVELS,
462 				      oldc->range.level[l].sens - 1);
463 
464 		levdatum = symtab_search(&newp->p_levels, name);
465 
466 		if (!levdatum)
467 			return -EINVAL;
468 		newc->range.level[l].sens = levdatum->level->sens;
469 
470 		ebitmap_for_each_positive_bit(&oldc->range.level[l].cat,
471 					      node, i) {
472 			int rc;
473 
474 			catdatum = symtab_search(&newp->p_cats,
475 						 sym_name(oldp, SYM_CATS, i));
476 			if (!catdatum)
477 				return -EINVAL;
478 			rc = ebitmap_set_bit(&newc->range.level[l].cat,
479 					     catdatum->value - 1, 1);
480 			if (rc)
481 				return rc;
482 		}
483 	}
484 
485 	return 0;
486 }
487 
488 int mls_compute_sid(struct policydb *p,
489 		    struct context *scontext,
490 		    struct context *tcontext,
491 		    u16 tclass,
492 		    u32 specified,
493 		    struct context *newcontext,
494 		    bool sock)
495 {
496 	struct range_trans rtr;
497 	struct mls_range *r;
498 	struct class_datum *cladatum;
499 	int default_range = 0;
500 
501 	if (!p->mls_enabled)
502 		return 0;
503 
504 	switch (specified) {
505 	case AVTAB_TRANSITION:
506 		/* Look for a range transition rule. */
507 		rtr.source_type = scontext->type;
508 		rtr.target_type = tcontext->type;
509 		rtr.target_class = tclass;
510 		r = policydb_rangetr_search(p, &rtr);
511 		if (r)
512 			return mls_range_set(newcontext, r);
513 
514 		if (tclass && tclass <= p->p_classes.nprim) {
515 			cladatum = p->class_val_to_struct[tclass - 1];
516 			if (cladatum)
517 				default_range = cladatum->default_range;
518 		}
519 
520 		switch (default_range) {
521 		case DEFAULT_SOURCE_LOW:
522 			return mls_context_cpy_low(newcontext, scontext);
523 		case DEFAULT_SOURCE_HIGH:
524 			return mls_context_cpy_high(newcontext, scontext);
525 		case DEFAULT_SOURCE_LOW_HIGH:
526 			return mls_context_cpy(newcontext, scontext);
527 		case DEFAULT_TARGET_LOW:
528 			return mls_context_cpy_low(newcontext, tcontext);
529 		case DEFAULT_TARGET_HIGH:
530 			return mls_context_cpy_high(newcontext, tcontext);
531 		case DEFAULT_TARGET_LOW_HIGH:
532 			return mls_context_cpy(newcontext, tcontext);
533 		case DEFAULT_GLBLUB:
534 			return mls_context_glblub(newcontext,
535 						  scontext, tcontext);
536 		}
537 
538 		fallthrough;
539 	case AVTAB_CHANGE:
540 		if ((tclass == p->process_class) || sock)
541 			/* Use the process MLS attributes. */
542 			return mls_context_cpy(newcontext, scontext);
543 		else
544 			/* Use the process effective MLS attributes. */
545 			return mls_context_cpy_low(newcontext, scontext);
546 	case AVTAB_MEMBER:
547 		/* Use the process effective MLS attributes. */
548 		return mls_context_cpy_low(newcontext, scontext);
549 	}
550 	return -EINVAL;
551 }
552 
553 #ifdef CONFIG_NETLABEL
554 /**
555  * mls_export_netlbl_lvl - Export the MLS sensitivity levels to NetLabel
556  * @p: the policy
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  * @p: the policy
579  * @context: the security context
580  * @secattr: the NetLabel security attributes
581  *
582  * Description:
583  * Given the security context and the NetLabel security attributes, copy the
584  * NetLabel MLS sensitivity level into the context.
585  *
586  */
587 void mls_import_netlbl_lvl(struct policydb *p,
588 			   struct context *context,
589 			   struct netlbl_lsm_secattr *secattr)
590 {
591 	if (!p->mls_enabled)
592 		return;
593 
594 	context->range.level[0].sens = secattr->attr.mls.lvl + 1;
595 	context->range.level[1].sens = context->range.level[0].sens;
596 }
597 
598 /**
599  * mls_export_netlbl_cat - Export the MLS categories to NetLabel
600  * @p: the policy
601  * @context: the security context
602  * @secattr: the NetLabel security attributes
603  *
604  * Description:
605  * Given the security context copy the low MLS categories into the NetLabel
606  * MLS category field.  Returns zero on success, negative values on failure.
607  *
608  */
609 int mls_export_netlbl_cat(struct policydb *p,
610 			  struct context *context,
611 			  struct netlbl_lsm_secattr *secattr)
612 {
613 	int rc;
614 
615 	if (!p->mls_enabled)
616 		return 0;
617 
618 	rc = ebitmap_netlbl_export(&context->range.level[0].cat,
619 				   &secattr->attr.mls.cat);
620 	if (rc == 0 && secattr->attr.mls.cat != NULL)
621 		secattr->flags |= NETLBL_SECATTR_MLS_CAT;
622 
623 	return rc;
624 }
625 
626 /**
627  * mls_import_netlbl_cat - Import the MLS categories from NetLabel
628  * @p: the policy
629  * @context: the security context
630  * @secattr: the NetLabel security attributes
631  *
632  * Description:
633  * Copy the NetLabel security attributes into the SELinux context; since the
634  * NetLabel security attribute only contains a single MLS category use it for
635  * both the low and high categories of the context.  Returns zero on success,
636  * negative values on failure.
637  *
638  */
639 int mls_import_netlbl_cat(struct policydb *p,
640 			  struct context *context,
641 			  struct netlbl_lsm_secattr *secattr)
642 {
643 	int rc;
644 
645 	if (!p->mls_enabled)
646 		return 0;
647 
648 	rc = ebitmap_netlbl_import(&context->range.level[0].cat,
649 				   secattr->attr.mls.cat);
650 	if (rc)
651 		goto import_netlbl_cat_failure;
652 	memcpy(&context->range.level[1].cat, &context->range.level[0].cat,
653 	       sizeof(context->range.level[0].cat));
654 
655 	return 0;
656 
657 import_netlbl_cat_failure:
658 	ebitmap_destroy(&context->range.level[0].cat);
659 	return rc;
660 }
661 #endif /* CONFIG_NETLABEL */
662