[BACK]Return to mls.c CVS log [TXT][DIR] Up to [Development] / linux-2.6-xfs / security / selinux / ss

File: [Development] / linux-2.6-xfs / security / selinux / ss / mls.c (download)

Revision 1.5, Fri Mar 4 14:41:21 2005 UTC (12 years, 7 months ago) by nathans.longdrop.melbourne.sgi.com
Branch: MAIN
Changes since 1.4: +43 -46 lines

Merge up to 2.6.11
Merge of 2.6.x-xfs-melb:linux:21721a by kenmcd.

/*
 * Implementation of the multi-level security (MLS) policy.
 *
 * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
 */
#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"

/*
 * Remove any permissions from `allowed' that are
 * denied by the MLS policy.
 */
void mls_compute_av(struct context *scontext,
		    struct context *tcontext,
		    struct class_datum *tclass,
		    u32 *allowed)
{
	unsigned int rel[2];
	int l;

	for (l = 0; l < 2; l++)
		rel[l] = mls_level_relation(scontext->range.level[l],
					    tcontext->range.level[l]);

	if (rel[1] != MLS_RELATION_EQ) {
		if (rel[1] != MLS_RELATION_DOM &&
		    !ebitmap_get_bit(&policydb.trustedreaders, scontext->type - 1) &&
		    !ebitmap_get_bit(&policydb.trustedobjects, tcontext->type - 1)) {
			/* read(s,t) = (s.high >= t.high) = False */
			*allowed = (*allowed) & ~(tclass->mlsperms.read);
		}
		if (rel[1] != MLS_RELATION_DOMBY &&
		    !ebitmap_get_bit(&policydb.trustedreaders, tcontext->type - 1) &&
		    !ebitmap_get_bit(&policydb.trustedobjects, scontext->type - 1)) {
			/* readby(s,t) = read(t,s) = False */
			*allowed = (*allowed) & ~(tclass->mlsperms.readby);
		}
	}
	if (((rel[0] != MLS_RELATION_DOMBY && rel[0] != MLS_RELATION_EQ) ||
	    ((!mls_level_eq(tcontext->range.level[0],
			    tcontext->range.level[1])) &&
	     (rel[1] != MLS_RELATION_DOM && rel[1] != MLS_RELATION_EQ))) &&
	    !ebitmap_get_bit(&policydb.trustedwriters, scontext->type - 1) &&
	    !ebitmap_get_bit(&policydb.trustedobjects, tcontext->type - 1)) {
		/*
		 * write(s,t) = ((s.low <= t.low = t.high) or (s.low
		 * <= t.low <= t.high <= s.high)) = False
		 */
		*allowed = (*allowed) & ~(tclass->mlsperms.write);
	}

	if (((rel[0] != MLS_RELATION_DOM && rel[0] != MLS_RELATION_EQ) ||
	    ((!mls_level_eq(scontext->range.level[0],
			    scontext->range.level[1])) &&
	     (rel[1] != MLS_RELATION_DOMBY && rel[1] != MLS_RELATION_EQ))) &&
	    !ebitmap_get_bit(&policydb.trustedwriters, tcontext->type - 1) &&
	    !ebitmap_get_bit(&policydb.trustedobjects, scontext->type - 1)) {
		/* writeby(s,t) = write(t,s) = False */
		*allowed = (*allowed) & ~(tclass->mlsperms.writeby);
	}
}

/*
 * 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;


	len = 0;
	for (l = 0; l < 2; l++) {
		len += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]) + 1;

		for (i = 1; i <= ebitmap_length(&context->range.level[l].cat); i++)
			if (ebitmap_get_bit(&context->range.level[l].cat, i - 1))
				len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1;

		if (mls_level_relation(context->range.level[0], context->range.level[1])
				== MLS_RELATION_EQ)
			break;
	}

	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.
 */
