diff mbox series

[v2] libsepol: validate expressions by evaluating

Message ID 20220222181148.54329-1-cgzones@googlemail.com (mailing list archive)
State Superseded
Headers show
Series [v2] libsepol: validate expressions by evaluating | expand

Commit Message

Christian Göttsche Feb. 22, 2022, 6:11 p.m. UTC
Evaluate expressions similar to the actual kernel security server such
that invalid expressions, e.g. `t2 == t3` for validatetrans, are
rejected.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
v2:
    reject third task context in normal constraints
---
 libsepol/src/policydb_validate.c | 226 ++++++++++++++++++++++---------
 1 file changed, 159 insertions(+), 67 deletions(-)

Comments

James Carter March 3, 2022, 9:53 p.m. UTC | #1
On Tue, Feb 22, 2022 at 9:51 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Evaluate expressions similar to the actual kernel security server such
> that invalid expressions, e.g. `t2 == t3` for validatetrans, are
> rejected.
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
> v2:
>     reject third task context in normal constraints
> ---
>  libsepol/src/policydb_validate.c | 226 ++++++++++++++++++++++---------
>  1 file changed, 159 insertions(+), 67 deletions(-)
>
> diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
> index 735c7a33..3b0ea0e1 100644
> --- a/libsepol/src/policydb_validate.c
> +++ b/libsepol/src/policydb_validate.c
> @@ -223,90 +223,182 @@ bad:
>         return -1;
>  }
>
> -static int validate_constraint_nodes(sepol_handle_t *handle, unsigned int nperms, constraint_node_t *cons, validate_t flavors[])
> +/*
> + * Follow evaluation of expressions to find invalid ones.
> + * Keep in sync with kernel source security/selinux/ss/services.c::constraint_expr_eval()
> + */
> +static int validate_expression(sepol_handle_t *handle, constraint_expr_t *e, int validatetrans, validate_t flavors[])
>  {
> -       constraint_expr_t *cexp;
> -
> -       for (; cons; cons = cons->next) {
> -               if (nperms > 0 && cons->permissions == 0)
> -                       goto bad;
> -               if (nperms > 0 && nperms != PERM_SYMTAB_SIZE && cons->permissions >= (UINT32_C(1) << nperms))
> -                       goto bad;
> +       int sp = -1;

The function read_cons_helper() is used when reading in the policy and
it keeps track of the stack and will return an error if there is a
problem, so I don't think that this is going to be useful when
validating the policy.

Most of what you have here is concerned with keeping track of the
stack. There is more validation that can be done, however. See if the
patch that I sent to the list will meet your needs.

Thanks,
Jim


>
> -               for (cexp = cons->expr; cexp; cexp = cexp->next) {
> -                       if (cexp->attr & CEXPR_USER) {
> -                               if (validate_ebitmap(&cexp->names, &flavors[SYM_USERS]))
> -                                       goto bad;
> -                               if (validate_empty_type_set(cexp->type_names))
> -                                       goto bad;
> -                       } else if (cexp->attr & CEXPR_ROLE) {
> -                               if (validate_ebitmap(&cexp->names, &flavors[SYM_ROLES]))
> -                                       goto bad;
> -                               if (validate_empty_type_set(cexp->type_names))
> -                                       goto bad;
> -                       } else if (cexp->attr & CEXPR_TYPE) {
> -                               if (validate_ebitmap(&cexp->names, &flavors[SYM_TYPES]))
> -                                       goto bad;
> -                               if (validate_type_set(cexp->type_names, &flavors[SYM_TYPES]))
> -                                       goto bad;
> -                       } else {
> -                               if (!ebitmap_is_empty(&cexp->names))
> -                                       goto bad;
> -                               if (validate_empty_type_set(cexp->type_names))
> -                                       goto bad;
> -                       }
> +       for (; e; e = e->next) {
> +               /* validate symbols (implied in kernel source) */
> +               if (e->attr & CEXPR_USER) {
> +                       if (validate_ebitmap(&e->names, &flavors[SYM_USERS]))
> +                               goto bad;
> +                       if (validate_empty_type_set(e->type_names))
> +                               goto bad;
> +               } else if (e->attr & CEXPR_ROLE) {
> +                       if (validate_ebitmap(&e->names, &flavors[SYM_ROLES]))
> +                               goto bad;
> +                       if (validate_empty_type_set(e->type_names))
> +                               goto bad;
> +               } else if (e->attr & CEXPR_TYPE) {
> +                       if (validate_ebitmap(&e->names, &flavors[SYM_TYPES]))
> +                               goto bad;
> +                       if (validate_type_set(e->type_names, &flavors[SYM_TYPES]))
> +                               goto bad;
> +               } else {
> +                       if (!ebitmap_is_empty(&e->names))
> +                               goto bad;
> +                       if (validate_empty_type_set(e->type_names))
> +                               goto bad;
> +               }
>
> -                       if (cexp->expr_type == CEXPR_ATTR || cexp->expr_type == CEXPR_NAMES) {
> -                               switch (cexp->op) {
> -                               case CEXPR_EQ:
> -                               case CEXPR_NEQ:
> +               switch (e->expr_type) {
> +               case CEXPR_NOT:
> +                       if(sp < 0)
> +                               goto bad;
> +                       break;
> +               case CEXPR_AND:
> +                       if(sp < 0)
> +                               goto bad;
> +                       sp--;
> +                       break;
> +               case CEXPR_OR:
> +                       if(sp < 0)
> +                               goto bad;
> +                       sp--;
> +                       break;
> +               case CEXPR_ATTR:
> +                       if (sp == (CEXPR_MAXDEPTH - 1))
> +                               return 0;
> +                       switch (e->attr) {
> +                       case CEXPR_USER:
> +                               break;
> +                       case CEXPR_TYPE:
> +                               break;
> +                       case CEXPR_ROLE:
> +                               switch (e->op) {
>                                 case CEXPR_DOM:
> +                                       ++sp;
> +                                       continue;
>                                 case CEXPR_DOMBY:
> +                                       ++sp;
> +                                       continue;
>                                 case CEXPR_INCOMP:
> -                                       break;
> +                                       ++sp;
> +                                       continue;
>                                 default:
> -                                       goto bad;
> -                               }
> -
> -                               switch (cexp->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:
> -                               case CEXPR_L1L2:
> -                               case CEXPR_L1H2:
> -                               case CEXPR_H1L2:
> -                               case CEXPR_H1H2:
> -                               case CEXPR_L1H1:
> -                               case CEXPR_L2H2:
>                                         break;
> -                               default:
> -                                       goto bad;
>                                 }
> -                       } else {
> -                               switch (cexp->expr_type) {
> -                               case CEXPR_NOT:
> -                               case CEXPR_AND:
> -                               case CEXPR_OR:
> -                                       break;
> +                               break;
> +                       case CEXPR_L1L2:
> +                               goto mls_ops;
> +                       case CEXPR_L1H2:
> +                               goto mls_ops;
> +                       case CEXPR_H1L2:
> +                               goto mls_ops;
> +                       case CEXPR_H1H2:
> +                               goto mls_ops;
> +                       case CEXPR_L1H1:
> +                               goto mls_ops;
> +                       case CEXPR_L2H2:
> +                               goto mls_ops;
> +mls_ops:
> +                               switch (e->op) {
> +                               case CEXPR_EQ:
> +                                       ++sp;
> +                                       continue;
> +                               case CEXPR_NEQ:
> +                                       ++sp;
> +                                       continue;
> +                               case CEXPR_DOM:
> +                                       ++sp;
> +                                       continue;
> +                               case CEXPR_DOMBY:
> +                                       ++sp;
> +                                       continue;
> +                               case CEXPR_INCOMP:
> +                                       ++sp;
> +                                       continue;
>                                 default:
>                                         goto bad;
>                                 }
> +                               break;
> +                       default:
> +                               goto bad;
> +                       }
>
> -                               if (cexp->op != 0)
> +                       switch (e->op) {
> +                       case CEXPR_EQ:
> +                               ++sp;
> +                               break;
> +                       case CEXPR_NEQ:
> +                               ++sp;
> +                               break;
> +                       default:
> +                               goto bad;
> +                       }
> +                       break;
> +               case CEXPR_NAMES:
> +                       if (sp == (CEXPR_MAXDEPTH-1))
> +                               return 0;
> +                       if (e->attr & CEXPR_TARGET)
> +                               ;
> +                       else if (e->attr & CEXPR_XTARGET) {
> +                               if (!validatetrans)
>                                         goto bad;
> +                       }
> +                       if (e->attr & CEXPR_USER)
> +                               ;
> +                       else if (e->attr & CEXPR_ROLE)
> +                               ;
> +                       else if (e->attr & CEXPR_TYPE)
> +                               ;
> +                       else
> +                               goto bad;
>
> -                               if (cexp->attr != 0)
> -                                       goto bad;
> +                       switch (e->op) {
> +                       case CEXPR_EQ:
> +                               ++sp;
> +                               break;
> +                       case CEXPR_NEQ:
> +                               ++sp;
> +                               break;
> +                       default:
> +                               goto bad;
>                         }
> +                       break;
> +               default:
> +                       goto bad;
>                 }
>         }
>
> +       if (sp != 0)
> +               goto bad;
> +
> +       return 0;
> +
> +bad:
> +       ERR(handle, "Invalid expression");
> +       return -1;
> +}
> +
> +static int validate_constraint_nodes(sepol_handle_t *handle, unsigned int nperms, constraint_node_t *cons, int validatetrans, validate_t flavors[])
> +{
> +       for (; cons; cons = cons->next) {
> +               if (validatetrans && cons->permissions != 0)
> +                       goto bad;
> +               if (!validatetrans && cons->permissions == 0)
> +                       goto bad;
> +               if (!validatetrans && nperms != PERM_SYMTAB_SIZE && cons->permissions >= (UINT32_C(1) << nperms))
> +                       goto bad;
> +
> +               if (validate_expression(handle, cons->expr, validatetrans, flavors))
> +                       goto bad;
> +       }
> +
>         return 0;
>
>  bad:
> @@ -320,9 +412,9 @@ static int validate_class_datum(sepol_handle_t *handle, class_datum_t *class, va
>                 goto bad;
>         if (class->permissions.nprim > PERM_SYMTAB_SIZE)
>                 goto bad;
> -       if (validate_constraint_nodes(handle, class->permissions.nprim, class->constraints, flavors))
> +       if (validate_constraint_nodes(handle, class->permissions.nprim, class->constraints, 0, flavors))
>                 goto bad;
> -       if (validate_constraint_nodes(handle, 0, class->validatetrans, flavors))
> +       if (validate_constraint_nodes(handle, class->permissions.nprim, class->validatetrans, 1, flavors))
>                 goto bad;
>
>         switch (class->default_user) {
> --
> 2.35.1
>
Christian Göttsche March 8, 2022, 7:06 p.m. UTC | #2
On Thu, 3 Mar 2022 at 22:53, James Carter <jwcart2@gmail.com> wrote:
>
> On Tue, Feb 22, 2022 at 9:51 PM Christian Göttsche
> <cgzones@googlemail.com> wrote:
> >
> > Evaluate expressions similar to the actual kernel security server such
> > that invalid expressions, e.g. `t2 == t3` for validatetrans, are
> > rejected.
> >
> > Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> > ---
> > v2:
> >     reject third task context in normal constraints
> > ---
> >  libsepol/src/policydb_validate.c | 226 ++++++++++++++++++++++---------
> >  1 file changed, 159 insertions(+), 67 deletions(-)
> >
> > diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
> > index 735c7a33..3b0ea0e1 100644
> > --- a/libsepol/src/policydb_validate.c
> > +++ b/libsepol/src/policydb_validate.c
> > @@ -223,90 +223,182 @@ bad:
> >         return -1;
> >  }
> >
> > -static int validate_constraint_nodes(sepol_handle_t *handle, unsigned int nperms, constraint_node_t *cons, validate_t flavors[])
> > +/*
> > + * Follow evaluation of expressions to find invalid ones.
> > + * Keep in sync with kernel source security/selinux/ss/services.c::constraint_expr_eval()
> > + */
> > +static int validate_expression(sepol_handle_t *handle, constraint_expr_t *e, int validatetrans, validate_t flavors[])
> >  {
> > -       constraint_expr_t *cexp;
> > -
> > -       for (; cons; cons = cons->next) {
> > -               if (nperms > 0 && cons->permissions == 0)
> > -                       goto bad;
> > -               if (nperms > 0 && nperms != PERM_SYMTAB_SIZE && cons->permissions >= (UINT32_C(1) << nperms))
> > -                       goto bad;
> > +       int sp = -1;
>
> The function read_cons_helper() is used when reading in the policy and
> it keeps track of the stack and will return an error if there is a
> problem, so I don't think that this is going to be useful when
> validating the policy.
>
> Most of what you have here is concerned with keeping track of the
> stack. There is more validation that can be done, however. See if the
> patch that I sent to the list will meet your needs.

Indeed it does (and thus renders this patch superseded).
Thanks.

>
> Thanks,
> Jim
>
>
> >
> > -               for (cexp = cons->expr; cexp; cexp = cexp->next) {
> > -                       if (cexp->attr & CEXPR_USER) {
> > -                               if (validate_ebitmap(&cexp->names, &flavors[SYM_USERS]))
> > -                                       goto bad;
> > -                               if (validate_empty_type_set(cexp->type_names))
> > -                                       goto bad;
> > -                       } else if (cexp->attr & CEXPR_ROLE) {
> > -                               if (validate_ebitmap(&cexp->names, &flavors[SYM_ROLES]))
> > -                                       goto bad;
> > -                               if (validate_empty_type_set(cexp->type_names))
> > -                                       goto bad;
> > -                       } else if (cexp->attr & CEXPR_TYPE) {
> > -                               if (validate_ebitmap(&cexp->names, &flavors[SYM_TYPES]))
> > -                                       goto bad;
> > -                               if (validate_type_set(cexp->type_names, &flavors[SYM_TYPES]))
> > -                                       goto bad;
> > -                       } else {
> > -                               if (!ebitmap_is_empty(&cexp->names))
> > -                                       goto bad;
> > -                               if (validate_empty_type_set(cexp->type_names))
> > -                                       goto bad;
> > -                       }
> > +       for (; e; e = e->next) {
> > +               /* validate symbols (implied in kernel source) */
> > +               if (e->attr & CEXPR_USER) {
> > +                       if (validate_ebitmap(&e->names, &flavors[SYM_USERS]))
> > +                               goto bad;
> > +                       if (validate_empty_type_set(e->type_names))
> > +                               goto bad;
> > +               } else if (e->attr & CEXPR_ROLE) {
> > +                       if (validate_ebitmap(&e->names, &flavors[SYM_ROLES]))
> > +                               goto bad;
> > +                       if (validate_empty_type_set(e->type_names))
> > +                               goto bad;
> > +               } else if (e->attr & CEXPR_TYPE) {
> > +                       if (validate_ebitmap(&e->names, &flavors[SYM_TYPES]))
> > +                               goto bad;
> > +                       if (validate_type_set(e->type_names, &flavors[SYM_TYPES]))
> > +                               goto bad;
> > +               } else {
> > +                       if (!ebitmap_is_empty(&e->names))
> > +                               goto bad;
> > +                       if (validate_empty_type_set(e->type_names))
> > +                               goto bad;
> > +               }
> >
> > -                       if (cexp->expr_type == CEXPR_ATTR || cexp->expr_type == CEXPR_NAMES) {
> > -                               switch (cexp->op) {
> > -                               case CEXPR_EQ:
> > -                               case CEXPR_NEQ:
> > +               switch (e->expr_type) {
> > +               case CEXPR_NOT:
> > +                       if(sp < 0)
> > +                               goto bad;
> > +                       break;
> > +               case CEXPR_AND:
> > +                       if(sp < 0)
> > +                               goto bad;
> > +                       sp--;
> > +                       break;
> > +               case CEXPR_OR:
> > +                       if(sp < 0)
> > +                               goto bad;
> > +                       sp--;
> > +                       break;
> > +               case CEXPR_ATTR:
> > +                       if (sp == (CEXPR_MAXDEPTH - 1))
> > +                               return 0;
> > +                       switch (e->attr) {
> > +                       case CEXPR_USER:
> > +                               break;
> > +                       case CEXPR_TYPE:
> > +                               break;
> > +                       case CEXPR_ROLE:
> > +                               switch (e->op) {
> >                                 case CEXPR_DOM:
> > +                                       ++sp;
> > +                                       continue;
> >                                 case CEXPR_DOMBY:
> > +                                       ++sp;
> > +                                       continue;
> >                                 case CEXPR_INCOMP:
> > -                                       break;
> > +                                       ++sp;
> > +                                       continue;
> >                                 default:
> > -                                       goto bad;
> > -                               }
> > -
> > -                               switch (cexp->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:
> > -                               case CEXPR_L1L2:
> > -                               case CEXPR_L1H2:
> > -                               case CEXPR_H1L2:
> > -                               case CEXPR_H1H2:
> > -                               case CEXPR_L1H1:
> > -                               case CEXPR_L2H2:
> >                                         break;
> > -                               default:
> > -                                       goto bad;
> >                                 }
> > -                       } else {
> > -                               switch (cexp->expr_type) {
> > -                               case CEXPR_NOT:
> > -                               case CEXPR_AND:
> > -                               case CEXPR_OR:
> > -                                       break;
> > +                               break;
> > +                       case CEXPR_L1L2:
> > +                               goto mls_ops;
> > +                       case CEXPR_L1H2:
> > +                               goto mls_ops;
> > +                       case CEXPR_H1L2:
> > +                               goto mls_ops;
> > +                       case CEXPR_H1H2:
> > +                               goto mls_ops;
> > +                       case CEXPR_L1H1:
> > +                               goto mls_ops;
> > +                       case CEXPR_L2H2:
> > +                               goto mls_ops;
> > +mls_ops:
> > +                               switch (e->op) {
> > +                               case CEXPR_EQ:
> > +                                       ++sp;
> > +                                       continue;
> > +                               case CEXPR_NEQ:
> > +                                       ++sp;
> > +                                       continue;
> > +                               case CEXPR_DOM:
> > +                                       ++sp;
> > +                                       continue;
> > +                               case CEXPR_DOMBY:
> > +                                       ++sp;
> > +                                       continue;
> > +                               case CEXPR_INCOMP:
> > +                                       ++sp;
> > +                                       continue;
> >                                 default:
> >                                         goto bad;
> >                                 }
> > +                               break;
> > +                       default:
> > +                               goto bad;
> > +                       }
> >
> > -                               if (cexp->op != 0)
> > +                       switch (e->op) {
> > +                       case CEXPR_EQ:
> > +                               ++sp;
> > +                               break;
> > +                       case CEXPR_NEQ:
> > +                               ++sp;
> > +                               break;
> > +                       default:
> > +                               goto bad;
> > +                       }
> > +                       break;
> > +               case CEXPR_NAMES:
> > +                       if (sp == (CEXPR_MAXDEPTH-1))
> > +                               return 0;
> > +                       if (e->attr & CEXPR_TARGET)
> > +                               ;
> > +                       else if (e->attr & CEXPR_XTARGET) {
> > +                               if (!validatetrans)
> >                                         goto bad;
> > +                       }
> > +                       if (e->attr & CEXPR_USER)
> > +                               ;
> > +                       else if (e->attr & CEXPR_ROLE)
> > +                               ;
> > +                       else if (e->attr & CEXPR_TYPE)
> > +                               ;
> > +                       else
> > +                               goto bad;
> >
> > -                               if (cexp->attr != 0)
> > -                                       goto bad;
> > +                       switch (e->op) {
> > +                       case CEXPR_EQ:
> > +                               ++sp;
> > +                               break;
> > +                       case CEXPR_NEQ:
> > +                               ++sp;
> > +                               break;
> > +                       default:
> > +                               goto bad;
> >                         }
> > +                       break;
> > +               default:
> > +                       goto bad;
> >                 }
> >         }
> >
> > +       if (sp != 0)
> > +               goto bad;
> > +
> > +       return 0;
> > +
> > +bad:
> > +       ERR(handle, "Invalid expression");
> > +       return -1;
> > +}
> > +
> > +static int validate_constraint_nodes(sepol_handle_t *handle, unsigned int nperms, constraint_node_t *cons, int validatetrans, validate_t flavors[])
> > +{
> > +       for (; cons; cons = cons->next) {
> > +               if (validatetrans && cons->permissions != 0)
> > +                       goto bad;
> > +               if (!validatetrans && cons->permissions == 0)
> > +                       goto bad;
> > +               if (!validatetrans && nperms != PERM_SYMTAB_SIZE && cons->permissions >= (UINT32_C(1) << nperms))
> > +                       goto bad;
> > +
> > +               if (validate_expression(handle, cons->expr, validatetrans, flavors))
> > +                       goto bad;
> > +       }
> > +
> >         return 0;
> >
> >  bad:
> > @@ -320,9 +412,9 @@ static int validate_class_datum(sepol_handle_t *handle, class_datum_t *class, va
> >                 goto bad;
> >         if (class->permissions.nprim > PERM_SYMTAB_SIZE)
> >                 goto bad;
> > -       if (validate_constraint_nodes(handle, class->permissions.nprim, class->constraints, flavors))
> > +       if (validate_constraint_nodes(handle, class->permissions.nprim, class->constraints, 0, flavors))
> >                 goto bad;
> > -       if (validate_constraint_nodes(handle, 0, class->validatetrans, flavors))
> > +       if (validate_constraint_nodes(handle, class->permissions.nprim, class->validatetrans, 1, flavors))
> >                 goto bad;
> >
> >         switch (class->default_user) {
> > --
> > 2.35.1
> >
diff mbox series

Patch

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 735c7a33..3b0ea0e1 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -223,90 +223,182 @@  bad:
 	return -1;
 }
 
-static int validate_constraint_nodes(sepol_handle_t *handle, unsigned int nperms, constraint_node_t *cons, validate_t flavors[])
+/*
+ * Follow evaluation of expressions to find invalid ones.
+ * Keep in sync with kernel source security/selinux/ss/services.c::constraint_expr_eval()
+ */
+static int validate_expression(sepol_handle_t *handle, constraint_expr_t *e, int validatetrans, validate_t flavors[])
 {
-	constraint_expr_t *cexp;
-
-	for (; cons; cons = cons->next) {
-		if (nperms > 0 && cons->permissions == 0)
-			goto bad;
-		if (nperms > 0 && nperms != PERM_SYMTAB_SIZE && cons->permissions >= (UINT32_C(1) << nperms))
-			goto bad;
+	int sp = -1;
 
-		for (cexp = cons->expr; cexp; cexp = cexp->next) {
-			if (cexp->attr & CEXPR_USER) {
-				if (validate_ebitmap(&cexp->names, &flavors[SYM_USERS]))
-					goto bad;
-				if (validate_empty_type_set(cexp->type_names))
-					goto bad;
-			} else if (cexp->attr & CEXPR_ROLE) {
-				if (validate_ebitmap(&cexp->names, &flavors[SYM_ROLES]))
-					goto bad;
-				if (validate_empty_type_set(cexp->type_names))
-					goto bad;
-			} else if (cexp->attr & CEXPR_TYPE) {
-				if (validate_ebitmap(&cexp->names, &flavors[SYM_TYPES]))
-					goto bad;
-				if (validate_type_set(cexp->type_names, &flavors[SYM_TYPES]))
-					goto bad;
-			} else {
-				if (!ebitmap_is_empty(&cexp->names))
-					goto bad;
-				if (validate_empty_type_set(cexp->type_names))
-					goto bad;
-			}
+	for (; e; e = e->next) {
+		/* validate symbols (implied in kernel source) */
+		if (e->attr & CEXPR_USER) {
+			if (validate_ebitmap(&e->names, &flavors[SYM_USERS]))
+				goto bad;
+			if (validate_empty_type_set(e->type_names))
+				goto bad;
+		} else if (e->attr & CEXPR_ROLE) {
+			if (validate_ebitmap(&e->names, &flavors[SYM_ROLES]))
+				goto bad;
+			if (validate_empty_type_set(e->type_names))
+				goto bad;
+		} else if (e->attr & CEXPR_TYPE) {
+			if (validate_ebitmap(&e->names, &flavors[SYM_TYPES]))
+				goto bad;
+			if (validate_type_set(e->type_names, &flavors[SYM_TYPES]))
+				goto bad;
+		} else {
+			if (!ebitmap_is_empty(&e->names))
+				goto bad;
+			if (validate_empty_type_set(e->type_names))
+				goto bad;
+		}
 
-			if (cexp->expr_type == CEXPR_ATTR || cexp->expr_type == CEXPR_NAMES) {
-				switch (cexp->op) {
-				case CEXPR_EQ:
-				case CEXPR_NEQ:
+		switch (e->expr_type) {
+		case CEXPR_NOT:
+			if(sp < 0)
+				goto bad;
+			break;
+		case CEXPR_AND:
+			if(sp < 0)
+				goto bad;
+			sp--;
+			break;
+		case CEXPR_OR:
+			if(sp < 0)
+				goto bad;
+			sp--;
+			break;
+		case CEXPR_ATTR:
+			if (sp == (CEXPR_MAXDEPTH - 1))
+				return 0;
+			switch (e->attr) {
+			case CEXPR_USER:
+				break;
+			case CEXPR_TYPE:
+				break;
+			case CEXPR_ROLE:
+				switch (e->op) {
 				case CEXPR_DOM:
+					++sp;
+					continue;
 				case CEXPR_DOMBY:
+					++sp;
+					continue;
 				case CEXPR_INCOMP:
-					break;
+					++sp;
+					continue;
 				default:
-					goto bad;
-				}
-
-				switch (cexp->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:
-				case CEXPR_L1L2:
-				case CEXPR_L1H2:
-				case CEXPR_H1L2:
-				case CEXPR_H1H2:
-				case CEXPR_L1H1:
-				case CEXPR_L2H2:
 					break;
-				default:
-					goto bad;
 				}
-			} else {
-				switch (cexp->expr_type) {
-				case CEXPR_NOT:
-				case CEXPR_AND:
-				case CEXPR_OR:
-					break;
+				break;
+			case CEXPR_L1L2:
+				goto mls_ops;
+			case CEXPR_L1H2:
+				goto mls_ops;
+			case CEXPR_H1L2:
+				goto mls_ops;
+			case CEXPR_H1H2:
+				goto mls_ops;
+			case CEXPR_L1H1:
+				goto mls_ops;
+			case CEXPR_L2H2:
+				goto mls_ops;
+mls_ops:
+				switch (e->op) {
+				case CEXPR_EQ:
+					++sp;
+					continue;
+				case CEXPR_NEQ:
+					++sp;
+					continue;
+				case CEXPR_DOM:
+					++sp;
+					continue;
+				case CEXPR_DOMBY:
+					++sp;
+					continue;
+				case CEXPR_INCOMP:
+					++sp;
+					continue;
 				default:
 					goto bad;
 				}
+				break;
+			default:
+				goto bad;
+			}
 
-				if (cexp->op != 0)
+			switch (e->op) {
+			case CEXPR_EQ:
+				++sp;
+				break;
+			case CEXPR_NEQ:
+				++sp;
+				break;
+			default:
+				goto bad;
+			}
+			break;
+		case CEXPR_NAMES:
+			if (sp == (CEXPR_MAXDEPTH-1))
+				return 0;
+			if (e->attr & CEXPR_TARGET)
+				;
+			else if (e->attr & CEXPR_XTARGET) {
+				if (!validatetrans)
 					goto bad;
+			}
+			if (e->attr & CEXPR_USER)
+				;
+			else if (e->attr & CEXPR_ROLE)
+				;
+			else if (e->attr & CEXPR_TYPE)
+				;
+			else
+				goto bad;
 
-				if (cexp->attr != 0)
-					goto bad;
+			switch (e->op) {
+			case CEXPR_EQ:
+				++sp;
+				break;
+			case CEXPR_NEQ:
+				++sp;
+				break;
+			default:
+				goto bad;
 			}
+			break;
+		default:
+			goto bad;
 		}
 	}
 
+	if (sp != 0)
+		goto bad;
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid expression");
+	return -1;
+}
+
+static int validate_constraint_nodes(sepol_handle_t *handle, unsigned int nperms, constraint_node_t *cons, int validatetrans, validate_t flavors[])
+{
+	for (; cons; cons = cons->next) {
+		if (validatetrans && cons->permissions != 0)
+			goto bad;
+		if (!validatetrans && cons->permissions == 0)
+			goto bad;
+		if (!validatetrans && nperms != PERM_SYMTAB_SIZE && cons->permissions >= (UINT32_C(1) << nperms))
+			goto bad;
+
+		if (validate_expression(handle, cons->expr, validatetrans, flavors))
+			goto bad;
+	}
+
 	return 0;
 
 bad:
@@ -320,9 +412,9 @@  static int validate_class_datum(sepol_handle_t *handle, class_datum_t *class, va
 		goto bad;
 	if (class->permissions.nprim > PERM_SYMTAB_SIZE)
 		goto bad;
-	if (validate_constraint_nodes(handle, class->permissions.nprim, class->constraints, flavors))
+	if (validate_constraint_nodes(handle, class->permissions.nprim, class->constraints, 0, flavors))
 		goto bad;
-	if (validate_constraint_nodes(handle, 0, class->validatetrans, flavors))
+	if (validate_constraint_nodes(handle, class->permissions.nprim, class->validatetrans, 1, flavors))
 		goto bad;
 
 	switch (class->default_user) {