diff mbox series

[v3,4/8] checkpolicy: add front-end support for segregate attributes

Message ID 20220721150515.19843-4-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 specifying segregate attributes.

The following two blocks are equivalent:

    segregate_attributes attr1, attr2, attr3;

    segregate_attributes attr1, attr2;
    segregate_attributes attr1, attr3;
    segregate_attributes attr2, attr3;

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 checkpolicy/policy_define.c | 66 +++++++++++++++++++++++++++++++++++++
 checkpolicy/policy_define.h |  1 +
 checkpolicy/policy_parse.y  |  5 +++
 checkpolicy/policy_scan.l   |  2 ++
 4 files changed, 74 insertions(+)

Comments

James Carter Aug. 8, 2022, 5:09 p.m. UTC | #1
On Thu, Jul 21, 2022 at 11:11 AM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Support specifying segregate attributes.
>
> The following two blocks are equivalent:
>
>     segregate_attributes attr1, attr2, attr3;
>
>     segregate_attributes attr1, attr2;
>     segregate_attributes attr1, attr3;
>     segregate_attributes attr2, attr3;
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
>  checkpolicy/policy_define.c | 66 +++++++++++++++++++++++++++++++++++++
>  checkpolicy/policy_define.h |  1 +
>  checkpolicy/policy_parse.y  |  5 +++
>  checkpolicy/policy_scan.l   |  2 ++
>  4 files changed, 74 insertions(+)
>
> diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c
> index 8bf36859..cf6fbf08 100644
> --- a/checkpolicy/policy_define.c
> +++ b/checkpolicy/policy_define.c
> @@ -1220,6 +1220,72 @@ exit:
>         return rc;
>  }
>
> +int define_segregate_attributes(void)
> +{
> +       char *id = NULL;
> +       segregate_attributes_rule_t *sattr = NULL;
> +       int rc = -1;
> +
> +       if (pass == 1) {
> +               while ((id = queue_remove(id_queue)))
> +                       free(id);
> +               return 0;
> +       }
> +
> +       sattr = malloc(sizeof(segregate_attributes_rule_t));
> +       if (!sattr) {
> +               yyerror("Out of memory!");
> +               goto exit;
> +       }
> +
> +       ebitmap_init(&sattr->attrs);
> +
> +       while ((id = queue_remove(id_queue))) {
> +               const type_datum_t *attr;
> +
> +               if (!is_id_in_scope(SYM_TYPES, id)) {
> +                       yyerror2("attribute %s is not within scope", id);
> +                       goto exit;
> +               }
> +
> +               attr = hashtab_search(policydbp->p_types.table, id);
> +               if (!attr) {
> +                       yyerror2("attribute %s is not declared", id);
> +                       goto exit;
> +               }
> +
> +               if (attr->flavor != TYPE_ATTRIB) {
> +                       yyerror2("%s is a type, not an attribute", id);
> +                       goto exit;
> +               }
> +

It seems like it would be useful to check a type, so an error would be
given if the type is associated with the attribute.

> +               if (ebitmap_get_bit(&sattr->attrs, attr->s.value - 1)) {
> +                       yyerror2("attribute %s used multiple times", id);
> +                       goto exit;
> +               }
> +
> +               if (ebitmap_set_bit(&sattr->attrs, attr->s.value - 1, TRUE)) {
> +                       yyerror("Out of memory!");
> +                       goto exit;
> +               }
> +
> +               free(id);
> +       }
> +
> +       sattr->next = policydbp->segregate_attributes;
> +       policydbp->segregate_attributes = sattr;
> +
> +       sattr = NULL;
> +       rc = 0;
> +exit:
> +       if (sattr) {
> +               ebitmap_destroy(&sattr->attrs);
> +               free(sattr);
> +       }
> +       free(id);
> +       return rc;
> +}
> +
>  static int add_aliases_to_type(type_datum_t * type)
>  {
>         char *id;
> diff --git a/checkpolicy/policy_define.h b/checkpolicy/policy_define.h
> index 50a7ba78..f55d0b17 100644
> --- a/checkpolicy/policy_define.h
> +++ b/checkpolicy/policy_define.h
> @@ -68,6 +68,7 @@ int define_type(int alias);
>  int define_user(void);
>  int define_validatetrans(constraint_expr_t *expr);
>  int expand_attrib(void);
> +int define_segregate_attributes(void);
>  int insert_id(const char *id,int push);
>  int insert_separator(int push);
>  role_datum_t *define_role_dom(role_datum_t *r);
> diff --git a/checkpolicy/policy_parse.y b/checkpolicy/policy_parse.y
> index 45f973ff..acd6096d 100644
> --- a/checkpolicy/policy_parse.y
> +++ b/checkpolicy/policy_parse.y
> @@ -104,6 +104,7 @@ typedef int (* require_func_t)(int pass);
>  %token ALIAS
>  %token ATTRIBUTE
>  %token EXPANDATTRIBUTE
> +%token SEGREGATEATTRIBUTES
>  %token BOOL
>  %token TUNABLE
>  %token IF
> @@ -320,6 +321,7 @@ rbac_decl           : attribute_role_def
>                         ;
>  te_decl                        : attribute_def
>                          | expandattribute_def
> +                        | segregateattributes_def
>                          | type_def
>                          | typealias_def
>                          | typeattribute_def
> @@ -337,6 +339,9 @@ attribute_def           : ATTRIBUTE identifier ';'
>  expandattribute_def     : EXPANDATTRIBUTE names bool_val ';'
>                          { if (expand_attrib()) return -1;}
>                          ;
> +segregateattributes_def : SEGREGATEATTRIBUTES identifier ',' id_comma_list ';'
> +                        { if (define_segregate_attributes()) return -1;}
> +

I don't see the need for comparing more than two at a time.

Something like:
disjoint_types attr1 attr2;

Thanks,
Jim

                       ;
>  type_def               : TYPE identifier alias_def opt_attr_list ';'
>                          {if (define_type(1)) return -1;}
>                         | TYPE identifier opt_attr_list ';'
> diff --git a/checkpolicy/policy_scan.l b/checkpolicy/policy_scan.l
> index 9fefea7b..d865dcb6 100644
> --- a/checkpolicy/policy_scan.l
> +++ b/checkpolicy/policy_scan.l
> @@ -123,6 +123,8 @@ ATTRIBUTE |
>  attribute                      { return(ATTRIBUTE); }
>  EXPANDATTRIBUTE |
>  expandattribute                 { return(EXPANDATTRIBUTE); }
> +SEGREGATE_ATTRIBUTES |
> +segregate_attributes           { return(SEGREGATEATTRIBUTES); }
>  TYPE_TRANSITION |
>  type_transition                        { return(TYPE_TRANSITION); }
>  TYPE_MEMBER |
> --
> 2.36.1
>
Christian Göttsche Aug. 11, 2023, 4:38 p.m. UTC | #2
On Mon, 8 Aug 2022 at 19:09, James Carter <jwcart2@gmail.com> wrote:
>
> On Thu, Jul 21, 2022 at 11:11 AM Christian Göttsche
> <cgzones@googlemail.com> wrote:
> >
> > Support specifying segregate attributes.
> >
> > The following two blocks are equivalent:
> >
> >     segregate_attributes attr1, attr2, attr3;
> >
> >     segregate_attributes attr1, attr2;
> >     segregate_attributes attr1, attr3;
> >     segregate_attributes attr2, attr3;
> >
> > Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> > ---
> >  checkpolicy/policy_define.c | 66 +++++++++++++++++++++++++++++++++++++
> >  checkpolicy/policy_define.h |  1 +
> >  checkpolicy/policy_parse.y  |  5 +++
> >  checkpolicy/policy_scan.l   |  2 ++
> >  4 files changed, 74 insertions(+)
> >
> > diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c
> > index 8bf36859..cf6fbf08 100644
> > --- a/checkpolicy/policy_define.c
> > +++ b/checkpolicy/policy_define.c
> > @@ -1220,6 +1220,72 @@ exit:
> >         return rc;
> >  }
> >
> > +int define_segregate_attributes(void)
> > +{
> > +       char *id = NULL;
> > +       segregate_attributes_rule_t *sattr = NULL;
> > +       int rc = -1;
> > +
> > +       if (pass == 1) {
> > +               while ((id = queue_remove(id_queue)))
> > +                       free(id);
> > +               return 0;
> > +       }
> > +
> > +       sattr = malloc(sizeof(segregate_attributes_rule_t));
> > +       if (!sattr) {
> > +               yyerror("Out of memory!");
> > +               goto exit;
> > +       }
> > +
> > +       ebitmap_init(&sattr->attrs);
> > +
> > +       while ((id = queue_remove(id_queue))) {
> > +               const type_datum_t *attr;
> > +
> > +               if (!is_id_in_scope(SYM_TYPES, id)) {
> > +                       yyerror2("attribute %s is not within scope", id);
> > +                       goto exit;
> > +               }
> > +
> > +               attr = hashtab_search(policydbp->p_types.table, id);
> > +               if (!attr) {
> > +                       yyerror2("attribute %s is not declared", id);
> > +                       goto exit;
> > +               }
> > +
> > +               if (attr->flavor != TYPE_ATTRIB) {
> > +                       yyerror2("%s is a type, not an attribute", id);
> > +                       goto exit;
> > +               }
> > +
>
> It seems like it would be useful to check a type, so an error would be
> given if the type is associated with the attribute.
>

I am not exactly sure what you mean.
Do you like to have a policy statement like

    nevertypeattribute TYPE ATTRIBUTE;

that checks at compile time a type is not associated with an attribute?

> > +               if (ebitmap_get_bit(&sattr->attrs, attr->s.value - 1)) {
> > +                       yyerror2("attribute %s used multiple times", id);
> > +                       goto exit;
> > +               }
> > +
> > +               if (ebitmap_set_bit(&sattr->attrs, attr->s.value - 1, TRUE)) {
> > +                       yyerror("Out of memory!");
> > +                       goto exit;
> > +               }
> > +
> > +               free(id);
> > +       }
> > +
> > +       sattr->next = policydbp->segregate_attributes;
> > +       policydbp->segregate_attributes = sattr;
> > +
> > +       sattr = NULL;
> > +       rc = 0;
> > +exit:
> > +       if (sattr) {
> > +               ebitmap_destroy(&sattr->attrs);
> > +               free(sattr);
> > +       }
> > +       free(id);
> > +       return rc;
> > +}
> > +
> >  static int add_aliases_to_type(type_datum_t * type)
> >  {
> >         char *id;
> > diff --git a/checkpolicy/policy_define.h b/checkpolicy/policy_define.h
> > index 50a7ba78..f55d0b17 100644
> > --- a/checkpolicy/policy_define.h
> > +++ b/checkpolicy/policy_define.h
> > @@ -68,6 +68,7 @@ int define_type(int alias);
> >  int define_user(void);
> >  int define_validatetrans(constraint_expr_t *expr);
> >  int expand_attrib(void);
> > +int define_segregate_attributes(void);
> >  int insert_id(const char *id,int push);
> >  int insert_separator(int push);
> >  role_datum_t *define_role_dom(role_datum_t *r);
> > diff --git a/checkpolicy/policy_parse.y b/checkpolicy/policy_parse.y
> > index 45f973ff..acd6096d 100644
> > --- a/checkpolicy/policy_parse.y
> > +++ b/checkpolicy/policy_parse.y
> > @@ -104,6 +104,7 @@ typedef int (* require_func_t)(int pass);
> >  %token ALIAS
> >  %token ATTRIBUTE
> >  %token EXPANDATTRIBUTE
> > +%token SEGREGATEATTRIBUTES
> >  %token BOOL
> >  %token TUNABLE
> >  %token IF
> > @@ -320,6 +321,7 @@ rbac_decl           : attribute_role_def
> >                         ;
> >  te_decl                        : attribute_def
> >                          | expandattribute_def
> > +                        | segregateattributes_def
> >                          | type_def
> >                          | typealias_def
> >                          | typeattribute_def
> > @@ -337,6 +339,9 @@ attribute_def           : ATTRIBUTE identifier ';'
> >  expandattribute_def     : EXPANDATTRIBUTE names bool_val ';'
> >                          { if (expand_attrib()) return -1;}
> >                          ;
> > +segregateattributes_def : SEGREGATEATTRIBUTES identifier ',' id_comma_list ';'
> > +                        { if (define_segregate_attributes()) return -1;}
> > +
>
> I don't see the need for comparing more than two at a time.
>
> Something like:
> disjoint_types attr1 attr2;

That would lead to quadratic growth of statements, for example in the
Reference Policy example of

    ibendport_type, packet_type, sysctl_type, device_node,
ibpkey_type, sysfs_types, domain, boolean_type, netif_type, file_type,
node_type, proc_type, port_type

Also one could see supporting more than two attributes as syntactic
sugar, which the traditional language already supports, e.g.

    allow { TYPE1 TYPE2 } { TYPE3 TYPE4 } : { CLASS1 CLASS2 } perm_list;

>
> Thanks,
> Jim
>
>                        ;
> >  type_def               : TYPE identifier alias_def opt_attr_list ';'
> >                          {if (define_type(1)) return -1;}
> >                         | TYPE identifier opt_attr_list ';'
> > diff --git a/checkpolicy/policy_scan.l b/checkpolicy/policy_scan.l
> > index 9fefea7b..d865dcb6 100644
> > --- a/checkpolicy/policy_scan.l
> > +++ b/checkpolicy/policy_scan.l
> > @@ -123,6 +123,8 @@ ATTRIBUTE |
> >  attribute                      { return(ATTRIBUTE); }
> >  EXPANDATTRIBUTE |
> >  expandattribute                 { return(EXPANDATTRIBUTE); }
> > +SEGREGATE_ATTRIBUTES |
> > +segregate_attributes           { return(SEGREGATEATTRIBUTES); }
> >  TYPE_TRANSITION |
> >  type_transition                        { return(TYPE_TRANSITION); }
> >  TYPE_MEMBER |
> > --
> > 2.36.1
> >
James Carter Aug. 11, 2023, 7:48 p.m. UTC | #3
On Fri, Aug 11, 2023 at 12:38 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> On Mon, 8 Aug 2022 at 19:09, James Carter <jwcart2@gmail.com> wrote:
> >
> > On Thu, Jul 21, 2022 at 11:11 AM Christian Göttsche
> > <cgzones@googlemail.com> wrote:
> > >
> > > Support specifying segregate attributes.
> > >
> > > The following two blocks are equivalent:
> > >
> > >     segregate_attributes attr1, attr2, attr3;
> > >
> > >     segregate_attributes attr1, attr2;
> > >     segregate_attributes attr1, attr3;
> > >     segregate_attributes attr2, attr3;
> > >
> > > Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> > > ---
> > >  checkpolicy/policy_define.c | 66 +++++++++++++++++++++++++++++++++++++
> > >  checkpolicy/policy_define.h |  1 +
> > >  checkpolicy/policy_parse.y  |  5 +++
> > >  checkpolicy/policy_scan.l   |  2 ++
> > >  4 files changed, 74 insertions(+)
> > >
> > > diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c
> > > index 8bf36859..cf6fbf08 100644
> > > --- a/checkpolicy/policy_define.c
> > > +++ b/checkpolicy/policy_define.c
> > > @@ -1220,6 +1220,72 @@ exit:
> > >         return rc;
> > >  }
> > >
> > > +int define_segregate_attributes(void)
> > > +{
> > > +       char *id = NULL;
> > > +       segregate_attributes_rule_t *sattr = NULL;
> > > +       int rc = -1;
> > > +
> > > +       if (pass == 1) {
> > > +               while ((id = queue_remove(id_queue)))
> > > +                       free(id);
> > > +               return 0;
> > > +       }
> > > +
> > > +       sattr = malloc(sizeof(segregate_attributes_rule_t));
> > > +       if (!sattr) {
> > > +               yyerror("Out of memory!");
> > > +               goto exit;
> > > +       }
> > > +
> > > +       ebitmap_init(&sattr->attrs);
> > > +
> > > +       while ((id = queue_remove(id_queue))) {
> > > +               const type_datum_t *attr;
> > > +
> > > +               if (!is_id_in_scope(SYM_TYPES, id)) {
> > > +                       yyerror2("attribute %s is not within scope", id);
> > > +                       goto exit;
> > > +               }
> > > +
> > > +               attr = hashtab_search(policydbp->p_types.table, id);
> > > +               if (!attr) {
> > > +                       yyerror2("attribute %s is not declared", id);
> > > +                       goto exit;
> > > +               }
> > > +
> > > +               if (attr->flavor != TYPE_ATTRIB) {
> > > +                       yyerror2("%s is a type, not an attribute", id);
> > > +                       goto exit;
> > > +               }
> > > +
> >
> > It seems like it would be useful to check a type, so an error would be
> > given if the type is associated with the attribute.
> >
>
> I am not exactly sure what you mean.
> Do you like to have a policy statement like
>
>     nevertypeattribute TYPE ATTRIBUTE;
>
> that checks at compile time a type is not associated with an attribute?
>
> > > +               if (ebitmap_get_bit(&sattr->attrs, attr->s.value - 1)) {
> > > +                       yyerror2("attribute %s used multiple times", id);
> > > +                       goto exit;
> > > +               }
> > > +
> > > +               if (ebitmap_set_bit(&sattr->attrs, attr->s.value - 1, TRUE)) {
> > > +                       yyerror("Out of memory!");
> > > +                       goto exit;
> > > +               }
> > > +
> > > +               free(id);
> > > +       }
> > > +
> > > +       sattr->next = policydbp->segregate_attributes;
> > > +       policydbp->segregate_attributes = sattr;
> > > +
> > > +       sattr = NULL;
> > > +       rc = 0;
> > > +exit:
> > > +       if (sattr) {
> > > +               ebitmap_destroy(&sattr->attrs);
> > > +               free(sattr);
> > > +       }
> > > +       free(id);
> > > +       return rc;
> > > +}
> > > +
> > >  static int add_aliases_to_type(type_datum_t * type)
> > >  {
> > >         char *id;
> > > diff --git a/checkpolicy/policy_define.h b/checkpolicy/policy_define.h
> > > index 50a7ba78..f55d0b17 100644
> > > --- a/checkpolicy/policy_define.h
> > > +++ b/checkpolicy/policy_define.h
> > > @@ -68,6 +68,7 @@ int define_type(int alias);
> > >  int define_user(void);
> > >  int define_validatetrans(constraint_expr_t *expr);
> > >  int expand_attrib(void);
> > > +int define_segregate_attributes(void);
> > >  int insert_id(const char *id,int push);
> > >  int insert_separator(int push);
> > >  role_datum_t *define_role_dom(role_datum_t *r);
> > > diff --git a/checkpolicy/policy_parse.y b/checkpolicy/policy_parse.y
> > > index 45f973ff..acd6096d 100644
> > > --- a/checkpolicy/policy_parse.y
> > > +++ b/checkpolicy/policy_parse.y
> > > @@ -104,6 +104,7 @@ typedef int (* require_func_t)(int pass);
> > >  %token ALIAS
> > >  %token ATTRIBUTE
> > >  %token EXPANDATTRIBUTE
> > > +%token SEGREGATEATTRIBUTES
> > >  %token BOOL
> > >  %token TUNABLE
> > >  %token IF
> > > @@ -320,6 +321,7 @@ rbac_decl           : attribute_role_def
> > >                         ;
> > >  te_decl                        : attribute_def
> > >                          | expandattribute_def
> > > +                        | segregateattributes_def
> > >                          | type_def
> > >                          | typealias_def
> > >                          | typeattribute_def
> > > @@ -337,6 +339,9 @@ attribute_def           : ATTRIBUTE identifier ';'
> > >  expandattribute_def     : EXPANDATTRIBUTE names bool_val ';'
> > >                          { if (expand_attrib()) return -1;}
> > >                          ;
> > > +segregateattributes_def : SEGREGATEATTRIBUTES identifier ',' id_comma_list ';'
> > > +                        { if (define_segregate_attributes()) return -1;}
> > > +
> >
> > I don't see the need for comparing more than two at a time.
> >
> > Something like:
> > disjoint_types attr1 attr2;
>
> That would lead to quadratic growth of statements, for example in the
> Reference Policy example of
>
>     ibendport_type, packet_type, sysctl_type, device_node,
> ibpkey_type, sysfs_types, domain, boolean_type, netif_type, file_type,
> node_type, proc_type, port_type
>
> Also one could see supporting more than two attributes as syntactic
> sugar, which the traditional language already supports, e.g.
>
>     allow { TYPE1 TYPE2 } { TYPE3 TYPE4 } : { CLASS1 CLASS2 } perm_list;
>

The case above would be a pain to do and making it a list would be
better. I guess using a list is not that big of a deal.

The problem with checking that attributes are disjoint is that it does
not tell me *why* they should be disjoint.
It would be better to use more neverallow rules because they express
the goals of the security policy.
If a neverallow rule cannot be written to say why two attributes
should be disjoint, then either the policy is not fine-grained enough
for it to matter or there is a problem with the policy.

Jim


> >
> > Thanks,
> > Jim
> >
> >                        ;
> > >  type_def               : TYPE identifier alias_def opt_attr_list ';'
> > >                          {if (define_type(1)) return -1;}
> > >                         | TYPE identifier opt_attr_list ';'
> > > diff --git a/checkpolicy/policy_scan.l b/checkpolicy/policy_scan.l
> > > index 9fefea7b..d865dcb6 100644
> > > --- a/checkpolicy/policy_scan.l
> > > +++ b/checkpolicy/policy_scan.l
> > > @@ -123,6 +123,8 @@ ATTRIBUTE |
> > >  attribute                      { return(ATTRIBUTE); }
> > >  EXPANDATTRIBUTE |
> > >  expandattribute                 { return(EXPANDATTRIBUTE); }
> > > +SEGREGATE_ATTRIBUTES |
> > > +segregate_attributes           { return(SEGREGATEATTRIBUTES); }
> > >  TYPE_TRANSITION |
> > >  type_transition                        { return(TYPE_TRANSITION); }
> > >  TYPE_MEMBER |
> > > --
> > > 2.36.1
> > >
diff mbox series

Patch

diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c
index 8bf36859..cf6fbf08 100644
--- a/checkpolicy/policy_define.c
+++ b/checkpolicy/policy_define.c
@@ -1220,6 +1220,72 @@  exit:
 	return rc;
 }
 
