Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b07e3c3a authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* 'for-2.6.20' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/selinux-2.6:
  SELinux: validate kernel object classes and permissions
  SELinux: ensure keys constant in hashtab_search
  SELinux: export object class and permission definitions
  SELinux: remove current object class and permission validation mechanism
parents 5f56bbdf b94c7e67
Loading
Loading
Loading
Loading
+11 −12
Original line number Diff line number Diff line
@@ -32,12 +32,7 @@
#include "avc.h"
#include "avc_ss.h"

static const struct av_perm_to_string
{
  u16 tclass;
  u32 value;
  const char *name;
} av_perm_to_string[] = {
static const struct av_perm_to_string av_perm_to_string[] = {
#define S_(c, v, s) { c, v, s },
#include "av_perm_to_string.h"
#undef S_
@@ -57,17 +52,21 @@ static const char *class_to_string[] = {
#undef TE_
#undef S_

static const struct av_inherit
{
    u16 tclass;
    const char **common_pts;
    u32 common_base;
} av_inherit[] = {
static const struct av_inherit av_inherit[] = {
#define S_(c, i, b) { c, common_##i##_perm_to_string, b },
#include "av_inherit.h"
#undef S_
};

const struct selinux_class_perm selinux_class_perm = {
	av_perm_to_string,
	ARRAY_SIZE(av_perm_to_string),
	class_to_string,
	ARRAY_SIZE(class_to_string),
	av_inherit,
	ARRAY_SIZE(av_inherit)
};

#define AVC_CACHE_SLOTS			512
#define AVC_DEF_CACHE_THRESHOLD		512
#define AVC_CACHE_RECLAIM		16
+24 −0
Original line number Diff line number Diff line
@@ -10,5 +10,29 @@

int avc_ss_reset(u32 seqno);

struct av_perm_to_string
{
	u16 tclass;
	u32 value;
	const char *name;
};

struct av_inherit
{
	u16 tclass;
	const char **common_pts;
	u32 common_base;
};

struct selinux_class_perm
{
	const struct av_perm_to_string *av_perm_to_string;
	u32 av_pts_len;
	const char **class_to_string;
	u32 cts_len;
	const struct av_inherit *av_inherit;
	u32 av_inherit_len;
};

#endif /* _SELINUX_AVC_SS_H_ */
+3 −3
Original line number Diff line number Diff line
@@ -8,8 +8,8 @@
#include <linux/errno.h>
#include "hashtab.h"

struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key),
                               int (*keycmp)(struct hashtab *h, void *key1, void *key2),
struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key),
                               int (*keycmp)(struct hashtab *h, const void *key1, const void *key2),
                               u32 size)
{
	struct hashtab *p;
@@ -71,7 +71,7 @@ int hashtab_insert(struct hashtab *h, void *key, void *datum)
	return 0;
}

void *hashtab_search(struct hashtab *h, void *key)
void *hashtab_search(struct hashtab *h, const void *key)
{
	u32 hvalue;
	struct hashtab_node *cur;
+5 −5
Original line number Diff line number Diff line
@@ -22,9 +22,9 @@ struct hashtab {
	struct hashtab_node **htable;	/* hash table */
	u32 size;			/* number of slots in hash table */
	u32 nel;			/* number of elements in hash table */
	u32 (*hash_value)(struct hashtab *h, void *key);
	u32 (*hash_value)(struct hashtab *h, const void *key);
					/* hash function */
	int (*keycmp)(struct hashtab *h, void *key1, void *key2);
	int (*keycmp)(struct hashtab *h, const void *key1, const void *key2);
					/* key comparison function */
};

@@ -39,8 +39,8 @@ struct hashtab_info {
 * Returns NULL if insufficent space is available or
 * the new hash table otherwise.
 */
struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key),
                               int (*keycmp)(struct hashtab *h, void *key1, void *key2),
struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key),
                               int (*keycmp)(struct hashtab *h, const void *key1, const void *key2),
                               u32 size);

/*
@@ -59,7 +59,7 @@ int hashtab_insert(struct hashtab *h, void *k, void *d);
 * Returns NULL if no entry has the specified key or
 * the datum of the entry otherwise.
 */
void *hashtab_search(struct hashtab *h, void *k);
void *hashtab_search(struct hashtab *h, const void *k);