int mls_sid_to_context(struct context *context,
		       char **scontext)
{
	char *scontextp;
	int i, l;

	scontextp = *scontext;

	for (l = 0; l < 2; l++) {
		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]);
		*scontextp = ':';
		scontextp++;
		for (i = 1; i <= ebitmap_length(&context->range.level[l].cat); i++)
			if (ebitmap_get_bit(&context->range.level[l].cat, i - 1)) {
				strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]);
				scontextp += strlen(policydb.p_cat_val_to_name[i - 1]);
				*scontextp = ',';
				scontextp++;
			}
		if (mls_level_relation(context->range.level[0], context->range.level[1])
				!= MLS_RELATION_EQ) {
			scontextp--;
			sprintf(scontextp, "-");
			scontextp++;

		} else {
			break;
		}
	}

	*scontext = scontextp;
	return 0;
}

/*
 * 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)
{
	unsigned int relation;
	struct level_datum *levdatum;
	struct user_datum *usrdatum;
	struct mls_range_list *rnode;
	int i, l;

	/*
	 * 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)
	 */
	relation = mls_level_relation(c->range.level[1],
				      c->range.level[0]);
	if (!(relation & (MLS_RELATION_DOM | MLS_RELATION_EQ)))
		/* 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];
	for (rnode = usrdatum->ranges; rnode; rnode = rnode->next) {
		if (mls_range_contains(rnode->range, c->range))
			break;
	}
	if (!rnode)
		/* user may not be associated with range */
		return 0;

	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;
	struct level_datum *levdatum;
	struct cat_datum *catdatum;
	int l, rc = -EINVAL;

	if (!oldc) {
		/* No MLS component to the security context.  Try
		   to use a default 'unclassified' value. */
		levdatum = hashtab_search(policydb.p_levels.table,
		                          "unclassified");
		if (!levdatum)
			goto out;
		context->range.level[0].sens = levdatum->level->sens;
		context->range.level[1].sens = context->range.level[0].sens;
		rc = 0;
		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)
			goto out;

		context->range.level[l].sens = levdatum->level->sens;

		if (delim == ':') {
			/* Extract low category set. */
			while (1) {
				scontextp = p;
				while (*p && *p != ',' && *p != '-')
					p++;
				delim = *p;
				if (delim != 0)
					*p++ = 0;

				catdatum = hashtab_search(policydb.p_cats.table,
				                          scontextp);
				if (!catdatum)
					goto out;

				rc = ebitmap_set_bit(&context->range.level[l].cat,
				                     catdatum->value - 1, 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;
}

/*
 * 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;

	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)
{
	switch (specified) {
	case AVTAB_TRANSITION:
	case AVTAB_CHANGE:
		/* Use the process MLS attributes. */
		return mls_copy_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 MLS attributes. */
			return mls_copy_context(newcontext, scontext);
		} else {
			/* Use the related object MLS attributes. */
			return mls_copy_context(newcontext, tcontext);
		}
	default:
		return -EINVAL;
	}
	return -EINVAL;
}

void mls_user_destroy(struct user_datum *usrdatum)
{
	struct mls_range_list *rnode, *rtmp;
	rnode = usrdatum->ranges;
	while (rnode) {
		rtmp = rnode;
		rnode = rnode->next;
		ebitmap_destroy(&rtmp->range.level[0].cat);
		ebitmap_destroy(&rtmp->range.level[1].cat);
		kfree(rtmp);
	}
}

int mls_read_perm(struct perm_datum *perdatum, void *fp)
{
	u32 buf[1];
	int rc;

	rc = next_entry(buf, fp, sizeof buf);
	if (rc < 0)
		return -EINVAL;
	perdatum->base_perms = le32_to_cpu(buf[0]);
	return 0;
}

/*
 * Read a MLS level structure from a policydb binary
 * representation file.
 */
struct mls_level *mls_read_level(void *fp)
{
	struct mls_level *l;
	u32 buf[1];
	int rc;

	l = kmalloc(sizeof(*l), GFP_ATOMIC);
	if (!l) {
		printk(KERN_ERR "security: mls: out of memory\n");
		return NULL;
	}
	memset(l, 0, sizeof(*l));

