Blame SOURCES/libsepol-rhat.patch

c0276d
diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h
c0276d
index c27275e..0165eed 100644
c0276d
--- a/libsepol/include/sepol/policydb/policydb.h
c0276d
+++ b/libsepol/include/sepol/policydb/policydb.h
c0276d
@@ -683,10 +683,11 @@ extern int policydb_set_target_platform(policydb_t *p, int platform);
c0276d
 #define POLICYDB_VERSION_ROLETRANS	26
c0276d
 #define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS	27
c0276d
 #define POLICYDB_VERSION_DEFAULT_TYPE	28
c0276d
+#define POLICYDB_VERSION_CONSTRAINT_NAMES	29
c0276d
 
c0276d
 /* Range of policy versions we understand*/
c0276d
 #define POLICYDB_VERSION_MIN	POLICYDB_VERSION_BASE
c0276d
-#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_DEFAULT_TYPE
c0276d
+#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_CONSTRAINT_NAMES
c0276d
 
c0276d
 /* Module versions and specific changes*/
c0276d
 #define MOD_POLICYDB_VERSION_BASE		4
c0276d
@@ -704,9 +705,10 @@ extern int policydb_set_target_platform(policydb_t *p, int platform);
c0276d
 #define MOD_POLICYDB_VERSION_TUNABLE_SEP	14
c0276d
 #define MOD_POLICYDB_VERSION_NEW_OBJECT_DEFAULTS	15
c0276d
 #define MOD_POLICYDB_VERSION_DEFAULT_TYPE	16
c0276d
+#define MOD_POLICYDB_VERSION_CONSTRAINT_NAMES	17
c0276d
 
c0276d
 #define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE
c0276d
-#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_DEFAULT_TYPE
c0276d
+#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_CONSTRAINT_NAMES
c0276d
 
c0276d
 #define POLICYDB_CONFIG_MLS    1
c0276d
 
c0276d
diff --git a/libsepol/include/sepol/policydb/services.h b/libsepol/include/sepol/policydb/services.h
c0276d
index aef0c7b..1969a10 100644
c0276d
--- a/libsepol/include/sepol/policydb/services.h
c0276d
+++ b/libsepol/include/sepol/policydb/services.h
c0276d
@@ -58,6 +58,38 @@ extern int sepol_compute_av_reason(sepol_security_id_t ssid,
c0276d
 				   struct sepol_av_decision *avd,
c0276d
 				   unsigned int *reason);
c0276d
 
c0276d
+/* 
c0276d
+ * Same as above, but also returns the constraint expression calculations
c0276d
+ * whether allowed or denied in a buffer. This buffer is allocated by
c0276d
+ * this call and must be free'd by the caller using free(3). The contraint
c0276d
+ * buffer will contain any constraints in infix notation.
c0276d
+ * If the SHOW_GRANTED flag is set it will show granted and denied
c0276d
+ * constraints. The default is to show only denied constraints.
c0276d
+ */
c0276d
+#define SHOW_GRANTED      1
c0276d
+extern int sepol_compute_av_reason_buffer(sepol_security_id_t ssid,
c0276d
+				   sepol_security_id_t tsid,
c0276d
+				   sepol_security_class_t tclass,
c0276d
+				   sepol_access_vector_t requested,
c0276d
+				   struct sepol_av_decision *avd,
c0276d
+				   unsigned int *reason,
c0276d
+				   char **reason_buf,
c0276d
+				   unsigned int flags);
c0276d
+/*
c0276d
+ * Return a class ID associated with the class string representation
c0276d
+ * specified by `class_name'.
c0276d
+ */
c0276d
+extern int sepol_class_name_to_id(const char *class_name,
c0276d
+					sepol_security_class_t  *tclass);
c0276d
+
c0276d
+/*
c0276d
+ * Return a permission av bit associated with tclass and the string
c0276d
+ * representation of the `perm_name'.
c0276d
+ */
c0276d
+extern int sepol_perm_name_to_av(sepol_security_class_t tclass,
c0276d
+					const char *perm_name,
c0276d
+					sepol_access_vector_t *av);
c0276d
+
c0276d
 /*
c0276d
  * Compute a SID to use for labeling a new object in the 
c0276d
  * class `tclass' based on a SID pair.  
c0276d
diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
c0276d
index f0555bb..6fd992f 100644
c0276d
--- a/libsepol/src/expand.c
c0276d
+++ b/libsepol/src/expand.c
c0276d
@@ -384,6 +384,17 @@ static int constraint_node_clone(constraint_node_t ** dst,
c0276d
 			new_expr->op = expr->op;
c0276d
 			if (new_expr->expr_type == CEXPR_NAMES) {
c0276d
 				if (new_expr->attr & CEXPR_TYPE) {
c0276d
+					/*
c0276d
+					 * Copy over constraint policy source types and/or
c0276d
+					 * attributes for sepol_compute_av_reason_buffer(3) so that
c0276d
+					 * utilities can analyse constraint errors.
c0276d
+					 */
c0276d
+					if (map_ebitmap(&expr->type_names->types,
c0276d
+							&new_expr->type_names->types,
c0276d
+							state->typemap)) {
c0276d
+						ERR(NULL, "Failed to map type_names->types");
c0276d
+						goto out_of_mem;
c0276d
+					}
c0276d
 					/* Type sets require expansion and conversion. */
