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