	rc = next_entry(buf, fp, sizeof buf);
	if (rc < 0) {
		printk(KERN_ERR "security: mls: truncated level\n");
		goto bad;
	}
	l->sens = cpu_to_le32(buf[0]);

	if (ebitmap_read(&l->cat, fp)) {
		printk(KERN_ERR "security: mls:  error reading level "
		       "categories\n");
		goto bad;
	}
	return l;

bad:
	kfree(l);
	return NULL;
}


/*
 * Read a MLS range structure from a policydb binary
 * representation file.
 */
static int mls_read_range_helper(struct mls_range *r, void *fp)
{
	u32 buf[2], items;
	int rc;

	rc = next_entry(buf, fp, sizeof(u32));
	if (rc < 0)
		goto out;

	items = le32_to_cpu(buf[0]);
	if (items > ARRAY_SIZE(buf)) {
		printk(KERN_ERR "security: mls:  range overflow\n");
		rc = -EINVAL;
		goto out;
	}
	rc = next_entry(buf, fp, sizeof(u32) * items);
	if (rc < 0) {
		printk(KERN_ERR "security: mls:  truncated range\n");
		goto out;
	}
	r->level[0].sens = le32_to_cpu(buf[0]);
	if (items > 1) {
		r->level[1].sens = le32_to_cpu(buf[1]);
	} else {
		r->level[1].sens = r->level[0].sens;
	}

	rc = ebitmap_read(&r->level[0].cat, fp);
	if (rc) {
		printk(KERN_ERR "security: mls:  error reading low "
		       "categories\n");
		goto out;
	}
	if (items > 1) {
		rc = ebitmap_read(&r->level[1].cat, fp);
		if (rc) {
			printk(KERN_ERR "security: mls:  error reading high "
			       "categories\n");
			goto bad_high;
		}
	} else {
		rc = ebitmap_cpy(&r->level[1].cat, &r->level[0].cat);
		if (rc) {
			printk(KERN_ERR "security: mls:  out of memory\n");
			goto bad_high;
		}
	}

	rc = 0;
out:
	return rc;
bad_high:
	ebitmap_destroy(&r->level[0].cat);
	goto out;
}

int mls_read_range(struct context *c, void *fp)
{
	return mls_read_range_helper(&c->range, fp);
}


/*
 * Read a MLS perms structure from a policydb binary
 * representation file.
 */
int mls_read_class(struct class_datum *cladatum, void *fp)
{
	struct mls_perms *p = &cladatum->mlsperms;
	u32 buf[4];
	int rc;

	rc = next_entry(buf, fp, sizeof buf);
	if (rc < 0) {
		printk(KERN_ERR "security: mls:  truncated mls permissions\n");
		return -EINVAL;
	}
	p->read = le32_to_cpu(buf[0]);
	p->readby = le32_to_cpu(buf[1]);
	p->write = le32_to_cpu(buf[2]);
	p->writeby = le32_to_cpu(buf[3]);
	return 0;
}

int mls_read_user(struct user_datum *usrdatum, void *fp)
{
	struct mls_range_list *r, *l;
	int rc;
	u32 nel, i;
	u32 buf[1];

	rc = next_entry(buf, fp, sizeof buf);
	if (rc < 0)
		goto out;
	nel = le32_to_cpu(buf[0]);
	l = NULL;
	for (i = 0; i < nel; i++) {
		r = kmalloc(sizeof(*r), GFP_ATOMIC);
		if (!r) {
			rc = -ENOMEM;
			goto out;
		}
		memset(r, 0, sizeof(*r));

		rc = mls_read_range_helper(&r->range, fp);
		if (rc) {
			kfree(r);
			goto out;
		}

		if (l)
			l->next = r;
		else
			usrdatum->ranges = r;
		l = r;
	}
out:
	return rc;
}

int mls_read_nlevels(struct policydb *p, void *fp)
{
	u32 buf[1];
	int rc;

	rc = next_entry(buf, fp, sizeof buf);
	if (rc < 0)
		return -EINVAL;
	p->nlevels = le32_to_cpu(buf[0]);
	return 0;
}

