diff mbox series

[RFC,4/9] libsepol/cil: Process deny rules

Message ID 20221215213429.998948-5-jwcart2@gmail.com (mailing list archive)
State Superseded
Delegated to: Petr Lautrbach
Headers show
Series Add CIL Deny Rule | expand

Commit Message

James Carter Dec. 15, 2022, 9:34 p.m. UTC
A deny rule is like a neverallow rule, except that permissions are
removed rather than an error reported.

(allow S1 T1 P1)
(deny  S2 T2 P2)

In general what is needed is:
1) Allow permissions that do not match the deny rule
  P3 = P1 and not P2
  (allow S1 T1 P3)
2) Allow source types that do not match the deny rule
  S3 = S1 and not S2
  (allow S3 T1 P1)
3) Allow target types that do not match the deny rule
  T3 = T1 and not T2
  (allow S1 T3 P1)

If a self rule is involved then
  If S1 is an attribute then
    If T1 is self and T2 is not self then
      S3 = S1 and not (S2 and T2)
    else if T1 is not self and T2 is self then
      S3 = S1 and not (T1 and S2)
    else if T1 is self and T2 is self then
      S3 = S1 and not S2
    (allow S3 T1 P1)
  If T1 is an attribute then
    If T2 is self then
      T3 = T1 and not (S1 and S2)
    (allow S1 T3 P1)
    If S1 is attribute and S2 is attribute and T2 is self then
      # In addition to any rules involving S3 and T3 above
      SX = S1 and S2 and T1
      For S in SX do
        T = SX and not S
        (allow S T P1)

In all cases, do not create a rule that has an empty set

Signed-off-by: James Carter <jwcart2@gmail.com>
---
 libsepol/cil/src/cil_deny.c | 957 ++++++++++++++++++++++++++++++++++++
 libsepol/cil/src/cil_deny.h |  34 ++
 libsepol/cil/src/cil_post.c |   7 +
 3 files changed, 998 insertions(+)
 create mode 100644 libsepol/cil/src/cil_deny.c
 create mode 100644 libsepol/cil/src/cil_deny.h

Comments

