Commits

Stephen Smalley committed 8fd7c65

Update to libsepol 2.2.

Update to current upstream libsepol release.
Includes memory leak fixes, improved handling of filename-based
type transitions, and support for latest kernel policy version.

Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>

(cherry picked from commit dedcd596b31e0e4fc15d75b3a8b5e6b61e6c28b3)

Change-Id: I273e3aac223d25c918f71d566ea752b64ec59cd7

Comments (0)

Files changed (28)

+2.2 2013-10-30
+	* Allow constraint denial cause to be determined from Richard Haines.
+	  - Add kernel policy version 29.
+	  - Add modular policy version 17.
+	  - Add sepol_compute_av_reason_buffer(), sepol_string_to_security_class(), sepol_string_to_av_perm().
+	* Support overriding Makefile RANLIB from Sven Vermeulen.
+	* Fix man pages from Laurent Bigonville.
+
+2.1.9 2013-02-01
+	* filename_trans: use some better sorting to compare and merge
+	* coverity fixes
+	* implement default type policy syntax
+	* Fix memory leak issues found by Klocwork
+
 2.1.8 2011-09-13
 	* fix neverallow checking on attributes
 	* Move context_copy() after switch block in ocontext_copy_*().
-2.1.8
+2.2

include/sepol/policydb/policydb.h

 #define DEFAULT_TARGET		2
 	char default_user;
 	char default_role;
+	char default_type;
 /* Options how a new object range should be decided */
 #define DEFAULT_SOURCE_LOW	1
 #define DEFAULT_SOURCE_HIGH	2
 #define POLICYDB_VERSION_FILENAME_TRANS	25
 #define POLICYDB_VERSION_ROLETRANS	26
 #define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS	27
+#define POLICYDB_VERSION_DEFAULT_TYPE	28
+#define POLICYDB_VERSION_CONSTRAINT_NAMES	29
 
 /* Range of policy versions we understand*/
 #define POLICYDB_VERSION_MIN	POLICYDB_VERSION_BASE
-#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_NEW_OBJECT_DEFAULTS
+#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_CONSTRAINT_NAMES
 
 /* Module versions and specific changes*/
 #define MOD_POLICYDB_VERSION_BASE		4
 #define MOD_POLICYDB_VERSION_ROLEATTRIB		13
 #define MOD_POLICYDB_VERSION_TUNABLE_SEP	14
 #define MOD_POLICYDB_VERSION_NEW_OBJECT_DEFAULTS	15
+#define MOD_POLICYDB_VERSION_DEFAULT_TYPE	16
+#define MOD_POLICYDB_VERSION_CONSTRAINT_NAMES  17
 
 #define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE
-#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_NEW_OBJECT_DEFAULTS
+#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_CONSTRAINT_NAMES
 
 #define POLICYDB_CONFIG_MLS    1
 

include/sepol/policydb/services.h

 				   unsigned int *reason);
 
 /*
+ * Same as above, but also returns the constraint expression calculations
+ * whether allowed or denied in a buffer. This buffer is allocated by
+ * this call and must be free'd by the caller using free(3). The contraint
+ * buffer will contain any constraints in infix notation.
+ * If the SHOW_GRANTED flag is set it will show granted and denied
+ * constraints. The default is to show only denied constraints.
+ */
+#define SHOW_GRANTED 1
+extern int sepol_compute_av_reason_buffer(sepol_security_id_t ssid,
+				   sepol_security_id_t tsid,
+				   sepol_security_class_t tclass,
+				   sepol_access_vector_t requested,
+				   struct sepol_av_decision *avd,
+				   unsigned int *reason,
+				   char **reason_buf,
+				   unsigned int flags);
+/*
+ * Return a class ID associated with the class string representation
+ * specified by `class_name'.
+ */
+extern int sepol_string_to_security_class(const char *class_name,
+					sepol_security_class_t  *tclass);
+
+/*
+ * Return a permission av bit associated with tclass and the string
+ * representation of the `perm_name'.
+ */
+extern int sepol_string_to_av_perm(sepol_security_class_t tclass,
+					const char *perm_name,
+					sepol_access_vector_t *av);
+
+/*
  * Compute a SID to use for labeling a new object in the 
  * class `tclass' based on a SID pair.  
  */

man/man3/sepol_check_context.3

 from libselinux instead.
 
 .SH "RETURN VALUE"
-Returns 0 on success or -1 with errno set otherwise.
+Returns 0 on success or \-1 with errno set otherwise.

man/man3/sepol_genbools.3

 (names, values) with nel elements each.
 
 .SH "RETURN VALUE"
-Returns 0 on success or -1 otherwise, with errno set appropriately.
+Returns 0 on success or \-1 otherwise, with errno set appropriately.
 An errno of ENOENT indicates that the boolean file did not exist.
 An errno of EINVAL indicates that one or more booleans listed in the
 boolean file was undefined in the policy or had an invalid value specified;

man/man3/sepol_genusers.3

 in order to enable deletion of such users.
 
 .SH "RETURN VALUE"
-Returns 0 on success or -1 otherwise, with errno set appropriately.
+Returns 0 on success or \-1 otherwise, with errno set appropriately.
 An errno of ENOENT indicates that one or both of the user
 configuration files did not exist.  An errno of EINVAL indicates that
 either the original binary policy image or the generated one were
 INCLUDEDIR ?= $(PREFIX)/include
 LIBDIR ?= $(PREFIX)/lib
 SHLIBDIR ?= $(DESTDIR)/lib
-LIBBASE=$(shell basename $(LIBDIR))
+RANLIB ?= ranlib
+LIBBASE ?= $(shell basename $(LIBDIR))
 
 VERSION = $(shell cat ../VERSION)
 LIBVERSION = 1
 
 $(LIBA):  $(OBJS)
 	$(AR) rcs $@ $^
-	ranlib $@
+	$(RANLIB) $@
 
 $(LIBSO): $(LOBJS)
 	$(CC) $(CFLAGS) $(LDFLAGS) -shared -o $@ $^ -Wl,-soname,$(LIBSO),--version-script=libsepol.map,-z,defs

src/avrule_block.c

 	for (i = 0; i < SYM_NUM; i++) {
 		if (symtab_init(&decl->symtab[i], symtab_sizes[i])) {
 			avrule_decl_destroy(decl);
-			free(decl);
 			return NULL;
 		}
 	}
 	int expand_neverallow;
 } expand_state_t;
 
