xref: /linux/security/selinux/ss/mls.c (revision 3b0744a6358624d46f7ee2573faca2b7d92cf32b)
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  */
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  */
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 
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 
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  */
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  */
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  */
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  */
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 
403 /*
404  * Convert the MLS fields in the security context
405  * structure `oldc' from the values specified in the
406  * policy `oldp' to the values specified in the policy `newp',
407  * storing the resulting context in `newc'.
408  */
409 int mls_convert_context(struct policydb *oldp, struct policydb *newp,
410 			struct context *oldc, struct context *newc)
411 {
412 	struct level_datum *levdatum;
413 	struct cat_datum *catdatum;
414 	struct ebitmap_node *node;
415 	u32 i;
416 	int l;
417 
418 	if (!oldp->mls_enabled || !newp->mls_enabled)
419 		return 0;
420 
421 	for (l = 0; l < 2; l++) {
422 		const char *name = sym_name(oldp, SYM_LEVELS,
423 					    oldc->range.level[l].sens - 1);
424 
425 		levdatum = symtab_search(&newp->p_levels, name);
426 
427 		if (!levdatum)
428 			return -EINVAL;
429 		newc->range.level[l].sens = levdatum->level.sens;
430 
431 		ebitmap_for_each_positive_bit(&oldc->range.level[l].cat, node,
432 					      i)
433 		{
434 			int rc;
435 
436 			catdatum = symtab_search(&newp->p_cats,
437 						 sym_name(oldp, SYM_CATS, i));
438 			if (!catdatum)
439 				return -EINVAL;
440 			rc = ebitmap_set_bit(&newc->range.level[l].cat,
441 					     catdatum->value - 1, 1);
442 			if (rc)
443 				return rc;
444 		}
445 	}
446 
447 	return 0;
448 }
449 
450 int mls_compute_sid(struct policydb *p, struct context *scontext,
451 		    struct context *tcontext, u16 tclass, u32 specified,
452 		    struct context *newcontext, bool sock)
453 {
454 	struct range_trans rtr;
455 	struct mls_range *r;
456 	struct class_datum *cladatum;
457 	char default_range = 0;
458 
459 	if (!p->mls_enabled)
460 		return 0;
461 
462 	switch (specified) {
463 	case AVTAB_TRANSITION:
464 		/* Look for a range transition rule. */
465 		rtr.source_type = scontext->type;
466 		rtr.target_type = tcontext->type;
467 		rtr.target_class = tclass;
468 		r = policydb_rangetr_search(p, &rtr);
469 		if (r)
470 			return mls_range_set(newcontext, r);
471 
472 		if (tclass && tclass <= p->p_classes.nprim) {
473 			cladatum = p->class_val_to_struct[tclass - 1];
474 			if (cladatum)
475 				default_range = cladatum->default_range;
476 		}
477 
478 		switch (default_range) {
479 		case DEFAULT_SOURCE_LOW:
480 			return mls_context_cpy_low(newcontext, scontext);
481 		case DEFAULT_SOURCE_HIGH:
482 			return mls_context_cpy_high(newcontext, scontext);
483 		case DEFAULT_SOURCE_LOW_HIGH:
484 			return mls_context_cpy(newcontext, scontext);
485 		case DEFAULT_TARGET_LOW:
486 			return mls_context_cpy_low(newcontext, tcontext);
487 		case DEFAULT_TARGET_HIGH:
488 			return mls_context_cpy_high(newcontext, tcontext);
489 		case DEFAULT_TARGET_LOW_HIGH:
490 			return mls_context_cpy(newcontext, tcontext);
491 		case DEFAULT_GLBLUB:
492 			return mls_context_glblub(newcontext, scontext,
493 						  tcontext);
494 		}
495 
496 		fallthrough;
497 	case AVTAB_CHANGE:
498 		if ((tclass == p->process_class) || sock)
499 			/* Use the process MLS attributes. */
500 			return mls_context_cpy(newcontext, scontext);
501 		else
502 			/* Use the process effective MLS attributes. */
503 			return mls_context_cpy_low(newcontext, scontext);
504 	case AVTAB_MEMBER:
505 		/* Use the process effective MLS attributes. */
506 		return mls_context_cpy_low(newcontext, scontext);
507 	}
508 	return -EINVAL;
509 }
510 
511 #ifdef CONFIG_NETLABEL
512 /**
513  * mls_export_netlbl_lvl - Export the MLS sensitivity levels to NetLabel
514  * @p: the policy
515  * @context: the security context
516  * @secattr: the NetLabel security attributes
517  *
518  * Description:
519  * Given the security context copy the low MLS sensitivity level into the
520  * NetLabel MLS sensitivity level field.
521  *
522  */
523 void mls_export_netlbl_lvl(struct policydb *p, struct context *context,
524 			   struct netlbl_lsm_secattr *secattr)
525 {
526 	if (!p->mls_enabled)
527 		return;
528 
529 	secattr->attr.mls.lvl = context->range.level[0].sens - 1;
530 	secattr->flags |= NETLBL_SECATTR_MLS_LVL;
531 }
532 
533 /**
534  * mls_import_netlbl_lvl - Import the NetLabel MLS sensitivity levels
535  * @p: the policy
536  * @context: the security context
537  * @secattr: the NetLabel security attributes
538  *
539  * Description:
540  * Given the security context and the NetLabel security attributes, copy the
541  * NetLabel MLS sensitivity level into the context.
542  *
543  */
544 void mls_import_netlbl_lvl(struct policydb *p, struct context *context,
545 			   struct netlbl_lsm_secattr *secattr)
546 {
547 	if (!p->mls_enabled)
548 		return;
549 
550 	context->range.level[0].sens = secattr->attr.mls.lvl + 1;
551 	context->range.level[1].sens = context->range.level[0].sens;
552 }
553 
554 /**
555  * mls_export_netlbl_cat - Export the MLS categories to NetLabel
556  * @p: the policy
557  * @context: the security context
558  * @secattr: the NetLabel security attributes
559  *
560  * Description:
561  * Given the security context copy the low MLS categories into the NetLabel
562  * MLS category field.  Returns zero on success, negative values on failure.
563  *
564  */
565 int mls_export_netlbl_cat(struct policydb *p, struct context *context,
566 			  struct netlbl_lsm_secattr *secattr)
567 {
568 	int rc;
569 
570 	if (!p->mls_enabled)
571 		return 0;
572 
573 	rc = ebitmap_netlbl_export(&context->range.level[0].cat,
574 				   &secattr->attr.mls.cat);
575 	if (rc == 0 && secattr->attr.mls.cat != NULL)
576 		secattr->flags |= NETLBL_SECATTR_MLS_CAT;
577 
578 	return rc;
579 }
580 
581 /**
582  * mls_import_netlbl_cat - Import the MLS categories from NetLabel
583  * @p: the policy
584  * @context: the security context
585  * @secattr: the NetLabel security attributes
586  *
587  * Description:
588  * Copy the NetLabel security attributes into the SELinux context; since the
589  * NetLabel security attribute only contains a single MLS category use it for
590  * both the low and high categories of the context.  Returns zero on success,
591  * negative values on failure.
592  *
593  */
594 int mls_import_netlbl_cat(struct policydb *p, struct context *context,
595 			  struct netlbl_lsm_secattr *secattr)
596 {
597 	int rc;
598 
599 	if (!p->mls_enabled)
600 		return 0;
601 
602 	rc = ebitmap_netlbl_import(&context->range.level[0].cat,
603 				   secattr->attr.mls.cat);
604 	if (rc)
605 		goto import_netlbl_cat_failure;
606 	memcpy(&context->range.level[1].cat, &context->range.level[0].cat,
607 	       sizeof(context->range.level[0].cat));
608 
609 	return 0;
610 
611 import_netlbl_cat_failure:
612 	ebitmap_destroy(&context->range.level[0].cat);
613 	return rc;
614 }
615 #endif /* CONFIG_NETLABEL */
616