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