From c6d3aaa4e35c71a32a86ececacd4eea7ecfc316c Mon Sep 17 00:00:00 2001
From: Stephen Smalley <sds@tycho.nsa.gov>
Date: Wed, 30 Sep 2009 13:37:50 -0400
Subject: selinux: dynamic class/perm discovery

Modify SELinux to dynamically discover class and permission values
upon policy load, based on the dynamic object class/perm discovery
logic from libselinux.  A mapping is created between kernel-private
class and permission indices used outside the security server and the
policy values used within the security server.

The mappings are only applied upon kernel-internal computations;
similar mappings for the private indices of userspace object managers
is handled on a per-object manager basis by the userspace AVC.  The
interfaces for compute_av and transition_sid are split for kernel
vs. userspace; the userspace functions are distinguished by a _user
suffix.

The kernel-private class indices are no longer tied to the policy
values and thus do not need to skip indices for userspace classes;
thus the kernel class index values are compressed.  The flask.h
definitions were regenerated by deleting the userspace classes from
refpolicy's definitions and then regenerating the headers.  Going
forward, we can just maintain the flask.h, av_permissions.h, and
classmap.h definitions separately from policy as they are no longer
tied to the policy values.  The next patch introduces a utility to
automate generation of flask.h and av_permissions.h from the
classmap.h definitions.

The older kernel class and permission string tables are removed and
replaced by a single security class mapping table that is walked at
policy load to generate the mapping.  The old kernel class validation
logic is completely replaced by the mapping logic.

The handle unknown logic is reworked.  reject_unknown=1 is handled
when the mappings are computed at policy load time, similar to the old
handling by the class validation logic.  allow_unknown=1 is handled
when computing and mapping decisions - if the permission was not able
to be mapped (i.e. undefined, mapped to zero), then it is
automatically added to the allowed vector.  If the class was not able
to be mapped (i.e. undefined, mapped to zero), then all permissions
are allowed for it if allow_unknown=1.

avc_audit leverages the new security class mapping table to lookup the
class and permission names from the kernel-private indices.

The mdp program is updated to use the new table when generating the
class definitions and allow rules for a minimal boot policy for the
kernel.  It should be noted that this policy will not include any
userspace classes, nor will its policy index values for the kernel
classes correspond with the ones in refpolicy (they will instead match
the kernel-private indices).

Signed-off-by:  Stephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: James Morris <jmorris@namei.org>
---
 security/selinux/ss/services.c | 540 ++++++++++++++++++++++-------------------
 1 file changed, 287 insertions(+), 253 deletions(-)

(limited to 'security/selinux/ss/services.c')

diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index ff17820d35e..e19baa81fde 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -70,11 +70,6 @@ unsigned int policydb_loaded_version;
 int selinux_policycap_netpeer;
 int selinux_policycap_openperm;
 
-/*
- * This is declared in avc.c
- */
-extern const struct selinux_class_perm selinux_class_perm;
-
 static DEFINE_RWLOCK(policy_rwlock);
 
 static struct sidtab sidtab;
@@ -98,6 +93,158 @@ static int context_struct_compute_av(struct context *scontext,
 				     u16 tclass,
 				     u32 requested,
 				     struct av_decision *avd);
