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>
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index ff17820..e19baa8 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -70,11 +70,6 @@
 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 @@
 				     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 @@
 	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 @@
 	 * 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 @@
 	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 @@
 	 * 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 @@
 				break;
 		}
 		if (!ra)
-			avd->allowed &= ~(PROCESS__TRANSITION |
-					  PROCESS__DYNTRANSITION);
+			avd->allowed &= ~policydb.process_trans_perms;
 	}
 
 	/*
@@ -590,21 +713,6 @@
 				 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 @@
 }
 
 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 @@
 
 	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 @@
 	 * 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 @@
 }
 
 
+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 @@
  */
 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 @@
 	}
 
 	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 @@
 
 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 @@
 
 	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 @@
 	}
 
 	/* 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 @@
 	}
 
 	/* 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 @@
 				}
 			}
 		}
-		break;
-	default:
-		break;
 	}
 
 	/* Set the MLS attributes.
@@ -1358,7 +1513,17 @@
 			    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 @@
 			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 @@
 			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 @@
 {
 	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 @@
 			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 @@
 		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 @@
 	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 @@
 	/* 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 @@
 	return 0;
 
 err:
+	kfree(map);
 	sidtab_destroy(&newsidtab);
 	policydb_destroy(&newpolicydb);
 	return rc;
@@ -2091,7 +2122,7 @@
 	}
 	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 @@
  */
 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 @@
 
 	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)