summary refs log tree commit diff
path: root/security/selinux/ss/mls.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/selinux/ss/mls.c')
-rw-r--r--security/selinux/ss/mls.c527
1 files changed, 527 insertions, 0 deletions
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
new file mode 100644
index 000000000000..756036bcc243
--- /dev/null
+++ b/security/selinux/ss/mls.c
@@ -0,0 +1,527 @@
+/*
+ * Implementation of the multi-level security (MLS) policy.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+/*
+ * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ *
+ *	Support for enhanced MLS infrastructure.
+ *
+ * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include "mls.h"
+#include "policydb.h"
+#include "services.h"
+
+/*
+ * Return the length in bytes for the MLS fields of the
+ * security context string representation of `context'.
+ */
+int mls_compute_context_len(struct context * context)
+{
+	int i, l, len, range;
+
+	if (!selinux_mls_enabled)
+		return 0;
+
+	len = 1; /* for the beginning ":" */
+	for (l = 0; l < 2; l++) {
+		range = 0;
+		len += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
+
+		for (i = 1; i <= ebitmap_length(&context->range.level[l].cat); i++) {
+			if (ebitmap_get_bit(&context->range.level[l].cat, i - 1)) {
+				if (range) {
+					range++;
+					continue;
+				}
+
+				len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1;
+				range++;
+			} else {
+				if (range > 1)
+					len += strlen(policydb.p_cat_val_to_name[i - 2]) + 1;
+				range = 0;
+			}
+		}
+		/* Handle case where last category is the end of range */
+		if (range > 1)
+			len += strlen(policydb.p_cat_val_to_name[i - 2]) + 1;
+
+		if (l == 0) {
+			if (mls_level_eq(&context->range.level[0],
+			                 &context->range.level[1]))
+				break;
+			else
+				len++;
+		}
+	}
+
+	return len;
+}
+
+/*
+ * Write the security context string representation of
+ * the MLS fields of `context' into the string `*scontext'.
+ * Update `*scontext' to point to the end of the MLS fields.
+ */
+void mls_sid_to_context(struct context *context,
+                        char **scontext)
+{
+	char *scontextp;
+	int i, l, range, wrote_sep;
+
+	if (!selinux_mls_enabled)
+		return;
+
+	scontextp = *scontext;
+
+	*scontextp = ':';
+	scontextp++;
+
+	for (l = 0; l < 2; l++) {
+		range = 0;
+		wrote_sep = 0;
+		strcpy(scontextp,
+		       policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
+		scontextp += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
+
+		/* categories */
+		for (i = 1; i <= ebitmap_length(&context->range.level[l].cat); i++) {
+			if (ebitmap_get_bit(&context->range.level[l].cat, i - 1)) {
+				if (range) {
+					range++;
+					continue;
+				}
+
+				if (!wrote_sep) {
+					*scontextp++ = ':';
+					wrote_sep = 1;
+				} else
+					*scontextp++ = ',';
+				strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]);
+				scontextp += strlen(policydb.p_cat_val_to_name[i - 1]);
+				range++;
+			} else {
+				if (range > 1) {
+					if (range > 2)
+						*scontextp++ = '.';
+					else
+						*scontextp++ = ',';
+
+					strcpy(scontextp, policydb.p_cat_val_to_name[i - 2]);
+					scontextp += strlen(policydb.p_cat_val_to_name[i - 2]);
+				}
+				range = 0;
+			}
+		}
+
+		/* Handle case where last category is the end of range */
+		if (range > 1) {
+			if (range > 2)
+				*scontextp++ = '.';
+			else
+				*scontextp++ = ',';
+
+			strcpy(scontextp, policydb.p_cat_val_to_name[i - 2]);
+			scontextp += strlen(policydb.p_cat_val_to_name[i - 2]);
+		}
+
+		if (l == 0) {
+			if (mls_level_eq(&context->range.level[0],
+			                 &context->range.level[1]))
+				break;
+			else {
+				*scontextp = '-';
+				scontextp++;
+			}
+		}
+	}
+
+	*scontext = scontextp;
+	return;
+}
+
+/*
+ * Return 1 if the MLS fields in the security context
+ * structure `c' are valid.  Return 0 otherwise.
+ */
+int mls_context_isvalid(struct policydb *p, struct context *c)
+{
+	struct level_datum *levdatum;
+	struct user_datum *usrdatum;
+	int i, l;
+
+	if (!selinux_mls_enabled)
+		return 1;
+
+	/*
+	 * MLS range validity checks: high must dominate low, low level must
+	 * be valid (category set <-> sensitivity check), and high level must
+	 * be valid (category set <-> sensitivity check)
+	 */
+	if (!mls_level_dom(&c->range.level[1], &c->range.level[0]))
+		/* High does not dominate low. */
+		return 0;
+
+	for (l = 0; l < 2; l++) {
+		if (!c->range.level[l].sens || c->range.level[l].sens > p->p_levels.nprim)
+			return 0;
+		levdatum = hashtab_search(p->p_levels.table,
+			p->p_sens_val_to_name[c->range.level[l].sens - 1]);
+		if (!levdatum)
+			return 0;
+
+		for (i = 1; i <= ebitmap_length(&c->range.level[l].cat); i++) {
+			if (ebitmap_get_bit(&c->range.level[l].cat, i - 1)) {
+				if (i > p->p_cats.nprim)
+					return 0;
+				if (!ebitmap_get_bit(&levdatum->level->cat, i - 1))
+					/*
+					 * Category may not be associated with
+					 * sensitivity in low level.
+					 */
+					return 0;
+			}
+		}
+	}
+
+	if (c->role == OBJECT_R_VAL)
+		return 1;
+
+	/*
+	 * User must be authorized for the MLS range.
+	 */
+	if (!c->user || c->user > p->p_users.nprim)
+		return 0;
+	usrdatum = p->user_val_to_struct[c->user - 1];
+	if (!mls_range_contains(usrdatum->range, c->range))
+		return 0; /* user may not be associated with range */
+
+	return 1;
+}
+
+/*
+ * Set the MLS fields in the security context structure
+ * `context' based on the string representation in
+ * the string `*scontext'.  Update `*scontext' to
+ * point to the end of the string representation of
+ * the MLS fields.
+ *
+ * This function modifies the string in place, inserting
+ * NULL characters to terminate the MLS fields.
+ */
+int mls_context_to_sid(char oldc,
+		       char **scontext,
+		       struct context *context)
+{
+
+	char delim;
+	char *scontextp, *p, *rngptr;
+	struct level_datum *levdatum;
+	struct cat_datum *catdatum, *rngdatum;
+	int l, rc = -EINVAL;
+
+	if (!selinux_mls_enabled)
+		return 0;
+
+	/* No MLS component to the security context. */
+	if (!oldc)
+		goto out;
+
+	/* Extract low sensitivity. */
+	scontextp = p = *scontext;
+	while (*p && *p != ':' && *p != '-')
+		p++;
+
+	delim = *p;
+	if (delim != 0)
+		*p++ = 0;
+
+	for (l = 0; l < 2; l++) {
+		levdatum = hashtab_search(policydb.p_levels.table, scontextp);
+		if (!levdatum) {
+			rc = -EINVAL;
+			goto out;
+		}
+
+		context->range.level[l].sens = levdatum->level->sens;
+
+		if (delim == ':') {
+			/* Extract category set. */
+			while (1) {
+				scontextp = p;
+				while (*p && *p != ',' && *p != '-')
+					p++;
+				delim = *p;
+				if (delim != 0)
+					*p++ = 0;
+
+				/* Separate into range if exists */
+				if ((rngptr = strchr(scontextp, '.')) != NULL) {
+					/* Remove '.' */
+					*rngptr++ = 0;
+				}
+
+				catdatum = hashtab_search(policydb.p_cats.table,
+				                          scontextp);
+				if (!catdatum) {
+					rc = -EINVAL;
+					goto out;
+				}
+
+				rc = ebitmap_set_bit(&context->range.level[l].cat,
+				                     catdatum->value - 1, 1);
+				if (rc)
+					goto out;
+
+				/* If range, set all categories in range */
+				if (rngptr) {
+					int i;
+
+					rngdatum = hashtab_search(policydb.p_cats.table, rngptr);
+					if (!rngdatum) {
+						rc = -EINVAL;
+						goto out;
+					}
+
+					if (catdatum->value >= rngdatum->value) {
+						rc = -EINVAL;
+						goto out;
+					}
+
+					for (i = catdatum->value; i < rngdatum->value; i++) {
+						rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
+						if (rc)
+							goto out;
+					}
+				}
+
+				if (delim != ',')
+					break;
+			}
+		}
+		if (delim == '-') {
+			/* Extract high sensitivity. */
+			scontextp = p;
+			while (*p && *p != ':')
+				p++;
+
+			delim = *p;
+			if (delim != 0)
+				*p++ = 0;
+		} else
+			break;
+	}
+
+	if (l == 0) {
+		context->range.level[1].sens = context->range.level[0].sens;
+		rc = ebitmap_cpy(&context->range.level[1].cat,
+				 &context->range.level[0].cat);
+		if (rc)
+			goto out;
+	}
+	*scontext = ++p;
+	rc = 0;
+out:
+	return rc;
+}
+
+/*
+ * Copies the MLS range from `src' into `dst'.
+ */
+static inline int mls_copy_context(struct context *dst,
+				   struct context *src)
+{
+	int l, rc = 0;
+
+	/* Copy the MLS range from the source context */
+	for (l = 0; l < 2; l++) {
+		dst->range.level[l].sens = src->range.level[l].sens;
+		rc = ebitmap_cpy(&dst->range.level[l].cat,
+				 &src->range.level[l].cat);
+		if (rc)
+			break;
+	}
+
+	return rc;
+}
+
+/*
+ * Copies the effective MLS range from `src' into `dst'.
+ */
+static inline int mls_scopy_context(struct context *dst,
+                                    struct context *src)
+{
+	int l, rc = 0;
+
+	/* Copy the MLS range from the source context */
+	for (l = 0; l < 2; l++) {
+		dst->range.level[l].sens = src->range.level[0].sens;
+		rc = ebitmap_cpy(&dst->range.level[l].cat,
+				 &src->range.level[0].cat);
+		if (rc)
+			break;
+	}
+
+	return rc;
+}
+
+/*
+ * Copies the MLS range `range' into `context'.
+ */
+static inline int mls_range_set(struct context *context,
+                                struct mls_range *range)
+{
+	int l, rc = 0;
+
+	/* Copy the MLS range into the  context */
+	for (l = 0; l < 2; l++) {
+		context->range.level[l].sens = range->level[l].sens;
+		rc = ebitmap_cpy(&context->range.level[l].cat,
+				 &range->level[l].cat);
+		if (rc)
+			break;
+	}
+
+	return rc;
+}
+
+int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
+                         struct context *usercon)
+{
+	if (selinux_mls_enabled) {
+		struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
+		struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
+		struct mls_level *user_low = &(user->range.level[0]);
+		struct mls_level *user_clr = &(user->range.level[1]);
+		struct mls_level *user_def = &(user->dfltlevel);
+		struct mls_level *usercon_sen = &(usercon->range.level[0]);
+		struct mls_level *usercon_clr = &(usercon->range.level[1]);
+
+		/* Honor the user's default level if we can */
+		if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) {
+			*usercon_sen = *user_def;
+		} else if (mls_level_between(fromcon_sen, user_def, user_clr)) {
+			*usercon_sen = *fromcon_sen;
+		} else if (mls_level_between(fromcon_clr, user_low, user_def)) {
+			*usercon_sen = *user_low;
+		} else
+			return -EINVAL;
+
+		/* Lower the clearance of available contexts
+		   if the clearance of "fromcon" is lower than
+		   that of the user's default clearance (but
+		   only if the "fromcon" clearance dominates
+		   the user's computed sensitivity level) */
+		if (mls_level_dom(user_clr, fromcon_clr)) {
+			*usercon_clr = *fromcon_clr;
+		} else if (mls_level_dom(fromcon_clr, user_clr)) {
+			*usercon_clr = *user_clr;
+		} else
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Convert the MLS fields in the security context
+ * structure `c' from the values specified in the
+ * policy `oldp' to the values specified in the policy `newp'.
+ */
+int mls_convert_context(struct policydb *oldp,
+			struct policydb *newp,
+			struct context *c)
+{
+	struct level_datum *levdatum;
+	struct cat_datum *catdatum;
+	struct ebitmap bitmap;
+	int l, i;
+
+	if (!selinux_mls_enabled)
+		return 0;
+
+	for (l = 0; l < 2; l++) {
+		levdatum = hashtab_search(newp->p_levels.table,
+			oldp->p_sens_val_to_name[c->range.level[l].sens - 1]);
+
+		if (!levdatum)
+			return -EINVAL;
+		c->range.level[l].sens = levdatum->level->sens;
+
+		ebitmap_init(&bitmap);
+		for (i = 1; i <= ebitmap_length(&c->range.level[l].cat); i++) {
+			if (ebitmap_get_bit(&c->range.level[l].cat, i - 1)) {
+				int rc;
+
+				catdatum = hashtab_search(newp->p_cats.table,
+				         	oldp->p_cat_val_to_name[i - 1]);
+				if (!catdatum)
+					return -EINVAL;
+				rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
+				if (rc)
+					return rc;
+			}
+		}
+		ebitmap_destroy(&c->range.level[l].cat);
+		c->range.level[l].cat = bitmap;
+	}
+
+	return 0;
+}
+
+int mls_compute_sid(struct context *scontext,
+		    struct context *tcontext,
+		    u16 tclass,
+		    u32 specified,
+		    struct context *newcontext)
+{
+	if (!selinux_mls_enabled)
+		return 0;
+
+	switch (specified) {
+	case AVTAB_TRANSITION:
+		if (tclass == SECCLASS_PROCESS) {
+			struct range_trans *rangetr;
+			/* Look for a range transition rule. */
+			for (rangetr = policydb.range_tr; rangetr;
+			     rangetr = rangetr->next) {
+				if (rangetr->dom == scontext->type &&
+				    rangetr->type == tcontext->type) {
+					/* Set the range from the rule */
+					return mls_range_set(newcontext,
+					                     &rangetr->range);
+				}
+			}
+		}
+		/* Fallthrough */
+	case AVTAB_CHANGE:
+		if (tclass == SECCLASS_PROCESS)
+			/* Use the process MLS attributes. */
+			return mls_copy_context(newcontext, scontext);
+		else
+			/* Use the process effective MLS attributes. */
+			return mls_scopy_context(newcontext, scontext);
+	case AVTAB_MEMBER:
+		/* Only polyinstantiate the MLS attributes if
+		   the type is being polyinstantiated */
+		if (newcontext->type != tcontext->type) {
+			/* Use the process effective MLS attributes. */
+			return mls_scopy_context(newcontext, scontext);
+		} else {
+			/* Use the related object MLS attributes. */
+			return mls_copy_context(newcontext, tcontext);
+		}
+	default:
+		return -EINVAL;
+	}
+	return -EINVAL;
+}
+