int mls_read_trusted(struct policydb *p, void *fp)
{
	int rc = 0;

	rc = ebitmap_read(&p->trustedreaders, fp);
	if (rc)
		goto out;
	rc = ebitmap_read(&p->trustedwriters, fp);
	if (rc)
		goto bad;
	rc = ebitmap_read(&p->trustedobjects, fp);
	if (rc)
		goto bad2;
out:
	return rc;
bad2:
	ebitmap_destroy(&p->trustedwriters);
bad:
	ebitmap_destroy(&p->trustedreaders);
	goto out;
}

int sens_index(void *key, void *datum, void *datap)
{
	struct policydb *p;
	struct level_datum *levdatum;


	levdatum = datum;
	p = datap;

	if (!levdatum->isalias)
		p->p_sens_val_to_name[levdatum->level->sens - 1] = key;

	return 0;
}

int cat_index(void *key, void *datum, void *datap)
{
	struct policydb *p;
	struct cat_datum *catdatum;


	catdatum = datum;
	p = datap;


	if (!catdatum->isalias)
		p->p_cat_val_to_name[catdatum->value - 1] = key;

	return 0;
}

int sens_destroy(void *key, void *datum, void *p)
{
	struct level_datum *levdatum;

	kfree(key);
	levdatum = datum;
	if (!levdatum->isalias) {
		ebitmap_destroy(&levdatum->level->cat);
		kfree(levdatum->level);
	}
	kfree(datum);
	return 0;
}

int cat_destroy(void *key, void *datum, void *p)
{
	kfree(key);
	kfree(datum);
	return 0;
}

int sens_read(struct policydb *p, struct hashtab *h, void *fp)
{
	char *key = NULL;
	struct level_datum *levdatum;
	int rc;
	u32 buf[2], len;

	levdatum = kmalloc(sizeof(*levdatum), GFP_ATOMIC);
	if (!levdatum) {
		rc = -ENOMEM;
		goto out;
	}
	memset(levdatum, 0, sizeof(*levdatum));

	rc = next_entry(buf, fp, sizeof buf);
	if (rc < 0)
		goto bad;

	len = le32_to_cpu(buf[0]);
	levdatum->isalias = le32_to_cpu(buf[1]);

	key = kmalloc(len + 1,GFP_ATOMIC);
	if (!key) {
		rc = -ENOMEM;
		goto bad;
	}
	rc = next_entry(key, fp, len);
	if (rc < 0)
		goto bad;
	key[len] = 0;

	levdatum->level = mls_read_level(fp);
	if (!levdatum->level) {
		rc = -EINVAL;
		goto bad;
	}

	rc = hashtab_insert(h, key, levdatum);
	if (rc)
		goto bad;
out:
	return rc;
bad:
	sens_destroy(key, levdatum, NULL);
	goto out;
}


int cat_read(struct policydb *p, struct hashtab *h, void *fp)
{
	char *key = NULL;
	struct cat_datum *catdatum;
	int rc;
	u32 buf[3], len;

	catdatum = kmalloc(sizeof(*catdatum), GFP_ATOMIC);
	if (!catdatum) {
		rc = -ENOMEM;
		goto out;
	}
	memset(catdatum, 0, sizeof(*catdatum));

	rc = next_entry(buf, fp, sizeof buf);
	if (rc < 0)
		goto bad;

	len = le32_to_cpu(buf[0]);
	catdatum->value = le32_to_cpu(buf[1]);
	catdatum->isalias = le32_to_cpu(buf[2]);

	key = kmalloc(len + 1,GFP_ATOMIC);
	if (!key) {
		rc = -ENOMEM;
		goto bad;
	}
	rc = next_entry(key, fp, len);
	if (rc < 0)
		goto bad;
	key[len] = 0;

	rc = hashtab_insert(h, key, catdatum);
	if (rc)
		goto bad;
out:
	return rc;

bad:
	cat_destroy(key, catdatum, NULL);
	goto out;
}