diff mbox series

[v3,6/8] libsepol/cil: add support for segregate attributes

Message ID 20220721150515.19843-6-cgzones@googlemail.com (mailing list archive)
State New, archived
Headers show
Series [v3,1/8] libsepol: refactor ebitmap conversion in link.c | expand

Commit Message

Christian Göttsche July 21, 2022, 3:05 p.m. UTC
Support the compile time constraint with the following syntax:

    (segregateattributes (attr1 attr2 [...]))

and reports like:

    ...
    Qualifying Names
    Compile post process
    Building policy binary
    Checking Neverallows
    Checking Segregate Attributes
    Segregate Attributes violation, type test_type associated with attributes attr1 attr2
    Checking User Bounds
    Checking Role Bounds
    Checking Type Bounds
    Failed to generate binary
    Failed to build policydb

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/cil/src/cil.c             | 17 +++++++
 libsepol/cil/src/cil_binary.c      | 75 ++++++++++++++++++++++++++++++
 libsepol/cil/src/cil_build_ast.c   | 58 +++++++++++++++++++++++
 libsepol/cil/src/cil_build_ast.h   |  2 +
 libsepol/cil/src/cil_copy_ast.c    | 18 +++++++
 libsepol/cil/src/cil_flavor.h      |  1 +
 libsepol/cil/src/cil_internal.h    |  8 ++++
 libsepol/cil/src/cil_policy.c      | 26 +++++++++++
 libsepol/cil/src/cil_reset_ast.c   |  8 ++++
 libsepol/cil/src/cil_resolve_ast.c | 38 +++++++++++++++
 libsepol/cil/src/cil_resolve_ast.h |  1 +
 libsepol/cil/src/cil_write_ast.c   | 11 +++++
 libsepol/src/kernel_to_cil.c       | 32 +++++++++++++
 secilc/docs/README.md              |  1 +
 secilc/docs/cil_type_statements.md | 50 ++++++++++++++++++++
 15 files changed, 346 insertions(+)

Comments

James Carter Aug. 8, 2022, 5:15 p.m. UTC | #1
On Thu, Jul 21, 2022 at 11:11 AM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Support the compile time constraint with the following syntax:
>
>     (segregateattributes (attr1 attr2 [...]))

Not a list. The vast majority of the cases are going to be that a type
can be either one or another.

(typesdisjoint attr1 attr2)

Keep it simple.

