xref: /linux/security/selinux/ss/mls.c (revision 13abf8130139c2ccd4962a7e5a8902be5e6cb5a7)
1 /*
2  * Implementation of the multi-level security (MLS) policy.
3  *
4  * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
5  */
6 /*
7  * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
8  *
9  *	Support for enhanced MLS infrastructure.
10  *
11  * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
12  */
13 
14 #include <linux/kernel.h>
15 #include <linux/slab.h>
16 #include <linux/string.h>
17 #include <linux/errno.h>
18 #include "sidtab.h"
19 #include "mls.h"
20 #include "policydb.h"
21 #include "services.h"
22 
23 /*
24  * Return the length in bytes for the MLS fields of the
25  * security context string representation of `context'.
26  */
27 int mls_compute_context_len(struct context * context)
28 {
29 	int i, l, len, range;
30 	struct ebitmap_node *node;
31 
32 	if (!selinux_mls_enabled)
33 		return 0;
34 
35 	len = 1; /* for the beginning ":" */
36 	for (l = 0; l < 2; l++) {
37 		range = 0;
38 		len += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
39 
40 		ebitmap_for_each_bit(&context->range.level[l].cat, node, i) {
41 			if (ebitmap_node_get_bit(node, i)) {
42 				if (range) {
43 					range++;
44 					continue;
45 				}
46 
47 				len += strlen(policydb.p_cat_val_to_name[i]) + 1;
48 				range++;
49 			} else {
50 				if (range > 1)
51 					len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1;
52 				range = 0;
53 			}
54 		}
55 		/* Handle case where last category is the end of range */
56 		if (range > 1)
57 			len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1;
58 
59 		if (l == 0) {
60 			if (mls_level_eq(&context->range.level[0],
61 			                 &context->range.level[1]))
62 				break;
63 			else
64 				len++;
65 		}
66 	}
67 
68 	return len;
69 }
70 
71 /*
72  * Write the security context string representation of
73  * the MLS fields of `context' into the string `*scontext'.
74  * Update `*scontext' to point to the end of the MLS fields.
75  */
76 void mls_sid_to_context(struct context *context,
77                         char **scontext)
78 {
79 	char *scontextp;
80 	int i, l, range, wrote_sep;
81 	struct ebitmap_node *node;
82 
83 	if (!selinux_mls_enabled)
84 		return;
85 
86 	scontextp = *scontext;
87 
88 	*scontextp = ':';
89 	scontextp++;
90 
91 	for (l = 0; l < 2; l++) {
92 		range = 0;
93 		wrote_sep = 0;
94 		strcpy(scontextp,
95 		       policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
96 		scontextp += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
97 
98 		/* categories */
99 		ebitmap_for_each_bit(&context->range.level[l].cat, node, i) {
100 			if (ebitmap_node_get_bit(node, i)) {
101 				if (range) {
102 					range++;
103 					continue;
104 				}
105 
106 				if (!wrote_sep) {
107 					*scontextp++ = ':';
108 					wrote_sep = 1;
109 				} else
110 					*scontextp++ = ',';
111 				strcpy(scontextp, policydb.p_cat_val_to_name[i]);
112 				scontextp += strlen(policydb.p_cat_val_to_name[i]);
113 				range++;
114 			} else {
115 				if (range > 1) {
116 					if (range > 2)
117 						*scontextp++ = '.';
118 					else
119 						*scontextp++ = ',';
120 
121 					strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]);
122 					scontextp += strlen(policydb.p_cat_val_to_name[i - 1]);
123 				}
124 				range = 0;
125 			}
126 		}
127 
128 		/* Handle case where last category is the end of range */
129 		if (range > 1) {
130 			if (range > 2)
131 				*scontextp++ = '.';
132 			else
133 				*scontextp++ = ',';
134 
135 			strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]);
136 			scontextp += strlen(policydb.p_cat_val_to_name[i - 1]);
137 		}
138 
139 		if (l == 0) {
140 			if (mls_level_eq(&context->range.level[0],
141 			                 &context->range.level[1]))
142 				break;
143 			else {
144 				*scontextp = '-';
145 				scontextp++;
146 			}
147 		}
148 	}
149 
150 	*scontext = scontextp;
151 	return;
152 }
153 
154 /*
155  * Return 1 if the MLS fields in the security context
156  * structure `c' are valid.  Return 0 otherwise.
157  */
158 int mls_context_isvalid(struct policydb *p, struct context *c)
159 {
160 	struct level_datum *levdatum;
161 	struct user_datum *usrdatum;
162 	struct ebitmap_node *node;
163 	int i, l;
164 
165 	if (!selinux_mls_enabled)
166 		return 1;
167 
168 	/*
169 	 * MLS range validity checks: high must dominate low, low level must
170 	 * be valid (category set <-> sensitivity check), and high level must
171 	 * be valid (category set <-> sensitivity check)
172 	 */
173 	if (!mls_level_dom(&c->range.level[1], &c->range.level[0]))
174 		/* High does not dominate low. */
175 		return 0;
176 
177 	for (l = 0; l < 2; l++) {
178 		if (!c->range.level[l].sens || c->range.level[l].sens > p->p_levels.nprim)
179 			return 0;
180 		levdatum = hashtab_search(p->p_levels.table,
181 			p->p_sens_val_to_name[c->range.level[l].sens - 1]);
182 		if (!levdatum)
183 			return 0;
184 
185 		ebitmap_for_each_bit(&c->range.level[l].cat, node, i) {
186 			if (ebitmap_node_get_bit(node, i)) {
187 				if (i > p->p_cats.nprim)
188 					return 0;
189 				if (!ebitmap_get_bit(&levdatum->level->cat, i))
190 					/*
191 					 * Category may not be associated with
192 					 * sensitivity in low level.
193 					 */
194 					return 0;
195 			}
196 		}
197 	}
198 
199 	if (c->role == OBJECT_R_VAL)
200 		return 1;
201 
202 	/*
203 	 * User must be authorized for the MLS range.
204 	 */
205 	if (!c->user || c->user > p->p_users.nprim)
206 		return 0;
207 	usrdatum = p->user_val_to_struct[c->user - 1];
208 	if (!mls_range_contains(usrdatum->range, c->range))
209 		return 0; /* user may not be associated with range */
210 
211 	return 1;
212 }
213 
214 /*
215  * Copies the MLS range from `src' into `dst'.
216  */
217 static inline int mls_copy_context(struct context *dst,
218 				   struct context *src)
219 {
220 	int l, rc = 0;
221 
222 	/* Copy the MLS range from the source context */
223 	for (l = 0; l < 2; l++) {
224 		dst->range.level[l].sens = src->range.level[l].sens;
225 		rc = ebitmap_cpy(&dst->range.level[l].cat,
226 				 &src->range.level[l].cat);
227 		if (rc)
228 			break;
229 	}
230 
231 	return rc;
232 }
233 
234 /*
235  * Set the MLS fields in the security context structure
236  * `context' based on the string representation in
237  * the string `*scontext'.  Update `*scontext' to
238  * point to the end of the string representation of
239  * the MLS fields.
240  *
241  * This function modifies the string in place, inserting
242  * NULL characters to terminate the MLS fields.
243  *
244  * If a def_sid is provided and no MLS field is present,
245  * copy the MLS field of the associated default context.
246  * Used for upgraded to MLS systems where objects may lack
247  * MLS fields.
248  *
249  * Policy read-lock must be held for sidtab lookup.
250  *
251  */
252 int mls_context_to_sid(char oldc,
253 		       char **scontext,
254 		       struct context *context,
255 		       struct sidtab *s,
256 		       u32 def_sid)
257 {
258 
259 	char delim;
260 	char *scontextp, *p, *rngptr;
261 	struct level_datum *levdatum;
262 	struct cat_datum *catdatum, *rngdatum;
263 	int l, rc = -EINVAL;
264 
265 	if (!selinux_mls_enabled)
266 		return 0;
267 
268 	/*
269 	 * No MLS component to the security context, try and map to
270 	 * default if provided.
271 	 */
272 	if (!oldc) {
273 		struct context *defcon;
274 
275 		if (def_sid == SECSID_NULL)
276 			goto out;
277 
278 		defcon = sidtab_search(s, def_sid);
279 		if (!defcon)
280 			goto out;
281 
282 		rc = mls_copy_context(context, defcon);
283 		goto out;
284 	}
285 
286 	/* Extract low sensitivity. */
287 	scontextp = p = *scontext;
288 	while (*p && *p != ':' && *p != '-')
289 		p++;
290 
291 	delim = *p;
292 	if (delim != 0)
293 		*p++ = 0;
294 
295 	for (l = 0; l < 2; l++) {
296 		levdatum = hashtab_search(policydb.p_levels.table, scontextp);
297 		if (!levdatum) {
298 			rc = -EINVAL;
299 			goto out;
300 		}
301 
302 		context->range.level[l].sens = levdatum->level->sens;
303 
304 		if (delim == ':') {
305 			/* Extract category set. */
306 			while (1) {
307 				scontextp = p;
308 				while (*p && *p != ',' && *p != '-')
309 					p++;
310 				delim = *p;
311 				if (delim != 0)
312 					*p++ = 0;
313 
314 				/* Separate into range if exists */
315 				if ((rngptr = strchr(scontextp, '.')) != NULL) {
316 					/* Remove '.' */
317 					*rngptr++ = 0;
318 				}
319 
320 				catdatum = hashtab_search(policydb.p_cats.table,
321 				                          scontextp);
322 				if (!catdatum) {
323 					rc = -EINVAL;
324 					goto out;
325 				}
326 
327 				rc = ebitmap_set_bit(&context->range.level[l].cat,
328 				                     catdatum->value - 1, 1);
329 				if (rc)
330 					goto out;
331 
332 				/* If range, set all categories in range */
333 				if (rngptr) {
334 					int i;
335 
336 					rngdatum = hashtab_search(policydb.p_cats.table, rngptr);
337 					if (!rngdatum) {
338 						rc = -EINVAL;
339 						goto out;
340 					}
341 
342 					if (catdatum->value >= rngdatum->value) {
343 						rc = -EINVAL;
344 						goto out;
345 					}
346 
347 					for (i = catdatum->value; i < rngdatum->value; i++) {
348 						rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
349 						if (rc)
350 							goto out;
351 					}
352 				}
353 
354 				if (delim != ',')
355 					break;
356 			}
357 		}
358 		if (delim == '-') {
359 			/* Extract high sensitivity. */
360 			scontextp = p;
361 			while (*p && *p != ':')
362 				p++;
363 
364 			delim = *p;
365 			if (delim != 0)
366 				*p++ = 0;
367 		} else
368 			break;
369 	}
370 
371 	if (l == 0) {
372 		context->range.level[1].sens = context->range.level[0].sens;
373 		rc = ebitmap_cpy(&context->range.level[1].cat,
374 				 &context->range.level[0].cat);
375 		if (rc)
376 			goto out;
377 	}
378 	*scontext = ++p;
379 	rc = 0;
380 out:
381 	return rc;
382 }
383 
384 /*
385  * Copies the effective MLS range from `src' into `dst'.
386  */
387 static inline int mls_scopy_context(struct context *dst,
388                                     struct context *src)
389 {
390 	int l, rc = 0;
391 
392 	/* Copy the MLS range from the source context */
393 	for (l = 0; l < 2; l++) {
394 		dst->range.level[l].sens = src->range.level[0].sens;
395 		rc = ebitmap_cpy(&dst->range.level[l].cat,
396 				 &src->range.level[0].cat);
397 		if (rc)
398 			break;
399 	}
400 
401 	return rc;
402 }
403 
404 /*
405  * Copies the MLS range `range' into `context'.
406  */
407 static inline int mls_range_set(struct context *context,
408                                 struct mls_range *range)
409 {
410 	int l, rc = 0;
411 
412 	/* Copy the MLS range into the  context */
413 	for (l = 0; l < 2; l++) {
414 		context->range.level[l].sens = range->level[l].sens;
415 		rc = ebitmap_cpy(&context->range.level[l].cat,
416 				 &range->level[l].cat);
417 		if (rc)
418 			break;
419 	}
420 
421 	return rc;
422 }
423 
424 int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
425                          struct context *usercon)
426 {
427 	if (selinux_mls_enabled) {
428 		struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
429 		struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
430 		struct mls_level *user_low = &(user->range.level[0]);
431 		struct mls_level *user_clr = &(user->range.level[1]);
432 		struct mls_level *user_def = &(user->dfltlevel);
433 		struct mls_level *usercon_sen = &(usercon->range.level[0]);
434 		struct mls_level *usercon_clr = &(usercon->range.level[1]);
435 
436 		/* Honor the user's default level if we can */
437 		if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) {
438 			*usercon_sen = *user_def;
439 		} else if (mls_level_between(fromcon_sen, user_def, user_clr)) {
440 			*usercon_sen = *fromcon_sen;
441 		} else if (mls_level_between(fromcon_clr, user_low, user_def)) {
442 			*usercon_sen = *user_low;
443 		} else
444 			return -EINVAL;
445 
446 		/* Lower the clearance of available contexts
447 		   if the clearance of "fromcon" is lower than
448 		   that of the user's default clearance (but
449 		   only if the "fromcon" clearance dominates
450 		   the user's computed sensitivity level) */
451 		if (mls_level_dom(user_clr, fromcon_clr)) {
452 			*usercon_clr = *fromcon_clr;
453 		} else if (mls_level_dom(fromcon_clr, user_clr)) {
454 			*usercon_clr = *user_clr;
455 		} else
456 			return -EINVAL;
457 	}
458 
459 	return 0;
460 }
461 
462 /*
463  * Convert the MLS fields in the security context
464  * structure `c' from the values specified in the
465  * policy `oldp' to the values specified in the policy `newp'.
466  */
467 int mls_convert_context(struct policydb *oldp,
468 			struct policydb *newp,
469 			struct context *c)
470 {
471 	struct level_datum *levdatum;
472 	struct cat_datum *catdatum;
473 	struct ebitmap bitmap;
474 	struct ebitmap_node *node;
475 	int l, i;
476 
477 	if (!selinux_mls_enabled)
478 		return 0;
479 
480 	for (l = 0; l < 2; l++) {
481 		levdatum = hashtab_search(newp->p_levels.table,
482 			oldp->p_sens_val_to_name[c->range.level[l].sens - 1]);
483 
484 		if (!levdatum)
485 			return -EINVAL;
486 		c->range.level[l].sens = levdatum->level->sens;
487 
488 		ebitmap_init(&bitmap);
489 		ebitmap_for_each_bit(&c->range.level[l].cat, node, i) {
490 			if (ebitmap_node_get_bit(node, i)) {
491 				int rc;
492 
493 				catdatum = hashtab_search(newp->p_cats.table,
494 				         	oldp->p_cat_val_to_name[i]);
495 				if (!catdatum)
496 					return -EINVAL;
497 				rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
498 				if (rc)
499 					return rc;
500 			}
501 		}
502 		ebitmap_destroy(&c->range.level[l].cat);
503 		c->range.level[l].cat = bitmap;
504 	}
505 
506 	return 0;
507 }
508 
509 int mls_compute_sid(struct context *scontext,
510 		    struct context *tcontext,
511 		    u16 tclass,
512 		    u32 specified,
513 		    struct context *newcontext)
514 {
515 	if (!selinux_mls_enabled)
516 		return 0;
517 
518 	switch (specified) {
519 	case AVTAB_TRANSITION:
520 		if (tclass == SECCLASS_PROCESS) {
521 			struct range_trans *rangetr;
522 			/* Look for a range transition rule. */
523 			for (rangetr = policydb.range_tr; rangetr;
524 			     rangetr = rangetr->next) {
525 				if (rangetr->dom == scontext->type &&
526 				    rangetr->type == tcontext->type) {
527 					/* Set the range from the rule */
528 					return mls_range_set(newcontext,
529 					                     &rangetr->range);
530 				}
531 			}
532 		}
533 		/* Fallthrough */
534 	case AVTAB_CHANGE:
535 		if (tclass == SECCLASS_PROCESS)
536 			/* Use the process MLS attributes. */
537 			return mls_copy_context(newcontext, scontext);
538 		else
539 			/* Use the process effective MLS attributes. */
540 			return mls_scopy_context(newcontext, scontext);
541 	case AVTAB_MEMBER:
542 		/* Only polyinstantiate the MLS attributes if
543 		   the type is being polyinstantiated */
544 		if (newcontext->type != tcontext->type) {
545 			/* Use the process effective MLS attributes. */
546 			return mls_scopy_context(newcontext, scontext);
547 		} else {
548 			/* Use the related object MLS attributes. */
549 			return mls_copy_context(newcontext, tcontext);
550 		}
551 	default:
552 		return -EINVAL;
553 	}
554 	return -EINVAL;
555 }
556 
557