c0276d
 					if (expand_convert_type_set(state->out,
c0276d
 								    state->
c0276d
diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
c0276d
index 1f49261..8c7efbc 100644
c0276d
--- a/libsepol/src/policydb.c
c0276d
+++ b/libsepol/src/policydb.c
c0276d
@@ -165,6 +165,13 @@ static struct policydb_compat_info policydb_compat[] = {
c0276d
 	 .target_platform = SEPOL_TARGET_SELINUX,
c0276d
 	},
c0276d
 	{
c0276d
+	 .type = POLICY_KERN,
c0276d
+	 .version = POLICYDB_VERSION_CONSTRAINT_NAMES,
c0276d
+	 .sym_num = SYM_NUM,
c0276d
+	 .ocon_num = OCON_NODE6 + 1,
c0276d
+	 .target_platform = SEPOL_TARGET_SELINUX,
c0276d
+	},
c0276d
+	{
c0276d
 	 .type = POLICY_BASE,
c0276d
 	 .version = MOD_POLICYDB_VERSION_BASE,
c0276d
 	 .sym_num = SYM_NUM,
c0276d
@@ -256,6 +263,13 @@ static struct policydb_compat_info policydb_compat[] = {
c0276d
 	 .target_platform = SEPOL_TARGET_SELINUX,
c0276d
 	},
c0276d
 	{
c0276d
+	 .type = POLICY_BASE,
c0276d
+	 .version = MOD_POLICYDB_VERSION_CONSTRAINT_NAMES,
c0276d
+	 .sym_num = SYM_NUM,
c0276d
+	 .ocon_num = OCON_NODE6 + 1,
c0276d
+	 .target_platform = SEPOL_TARGET_SELINUX,
c0276d
+	},
c0276d
+	{
c0276d
 	 .type = POLICY_MOD,
c0276d
 	 .version = MOD_POLICYDB_VERSION_BASE,
c0276d
 	 .sym_num = SYM_NUM,
c0276d
@@ -346,6 +360,13 @@ static struct policydb_compat_info policydb_compat[] = {
c0276d
 	 .ocon_num = 0,
c0276d
 	 .target_platform = SEPOL_TARGET_SELINUX,
c0276d
 	},
c0276d
+	{
c0276d
+	 .type = POLICY_MOD,
c0276d
+	 .version = MOD_POLICYDB_VERSION_CONSTRAINT_NAMES,
c0276d
+	 .sym_num = SYM_NUM,
c0276d
+	 .ocon_num = 0,
c0276d
+	 .target_platform = SEPOL_TARGET_SELINUX,
c0276d
+	},
c0276d
 };
c0276d
 
c0276d
 #if 0
c0276d
@@ -2019,6 +2040,10 @@ static int read_cons_helper(policydb_t * p, constraint_node_t ** nodep,
c0276d
 				if (p->policy_type != POLICY_KERN &&
c0276d
 				    type_set_read(e->type_names, fp))
c0276d
 					return -1;
c0276d
+				else if (p->policy_type == POLICY_KERN &&
c0276d
+					 p->policyvers >= POLICYDB_VERSION_CONSTRAINT_NAMES &&
c0276d
+					 type_set_read(e->type_names, fp))
c0276d
+					return -1;
c0276d
 				break;
c0276d
 			default:
c0276d
 				return -1;
c0276d
diff --git a/libsepol/src/services.c b/libsepol/src/services.c
c0276d
index 7fac4a0..43ec07e 100644
c0276d
--- a/libsepol/src/services.c
c0276d
+++ b/libsepol/src/services.c
c0276d
@@ -43,6 +43,11 @@
c0276d
  * Implementation of the security services.
c0276d
  */
c0276d
 
c0276d
+/* The initial sizes malloc'd for sepol_compute_av_reason_buffer() support */
c0276d
+#define REASON_BUF_SIZE 2048
c0276d
+#define EXPR_BUF_SIZE 1024
c0276d
+#define STACK_LEN 32
c0276d
+
c0276d
 #include <stdlib.h>
c0276d
 #include <sys/types.h>
c0276d
 #include <sys/socket.h>
c0276d
@@ -54,6 +59,7 @@
c0276d
 #include <sepol/policydb/services.h>
c0276d
 #include <sepol/policydb/conditional.h>
c0276d
 #include <sepol/policydb/flask.h>
c0276d
+#include <sepol/policydb/util.h>
c0276d
 
c0276d
 #include "debug.h"
c0276d
 #include "private.h"
c0276d
@@ -70,6 +76,50 @@ static int selinux_enforcing = 1;
c0276d
 static sidtab_t mysidtab, *sidtab = &mysidtab;
c0276d
 static policydb_t mypolicydb, *policydb = &mypolicydb;
c0276d
 
c0276d
+/* Used by sepol_compute_av_reason_buffer() to keep track of entries */
c0276d
+static int reason_buf_used;
c0276d
+static int reason_buf_len;
c0276d
+
c0276d
+/* Stack services for RPN to infix conversion. */
c0276d
+static char **stack;
c0276d
+static int stack_len;
c0276d
+static int next_stack_entry;
c0276d
+
c0276d
+static void push(char * expr_ptr)
c0276d
+{
c0276d
+	if (next_stack_entry >= stack_len) {
c0276d
+		char **new_stack = stack;
c0276d
+		int new_stack_len;
c0276d
+
c0276d
+		if (stack_len == 0)
c0276d
+			new_stack_len = STACK_LEN;
c0276d
+		else
c0276d
+			new_stack_len = stack_len * 2;
c0276d
+
c0276d
+		new_stack = realloc(stack, new_stack_len * sizeof(*stack));
c0276d
+		if (!new_stack) {
c0276d
+			ERR(NULL, "unable to allocate stack space");
c0276d
+			return;
c0276d
+		}
c0276d
+		stack_len = new_stack_len;
c0276d
+		stack = new_stack;
c0276d
+	}
c0276d
+	stack[next_stack_entry] = expr_ptr;
c0276d
+	next_stack_entry++;
c0276d
+}
c0276d
+
c0276d
+static char *pop(void)
c0276d
+{
c0276d
+	next_stack_entry--;
c0276d
+	if (next_stack_entry < 0) {
c0276d
+		next_stack_entry = 0;
c0276d
+		ERR(NULL, "pop called with no stack entries");
c0276d
+		return NULL;
c0276d
+	}
c0276d
+	return stack[next_stack_entry];
c0276d
+}
c0276d
+/* End Stack services */
c0276d
+
c0276d
 int hidden sepol_set_sidtab(sidtab_t * s)
c0276d
 {
c0276d
 	sidtab = s;
c0276d
@@ -113,20 +163,195 @@ int sepol_set_policydb_from_file(FILE * fp)
c0276d
 static uint32_t latest_granting = 0;
c0276d
 
c0276d
 /*
c0276d
- * Return the boolean value of a constraint expression 
c0276d
- * when it is applied to the specified source and target 
c0276d
+ * cat_expr_buf adds a string to an expression buffer and handles realloc's if
c0276d
+ * buffer is too small. The array of expression text buffer pointers and its
c0276d
+ * counter are globally defined here as constraint_expr_eval_reason() sets
c0276d
+ * them up and cat_expr_buf updates the e_buf pointer if the buffer is realloc'ed.
c0276d
+ */
c0276d
+static int expr_counter;
c0276d
+static char **expr_list;
c0276d
+static int expr_buf_used;
c0276d
+static int expr_buf_len;
c0276d
+
c0276d
+static void cat_expr_buf(char *e_buf, char *string)
c0276d
+{
c0276d
+	int len, new_buf_len;
c0276d
+	char *p, *new_buf = e_buf;
c0276d
+
c0276d
+	while (1) {
c0276d
+		p = e_buf + expr_buf_used;
c0276d
+		len = snprintf(p, expr_buf_len - expr_buf_used, "%s", string);
c0276d
+		if (len < 0 || len >= expr_buf_len - expr_buf_used) {
c0276d
+			new_buf_len = expr_buf_len + EXPR_BUF_SIZE;
c0276d
+			new_buf = realloc(e_buf, new_buf_len);
c0276d
+			if (!new_buf) {
c0276d
+				ERR(NULL, "failed to realloc expr buffer");
c0276d
+				return;
c0276d
+			}
c0276d
+			/* Update the new ptr in the expr list and locally + new len */
c0276d
+			expr_list[expr_counter] = new_buf;
c0276d
+			e_buf = new_buf;
c0276d
+			expr_buf_len = new_buf_len;
c0276d
+		} else {
c0276d
+			expr_buf_used += len;
c0276d
+			return;
c0276d
+		}
c0276d
+	}
c0276d
+}
c0276d
+
c0276d
+/*
c0276d
+ * If the POLICY_KERN version is < POLICYDB_VERSION_CONSTRAINT_NAMES,
c0276d
+ * then just return.
c0276d
+ *
c0276d
+ * If the POLICY_KERN version is >= POLICYDB_VERSION_CONSTRAINT_NAMES,
c0276d
+ * then for 'types' only, read the types_names->types list as it will
c0276d
+ * contain a list of types and attributes that were defined in the
c0276d
+ * policy source.
c0276d
+ */
c0276d
+static void get_names_list(constraint_expr_t *e, int type)
c0276d
+{
c0276d
+	ebitmap_t *types;
c0276d
+	types = &e->type_names->types;
c0276d
+	int rc = 0;
c0276d
+	unsigned int i;
c0276d
+	char tmp_buf[128];
c0276d
+	/* if -type_names->types is 0, then output string <empty_set> */
c0276d
+	int empty_set = 0;
c0276d
+
c0276d
+	if (policydb->policy_type == POLICY_KERN &&
c0276d
+			policydb->policyvers >= POLICYDB_VERSION_CONSTRAINT_NAMES &&
c0276d
+			type == CEXPR_TYPE) {
c0276d
+		/*
c0276d
+		 * Process >= POLICYDB_VERSION_CONSTRAINT_NAMES with CEXPR_TYPE, then
c0276d
+		 * obtain the list of names defined in the policy source.
c0276d
+		 */
c0276d
+		cat_expr_buf(expr_list[expr_counter], "{ POLICY_SOURCE: ");
c0276d
+		for (i = ebitmap_startbit(types); i < ebitmap_length(types); i++) {
c0276d
+			if ((rc = ebitmap_get_bit(types, i)) == 0)
c0276d
+				continue;
c0276d
+			/* Collect entries */
c0276d
+			snprintf(tmp_buf, sizeof(tmp_buf), "%s ", policydb->p_type_val_to_name[i]);
c0276d
+			cat_expr_buf(expr_list[expr_counter], tmp_buf);
c0276d
+			empty_set++;
c0276d
+		}
c0276d
+		if (empty_set == 0)
c0276d
+			cat_expr_buf(expr_list[expr_counter], "<empty_set> ");
c0276d
+		cat_expr_buf(expr_list[expr_counter], "} ");
c0276d
+	}
c0276d
+	return;
c0276d
+}
c0276d
+
c0276d
+static void msgcat(char *src, char *tgt, char *rel, int failed)
c0276d
+{
c0276d
+	char tmp_buf[1024];
c0276d
+	if (failed)
c0276d
+		snprintf(tmp_buf, sizeof(tmp_buf), "(%s %s %s -Fail-) ",
c0276d
+				src, rel, tgt);
c0276d
+	else
c0276d
+		snprintf(tmp_buf, sizeof(tmp_buf), "(%s %s %s -Pass-) ",
c0276d
+				src, rel, tgt);
c0276d
+	cat_expr_buf(expr_list[expr_counter], tmp_buf);
c0276d
+}
c0276d
+
c0276d
+/* Returns a buffer with class, statement type and permissions */
c0276d
+static char *get_class_info(sepol_security_class_t tclass,
c0276d
+							constraint_node_t *constraint,
c0276d
+							context_struct_t * xcontext)
c0276d
+{
c0276d
+	constraint_expr_t *e;
c0276d
+	int mls, state_num;
c0276d
+
c0276d
+	/* Find if MLS statement or not */
c0276d
+	mls = 0;
c0276d
+	for (e = constraint->expr; e; e = e->next) {
c0276d
+		if (e->attr >= CEXPR_L1L2) {
c0276d
+			mls = 1;
c0276d
+			break;
c0276d
+		}
c0276d
+	}
c0276d
+
c0276d
+	/* Determine statement type */
c0276d
+	char *statements[] = {
c0276d
+        "constrain ",			/* 0 */
c0276d
+		"mlsconstrain ",		/* 1 */
c0276d
+        "validatetrans ",		/* 2 */
c0276d
+		"mlsvalidatetrans ",	/* 3 */
c0276d
+        0 };
c0276d
+
c0276d
+	if (xcontext == NULL)
c0276d
+		state_num = mls + 0;
c0276d
+	else
c0276d
+		state_num = mls + 2;
c0276d
+
c0276d
+	int class_buf_len = 0;
c0276d
+	int new_class_buf_len;
c0276d
+	int len, buf_used;
c0276d
+	char *class_buf = NULL, *p;
c0276d
+	char *new_class_buf = NULL;
c0276d
+
c0276d
+	while (1) {
c0276d
+		new_class_buf_len = class_buf_len + EXPR_BUF_SIZE;
c0276d
+		new_class_buf = realloc(class_buf, new_class_buf_len);
c0276d
+			if (!new_class_buf)
c0276d
+				return NULL;
c0276d
+		class_buf_len = new_class_buf_len;
c0276d
+		class_buf = new_class_buf;
c0276d
+		buf_used = 0;
c0276d
+		p = class_buf;
c0276d
+
c0276d
+		/* Add statement type */
c0276d
+		len = snprintf(p, class_buf_len - buf_used, "%s", statements[state_num]);
c0276d
+		if (len < 0 || len >= class_buf_len - buf_used)
c0276d
+			continue;
c0276d
+
c0276d
+		/* Add class entry */
c0276d
+		p += len;
c0276d
+		buf_used += len;
c0276d
+		len = snprintf(p, class_buf_len - buf_used, "%s ",
c0276d
+				policydb->p_class_val_to_name[tclass - 1]);
c0276d
+		if (len < 0 || len >= class_buf_len - buf_used)
c0276d
+			continue;
c0276d
+
c0276d
+		/* Add permission entries */
c0276d
+		p += len;
c0276d
+		buf_used += len;
c0276d
+		len = snprintf(p, class_buf_len - buf_used, "{%s } (",
c0276d
+				sepol_av_to_string(policydb, tclass, constraint->permissions));
c0276d
+		if (len < 0 || len >= class_buf_len - buf_used)
c0276d
+			continue;
c0276d
+		break;
c0276d
+	}
c0276d
+	return class_buf;
c0276d
+}
c0276d
+
c0276d
+/*
c0276d
+ * Modified version of constraint_expr_eval that will process each
c0276d
+ * constraint as before but adds the information to text buffers that
c0276d
+ * will hold various components. The expression will be in RPN format,
c0276d
+ * therefore there is a stack based RPN to infix converter to produce
c0276d
+ * the final readable constraint.
c0276d
+ *
c0276d
+ * Return the boolean value of a constraint expression
c0276d
+ * when it is applied to the specified source and target
c0276d
  * security contexts.
c0276d
  *
c0276d
  * xcontext is a special beast...  It is used by the validatetrans rules
c0276d
  * only.  For these rules, scontext is the context before the transition,
c0276d
  * tcontext is the context after the transition, and xcontext is the context
c0276d
  * of the process performing the transition.  All other callers of
c0276d
- * constraint_expr_eval should pass in NULL for xcontext.
c0276d
+ * constraint_expr_eval_reason should pass in NULL for xcontext.
c0276d
+ * 
c0276d
+ * This function will also build a buffer as the constraint is processed
c0276d
+ * for analysis. If this option is not required, then:
c0276d
+ *      'tclass' should be '0' and r_buf MUST be NULL.
c0276d
  */
c0276d
-static int constraint_expr_eval(context_struct_t * scontext,
c0276d
+static int constraint_expr_eval_reason(context_struct_t * scontext,
c0276d
 				context_struct_t * tcontext,
c0276d
 				context_struct_t * xcontext,
c0276d
-				constraint_expr_t * cexpr)
c0276d
+				sepol_security_class_t tclass,
c0276d
+				constraint_node_t *constraint,
c0276d
+				char **r_buf,
c0276d
+				unsigned int flags)
c0276d
 {
c0276d
 	uint32_t val1, val2;
c0276d
 	context_struct_t *c;
c0276d
@@ -136,56 +361,137 @@ static int constraint_expr_eval(context_struct_t * scontext,
c0276d
 	int s[CEXPR_MAXDEPTH];
c0276d
 	int sp = -1;
c0276d
 
c0276d
-	for (e = cexpr; e; e = e->next) {
c0276d
+	char tmp_buf[128];
c0276d
+
c0276d
+/*
c0276d
+ * Define the s_t_x_num values that make up r1, t2 etc. in text strings
c0276d
+ * Set 1 = source, 2 = target, 3 = xcontext for validatetrans
c0276d
+ */
c0276d
+#define SOURCE  1
c0276d
+#define TARGET  2
c0276d
+#define XTARGET 3
c0276d
+
c0276d
+	int s_t_x_num = SOURCE;
c0276d
+
c0276d
+	/* Set 0 = fail, u = CEXPR_USER, r = CEXPR_ROLE, t = CEXPR_TYPE */
c0276d
+	int u_r_t = 0;
c0276d
+
c0276d
+	char *name1, *name2;
c0276d
+	char *src = NULL;
c0276d
+	char *tgt = NULL;
c0276d
+
c0276d
+	int rc = 0, x;
c0276d
+
c0276d
+	char *class_buf = NULL;
c0276d
+
c0276d
+	class_buf = get_class_info(tclass, constraint, xcontext);
c0276d
+	if (!class_buf) {
c0276d
+		ERR(NULL, "failed to allocate class buffer");
c0276d
+		return -ENOMEM;
c0276d
+	}
c0276d
+
c0276d
+	/* Original function but with buffer support */
c0276d
+	int expr_list_len = 0;
c0276d
+	expr_counter = 0;
c0276d
+	expr_list = NULL;
c0276d
+	for (e = constraint->expr; e; e = e->next) {
c0276d
+		/* Allocate a stack to hold expression buffer entries */
c0276d
+		if (expr_counter >= expr_list_len) {
c0276d
+			char **new_expr_list = expr_list;
c0276d
+			int new_expr_list_len;
c0276d
+
c0276d
+			if (expr_list_len == 0)
c0276d
+				new_expr_list_len = STACK_LEN;
c0276d
+			else
c0276d
+				new_expr_list_len = expr_list_len * 2;
c0276d
+
c0276d
+			new_expr_list = realloc(expr_list, new_expr_list_len * sizeof(*expr_list));
c0276d
+			if (!new_expr_list) {
c0276d
+				ERR(NULL, "failed to allocate expr buffer stack");
c0276d
+				rc = -ENOMEM;
c0276d
+				goto out;
c0276d
+			}
c0276d
+			expr_list_len = new_expr_list_len;
c0276d
+			expr_list = new_expr_list;
c0276d
+		}
c0276d
+
c0276d
+		/*
c0276d
+		 * malloc a buffer to store each expression text component. If the
c0276d
+		 * buffer is too small cat_expr_buf() will realloc extra space.
c0276d
+		 */
c0276d
+		expr_buf_len = EXPR_BUF_SIZE;
c0276d
+		expr_list[expr_counter] = malloc(expr_buf_len);
c0276d
+		if (!expr_list[expr_counter]) {
c0276d
+			ERR(NULL, "failed to allocate expr buffer");
c0276d
+			rc = -ENOMEM;
c0276d
+			goto out;
c0276d
+		}
c0276d
+		expr_buf_used = 0;
c0276d
+
c0276d
+		/* Now process each expression of the constraint */
c0276d
 		switch (e->expr_type) {
c0276d
 		case CEXPR_NOT:
c0276d
 			BUG_ON(sp < 0);
c0276d
 			s[sp] = !s[sp];
c0276d
+			cat_expr_buf(expr_list[expr_counter], "not");
c0276d
 			break;
c0276d
 		case CEXPR_AND:
c0276d
 			BUG_ON(sp < 1);
c0276d
 			sp--;
c0276d
 			s[sp] &= s[sp + 1];
c0276d
+			cat_expr_buf(expr_list[expr_counter], "and");
c0276d
 			break;
c0276d
 		case CEXPR_OR:
c0276d
 			BUG_ON(sp < 1);
c0276d
 			sp--;
c0276d
 			s[sp] |= s[sp + 1];
c0276d
+			cat_expr_buf(expr_list[expr_counter], "or");
c0276d
 			break;
c0276d
 		case CEXPR_ATTR:
c0276d
 			if (sp == (CEXPR_MAXDEPTH - 1))
c0276d
-				return 0;
c0276d
+				goto out;
c0276d
+
c0276d
 			switch (e->attr) {
c0276d
 			case CEXPR_USER:
c0276d
 				val1 = scontext->user;
c0276d
 				val2 = tcontext->user;
c0276d
+				free(src); src = strdup("u1");
c0276d
+				free(tgt); tgt = strdup("u2");
c0276d
 				break;
c0276d
 			case CEXPR_TYPE:
c0276d
 				val1 = scontext->type;
c0276d
 				val2 = tcontext->type;
c0276d
+				free(src); src = strdup("t1");
c0276d
+				free(tgt); tgt = strdup("t2");
c0276d
 				break;
c0276d
 			case CEXPR_ROLE:
c0276d
 				val1 = scontext->role;
c0276d
 				val2 = tcontext->role;
c0276d
 				r1 = policydb->role_val_to_struct[val1 - 1];
c0276d
 				r2 = policydb->role_val_to_struct[val2 - 1];
c0276d
+				name1 = policydb->p_role_val_to_name[r1->s.value - 1];
c0276d
+				name2 = policydb->p_role_val_to_name[r2->s.value - 1];
c0276d
+				snprintf(tmp_buf, sizeof(tmp_buf), "r1=%s", name1);
c0276d
+				free(src); src = strdup(tmp_buf);
c0276d
+				snprintf(tmp_buf, sizeof(tmp_buf), "r2=%s ", name2);
c0276d
+				free(tgt); tgt = strdup(tmp_buf);
c0276d
+
c0276d
 				switch (e->op) {
c0276d
 				case CEXPR_DOM:
c0276d
-					s[++sp] =
c0276d
-					    ebitmap_get_bit(&r1->dominates,
c0276d
-							    val2 - 1);
c0276d
+					s[++sp] = ebitmap_get_bit(&r1->dominates, val2 - 1);
c0276d
+					msgcat(src, tgt, "dom", s[sp] == 0);
c0276d
+					expr_counter++;
c0276d
 					continue;
c0276d
 				case CEXPR_DOMBY:
c0276d
-					s[++sp] =
c0276d
-					    ebitmap_get_bit(&r2->dominates,
c0276d
-							    val1 - 1);
c0276d
+					s[++sp] = ebitmap_get_bit(&r2->dominates, val1 - 1);
c0276d
+					msgcat(src, tgt, "domby", s[sp] == 0);
c0276d
+					expr_counter++;
c0276d
 					continue;
c0276d
 				case CEXPR_INCOMP:
c0276d
-					s[++sp] =
c0276d
-					    (!ebitmap_get_bit
c0276d
-					     (&r1->dominates, val2 - 1)
c0276d
-					     && !ebitmap_get_bit(&r2->dominates,
c0276d
-								 val1 - 1));
c0276d
+					s[++sp] = (!ebitmap_get_bit(&r1->dominates, val2 - 1)
c0276d
+						 && !ebitmap_get_bit(&r2->dominates, val1 - 1));
c0276d
+					msgcat(src, tgt, "incomp", s[sp] == 0);
c0276d
+					expr_counter++;
c0276d
 					continue;
c0276d
 				default:
c0276d
 					break;
c0276d
@@ -194,110 +500,327 @@ static int constraint_expr_eval(context_struct_t * scontext,
c0276d
 			case CEXPR_L1L2:
c0276d
 				l1 = &(scontext->range.level[0]);
c0276d
 				l2 = &(tcontext->range.level[0]);
c0276d
+				free(src); src = strdup("l1");
c0276d
+				free(tgt); tgt = strdup("l2");
c0276d
 				goto mls_ops;
c0276d
 			case CEXPR_L1H2:
c0276d
 				l1 = &(scontext->range.level[0]);
c0276d
 				l2 = &(tcontext->range.level[1]);
c0276d
+				free(src); src = strdup("l1");
c0276d
+				free(tgt); tgt = strdup("h2");
c0276d
 				goto mls_ops;
c0276d
 			case CEXPR_H1L2:
c0276d
 				l1 = &(scontext->range.level[1]);
c0276d
 				l2 = &(tcontext->range.level[0]);
c0276d
+				free(src); src = strdup("h1");
c0276d
+				free(tgt); tgt = strdup("L2");
c0276d
 				goto mls_ops;
c0276d
 			case CEXPR_H1H2:
c0276d
 				l1 = &(scontext->range.level[1]);
c0276d
 				l2 = &(tcontext->range.level[1]);
c0276d
+				free(src); src = strdup("h1");
c0276d
+				free(tgt); tgt = strdup("h2");
c0276d
 				goto mls_ops;
c0276d
 			case CEXPR_L1H1:
c0276d
 				l1 = &(scontext->range.level[0]);
c0276d
 				l2 = &(scontext->range.level[1]);
c0276d
+				free(src); src = strdup("l1");
c0276d
+				free(tgt); tgt = strdup("h1");
c0276d
 				goto mls_ops;
c0276d
 			case CEXPR_L2H2:
c0276d
 				l1 = &(tcontext->range.level[0]);
c0276d
 				l2 = &(tcontext->range.level[1]);
c0276d
-				goto mls_ops;
c0276d
-			      mls_ops:
c0276d
+				free(src); src = strdup("l2");
c0276d
+				free(tgt); tgt = strdup("h2");
c0276d
+			mls_ops:
c0276d
 				switch (e->op) {
c0276d
 				case CEXPR_EQ:
c0276d
 					s[++sp] = mls_level_eq(l1, l2);
c0276d
+					msgcat(src, tgt, "eq", s[sp] == 0);
c0276d
+					expr_counter++;
c0276d
 					continue;
c0276d
 				case CEXPR_NEQ:
c0276d
 					s[++sp] = !mls_level_eq(l1, l2);
c0276d
+					msgcat(src, tgt, "neq", s[sp] == 0);
c0276d
+					expr_counter++;
c0276d
 					continue;
c0276d
 				case CEXPR_DOM:
c0276d
 					s[++sp] = mls_level_dom(l1, l2);
c0276d
+					msgcat(src, tgt, "dom", s[sp] == 0);
c0276d
+					expr_counter++;
c0276d
 					continue;
c0276d
 				case CEXPR_DOMBY:
c0276d
 					s[++sp] = mls_level_dom(l2, l1);
c0276d
+					msgcat(src, tgt, "domby", s[sp] == 0);
c0276d
+					expr_counter++;
c0276d
 					continue;
c0276d
 				case CEXPR_INCOMP:
c0276d
 					s[++sp] = mls_level_incomp(l2, l1);
c0276d
+					msgcat(src, tgt, "incomp", s[sp] == 0);
c0276d
+					expr_counter++;
c0276d
 					continue;
c0276d
 				default:
c0276d
 					BUG();
c0276d
-					return 0;
c0276d
+					goto out;
c0276d
 				}
c0276d
 				break;
c0276d
 			default:
c0276d
 				BUG();
c0276d
-				return 0;
c0276d
+				goto out;
c0276d
 			}
c0276d
 
c0276d
 			switch (e->op) {
c0276d
 			case CEXPR_EQ:
c0276d
 				s[++sp] = (val1 == val2);
c0276d
+				msgcat(src, tgt, "eq", s[sp] == 0);
c0276d
 				break;
c0276d
 			case CEXPR_NEQ:
c0276d
 				s[++sp] = (val1 != val2);
c0276d
+				msgcat(src, tgt, "neq", s[sp] == 0);
c0276d
 				break;
c0276d
 			default:
c0276d
 				BUG();
c0276d
-				return 0;
c0276d
+				goto out;
c0276d
 			}
c0276d
 			break;
c0276d
 		case CEXPR_NAMES:
c0276d
 			if (sp == (CEXPR_MAXDEPTH - 1))
c0276d
-				return 0;
c0276d
+				goto out;
c0276d
+			s_t_x_num = SOURCE;
c0276d
 			c = scontext;
c0276d
-			if (e->attr & CEXPR_TARGET)
c0276d
+			if (e->attr & CEXPR_TARGET) {
c0276d
+				s_t_x_num = TARGET;
c0276d
 				c = tcontext;
c0276d
-			else if (e->attr & CEXPR_XTARGET) {
c0276d
+			} else if (e->attr & CEXPR_XTARGET) {
c0276d
+				s_t_x_num = XTARGET;
c0276d
 				c = xcontext;
c0276d
-				if (!c) {
c0276d
-					BUG();
c0276d
-					return 0;
c0276d
-				}
c0276d
 			}
c0276d
-			if (e->attr & CEXPR_USER)
c0276d
+			if (!c) {
c0276d
+				BUG();
c0276d
+				goto out;
c0276d
+			}
c0276d
+			if (e->attr & CEXPR_USER) {
c0276d
+				u_r_t = CEXPR_USER;
c0276d
 				val1 = c->user;
c0276d
-			else if (e->attr & CEXPR_ROLE)
c0276d
+				name1 = policydb->p_user_val_to_name[val1 - 1];
c0276d
+				snprintf(tmp_buf, sizeof(tmp_buf), "u%d=%s ",
c0276d
+						s_t_x_num, name1);
c0276d
+				free(src); src = strdup(tmp_buf);
c0276d
+			}
c0276d
+			else if (e->attr & CEXPR_ROLE) {
c0276d
+				u_r_t = CEXPR_ROLE;
c0276d
 				val1 = c->role;
c0276d
-			else if (e->attr & CEXPR_TYPE)
c0276d
+				name1 = policydb->p_role_val_to_name[val1 - 1];
c0276d
+				snprintf(tmp_buf, sizeof(tmp_buf), "r%d=%s ", s_t_x_num, name1);
c0276d
+				free(src); src = strdup(tmp_buf);
c0276d
+			}
c0276d
+			else if (e->attr & CEXPR_TYPE) {
c0276d
+				u_r_t = CEXPR_TYPE;
c0276d
 				val1 = c->type;
c0276d
+				name1 = policydb->p_type_val_to_name[val1 - 1];
c0276d
+				snprintf(tmp_buf, sizeof(tmp_buf),
c0276d
+						"t%d=%s ", s_t_x_num, name1);
c0276d
+				free(src); src = strdup(tmp_buf);
c0276d
+			}
c0276d
 			else {
c0276d
 				BUG();
c0276d
-				return 0;
c0276d
+				goto out;
c0276d
 			}
c0276d
 
c0276d
 			switch (e->op) {
c0276d
 			case CEXPR_EQ:
c0276d
+				switch (u_r_t) {
c0276d
+				case CEXPR_USER:
c0276d
+					free(tgt); tgt=strdup("USER_ENTRY");
c0276d
+					break;
c0276d
+				case CEXPR_ROLE:
c0276d
+					free(tgt); tgt=strdup("ROLE_ENTRY");
c0276d
+					break;
c0276d
+				case CEXPR_TYPE:
c0276d
+					free(tgt); tgt=strdup("TYPE_ENTRY");
c0276d
+					break;
c0276d
+				default:
c0276d
+					ERR(NULL, "unrecognized u_r_t Value: %d", u_r_t);
c0276d
+					break;
c0276d
+				}
c0276d
+
c0276d
 				s[++sp] = ebitmap_get_bit(&e->names, val1 - 1);
c0276d
+				msgcat(src, tgt, "eq", s[sp] == 0);
c0276d
+				if (s[sp] == 0) {
c0276d
+					get_names_list(e, u_r_t);
c0276d
+				}
c0276d
 				break;
c0276d
+
c0276d
 			case CEXPR_NEQ:
c0276d
+				switch (u_r_t) {
c0276d
+				case CEXPR_USER:
c0276d
+					free(tgt); tgt=strdup("USER_ENTRY");
c0276d
+					break;
c0276d
+				case CEXPR_ROLE:
c0276d
+					free(tgt); tgt=strdup("ROLE_ENTRY");
c0276d
+					break;
c0276d
+				case CEXPR_TYPE:
c0276d
+					free(tgt); tgt=strdup("TYPE_ENTRY");
c0276d
+					break;
c0276d
+				default:
c0276d
+					ERR(NULL, "unrecognized u_r_t Value: %d", u_r_t);
c0276d
+					break;
c0276d
+				}
c0276d
+
c0276d
 				s[++sp] = !ebitmap_get_bit(&e->names, val1 - 1);
c0276d
+				msgcat(src, tgt, "neq", s[sp] == 0);
c0276d
+				if (s[sp] == 0) {
c0276d
+					get_names_list(e, u_r_t);
c0276d
+				}
c0276d
 				break;
c0276d
 			default:
c0276d
 				BUG();
c0276d
-				return 0;
c0276d
+				goto out;
c0276d
 			}
c0276d
 			break;
c0276d
 		default:
c0276d
 			BUG();
c0276d
-			return 0;
c0276d
+			goto out;
c0276d
 		}
c0276d
+		expr_counter++;
c0276d
+	}
c0276d
+
c0276d
+	/*
c0276d
+	 * At this point each expression of the constraint is in
c0276d
+	 * expr_list[n+1] and in RPN format. Now convert to 'infix'
c0276d
+	 */
c0276d
+
c0276d
+	/*
c0276d
+	 * Save expr count but zero expr_counter to detect if 'BUG(); goto out;'
c0276d
+	 * was called as we need to release any used expr_list malloc's. Normally
c0276d
+	 * they are released by the RPN to infix code.
c0276d
+	 */
c0276d
+	int expr_count = expr_counter;
c0276d
+	expr_counter = 0;
c0276d
+
c0276d
+	/*
c0276d
+	 * The array of expression answer buffer pointers and counter. Generate
c0276d
+	 * the same number of answer buffer entries as expression buffers (as
c0276d
+	 * there will never be more required).
c0276d
+	 */
c0276d
+	char **answer_list;
c0276d
+	int answer_counter = 0;
c0276d
+
c0276d
+	answer_list = malloc(expr_count * sizeof(*answer_list));
c0276d
+	if (!answer_list) {
c0276d
+		ERR(NULL, "failed to allocate answer stack");
c0276d
+		rc = -ENOMEM;
c0276d
+		goto out;
c0276d
 	}
c0276d
 
c0276d
-	BUG_ON(sp != 0);
c0276d
-	return s[0];
c0276d
+	/* The pop operands */
c0276d
+	char *a;
c0276d
+	char *b;
c0276d
+	int a_len, b_len;
c0276d
+
c0276d
+	/* Convert constraint from RPN to infix notation. */
c0276d
+	for (x = 0; x != expr_count; x++) {
c0276d
+		if (strncmp(expr_list[x], "and", 3) == 0 || strncmp(expr_list[x],
c0276d
+					"or", 2) == 0) {
c0276d
+			b = pop();
c0276d
+			b_len = strlen(b);
c0276d
+			a = pop();
c0276d
+			a_len = strlen(a);
c0276d
+
c0276d
+			/* get a buffer to hold the answer */
c0276d
+			answer_list[answer_counter] = malloc(a_len + b_len + 8);
c0276d
+			if (!answer_list[answer_counter]) {
c0276d
+				ERR(NULL, "failed to allocate answer buffer");
c0276d
+				rc = -ENOMEM;
c0276d
+				goto out;
c0276d
+			}
c0276d
+			memset(answer_list[answer_counter], '\0', a_len + b_len + 8);
c0276d
+
c0276d
+			sprintf(answer_list[answer_counter], "%s %s %s", a, expr_list[x], b);
c0276d
+			push(answer_list[answer_counter++]);
c0276d
+			free(a);
c0276d
+			free(b);
c0276d
+		} else if (strncmp(expr_list[x], "not", 3) == 0) {
c0276d
+			b = pop();
c0276d
+			b_len = strlen(b);
c0276d
+
c0276d
+			answer_list[answer_counter] = malloc(b_len + 8);
c0276d
+			if (!answer_list[answer_counter]) {
c0276d
+				ERR(NULL, "failed to allocate answer buffer");
c0276d
+				rc = -ENOMEM;
c0276d
+				goto out;
c0276d
+			}
c0276d
+			memset(answer_list[answer_counter], '\0', b_len + 8);
c0276d
+
c0276d
+			if (strncmp(b, "not", 3) == 0)
c0276d
+				sprintf(answer_list[answer_counter], "%s (%s)", expr_list[x], b);
c0276d
+			else
c0276d
+				sprintf(answer_list[answer_counter], "%s%s", expr_list[x], b);
c0276d
+			push(answer_list[answer_counter++]);
c0276d
+			free(b);
c0276d
+		} else {
c0276d
+			push(expr_list[x]);
c0276d
+		}
c0276d
+	}
c0276d
+	/* Get the final answer from tos and build constraint text */
c0276d
+	a = pop();
c0276d
+
c0276d
+	/* Constraint calculation: rc = 0 is denied, rc = 1 is granted */
c0276d
+	sprintf(tmp_buf,"Constraint %s\n", s[0] ? "GRANTED" : "DENIED");
c0276d
+
c0276d
+	int len, new_buf_len;
c0276d
+	char *p, **new_buf = r_buf;
c0276d
+	/*
c0276d
+	 * These contain the constraint components that are added to the
c0276d
+	 * callers reason buffer.
c0276d
+	 */
c0276d
+	char *buffers[] = { class_buf, a, "); ", tmp_buf, 0 };
c0276d
+
c0276d
+	/*
c0276d
+	 * This will add the constraints to the callers reason buffer (who is
c0276d
+	 * responsible for freeing the memory). It will handle any realloc's
c0276d
+	 * should the buffer be too short.
c0276d
+	 * The reason_buf_used and reason_buf_len counters are defined globally
c0276d
+	 * as multiple constraints can be in the buffer.
c0276d
+	 */
c0276d
+	if (r_buf && ((s[0] == 0) || ((s[0] == 1 &&
c0276d
+				(flags & SHOW_GRANTED) == SHOW_GRANTED)))) {
c0276d
+		for (x = 0; buffers[x] != NULL; x++) {
c0276d
+			while (1) {
c0276d
+				p = *r_buf + reason_buf_used;
c0276d
+				len = snprintf(p, reason_buf_len - reason_buf_used, "%s", buffers[x]);
c0276d
+				if (len < 0 || len >= reason_buf_len - reason_buf_used) {
c0276d
+					new_buf_len = reason_buf_len + REASON_BUF_SIZE;
c0276d
+					*new_buf = realloc(*r_buf, new_buf_len);
c0276d
+					if (!new_buf) {
c0276d
+						ERR(NULL, "failed to realloc reason buffer");
c0276d
+						goto out1;
c0276d
+					}
c0276d
+					**r_buf = **new_buf;
c0276d
+					reason_buf_len = new_buf_len;
c0276d
+					continue;
c0276d
+				} else {
c0276d
+					reason_buf_used += len;
c0276d
+					break;
c0276d
+				}
c0276d
+			}
c0276d
+		}
c0276d
+	}
c0276d
+
c0276d
+out1:
c0276d
+	rc = s[0];
c0276d
+	free(a);
c0276d
+
c0276d
+out:
c0276d
+	free(class_buf);
c0276d
+	free(src);
c0276d
+	free(tgt);
c0276d
+
c0276d
+	if (expr_counter) {
c0276d
+		for (x = 0; expr_list[x] != NULL; x++)
c0276d
+			free(expr_list[x]);
c0276d
+	}
c0276d
+	return rc;
c0276d
 }
c0276d
 
c0276d
 /*
c0276d
@@ -309,7 +832,9 @@ static int context_struct_compute_av(context_struct_t * scontext,
c0276d
 				     sepol_security_class_t tclass,
c0276d
 				     sepol_access_vector_t requested,
c0276d
 				     struct sepol_av_decision *avd,
c0276d
-				     unsigned int *reason)
c0276d
+				     unsigned int *reason,
c0276d
+				     char **r_buf,
c0276d
+					 unsigned int flags)
c0276d
 {
c0276d
 	constraint_node_t *constraint;
c0276d
 	struct role_allow *ra;
c0276d
@@ -384,8 +909,8 @@ static int context_struct_compute_av(context_struct_t * scontext,
c0276d
 	constraint = tclass_datum->constraints;
c0276d
 	while (constraint) {
c0276d
 		if ((constraint->permissions & (avd->allowed)) &&
c0276d
-		    !constraint_expr_eval(scontext, tcontext, NULL,
c0276d
-					  constraint->expr)) {
c0276d
+		    !constraint_expr_eval_reason(scontext, tcontext, NULL,
c0276d
+					  tclass, constraint, r_buf, flags)) {
c0276d
 			avd->allowed =
c0276d
 			    (avd->allowed) & ~(constraint->permissions);
c0276d
 		}
c0276d
@@ -460,8 +985,8 @@ int hidden sepol_validate_transition(sepol_security_id_t oldsid,
c0276d
 
c0276d
 	constraint = tclass_datum->validatetrans;
c0276d
 	while (constraint) {
c0276d
-		if (!constraint_expr_eval(ocontext, ncontext, tcontext,
c0276d
-					  constraint->expr)) {
c0276d
+		if (!constraint_expr_eval_reason(ocontext, ncontext, tcontext,
c0276d
+					  0, constraint, NULL, 0)) {
c0276d
 			return -EPERM;
c0276d
 		}
c0276d
 		constraint = constraint->next;
c0276d
@@ -494,11 +1019,59 @@ int hidden sepol_compute_av_reason(sepol_security_id_t ssid,
c0276d
 	}
c0276d
 
c0276d
 	rc = context_struct_compute_av(scontext, tcontext, tclass,
c0276d
-				       requested, avd, reason);
c0276d
+					requested, avd, reason, NULL, 0);
c0276d
       out:
c0276d
 	return rc;
c0276d
 }
c0276d
 
c0276d
+/* 
c0276d
+ * sepol_compute_av_reason_buffer - the reason buffer is malloc'd to
c0276d
+ * REASON_BUF_SIZE. If the buffer size is exceeded, then it is realloc'd
c0276d
+ * in the constraint_expr_eval_reason() function.
c0276d
+ */
c0276d
+int hidden sepol_compute_av_reason_buffer(sepol_security_id_t ssid,
c0276d
+				   sepol_security_id_t tsid,
c0276d
+				   sepol_security_class_t tclass,
c0276d
+				   sepol_access_vector_t requested,
c0276d
+				   struct sepol_av_decision *avd,
c0276d
+				   unsigned int *reason,
c0276d
+				   char **reason_buf,
c0276d
+				   unsigned int flags)
c0276d
+{
c0276d
+	*reason_buf = malloc(REASON_BUF_SIZE);
c0276d
+	if (!*reason_buf) {
c0276d
+		ERR(NULL, "failed to allocate reason buffer");
c0276d
+		return -ENOMEM;
c0276d
+	}
c0276d
+	/*
c0276d
+	 * These are defined globally as the buffer can contain multiple
c0276d
+	 * constraint statements so need to keep track
c0276d
+	 */
c0276d
+	reason_buf_used = 0;
c0276d
+	reason_buf_len = REASON_BUF_SIZE;
c0276d
+
c0276d
+	context_struct_t *scontext = 0, *tcontext = 0;
c0276d
+	int rc = 0;
c0276d
+
c0276d
+	scontext = sepol_sidtab_search(sidtab, ssid);
c0276d
+	if (!scontext) {
c0276d
+		ERR(NULL, "unrecognized SID %d", ssid);
c0276d
+		rc = -EINVAL;
c0276d
+		goto out;
c0276d
+	}
c0276d
+	tcontext = sepol_sidtab_search(sidtab, tsid);
c0276d
+	if (!tcontext) {
c0276d
+		ERR(NULL, "unrecognized SID %d", tsid);
c0276d
+		rc = -EINVAL;
c0276d
+		goto out;
c0276d
+	}
c0276d
+
c0276d
+	rc = context_struct_compute_av(scontext, tcontext, tclass,
c0276d
+					   requested, avd, reason, reason_buf, flags);
c0276d
+out:
c0276d
+	return rc;
c0276d
+}
c0276d
+
c0276d
 int hidden sepol_compute_av(sepol_security_id_t ssid,
c0276d
 			    sepol_security_id_t tsid,
c0276d
 			    sepol_security_class_t tclass,
c0276d
@@ -511,6 +1084,70 @@ int hidden sepol_compute_av(sepol_security_id_t ssid,
c0276d
 }
c0276d
 
c0276d
 /*
c0276d
+ * Return a class ID associated with the class string specified by
c0276d
+ * class_name.
c0276d
+ */
c0276d
+int hidden sepol_class_name_to_id(const char *class_name,
c0276d
+			sepol_security_class_t *tclass)
c0276d
+{
c0276d
+	char *class = NULL;
c0276d
+	sepol_security_class_t id;
c0276d
+
c0276d
+	for (id = 1; ; id++) {
c0276d
+		if ((class = policydb->p_class_val_to_name[id - 1]) == NULL) {
c0276d
+			ERR(NULL, "could not convert %s to class id", class_name);
c0276d
+			return STATUS_ERR;
c0276d
+		}
c0276d
+		if ((strcmp(class, class_name)) == 0) {
c0276d
+			*tclass = id;
c0276d
+			return STATUS_SUCCESS;
c0276d
+		}
c0276d
+	}
c0276d
+}
c0276d
+
c0276d
+/*
c0276d
+ * Return access vector bit associated with the class ID and permission
c0276d
+ * string.
c0276d
+ */
c0276d
+int hidden sepol_perm_name_to_av(sepol_security_class_t tclass,
c0276d
+					const char *perm_name,
c0276d
+					sepol_access_vector_t *av)
c0276d
+{
c0276d
+	class_datum_t *tclass_datum;
c0276d
+	perm_datum_t *perm_datum;
c0276d
+
c0276d
+	if (!tclass || tclass > policydb->p_classes.nprim) {
c0276d
+		ERR(NULL, "unrecognized class %d", tclass);
c0276d
+		return -EINVAL;
c0276d
+	}
c0276d
+	tclass_datum = policydb->class_val_to_struct[tclass - 1];
c0276d
+
c0276d
+	/* Check for unique perms then the common ones (if any) */
c0276d
+	perm_datum = (perm_datum_t *)
c0276d
+			hashtab_search(tclass_datum->permissions.table,
c0276d
+			(hashtab_key_t)perm_name);
c0276d
+	if (perm_datum != NULL) {
c0276d
+		*av = 0x1 << (perm_datum->s.value - 1);
c0276d
+		return STATUS_SUCCESS;
c0276d
+	}
c0276d
+
c0276d
+	if (tclass_datum->comdatum == NULL)
c0276d
+	   	goto out;
c0276d
+
c0276d
+	perm_datum = (perm_datum_t *)
c0276d
+			hashtab_search(tclass_datum->comdatum->permissions.table,
c0276d
+			(hashtab_key_t)perm_name);
c0276d
+
c0276d
+	if (perm_datum != NULL) {
c0276d
+		*av = 0x1 << (perm_datum->s.value - 1);
c0276d
+		return STATUS_SUCCESS;
c0276d
+	}
c0276d
+out:	
c0276d
+	ERR(NULL, "could not convert %s to av bit", perm_name);
c0276d
+   	return STATUS_ERR;
c0276d
+}
c0276d
+
c0276d
+/*
c0276d
  * Write the security context string representation of 
c0276d
  * the context associated with `sid' into a dynamically
c0276d
  * allocated string of the correct size.  Set `*scontext'
c0276d
@@ -1339,7 +1976,7 @@ int hidden sepol_get_user_sids(sepol_security_id_t fromsid,
c0276d
 			rc = context_struct_compute_av(fromcon, &usercon,
c0276d
 						       SECCLASS_PROCESS,
c0276d
 						       PROCESS__TRANSITION,
c0276d
-						       &avd, &reason);
c0276d
+						       &avd, &reason, NULL, 0);
c0276d
 			if (rc || !(avd.allowed & PROCESS__TRANSITION))
c0276d
 				continue;
c0276d
 			rc = sepol_sidtab_context_to_sid(sidtab, &usercon,
c0276d
diff --git a/libsepol/src/write.c b/libsepol/src/write.c
c0276d
index 55992f8..6fe73e6 100644
c0276d
--- a/libsepol/src/write.c
c0276d
+++ b/libsepol/src/write.c
c0276d
@@ -893,8 +893,11 @@ static int write_cons_helper(policydb_t * p,
c0276d
 				if (ebitmap_write(&e->names, fp)) {
c0276d
 					return POLICYDB_ERROR;
c0276d
 				}
c0276d
-				if (p->policy_type != POLICY_KERN &&
c0276d
-				    type_set_write(e->type_names, fp)) {
c0276d
+				if ((p->policy_type != POLICY_KERN &&
c0276d
+						type_set_write(e->type_names, fp)) ||
c0276d
+						(p->policy_type == POLICY_KERN &&
c0276d
+						(p->policyvers >= POLICYDB_VERSION_CONSTRAINT_NAMES) &&
c0276d
+						type_set_write(e->type_names, fp))) {
c0276d
 					return POLICYDB_ERROR;
c0276d
 				}
c0276d
 				break;