+int define_segregate_attributes(void)
+{
+	char *id = NULL;
+	segregate_attributes_rule_t *sattr = NULL;
+	int rc = -1;
+
+	if (pass == 1) {
+		while ((id = queue_remove(id_queue)))
+			free(id);
+		return 0;
+	}
+
+	sattr = malloc(sizeof(segregate_attributes_rule_t));
+	if (!sattr) {
+		yyerror("Out of memory!");
+		goto exit;
+	}
+
+	ebitmap_init(&sattr->attrs);
+
+	while ((id = queue_remove(id_queue))) {
+		const type_datum_t *attr;
+
+		if (!is_id_in_scope(SYM_TYPES, id)) {
+			yyerror2("attribute %s is not within scope", id);
+			goto exit;
+		}
+
+		attr = hashtab_search(policydbp->p_types.table, id);
+		if (!attr) {
+			yyerror2("attribute %s is not declared", id);
+			goto exit;
+		}
+
+		if (attr->flavor != TYPE_ATTRIB) {
+			yyerror2("%s is a type, not an attribute", id);
+			goto exit;
+		}
+
+		if (ebitmap_get_bit(&sattr->attrs, attr->s.value - 1)) {
+			yyerror2("attribute %s used multiple times", id);
+			goto exit;
+		}
+
+		if (ebitmap_set_bit(&sattr->attrs, attr->s.value - 1, TRUE)) {
+			yyerror("Out of memory!");
+			goto exit;
+		}
+
+		free(id);
+	}
+
+	sattr->next = policydbp->segregate_attributes;
+	policydbp->segregate_attributes = sattr;
+
+	sattr = NULL;
+	rc = 0;
+exit:
+	if (sattr) {
+		ebitmap_destroy(&sattr->attrs);
+		free(sattr);
+	}
+	free(id);
+	return rc;
+}
+
 static int add_aliases_to_type(type_datum_t * type)
 {
 	char *id;
diff --git a/checkpolicy/policy_define.h b/checkpolicy/policy_define.h
index 50a7ba78..f55d0b17 100644
--- a/checkpolicy/policy_define.h
+++ b/checkpolicy/policy_define.h
@@ -68,6 +68,7 @@  int define_type(int alias);
 int define_user(void);
 int define_validatetrans(constraint_expr_t *expr);
 int expand_attrib(void);
+int define_segregate_attributes(void);
 int insert_id(const char *id,int push);
 int insert_separator(int push);
 role_datum_t *define_role_dom(role_datum_t *r);
diff --git a/checkpolicy/policy_parse.y b/checkpolicy/policy_parse.y
index 45f973ff..acd6096d 100644
--- a/checkpolicy/policy_parse.y
+++ b/checkpolicy/policy_parse.y
@@ -104,6 +104,7 @@  typedef int (* require_func_t)(int pass);
 %token ALIAS
 %token ATTRIBUTE
 %token EXPANDATTRIBUTE
+%token SEGREGATEATTRIBUTES
 %token BOOL
 %token TUNABLE
 %token IF
@@ -320,6 +321,7 @@  rbac_decl		: attribute_role_def
 			;
 te_decl			: attribute_def
                         | expandattribute_def
+                        | segregateattributes_def
                         | type_def
                         | typealias_def
                         | typeattribute_def
@@ -337,6 +339,9 @@  attribute_def           : ATTRIBUTE identifier ';'
 expandattribute_def     : EXPANDATTRIBUTE names bool_val ';'
                         { if (expand_attrib()) return -1;}
                         ;
+segregateattributes_def : SEGREGATEATTRIBUTES identifier ',' id_comma_list ';'
+                        { if (define_segregate_attributes()) return -1;}
+                        ;
 type_def		: TYPE identifier alias_def opt_attr_list ';'
                         {if (define_type(1)) return -1;}
 	                | TYPE identifier opt_attr_list ';'
diff --git a/checkpolicy/policy_scan.l b/checkpolicy/policy_scan.l
index 9fefea7b..d865dcb6 100644
--- a/checkpolicy/policy_scan.l
+++ b/checkpolicy/policy_scan.l
@@ -123,6 +123,8 @@  ATTRIBUTE |
 attribute			{ return(ATTRIBUTE); }
 EXPANDATTRIBUTE |
 expandattribute                 { return(EXPANDATTRIBUTE); }
+SEGREGATE_ATTRIBUTES |
+segregate_attributes		{ return(SEGREGATEATTRIBUTES); }
 TYPE_TRANSITION |
 type_transition			{ return(TYPE_TRANSITION); }
 TYPE_MEMBER |