Daniel Burgener Feb. 3, 2023, 10:54 p.m. UTC | #1
On 12/15/2022 4:34 PM, James Carter wrote:
> A deny rule is like a neverallow rule, except that permissions are
> removed rather than an error reported.
> 
> (allow S1 T1 P1)
> (deny  S2 T2 P2)
> 
> In general what is needed is:
> 1) Allow permissions that do not match the deny rule
>    P3 = P1 and not P2
>    (allow S1 T1 P3)
> 2) Allow source types that do not match the deny rule
>    S3 = S1 and not S2
>    (allow S3 T1 P1)
> 3) Allow target types that do not match the deny rule
>    T3 = T1 and not T2
>    (allow S1 T3 P1)
> 
> If a self rule is involved then
>    If S1 is an attribute then
>      If T1 is self and T2 is not self then
>        S3 = S1 and not (S2 and T2)
>      else if T1 is not self and T2 is self then
>        S3 = S1 and not (T1 and S2)
>      else if T1 is self and T2 is self then
>        S3 = S1 and not S2
>      (allow S3 T1 P1)
>    If T1 is an attribute then
>      If T2 is self then
>        T3 = T1 and not (S1 and S2)
>      (allow S1 T3 P1)
>      If S1 is attribute and S2 is attribute and T2 is self then
>        # In addition to any rules involving S3 and T3 above
>        SX = S1 and S2 and T1
>        For S in SX do
>          T = SX and not S
>          (allow S T P1)
> 
> In all cases, do not create a rule that has an empty set
> 
> Signed-off-by: James Carter <jwcart2@gmail.com>
> ---
>   libsepol/cil/src/cil_deny.c | 957 ++++++++++++++++++++++++++++++++++++
>   libsepol/cil/src/cil_deny.h |  34 ++
>   libsepol/cil/src/cil_post.c |   7 +
>   3 files changed, 998 insertions(+)
>   create mode 100644 libsepol/cil/src/cil_deny.c
>   create mode 100644 libsepol/cil/src/cil_deny.h
> 
> diff --git a/libsepol/cil/src/cil_deny.c b/libsepol/cil/src/cil_deny.c
> new file mode 100644
> index 00000000..43043c37
> --- /dev/null
> +++ b/libsepol/cil/src/cil_deny.c
> @@ -0,0 +1,957 @@
> +/*
> + * This file is public domain software, i.e. not copyrighted.
> + *
> + * Warranty Exclusion
> + * ------------------
> + * You agree that this software is a non-commercially developed program
> + * that may contain "bugs" (as that term is used in the industry) and
> + * that it may not function as intended. The software is licensed
> + * "as is". NSA makes no, and hereby expressly disclaims all, warranties,
> + * express, implied, statutory, or otherwise with respect to the software,
> + * including noninfringement and the implied warranties of merchantability
> + * and fitness for a particular purpose.
> + *
> + * Limitation of Liability
> + *-----------------------
> + * In no event will NSA be liable for any damages, including loss of data,
> + * lost profits, cost of cover, or other special, incidental, consequential,
> + * direct or indirect damages arising from the software or the use thereof,
> + * however caused and on any theory of liability. This limitation will apply
> + * even if NSA has been advised of the possibility of such damage. You
> + * acknowledge that this is a reasonable allocation of risk.
> + *
> + * Original author: James Carter
> + */
> +
> +#include <sepol/policydb/ebitmap.h>
> +
> +#include "cil_internal.h"
> +#include "cil_find.h"
> +#include "cil_flavor.h"
> +#include "cil_list.h"
> +#include "cil_strpool.h"
> +#include "cil_log.h"
> +#include "cil_symtab.h"
> +#include "cil_build_ast.h"
> +#include "cil_copy_ast.h"
> +#include "cil_deny.h"
> +
> +#define CIL_DENY_ATTR_PREFIX "deny_rule_attr"
> +
> +/*
> + * A deny rule will remove the specified permissions from the CIL AST.
> + *
> + * (allow S1 T1 P1)
> + * (deny  S2 T2 P2)
> + *
> + * In general what is needed is:
> + * 1) Allow permissions that do not match the deny rule
> + *   P3 = P1 and not P2
> + *   (allow S1 T1 P3)
> + * 2) Allow source types that do not match the deny rule
> + *   S3 = S1 and not S2
> + *   (allow S3 T1 P1)
> + * 3) Allow target types that do not match the deny rule
> + *   T3 = T1 and not T2
> + *   (allow S1 T3 P1)
> + *
> + * If a self rule is involved then
> + *   If S1 is an attribute then
> + *     If T1 is self and T2 is not self then
> + *       S3 = S1 and not (S2 and T2)
> + *     else if T1 is not self and T2 is self then
> + *       S3 = S1 and not (T1 and S2)
> + *     else if T1 is self and T2 is self then
> + *       S3 = S1 and not S2
> + *     (allow S3 T1 P1)
> + *   If T1 is an attribute then
> + *     If T2 is self then
> + *       T3 = T1 and not (S1 and S2)
> + *     (allow S1 T3 P1)
> + *     If S1 is attribute and S2 is attribute and T2 is self then

nit: The rest of the lines refer to "an" attribute, and this one drops 
the article.

BTW, thanks for this description of the cases.  It's very helpful 
working through the logic.

> + *       # In addition to any rules involving S3 and T3 above
> + *       SX = S1 and S2 and T1
> + *       For S in SX do
> + *         T = SX and not S
> + *         (allow S T P1)
> + *
> + * Do not create a rule that has an empty set
> + */
> +
> +static int cil_perm_match(const struct cil_perm *p1, const struct cil_list *pl2)
> +{
> +	struct cil_list_item *curr;
> +
> +	cil_list_for_each(curr, pl2) {
> +		struct cil_perm *p = curr->data;
> +		if (p == p1) {
> +			return CIL_TRUE;
> +		}

Why are we comparing the pointers rather than the value fields?

> +	}
> +	return CIL_FALSE;
> +}
> +
> +static int cil_class_perm_match(const struct cil_class *c1, const struct cil_perm *p1, const struct cil_list *cpl2)
> +{
> +	struct cil_list_item *curr;
> +
> +	cil_list_for_each(curr, cpl2) {
> +		if (curr->flavor == CIL_CLASSPERMS) {
> +			struct cil_classperms *cp = curr->data;
> +			if (FLAVOR(cp->class) == CIL_CLASS) {
> +				if (cp->class == c1) {
> +					if (cil_perm_match(p1, cp->perms)) {
> +						return CIL_TRUE;
> +					}
> +				}
> +			} else { /* MAP */
> +				struct cil_list_item *p;
> +				cil_list_for_each(p, cp->perms) {
> +					struct cil_perm *cmp = p->data;
> +					if (cil_class_perm_match(c1, p1, cmp->classperms)) {
> +						return CIL_TRUE;
> +					}
> +				}
> +			}
> +		} else { /* SET */
> +			struct cil_classperms_set *cp_set = curr->data;
> +			struct cil_classpermission *cp = cp_set->set;
> +			if (cil_class_perm_match(c1, p1, cp->classperms)) {
> +				return CIL_TRUE;
> +			}
> +		}
> +	}
> +	return CIL_FALSE;
> +}
> +
> +static int cil_classperms_match(const struct cil_classperms *cp1, const struct cil_list *cpl2)
> +{
> +	struct cil_list_item *curr;
> +
> +	cil_list_for_each(curr, cp1->perms) {
> +		struct cil_perm *perm = curr->data;
> +		if (cil_class_perm_match(cp1->class, perm, cpl2)) {
> +			return CIL_TRUE;
> +		}
> +	}
> +	return CIL_FALSE;
> +}
> +
> +int cil_classperms_list_match(const struct cil_list *cpl1, const struct cil_list *cpl2)
> +{
> +	struct cil_list_item *curr;
> +
> +	if (!cpl1 || !cpl2) {
> +		return (!cpl1 && !cpl2) ? CIL_TRUE : CIL_FALSE;
> +	}
> +
> +	cil_list_for_each(curr, cpl1) {
> +		if (curr->flavor == CIL_CLASSPERMS) {
> +			struct cil_classperms *cp = curr->data;
> +			if (FLAVOR(cp->class) == CIL_CLASS) {
> +				if (cil_classperms_match(cp, cpl2)) {
> +					return CIL_TRUE;
> +				}
> +			} else { /* MAP */
> +				struct cil_list_item *p;
> +				cil_list_for_each(p, cp->perms) {
> +					struct cil_perm *cmp = p->data;
> +					if (cil_classperms_list_match(cmp->classperms, cpl2)) {
> +						return CIL_TRUE;
> +					}
> +				}
> +			}
> +		} else { /* SET */
> +			struct cil_classperms_set *cp_set = curr->data;
> +			struct cil_classpermission *cp = cp_set->set;
> +			if (cil_classperms_list_match(cp->classperms, cpl2)) {
> +				return CIL_TRUE;
> +			}
> +		}
> +	}
> +	return CIL_FALSE;
> +}
> +
> +static void cil_classperms_copy(struct cil_classperms **new, const struct cil_classperms *old)
> +{
> +	cil_classperms_init(new);
> +	(*new)->class_str = old->class_str;
> +	(*new)->class = old->class;
> +	cil_copy_list(old->perm_strs, &(*new)->perm_strs);
> +	cil_copy_list(old->perms, &(*new)->perms);
> +}
> +
> +static void cil_classperms_set_copy(struct cil_classperms_set **new, const struct cil_classperms_set *old)
> +{
> +	cil_classperms_set_init(new);
> +	(*new)->set_str = old->set_str;
> +	(*new)->set = old->set;
> +}
> +
> +void cil_classperms_list_copy(struct cil_list **new, const struct cil_list *old)
> +{
> +	struct cil_list_item *curr;
> +
> +	if (!new) {
> +		return;
> +	}
> +
> +	if (!old) {
> +		*new = NULL;
> +		return;
> +	}
> +
> +	cil_list_init(new, CIL_LIST);
> +
> +	cil_list_for_each(curr, old) {
> +		if (curr->flavor == CIL_CLASSPERMS) {
> +			struct cil_classperms *new_cp;
> +			cil_classperms_copy(&new_cp, curr->data);
> +			cil_list_append(*new, CIL_CLASSPERMS, new_cp);
> +		} else { /* SET */
> +			struct cil_classperms_set *new_cps;
> +			cil_classperms_set_copy(&new_cps, curr->data);
> +			cil_list_append(*new, CIL_CLASSPERMS_SET, new_cps);
> +		}
> +	}
> +
> +	if (cil_list_is_empty(*new)) {
> +		cil_list_destroy(new, CIL_FALSE);
> +	}
> +}
> +
> +/* Append cp1 and not cpl2 to result */
> +static void cil_classperms_andnot(struct cil_list **result, const struct cil_classperms *cp1, const struct cil_list *cpl2)
> +{
> +	struct cil_classperms *new_cp = NULL;
> +	struct cil_list_item *curr;
> +
> +	if (!cil_classperms_match(cp1, cpl2)) {
> +		cil_classperms_copy(&new_cp, cp1);
> +		cil_list_append(*result, CIL_CLASSPERMS, new_cp);
> +		return;
> +	}
> +
> +	cil_list_for_each(curr, cp1->perms) {
> +		struct cil_perm *perm = curr->data;
> +		if (!cil_class_perm_match(cp1->class, perm, cpl2)) {
> +			if (new_cp == NULL) {
> +				cil_classperms_init(&new_cp);
> +				new_cp->class_str = cp1->class_str;
> +				new_cp->class = cp1->class;
> +				cil_list_init(&new_cp->perm_strs, CIL_PERM);
> +				cil_list_init(&new_cp->perms, CIL_PERM);
> +				cil_list_append(*result, CIL_CLASSPERMS, new_cp) > +			}
> +			cil_list_append(new_cp->perm_strs, CIL_STRING, perm->datum.fqn);
> +			cil_list_append(new_cp->perms, CIL_DATUM, perm);

Why can't you just use cil_classperms_copy() here?

> +		}
> +	}
> +}
> +
> +/* Append cp1 and not cpl2 to result */
> +static void cil_classperms_map_andnot(struct cil_list **result, const struct cil_classperms *cp1, const struct cil_list *cpl2)
> +{
> +	struct cil_classperms *new_cp = NULL;
> +	struct cil_list_item *p;
> +
> +	cil_list_for_each(p, cp1->perms) {
> +		struct cil_perm *map_perm = p->data;
> +		if (!cil_classperms_list_match(map_perm->classperms, cpl2)) {
> +			if (new_cp == NULL) {
> +				cil_classperms_init(&new_cp);
> +				new_cp->class_str = cp1->class_str;
> +				new_cp->class = cp1->class;
> +				cil_list_init(&new_cp->perm_strs, CIL_PERM);
> +				cil_list_init(&new_cp->perms, CIL_PERM);
> +				cil_list_append(*result, CIL_CLASSPERMS, new_cp);
> +			}
> +			cil_list_append(new_cp->perm_strs, CIL_STRING, map_perm->datum.fqn);
> +			cil_list_append(new_cp->perms, CIL_DATUM, map_perm);

Same question about cil_classperms_copy().

> +		} else {
> +			struct cil_list *new_cpl = NULL;
> +			cil_classperms_list_andnot(&new_cpl, map_perm->classperms, cpl2);
> +			if (new_cpl) {
> +				struct cil_list_item *i;
> +				cil_list_for_each(i, new_cpl) {
> +					cil_list_append(*result, i->flavor, i->data);
> +				}
> +				cil_list_destroy(&new_cpl, CIL_FALSE);
> +			}
> +		}
> +	}
> +}
> +
> +/* Append cps1 and not cpl2 to result */
> +static void cil_classperms_set_andnot(struct cil_list **result, const struct cil_classperms_set *cps1, const struct cil_list *cpl2)
> +{
> +	struct cil_classpermission *cp = cps1->set;
> +
> +	if (!cil_classperms_list_match(cp->classperms, cpl2)) {
> +		struct cil_classperms_set *new_cps;
> +		cil_classperms_set_copy(&new_cps, cps1);
> +		cil_list_append(*result, CIL_CLASSPERMS_SET, new_cps);
> +	} else {
> +		struct cil_list *new_cpl;
> +		cil_classperms_list_andnot(&new_cpl, cp->classperms, cpl2);
> +		if (new_cpl) {
> +			struct cil_list_item *i;
> +			cil_list_for_each(i, new_cpl) {
> +				cil_list_append(*result, i->flavor, i->data);
> +			}
> +			cil_list_destroy(&new_cpl, CIL_FALSE);
> +		}
> +	}
> +}
> +
> +/* result = cpl1 and not cpl2 */
> +void cil_classperms_list_andnot(struct cil_list **result, const struct cil_list *cpl1, const struct cil_list *cpl2)
> +{
> +	struct cil_list_item *curr;
> +
> +	if (!result) {
> +		return;
> +	}
> +
> +	if (!cpl1) {
> +		*result = NULL;
> +		return;
> +	}
> +
> +	cil_list_init(result, CIL_LIST);
> +
> +	if (!cpl2 || !cil_classperms_list_match(cpl1, cpl2)) {
> +		cil_classperms_list_copy(result, cpl1);
> +		return;
> +	}
> +
> +	cil_list_for_each(curr, cpl1) {
> +		if (curr->flavor == CIL_CLASSPERMS) {
> +			struct cil_classperms *cp = curr->data;
> +			if (FLAVOR(cp->class) == CIL_CLASS) {
> +				cil_classperms_andnot(result, cp, cpl2);
> +			} else { /* MAP */
> +				cil_classperms_map_andnot(result, cp, cpl2);
> +			}
> +		} else { /* SET */
> +			struct cil_classperms_set *cps = curr->data;
> +			cil_classperms_set_andnot(result, cps, cpl2);
> +		}
> +	}
> +
> +	if (cil_list_is_empty(*result)) {
> +		cil_list_destroy(result, CIL_FALSE);
> +	}
> +}
> +
> +/* result = d1 and d2 */
> +static int cil_datums_and(ebitmap_t *result, const struct cil_symtab_datum *d1, const struct cil_symtab_datum *d2)
> +{
> +	int rc = SEPOL_OK;
> +	enum cil_flavor f1 = FLAVOR(d1);
> +	enum cil_flavor f2 = FLAVOR(d2);
> +
> +	if (f1 != CIL_TYPEATTRIBUTE && f2 != CIL_TYPEATTRIBUTE) {
> +		struct cil_type *t1 = (struct cil_type *)d1;
> +		struct cil_type *t2 = (struct cil_type *)d2;
> +		ebitmap_init(result);
> +		if (t1->value == t2->value) {
> +			rc = ebitmap_set_bit(result, t1->value, 1);
> +			if (rc != SEPOL_OK) {
> +				ebitmap_destroy(result);
> +				goto exit;
> +			}
> +		}
> +	} else if (f1 == CIL_TYPEATTRIBUTE && f2 != CIL_TYPEATTRIBUTE) {
> +		struct cil_typeattribute *a1 = (struct cil_typeattribute *)d1;
> +		struct cil_type *t2 = (struct cil_type *)d2;
> +		ebitmap_init(result);
> +		if (ebitmap_get_bit(a1->types, t2->value)) {
> +			rc = ebitmap_set_bit(result, t2->value, 1);
> +			if (rc != SEPOL_OK) {
> +				ebitmap_destroy(result);
> +				goto exit;
> +			}
> +		}
> +	} else if (f1 != CIL_TYPEATTRIBUTE && f2 == CIL_TYPEATTRIBUTE) {
> +		struct cil_type *t1 = (struct cil_type *)d1;
> +		struct cil_typeattribute *a2 = (struct cil_typeattribute *)d2;
> +		ebitmap_init(result);
> +		if (ebitmap_get_bit(a2->types, t1->value)) {
> +			rc = ebitmap_set_bit(result, t1->value, 1);
> +			if (rc != SEPOL_OK) {
> +				ebitmap_destroy(result);
> +				goto exit;
> +			}
> +		}
> +	} else {
> +		/* Both are attributes */
> +		struct cil_typeattribute *a1 = (struct cil_typeattribute *)d1;
> +		struct cil_typeattribute *a2 = (struct cil_typeattribute *)d2;
> +		rc = ebitmap_and(result, a1->types, a2->types);
> +		if (rc != SEPOL_OK) {
> +			ebitmap_destroy(result);
> +			goto exit;
> +		}
> +	}
> +exit:
> +	return rc;
> +}
> +
> +/* result = d1 and not d2 */
> +static int cil_datums_andnot(ebitmap_t *result, const struct cil_symtab_datum *d1, const struct cil_symtab_datum *d2)
> +{
> +	int rc = SEPOL_OK;
> +	enum cil_flavor f1 = FLAVOR(d1);
> +	enum cil_flavor f2 = FLAVOR(d2);
> +
> +	if (f1 != CIL_TYPEATTRIBUTE && f2 != CIL_TYPEATTRIBUTE) {
> +		struct cil_type *t1 = (struct cil_type *)d1;
> +		struct cil_type *t2 = (struct cil_type *)d2;
> +		ebitmap_init(result);
> +		if (t1->value != t2->value) {
> +			rc = ebitmap_set_bit(result, t1->value, 1);
> +			if (rc != SEPOL_OK) {
> +				ebitmap_destroy(result);
> +				goto exit;
> +			}
> +		}
> +	} else if (f1 == CIL_TYPEATTRIBUTE && f2 != CIL_TYPEATTRIBUTE) {
> +		struct cil_typeattribute *a1 = (struct cil_typeattribute *)d1;
> +		struct cil_type *t2 = (struct cil_type *)d2;
> +		rc = ebitmap_cpy(result, a1->types);
> +		if (rc != SEPOL_OK) {
> +			goto exit;
> +		}
> +		rc = ebitmap_set_bit(result, t2->value, 0);
> +		if (rc != SEPOL_OK) {
> +			ebitmap_destroy(result);
> +			goto exit;
> +		}
> +	} else if (f1 != CIL_TYPEATTRIBUTE && f2 == CIL_TYPEATTRIBUTE) {
> +		struct cil_type *t1 = (struct cil_type *)d1;
> +		struct cil_typeattribute *a2 = (struct cil_typeattribute *)d2;
> +		ebitmap_init(result);
> +		if (!ebitmap_get_bit(a2->types, t1->value)) {
> +			rc = ebitmap_set_bit(result, t1->value, 1);
> +			if (rc != SEPOL_OK) {
> +				ebitmap_destroy(result);
> +				goto exit;
> +			}
> +		}
> +	} else {
> +		/* Both are attributes */
> +		struct cil_typeattribute *a1 = (struct cil_typeattribute *)d1;
> +		struct cil_typeattribute *a2 = (struct cil_typeattribute *)d2;
> +		rc = ebitmap_andnot(result, a1->types, a2->types, a1->types->highbit);
> +		if (rc != SEPOL_OK) {
> +			ebitmap_destroy(result);
> +			goto exit;
> +		}
> +	}
> +exit:
> +	return rc;
> +}
> +
> +static size_t num_digits(unsigned n)
> +{
> +	size_t num = 1;
> +	while (n >= 10) {
> +		n /= 10;
> +		num++;
> +	}
> +	return num;
> +}
> +
> +static char *cil_create_new_attribute_name(unsigned num)
> +{
> +	char *s1 = NULL;
> +	char *s2 = NULL;
> +	size_t len_num = num_digits(num);
> +	size_t len = strlen(CIL_DENY_ATTR_PREFIX) + 1 + len_num + 1;
> +	int rc;
> +
> +	if (len >= CIL_MAX_NAME_LENGTH) {
> +		cil_log(CIL_ERR, "Name length greater than max name length of %d",
> +				CIL_MAX_NAME_LENGTH);
> +		goto exit;
> +	}
> +
> +	s1 = cil_malloc(len);
> +	rc = snprintf(s1, len, "%s_%u", CIL_DENY_ATTR_PREFIX, num);
> +	if (rc < 0 || (size_t)rc >= len) {
> +		cil_log(CIL_ERR, "Error creating new attribute name");
> +		free(s1);
> +		goto exit;
> +	}
> +
> +	s2 = cil_strpool_add(s1);
> +	free(s1);
> +
> +exit:
> +	return s2;
> +}
> +
> +static struct cil_list *cil_create_and_expr_list(enum cil_flavor flavor, void *v1, void *v2)
> +{
> +	struct cil_list *expr;
> +
> +	cil_list_init(&expr, CIL_TYPE);
> +	cil_list_append(expr, CIL_OP, (void *)CIL_AND);
> +	cil_list_append(expr, flavor, v1);
> +	cil_list_append(expr, flavor, v2);
> +
> +	return expr;
> +}
> +
> +static struct cil_list *cil_create_andnot_expr_list(enum cil_flavor flavor, void *v1, void *v2)
> +{
> +	struct cil_list *expr, *sub_expr;
> +
> +	cil_list_init(&expr, CIL_TYPE);
> +	cil_list_append(expr, CIL_OP, (void *)CIL_AND);
> +	cil_list_append(expr, flavor, v1);
> +	cil_list_init(&sub_expr, CIL_TYPE);
> +	cil_list_append(sub_expr, CIL_OP, (void *)CIL_NOT);
> +	cil_list_append(sub_expr, flavor, v2);
> +	cil_list_append(expr, CIL_LIST, sub_expr);
> +
> +	return expr;
> +}
> +
> +static struct cil_tree_node *cil_create_and_insert_node(struct cil_tree_node *prev, enum cil_flavor flavor, void *data)
> +{
> +	struct cil_tree_node *new;
> +
> +	cil_tree_node_init(&new);
> +	new->parent = prev->parent;
> +	new->line = prev->line;
> +	new->hll_offset = prev->hll_offset;
> +	new->flavor = flavor;
> +	new->data = data;
> +	new->next = prev->next;
> +	prev->next = new;
> +
> +	return new;
> +}
> +
> +static int cil_create_and_insert_attribute_and_set(struct cil_db *db, struct cil_tree_node *prev, struct cil_list *str_expr, struct cil_list *datum_expr, ebitmap_t *types, struct cil_symtab_datum **d)
> +{
> +	struct cil_tree_node *attr_node = NULL;
> +	char *name;
> +	struct cil_typeattribute *attr = NULL;
> +	struct cil_tree_node *attrset_node = NULL;
> +	struct cil_typeattributeset *attrset = NULL;
> +	symtab_t *symtab = NULL;
> +	int rc = SEPOL_ERR;
> +
> +	name = cil_create_new_attribute_name(db->num_types_and_attrs);
> +	if (!name) {
> +		goto exit;
> +	}
> +
> +	cil_typeattributeset_init(&attrset);
> +	attrset->attr_str = name;
> +	attrset->str_expr = str_expr;
> +	attrset->datum_expr = datum_expr;
> +
> +	cil_typeattribute_init(&attr);
> +	cil_list_init(&attr->expr_list, CIL_TYPE);
> +	cil_list_append(attr->expr_list, CIL_LIST, datum_expr);
> +	attr->types = types;
> +	attr->used = CIL_ATTR_AVRULE;
> +	attr->keep = (ebitmap_cardinality(types) < db->attrs_expand_size) ? CIL_FALSE : CIL_TRUE;
> +
> +	attr_node = cil_create_and_insert_node(prev, CIL_TYPEATTRIBUTE, attr);
> +	attrset_node = cil_create_and_insert_node(attr_node, CIL_TYPEATTRIBUTESET, attrset);
> +
> +	rc = cil_get_symtab(prev->parent, &symtab, CIL_SYM_TYPES);
> +	if (rc != SEPOL_OK) {
> +		goto exit;
> +	}
> +
> +	rc = cil_symtab_insert(symtab, name, &attr->datum, attr_node);
> +	if (rc != SEPOL_OK) {
> +		goto exit;
> +	}
> +
> +	db->num_types_and_attrs++;
> +
> +	*d = &attr->datum;
> +
> +	return SEPOL_OK;
> +
> +exit:
> +	if (attrset_node) {
> +		prev->next = attrset_node->next;
> +		cil_destroy_typeattribute(attr_node->data);
> +		free(attr_node);
> +		cil_destroy_typeattributeset(attrset_node->data);
> +		free(attrset_node);
> +	}
> +	return rc;
> +}
> +
> +static int cil_create_attribute_d1_and_not_d2(struct cil_db *db, struct cil_tree_node *prev, struct cil_symtab_datum *d1, struct cil_symtab_datum *d2, struct cil_symtab_datum **d3)
> +{
> +	struct cil_list *str_expr;
> +	struct cil_list *datum_expr;
> +	ebitmap_t *types;
> +	int rc;
> +
> +	*d3 = NULL;
> +
> +	if (d1 == d2) {
> +		return SEPOL_OK;
> +	}
> +
> +	str_expr = cil_create_andnot_expr_list(CIL_STRING, d1->fqn, d2->fqn);
> +	datum_expr = cil_create_andnot_expr_list(CIL_DATUM, d1, d2);
> +
> +	types = cil_malloc(sizeof(*types));
> +	rc = cil_datums_andnot(types, d1, d2);
> +	if (rc != SEPOL_OK) {
> +		goto exit;
> +	}
> +	if (ebitmap_is_empty(types)) {
> +		rc = SEPOL_OK;
> +		goto exit;
> +	}
> +
> +	if (ebitmap_cardinality(types) == 1) {
> +		unsigned i = ebitmap_highest_set_bit(types);
> +		*d3 = DATUM(db->val_to_type[i]);
> +		ebitmap_destroy(types);
> +		rc = SEPOL_OK;
> +		goto exit;
> +	}
> +
> +	return cil_create_and_insert_attribute_and_set(db, prev, str_expr, datum_expr, types, d3);
> +
> +exit:
> +	cil_list_destroy(&str_expr, CIL_FALSE);
> +	cil_list_destroy(&datum_expr, CIL_FALSE);
> +	free(types);
> +	return rc;
> +}
> +
> +static int cil_create_attribute_d1_and_d2(struct cil_db *db, struct cil_tree_node *prev, struct cil_symtab_datum *d1, struct cil_symtab_datum *d2, struct cil_symtab_datum **d3)
> +{
> +	struct cil_list *str_expr;
> +	struct cil_list *datum_expr;
> +	ebitmap_t *types;
> +	int rc;
> +
> +	if (d1 == d2) {
> +		*d3 = d1;
> +		return SEPOL_OK;
> +	}
> +
> +	*d3 = NULL;
> +
> +	str_expr = cil_create_and_expr_list(CIL_STRING, d1->fqn, d2->fqn);
> +	datum_expr = cil_create_and_expr_list(CIL_DATUM, d1, d2);
> +
> +	types = cil_malloc(sizeof(*types));
> +	rc = cil_datums_and(types, d1, d2);
> +	if (rc != SEPOL_OK) {
> +		goto exit;
> +	}
> +	if (ebitmap_is_empty(types)) {
> +		rc = SEPOL_OK;
> +		goto exit;
> +	}
> +
> +	if (ebitmap_cardinality(types) == 1) {
> +		unsigned i = ebitmap_highest_set_bit(types);
> +		*d3 = DATUM(db->val_to_type[i]);
> +		ebitmap_destroy(types);
> +		rc = SEPOL_OK;
> +		goto exit;
> +	}
> +
> +	rc = cil_create_and_insert_attribute_and_set(db, prev, str_expr, datum_expr, types, d3);
> +	if (rc != SEPOL_OK) {
> +		goto exit;
> +	}

This and the and_not variant above differ in this error handling.  The 
and_not one trusts cil_create_and_insert_attribute_and_set() to do the 
cleanup, and this one does its own cleanup.

I think they're both potentially wrong, as 
cil_create_and_insert_attribute_and_set() calls 
cil_destroy_typeattributeset() which does the same cleanup only if it 
makes it halfway through the function.  So if 
cil_create_new_attribute_name() fails, then this case works correctly 
and the and_not case doesn't free str_expr and datum_expr.  On the other 
hand, if something later in cil_create_and_insert_attribute_and_set() 
fails, then the and_not case is correct, and this one will double free 
str_expr and datum_expr.

> +
> +	return SEPOL_OK;
> +
> +exit:
> +	cil_list_destroy(&str_expr, CIL_FALSE);
> +	cil_list_destroy(&datum_expr, CIL_FALSE);
> +	free(types);
> +	return rc;
> +}
> +
> +static struct cil_avrule *cil_create_avrule(struct cil_symtab_datum *src, struct cil_symtab_datum *tgt, struct cil_list *classperms)
> +{
> +	struct cil_avrule *new;
> +
> +	cil_avrule_init(&new);
> +	new->is_extended = CIL_FALSE;
> +	new->rule_kind = CIL_AVRULE_ALLOWED;
> +	new->src_str = src->name;
> +	new->src = src;
> +	new->tgt_str = tgt->name;
> +	new->tgt = tgt;
> +	new->perms.classperms = classperms;
> +
> +	return new;
> +}
> +
> +static struct cil_tree_node *cil_create_and_add_avrule(struct cil_tree_node *curr, struct cil_symtab_datum *src, struct cil_symtab_datum *tgt, struct cil_list *classperms)
> +{
> +	struct cil_avrule *new_avrule;
> +
> +	new_avrule = cil_create_avrule(src, tgt, classperms);
> +	return cil_create_and_insert_node(curr, CIL_AVRULE, new_avrule);
> +}
> +
> +static int cil_remove_permissions_from_rule(struct cil_db *db, struct cil_tree_node *allow_node, const struct cil_tree_node *deny_node)
> +{
> +	struct cil_avrule *allow_rule = allow_node->data;
> +	struct cil_deny_rule *deny_rule = deny_node->data;
> +	struct cil_symtab_datum *s1 = allow_rule->src;
> +	struct cil_symtab_datum *t1 = allow_rule->tgt;
> +	struct cil_list *p1 = allow_rule->perms.classperms;
> +	struct cil_symtab_datum *s2 = deny_rule->src;
> +	struct cil_symtab_datum *t2 = deny_rule->tgt;
> +	struct cil_list *p2 = deny_rule->classperms;
> +	struct cil_list *p3 = NULL;
> +	struct cil_tree_node *curr = allow_node;
> +	int rc;
> +
> +	cil_classperms_list_andnot(&p3, p1, p2);
> +
> +	if (!cil_list_is_empty(p3)) {;
> +		curr = cil_create_and_add_avrule(curr, s1, t1, p3);
> +	} else {
> +		cil_destroy_classperms_list(&p3);
> +		p3 = NULL;
> +	}
> +
> +	if (FLAVOR(s1) == CIL_TYPEATTRIBUTE) {
> +		struct cil_symtab_datum *s3 = NULL;
> +		struct cil_symtab_datum *d = NULL > +		struct cil_list *cp = NULL;
> +		if (t1->fqn == CIL_KEY_SELF && t2->fqn != CIL_KEY_SELF) {
> +			/* S3 = S1 and not (S2 and T2) */
> +			rc = cil_create_attribute_d1_and_d2(db, NODE(s2), s2, t2, &d);
> +			if (rc != SEPOL_OK) {
> +				goto exit;
> +			}
> +			if (d) {
> +				rc = cil_create_attribute_d1_and_not_d2(db, NODE(s1), s1, d, &s3);
> +				if (rc != SEPOL_OK) {
> +					goto exit;
> +				}
> +			}
> +		} else if (t1->fqn != CIL_KEY_SELF && t2->fqn == CIL_KEY_SELF) {
> +			/* S3 = S1 and not (T1 and S2) */
> +			rc = cil_create_attribute_d1_and_d2(db, NODE(t1), t1, s2, &d);
> +			if (rc != SEPOL_OK) {
> +				goto exit;
> +			}
> +			if (d) {
> +				rc = cil_create_attribute_d1_and_not_d2(db, NODE(s1), s1, d, &s3);
> +				if (rc != SEPOL_OK) {
> +					goto exit;
> +				}
> +			}
> +		} else {
> +			/* S3 = S1 and not S2 */
> +			rc = cil_create_attribute_d1_and_not_d2(db, NODE(s1), s1, s2, &s3);
> +			if (rc != SEPOL_OK) {
> +				goto exit;
> +			}
> +		}
> +		if (s3) {
> +			cil_classperms_list_copy(&cp, p1);
> +			curr = cil_create_and_add_avrule(curr, s3, t1, cp);
> +		}
> +	}
> +
> +	if (t1->fqn != CIL_KEY_SELF && FLAVOR(t1) == CIL_TYPEATTRIBUTE) {
> +		struct cil_symtab_datum *t3 = NULL;
> +		struct cil_symtab_datum *d = NULL;
> +		struct cil_list *cp = NULL;
> +		if (t2->fqn == CIL_KEY_SELF) {
> +			/* T3 = T1 and not (S1 AND S2) */
> +			rc = cil_create_attribute_d1_and_d2(db, NODE(s1), s1, s2, &d);
> +			if (rc != SEPOL_OK) {
> +				goto exit;
> +			}
> +			if (d) {
> +				rc = cil_create_attribute_d1_and_not_d2(db, NODE(t1), t1, d, &t3);
> +				if (rc != SEPOL_OK) {
> +					goto exit;
> +				}
> +			}
> +		} else {
> +			/* S3 = T1 and not T2 */
> +			rc = cil_create_attribute_d1_and_not_d2(db, NODE(t1), t1, t2, &t3);
> +			if (rc != SEPOL_OK) {
> +				goto exit;
> +			}
> +		}
> +		if (t3) {
> +			cil_classperms_list_copy(&cp, p1);
> +			curr = cil_create_and_add_avrule(curr, s1, t3, cp);
> +		}
> +
> +		if (FLAVOR(s1) == CIL_TYPEATTRIBUTE && FLAVOR(s2) == CIL_TYPEATTRIBUTE &&
> +			t2->fqn == CIL_KEY_SELF) {
> +			/* Really special case */
> +			/* FLAVOR(t1) == CIL_TYPEATTRIBUTE */
> +			/* SX = S1 and S2 and T1 */
> +			struct cil_symtab_datum *sx = NULL;
> +			struct cil_typeattribute *sx_attr = NULL;
> +			
> +			d = NULL;
> +			cp = NULL;
> +
> +			rc = cil_create_attribute_d1_and_d2(db, NODE(s2), s2, t1, &d);
> +			if (rc != SEPOL_OK) {
> +				goto exit;
> +			}
> +			if (d) {
> +				rc = cil_create_attribute_d1_and_d2(db, NODE(s1), s1, d, &sx);
> +				if (rc != SEPOL_OK) {
> +					goto exit;
> +				}
> +				sx_attr = (struct cil_typeattribute *)sx;
> +			}
> +			if (sx && ebitmap_cardinality(sx_attr->types) > 1) {
> +				struct cil_symtab_datum *s = NULL;
> +				struct cil_symtab_datum *t = NULL;
> +				ebitmap_node_t *n;
> +				unsigned i;
> +				ebitmap_for_each_positive_bit(sx_attr->types, n, i) {
> +					s = DATUM(db->val_to_type[i]);
> +					rc = cil_create_attribute_d1_and_not_d2(db, NODE(sx), sx, s, &t);
> +					if (rc != SEPOL_OK) {
> +						goto exit;
> +					}
> +					if (t) {
> +						cil_classperms_list_copy(&cp, p1);
> +						curr = cil_create_and_add_avrule(curr, s, t, cp);
> +					}
> +				}
> +			}
> +		}
> +	}
> +
> +exit:
> +	return rc;
> +}

