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-2006 Trusted Computer Solutions, Inc.
12 */
13/*
14 * Updated: Hewlett-Packard <paul.moore@hp.com>
15 *
16 *      Added support to import/export the MLS label from NetLabel
17 *
18 * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
19 */
20
21#include <linux/kernel.h>
22#include <linux/slab.h>
23#include <linux/string.h>
24#include <linux/errno.h>
25#include <net/netlabel.h>
26#include "sidtab.h"
27#include "mls.h"
28#include "policydb.h"
29#include "services.h"
30
31/*
32 * Return the length in bytes for the MLS fields of the
33 * security context string representation of `context'.
34 */
35int mls_compute_context_len(struct context * context)
36{
37	int i, l, len, range;
38	struct ebitmap_node *node;
39
40	if (!selinux_mls_enabled)
41		return 0;
42
43	len = 1; /* for the beginning ":" */
44	for (l = 0; l < 2; l++) {
45		range = 0;
46		len += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
47
48		ebitmap_for_each_bit(&context->range.level[l].cat, node, i) {
49			if (ebitmap_node_get_bit(node, i)) {
50				if (range) {
51					range++;
52					continue;
53				}
54
55				len += strlen(policydb.p_cat_val_to_name[i]) + 1;
56				range++;
57			} else {
58				if (range > 1)
59					len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1;
60				range = 0;
61			}
62		}
63		/* Handle case where last category is the end of range */
64		if (range > 1)
65			len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1;
66
67		if (l == 0) {
68			if (mls_level_eq(&context->range.level[0],
69			                 &context->range.level[1]))
70				break;
71			else
72				len++;
73		}
74	}
75
76	return len;
77}
78
79/*
80 * Write the security context string representation of
81 * the MLS fields of `context' into the string `*scontext'.
82 * Update `*scontext' to point to the end of the MLS fields.
83 */
84void mls_sid_to_context(struct context *context,
85                        char **scontext)
86{
87	char *scontextp;
88	int i, l, range, wrote_sep;
89	struct ebitmap_node *node;
90
91	if (!selinux_mls_enabled)
92		return;
93
94	scontextp = *scontext;
95
96	*scontextp = ':';
97	scontextp++;
98
99	for (l = 0; l < 2; l++) {
100		range = 0;
101		wrote_sep = 0;
102		strcpy(scontextp,
103		       policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
104		scontextp += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
105
106		/* categories */
107		ebitmap_for_each_bit(&context->range.level[l].cat, node, i) {
108			if (ebitmap_node_get_bit(node, i)) {
109				if (range) {
110					range++;
111					continue;
112				}
113
114				if (!wrote_sep) {
115					*scontextp++ = ':';
116					wrote_sep = 1;
117				} else
118					*scontextp++ = ',';
119				strcpy(scontextp, policydb.p_cat_val_to_name[i]);
120				scontextp += strlen(policydb.p_cat_val_to_name[i]);
121				range++;
122			} else {
123				if (range > 1) {
124					if (range > 2)
125						*scontextp++ = '.';
126					else
127						*scontextp++ = ',';
128
129					strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]);
130					scontextp += strlen(policydb.p_cat_val_to_name[i - 1]);
131				}
132				range = 0;
133			}
134		}
135
136		/* Handle case where last category is the end of range */
137		if (range > 1) {
138			if (range > 2)
139				*scontextp++ = '.';
140			else
141				*scontextp++ = ',';
142
143			strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]);
144			scontextp += strlen(policydb.p_cat_val_to_name[i - 1]);
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				scontextp++;
154			}
155		}
156	}
157
158	*scontext = scontextp;
159	return;
160}
161
162/*
163 * Return 1 if the MLS fields in the security context
164 * structure `c' are valid.  Return 0 otherwise.
165 */
166int mls_context_isvalid(struct policydb *p, struct context *c)
167{
168	struct level_datum *levdatum;
169	struct user_datum *usrdatum;
170	struct ebitmap_node *node;
171	int i, l;
172
173	if (!selinux_mls_enabled)
174		return 1;
175
176	/*
177	 * MLS range validity checks: high must dominate low, low level must
178	 * be valid (category set <-> sensitivity check), and high level must
179	 * be valid (category set <-> sensitivity check)
180	 */
181	if (!mls_level_dom(&c->range.level[1], &c->range.level[0]))
182		/* High does not dominate low. */
183		return 0;
184
185	for (l = 0; l < 2; l++) {
186		if (!c->range.level[l].sens || c->range.level[l].sens > p->p_levels.nprim)
187			return 0;
188		levdatum = hashtab_search(p->p_levels.table,
189			p->p_sens_val_to_name[c->range.level[l].sens - 1]);
190		if (!levdatum)
191			return 0;
192
193		ebitmap_for_each_bit(&c->range.level[l].cat, node, i) {
194			if (ebitmap_node_get_bit(node, i)) {
195				if (i > p->p_cats.nprim)
196					return 0;
197				if (!ebitmap_get_bit(&levdatum->level->cat, i))
198					/*
199					 * Category may not be associated with
200					 * sensitivity in low level.
201					 */
202					return 0;
203			}
204		}
205	}
206
207	if (c->role == OBJECT_R_VAL)
208		return 1;
209
210	/*
211	 * User must be authorized for the MLS range.
212	 */
213	if (!c->user || c->user > p->p_users.nprim)
214		return 0;
215	usrdatum = p->user_val_to_struct[c->user - 1];
216	if (!mls_range_contains(usrdatum->range, c->range))
217		return 0; /* user may not be associated with range */
218
219	return 1;
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'.  Update `*scontext' to
226 * point to the end of the string representation of
227 * the MLS fields.
228 *
229 * This function modifies the string in place, inserting
230 * NULL characters to terminate the MLS fields.
231 *
232 * If a def_sid is provided and no MLS field is present,
233 * copy the MLS field of the associated default context.
234 * Used for upgraded to MLS systems where objects may lack
235 * MLS fields.
236 *
237 * Policy read-lock must be held for sidtab lookup.
238 *
239 */
240int mls_context_to_sid(char oldc,
241		       char **scontext,
242		       struct context *context,
243		       struct sidtab *s,
244		       u32 def_sid)
245{
246
247	char delim;
248	char *scontextp, *p, *rngptr;
249	struct level_datum *levdatum;
250	struct cat_datum *catdatum, *rngdatum;
251	int l, rc = -EINVAL;
252
253	if (!selinux_mls_enabled) {
254		if (def_sid != SECSID_NULL && oldc)
255			*scontext += strlen(*scontext)+1;
256		return 0;
257	}
258
259	/*
260	 * No MLS component to the security context, try and map to
261	 * default if provided.
262	 */
263	if (!oldc) {
264		struct context *defcon;
265
266		if (def_sid == SECSID_NULL)
267			goto out;
268
269		defcon = sidtab_search(s, def_sid);
270		if (!defcon)
271			goto out;
272
273		rc = mls_context_cpy(context, defcon);
274		goto out;
275	}
276
277	/* Extract low sensitivity. */
278	scontextp = p = *scontext;
279	while (*p && *p != ':' && *p != '-')
280		p++;
281
282	delim = *p;
283	if (delim != 0)
284		*p++ = 0;
285
286	for (l = 0; l < 2; l++) {
287		levdatum = hashtab_search(policydb.p_levels.table, scontextp);
288		if (!levdatum) {
289			rc = -EINVAL;
290			goto out;
291		}
292
293		context->range.level[l].sens = levdatum->level->sens;
294
295		if (delim == ':') {
296			/* Extract category set. */
297			while (1) {
298				scontextp = p;
299				while (*p && *p != ',' && *p != '-')
300					p++;
301				delim = *p;
302				if (delim != 0)
303					*p++ = 0;
304
305				/* Separate into range if exists */
306				if ((rngptr = strchr(scontextp, '.')) != NULL) {
307					/* Remove '.' */
308					*rngptr++ = 0;
309				}
310
311				catdatum = hashtab_search(policydb.p_cats.table,
312				                          scontextp);
313				if (!catdatum) {
314					rc = -EINVAL;
315					goto out;
316				}
317
318				rc = ebitmap_set_bit(&context->range.level[l].cat,
319				                     catdatum->value - 1, 1);
320				if (rc)
321					goto out;
322
323				/* If range, set all categories in range */
324				if (rngptr) {
325					int i;
326
327					rngdatum = hashtab_search(policydb.p_cats.table, rngptr);
328					if (!rngdatum) {
329						rc = -EINVAL;
330						goto out;
331					}
332
333					if (catdatum->value >= rngdatum->value) {
334						rc = -EINVAL;
335						goto out;
336					}
337
338					for (i = catdatum->value; i < rngdatum->value; i++) {
339						rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
340						if (rc)
341							goto out;
342					}
343				}
344
345				if (delim != ',')
346					break;
347			}
348		}
349		if (delim == '-') {
350			/* Extract high sensitivity. */
351			scontextp = p;
352			while (*p && *p != ':')
353				p++;
354
355			delim = *p;
356			if (delim != 0)
357				*p++ = 0;
358		} else
359			break;
360	}
361
362	if (l == 0) {
363		context->range.level[1].sens = context->range.level[0].sens;
364		rc = ebitmap_cpy(&context->range.level[1].cat,
365				 &context->range.level[0].cat);
366		if (rc)
367			goto out;
368	}
369	*scontext = ++p;
370	rc = 0;
371out:
372	return rc;
373}
374
375/*
376 * Set the MLS fields in the security context structure
377 * `context' based on the string representation in
378 * the string `str'.  This function will allocate temporary memory with the
379 * given constraints of gfp_mask.
380 */
381int mls_from_string(char *str, struct context *context, gfp_t gfp_mask)
382{
383	char *tmpstr, *freestr;
384	int rc;
385
386	if (!selinux_mls_enabled)
387		return -EINVAL;
388
389	/* we need freestr because mls_context_to_sid will change
390	   the value of tmpstr */
391	tmpstr = freestr = kstrdup(str, gfp_mask);
392	if (!tmpstr) {
393		rc = -ENOMEM;
394	} else {
395		rc = mls_context_to_sid(':', &tmpstr, context,
396		                        NULL, SECSID_NULL);
397		kfree(freestr);
398	}
399
400	return rc;
401}
402
403/*
404 * Copies the MLS range `range' into `context'.
405 */
406static inline int mls_range_set(struct context *context,
407                                struct mls_range *range)
408{
409	int l, rc = 0;
410
411	/* Copy the MLS range into the  context */
412	for (l = 0; l < 2; l++) {
413		context->range.level[l].sens = range->level[l].sens;
414		rc = ebitmap_cpy(&context->range.level[l].cat,
415				 &range->level[l].cat);
416		if (rc)
417			break;
418	}
419
420	return rc;
421}
422
423int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
424                         struct context *usercon)
425{
426	if (selinux_mls_enabled) {
427		struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
428		struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
429		struct mls_level *user_low = &(user->range.level[0]);
430		struct mls_level *user_clr = &(user->range.level[1]);
431		struct mls_level *user_def = &(user->dfltlevel);
432		struct mls_level *usercon_sen = &(usercon->range.level[0]);
433		struct mls_level *usercon_clr = &(usercon->range.level[1]);
434
435		/* Honor the user's default level if we can */
436		if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) {
437			*usercon_sen = *user_def;
438		} else if (mls_level_between(fromcon_sen, user_def, user_clr)) {
439			*usercon_sen = *fromcon_sen;
440		} else if (mls_level_between(fromcon_clr, user_low, user_def)) {
441			*usercon_sen = *user_low;
442		} else
443			return -EINVAL;
444
445		/* Lower the clearance of available contexts
446		   if the clearance of "fromcon" is lower than
447		   that of the user's default clearance (but
448		   only if the "fromcon" clearance dominates
449		   the user's computed sensitivity level) */
450		if (mls_level_dom(user_clr, fromcon_clr)) {
451			*usercon_clr = *fromcon_clr;
452		} else if (mls_level_dom(fromcon_clr, user_clr)) {
453			*usercon_clr = *user_clr;
454		} else
455			return -EINVAL;
456	}
457
458	return 0;
459}
460
461/*
462 * Convert the MLS fields in the security context
463 * structure `c' from the values specified in the
464 * policy `oldp' to the values specified in the policy `newp'.
465 */
466int mls_convert_context(struct policydb *oldp,
467			struct policydb *newp,
468			struct context *c)
469{
470	struct level_datum *levdatum;
471	struct cat_datum *catdatum;
472	struct ebitmap bitmap;
473	struct ebitmap_node *node;
474	int l, i;
475
476	if (!selinux_mls_enabled)
477		return 0;
478
479	for (l = 0; l < 2; l++) {
480		levdatum = hashtab_search(newp->p_levels.table,
481			oldp->p_sens_val_to_name[c->range.level[l].sens - 1]);
482
483		if (!levdatum)
484			return -EINVAL;
485		c->range.level[l].sens = levdatum->level->sens;
486
487		ebitmap_init(&bitmap);
488		ebitmap_for_each_bit(&c->range.level[l].cat, node, i) {
489			if (ebitmap_node_get_bit(node, i)) {
490				int rc;
491
492				catdatum = hashtab_search(newp->p_cats.table,
493				         	oldp->p_cat_val_to_name[i]);
494				if (!catdatum)
495					return -EINVAL;
496				rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
497				if (rc)
498					return rc;
499			}
500		}
501		ebitmap_destroy(&c->range.level[l].cat);
502		c->range.level[l].cat = bitmap;
503	}
504
505	return 0;
506}
507
508int mls_compute_sid(struct context *scontext,
509		    struct context *tcontext,
510		    u16 tclass,
511		    u32 specified,
512		    struct context *newcontext)
513{
514	struct range_trans *rtr;
515
516	if (!selinux_mls_enabled)
517		return 0;
518
519	switch (specified) {
520	case AVTAB_TRANSITION:
521		/* Look for a range transition rule. */
522		for (rtr = policydb.range_tr; rtr; rtr = rtr->next) {
523			if (rtr->source_type == scontext->type &&
524			    rtr->target_type == tcontext->type &&
525			    rtr->target_class == tclass) {
526				/* Set the range from the rule */
527				return mls_range_set(newcontext,
528				                     &rtr->target_range);
529			}
530		}
531		/* Fallthrough */
532	case AVTAB_CHANGE:
533		if (tclass == SECCLASS_PROCESS)
534			/* Use the process MLS attributes. */
535			return mls_context_cpy(newcontext, scontext);
536		else
537			/* Use the process effective MLS attributes. */
538			return mls_context_cpy_low(newcontext, scontext);
539	case AVTAB_MEMBER:
540		/* Only polyinstantiate the MLS attributes if
541		   the type is being polyinstantiated */
542		if (newcontext->type != tcontext->type) {
543			/* Use the process effective MLS attributes. */
544			return mls_context_cpy_low(newcontext, scontext);
545		} else {
546			/* Use the related object MLS attributes. */
547			return mls_context_cpy(newcontext, tcontext);
548		}
549	default:
550		return -EINVAL;
551	}
552	return -EINVAL;
553}
554
555#ifdef CONFIG_NETLABEL
556/**
557 * mls_export_netlbl_lvl - Export the MLS sensitivity levels to NetLabel
558 * @context: the security context
559 * @secattr: the NetLabel security attributes
560 *
561 * Description:
562 * Given the security context copy the low MLS sensitivity level into the
563 * NetLabel MLS sensitivity level field.
564 *
565 */
566void mls_export_netlbl_lvl(struct context *context,
567			   struct netlbl_lsm_secattr *secattr)
568{
569	if (!selinux_mls_enabled)
570		return;
571
572	secattr->mls_lvl = context->range.level[0].sens - 1;
573	secattr->flags |= NETLBL_SECATTR_MLS_LVL;
574}
575
576/**
577 * mls_import_netlbl_lvl - Import the NetLabel MLS sensitivity levels
578 * @context: the security context
579 * @secattr: the NetLabel security attributes
580 *
581 * Description:
582 * Given the security context and the NetLabel security attributes, copy the
583 * NetLabel MLS sensitivity level into the context.
584 *
585 */
586void mls_import_netlbl_lvl(struct context *context,
587			   struct netlbl_lsm_secattr *secattr)
588{
589	if (!selinux_mls_enabled)
590		return;
591
592	context->range.level[0].sens = secattr->mls_lvl + 1;
593	context->range.level[1].sens = context->range.level[0].sens;
594}
595
596/**
597 * mls_export_netlbl_cat - Export the MLS categories to NetLabel
598 * @context: the security context
599 * @secattr: the NetLabel security attributes
600 *
601 * Description:
602 * Given the security context copy the low MLS categories into the NetLabel
603 * MLS category field.  Returns zero on success, negative values on failure.
604 *
605 */
606int mls_export_netlbl_cat(struct context *context,
607			  struct netlbl_lsm_secattr *secattr)
608{
609	int rc;
610
611	if (!selinux_mls_enabled)
612		return 0;
613
614	rc = ebitmap_netlbl_export(&context->range.level[0].cat,
615				   &secattr->mls_cat);
616	if (rc == 0 && secattr->mls_cat != NULL)
617		secattr->flags |= NETLBL_SECATTR_MLS_CAT;
618
619	return rc;
620}
621
622/**
623 * mls_import_netlbl_cat - Import the MLS categories from NetLabel
624 * @context: the security context
625 * @secattr: the NetLabel security attributes
626 *
627 * Description:
628 * Copy the NetLabel security attributes into the SELinux context; since the
629 * NetLabel security attribute only contains a single MLS category use it for
630 * both the low and high categories of the context.  Returns zero on success,
631 * negative values on failure.
632 *
633 */
634int mls_import_netlbl_cat(struct context *context,
635			  struct netlbl_lsm_secattr *secattr)
636{
637	int rc;
638
639	if (!selinux_mls_enabled)
640		return 0;
641
642	rc = ebitmap_netlbl_import(&context->range.level[0].cat,
643				   secattr->mls_cat);
644	if (rc != 0)
645		goto import_netlbl_cat_failure;
646
647	rc = ebitmap_cpy(&context->range.level[1].cat,
648			 &context->range.level[0].cat);
649	if (rc != 0)
650		goto import_netlbl_cat_failure;
651
652	return 0;
653
654import_netlbl_cat_failure:
655	ebitmap_destroy(&context->range.level[0].cat);
656	ebitmap_destroy(&context->range.level[1].cat);
657	return rc;
658}
659#endif /* CONFIG_NETLABEL */
660