xref: /openbmc/linux/security/selinux/ss/mls.c (revision cef69974)
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 }
160 
161 int mls_level_isvalid(struct policydb *p, struct mls_level *l)
162 {
163 	struct level_datum *levdatum;
164 
165 	if (!l->sens || l->sens > p->p_levels.nprim)
166 		return 0;
167 	levdatum = symtab_search(&p->p_levels,
168 				 sym_name(p, SYM_LEVELS, l->sens - 1));
169 	if (!levdatum)
170 		return 0;
171 
172 	/*
173 	 * Return 1 iff all the bits set in l->cat are also be set in
174 	 * levdatum->level->cat and no bit in l->cat is larger than
175 	 * p->p_cats.nprim.
176 	 */
177 	return ebitmap_contains(&levdatum->level->cat, &l->cat,
178 				p->p_cats.nprim);
179 }
180 
181 int mls_range_isvalid(struct policydb *p, struct mls_range *r)
182 {
183 	return (mls_level_isvalid(p, &r->level[0]) &&
184 		mls_level_isvalid(p, &r->level[1]) &&
185 		mls_level_dom(&r->level[1], &r->level[0]));
186 }
187 
188 /*
189  * Return 1 if the MLS fields in the security context
190  * structure `c' are valid.  Return 0 otherwise.
191  */
192 int mls_context_isvalid(struct policydb *p, struct context *c)
193 {
194 	struct user_datum *usrdatum;
195 
196 	if (!p->mls_enabled)
197 		return 1;
198 
199 	if (!mls_range_isvalid(p, &c->range))
200 		return 0;
201 
202 	if (c->role == OBJECT_R_VAL)
203 		return 1;
204 
205 	/*
206 	 * User must be authorized for the MLS range.
207 	 */
208 	if (!c->user || c->user > p->p_users.nprim)
209 		return 0;
210 	usrdatum = p->user_val_to_struct[c->user - 1];
211 	if (!mls_range_contains(usrdatum->range, c->range))
212 		return 0; /* user may not be associated with range */
213 
214 	return 1;
215 }
216 
217 /*
218  * Set the MLS fields in the security context structure
219  * `context' based on the string representation in
220  * the string `scontext'.
221  *
222  * This function modifies the string in place, inserting
223  * NULL characters to terminate the MLS fields.
224  *
225  * If a def_sid is provided and no MLS field is present,
226  * copy the MLS field of the associated default context.
227  * Used for upgraded to MLS systems where objects may lack
228  * MLS fields.
229  *
230  * Policy read-lock must be held for sidtab lookup.
231  *
232  */
233 int mls_context_to_sid(struct policydb *pol,
234 		       char oldc,
235 		       char *scontext,
236 		       struct context *context,
237 		       struct sidtab *s,
238 		       u32 def_sid)
239 {
240 	char *sensitivity, *cur_cat, *next_cat, *rngptr;
241 	struct level_datum *levdatum;
242 	struct cat_datum *catdatum, *rngdatum;
243 	int l, rc, i;
244 	char *rangep[2];
245 
246 	if (!pol->mls_enabled) {
247 		/*
248 		 * With no MLS, only return -EINVAL if there is a MLS field
249 		 * and it did not come from an xattr.
250 		 */
251 		if (oldc && def_sid == SECSID_NULL)
252 			return -EINVAL;
253 		return 0;
254 	}
255 
256 	/*
257 	 * No MLS component to the security context, try and map to
258 	 * default if provided.
259 	 */
260 	if (!oldc) {
261 		struct context *defcon;
262 
263 		if (def_sid == SECSID_NULL)
264 			return -EINVAL;
265 
266 		defcon = sidtab_search(s, def_sid);
267 		if (!defcon)
268 			return -EINVAL;
269 
270 		return mls_context_cpy(context, defcon);
271 	}
272 
273 	/*
274 	 * If we're dealing with a range, figure out where the two parts
275 	 * of the range begin.
276 	 */
277 	rangep[0] = scontext;
278 	rangep[1] = strchr(scontext, '-');
279 	if (rangep[1]) {
280 		rangep[1][0] = '\0';
281 		rangep[1]++;
282 	}
283 
284 	/* For each part of the range: */
285 	for (l = 0; l < 2; l++) {
286 		/* Split sensitivity and category set. */
287 		sensitivity = rangep[l];
288 		if (sensitivity == NULL)
289 			break;
290 		next_cat = strchr(sensitivity, ':');
291 		if (next_cat)
292 			*(next_cat++) = '\0';
293 
294 		/* Parse sensitivity. */
295 		levdatum = symtab_search(&pol->p_levels, sensitivity);
296 		if (!levdatum)
297 			return -EINVAL;
298 		context->range.level[l].sens = levdatum->level->sens;
299 
300 		/* Extract category set. */
301 		while (next_cat != NULL) {
302 			cur_cat = next_cat;
303 			next_cat = strchr(next_cat, ',');
304 			if (next_cat != NULL)
305 				*(next_cat++) = '\0';
306 
307 			/* Separate into range if exists */
308 			rngptr = strchr(cur_cat, '.');
309 			if (rngptr != NULL) {
310 				/* Remove '.' */
311 				*rngptr++ = '\0';
312 			}
313 
314 			catdatum = symtab_search(&pol->p_cats, cur_cat);
315 			if (!catdatum)
316 				return -EINVAL;
317 
318 			rc = ebitmap_set_bit(&context->range.level[l].cat,
319 					     catdatum->value - 1, 1);
320 			if (rc)
321 				return rc;
322 
323 			/* If range, set all categories in range */
324 			if (rngptr == NULL)
325 				continue;
326 
327 			rngdatum = symtab_search(&pol->p_cats, rngptr);
328 			if (!rngdatum)
329 				return -EINVAL;
330 
331 			if (catdatum->value >= rngdatum->value)
332 				return -EINVAL;
333 
334 			for (i = catdatum->value; i < rngdatum->value; i++) {
335 				rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
336 				if (rc)
337 					return rc;
338 			}
339 		}
340 	}
341 
342 	/* If we didn't see a '-', the range start is also the range end. */
343 	if (rangep[1] == NULL) {
344 		context->range.level[1].sens = context->range.level[0].sens;
345 		rc = ebitmap_cpy(&context->range.level[1].cat,
346 				 &context->range.level[0].cat);
347 		if (rc)
348 			return rc;
349 	}
350 
351 	return 0;
352 }
353 
354 /*
355  * Set the MLS fields in the security context structure
356  * `context' based on the string representation in
357  * the string `str'.  This function will allocate temporary memory with the
358  * given constraints of gfp_mask.
359  */
360 int mls_from_string(struct policydb *p, char *str, struct context *context,
361 		    gfp_t gfp_mask)
362 {
363 	char *tmpstr;
364 	int rc;
365 
366 	if (!p->mls_enabled)
367 		return -EINVAL;
368 
369 	tmpstr = kstrdup(str, gfp_mask);
370 	if (!tmpstr) {
371 		rc = -ENOMEM;
372 	} else {
373 		rc = mls_context_to_sid(p, ':', tmpstr, context,
374 					NULL, SECSID_NULL);
375 		kfree(tmpstr);
376 	}
377 
378 	return rc;
379 }
380 
381 /*
382  * Copies the MLS range `range' into `context'.
383  */
384 int mls_range_set(struct context *context,
385 				struct mls_range *range)
386 {
387 	int l, rc = 0;
388 
389 	/* Copy the MLS range into the  context */
390 	for (l = 0; l < 2; l++) {
391 		context->range.level[l].sens = range->level[l].sens;
392 		rc = ebitmap_cpy(&context->range.level[l].cat,
393 				 &range->level[l].cat);
394 		if (rc)
395 			break;
396 	}
397 
398 	return rc;
399 }
400 
401 int mls_setup_user_range(struct policydb *p,
402 			 struct context *fromcon, struct user_datum *user,
403 			 struct context *usercon)
404 {
405 	if (p->mls_enabled) {
406 		struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
407 		struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
408 		struct mls_level *user_low = &(user->range.level[0]);
409 		struct mls_level *user_clr = &(user->range.level[1]);
410 		struct mls_level *user_def = &(user->dfltlevel);
411 		struct mls_level *usercon_sen = &(usercon->range.level[0]);
412 		struct mls_level *usercon_clr = &(usercon->range.level[1]);
413 
414 		/* Honor the user's default level if we can */
415 		if (mls_level_between(user_def, fromcon_sen, fromcon_clr))
416 			*usercon_sen = *user_def;
417 		else if (mls_level_between(fromcon_sen, user_def, user_clr))
418 			*usercon_sen = *fromcon_sen;
419 		else if (mls_level_between(fromcon_clr, user_low, user_def))
420 			*usercon_sen = *user_low;
421 		else
422 			return -EINVAL;
423 
424 		/* Lower the clearance of available contexts
425 		   if the clearance of "fromcon" is lower than
426 		   that of the user's default clearance (but
427 		   only if the "fromcon" clearance dominates
428 		   the user's computed sensitivity level) */
429 		if (mls_level_dom(user_clr, fromcon_clr))
430 			*usercon_clr = *fromcon_clr;
431 		else if (mls_level_dom(fromcon_clr, user_clr))
432 			*usercon_clr = *user_clr;
433 		else
434 			return -EINVAL;
435 	}
436 
437 	return 0;
438 }
439 
440 /*
441  * Convert the MLS fields in the security context
442  * structure `oldc' from the values specified in the
443  * policy `oldp' to the values specified in the policy `newp',
444  * storing the resulting context in `newc'.
445  */
446 int mls_convert_context(struct policydb *oldp,
447 			struct policydb *newp,
448 			struct context *oldc,
449 			struct context *newc)
450 {
451 	struct level_datum *levdatum;
452 	struct cat_datum *catdatum;
453 	struct ebitmap_node *node;
454 	int l, i;
455 
456 	if (!oldp->mls_enabled || !newp->mls_enabled)
457 		return 0;
458 
459 	for (l = 0; l < 2; l++) {
460 		char *name = sym_name(oldp, SYM_LEVELS,
461 				      oldc->range.level[l].sens - 1);
462 
463 		levdatum = symtab_search(&newp->p_levels, name);
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 = symtab_search(&newp->p_cats,
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 = policydb_rangetr_search(p, &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)
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 	return -EINVAL;
550 }
551 
552 #ifdef CONFIG_NETLABEL
553 /**
554  * mls_export_netlbl_lvl - Export the MLS sensitivity levels to NetLabel
555  * @p: the policy
556  * @context: the security context
557  * @secattr: the NetLabel security attributes
558  *
559  * Description:
560  * Given the security context copy the low MLS sensitivity level into the
561  * NetLabel MLS sensitivity level field.
562  *
563  */
564 void mls_export_netlbl_lvl(struct policydb *p,
565 			   struct context *context,
566 			   struct netlbl_lsm_secattr *secattr)
567 {
568 	if (!p->mls_enabled)
569 		return;
570 
571 	secattr->attr.mls.lvl = context->range.level[0].sens - 1;
572 	secattr->flags |= NETLBL_SECATTR_MLS_LVL;
573 }
574 
575 /**
576  * mls_import_netlbl_lvl - Import the NetLabel MLS sensitivity levels
577  * @p: the policy
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  * @p: the policy
600  * @context: the security context
601  * @secattr: the NetLabel security attributes
602  *
603  * Description:
604  * Given the security context copy the low MLS categories into the NetLabel
605  * MLS category field.  Returns zero on success, negative values on failure.
606  *
607  */
608 int mls_export_netlbl_cat(struct policydb *p,
609 			  struct context *context,
610 			  struct netlbl_lsm_secattr *secattr)
611 {
612 	int rc;
613 
614 	if (!p->mls_enabled)
615 		return 0;
616 
617 	rc = ebitmap_netlbl_export(&context->range.level[0].cat,
618 				   &secattr->attr.mls.cat);
619 	if (rc == 0 && secattr->attr.mls.cat != NULL)
620 		secattr->flags |= NETLBL_SECATTR_MLS_CAT;
621 
622 	return rc;
623 }
624 
625 /**
626  * mls_import_netlbl_cat - Import the MLS categories from NetLabel
627  * @p: the policy
628  * @context: the security context
629  * @secattr: the NetLabel security attributes
630  *
631  * Description:
632  * Copy the NetLabel security attributes into the SELinux context; since the
633  * NetLabel security attribute only contains a single MLS category use it for
634  * both the low and high categories of the context.  Returns zero on success,
635  * negative values on failure.
636  *
637  */
638 int mls_import_netlbl_cat(struct policydb *p,
639 			  struct context *context,
640 			  struct netlbl_lsm_secattr *secattr)
641 {
642 	int rc;
643 
644 	if (!p->mls_enabled)
645 		return 0;
646 
647 	rc = ebitmap_netlbl_import(&context->range.level[0].cat,
648 				   secattr->attr.mls.cat);
649 	if (rc)
650 		goto import_netlbl_cat_failure;
651 	memcpy(&context->range.level[1].cat, &context->range.level[0].cat,
652 	       sizeof(context->range.level[0].cat));
653 
654 	return 0;
655 
656 import_netlbl_cat_failure:
657 	ebitmap_destroy(&context->range.level[0].cat);
658 	return rc;
659 }
660 #endif /* CONFIG_NETLABEL */
661