+
+struct selinux_mapping {
+	u16 value; /* policy value */
+	unsigned num_perms;
+	u32 perms[sizeof(u32) * 8];
+};
+
+static struct selinux_mapping *current_mapping;
+static u16 current_mapping_size;
+
+static int selinux_set_mapping(struct policydb *pol,
+			       struct security_class_mapping *map,
+			       struct selinux_mapping **out_map_p,
+			       u16 *out_map_size)
+{
+	struct selinux_mapping *out_map = NULL;
+	size_t size = sizeof(struct selinux_mapping);
+	u16 i, j;
+	unsigned k;
+	bool print_unknown_handle = false;
+
+	/* Find number of classes in the input mapping */
+	if (!map)
+		return -EINVAL;
+	i = 0;
+	while (map[i].name)
+		i++;
+
+	/* Allocate space for the class records, plus one for class zero */
+	out_map = kcalloc(++i, size, GFP_ATOMIC);
+	if (!out_map)
+		return -ENOMEM;
+
+	/* Store the raw class and permission values */
+	j = 0;
+	while (map[j].name) {
+		struct security_class_mapping *p_in = map + (j++);
+		struct selinux_mapping *p_out = out_map + j;
+
+		/* An empty class string skips ahead */
+		if (!strcmp(p_in->name, "")) {
+			p_out->num_perms = 0;
+			continue;
+		}
+
+		p_out->value = string_to_security_class(pol, p_in->name);
+		if (!p_out->value) {
+			printk(KERN_INFO
+			       "SELinux:  Class %s not defined in policy.\n",
+			       p_in->name);
+			if (pol->reject_unknown)
+				goto err;
+			p_out->num_perms = 0;
+			print_unknown_handle = true;
+			continue;
+		}
+
+		k = 0;
+		while (p_in->perms && p_in->perms[k]) {
+			/* An empty permission string skips ahead */
+			if (!*p_in->perms[k]) {
+				k++;
+				continue;
+			}
+			p_out->perms[k] = string_to_av_perm(pol, p_out->value,
+							    p_in->perms[k]);
+			if (!p_out->perms[k]) {
+				printk(KERN_INFO
+				       "SELinux:  Permission %s in class %s not defined in policy.\n",
+				       p_in->perms[k], p_in->name);
+				if (pol->reject_unknown)
+					goto err;
+				print_unknown_handle = true;
+			}
+
+			k++;
+		}
+		p_out->num_perms = k;
+	}
+
+	if (print_unknown_handle)
+		printk(KERN_INFO "SELinux: the above unknown classes and permissions will be %s\n",
+		       pol->allow_unknown ? "allowed" : "denied");
+
+	*out_map_p = out_map;
+	*out_map_size = i;
+	return 0;
+err:
+	kfree(out_map);
+	return -EINVAL;
+}
+
+/*
+ * Get real, policy values from mapped values
+ */
+
+static u16 unmap_class(u16 tclass)
+{
+	if (tclass < current_mapping_size)
+		return current_mapping[tclass].value;
+
+	return tclass;
+}
+
+static u32 unmap_perm(u16 tclass, u32 tperm)
+{
+	if (tclass < current_mapping_size) {
+		unsigned i;
+		u32 kperm = 0;
+
+		for (i = 0; i < current_mapping[tclass].num_perms; i++)
+			if (tperm & (1<<i)) {
+				kperm |= current_mapping[tclass].perms[i];
+				tperm &= ~(1<<i);
+			}
+		return kperm;
+	}
+
+	return tperm;
+}
+
+static void map_decision(u16 tclass, struct av_decision *avd,
+			 int allow_unknown)
+{
+	if (tclass < current_mapping_size) {
+		unsigned i, n = current_mapping[tclass].num_perms;
+		u32 result;
+
+		for (i = 0, result = 0; i < n; i++) {
+			if (avd->allowed & current_mapping[tclass].perms[i])
+				result |= 1<<i;
+			if (allow_unknown && !current_mapping[tclass].perms[i])
+				result |= 1<<i;
+		}
+		avd->allowed = result;
+
+		for (i = 0, result = 0; i < n; i++)
+			if (avd->auditallow & current_mapping[tclass].perms[i])
+				result |= 1<<i;
+		avd->auditallow = result;
+
+		for (i = 0, result = 0; i < n; i++) {
+			if (avd->auditdeny & current_mapping[tclass].perms[i])
+				result |= 1<<i;
+			if (!allow_unknown && !current_mapping[tclass].perms[i])
+				result |= 1<<i;
+		}
+		avd->auditdeny = result;
+	}
+}
+
+
 /*
  * Return the boolean value of a constraint expression
  * when it is applied to the specified source and target
@@ -467,7 +614,6 @@ static int context_struct_compute_av(struct context *scontext,
 	struct class_datum *tclass_datum;
 	struct ebitmap *sattr, *tattr;
 	struct ebitmap_node *snode, *tnode;
-	const struct selinux_class_perm *kdefs = &selinux_class_perm;
 	unsigned int i, j;
 
 	/*
@@ -477,9 +623,9 @@ static int context_struct_compute_av(struct context *scontext,
 	 * to remain in the correct class.
 	 */
 	if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS)