>
> and reports like:
>
>     ...
>     Qualifying Names
>     Compile post process
>     Building policy binary
>     Checking Neverallows
>     Checking Segregate Attributes
>     Segregate Attributes violation, type test_type associated with attributes attr1 attr2
>     Checking User Bounds
>     Checking Role Bounds
>     Checking Type Bounds
>     Failed to generate binary
>     Failed to build policydb
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
>  libsepol/cil/src/cil.c             | 17 +++++++
>  libsepol/cil/src/cil_binary.c      | 75 ++++++++++++++++++++++++++++++
>  libsepol/cil/src/cil_build_ast.c   | 58 +++++++++++++++++++++++
>  libsepol/cil/src/cil_build_ast.h   |  2 +
>  libsepol/cil/src/cil_copy_ast.c    | 18 +++++++
>  libsepol/cil/src/cil_flavor.h      |  1 +
>  libsepol/cil/src/cil_internal.h    |  8 ++++
>  libsepol/cil/src/cil_policy.c      | 26 +++++++++++
>  libsepol/cil/src/cil_reset_ast.c   |  8 ++++
>  libsepol/cil/src/cil_resolve_ast.c | 38 +++++++++++++++
>  libsepol/cil/src/cil_resolve_ast.h |  1 +
>  libsepol/cil/src/cil_write_ast.c   | 11 +++++
>  libsepol/src/kernel_to_cil.c       | 32 +++++++++++++
>  secilc/docs/README.md              |  1 +
>  secilc/docs/cil_type_statements.md | 50 ++++++++++++++++++++
>  15 files changed, 346 insertions(+)
>
> diff --git a/libsepol/cil/src/cil.c b/libsepol/cil/src/cil.c
> index 38edcf8e..cc6adb90 100644
> --- a/libsepol/cil/src/cil.c
> +++ b/libsepol/cil/src/cil.c
> @@ -225,6 +225,7 @@ char *CIL_KEY_SRC_CIL;
>  char *CIL_KEY_SRC_HLL_LMS;
>  char *CIL_KEY_SRC_HLL_LMX;
>  char *CIL_KEY_SRC_HLL_LME;
> +char *CIL_KEY_SEGREGATEATTRIBUTES;
>
>  static void cil_init_keys(void)
>  {
> @@ -394,6 +395,7 @@ static void cil_init_keys(void)
>         CIL_KEY_SRC_HLL_LMS = cil_strpool_add("lms");
>         CIL_KEY_SRC_HLL_LMX = cil_strpool_add("lmx");
>         CIL_KEY_SRC_HLL_LME = cil_strpool_add("lme");
> +       CIL_KEY_SEGREGATEATTRIBUTES = cil_strpool_add("segregateattributes");
>  }
>
>  void cil_db_init(struct cil_db **db)
> @@ -426,6 +428,7 @@ void cil_db_init(struct cil_db **db)
>         cil_list_init(&(*db)->userprefixes, CIL_LIST_ITEM);
>         cil_list_init(&(*db)->selinuxusers, CIL_LIST_ITEM);
>         cil_list_init(&(*db)->names, CIL_LIST_ITEM);
> +       cil_list_init(&(*db)->segregateattributes, CIL_LIST_ITEM);
>
>         cil_type_init(&(*db)->selftype);
>         (*db)->selftype->datum.name = CIL_KEY_SELF;
> @@ -481,6 +484,7 @@ void cil_db_destroy(struct cil_db **db)
>         cil_list_destroy(&(*db)->userprefixes, CIL_FALSE);
>         cil_list_destroy(&(*db)->selinuxusers, CIL_FALSE);
>         cil_list_destroy(&(*db)->names, CIL_TRUE);
> +       cil_list_destroy(&(*db)->segregateattributes, CIL_FALSE);
>
>         cil_destroy_type((*db)->selftype);
>
> @@ -1005,6 +1009,9 @@ void cil_destroy_data(void **data, enum cil_flavor flavor)
>         case CIL_SRC_INFO:
>                 cil_destroy_src_info(*data);
>                 break;
> +       case CIL_SEGREGATEATTRIBUTES:
> +               cil_destroy_segregateattributes(*data);
> +               break;
>         case CIL_OP:
>         case CIL_CONS_OPERAND:
>                 break;
> @@ -1413,6 +1420,8 @@ const char * cil_node_to_string(struct cil_tree_node *node)
>                 return CIL_KEY_CONS_H1;
>         case CIL_CONS_H2:
>                 return CIL_KEY_CONS_H2;
> +       case CIL_SEGREGATEATTRIBUTES:
> +               return CIL_KEY_SEGREGATEATTRIBUTES;
>
>         default:
>                 break;
> @@ -2904,3 +2913,11 @@ void cil_src_info_init(struct cil_src_info **info)
>         (*info)->hll_line = 0;
>         (*info)->path = NULL;
>  }
> +
> +void cil_segregateattributes_init(struct cil_segregateattributes **sattrs)
> +{
> +       *sattrs = cil_malloc(sizeof(**sattrs));
> +
> +       (*sattrs)->str_expr = NULL;
> +       (*sattrs)->datum_expr = NULL;
> +}
> diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c
> index 40615db2..0301d739 100644
> --- a/libsepol/cil/src/cil_binary.c
> +++ b/libsepol/cil/src/cil_binary.c
> @@ -3818,6 +3818,38 @@ exit:
>         return SEPOL_ERR;
>  }
>
> +static int cil_segregateattributes_to_policydb(policydb_t *pdb, const struct cil_segregateattributes *sattrs)
> +{
> +       segregate_attributes_rule_t *sattr;
> +       struct cil_list_item *curr;
> +       type_datum_t *sepol_type;
> +       int rc = SEPOL_ERR;
> +
> +       sattr = cil_malloc(sizeof(segregate_attributes_rule_t));
> +       ebitmap_init(&sattr->attrs);
> +
> +       cil_list_for_each(curr, sattrs->datum_expr) {
> +               rc = __cil_get_sepol_type_datum(pdb, DATUM(curr->data), &sepol_type);
> +               if (rc != SEPOL_OK) goto exit;
> +
> +               if (ebitmap_set_bit(&sattr->attrs, sepol_type->s.value - 1, 1)) {
> +                       goto exit;
> +               }
> +       }
> +
> +       sattr->next = pdb->segregate_attributes;
> +       pdb->segregate_attributes = sattr;
> +
> +       return SEPOL_OK;
> +
> +exit:
> +       if (sattr) {
> +               ebitmap_destroy(&sattr->attrs);
> +               free(sattr);
> +       }
> +       return rc;
> +}
> +
>  static int __cil_node_to_policydb(struct cil_tree_node *node, void *extra_args)
>  {
>         int rc = SEPOL_OK;
> @@ -3960,6 +3992,9 @@ static int __cil_node_to_policydb(struct cil_tree_node *node, void *extra_args)
>                 case CIL_DEFAULTRANGE:
>                         rc = cil_defaultrange_to_policydb(pdb, node->data);
>                         break;
> +               case CIL_SEGREGATEATTRIBUTES:
> +                       rc = cil_segregateattributes_to_policydb(pdb, node->data);
> +                       break;
>                 default:
>                         break;
>                 }
> @@ -4890,6 +4925,42 @@ exit:
>         return rc;
>  }
>
> +static int cil_check_segregateattributes(const policydb_t *pdb, int *violation)
> +{
> +       const segregate_attributes_rule_t *sattr;
> +
> +       for (sattr = pdb->segregate_attributes; sattr; sattr = sattr->next) {
> +               ebitmap_node_t *first_node;
> +               unsigned int first_bit;
> +
> +               ebitmap_for_each_positive_bit(&sattr->attrs, first_node, first_bit) {
> +                       ebitmap_node_t *second_node;
> +                       unsigned int second_bit;
> +
> +                       ebitmap_for_each_positive_bit_after(&sattr->attrs, second_node, second_bit, first_node, first_bit) {
> +                               ebitmap_t attr_union;
> +                               ebitmap_node_t *type_node;
> +                               unsigned int type_bit;
> +
> +                               if (ebitmap_and(&attr_union, &pdb->attr_type_map[first_bit], &pdb->attr_type_map[second_bit]))
> +                                       return SEPOL_ERR;
> +
> +                               ebitmap_for_each_positive_bit(&attr_union, type_node, type_bit) {
> +                                       cil_log(CIL_ERR, "Segregate Attributes violation, type %s associated with attributes %s and %s\n",
> +                                                        pdb->p_type_val_to_name[type_bit],
> +                                                        pdb->p_type_val_to_name[first_bit],
> +                                                        pdb->p_type_val_to_name[second_bit]);
> +                                       *violation = CIL_TRUE;

It needs to report the file and linenumber of the violation. This is
the case for checkpolicy as well. There is a reason that we carry that
information for neverallow rules.

Thanks,
Jim


> +                               }
> +
> +                               ebitmap_destroy(&attr_union);
> +                       }
> +               }
> +       }
> +
> +       return SEPOL_OK;
> +}
> +
>  static struct cil_list *cil_classperms_from_sepol(policydb_t *pdb, uint16_t class, uint32_t data, struct cil_class *class_value_to_cil[], struct cil_perm **perm_value_to_cil[])
>  {
>         struct cil_classperms *cp;
> @@ -5160,6 +5231,10 @@ int cil_binary_create_allocated_pdb(const struct cil_db *db, sepol_policydb_t *p
>                 rc = cil_check_neverallows(db, pdb, neverallows, &violation);
>                 if (rc != SEPOL_OK) goto exit;
>
> +               cil_log(CIL_INFO, "Checking Segregate Attributes\n");
> +               rc = cil_check_segregateattributes(pdb, &violation);
> +               if (rc != SEPOL_OK) goto exit;
> +
>                 cil_log(CIL_INFO, "Checking User Bounds\n");
>                 rc = bounds_check_users(NULL, pdb);
>                 if (rc) {
> diff --git a/libsepol/cil/src/cil_build_ast.c b/libsepol/cil/src/cil_build_ast.c
> index 4177c9f6..611aade8 100644
> --- a/libsepol/cil/src/cil_build_ast.c
> +++ b/libsepol/cil/src/cil_build_ast.c
> @@ -6164,6 +6164,62 @@ void cil_destroy_src_info(struct cil_src_info *info)
>         free(info);
>  }
>
> +int cil_gen_segregateattributes(struct cil_db *db, struct cil_tree_node *parse_current, struct cil_tree_node *ast_node)
> +{
> +       enum cil_syntax syntax[] = {
> +               CIL_SYN_STRING,
> +               CIL_SYN_LIST,
> +               CIL_SYN_END
> +       };
> +       size_t syntax_len = sizeof(syntax)/sizeof(*syntax);
> +       struct cil_segregateattributes *sattrs = NULL;
> +       int rc = SEPOL_ERR;
> +
> +       if (db == NULL || parse_current == NULL || ast_node == NULL) {
> +               goto exit;
> +       }
> +
> +       rc = __cil_verify_syntax(parse_current, syntax, syntax_len);
> +       if (rc != SEPOL_OK) {
> +               goto exit;
> +       }
> +
> +       cil_segregateattributes_init(&sattrs);
> +
> +       rc = cil_gen_expr(parse_current->next, CIL_TYPEATTRIBUTE, &sattrs->str_expr);
> +       if (rc != SEPOL_OK) {
> +               goto exit;
> +       }
> +
> +       /* require at least two attributes */
> +       if (sattrs->str_expr->head == sattrs->str_expr->tail) {
> +               rc = SEPOL_ERR;
> +               goto exit;
> +       }
> +
> +       ast_node->data = sattrs;
> +       ast_node->flavor = CIL_SEGREGATEATTRIBUTES;
> +
> +       return SEPOL_OK;
> +
> +exit:
> +       cil_tree_log(parse_current, CIL_ERR, "Bad segregate attributes declaration");
> +       cil_destroy_segregateattributes(sattrs);
> +       return rc;
> +}
> +
> +void cil_destroy_segregateattributes(struct cil_segregateattributes *sattrs)
> +{
> +       if (sattrs == NULL) {
> +               return;
> +       }
> +
> +       cil_list_destroy(&sattrs->str_expr, CIL_TRUE);
> +       cil_list_destroy(&sattrs->datum_expr, CIL_FALSE);
> +
> +       free(sattrs);
> +}
> +
>  static int check_for_illegal_statement(struct cil_tree_node *parse_current, struct cil_args_build *args)
>  {
>         if (args->tunif != NULL) {
> @@ -6455,6 +6511,8 @@ static struct cil_tree_node * parse_statement(struct cil_db *db, struct cil_tree
>                 rc = cil_gen_mls(parse_current, new_ast_node);
>         } else if (parse_current->data == CIL_KEY_SRC_INFO) {
>                 rc = cil_gen_src_info(parse_current, new_ast_node);
> +       } else if (parse_current->data == CIL_KEY_SEGREGATEATTRIBUTES) {
> +               rc = cil_gen_segregateattributes(db, parse_current, new_ast_node);
>         } else {
>                 cil_log(CIL_ERR, "Error: Unknown keyword %s\n", (char *)parse_current->data);
>                 rc = SEPOL_ERR;
> diff --git a/libsepol/cil/src/cil_build_ast.h b/libsepol/cil/src/cil_build_ast.h
> index fd9053ce..d815a22f 100644
> --- a/libsepol/cil/src/cil_build_ast.h
> +++ b/libsepol/cil/src/cil_build_ast.h
> @@ -225,6 +225,8 @@ int cil_gen_defaultrange(struct cil_tree_node *parse_current, struct cil_tree_no
>  void cil_destroy_defaultrange(struct cil_defaultrange *def);
>  int cil_gen_src_info(struct cil_tree_node *parse_current, struct cil_tree_node *ast_node);
>  void cil_destroy_src_info(struct cil_src_info *info);
> +int cil_gen_segregateattributes(struct cil_db *db, struct cil_tree_node *parse_current, struct cil_tree_node *ast_node);
> +void cil_destroy_segregateattributes(struct cil_segregateattributes *sattrs);
>
>  int cil_fill_cats(struct cil_tree_node *curr, struct cil_cats **cats);
>  void cil_destroy_cats(struct cil_cats *cats);
> diff --git a/libsepol/cil/src/cil_copy_ast.c b/libsepol/cil/src/cil_copy_ast.c
> index 17f05021..e0f3ba4f 100644
> --- a/libsepol/cil/src/cil_copy_ast.c
> +++ b/libsepol/cil/src/cil_copy_ast.c
> @@ -1697,6 +1697,21 @@ static int cil_copy_src_info(__attribute__((unused)) struct cil_db *db, void *da
>         return SEPOL_OK;
>  }
>
> +static int cil_copy_segregateattributes(__attribute__((unused)) struct cil_db *db, void *data, void **copy, __attribute__((unused)) symtab_t *symtab)
> +{
> +       struct cil_segregateattributes *orig = data;
> +       struct cil_segregateattributes *new = NULL;
> +
> +       cil_segregateattributes_init(&new);
> +
> +       cil_copy_expr(db, orig->str_expr, &new->str_expr);
> +       cil_copy_expr(db, orig->datum_expr, &new->datum_expr);
> +
> +       *copy = new;
> +
> +       return SEPOL_OK;
> +}
> +
>  static int __cil_copy_node_helper(struct cil_tree_node *orig, uint32_t *finished, void *extra_args)
>  {
>         int rc = SEPOL_ERR;
> @@ -1990,6 +2005,9 @@ static int __cil_copy_node_helper(struct cil_tree_node *orig, uint32_t *finished
>         case CIL_SRC_INFO:
>                 copy_func = &cil_copy_src_info;
>                 break;
> +       case CIL_SEGREGATEATTRIBUTES:
> +               copy_func = &cil_copy_segregateattributes;
> +               break;
>         default:
>                 goto exit;
>         }
> diff --git a/libsepol/cil/src/cil_flavor.h b/libsepol/cil/src/cil_flavor.h
> index c2f0cee7..ffbd5877 100644
> --- a/libsepol/cil/src/cil_flavor.h
> +++ b/libsepol/cil/src/cil_flavor.h
> @@ -115,6 +115,7 @@ enum cil_flavor {
>         CIL_SRC_INFO,
>         CIL_IBPKEYCON,
>         CIL_IBENDPORTCON,
> +       CIL_SEGREGATEATTRIBUTES,
>
>  /*
>   *          boolean  constraint  set  catset
> diff --git a/libsepol/cil/src/cil_internal.h b/libsepol/cil/src/cil_internal.h
> index a7604762..e22c2f87 100644
> --- a/libsepol/cil/src/cil_internal.h
> +++ b/libsepol/cil/src/cil_internal.h
> @@ -242,6 +242,7 @@ extern char *CIL_KEY_SRC_CIL;
>  extern char *CIL_KEY_SRC_HLL_LMS;
>  extern char *CIL_KEY_SRC_HLL_LMX;
>  extern char *CIL_KEY_SRC_HLL_LME;
> +extern char *CIL_KEY_SEGREGATEATTRIBUTES;
>
>  /*
>         Symbol Table Array Indices
> @@ -309,6 +310,7 @@ struct cil_db {
>         struct cil_list *userprefixes;
>         struct cil_list *selinuxusers;
>         struct cil_list *names;
> +       struct cil_list *segregateattributes;
>         int num_types_and_attrs;
>         int num_classes;
>         int num_cats;
> @@ -975,6 +977,11 @@ struct cil_src_info {
>         char *path;
>  };
>
> +struct cil_segregateattributes {
> +       struct cil_list *str_expr;
> +       struct cil_list *datum_expr;
> +};
> +
>  void cil_db_init(struct cil_db **db);
>  void cil_db_destroy(struct cil_db **db);
>
> @@ -1085,5 +1092,6 @@ void cil_mls_init(struct cil_mls **mls);
>  void cil_src_info_init(struct cil_src_info **info);
>  void cil_userattribute_init(struct cil_userattribute **attribute);
>  void cil_userattributeset_init(struct cil_userattributeset **attrset);
> +void cil_segregateattributes_init(struct cil_segregateattributes **sattrs);
>
>  #endif
> diff --git a/libsepol/cil/src/cil_policy.c b/libsepol/cil/src/cil_policy.c
> index 7c543c47..36f6780d 100644
> --- a/libsepol/cil/src/cil_policy.c
> +++ b/libsepol/cil/src/cil_policy.c
> @@ -69,6 +69,7 @@ enum cil_statement_list {
>         CIL_LIST_USER,
>         CIL_LIST_CONSTRAINT,
>         CIL_LIST_VALIDATETRANS,
> +       CIL_LIST_SEGREGATEATTRIBUTES,
>         CIL_LIST_NUM_LISTS
>  };
>
> @@ -168,6 +169,9 @@ static int __cil_gather_statements_helper(struct cil_tree_node *node, uint32_t *
>         case CIL_VALIDATETRANS:
>                 kind = CIL_LIST_VALIDATETRANS;
>                 break;
> +       case CIL_SEGREGATEATTRIBUTES:
> +               kind = CIL_LIST_SEGREGATEATTRIBUTES;
> +               break;
>         default:
>                 break;
>         }
> @@ -1911,6 +1915,27 @@ static void cil_devicetreecons_to_policy(FILE *out, struct cil_sort *devicetreec
>         }
>  }
>
> +static void cil_segregateattributes_to_policy(FILE *out, struct cil_list *sattrs_list)
> +{
> +       struct cil_list_item *curr_sattrs, *curr_attr;
> +       struct cil_segregateattributes *sattrs;
> +       int first = 1;
> +
> +       cil_list_for_each(curr_sattrs, sattrs_list) {
> +               sattrs = curr_sattrs->data;
> +               fprintf(out, "segregate_attriutes ");
> +               cil_list_for_each(curr_attr, sattrs->datum_expr) {
> +                       if (!first) {
> +                               first = 0;
> +                       } else {
> +                               fprintf(out, ", ");
> +                       }
> +                       fprintf(out, "%s", DATUM(curr_attr->data)->fqn);
> +               }
> +               fprintf(out, ";\n");
> +       }
> +}
> +
>  void cil_gen_policy(FILE *out, struct cil_db *db)
>  {
>         unsigned i;
> @@ -1956,6 +1981,7 @@ void cil_gen_policy(FILE *out, struct cil_db *db)
>         cil_typebounds_to_policy(out, lists[CIL_LIST_TYPE]);
>         cil_typeattributes_to_policy(out, lists[CIL_LIST_TYPE], lists[CIL_LIST_TYPEATTRIBUTE]);
>         cil_te_rules_to_policy(out, head, db->mls);
> +       cil_segregateattributes_to_policy(out, db->segregateattributes);
>
>         cil_roles_to_policy(out, lists[CIL_LIST_ROLE]);
>         cil_role_types_to_policy(out, lists[CIL_LIST_ROLE], lists[CIL_LIST_TYPE]);
> diff --git a/libsepol/cil/src/cil_reset_ast.c b/libsepol/cil/src/cil_reset_ast.c
> index 0864d7ef..c5ac83c8 100644
> --- a/libsepol/cil/src/cil_reset_ast.c
> +++ b/libsepol/cil/src/cil_reset_ast.c
> @@ -475,6 +475,11 @@ static void cil_reset_booleanif(struct cil_booleanif *bif)
>         cil_list_destroy(&bif->datum_expr, CIL_FALSE);
>  }
>
> +static void cil_reset_segregateattributes(struct cil_segregateattributes *sattrs)
> +{
> +       cil_list_destroy(&sattrs->datum_expr, CIL_FALSE);
> +}
> +
>  static int __cil_reset_node(struct cil_tree_node *node,  __attribute__((unused)) uint32_t *finished, __attribute__((unused)) void *extra_args)
>  {
>         switch (node->flavor) {
> @@ -630,6 +635,9 @@ static int __cil_reset_node(struct cil_tree_node *node,  __attribute__((unused))
>         case CIL_BOOLEANIF:
>                 cil_reset_booleanif(node->data);
>                 break;
> +       case CIL_SEGREGATEATTRIBUTES:
> +               cil_reset_segregateattributes(node->data);
> +               break;
>         case CIL_TUNABLEIF:
>         case CIL_CALL:
>                 break; /* Not effected by optional block disabling */
> diff --git a/libsepol/cil/src/cil_resolve_ast.c b/libsepol/cil/src/cil_resolve_ast.c
> index f5e22c97..36a96199 100644
> --- a/libsepol/cil/src/cil_resolve_ast.c
> +++ b/libsepol/cil/src/cil_resolve_ast.c
> @@ -3265,6 +3265,7 @@ int cil_resolve_expr(enum cil_flavor expr_type, struct cil_list *str_expr, struc
>                 sym_index = CIL_SYM_TUNABLES;
>                 break;
>         case CIL_TYPE:
> +       case CIL_TYPEATTRIBUTE:
>                 sym_index = CIL_SYM_TYPES;
>                 break;
>         case CIL_ROLE:
> @@ -3312,6 +3313,13 @@ int cil_resolve_expr(enum cil_flavor expr_type, struct cil_list *str_expr, struc
>                         } else {
>                                 if (sym_index == CIL_SYM_TYPES && (expr_type == CIL_CONSTRAIN || expr_type == CIL_VALIDATETRANS)) {
>                                         cil_type_used(res_datum, CIL_ATTR_CONSTRAINT);
> +                               } else if (expr_type == CIL_SEGREGATEATTRIBUTES) {
> +                                       if (FLAVOR(res_datum) != CIL_TYPEATTRIBUTE) {
> +                                               cil_tree_log(parent, CIL_ERR, "Type or type alias not supported in segregate attributes declaration");
> +                                               rc = SEPOL_ERR;
> +                                               goto exit;
> +                                       }
> +                                       cil_type_used(res_datum, CIL_ATTR_NEVERALLOW);
>                                 }
>                                 cil_list_append(*datum_expr, CIL_DATUM, res_datum);
>                         }
> @@ -3508,6 +3516,33 @@ exit:
>         return rc;
>  }
>
> +int cil_resolve_segregateattributes(struct cil_tree_node *current, void *extra_args)
> +{
> +       struct cil_segregateattributes *sattrs = current->data;
> +       struct cil_list_item *first, *second;
> +       int rc;
> +
> +       rc = cil_resolve_expr(CIL_SEGREGATEATTRIBUTES, sattrs->str_expr, &sattrs->datum_expr, current, extra_args);
> +       if (rc != SEPOL_OK) {
> +               goto exit;
> +       }
> +
> +       cil_list_for_each(first, sattrs->datum_expr) {
> +               for (second = first->next; second; second = second->next) {
> +                       if (first->data == second->data) {
> +                               cil_tree_log(current, CIL_ERR, "Repeated attribute in segregate attributes declaration");
> +                               rc = SEPOL_ERR;
> +                               goto exit;
> +                       }
> +               }
> +       }
> +
> +       return SEPOL_OK;
> +
> +exit:
> +       return rc;
> +}
> +
>  /*
>   * Degenerate inheritance leads to exponential growth of the policy
>   * It can take many forms, but here is one example.
> @@ -3888,6 +3923,9 @@ static int __cil_resolve_ast_node(struct cil_tree_node *node, void *extra_args)
>                 case CIL_USERATTRIBUTESET:
>                         rc = cil_resolve_userattributeset(node, args);
>                         break;
> +               case CIL_SEGREGATEATTRIBUTES:
> +                       rc = cil_resolve_segregateattributes(node, args);
> +                       break;
>                 default:
>                         break;
>                 }
> diff --git a/libsepol/cil/src/cil_resolve_ast.h b/libsepol/cil/src/cil_resolve_ast.h
> index 1d971fd6..31594954 100644
> --- a/libsepol/cil/src/cil_resolve_ast.h
> +++ b/libsepol/cil/src/cil_resolve_ast.h
> @@ -96,6 +96,7 @@ int cil_resolve_expr(enum cil_flavor expr_type, struct cil_list *str_expr, struc
>  int cil_resolve_boolif(struct cil_tree_node *current, void *extra_args);
>  int cil_evaluate_expr(struct cil_list *datum_expr, uint16_t *result);
>  int cil_resolve_tunif(struct cil_tree_node *current, void *extra_args);
> +int cil_resolve_segregateattributes(struct cil_tree_node *current, void *extra_args);
>
>  int cil_resolve_ast(struct cil_db *db, struct cil_tree_node *current);
>  int cil_resolve_name(struct cil_tree_node *ast_node, char *name, enum cil_sym_index sym_index, void *extra_args, struct cil_symtab_datum **datum);
> diff --git a/libsepol/cil/src/cil_write_ast.c b/libsepol/cil/src/cil_write_ast.c
> index b75784ef..d0fb555b 100644
> --- a/libsepol/cil/src/cil_write_ast.c
> +++ b/libsepol/cil/src/cil_write_ast.c
> @@ -1474,7 +1474,18 @@ void cil_write_ast_node(FILE *out, struct cil_tree_node *node)
>                 fprintf(out, "(ipaddr %s %s)\n", datum_to_str(&ipaddr->datum), buf);
>                 break;
>         }
> +       case CIL_SEGREGATEATTRIBUTES: {
> +               struct cil_segregateattributes *sattrs = node->data;
> +               fprintf(out, "(segregateattributes ");
> +               if (sattrs->datum_expr)
> +                       write_expr(out, sattrs->datum_expr);
> +               else
> +                       write_expr(out, sattrs->str_expr);
> +               fprintf(out, ")\n");
> +               break;
> +       }
>         default :
> +               cil_log(CIL_ERR, "Unsupported flavor: %d\n", node->flavor);
>                 fprintf(out, "(<?RULE:%s>)\n", cil_node_to_string(node));
>                 break;
>         }
> diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c
> index 9128ac55..4b99208d 100644
> --- a/libsepol/src/kernel_to_cil.c
> +++ b/libsepol/src/kernel_to_cil.c
> @@ -1906,6 +1906,33 @@ static int map_filename_trans_to_str(hashtab_key_t key, void *data, void *arg)
>         return 0;
>  }
>
> +static int write_segregate_attributes_to_cil(FILE *out, const struct policydb *pdb)
> +{
> +       const segregate_attributes_rule_t *sattr;
> +
> +       for (sattr = pdb->segregate_attributes; sattr; sattr = sattr->next) {
> +               struct ebitmap_node *node;
> +               unsigned int bit;
> +               int first = 1;
> +
> +               sepol_printf(out, "(segregateattributes (");
> +
> +               ebitmap_for_each_positive_bit(&sattr->attrs, node, bit) {
> +                       if (first) {
> +                               first = 0;
> +                       } else {
> +                               sepol_printf(out, " ");
> +                       }
> +
> +                       sepol_printf(out, "%s", pdb->p_type_val_to_name[bit - 1]);
> +               }
> +
> +               sepol_printf(out, "))\n");
> +       }
> +
> +       return 0;
> +}
> +
>  static int write_filename_trans_rules_to_cil(FILE *out, struct policydb *pdb)
>  {
>         struct map_filename_trans_args args;
> @@ -3329,6 +3356,11 @@ int sepol_kernel_policydb_to_cil(FILE *out, struct policydb *pdb)
>                 goto exit;
>         }
>
> +       rc = write_segregate_attributes_to_cil(out, pdb);
> +       if (rc != 0) {
> +               goto exit;
> +       }
> +
>         rc = write_filename_trans_rules_to_cil(out, pdb);
>         if (rc != 0) {
>                 goto exit;
> diff --git a/secilc/docs/README.md b/secilc/docs/README.md
> index efab2a71..8f584019 100644
> --- a/secilc/docs/README.md
> +++ b/secilc/docs/README.md
> @@ -132,6 +132,7 @@ CIL (Common Intermediate Language)
>    * [typemember](cil_type_statements.md#typemember)
>    * [typetransition](cil_type_statements.md#typetransition)
>    * [typepermissive](cil_type_statements.md#typepermissive)
> +  * [segregateattributes](cil_type_statements.md#segregateattributes)
>
>  * [User Statements](cil_user_statements.md#user-statements)
>    * [user](cil_user_statements.md#user)
> diff --git a/secilc/docs/cil_type_statements.md b/secilc/docs/cil_type_statements.md
> index 19438417..56533eea 100644
> --- a/secilc/docs/cil_type_statements.md
> +++ b/secilc/docs/cil_type_statements.md
> @@ -601,3 +601,53 @@ This example will allow SELinux to run the `healthd.process` domain in permissiv
>          (allow ...)
>      )
>  ```
> +
> +segregateattributes
> +-------------------
> +
> +Libsepol and secilc version 3.5 introduced the segregateattributes statement
> +to mark two or more type attributes mutual exclusive. This is a compiler
> +enforced action that will stop compilation until the offending associations
> +are modified.
> +
> +Note that these constraints can be over-ridden by the CIL compiler command
> +line parameter `-N` or `--disable-neverallow` flags.
> +
> +**Statement definition:**
> +
> +```secil
> +    (segregateattributes (typeattribute_id typeattribute_id...))
> +```
> +
> +**Where:**
> +
> +<table>
> +<colgroup>
> +<col width="27%" />
> +<col width="72%" />
> +</colgroup>
> +<tbody>
> +<tr class="odd">
> +<td align="left"><p><code>segregateattributes</code></p></td>
> +<td align="left"><p>The <code>segregateattributes</code> keyword.</p></td>
> +</tr>
> +<tr class="even">
> +<td align="left"><p><code>typeattribute_id</code></p></td>
> +<td align="left"><p>At least two previously declared <code>typeattribute</code> identifier.</p>
> +<p>Note that the same <code>typeattribute</code> identifier must not be repeated.</p></td>
> +</tr>
> +</tbody>
> +</table>
> +
> +**Example:**
> +
> +This example will not compile as `type_1` is associated with type attributes `attr_1` and `attr_2`:
> +
> +```secil
> +    (type type_1)
> +    (typeattribute attr_1)
> +    (typeattribute attr_2)
> +    (typeattributeset attr_1 (type_1))
> +    (typeattributeset attr_2 (type_1))
> +    (segregateattributes (attr_1 attr_2))
> +```
> --
> 2.36.1
>
diff mbox series

Patch

diff --git a/libsepol/cil/src/cil.c b/libsepol/cil/src/cil.c
index 38edcf8e..cc6adb90 100644
--- a/libsepol/cil/src/cil.c
+++ b/libsepol/cil/src/cil.c
@@ -225,6 +225,7 @@  char *CIL_KEY_SRC_CIL;
 char *CIL_KEY_SRC_HLL_LMS;
 char *CIL_KEY_SRC_HLL_LMX;
 char *CIL_KEY_SRC_HLL_LME;
+char *CIL_KEY_SEGREGATEATTRIBUTES;
 
 static void cil_init_keys(void)
 {
@@ -394,6 +395,7 @@  static void cil_init_keys(void)
 	CIL_KEY_SRC_HLL_LMS = cil_strpool_add("lms");
 	CIL_KEY_SRC_HLL_LMX = cil_strpool_add("lmx");
 	CIL_KEY_SRC_HLL_LME = cil_strpool_add("lme");
+	CIL_KEY_SEGREGATEATTRIBUTES = cil_strpool_add("segregateattributes");
 }
 
 void cil_db_init(struct cil_db **db)
@@ -426,6 +428,7 @@  void cil_db_init(struct cil_db **db)
 	cil_list_init(&(*db)->userprefixes, CIL_LIST_ITEM);
 	cil_list_init(&(*db)->selinuxusers, CIL_LIST_ITEM);
 	cil_list_init(&(*db)->names, CIL_LIST_ITEM);
+	cil_list_init(&(*db)->segregateattributes, CIL_LIST_ITEM);
 
 	cil_type_init(&(*db)->selftype);
 	(*db)->selftype->datum.name = CIL_KEY_SELF;
@@ -481,6 +484,7 @@  void cil_db_destroy(struct cil_db **db)
 	cil_list_destroy(&(*db)->userprefixes, CIL_FALSE);
 	cil_list_destroy(&(*db)->selinuxusers, CIL_FALSE);
 	cil_list_destroy(&(*db)->names, CIL_TRUE);
+	cil_list_destroy(&(*db)->segregateattributes, CIL_FALSE);
 
 	cil_destroy_type((*db)->selftype);
 
@@ -1005,6 +1009,9 @@  void cil_destroy_data(void **data, enum cil_flavor flavor)
 	case CIL_SRC_INFO:
 		cil_destroy_src_info(*data);
 		break;
+	case CIL_SEGREGATEATTRIBUTES:
+		cil_destroy_segregateattributes(*data);
+		break;
 	case CIL_OP:
 	case CIL_CONS_OPERAND:
 		break;
@@ -1413,6 +1420,8 @@  const char * cil_node_to_string(struct cil_tree_node *node)
 		return CIL_KEY_CONS_H1;
 	case CIL_CONS_H2:
 		return CIL_KEY_CONS_H2;
+	case CIL_SEGREGATEATTRIBUTES:
+		return CIL_KEY_SEGREGATEATTRIBUTES;
 
 	default:
 		break;
@@ -2904,3 +2913,11 @@  void cil_src_info_init(struct cil_src_info **info)
 	(*info)->hll_line = 0;
 	(*info)->path = NULL;
 }
+
+void cil_segregateattributes_init(struct cil_segregateattributes **sattrs)
+{
+	*sattrs = cil_malloc(sizeof(**sattrs));
+
+	(*sattrs)->str_expr = NULL;
+	(*sattrs)->datum_expr = NULL;
+}
diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c
index 40615db2..0301d739 100644
--- a/libsepol/cil/src/cil_binary.c
+++ b/libsepol/cil/src/cil_binary.c
@@ -3818,6 +3818,38 @@  exit:
 	return SEPOL_ERR;
 }
 
+static int cil_segregateattributes_to_policydb(policydb_t *pdb, const struct cil_segregateattributes *sattrs)
+{
+	segregate_attributes_rule_t *sattr;
+	struct cil_list_item *curr;
+	type_datum_t *sepol_type;
+	int rc = SEPOL_ERR;
+
+	sattr = cil_malloc(sizeof(segregate_attributes_rule_t));
+	ebitmap_init(&sattr->attrs);
+
+	cil_list_for_each(curr, sattrs->datum_expr) {
+		rc = __cil_get_sepol_type_datum(pdb, DATUM(curr->data), &sepol_type);
+		if (rc != SEPOL_OK) goto exit;
+
+		if (ebitmap_set_bit(&sattr->attrs, sepol_type->s.value - 1, 1)) {
+			goto exit;
+		}
+	}
+
+	sattr->next = pdb->segregate_attributes;
+	pdb->segregate_attributes = sattr;
+
+	return SEPOL_OK;
+
+exit:
+	if (sattr) {
+		ebitmap_destroy(&sattr->attrs);
+		free(sattr);
+	}
+	return rc;
+}
+
 static int __cil_node_to_policydb(struct cil_tree_node *node, void *extra_args)
 {
 	int rc = SEPOL_OK;
@@ -3960,6 +3992,9 @@  static int __cil_node_to_policydb(struct cil_tree_node *node, void *extra_args)
 		case CIL_DEFAULTRANGE:
 			rc = cil_defaultrange_to_policydb(pdb, node->data);
 			break;
+		case CIL_SEGREGATEATTRIBUTES:
+			rc = cil_segregateattributes_to_policydb(pdb, node->data);
+			break;
 		default:
 			break;
 		}
@@ -4890,6 +4925,42 @@  exit:
 	return rc;
 }
 
+static int cil_check_segregateattributes(const policydb_t *pdb, int *violation)
+{
+	const segregate_attributes_rule_t *sattr;
+
+	for (sattr = pdb->segregate_attributes; sattr; sattr = sattr->next) {
+		ebitmap_node_t *first_node;
+		unsigned int first_bit;
+
+		ebitmap_for_each_positive_bit(&sattr->attrs, first_node, first_bit) {
+			ebitmap_node_t *second_node;
+			unsigned int second_bit;
+
+			ebitmap_for_each_positive_bit_after(&sattr->attrs, second_node, second_bit, first_node, first_bit) {
+				ebitmap_t attr_union;
+				ebitmap_node_t *type_node;
+				unsigned int type_bit;
+
+				if (ebitmap_and(&attr_union, &pdb->attr_type_map[first_bit], &pdb->attr_type_map[second_bit]))
+					return SEPOL_ERR;
+
+				ebitmap_for_each_positive_bit(&attr_union, type_node, type_bit) {
+					cil_log(CIL_ERR, "Segregate Attributes violation, type %s associated with attributes %s and %s\n",
+					                 pdb->p_type_val_to_name[type_bit],
+					                 pdb->p_type_val_to_name[first_bit],
+					                 pdb->p_type_val_to_name[second_bit]);
+					*violation = CIL_TRUE;
+				}
+
+				ebitmap_destroy(&attr_union);
+			}
+		}
+	}
+
+	return SEPOL_OK;
+}
+
 static struct cil_list *cil_classperms_from_sepol(policydb_t *pdb, uint16_t class, uint32_t data, struct cil_class *class_value_to_cil[], struct cil_perm **perm_value_to_cil[])
 {
 	struct cil_classperms *cp;
@@ -5160,6 +5231,10 @@  int cil_binary_create_allocated_pdb(const struct cil_db *db, sepol_policydb_t *p
 		rc = cil_check_neverallows(db, pdb, neverallows, &violation);
 		if (rc != SEPOL_OK) goto exit;
 
+		cil_log(CIL_INFO, "Checking Segregate Attributes\n");
+		rc = cil_check_segregateattributes(pdb, &violation);
+		if (rc != SEPOL_OK) goto exit;
+
 		cil_log(CIL_INFO, "Checking User Bounds\n");
 		rc = bounds_check_users(NULL, pdb);
 		if (rc) {
diff --git a/libsepol/cil/src/cil_build_ast.c b/libsepol/cil/src/cil_build_ast.c
index 4177c9f6..611aade8 100644
--- a/libsepol/cil/src/cil_build_ast.c
+++ b/libsepol/cil/src/cil_build_ast.c
@@ -6164,6 +6164,62 @@  void cil_destroy_src_info(struct cil_src_info *info)
 	free(info);
 }
 
+int cil_gen_segregateattributes(struct cil_db *db, struct cil_tree_node *parse_current, struct cil_tree_node *ast_node)
+{
+	enum cil_syntax syntax[] = {
+		CIL_SYN_STRING,
+		CIL_SYN_LIST,
+		CIL_SYN_END
+	};
+	size_t syntax_len = sizeof(syntax)/sizeof(*syntax);
+	struct cil_segregateattributes *sattrs = NULL;
+	int rc = SEPOL_ERR;
+
+	if (db == NULL || parse_current == NULL || ast_node == NULL) {
+		goto exit;
+	}
+
+	rc = __cil_verify_syntax(parse_current, syntax, syntax_len);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+
+	cil_segregateattributes_init(&sattrs);
+
+	rc = cil_gen_expr(parse_current->next, CIL_TYPEATTRIBUTE, &sattrs->str_expr);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+
+	/* require at least two attributes */
+	if (sattrs->str_expr->head == sattrs->str_expr->tail) {
+		rc = SEPOL_ERR;
+		goto exit;
+	}
+
+	ast_node->data = sattrs;
+	ast_node->flavor = CIL_SEGREGATEATTRIBUTES;
+
+	return SEPOL_OK;
+
+exit:
+	cil_tree_log(parse_current, CIL_ERR, "Bad segregate attributes declaration");
+	cil_destroy_segregateattributes(sattrs);
+	return rc;
+}
+
+void cil_destroy_segregateattributes(struct cil_segregateattributes *sattrs)
+{
+	if (sattrs == NULL) {
+		return;
+	}
+
+	cil_list_destroy(&sattrs->str_expr, CIL_TRUE);
+	cil_list_destroy(&sattrs->datum_expr, CIL_FALSE);
+
+	free(sattrs);
+}
+
 static int check_for_illegal_statement(struct cil_tree_node *parse_current, struct cil_args_build *args)
 {
 	if (args->tunif != NULL) {
@@ -6455,6 +6511,8 @@  static struct cil_tree_node * parse_statement(struct cil_db *db, struct cil_tree
 		rc = cil_gen_mls(parse_current, new_ast_node);
 	} else if (parse_current->data == CIL_KEY_SRC_INFO) {
 		rc = cil_gen_src_info(parse_current, new_ast_node);
+	} else if (parse_current->data == CIL_KEY_SEGREGATEATTRIBUTES) {
+		rc = cil_gen_segregateattributes(db, parse_current, new_ast_node);
 	} else {
 		cil_log(CIL_ERR, "Error: Unknown keyword %s\n", (char *)parse_current->data);
 		rc = SEPOL_ERR;
diff --git a/libsepol/cil/src/cil_build_ast.h b/libsepol/cil/src/cil_build_ast.h
index fd9053ce..d815a22f 100644
--- a/libsepol/cil/src/cil_build_ast.h
+++ b/libsepol/cil/src/cil_build_ast.h
@@ -225,6 +225,8 @@  int cil_gen_defaultrange(struct cil_tree_node *parse_current, struct cil_tree_no
 void cil_destroy_defaultrange(struct cil_defaultrange *def);
 int cil_gen_src_info(struct cil_tree_node *parse_current, struct cil_tree_node *ast_node);
 void cil_destroy_src_info(struct cil_src_info *info);
+int cil_gen_segregateattributes(struct cil_db *db, struct cil_tree_node *parse_current, struct cil_tree_node *ast_node);
+void cil_destroy_segregateattributes(struct cil_segregateattributes *sattrs);
 
 int cil_fill_cats(struct cil_tree_node *curr, struct cil_cats **cats);
 void cil_destroy_cats(struct cil_cats *cats);
diff --git a/libsepol/cil/src/cil_copy_ast.c b/libsepol/cil/src/cil_copy_ast.c
index 17f05021..e0f3ba4f 100644
--- a/libsepol/cil/src/cil_copy_ast.c
+++ b/libsepol/cil/src/cil_copy_ast.c
@@ -1697,6 +1697,21 @@  static int cil_copy_src_info(__attribute__((unused)) struct cil_db *db, void *da
 	return SEPOL_OK;
 }
 
+static int cil_copy_segregateattributes(__attribute__((unused)) struct cil_db *db, void *data, void **copy, __attribute__((unused)) symtab_t *symtab)
+{
+	struct cil_segregateattributes *orig = data;
+	struct cil_segregateattributes *new = NULL;
+
+	cil_segregateattributes_init(&new);
+
+	cil_copy_expr(db, orig->str_expr, &new->str_expr);
+	cil_copy_expr(db, orig->datum_expr, &new->datum_expr);
+
+	*copy = new;
+
+	return SEPOL_OK;
+}
+
 static int __cil_copy_node_helper(struct cil_tree_node *orig, uint32_t *finished, void *extra_args)
 {
 	int rc = SEPOL_ERR;
@@ -1990,6 +2005,9 @@  static int __cil_copy_node_helper(struct cil_tree_node *orig, uint32_t *finished
 	case CIL_SRC_INFO:
 		copy_func = &cil_copy_src_info;
 		break;
+	case CIL_SEGREGATEATTRIBUTES:
+		copy_func = &cil_copy_segregateattributes;
+		break;
 	default:
 		goto exit;
 	}
diff --git a/libsepol/cil/src/cil_flavor.h b/libsepol/cil/src/cil_flavor.h
index c2f0cee7..ffbd5877 100644
--- a/libsepol/cil/src/cil_flavor.h
+++ b/libsepol/cil/src/cil_flavor.h
@@ -115,6 +115,7 @@  enum cil_flavor {
 	CIL_SRC_INFO,
 	CIL_IBPKEYCON,
 	CIL_IBENDPORTCON,
+	CIL_SEGREGATEATTRIBUTES,
 
 /*
  *          boolean  constraint  set  catset
diff --git a/libsepol/cil/src/cil_internal.h b/libsepol/cil/src/cil_internal.h
index a7604762..e22c2f87 100644
--- a/libsepol/cil/src/cil_internal.h
+++ b/libsepol/cil/src/cil_internal.h
@@ -242,6 +242,7 @@  extern char *CIL_KEY_SRC_CIL;
 extern char *CIL_KEY_SRC_HLL_LMS;
 extern char *CIL_KEY_SRC_HLL_LMX;
 extern char *CIL_KEY_SRC_HLL_LME;
+extern char *CIL_KEY_SEGREGATEATTRIBUTES;
 
 /*
 	Symbol Table Array Indices
@@ -309,6 +310,7 @@  struct cil_db {
 	struct cil_list *userprefixes;
 	struct cil_list *selinuxusers;
 	struct cil_list *names;
+	struct cil_list *segregateattributes;
 	int num_types_and_attrs;
 	int num_classes;
 	int num_cats;
@@ -975,6 +977,11 @@  struct cil_src_info {
 	char *path;
 };
 
+struct cil_segregateattributes {
+	struct cil_list *str_expr;
+	struct cil_list *datum_expr;
+};
+
 void cil_db_init(struct cil_db **db);
 void cil_db_destroy(struct cil_db **db);
 
@@ -1085,5 +1092,6 @@  void cil_mls_init(struct cil_mls **mls);
 void cil_src_info_init(struct cil_src_info **info);
 void cil_userattribute_init(struct cil_userattribute **attribute);
 void cil_userattributeset_init(struct cil_userattributeset **attrset);
+void cil_segregateattributes_init(struct cil_segregateattributes **sattrs);
 
 #endif
diff --git a/libsepol/cil/src/cil_policy.c b/libsepol/cil/src/cil_policy.c
index 7c543c47..36f6780d 100644
--- a/libsepol/cil/src/cil_policy.c
+++ b/libsepol/cil/src/cil_policy.c
@@ -69,6 +69,7 @@  enum cil_statement_list {
 	CIL_LIST_USER,
 	CIL_LIST_CONSTRAINT,
 	CIL_LIST_VALIDATETRANS,
+	CIL_LIST_SEGREGATEATTRIBUTES,
 	CIL_LIST_NUM_LISTS
 };
 
@@ -168,6 +169,9 @@  static int __cil_gather_statements_helper(struct cil_tree_node *node, uint32_t *
 	case CIL_VALIDATETRANS:
 		kind = CIL_LIST_VALIDATETRANS;
 		break;
+	case CIL_SEGREGATEATTRIBUTES:
+		kind = CIL_LIST_SEGREGATEATTRIBUTES;
+		break;
 	default:
 		break;
 	}
@@ -1911,6 +1915,27 @@  static void cil_devicetreecons_to_policy(FILE *out, struct cil_sort *devicetreec
 	}
 }
 
+static void cil_segregateattributes_to_policy(FILE *out, struct cil_list *sattrs_list)
+{
+	struct cil_list_item *curr_sattrs, *curr_attr;
+	struct cil_segregateattributes *sattrs;
+	int first = 1;
+
+	cil_list_for_each(curr_sattrs, sattrs_list) {
+		sattrs = curr_sattrs->data;
+		fprintf(out, "segregate_attriutes ");
+		cil_list_for_each(curr_attr, sattrs->datum_expr) {
+			if (!first) {
+				first = 0;
+			} else {
+				fprintf(out, ", ");
+			}
+			fprintf(out, "%s", DATUM(curr_attr->data)->fqn);
+		}
+		fprintf(out, ";\n");
+	}
+}
+
 void cil_gen_policy(FILE *out, struct cil_db *db)
 {
 	unsigned i;
@@ -1956,6 +1981,7 @@  void cil_gen_policy(FILE *out, struct cil_db *db)
 	cil_typebounds_to_policy(out, lists[CIL_LIST_TYPE]);
 	cil_typeattributes_to_policy(out, lists[CIL_LIST_TYPE], lists[CIL_LIST_TYPEATTRIBUTE]);
 	cil_te_rules_to_policy(out, head, db->mls);
+	cil_segregateattributes_to_policy(out, db->segregateattributes);
 
 	cil_roles_to_policy(out, lists[CIL_LIST_ROLE]);
 	cil_role_types_to_policy(out, lists[CIL_LIST_ROLE], lists[CIL_LIST_TYPE]);
diff --git a/libsepol/cil/src/cil_reset_ast.c b/libsepol/cil/src/cil_reset_ast.c
index 0864d7ef..c5ac83c8 100644
--- a/libsepol/cil/src/cil_reset_ast.c
+++ b/libsepol/cil/src/cil_reset_ast.c
@@ -475,6 +475,11 @@  static void cil_reset_booleanif(struct cil_booleanif *bif)
 	cil_list_destroy(&bif->datum_expr, CIL_FALSE);
 }
 
+static void cil_reset_segregateattributes(struct cil_segregateattributes *sattrs)
+{
+	cil_list_destroy(&sattrs->datum_expr, CIL_FALSE);
+}
+
 static int __cil_reset_node(struct cil_tree_node *node,  __attribute__((unused)) uint32_t *finished, __attribute__((unused)) void *extra_args)
 {
 	switch (node->flavor) {
@@ -630,6 +635,9 @@  static int __cil_reset_node(struct cil_tree_node *node,  __attribute__((unused))
 	case CIL_BOOLEANIF:
 		cil_reset_booleanif(node->data);
 		break;
+	case CIL_SEGREGATEATTRIBUTES:
+		cil_reset_segregateattributes(node->data);
+		break;
 	case CIL_TUNABLEIF:
 	case CIL_CALL:
 		break; /* Not effected by optional block disabling */
diff --git a/libsepol/cil/src/cil_resolve_ast.c b/libsepol/cil/src/cil_resolve_ast.c
index f5e22c97..36a96199 100644
--- a/libsepol/cil/src/cil_resolve_ast.c
+++ b/libsepol/cil/src/cil_resolve_ast.c
@@ -3265,6 +3265,7 @@  int cil_resolve_expr(enum cil_flavor expr_type, struct cil_list *str_expr, struc
 		sym_index = CIL_SYM_TUNABLES;
 		break;
 	case CIL_TYPE:
+	case CIL_TYPEATTRIBUTE:
 		sym_index = CIL_SYM_TYPES;
 		break;
 	case CIL_ROLE:
@@ -3312,6 +3313,13 @@  int cil_resolve_expr(enum cil_flavor expr_type, struct cil_list *str_expr, struc
 			} else {
 				if (sym_index == CIL_SYM_TYPES && (expr_type == CIL_CONSTRAIN || expr_type == CIL_VALIDATETRANS)) {
 					cil_type_used(res_datum, CIL_ATTR_CONSTRAINT);
+				} else if (expr_type == CIL_SEGREGATEATTRIBUTES) {
+					if (FLAVOR(res_datum) != CIL_TYPEATTRIBUTE) {
+						cil_tree_log(parent, CIL_ERR, "Type or type alias not supported in segregate attributes declaration");
+						rc = SEPOL_ERR;
+						goto exit;
+					}
+					cil_type_used(res_datum, CIL_ATTR_NEVERALLOW);
 				}
 				cil_list_append(*datum_expr, CIL_DATUM, res_datum);
 			}
@@ -3508,6 +3516,33 @@  exit:
 	return rc;
 }
 
+int cil_resolve_segregateattributes(struct cil_tree_node *current, void *extra_args)
+{
+	struct cil_segregateattributes *sattrs = current->data;
+	struct cil_list_item *first, *second;
+	int rc;
+
+	rc = cil_resolve_expr(CIL_SEGREGATEATTRIBUTES, sattrs->str_expr, &sattrs->datum_expr, current, extra_args);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+
+	cil_list_for_each(first, sattrs->datum_expr) {
+		for (second = first->next; second; second = second->next) {
+			if (first->data == second->data) {
+				cil_tree_log(current, CIL_ERR, "Repeated attribute in segregate attributes declaration");
+				rc = SEPOL_ERR;
+				goto exit;
+			}
+		}
+	}
+
+	return SEPOL_OK;
+
+exit:
+	return rc;
+}
+
 /*
  * Degenerate inheritance leads to exponential growth of the policy
  * It can take many forms, but here is one example.
@@ -3888,6 +3923,9 @@  static int __cil_resolve_ast_node(struct cil_tree_node *node, void *extra_args)
 		case CIL_USERATTRIBUTESET:
 			rc = cil_resolve_userattributeset(node, args);
 			break;
+		case CIL_SEGREGATEATTRIBUTES:
+			rc = cil_resolve_segregateattributes(node, args);
+			break;
 		default:
 			break;
 		}
diff --git a/libsepol/cil/src/cil_resolve_ast.h b/libsepol/cil/src/cil_resolve_ast.h
index 1d971fd6..31594954 100644
--- a/libsepol/cil/src/cil_resolve_ast.h
+++ b/libsepol/cil/src/cil_resolve_ast.h
@@ -96,6 +96,7 @@  int cil_resolve_expr(enum cil_flavor expr_type, struct cil_list *str_expr, struc
 int cil_resolve_boolif(struct cil_tree_node *current, void *extra_args);
 int cil_evaluate_expr(struct cil_list *datum_expr, uint16_t *result);
 int cil_resolve_tunif(struct cil_tree_node *current, void *extra_args);
+int cil_resolve_segregateattributes(struct cil_tree_node *current, void *extra_args);
 
 int cil_resolve_ast(struct cil_db *db, struct cil_tree_node *current);
 int cil_resolve_name(struct cil_tree_node *ast_node, char *name, enum cil_sym_index sym_index, void *extra_args, struct cil_symtab_datum **datum);
diff --git a/libsepol/cil/src/cil_write_ast.c b/libsepol/cil/src/cil_write_ast.c
index b75784ef..d0fb555b 100644
--- a/libsepol/cil/src/cil_write_ast.c
+++ b/libsepol/cil/src/cil_write_ast.c
@@ -1474,7 +1474,18 @@  void cil_write_ast_node(FILE *out, struct cil_tree_node *node)
 		fprintf(out, "(ipaddr %s %s)\n", datum_to_str(&ipaddr->datum), buf);
 		break;
 	}
+	case CIL_SEGREGATEATTRIBUTES: {
+		struct cil_segregateattributes *sattrs = node->data;
+		fprintf(out, "(segregateattributes ");
+		if (sattrs->datum_expr)
+			write_expr(out, sattrs->datum_expr);
+		else
+			write_expr(out, sattrs->str_expr);
+		fprintf(out, ")\n");
+		break;
+	}
 	default :
+		cil_log(CIL_ERR, "Unsupported flavor: %d\n", node->flavor);
 		fprintf(out, "(<?RULE:%s>)\n", cil_node_to_string(node));
 		break;
 	}
diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c
index 9128ac55..4b99208d 100644
--- a/libsepol/src/kernel_to_cil.c
+++ b/libsepol/src/kernel_to_cil.c
@@ -1906,6 +1906,33 @@  static int map_filename_trans_to_str(hashtab_key_t key, void *data, void *arg)
 	return 0;
 }
 
+static int write_segregate_attributes_to_cil(FILE *out, const struct policydb *pdb)
+{
+	const segregate_attributes_rule_t *sattr;
+
+	for (sattr = pdb->segregate_attributes; sattr; sattr = sattr->next) {
+		struct ebitmap_node *node;
+		unsigned int bit;
+		int first = 1;
+
+		sepol_printf(out, "(segregateattributes (");
+
+		ebitmap_for_each_positive_bit(&sattr->attrs, node, bit) {
+			if (first) {
+				first = 0;
+			} else {
+				sepol_printf(out, " ");
+			}
+
+			sepol_printf(out, "%s", pdb->p_type_val_to_name[bit - 1]);
+		}
+
+		sepol_printf(out, "))\n");
+	}
+
+	return 0;
+}
+
 static int write_filename_trans_rules_to_cil(FILE *out, struct policydb *pdb)
 {
 	struct map_filename_trans_args args;
@@ -3329,6 +3356,11 @@  int sepol_kernel_policydb_to_cil(FILE *out, struct policydb *pdb)
 		goto exit;
 	}
 
+	rc = write_segregate_attributes_to_cil(out, pdb);
+	if (rc != 0) {
+		goto exit;
+	}
+
 	rc = write_filename_trans_rules_to_cil(out, pdb);
 	if (rc != 0) {
 		goto exit;
diff --git a/secilc/docs/README.md b/secilc/docs/README.md
index efab2a71..8f584019 100644
--- a/secilc/docs/README.md
+++ b/secilc/docs/README.md
@@ -132,6 +132,7 @@  CIL (Common Intermediate Language)
   * [typemember](cil_type_statements.md#typemember)
   * [typetransition](cil_type_statements.md#typetransition)
   * [typepermissive](cil_type_statements.md#typepermissive)
+  * [segregateattributes](cil_type_statements.md#segregateattributes)
 
 * [User Statements](cil_user_statements.md#user-statements)
   * [user](cil_user_statements.md#user)
diff --git a/secilc/docs/cil_type_statements.md b/secilc/docs/cil_type_statements.md
index 19438417..56533eea 100644
--- a/secilc/docs/cil_type_statements.md
+++ b/secilc/docs/cil_type_statements.md
@@ -601,3 +601,53 @@  This example will allow SELinux to run the `healthd.process` domain in permissiv
         (allow ...)
     )
 ```
+
+segregateattributes
+-------------------
+
+Libsepol and secilc version 3.5 introduced the segregateattributes statement
+to mark two or more type attributes mutual exclusive. This is a compiler
+enforced action that will stop compilation until the offending associations
+are modified.
+
+Note that these constraints can be over-ridden by the CIL compiler command
+line parameter `-N` or `--disable-neverallow` flags.
+
+**Statement definition:**
+
+```secil
+    (segregateattributes (typeattribute_id typeattribute_id...))
+```
+
+**Where:**
+
+<table>
+<colgroup>
+<col width="27%" />
+<col width="72%" />
+</colgroup>
+<tbody>
+<tr class="odd">
+<td align="left"><p><code>segregateattributes</code></p></td>
+<td align="left"><p>The <code>segregateattributes</code> keyword.</p></td>
+</tr>
+<tr class="even">
+<td align="left"><p><code>typeattribute_id</code></p></td>
+<td align="left"><p>At least two previously declared <code>typeattribute</code> identifier.</p>
+<p>Note that the same <code>typeattribute</code> identifier must not be repeated.</p></td>
+</tr>
+</tbody>
+</table>
+
+**Example:**
+
+This example will not compile as `type_1` is associated with type attributes `attr_1` and `attr_2`:
+
+```secil
+    (type type_1)
+    (typeattribute attr_1)
+    (typeattribute attr_2)
+    (typeattributeset attr_1 (type_1))
+    (typeattributeset attr_2 (type_1))
+    (segregateattributes (attr_1 attr_2))
+```