From e46d08f24cb8c38041e4c3c65e531e38a1f3590d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Thu, 16 Jun 2022 15:12:29 +0200 Subject: [PATCH 1/6] libsepol: add ebitmap iterator wrapper with startnode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar like ebitmap_for_each_bit() iterates over all bits of an ebitmap add ebitmap_for_each_bit_starting() iterating over all bits starting from a specific node and bit, which can be from an outer iteration. Signed-off-by: Christian Göttsche --- v4: merge two statements into for loop initializer v2: * use _after suffix * reorder parameters --- libsepol/include/sepol/policydb/ebitmap.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libsepol/include/sepol/policydb/ebitmap.h b/libsepol/include/sepol/policydb/ebitmap.h index 7e19c301d..a5785bab0 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); From 054e921d033f6e57cd0a3f2c597e96fc8af2749a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Thu, 16 Jun 2022 15:12:31 +0200 Subject: [PATCH 2/6] libsepol: add compile-time constraint for mutual exclusive attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new compile-time constraint, similar to neverallow, which enables to specify two or more type attributes to be mutual exclusive. This means no type can be associated with more than one of them. The constraints are stored as a linked-list in the policy for modular policies, by a new modular policy version, and are discarded in kernel policies, not needing any kernel support. Some Reference Policy examples: unpriv_userdomain, admindomain: client_packet_type, server_packet_type: auth_file_type, non_auth_file_type: pseudofs, xattrfs, noxattrfs: reserved_port_type, unreserved_port_type: security_file_type, non_security_file_type: libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type dnssec_t associated with attributes security_file_type and non_security_file_type 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: libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type sysctl_fs_t associated with attributes sysctl_type and file_type libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type sysctl_t associated with attributes sysctl_type and file_type libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type virt_content_t associated with attributes device_node and file_type libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type initrc_devpts_t associated with attributes device_node and file_type libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type qemu_image_t associated with attributes device_node and file_type libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type user_devpts_t associated with attributes device_node and file_type libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type cardmgr_dev_t associated with attributes device_node and file_type libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type bootloader_tmp_t associated with attributes device_node and file_type libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type xen_image_t associated with attributes device_node and file_type libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type svirt_prot_exec_image_t associated with attributes device_node and file_type libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type xen_devpts_t associated with attributes device_node and file_type libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type svirt_image_t associated with attributes device_node and file_type libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type virt_image_t associated with attributes device_node and file_type libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type container_file_t associated with attributes device_node and file_type libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type cpu_online_t associated with attributes sysfs_types and file_type libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type sysfs_t associated with attributes sysfs_types and file_type libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type dockerc_t associated with attributes domain and file_type libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type proc_t associated with attributes file_type and proc_type libsepol.check_disjoint_attributes: Disjoint Attributes Rule violation, type proc_xen_t associated with attributes file_type and proc_type libsepol.check_assertions: 20 Disjoint Attributes Rule failures occurred Closes: https://github.com/SELinuxProject/selinux/issues/42 Signed-off-by: Christian Göttsche --- v4: rename to disjoint attributes v3: - drop source location information: this information was already lost for binary modular policies and CIL policies; also typeattribute statements have none and the few segregate_attributes statements can be easily grepped - misc renaming v2: rebase onto _after suffix change --- libsepol/include/sepol/policydb/policydb.h | 15 ++++- libsepol/src/assertion.c | 66 +++++++++++++++---- libsepol/src/expand.c | 45 ++++++++++++- libsepol/src/kernel_to_conf.c | 32 +++++++++ libsepol/src/link.c | 44 +++++++++++++ libsepol/src/policydb.c | 76 ++++++++++++++++++++++ libsepol/src/policydb_validate.c | 29 +++++++++ libsepol/src/write.c | 34 +++++++++- 8 files changed, 325 insertions(+), 16 deletions(-) diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h index 56d2cb01d..b2c7d1835 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 3076babed..3f042ad3b 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 e63414b14..e7b23da46 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_conf.c b/libsepol/src/kernel_to_conf.c index 5860a513a..33f25df5d 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 b8272308b..19e262c60 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 49f9e8af2..20e43a151 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 c4f8c3002..8ffc12245 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 283d11c8a..26d220a7f 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; } From b26a94278a702bc87cb0f86f1764d4bcb9571bb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Thu, 16 Jun 2022 15:12:36 +0200 Subject: [PATCH 3/6] checkpolicy: add front-end support for disjoint attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support specifying disjoint attributes rules. The following two blocks are equivalent and prevent at compile time that any type can be associated with more than one of the listed attributes: disjoint_attributes attr1, attr2, attr3; disjoint_attributes attr1, attr2; disjoint_attributes attr1, attr3; disjoint_attributes attr2, attr3; Accept more than two attributes with a rule to avoid quadratic growth of necessary statements for a group of attributes. Signed-off-by: Christian Göttsche --- v4: rename to disjoint_attributes --- checkpolicy/policy_define.c | 66 +++++++++++++++++++++++++++ checkpolicy/policy_define.h | 1 + checkpolicy/policy_parse.y | 5 ++ checkpolicy/policy_scan.l | 2 + checkpolicy/tests/policy_allonce.conf | 1 + 5 files changed, 75 insertions(+) diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c index 1c019a3b8..6ea544d0b 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 bcbfe4f33..274d7cff8 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 1b275ebc1..d9bfc9b1a 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 e46677a88..a3797e52a 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 34e6402df..6132b29ea 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 }; From fbf66c0232466c666e89fae0af87fcaddb844a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Wed, 29 Jun 2022 17:55:45 +0200 Subject: [PATCH 4/6] libsepol/tests: add test for disjoint attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test assertions are correctly computed and linking and expanding of modules works correctly. Signed-off-by: Christian Göttsche --- v4: rename to disjoint attributes --- libsepol/tests/libsepol-tests.c | 2 + .../policies/test-disjointattrs/single.conf | 87 ++++++++ .../test-disjointattrs/split_base.conf | 53 +++++ .../test-disjointattrs/split_module1.conf | 9 + .../test-disjointattrs/split_module2.conf | 9 + .../test-disjointattrs/split_module3.conf | 9 + libsepol/tests/test-disjointattributes.c | 197 ++++++++++++++++++ libsepol/tests/test-disjointattributes.h | 10 + 8 files changed, 376 insertions(+) create mode 100644 libsepol/tests/policies/test-disjointattrs/single.conf create mode 100644 libsepol/tests/policies/test-disjointattrs/split_base.conf create mode 100644 libsepol/tests/policies/test-disjointattrs/split_module1.conf create mode 100644 libsepol/tests/policies/test-disjointattrs/split_module2.conf create mode 100644 libsepol/tests/policies/test-disjointattrs/split_module3.conf create mode 100644 libsepol/tests/test-disjointattributes.c create mode 100644 libsepol/tests/test-disjointattributes.h diff --git a/libsepol/tests/libsepol-tests.c b/libsepol/tests/libsepol-tests.c index 968e3cc21..0985391f9 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 000000000..1a9745fc2 --- /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 000000000..6fba8cddc --- /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 000000000..042f2d27d --- /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 000000000..c7888f557 --- /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 000000000..edddcf443 --- /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 000000000..e145fa99f --- /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 000000000..bdc460060 --- /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__ */ From d9a1b8200535387e5fb26619650a6adb06b858f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Thu, 21 Jul 2022 15:14:44 +0200 Subject: [PATCH 5/6] libsepol/cil: add support for segregate attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support the compile time constraint with the following syntax: (disjointattributes (attr1 attr2 [...])) and reports like: ... Qualifying Names Compile post process Building policy binary Checking Neverallows Checking Segregate Attributes Disjoint Attributes Rule violation, type test_type associated with attributes attr1 attr2 Checking User Bounds Checking Role Bounds Checking Type Bounds Failed to generate binary Failed to build policydb Signed-off-by: Christian Göttsche --- v4: rename to disjointattributes --- libsepol/cil/src/cil.c | 17 +++++++ libsepol/cil/src/cil_binary.c | 75 ++++++++++++++++++++++++++++++ libsepol/cil/src/cil_build_ast.c | 58 +++++++++++++++++++++++ libsepol/cil/src/cil_build_ast.h | 2 + libsepol/cil/src/cil_copy_ast.c | 18 +++++++ libsepol/cil/src/cil_flavor.h | 1 + libsepol/cil/src/cil_internal.h | 8 ++++ libsepol/cil/src/cil_policy.c | 26 +++++++++++ libsepol/cil/src/cil_reset_ast.c | 8 ++++ libsepol/cil/src/cil_resolve_ast.c | 38 +++++++++++++++ libsepol/cil/src/cil_resolve_ast.h | 1 + libsepol/cil/src/cil_write_ast.c | 11 +++++ libsepol/src/kernel_to_cil.c | 32 +++++++++++++ secilc/docs/README.md | 1 + secilc/docs/cil_type_statements.md | 50 ++++++++++++++++++++ 15 files changed, 346 insertions(+) diff --git a/libsepol/cil/src/cil.c b/libsepol/cil/src/cil.c index 067e28a6a..cdd4f3fc5 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 95bd18baa..96031cbe2 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 56dac8916..7a155f742 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 7fa4299c9..f7d71981a 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 1507edb44..d1c296ebd 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 155d7c800..e58bb7fde 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 47b67c89d..883892184 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 e9a8f75d8..04671f270 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 fa312c6f0..5352a76fc 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 427a320c9..dca1d8998 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 2f6b7e86a..d2c5515c3 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 f4f9f1670..d944484e3 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/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c index e20ba4afa..d48f40d45 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/secilc/docs/README.md b/secilc/docs/README.md index 5e00fc3b9..ce7c66716 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 19438417c..2fe64c522 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)) +``` From 63e6cee90c58b3b60749c8e783eb06f98b622c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Thu, 21 Jul 2022 12:11:01 +0200 Subject: [PATCH 6/6] secilc/test: add disjoint attributes rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian Göttsche --- v4: rename to disjointattributes --- secilc/test/policy.cil | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/secilc/test/policy.cil b/secilc/test/policy.cil index e6b78618f..b92d3e85e 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)