SELinux: add more validity checks on policy load

Add more validity checks at policy load time to reject malformed
policies and prevent subsequent out-of-range indexing when in permissive
mode.  Resolves the NULL pointer dereference reported in
https://bugzilla.redhat.com/show_bug.cgi?id=357541.

Signed-off-by:  Stephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: James Morris <jmorris@namei.org>
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index 9a11dea..fb5d70a 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -157,49 +157,55 @@
 	return;
 }
 
+int mls_level_isvalid(struct policydb *p, struct mls_level *l)
+{
+	struct level_datum *levdatum;
+	struct ebitmap_node *node;
+	int i;
+
+	if (!l->sens || l->sens > p->p_levels.nprim)
+		return 0;
+	levdatum = hashtab_search(p->p_levels.table,
+				  p->p_sens_val_to_name[l->sens - 1]);
+	if (!levdatum)
+		return 0;
+
+	ebitmap_for_each_positive_bit(&l->cat, node, i) {
+		if (i > p->p_cats.nprim)
+			return 0;
+		if (!ebitmap_get_bit(&levdatum->level->cat, i)) {
+			/*
+			 * Category may not be associated with
+			 * sensitivity.
+			 */
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+int mls_range_isvalid(struct policydb *p, struct mls_range *r)
+{
+	return (mls_level_isvalid(p, &r->level[0]) &&
+		mls_level_isvalid(p, &r->level[1]) &&
+		mls_level_dom(&r->level[1], &r->level[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)
 {
-	struct level_datum *levdatum;
 	struct user_datum *usrdatum;
-	struct ebitmap_node *node;
-	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. */
+	if (!mls_range_isvalid(p, &c->range))
 		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;
-
-		ebitmap_for_each_positive_bit(&c->range.level[l].cat, node, i) {
-			if (i > p->p_cats.nprim)
-				return 0;
-			if (!ebitmap_get_bit(&levdatum->level->cat, i))
-				/*
-				 * Category may not be associated with
-				 * sensitivity in low level.
-				 */
-				return 0;
-		}
-	}
-
 	if (c->role == OBJECT_R_VAL)
 		return 1;