diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c index 1c019a3b89..6ea544d0b6 100644 --- a/checkpolicy/policy_define.c +++ b/checkpolicy/policy_define.c @@ -1253,6 +1253,72 @@ int expand_attrib(void) return rc; } +int define_disjoint_attributes(void) +{ + char *id = NULL; + disjoint_attributes_rule_t *dattr = NULL; + int rc = -1; + + if (pass == 1) { + while ((id = queue_remove(id_queue))) + free(id); + return 0; + } + + dattr = malloc(sizeof(disjoint_attributes_rule_t)); + if (!dattr) { + yyerror("Out of memory!"); + goto exit; + } + + ebitmap_init(&dattr->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(&dattr->attrs, attr->s.value - 1)) { + yyerror2("attribute %s used multiple times", id); + goto exit; + } + + if (ebitmap_set_bit(&dattr->attrs, attr->s.value - 1, TRUE)) { + yyerror("Out of memory!"); + goto exit; + } + + free(id); + } + + dattr->next = policydbp->disjoint_attributes; + policydbp->disjoint_attributes = dattr; + + dattr = NULL; + rc = 0; +exit: + if (dattr) { + ebitmap_destroy(&dattr->attrs); + free(dattr); + } + 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 bcbfe4f332..274d7cff8a 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_disjoint_attributes(void); int insert_id(const char *id,int push); int insert_separator(int push); uintptr_t define_cexpr(uint32_t expr_type, uintptr_t arg1, uintptr_t arg2); diff --git a/checkpolicy/policy_parse.y b/checkpolicy/policy_parse.y index 1b275ebc1c..d9bfc9b1ae 100644 --- a/checkpolicy/policy_parse.y +++ b/checkpolicy/policy_parse.y @@ -102,6 +102,7 @@ typedef int (* require_func_t)(int pass); %token ALIAS %token ATTRIBUTE %token EXPANDATTRIBUTE +%token DISJOINTATTRIBUTES %token BOOL %token TUNABLE %token IF @@ -318,6 +319,7 @@ rbac_decl : attribute_role_def ; te_decl : attribute_def | expandattribute_def + | disjointattributes_def | type_def | typealias_def | typeattribute_def @@ -335,6 +337,9 @@ attribute_def : ATTRIBUTE identifier ';' expandattribute_def : EXPANDATTRIBUTE names bool_val ';' { if (expand_attrib()) YYABORT;} ; +disjointattributes_def : DISJOINTATTRIBUTES identifier ',' id_comma_list ';' + { if (define_disjoint_attributes()) return -1;} + ; type_def : TYPE identifier alias_def opt_attr_list ';' {if (define_type(1)) YYABORT;} | TYPE identifier opt_attr_list ';' diff --git a/checkpolicy/policy_scan.l b/checkpolicy/policy_scan.l index e46677a88d..a3797e52aa 100644 --- a/checkpolicy/policy_scan.l +++ b/checkpolicy/policy_scan.l @@ -137,6 +137,8 @@ ATTRIBUTE | attribute { return(ATTRIBUTE); } EXPANDATTRIBUTE | expandattribute { return(EXPANDATTRIBUTE); } +DISJOINT_ATTRIBUTES | +disjoint_attributes { return(DISJOINTATTRIBUTES); } TYPE_TRANSITION | type_transition { return(TYPE_TRANSITION); } TYPE_MEMBER | diff --git a/checkpolicy/tests/policy_allonce.conf b/checkpolicy/tests/policy_allonce.conf index 34e6402df7..6132b29ead 100644 --- a/checkpolicy/tests/policy_allonce.conf +++ b/checkpolicy/tests/policy_allonce.conf @@ -18,6 +18,7 @@ attribute ATTR1; attribute ATTR2; expandattribute ATTR1 true; expandattribute ATTR2 false; +disjoint_attributes ATTR1, ATTR2; type TYPE1; type TYPE2, ATTR1; type TYPE3 alias { TYPEALIAS3A TYPEALIAS3B }; diff --git a/libsepol/cil/src/cil.c b/libsepol/cil/src/cil.c index 067e28a6a1..cdd4f3fc57 100644 --- a/libsepol/cil/src/cil.c +++ b/libsepol/cil/src/cil.c @@ -228,6 +228,7 @@ char *CIL_KEY_SRC_HLL_LMS; char *CIL_KEY_SRC_HLL_LMX; char *CIL_KEY_SRC_HLL_LME; char *CIL_KEY_DENY_RULE; +char *CIL_KEY_DISJOINTATTRIBUTES; static void cil_init_keys(void) { @@ -400,6 +401,7 @@ static void cil_init_keys(void) CIL_KEY_SRC_HLL_LMX = cil_strpool_add("lmx"); CIL_KEY_SRC_HLL_LME = cil_strpool_add("lme"); CIL_KEY_DENY_RULE = cil_strpool_add("deny"); + CIL_KEY_DISJOINTATTRIBUTES = cil_strpool_add("disjointattributes"); } void cil_db_init(struct cil_db **db) @@ -432,6 +434,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)->declared_strings, CIL_LIST_ITEM); + cil_list_init(&(*db)->disjointattributes, CIL_LIST_ITEM); cil_type_init(&(*db)->selftype); (*db)->selftype->datum.name = CIL_KEY_SELF; @@ -504,6 +507,7 @@ void cil_db_destroy(struct cil_db **db) cil_sort_destroy(&(*db)->fsuse); cil_list_destroy(&(*db)->userprefixes, CIL_FALSE); cil_list_destroy(&(*db)->selinuxusers, CIL_FALSE); + cil_list_destroy(&(*db)->disjointattributes, CIL_FALSE); cil_declared_strings_list_destroy(&(*db)->declared_strings); @@ -1084,6 +1088,9 @@ void cil_destroy_data(void **data, enum cil_flavor flavor) case CIL_SRC_INFO: cil_destroy_src_info(*data); break; + case CIL_DISJOINTATTRIBUTES: + cil_destroy_disjointattributes(*data); + break; case CIL_OP: case CIL_CONS_OPERAND: break; @@ -1492,6 +1499,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_DISJOINTATTRIBUTES: + return CIL_KEY_DISJOINTATTRIBUTES; default: break; @@ -2984,3 +2993,11 @@ void cil_src_info_init(struct cil_src_info **info) (*info)->hll_line = 0; (*info)->path = NULL; } + +void cil_disjointattributes_init(struct cil_disjointattributes **dattrs) +{ + *dattrs = cil_malloc(sizeof(**dattrs)); + + (*dattrs)->str_expr = NULL; + (*dattrs)->datum_expr = NULL; +} diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c index 95bd18baaa..96031cbe23 100644 --- a/libsepol/cil/src/cil_binary.c +++ b/libsepol/cil/src/cil_binary.c @@ -3896,6 +3896,38 @@ static int cil_defaultrange_to_policydb(policydb_t *pdb, struct cil_defaultrange return SEPOL_ERR; } +static int cil_disjointattributes_to_policydb(policydb_t *pdb, const struct cil_disjointattributes *dattrs) +{ + disjoint_attributes_rule_t *dattr; + struct cil_list_item *curr; + type_datum_t *sepol_type; + int rc = SEPOL_ERR; + + dattr = cil_malloc(sizeof(disjoint_attributes_rule_t)); + ebitmap_init(&dattr->attrs); + + cil_list_for_each(curr, dattrs->datum_expr) { + rc = __cil_get_sepol_type_datum(pdb, DATUM(curr->data), &sepol_type); + if (rc != SEPOL_OK) goto exit; + + if (ebitmap_set_bit(&dattr->attrs, sepol_type->s.value - 1, 1)) { + goto exit; + } + } + + dattr->next = pdb->disjoint_attributes; + pdb->disjoint_attributes = dattr; + + return SEPOL_OK; + +exit: + if (dattr) { + ebitmap_destroy(&dattr->attrs); + free(dattr); + } + return rc; +} + static int __cil_node_to_policydb(struct cil_tree_node *node, void *extra_args) { int rc = SEPOL_OK; @@ -4038,6 +4070,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_DISJOINTATTRIBUTES: + rc = cil_disjointattributes_to_policydb(pdb, node->data); + break; default: break; } @@ -4976,6 +5011,42 @@ static int cil_check_neverallows(const struct cil_db *db, policydb_t *pdb, struc return rc; } +static int cil_check_disjointattributes(const policydb_t *pdb, int *violation) +{ + const disjoint_attributes_rule_t *dattr; + + for (dattr = pdb->disjoint_attributes; dattr; dattr = dattr->next) { + ebitmap_node_t *first_node; + unsigned int first_bit; + + ebitmap_for_each_positive_bit(&dattr->attrs, first_node, first_bit) { + ebitmap_node_t *second_node; + unsigned int second_bit; + + ebitmap_for_each_positive_bit_after(&dattr->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, "Disjoint Attributes Rule 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; @@ -5246,6 +5317,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 Disjoint Attributes Rules\n"); + rc = cil_check_disjointattributes(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 56dac8916f..7a155f742d 100644 --- a/libsepol/cil/src/cil_build_ast.c +++ b/libsepol/cil/src/cil_build_ast.c @@ -6108,6 +6108,62 @@ void cil_destroy_src_info(struct cil_src_info *info) free(info); } +int cil_gen_disjointattributes(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_disjointattributes *dattrs = 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_disjointattributes_init(&dattrs); + + rc = cil_gen_expr(parse_current->next, CIL_TYPEATTRIBUTE, &dattrs->str_expr); + if (rc != SEPOL_OK) { + goto exit; + } + + /* require at least two attributes */ + if (dattrs->str_expr->head == dattrs->str_expr->tail) { + rc = SEPOL_ERR; + goto exit; + } + + ast_node->data = dattrs; + ast_node->flavor = CIL_DISJOINTATTRIBUTES; + + return SEPOL_OK; + +exit: + cil_tree_log(parse_current, CIL_ERR, "Bad disjoint attributes rule declaration"); + cil_destroy_disjointattributes(dattrs); + return rc; +} + +void cil_destroy_disjointattributes(struct cil_disjointattributes *dattrs) +{ + if (dattrs == NULL) { + return; + } + + cil_list_destroy(&dattrs->str_expr, CIL_TRUE); + cil_list_destroy(&dattrs->datum_expr, CIL_FALSE); + + free(dattrs); +} + static int check_for_illegal_statement(struct cil_tree_node *parse_current, struct cil_args_build *args) { if (args->tunif != NULL) { @@ -6401,6 +6457,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_DISJOINTATTRIBUTES) { + rc = cil_gen_disjointattributes(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 7fa4299c9e..f7d71981ab 100644 --- a/libsepol/cil/src/cil_build_ast.h +++ b/libsepol/cil/src/cil_build_ast.h @@ -222,6 +222,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_disjointattributes(struct cil_db *db, struct cil_tree_node *parse_current, struct cil_tree_node *ast_node); +void cil_destroy_disjointattributes(struct cil_disjointattributes *dattrs); 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 1507edb447..d1c296ebd5 100644 --- a/libsepol/cil/src/cil_copy_ast.c +++ b/libsepol/cil/src/cil_copy_ast.c @@ -1681,6 +1681,21 @@ static int cil_copy_src_info(__attribute__((unused)) struct cil_db *db, void *da return SEPOL_OK; } +static int cil_copy_disjointattributes(__attribute__((unused)) struct cil_db *db, void *data, void **copy, __attribute__((unused)) symtab_t *symtab) +{ + struct cil_disjointattributes *orig = data; + struct cil_disjointattributes *new = NULL; + + cil_disjointattributes_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; @@ -1977,6 +1992,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_DISJOINTATTRIBUTES: + copy_func = &cil_copy_disjointattributes; + break; default: goto exit; } diff --git a/libsepol/cil/src/cil_flavor.h b/libsepol/cil/src/cil_flavor.h index 155d7c8008..e58bb7fdea 100644 --- a/libsepol/cil/src/cil_flavor.h +++ b/libsepol/cil/src/cil_flavor.h @@ -116,6 +116,7 @@ enum cil_flavor { CIL_SRC_INFO, CIL_IBPKEYCON, CIL_IBENDPORTCON, + CIL_DISJOINTATTRIBUTES, /* * boolean constraint set catset diff --git a/libsepol/cil/src/cil_internal.h b/libsepol/cil/src/cil_internal.h index 47b67c89d9..883892184a 100644 --- a/libsepol/cil/src/cil_internal.h +++ b/libsepol/cil/src/cil_internal.h @@ -245,6 +245,7 @@ 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_DENY_RULE; +extern char *CIL_KEY_DISJOINTATTRIBUTES; /* Symbol Table Array Indices @@ -314,6 +315,7 @@ struct cil_db { struct cil_list *userprefixes; struct cil_list *selinuxusers; struct cil_list *declared_strings; + struct cil_list *disjointattributes; int num_types_and_attrs; int num_classes; int num_cats; @@ -992,6 +994,11 @@ struct cil_src_info { char *path; }; +struct cil_disjointattributes { + 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); @@ -1099,5 +1106,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_disjointattributes_init(struct cil_disjointattributes **dattrs); #endif diff --git a/libsepol/cil/src/cil_policy.c b/libsepol/cil/src/cil_policy.c index e9a8f75d82..04671f2703 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_DISJOINTATTRIBUTES, 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_DISJOINTATTRIBUTES: + kind = CIL_LIST_DISJOINTATTRIBUTES; + break; default: break; } @@ -1910,6 +1914,27 @@ static void cil_devicetreecons_to_policy(FILE *out, struct cil_sort *devicetreec } } +static void cil_disjointattributes_to_policy(FILE *out, struct cil_list *dattrs_list) +{ + struct cil_list_item *curr_dattrs, *curr_attr; + struct cil_disjointattributes *dattrs; + int first = 1; + + cil_list_for_each(curr_dattrs, dattrs_list) { + dattrs = curr_dattrs->data; + fprintf(out, "disjoint_attributes "); + cil_list_for_each(curr_attr, dattrs->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; @@ -1955,6 +1980,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_disjointattributes_to_policy(out, db->disjointattributes); 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 fa312c6f09..5352a76fcf 100644 --- a/libsepol/cil/src/cil_reset_ast.c +++ b/libsepol/cil/src/cil_reset_ast.c @@ -486,6 +486,11 @@ static void cil_reset_booleanif(struct cil_booleanif *bif) cil_list_destroy(&bif->datum_expr, CIL_FALSE); } +static void cil_reset_disjointattributes(struct cil_disjointattributes *dattrs) +{ + cil_list_destroy(&dattrs->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) { @@ -644,6 +649,9 @@ static int __cil_reset_node(struct cil_tree_node *node, __attribute__((unused)) case CIL_BOOLEANIF: cil_reset_booleanif(node->data); break; + case CIL_DISJOINTATTRIBUTES: + cil_reset_disjointattributes(node->data); + break; case CIL_SIDORDER: case CIL_CLASSORDER: case CIL_CATORDER: diff --git a/libsepol/cil/src/cil_resolve_ast.c b/libsepol/cil/src/cil_resolve_ast.c index 427a320c97..dca1d89980 100644 --- a/libsepol/cil/src/cil_resolve_ast.c +++ b/libsepol/cil/src/cil_resolve_ast.c @@ -3166,6 +3166,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: @@ -3213,6 +3214,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_DISJOINTATTRIBUTES) { + if (FLAVOR(res_datum) != CIL_TYPEATTRIBUTE) { + cil_tree_log(parent, CIL_ERR, "Type or type alias not supported in disjoint attributes rule declaration"); + rc = SEPOL_ERR; + goto exit; + } + cil_type_used(res_datum, CIL_ATTR_NEVERALLOW); } cil_list_append(*datum_expr, CIL_DATUM, res_datum); } @@ -3404,6 +3412,33 @@ int cil_resolve_userattributeset(struct cil_tree_node *current, struct cil_db *d return rc; } +int cil_resolve_disjointattributes(struct cil_tree_node *current, struct cil_db *db) +{ + struct cil_disjointattributes *dattrs = current->data; + struct cil_list_item *first, *second; + int rc; + + rc = cil_resolve_expr(CIL_DISJOINTATTRIBUTES, dattrs->str_expr, &dattrs->datum_expr, current, db); + if (rc != SEPOL_OK) { + goto exit; + } + + cil_list_for_each(first, dattrs->datum_expr) { + for (second = first->next; second; second = second->next) { + if (first->data == second->data) { + cil_tree_log(current, CIL_ERR, "Repeated attribute in disjoint attributes rule 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. @@ -3783,6 +3818,9 @@ static int __cil_resolve_ast_node(struct cil_tree_node *node, struct cil_args_re case CIL_USERATTRIBUTESET: rc = cil_resolve_userattributeset(node, db); break; + case CIL_DISJOINTATTRIBUTES: + rc = cil_resolve_disjointattributes(node, db); + break; default: break; } diff --git a/libsepol/cil/src/cil_resolve_ast.h b/libsepol/cil/src/cil_resolve_ast.h index 2f6b7e86a6..d2c5515c33 100644 --- a/libsepol/cil/src/cil_resolve_ast.h +++ b/libsepol/cil/src/cil_resolve_ast.h @@ -97,6 +97,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, struct cil_db *db); int cil_evaluate_expr(struct cil_list *datum_expr, uint16_t *result); int cil_resolve_tunif(struct cil_tree_node *current, struct cil_db *db); +int cil_resolve_disjointattributes(struct cil_tree_node *current, struct cil_db *db); 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, struct cil_db *db, struct cil_symtab_datum **datum); diff --git a/libsepol/cil/src/cil_write_ast.c b/libsepol/cil/src/cil_write_ast.c index f4f9f16709..d944484e3f 100644 --- a/libsepol/cil/src/cil_write_ast.c +++ b/libsepol/cil/src/cil_write_ast.c @@ -1527,7 +1527,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_DISJOINTATTRIBUTES: { + struct cil_disjointattributes *dattrs = node->data; + fprintf(out, "(disjointattributes "); + if (dattrs->datum_expr) + write_expr(out, dattrs->datum_expr); + else + write_expr(out, dattrs->str_expr); + fprintf(out, ")\n"); + break; + } default : + cil_log(CIL_ERR, "Unsupported flavor: %d\n", node->flavor); fprintf(out, "()\n", cil_node_to_string(node)); break; } diff --git a/libsepol/include/sepol/policydb/ebitmap.h b/libsepol/include/sepol/policydb/ebitmap.h index 7e19c301d6..a5785bab0c 100644 --- a/libsepol/include/sepol/policydb/ebitmap.h +++ b/libsepol/include/sepol/policydb/ebitmap.h @@ -80,6 +80,12 @@ static inline int ebitmap_node_get_bit(const ebitmap_node_t * n, unsigned int bi #define ebitmap_for_each_positive_bit(e, n, bit) \ ebitmap_for_each_bit(e, n, bit) if (ebitmap_node_get_bit(n, bit)) \ +#define ebitmap_for_each_bit_after(e, n, bit, startnode, startbit) \ + for (n = startnode, bit = ebitmap_next(&n, startbit); bit < ebitmap_length(e); bit = ebitmap_next(&n, bit)) \ + +#define ebitmap_for_each_positive_bit_after(e, n, bit, startnode, startbit) \ + ebitmap_for_each_bit_after(e, n, bit, startnode, startbit) if (ebitmap_node_get_bit(n, bit)) \ + extern int ebitmap_cmp(const ebitmap_t * e1, const ebitmap_t * e2); extern int ebitmap_or(ebitmap_t * dst, const ebitmap_t * e1, const ebitmap_t * e2); extern int ebitmap_union(ebitmap_t * dst, const ebitmap_t * e1); diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h index 56d2cb01d8..b2c7d1835f 100644 --- a/libsepol/include/sepol/policydb/policydb.h +++ b/libsepol/include/sepol/policydb/policydb.h @@ -192,6 +192,12 @@ typedef struct type_datum { uint32_t bounds; /* bounds type, if exist */ } type_datum_t; +/* Mutual exclusive attributes */ +typedef struct disjoint_attributes_rule { + ebitmap_t attrs; /* mutual exclusive attributes */ + struct disjoint_attributes_rule *next; +} disjoint_attributes_rule_t; + /* * Properties of type_datum * available on the policy version >= (MOD_)POLICYDB_VERSION_BOUNDARY @@ -606,6 +612,10 @@ typedef struct policydb { bitmaps. Someday the 0 bit may be used for global permissive */ ebitmap_t permissive_map; + /* mutual exclusive attributes (not preserved in kernel policy). + stored as linked list */ + disjoint_attributes_rule_t *disjoint_attributes; + unsigned policyvers; unsigned handle_unknown; @@ -697,6 +707,8 @@ extern void level_datum_init(level_datum_t * x); extern void level_datum_destroy(level_datum_t * x); extern void cat_datum_init(cat_datum_t * x); extern void cat_datum_destroy(cat_datum_t * x); +extern void disjoint_attributes_rule_init(disjoint_attributes_rule_t * x); +extern void disjoint_attributes_rule_destroy(disjoint_attributes_rule_t * x); extern int check_assertion(policydb_t *p, avrule_t *avrule); extern int check_assertions(sepol_handle_t * handle, policydb_t * p, avrule_t * avrules); @@ -784,9 +796,10 @@ extern int policydb_set_target_platform(policydb_t *p, int platform); #define MOD_POLICYDB_VERSION_INFINIBAND 19 #define MOD_POLICYDB_VERSION_GLBLUB 20 #define MOD_POLICYDB_VERSION_SELF_TYPETRANS 21 +#define MOD_POLICYDB_VERSION_DISJOINT_ATTRIBUTES 22 /* disjoint attributes compile time constraint */ #define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE -#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_SELF_TYPETRANS +#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_DISJOINT_ATTRIBUTES #define POLICYDB_CONFIG_MLS 1 diff --git a/libsepol/src/assertion.c b/libsepol/src/assertion.c index 3076babedc..3f042ad3b5 100644 --- a/libsepol/src/assertion.c +++ b/libsepol/src/assertion.c @@ -36,7 +36,7 @@ struct avtab_match_args { unsigned long errors; }; -static const char* policy_name(policydb_t *p) { +static const char* policy_name(const policydb_t *p) { const char *policy_file = "policy.conf"; if (p->name) { policy_file = p->name; @@ -146,7 +146,7 @@ static void extended_permissions_violated(avtab_extended_perms_t *result, } /* Same scenarios of interest as check_assertion_extended_permissions */ -static int report_assertion_extended_permissions(sepol_handle_t *handle, +static unsigned long report_assertion_extended_permissions(sepol_handle_t *handle, policydb_t *p, const avrule_t *avrule, unsigned int stype, unsigned int ttype, const class_perm_node_t *curperm, uint32_t perms, @@ -162,7 +162,7 @@ static int report_assertion_extended_permissions(sepol_handle_t *handle, unsigned int i, j; int rc; int found_xperm = 0; - int errors = 0; + unsigned long errors = 0; memcpy(&tmp_key, k, sizeof(avtab_key_t)); tmp_key.specified = AVTAB_XPERMS_ALLOWED; @@ -319,7 +319,7 @@ static int report_assertion_avtab_matches(avtab_key_t *k, avtab_datum_t *d, void return rc; } -static int report_assertion_failures(sepol_handle_t *handle, policydb_t *p, avrule_t *avrule) +static long int report_assertion_failures(sepol_handle_t *handle, policydb_t *p, avrule_t *avrule) { int rc; struct avtab_match_args args; @@ -640,20 +640,52 @@ int check_assertion(policydb_t *p, avrule_t *avrule) return rc; } +static long int check_disjoint_attributes(sepol_handle_t *handle, const policydb_t *p) +{ + const disjoint_attributes_rule_t *dattr; + unsigned long errors = 0; + + for (dattr = p->disjoint_attributes; dattr; dattr = dattr->next) { + ebitmap_node_t *first_node; + unsigned int first_bit; + + ebitmap_for_each_positive_bit(&dattr->attrs, first_node, first_bit) { + ebitmap_node_t *second_node; + unsigned int second_bit; + + ebitmap_for_each_positive_bit_after(&dattr->attrs, second_node, second_bit, first_node, first_bit) { + ebitmap_t attr_union; + ebitmap_node_t *type_node; + unsigned int type_bit; + int rc; + + rc = ebitmap_and(&attr_union, &p->attr_type_map[first_bit], &p->attr_type_map[second_bit]); + if (rc < 0) + return rc; + + ebitmap_for_each_positive_bit(&attr_union, type_node, type_bit) { + ERR(handle, "Disjoint Attributes Rule violation, type %s associated with attributes %s and %s", + p->p_type_val_to_name[type_bit], + p->p_type_val_to_name[first_bit], + p->p_type_val_to_name[second_bit]); + errors++; + } + + ebitmap_destroy(&attr_union); + } + } + } + + return errors; +} + int check_assertions(sepol_handle_t * handle, policydb_t * p, avrule_t * avrules) { - int rc; + long int rc; avrule_t *a; unsigned long errors = 0; - if (!avrules) { - /* Since assertions are stored in avrules, if it is NULL - there won't be any to check. This also prevents an invalid - free if the avtabs are never initialized */ - return 0; - } - for (a = avrules; a != NULL; a = a->next) { if (!(a->specified & (AVRULE_NEVERALLOW | AVRULE_XPERMS_NEVERALLOW))) continue; @@ -675,5 +707,15 @@ int check_assertions(sepol_handle_t * handle, policydb_t * p, if (errors) ERR(handle, "%lu neverallow failures occurred", errors); + rc = check_disjoint_attributes(handle, p); + if (rc < 0) { + ERR(handle, "Error occurred while checking Disjoint Attributes Rules"); + return -1; + } + if (rc) { + ERR(handle, "%ld Disjoint Attributes Rule failures occurred", rc); + errors += rc; + } + return errors ? -1 : 0; } diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c index e63414b149..e7b23da469 100644 --- a/libsepol/src/expand.c +++ b/libsepol/src/expand.c @@ -56,7 +56,7 @@ static void expand_state_init(expand_state_t * state) memset(state, 0, sizeof(expand_state_t)); } -static int map_ebitmap(ebitmap_t * src, ebitmap_t * dst, uint32_t * map) +static int map_ebitmap(const ebitmap_t * src, ebitmap_t * dst, const uint32_t * map) { unsigned int i; ebitmap_node_t *tnode; @@ -2341,6 +2341,45 @@ static int genfs_copy(expand_state_t * state) return 0; } +static int disjoint_attributes_copy(expand_state_t *state) +{ + const disjoint_attributes_rule_t *old; + disjoint_attributes_rule_t *list = NULL; + + for (old = state->base->disjoint_attributes; old; old = old->next) { + disjoint_attributes_rule_t *new; + + new = malloc(sizeof(disjoint_attributes_rule_t)); + if (!new) { + ERR(state->handle, "Out of memory!"); + return -1; + } + + disjoint_attributes_rule_init(new); + + if (map_ebitmap(&old->attrs, &new->attrs, state->typemap)) { + ERR(state->handle, "out of memory"); + ebitmap_destroy(&new->attrs); + free(new); + return -1; + } + + if (list) + list->next = new; + else { + if (state->out->disjoint_attributes) { + disjoint_attributes_rule_t *d; + for (d = state->out->disjoint_attributes; d->next; d = d->next) {} + d->next = new; + } else + state->out->disjoint_attributes = new; + } + list = new; + } + + return 0; +} + static int type_attr_map(hashtab_key_t key __attribute__ ((unused)), hashtab_datum_t datum, void *ptr) @@ -3177,6 +3216,10 @@ int expand_module(sepol_handle_t * handle, if (genfs_copy(&state)) goto cleanup; + /* copy disjoint attributes rules */ + if (disjoint_attributes_copy(&state)) + goto cleanup; + /* Build the type<->attribute maps and remove attributes. */ state.out->attr_type_map = calloc(state.out->p_types.nprim, sizeof(ebitmap_t)); diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c index e20ba4afad..d48f40d450 100644 --- a/libsepol/src/kernel_to_cil.c +++ b/libsepol/src/kernel_to_cil.c @@ -1968,6 +1968,33 @@ static int write_filename_trans_rules_to_cil(FILE *out, struct policydb *pdb) return rc; } +static int write_disjoint_attributes_to_cil(FILE *out, const struct policydb *pdb) +{ + const disjoint_attributes_rule_t *dattr; + + for (dattr = pdb->disjoint_attributes; dattr; dattr = dattr->next) { + struct ebitmap_node *node; + unsigned int bit; + int first = 1; + + sepol_printf(out, "(disjointattributes ("); + + ebitmap_for_each_positive_bit(&dattr->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 char *level_to_str(struct policydb *pdb, struct mls_level *level) { ebitmap_t *cats = &level->cat; @@ -3363,6 +3390,11 @@ int sepol_kernel_policydb_to_cil(FILE *out, struct policydb *pdb) goto exit; } + rc = write_disjoint_attributes_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + if (pdb->mls) { rc = write_range_trans_rules_to_cil(out, pdb); if (rc != 0) { diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c index 5860a513aa..33f25df5db 100644 --- a/libsepol/src/kernel_to_conf.c +++ b/libsepol/src/kernel_to_conf.c @@ -1933,6 +1933,33 @@ static int write_filename_trans_rules_to_conf(FILE *out, struct policydb *pdb) return rc; } +static int write_disjoint_attributes_to_conf(FILE *out, const struct policydb *pdb) +{ + const disjoint_attributes_rule_t *dattr; + + for (dattr = pdb->disjoint_attributes; dattr; dattr = dattr->next) { + struct ebitmap_node *node; + unsigned int bit; + int first = 1; + + sepol_printf(out, "disjoint_attributes "); + + ebitmap_for_each_positive_bit(&dattr->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 char *level_to_str(struct policydb *pdb, struct mls_level *level) { ebitmap_t *cats = &level->cat; @@ -3223,6 +3250,11 @@ int sepol_kernel_policydb_to_conf(FILE *out, struct policydb *pdb) } write_filename_trans_rules_to_conf(out, pdb); + rc = write_disjoint_attributes_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + if (pdb->mls) { rc = write_range_trans_rules_to_conf(out, pdb); if (rc != 0) { diff --git a/libsepol/src/link.c b/libsepol/src/link.c index b8272308bb..19e262c602 100644 --- a/libsepol/src/link.c +++ b/libsepol/src/link.c @@ -1857,6 +1857,45 @@ static int scope_copy_callback(hashtab_key_t key, hashtab_datum_t datum, return -1; } +static int copy_disjoint_attributes(link_state_t * state, const policy_module_t *module) +{ + const disjoint_attributes_rule_t *dattr_rule; + disjoint_attributes_rule_t *list = NULL; + + for (dattr_rule = module->policy->disjoint_attributes; dattr_rule; dattr_rule = dattr_rule->next) { + disjoint_attributes_rule_t *new_dattr; + + new_dattr = malloc(sizeof(disjoint_attributes_rule_t)); + if (!new_dattr) { + ERR(state->handle, "Out of memory!"); + return -1; + } + + disjoint_attributes_rule_init(new_dattr); + + if (ebitmap_convert(&dattr_rule->attrs, &new_dattr->attrs, module->map[SYM_TYPES])) { + ebitmap_destroy(&new_dattr->attrs); + free(new_dattr); + ERR(state->handle, "Out of memory!"); + return -1; + } + + if (list) + list->next = new_dattr; + else { + if (state->base->disjoint_attributes) { + disjoint_attributes_rule_t *d; + for (d = state->base->disjoint_attributes; d->next; d = d->next) {} + d->next = new_dattr; + } else + state->base->disjoint_attributes = new_dattr; + } + list = new_dattr; + } + + return 0; +} + /* Copy a module over to a base, remapping all values within. After * all identifiers and rules are done, copy the scoping information. * This is when it checks for duplicate declarations. */ @@ -1891,6 +1930,11 @@ static int copy_module(link_state_t * state, policy_module_t * module) } } + ret = copy_disjoint_attributes(state, module); + if (ret) { + return ret; + } + return 0; } diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c index 49f9e8af20..20e43a151a 100644 --- a/libsepol/src/policydb.c +++ b/libsepol/src/policydb.c @@ -334,6 +334,13 @@ static const struct policydb_compat_info policydb_compat[] = { .ocon_num = OCON_IBENDPORT + 1, .target_platform = SEPOL_TARGET_SELINUX, }, + { + .type = POLICY_BASE, + .version = MOD_POLICYDB_VERSION_DISJOINT_ATTRIBUTES, + .sym_num = SYM_NUM, + .ocon_num = OCON_IBENDPORT + 1, + .target_platform = SEPOL_TARGET_SELINUX, + }, { .type = POLICY_MOD, .version = MOD_POLICYDB_VERSION_BASE, @@ -460,6 +467,13 @@ static const struct policydb_compat_info policydb_compat[] = { .ocon_num = 0, .target_platform = SEPOL_TARGET_SELINUX, }, + { + .type = POLICY_MOD, + .version = MOD_POLICYDB_VERSION_DISJOINT_ATTRIBUTES, + .sym_num = SYM_NUM, + .ocon_num = 0, + .target_platform = SEPOL_TARGET_SELINUX, + }, }; #if 0 @@ -760,6 +774,20 @@ void avrule_list_destroy(avrule_t * x) } } +void disjoint_attributes_rule_init(disjoint_attributes_rule_t * x) +{ + ebitmap_init(&x->attrs); + x->next = NULL; +} + + +void disjoint_attributes_rule_destroy(disjoint_attributes_rule_t * x) +{ + if (!x) + return; + ebitmap_destroy(&x->attrs); +} + /* * Initialize the role table by implicitly adding role 'object_r'. If * the policy is a module, set object_r's scope to be SCOPE_REQ, @@ -1493,6 +1521,7 @@ void policydb_destroy(policydb_t * p) unsigned int i; role_allow_t *ra, *lra = NULL; role_trans_t *tr, *ltr = NULL; + disjoint_attributes_rule_t *dattr, *dattr_next; if (!p) return; @@ -1586,6 +1615,12 @@ void policydb_destroy(policydb_t * p) free(p->attr_type_map); } + for (dattr = p->disjoint_attributes; dattr; dattr = dattr_next) { + dattr_next = dattr->next; + disjoint_attributes_rule_destroy(dattr); + free(dattr); + } + return; } @@ -4073,6 +4108,41 @@ static int scope_read(policydb_t * p, int symnum, struct policy_file *fp) return -1; } +static int disjoint_attributes_read(policydb_t * p, struct policy_file *fp) +{ + disjoint_attributes_rule_t *list = NULL; + uint32_t buf, nel, i; + int rc; + + rc = next_entry(&buf, fp, sizeof(uint32_t)); + if (rc < 0) + return -1; + nel = le32_to_cpu(buf); + for (i = 0; i < nel; i++) { + disjoint_attributes_rule_t *dattr_rule; + + dattr_rule = malloc(sizeof(disjoint_attributes_rule_t)); + if (!dattr_rule) + return -1; + + disjoint_attributes_rule_init(dattr_rule); + + if (ebitmap_read(&dattr_rule->attrs, fp) < 0) { + ebitmap_destroy(&dattr_rule->attrs); + free(dattr_rule); + return -1; + } + + if (list) + list->next = dattr_rule; + else + p->disjoint_attributes = dattr_rule; + list = dattr_rule; + } + + return 0; +} + static sepol_security_class_t policydb_string_to_security_class( struct policydb *policydb, const char *class_name) @@ -4459,6 +4529,12 @@ int policydb_read(policydb_t * p, struct policy_file *fp, unsigned verbose) } } + if (p->policy_type != POLICY_KERN && + p->policyvers >= MOD_POLICYDB_VERSION_DISJOINT_ATTRIBUTES) { + if (disjoint_attributes_read(p, fp)) + return POLICYDB_ERROR; + } + if (policydb_validate(fp->handle, p)) goto bad; diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c index c4f8c30026..8ffc122450 100644 --- a/libsepol/src/policydb_validate.c +++ b/libsepol/src/policydb_validate.c @@ -1610,6 +1610,32 @@ static int validate_typeattr_map(sepol_handle_t *handle, const policydb_t *p, va return -1; } +static int validate_disjoint_attributes(sepol_handle_t *handle, const policydb_t *p, validate_t flavors[]) +{ + const disjoint_attributes_rule_t *dattr_rule; + ebitmap_node_t *node; + unsigned int i; + + for (dattr_rule = p->disjoint_attributes; dattr_rule; dattr_rule = dattr_rule->next) { + if (ebitmap_cardinality(&dattr_rule->attrs) < 2) + goto bad; + + if (validate_ebitmap(&dattr_rule->attrs, &flavors[SYM_TYPES])) + goto bad; + + ebitmap_for_each_positive_bit(&dattr_rule->attrs, node, i) { + if (p->type_val_to_struct[i]->flavor != TYPE_ATTRIB) + goto bad; + } + } + + return 0; + +bad: + ERR(handle, "Invalid disjoint attributes rule definition"); + return -1; +} + static int validate_properties(sepol_handle_t *handle, const policydb_t *p) { switch (p->policy_type) { @@ -1748,6 +1774,9 @@ int policydb_validate(sepol_handle_t *handle, const policydb_t *p) goto bad; } + if (validate_disjoint_attributes(handle, p, flavors)) + goto bad; + validate_array_destroy(flavors); return 0; diff --git a/libsepol/src/write.c b/libsepol/src/write.c index 283d11c8a6..26d220a7f6 100644 --- a/libsepol/src/write.c +++ b/libsepol/src/write.c @@ -58,9 +58,9 @@ struct policy_data { static int avrule_write_list(policydb_t *p, avrule_t * avrules, struct policy_file *fp); -static int ebitmap_write(ebitmap_t * e, struct policy_file *fp) +static int ebitmap_write(const ebitmap_t * e, struct policy_file *fp) { - ebitmap_node_t *n; + const ebitmap_node_t *n; uint32_t buf[32], bit, count; uint64_t map; size_t items; @@ -2191,6 +2191,30 @@ static int role_attr_uncount(hashtab_key_t key __attribute__ ((unused)), return 0; } +static int disjoint_attributes_write(const policydb_t *p, struct policy_file *fp) +{ + const disjoint_attributes_rule_t *dattr; + size_t items; + uint32_t buf, count = 0; + + for (dattr = p->disjoint_attributes; dattr; dattr = dattr->next) { + if (__builtin_add_overflow(count, 1, &count)) + return POLICYDB_ERROR; + } + + buf = cpu_to_le32(count); + items = put_entry(&buf, sizeof(uint32_t), 1, fp); + if (items != 1) + return POLICYDB_ERROR; + + for (dattr = p->disjoint_attributes; dattr; dattr = dattr->next) { + if (ebitmap_write(&dattr->attrs, fp)) + return POLICYDB_ERROR; + } + + return POLICYDB_SUCCESS; +} + /* * Write the configuration data in a policy database * structure to a policy database binary representation @@ -2413,5 +2437,11 @@ int policydb_write(policydb_t * p, struct policy_file *fp) } } + if (p->policy_type != POLICY_KERN && + p->policyvers >= MOD_POLICYDB_VERSION_DISJOINT_ATTRIBUTES) { + if (disjoint_attributes_write(p, fp)) + return POLICYDB_ERROR; + } + return POLICYDB_SUCCESS; } diff --git a/libsepol/tests/libsepol-tests.c b/libsepol/tests/libsepol-tests.c index 968e3cc214..0985391f99 100644 --- a/libsepol/tests/libsepol-tests.c +++ b/libsepol/tests/libsepol-tests.c @@ -25,6 +25,7 @@ #include "test-deps.h" #include "test-downgrade.h" #include "test-neverallow.h" +#include "test-disjointattributes.h" #include #include @@ -73,6 +74,7 @@ static bool do_tests(int interactive, int verbose) DECLARE_SUITE(deps); DECLARE_SUITE(downgrade); DECLARE_SUITE(neverallow); + DECLARE_SUITE(disjointattrs); if (verbose) CU_basic_set_mode(CU_BRM_VERBOSE); diff --git a/libsepol/tests/policies/test-disjointattrs/single.conf b/libsepol/tests/policies/test-disjointattrs/single.conf new file mode 100644 index 0000000000..1a9745fc2f --- /dev/null +++ b/libsepol/tests/policies/test-disjointattrs/single.conf @@ -0,0 +1,87 @@ +class process +class blk_file +class chr_file +class dir +class fifo_file +class file +class lnk_file +class sock_file + +sid kernel +sid security +sid unlabeled +sid file +sid port +sid netif +sid netmsg +sid node +sid devnull + +class process { dyntransition transition } +class file { write } + +ifdef(`enable_mls',` +sensitivity s0; +dominance { s0 } +category c0; category c1; category c2; category c3; +category c4; category c5; category c6; category c7; +category c8; category c9; category c10; category c11; +category c12; category c13; category c14; category c15; +category c16; category c17; category c18; category c19; +category c20; category c21; category c22; category c23; + +level s0:c0.c23; + +mlsconstrain file { write } ( h1 dom h2 ); +') + +# +# Test start +# + +attribute test1_attr1; +attribute test1_attr2; +type test1_type; +typeattribute test1_type test1_attr1; +typeattribute test1_type test1_attr2; +disjoint_attributes test1_attr1, test1_attr2; + + +attribute test2_attr1; +attribute test2_attr2; +attribute test2_attr3; +type test2_type1; +type test2_type2; +type test2_type3; +type test2_type4; +typeattribute test2_type1 test2_attr1; +typeattribute test2_type1 test2_attr2; +typeattribute test2_type2 test2_attr1; +typeattribute test2_type2 test2_attr3; +typeattribute test2_type3 test2_attr2; +typeattribute test2_type3 test2_attr3; +typeattribute test2_type4 test2_attr1; +typeattribute test2_type4 test2_attr2; +typeattribute test2_type4 test2_attr3; +disjoint_attributes test2_attr1, test2_attr2, test2_attr3; + +# +# Test End +# + +type sys_isid; +allow sys_isid self : process { dyntransition transition }; +role sys_role; +role sys_role types sys_isid; +gen_user(sys_user,, sys_role, s0, s0 - s0:c0.c23) +sid kernel gen_context(sys_user:sys_role:sys_isid, s0) +sid security gen_context(sys_user:sys_role:sys_isid, s0) +sid unlabeled gen_context(sys_user:sys_role:sys_isid, s0) +sid file gen_context(sys_user:sys_role:sys_isid, s0) +sid port gen_context(sys_user:sys_role:sys_isid, s0) +sid netif gen_context(sys_user:sys_role:sys_isid, s0) +sid netmsg gen_context(sys_user:sys_role:sys_isid, s0) +sid node gen_context(sys_user:sys_role:sys_isid, s0) +sid devnull gen_context(sys_user:sys_role:sys_isid, s0) +fs_use_trans devpts gen_context(sys_user:sys_role:sys_isid, s0); +fs_use_trans devtmpfs gen_context(sys_user:sys_role:sys_isid, s0); diff --git a/libsepol/tests/policies/test-disjointattrs/split_base.conf b/libsepol/tests/policies/test-disjointattrs/split_base.conf new file mode 100644 index 0000000000..6fba8cddce --- /dev/null +++ b/libsepol/tests/policies/test-disjointattrs/split_base.conf @@ -0,0 +1,53 @@ +class process +class blk_file +class chr_file +class dir +class fifo_file +class file +class lnk_file +class sock_file + +sid kernel +sid security +sid unlabeled +sid file +sid port +sid netif +sid netmsg +sid node +sid devnull + +class process { dyntransition transition } +class file { write } + +ifdef(`enable_mls',` +sensitivity s0; +dominance { s0 } +category c0; category c1; category c2; category c3; +category c4; category c5; category c6; category c7; +category c8; category c9; category c10; category c11; +category c12; category c13; category c14; category c15; +category c16; category c17; category c18; category c19; +category c20; category c21; category c22; category c23; + +level s0:c0.c23; + +mlsconstrain file { write } ( h1 dom h2 ); +') + +type sys_isid; +allow sys_isid self : process { dyntransition transition }; +role sys_role; +role sys_role types sys_isid; +gen_user(sys_user,, sys_role, s0, s0 - s0:c0.c23) +sid kernel gen_context(sys_user:sys_role:sys_isid, s0) +sid security gen_context(sys_user:sys_role:sys_isid, s0) +sid unlabeled gen_context(sys_user:sys_role:sys_isid, s0) +sid file gen_context(sys_user:sys_role:sys_isid, s0) +sid port gen_context(sys_user:sys_role:sys_isid, s0) +sid netif gen_context(sys_user:sys_role:sys_isid, s0) +sid netmsg gen_context(sys_user:sys_role:sys_isid, s0) +sid node gen_context(sys_user:sys_role:sys_isid, s0) +sid devnull gen_context(sys_user:sys_role:sys_isid, s0) +fs_use_trans devpts gen_context(sys_user:sys_role:sys_isid, s0); +fs_use_trans devtmpfs gen_context(sys_user:sys_role:sys_isid, s0); diff --git a/libsepol/tests/policies/test-disjointattrs/split_module1.conf b/libsepol/tests/policies/test-disjointattrs/split_module1.conf new file mode 100644 index 0000000000..042f2d27df --- /dev/null +++ b/libsepol/tests/policies/test-disjointattrs/split_module1.conf @@ -0,0 +1,9 @@ +module disjointattrs_test_1 1.0; + +require { + type test_type_t; +} + +attribute attr1; + +typeattribute test_type_t attr1; diff --git a/libsepol/tests/policies/test-disjointattrs/split_module2.conf b/libsepol/tests/policies/test-disjointattrs/split_module2.conf new file mode 100644 index 0000000000..c7888f557e --- /dev/null +++ b/libsepol/tests/policies/test-disjointattrs/split_module2.conf @@ -0,0 +1,9 @@ +module disjointattrs_test_2 1.0; + +require { + type test_type_t; +} + +attribute attr2; + +typeattribute test_type_t attr2; diff --git a/libsepol/tests/policies/test-disjointattrs/split_module3.conf b/libsepol/tests/policies/test-disjointattrs/split_module3.conf new file mode 100644 index 0000000000..edddcf443d --- /dev/null +++ b/libsepol/tests/policies/test-disjointattrs/split_module3.conf @@ -0,0 +1,9 @@ +module disjointattrs_test_3 1.0; + +require { + attribute attr1, attr2; +} + +type test_type_t; + +disjoint_attributes attr1, attr2; diff --git a/libsepol/tests/test-disjointattributes.c b/libsepol/tests/test-disjointattributes.c new file mode 100644 index 0000000000..e145fa99f9 --- /dev/null +++ b/libsepol/tests/test-disjointattributes.c @@ -0,0 +1,197 @@ +#define _GNU_SOURCE + +#include "test-disjointattributes.h" + +#include "helpers.h" +#include "test-common.h" + +#include +#include +#include + +#include +#include + +extern int mls; + +int disjointattrs_test_init(void) +{ + return 0; +} + +int disjointattrs_test_cleanup(void) +{ + return 0; +} + +static struct msg_list { + char *msg; + struct msg_list *next; +} *messages; + +static void messages_clean(void) +{ + while (messages) { + struct msg_list *n = messages->next; + free(messages->msg); + free(messages); + messages = n; + } +} + +static void messages_check(unsigned count, const char *const expected[count]) +{ + unsigned i; + const struct msg_list *m = messages; + + for (i = 0; i < count; i++, m = m->next) { + if (!m) { + CU_FAIL("less messages than expected"); + return; + } + + if (strcmp(expected[i], m->msg) != 0) { + CU_FAIL("messages differs from expected"); + fprintf(stderr, "\n\n", expected[i], m->msg); + } + } + + if (m) { + CU_FAIL("more messages than expected"); + fprintf(stderr, "\n\n", m->msg); + } +} + +#ifdef __GNUC__ +__attribute__ ((format(printf, 3, 4))) +#endif +static void msg_handler(void *varg __attribute__ ((unused)), + sepol_handle_t * handle __attribute__ ((unused)), + const char *fmt, ...) +{ + char *msg; + va_list ap; + + va_start(ap, fmt); + vasprintf(&msg, fmt, ap); + va_end(ap); + + struct msg_list *new = malloc(sizeof(struct msg_list)); + new->msg = msg; + new->next = messages; + messages = new; +} + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a)) + +static void test_disjointattrs_single(void) +{ + policydb_t basemod, base_expanded; + sepol_handle_t *handle; + const char *const expected_messages[] = { + "7 Disjoint Attributes Rule failures occurred", + "Disjoint Attributes Rule violation, type test1_type associated with attributes test1_attr2 and test1_attr1", + "Disjoint Attributes Rule violation, type test2_type3 associated with attributes test2_attr3 and test2_attr2", + "Disjoint Attributes Rule violation, type test2_type4 associated with attributes test2_attr3 and test2_attr2", + "Disjoint Attributes Rule violation, type test2_type1 associated with attributes test2_attr1 and test2_attr2", + "Disjoint Attributes Rule violation, type test2_type4 associated with attributes test2_attr1 and test2_attr2", + "Disjoint Attributes Rule violation, type test2_type2 associated with attributes test2_attr1 and test2_attr3", + "Disjoint Attributes Rule violation, type test2_type4 associated with attributes test2_attr1 and test2_attr3", + }; + + if (policydb_init(&base_expanded)) + CU_FAIL_FATAL("Failed to initialize policy"); + + if (test_load_policy(&basemod, POLICY_BASE, mls, "test-disjointattrs", "single.conf")) + CU_FAIL_FATAL("Failed to load policy"); + + if (link_modules(NULL, &basemod, NULL, 0, 0)) + CU_FAIL_FATAL("Failed to link base module"); + + if (expand_module(NULL, &basemod, &base_expanded, 0, 0)) + CU_FAIL_FATAL("Failed to expand policy"); + + if ((handle = sepol_handle_create()) == NULL) + CU_FAIL_FATAL("Failed to initialize handle"); + + sepol_msg_set_callback(handle, msg_handler, NULL); + + if (check_assertions(handle, &base_expanded, NULL) != -1) + CU_FAIL("Assertions did not trigger"); + + messages_check(ARRAY_SIZE(expected_messages), expected_messages); + + sepol_handle_destroy(handle); + messages_clean(); + policydb_destroy(&basemod); + policydb_destroy(&base_expanded); +} + +#define NUM_MODS 3 + +static void test_disjointattrs_split(void) +{ + policydb_t basemod, base_expanded; + policydb_t *modules[NUM_MODS]; + const char *policies[NUM_MODS] = { "split_module1.conf", "split_module2.conf", "split_module3.conf" }; + sepol_handle_t *handle; + const char *const expected_messages[] = { + "1 Disjoint Attributes Rule failures occurred", + "Disjoint Attributes Rule violation, type test_type_t associated with attributes attr2 and attr1", + }; + unsigned i; + + if (policydb_init(&base_expanded)) + CU_FAIL_FATAL("Failed to initialize policy"); + + if (test_load_policy(&basemod, POLICY_BASE, mls, "test-disjointattrs", "split_base.conf")) + CU_FAIL_FATAL("Failed to load policy"); + + for (i = 0; i < NUM_MODS; i++) { + modules[i] = calloc(1, sizeof(*modules[i])); + if (!modules[i]) + CU_FAIL_FATAL("Failed to allocate module"); + + if (test_load_policy(modules[i], POLICY_MOD, mls, "test-disjointattrs", policies[i])) + CU_FAIL_FATAL("Failed to load module"); + } + + if (link_modules(NULL, &basemod, modules, 3, 0)) + CU_FAIL_FATAL("Failed to link base module"); + + if (expand_module(NULL, &basemod, &base_expanded, 0, 0)) + CU_FAIL_FATAL("Failed to expand policy"); + + if ((handle = sepol_handle_create()) == NULL) + CU_FAIL_FATAL("Failed to initialize handle"); + + sepol_msg_set_callback(handle, msg_handler, NULL); + + if (check_assertions(handle, &base_expanded, NULL) != -1) + CU_FAIL("Assertions did not trigger"); + + messages_check(ARRAY_SIZE(expected_messages), expected_messages); + + sepol_handle_destroy(handle); + messages_clean(); + for (i = 0; i < NUM_MODS; i++) { + policydb_destroy(modules[i]); + free(modules[i]); + } + policydb_destroy(&basemod); + policydb_destroy(&base_expanded); +} + +int disjointattrs_add_tests(CU_pSuite suite) +{ + if (NULL == CU_add_test(suite, "disjointattrs_single", test_disjointattrs_single)) { + CU_cleanup_registry(); + return CU_get_error(); + } + if (NULL == CU_add_test(suite, "disjointattrs_split", test_disjointattrs_split)) { + CU_cleanup_registry(); + return CU_get_error(); + } + + return 0; +} diff --git a/libsepol/tests/test-disjointattributes.h b/libsepol/tests/test-disjointattributes.h new file mode 100644 index 0000000000..bdc4600606 --- /dev/null +++ b/libsepol/tests/test-disjointattributes.h @@ -0,0 +1,10 @@ +#ifndef TEST_DISJOINTATTRIBUTES_H__ +#define TEST_DISJOINTATTRIBUTES_H__ + +#include + +int disjointattrs_test_init(void); +int disjointattrs_test_cleanup(void); +int disjointattrs_add_tests(CU_pSuite suite); + +#endif /* TEST_DISJOINTATTRIBUTES_H__ */ diff --git a/secilc/docs/README.md b/secilc/docs/README.md index 5e00fc3b91..ce7c66716a 100644 --- a/secilc/docs/README.md +++ b/secilc/docs/README.md @@ -131,6 +131,7 @@ CIL (Common Intermediate Language) * [typemember](cil_type_statements.md#typemember) * [typetransition](cil_type_statements.md#typetransition) * [typepermissive](cil_type_statements.md#typepermissive) + * [disjointattributes](cil_type_statements.md#disjointattributes) * [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 19438417c5..2fe64c5222 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 ...) ) ``` + +disjointattributes +------------------ + +Libsepol and secilc version 3.7 introduced the disjointattributes 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 + (disjointattributes (typeattribute_id typeattribute_id ...)) +``` + +**Where:** + + ++++ + + + + + + + + + + +

disjointattributes

The disjointattributes keyword.

typeattribute_id

A previously declared typeattribute identifier.

+

Note that the same typeattribute identifier must not be repeated.

+ +**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)) + (disjointattributes (attr_1 attr_2)) +``` diff --git a/secilc/test/policy.cil b/secilc/test/policy.cil index e6b78618fa..b92d3e85eb 100644 --- a/secilc/test/policy.cil +++ b/secilc/test/policy.cil @@ -118,13 +118,17 @@ (typeattribute foo_type) (typeattribute bar_type) (typeattribute baz_type) + (typeattribute bad_type) (typeattribute not_bad_type) + (typeattribute empty_type) (typeattributeset exec_type (or bin_t kernel_t)) (typeattributeset foo_type (and exec_type kernel_t)) (typeattributeset bar_type (xor exec_type foo_type)) (typeattributeset baz_type (not bin_t)) (typeattributeset baz_type (and exec_type (and bar_type bin_t))) + (typeattributeset bad_type (bad_t)) (typeattributeset not_bad_type (not bad_t)) + (disjointattributes (bad_type not_bad_type empty_type)) (typealias sbin_t) (typealiasactual sbin_t bin_t) (typepermissive device_t)