+struct linear_probe {
+	filename_trans_t **table;	/* filename_trans chunks with same stype */
+	filename_trans_t **ends;	/* pointers to ends of **table chunks */
+	uint32_t length;		/* length of the table */
+};
+
+static int linear_probe_create(struct linear_probe *probe, uint32_t length)
+{
+	probe->table = calloc(length, sizeof(*probe->table));
+	if (probe->table == NULL)
+		return -1;
+
+	probe->ends = calloc(length, sizeof(*probe->ends));
+	if (probe->ends == NULL)
+		return -1;
+
+	probe->length = length;
+
+	return 0;
+}
+
+static void linear_probe_destroy(struct linear_probe *probe)
+{
+	if (probe->length == 0)
+		return;
+
+	free(probe->table);
+	free(probe->ends);
+	memset(probe, 0, sizeof(*probe));
+}
+
+static void linear_probe_insert(struct linear_probe *probe, uint32_t key,
+				filename_trans_t *data)
+{
+	assert(probe->length > key);
+
+	if (probe->table[key] != NULL) {
+		data->next = probe->table[key];
+		probe->table[key] = data;
+	} else {
+		probe->table[key] = probe->ends[key] = data;
+	}
+}
+
+static filename_trans_t *linear_probe_find(struct linear_probe *probe, uint32_t key)
+{
+	assert(probe->length > key);
+
+	return probe->table[key];
+}
+
+/* Returns all chunks stored in the *probe as single-linked list */
+static filename_trans_t *linear_probe_dump(struct linear_probe *probe,
+					   filename_trans_t **endp)
+{
+	uint32_t i;
+	filename_trans_t *result = NULL;
+	filename_trans_t *end = NULL;
+
+	for (i = 0; i < probe->length; i++) {
+		if (probe->table[i] != NULL) {
+			if (end == NULL)
+				end = probe->ends[i];
+			probe->ends[i]->next = result;
+			result = probe->table[i];
+			probe->table[i] = probe->ends[i] = NULL;
+		}
+	}
+
+	/* Incoherent result and end pointers indicates bug */
+	assert((result != NULL && end != NULL) || (result == NULL && end == NULL));
+
+	*endp = end;
+	return result;
+}
+
 static void expand_state_init(expand_state_t * state)
 {
 	memset(state, 0, sizeof(expand_state_t));
 	new_id = strdup(id);
 	if (!new_id) {
 		ERR(state->handle, "Out of memory!");
+		/* free memory created by symtab_init first, then free new_common */
 		symtab_destroy(&new_common->permissions);
 		free(new_common);
 		return -1;
 			   (hashtab_datum_t *) new_common);
 	if (ret) {
 		ERR(state->handle, "hashtab overflow");
-		symtab_destroy(&new_common->permissions);
 		free(new_common);
 		free(new_id);
 		return -1;
 			new_expr->op = expr->op;
 			if (new_expr->expr_type == CEXPR_NAMES) {
 				if (new_expr->attr & CEXPR_TYPE) {
+					/*
+					 * Copy over constraint policy source types and/or
+					 * attributes for sepol_compute_av_reason_buffer(3)
+					 * so that utilities can analyse constraint errors.
+					 */
+					if (map_ebitmap(&expr->type_names->types,
+							&new_expr->type_names->types,
+							state->typemap)) {
+						ERR(NULL, "Failed to map type_names->types");
+						goto out_of_mem;
+					}
 					/* Type sets require expansion and conversion. */
 					if (expand_convert_type_set(state->out,
 								    state->
 		}
 		newdatum->default_role = olddatum->default_role;
 	}
+	if (olddatum->default_type) {
+		if (newdatum->default_type && olddatum->default_type != newdatum->default_type) {
+			ERR(state->handle, "Found conflicting default type definitions");
+			return SEPOL_ENOTSUP;
+		}
+		newdatum->default_type = olddatum->default_type;
+	}
 	if (olddatum->default_range) {
 		if (newdatum->default_range && olddatum->default_range != newdatum->default_range) {
 			ERR(state->handle, "Found conflicting default range definitions");
 
 	l->sens = sl->sens;
 	levdatum = (level_datum_t *) hashtab_search(p->p_levels.table,
-						    p->p_sens_val_to_name[l->
-									  sens -
-									  1]);
+						    p->p_sens_val_to_name[l->sens - 1]);
+	if (!levdatum) {
+		ERR(h, "%s: Impossible situation found, nothing in p_levels.table.\n",
+		    __func__);
+		errno = ENOENT;
+		return -1;
+	}
 	for (cat = sl->cat; cat; cat = cat->next) {
 		if (cat->low > cat->high) {
 			ERR(h, "Category range is not valid %s.%s",
 static int expand_filename_trans(expand_state_t *state, filename_trans_rule_t *rules)
 {
 	unsigned int i, j;
-	filename_trans_t *new_trans, *cur_trans;
+	filename_trans_t *new_trans, *cur_trans, *end;
 	filename_trans_rule_t *cur_rule;
 	ebitmap_t stypes, ttypes;
 	ebitmap_node_t *snode, *tnode;
+	struct linear_probe probe;
+
+	/*
+	 * Linear probing speeds-up finding filename_trans rules with certain
+	 * "stype" value.
+	 */
+	if (linear_probe_create(&probe, 4096)) { /* Assume 4096 is enough for most cases */
+		ERR(state->handle, "Out of memory!");
+		return -1;
+	}
 
 	cur_rule = rules;
 	while (cur_rule) {
 
 		mapped_otype = state->typemap[cur_rule->otype - 1];
 
+		if (ebitmap_length(&stypes) > probe.length) {
+			linear_probe_destroy(&probe);
+			if (linear_probe_create(&probe, ebitmap_length(&stypes))) {
+				ERR(state->handle, "Out of memory!");
+				return -1;
+			}
+		}
+
 		ebitmap_for_each_bit(&stypes, snode, i) {
 			if (!ebitmap_node_get_bit(snode, i))
 				continue;
 				if (!ebitmap_node_get_bit(tnode, j))
 					continue;
 
-				cur_trans = state->out->filename_trans;
-				while (cur_trans) {
-					if ((cur_trans->stype == i + 1) &&
-					    (cur_trans->ttype == j + 1) &&
+				cur_trans = linear_probe_find(&probe, i);
+				while (cur_trans != NULL) {
+					if ((cur_trans->ttype == j + 1) &&
 					    (cur_trans->tclass == cur_rule->tclass) &&
 					    (!strcmp(cur_trans->name, cur_rule->name))) {
 						/* duplicate rule, who cares */
 						if (cur_trans->otype == mapped_otype)
 							break;
-
 						ERR(state->handle, "Conflicting filename trans rules %s %s %s : %s otype1:%s otype2:%s",
 						    cur_trans->name,
 						    state->out->p_type_val_to_name[i],
 						    state->out->p_class_val_to_name[cur_trans->tclass - 1],
 						    state->out->p_type_val_to_name[cur_trans->otype - 1],
 						    state->out->p_type_val_to_name[mapped_otype - 1]);
-						    
+
 						return -1;
 					}
 					cur_trans = cur_trans->next;
 					return -1;
 				}
 				memset(new_trans, 0, sizeof(*new_trans));
-				new_trans->next = state->out->filename_trans;
-				state->out->filename_trans = new_trans;
 
 				new_trans->name = strdup(cur_rule->name);
 				if (!new_trans->name) {
 				new_trans->ttype = j + 1;
 				new_trans->tclass = cur_rule->tclass;
 				new_trans->otype = mapped_otype;
+				linear_probe_insert(&probe, i, new_trans);
 			}
 		}
 
+		cur_trans = linear_probe_dump(&probe, &end);
+		if (cur_trans != NULL) {
+			end->next = state->out->filename_trans;
+			state->out->filename_trans = cur_trans;
+		}
+
 		ebitmap_destroy(&stypes);
 		ebitmap_destroy(&ttypes);
 
 	}
 
 	if (cond_node_map_bools(state, tmp)) {
-		ERR(state->handle, "Error mapping booleans");
+		cond_node_destroy(tmp);
 		free(tmp);
+		ERR(state->handle, "Error mapping booleans");
 		return -1;
 	}
 
 		memset(newgenfs, 0, sizeof(genfs_t));
 		newgenfs->fstype = strdup(genfs->fstype);
 		if (!newgenfs->fstype) {
-			ERR(state->handle, "Out of memory!");
 			free(newgenfs);
+			ERR(state->handle, "Out of memory!");
 			return -1;
 		}
+		if (!end)
+			state->out->genfs = newgenfs;
+		else
+			end->next = newgenfs;
+		end = newgenfs;
 
 		l = NULL;
 		for (c = genfs->head; c; c = c->next) {
 			newc = malloc(sizeof(ocontext_t));
 			if (!newc) {
 				ERR(state->handle, "Out of memory!");
-				free(newgenfs->fstype);
-				free(newgenfs);
 				return -1;
 			}
 			memset(newc, 0, sizeof(ocontext_t));
 			if (!newc->u.name) {
 				ERR(state->handle, "Out of memory!");
 				free(newc);
-				free(newgenfs->fstype);
-				free(newgenfs);
 				return -1;
 			}
 			newc->v.sclass = c->v.sclass;
 				newgenfs->head = newc;
 			l = newc;
 		}
-		if (!end) {
-			state->out->genfs = newgenfs;
-		} else {
-			end->next = newgenfs;
-		}
-		end = newgenfs;
 	}
 	return 0;
 }
 	}
 
 	cond_optimize_lists(state.out->cond_list);
-	evaluate_conds(state.out);
+	if (evaluate_conds(state.out))
+		goto cleanup;
 
 	/* copy ocontexts */
 	if (ocontext_copy(&state, out->target_platform))
 static int process_boolean(char *buffer, char *name, int namesize, int *val)
 {
 	char name1[BUFSIZ];
-	char *ptr;
+	char *ptr = NULL;
 	char *tok = strtok_r(buffer, "=", &ptr);
 	if (tok) {
 		strncpy(name1, tok, BUFSIZ - 1);
 			ebitmap_init(&usrdatum->roles.roles);
 		} else {
 			char *id = strdup(q);
+
 			if (!id) {
 				ERR(NULL, "out of memory");
 				free(buffer);
 			}
 
 			/* Adding a new user definition. */
-			usrdatum =
-			    (user_datum_t *) malloc(sizeof(user_datum_t));
+			usrdatum = malloc(sizeof(user_datum_t));
 			if (!usrdatum) {
 				ERR(NULL, "out of memory");
 				free(buffer);
 				fclose(fp);
 				return -1;
 			}
-			memset(usrdatum, 0, sizeof(user_datum_t));
+
+			user_datum_init(usrdatum);
 			usrdatum->s.value = ++policydb->p_users.nprim;
-			ebitmap_init(&usrdatum->roles.roles);
 			if (hashtab_insert(policydb->p_users.table,
 					   id, (hashtab_datum_t) usrdatum)) {
 				ERR(NULL, "out of memory");
 				free(buffer);
 				free(id);
+				user_datum_destroy(usrdatum);
 				free(usrdatum);
 				fclose(fp);
 				return -1;
 		}
 		newdatum->default_role = olddatum->default_role;
 	}
+	if (olddatum->default_type) {
+		if (newdatum->default_type && olddatum->default_type != newdatum->default_type) {
+			ERR(state->handle, "Found conflicting default type definitions");
+			return SEPOL_ENOTSUP;
+		}
+		newdatum->default_type = olddatum->default_type;
+	}
 	if (olddatum->default_range) {
 		if (newdatum->default_range && olddatum->default_range != newdatum->default_range) {
 			ERR(state->handle, "Found conflicting default range definitions");
 			}
 			new_id = strdup(id);
 			if (new_id == NULL) {
-				symtab_destroy(&new_class->permissions);
 				ERR(state->handle, "Memory error\n");
+				symtab_destroy(&new_class->permissions);
 				ret = SEPOL_ERR;
 				goto err;
 			}
 					     (hashtab_key_t) new_id,
 					     (hashtab_datum_t) new_class);
 			if (ret) {
-				symtab_destroy(&new_class->permissions);
 				ERR(state->handle,
 				    "could not insert new class into symtab");
+				symtab_destroy(&new_class->permissions);
 				goto err;
 			}
 			new_class->s.value = ++(state->base->p_classes.nprim);
 			    "%s: Modules may not declare new sensitivities.",
 			    state->cur_mod_name);
 			return SEPOL_ENOTSUP;
-		}
-		if (scope->scope == SCOPE_REQ) {
+		} else if (scope->scope == SCOPE_REQ) {
 			/* unmet requirement */
 			ERR(state->handle,
 			    "%s: Sensitivity %s not declared by base.",
 			    state->cur_mod_name, id);
 			return SEPOL_ENOTSUP;
+		} else {
+			ERR(state->handle,
+			    "%s: has an unknown scope: %d\n",
+			    state->cur_mod_name, scope->scope);
+			return SEPOL_ENOTSUP;
 		}
 	}
 
 
 	base_cat = hashtab_search(state->base->p_cats.table, id);
 	if (!base_cat) {
-		scope =
-		    hashtab_search(state->cur->policy->p_cat_scope.table, id);
+		scope = hashtab_search(state->cur->policy->p_cat_scope.table, id);
 		if (!scope)
 			return SEPOL_ERR;
 		if (scope->scope == SCOPE_DECL) {
 			    "%s: Modules may not declare new categories.",
 			    state->cur_mod_name);
 			return SEPOL_ENOTSUP;
-		}
-		if (scope->scope == SCOPE_REQ) {
+		} else if (scope->scope == SCOPE_REQ) {
 			/* unmet requirement */
 			ERR(state->handle,
 			    "%s: Category %s not declared by base.",
 			    state->cur_mod_name, id);
 			return SEPOL_ENOTSUP;
+		} else {
+			/* unknown scope?  malformed policy? */
+			ERR(state->handle,
+			    "%s: has an unknown scope: %d\n",
+			    state->cur_mod_name, scope->scope);
+			return SEPOL_ENOTSUP;
 		}
 	}
 
 
 			if (new_rule->perms == NULL) {
 				new_rule->perms = new_perm;
-			} else if (tail_perm) {
+			} else {
+				assert(tail_perm);
 				tail_perm->next = new_perm;
 			}
 			tail_perm = new_perm;
 			struct find_perm_arg fparg;
 			class_datum_t *cladatum;
 			uint32_t perm_value = j + 1;
+			int rc;
 			scope_datum_t *scope;
 
 			if (!ebitmap_node_get_bit(node, j)) {
 			fparg.valuep = perm_value;
 			fparg.key = NULL;
 
-			hashtab_map(cladatum->permissions.table, find_perm,
+			(void)hashtab_map(cladatum->permissions.table, find_perm,
 				    &fparg);
-			if (fparg.key == NULL && cladatum->comdatum != NULL)
-				hashtab_map(cladatum->comdatum->permissions.
-					    table, find_perm, &fparg);
+			if (fparg.key == NULL && cladatum->comdatum != NULL) {
+				rc = hashtab_map(cladatum->comdatum->permissions.table,
+						 find_perm, &fparg);
+				assert(rc == 1);
+			}
 			perm_id = fparg.key;
 
 			assert(perm_id != NULL);
 	int ret;
 	avrule_block_t *cur;
 	missing_requirement_t req;
+	memset(&req, 0, sizeof(req));
 
 	for (cur = p->global; cur != NULL; cur = cur->next) {
 		if (cur->enabled != NULL)
 			char *mod_name = cur->branch_list->module_name ?
 			    cur->branch_list->module_name : "BASE";
 			if (req.symbol_type == SYM_CLASSES) {
-
 				struct find_perm_arg fparg;
 
 				class_datum_t *cladatum;
-				cladatum =
-				    p->class_val_to_struct[req.symbol_value -
-							   1];
+				cladatum = p->class_val_to_struct[req.symbol_value - 1];
 
 				fparg.valuep = req.perm_value;
 				fparg.key = NULL;
-				hashtab_map(cladatum->permissions.table,
-					    find_perm, &fparg);
+				(void)hashtab_map(cladatum->permissions.table,
+						  find_perm, &fparg);
 
 				if (cur->flags & AVRULE_OPTIONAL) {
 					ERR(state->handle,
 					    "%s[%d]'s optional requirements were not met: class %s, permission %s",
 					    mod_name, cur->branch_list->decl_id,
-					    p->p_class_val_to_name[req.
-								   symbol_value
-								   - 1],
+					    p->p_class_val_to_name[req.symbol_value - 1],
 					    fparg.key);
 				} else {
 					ERR(state->handle,
 					    "%s[%d]'s global requirements were not met: class %s, permission %s",
 					    mod_name, cur->branch_list->decl_id,
-					    p->p_class_val_to_name[req.
-								   symbol_value
-								   - 1],
+					    p->p_class_val_to_name[req.symbol_value - 1],
 					    fparg.key);
 				}
 			} else {
 
 		fparg.valuep = req->perm_value;
 		fparg.key = NULL;
-		hashtab_map(cladatum->permissions.table, find_perm, &fparg);
+		(void)hashtab_map(cladatum->permissions.table, find_perm, &fparg);
 
 		ERR(state->handle,
 		    "%s's global requirements were not met: class %s, permission %s",
 		    "%s's global requirements were not met: %s %s",
 		    mod_name,
 		    symtab_names[req->symbol_type],
-		    p->sym_val_to_name[req->symbol_type][req->symbol_value -
-							 1]);
+		    p->sym_val_to_name[req->symbol_type][req->symbol_value - 1]);
 	}
 }
 
 	}
 }
 
-static size_t policy_file_length(struct policy_file *fp)
+static int policy_file_length(struct policy_file *fp, size_t *out)
 {
 	long prev_offset, end_offset;
+	int rc;
 	switch (fp->type) {
 	case PF_USE_STDIO:
 		prev_offset = ftell(fp->fp);
-		fseek(fp->fp, 0L, SEEK_END);
+		if (prev_offset < 0)
+			return prev_offset;
+		rc = fseek(fp->fp, 0L, SEEK_END);
+		if (rc < 0)
+			return rc;
 		end_offset = ftell(fp->fp);
-		fseek(fp->fp, prev_offset, SEEK_SET);
-		return end_offset;
+		if (end_offset < 0)
+			return end_offset;
+		rc = fseek(fp->fp, prev_offset, SEEK_SET);
+		if (rc < 0)
+			return rc;
+		*out = end_offset;
+		break;
 	case PF_USE_MEMORY:
-		return fp->size;
+		*out = fp->size;
+		break;;
 	default:
-		return 0;
+		*out = 0;
+		break;
 	}
+	return 0;
 }
 
 static int module_package_init(sepol_module_package_t * p)
 
 int sepol_module_package_create(sepol_module_package_t ** p)
 {
+	int rc;
+
 	*p = calloc(1, sizeof(sepol_module_package_t));
 	if (!(*p))
 		return -1;
-	return module_package_init(*p);
+
+	rc = module_package_init(*p);
+	if (rc < 0)
+		free(*p);
+
+	return rc;
 }
 
 hidden_def(sepol_module_package_create)
 		}
 	}
 
-	off[nsec] = policy_file_length(file);
+	rc = policy_file_length(file, &off[nsec]);
+	if (rc < 0)
+		goto err;
+
 	if (nsec && off[nsec] < off[nsec-1]) {
 		ERR(file->handle, "offset greater than file size (at %u, "
 		    "offset %zu -> %zu", nsec, off[nsec - 1],
 	 .target_platform = SEPOL_TARGET_SELINUX,
 	},
 	{
+	 .type = POLICY_KERN,
+	 .version = POLICYDB_VERSION_DEFAULT_TYPE,
+	 .sym_num = SYM_NUM,
+	 .ocon_num = OCON_NODE6 + 1,
+	 .target_platform = SEPOL_TARGET_SELINUX,
+	},
+	{
+	 .type = POLICY_KERN,
+	 .version = POLICYDB_VERSION_CONSTRAINT_NAMES,
+	 .sym_num = SYM_NUM,
+	 .ocon_num = OCON_NODE6 + 1,
+	 .target_platform = SEPOL_TARGET_SELINUX,
+	},
+	{
 	 .type = POLICY_BASE,
 	 .version = MOD_POLICYDB_VERSION_BASE,
 	 .sym_num = SYM_NUM,
 	 .target_platform = SEPOL_TARGET_SELINUX,
 	},
 	{
+	 .type = POLICY_BASE,
+	 .version = MOD_POLICYDB_VERSION_DEFAULT_TYPE,
+	 .sym_num = SYM_NUM,
+	 .ocon_num = OCON_NODE6 + 1,
+	 .target_platform = SEPOL_TARGET_SELINUX,
+	},
+	{
+	 .type = POLICY_BASE,
+	 .version = MOD_POLICYDB_VERSION_CONSTRAINT_NAMES,
+	 .sym_num = SYM_NUM,
+	 .ocon_num = OCON_NODE6 + 1,
+	 .target_platform = SEPOL_TARGET_SELINUX,
+	},
+	{
 	 .type = POLICY_MOD,
 	 .version = MOD_POLICYDB_VERSION_BASE,
 	 .sym_num = SYM_NUM,
 	 .ocon_num = 0,
 	 .target_platform = SEPOL_TARGET_SELINUX,
 	},
+	{
+	 .type = POLICY_MOD,
+	 .version = MOD_POLICYDB_VERSION_DEFAULT_TYPE,
+	 .sym_num = SYM_NUM,
+	 .ocon_num = 0,
+	 .target_platform = SEPOL_TARGET_SELINUX,
+	},
+	{
+	 .type = POLICY_MOD,
+	 .version = MOD_POLICYDB_VERSION_CONSTRAINT_NAMES,
+	 .sym_num = SYM_NUM,
+	 .ocon_num = 0,
+	 .target_platform = SEPOL_TARGET_SELINUX,
+	},
 };
 
 #if 0
 	if (key)
 		free(key);
 	comdatum = (common_datum_t *) datum;
-	hashtab_map(comdatum->permissions.table, perm_destroy, 0);
+	(void)hashtab_map(comdatum->permissions.table, perm_destroy, 0);
 	hashtab_destroy(comdatum->permissions.table);
 	free(datum);
 	return 0;
 	if (cladatum == NULL) {
 		return 0;
 	}
-	hashtab_map(cladatum->permissions.table, perm_destroy, 0);
+	(void)hashtab_map(cladatum->permissions.table, perm_destroy, 0);
 	hashtab_destroy(cladatum->permissions.table);
 	constraint = cladatum->constraints;
 	while (constraint) {
 	free(p->decl_val_to_struct);
 
 	for (i = 0; i < SYM_NUM; i++) {
-		hashtab_map(p->scope[i].table, scope_destroy, 0);
+		(void)hashtab_map(p->scope[i].table, scope_destroy, 0);
 		hashtab_destroy(p->scope[i].table);
 	}
 	avrule_block_list_destroy(p->global);
 {
 	int i;
 	for (i = 0; i < SYM_NUM; i++) {
-		hashtab_map(symtab[i].table, destroy_f[i], 0);
+		(void)hashtab_map(symtab[i].table, destroy_f[i], 0);
 		hashtab_destroy(symtab[i].table);
 	}
 }
 				if (p->policy_type != POLICY_KERN &&
 				    type_set_read(e->type_names, fp))
 					return -1;
+				else if (p->policy_type == POLICY_KERN &&
+					 p->policyvers >= POLICYDB_VERSION_CONSTRAINT_NAMES &&
+					 type_set_read(e->type_names, fp))
+					return -1;
 				break;
 			default:
 				return -1;
 		cladatum->default_range = le32_to_cpu(buf[2]);
 	}
 
+	if ((p->policy_type == POLICY_KERN &&
+	     p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) ||
+	    (p->policy_type == POLICY_BASE &&
+	     p->policyvers >= MOD_POLICYDB_VERSION_DEFAULT_TYPE)) {
+		rc = next_entry(buf, fp, sizeof(uint32_t));
+		if (rc < 0)
+			goto bad;
+		cladatum->default_type = le32_to_cpu(buf[0]);
+	}
+
 	if (hashtab_insert(h, key, cladatum))
 		goto bad;
 
 	uint32_t buf[1], num_blocks, nel;
 	int rc;
 
+	assert(*block == NULL);
+
 	rc = next_entry(buf, fp, sizeof(uint32_t));
 	if (rc < 0)
 		return -1;
 			 * decl chain in its correct order */
 			if (curblock->branch_list == NULL) {
 				curblock->branch_list = curdecl;
-			} else if (last_decl != NULL) {
+			} else {
+				assert(last_decl);
 				last_decl->next = curdecl;
 			}
 			last_decl = curdecl;
 
 		if (*block == NULL) {
 			*block = curblock;
-		} else if (last_block != NULL) {
+		} else {
+			assert(last_block);
 			last_block->next = curblock;
 		}
 		last_block = curblock;
  * Implementation of the security services.
  */
 
+/* Initial sizes malloc'd for sepol_compute_av_reason_buffer() support */
+#define REASON_BUF_SIZE 2048
+#define EXPR_BUF_SIZE 1024
+#define STACK_LEN 32
+
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sepol/policydb/services.h>
 #include <sepol/policydb/conditional.h>
 #include <sepol/policydb/flask.h>
+#include <sepol/policydb/util.h>
 
 #include "debug.h"
 #include "private.h"
 static sidtab_t mysidtab, *sidtab = &mysidtab;
 static policydb_t mypolicydb, *policydb = &mypolicydb;
 
+/* Used by sepol_compute_av_reason_buffer() to keep track of entries */
+static int reason_buf_used;
+static int reason_buf_len;
+
+/* Stack services for RPN to infix conversion. */
+static char **stack;
+static int stack_len;
+static int next_stack_entry;
+
+static void push(char *expr_ptr)
+{
+	if (next_stack_entry >= stack_len) {
+		char **new_stack = stack;
+		int new_stack_len;
+
+		if (stack_len == 0)
+			new_stack_len = STACK_LEN;
+		else
+			new_stack_len = stack_len * 2;
+
+		new_stack = realloc(stack, new_stack_len * sizeof(*stack));
+		if (!new_stack) {
+			ERR(NULL, "unable to allocate stack space");
+			return;
+		}
+		stack_len = new_stack_len;
+		stack = new_stack;
+	}
+	stack[next_stack_entry] = expr_ptr;
+	next_stack_entry++;
+}
+
+static char *pop(void)
+{
+	next_stack_entry--;
+	if (next_stack_entry < 0) {
+		next_stack_entry = 0;
+		ERR(NULL, "pop called with no stack entries");
+		return NULL;
+	}
+	return stack[next_stack_entry];
+}
+/* End Stack services */
+
 int hidden sepol_set_sidtab(sidtab_t * s)
 {
 	sidtab = s;
 static uint32_t latest_granting = 0;
 
 /*
- * Return the boolean value of a constraint expression 
- * when it is applied to the specified source and target 
+ * cat_expr_buf adds a string to an expression buffer and handles
+ * realloc's if buffer is too small. The array of expression text
+ * buffer pointers and its counter are globally defined here as
+ * constraint_expr_eval_reason() sets them up and cat_expr_buf
+ * updates the e_buf pointer.
+ */
+static int expr_counter;
+static char **expr_list;
+static int expr_buf_used;
+static int expr_buf_len;
+
+static void cat_expr_buf(char *e_buf, char *string)
+{
+	int len, new_buf_len;
+	char *p, *new_buf = e_buf;
+
+	while (1) {
+		p = e_buf + expr_buf_used;
+		len = snprintf(p, expr_buf_len - expr_buf_used, "%s", string);
+		if (len < 0 || len >= expr_buf_len - expr_buf_used) {
+			new_buf_len = expr_buf_len + EXPR_BUF_SIZE;
+			new_buf = realloc(e_buf, new_buf_len);
+			if (!new_buf) {
+				ERR(NULL, "failed to realloc expr buffer");
+				return;
+			}
+			/* Update new ptr in expr list and locally + new len */
+			expr_list[expr_counter] = new_buf;
+			e_buf = new_buf;
+			expr_buf_len = new_buf_len;
+		} else {
+			expr_buf_used += len;
+			return;
+		}
+	}
+}
+
+/*
+ * If the POLICY_KERN version is >= POLICYDB_VERSION_CONSTRAINT_NAMES,
+ * then for 'types' only, read the types_names->types list as it will
+ * contain a list of types and attributes that were defined in the
+ * policy source.
+ * For user and role plus types (for policy vers <
+ * POLICYDB_VERSION_CONSTRAINT_NAMES) just read the e->names list.
+ */
+static void get_name_list(constraint_expr_t *e, int type,
+							char *src, char *op, int failed)
+{
+	ebitmap_t *types;
+	int rc = 0;
+	unsigned int i;
+	char tmp_buf[128];
+	int counter = 0;
+
+	if (policydb->policy_type == POLICY_KERN &&
+			policydb->policyvers >= POLICYDB_VERSION_CONSTRAINT_NAMES &&
+			type == CEXPR_TYPE)
+		types = &e->type_names->types;
+	else
+		types = &e->names;
+
+	/* Find out how many entries */
+	for (i = ebitmap_startbit(types); i < ebitmap_length(types); i++) {
+		rc = ebitmap_get_bit(types, i);
+		if (rc == 0)
+			continue;
+		else
+			counter++;
+	}
+	snprintf(tmp_buf, sizeof(tmp_buf), "(%s%s", src, op);
+	cat_expr_buf(expr_list[expr_counter], tmp_buf);
+
+	if (counter == 0)
+		cat_expr_buf(expr_list[expr_counter], "<empty_set> ");
+	if (counter > 1)
+		cat_expr_buf(expr_list[expr_counter], " {");
+	if (counter >= 1) {
+		for (i = ebitmap_startbit(types); i < ebitmap_length(types); i++) {
+			rc = ebitmap_get_bit(types, i);
+			if (rc == 0)
+				continue;
+
+			/* Collect entries */
+			switch (type) {
+			case CEXPR_USER:
+				snprintf(tmp_buf, sizeof(tmp_buf), " %s",
+							policydb->p_user_val_to_name[i]);
+				break;
+			case CEXPR_ROLE:
+				snprintf(tmp_buf, sizeof(tmp_buf), " %s",
+							policydb->p_role_val_to_name[i]);
+				break;
+			case CEXPR_TYPE:
+				snprintf(tmp_buf, sizeof(tmp_buf), " %s",
+							policydb->p_type_val_to_name[i]);
+				break;
+			}
+			cat_expr_buf(expr_list[expr_counter], tmp_buf);
+		}
+	}
+	if (counter > 1)
+		cat_expr_buf(expr_list[expr_counter], " }");
+	if (failed)
+		cat_expr_buf(expr_list[expr_counter], " -Fail-) ");
+	else
+		cat_expr_buf(expr_list[expr_counter], ") ");
+
+	return;
+}
+
+static void msgcat(char *src, char *tgt, char *op, int failed)
+{
+	char tmp_buf[128];
+	if (failed)
+		snprintf(tmp_buf, sizeof(tmp_buf), "(%s %s %s -Fail-) ",
+				src, op, tgt);
+	else
+		snprintf(tmp_buf, sizeof(tmp_buf), "(%s %s %s) ",
+				src, op, tgt);
+	cat_expr_buf(expr_list[expr_counter], tmp_buf);
+}
+
+/* Returns a buffer with class, statement type and permissions */
+static char *get_class_info(sepol_security_class_t tclass,
+							constraint_node_t *constraint,
+							context_struct_t *xcontext)
+{
+	constraint_expr_t *e;
+	int mls, state_num;
+
+	/* Find if MLS statement or not */
+	mls = 0;
+	for (e = constraint->expr; e; e = e->next) {
+		if (e->attr >= CEXPR_L1L2) {
+			mls = 1;
+			break;
+		}
+	}
+
+	/* Determine statement type */
+	char *statements[] = {
+		"constrain ",			/* 0 */
+		"mlsconstrain ",		/* 1 */
+		"validatetrans ",		/* 2 */
+		"mlsvalidatetrans ",	/* 3 */
+		0 };
+
+	if (xcontext == NULL)
+		state_num = mls + 0;
+	else
+		state_num = mls + 2;
+
+	int class_buf_len = 0;
+	int new_class_buf_len;
+	int len, buf_used;
+	char *class_buf = NULL, *p;
+	char *new_class_buf = NULL;
+
+	while (1) {
+		new_class_buf_len = class_buf_len + EXPR_BUF_SIZE;
+		new_class_buf = realloc(class_buf, new_class_buf_len);
+			if (!new_class_buf)
+				return NULL;
+		class_buf_len = new_class_buf_len;
+		class_buf = new_class_buf;
+		buf_used = 0;
+		p = class_buf;
+
+		/* Add statement type */
+		len = snprintf(p, class_buf_len - buf_used, "%s", statements[state_num]);
+		if (len < 0 || len >= class_buf_len - buf_used)
+			continue;
+
+		/* Add class entry */
+		p += len;
+		buf_used += len;
+		len = snprintf(p, class_buf_len - buf_used, "%s ",
+				policydb->p_class_val_to_name[tclass - 1]);
+		if (len < 0 || len >= class_buf_len - buf_used)
+			continue;
+
+		/* Add permission entries */
+		p += len;
+		buf_used += len;
+		len = snprintf(p, class_buf_len - buf_used, "{%s } (",
+				sepol_av_to_string(policydb, tclass, constraint->permissions));
+		if (len < 0 || len >= class_buf_len - buf_used)
+			continue;
+		break;
+	}
+	return class_buf;
+}
+
+/*
+ * Modified version of constraint_expr_eval that will process each
+ * constraint as before but adds the information to text buffers that
+ * will hold various components. The expression will be in RPN format,
+ * therefore there is a stack based RPN to infix converter to produce
+ * the final readable constraint.
+ *
+ * Return the boolean value of a constraint expression
+ * when it is applied to the specified source and target
  * security contexts.
  *
  * xcontext is a special beast...  It is used by the validatetrans rules
  * only.  For these rules, scontext is the context before the transition,
- * tcontext is the context after the transition, and xcontext is the context
- * of the process performing the transition.  All other callers of
- * constraint_expr_eval should pass in NULL for xcontext.
+ * tcontext is the context after the transition, and xcontext is the
+ * context of the process performing the transition.  All other callers
+ * of constraint_expr_eval_reason should pass in NULL for xcontext.
+ *
+ * This function will also build a buffer as the constraint is processed
+ * for analysis. If this option is not required, then:
+ *      'tclass' should be '0' and r_buf MUST be NULL.
  */
-static int constraint_expr_eval(context_struct_t * scontext,
-				context_struct_t * tcontext,
-				context_struct_t * xcontext,
-				constraint_expr_t * cexpr)
+static int constraint_expr_eval_reason(context_struct_t *scontext,
+				context_struct_t *tcontext,
+				context_struct_t *xcontext,
+				sepol_security_class_t tclass,
+				constraint_node_t *constraint,
+				char **r_buf,
+				unsigned int flags)
 {
 	uint32_t val1, val2;
 	context_struct_t *c;
 	constraint_expr_t *e;
 	int s[CEXPR_MAXDEPTH];
 	int sp = -1;
+	char tmp_buf[128];
+
+/*
+ * Define the s_t_x_num values that make up r1, t2 etc. in text strings
+ * Set 1 = source, 2 = target, 3 = xcontext for validatetrans
+ */
+#define SOURCE  1
+#define TARGET  2
+#define XTARGET 3
+
+	int s_t_x_num = SOURCE;
+
+	/* Set 0 = fail, u = CEXPR_USER, r = CEXPR_ROLE, t = CEXPR_TYPE */
+	int u_r_t = 0;
 
-	for (e = cexpr; e; e = e->next) {
+	char *src = NULL;
+	char *tgt = NULL;
+	int rc = 0, x;
+	char *class_buf = NULL;
+
+	class_buf = get_class_info(tclass, constraint, xcontext);
+	if (!class_buf) {
+		ERR(NULL, "failed to allocate class buffer");
+		return -ENOMEM;
+	}
+
+	/* Original function but with buffer support */
+	int expr_list_len = 0;
+	expr_counter = 0;
+	expr_list = NULL;
+	for (e = constraint->expr; e; e = e->next) {
+		/* Allocate a stack to hold expression buffer entries */
+		if (expr_counter >= expr_list_len) {
+			char **new_expr_list = expr_list;
+			int new_expr_list_len;
+
+			if (expr_list_len == 0)
+				new_expr_list_len = STACK_LEN;
+			else
+				new_expr_list_len = expr_list_len * 2;
+
+			new_expr_list = realloc(expr_list,
+					new_expr_list_len * sizeof(*expr_list));
+			if (!new_expr_list) {
+				ERR(NULL, "failed to allocate expr buffer stack");
+				rc = -ENOMEM;
+				goto out;
+			}
+			expr_list_len = new_expr_list_len;
+			expr_list = new_expr_list;
+		}
+
+		/*
+		 * malloc a buffer to store each expression text component. If
+		 * buffer is too small cat_expr_buf() will realloc extra space.
+		 */
+		expr_buf_len = EXPR_BUF_SIZE;
+		expr_list[expr_counter] = malloc(expr_buf_len);
+		if (!expr_list[expr_counter]) {
+			ERR(NULL, "failed to allocate expr buffer");
+			rc = -ENOMEM;
+			goto out;
+		}
+		expr_buf_used = 0;
+
+		/* Now process each expression of the constraint */
 		switch (e->expr_type) {
 		case CEXPR_NOT:
 			BUG_ON(sp < 0);
 			s[sp] = !s[sp];
+			cat_expr_buf(expr_list[expr_counter], "not");
 			break;
 		case CEXPR_AND:
 			BUG_ON(sp < 1);
 			sp--;
 			s[sp] &= s[sp + 1];
+			cat_expr_buf(expr_list[expr_counter], "and");
 			break;
 		case CEXPR_OR:
 			BUG_ON(sp < 1);
 			sp--;
 			s[sp] |= s[sp + 1];
+			cat_expr_buf(expr_list[expr_counter], "or");
 			break;
 		case CEXPR_ATTR:
 			if (sp == (CEXPR_MAXDEPTH - 1))
-				return 0;
+				goto out;
+
 			switch (e->attr) {
 			case CEXPR_USER:
 				val1 = scontext->user;
 				val2 = tcontext->user;
+				free(src); src = strdup("u1");
+				free(tgt); tgt = strdup("u2");
 				break;
 			case CEXPR_TYPE:
 				val1 = scontext->type;
 				val2 = tcontext->type;
+				free(src); src = strdup("t1");
+				free(tgt); tgt = strdup("t2");
 				break;
 			case CEXPR_ROLE:
 				val1 = scontext->role;
 				val2 = tcontext->role;
 				r1 = policydb->role_val_to_struct[val1 - 1];
 				r2 = policydb->role_val_to_struct[val2 - 1];
+				free(src); src = strdup("r1");
+				free(tgt); tgt = strdup("r2");
+
 				switch (e->op) {
 				case CEXPR_DOM:
-					s[++sp] =
-					    ebitmap_get_bit(&r1->dominates,
-							    val2 - 1);
+					s[++sp] = ebitmap_get_bit(&r1->dominates, val2 - 1);
+					msgcat(src, tgt, "dom", s[sp] == 0);
+					expr_counter++;
 					continue;
 				case CEXPR_DOMBY:
-					s[++sp] =
-					    ebitmap_get_bit(&r2->dominates,
-							    val1 - 1);
+					s[++sp] = ebitmap_get_bit(&r2->dominates, val1 - 1);
+					msgcat(src, tgt, "domby", s[sp] == 0);
+					expr_counter++;
 					continue;
 				case CEXPR_INCOMP:
-					s[++sp] =
-					    (!ebitmap_get_bit
-					     (&r1->dominates, val2 - 1)
-					     && !ebitmap_get_bit(&r2->dominates,
-								 val1 - 1));
+					s[++sp] = (!ebitmap_get_bit(&r1->dominates, val2 - 1)
+						 && !ebitmap_get_bit(&r2->dominates, val1 - 1));
+					msgcat(src, tgt, "incomp", s[sp] == 0);
+					expr_counter++;
 					continue;
 				default:
 					break;
 			case CEXPR_L1L2:
 				l1 = &(scontext->range.level[0]);
 				l2 = &(tcontext->range.level[0]);
+				free(src); src = strdup("l1");
+				free(tgt); tgt = strdup("l2");
 				goto mls_ops;
 			case CEXPR_L1H2:
 				l1 = &(scontext->range.level[0]);
 				l2 = &(tcontext->range.level[1]);
+				free(src); src = strdup("l1");
+				free(tgt); tgt = strdup("h2");
 				goto mls_ops;
 			case CEXPR_H1L2:
 				l1 = &(scontext->range.level[1]);
 				l2 = &(tcontext->range.level[0]);
+				free(src); src = strdup("h1");
+				free(tgt); tgt = strdup("l2");
 				goto mls_ops;
 			case CEXPR_H1H2:
 				l1 = &(scontext->range.level[1]);
 				l2 = &(tcontext->range.level[1]);
+				free(src); src = strdup("h1");
+				free(tgt); tgt = strdup("h2");
 				goto mls_ops;
 			case CEXPR_L1H1:
 				l1 = &(scontext->range.level[0]);
 				l2 = &(scontext->range.level[1]);
+				free(src); src = strdup("l1");
+				free(tgt); tgt = strdup("h1");
 				goto mls_ops;
 			case CEXPR_L2H2:
 				l1 = &(tcontext->range.level[0]);
 				l2 = &(tcontext->range.level[1]);
-				goto mls_ops;
-			      mls_ops:
+				free(src); src = strdup("l2");
+				free(tgt); tgt = strdup("h2");
+mls_ops:
 				switch (e->op) {
 				case CEXPR_EQ:
 					s[++sp] = mls_level_eq(l1, l2);
+					msgcat(src, tgt, "eq", s[sp] == 0);
+					expr_counter++;
 					continue;
 				case CEXPR_NEQ:
 					s[++sp] = !mls_level_eq(l1, l2);
+					msgcat(src, tgt, "!=", s[sp] == 0);
+					expr_counter++;
 					continue;
 				case CEXPR_DOM:
 					s[++sp] = mls_level_dom(l1, l2);
+					msgcat(src, tgt, "dom", s[sp] == 0);
+					expr_counter++;
 					continue;
 				case CEXPR_DOMBY:
 					s[++sp] = mls_level_dom(l2, l1);
+					msgcat(src, tgt, "domby", s[sp] == 0);
+					expr_counter++;
 					continue;
 				case CEXPR_INCOMP:
 					s[++sp] = mls_level_incomp(l2, l1);
+					msgcat(src, tgt, "incomp", s[sp] == 0);
+					expr_counter++;
 					continue;
 				default:
 					BUG();
-					return 0;
+					goto out;
 				}
 				break;
 			default:
 				BUG();
-				return 0;
+				goto out;
 			}
 
 			switch (e->op) {
 			case CEXPR_EQ:
 				s[++sp] = (val1 == val2);
+				msgcat(src, tgt, "==", s[sp] == 0);
 				break;
 			case CEXPR_NEQ:
 				s[++sp] = (val1 != val2);
+				msgcat(src, tgt, "!=", s[sp] == 0);
 				break;
 			default:
 				BUG();
-				return 0;
+				goto out;
 			}
 			break;
 		case CEXPR_NAMES:
 			if (sp == (CEXPR_MAXDEPTH - 1))
-				return 0;
+				goto out;
+			s_t_x_num = SOURCE;
 			c = scontext;
-			if (e->attr & CEXPR_TARGET)
+			if (e->attr & CEXPR_TARGET) {
+				s_t_x_num = TARGET;
 				c = tcontext;
-			else if (e->attr & CEXPR_XTARGET) {
+			} else if (e->attr & CEXPR_XTARGET) {
+				s_t_x_num = XTARGET;
 				c = xcontext;
-				if (!c) {
-					BUG();
-					return 0;
-				}
 			}
-			if (e->attr & CEXPR_USER)
+			if (!c) {
+				BUG();
+				goto out;
+			}
+			if (e->attr & CEXPR_USER) {
+				u_r_t = CEXPR_USER;
 				val1 = c->user;
-			else if (e->attr & CEXPR_ROLE)
+				snprintf(tmp_buf, sizeof(tmp_buf), "u%d ", s_t_x_num);
+				free(src); src = strdup(tmp_buf);
+			} else if (e->attr & CEXPR_ROLE) {
+				u_r_t = CEXPR_ROLE;
 				val1 = c->role;
-			else if (e->attr & CEXPR_TYPE)
+				snprintf(tmp_buf, sizeof(tmp_buf), "r%d ", s_t_x_num);
+				free(src); src = strdup(tmp_buf);
+			} else if (e->attr & CEXPR_TYPE) {
+				u_r_t = CEXPR_TYPE;
 				val1 = c->type;
-			else {
+				snprintf(tmp_buf, sizeof(tmp_buf), "t%d ", s_t_x_num);
+				free(src); src = strdup(tmp_buf);
+			} else {
 				BUG();
-				return 0;
+				goto out;
 			}
 
 			switch (e->op) {
 			case CEXPR_EQ:
 				s[++sp] = ebitmap_get_bit(&e->names, val1 - 1);
+				get_name_list(e, u_r_t, src, "==", s[sp] == 0);
 				break;
+
 			case CEXPR_NEQ:
 				s[++sp] = !ebitmap_get_bit(&e->names, val1 - 1);
+				get_name_list(e, u_r_t, src, "!=", s[sp] == 0);
 				break;
 			default:
 				BUG();
-				return 0;
+				goto out;
 			}
 			break;
 		default:
 			BUG();
-			return 0;
+			goto out;
 		}
+		expr_counter++;
 	}
 
-	BUG_ON(sp != 0);
-	return s[0];
+	/*
+	 * At this point each expression of the constraint is in
+	 * expr_list[n+1] and in RPN format. Now convert to 'infix'
+	 */
+
+	/*
+	 * Save expr count but zero expr_counter to detect if
+	 * 'BUG(); goto out;' was called as we need to release any used
+	 * expr_list malloc's. Normally they are released by the RPN to
+	 * infix code.
+	 */
+	int expr_count = expr_counter;
+	expr_counter = 0;
+
+	/*
+	 * The array of expression answer buffer pointers and counter.
+	 * Generate the same number of answer buffer entries as expression
+	 * buffers (as there will never be more).
+	 */
+	char **answer_list;
+	int answer_counter = 0;
+
+	answer_list = malloc(expr_count * sizeof(*answer_list));
+	if (!answer_list) {
+		ERR(NULL, "failed to allocate answer stack");
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	/* The pop operands */
+	char *a;
+	char *b;
+	int a_len, b_len;
+
+	/* Convert constraint from RPN to infix notation. */
+	for (x = 0; x != expr_count; x++) {
+		if (strncmp(expr_list[x], "and", 3) == 0 || strncmp(expr_list[x],
+					"or", 2) == 0) {
+			b = pop();
+			b_len = strlen(b);
+			a = pop();
+			a_len = strlen(a);
+
+			/* get a buffer to hold the answer */
+			answer_list[answer_counter] = malloc(a_len + b_len + 8);
+			if (!answer_list[answer_counter]) {
+				ERR(NULL, "failed to allocate answer buffer");
+				rc = -ENOMEM;
+				goto out;
+			}
+			memset(answer_list[answer_counter], '\0', a_len + b_len + 8);
+
+			sprintf(answer_list[answer_counter], "%s %s %s", a,
+					expr_list[x], b);
+			push(answer_list[answer_counter++]);
+			free(a);
+			free(b);
+		} else if (strncmp(expr_list[x], "not", 3) == 0) {
+			b = pop();
+			b_len = strlen(b);
+
+			answer_list[answer_counter] = malloc(b_len + 8);
+			if (!answer_list[answer_counter]) {
+				ERR(NULL, "failed to allocate answer buffer");
+				rc = -ENOMEM;
+				goto out;
+			}
+			memset(answer_list[answer_counter], '\0', b_len + 8);
+
+			if (strncmp(b, "not", 3) == 0)
+				sprintf(answer_list[answer_counter], "%s (%s)",
+						expr_list[x], b);
+			else
+				sprintf(answer_list[answer_counter], "%s%s",
+						expr_list[x], b);
+			push(answer_list[answer_counter++]);
+			free(b);
+		} else {
+			push(expr_list[x]);
+		}
+	}
+	/* Get the final answer from tos and build constraint text */
+	a = pop();
+
+	/* Constraint calculation: rc = 0 is denied, rc = 1 is granted */
+	sprintf(tmp_buf, "Constraint %s\n", s[0] ? "GRANTED" : "DENIED");
+
+	int len, new_buf_len;
+	char *p, **new_buf = r_buf;
+	/*
+	 * These contain the constraint components that are added to the
+	 * callers reason buffer.
+	 */
+	char *buffers[] = { class_buf, a, "); ", tmp_buf, 0 };
+
+	/*
+	 * This will add the constraints to the callers reason buffer (who is
+	 * responsible for freeing the memory). It will handle any realloc's
+	 * should the buffer be too short.
+	 * The reason_buf_used and reason_buf_len counters are defined
+	 * globally as multiple constraints can be in the buffer.
+	 */
+
+	if (r_buf && ((s[0] == 0) || ((s[0] == 1 &&
+				(flags & SHOW_GRANTED) == SHOW_GRANTED)))) {
+		for (x = 0; buffers[x] != NULL; x++) {
+			while (1) {
+				p = *r_buf + reason_buf_used;
+				len = snprintf(p, reason_buf_len - reason_buf_used,
+						"%s", buffers[x]);
+				if (len < 0 || len >= reason_buf_len - reason_buf_used) {
+					new_buf_len = reason_buf_len + REASON_BUF_SIZE;
+					*new_buf = realloc(*r_buf, new_buf_len);
+					if (!new_buf) {
+						ERR(NULL, "failed to realloc reason buffer");
+						goto out1;
+					}
+					**r_buf = **new_buf;
+					reason_buf_len = new_buf_len;
+					continue;
+				} else {
+					reason_buf_used += len;
+					break;
+				}
+			}
+		}
+	}
+
+out1:
+	rc = s[0];
+	free(a);
+
+out:
+	free(class_buf);
+	free(src);
+	free(tgt);
+
+	if (expr_counter) {
+		for (x = 0; expr_list[x] != NULL; x++)
+			free(expr_list[x]);
+	}
+	return rc;
 }
 
 /*
 				     sepol_security_class_t tclass,
 				     sepol_access_vector_t requested,
 				     struct sepol_av_decision *avd,
-				     unsigned int *reason)
+				     unsigned int *reason,
+				     char **r_buf,
+					 unsigned int flags)
 {
 	constraint_node_t *constraint;
 	struct role_allow *ra;
 	constraint = tclass_datum->constraints;
 	while (constraint) {
 		if ((constraint->permissions & (avd->allowed)) &&
-		    !constraint_expr_eval(scontext, tcontext, NULL,
-					  constraint->expr)) {
+		    !constraint_expr_eval_reason(scontext, tcontext, NULL,
+					  tclass, constraint, r_buf, flags)) {
 			avd->allowed =
 			    (avd->allowed) & ~(constraint->permissions);
 		}
 
 	constraint = tclass_datum->validatetrans;
 	while (constraint) {
-		if (!constraint_expr_eval(ocontext, ncontext, tcontext,
-					  constraint->expr)) {
+		if (!constraint_expr_eval_reason(ocontext, ncontext, tcontext,
+					  0, constraint, NULL, 0)) {
 			return -EPERM;
 		}
 		constraint = constraint->next;
 	}
 
 	rc = context_struct_compute_av(scontext, tcontext, tclass,
-				       requested, avd, reason);
+					requested, avd, reason, NULL, 0);
       out:
 	return rc;
 }
 
+/*
+ * sepol_compute_av_reason_buffer - the reason buffer is malloc'd to
+ * REASON_BUF_SIZE. If the buffer size is exceeded, then it is realloc'd
+ * in the constraint_expr_eval_reason() function.
+ */
+int hidden sepol_compute_av_reason_buffer(sepol_security_id_t ssid,
+				   sepol_security_id_t tsid,
+				   sepol_security_class_t tclass,
+				   sepol_access_vector_t requested,
+				   struct sepol_av_decision *avd,
+				   unsigned int *reason,
+				   char **reason_buf,
+				   unsigned int flags)
+{
+	context_struct_t *scontext = 0, *tcontext = 0;
+	int rc = 0;
+
+	scontext = sepol_sidtab_search(sidtab, ssid);
+	if (!scontext) {
+		ERR(NULL, "unrecognized SID %d", ssid);
+		rc = -EINVAL;
+		goto out;
+	}
+	tcontext = sepol_sidtab_search(sidtab, tsid);
+	if (!tcontext) {
+		ERR(NULL, "unrecognized SID %d", tsid);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	/*
+	 * Set the buffer to NULL as constraints may not be processed.
+	 * If a buffer is required, then the routines in
+	 * constraint_expr_eval_reason will realloc in REASON_BUF_SIZE
+	 * chunks (as it gets called for each constraint processed).
+	 * We just make sure these start from zero.
+	 */
+	*reason_buf = NULL;
+	reason_buf_used = 0;
+	reason_buf_len = 0;
+
+	rc = context_struct_compute_av(scontext, tcontext, tclass,
+					   requested, avd, reason, reason_buf, flags);
+out:
+	return rc;
+}
+
 int hidden sepol_compute_av(sepol_security_id_t ssid,
 			    sepol_security_id_t tsid,
 			    sepol_security_class_t tclass,
 }
 
 /*
+ * Return a class ID associated with the class string specified by
+ * class_name.
+ */
+int hidden sepol_string_to_security_class(const char *class_name,
+			sepol_security_class_t *tclass)
+{
+	char *class = NULL;
+	sepol_security_class_t id;
+
+	for (id = 1;; id++) {
+		class = policydb->p_class_val_to_name[id - 1];
+		if (class == NULL) {
+			ERR(NULL, "could not convert %s to class id", class_name);
+			return STATUS_ERR;
+		}
+		if ((strcmp(class, class_name)) == 0) {
+			*tclass = id;
+			return STATUS_SUCCESS;
+		}
+	}
+}
+
+/*
+ * Return access vector bit associated with the class ID and permission
+ * string.
+ */
+int hidden sepol_string_to_av_perm(sepol_security_class_t tclass,
+					const char *perm_name,
+					sepol_access_vector_t *av)
+{
+	class_datum_t *tclass_datum;
+	perm_datum_t *perm_datum;
+
+	if (!tclass || tclass > policydb->p_classes.nprim) {
+		ERR(NULL, "unrecognized class %d", tclass);
+		return -EINVAL;
+	}
+	tclass_datum = policydb->class_val_to_struct[tclass - 1];
+
+	/* Check for unique perms then the common ones (if any) */
+	perm_datum = (perm_datum_t *)
+			hashtab_search(tclass_datum->permissions.table,
+			(hashtab_key_t)perm_name);
+	if (perm_datum != NULL) {
+		*av = 0x1 << (perm_datum->s.value - 1);
+		return STATUS_SUCCESS;
+	}
+
+	if (tclass_datum->comdatum == NULL)
+		goto out;
+
+	perm_datum = (perm_datum_t *)
+			hashtab_search(tclass_datum->comdatum->permissions.table,
+			(hashtab_key_t)perm_name);
+
+	if (perm_datum != NULL) {
+		*av = 0x1 << (perm_datum->s.value - 1);
+		return STATUS_SUCCESS;
+	}
+out:
+	ERR(NULL, "could not convert %s to av bit", perm_name);
+	return STATUS_ERR;
+}
+
+/*
  * Write the security context string representation of 
  * the context associated with `sid' into a dynamically
  * allocated string of the correct size.  Set `*scontext'
 		return -ENOMEM;
 
 	if (policydb_read(&newpolicydb, fp, 1)) {
-		policydb_destroy(&newpolicydb);
+		policydb_destroy(&mypolicydb);
 		return -EINVAL;
 	}
 
 			rc = context_struct_compute_av(fromcon, &usercon,
 						       SECCLASS_PROCESS,
 						       PROCESS__TRANSITION,
-						       &avd, &reason);
+						       &avd, &reason, NULL, 0);
 			if (rc || !(avd.allowed & PROCESS__TRANSITION))
 				continue;
 			rc = sepol_sidtab_context_to_sid(sidtab, &usercon,
 				if (ebitmap_write(&e->names, fp)) {
 					return POLICYDB_ERROR;
 				}
-				if (p->policy_type != POLICY_KERN &&
-				    type_set_write(e->type_names, fp)) {
+				if ((p->policy_type != POLICY_KERN &&
+						type_set_write(e->type_names, fp)) ||
+						(p->policy_type == POLICY_KERN &&
+						(p->policyvers >= POLICYDB_VERSION_CONSTRAINT_NAMES) &&
+						type_set_write(e->type_names, fp))) {
 					return POLICYDB_ERROR;
 				}
 				break;
 			return POLICYDB_ERROR;
 	}
 
+	if ((p->policy_type == POLICY_KERN &&
+	     p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) ||
+	    (p->policy_type == POLICY_BASE &&
+	     p->policyvers >= MOD_POLICYDB_VERSION_DEFAULT_TYPE)) {
+		buf[0] = cpu_to_le32(cladatum->default_type);
+		items = put_entry(buf, sizeof(uint32_t), 1, fp);
+		if (items != 1)
+			return POLICYDB_ERROR;
+	}
+
 	return POLICYDB_SUCCESS;
 }
 
 	uint32_t static_buf[32], *dyn_buf = NULL, *buf;
 	size_t key_len = strlen(key);
 	unsigned int items = 2 + scope->decl_ids_len, i;
+	int rc;
 
+	buf = static_buf;
 	if (items >= (sizeof(static_buf) / 4)) {
 		/* too many things required, so dynamically create a
 		 * buffer.  this would have been easier with C99's
 		 * dynamic arrays... */
-		if ((dyn_buf = malloc(items * sizeof(*dyn_buf))) == NULL) {
-			return POLICYDB_ERROR;
-		}
+		rc = POLICYDB_ERROR;
+		dyn_buf = malloc(items * sizeof(*dyn_buf));
+		if (!dyn_buf)
+			goto err;
 		buf = dyn_buf;
-	} else {
-		buf = static_buf;
 	}
 	buf[0] = cpu_to_le32(key_len);
+
+	rc = POLICYDB_ERROR;
 	if (put_entry(buf, sizeof(*buf), 1, fp) != 1 ||
-	    put_entry(key, 1, key_len, fp) != key_len) {
-		free(dyn_buf);
-		return POLICYDB_ERROR;
-	}
+	    put_entry(key, 1, key_len, fp) != key_len)
+		goto err;
 	buf[0] = cpu_to_le32(scope->scope);
 	buf[1] = cpu_to_le32(scope->decl_ids_len);
-	for (i = 0; i < scope->decl_ids_len; i++) {
+
+	for (i = 0; i < scope->decl_ids_len; i++)
 		buf[2 + i] = cpu_to_le32(scope->decl_ids[i]);
-	}
-	if (put_entry(buf, sizeof(*buf), items, fp) != items) {
-		free(dyn_buf);
-		return POLICYDB_ERROR;
-	}
+
+	rc = POLICYDB_ERROR;
+	if (put_entry(buf, sizeof(*buf), items, fp) != items)
+		goto err;
+	rc = POLICYDB_SUCCESS;
+err:
 	free(dyn_buf);
-	return POLICYDB_SUCCESS;
+	return rc;
 }
 
 static int type_attr_uncount(hashtab_key_t key __attribute__ ((unused)),
 		    ((p->policy_type == POLICY_KERN) ||
 		     (p->policy_type != POLICY_KERN &&
 		      p->policyvers < MOD_POLICYDB_VERSION_ROLEATTRIB)))
-			hashtab_map(p->symtab[i].table, role_attr_uncount, &buf[1]);
+			(void)hashtab_map(p->symtab[i].table, role_attr_uncount, &buf[1]);
 
 		buf[1] = cpu_to_le32(buf[1]);
 		items = put_entry(buf, sizeof(uint32_t), 2, fp);

tests/policies/test-cond/refpolicy-base.conf

 role sysadm_r;
 role staff_r;
 role user_r;
+role secadm_r;
 	typeattribute kernel_t domain;
 	allow kernel_t self:dir { read getattr lock search ioctl };
 	allow kernel_t self:lnk_file { read getattr lock ioctl };

tests/policies/test-deps/base-metreq.conf

 
 type net_foo_t, foo;
 type sys_foo_t, foo, system;
+role system_r;
 role system_r types sys_foo_t;
 
 type user_t, domain;
+role user_r;
 role user_r types user_t;
 
 type sysadm_t, domain, system;
+role sysadm_r;
 role sysadm_r types sysadm_t;
 
 type system_t, domain, system, foo;
+role system_r;
 role system_r types { system_t sys_foo_t };
 
 type file_t;

tests/policies/test-deps/base-notmetreq.conf

 
 type net_foo_t, foo;
 type sys_foo_t, foo, system;
+role system_r;
 role system_r types sys_foo_t;
 
 type user_t, domain;
+role user_r;
 role user_r types user_t;
 
 type sysadm_t, domain, system;
+role sysadm_r;
 role sysadm_r types sysadm_t;
 
 type system_t, domain, system, foo;
+role system_r;
 role system_r types { system_t sys_foo_t };
 
 type file_t;

tests/policies/test-expander/alias-base.conf

 type fs_t;
 type system_t;
 type user_t;
+role system_r;
+role user_r;
+role sysadm_r;
 role system_r types system_t;
 role user_r types user_t;
 role sysadm_r types system_t;

tests/policies/test-expander/base-base-only.conf

 
 attribute myattr;
 type mytype_t;
+role myrole_r;
 role myrole_r types mytype_t;
 bool mybool true;
 gen_user(myuser_u,, myrole_r, s0, s0 - s0:c0)

tests/policies/test-expander/role-base.conf

 
 # Role mapping test
 type role_check_1_1_t;
+role role_check_1;
 role role_check_1 types role_check_1_1_t;
 
 ########
 type fs_t;
 type system_t;
 type user_t;
+role system_r;
+role user_r;
+role sysadm_r;
 role system_r types system_t;
 role user_r types user_t;
 role sysadm_r types system_t;

tests/policies/test-expander/small-base.conf

 
 type net_foo_t, foo;
 type sys_foo_t, foo, system;
+role system_r;
 role system_r types sys_foo_t;
 
 type user_t, domain;
+role user_r;
 role user_r types user_t;
 
 type sysadm_t, domain, system;
+role sysadm_r;
 role sysadm_r types sysadm_t;
 
 type system_t, domain, system, foo;

tests/policies/test-expander/user-base.conf

 # User mapping test
 type user_check_1_1_t;
 type user_check_1_2_t;
+role user_check_1_1_r;
+role user_check_1_2_r;
 role user_check_1_1_r types user_check_1_1_t;
 role user_check_1_2_r types user_check_1_2_t;
 
 type fs_t;
 type system_t;
 type user_t;
+role system_r;
+role user_r;
+role sysadm_r;
 role system_r types system_t;
 role user_r types user_t;
 role sysadm_r types system_t;

tests/policies/test-linker/module1.conf

 typeattribute g_m1_type_2 g_m1_attr_1;
 
 #add role in module test
+role g_m1_role_1;
 role g_m1_role_1 types g_m1_type_1;
 
 # test for attr declared in base, added to in module
 
 #add type to base role test
 role g_b_role_2 types g_m1_type_1;
+role g_b_role_3;
 role g_b_role_3 types g_m1_type_2;
 
 #add type to base optional role test
+role o1_b_role_2;
 role o1_b_role_2 types g_m1_type_1;
 
 #optional base role w/ adds in 2 modules
+role o4_b_role_1;
 role o4_b_role_1 types g_m1_type_2;
 
 # attr a added to in base optional, declared/added to in module, added to in other module
 	type o1_m1_type_2, o1_m1_attr_1;
 	
 	type o1_m1_type_1;
+	role o1_m1_role_1;
 	role o1_m1_role_1 types o1_m1_type_1;
 
 	type o1_m1_type_3;
 	
 	type tag_o2_m1;
 
+	role g_b_role_4;
 	role g_b_role_4 types g_m1_type_2;
 }
 
 	type tag_o3_m1;
 
 	type o3_m1_type_1;	
+	role o3_b_role_1;
         role o3_b_role_1 types o3_m1_type_1;
 
 	type o3_m1_type_2, g_b_attr_6;

tests/policies/test-linker/module2.conf

 type tag_g_m2;
 
 type g_m2_type_1;
+role g_m2_role_1;