-		if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET &&
-		    tclass <= SECCLASS_NETLINK_DNRT_SOCKET)
-			tclass = SECCLASS_NETLINK_SOCKET;
+		if (tclass >= unmap_class(SECCLASS_NETLINK_ROUTE_SOCKET) &&
+		    tclass <= unmap_class(SECCLASS_NETLINK_DNRT_SOCKET))
+			tclass = unmap_class(SECCLASS_NETLINK_SOCKET);
 
 	/*
 	 * Initialize the access vectors to the default values.
@@ -490,33 +636,11 @@ static int context_struct_compute_av(struct context *scontext,
 	avd->seqno = latest_granting;
 	avd->flags = 0;
 
-	/*
-	 * Check for all the invalid cases.
-	 * - tclass 0
-	 * - tclass > policy and > kernel
-	 * - tclass > policy but is a userspace class
-	 * - tclass > policy but we do not allow unknowns
-	 */
-	if (unlikely(!tclass))
-		goto inval_class;
-	if (unlikely(tclass > policydb.p_classes.nprim))
-		if (tclass > kdefs->cts_len ||
-		    !kdefs->class_to_string[tclass] ||
-		    !policydb.allow_unknown)
-			goto inval_class;
-
-	/*
-	 * Kernel class and we allow unknown so pad the allow decision
-	 * the pad will be all 1 for unknown classes.
-	 */
-	if (tclass <= kdefs->cts_len && policydb.allow_unknown)
-		avd->allowed = policydb.undefined_perms[tclass - 1];
-
-	/*
-	 * Not in policy. Since decision is completed (all 1 or all 0) return.
-	 */
-	if (unlikely(tclass > policydb.p_classes.nprim))
-		return 0;
+	if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) {
+		if (printk_ratelimit())
+			printk(KERN_WARNING "SELinux:  Invalid class %hu\n", tclass);
+		return -EINVAL;
+	}
 
 	tclass_datum = policydb.class_val_to_struct[tclass - 1];
 
@@ -568,8 +692,8 @@ static int context_struct_compute_av(struct context *scontext,
 	 * role is changing, then check the (current_role, new_role)
 	 * pair.
 	 */
-	if (tclass == SECCLASS_PROCESS &&
-	    (avd->allowed & (PROCESS__TRANSITION | PROCESS__DYNTRANSITION)) &&
+	if (tclass == policydb.process_class &&
+	    (avd->allowed & policydb.process_trans_perms) &&
 	    scontext->role != tcontext->role) {
 		for (ra = policydb.role_allow; ra; ra = ra->next) {
 			if (scontext->role == ra->role &&
@@ -577,8 +701,7 @@ static int context_struct_compute_av(struct context *scontext,
 				break;
 		}
 		if (!ra)
-			avd->allowed &= ~(PROCESS__TRANSITION |
-					  PROCESS__DYNTRANSITION);
+			avd->allowed &= ~policydb.process_trans_perms;
 	}
 
 	/*
@@ -590,21 +713,6 @@ static int context_struct_compute_av(struct context *scontext,
 				 tclass, requested, avd);
 
 	return 0;
-
-inval_class:
-	if (!tclass || tclass > kdefs->cts_len ||
-	    !kdefs->class_to_string[tclass]) {
-		if (printk_ratelimit())
-			printk(KERN_ERR "SELinux: %s:  unrecognized class %d\n",
-			       __func__, tclass);
-		return -EINVAL;
-	}
-
-	/*
-	 * Known to the kernel, but not to the policy.
-	 * Handle as a denial (allowed is 0).
-	 */
-	return 0;
 }
 
 static int security_validtrans_handle_fail(struct context *ocontext,
@@ -636,13 +744,14 @@ out:
 }
 
 int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
-				 u16 tclass)
+				 u16 orig_tclass)
 {
 	struct context *ocontext;
 	struct context *ncontext;
 	struct context *tcontext;
 	struct class_datum *tclass_datum;
 	struct constraint_node *constraint;
+	u16 tclass;
 	int rc = 0;
 
 	if (!ss_initialized)
@@ -650,6 +759,8 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
 
 	read_lock(&policy_rwlock);
 
+	tclass = unmap_class(orig_tclass);
+
 	/*
 	 * Remap extended Netlink classes for old policy versions.
 	 * Do this here rather than socket_type_to_security_class()
@@ -657,9 +768,9 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
 	 * to remain in the correct class.
 	 */
 	if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS)
-		if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET &&
-		    tclass <= SECCLASS_NETLINK_DNRT_SOCKET)
-			tclass = SECCLASS_NETLINK_SOCKET;
+		if (tclass >= unmap_class(SECCLASS_NETLINK_ROUTE_SOCKET) &&
+		    tclass <= unmap_class(SECCLASS_NETLINK_DNRT_SOCKET))
+			tclass = unmap_class(SECCLASS_NETLINK_SOCKET);
 
 	if (!tclass || tclass > policydb.p_classes.nprim) {
 		printk(KERN_ERR "SELinux: %s:  unrecognized class %d\n",
@@ -792,6 +903,38 @@ out:
 }
 
 
+static int security_compute_av_core(u32 ssid,
+				    u32 tsid,
+				    u16 tclass,
+				    u32 requested,
+				    struct av_decision *avd)
+{
+	struct context *scontext = NULL, *tcontext = NULL;
+	int rc = 0;
+
+	scontext = sidtab_search(&sidtab, ssid);
+	if (!scontext) {
+		printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+		       __func__, ssid);
+		return -EINVAL;
+	}
+	tcontext = sidtab_search(&sidtab, tsid);
+	if (!tcontext) {
+		printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+		       __func__, tsid);
+		return -EINVAL;
+	}
+
+	rc = context_struct_compute_av(scontext, tcontext, tclass,
+				       requested, avd);
+
+	/* permissive domain? */
+	if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
+		avd->flags |= AVD_FLAGS_PERMISSIVE;
+
+	return rc;
+}
+
 /**
  * security_compute_av - Compute access vector decisions.
  * @ssid: source security identifier
@@ -807,12 +950,45 @@ out:
  */
 int security_compute_av(u32 ssid,
 			u32 tsid,
-			u16 tclass,
-			u32 requested,
+			u16 orig_tclass,
+			u32 orig_requested,
 			struct av_decision *avd)
 {
-	struct context *scontext = NULL, *tcontext = NULL;
-	int rc = 0;
+	u16 tclass;
+	u32 requested;
+	int rc;
+
+	if (!ss_initialized)
+		goto allow;
+
+	read_lock(&policy_rwlock);
+	requested = unmap_perm(orig_tclass, orig_requested);
+	tclass = unmap_class(orig_tclass);
+	if (unlikely(orig_tclass && !tclass)) {
+		if (policydb.allow_unknown)
+			goto allow;
+		return -EINVAL;
+	}
+	rc = security_compute_av_core(ssid, tsid, tclass, requested, avd);
+	map_decision(orig_tclass, avd, policydb.allow_unknown);
+	read_unlock(&policy_rwlock);
+	return rc;
+allow:
+	avd->allowed = 0xffffffff;
+	avd->auditallow = 0;
+	avd->auditdeny = 0xffffffff;
+	avd->seqno = latest_granting;
+	avd->flags = 0;
+	return 0;
+}
+
+int security_compute_av_user(u32 ssid,
+			     u32 tsid,
+			     u16 tclass,
+			     u32 requested,
+			     struct av_decision *avd)
+{
+	int rc;
 
 	if (!ss_initialized) {
 		avd->allowed = 0xffffffff;
@@ -823,29 +999,7 @@ int security_compute_av(u32 ssid,
 	}
 
 	read_lock(&policy_rwlock);
-
-	scontext = sidtab_search(&sidtab, ssid);
-	if (!scontext) {
-		printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
-		       __func__, ssid);
-		rc = -EINVAL;
-		goto out;
-	}
-	tcontext = sidtab_search(&sidtab, tsid);
-	if (!tcontext) {
-		printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
-		       __func__, tsid);
-		rc = -EINVAL;
-		goto out;
-	}
-
-	rc = context_struct_compute_av(scontext, tcontext, tclass,
-				       requested, avd);
-
-	/* permissive domain? */
-	if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
-	    avd->flags |= AVD_FLAGS_PERMISSIVE;
-out:
+	rc = security_compute_av_core(ssid, tsid, tclass, requested, avd);
 	read_unlock(&policy_rwlock);
 	return rc;
 }
