[PATCH 04/17] libsepol: V1 Allow constraint denials to be determined.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Updated patch from Richard Haines with changes suggest by Stephen Smalley. I
also broke the other fixes to libselinux, sepolgen and audit2allow into
separate patches.
   This patch looks good to me. acked.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.15 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iEYEARECAAYFAlJuvxcACgkQrlYvE4MpobNO/QCfdAx+OyMSgt/OV9cYtfA9oQY6
t/IAn2c7MZbi2P9Kt063X31XdLU6Onxn
=wMCC
-----END PGP SIGNATURE-----
>From 34977970ba30b63a1c836e4d0b477fd5c7bb8c5b Mon Sep 17 00:00:00 2001
From: Richard Haines <richard_c_haines@xxxxxxxxxxxxxx>
Date: Mon, 28 Oct 2013 15:30:12 +0000
Subject: [PATCH 04/17] libsepol: V1 Allow constraint denials to be determined.

Adds policy source defined 'type' or 'typeattribute' names to
constraints by adding additional structures (->type_names->types) to a
binary policy.
Before this change all typeattributes were expanded to lists of types
and added to the constraint under ->names. This made it difficult for
system admins to determine from the policy source what attribute
needed to be updated. To facilitate analysis of constraint failures
a new function has also been added, see sepol_compute_av_reason_buffer.

As additional structures have been added to policy, the policy version
is also updated (POLICYDB_VERSION_CONSTRAINT_NAMES). There is also a
corresponding kernel patch to handle the additional structures.

sepol_compute_av_reason_buffer is an extended version of
sepol_compute_av_reason. This will return a buffer with constraint
expression information, containing the constrain type, class, perms,
keywords etc.. It will also contain which constraint expr failed plus
the final outcome. The buffer MUST be free'd with free(3).

The type information output by sepol_compute_av_reason_buffer depends on
the policy version:
If >= POLICYDB_VERSION_CONSTRAINT_NAMES, then the output will be
whatever was in the original policy (type or attribute names).
If < POLICYDB_VERSION_CONSTRAINT_NAMES, then the output will be
the types listed in the constraint (as no attribute information is
available in these versions).
For users and roles whatever policy version, only the names are listed
(as role attributes are not currently held in the constraint).

Also added are two functions that obtain the class and permissions
from a binary policy file that has been loaded for testing:
sepol_string_to_security_class
sepol_string_to_av_perm

Signed-off-by: Richard Haines <richard_c_haines@xxxxxxxxxxxxxx>
---
 libsepol/include/sepol/policydb/policydb.h |   3 +-
 libsepol/include/sepol/policydb/services.h |  32 ++
 libsepol/src/expand.c                      |  11 +
 libsepol/src/policydb.c                    |  11 +
 libsepol/src/services.c                    | 724 +++++++++++++++++++++++++++--
 libsepol/src/write.c                       |   7 +-
 6 files changed, 735 insertions(+), 53 deletions(-)

diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h
index c27275e..99b66d1 100644
--- a/libsepol/include/sepol/policydb/policydb.h
+++ b/libsepol/include/sepol/policydb/policydb.h
@@ -683,10 +683,11 @@ extern int policydb_set_target_platform(policydb_t *p, int platform);
 #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_DEFAULT_TYPE
+#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_CONSTRAINT_NAMES
 
 /* Module versions and specific changes*/
 #define MOD_POLICYDB_VERSION_BASE		4
diff --git a/libsepol/include/sepol/policydb/services.h b/libsepol/include/sepol/policydb/services.h
index aef0c7b..8b2065f 100644
--- a/libsepol/include/sepol/policydb/services.h
+++ b/libsepol/include/sepol/policydb/services.h
@@ -59,6 +59,38 @@ extern int sepol_compute_av_reason(sepol_security_id_t ssid,
 				   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.  
  */
diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
index f0555bb..7f3c3f5 100644
--- a/libsepol/src/expand.c
+++ b/libsepol/src/expand.c
@@ -384,6 +384,17 @@ static int constraint_node_clone(constraint_node_t ** dst,
 			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->
diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index 1f49261..ab712c3 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -165,6 +165,13 @@ static struct policydb_compat_info policydb_compat[] = {
 	 .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,
@@ -2019,6 +2026,10 @@ static int read_cons_helper(policydb_t * p, constraint_node_t ** nodep,
 				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;
diff --git a/libsepol/src/services.c b/libsepol/src/services.c
index 7fac4a0..73faa05 100644
--- a/libsepol/src/services.c
+++ b/libsepol/src/services.c
@@ -43,6 +43,11 @@
  * 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>
@@ -54,6 +59,7 @@
 #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"
@@ -70,6 +76,50 @@ static int selinux_enforcing = 1;
 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;
@@ -113,20 +163,227 @@ int sepol_set_policydb_from_file(FILE * fp)
 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;
@@ -135,57 +392,131 @@ static int constraint_expr_eval(context_struct_t * scontext,
 	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;
@@ -194,110 +525,289 @@ static int constraint_expr_eval(context_struct_t * scontext,
 			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;
 }
 
 /*
@@ -309,7 +819,9 @@ static int context_struct_compute_av(context_struct_t * scontext,
 				     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;
@@ -384,8 +896,8 @@ static int context_struct_compute_av(context_struct_t * scontext,
 	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);
 		}
@@ -460,8 +972,8 @@ int hidden sepol_validate_transition(sepol_security_id_t oldsid,
 
 	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;
@@ -494,11 +1006,58 @@ int hidden sepol_compute_av_reason(sepol_security_id_t ssid,
 	}
 
 	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,
@@ -511,6 +1070,71 @@ int hidden sepol_compute_av(sepol_security_id_t ssid,
 }
 
 /*
+ * 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'
@@ -1339,7 +1963,7 @@ int hidden sepol_get_user_sids(sepol_security_id_t fromsid,
 			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,
diff --git a/libsepol/src/write.c b/libsepol/src/write.c
index 55992f8..6fe73e6 100644
--- a/libsepol/src/write.c
+++ b/libsepol/src/write.c
@@ -893,8 +893,11 @@ static int write_cons_helper(policydb_t * p,
 				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;
-- 
1.8.3.1


[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux