Skip to content

Commit

Permalink
selinux: validate constraints
Browse files Browse the repository at this point in the history
Validate constraint expressions during reading the policy.
Avoid the usage of BUG() on constraint evaluation, to mitigate malformed
policies halting the system.

Closes: SELinuxProject/selinux-testsuite#76

Signed-off-by: Christian Göttsche <[email protected]>
  • Loading branch information
cgzones committed Dec 16, 2024
1 parent 3ec5d7b commit 349418c
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 28 deletions.
61 changes: 59 additions & 2 deletions security/selinux/ss/policydb.c
Original file line number Diff line number Diff line change
Expand Up @@ -1256,6 +1256,8 @@ static int read_cons_helper(struct policydb *p, struct constraint_node **nodep,
return rc;
c->permissions = le32_to_cpu(buf[0]);
nexpr = le32_to_cpu(buf[1]);
if (nexpr == 0)
return -EINVAL;
le = NULL;
depth = -1;
for (j = 0; j < nexpr; j++) {
Expand Down Expand Up @@ -1287,15 +1289,70 @@ static int read_cons_helper(struct policydb *p, struct constraint_node **nodep,
depth--;
break;
case CEXPR_ATTR:
if (depth == (CEXPR_MAXDEPTH - 1))
if (depth >= (CEXPR_MAXDEPTH - 1))
return -EINVAL;
depth++;
break;

switch (e->op) {
case CEXPR_EQ:
case CEXPR_NEQ:
break;
case CEXPR_DOM:
case CEXPR_DOMBY:
case CEXPR_INCOMP:
if ((e->attr & CEXPR_USER) || (e->attr & CEXPR_TYPE))
return -EINVAL;
break;
default:
return -EINVAL;
}

switch (e->attr) {
case CEXPR_USER:
case CEXPR_ROLE:
case CEXPR_TYPE:
case CEXPR_L1L2:
case CEXPR_L1H2:
case CEXPR_H1L2:
case CEXPR_H1H2:
case CEXPR_L1H1:
case CEXPR_L2H2:
break;
default:
return -EINVAL;
}

break;
case CEXPR_NAMES:
if (!allowxtarget && (e->attr & CEXPR_XTARGET))
return -EINVAL;
if (depth == (CEXPR_MAXDEPTH - 1))
if (depth >= (CEXPR_MAXDEPTH - 1))
return -EINVAL;

switch (e->op) {
case CEXPR_EQ:
case CEXPR_NEQ:
break;
default:
return -EINVAL;
}

switch (e->attr) {
case CEXPR_USER:
case CEXPR_USER | CEXPR_TARGET:
case CEXPR_USER | CEXPR_XTARGET:
case CEXPR_ROLE:
case CEXPR_ROLE | CEXPR_TARGET:
case CEXPR_ROLE | CEXPR_XTARGET:
case CEXPR_TYPE:
case CEXPR_TYPE | CEXPR_TARGET:
case CEXPR_TYPE | CEXPR_XTARGET:
break;
default:
return -EINVAL;
}

depth++;
rc = ebitmap_read(&e->names, fp);
if (rc)
Expand Down
55 changes: 29 additions & 26 deletions security/selinux/ss/services.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,22 +278,25 @@ static int constraint_expr_eval(struct policydb *policydb,
for (e = cexpr; e; e = e->next) {
switch (e->expr_type) {
case CEXPR_NOT:
BUG_ON(sp < 0);
if (unlikely(sp < 0))
goto invalid;
s[sp] = !s[sp];
break;
case CEXPR_AND:
BUG_ON(sp < 1);
if (unlikely(sp < 1))
goto invalid;
sp--;
s[sp] &= s[sp + 1];
break;
case CEXPR_OR:
BUG_ON(sp < 1);
if (unlikely(sp < 1))
goto invalid;
sp--;
s[sp] |= s[sp + 1];
break;
case CEXPR_ATTR:
if (sp == (CEXPR_MAXDEPTH - 1))
return 0;
if (unlikely(sp >= (CEXPR_MAXDEPTH - 1)))
goto invalid;
switch (e->attr) {
case CEXPR_USER:
val1 = scontext->user;
Expand Down Expand Up @@ -369,13 +372,11 @@ static int constraint_expr_eval(struct policydb *policydb,
s[++sp] = mls_level_incomp(l2, l1);
continue;
default:
BUG();
return 0;
goto invalid;
}
break;
default:
BUG();
return 0;
goto invalid;
}

switch (e->op) {
Expand All @@ -386,33 +387,28 @@ static int constraint_expr_eval(struct policydb *policydb,
s[++sp] = (val1 != val2);
break;
default:
BUG();
return 0;
goto invalid;
}
break;
case CEXPR_NAMES:
if (sp == (CEXPR_MAXDEPTH-1))
return 0;
if (unlikely(sp >= (CEXPR_MAXDEPTH-1)))
goto invalid;
c = scontext;
if (e->attr & CEXPR_TARGET)
c = tcontext;
else if (e->attr & CEXPR_XTARGET) {
c = xcontext;
if (!c) {
BUG();
return 0;
}
if (unlikely(!c))
goto invalid;
}
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 {
BUG();
return 0;
}
else
goto invalid;

switch (e->op) {
case CEXPR_EQ:
Expand All @@ -422,18 +418,25 @@ static int constraint_expr_eval(struct policydb *policydb,
s[++sp] = !ebitmap_get_bit(&e->names, val1 - 1);
break;
default:
BUG();
return 0;
goto invalid;
}
break;
default:
BUG();
return 0;
goto invalid;
}
}

BUG_ON(sp != 0);
if (unlikely(sp != 0))
goto invalid;

return s[0];

invalid:
/* Should *never* be reached, cause malformed constraints should
* have been filtered by read_cons_helper().
*/
WARN_ONCE(true, "SELinux: invalid constraint passed validation\n");
return 0;
}

/*
Expand Down

0 comments on commit 349418c

Please sign in to comment.