diff --git a/libsepol-rhat.patch b/libsepol-rhat.patch deleted file mode 100644 index 552c827..0000000 --- a/libsepol-rhat.patch +++ /dev/null @@ -1,1118 +0,0 @@ -diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h -index c27275e..0165eed 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 -@@ -704,9 +705,10 @@ extern int policydb_set_target_platform(policydb_t *p, int platform); - #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_DEFAULT_TYPE -+#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_CONSTRAINT_NAMES - - #define POLICYDB_CONFIG_MLS 1 - -diff --git a/libsepol/include/sepol/policydb/services.h b/libsepol/include/sepol/policydb/services.h -index aef0c7b..1969a10 100644 ---- a/libsepol/include/sepol/policydb/services.h -+++ b/libsepol/include/sepol/policydb/services.h -@@ -58,6 +58,38 @@ extern int sepol_compute_av_reason(sepol_security_id_t ssid, - struct sepol_av_decision *avd, - 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_class_name_to_id(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_perm_name_to_av(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..6fd992f 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..8c7efbc 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, -@@ -256,6 +263,13 @@ static struct policydb_compat_info policydb_compat[] = { - .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, -@@ -346,6 +360,13 @@ static struct policydb_compat_info policydb_compat[] = { - .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 -@@ -2019,6 +2040,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..43ec07e 100644 ---- a/libsepol/src/services.c -+++ b/libsepol/src/services.c -@@ -43,6 +43,11 @@ - * Implementation of the security services. - */ - -+/* The 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 - #include - #include -@@ -54,6 +59,7 @@ - #include - #include - #include -+#include - - #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,195 @@ 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 if the buffer is realloc'ed. -+ */ -+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 the new ptr in the 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 just 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. -+ */ -+static void get_names_list(constraint_expr_t *e, int type) -+{ -+ ebitmap_t *types; -+ types = &e->type_names->types; -+ int rc = 0; -+ unsigned int i; -+ char tmp_buf[128]; -+ /* if -type_names->types is 0, then output string */ -+ int empty_set = 0; -+ -+ if (policydb->policy_type == POLICY_KERN && -+ policydb->policyvers >= POLICYDB_VERSION_CONSTRAINT_NAMES && -+ type == CEXPR_TYPE) { -+ /* -+ * Process >= POLICYDB_VERSION_CONSTRAINT_NAMES with CEXPR_TYPE, then -+ * obtain the list of names defined in the policy source. -+ */ -+ cat_expr_buf(expr_list[expr_counter], "{ POLICY_SOURCE: "); -+ for (i = ebitmap_startbit(types); i < ebitmap_length(types); i++) { -+ if ((rc = ebitmap_get_bit(types, i)) == 0) -+ continue; -+ /* Collect entries */ -+ snprintf(tmp_buf, sizeof(tmp_buf), "%s ", policydb->p_type_val_to_name[i]); -+ cat_expr_buf(expr_list[expr_counter], tmp_buf); -+ empty_set++; -+ } -+ if (empty_set == 0) -+ cat_expr_buf(expr_list[expr_counter], " "); -+ cat_expr_buf(expr_list[expr_counter], "} "); -+ } -+ return; -+} -+ -+static void msgcat(char *src, char *tgt, char *rel, int failed) -+{ -+ char tmp_buf[1024]; -+ if (failed) -+ snprintf(tmp_buf, sizeof(tmp_buf), "(%s %s %s -Fail-) ", -+ src, rel, tgt); -+ else -+ snprintf(tmp_buf, sizeof(tmp_buf), "(%s %s %s -Pass-) ", -+ src, rel, 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. -+ * 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, -+static int constraint_expr_eval_reason(context_struct_t * scontext, - context_struct_t * tcontext, - context_struct_t * xcontext, -- constraint_expr_t * cexpr) -+ sepol_security_class_t tclass, -+ constraint_node_t *constraint, -+ char **r_buf, -+ unsigned int flags) - { - uint32_t val1, val2; - context_struct_t *c; -@@ -136,56 +361,137 @@ static int constraint_expr_eval(context_struct_t * scontext, - int s[CEXPR_MAXDEPTH]; - int sp = -1; - -- for (e = cexpr; e; e = e->next) { -+ 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; -+ -+ char *name1, *name2; -+ 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 the -+ * 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]; -+ name1 = policydb->p_role_val_to_name[r1->s.value - 1]; -+ name2 = policydb->p_role_val_to_name[r2->s.value - 1]; -+ snprintf(tmp_buf, sizeof(tmp_buf), "r1=%s", name1); -+ free(src); src = strdup(tmp_buf); -+ snprintf(tmp_buf, sizeof(tmp_buf), "r2=%s ", name2); -+ free(tgt); tgt = strdup(tmp_buf); -+ - 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 +500,327 @@ 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, "neq", 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, "eq", s[sp] == 0); - break; - case CEXPR_NEQ: - s[++sp] = (val1 != val2); -+ msgcat(src, tgt, "neq", 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) -+ name1 = policydb->p_user_val_to_name[val1 - 1]; -+ snprintf(tmp_buf, sizeof(tmp_buf), "u%d=%s ", -+ s_t_x_num, name1); -+ 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) -+ name1 = policydb->p_role_val_to_name[val1 - 1]; -+ snprintf(tmp_buf, sizeof(tmp_buf), "r%d=%s ", s_t_x_num, name1); -+ free(src); src = strdup(tmp_buf); -+ } -+ else if (e->attr & CEXPR_TYPE) { -+ u_r_t = CEXPR_TYPE; - val1 = c->type; -+ name1 = policydb->p_type_val_to_name[val1 - 1]; -+ snprintf(tmp_buf, sizeof(tmp_buf), -+ "t%d=%s ", s_t_x_num, name1); -+ free(src); src = strdup(tmp_buf); -+ } - else { - BUG(); -- return 0; -+ goto out; - } - - switch (e->op) { - case CEXPR_EQ: -+ switch (u_r_t) { -+ case CEXPR_USER: -+ free(tgt); tgt=strdup("USER_ENTRY"); -+ break; -+ case CEXPR_ROLE: -+ free(tgt); tgt=strdup("ROLE_ENTRY"); -+ break; -+ case CEXPR_TYPE: -+ free(tgt); tgt=strdup("TYPE_ENTRY"); -+ break; -+ default: -+ ERR(NULL, "unrecognized u_r_t Value: %d", u_r_t); -+ break; -+ } -+ - s[++sp] = ebitmap_get_bit(&e->names, val1 - 1); -+ msgcat(src, tgt, "eq", s[sp] == 0); -+ if (s[sp] == 0) { -+ get_names_list(e, u_r_t); -+ } - break; -+ - case CEXPR_NEQ: -+ switch (u_r_t) { -+ case CEXPR_USER: -+ free(tgt); tgt=strdup("USER_ENTRY"); -+ break; -+ case CEXPR_ROLE: -+ free(tgt); tgt=strdup("ROLE_ENTRY"); -+ break; -+ case CEXPR_TYPE: -+ free(tgt); tgt=strdup("TYPE_ENTRY"); -+ break; -+ default: -+ ERR(NULL, "unrecognized u_r_t Value: %d", u_r_t); -+ break; -+ } -+ - s[++sp] = !ebitmap_get_bit(&e->names, val1 - 1); -+ msgcat(src, tgt, "neq", s[sp] == 0); -+ if (s[sp] == 0) { -+ get_names_list(e, u_r_t); -+ } - break; - default: - BUG(); -- return 0; -+ goto out; - } - break; - default: - BUG(); -- return 0; -+ goto out; - } -+ expr_counter++; -+ } -+ -+ /* -+ * 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 required). -+ */ -+ 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; - } - -- BUG_ON(sp != 0); -- return s[0]; -+ /* 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 +832,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 +909,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 +985,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 +1019,59 @@ 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) -+{ -+ *reason_buf = malloc(REASON_BUF_SIZE); -+ if (!*reason_buf) { -+ ERR(NULL, "failed to allocate reason buffer"); -+ return -ENOMEM; -+ } -+ /* -+ * These are defined globally as the buffer can contain multiple -+ * constraint statements so need to keep track -+ */ -+ reason_buf_used = 0; -+ reason_buf_len = REASON_BUF_SIZE; -+ -+ 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; -+ } -+ -+ 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 +1084,70 @@ 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_class_name_to_id(const char *class_name, -+ sepol_security_class_t *tclass) -+{ -+ char *class = NULL; -+ sepol_security_class_t id; -+ -+ for (id = 1; ; id++) { -+ if ((class = policydb->p_class_val_to_name[id - 1]) == 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_perm_name_to_av(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 +1976,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;