xref: /openbmc/linux/security/selinux/ss/mls.c (revision 4bf3bd0f)
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 		if ((def_sid != SECSID_NULL && oldc) || (*scontext) == '\0')
249 			return 0;
250 		return -EINVAL;
251 	}
252 
253 	/*
254 	 * No MLS component to the security context, try and map to
255 	 * default if provided.
256 	 */
257 	if (!oldc) {
258 		struct context *defcon;
259 
260 		if (def_sid == SECSID_NULL)
261 			return -EINVAL;
262 
263 		defcon = sidtab_search(s, def_sid);
264 		if (!defcon)
265 			return -EINVAL;
266 
267 		return mls_context_cpy(context, defcon);
268 	}
269 
270 	/*
271 	 * If we're dealing with a range, figure out where the two parts
272 	 * of the range begin.
273 	 */
274 	rangep[0] = scontext;
275 	rangep[1] = strchr(scontext, '-');
276 	if (rangep[1]) {
277 		rangep[1][0] = '\0';
278 		rangep[1]++;
279 	}
280 
281 	/* For each part of the range: */
282 	for (l = 0; l < 2; l++) {
283 		/* Split sensitivity and category set. */
284 		sensitivity = rangep[l];
285 		if (sensitivity == NULL)
286 			break;
287 		next_cat = strchr(sensitivity, ':');
288 		if (next_cat)
289 			*(next_cat++) = '\0';
290 
291 		/* Parse sensitivity. */
292 		levdatum = hashtab_search(pol->p_levels.table, sensitivity);
293 		if (!levdatum)
294 			return -EINVAL;
295 		context->range.level[l].sens = levdatum->level->sens;
296 
297 		/* Extract category set. */
298 		while (next_cat != NULL) {
299 			cur_cat = next_cat;
300 			next_cat = strchr(next_cat, ',');
301 			if (next_cat != NULL)
302 				*(next_cat++) = '\0';
303 
304 			/* Separate into range if exists */
305 			rngptr = strchr(cur_cat, '.');
306 			if (rngptr != NULL) {
307 				/* Remove '.' */
308 				*rngptr++ = '\0';
309 			}
310 
311 			catdatum = hashtab_search(pol->p_cats.table, cur_cat);
312 			if (!catdatum)
313 				return -EINVAL;
314 
315 			rc = ebitmap_set_bit(&context->range.level[l].cat,
316 					     catdatum->value - 1, 1);
317 			if (rc)
318 				return rc;
319 
320 			/* If range, set all categories in range */
321 			if (rngptr == NULL)
322 				continue;
323 
324 			rngdatum = hashtab_search(pol->p_cats.table, rngptr);
325 			if (!rngdatum)
326 				return -EINVAL;
327 
328 			if (catdatum->value >= rngdatum->value)
329 				return -EINVAL;
330 
331 			for (i = catdatum->value; i < rngdatum->value; i++) {
332 				rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
333 				if (rc)
334 					return rc;
335 			}
336 		}
337 	}
338 
339 	/* If we didn't see a '-', the range start is also the range end. */
340 	if (rangep[1] == NULL) {
341 		context->range.level[1].sens = context->range.level[0].sens;
342 		rc = ebitmap_cpy(&context->range.level[1].cat,
343 				 &context->range.level[0].cat);
344 		if (rc)
345 			return rc;
346 	}
347 
348 	return 0;
349 }
350 
351 /*
352  * Set the MLS fields in the security context structure
353  * `context' based on the string representation in
354  * the string `str'.  This function will allocate temporary memory with the
355  * given constraints of gfp_mask.
356  */
357 int mls_from_string(struct policydb *p, char *str, struct context *context,
358 		    gfp_t gfp_mask)
359 {
360 	char *tmpstr;
361 	int rc;
362 
363 	if (!p->mls_enabled)
364 		return -EINVAL;
365 
366 	tmpstr = kstrdup(str, gfp_mask);
367 	if (!tmpstr) {
368 		rc = -ENOMEM;
369 	} else {
370 		rc = mls_context_to_sid(p, ':', tmpstr, context,
371 					NULL, SECSID_NULL);
372 		kfree(tmpstr);
373 	}
374 
375 	return rc;
376 }
377 
378 /*
379  * Copies the MLS range `range' into `context'.
380  */
381 int mls_range_set(struct context *context,
382 				struct mls_range *range)
383 {
384 	int l, rc = 0;
385 
386 	/* Copy the MLS range into the  context */
387 	for (l = 0; l < 2; l++) {
388 		context->range.level[l].sens = range->level[l].sens;
389 		rc = ebitmap_cpy(&context->range.level[l].cat,
390 				 &range->level[l].cat);
391 		if (rc)
392 			break;
393 	}
394 
395 	return rc;
396 }
397 
398 int mls_setup_user_range(struct policydb *p,
399 			 struct context *fromcon, struct user_datum *user,
400 			 struct context *usercon)
401 {
402 	if (p->mls_enabled) {
403 		struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
404 		struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
405 		struct mls_level *user_low = &(user->range.level[0]);
406 		struct mls_level *user_clr = &(user->range.level[1]);
407 		struct mls_level *user_def = &(user->dfltlevel);
408 		struct mls_level *usercon_sen = &(usercon->range.level[0]);
409 		struct mls_level *usercon_clr = &(usercon->range.level[1]);
410 
411 		/* Honor the user's default level if we can */
412 		if (mls_level_between(user_def, fromcon_sen, fromcon_clr))
413 			*usercon_sen = *user_def;
414 		else if (mls_level_between(fromcon_sen, user_def, user_clr))
415 			*usercon_sen = *fromcon_sen;
416 		else if (mls_level_between(fromcon_clr, user_low, user_def))
417 			*usercon_sen = *user_low;
418 		else
419 			return -EINVAL;
420 
421 		/* Lower the clearance of available contexts
422 		   if the clearance of "fromcon" is lower than
423 		   that of the user's default clearance (but
424 		   only if the "fromcon" clearance dominates
425 		   the user's computed sensitivity level) */
426 		if (mls_level_dom(user_clr, fromcon_clr))
427 			*usercon_clr = *fromcon_clr;
428 		else if (mls_level_dom(fromcon_clr, user_clr))
429 			*usercon_clr = *user_clr;
430 		else
431 			return -EINVAL;
432 	}
433 
434 	return 0;
435 }
436 
437 /*
438  * Convert the MLS fields in the security context
439  * structure `c' from the values specified in the
440  * policy `oldp' to the values specified in the policy `newp'.
441  */
442 int mls_convert_context(struct policydb *oldp,
443 			struct policydb *newp,
444 			struct context *c)
445 {
446 	struct level_datum *levdatum;
447 	struct cat_datum *catdatum;
448 	struct ebitmap bitmap;
449 	struct ebitmap_node *node;
450 	int l, i;
451 
452 	if (!oldp->mls_enabled || !newp->mls_enabled)
453 		return 0;
454 
455 	for (l = 0; l < 2; l++) {
456 		levdatum = hashtab_search(newp->p_levels.table,
457 					  sym_name(oldp, SYM_LEVELS,
458 						   c->range.level[l].sens - 1));
459 
460 		if (!levdatum)
461 			return -EINVAL;
462 		c->range.level[l].sens = levdatum->level->sens;
463 
464 		ebitmap_init(&bitmap);
465 		ebitmap_for_each_positive_bit(&c->range.level[l].cat, node, i) {
466 			int rc;
467 
468 			catdatum = hashtab_search(newp->p_cats.table,
469 						  sym_name(oldp, SYM_CATS, i));
470 			if (!catdatum)
471 				return -EINVAL;
472 			rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
473 			if (rc)
474 				return rc;
475 
476 			cond_resched();
477 		}
478 		ebitmap_destroy(&c->range.level[l].cat);
479 		c->range.level[l].cat = bitmap;
480 	}
481 
482 	return 0;
483 }
484 
485 int mls_compute_sid(struct policydb *p,
486 		    struct context *scontext,
487 		    struct context *tcontext,
488 		    u16 tclass,
489 		    u32 specified,
490 		    struct context *newcontext,
491 		    bool sock)
492 {
493 	struct range_trans rtr;
494 	struct mls_range *r;
495 	struct class_datum *cladatum;
496 	int default_range = 0;
497 
498 	if (!p->mls_enabled)
499 		return 0;
500 
501 	switch (specified) {
502 	case AVTAB_TRANSITION:
503 		/* Look for a range transition rule. */
504 		rtr.source_type = scontext->type;
505 		rtr.target_type = tcontext->type;
506 		rtr.target_class = tclass;
507 		r = hashtab_search(p->range_tr, &rtr);
508 		if (r)
509 			return mls_range_set(newcontext, r);
510 
511 		if (tclass && tclass <= p->p_classes.nprim) {
512 			cladatum = p->class_val_to_struct[tclass - 1];
513 			if (cladatum)
514 				default_range = cladatum->default_range;
515 		}
516 
517 		switch (default_range) {
518 		case DEFAULT_SOURCE_LOW:
519 			return mls_context_cpy_low(newcontext, scontext);
520 		case DEFAULT_SOURCE_HIGH:
521 			return mls_context_cpy_high(newcontext, scontext);
522 		case DEFAULT_SOURCE_LOW_HIGH:
523 			return mls_context_cpy(newcontext, scontext);
524 		case DEFAULT_TARGET_LOW:
525 			return mls_context_cpy_low(newcontext, tcontext);
526 		case DEFAULT_TARGET_HIGH:
527 			return mls_context_cpy_high(newcontext, tcontext);
528 		case DEFAULT_TARGET_LOW_HIGH:
529 			return mls_context_cpy(newcontext, tcontext);
530 		}
531 
532 		/* Fallthrough */
533 	case AVTAB_CHANGE:
534 		if ((tclass == p->process_class) || (sock == true))
535 			/* Use the process MLS attributes. */
536 			return mls_context_cpy(newcontext, scontext);
537 		else
538 			/* Use the process effective MLS attributes. */
539 			return mls_context_cpy_low(newcontext, scontext);
540 	case AVTAB_MEMBER:
541 		/* Use the process effective MLS attributes. */
542 		return mls_context_cpy_low(newcontext, scontext);
543 
544 	/* fall through */
545 	}
546 	return -EINVAL;
547 }
548 
549 #ifdef CONFIG_NETLABEL
550 /**
551  * mls_export_netlbl_lvl - Export the MLS sensitivity levels to NetLabel
552  * @context: the security context
553  * @secattr: the NetLabel security attributes
554  *
555  * Description:
556  * Given the security context copy the low MLS sensitivity level into the
557  * NetLabel MLS sensitivity level field.
558  *
559  */
560 void mls_export_netlbl_lvl(struct policydb *p,
561 			   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  * @context: the security context
574  * @secattr: the NetLabel security attributes
575  *
576  * Description:
577  * Given the security context and the NetLabel security attributes, copy the
578  * NetLabel MLS sensitivity level into the context.
579  *
580  */
581 void mls_import_netlbl_lvl(struct policydb *p,
582 			   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  * @context: the security context
595  * @secattr: the NetLabel security attributes
596  *
597  * Description:
598  * Given the security context copy the low MLS categories into the NetLabel
599  * MLS category field.  Returns zero on success, negative values on failure.
600  *
601  */
602 int mls_export_netlbl_cat(struct policydb *p,
603 			  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  * @context: the security context
622  * @secattr: the NetLabel security attributes
623  *
624  * Description:
625  * Copy the NetLabel security attributes into the SELinux context; since the
626  * NetLabel security attribute only contains a single MLS category use it for
627  * both the low and high categories of the context.  Returns zero on success,
628  * negative values on failure.
629  *
630  */
631 int mls_import_netlbl_cat(struct policydb *p,
632 			  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