@@ -1204,20 +1358,22 @@ out:
 
 static int security_compute_sid(u32 ssid,
 				u32 tsid,
-				u16 tclass,
+				u16 orig_tclass,
 				u32 specified,
-				u32 *out_sid)
+				u32 *out_sid,
+				bool kern)
 {
 	struct context *scontext = NULL, *tcontext = NULL, newcontext;
 	struct role_trans *roletr = NULL;
 	struct avtab_key avkey;
 	struct avtab_datum *avdatum;
 	struct avtab_node *node;
+	u16 tclass;
 	int rc = 0;
 
 	if (!ss_initialized) {
-		switch (tclass) {
-		case SECCLASS_PROCESS:
+		switch (orig_tclass) {
+		case SECCLASS_PROCESS: /* kernel value */
 			*out_sid = ssid;
 			break;
 		default:
@@ -1231,6 +1387,11 @@ static int security_compute_sid(u32 ssid,
 
 	read_lock(&policy_rwlock);
 
+	if (kern)
+		tclass = unmap_class(orig_tclass);
+	else
+		tclass = orig_tclass;
+
 	scontext = sidtab_search(&sidtab, ssid);
 	if (!scontext) {
 		printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
@@ -1260,13 +1421,11 @@ static int security_compute_sid(u32 ssid,
 	}
 
 	/* Set the role and type to default values. */
-	switch (tclass) {
-	case SECCLASS_PROCESS:
+	if (tclass == policydb.process_class) {
 		/* Use the current role and type of process. */
 		newcontext.role = scontext->role;
 		newcontext.type = scontext->type;
-		break;
-	default:
+	} else {
 		/* Use the well-defined object role. */
 		newcontext.role = OBJECT_R_VAL;
 		/* Use the type of the related object. */
@@ -1297,8 +1456,7 @@ static int security_compute_sid(u32 ssid,
 	}
 
 	/* Check for class-specific changes. */
-	switch (tclass) {
-	case SECCLASS_PROCESS:
+	if  (tclass == policydb.process_class) {
 		if (specified & AVTAB_TRANSITION) {
 			/* Look for a role transition rule. */
 			for (roletr = policydb.role_tr; roletr;
@@ -1311,9 +1469,6 @@ static int security_compute_sid(u32 ssid,
 				}
 			}
 		}
-		break;
-	default:
-		break;
 	}
 
 	/* Set the MLS attributes.
@@ -1358,7 +1513,17 @@ int security_transition_sid(u32 ssid,
 			    u16 tclass,
 			    u32 *out_sid)
 {
-	return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, out_sid);
+	return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION,
+				    out_sid, true);
+}
+
+int security_transition_sid_user(u32 ssid,
+				 u32 tsid,
+				 u16 tclass,
+				 u32 *out_sid)
+{
+	return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION,
+				    out_sid, false);
 }
 
 /**
@@ -1379,7 +1544,8 @@ int security_member_sid(u32 ssid,
 			u16 tclass,
 			u32 *out_sid)
 {
-	return security_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, out_sid);
+	return security_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, out_sid,
+				    false);
 }
 
 /**
@@ -1400,144 +1566,8 @@ int security_change_sid(u32 ssid,
 			u16 tclass,
 			u32 *out_sid)
 {
-	return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, out_sid);
-}
-
-/*
- * Verify that each kernel class that is defined in the
- * policy is correct
- */
-static int validate_classes(struct policydb *p)
-{
-	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;
-	bool print_unknown_handle = 0;
-
-	if (p->allow_unknown) {
-		u32 num_classes = kdefs->cts_len;
-		p->undefined_perms = kcalloc(num_classes, sizeof(u32), GFP_KERNEL);
-		if (!p->undefined_perms)
-			return -ENOMEM;
-	}
-
-	for (i = 1; i < kdefs->cts_len; i++) {
-		def_class = kdefs->class_to_string[i];
-		if (!def_class)
-			continue;
-		if (i > p->p_classes.nprim) {
-			printk(KERN_INFO
-			       "SELinux:  class %s not defined in policy\n",
-			       def_class);
-			if (p->reject_unknown)
-				return -EINVAL;
-			if (p->allow_unknown)
-				p->undefined_perms[i-1] = ~0U;
-			print_unknown_handle = 1;
-			continue;
-		}
-		pol_class = p->p_class_val_to_name[i-1];
-		if (strcmp(pol_class, def_class)) {
-			printk(KERN_ERR
-			       "SELinux:  class %d is incorrect, found %s but should be %s\n",
-			       i, pol_class, def_class);
-			return -EINVAL;
-		}
-	}
-	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
-			       "SELinux:  permission %s in class %s not defined in policy\n",
-			       def_perm, pol_class);
-			if (p->reject_unknown)
-				return -EINVAL;
-			if (p->allow_unknown)
-				p->undefined_perms[class_val-1] |= perm_val;
-			print_unknown_handle = 1;
-			continue;
-		}
-		perdatum = hashtab_search(perms->table, def_perm);
-		if (perdatum == NULL) {
-			printk(KERN_ERR
-			       "SELinux:  permission %s in class %s not found in policy, bad policy\n",
-			       def_perm, pol_class);
-			return -EINVAL;
-		}
-		pol_val = 1 << (perdatum->value - 1);
-		if (pol_val != perm_val) {
-			printk(KERN_ERR
-			       "SELinux:  permission %s in class %s has incorrect value\n",
-			       def_perm, pol_class);
-			return -EINVAL;
-		}
-	}
-	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
-			       "SELinux:  class %s should have an inherits clause but does not\n",
-			       pol_class);
-			return -EINVAL;
-		}
-		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
-				       "SELinux:  permission %s in class %s not defined in policy\n",
-				       def_perm, pol_class);
-				if (p->reject_unknown)
-					return -EINVAL;
-				if (p->allow_unknown)
-					p->undefined_perms[class_val-1] |= (1 << j);
-				print_unknown_handle = 1;
-				continue;
-			}
-			perdatum = hashtab_search(perms->table, def_perm);
-			if (perdatum == NULL) {
-				printk(KERN_ERR
-				       "SELinux:  permission %s in class %s not found in policy, bad policy\n",
-				       def_perm, pol_class);
-				return -EINVAL;
-			}
-			if (perdatum->value != j + 1) {
-				printk(KERN_ERR
-				       "SELinux:  permission %s in class %s has incorrect value\n",
-				       def_perm, pol_class);
-				return -EINVAL;
-			}
-		}
-	}
-	if (print_unknown_handle)
-		printk(KERN_INFO "SELinux: the above unknown classes and permissions will be %s\n",
-			(security_get_allow_unknown() ? "allowed" : "denied"));
-	return 0;
+	return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, out_sid,
+				    false);
 }
 
 /* Clone the SID into the new SID table. */
