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