As I mentioned briefly back in December, it might be in the attributes 
case to only grant p2 rather than p3.  I *think* that's safe, since the 
initial allow rule grants everything p3, so the only things needed to be 
added back in the attributes are the denied permissions (p2).

> +
> +static int cil_find_matching_allow_rules(struct cil_list *matching, struct cil_tree_node *start, struct cil_tree_node *deny_node)
> +{
> +	struct cil_deny_rule *deny_rule = deny_node->data;
> +	struct cil_avrule target;
> +
> +	target.rule_kind = CIL_AVRULE_ALLOWED;
> +	target.is_extended = CIL_FALSE;
> +	target.src = deny_rule->src;
> +	target.tgt = deny_rule->tgt;
> +	target.perms.classperms = deny_rule->classperms;
> +
> +	return cil_find_matching_avrule_in_ast(start, CIL_AVRULE, &target, matching, CIL_FALSE);
> +}
> +
> +static int cil_process_deny_rule(struct cil_db *db, struct cil_tree_node *start, struct cil_tree_node *deny_node)
> +{
> +	struct cil_list *matching;
> +	struct cil_list_item *item;
> +	int rc;
> +
> +	cil_list_init(&matching, CIL_NODE);
> +
> +	rc = cil_find_matching_allow_rules(matching, start, deny_node);
> +	if (rc != SEPOL_OK) {
> +		goto exit;
> +	}
> +
> +	cil_list_for_each(item, matching) {
> +		struct cil_tree_node *allow_node = item->data;
> +		rc = cil_remove_permissions_from_rule(db, allow_node, deny_node);
> +		cil_tree_remove_node(allow_node);
> +		cil_tree_node_destroy(&allow_node);
> +		if (rc != SEPOL_OK) {
> +			goto exit;
> +		}
> +
> +	}
> +
> +exit:
> +	cil_list_destroy(&matching, CIL_FALSE);
> +	return rc;
> +}
> +
> +static int cil_process_deny_rules(struct cil_db *db, struct cil_tree_node *start, struct cil_list *deny_rules)
> +{
> +	struct cil_list_item *item;
> +	int rc;
> +
> +	cil_list_for_each(item, deny_rules) {
> +		struct cil_tree_node *deny_node = item->data;
> +		rc = cil_process_deny_rule(db, start, deny_node);
> +		if (rc != SEPOL_OK) {
> +			goto exit;
> +		}
> +		cil_tree_remove_node(deny_node);
> +		cil_tree_node_destroy(&deny_node);
> +	}
> +
> +exit:
> +	return rc;
> +}
> +
> +static int __cil_find_deny_rules(struct cil_tree_node *node,  uint32_t *finished, void *extra_args)
> +{
> +	struct cil_list *deny_rules = extra_args;
> +
> +	if (node->flavor == CIL_BLOCK) {
> +		struct cil_block *block = node->data;
> +		if (block->is_abstract == CIL_TRUE) {
> +			*finished = CIL_TREE_SKIP_HEAD;
> +		}
> +	} else if (node->flavor == CIL_MACRO) {
> +		*finished = CIL_TREE_SKIP_HEAD;
> +	} else if (node->flavor == CIL_DENY_RULE) {
> +		cil_list_append(deny_rules, CIL_DENY_RULE, node);
> +	}
> +	return SEPOL_OK;
> +}
> +
> +int cil_process_deny_rules_in_ast(struct cil_db *db)
> +{
> +	struct cil_tree_node *start;
> +	struct cil_list *deny_rules;
> +	int rc = SEPOL_ERR;
> +
> +	cil_list_init(&deny_rules, CIL_DENY_RULE);
> +
> +	if (!db) {
> +		cil_log(CIL_ERR, "No CIL db provided to process deny rules\n");
> +		goto exit;
> +	}
> +
> +	start = db->ast->root;
> +	rc = cil_tree_walk(start, __cil_find_deny_rules, NULL, NULL, deny_rules);
> +	if (rc != SEPOL_OK) {
> +		cil_log(CIL_ERR, "An error occurred while getting deny rules\n");
> +		goto exit;
> +	}
> +
> +	rc = cil_process_deny_rules(db, start, deny_rules);
> +	if (rc != SEPOL_OK) {
> +		cil_log(CIL_ERR, "An error occurred while processing deny rules\n");
> +		goto exit;
> +	}
> +
> +exit:
> +	cil_list_destroy(&deny_rules, CIL_FALSE);
> +	return rc;
> +}
> diff --git a/libsepol/cil/src/cil_deny.h b/libsepol/cil/src/cil_deny.h
> new file mode 100644
> index 00000000..4a9e92d1
> --- /dev/null
> +++ b/libsepol/cil/src/cil_deny.h
> @@ -0,0 +1,34 @@
> +/*
> + * This file is public domain software, i.e. not copyrighted.
> + *
> + * Warranty Exclusion
> + * ------------------
> + * You agree that this software is a non-commercially developed program
> + * that may contain "bugs" (as that term is used in the industry) and
> + * that it may not function as intended. The software is licensed
> + * "as is". NSA makes no, and hereby expressly disclaims all, warranties,
> + * express, implied, statutory, or otherwise with respect to the software,
> + * including noninfringement and the implied warranties of merchantability
> + * and fitness for a particular purpose.
> + *
> + * Limitation of Liability
> + *-----------------------
> + * In no event will NSA be liable for any damages, including loss of data,
> + * lost profits, cost of cover, or other special, incidental, consequential,
> + * direct or indirect damages arising from the software or the use thereof,
> + * however caused and on any theory of liability. This limitation will apply
> + * even if NSA has been advised of the possibility of such damage. You
> + * acknowledge that this is a reasonable allocation of risk.
> + *
> + * Original author: James Carter
> + */
> +
> +#ifndef CIL_DENY_H_
> +#define CIL_DENY_H_
> +
> +int cil_classperms_list_match(const struct cil_list *cpl1, const struct cil_list *cpl2);
> +void cil_classperms_list_copy(struct cil_list **new, const struct cil_list *old);
> +void cil_classperms_list_andnot(struct cil_list **result, const struct cil_list *cpl1, const struct cil_list *cpl2);
> +int cil_process_deny_rules_in_ast(struct cil_db *db);
> +
> +#endif /* CIL_DENY_H_ */
> diff --git a/libsepol/cil/src/cil_post.c b/libsepol/cil/src/cil_post.c
> index 11e572e2..fb7eec2d 100644
> --- a/libsepol/cil/src/cil_post.c
> +++ b/libsepol/cil/src/cil_post.c
> @@ -47,6 +47,7 @@
>   #include "cil_policy.h"
>   #include "cil_verify.h"
>   #include "cil_symtab.h"
> +#include "cil_deny.h"
>   
>   #define GEN_REQUIRE_ATTR "cil_gen_require" /* Also in libsepol/src/module_to_cil.c */
>   #define TYPEATTR_INFIX "_typeattr_"        /* Also in libsepol/src/module_to_cil.c */
> @@ -2551,6 +2552,12 @@ int cil_post_process(struct cil_db *db)
>   		goto exit;
>   	}
>   
> +	rc = cil_process_deny_rules_in_ast(db);
> +	if (rc != SEPOL_OK) {
> +		cil_log(CIL_ERR, "Failed to process deny rules\n");
> +		goto exit;
> +	}
> +
>   	rc = cil_post_verify(db);
>   	if (rc != SEPOL_OK) {
>   		cil_log(CIL_ERR, "Failed to verify cil database\n");
James Carter Feb. 8, 2023, 9:57 p.m. UTC | #2
On Fri, Feb 3, 2023 at 5:54 PM Daniel Burgener
<dburgener@linux.microsoft.com> wrote:
>
> On 12/15/2022 4:34 PM, James Carter wrote:
> > A deny rule is like a neverallow rule, except that permissions are
> > removed rather than an error reported.
> >
> > (allow S1 T1 P1)
> > (deny  S2 T2 P2)
> >
> > In general what is needed is:
> > 1) Allow permissions that do not match the deny rule
> >    P3 = P1 and not P2
> >    (allow S1 T1 P3)
> > 2) Allow source types that do not match the deny rule
> >    S3 = S1 and not S2
> >    (allow S3 T1 P1)
> > 3) Allow target types that do not match the deny rule
> >    T3 = T1 and not T2
> >    (allow S1 T3 P1)
> >
> > If a self rule is involved then
> >    If S1 is an attribute then
> >      If T1 is self and T2 is not self then
> >        S3 = S1 and not (S2 and T2)
> >      else if T1 is not self and T2 is self then
> >        S3 = S1 and not (T1 and S2)
> >      else if T1 is self and T2 is self then
> >        S3 = S1 and not S2
> >      (allow S3 T1 P1)
> >    If T1 is an attribute then
> >      If T2 is self then
> >        T3 = T1 and not (S1 and S2)
> >      (allow S1 T3 P1)
> >      If S1 is attribute and S2 is attribute and T2 is self then
> >        # In addition to any rules involving S3 and T3 above
> >        SX = S1 and S2 and T1
> >        For S in SX do
> >          T = SX and not S
> >          (allow S T P1)
> >
> > In all cases, do not create a rule that has an empty set
> >
> > Signed-off-by: James Carter <jwcart2@gmail.com>
> > ---
> >   libsepol/cil/src/cil_deny.c | 957 ++++++++++++++++++++++++++++++++++++
> >   libsepol/cil/src/cil_deny.h |  34 ++
> >   libsepol/cil/src/cil_post.c |   7 +
> >   3 files changed, 998 insertions(+)
> >   create mode 100644 libsepol/cil/src/cil_deny.c
> >   create mode 100644 libsepol/cil/src/cil_deny.h
> >
> > diff --git a/libsepol/cil/src/cil_deny.c b/libsepol/cil/src/cil_deny.c
> > new file mode 100644
> > index 00000000..43043c37
> > --- /dev/null
> > +++ b/libsepol/cil/src/cil_deny.c
> > @@ -0,0 +1,957 @@
> > +/*
> > + * This file is public domain software, i.e. not copyrighted.
> > + *
> > + * Warranty Exclusion
> > + * ------------------
> > + * You agree that this software is a non-commercially developed program
> > + * that may contain "bugs" (as that term is used in the industry) and
> > + * that it may not function as intended. The software is licensed
> > + * "as is". NSA makes no, and hereby expressly disclaims all, warranties,
> > + * express, implied, statutory, or otherwise with respect to the software,
> > + * including noninfringement and the implied warranties of merchantability
> > + * and fitness for a particular purpose.
> > + *
> > + * Limitation of Liability
> > + *-----------------------
> > + * In no event will NSA be liable for any damages, including loss of data,
> > + * lost profits, cost of cover, or other special, incidental, consequential,
> > + * direct or indirect damages arising from the software or the use thereof,
> > + * however caused and on any theory of liability. This limitation will apply
> > + * even if NSA has been advised of the possibility of such damage. You
> > + * acknowledge that this is a reasonable allocation of risk.
> > + *
> > + * Original author: James Carter
> > + */
> > +
> > +#include <sepol/policydb/ebitmap.h>
> > +
> > +#include "cil_internal.h"
> > +#include "cil_find.h"
> > +#include "cil_flavor.h"
> > +#include "cil_list.h"
> > +#include "cil_strpool.h"
> > +#include "cil_log.h"
> > +#include "cil_symtab.h"
> > +#include "cil_build_ast.h"
> > +#include "cil_copy_ast.h"
> > +#include "cil_deny.h"
> > +
> > +#define CIL_DENY_ATTR_PREFIX "deny_rule_attr"
> > +
> > +/*
> > + * A deny rule will remove the specified permissions from the CIL AST.
> > + *
> > + * (allow S1 T1 P1)
> > + * (deny  S2 T2 P2)
> > + *
> > + * In general what is needed is:
> > + * 1) Allow permissions that do not match the deny rule
> > + *   P3 = P1 and not P2
> > + *   (allow S1 T1 P3)
> > + * 2) Allow source types that do not match the deny rule
> > + *   S3 = S1 and not S2
> > + *   (allow S3 T1 P1)
> > + * 3) Allow target types that do not match the deny rule
> > + *   T3 = T1 and not T2
> > + *   (allow S1 T3 P1)
> > + *
> > + * If a self rule is involved then
> > + *   If S1 is an attribute then
> > + *     If T1 is self and T2 is not self then
> > + *       S3 = S1 and not (S2 and T2)
> > + *     else if T1 is not self and T2 is self then
> > + *       S3 = S1 and not (T1 and S2)
> > + *     else if T1 is self and T2 is self then
> > + *       S3 = S1 and not S2
> > + *     (allow S3 T1 P1)
> > + *   If T1 is an attribute then
> > + *     If T2 is self then
> > + *       T3 = T1 and not (S1 and S2)
> > + *     (allow S1 T3 P1)
> > + *     If S1 is attribute and S2 is attribute and T2 is self then
>
> nit: The rest of the lines refer to "an" attribute, and this one drops
> the article.
>
> BTW, thanks for this description of the cases.  It's very helpful
> working through the logic.
>

The logic will be changing. I've been looking into eliminating all the overlap.

> > + *       # In addition to any rules involving S3 and T3 above
> > + *       SX = S1 and S2 and T1
> > + *       For S in SX do
> > + *         T = SX and not S
> > + *         (allow S T P1)
> > + *
> > + * Do not create a rule that has an empty set
> > + */
> > +
> > +static int cil_perm_match(const struct cil_perm *p1, const struct cil_list *pl2)
> > +{
> > +     struct cil_list_item *curr;
> > +
> > +     cil_list_for_each(curr, pl2) {
> > +             struct cil_perm *p = curr->data;
> > +             if (p == p1) {
> > +                     return CIL_TRUE;
> > +             }
>
> Why are we comparing the pointers rather than the value fields?
>

There is only one struct cil_perm for each permission, so there will
never be a case where the pointers are not equal, but it is the same
permission.
Every declared object in CIL has a struct cil_symtab_datum datum as
its first field. The symbol table points to the datum (which is at the
same address as the enclosing struct).
If you are searching for a declared object, then you only need to
check the pointers.

> > +     }
> > +     return CIL_FALSE;
> > +}
> > +
> > +static int cil_class_perm_match(const struct cil_class *c1, const struct cil_perm *p1, const struct cil_list *cpl2)
> > +{
> > +     struct cil_list_item *curr;
> > +
> > +     cil_list_for_each(curr, cpl2) {
> > +             if (curr->flavor == CIL_CLASSPERMS) {
> > +                     struct cil_classperms *cp = curr->data;
> > +                     if (FLAVOR(cp->class) == CIL_CLASS) {
> > +                             if (cp->class == c1) {
> > +                                     if (cil_perm_match(p1, cp->perms)) {
> > +                                             return CIL_TRUE;
> > +                                     }
> > +                             }
> > +                     } else { /* MAP */
> > +                             struct cil_list_item *p;
> > +                             cil_list_for_each(p, cp->perms) {
> > +                                     struct cil_perm *cmp = p->data;
> > +                                     if (cil_class_perm_match(c1, p1, cmp->classperms)) {
> > +                                             return CIL_TRUE;
> > +                                     }
> > +                             }
> > +                     }
> > +             } else { /* SET */
> > +                     struct cil_classperms_set *cp_set = curr->data;
> > +                     struct cil_classpermission *cp = cp_set->set;
> > +                     if (cil_class_perm_match(c1, p1, cp->classperms)) {
> > +                             return CIL_TRUE;
> > +                     }
> > +             }
> > +     }
> > +     return CIL_FALSE;
> > +}
> > +
> > +static int cil_classperms_match(const struct cil_classperms *cp1, const struct cil_list *cpl2)
> > +{
> > +     struct cil_list_item *curr;
> > +
> > +     cil_list_for_each(curr, cp1->perms) {
> > +             struct cil_perm *perm = curr->data;
> > +             if (cil_class_perm_match(cp1->class, perm, cpl2)) {
> > +                     return CIL_TRUE;
> > +             }
> > +     }
> > +     return CIL_FALSE;
> > +}
> > +
> > +int cil_classperms_list_match(const struct cil_list *cpl1, const struct cil_list *cpl2)
> > +{
> > +     struct cil_list_item *curr;
> > +
> > +     if (!cpl1 || !cpl2) {
> > +             return (!cpl1 && !cpl2) ? CIL_TRUE : CIL_FALSE;
> > +     }
> > +
> > +     cil_list_for_each(curr, cpl1) {
> > +             if (curr->flavor == CIL_CLASSPERMS) {
> > +                     struct cil_classperms *cp = curr->data;
> > +                     if (FLAVOR(cp->class) == CIL_CLASS) {
> > +                             if (cil_classperms_match(cp, cpl2)) {
> > +                                     return CIL_TRUE;
> > +                             }
> > +                     } else { /* MAP */
> > +                             struct cil_list_item *p;
> > +                             cil_list_for_each(p, cp->perms) {
> > +                                     struct cil_perm *cmp = p->data;
> > +                                     if (cil_classperms_list_match(cmp->classperms, cpl2)) {
> > +                                             return CIL_TRUE;
> > +                                     }
> > +                             }
> > +                     }
> > +             } else { /* SET */
> > +                     struct cil_classperms_set *cp_set = curr->data;
> > +                     struct cil_classpermission *cp = cp_set->set;
> > +                     if (cil_classperms_list_match(cp->classperms, cpl2)) {
> > +                             return CIL_TRUE;
> > +                     }
> > +             }
> > +     }
> > +     return CIL_FALSE;
> > +}
> > +
> > +static void cil_classperms_copy(struct cil_classperms **new, const struct cil_classperms *old)
> > +{
> > +     cil_classperms_init(new);
> > +     (*new)->class_str = old->class_str;
> > +     (*new)->class = old->class;
> > +     cil_copy_list(old->perm_strs, &(*new)->perm_strs);
> > +     cil_copy_list(old->perms, &(*new)->perms);
> > +}
> > +
> > +static void cil_classperms_set_copy(struct cil_classperms_set **new, const struct cil_classperms_set *old)
> > +{
> > +     cil_classperms_set_init(new);
> > +     (*new)->set_str = old->set_str;
> > +     (*new)->set = old->set;
> > +}
> > +
> > +void cil_classperms_list_copy(struct cil_list **new, const struct cil_list *old)
> > +{
> > +     struct cil_list_item *curr;
> > +
> > +     if (!new) {
> > +             return;
> > +     }
> > +
> > +     if (!old) {
> > +             *new = NULL;
> > +             return;
> > +     }
> > +
> > +     cil_list_init(new, CIL_LIST);
> > +
> > +     cil_list_for_each(curr, old) {
> > +             if (curr->flavor == CIL_CLASSPERMS) {
> > +                     struct cil_classperms *new_cp;
> > +                     cil_classperms_copy(&new_cp, curr->data);
> > +                     cil_list_append(*new, CIL_CLASSPERMS, new_cp);
> > +             } else { /* SET */
> > +                     struct cil_classperms_set *new_cps;
> > +                     cil_classperms_set_copy(&new_cps, curr->data);
> > +                     cil_list_append(*new, CIL_CLASSPERMS_SET, new_cps);
> > +             }
> > +     }
> > +
> > +     if (cil_list_is_empty(*new)) {
> > +             cil_list_destroy(new, CIL_FALSE);
> > +     }
> > +}
> > +
> > +/* Append cp1 and not cpl2 to result */
> > +static void cil_classperms_andnot(struct cil_list **result, const struct cil_classperms *cp1, const struct cil_list *cpl2)
> > +{
> > +     struct cil_classperms *new_cp = NULL;
> > +     struct cil_list_item *curr;
> > +
> > +     if (!cil_classperms_match(cp1, cpl2)) {
> > +             cil_classperms_copy(&new_cp, cp1);
> > +             cil_list_append(*result, CIL_CLASSPERMS, new_cp);
> > +             return;
> > +     }
> > +
> > +     cil_list_for_each(curr, cp1->perms) {
> > +             struct cil_perm *perm = curr->data;
> > +             if (!cil_class_perm_match(cp1->class, perm, cpl2)) {
> > +                     if (new_cp == NULL) {
> > +                             cil_classperms_init(&new_cp);
> > +                             new_cp->class_str = cp1->class_str;
> > +                             new_cp->class = cp1->class;
> > +                             cil_list_init(&new_cp->perm_strs, CIL_PERM);
> > +                             cil_list_init(&new_cp->perms, CIL_PERM);
> > +                             cil_list_append(*result, CIL_CLASSPERMS, new_cp) > +                    }
> > +                     cil_list_append(new_cp->perm_strs, CIL_STRING, perm->datum.fqn);
> > +                     cil_list_append(new_cp->perms, CIL_DATUM, perm);
>
> Why can't you just use cil_classperms_copy() here?
>

Because I am not copying every permission. I don't want to initialize
the new classperms struct if there are no matches, so I wait until the
first match to initialize it. Immediately appending the new struct
might be confusing. As soon as there is a match I know that the new
classperms struct is going to be appended to the results, but
permissions will continue to be added to it even after it has been
appended.


> > +             }
> > +     }
> > +}
> > +
> > +/* Append cp1 and not cpl2 to result */
> > +static void cil_classperms_map_andnot(struct cil_list **result, const struct cil_classperms *cp1, const struct cil_list *cpl2)
> > +{
> > +     struct cil_classperms *new_cp = NULL;
> > +     struct cil_list_item *p;
> > +
> > +     cil_list_for_each(p, cp1->perms) {
> > +             struct cil_perm *map_perm = p->data;
> > +             if (!cil_classperms_list_match(map_perm->classperms, cpl2)) {
> > +                     if (new_cp == NULL) {
> > +                             cil_classperms_init(&new_cp);
> > +                             new_cp->class_str = cp1->class_str;
> > +                             new_cp->class = cp1->class;
> > +                             cil_list_init(&new_cp->perm_strs, CIL_PERM);
> > +                             cil_list_init(&new_cp->perms, CIL_PERM);
> > +                             cil_list_append(*result, CIL_CLASSPERMS, new_cp);
> > +                     }
> > +                     cil_list_append(new_cp->perm_strs, CIL_STRING, map_perm->datum.fqn);
> > +                     cil_list_append(new_cp->perms, CIL_DATUM, map_perm);
>
> Same question about cil_classperms_copy().
>

Same thing here.

> > +             } else {
> > +                     struct cil_list *new_cpl = NULL;
> > +                     cil_classperms_list_andnot(&new_cpl, map_perm->classperms, cpl2);
> > +                     if (new_cpl) {
> > +                             struct cil_list_item *i;
> > +                             cil_list_for_each(i, new_cpl) {
> > +                                     cil_list_append(*result, i->flavor, i->data);
> > +                             }
> > +                             cil_list_destroy(&new_cpl, CIL_FALSE);
> > +                     }
> > +             }
> > +     }
> > +}
> > +
> > +/* Append cps1 and not cpl2 to result */
> > +static void cil_classperms_set_andnot(struct cil_list **result, const struct cil_classperms_set *cps1, const struct cil_list *cpl2)
> > +{
> > +     struct cil_classpermission *cp = cps1->set;
> > +
> > +     if (!cil_classperms_list_match(cp->classperms, cpl2)) {
> > +             struct cil_classperms_set *new_cps;
> > +             cil_classperms_set_copy(&new_cps, cps1);
> > +             cil_list_append(*result, CIL_CLASSPERMS_SET, new_cps);
> > +     } else {
> > +             struct cil_list *new_cpl;
> > +             cil_classperms_list_andnot(&new_cpl, cp->classperms, cpl2);
> > +             if (new_cpl) {
> > +                     struct cil_list_item *i;
> > +                     cil_list_for_each(i, new_cpl) {
> > +                             cil_list_append(*result, i->flavor, i->data);
> > +                     }
> > +                     cil_list_destroy(&new_cpl, CIL_FALSE);
> > +             }
> > +     }
> > +}
> > +
> > +/* result = cpl1 and not cpl2 */
> > +void cil_classperms_list_andnot(struct cil_list **result, const struct cil_list *cpl1, const struct cil_list *cpl2)
> > +{
> > +     struct cil_list_item *curr;
> > +
> > +     if (!result) {
> > +             return;
> > +     }
> > +
> > +     if (!cpl1) {
> > +             *result = NULL;
> > +             return;
> > +     }
> > +
> > +     cil_list_init(result, CIL_LIST);
> > +
> > +     if (!cpl2 || !cil_classperms_list_match(cpl1, cpl2)) {
> > +             cil_classperms_list_copy(result, cpl1);
> > +             return;
> > +     }
> > +
> > +     cil_list_for_each(curr, cpl1) {
> > +             if (curr->flavor == CIL_CLASSPERMS) {
> > +                     struct cil_classperms *cp = curr->data;
> > +                     if (FLAVOR(cp->class) == CIL_CLASS) {
> > +                             cil_classperms_andnot(result, cp, cpl2);
> > +                     } else { /* MAP */
> > +                             cil_classperms_map_andnot(result, cp, cpl2);
> > +                     }
> > +             } else { /* SET */
> > +                     struct cil_classperms_set *cps = curr->data;
> > +                     cil_classperms_set_andnot(result, cps, cpl2);
> > +             }
> > +     }
> > +
> > +     if (cil_list_is_empty(*result)) {
> > +             cil_list_destroy(result, CIL_FALSE);
> > +     }
> > +}
> > +
> > +/* result = d1 and d2 */
> > +static int cil_datums_and(ebitmap_t *result, const struct cil_symtab_datum *d1, const struct cil_symtab_datum *d2)
> > +{
> > +     int rc = SEPOL_OK;
> > +     enum cil_flavor f1 = FLAVOR(d1);
> > +     enum cil_flavor f2 = FLAVOR(d2);
> > +
> > +     if (f1 != CIL_TYPEATTRIBUTE && f2 != CIL_TYPEATTRIBUTE) {
> > +             struct cil_type *t1 = (struct cil_type *)d1;
> > +             struct cil_type *t2 = (struct cil_type *)d2;
> > +             ebitmap_init(result);
> > +             if (t1->value == t2->value) {
> > +                     rc = ebitmap_set_bit(result, t1->value, 1);
> > +                     if (rc != SEPOL_OK) {
> > +                             ebitmap_destroy(result);
> > +                             goto exit;
> > +                     }
> > +             }
> > +     } else if (f1 == CIL_TYPEATTRIBUTE && f2 != CIL_TYPEATTRIBUTE) {
> > +             struct cil_typeattribute *a1 = (struct cil_typeattribute *)d1;
> > +             struct cil_type *t2 = (struct cil_type *)d2;
> > +             ebitmap_init(result);
> > +             if (ebitmap_get_bit(a1->types, t2->value)) {
> > +                     rc = ebitmap_set_bit(result, t2->value, 1);
> > +                     if (rc != SEPOL_OK) {
> > +                             ebitmap_destroy(result);
> > +                             goto exit;
> > +                     }
> > +             }
> > +     } else if (f1 != CIL_TYPEATTRIBUTE && f2 == CIL_TYPEATTRIBUTE) {
> > +             struct cil_type *t1 = (struct cil_type *)d1;
> > +             struct cil_typeattribute *a2 = (struct cil_typeattribute *)d2;
> > +             ebitmap_init(result);
> > +             if (ebitmap_get_bit(a2->types, t1->value)) {
> > +                     rc = ebitmap_set_bit(result, t1->value, 1);
> > +                     if (rc != SEPOL_OK) {
> > +                             ebitmap_destroy(result);
> > +                             goto exit;
> > +                     }
> > +             }
> > +     } else {
> > +             /* Both are attributes */
> > +             struct cil_typeattribute *a1 = (struct cil_typeattribute *)d1;
> > +             struct cil_typeattribute *a2 = (struct cil_typeattribute *)d2;
> > +             rc = ebitmap_and(result, a1->types, a2->types);
> > +             if (rc != SEPOL_OK) {
> > +                     ebitmap_destroy(result);
> > +                     goto exit;
> > +             }
> > +     }
> > +exit:
> > +     return rc;
> > +}
> > +
> > +/* result = d1 and not d2 */
> > +static int cil_datums_andnot(ebitmap_t *result, const struct cil_symtab_datum *d1, const struct cil_symtab_datum *d2)
> > +{
> > +     int rc = SEPOL_OK;
> > +     enum cil_flavor f1 = FLAVOR(d1);
> > +     enum cil_flavor f2 = FLAVOR(d2);
> > +
> > +     if (f1 != CIL_TYPEATTRIBUTE && f2 != CIL_TYPEATTRIBUTE) {
> > +             struct cil_type *t1 = (struct cil_type *)d1;
> > +             struct cil_type *t2 = (struct cil_type *)d2;
> > +             ebitmap_init(result);
> > +             if (t1->value != t2->value) {
> > +                     rc = ebitmap_set_bit(result, t1->value, 1);
> > +                     if (rc != SEPOL_OK) {
> > +                             ebitmap_destroy(result);
> > +                             goto exit;
> > +                     }
> > +             }
> > +     } else if (f1 == CIL_TYPEATTRIBUTE && f2 != CIL_TYPEATTRIBUTE) {
> > +             struct cil_typeattribute *a1 = (struct cil_typeattribute *)d1;
> > +             struct cil_type *t2 = (struct cil_type *)d2;
> > +             rc = ebitmap_cpy(result, a1->types);
> > +             if (rc != SEPOL_OK) {
> > +                     goto exit;
> > +             }
> > +             rc = ebitmap_set_bit(result, t2->value, 0);
> > +             if (rc != SEPOL_OK) {
> > +                     ebitmap_destroy(result);
> > +                     goto exit;
> > +             }
> > +     } else if (f1 != CIL_TYPEATTRIBUTE && f2 == CIL_TYPEATTRIBUTE) {
> > +             struct cil_type *t1 = (struct cil_type *)d1;
> > +             struct cil_typeattribute *a2 = (struct cil_typeattribute *)d2;
> > +             ebitmap_init(result);
> > +             if (!ebitmap_get_bit(a2->types, t1->value)) {
> > +                     rc = ebitmap_set_bit(result, t1->value, 1);
> > +                     if (rc != SEPOL_OK) {
> > +                             ebitmap_destroy(result);
> > +                             goto exit;
> > +                     }
> > +             }
> > +     } else {
> > +             /* Both are attributes */
> > +             struct cil_typeattribute *a1 = (struct cil_typeattribute *)d1;
> > +             struct cil_typeattribute *a2 = (struct cil_typeattribute *)d2;
> > +             rc = ebitmap_andnot(result, a1->types, a2->types, a1->types->highbit);
> > +             if (rc != SEPOL_OK) {
> > +                     ebitmap_destroy(result);
> > +                     goto exit;
> > +             }
> > +     }
> > +exit:
> > +     return rc;
> > +}
> > +
> > +static size_t num_digits(unsigned n)
> > +{
> > +     size_t num = 1;
> > +     while (n >= 10) {
> > +             n /= 10;
> > +             num++;
> > +     }
> > +     return num;
> > +}
> > +
> > +static char *cil_create_new_attribute_name(unsigned num)
> > +{
> > +     char *s1 = NULL;
> > +     char *s2 = NULL;
> > +     size_t len_num = num_digits(num);
> > +     size_t len = strlen(CIL_DENY_ATTR_PREFIX) + 1 + len_num + 1;
> > +     int rc;
> > +
> > +     if (len >= CIL_MAX_NAME_LENGTH) {
> > +             cil_log(CIL_ERR, "Name length greater than max name length of %d",
> > +                             CIL_MAX_NAME_LENGTH);
> > +             goto exit;
> > +     }
> > +
> > +     s1 = cil_malloc(len);
> > +     rc = snprintf(s1, len, "%s_%u", CIL_DENY_ATTR_PREFIX, num);
> > +     if (rc < 0 || (size_t)rc >= len) {
> > +             cil_log(CIL_ERR, "Error creating new attribute name");
> > +             free(s1);
> > +             goto exit;
> > +     }
> > +
> > +     s2 = cil_strpool_add(s1);
> > +     free(s1);
> > +
> > +exit:
> > +     return s2;
> > +}
> > +
> > +static struct cil_list *cil_create_and_expr_list(enum cil_flavor flavor, void *v1, void *v2)
> > +{
> > +     struct cil_list *expr;
> > +
> > +     cil_list_init(&expr, CIL_TYPE);
> > +     cil_list_append(expr, CIL_OP, (void *)CIL_AND);
> > +     cil_list_append(expr, flavor, v1);
> > +     cil_list_append(expr, flavor, v2);
> > +
> > +     return expr;
> > +}
> > +
> > +static struct cil_list *cil_create_andnot_expr_list(enum cil_flavor flavor, void *v1, void *v2)
> > +{
> > +     struct cil_list *expr, *sub_expr;
> > +
> > +     cil_list_init(&expr, CIL_TYPE);
> > +     cil_list_append(expr, CIL_OP, (void *)CIL_AND);
> > +     cil_list_append(expr, flavor, v1);
> > +     cil_list_init(&sub_expr, CIL_TYPE);
> > +     cil_list_append(sub_expr, CIL_OP, (void *)CIL_NOT);
> > +     cil_list_append(sub_expr, flavor, v2);
> > +     cil_list_append(expr, CIL_LIST, sub_expr);
> > +
> > +     return expr;
> > +}
> > +
> > +static struct cil_tree_node *cil_create_and_insert_node(struct cil_tree_node *prev, enum cil_flavor flavor, void *data)
> > +{
> > +     struct cil_tree_node *new;
> > +
> > +     cil_tree_node_init(&new);
> > +     new->parent = prev->parent;
> > +     new->line = prev->line;
> > +     new->hll_offset = prev->hll_offset;
> > +     new->flavor = flavor;
> > +     new->data = data;
> > +     new->next = prev->next;
> > +     prev->next = new;
> > +
> > +     return new;
> > +}
> > +
> > +static int cil_create_and_insert_attribute_and_set(struct cil_db *db, struct cil_tree_node *prev, struct cil_list *str_expr, struct cil_list *datum_expr, ebitmap_t *types, struct cil_symtab_datum **d)
> > +{
> > +     struct cil_tree_node *attr_node = NULL;
> > +     char *name;
> > +     struct cil_typeattribute *attr = NULL;
> > +     struct cil_tree_node *attrset_node = NULL;
> > +     struct cil_typeattributeset *attrset = NULL;
> > +     symtab_t *symtab = NULL;
> > +     int rc = SEPOL_ERR;
> > +
> > +     name = cil_create_new_attribute_name(db->num_types_and_attrs);
> > +     if (!name) {
> > +             goto exit;
> > +     }
> > +
> > +     cil_typeattributeset_init(&attrset);
> > +     attrset->attr_str = name;
> > +     attrset->str_expr = str_expr;
> > +     attrset->datum_expr = datum_expr;
> > +
> > +     cil_typeattribute_init(&attr);
> > +     cil_list_init(&attr->expr_list, CIL_TYPE);
> > +     cil_list_append(attr->expr_list, CIL_LIST, datum_expr);
> > +     attr->types = types;
> > +     attr->used = CIL_ATTR_AVRULE;
> > +     attr->keep = (ebitmap_cardinality(types) < db->attrs_expand_size) ? CIL_FALSE : CIL_TRUE;
> > +
> > +     attr_node = cil_create_and_insert_node(prev, CIL_TYPEATTRIBUTE, attr);
> > +     attrset_node = cil_create_and_insert_node(attr_node, CIL_TYPEATTRIBUTESET, attrset);
> > +
> > +     rc = cil_get_symtab(prev->parent, &symtab, CIL_SYM_TYPES);
> > +     if (rc != SEPOL_OK) {
> > +             goto exit;
> > +     }
> > +
> > +     rc = cil_symtab_insert(symtab, name, &attr->datum, attr_node);
> > +     if (rc != SEPOL_OK) {
> > +             goto exit;
> > +     }
> > +
> > +     db->num_types_and_attrs++;
> > +
> > +     *d = &attr->datum;
> > +
> > +     return SEPOL_OK;
> > +
> > +exit:
> > +     if (attrset_node) {
> > +             prev->next = attrset_node->next;
> > +             cil_destroy_typeattribute(attr_node->data);
> > +             free(attr_node);
> > +             cil_destroy_typeattributeset(attrset_node->data);
> > +             free(attrset_node);
> > +     }
> > +     return rc;
> > +}
> > +
> > +static int cil_create_attribute_d1_and_not_d2(struct cil_db *db, struct cil_tree_node *prev, struct cil_symtab_datum *d1, struct cil_symtab_datum *d2, struct cil_symtab_datum **d3)
> > +{
> > +     struct cil_list *str_expr;
> > +     struct cil_list *datum_expr;
> > +     ebitmap_t *types;
> > +     int rc;
> > +
> > +     *d3 = NULL;
> > +
> > +     if (d1 == d2) {
> > +             return SEPOL_OK;
> > +     }
> > +
> > +     str_expr = cil_create_andnot_expr_list(CIL_STRING, d1->fqn, d2->fqn);
> > +     datum_expr = cil_create_andnot_expr_list(CIL_DATUM, d1, d2);
> > +
> > +     types = cil_malloc(sizeof(*types));
> > +     rc = cil_datums_andnot(types, d1, d2);
> > +     if (rc != SEPOL_OK) {
> > +             goto exit;
> > +     }
> > +     if (ebitmap_is_empty(types)) {
> > +             rc = SEPOL_OK;
> > +             goto exit;
> > +     }
> > +
> > +     if (ebitmap_cardinality(types) == 1) {
> > +             unsigned i = ebitmap_highest_set_bit(types);
> > +             *d3 = DATUM(db->val_to_type[i]);
> > +             ebitmap_destroy(types);
> > +             rc = SEPOL_OK;
> > +             goto exit;
> > +     }
> > +
> > +     return cil_create_and_insert_attribute_and_set(db, prev, str_expr, datum_expr, types, d3);
> > +
> > +exit:
> > +     cil_list_destroy(&str_expr, CIL_FALSE);
> > +     cil_list_destroy(&datum_expr, CIL_FALSE);
> > +     free(types);
> > +     return rc;
> > +}
> > +
> > +static int cil_create_attribute_d1_and_d2(struct cil_db *db, struct cil_tree_node *prev, struct cil_symtab_datum *d1, struct cil_symtab_datum *d2, struct cil_symtab_datum **d3)
> > +{
> > +     struct cil_list *str_expr;
> > +     struct cil_list *datum_expr;
> > +     ebitmap_t *types;
> > +     int rc;
> > +
> > +     if (d1 == d2) {
> > +             *d3 = d1;
> > +             return SEPOL_OK;
> > +     }
> > +
> > +     *d3 = NULL;
> > +
> > +     str_expr = cil_create_and_expr_list(CIL_STRING, d1->fqn, d2->fqn);
> > +     datum_expr = cil_create_and_expr_list(CIL_DATUM, d1, d2);
> > +
> > +     types = cil_malloc(sizeof(*types));
> > +     rc = cil_datums_and(types, d1, d2);
> > +     if (rc != SEPOL_OK) {
> > +             goto exit;
> > +     }
> > +     if (ebitmap_is_empty(types)) {
> > +             rc = SEPOL_OK;
> > +             goto exit;
> > +     }
> > +
> > +     if (ebitmap_cardinality(types) == 1) {
> > +             unsigned i = ebitmap_highest_set_bit(types);
> > +             *d3 = DATUM(db->val_to_type[i]);
> > +             ebitmap_destroy(types);
> > +             rc = SEPOL_OK;
> > +             goto exit;
> > +     }
> > +
> > +     rc = cil_create_and_insert_attribute_and_set(db, prev, str_expr, datum_expr, types, d3);
> > +     if (rc != SEPOL_OK) {
> > +             goto exit;
> > +     }
>
> This and the and_not variant above differ in this error handling.  The
> and_not one trusts cil_create_and_insert_attribute_and_set() to do the
> cleanup, and this one does its own cleanup.
>
> I think they're both potentially wrong, as
> cil_create_and_insert_attribute_and_set() calls
> cil_destroy_typeattributeset() which does the same cleanup only if it
> makes it halfway through the function.  So if
> cil_create_new_attribute_name() fails, then this case works correctly
> and the and_not case doesn't free str_expr and datum_expr.  On the other
> hand, if something later in cil_create_and_insert_attribute_and_set()
> fails, then the and_not case is correct, and this one will double free
> str_expr and datum_expr.
>

Yes. I really don't want to call cil_destroy_typeattributeset(), I
want the calling functions to deal with str_expr and datum_expr, since
they passed them in.

> > +
> > +     return SEPOL_OK;
> > +
> > +exit:
> > +     cil_list_destroy(&str_expr, CIL_FALSE);
> > +     cil_list_destroy(&datum_expr, CIL_FALSE);
> > +     free(types);
> > +     return rc;
> > +}
> > +
> > +static struct cil_avrule *cil_create_avrule(struct cil_symtab_datum *src, struct cil_symtab_datum *tgt, struct cil_list *classperms)
> > +{
> > +     struct cil_avrule *new;
> > +
> > +     cil_avrule_init(&new);
> > +     new->is_extended = CIL_FALSE;
> > +     new->rule_kind = CIL_AVRULE_ALLOWED;
> > +     new->src_str = src->name;
> > +     new->src = src;
> > +     new->tgt_str = tgt->name;
> > +     new->tgt = tgt;
> > +     new->perms.classperms = classperms;
> > +
> > +     return new;
> > +}
> > +
> > +static struct cil_tree_node *cil_create_and_add_avrule(struct cil_tree_node *curr, struct cil_symtab_datum *src, struct cil_symtab_datum *tgt, struct cil_list *classperms)
> > +{
> > +     struct cil_avrule *new_avrule;
> > +
> > +     new_avrule = cil_create_avrule(src, tgt, classperms);
> > +     return cil_create_and_insert_node(curr, CIL_AVRULE, new_avrule);
> > +}
> > +
> > +static int cil_remove_permissions_from_rule(struct cil_db *db, struct cil_tree_node *allow_node, const struct cil_tree_node *deny_node)
> > +{
> > +     struct cil_avrule *allow_rule = allow_node->data;
> > +     struct cil_deny_rule *deny_rule = deny_node->data;
> > +     struct cil_symtab_datum *s1 = allow_rule->src;
> > +     struct cil_symtab_datum *t1 = allow_rule->tgt;
> > +     struct cil_list *p1 = allow_rule->perms.classperms;
> > +     struct cil_symtab_datum *s2 = deny_rule->src;
> > +     struct cil_symtab_datum *t2 = deny_rule->tgt;
> > +     struct cil_list *p2 = deny_rule->classperms;
> > +     struct cil_list *p3 = NULL;
> > +     struct cil_tree_node *curr = allow_node;
> > +     int rc;
> > +
> > +     cil_classperms_list_andnot(&p3, p1, p2);
> > +
> > +     if (!cil_list_is_empty(p3)) {;
> > +             curr = cil_create_and_add_avrule(curr, s1, t1, p3);
> > +     } else {
> > +             cil_destroy_classperms_list(&p3);
> > +             p3 = NULL;
> > +     }
> > +
> > +     if (FLAVOR(s1) == CIL_TYPEATTRIBUTE) {
> > +             struct cil_symtab_datum *s3 = NULL;
> > +             struct cil_symtab_datum *d = NULL > +           struct cil_list *cp = NULL;
> > +             if (t1->fqn == CIL_KEY_SELF && t2->fqn != CIL_KEY_SELF) {
> > +                     /* S3 = S1 and not (S2 and T2) */
> > +                     rc = cil_create_attribute_d1_and_d2(db, NODE(s2), s2, t2, &d);
> > +                     if (rc != SEPOL_OK) {
> > +                             goto exit;
> > +                     }
> > +                     if (d) {
> > +                             rc = cil_create_attribute_d1_and_not_d2(db, NODE(s1), s1, d, &s3);
> > +                             if (rc != SEPOL_OK) {
> > +                                     goto exit;
> > +                             }
> > +                     }
> > +             } else if (t1->fqn != CIL_KEY_SELF && t2->fqn == CIL_KEY_SELF) {
> > +                     /* S3 = S1 and not (T1 and S2) */
> > +                     rc = cil_create_attribute_d1_and_d2(db, NODE(t1), t1, s2, &d);
> > +                     if (rc != SEPOL_OK) {
> > +                             goto exit;
> > +                     }
> > +                     if (d) {
> > +                             rc = cil_create_attribute_d1_and_not_d2(db, NODE(s1), s1, d, &s3);
> > +                             if (rc != SEPOL_OK) {
> > +                                     goto exit;
> > +                             }
> > +                     }
> > +             } else {
> > +                     /* S3 = S1 and not S2 */
> > +                     rc = cil_create_attribute_d1_and_not_d2(db, NODE(s1), s1, s2, &s3);
> > +                     if (rc != SEPOL_OK) {
> > +                             goto exit;
> > +                     }
> > +             }
> > +             if (s3) {
> > +                     cil_classperms_list_copy(&cp, p1);
> > +                     curr = cil_create_and_add_avrule(curr, s3, t1, cp);
> > +             }
> > +     }
> > +
> > +     if (t1->fqn != CIL_KEY_SELF && FLAVOR(t1) == CIL_TYPEATTRIBUTE) {
> > +             struct cil_symtab_datum *t3 = NULL;
> > +             struct cil_symtab_datum *d = NULL;
> > +             struct cil_list *cp = NULL;
> > +             if (t2->fqn == CIL_KEY_SELF) {
> > +                     /* T3 = T1 and not (S1 AND S2) */
> > +                     rc = cil_create_attribute_d1_and_d2(db, NODE(s1), s1, s2, &d);
> > +                     if (rc != SEPOL_OK) {
> > +                             goto exit;
> > +                     }
> > +                     if (d) {
> > +                             rc = cil_create_attribute_d1_and_not_d2(db, NODE(t1), t1, d, &t3);
> > +                             if (rc != SEPOL_OK) {
> > +                                     goto exit;
> > +                             }
> > +                     }
> > +             } else {
> > +                     /* S3 = T1 and not T2 */
> > +                     rc = cil_create_attribute_d1_and_not_d2(db, NODE(t1), t1, t2, &t3);
> > +                     if (rc != SEPOL_OK) {
> > +                             goto exit;
> > +                     }
> > +             }
> > +             if (t3) {
> > +                     cil_classperms_list_copy(&cp, p1);
> > +                     curr = cil_create_and_add_avrule(curr, s1, t3, cp);
> > +             }
> > +
> > +             if (FLAVOR(s1) == CIL_TYPEATTRIBUTE && FLAVOR(s2) == CIL_TYPEATTRIBUTE &&
> > +                     t2->fqn == CIL_KEY_SELF) {
> > +                     /* Really special case */
> > +                     /* FLAVOR(t1) == CIL_TYPEATTRIBUTE */
> > +                     /* SX = S1 and S2 and T1 */
> > +                     struct cil_symtab_datum *sx = NULL;
> > +                     struct cil_typeattribute *sx_attr = NULL;
> > +
> > +                     d = NULL;
> > +                     cp = NULL;
> > +
> > +                     rc = cil_create_attribute_d1_and_d2(db, NODE(s2), s2, t1, &d);
> > +                     if (rc != SEPOL_OK) {
> > +                             goto exit;
> > +                     }
> > +                     if (d) {
> > +                             rc = cil_create_attribute_d1_and_d2(db, NODE(s1), s1, d, &sx);
> > +                             if (rc != SEPOL_OK) {
> > +                                     goto exit;
> > +                             }
> > +                             sx_attr = (struct cil_typeattribute *)sx;
> > +                     }
> > +                     if (sx && ebitmap_cardinality(sx_attr->types) > 1) {
> > +                             struct cil_symtab_datum *s = NULL;
> > +                             struct cil_symtab_datum *t = NULL;
> > +                             ebitmap_node_t *n;
> > +                             unsigned i;
> > +                             ebitmap_for_each_positive_bit(sx_attr->types, n, i) {
> > +                                     s = DATUM(db->val_to_type[i]);
> > +                                     rc = cil_create_attribute_d1_and_not_d2(db, NODE(sx), sx, s, &t);
> > +                                     if (rc != SEPOL_OK) {
> > +                                             goto exit;
> > +                                     }
> > +                                     if (t) {
> > +                                             cil_classperms_list_copy(&cp, p1);
> > +                                             curr = cil_create_and_add_avrule(curr, s, t, cp);
> > +                                     }
> > +                             }
> > +                     }
> > +             }
> > +     }
> > +
> > +exit:
> > +     return rc;
> > +}
>
> As I mentioned briefly back in December, it might be in the attributes
> case to only grant p2 rather than p3.  I *think* that's safe, since the
> initial allow rule grants everything p3, so the only things needed to be
> added back in the attributes are the denied permissions (p2).
>

