• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
mls_compute_context_len(struct context * context)36 int mls_compute_context_len(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 (!policydb.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(&policydb, 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(&policydb, SYM_CATS, prev);
60 					len += strlen(nm) + 1;
61 				}
62 				nm = sym_name(&policydb, SYM_CATS, i);
63 				len += strlen(nm) + 1;
64 				head = i;
65 			}
66 			prev = i;
67 		}
68 		if (prev != head) {
69 			nm = sym_name(&policydb, 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 context * context,char ** scontext)89 void mls_sid_to_context(struct context *context,
90 			char **scontext)
91 {
92 	char *scontextp, *nm;
93 	int i, l, head, prev;
94 	struct ebitmap *e;
95 	struct ebitmap_node *node;
96 
97 	if (!policydb.mls_enabled)
98 		return;
99 
100 	scontextp = *scontext;
101 
102 	*scontextp = ':';
103 	scontextp++;
104 
105 	for (l = 0; l < 2; l++) {
106 		strcpy(scontextp, sym_name(&policydb, SYM_LEVELS,
107 					   context->range.level[l].sens - 1));
108 		scontextp += strlen(scontextp);
109 
110 		/* categories */
111 		head = -2;
112 		prev = -2;
113 		e = &context->range.level[l].cat;
114 		ebitmap_for_each_positive_bit(e, node, i) {
115 			if (i - prev > 1) {
116 				/* one or more negative bits are skipped */
117 				if (prev != head) {
118 					if (prev - head > 1)
119 						*scontextp++ = '.';
120 					else
121 						*scontextp++ = ',';
122 					nm = sym_name(&policydb, SYM_CATS, prev);
123 					strcpy(scontextp, nm);
124 					scontextp += strlen(nm);
125 				}
126 				if (prev < 0)
127 					*scontextp++ = ':';
128 				else
129 					*scontextp++ = ',';
130 				nm = sym_name(&policydb, SYM_CATS, i);
131 				strcpy(scontextp, nm);
132 				scontextp += strlen(nm);
133 				head = i;
134 			}
135 			prev = i;
136 		}
137 
138 		if (prev != head) {
139 			if (prev - head > 1)
140 				*scontextp++ = '.';
141 			else
142 				*scontextp++ = ',';
143 			nm = sym_name(&policydb, SYM_CATS, prev);
144 			strcpy(scontextp, nm);
145 			scontextp += strlen(nm);
146 		}
147 
148 		if (l == 0) {
149 			if (mls_level_eq(&context->range.level[0],
150 					 &context->range.level[1]))
151 				break;
152 			else
153 				*scontextp++ = '-';
154 		}
155 	}
156 
157 	*scontext = scontextp;
158 	return;
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 = hashtab_search(p->p_levels.table,
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'.  Update `*scontext' to
221  * point to the end of the string representation of
222  * the MLS fields.
223  *
224  * This function modifies the string in place, inserting
225  * NULL characters to terminate the MLS fields.
226  *
227  * If a def_sid is provided and no MLS field is present,
228  * copy the MLS field of the associated default context.
229  * Used for upgraded to MLS systems where objects may lack
230  * MLS fields.
231  *
232  * Policy read-lock must be held for sidtab lookup.
233  *
234  */
mls_context_to_sid(struct policydb * pol,char oldc,char ** scontext,struct context * context,struct sidtab * s,u32 def_sid)235 int mls_context_to_sid(struct policydb *pol,
236 		       char oldc,
237 		       char **scontext,
238 		       struct context *context,
239 		       struct sidtab *s,
240 		       u32 def_sid)
241 {
242 
243 	char delim;
244 	char *scontextp, *p, *rngptr;
245 	struct level_datum *levdatum;
246 	struct cat_datum *catdatum, *rngdatum;
247 	int l, rc = -EINVAL;
248 
249 	if (!pol->mls_enabled) {
250 		if (def_sid != SECSID_NULL && oldc)
251 			*scontext += strlen(*scontext) + 1;
252 		return 0;
253 	}
254 
255 	/*
256 	 * No MLS component to the security context, try and map to
257 	 * default if provided.
258 	 */
259 	if (!oldc) {
260 		struct context *defcon;
261 
262 		if (def_sid == SECSID_NULL)
263 			goto out;
264 
265 		defcon = sidtab_search(s, def_sid);
266 		if (!defcon)
267 			goto out;
268 
269 		rc = mls_context_cpy(context, defcon);
270 		goto out;
271 	}
272 
273 	/* Extract low sensitivity. */
274 	scontextp = p = *scontext;
275 	while (*p && *p != ':' && *p != '-')
276 		p++;
277 
278 	delim = *p;
279 	if (delim != '\0')
280 		*p++ = '\0';
281 
282 	for (l = 0; l < 2; l++) {
283 		levdatum = hashtab_search(pol->p_levels.table, scontextp);
284 		if (!levdatum) {
285 			rc = -EINVAL;
286 			goto out;
287 		}
288 
289 		context->range.level[l].sens = levdatum->level->sens;
290 
291 		if (delim == ':') {
292 			/* Extract category set. */
293 			while (1) {
294 				scontextp = p;
295 				while (*p && *p != ',' && *p != '-')
296 					p++;
297 				delim = *p;
298 				if (delim != '\0')
299 					*p++ = '\0';
300 
301 				/* Separate into range if exists */
302 				rngptr = strchr(scontextp, '.');
303 				if (rngptr != NULL) {
304 					/* Remove '.' */
305 					*rngptr++ = '\0';
306 				}
307 
308 				catdatum = hashtab_search(pol->p_cats.table,
309 							  scontextp);
310 				if (!catdatum) {
311 					rc = -EINVAL;
312 					goto out;
313 				}
314 
315 				rc = ebitmap_set_bit(&context->range.level[l].cat,
316 						     catdatum->value - 1, 1);
317 				if (rc)
318 					goto out;
319 
320 				/* If range, set all categories in range */
321 				if (rngptr) {
322 					int i;
323 
324 					rngdatum = hashtab_search(pol->p_cats.table, rngptr);
325 					if (!rngdatum) {
326 						rc = -EINVAL;
327 						goto out;
328 					}
329 
330 					if (catdatum->value >= rngdatum->value) {
331 						rc = -EINVAL;
332 						goto out;
333 					}
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 							goto out;
339 					}
340 				}
341 
342 				if (delim != ',')
343 					break;
344 			}
345 		}
346 		if (delim == '-') {
347 			/* Extract high sensitivity. */
348 			scontextp = p;
349 			while (*p && *p != ':')
350 				p++;
351 
352 			delim = *p;
353 			if (delim != '\0')
354 				*p++ = '\0';
355 		} else
356 			break;
357 	}
358 
359 	if (l == 0) {
360 		context->range.level[1].sens = context->range.level[0].sens;
361 		rc = ebitmap_cpy(&context->range.level[1].cat,
362 				 &context->range.level[0].cat);
363 		if (rc)
364 			goto out;
365 	}
366 	*scontext = ++p;
367 	rc = 0;
368 out:
369 	return rc;
370 }
371 
372 /*
373  * Set the MLS fields in the security context structure
374  * `context' based on the string representation in
375  * the string `str'.  This function will allocate temporary memory with the
376  * given constraints of gfp_mask.
377  */
mls_from_string(char * str,struct context * context,gfp_t gfp_mask)378 int mls_from_string(char *str, struct context *context, gfp_t gfp_mask)
379 {
380 	char *tmpstr, *freestr;
381 	int rc;
382 
383 	if (!policydb.mls_enabled)
384 		return -EINVAL;
385 
386 	/* we need freestr because mls_context_to_sid will change
387 	   the value of tmpstr */
388 	tmpstr = freestr = kstrdup(str, gfp_mask);
389 	if (!tmpstr) {
390 		rc = -ENOMEM;
391 	} else {
392 		rc = mls_context_to_sid(&policydb, ':', &tmpstr, context,
393 					NULL, SECSID_NULL);
394 		kfree(freestr);
395 	}
396 
397 	return rc;
398 }
399 
400 /*
401  * Copies the MLS range `range' into `context'.
402  */
mls_range_set(struct context * context,struct mls_range * range)403 int mls_range_set(struct context *context,
404 				struct mls_range *range)
405 {
406 	int l, rc = 0;
407 
408 	/* Copy the MLS range into the  context */
409 	for (l = 0; l < 2; l++) {
410 		context->range.level[l].sens = range->level[l].sens;
411 		rc = ebitmap_cpy(&context->range.level[l].cat,
412 				 &range->level[l].cat);
413 		if (rc)
414 			break;
415 	}
416 
417 	return rc;
418 }
419 
mls_setup_user_range(struct context * fromcon,struct user_datum * user,struct context * usercon)420 int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
421 			 struct context *usercon)
422 {
423 	if (policydb.mls_enabled) {
424 		struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
425 		struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
426 		struct mls_level *user_low = &(user->range.level[0]);
427 		struct mls_level *user_clr = &(user->range.level[1]);
428 		struct mls_level *user_def = &(user->dfltlevel);
429 		struct mls_level *usercon_sen = &(usercon->range.level[0]);
430 		struct mls_level *usercon_clr = &(usercon->range.level[1]);
431 
432 		/* Honor the user's default level if we can */
433 		if (mls_level_between(user_def, fromcon_sen, fromcon_clr))
434 			*usercon_sen = *user_def;
435 		else if (mls_level_between(fromcon_sen, user_def, user_clr))
436 			*usercon_sen = *fromcon_sen;
437 		else if (mls_level_between(fromcon_clr, user_low, user_def))
438 			*usercon_sen = *user_low;
439 		else
440 			return -EINVAL;
441 
442 		/* Lower the clearance of available contexts
443 		   if the clearance of "fromcon" is lower than
444 		   that of the user's default clearance (but
445 		   only if the "fromcon" clearance dominates
446 		   the user's computed sensitivity level) */
447 		if (mls_level_dom(user_clr, fromcon_clr))
448 			*usercon_clr = *fromcon_clr;
449 		else if (mls_level_dom(fromcon_clr, user_clr))
450 			*usercon_clr = *user_clr;
451 		else
452 			return -EINVAL;
453 	}
454 
455 	return 0;
456 }
457 
458 /*
459  * Convert the MLS fields in the security context
460  * structure `c' from the values specified in the
461  * policy `oldp' to the values specified in the policy `newp'.
462  */
mls_convert_context(struct policydb * oldp,struct policydb * newp,struct context * c)463 int mls_convert_context(struct policydb *oldp,
464 			struct policydb *newp,
465 			struct context *c)
466 {
467 	struct level_datum *levdatum;
468 	struct cat_datum *catdatum;
469 	struct ebitmap bitmap;
470 	struct ebitmap_node *node;
471 	int l, i;
472 
473 	if (!policydb.mls_enabled)
474 		return 0;
475 
476 	for (l = 0; l < 2; l++) {
477 		levdatum = hashtab_search(newp->p_levels.table,
478 					  sym_name(oldp, SYM_LEVELS,
479 						   c->range.level[l].sens - 1));
480 
481 		if (!levdatum)
482 			return -EINVAL;
483 		c->range.level[l].sens = levdatum->level->sens;
484 
485 		ebitmap_init(&bitmap);
486 		ebitmap_for_each_positive_bit(&c->range.level[l].cat, node, i) {
487 			int rc;
488 
489 			catdatum = hashtab_search(newp->p_cats.table,
490 						  sym_name(oldp, SYM_CATS, i));
491 			if (!catdatum)
492 				return -EINVAL;
493 			rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
494 			if (rc)
495 				return rc;
496 
497 			cond_resched();
498 		}
499 		ebitmap_destroy(&c->range.level[l].cat);
500 		c->range.level[l].cat = bitmap;
501 	}
502 
503 	return 0;
504 }
505 
mls_compute_sid(struct context * scontext,struct context * tcontext,u16 tclass,u32 specified,struct context * newcontext,bool sock)506 int mls_compute_sid(struct context *scontext,
507 		    struct context *tcontext,
508 		    u16 tclass,
509 		    u32 specified,
510 		    struct context *newcontext,
511 		    bool sock)
512 {
513 	struct range_trans rtr;
514 	struct mls_range *r;
515 	struct class_datum *cladatum;
516 	int default_range = 0;
517 
518 	if (!policydb.mls_enabled)
519 		return 0;
520 
521 	switch (specified) {
522 	case AVTAB_TRANSITION:
523 		/* Look for a range transition rule. */
524 		rtr.source_type = scontext->type;
525 		rtr.target_type = tcontext->type;
526 		rtr.target_class = tclass;
527 		r = hashtab_search(policydb.range_tr, &rtr);
528 		if (r)
529 			return mls_range_set(newcontext, r);
530 
531 		if (tclass && tclass <= policydb.p_classes.nprim) {
532 			cladatum = policydb.class_val_to_struct[tclass - 1];
533 			if (cladatum)
534 				default_range = cladatum->default_range;
535 		}
536 
537 		switch (default_range) {
538 		case DEFAULT_SOURCE_LOW:
539 			return mls_context_cpy_low(newcontext, scontext);
540 		case DEFAULT_SOURCE_HIGH:
541 			return mls_context_cpy_high(newcontext, scontext);
542 		case DEFAULT_SOURCE_LOW_HIGH:
543 			return mls_context_cpy(newcontext, scontext);
544 		case DEFAULT_TARGET_LOW:
545 			return mls_context_cpy_low(newcontext, tcontext);
546 		case DEFAULT_TARGET_HIGH:
547 			return mls_context_cpy_high(newcontext, tcontext);
548 		case DEFAULT_TARGET_LOW_HIGH:
549 			return mls_context_cpy(newcontext, tcontext);
550 		}
551 
552 		/* Fallthrough */
553 	case AVTAB_CHANGE:
554 		if ((tclass == policydb.process_class) || (sock == true))
555 			/* Use the process MLS attributes. */
556 			return mls_context_cpy(newcontext, scontext);
557 		else
558 			/* Use the process effective MLS attributes. */
559 			return mls_context_cpy_low(newcontext, scontext);
560 	case AVTAB_MEMBER:
561 		/* Use the process effective MLS attributes. */
562 		return mls_context_cpy_low(newcontext, scontext);
563 
564 	/* fall through */
565 	}
566 	return -EINVAL;
567 }
568 
569 #ifdef CONFIG_NETLABEL
570 /**
571  * mls_export_netlbl_lvl - Export the MLS sensitivity levels to NetLabel
572  * @context: the security context
573  * @secattr: the NetLabel security attributes
574  *
575  * Description:
576  * Given the security context copy the low MLS sensitivity level into the
577  * NetLabel MLS sensitivity level field.
578  *
579  */
mls_export_netlbl_lvl(struct context * context,struct netlbl_lsm_secattr * secattr)580 void mls_export_netlbl_lvl(struct context *context,
581 			   struct netlbl_lsm_secattr *secattr)
582 {
583 	if (!policydb.mls_enabled)
584 		return;
585 
586 	secattr->attr.mls.lvl = context->range.level[0].sens - 1;
587 	secattr->flags |= NETLBL_SECATTR_MLS_LVL;
588 }
589 
590 /**
591  * mls_import_netlbl_lvl - Import the NetLabel MLS sensitivity levels
592  * @context: the security context
593  * @secattr: the NetLabel security attributes
594  *
595  * Description:
596  * Given the security context and the NetLabel security attributes, copy the
597  * NetLabel MLS sensitivity level into the context.
598  *
599  */
mls_import_netlbl_lvl(struct context * context,struct netlbl_lsm_secattr * secattr)600 void mls_import_netlbl_lvl(struct context *context,
601 			   struct netlbl_lsm_secattr *secattr)
602 {
603 	if (!policydb.mls_enabled)
604 		return;
605 
606 	context->range.level[0].sens = secattr->attr.mls.lvl + 1;
607 	context->range.level[1].sens = context->range.level[0].sens;
608 }
609 
610 /**
611  * mls_export_netlbl_cat - Export the MLS categories to NetLabel
612  * @context: the security context
613  * @secattr: the NetLabel security attributes
614  *
615  * Description:
616  * Given the security context copy the low MLS categories into the NetLabel
617  * MLS category field.  Returns zero on success, negative values on failure.
618  *
619  */
mls_export_netlbl_cat(struct context * context,struct netlbl_lsm_secattr * secattr)620 int mls_export_netlbl_cat(struct context *context,
621 			  struct netlbl_lsm_secattr *secattr)
622 {
623 	int rc;
624 
625 	if (!policydb.mls_enabled)
626 		return 0;
627 
628 	rc = ebitmap_netlbl_export(&context->range.level[0].cat,
629 				   &secattr->attr.mls.cat);
630 	if (rc == 0 && secattr->attr.mls.cat != NULL)
631 		secattr->flags |= NETLBL_SECATTR_MLS_CAT;
632 
633 	return rc;
634 }
635 
636 /**
637  * mls_import_netlbl_cat - Import the MLS categories from NetLabel
638  * @context: the security context
639  * @secattr: the NetLabel security attributes
640  *
641  * Description:
642  * Copy the NetLabel security attributes into the SELinux context; since the
643  * NetLabel security attribute only contains a single MLS category use it for
644  * both the low and high categories of the context.  Returns zero on success,
645  * negative values on failure.
646  *
647  */
mls_import_netlbl_cat(struct context * context,struct netlbl_lsm_secattr * secattr)648 int mls_import_netlbl_cat(struct context *context,
649 			  struct netlbl_lsm_secattr *secattr)
650 {
651 	int rc;
652 
653 	if (!policydb.mls_enabled)
654 		return 0;
655 
656 	rc = ebitmap_netlbl_import(&context->range.level[0].cat,
657 				   secattr->attr.mls.cat);
658 	if (rc)
659 		goto import_netlbl_cat_failure;
660 	memcpy(&context->range.level[1].cat, &context->range.level[0].cat,
661 	       sizeof(context->range.level[0].cat));
662 
663 	return 0;
664 
665 import_netlbl_cat_failure:
666 	ebitmap_destroy(&context->range.level[0].cat);
667 	return rc;
668 }
669 #endif /* CONFIG_NETLABEL */
670