Merge branch 'constraintnames'

This commit is contained in:
Stephen Smalley 2013-10-30 11:56:36 -04:00
commit 4b41f10db9
10 changed files with 782 additions and 78 deletions

View File

@ -299,6 +299,7 @@ static PyObject *init(PyObject *self __attribute__((unused)), PyObject *args) {
}
static PyObject *analyze(PyObject *self __attribute__((unused)) , PyObject *args) {
char *reason_buf = NULL;
security_context_t scon;
security_context_t tcon;
char *tclassstr;
@ -364,7 +365,7 @@ static PyObject *analyze(PyObject *self __attribute__((unused)) , PyObject *args
}
/* Reproduce the computation. */
rc = sepol_compute_av_reason(ssid, tsid, tclass, av, &avd, &reason);
rc = sepol_compute_av_reason_buffer(ssid, tsid, tclass, av, &avd, &reason, &reason_buf, 0);
if (rc < 0)
RETURN(BADCOMPUTE)
@ -404,6 +405,12 @@ static PyObject *analyze(PyObject *self __attribute__((unused)) , PyObject *args
}
if (reason & SEPOL_COMPUTEAV_CONS) {
if (reason_buf) {
PyObject *result = NULL;
result = Py_BuildValue("is", CONSTRAINT, reason_buf);
free(reason_buf);
return result;
}
RETURN(CONSTRAINT)
}

View File

@ -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

View File

@ -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_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.

View File

@ -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->

View File

@ -164,6 +164,13 @@ static struct policydb_compat_info policydb_compat[] = {
.ocon_num = OCON_NODE6 + 1,
.target_platform = SEPOL_TARGET_SELINUX,
},
{
.type = POLICY_KERN,
.version = POLICYDB_VERSION_CONSTRAINT_NAMES,
.sym_num = SYM_NUM,
.ocon_num = OCON_NODE6 + 1,
.target_platform = SEPOL_TARGET_SELINUX,
},
{
.type = POLICY_BASE,
.version = MOD_POLICYDB_VERSION_BASE,
@ -255,6 +262,13 @@ static struct policydb_compat_info policydb_compat[] = {
.ocon_num = OCON_NODE6 + 1,
.target_platform = SEPOL_TARGET_SELINUX,
},
{
.type = POLICY_BASE,
.version = MOD_POLICYDB_VERSION_CONSTRAINT_NAMES,
.sym_num = SYM_NUM,
.ocon_num = OCON_NODE6 + 1,
.target_platform = SEPOL_TARGET_SELINUX,
},
{
.type = POLICY_MOD,
.version = MOD_POLICYDB_VERSION_BASE,
@ -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;

View File

@ -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];
for (e = cexpr; e; e = e->next) {
/*
* 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 *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)
val1 = c->user;
else if (e->attr & CEXPR_ROLE)
val1 = c->role;
else if (e->attr & CEXPR_TYPE)
val1 = c->type;
else {
if (!c) {
BUG();
return 0;
goto out;
}
if (e->attr & CEXPR_USER) {
u_r_t = CEXPR_USER;
val1 = c->user;
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;
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;
snprintf(tmp_buf, sizeof(tmp_buf), "t%d ", s_t_x_num);
free(src); src = strdup(tmp_buf);
} else {
BUG();
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++;
}
/*
* 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;
}
}
}
}
BUG_ON(sp != 0);
return s[0];
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,
@ -510,6 +1069,71 @@ int hidden sepol_compute_av(sepol_security_id_t ssid,
&reason);
}
/*
* 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
@ -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,

View File

@ -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;

View File

@ -269,12 +269,11 @@ class AuditToPolicy:
continue
if rc == audit2why.CONSTRAINT:
print "\t\tPolicy constraint violation.\n"
print "\t\tMay require adding a type attribute to the domain or type to satisfy the constraint.\n"
print "\t\tConstraints are defined in the policy sources in policy/constraints (general), policy/mcs (MCS), and policy/mls (MLS).\n"
for reason in data:
print "\t\tNote: Possible cause is the source and target %s differ\n" % reason
continue
print #!!!! This avc is a constraint violation. You would need to modify the attributes of either the source or target types to allow this access.\n"
print "#Constraint rule:"
print "\n\t" + data[0]
for reason in data[1:]:
print "#\tPossible cause is the source %s and target %s are different.\n" % reason
if rc == audit2why.RBAC:
print "\t\tMissing role allow rule.\n"

View File

@ -259,13 +259,13 @@ class AVCMessage(AuditMessage):
raise ValueError("Error during access vector computation")
if self.type == audit2why.CONSTRAINT:
self.data = []
self.data = [ self.data ]
if self.scontext.user != self.tcontext.user:
self.data.append("user")
self.data.append(("user (%s)" % self.scontext.user, 'user (%s)' % self.tcontext.user))
if self.scontext.role != self.tcontext.role and self.tcontext.role != "object_r":
self.data.append("role")
self.data.append(("role (%s)" % self.scontext.role, 'role (%s)' % self.tcontext.role))
if self.scontext.level != self.tcontext.level:
self.data.append("level")
self.data.append(("level (%s)" % self.scontext.level, 'level (%s)' % self.tcontext.level))
avcdict[(scontext, tcontext, self.tclass, access_tuple)] = (self.type, self.data)

View File

@ -161,21 +161,22 @@ class PolicyGenerator:
if self.explain:
rule.comment = str(refpolicy.Comment(explain_access(av, verbosity=self.explain)))
if av.type == audit2why.ALLOW:
rule.comment += "#!!!! This avc is allowed in the current policy\n"
rule.comment += "\n#!!!! This avc is allowed in the current policy"
if av.type == audit2why.DONTAUDIT:
rule.comment += "#!!!! This avc has a dontaudit rule in the current policy\n"
rule.comment += "\n#!!!! This avc has a dontaudit rule in the current policy"
if av.type == audit2why.BOOLEAN:
if len(av.data) > 1:
rule.comment += "#!!!! This avc can be allowed using one of the these booleans:\n# %s\n" % ", ".join(map(lambda x: x[0], av.data))
rule.comment += "\n#!!!! This avc can be allowed using one of the these booleans:\n# %s" % ", ".join(map(lambda x: x[0], av.data))
else:
rule.comment += "#!!!! This avc can be allowed using the boolean '%s'\n" % av.data[0][0]
rule.comment += "\n#!!!! This avc can be allowed using the boolean '%s'" % av.data[0][0]
if av.type == audit2why.CONSTRAINT:
rule.comment += "#!!!! This avc is a constraint violation. You will need to add an attribute to either the source or target type to make it work.\n"
rule.comment += "#Constraint rule: "
for reason in av.data:
rule.comment += "\n#\tPossible cause source context and target context '%s' differ\b" % reason
rule.comment += "\n#!!!! This avc is a constraint violation. You would need to modify the attributes of either the source or target types to allow this access."
rule.comment += "\n#Constraint rule: "
rule.comment += "\n\t" + av.data[0]
for reason in av.data[1:]:
rule.comment += "\n#\tPossible cause is the source %s and target %s are different." % reason
try:
if ( av.type == audit2why.TERULE and
@ -189,9 +190,9 @@ class PolicyGenerator:
if i not in self.domains:
types.append(i)
if len(types) == 1:
rule.comment += "#!!!! The source type '%s' can write to a '%s' of the following type:\n# %s\n" % ( av.src_type, av.obj_class, ", ".join(types))
rule.comment += "\n#!!!! The source type '%s' can write to a '%s' of the following type:\n# %s\n" % ( av.src_type, av.obj_class, ", ".join(types))
elif len(types) >= 1:
rule.comment += "#!!!! The source type '%s' can write to a '%s' of the following types:\n# %s\n" % ( av.src_type, av.obj_class, ", ".join(types))
rule.comment += "\n#!!!! The source type '%s' can write to a '%s' of the following types:\n# %s\n" % ( av.src_type, av.obj_class, ", ".join(types))
except:
pass
self.module.children.append(rule)