Not p2, but p1 and p2

So the new logic that I am looking at is (for the general case):

P3 = P1 and not P2
(allow S1 T1 P3)

P4 = P1 and P2
S3 = S1 and not S2
(allow S3 T1 P4)      ; T1 is attribute, type, self, notself, or other

S4 = S1 and S2
T3 = T1 and not T2  ; Only if T1 is attribute, otherwise there are
different rules
(allow S4 T3 P4)

I am also looking at how adding "notself" and "other" (previously
called "minusself") will effect things. Using "other" actually makes
some cases easier.

Thanks for the thorough review,
Jim

> > +
> > +static int cil_find_matching_allow_rules(struct cil_list *matching, struct cil_tree_node *start, struct cil_tree_node *deny_node)
> > +{
> > +     struct cil_deny_rule *deny_rule = deny_node->data;
> > +     struct cil_avrule target;
> > +
> > +     target.rule_kind = CIL_AVRULE_ALLOWED;
> > +     target.is_extended = CIL_FALSE;
> > +     target.src = deny_rule->src;
> > +     target.tgt = deny_rule->tgt;
> > +     target.perms.classperms = deny_rule->classperms;
> > +
> > +     return cil_find_matching_avrule_in_ast(start, CIL_AVRULE, &target, matching, CIL_FALSE);
> > +}
> > +
> > +static int cil_process_deny_rule(struct cil_db *db, struct cil_tree_node *start, struct cil_tree_node *deny_node)
> > +{
> > +     struct cil_list *matching;
> > +     struct cil_list_item *item;
> > +     int rc;
> > +
> > +     cil_list_init(&matching, CIL_NODE);
> > +
> > +     rc = cil_find_matching_allow_rules(matching, start, deny_node);
> > +     if (rc != SEPOL_OK) {
> > +             goto exit;
> > +     }
> > +
> > +     cil_list_for_each(item, matching) {
> > +             struct cil_tree_node *allow_node = item->data;
> > +             rc = cil_remove_permissions_from_rule(db, allow_node, deny_node);
> > +             cil_tree_remove_node(allow_node);
> > +             cil_tree_node_destroy(&allow_node);
> > +             if (rc != SEPOL_OK) {
> > +                     goto exit;
> > +             }
> > +
> > +     }
> > +
> > +exit:
> > +     cil_list_destroy(&matching, CIL_FALSE);
> > +     return rc;
> > +}
> > +
> > +static int cil_process_deny_rules(struct cil_db *db, struct cil_tree_node *start, struct cil_list *deny_rules)
> > +{
> > +     struct cil_list_item *item;
> > +     int rc;
> > +
> > +     cil_list_for_each(item, deny_rules) {
> > +             struct cil_tree_node *deny_node = item->data;
> > +             rc = cil_process_deny_rule(db, start, deny_node);
> > +             if (rc != SEPOL_OK) {
> > +                     goto exit;
> > +             }
> > +             cil_tree_remove_node(deny_node);
> > +             cil_tree_node_destroy(&deny_node);
> > +     }
> > +
> > +exit:
> > +     return rc;
> > +}
> > +
> > +static int __cil_find_deny_rules(struct cil_tree_node *node,  uint32_t *finished, void *extra_args)
> > +{
> > +     struct cil_list *deny_rules = extra_args;
> > +
> > +     if (node->flavor == CIL_BLOCK) {
> > +             struct cil_block *block = node->data;
> > +             if (block->is_abstract == CIL_TRUE) {
> > +                     *finished = CIL_TREE_SKIP_HEAD;
> > +             }
> > +     } else if (node->flavor == CIL_MACRO) {
> > +             *finished = CIL_TREE_SKIP_HEAD;
> > +     } else if (node->flavor == CIL_DENY_RULE) {
> > +             cil_list_append(deny_rules, CIL_DENY_RULE, node);
> > +     }
> > +     return SEPOL_OK;
> > +}
> > +
> > +int cil_process_deny_rules_in_ast(struct cil_db *db)
> > +{
> > +     struct cil_tree_node *start;
> > +     struct cil_list *deny_rules;
> > +     int rc = SEPOL_ERR;
> > +
> > +     cil_list_init(&deny_rules, CIL_DENY_RULE);
> > +
> > +     if (!db) {
> > +             cil_log(CIL_ERR, "No CIL db provided to process deny rules\n");
> > +             goto exit;
> > +     }
> > +
> > +     start = db->ast->root;
> > +     rc = cil_tree_walk(start, __cil_find_deny_rules, NULL, NULL, deny_rules);
> > +     if (rc != SEPOL_OK) {
> > +             cil_log(CIL_ERR, "An error occurred while getting deny rules\n");
> > +             goto exit;
> > +     }
> > +
> > +     rc = cil_process_deny_rules(db, start, deny_rules);
> > +     if (rc != SEPOL_OK) {
> > +             cil_log(CIL_ERR, "An error occurred while processing deny rules\n");
> > +             goto exit;
> > +     }
> > +
> > +exit:
> > +     cil_list_destroy(&deny_rules, CIL_FALSE);
> > +     return rc;
> > +}
> > diff --git a/libsepol/cil/src/cil_deny.h b/libsepol/cil/src/cil_deny.h
> > new file mode 100644
> > index 00000000..4a9e92d1
> > --- /dev/null
> > +++ b/libsepol/cil/src/cil_deny.h
> > @@ -0,0 +1,34 @@
> > +/*
> > + * This file is public domain software, i.e. not copyrighted.
> > + *
> > + * Warranty Exclusion
> > + * ------------------
> > + * You agree that this software is a non-commercially developed program
> > + * that may contain "bugs" (as that term is used in the industry) and
> > + * that it may not function as intended. The software is licensed
> > + * "as is". NSA makes no, and hereby expressly disclaims all, warranties,
> > + * express, implied, statutory, or otherwise with respect to the software,
> > + * including noninfringement and the implied warranties of merchantability
> > + * and fitness for a particular purpose.
> > + *
> > + * Limitation of Liability
> > + *-----------------------
> > + * In no event will NSA be liable for any damages, including loss of data,
> > + * lost profits, cost of cover, or other special, incidental, consequential,
> > + * direct or indirect damages arising from the software or the use thereof,
> > + * however caused and on any theory of liability. This limitation will apply
> > + * even if NSA has been advised of the possibility of such damage. You
> > + * acknowledge that this is a reasonable allocation of risk.
> > + *
> > + * Original author: James Carter
> > + */
> > +
> > +#ifndef CIL_DENY_H_
> > +#define CIL_DENY_H_
> > +
> > +int cil_classperms_list_match(const struct cil_list *cpl1, const struct cil_list *cpl2);
> > +void cil_classperms_list_copy(struct cil_list **new, const struct cil_list *old);
> > +void cil_classperms_list_andnot(struct cil_list **result, const struct cil_list *cpl1, const struct cil_list *cpl2);
> > +int cil_process_deny_rules_in_ast(struct cil_db *db);
> > +
> > +#endif /* CIL_DENY_H_ */
> > diff --git a/libsepol/cil/src/cil_post.c b/libsepol/cil/src/cil_post.c
> > index 11e572e2..fb7eec2d 100644
> > --- a/libsepol/cil/src/cil_post.c
> > +++ b/libsepol/cil/src/cil_post.c
> > @@ -47,6 +47,7 @@
> >   #include "cil_policy.h"
> >   #include "cil_verify.h"
> >   #include "cil_symtab.h"
> > +#include "cil_deny.h"
> >
> >   #define GEN_REQUIRE_ATTR "cil_gen_require" /* Also in libsepol/src/module_to_cil.c */
> >   #define TYPEATTR_INFIX "_typeattr_"        /* Also in libsepol/src/module_to_cil.c */
> > @@ -2551,6 +2552,12 @@ int cil_post_process(struct cil_db *db)
> >               goto exit;
> >       }
> >
> > +     rc = cil_process_deny_rules_in_ast(db);
> > +     if (rc != SEPOL_OK) {
> > +             cil_log(CIL_ERR, "Failed to process deny rules\n");
> > +             goto exit;
> > +     }
> > +
> >       rc = cil_post_verify(db);
> >       if (rc != SEPOL_OK) {
> >               cil_log(CIL_ERR, "Failed to verify cil database\n");
>
diff mbox series

Patch

diff --git a/libsepol/cil/src/cil_deny.c b/libsepol/cil/src/cil_deny.c
new file mode 100644
index 00000000..43043c37
--- /dev/null
+++ b/libsepol/cil/src/cil_deny.c
@@ -0,0 +1,957 @@ 
+/*
+ * This file is public domain software, i.e. not copyrighted.
+ *
+ * Warranty Exclusion
+ * ------------------
+ * You agree that this software is a non-commercially developed program
+ * that may contain "bugs" (as that term is used in the industry) and
+ * that it may not function as intended. The software is licensed
+ * "as is". NSA makes no, and hereby expressly disclaims all, warranties,
+ * express, implied, statutory, or otherwise with respect to the software,
+ * including noninfringement and the implied warranties of merchantability
+ * and fitness for a particular purpose.
+ *
+ * Limitation of Liability
+ *-----------------------
+ * In no event will NSA be liable for any damages, including loss of data,
+ * lost profits, cost of cover, or other special, incidental, consequential,
+ * direct or indirect damages arising from the software or the use thereof,
+ * however caused and on any theory of liability. This limitation will apply
+ * even if NSA has been advised of the possibility of such damage. You
+ * acknowledge that this is a reasonable allocation of risk.
+ *
+ * Original author: James Carter
+ */
+
+#include <sepol/policydb/ebitmap.h>
+
+#include "cil_internal.h"
+#include "cil_find.h"
+#include "cil_flavor.h"
+#include "cil_list.h"
+#include "cil_strpool.h"
+#include "cil_log.h"
+#include "cil_symtab.h"
+#include "cil_build_ast.h"
+#include "cil_copy_ast.h"
+#include "cil_deny.h"
+
+#define CIL_DENY_ATTR_PREFIX "deny_rule_attr"
+
+/*
+ * A deny rule will remove the specified permissions from the CIL AST.
+ *
+ * (allow S1 T1 P1)
+ * (deny  S2 T2 P2)
+ *
+ * In general what is needed is:
+ * 1) Allow permissions that do not match the deny rule
+ *   P3 = P1 and not P2
+ *   (allow S1 T1 P3)
+ * 2) Allow source types that do not match the deny rule
+ *   S3 = S1 and not S2
+ *   (allow S3 T1 P1)
+ * 3) Allow target types that do not match the deny rule
+ *   T3 = T1 and not T2
+ *   (allow S1 T3 P1)
+ *
+ * If a self rule is involved then
+ *   If S1 is an attribute then
+ *     If T1 is self and T2 is not self then
+ *       S3 = S1 and not (S2 and T2)
+ *     else if T1 is not self and T2 is self then
+ *       S3 = S1 and not (T1 and S2)
+ *     else if T1 is self and T2 is self then
+ *       S3 = S1 and not S2
+ *     (allow S3 T1 P1)
+ *   If T1 is an attribute then
+ *     If T2 is self then
+ *       T3 = T1 and not (S1 and S2)
+ *     (allow S1 T3 P1)
+ *     If S1 is attribute and S2 is attribute and T2 is self then
+ *       # In addition to any rules involving S3 and T3 above
+ *       SX = S1 and S2 and T1
+ *       For S in SX do
+ *         T = SX and not S
+ *         (allow S T P1)
+ *
+ * Do not create a rule that has an empty set
+ */
+
+static int cil_perm_match(const struct cil_perm *p1, const struct cil_list *pl2)
+{
+	struct cil_list_item *curr;
+
+	cil_list_for_each(curr, pl2) {
+		struct cil_perm *p = curr->data;
+		if (p == p1) {
+			return CIL_TRUE;
+		}
+	}
+	return CIL_FALSE;
+}
+
+static int cil_class_perm_match(const struct cil_class *c1, const struct cil_perm *p1, const struct cil_list *cpl2)
+{
+	struct cil_list_item *curr;
+
+	cil_list_for_each(curr, cpl2) {
+		if (curr->flavor == CIL_CLASSPERMS) {
+			struct cil_classperms *cp = curr->data;
+			if (FLAVOR(cp->class) == CIL_CLASS) {
+				if (cp->class == c1) {
+					if (cil_perm_match(p1, cp->perms)) {
+						return CIL_TRUE;
+					}
+				}
+			} else { /* MAP */
+				struct cil_list_item *p;
+				cil_list_for_each(p, cp->perms) {
+					struct cil_perm *cmp = p->data;
+					if (cil_class_perm_match(c1, p1, cmp->classperms)) {
+						return CIL_TRUE;
+					}
+				}
+			}
+		} else { /* SET */
+			struct cil_classperms_set *cp_set = curr->data;
+			struct cil_classpermission *cp = cp_set->set;
+			if (cil_class_perm_match(c1, p1, cp->classperms)) {
+				return CIL_TRUE;
+			}
+		}
+	}
+	return CIL_FALSE;
+}
+
+static int cil_classperms_match(const struct cil_classperms *cp1, const struct cil_list *cpl2)
+{
+	struct cil_list_item *curr;
+
+	cil_list_for_each(curr, cp1->perms) {
+		struct cil_perm *perm = curr->data;
+		if (cil_class_perm_match(cp1->class, perm, cpl2)) {
+			return CIL_TRUE;
+		}
+	}
+	return CIL_FALSE;
+}
+
+int cil_classperms_list_match(const struct cil_list *cpl1, const struct cil_list *cpl2)
+{
+	struct cil_list_item *curr;
+
+	if (!cpl1 || !cpl2) {
+		return (!cpl1 && !cpl2) ? CIL_TRUE : CIL_FALSE;
+	}
+
+	cil_list_for_each(curr, cpl1) {
+		if (curr->flavor == CIL_CLASSPERMS) {
+			struct cil_classperms *cp = curr->data;
+			if (FLAVOR(cp->class) == CIL_CLASS) {
+				if (cil_classperms_match(cp, cpl2)) {
+					return CIL_TRUE;
+				}
+			} else { /* MAP */
+				struct cil_list_item *p;
+				cil_list_for_each(p, cp->perms) {
+					struct cil_perm *cmp = p->data;
+					if (cil_classperms_list_match(cmp->classperms, cpl2)) {
+						return CIL_TRUE;
+					}
+				}
+			}
+		} else { /* SET */
+			struct cil_classperms_set *cp_set = curr->data;
+			struct cil_classpermission *cp = cp_set->set;
+			if (cil_classperms_list_match(cp->classperms, cpl2)) {
+				return CIL_TRUE;
+			}
+		}
+	}
+	return CIL_FALSE;
+}
+
+static void cil_classperms_copy(struct cil_classperms **new, const struct cil_classperms *old)
+{
+	cil_classperms_init(new);
+	(*new)->class_str = old->class_str;
+	(*new)->class = old->class;
+	cil_copy_list(old->perm_strs, &(*new)->perm_strs);
+	cil_copy_list(old->perms, &(*new)->perms);
+}
+
+static void cil_classperms_set_copy(struct cil_classperms_set **new, const struct cil_classperms_set *old)
+{
+	cil_classperms_set_init(new);
+	(*new)->set_str = old->set_str;
+	(*new)->set = old->set;
+}
+
+void cil_classperms_list_copy(struct cil_list **new, const struct cil_list *old)
+{
+	struct cil_list_item *curr;
+
+	if (!new) {
+		return;
+	}
+
+	if (!old) {
+		*new = NULL;
+		return;
+	}
+
+	cil_list_init(new, CIL_LIST);
+
+	cil_list_for_each(curr, old) {
+		if (curr->flavor == CIL_CLASSPERMS) {
+			struct cil_classperms *new_cp;
+			cil_classperms_copy(&new_cp, curr->data);
+			cil_list_append(*new, CIL_CLASSPERMS, new_cp);
+		} else { /* SET */
+			struct cil_classperms_set *new_cps;
+			cil_classperms_set_copy(&new_cps, curr->data);
+			cil_list_append(*new, CIL_CLASSPERMS_SET, new_cps);
+		}
+	}
+
+	if (cil_list_is_empty(*new)) {
+		cil_list_destroy(new, CIL_FALSE);
+	}
+}
+
+/* Append cp1 and not cpl2 to result */
+static void cil_classperms_andnot(struct cil_list **result, const struct cil_classperms *cp1, const struct cil_list *cpl2)
+{
+	struct cil_classperms *new_cp = NULL;
+	struct cil_list_item *curr;
+
+	if (!cil_classperms_match(cp1, cpl2)) {
+		cil_classperms_copy(&new_cp, cp1);
+		cil_list_append(*result, CIL_CLASSPERMS, new_cp);
+		return;
+	}
+
+	cil_list_for_each(curr, cp1->perms) {
+		struct cil_perm *perm = curr->data;
+		if (!cil_class_perm_match(cp1->class, perm, cpl2)) {
+			if (new_cp == NULL) {
+				cil_classperms_init(&new_cp);
+				new_cp->class_str = cp1->class_str;
+				new_cp->class = cp1->class;
+				cil_list_init(&new_cp->perm_strs, CIL_PERM);
+				cil_list_init(&new_cp->perms, CIL_PERM);
+				cil_list_append(*result, CIL_CLASSPERMS, new_cp);
+			}
+			cil_list_append(new_cp->perm_strs, CIL_STRING, perm->datum.fqn);
+			cil_list_append(new_cp->perms, CIL_DATUM, perm);
+		}
+	}
+}
+
+/* Append cp1 and not cpl2 to result */
+static void cil_classperms_map_andnot(struct cil_list **result, const struct cil_classperms *cp1, const struct cil_list *cpl2)
+{
+	struct cil_classperms *new_cp = NULL;
+	struct cil_list_item *p;
+
+	cil_list_for_each(p, cp1->perms) {
+		struct cil_perm *map_perm = p->data;
+		if (!cil_classperms_list_match(map_perm->classperms, cpl2)) {
+			if (new_cp == NULL) {
+				cil_classperms_init(&new_cp);
+				new_cp->class_str = cp1->class_str;
+				new_cp->class = cp1->class;
+				cil_list_init(&new_cp->perm_strs, CIL_PERM);
+				cil_list_init(&new_cp->perms, CIL_PERM);
+				cil_list_append(*result, CIL_CLASSPERMS, new_cp);
+			}
+			cil_list_append(new_cp->perm_strs, CIL_STRING, map_perm->datum.fqn);
+			cil_list_append(new_cp->perms, CIL_DATUM, map_perm);
+		} else {
+			struct cil_list *new_cpl = NULL;
+			cil_classperms_list_andnot(&new_cpl, map_perm->classperms, cpl2);
+			if (new_cpl) {
+				struct cil_list_item *i;
+				cil_list_for_each(i, new_cpl) {
+					cil_list_append(*result, i->flavor, i->data);
+				}
+				cil_list_destroy(&new_cpl, CIL_FALSE);
+			}
+		}
+	}
+}
+
+/* Append cps1 and not cpl2 to result */
+static void cil_classperms_set_andnot(struct cil_list **result, const struct cil_classperms_set *cps1, const struct cil_list *cpl2)
+{
+	struct cil_classpermission *cp = cps1->set;
+
+	if (!cil_classperms_list_match(cp->classperms, cpl2)) {
+		struct cil_classperms_set *new_cps;
+		cil_classperms_set_copy(&new_cps, cps1);
+		cil_list_append(*result, CIL_CLASSPERMS_SET, new_cps);
+	} else {
+		struct cil_list *new_cpl;
+		cil_classperms_list_andnot(&new_cpl, cp->classperms, cpl2);
+		if (new_cpl) {
+			struct cil_list_item *i;
+			cil_list_for_each(i, new_cpl) {
+				cil_list_append(*result, i->flavor, i->data);
+			}
+			cil_list_destroy(&new_cpl, CIL_FALSE);
+		}
+	}
+}
+
+/* result = cpl1 and not cpl2 */
+void cil_classperms_list_andnot(struct cil_list **result, const struct cil_list *cpl1, const struct cil_list *cpl2)
+{
+	struct cil_list_item *curr;
+
+	if (!result) {
+		return;
+	}
+
+	if (!cpl1) {
+		*result = NULL;
+		return;
+	}
+
+	cil_list_init(result, CIL_LIST);
+
+	if (!cpl2 || !cil_classperms_list_match(cpl1, cpl2)) {
+		cil_classperms_list_copy(result, cpl1);
+		return;
+	}
+
+	cil_list_for_each(curr, cpl1) {
+		if (curr->flavor == CIL_CLASSPERMS) {
+			struct cil_classperms *cp = curr->data;
+			if (FLAVOR(cp->class) == CIL_CLASS) {
+				cil_classperms_andnot(result, cp, cpl2);
+			} else { /* MAP */
+				cil_classperms_map_andnot(result, cp, cpl2);
+			}
+		} else { /* SET */
+			struct cil_classperms_set *cps = curr->data;
+			cil_classperms_set_andnot(result, cps, cpl2);
+		}
+	}
+
+	if (cil_list_is_empty(*result)) {
+		cil_list_destroy(result, CIL_FALSE);
+	}
+}
+
+/* result = d1 and d2 */
+static int cil_datums_and(ebitmap_t *result, const struct cil_symtab_datum *d1, const struct cil_symtab_datum *d2)
+{
+	int rc = SEPOL_OK;
+	enum cil_flavor f1 = FLAVOR(d1);
+	enum cil_flavor f2 = FLAVOR(d2);
+
+	if (f1 != CIL_TYPEATTRIBUTE && f2 != CIL_TYPEATTRIBUTE) {
+		struct cil_type *t1 = (struct cil_type *)d1;
+		struct cil_type *t2 = (struct cil_type *)d2;
+		ebitmap_init(result);
+		if (t1->value == t2->value) {
+			rc = ebitmap_set_bit(result, t1->value, 1);
+			if (rc != SEPOL_OK) {
+				ebitmap_destroy(result);
+				goto exit;
+			}
+		}
+	} else if (f1 == CIL_TYPEATTRIBUTE && f2 != CIL_TYPEATTRIBUTE) {
+		struct cil_typeattribute *a1 = (struct cil_typeattribute *)d1;
+		struct cil_type *t2 = (struct cil_type *)d2;
+		ebitmap_init(result);
+		if (ebitmap_get_bit(a1->types, t2->value)) {
+			rc = ebitmap_set_bit(result, t2->value, 1);
+			if (rc != SEPOL_OK) {
+				ebitmap_destroy(result);
+				goto exit;
+			}
+		}
+	} else if (f1 != CIL_TYPEATTRIBUTE && f2 == CIL_TYPEATTRIBUTE) {
+		struct cil_type *t1 = (struct cil_type *)d1;
+		struct cil_typeattribute *a2 = (struct cil_typeattribute *)d2;
+		ebitmap_init(result);
+		if (ebitmap_get_bit(a2->types, t1->value)) {
+			rc = ebitmap_set_bit(result, t1->value, 1);
+			if (rc != SEPOL_OK) {
+				ebitmap_destroy(result);
+				goto exit;
+			}
+		}
+	} else {
+		/* Both are attributes */
+		struct cil_typeattribute *a1 = (struct cil_typeattribute *)d1;
+		struct cil_typeattribute *a2 = (struct cil_typeattribute *)d2;
+		rc = ebitmap_and(result, a1->types, a2->types);
+		if (rc != SEPOL_OK) {
+			ebitmap_destroy(result);
+			goto exit;
+		}
+	}
+exit:
+	return rc;
+}
+
+/* result = d1 and not d2 */
+static int cil_datums_andnot(ebitmap_t *result, const struct cil_symtab_datum *d1, const struct cil_symtab_datum *d2)
+{
+	int rc = SEPOL_OK;
+	enum cil_flavor f1 = FLAVOR(d1);
+	enum cil_flavor f2 = FLAVOR(d2);
+
+	if (f1 != CIL_TYPEATTRIBUTE && f2 != CIL_TYPEATTRIBUTE) {
+		struct cil_type *t1 = (struct cil_type *)d1;
+		struct cil_type *t2 = (struct cil_type *)d2;
+		ebitmap_init(result);
+		if (t1->value != t2->value) {
+			rc = ebitmap_set_bit(result, t1->value, 1);
+			if (rc != SEPOL_OK) {
+				ebitmap_destroy(result);
+				goto exit;
+			}
+		}
+	} else if (f1 == CIL_TYPEATTRIBUTE && f2 != CIL_TYPEATTRIBUTE) {
+		struct cil_typeattribute *a1 = (struct cil_typeattribute *)d1;
+		struct cil_type *t2 = (struct cil_type *)d2;
+		rc = ebitmap_cpy(result, a1->types);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+		rc = ebitmap_set_bit(result, t2->value, 0);
+		if (rc != SEPOL_OK) {
+			ebitmap_destroy(result);
+			goto exit;
+		}
+	} else if (f1 != CIL_TYPEATTRIBUTE && f2 == CIL_TYPEATTRIBUTE) {
+		struct cil_type *t1 = (struct cil_type *)d1;
+		struct cil_typeattribute *a2 = (struct cil_typeattribute *)d2;
+		ebitmap_init(result);
+		if (!ebitmap_get_bit(a2->types, t1->value)) {
+			rc = ebitmap_set_bit(result, t1->value, 1);
+			if (rc != SEPOL_OK) {
+				ebitmap_destroy(result);
+				goto exit;
+			}
+		}
+	} else {
+		/* Both are attributes */
+		struct cil_typeattribute *a1 = (struct cil_typeattribute *)d1;
+		struct cil_typeattribute *a2 = (struct cil_typeattribute *)d2;
+		rc = ebitmap_andnot(result, a1->types, a2->types, a1->types->highbit);
+		if (rc != SEPOL_OK) {
+			ebitmap_destroy(result);
+			goto exit;
+		}
+	}
+exit:
+	return rc;
+}
+
+static size_t num_digits(unsigned n)
+{
+	size_t num = 1;
+	while (n >= 10) {
+		n /= 10;
+		num++;
+	}
+	return num;
+}
+
+static char *cil_create_new_attribute_name(unsigned num)
+{
+	char *s1 = NULL;
+	char *s2 = NULL;
+	size_t len_num = num_digits(num);
+	size_t len = strlen(CIL_DENY_ATTR_PREFIX) + 1 + len_num + 1;
+	int rc;
+
+	if (len >= CIL_MAX_NAME_LENGTH) {
+		cil_log(CIL_ERR, "Name length greater than max name length of %d",
+				CIL_MAX_NAME_LENGTH);
+		goto exit;
+	}
+
+	s1 = cil_malloc(len);
+	rc = snprintf(s1, len, "%s_%u", CIL_DENY_ATTR_PREFIX, num);
+	if (rc < 0 || (size_t)rc >= len) {
+		cil_log(CIL_ERR, "Error creating new attribute name");
+		free(s1);
+		goto exit;
+	}
+
+	s2 = cil_strpool_add(s1);
+	free(s1);
+
+exit:
+	return s2;
+}
+
+static struct cil_list *cil_create_and_expr_list(enum cil_flavor flavor, void *v1, void *v2)
+{
+	struct cil_list *expr;
+
+	cil_list_init(&expr, CIL_TYPE);
+	cil_list_append(expr, CIL_OP, (void *)CIL_AND);
+	cil_list_append(expr, flavor, v1);
+	cil_list_append(expr, flavor, v2);
+
+	return expr;
+}
+
+static struct cil_list *cil_create_andnot_expr_list(enum cil_flavor flavor, void *v1, void *v2)
+{
+	struct cil_list *expr, *sub_expr;
+
+	cil_list_init(&expr, CIL_TYPE);
+	cil_list_append(expr, CIL_OP, (void *)CIL_AND);
+	cil_list_append(expr, flavor, v1);
+	cil_list_init(&sub_expr, CIL_TYPE);
+	cil_list_append(sub_expr, CIL_OP, (void *)CIL_NOT);
+	cil_list_append(sub_expr, flavor, v2);
+	cil_list_append(expr, CIL_LIST, sub_expr);
+
+	return expr;
+}
+
+static struct cil_tree_node *cil_create_and_insert_node(struct cil_tree_node *prev, enum cil_flavor flavor, void *data)
+{
+	struct cil_tree_node *new;
+
+	cil_tree_node_init(&new);
+	new->parent = prev->parent;
+	new->line = prev->line;
+	new->hll_offset = prev->hll_offset;
+	new->flavor = flavor;
+	new->data = data;
+	new->next = prev->next;
+	prev->next = new;
+
+	return new;
+}
+
+static int cil_create_and_insert_attribute_and_set(struct cil_db *db, struct cil_tree_node *prev, struct cil_list *str_expr, struct cil_list *datum_expr, ebitmap_t *types, struct cil_symtab_datum **d)
+{
+	struct cil_tree_node *attr_node = NULL;
+	char *name;
+	struct cil_typeattribute *attr = NULL;
+	struct cil_tree_node *attrset_node = NULL;
+	struct cil_typeattributeset *attrset = NULL;
+	symtab_t *symtab = NULL;
+	int rc = SEPOL_ERR;
+
+	name = cil_create_new_attribute_name(db->num_types_and_attrs);
+	if (!name) {
+		goto exit;
+	}
+
+	cil_typeattributeset_init(&attrset);
+	attrset->attr_str = name;
+	attrset->str_expr = str_expr;
+	attrset->datum_expr = datum_expr;
+
+	cil_typeattribute_init(&attr);
+	cil_list_init(&attr->expr_list, CIL_TYPE);
+	cil_list_append(attr->expr_list, CIL_LIST, datum_expr);
+	attr->types = types;
+	attr->used = CIL_ATTR_AVRULE;
+	attr->keep = (ebitmap_cardinality(types) < db->attrs_expand_size) ? CIL_FALSE : CIL_TRUE;
+
+	attr_node = cil_create_and_insert_node(prev, CIL_TYPEATTRIBUTE, attr);
+	attrset_node = cil_create_and_insert_node(attr_node, CIL_TYPEATTRIBUTESET, attrset);
+
+	rc = cil_get_symtab(prev->parent, &symtab, CIL_SYM_TYPES);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+
+	rc = cil_symtab_insert(symtab, name, &attr->datum, attr_node);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+
+	db->num_types_and_attrs++;
+
+	*d = &attr->datum;
+
+	return SEPOL_OK;
+
+exit:
+	if (attrset_node) {
+		prev->next = attrset_node->next;
+		cil_destroy_typeattribute(attr_node->data);
+		free(attr_node);
+		cil_destroy_typeattributeset(attrset_node->data);
+		free(attrset_node);
+	}
+	return rc;
+}
+
+static int cil_create_attribute_d1_and_not_d2(struct cil_db *db, struct cil_tree_node *prev, struct cil_symtab_datum *d1, struct cil_symtab_datum *d2, struct cil_symtab_datum **d3)
+{
+	struct cil_list *str_expr;
+	struct cil_list *datum_expr;
+	ebitmap_t *types;
+	int rc;
+
+	*d3 = NULL;
+
+	if (d1 == d2) {
+		return SEPOL_OK;
+	}
+
+	str_expr = cil_create_andnot_expr_list(CIL_STRING, d1->fqn, d2->fqn);
+	datum_expr = cil_create_andnot_expr_list(CIL_DATUM, d1, d2);
+
+	types = cil_malloc(sizeof(*types));
+	rc = cil_datums_andnot(types, d1, d2);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+	if (ebitmap_is_empty(types)) {
+		rc = SEPOL_OK;
+		goto exit;
+	}
+
+	if (ebitmap_cardinality(types) == 1) {
+		unsigned i = ebitmap_highest_set_bit(types);
+		*d3 = DATUM(db->val_to_type[i]);
+		ebitmap_destroy(types);
+		rc = SEPOL_OK;
+		goto exit;
+	}
+
+	return cil_create_and_insert_attribute_and_set(db, prev, str_expr, datum_expr, types, d3);
+
+exit:
+	cil_list_destroy(&str_expr, CIL_FALSE);
+	cil_list_destroy(&datum_expr, CIL_FALSE);
+	free(types);
+	return rc;
+}
+
+static int cil_create_attribute_d1_and_d2(struct cil_db *db, struct cil_tree_node *prev, struct cil_symtab_datum *d1, struct cil_symtab_datum *d2, struct cil_symtab_datum **d3)
+{
+	struct cil_list *str_expr;
+	struct cil_list *datum_expr;
+	ebitmap_t *types;
+	int rc;
+
+	if (d1 == d2) {
+		*d3 = d1;
+		return SEPOL_OK;
+	}
+
+	*d3 = NULL;
+
+	str_expr = cil_create_and_expr_list(CIL_STRING, d1->fqn, d2->fqn);
+	datum_expr = cil_create_and_expr_list(CIL_DATUM, d1, d2);
+
+	types = cil_malloc(sizeof(*types));
+	rc = cil_datums_and(types, d1, d2);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+	if (ebitmap_is_empty(types)) {
+		rc = SEPOL_OK;
+		goto exit;
+	}
+
+	if (ebitmap_cardinality(types) == 1) {
+		unsigned i = ebitmap_highest_set_bit(types);
+		*d3 = DATUM(db->val_to_type[i]);
+		ebitmap_destroy(types);
+		rc = SEPOL_OK;
+		goto exit;
+	}
+
+	rc = cil_create_and_insert_attribute_and_set(db, prev, str_expr, datum_expr, types, d3);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+
+	return SEPOL_OK;
+
+exit:
+	cil_list_destroy(&str_expr, CIL_FALSE);
+	cil_list_destroy(&datum_expr, CIL_FALSE);
+	free(types);
+	return rc;
+}
+
+static struct cil_avrule *cil_create_avrule(struct cil_symtab_datum *src, struct cil_symtab_datum *tgt, struct cil_list *classperms)
+{
+	struct cil_avrule *new;
+
+	cil_avrule_init(&new);
+	new->is_extended = CIL_FALSE;
+	new->rule_kind = CIL_AVRULE_ALLOWED;
+	new->src_str = src->name;
+	new->src = src;
+	new->tgt_str = tgt->name;
+	new->tgt = tgt;
+	new->perms.classperms = classperms;
+
+	return new;
+}
+
+static struct cil_tree_node *cil_create_and_add_avrule(struct cil_tree_node *curr, struct cil_symtab_datum *src, struct cil_symtab_datum *tgt, struct cil_list *classperms)
+{
+	struct cil_avrule *new_avrule;
+
+	new_avrule = cil_create_avrule(src, tgt, classperms);
+	return cil_create_and_insert_node(curr, CIL_AVRULE, new_avrule);
+}
+
+static int cil_remove_permissions_from_rule(struct cil_db *db, struct cil_tree_node *allow_node, const struct cil_tree_node *deny_node)
+{
+	struct cil_avrule *allow_rule = allow_node->data;
+	struct cil_deny_rule *deny_rule = deny_node->data;
+	struct cil_symtab_datum *s1 = allow_rule->src;
+	struct cil_symtab_datum *t1 = allow_rule->tgt;
+	struct cil_list *p1 = allow_rule->perms.classperms;
+	struct cil_symtab_datum *s2 = deny_rule->src;
+	struct cil_symtab_datum *t2 = deny_rule->tgt;
+	struct cil_list *p2 = deny_rule->classperms;
+	struct cil_list *p3 = NULL;
+	struct cil_tree_node *curr = allow_node;
+	int rc;
+
+	cil_classperms_list_andnot(&p3, p1, p2);
+
+	if (!cil_list_is_empty(p3)) {;
+		curr = cil_create_and_add_avrule(curr, s1, t1, p3);
+	} else {
+		cil_destroy_classperms_list(&p3);
+		p3 = NULL;
+	}
+
+	if (FLAVOR(s1) == CIL_TYPEATTRIBUTE) {
+		struct cil_symtab_datum *s3 = NULL;
+		struct cil_symtab_datum *d = NULL;
+		struct cil_list *cp = NULL;
+		if (t1->fqn == CIL_KEY_SELF && t2->fqn != CIL_KEY_SELF) {
+			/* S3 = S1 and not (S2 and T2) */
+			rc = cil_create_attribute_d1_and_d2(db, NODE(s2), s2, t2, &d);
+			if (rc != SEPOL_OK) {
+				goto exit;
+			}
+			if (d) {
+				rc = cil_create_attribute_d1_and_not_d2(db, NODE(s1), s1, d, &s3);
+				if (rc != SEPOL_OK) {
+					goto exit;
+				}
+			}
+		} else if (t1->fqn != CIL_KEY_SELF && t2->fqn == CIL_KEY_SELF) {
+			/* S3 = S1 and not (T1 and S2) */
+			rc = cil_create_attribute_d1_and_d2(db, NODE(t1), t1, s2, &d);
+			if (rc != SEPOL_OK) {
+				goto exit;
+			}
+			if (d) {
+				rc = cil_create_attribute_d1_and_not_d2(db, NODE(s1), s1, d, &s3);
+				if (rc != SEPOL_OK) {
+					goto exit;
+				}
+			}
+		} else {
+			/* S3 = S1 and not S2 */
+			rc = cil_create_attribute_d1_and_not_d2(db, NODE(s1), s1, s2, &s3);
+			if (rc != SEPOL_OK) {
+				goto exit;
+			}
+		}
+		if (s3) {
+			cil_classperms_list_copy(&cp, p1);
+			curr = cil_create_and_add_avrule(curr, s3, t1, cp);
+		}
+	}
+
+	if (t1->fqn != CIL_KEY_SELF && FLAVOR(t1) == CIL_TYPEATTRIBUTE) {
+		struct cil_symtab_datum *t3 = NULL;
+		struct cil_symtab_datum *d = NULL;
+		struct cil_list *cp = NULL;
+		if (t2->fqn == CIL_KEY_SELF) {
+			/* T3 = T1 and not (S1 AND S2) */
+			rc = cil_create_attribute_d1_and_d2(db, NODE(s1), s1, s2, &d);
+			if (rc != SEPOL_OK) {
+				goto exit;
+			}
+			if (d) {
+				rc = cil_create_attribute_d1_and_not_d2(db, NODE(t1), t1, d, &t3);
+				if (rc != SEPOL_OK) {
+					goto exit;
+				}
+			}
+		} else {
+			/* S3 = T1 and not T2 */
+			rc = cil_create_attribute_d1_and_not_d2(db, NODE(t1), t1, t2, &t3);
+			if (rc != SEPOL_OK) {
+				goto exit;
+			}
+		}
+		if (t3) {
+			cil_classperms_list_copy(&cp, p1);
+			curr = cil_create_and_add_avrule(curr, s1, t3, cp);
+		}
+
+		if (FLAVOR(s1) == CIL_TYPEATTRIBUTE && FLAVOR(s2) == CIL_TYPEATTRIBUTE &&
+			t2->fqn == CIL_KEY_SELF) {
+			/* Really special case */
+			/* FLAVOR(t1) == CIL_TYPEATTRIBUTE */
+			/* SX = S1 and S2 and T1 */
+			struct cil_symtab_datum *sx = NULL;
+			struct cil_typeattribute *sx_attr = NULL;
+			
+			d = NULL;
+			cp = NULL;
+
+			rc = cil_create_attribute_d1_and_d2(db, NODE(s2), s2, t1, &d);
+			if (rc != SEPOL_OK) {
+				goto exit;
+			}
+			if (d) {
+				rc = cil_create_attribute_d1_and_d2(db, NODE(s1), s1, d, &sx);
+				if (rc != SEPOL_OK) {
+					goto exit;
+				}
+				sx_attr = (struct cil_typeattribute *)sx;
+			}
+			if (sx && ebitmap_cardinality(sx_attr->types) > 1) {
+				struct cil_symtab_datum *s = NULL;
+				struct cil_symtab_datum *t = NULL;
+				ebitmap_node_t *n;
+				unsigned i;
+				ebitmap_for_each_positive_bit(sx_attr->types, n, i) {
+					s = DATUM(db->val_to_type[i]);
+					rc = cil_create_attribute_d1_and_not_d2(db, NODE(sx), sx, s, &t);
+					if (rc != SEPOL_OK) {
+						goto exit;
+					}
+					if (t) {
+						cil_classperms_list_copy(&cp, p1);
+						curr = cil_create_and_add_avrule(curr, s, t, cp);
+					}
+				}
+			}
+		}
+	}
+
+exit:
+	return rc;
+}
+
+static int cil_find_matching_allow_rules(struct cil_list *matching, struct cil_tree_node *start, struct cil_tree_node *deny_node)
+{
+	struct cil_deny_rule *deny_rule = deny_node->data;
+	struct cil_avrule target;
+
+	target.rule_kind = CIL_AVRULE_ALLOWED;
+	target.is_extended = CIL_FALSE;
+	target.src = deny_rule->src;
+	target.tgt = deny_rule->tgt;
+	target.perms.classperms = deny_rule->classperms;
+
+	return cil_find_matching_avrule_in_ast(start, CIL_AVRULE, &target, matching, CIL_FALSE);
+}
+
+static int cil_process_deny_rule(struct cil_db *db, struct cil_tree_node *start, struct cil_tree_node *deny_node)
+{
+	struct cil_list *matching;
+	struct cil_list_item *item;
+	int rc;
+
+	cil_list_init(&matching, CIL_NODE);
+
+	rc = cil_find_matching_allow_rules(matching, start, deny_node);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+
+	cil_list_for_each(item, matching) {
+		struct cil_tree_node *allow_node = item->data;
+		rc = cil_remove_permissions_from_rule(db, allow_node, deny_node);
+		cil_tree_remove_node(allow_node);
+		cil_tree_node_destroy(&allow_node);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+
+	}
+
+exit:
+	cil_list_destroy(&matching, CIL_FALSE);
+	return rc;
+}
+
+static int cil_process_deny_rules(struct cil_db *db, struct cil_tree_node *start, struct cil_list *deny_rules)
+{
+	struct cil_list_item *item;
+	int rc;
+
+	cil_list_for_each(item, deny_rules) {
+		struct cil_tree_node *deny_node = item->data;
+		rc = cil_process_deny_rule(db, start, deny_node);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+		cil_tree_remove_node(deny_node);
+		cil_tree_node_destroy(&deny_node);
+	}
+
+exit:
+	return rc;
+}
+
+static int __cil_find_deny_rules(struct cil_tree_node *node,  uint32_t *finished, void *extra_args)
+{
+	struct cil_list *deny_rules = extra_args;
+
+	if (node->flavor == CIL_BLOCK) {
+		struct cil_block *block = node->data;
+		if (block->is_abstract == CIL_TRUE) {
+			*finished = CIL_TREE_SKIP_HEAD;
+		}
+	} else if (node->flavor == CIL_MACRO) {
+		*finished = CIL_TREE_SKIP_HEAD;
+	} else if (node->flavor == CIL_DENY_RULE) {
+		cil_list_append(deny_rules, CIL_DENY_RULE, node);
+	}
+	return SEPOL_OK;
+}
+
+int cil_process_deny_rules_in_ast(struct cil_db *db)
+{
+	struct cil_tree_node *start;
+	struct cil_list *deny_rules;
+	int rc = SEPOL_ERR;
+
+	cil_list_init(&deny_rules, CIL_DENY_RULE);
+
+	if (!db) {
+		cil_log(CIL_ERR, "No CIL db provided to process deny rules\n");
+		goto exit;
+	}
+
+	start = db->ast->root;
+	rc = cil_tree_walk(start, __cil_find_deny_rules, NULL, NULL, deny_rules);
+	if (rc != SEPOL_OK) {
+		cil_log(CIL_ERR, "An error occurred while getting deny rules\n");
+		goto exit;
+	}
+
+	rc = cil_process_deny_rules(db, start, deny_rules);
+	if (rc != SEPOL_OK) {
+		cil_log(CIL_ERR, "An error occurred while processing deny rules\n");
+		goto exit;
+	}
+
+exit:
+	cil_list_destroy(&deny_rules, CIL_FALSE);
+	return rc;
+}
diff --git a/libsepol/cil/src/cil_deny.h b/libsepol/cil/src/cil_deny.h
new file mode 100644
index 00000000..4a9e92d1
--- /dev/null
+++ b/libsepol/cil/src/cil_deny.h
@@ -0,0 +1,34 @@ 
+/*
+ * This file is public domain software, i.e. not copyrighted.
+ *
+ * Warranty Exclusion
+ * ------------------
+ * You agree that this software is a non-commercially developed program
+ * that may contain "bugs" (as that term is used in the industry) and
+ * that it may not function as intended. The software is licensed
+ * "as is". NSA makes no, and hereby expressly disclaims all, warranties,
+ * express, implied, statutory, or otherwise with respect to the software,
+ * including noninfringement and the implied warranties of merchantability
+ * and fitness for a particular purpose.
+ *
+ * Limitation of Liability
+ *-----------------------
+ * In no event will NSA be liable for any damages, including loss of data,
+ * lost profits, cost of cover, or other special, incidental, consequential,
+ * direct or indirect damages arising from the software or the use thereof,
+ * however caused and on any theory of liability. This limitation will apply
+ * even if NSA has been advised of the possibility of such damage. You
+ * acknowledge that this is a reasonable allocation of risk.
+ *
+ * Original author: James Carter
+ */
+
+#ifndef CIL_DENY_H_
+#define CIL_DENY_H_
+
+int cil_classperms_list_match(const struct cil_list *cpl1, const struct cil_list *cpl2);
+void cil_classperms_list_copy(struct cil_list **new, const struct cil_list *old);
+void cil_classperms_list_andnot(struct cil_list **result, const struct cil_list *cpl1, const struct cil_list *cpl2);
+int cil_process_deny_rules_in_ast(struct cil_db *db);
+
+#endif /* CIL_DENY_H_ */
diff --git a/libsepol/cil/src/cil_post.c b/libsepol/cil/src/cil_post.c
index 11e572e2..fb7eec2d 100644
--- a/libsepol/cil/src/cil_post.c
+++ b/libsepol/cil/src/cil_post.c
@@ -47,6 +47,7 @@ 
 #include "cil_policy.h"
 #include "cil_verify.h"
 #include "cil_symtab.h"
+#include "cil_deny.h"
 
 #define GEN_REQUIRE_ATTR "cil_gen_require" /* Also in libsepol/src/module_to_cil.c */
 #define TYPEATTR_INFIX "_typeattr_"        /* Also in libsepol/src/module_to_cil.c */
@@ -2551,6 +2552,12 @@  int cil_post_process(struct cil_db *db)
 		goto exit;
 	}
 
+	rc = cil_process_deny_rules_in_ast(db);
+	if (rc != SEPOL_OK) {
+		cil_log(CIL_ERR, "Failed to process deny rules\n");
+		goto exit;
+	}
+
 	rc = cil_post_verify(db);
 	if (rc != SEPOL_OK) {
 		cil_log(CIL_ERR, "Failed to verify cil database\n");