/*
 * Destroys the specified hash table.
+124 −79
Original line number Diff line number Diff line
@@ -17,9 +17,13 @@
 *
 *      Added support for NetLabel
 *
 * Updated: Chad Sellers <csellers@tresys.com>
 *
 *  Added validation of kernel classes and permissions
 *
 * Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
 * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
 * Copyright (C) 2003 - 2004 Tresys Technology, LLC
 * Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC
 * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
 *	This program is free software; you can redistribute it and/or modify
 *  	it under the terms of the GNU General Public License as published by
@@ -53,6 +57,11 @@
extern void selnl_notify_policyload(u32 seqno);
unsigned int policydb_loaded_version;

/*
 * This is declared in avc.c
 */
extern const struct selinux_class_perm selinux_class_perm;

static DEFINE_RWLOCK(policy_rwlock);
#define POLICY_RDLOCK read_lock(&policy_rwlock)
#define POLICY_WRLOCK write_lock_irq(&policy_rwlock)
@@ -1019,86 +1028,112 @@ int security_change_sid(u32 ssid,
}

/*
 * Verify that each permission that is defined under the
 * existing policy is still defined with the same value
 * in the new policy.
 * Verify that each kernel class that is defined in the
 * policy is correct
 */
static int validate_perm(void *key, void *datum, void *p)
static int validate_classes(struct policydb *p)
{
	struct hashtab *h;
	struct perm_datum *perdatum, *perdatum2;
	int rc = 0;


	h = p;
	perdatum = datum;

	perdatum2 = hashtab_search(h, key);
	if (!perdatum2) {
		printk(KERN_ERR "security:  permission %s disappeared",
		       (char *)key);
		rc = -ENOENT;
		goto out;
	int i, j;
	struct class_datum *cladatum;
	struct perm_datum *perdatum;
	u32 nprim, tmp, common_pts_len, perm_val, pol_val;
	u16 class_val;
	const struct selinux_class_perm *kdefs = &selinux_class_perm;
	const char *def_class, *def_perm, *pol_class;
	struct symtab *perms;

	for (i = 1; i < kdefs->cts_len; i++) {
		def_class = kdefs->class_to_string[i];
		if (i > p->p_classes.nprim) {
			printk(KERN_INFO
			       "security:  class %s not defined in policy\n",
			       def_class);
			continue;
		}
	if (perdatum->value != perdatum2->value) {
		printk(KERN_ERR "security:  the value of permission %s changed",
		       (char *)key);
		rc = -EINVAL;
		pol_class = p->p_class_val_to_name[i-1];
		if (strcmp(pol_class, def_class)) {
			printk(KERN_ERR
			       "security:  class %d is incorrect, found %s but should be %s\n",
			       i, pol_class, def_class);
			return -EINVAL;
		}
out:
	return rc;
	}

/*
 * Verify that each class that is defined under the
 * existing policy is still defined with the same
 * attributes in the new policy.
 */
static int validate_class(void *key, void *datum, void *p)
{
	struct policydb *newp;
	struct class_datum *cladatum, *cladatum2;
	int rc;

	newp = p;
	cladatum = datum;

	cladatum2 = hashtab_search(newp->p_classes.table, key);
	if (!cladatum2) {
		printk(KERN_ERR "security:  class %s disappeared\n",
		       (char *)key);
		rc = -ENOENT;
		goto out;
	for (i = 0; i < kdefs->av_pts_len; i++) {
		class_val = kdefs->av_perm_to_string[i].tclass;
		perm_val = kdefs->av_perm_to_string[i].value;
		def_perm = kdefs->av_perm_to_string[i].name;
		if (class_val > p->p_classes.nprim)
			continue;
		pol_class = p->p_class_val_to_name[class_val-1];
		cladatum = hashtab_search(p->p_classes.table, pol_class);
		BUG_ON(!cladatum);
		perms = &cladatum->permissions;
		nprim = 1 << (perms->nprim - 1);
		if (perm_val > nprim) {
			printk(KERN_INFO
			       "security:  permission %s in class %s not defined in policy\n",
			       def_perm, pol_class);
			continue;
		}
	if (cladatum->value != cladatum2->value) {
		printk(KERN_ERR "security:  the value of class %s changed\n",
		       (char *)key);
		rc = -EINVAL;
		goto out;
		perdatum = hashtab_search(perms->table, def_perm);
		if (perdatum == NULL) {
			printk(KERN_ERR
			       "security:  permission %s in class %s not found in policy\n",
			       def_perm, pol_class);
			return -EINVAL;
		}
	if ((cladatum->comdatum && !cladatum2->comdatum) ||
	    (!cladatum->comdatum && cladatum2->comdatum)) {
		printk(KERN_ERR "security:  the inherits clause for the access "
		       "vector definition for class %s changed\n", (char *)key);
		rc = -EINVAL;
		goto out;
		pol_val = 1 << (perdatum->value - 1);
		if (pol_val != perm_val) {
			printk(KERN_ERR
			       "security:  permission %s in class %s has incorrect value\n",
			       def_perm, pol_class);
			return -EINVAL;
		}
	if (cladatum->comdatum) {
		rc = hashtab_map(cladatum->comdatum->permissions.table, validate_perm,
		                 cladatum2->comdatum->permissions.table);
		if (rc) {
			printk(" in the access vector definition for class "
			       "%s\n", (char *)key);
			goto out;
	}
	for (i = 0; i < kdefs->av_inherit_len; i++) {
		class_val = kdefs->av_inherit[i].tclass;
		if (class_val > p->p_classes.nprim)
			continue;
		pol_class = p->p_class_val_to_name[class_val-1];
		cladatum = hashtab_search(p->p_classes.table, pol_class);
		BUG_ON(!cladatum);
		if (!cladatum->comdatum) {
			printk(KERN_ERR
			       "security:  class %s should have an inherits clause but does not\n",
			       pol_class);
			return -EINVAL;
		}
	rc = hashtab_map(cladatum->permissions.table, validate_perm,
	                 cladatum2->permissions.table);
	if (rc)
		printk(" in access vector definition for class %s\n",
		       (char *)key);
out:
	return rc;
		tmp = kdefs->av_inherit[i].common_base;
		common_pts_len = 0;
		while (!(tmp & 0x01)) {
			common_pts_len++;
			tmp >>= 1;
		}
		perms = &cladatum->comdatum->permissions;
		for (j = 0; j < common_pts_len; j++) {
			def_perm = kdefs->av_inherit[i].common_pts[j];
			if (j >= perms->nprim) {
				printk(KERN_INFO
				       "security:  permission %s in class %s not defined in policy\n",
				       def_perm, pol_class);
				continue;
			}
			perdatum = hashtab_search(perms->table, def_perm);
			if (perdatum == NULL) {
				printk(KERN_ERR
				       "security:  permission %s in class %s not found in policy\n",
				       def_perm, pol_class);
				return -EINVAL;
			}
			if (perdatum->value != j + 1) {
				printk(KERN_ERR
				       "security:  permission %s in class %s has incorrect value\n",
				       def_perm, pol_class);
				return -EINVAL;
			}
		}
	}
	return 0;
}

/* Clone the SID into the new SID table. */
@@ -1243,6 +1278,16 @@ int security_load_policy(void *data, size_t len)
			avtab_cache_destroy();
			return -EINVAL;
		}
		/* Verify that the kernel defined classes are correct. */
		if (validate_classes(&policydb)) {
			printk(KERN_ERR
			       "security:  the definition of a class is incorrect\n");
			LOAD_UNLOCK;
			sidtab_destroy(&sidtab);
			policydb_destroy(&policydb);
			avtab_cache_destroy();
			return -EINVAL;
		}
		policydb_loaded_version = policydb.policyvers;
		ss_initialized = 1;
		seqno = ++latest_granting;
@@ -1265,10 +1310,10 @@ int security_load_policy(void *data, size_t len)

	sidtab_init(&newsidtab);

	/* Verify that the existing classes did not change. */
	if (hashtab_map(policydb.p_classes.table, validate_class, &newpolicydb)) {
		printk(KERN_ERR "security:  the definition of an existing "
		       "class changed\n");
	/* Verify that the kernel defined classes are correct. */
	if (validate_classes(&newpolicydb)) {
		printk(KERN_ERR
		       "security:  the definition of a class is incorrect\n");
		rc = -EINVAL;
		goto err;
	}
Loading