162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Implementation of the multi-level security (MLS) policy.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author : Stephen Smalley, <stephen.smalley.work@gmail.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci/*
862306a36Sopenharmony_ci * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *	Support for enhanced MLS infrastructure.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci/*
1562306a36Sopenharmony_ci * Updated: Hewlett-Packard <paul@paul-moore.com>
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci *      Added support to import/export the MLS label from NetLabel
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <linux/kernel.h>
2362306a36Sopenharmony_ci#include <linux/slab.h>
2462306a36Sopenharmony_ci#include <linux/string.h>
2562306a36Sopenharmony_ci#include <linux/errno.h>
2662306a36Sopenharmony_ci#include <net/netlabel.h>
2762306a36Sopenharmony_ci#include "sidtab.h"
2862306a36Sopenharmony_ci#include "mls.h"
2962306a36Sopenharmony_ci#include "policydb.h"
3062306a36Sopenharmony_ci#include "services.h"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * Return the length in bytes for the MLS fields of the
3462306a36Sopenharmony_ci * security context string representation of `context'.
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ciint mls_compute_context_len(struct policydb *p, struct context *context)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	int i, l, len, head, prev;
3962306a36Sopenharmony_ci	char *nm;
4062306a36Sopenharmony_ci	struct ebitmap *e;
4162306a36Sopenharmony_ci	struct ebitmap_node *node;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	if (!p->mls_enabled)
4462306a36Sopenharmony_ci		return 0;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	len = 1; /* for the beginning ":" */
4762306a36Sopenharmony_ci	for (l = 0; l < 2; l++) {
4862306a36Sopenharmony_ci		u32 index_sens = context->range.level[l].sens;
4962306a36Sopenharmony_ci		len += strlen(sym_name(p, SYM_LEVELS, index_sens - 1));
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci		/* categories */
5262306a36Sopenharmony_ci		head = -2;
5362306a36Sopenharmony_ci		prev = -2;
5462306a36Sopenharmony_ci		e = &context->range.level[l].cat;
5562306a36Sopenharmony_ci		ebitmap_for_each_positive_bit(e, node, i) {
5662306a36Sopenharmony_ci			if (i - prev > 1) {
5762306a36Sopenharmony_ci				/* one or more negative bits are skipped */
5862306a36Sopenharmony_ci				if (head != prev) {
5962306a36Sopenharmony_ci					nm = sym_name(p, SYM_CATS, prev);
6062306a36Sopenharmony_ci					len += strlen(nm) + 1;
6162306a36Sopenharmony_ci				}
6262306a36Sopenharmony_ci				nm = sym_name(p, SYM_CATS, i);
6362306a36Sopenharmony_ci				len += strlen(nm) + 1;
6462306a36Sopenharmony_ci				head = i;
6562306a36Sopenharmony_ci			}
6662306a36Sopenharmony_ci			prev = i;
6762306a36Sopenharmony_ci		}
6862306a36Sopenharmony_ci		if (prev != head) {
6962306a36Sopenharmony_ci			nm = sym_name(p, SYM_CATS, prev);
7062306a36Sopenharmony_ci			len += strlen(nm) + 1;
7162306a36Sopenharmony_ci		}
7262306a36Sopenharmony_ci		if (l == 0) {
7362306a36Sopenharmony_ci			if (mls_level_eq(&context->range.level[0],
7462306a36Sopenharmony_ci					 &context->range.level[1]))
7562306a36Sopenharmony_ci				break;
7662306a36Sopenharmony_ci			else
7762306a36Sopenharmony_ci				len++;
7862306a36Sopenharmony_ci		}
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	return len;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/*
8562306a36Sopenharmony_ci * Write the security context string representation of
8662306a36Sopenharmony_ci * the MLS fields of `context' into the string `*scontext'.
8762306a36Sopenharmony_ci * Update `*scontext' to point to the end of the MLS fields.
8862306a36Sopenharmony_ci */
8962306a36Sopenharmony_civoid mls_sid_to_context(struct policydb *p,
9062306a36Sopenharmony_ci			struct context *context,
9162306a36Sopenharmony_ci			char **scontext)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	char *scontextp, *nm;
9462306a36Sopenharmony_ci	int i, l, head, prev;
9562306a36Sopenharmony_ci	struct ebitmap *e;
9662306a36Sopenharmony_ci	struct ebitmap_node *node;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (!p->mls_enabled)
9962306a36Sopenharmony_ci		return;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	scontextp = *scontext;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	*scontextp = ':';
10462306a36Sopenharmony_ci	scontextp++;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	for (l = 0; l < 2; l++) {
10762306a36Sopenharmony_ci		strcpy(scontextp, sym_name(p, SYM_LEVELS,
10862306a36Sopenharmony_ci					   context->range.level[l].sens - 1));
10962306a36Sopenharmony_ci		scontextp += strlen(scontextp);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		/* categories */
11262306a36Sopenharmony_ci		head = -2;
11362306a36Sopenharmony_ci		prev = -2;
11462306a36Sopenharmony_ci		e = &context->range.level[l].cat;
11562306a36Sopenharmony_ci		ebitmap_for_each_positive_bit(e, node, i) {
11662306a36Sopenharmony_ci			if (i - prev > 1) {
11762306a36Sopenharmony_ci				/* one or more negative bits are skipped */
11862306a36Sopenharmony_ci				if (prev != head) {
11962306a36Sopenharmony_ci					if (prev - head > 1)
12062306a36Sopenharmony_ci						*scontextp++ = '.';
12162306a36Sopenharmony_ci					else
12262306a36Sopenharmony_ci						*scontextp++ = ',';
12362306a36Sopenharmony_ci					nm = sym_name(p, SYM_CATS, prev);
12462306a36Sopenharmony_ci					strcpy(scontextp, nm);
12562306a36Sopenharmony_ci					scontextp += strlen(nm);
12662306a36Sopenharmony_ci				}
12762306a36Sopenharmony_ci				if (prev < 0)
12862306a36Sopenharmony_ci					*scontextp++ = ':';
12962306a36Sopenharmony_ci				else
13062306a36Sopenharmony_ci					*scontextp++ = ',';
13162306a36Sopenharmony_ci				nm = sym_name(p, SYM_CATS, i);
13262306a36Sopenharmony_ci				strcpy(scontextp, nm);
13362306a36Sopenharmony_ci				scontextp += strlen(nm);
13462306a36Sopenharmony_ci				head = i;
13562306a36Sopenharmony_ci			}
13662306a36Sopenharmony_ci			prev = i;
13762306a36Sopenharmony_ci		}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci		if (prev != head) {
14062306a36Sopenharmony_ci			if (prev - head > 1)
14162306a36Sopenharmony_ci				*scontextp++ = '.';
14262306a36Sopenharmony_ci			else
14362306a36Sopenharmony_ci				*scontextp++ = ',';
14462306a36Sopenharmony_ci			nm = sym_name(p, SYM_CATS, prev);
14562306a36Sopenharmony_ci			strcpy(scontextp, nm);
14662306a36Sopenharmony_ci			scontextp += strlen(nm);
14762306a36Sopenharmony_ci		}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		if (l == 0) {
15062306a36Sopenharmony_ci			if (mls_level_eq(&context->range.level[0],
15162306a36Sopenharmony_ci					 &context->range.level[1]))
15262306a36Sopenharmony_ci				break;
15362306a36Sopenharmony_ci			else
15462306a36Sopenharmony_ci				*scontextp++ = '-';
15562306a36Sopenharmony_ci		}
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	*scontext = scontextp;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ciint mls_level_isvalid(struct policydb *p, struct mls_level *l)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct level_datum *levdatum;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (!l->sens || l->sens > p->p_levels.nprim)
16662306a36Sopenharmony_ci		return 0;
16762306a36Sopenharmony_ci	levdatum = symtab_search(&p->p_levels,
16862306a36Sopenharmony_ci				 sym_name(p, SYM_LEVELS, l->sens - 1));
16962306a36Sopenharmony_ci	if (!levdatum)
17062306a36Sopenharmony_ci		return 0;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/*
17362306a36Sopenharmony_ci	 * Return 1 iff all the bits set in l->cat are also be set in
17462306a36Sopenharmony_ci	 * levdatum->level->cat and no bit in l->cat is larger than
17562306a36Sopenharmony_ci	 * p->p_cats.nprim.
17662306a36Sopenharmony_ci	 */
17762306a36Sopenharmony_ci	return ebitmap_contains(&levdatum->level->cat, &l->cat,
17862306a36Sopenharmony_ci				p->p_cats.nprim);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ciint mls_range_isvalid(struct policydb *p, struct mls_range *r)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	return (mls_level_isvalid(p, &r->level[0]) &&
18462306a36Sopenharmony_ci		mls_level_isvalid(p, &r->level[1]) &&
18562306a36Sopenharmony_ci		mls_level_dom(&r->level[1], &r->level[0]));
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/*
18962306a36Sopenharmony_ci * Return 1 if the MLS fields in the security context
19062306a36Sopenharmony_ci * structure `c' are valid.  Return 0 otherwise.
19162306a36Sopenharmony_ci */
19262306a36Sopenharmony_ciint mls_context_isvalid(struct policydb *p, struct context *c)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	struct user_datum *usrdatum;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (!p->mls_enabled)
19762306a36Sopenharmony_ci		return 1;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (!mls_range_isvalid(p, &c->range))
20062306a36Sopenharmony_ci		return 0;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (c->role == OBJECT_R_VAL)
20362306a36Sopenharmony_ci		return 1;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/*
20662306a36Sopenharmony_ci	 * User must be authorized for the MLS range.
20762306a36Sopenharmony_ci	 */
20862306a36Sopenharmony_ci	if (!c->user || c->user > p->p_users.nprim)
20962306a36Sopenharmony_ci		return 0;
21062306a36Sopenharmony_ci	usrdatum = p->user_val_to_struct[c->user - 1];
21162306a36Sopenharmony_ci	if (!mls_range_contains(usrdatum->range, c->range))
21262306a36Sopenharmony_ci		return 0; /* user may not be associated with range */
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	return 1;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci/*
21862306a36Sopenharmony_ci * Set the MLS fields in the security context structure
21962306a36Sopenharmony_ci * `context' based on the string representation in
22062306a36Sopenharmony_ci * the string `scontext'.
22162306a36Sopenharmony_ci *
22262306a36Sopenharmony_ci * This function modifies the string in place, inserting
22362306a36Sopenharmony_ci * NULL characters to terminate the MLS fields.
22462306a36Sopenharmony_ci *
22562306a36Sopenharmony_ci * If a def_sid is provided and no MLS field is present,
22662306a36Sopenharmony_ci * copy the MLS field of the associated default context.
22762306a36Sopenharmony_ci * Used for upgraded to MLS systems where objects may lack
22862306a36Sopenharmony_ci * MLS fields.
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci * Policy read-lock must be held for sidtab lookup.
23162306a36Sopenharmony_ci *
23262306a36Sopenharmony_ci */
23362306a36Sopenharmony_ciint mls_context_to_sid(struct policydb *pol,
23462306a36Sopenharmony_ci		       char oldc,
23562306a36Sopenharmony_ci		       char *scontext,
23662306a36Sopenharmony_ci		       struct context *context,
23762306a36Sopenharmony_ci		       struct sidtab *s,
23862306a36Sopenharmony_ci		       u32 def_sid)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	char *sensitivity, *cur_cat, *next_cat, *rngptr;
24162306a36Sopenharmony_ci	struct level_datum *levdatum;
24262306a36Sopenharmony_ci	struct cat_datum *catdatum, *rngdatum;
24362306a36Sopenharmony_ci	u32 i;
24462306a36Sopenharmony_ci	int l, rc;
24562306a36Sopenharmony_ci	char *rangep[2];
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	if (!pol->mls_enabled) {
24862306a36Sopenharmony_ci		/*
24962306a36Sopenharmony_ci		 * With no MLS, only return -EINVAL if there is a MLS field
25062306a36Sopenharmony_ci		 * and it did not come from an xattr.
25162306a36Sopenharmony_ci		 */
25262306a36Sopenharmony_ci		if (oldc && def_sid == SECSID_NULL)
25362306a36Sopenharmony_ci			return -EINVAL;
25462306a36Sopenharmony_ci		return 0;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	/*
25862306a36Sopenharmony_ci	 * No MLS component to the security context, try and map to
25962306a36Sopenharmony_ci	 * default if provided.
26062306a36Sopenharmony_ci	 */
26162306a36Sopenharmony_ci	if (!oldc) {
26262306a36Sopenharmony_ci		struct context *defcon;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci		if (def_sid == SECSID_NULL)
26562306a36Sopenharmony_ci			return -EINVAL;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci		defcon = sidtab_search(s, def_sid);
26862306a36Sopenharmony_ci		if (!defcon)
26962306a36Sopenharmony_ci			return -EINVAL;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		return mls_context_cpy(context, defcon);
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/*
27562306a36Sopenharmony_ci	 * If we're dealing with a range, figure out where the two parts
27662306a36Sopenharmony_ci	 * of the range begin.
27762306a36Sopenharmony_ci	 */
27862306a36Sopenharmony_ci	rangep[0] = scontext;
27962306a36Sopenharmony_ci	rangep[1] = strchr(scontext, '-');
28062306a36Sopenharmony_ci	if (rangep[1]) {
28162306a36Sopenharmony_ci		rangep[1][0] = '\0';
28262306a36Sopenharmony_ci		rangep[1]++;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/* For each part of the range: */
28662306a36Sopenharmony_ci	for (l = 0; l < 2; l++) {
28762306a36Sopenharmony_ci		/* Split sensitivity and category set. */
28862306a36Sopenharmony_ci		sensitivity = rangep[l];
28962306a36Sopenharmony_ci		if (sensitivity == NULL)
29062306a36Sopenharmony_ci			break;
29162306a36Sopenharmony_ci		next_cat = strchr(sensitivity, ':');
29262306a36Sopenharmony_ci		if (next_cat)
29362306a36Sopenharmony_ci			*(next_cat++) = '\0';
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci		/* Parse sensitivity. */
29662306a36Sopenharmony_ci		levdatum = symtab_search(&pol->p_levels, sensitivity);
29762306a36Sopenharmony_ci		if (!levdatum)
29862306a36Sopenharmony_ci			return -EINVAL;
29962306a36Sopenharmony_ci		context->range.level[l].sens = levdatum->level->sens;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci		/* Extract category set. */
30262306a36Sopenharmony_ci		while (next_cat != NULL) {
30362306a36Sopenharmony_ci			cur_cat = next_cat;
30462306a36Sopenharmony_ci			next_cat = strchr(next_cat, ',');
30562306a36Sopenharmony_ci			if (next_cat != NULL)
30662306a36Sopenharmony_ci				*(next_cat++) = '\0';
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci			/* Separate into range if exists */
30962306a36Sopenharmony_ci			rngptr = strchr(cur_cat, '.');
31062306a36Sopenharmony_ci			if (rngptr != NULL) {
31162306a36Sopenharmony_ci				/* Remove '.' */
31262306a36Sopenharmony_ci				*rngptr++ = '\0';
31362306a36Sopenharmony_ci			}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci			catdatum = symtab_search(&pol->p_cats, cur_cat);
31662306a36Sopenharmony_ci			if (!catdatum)
31762306a36Sopenharmony_ci				return -EINVAL;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci			rc = ebitmap_set_bit(&context->range.level[l].cat,
32062306a36Sopenharmony_ci					     catdatum->value - 1, 1);
32162306a36Sopenharmony_ci			if (rc)
32262306a36Sopenharmony_ci				return rc;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci			/* If range, set all categories in range */
32562306a36Sopenharmony_ci			if (rngptr == NULL)
32662306a36Sopenharmony_ci				continue;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci			rngdatum = symtab_search(&pol->p_cats, rngptr);
32962306a36Sopenharmony_ci			if (!rngdatum)
33062306a36Sopenharmony_ci				return -EINVAL;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci			if (catdatum->value >= rngdatum->value)
33362306a36Sopenharmony_ci				return -EINVAL;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci			for (i = catdatum->value; i < rngdatum->value; i++) {
33662306a36Sopenharmony_ci				rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
33762306a36Sopenharmony_ci				if (rc)
33862306a36Sopenharmony_ci					return rc;
33962306a36Sopenharmony_ci			}
34062306a36Sopenharmony_ci		}
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/* If we didn't see a '-', the range start is also the range end. */
34462306a36Sopenharmony_ci	if (rangep[1] == NULL) {
34562306a36Sopenharmony_ci		context->range.level[1].sens = context->range.level[0].sens;
34662306a36Sopenharmony_ci		rc = ebitmap_cpy(&context->range.level[1].cat,
34762306a36Sopenharmony_ci				 &context->range.level[0].cat);
34862306a36Sopenharmony_ci		if (rc)
34962306a36Sopenharmony_ci			return rc;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	return 0;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci/*
35662306a36Sopenharmony_ci * Set the MLS fields in the security context structure
35762306a36Sopenharmony_ci * `context' based on the string representation in
35862306a36Sopenharmony_ci * the string `str'.  This function will allocate temporary memory with the
35962306a36Sopenharmony_ci * given constraints of gfp_mask.
36062306a36Sopenharmony_ci */
36162306a36Sopenharmony_ciint mls_from_string(struct policydb *p, char *str, struct context *context,
36262306a36Sopenharmony_ci		    gfp_t gfp_mask)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	char *tmpstr;
36562306a36Sopenharmony_ci	int rc;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	if (!p->mls_enabled)
36862306a36Sopenharmony_ci		return -EINVAL;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	tmpstr = kstrdup(str, gfp_mask);
37162306a36Sopenharmony_ci	if (!tmpstr) {
37262306a36Sopenharmony_ci		rc = -ENOMEM;
37362306a36Sopenharmony_ci	} else {
37462306a36Sopenharmony_ci		rc = mls_context_to_sid(p, ':', tmpstr, context,
37562306a36Sopenharmony_ci					NULL, SECSID_NULL);
37662306a36Sopenharmony_ci		kfree(tmpstr);
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return rc;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci/*
38362306a36Sopenharmony_ci * Copies the MLS range `range' into `context'.
38462306a36Sopenharmony_ci */
38562306a36Sopenharmony_ciint mls_range_set(struct context *context,
38662306a36Sopenharmony_ci				struct mls_range *range)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	int l, rc = 0;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	/* Copy the MLS range into the  context */
39162306a36Sopenharmony_ci	for (l = 0; l < 2; l++) {
39262306a36Sopenharmony_ci		context->range.level[l].sens = range->level[l].sens;
39362306a36Sopenharmony_ci		rc = ebitmap_cpy(&context->range.level[l].cat,
39462306a36Sopenharmony_ci				 &range->level[l].cat);
39562306a36Sopenharmony_ci		if (rc)
39662306a36Sopenharmony_ci			break;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	return rc;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ciint mls_setup_user_range(struct policydb *p,
40362306a36Sopenharmony_ci			 struct context *fromcon, struct user_datum *user,
40462306a36Sopenharmony_ci			 struct context *usercon)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	if (p->mls_enabled) {
40762306a36Sopenharmony_ci		struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
40862306a36Sopenharmony_ci		struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
40962306a36Sopenharmony_ci		struct mls_level *user_low = &(user->range.level[0]);
41062306a36Sopenharmony_ci		struct mls_level *user_clr = &(user->range.level[1]);
41162306a36Sopenharmony_ci		struct mls_level *user_def = &(user->dfltlevel);
41262306a36Sopenharmony_ci		struct mls_level *usercon_sen = &(usercon->range.level[0]);
41362306a36Sopenharmony_ci		struct mls_level *usercon_clr = &(usercon->range.level[1]);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		/* Honor the user's default level if we can */
41662306a36Sopenharmony_ci		if (mls_level_between(user_def, fromcon_sen, fromcon_clr))
41762306a36Sopenharmony_ci			*usercon_sen = *user_def;
41862306a36Sopenharmony_ci		else if (mls_level_between(fromcon_sen, user_def, user_clr))
41962306a36Sopenharmony_ci			*usercon_sen = *fromcon_sen;
42062306a36Sopenharmony_ci		else if (mls_level_between(fromcon_clr, user_low, user_def))
42162306a36Sopenharmony_ci			*usercon_sen = *user_low;
42262306a36Sopenharmony_ci		else
42362306a36Sopenharmony_ci			return -EINVAL;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		/* Lower the clearance of available contexts
42662306a36Sopenharmony_ci		   if the clearance of "fromcon" is lower than
42762306a36Sopenharmony_ci		   that of the user's default clearance (but
42862306a36Sopenharmony_ci		   only if the "fromcon" clearance dominates
42962306a36Sopenharmony_ci		   the user's computed sensitivity level) */
43062306a36Sopenharmony_ci		if (mls_level_dom(user_clr, fromcon_clr))
43162306a36Sopenharmony_ci			*usercon_clr = *fromcon_clr;
43262306a36Sopenharmony_ci		else if (mls_level_dom(fromcon_clr, user_clr))
43362306a36Sopenharmony_ci			*usercon_clr = *user_clr;
43462306a36Sopenharmony_ci		else
43562306a36Sopenharmony_ci			return -EINVAL;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	return 0;
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci/*
44262306a36Sopenharmony_ci * Convert the MLS fields in the security context
44362306a36Sopenharmony_ci * structure `oldc' from the values specified in the
44462306a36Sopenharmony_ci * policy `oldp' to the values specified in the policy `newp',
44562306a36Sopenharmony_ci * storing the resulting context in `newc'.
44662306a36Sopenharmony_ci */
44762306a36Sopenharmony_ciint mls_convert_context(struct policydb *oldp,
44862306a36Sopenharmony_ci			struct policydb *newp,
44962306a36Sopenharmony_ci			struct context *oldc,
45062306a36Sopenharmony_ci			struct context *newc)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct level_datum *levdatum;
45362306a36Sopenharmony_ci	struct cat_datum *catdatum;
45462306a36Sopenharmony_ci	struct ebitmap_node *node;
45562306a36Sopenharmony_ci	u32 i;
45662306a36Sopenharmony_ci	int l;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (!oldp->mls_enabled || !newp->mls_enabled)
45962306a36Sopenharmony_ci		return 0;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	for (l = 0; l < 2; l++) {
46262306a36Sopenharmony_ci		char *name = sym_name(oldp, SYM_LEVELS,
46362306a36Sopenharmony_ci				      oldc->range.level[l].sens - 1);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		levdatum = symtab_search(&newp->p_levels, name);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		if (!levdatum)
46862306a36Sopenharmony_ci			return -EINVAL;
46962306a36Sopenharmony_ci		newc->range.level[l].sens = levdatum->level->sens;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		ebitmap_for_each_positive_bit(&oldc->range.level[l].cat,
47262306a36Sopenharmony_ci					      node, i) {
47362306a36Sopenharmony_ci			int rc;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci			catdatum = symtab_search(&newp->p_cats,
47662306a36Sopenharmony_ci						 sym_name(oldp, SYM_CATS, i));
47762306a36Sopenharmony_ci			if (!catdatum)
47862306a36Sopenharmony_ci				return -EINVAL;
47962306a36Sopenharmony_ci			rc = ebitmap_set_bit(&newc->range.level[l].cat,
48062306a36Sopenharmony_ci					     catdatum->value - 1, 1);
48162306a36Sopenharmony_ci			if (rc)
48262306a36Sopenharmony_ci				return rc;
48362306a36Sopenharmony_ci		}
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	return 0;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ciint mls_compute_sid(struct policydb *p,
49062306a36Sopenharmony_ci		    struct context *scontext,
49162306a36Sopenharmony_ci		    struct context *tcontext,
49262306a36Sopenharmony_ci		    u16 tclass,
49362306a36Sopenharmony_ci		    u32 specified,
49462306a36Sopenharmony_ci		    struct context *newcontext,
49562306a36Sopenharmony_ci		    bool sock)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	struct range_trans rtr;
49862306a36Sopenharmony_ci	struct mls_range *r;
49962306a36Sopenharmony_ci	struct class_datum *cladatum;
50062306a36Sopenharmony_ci	char default_range = 0;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (!p->mls_enabled)
50362306a36Sopenharmony_ci		return 0;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	switch (specified) {
50662306a36Sopenharmony_ci	case AVTAB_TRANSITION:
50762306a36Sopenharmony_ci		/* Look for a range transition rule. */
50862306a36Sopenharmony_ci		rtr.source_type = scontext->type;
50962306a36Sopenharmony_ci		rtr.target_type = tcontext->type;
51062306a36Sopenharmony_ci		rtr.target_class = tclass;
51162306a36Sopenharmony_ci		r = policydb_rangetr_search(p, &rtr);
51262306a36Sopenharmony_ci		if (r)
51362306a36Sopenharmony_ci			return mls_range_set(newcontext, r);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		if (tclass && tclass <= p->p_classes.nprim) {
51662306a36Sopenharmony_ci			cladatum = p->class_val_to_struct[tclass - 1];
51762306a36Sopenharmony_ci			if (cladatum)
51862306a36Sopenharmony_ci				default_range = cladatum->default_range;
51962306a36Sopenharmony_ci		}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		switch (default_range) {
52262306a36Sopenharmony_ci		case DEFAULT_SOURCE_LOW:
52362306a36Sopenharmony_ci			return mls_context_cpy_low(newcontext, scontext);
52462306a36Sopenharmony_ci		case DEFAULT_SOURCE_HIGH:
52562306a36Sopenharmony_ci			return mls_context_cpy_high(newcontext, scontext);
52662306a36Sopenharmony_ci		case DEFAULT_SOURCE_LOW_HIGH:
52762306a36Sopenharmony_ci			return mls_context_cpy(newcontext, scontext);
52862306a36Sopenharmony_ci		case DEFAULT_TARGET_LOW:
52962306a36Sopenharmony_ci			return mls_context_cpy_low(newcontext, tcontext);
53062306a36Sopenharmony_ci		case DEFAULT_TARGET_HIGH:
53162306a36Sopenharmony_ci			return mls_context_cpy_high(newcontext, tcontext);
53262306a36Sopenharmony_ci		case DEFAULT_TARGET_LOW_HIGH:
53362306a36Sopenharmony_ci			return mls_context_cpy(newcontext, tcontext);
53462306a36Sopenharmony_ci		case DEFAULT_GLBLUB:
53562306a36Sopenharmony_ci			return mls_context_glblub(newcontext,
53662306a36Sopenharmony_ci						  scontext, tcontext);
53762306a36Sopenharmony_ci		}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci		fallthrough;
54062306a36Sopenharmony_ci	case AVTAB_CHANGE:
54162306a36Sopenharmony_ci		if ((tclass == p->process_class) || sock)
54262306a36Sopenharmony_ci			/* Use the process MLS attributes. */
54362306a36Sopenharmony_ci			return mls_context_cpy(newcontext, scontext);
54462306a36Sopenharmony_ci		else
54562306a36Sopenharmony_ci			/* Use the process effective MLS attributes. */
54662306a36Sopenharmony_ci			return mls_context_cpy_low(newcontext, scontext);
54762306a36Sopenharmony_ci	case AVTAB_MEMBER:
54862306a36Sopenharmony_ci		/* Use the process effective MLS attributes. */
54962306a36Sopenharmony_ci		return mls_context_cpy_low(newcontext, scontext);
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci	return -EINVAL;
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci#ifdef CONFIG_NETLABEL
55562306a36Sopenharmony_ci/**
55662306a36Sopenharmony_ci * mls_export_netlbl_lvl - Export the MLS sensitivity levels to NetLabel
55762306a36Sopenharmony_ci * @p: the policy
55862306a36Sopenharmony_ci * @context: the security context
55962306a36Sopenharmony_ci * @secattr: the NetLabel security attributes
56062306a36Sopenharmony_ci *
56162306a36Sopenharmony_ci * Description:
56262306a36Sopenharmony_ci * Given the security context copy the low MLS sensitivity level into the
56362306a36Sopenharmony_ci * NetLabel MLS sensitivity level field.
56462306a36Sopenharmony_ci *
56562306a36Sopenharmony_ci */
56662306a36Sopenharmony_civoid mls_export_netlbl_lvl(struct policydb *p,
56762306a36Sopenharmony_ci			   struct context *context,
56862306a36Sopenharmony_ci			   struct netlbl_lsm_secattr *secattr)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	if (!p->mls_enabled)
57162306a36Sopenharmony_ci		return;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	secattr->attr.mls.lvl = context->range.level[0].sens - 1;
57462306a36Sopenharmony_ci	secattr->flags |= NETLBL_SECATTR_MLS_LVL;
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci/**
57862306a36Sopenharmony_ci * mls_import_netlbl_lvl - Import the NetLabel MLS sensitivity levels
57962306a36Sopenharmony_ci * @p: the policy
58062306a36Sopenharmony_ci * @context: the security context
58162306a36Sopenharmony_ci * @secattr: the NetLabel security attributes
58262306a36Sopenharmony_ci *
58362306a36Sopenharmony_ci * Description:
58462306a36Sopenharmony_ci * Given the security context and the NetLabel security attributes, copy the
58562306a36Sopenharmony_ci * NetLabel MLS sensitivity level into the context.
58662306a36Sopenharmony_ci *
58762306a36Sopenharmony_ci */
58862306a36Sopenharmony_civoid mls_import_netlbl_lvl(struct policydb *p,
58962306a36Sopenharmony_ci			   struct context *context,
59062306a36Sopenharmony_ci			   struct netlbl_lsm_secattr *secattr)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	if (!p->mls_enabled)
59362306a36Sopenharmony_ci		return;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	context->range.level[0].sens = secattr->attr.mls.lvl + 1;
59662306a36Sopenharmony_ci	context->range.level[1].sens = context->range.level[0].sens;
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci/**
60062306a36Sopenharmony_ci * mls_export_netlbl_cat - Export the MLS categories to NetLabel
60162306a36Sopenharmony_ci * @p: the policy
60262306a36Sopenharmony_ci * @context: the security context
60362306a36Sopenharmony_ci * @secattr: the NetLabel security attributes
60462306a36Sopenharmony_ci *
60562306a36Sopenharmony_ci * Description:
60662306a36Sopenharmony_ci * Given the security context copy the low MLS categories into the NetLabel
60762306a36Sopenharmony_ci * MLS category field.  Returns zero on success, negative values on failure.
60862306a36Sopenharmony_ci *
60962306a36Sopenharmony_ci */
61062306a36Sopenharmony_ciint mls_export_netlbl_cat(struct policydb *p,
61162306a36Sopenharmony_ci			  struct context *context,
61262306a36Sopenharmony_ci			  struct netlbl_lsm_secattr *secattr)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	int rc;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (!p->mls_enabled)
61762306a36Sopenharmony_ci		return 0;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	rc = ebitmap_netlbl_export(&context->range.level[0].cat,
62062306a36Sopenharmony_ci				   &secattr->attr.mls.cat);
62162306a36Sopenharmony_ci	if (rc == 0 && secattr->attr.mls.cat != NULL)
62262306a36Sopenharmony_ci		secattr->flags |= NETLBL_SECATTR_MLS_CAT;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	return rc;
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci/**
62862306a36Sopenharmony_ci * mls_import_netlbl_cat - Import the MLS categories from NetLabel
62962306a36Sopenharmony_ci * @p: the policy
63062306a36Sopenharmony_ci * @context: the security context
63162306a36Sopenharmony_ci * @secattr: the NetLabel security attributes
63262306a36Sopenharmony_ci *
63362306a36Sopenharmony_ci * Description:
63462306a36Sopenharmony_ci * Copy the NetLabel security attributes into the SELinux context; since the
63562306a36Sopenharmony_ci * NetLabel security attribute only contains a single MLS category use it for
63662306a36Sopenharmony_ci * both the low and high categories of the context.  Returns zero on success,
63762306a36Sopenharmony_ci * negative values on failure.
63862306a36Sopenharmony_ci *
63962306a36Sopenharmony_ci */
64062306a36Sopenharmony_ciint mls_import_netlbl_cat(struct policydb *p,
64162306a36Sopenharmony_ci			  struct context *context,
64262306a36Sopenharmony_ci			  struct netlbl_lsm_secattr *secattr)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	int rc;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	if (!p->mls_enabled)
64762306a36Sopenharmony_ci		return 0;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	rc = ebitmap_netlbl_import(&context->range.level[0].cat,
65062306a36Sopenharmony_ci				   secattr->attr.mls.cat);
65162306a36Sopenharmony_ci	if (rc)
65262306a36Sopenharmony_ci		goto import_netlbl_cat_failure;
65362306a36Sopenharmony_ci	memcpy(&context->range.level[1].cat, &context->range.level[0].cat,
65462306a36Sopenharmony_ci	       sizeof(context->range.level[0].cat));
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	return 0;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ciimport_netlbl_cat_failure:
65962306a36Sopenharmony_ci	ebitmap_destroy(&context->range.level[0].cat);
66062306a36Sopenharmony_ci	return rc;
66162306a36Sopenharmony_ci}
66262306a36Sopenharmony_ci#endif /* CONFIG_NETLABEL */
663