@@ -1710,8 +1740,10 @@ int security_load_policy(void *data, size_t len)
 {
 	struct policydb oldpolicydb, newpolicydb;
 	struct sidtab oldsidtab, newsidtab;
+	struct selinux_mapping *oldmap, *map = NULL;
 	struct convert_context_args args;
 	u32 seqno;
+	u16 map_size;
 	int rc = 0;
 	struct policy_file file = { data, len }, *fp = &file;
 
@@ -1721,16 +1753,14 @@ int security_load_policy(void *data, size_t len)
 			avtab_cache_destroy();
 			return -EINVAL;
 		}
-		if (policydb_load_isids(&policydb, &sidtab)) {
+		if (selinux_set_mapping(&policydb, secclass_map,
+					&current_mapping,
+					&current_mapping_size)) {
 			policydb_destroy(&policydb);
 			avtab_cache_destroy();
 			return -EINVAL;
 		}
-		/* Verify that the kernel defined classes are correct. */
-		if (validate_classes(&policydb)) {
-			printk(KERN_ERR
-			       "SELinux:  the definition of a class is incorrect\n");
-			sidtab_destroy(&sidtab);
+		if (policydb_load_isids(&policydb, &sidtab)) {
 			policydb_destroy(&policydb);
 			avtab_cache_destroy();
 			return -EINVAL;
@@ -1759,13 +1789,9 @@ int security_load_policy(void *data, size_t len)
 		return -ENOMEM;
 	}
 
-	/* Verify that the kernel defined classes are correct. */
-	if (validate_classes(&newpolicydb)) {
-		printk(KERN_ERR
-		       "SELinux:  the definition of a class is incorrect\n");
-		rc = -EINVAL;
+	if (selinux_set_mapping(&newpolicydb, secclass_map,
+				&map, &map_size))
 		goto err;
-	}
 
 	rc = security_preserve_bools(&newpolicydb);
 	if (rc) {
@@ -1799,6 +1825,9 @@ int security_load_policy(void *data, size_t len)
 	memcpy(&policydb, &newpolicydb, sizeof policydb);
 	sidtab_set(&sidtab, &newsidtab);
 	security_load_policycaps();
+	oldmap = current_mapping;
+	current_mapping = map;
+	current_mapping_size = map_size;
 	seqno = ++latest_granting;
 	policydb_loaded_version = policydb.policyvers;
 	write_unlock_irq(&policy_rwlock);
@@ -1806,6 +1835,7 @@ int security_load_policy(void *data, size_t len)
 	/* Free the old policydb and SID table. */
 	policydb_destroy(&oldpolicydb);
 	sidtab_destroy(&oldsidtab);
+	kfree(oldmap);
 
 	avc_ss_reset(seqno);
 	selnl_notify_policyload(seqno);
@@ -1815,6 +1845,7 @@ int security_load_policy(void *data, size_t len)
 	return 0;
 
 err:
+	kfree(map);
 	sidtab_destroy(&newsidtab);
 	policydb_destroy(&newpolicydb);
 	return rc;
@@ -2091,7 +2122,7 @@ out_unlock:
 	}
 	for (i = 0, j = 0; i < mynel; i++) {
 		rc = avc_has_perm_noaudit(fromsid, mysids[i],
-					  SECCLASS_PROCESS,
+					  SECCLASS_PROCESS, /* kernel value */
 					  PROCESS__TRANSITION, AVC_STRICT,
 					  NULL);
 		if (!rc)
@@ -2119,10 +2150,11 @@ out:
  */
 int security_genfs_sid(const char *fstype,
 		       char *path,
-		       u16 sclass,
+		       u16 orig_sclass,
 		       u32 *sid)
 {
 	int len;
+	u16 sclass;
 	struct genfs *genfs;
 	struct ocontext *c;
 	int rc = 0, cmp = 0;
@@ -2132,6 +2164,8 @@ int security_genfs_sid(const char *fstype,
 
 	read_lock(&policy_rwlock);
 
+	sclass = unmap_class(orig_sclass);
+
 	for (genfs = policydb.genfs; genfs; genfs = genfs->next) {
 		cmp = strcmp(fstype, genfs->fstype);
 		if (cmp <= 0)
-- 
cgit v1.2.3-70-g09d2


From 941fc5b2bf8f7dd1d0a9c502e152fa719ff6578e Mon Sep 17 00:00:00 2001
From: Stephen Smalley <sds@tycho.nsa.gov>
Date: Thu, 1 Oct 2009 14:48:23 -0400
Subject: selinux: drop remapping of netlink classes

Drop remapping of netlink classes and bypass of permission checking
based on netlink message type for policy version < 18.  This removes
compatibility code introduced when the original single netlink
security class used for all netlink sockets was split into
finer-grained netlink classes based on netlink protocol and when
permission checking was added based on netlink message type in Linux
2.6.8.  The only known distribution that shipped with SELinux and
policy < 18 was Fedora Core 2, which was EOL'd on 2005-04-11.

Given that the remapping code was never updated to address the
addition of newer netlink classes, that the corresponding userland
support was dropped in 2005, and that the assumptions made by the
remapping code about the fixed ordering among netlink classes in the
policy may be violated in the future due to the dynamic class/perm
discovery support, we should drop this compatibility code now.

Signed-off-by:  Stephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: James Morris <jmorris@namei.org>
---
 security/selinux/hooks.c       |  6 +-----
 security/selinux/ss/services.c | 25 -------------------------
 2 files changed, 1 insertion(+), 30 deletions(-)

(limited to 'security/selinux/ss/services.c')

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index a985d0bc59b..a29d6612a32 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -91,7 +91,6 @@
 
 #define NUM_SEL_MNT_OPTS 5
 
-extern unsigned int policydb_loaded_version;
 extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
 extern struct security_operations *security_ops;
 
@@ -4714,10 +4713,7 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
 	if (err)
 		return err;
 
-	if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS)
-		err = selinux_nlmsg_perm(sk, skb);
-
-	return err;
+	return selinux_nlmsg_perm(sk, skb);
 }
 
 static int selinux_netlink_recv(struct sk_buff *skb, int capability)
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index e19baa81fde..f270e378c0e 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -65,7 +65,6 @@
 #include "audit.h"
 
 extern void selnl_notify_policyload(u32 seqno);
-unsigned int policydb_loaded_version;
 
 int selinux_policycap_netpeer;
 int selinux_policycap_openperm;
@@ -616,17 +615,6 @@ static int context_struct_compute_av(struct context *scontext,
 	struct ebitmap_node *snode, *tnode;
 	unsigned int i, j;
 
-	/*
-	 * Remap extended Netlink classes for old policy versions.
-	 * Do this here rather than socket_type_to_security_class()
-	 * in case a newer policy version is loaded, allowing sockets
-	 * to remain in the correct class.
-	 */
-	if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS)
-		if (tclass >= unmap_class(SECCLASS_NETLINK_ROUTE_SOCKET) &&
-		    tclass <= unmap_class(SECCLASS_NETLINK_DNRT_SOCKET))
-			tclass = unmap_class(SECCLASS_NETLINK_SOCKET);
-
 	/*
 	 * Initialize the access vectors to the default values.
 	 */
@@ -761,17 +749,6 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
 
 	tclass = unmap_class(orig_tclass);
 
-	/*
-	 * Remap extended Netlink classes for old policy versions.
-	 * Do this here rather than socket_type_to_security_class()
-	 * in case a newer policy version is loaded, allowing sockets
-	 * to remain in the correct class.
-	 */
-	if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS)
-		if (tclass >= unmap_class(SECCLASS_NETLINK_ROUTE_SOCKET) &&
-		    tclass <= unmap_class(SECCLASS_NETLINK_DNRT_SOCKET))
-			tclass = unmap_class(SECCLASS_NETLINK_SOCKET);
-
 	if (!tclass || tclass > policydb.p_classes.nprim) {
 		printk(KERN_ERR "SELinux: %s:  unrecognized class %d\n",
 			__func__, tclass);
@@ -1766,7 +1743,6 @@ int security_load_policy(void *data, size_t len)
 			return -EINVAL;
 		}
 		security_load_policycaps();
-		policydb_loaded_version = policydb.policyvers;
 		ss_initialized = 1;
 		seqno = ++latest_granting;
 		selinux_complete_init();
@@ -1829,7 +1805,6 @@ int security_load_policy(void *data, size_t len)
 	current_mapping = map;
 	current_mapping_size = map_size;
 	seqno = ++latest_granting;
-	policydb_loaded_version = policydb.policyvers;
 	write_unlock_irq(&policy_rwlock);
 
 	/* Free the old policydb and SID table. */
-- 
cgit v1.2.3-70-g09d2


From b7f3008ad1d795935551e4dd810b0255a7bfa3c9 Mon Sep 17 00:00:00 2001
From: Stephen Smalley <sds@tycho.nsa.gov>
Date: Mon, 19 Oct 2009 10:08:50 -0400
Subject: SELinux: fix locking issue introduced with c6d3aaa4e35c71a3

Ensure that we release the policy read lock on all exit paths from
security_compute_av.

Signed-off-by:  Stephen D. Smalley <sds@tycho.nsa.gov>
Signed-off-by: James Morris <jmorris@namei.org>
---
 security/selinux/ss/services.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

(limited to 'security/selinux/ss/services.c')

diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index f270e378c0e..77f6e54bb43 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -935,19 +935,22 @@ int security_compute_av(u32 ssid,
 	u32 requested;
 	int rc;
 
+	read_lock(&policy_rwlock);
+
 	if (!ss_initialized)
 		goto allow;
 
-	read_lock(&policy_rwlock);
 	requested = unmap_perm(orig_tclass, orig_requested);
 	tclass = unmap_class(orig_tclass);
 	if (unlikely(orig_tclass && !tclass)) {
 		if (policydb.allow_unknown)
 			goto allow;
-		return -EINVAL;
+		rc = -EINVAL;
+		goto out;
 	}
 	rc = security_compute_av_core(ssid, tsid, tclass, requested, avd);
 	map_decision(orig_tclass, avd, policydb.allow_unknown);
+out:
 	read_unlock(&policy_rwlock);
 	return rc;
 allow:
@@ -956,7 +959,8 @@ allow:
 	avd->auditdeny = 0xffffffff;
 	avd->seqno = latest_granting;
 	avd->flags = 0;
-	return 0;
+	rc = 0;
+	goto out;
 }
 
 int security_compute_av_user(u32 ssid,
-- 
cgit v1.2.3-70-g09d2


From 0bce95279909aa4cc401a2e3140b4295ca22e72a Mon Sep 17 00:00:00 2001
From: Eric Paris <eparis@redhat.com>
Date: Mon, 23 Nov 2009 16:47:23 -0500
Subject: SELinux: print denials for buggy kernel with unknown perms

Historically we've seen cases where permissions are requested for classes
where they do not exist.  In particular we have seen CIFS forget to set
i_mode to indicate it is a directory so when we later check something like
remove_name we have problems since it wasn't defined in tclass file.  This
used to result in a avc which included the permission 0x2000 or something.
Currently the kernel will deny the operations (good thing) but will not
print ANY information (bad thing).  First the auditdeny field is no
extended to include unknown permissions.  After that is fixed the logic in
avc_dump_query to output this information isn't right since it will remove
the permission from the av and print the phrase "<NULL>".  This takes us
back to the behavior before the classmap rewrite.

Signed-off-by: Eric Paris <eparis@redhat.com>
Signed-off-by: James Morris <jmorris@namei.org>
---
 security/selinux/avc.c         | 2 +-
 security/selinux/ss/services.c | 7 +++++++
 2 files changed, 8 insertions(+), 1 deletion(-)

(limited to 'security/selinux/ss/services.c')

diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 18f4103e02b..f2dde268165 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -117,7 +117,7 @@ static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
 	i = 0;
 	perm = 1;
 	while (i < (sizeof(av) * 8)) {
-		if (perm & av) {
+		if ((perm & av) && perms[i]) {
 			audit_log_format(ab, " %s", perms[i]);
 			av &= ~perm;
 		}
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 77f6e54bb43..d6bb20cbad6 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -239,6 +239,13 @@ static void map_decision(u16 tclass, struct av_decision *avd,
 			if (!allow_unknown && !current_mapping[tclass].perms[i])
 				result |= 1<<i;
 		}
+		/*
+		 * In case the kernel has a bug and requests a permission
+		 * between num_perms and the maximum permission number, we
+		 * should audit that denial
+		 */
+		for (; i < (sizeof(u32)*8); i++)
+			result |= 1<<i;
 		avd->auditdeny = result;
 	}
 }
-- 
cgit v1.2.3-70-g09d2