---
checkpolicy/policy_define.c | 82 ++++++++++++++++++++++++++++++
checkpolicy/policy_define.h | 1 +
checkpolicy/policy_parse.y | 5 ++
checkpolicy/policy_scan.l | 2 +
libsepol/cil/src/cil.c | 15 ++++++
libsepol/cil/src/cil_build_ast.c | 72 ++++++++++++++++++++++++++
libsepol/cil/src/cil_build_ast.h | 2 +
libsepol/cil/src/cil_copy_ast.c | 26 ++++++++++
libsepol/cil/src/cil_flavor.h | 1 +
libsepol/cil/src/cil_internal.h | 16 ++++--
libsepol/cil/src/cil_post.c | 8 +++
libsepol/cil/src/cil_reset_ast.c | 1 +
libsepol/cil/src/cil_resolve_ast.c | 53 ++++++++++++++++++-
libsepol/cil/src/cil_tree.c | 11 ++++
libsepol/include/sepol/policydb/policydb.h | 6 ++-
libsepol/src/module_to_cil.c | 11 ++++
16 files changed, 307 insertions(+), 5 deletions(-)
diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c
index 949ca711..63e3c53f 100644
--- a/checkpolicy/policy_define.c
+++ b/checkpolicy/policy_define.c
@@ -1139,6 +1139,88 @@ int define_attrib(void)
return 0;
}
+int expand_attrib(void)
+{
+ char *id;
+ ebitmap_t attrs;
+ type_datum_t *attr;
+ ebitmap_node_t *node;
+ uint32_t i;
+ int rc = -1;
+ int flags = 0;
+
+ if (pass == 1) {
+ for (i = 0; i < 2; i++) {
+ while ((id = queue_remove(id_queue))) {
+ free(id);
+ }
+ }
+ return 0;
+ }
+
+ ebitmap_init(&attrs);
+ while ((id = queue_remove(id_queue))) {
+ if (!id) {
+ yyerror("No attribute name for expandattribute statement?");
+ goto exit;
+ }
+
+ 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 (attr->flags & TYPE_FLAGS_EXPAND_ATTR) {
+ yyerror2("%s already has the expandattribute option specified", id);
+ goto exit;
+ }
+ if (ebitmap_set_bit(&attrs, attr->s.value - 1, TRUE)) {
+ yyerror("Out of memory!");
+ goto exit;
+ }
+
+ free(id);
+ }
+
+ id = (char *) queue_remove(id_queue);
+ if (!id) {
+ yyerror("No option specified for attribute expansion.");
+ goto exit;
+ }
+
+ if (!strcmp(id, "T")) {
+ flags = TYPE_FLAGS_EXPAND_ATTR_TRUE;
+ } else {
+ flags = TYPE_FLAGS_EXPAND_ATTR_FALSE;
+ }
+
+ ebitmap_for_each_bit(&attrs, node, i) {
+ if (!ebitmap_node_get_bit(node, i)){
+ continue;
+ }
+ attr = hashtab_search(policydbp->p_types.table,
+ policydbp->sym_val_to_name[SYM_TYPES][i]);
+ attr->flags |= flags;
+ }
+
+ rc = 0;
+exit:
+ ebitmap_destroy(&attrs);
+ 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 964baae0..9f4b6d0d 100644
--- a/checkpolicy/policy_define.h
+++ b/checkpolicy/policy_define.h
@@ -65,6 +65,7 @@ int define_typebounds(void);
int define_type(int alias);
int define_user(void);
int define_validatetrans(constraint_expr_t *expr);
+int expand_attrib(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 3b6a2f86..1ac1c96b 100644
--- a/checkpolicy/policy_parse.y
+++ b/checkpolicy/policy_parse.y
@@ -103,6 +103,7 @@ typedef int (* require_func_t)(int pass);
%token TYPES
%token ALIAS
%token ATTRIBUTE
+%token EXPANDATTRIBUTE
%token BOOL
%token TUNABLE
%token IF
@@ -314,6 +315,7 @@ rbac_decl : attribute_role_def
| role_attr_def
;
te_decl : attribute_def
+ | expandattribute_def
| type_def
| typealias_def
| typeattribute_def
@@ -328,6 +330,9 @@ te_decl : attribute_def
attribute_def : ATTRIBUTE identifier ';'
{ if (define_attrib()) return -1;}
;
+expandattribute_def : EXPANDATTRIBUTE names bool_val ';'
+ { if (expand_attrib()) 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 2f7f2216..028bd25e 100644
--- a/checkpolicy/policy_scan.l
+++ b/checkpolicy/policy_scan.l
@@ -106,6 +106,8 @@ ALIAS |
alias { return(ALIAS); }
ATTRIBUTE |
attribute { return(ATTRIBUTE); }
+EXPANDATTRIBUTE |
+expandattribute { return(EXPANDATTRIBUTE); }
TYPE_TRANSITION |
type_transition { return(TYPE_TRANSITION); }
TYPE_MEMBER |
diff --git a/libsepol/cil/src/cil.c b/libsepol/cil/src/cil.c
index a64c5284..9b9ccc36 100644
--- a/libsepol/cil/src/cil.c
+++ b/libsepol/cil/src/cil.c
@@ -159,6 +159,7 @@ static void cil_init_keys(void)
CIL_KEY_SELINUXUSERDEFAULT = cil_strpool_add("selinuxuserdefault");
CIL_KEY_TYPEATTRIBUTE = cil_strpool_add("typeattribute");
CIL_KEY_TYPEATTRIBUTESET = cil_strpool_add("typeattributeset");
+ CIL_KEY_EXPANDTYPEATTRIBUTE = cil_strpool_add("expandtypeattribute");
CIL_KEY_TYPEALIAS = cil_strpool_add("typealias");
CIL_KEY_TYPEALIASACTUAL = cil_strpool_add("typealiasactual");
CIL_KEY_TYPEBOUNDS = cil_strpool_add("typebounds");
@@ -623,6 +624,9 @@ void cil_destroy_data(void **data, enum cil_flavor flavor)
case CIL_TYPEATTRIBUTESET:
cil_destroy_typeattributeset(*data);
break;
+ case CIL_EXPANDTYPEATTRIBUTE:
+ cil_destroy_expandtypeattribute(*data);
+ break;
case CIL_TYPEALIASACTUAL:
cil_destroy_aliasactual(*data);
break;
@@ -987,6 +991,8 @@ const char * cil_node_to_string(struct cil_tree_node *node)
return CIL_KEY_TYPEALIAS;
case CIL_TYPEATTRIBUTESET:
return CIL_KEY_TYPEATTRIBUTESET;
+ case CIL_EXPANDTYPEATTRIBUTE:
+ return CIL_KEY_EXPANDTYPEATTRIBUTE;
case CIL_TYPEALIASACTUAL:
return CIL_KEY_TYPEALIASACTUAL;
case CIL_TYPEBOUNDS:
@@ -2038,6 +2044,15 @@ void cil_typeattributeset_init(struct cil_typeattributeset **attrset)
(*attrset)->datum_expr = NULL;
}
+void cil_expandtypeattribute_init(struct cil_expandtypeattribute **expandattr)
+{
+ *expandattr = cil_malloc(sizeof(**expandattr));
+
+ (*expandattr)->attr_strs = NULL;
+ (*expandattr)->attr_datums = NULL;
+ (*expandattr)->expand = 0;
+}
+
void cil_alias_init(struct cil_alias **alias)
{
*alias = cil_malloc(sizeof(**alias));
diff --git a/libsepol/cil/src/cil_build_ast.c b/libsepol/cil/src/cil_build_ast.c
index 4b03dc35..36cc6735 100644
--- a/libsepol/cil/src/cil_build_ast.c
+++ b/libsepol/cil/src/cil_build_ast.c
@@ -3176,6 +3176,75 @@ void cil_destroy_typeattributeset(struct cil_typeattributeset *attrset)
free(attrset);
}
+int cil_gen_expandtypeattribute(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_STRING | CIL_SYN_LIST,
+ CIL_SYN_STRING,
+ CIL_SYN_END
+ };
+ char *expand_str;
+ int syntax_len = sizeof(syntax)/sizeof(*syntax);
+ struct cil_expandtypeattribute *expandattr = 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_expandtypeattribute_init(&expandattr);
+
+ if (parse_current->next->cl_head == NULL) {
+ cil_list_init(&expandattr->attr_strs, CIL_TYPE);
+ cil_list_append(expandattr->attr_strs, CIL_STRING, parse_current->next->data);
+ } else {
+ rc = cil_fill_list(parse_current->next->cl_head, CIL_TYPE, &expandattr->attr_strs);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ }
+
+ expand_str = parse_current->next->next->data;
+
+ if (expand_str == CIL_KEY_CONDTRUE) {
+ expandattr->expand = CIL_TRUE;
+ } else if (expand_str == CIL_KEY_CONDFALSE) {
+ expandattr->expand = CIL_FALSE;
+ } else {
+ cil_log(CIL_ERR, "Value must be either \'true\' or \'false\'");
+ goto exit;
+ }
+
+ ast_node->data = expandattr;
+ ast_node->flavor = CIL_EXPANDTYPEATTRIBUTE;
+
+ return SEPOL_OK;
+
+exit:
+ cil_tree_log(parse_current, CIL_ERR, "Bad expandtypeattribute statement");
+ cil_destroy_expandtypeattribute(expandattr);
+ return rc;
+}
+
+void cil_destroy_expandtypeattribute(struct cil_expandtypeattribute *expandattr)
+{
+ if (expandattr == NULL) {
+ return;
+ }
+
+ cil_list_destroy(&expandattr->attr_strs, CIL_TRUE);
+
+ cil_list_destroy(&expandattr->attr_datums, CIL_FALSE);
+
+ free(expandattr);
+}
+
int cil_gen_typepermissive(struct cil_db *db, struct cil_tree_node *parse_current, struct cil_tree_node *ast_node)
{
enum cil_syntax syntax[] = {
@@ -6013,6 +6082,9 @@ int __cil_build_ast_node_helper(struct cil_tree_node *parse_current, uint32_t *f
} else if (parse_current->data == CIL_KEY_TYPEATTRIBUTESET) {
rc = cil_gen_typeattributeset(db, parse_current, ast_node);
*finished = CIL_TREE_SKIP_NEXT;
+ } else if (parse_current->data == CIL_KEY_EXPANDTYPEATTRIBUTE) {
+ rc = cil_gen_expandtypeattribute(db, parse_current, ast_node);
+ *finished = CIL_TREE_SKIP_NEXT;
} else if (parse_current->data == CIL_KEY_TYPEALIAS) {
rc = cil_gen_alias(db, parse_current, ast_node, CIL_TYPEALIAS);
} else if (parse_current->data == CIL_KEY_TYPEALIASACTUAL) {
diff --git a/libsepol/cil/src/cil_build_ast.h b/libsepol/cil/src/cil_build_ast.h
index 54662035..33bae997 100644
--- a/libsepol/cil/src/cil_build_ast.h
+++ b/libsepol/cil/src/cil_build_ast.h
@@ -138,6 +138,8 @@ int cil_gen_aliasactual(struct cil_db *db, struct cil_tree_node *parse_current,
void cil_destroy_aliasactual(struct cil_aliasactual *aliasactual);
int cil_gen_typeattributeset(struct cil_db *db, struct cil_tree_node *parse_current, struct cil_tree_node *ast_node);
void cil_destroy_typeattributeset(struct cil_typeattributeset *attrtypes);
+int cil_gen_expandtypeattribute(struct cil_db *db, struct cil_tree_node *parse_current, struct cil_tree_node *ast_node);
+void cil_destroy_expandtypeattribute(struct cil_expandtypeattribute *expandattr);
int cil_gen_typebounds(struct cil_db *db, struct cil_tree_node *parse_current, struct cil_tree_node *ast_node);
int cil_gen_typepermissive(struct cil_db *db, struct cil_tree_node *parse_current, struct cil_tree_node *ast_node);
void cil_destroy_typepermissive(struct cil_typepermissive *typeperm);
diff --git a/libsepol/cil/src/cil_copy_ast.c b/libsepol/cil/src/cil_copy_ast.c
index 2d085dd7..d6685050 100644
--- a/libsepol/cil/src/cil_copy_ast.c
+++ b/libsepol/cil/src/cil_copy_ast.c
@@ -648,6 +648,29 @@ int cil_copy_typeattributeset(struct cil_db *db, void *data, void **copy, __attr
return SEPOL_OK;
}
+int cil_copy_expandtypeattribute(__attribute__((unused)) struct cil_db *db, void *data, void **copy, __attribute__((unused)) symtab_t *symtab)
+{
+ struct cil_expandtypeattribute *orig = data;
+ struct cil_expandtypeattribute *new = NULL;
+
+ fprintf(stderr, "%s %u\n", __func__, __LINE__);
+ cil_expandtypeattribute_init(&new);
+
+ if (orig->attr_strs != NULL) {
+ cil_copy_list(orig->attr_strs, &new->attr_strs);
+ }
+
+ if (orig->attr_datums != NULL) {
+ cil_copy_list(orig->attr_datums, &new->attr_datums);
+ }
+
+ new->expand = orig->expand;
+
+ *copy = new;
+
+ return SEPOL_OK;
+}
+
int cil_copy_alias(__attribute__((unused)) struct cil_db *db, void *data, void **copy, symtab_t *symtab)
{
struct cil_alias *orig = data;
@@ -1808,6 +1831,9 @@ int __cil_copy_node_helper(struct cil_tree_node *orig, __attribute__((unused)) u
case CIL_TYPEATTRIBUTESET:
copy_func = &cil_copy_typeattributeset;
break;
+ case CIL_EXPANDTYPEATTRIBUTE:
+ copy_func = &cil_copy_expandtypeattribute;
+ break;
case CIL_TYPEALIAS:
copy_func = &cil_copy_alias;
break;
diff --git a/libsepol/cil/src/cil_flavor.h b/libsepol/cil/src/cil_flavor.h
index cd08b972..c01f967a 100644
--- a/libsepol/cil/src/cil_flavor.h
+++ b/libsepol/cil/src/cil_flavor.h
@@ -73,6 +73,7 @@ enum cil_flavor {
CIL_ROLETYPE,
CIL_ROLEBOUNDS,
CIL_TYPEATTRIBUTESET,
+ CIL_EXPANDTYPEATTRIBUTE,
CIL_TYPEALIASACTUAL,
CIL_TYPEBOUNDS,
CIL_TYPEPERMISSIVE,
diff --git a/libsepol/cil/src/cil_internal.h b/libsepol/cil/src/cil_internal.h
index efa2cd6e..aee3f00c 100644
--- a/libsepol/cil/src/cil_internal.h
+++ b/libsepol/cil/src/cil_internal.h
@@ -174,6 +174,7 @@ char *CIL_KEY_SELINUXUSER;
char *CIL_KEY_SELINUXUSERDEFAULT;
char *CIL_KEY_TYPEATTRIBUTE;
char *CIL_KEY_TYPEATTRIBUTESET;
+char *CIL_KEY_EXPANDTYPEATTRIBUTE;
char *CIL_KEY_TYPEALIAS;
char *CIL_KEY_TYPEALIASACTUAL;
char *CIL_KEY_TYPEBOUNDS;
@@ -515,9 +516,11 @@ struct cil_type {
int value;
};
-#define CIL_ATTR_AVRULE 0x01
-#define CIL_ATTR_NEVERALLOW 0x02
-#define CIL_ATTR_CONSTRAINT 0x04
+#define CIL_ATTR_AVRULE (1 << 0)
+#define CIL_ATTR_NEVERALLOW (1 << 1)
+#define CIL_ATTR_CONSTRAINT (1 << 2)
+#define CIL_ATTR_EXPAND_TRUE (1 << 3)
+#define CIL_ATTR_EXPAND_FALSE (1 << 4)
struct cil_typeattribute {
struct cil_symtab_datum datum;
struct cil_list *expr_list;
@@ -531,6 +534,12 @@ struct cil_typeattributeset {
struct cil_list *datum_expr;
};
+struct cil_expandtypeattribute {
+ struct cil_list *attr_strs;
+ struct cil_list *attr_datums;
+ int expand;
+};
+
struct cil_typepermissive {
char *type_str;
void *type; /* type or alias */
@@ -977,6 +986,7 @@ void cil_roleattributeset_init(struct cil_roleattributeset **attrset);
void cil_roletype_init(struct cil_roletype **roletype);
void cil_typeattribute_init(struct cil_typeattribute **attribute);
void cil_typeattributeset_init(struct cil_typeattributeset **attrset);
+void cil_expandtypeattribute_init(struct cil_expandtypeattribute **expandattr);
void cil_alias_init(struct cil_alias **alias);
void cil_aliasactual_init(struct cil_aliasactual **aliasactual);
void cil_typepermissive_init(struct cil_typepermissive **typeperm);
diff --git a/libsepol/cil/src/cil_post.c b/libsepol/cil/src/cil_post.c
index e32a8fc9..1941fab3 100644
--- a/libsepol/cil/src/cil_post.c
+++ b/libsepol/cil/src/cil_post.c
@@ -1194,6 +1194,14 @@ static int cil_typeattribute_used(struct cil_typeattribute *attr, struct cil_db
return CIL_FALSE;
}
+ if (attr->used & CIL_ATTR_EXPAND_FALSE) {
+ return CIL_TRUE;
+ }
+
+ if (attr->used & CIL_ATTR_EXPAND_TRUE) {
+ return CIL_FALSE;
+ }
+
if (attr->used & CIL_ATTR_CONSTRAINT) {
return CIL_TRUE;
}
diff --git a/libsepol/cil/src/cil_reset_ast.c b/libsepol/cil/src/cil_reset_ast.c
index de00679e..676e156e 100644
--- a/libsepol/cil/src/cil_reset_ast.c
+++ b/libsepol/cil/src/cil_reset_ast.c
@@ -549,6 +549,7 @@ int __cil_reset_node(struct cil_tree_node *node, __attribute__((unused)) uint32
case CIL_CLASSORDER:
case CIL_CATORDER:
case CIL_SENSITIVITYORDER:
+ case CIL_EXPANDTYPEATTRIBUTE:
break; /* Nothing to reset */
default:
break;
diff --git a/libsepol/cil/src/cil_resolve_ast.c b/libsepol/cil/src/cil_resolve_ast.c
index 6da44ba1..8925b271 100644
--- a/libsepol/cil/src/cil_resolve_ast.c
+++ b/libsepol/cil/src/cil_resolve_ast.c
@@ -271,14 +271,24 @@ exit:
int cil_type_used(struct cil_symtab_datum *datum, int used)
{
+ int rc = SEPOL_ERR;
struct cil_typeattribute *attr = NULL;
if (FLAVOR(datum) == CIL_TYPEATTRIBUTE) {
attr = (struct cil_typeattribute*)datum;
attr->used |= used;
+ if ((attr->used & CIL_ATTR_EXPAND_TRUE) &&
+ (attr->used & CIL_ATTR_EXPAND_FALSE)) {
+ cil_log(CIL_ERR, "Conflicting use of expandtypeattribute. "
+ "Expandtypeattribute may be set to true or false "
+ "but not both. \n");
+ goto exit;
+ }
}
- return 0;
+ return SEPOL_OK;
+exit:
+ return rc;
}
int cil_resolve_permissionx(struct cil_tree_node *current, struct cil_permissionx *permx, void *extra_args)
@@ -453,6 +463,44 @@ exit:
return rc;
}
+int cil_resolve_expandtypeattribute(struct cil_tree_node *current, void *extra_args)
+{
+ struct cil_expandtypeattribute *expandattr = current->data;
+ struct cil_symtab_datum *attr_datum = NULL;
+ struct cil_tree_node *attr_node = NULL;
+ struct cil_list_item *curr;
+ int used;
+ int rc = SEPOL_ERR;
+
+ cil_list_init(&expandattr->attr_datums, CIL_TYPE);
+
+ cil_list_for_each(curr, expandattr->attr_strs) {
+ rc = cil_resolve_name(current, (char *)curr->data, CIL_SYM_TYPES, extra_args, &attr_datum);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+
+ attr_node = attr_datum->nodes->head->data;
+
+ if (attr_node->flavor != CIL_TYPEATTRIBUTE) {
+ rc = SEPOL_ERR;
+ cil_log(CIL_ERR, "Attribute type not an attribute\n");
+ goto exit;
+ }
+ used = expandattr->expand ? CIL_ATTR_EXPAND_TRUE : CIL_ATTR_EXPAND_FALSE;
+ rc = cil_type_used(attr_datum, used);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+
+ cil_list_append(expandattr->attr_datums, CIL_TYPE, attr_datum);
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
int cil_resolve_aliasactual(struct cil_tree_node *current, void *extra_args, enum cil_flavor flavor, enum cil_flavor alias_flavor)
{
int rc = SEPOL_ERR;
@@ -3432,6 +3480,9 @@ int __cil_resolve_ast_node(struct cil_tree_node *node, void *extra_args)
case CIL_TYPEATTRIBUTESET:
rc = cil_resolve_typeattributeset(node, args);
break;
+ case CIL_EXPANDTYPEATTRIBUTE:
+ rc = cil_resolve_expandtypeattribute(node, args);
+ break;
case CIL_TYPEBOUNDS:
rc = cil_resolve_bounds(node, args, CIL_TYPE, CIL_TYPEATTRIBUTE);
break;
diff --git a/libsepol/cil/src/cil_tree.c b/libsepol/cil/src/cil_tree.c
index 9ff9d4b4..2cc2744a 100644
--- a/libsepol/cil/src/cil_tree.c
+++ b/libsepol/cil/src/cil_tree.c
@@ -703,6 +703,17 @@ void cil_tree_print_node(struct cil_tree_node *node)
cil_log(CIL_INFO, "TYPE: %s\n", type->datum.name);
return;
}
+ case CIL_EXPANDTYPEATTRIBUTE: {
+ struct cil_expandtypeattribute *attr = node->data;
+
+ fprintf(stderr, "%s %u\n", __func__, __LINE__);
+ cil_log(CIL_INFO, "(EXPANDTYPEATTRIBUTE ");
+ cil_tree_print_expr(attr->attr_datums, attr->attr_strs);
+ cil_log(CIL_INFO, "%s)\n",attr->expand ?
+ CIL_KEY_CONDTRUE : CIL_KEY_CONDFALSE);
+
+ return;
+ }
case CIL_TYPEATTRIBUTESET: {
struct cil_typeattributeset *attr = node->data;
diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h
index 4336a3f2..37e0c9e5 100644
--- a/libsepol/include/sepol/policydb/policydb.h
+++ b/libsepol/include/sepol/policydb/policydb.h
@@ -178,7 +178,11 @@ typedef struct type_datum {
#define TYPE_ALIAS 2 /* alias in modular policy */
uint32_t flavor;
ebitmap_t types; /* types with this attribute */
-#define TYPE_FLAGS_PERMISSIVE 0x01
+#define TYPE_FLAGS_PERMISSIVE (1 << 0)
+#define TYPE_FLAGS_EXPAND_ATTR_TRUE (1 << 1)
+#define TYPE_FLAGS_EXPAND_ATTR_FALSE (1 << 2)
+#define TYPE_FLAGS_EXPAND_ATTR (TYPE_FLAGS_EXPAND_ATTR_TRUE | \
+ TYPE_FLAGS_EXPAND_ATTR_FALSE)
uint32_t flags;
uint32_t bounds; /* bounds type, if exist */
} type_datum_t;
diff --git a/libsepol/src/module_to_cil.c b/libsepol/src/module_to_cil.c
index ac095c30..7d8eb204 100644
--- a/libsepol/src/module_to_cil.c
+++ b/libsepol/src/module_to_cil.c
@@ -2244,6 +2244,17 @@ static int type_to_cil(int indent, struct policydb *pdb, struct avrule_block *UN
cil_println(indent, "(typeattribute %s)", key);
}
+ if (type->flags & TYPE_FLAGS_EXPAND_ATTR) {
+ cil_indent(indent);
+ cil_printf("(expandtypeattribute (%s) ", key);
+ if (type->flags & TYPE_FLAGS_EXPAND_ATTR_TRUE) {
+ cil_printf("true");
+ } else if (type->flags & TYPE_FLAGS_EXPAND_ATTR_FALSE) {
+ cil_printf("false");
+ }
+ cil_printf(")\n");
+ }
+
if (ebitmap_cardinality(&type->types) > 0) {
cil_indent(indent);
cil_printf("(typeattributeset %s (", key);