diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h index ef1a014a51..edc8f0b8a6 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 segregate_attributes_rule { + ebitmap_t attrs; /* mutual exclusive attributes */ + struct segregate_attributes_rule *next; +} segregate_attributes_rule_t; + /* * Properties of type_datum * available on the policy version >= (MOD_)POLICYDB_VERSION_BOUNDARY @@ -605,6 +611,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 */ + segregate_attributes_rule_t *segregate_attributes; + unsigned policyvers; unsigned handle_unknown; @@ -696,6 +706,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 segregate_attributes_rule_init(segregate_attributes_rule_t * x); +extern void segregate_attributes_rule_destroy(segregate_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); @@ -783,9 +795,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_SEGREGATE_ATTRIBUTES 22 #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_SEGREGATE_ATTRIBUTES #define POLICYDB_CONFIG_MLS 1 diff --git a/libsepol/src/assertion.c b/libsepol/src/assertion.c index 161874c33a..a6dda570de 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; @@ -535,6 +535,44 @@ int check_assertion(policydb_t *p, avrule_t *avrule) return rc; } +static int check_segregate_attributes(sepol_handle_t *handle, const policydb_t *p) +{ + const segregate_attributes_rule_t *sattr; + int errors = 0, rc; + + for (sattr = p->segregate_attributes; sattr; sattr = sattr->next) { + ebitmap_node_t *first_node; + unsigned int first_bit; + + ebitmap_for_each_positive_bit(&sattr->attrs, first_node, first_bit) { + ebitmap_node_t *second_node; + unsigned int second_bit; + + ebitmap_for_each_positive_bit_after(&sattr->attrs, second_node, second_bit, first_node, first_bit) { + ebitmap_t attr_union; + ebitmap_node_t *type_node; + unsigned int type_bit; + + 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, "Segregate Attributes 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) { @@ -542,13 +580,6 @@ int check_assertions(sepol_handle_t * handle, policydb_t * p, 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; @@ -570,5 +601,15 @@ int check_assertions(sepol_handle_t * handle, policydb_t * p, if (errors) ERR(handle, "%lu neverallow failures occurred", errors); + rc = check_segregate_attributes(handle, p); + if (rc < 0) { + ERR(handle, "Error occurred while checking Segregate Attributes"); + return -1; + } + if (rc) { + ERR(handle, "%d Segregate Attributes failures occurred", rc); + errors += rc; + } + return errors ? -1 : 0; } diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c index 8d19850eed..6f52d1ff9f 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 segregate_attributes_copy(expand_state_t *state) +{ + const segregate_attributes_rule_t *old; + segregate_attributes_rule_t *list = NULL; + + for (old = state->base->segregate_attributes; old; old = old->next) { + segregate_attributes_rule_t *new; + + new = malloc(sizeof(segregate_attributes_rule_t)); + if (!new) { + ERR(state->handle, "Out of memory!"); + return -1; + } + + segregate_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->segregate_attributes) { + segregate_attributes_rule_t *s; + for (s = state->out->segregate_attributes; s->next; s = s->next) {} + s->next = new; + } else + state->out->segregate_attributes = new; + } + list = new; + } + + return 0; +} + static int type_attr_map(hashtab_key_t key __attribute__ ((unused)), hashtab_datum_t datum, void *ptr) @@ -3173,6 +3212,10 @@ int expand_module(sepol_handle_t * handle, if (genfs_copy(&state)) goto cleanup; + /* copy segregate attributes */ + if (segregate_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_conf.c b/libsepol/src/kernel_to_conf.c index 63dffd9b47..f119d57275 100644 --- a/libsepol/src/kernel_to_conf.c +++ b/libsepol/src/kernel_to_conf.c @@ -1839,6 +1839,33 @@ static int write_avtab_to_conf(FILE *out, struct policydb *pdb, int indent) return rc; } +static int write_segregate_attributes_to_conf(FILE *out, const struct policydb *pdb) +{ + const segregate_attributes_rule_t *sattr; + + for (sattr = pdb->segregate_attributes; sattr; sattr = sattr->next) { + struct ebitmap_node *node; + unsigned int bit; + int first = 1; + + sepol_printf(out, "segregate_attributes "); + + ebitmap_for_each_positive_bit(&sattr->attrs, node, bit) { + if (first) { + first = 0; + } else { + sepol_printf(out, ", "); + } + + sepol_printf(out, "%s", pdb->p_type_val_to_name[bit - 1]); + } + + sepol_printf(out, ";\n"); + } + + return 0; +} + struct map_filename_trans_args { struct policydb *pdb; struct strs *strs; @@ -3200,7 +3227,16 @@ int sepol_kernel_policydb_to_conf(FILE *out, struct policydb *pdb) if (rc != 0) { goto exit; } - write_filename_trans_rules_to_conf(out, pdb); + + rc = write_segregate_attributes_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_filename_trans_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } if (pdb->mls) { rc = write_range_trans_rules_to_conf(out, pdb); diff --git a/libsepol/src/link.c b/libsepol/src/link.c index cbe4cea401..1650a9c05f 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_segregate_attributes(link_state_t * state, const policy_module_t *module) +{ + const segregate_attributes_rule_t *src_sattr; + segregate_attributes_rule_t *list = NULL; + + for (src_sattr = module->policy->segregate_attributes; src_sattr; src_sattr = src_sattr->next) { + segregate_attributes_rule_t *new_sattr; + + new_sattr = malloc(sizeof(segregate_attributes_rule_t)); + if (!new_sattr) { + ERR(state->handle, "Out of memory!"); + return -1; + } + + segregate_attributes_rule_init(new_sattr); + + if (ebitmap_convert(&src_sattr->attrs, &new_sattr->attrs, module->map[SYM_TYPES])) { + ebitmap_destroy(&new_sattr->attrs); + free(new_sattr); + ERR(state->handle, "Out of memory!"); + return -1; + } + + if (list) + list->next = new_sattr; + else { + if (state->base->segregate_attributes) { + segregate_attributes_rule_t *s; + for (s = state->base->segregate_attributes; s->next; s = s->next) {} + s->next = new_sattr; + } else + state->base->segregate_attributes = new_sattr; + } + list = new_sattr; + } + + 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_segregate_attributes(state, module); + if (ret) { + return ret; + } + return 0; } diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c index b79c19b94c..e45951d466 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_SEGREGATE_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_SEGREGATE_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 segregate_attributes_rule_init(segregate_attributes_rule_t * x) +{ + ebitmap_init(&x->attrs); + x->next = NULL; +} + + +void segregate_attributes_rule_destroy(segregate_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, @@ -1491,6 +1519,7 @@ void policydb_destroy(policydb_t * p) unsigned int i; role_allow_t *ra, *lra = NULL; role_trans_t *tr, *ltr = NULL; + segregate_attributes_rule_t *sattr, *sattr_next; if (!p) return; @@ -1584,6 +1613,12 @@ void policydb_destroy(policydb_t * p) free(p->attr_type_map); } + for (sattr = p->segregate_attributes; sattr; sattr = sattr_next) { + sattr_next = sattr->next; + segregate_attributes_rule_destroy(sattr); + free(sattr); + } + return; } @@ -4173,6 +4208,41 @@ static int scope_read(policydb_t * p, int symnum, struct policy_file *fp) return -1; } +static int segregate_attributes_read(policydb_t * p, struct policy_file *fp) +{ + segregate_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++) { + segregate_attributes_rule_t *sattr; + + sattr = malloc(sizeof(segregate_attributes_rule_t)); + if (!sattr) + return -1; + + segregate_attributes_rule_init(sattr); + + if (ebitmap_read(&sattr->attrs, fp) < 0) { + ebitmap_destroy(&sattr->attrs); + free(sattr); + return -1; + } + + if (list) + list->next = sattr; + else + p->segregate_attributes = sattr; + list = sattr; + } + + return 0; +} + static sepol_security_class_t policydb_string_to_security_class( struct policydb *policydb, const char *class_name) @@ -4569,6 +4639,12 @@ int policydb_read(policydb_t * p, struct policy_file *fp, unsigned verbose) } } + if (p->policyvers >= MOD_POLICYDB_VERSION_SEGREGATE_ATTRIBUTES && + p->policy_type != POLICY_KERN) { + if (segregate_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 521ea4ff51..f57a1dde24 100644 --- a/libsepol/src/policydb_validate.c +++ b/libsepol/src/policydb_validate.c @@ -1421,6 +1421,32 @@ static int validate_typeattr_map(sepol_handle_t *handle, const policydb_t *p, va return -1; } +static int validate_segregate_attributes(sepol_handle_t *handle, const policydb_t *p, validate_t flavors[]) +{ + const segregate_attributes_rule_t *sattr; + ebitmap_node_t *node; + unsigned int i; + + for (sattr = p->segregate_attributes; sattr; sattr = sattr->next) { + if (ebitmap_cardinality(&sattr->attrs) < 2) + goto bad; + + if (validate_ebitmap(&sattr->attrs, &flavors[SYM_TYPES])) + goto bad; + + ebitmap_for_each_positive_bit(&sattr->attrs, node, i) { + if (p->type_val_to_struct[i]->flavor != TYPE_ATTRIB) + goto bad; + } + } + + return 0; + +bad: + ERR(handle, "Invalid segregate attributes definition"); + return -1; +} + static int validate_properties(sepol_handle_t *handle, const policydb_t *p) { switch (p->policy_type) { @@ -1535,6 +1561,9 @@ int policydb_validate(sepol_handle_t *handle, const policydb_t *p) goto bad; } + if (validate_segregate_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 a9fdf93a8e..81323df9d4 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 segregate_attributes_write(const policydb_t *p, struct policy_file *fp) +{ + const segregate_attributes_rule_t *sattr; + size_t items; + uint32_t buf, count = 0; + + for (sattr = p->segregate_attributes; sattr; sattr = sattr->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 (sattr = p->segregate_attributes; sattr; sattr = sattr->next) { + if (ebitmap_write(&sattr->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->policyvers >= MOD_POLICYDB_VERSION_SEGREGATE_ATTRIBUTES && + p->policy_type != POLICY_KERN) { + if (segregate_attributes_write(p, fp)) + return POLICYDB_ERROR; + } + return POLICYDB_SUCCESS; }