On 06/19/2015 02:19 PM, James Carter wrote: > The largest change to the user and role bounds checking was to put > them in their own functions, so they could be called independently. > > The type bounds checking was changed to check one type bounds at > a time. An expanded avtab is still created, but now only the rules > of the parent type are expanded. If violations are discovered, > a list of avtab_ptr_t's provides details. This list is used to > display error messages for backwards compatibility and will be > used by CIL to provide a more detailed error message. > > Memory usage is reduced from 9,355M to 126M and time is reduced > from 9 sec to 2 sec. > > Signed-off-by: James Carter <jwcart2@xxxxxxxxxxxxx> Acked-by: Stephen Smalley <sds@xxxxxxxxxxxxx> > --- > libsepol/include/sepol/policydb/hierarchy.h | 11 + > libsepol/src/hierarchy.c | 996 +++++++++++++++++----------- > 2 files changed, 627 insertions(+), 380 deletions(-) > > diff --git a/libsepol/include/sepol/policydb/hierarchy.h b/libsepol/include/sepol/policydb/hierarchy.h > index b4eb9bc..88bc02e 100644 > --- a/libsepol/include/sepol/policydb/hierarchy.h > +++ b/libsepol/include/sepol/policydb/hierarchy.h > @@ -25,11 +25,22 @@ > #ifndef _SEPOL_POLICYDB_HIERARCHY_H_ > #define _SEPOL_POLICYDB_HIERARCHY_H_ > > +#include <sepol/policydb/avtab.h> > #include <sepol/policydb/policydb.h> > #include <sys/cdefs.h> > > __BEGIN_DECLS > > +extern int hierarchy_add_bounds(sepol_handle_t *handle, policydb_t *p); > + > +extern void bounds_destroy_bad(avtab_ptr_t cur); > +extern int bounds_check_type(sepol_handle_t *handle, policydb_t *p, uint32_t child, > + uint32_t parent, avtab_ptr_t *bad, int *numbad); > + > +extern int bounds_check_users(sepol_handle_t *handle, policydb_t *p); > +extern int bounds_check_roles(sepol_handle_t *handle, policydb_t *p); > +extern int bounds_check_types(sepol_handle_t *handle, policydb_t *p); > + > extern int hierarchy_check_constraints(sepol_handle_t * handle, policydb_t * p); > > __END_DECLS > diff --git a/libsepol/src/hierarchy.c b/libsepol/src/hierarchy.c > index d787a64..f6c5fae 100644 > --- a/libsepol/src/hierarchy.c > +++ b/libsepol/src/hierarchy.c > @@ -37,466 +37,702 @@ > > #include "debug.h" > > -typedef struct hierarchy_args { > - policydb_t *p; > - avtab_t *expa; /* expanded avtab */ > - /* This tells check_avtab_hierarchy to check this list in addition to the unconditional avtab */ > - cond_av_list_t *opt_cond_list; > - sepol_handle_t *handle; > - int numerr; > -} hierarchy_args_t; > +#define BOUNDS_AVTAB_SIZE 1024 > > -/* > - * find_parent_(type|role|user) > - * > - * This function returns the parent datum of given XXX_datum_t > - * object or NULL, if it doesn't exist. > - * > - * If the given datum has a valid bounds, this function merely > - * returns the indicated object. Otherwise, it looks up the > - * parent based on the based hierarchy. > - */ > -#define find_parent_template(prefix) \ > -int find_parent_##prefix(hierarchy_args_t *a, \ > - prefix##_datum_t *datum, \ > - prefix##_datum_t **parent) \ > -{ \ > - char *parent_name, *datum_name, *tmp; \ > - \ > - if (datum->bounds) \ > - *parent = a->p->prefix##_val_to_struct[datum->bounds - 1]; \ > - else { \ > - datum_name = a->p->p_##prefix##_val_to_name[datum->s.value - 1]; \ > - \ > - tmp = strrchr(datum_name, '.'); \ > - /* no '.' means it has no parent */ \ > - if (!tmp) { \ > - *parent = NULL; \ > - return 0; \ > - } \ > - \ > - parent_name = strdup(datum_name); \ > - if (!parent_name) \ > - return -1; \ > - parent_name[tmp - datum_name] = '\0'; \ > - \ > - *parent = hashtab_search(a->p->p_##prefix##s.table, parent_name); \ > - if (!*parent) { \ > - /* Orphan type/role/user */ \ > - ERR(a->handle, \ > - "%s doesn't exist, %s is an orphan", \ > - parent_name, \ > - a->p->p_##prefix##_val_to_name[datum->s.value - 1]); \ > - free(parent_name); \ > - return -1; \ > - } \ > - free(parent_name); \ > - } \ > - \ > - return 0; \ > +static int bounds_insert_helper(sepol_handle_t *handle, avtab_t *avtab, > + avtab_key_t *avtab_key, avtab_datum_t *datum) > +{ > + int rc = avtab_insert(avtab, avtab_key, datum); > + if (rc) { > + if (rc == SEPOL_ENOMEM) > + ERR(handle, "Insufficient memory"); > + else > + ERR(handle, "Unexpected error (%d)", rc); > + } > + return rc; > } > > -static find_parent_template(type) > -static find_parent_template(role) > -static find_parent_template(user) > > -static void compute_avtab_datum(hierarchy_args_t *args, > - avtab_key_t *key, > - avtab_datum_t *result) > +static int bounds_insert_rule(sepol_handle_t *handle, avtab_t *avtab, > + avtab_t *global, avtab_t *other, > + avtab_key_t *avtab_key, avtab_datum_t *datum) > { > - avtab_datum_t *avdatp; > - uint32_t av = 0; > - > - avdatp = avtab_search(args->expa, key); > - if (avdatp) > - av = avdatp->data; > - if (args->opt_cond_list) { > - avdatp = cond_av_list_search(key, args->opt_cond_list); > - if (avdatp) > - av |= avdatp->data; > + int rc = 0; > + avtab_datum_t *dup = avtab_search(avtab, avtab_key); > + > + if (!dup) { > + rc = bounds_insert_helper(handle, avtab, avtab_key, datum); > + if (rc) goto exit; > + } else { > + dup->data |= datum->data; > } > > - result->data = av; > + if (other) { > + /* Search the other conditional avtab for the key and > + * add any common permissions to the global avtab > + */ > + uint32_t data = 0; > + dup = avtab_search(other, avtab_key); > + if (dup) { > + data = dup->data & datum->data; > + if (data) { > + dup = avtab_search(global, avtab_key); > + if (!dup) { > + avtab_datum_t d; > + d.data = data; > + rc = bounds_insert_helper(handle, global, > + avtab_key, &d); > + if (rc) goto exit; > + } else { > + dup->data |= data; > + } > + } > + } > + } > + > +exit: > + return rc; > } > > -/* This function verifies that the type passed in either has a parent or is in the > - * root of the namespace, 0 on success, 1 on orphan and -1 on error > - */ > -static int check_type_hierarchy_callback(hashtab_key_t k, hashtab_datum_t d, > - void *args) > +static int bounds_expand_rule(sepol_handle_t *handle, policydb_t *p, > + avtab_t *avtab, avtab_t *global, avtab_t *other, > + uint32_t parent, uint32_t src, uint32_t tgt, > + uint32_t class, uint32_t data) > { > - hierarchy_args_t *a; > - type_datum_t *t, *tp; > - > - a = (hierarchy_args_t *) args; > - t = (type_datum_t *) d; > + int rc = 0; > + avtab_key_t avtab_key; > + avtab_datum_t datum; > + ebitmap_node_t *tnode; > + unsigned int i; > + > + avtab_key.specified = AVTAB_ALLOWED; > + avtab_key.target_class = class; > + datum.data = data; > + > + if (ebitmap_get_bit(&p->attr_type_map[src - 1], parent - 1)) { > + avtab_key.source_type = parent; > + ebitmap_for_each_bit(&p->attr_type_map[tgt - 1], tnode, i) { > + if (!ebitmap_node_get_bit(tnode, i)) > + continue; > + avtab_key.target_type = i + 1; > + rc = bounds_insert_rule(handle, avtab, global, other, > + &avtab_key, &datum); > + if (rc) goto exit; > + } > + } > > - if (t->flavor == TYPE_ATTRIB) { > - /* It's an attribute, we don't care */ > - return 0; > + if (ebitmap_get_bit(&p->attr_type_map[tgt - 1], parent - 1)) { > + avtab_key.target_type = parent; > + ebitmap_for_each_bit(&p->attr_type_map[src - 1], tnode, i) { > + if (!ebitmap_node_get_bit(tnode, i)) > + continue; > + avtab_key.source_type = i + 1; > + rc = bounds_insert_rule(handle, avtab, global, other, > + &avtab_key, &datum); > + if (rc) goto exit; > + } > } > - if (find_parent_type(a, t, &tp) < 0) > - return -1; > - > - if (tp && tp->flavor == TYPE_ATTRIB) { > - /* The parent is an attribute but the child isn't, not legal */ > - ERR(a->handle, "type %s is a child of an attribute %s", > - (char *) k, a->p->p_type_val_to_name[tp->s.value - 1]); > - a->numerr++; > - return -1; > + > +exit: > + return rc; > +} > + > +static int bounds_expand_cond_rules(sepol_handle_t *handle, policydb_t *p, > + cond_av_list_t *cur, avtab_t *avtab, > + avtab_t *global, avtab_t *other, > + uint32_t parent) > +{ > + int rc = 0; > + > + for (; cur; cur = cur->next) { > + avtab_ptr_t n = cur->node; > + rc = bounds_expand_rule(handle, p, avtab, global, other, parent, > + n->key.source_type, n->key.target_type, > + n->key.target_class, n->datum.data); > + if (rc) goto exit; > } > - return 0; > + > +exit: > + return rc; > } > > -/* This function only verifies that the avtab node passed in does not violate any > - * hiearchy constraint via any relationship with other types in the avtab. > - * it should be called using avtab_map, returns 0 on success, 1 on violation and > - * -1 on error. opt_cond_list is an optional argument that tells this to check > - * a conditional list for the relationship as well as the unconditional avtab > - */ > -static int check_avtab_hierarchy_callback(avtab_key_t * k, avtab_datum_t * d, > - void *args) > +struct bounds_expand_args { > + sepol_handle_t *handle; > + policydb_t *p; > + avtab_t *avtab; > + uint32_t parent; > +}; > + > +static int bounds_expand_rule_callback(avtab_key_t *k, avtab_datum_t *d, > + void *args) > { > - avtab_key_t key; > - hierarchy_args_t *a = (hierarchy_args_t *) args; > - type_datum_t *s, *t1 = NULL, *t2 = NULL; > - avtab_datum_t av; > + struct bounds_expand_args *a = (struct bounds_expand_args *)args; > > - if (!(k->specified & AVTAB_ALLOWED)) { > - /* This is not an allow rule, no checking done */ > + if (!(k->specified & AVTAB_ALLOWED)) > return 0; > - } > > - /* search for parent first */ > - s = a->p->type_val_to_struct[k->source_type - 1]; > - if (find_parent_type(a, s, &t1) < 0) > - return -1; > - if (t1) { > - /* > - * search for access allowed between type 1's > - * parent and type 2. > - */ > - key.source_type = t1->s.value; > - key.target_type = k->target_type; > - key.target_class = k->target_class; > - key.specified = AVTAB_ALLOWED; > - compute_avtab_datum(a, &key, &av); > - > - if ((av.data & d->data) == d->data) > - return 0; > + return bounds_expand_rule(a->handle, a->p, a->avtab, NULL, NULL, > + a->parent, k->source_type, k->target_type, > + k->target_class, d->data); > +} > + > +struct bounds_cond_info { > + avtab_t true_avtab; > + avtab_t false_avtab; > + cond_list_t *cond_list; > + struct bounds_cond_info *next; > +}; > + > +static void bounds_destroy_cond_info(struct bounds_cond_info *cur) > +{ > + struct bounds_cond_info *next; > + > + for (; cur; cur = next) { > + next = cur->next; > + avtab_destroy(&cur->true_avtab); > + avtab_destroy(&cur->false_avtab); > + cur->next = NULL; > + free(cur); > } > +} > > - /* next we try type 1 and type 2's parent */ > - s = a->p->type_val_to_struct[k->target_type - 1]; > - if (find_parent_type(a, s, &t2) < 0) > - return -1; > - if (t2) { > - /* > - * search for access allowed between type 1 and > - * type 2's parent. > - */ > - key.source_type = k->source_type; > - key.target_type = t2->s.value; > - key.target_class = k->target_class; > - key.specified = AVTAB_ALLOWED; > - compute_avtab_datum(a, &key, &av); > - > - if ((av.data & d->data) == d->data) > - return 0; > +static int bounds_expand_parent_rules(sepol_handle_t *handle, policydb_t *p, > + avtab_t *global_avtab, > + struct bounds_cond_info **cond_info, > + uint32_t parent) > +{ > + int rc = 0; > + struct bounds_expand_args args; > + cond_list_t *cur; > + > + avtab_init(global_avtab); > + rc = avtab_alloc(global_avtab, BOUNDS_AVTAB_SIZE); > + if (rc) goto oom; > + > + args.handle = handle; > + args.p = p; > + args.avtab = global_avtab; > + args.parent = parent; > + rc = avtab_map(&p->te_avtab, bounds_expand_rule_callback, &args); > + if (rc) goto exit; > + > + *cond_info = NULL; > + for (cur = p->cond_list; cur; cur = cur->next) { > + struct bounds_cond_info *ci; > + ci = malloc(sizeof(struct bounds_cond_info)); > + if (!ci) goto oom; > + avtab_init(&ci->true_avtab); > + avtab_init(&ci->false_avtab); > + ci->cond_list = cur; > + ci->next = *cond_info; > + *cond_info = ci; > + if (cur->true_list) { > + rc = avtab_alloc(&ci->true_avtab, BOUNDS_AVTAB_SIZE); > + if (rc) goto oom; > + rc = bounds_expand_cond_rules(handle, p, cur->true_list, > + &ci->true_avtab, NULL, > + NULL, parent); > + if (rc) goto exit; > + } > + if (cur->false_list) { > + rc = avtab_alloc(&ci->false_avtab, BOUNDS_AVTAB_SIZE); > + if (rc) goto oom; > + rc = bounds_expand_cond_rules(handle, p, cur->false_list, > + &ci->false_avtab, > + global_avtab, > + &ci->true_avtab, parent); > + if (rc) goto exit; > + } > } > > - if (t1 && t2) { > - /* > - * search for access allowed between type 1's parent > - * and type 2's parent. > - */ > - key.source_type = t1->s.value; > - key.target_type = t2->s.value; > - key.target_class = k->target_class; > - key.specified = AVTAB_ALLOWED; > - compute_avtab_datum(a, &key, &av); > - > - if ((av.data & d->data) == d->data) > - return 0; > + return 0; > + > +oom: > + ERR(handle, "Insufficient memory"); > + > +exit: > + ERR(handle,"Failed to expand parent rules\n"); > + avtab_destroy(global_avtab); > + bounds_destroy_cond_info(*cond_info); > + *cond_info = NULL; > + return rc; > +} > + > +static int bounds_not_covered(avtab_t *global_avtab, avtab_t *cur_avtab, > + avtab_key_t *avtab_key, uint32_t data) > +{ > + avtab_datum_t *datum = avtab_search(cur_avtab, avtab_key); > + if (datum) > + data &= ~datum->data; > + if (global_avtab && data) { > + datum = avtab_search(global_avtab, avtab_key); > + if (datum) > + data &= ~datum->data; > } > > - /* > - * Neither one of these types have parents and > - * therefore the hierarchical constraint does not apply > - */ > - if (!t1 && !t2) > - return 0; > + return data; > +} > + > +static int bounds_add_bad(sepol_handle_t *handle, uint32_t src, uint32_t tgt, > + uint32_t class, uint32_t data, avtab_ptr_t *bad) > +{ > + struct avtab_node *new = malloc(sizeof(struct avtab_node)); > + if (new == NULL) { > + ERR(handle, "Insufficient memory"); > + return SEPOL_ENOMEM; > + } > + memset(new, 0, sizeof(struct avtab_node)); > + new->key.source_type = src; > + new->key.target_type = tgt; > + new->key.target_class = class; > + new->datum.data = data; > + new->next = *bad; > + *bad = new; > > - /* > - * At this point there is a violation of the hierarchal > - * constraint, send error condition back > - */ > - ERR(a->handle, > - "hierarchy violation between types %s and %s : %s { %s }", > - a->p->p_type_val_to_name[k->source_type - 1], > - a->p->p_type_val_to_name[k->target_type - 1], > - a->p->p_class_val_to_name[k->target_class - 1], > - sepol_av_to_string(a->p, k->target_class, d->data & ~av.data)); > - a->numerr++; > return 0; > } > > -/* > - * If same permissions are allowed for same combination of > - * source and target, we can evaluate them as unconditional > - * one. > - * See the following example. A_t type is bounds of B_t type, > - * so B_t can never have wider permissions then A_t. > - * A_t has conditional permission on X_t, however, a part of > - * them (getattr and read) are unconditionaly allowed to A_t. > - * > - * Example) > - * typebounds A_t B_t; > - * > - * allow B_t X_t : file { getattr }; > - * if (foo_bool) { > - * allow A_t X_t : file { getattr read }; > - * } else { > - * allow A_t X_t : file { getattr read write }; > - * } > - * > - * We have to pull up them as unconditional ones in this case, > - * because it seems to us B_t is violated to bounds constraints > - * during unconditional policy checking. > - */ > -static int pullup_unconditional_perms(cond_list_t * cond_list, > - hierarchy_args_t * args) > +static int bounds_check_rule(sepol_handle_t *handle, policydb_t *p, > + avtab_t *global_avtab, avtab_t *cur_avtab, > + uint32_t child, uint32_t parent, uint32_t src, > + uint32_t tgt, uint32_t class, uint32_t data, > + avtab_ptr_t *bad, int *numbad) > { > - cond_list_t *cur_node; > - cond_av_list_t *cur_av, *expl_true = NULL, *expl_false = NULL; > - avtab_t expa_true, expa_false; > - avtab_datum_t *avdatp; > - avtab_datum_t avdat; > - avtab_ptr_t avnode; > - > - for (cur_node = cond_list; cur_node; cur_node = cur_node->next) { > - if (avtab_init(&expa_true)) > - goto oom0; > - if (avtab_init(&expa_false)) > - goto oom1; > - if (expand_cond_av_list(args->p, cur_node->true_list, > - &expl_true, &expa_true)) > - goto oom2; > - if (expand_cond_av_list(args->p, cur_node->false_list, > - &expl_false, &expa_false)) > - goto oom3; > - for (cur_av = expl_true; cur_av; cur_av = cur_av->next) { > - avdatp = avtab_search(&expa_false, > - &cur_av->node->key); > - if (!avdatp) > + int rc = 0; > + avtab_key_t avtab_key; > + type_datum_t *td; > + ebitmap_node_t *tnode; > + unsigned int i; > + uint32_t d; > + > + avtab_key.specified = AVTAB_ALLOWED; > + avtab_key.target_class = class; > + > + if (ebitmap_get_bit(&p->attr_type_map[src - 1], child - 1)) { > + avtab_key.source_type = parent; > + ebitmap_for_each_bit(&p->attr_type_map[tgt - 1], tnode, i) { > + if (!ebitmap_node_get_bit(tnode, i)) > continue; > - > - avdat.data = (cur_av->node->datum.data > - & avdatp->data); > - if (!avdat.data) > + avtab_key.target_type = i + 1; > + d = bounds_not_covered(global_avtab, cur_avtab, > + &avtab_key, data); > + if (!d) continue; > + td = p->type_val_to_struct[i]; > + if (td && td->bounds) { > + avtab_key.target_type = td->bounds; > + d = bounds_not_covered(global_avtab, cur_avtab, > + &avtab_key, data); > + if (!d) continue; > + } > + (*numbad)++; > + rc = bounds_add_bad(handle, child, i+1, class, d, bad); > + if (rc) goto exit; > + } > + } > + if (ebitmap_get_bit(&p->attr_type_map[tgt - 1], child - 1)) { > + avtab_key.target_type = parent; > + ebitmap_for_each_bit(&p->attr_type_map[src - 1], tnode, i) { > + if (!ebitmap_node_get_bit(tnode, i)) > continue; > - > - avnode = avtab_search_node(args->expa, > - &cur_av->node->key); > - if (avnode) { > - avnode->datum.data |= avdat.data; > - } else { > - if (avtab_insert(args->expa, > - &cur_av->node->key, > - &avdat)) > - goto oom4; > + avtab_key.source_type = i + 1; > + if (avtab_key.source_type == child) { > + /* Checked above */ > + continue; > + } > + d = bounds_not_covered(global_avtab, cur_avtab, > + &avtab_key, data); > + if (!d) continue; > + td = p->type_val_to_struct[i]; > + if (td && td->bounds) { > + avtab_key.source_type = td->bounds; > + d = bounds_not_covered(global_avtab, cur_avtab, > + &avtab_key, data); > + if (!d) continue; > } > + (*numbad)++; > + rc = bounds_add_bad(handle, i+1, child, class, d, bad); > + if (rc) goto exit; > } > - cond_av_list_destroy(expl_false); > - cond_av_list_destroy(expl_true); > - avtab_destroy(&expa_false); > - avtab_destroy(&expa_true); > } > - return 0; > > -oom4: > - cond_av_list_destroy(expl_false); > -oom3: > - cond_av_list_destroy(expl_true); > -oom2: > - avtab_destroy(&expa_false); > -oom1: > - avtab_destroy(&expa_true); > -oom0: > - ERR(args->handle, "out of memory on conditional av list expansion"); > - return 1; > +exit: > + return rc; > +} > + > +static int bounds_check_cond_rules(sepol_handle_t *handle, policydb_t *p, > + avtab_t *global_avtab, avtab_t *cond_avtab, > + cond_av_list_t *rules, uint32_t child, > + uint32_t parent, avtab_ptr_t *bad, > + int *numbad) > +{ > + int rc = 0; > + cond_av_list_t *cur; > + > + for (cur = rules; cur; cur = cur->next) { > + avtab_ptr_t ap = cur->node; > + avtab_key_t *key = &ap->key; > + avtab_datum_t *datum = &ap->datum; > + if (!(key->specified & AVTAB_ALLOWED)) > + continue; > + rc = bounds_check_rule(handle, p, global_avtab, cond_avtab, > + child, parent, key->source_type, > + key->target_type, key->target_class, > + datum->data, bad, numbad); > + if (rc) goto exit; > + } > + > +exit: > + return rc; > +} > + > +struct bounds_check_args { > + sepol_handle_t *handle; > + policydb_t *p; > + avtab_t *cur_avtab; > + uint32_t child; > + uint32_t parent; > + avtab_ptr_t bad; > + int numbad; > +}; > + > +static int bounds_check_rule_callback(avtab_key_t *k, avtab_datum_t *d, > + void *args) > +{ > + struct bounds_check_args *a = (struct bounds_check_args *)args; > + > + if (!(k->specified & AVTAB_ALLOWED)) > + return 0; > + > + return bounds_check_rule(a->handle, a->p, NULL, a->cur_avtab, a->child, > + a->parent, k->source_type, k->target_type, > + k->target_class, d->data, &a->bad, &a->numbad); > } > > -static int check_cond_avtab_hierarchy(cond_list_t * cond_list, > - hierarchy_args_t * args) > +static int bounds_check_child_rules(sepol_handle_t *handle, policydb_t *p, > + avtab_t *global_avtab, > + struct bounds_cond_info *cond_info, > + uint32_t child, uint32_t parent, > + avtab_ptr_t *bad, int *numbad) > { > int rc; > - cond_list_t *cur_node; > - cond_av_list_t *cur_av, *expl = NULL; > - avtab_t expa; > - hierarchy_args_t *a = (hierarchy_args_t *) args; > - avtab_datum_t avdat, *uncond; > - > - for (cur_node = cond_list; cur_node; cur_node = cur_node->next) { > - /* > - * Check true condition > - */ > - if (avtab_init(&expa)) > - goto oom; > - if (expand_cond_av_list(args->p, cur_node->true_list, > - &expl, &expa)) { > - avtab_destroy(&expa); > - goto oom; > - } > - args->opt_cond_list = expl; > - for (cur_av = expl; cur_av; cur_av = cur_av->next) { > - avdat.data = cur_av->node->datum.data; > - uncond = avtab_search(a->expa, &cur_av->node->key); > - if (uncond) > - avdat.data |= uncond->data; > - rc = check_avtab_hierarchy_callback(&cur_av->node->key, > - &avdat, args); > - if (rc) > - args->numerr++; > - } > - cond_av_list_destroy(expl); > - avtab_destroy(&expa); > + struct bounds_check_args args; > + struct bounds_cond_info *cur; > > - /* > - * Check false condition > - */ > - if (avtab_init(&expa)) > - goto oom; > - if (expand_cond_av_list(args->p, cur_node->false_list, > - &expl, &expa)) { > - avtab_destroy(&expa); > - goto oom; > - } > - args->opt_cond_list = expl; > - for (cur_av = expl; cur_av; cur_av = cur_av->next) { > - avdat.data = cur_av->node->datum.data; > - uncond = avtab_search(a->expa, &cur_av->node->key); > - if (uncond) > - avdat.data |= uncond->data; > - > - rc = check_avtab_hierarchy_callback(&cur_av->node->key, > - &avdat, args); > - if (rc) > - a->numerr++; > + args.handle = handle; > + args.p = p; > + args.cur_avtab = global_avtab; > + args.child = child; > + args.parent = parent; > + args.bad = NULL; > + args.numbad = 0; > + rc = avtab_map(&p->te_avtab, bounds_check_rule_callback, &args); > + if (rc) goto exit; > + > + for (cur = cond_info; cur; cur = cur->next) { > + cond_list_t *node = cur->cond_list; > + rc = bounds_check_cond_rules(handle, p, global_avtab, > + &cur->true_avtab, > + node->true_list, child, parent, > + &args.bad, &args.numbad); > + if (rc) goto exit; > + > + rc = bounds_check_cond_rules(handle, p, global_avtab, > + &cur->false_avtab, > + node->false_list, child, parent, > + &args.bad, &args.numbad); > + if (rc) goto exit; > + } > + > + *numbad += args.numbad; > + *bad = args.bad; > + > +exit: > + return rc; > +} > + > +int bounds_check_type(sepol_handle_t *handle, policydb_t *p, uint32_t child, > + uint32_t parent, avtab_ptr_t *bad, int *numbad) > +{ > + int rc = 0; > + avtab_t global_avtab; > + struct bounds_cond_info *cond_info = NULL; > + > + rc = bounds_expand_parent_rules(handle, p, &global_avtab, &cond_info, parent); > + if (rc) goto exit; > + > + rc = bounds_check_child_rules(handle, p, &global_avtab, cond_info, > + child, parent, bad, numbad); > + > + bounds_destroy_cond_info(cond_info); > + avtab_destroy(&global_avtab); > + > +exit: > + return rc; > +} > + > +struct bounds_args { > + sepol_handle_t *handle; > + policydb_t *p; > + int numbad; > +}; > + > +static void bounds_report(sepol_handle_t *handle, policydb_t *p, uint32_t child, > + uint32_t parent, avtab_ptr_t cur) > +{ > + ERR(handle, "Child type %s exceeds bounds of parent %s in the following rules:", > + p->p_type_val_to_name[child - 1], > + p->p_type_val_to_name[parent - 1]); > + for (; cur; cur = cur->next) { > + ERR(handle, " %s %s : %s { %s }", > + p->p_type_val_to_name[cur->key.source_type - 1], > + p->p_type_val_to_name[cur->key.target_type - 1], > + p->p_class_val_to_name[cur->key.target_class - 1], > + sepol_av_to_string(p, cur->key.target_class, > + cur->datum.data)); > + } > +} > + > +void bounds_destroy_bad(avtab_ptr_t cur) > +{ > + avtab_ptr_t next; > + > + for (; cur; cur = next) { > + next = cur->next; > + cur->next = NULL; > + free(cur); > + } > +} > + > +static int bounds_check_type_callback(hashtab_key_t k __attribute__ ((unused)), > + hashtab_datum_t d, void *args) > +{ > + int rc = 0; > + struct bounds_args *a = (struct bounds_args *)args; > + type_datum_t *t = (type_datum_t *)d; > + avtab_ptr_t bad = NULL; > + > + if (t->bounds) { > + rc = bounds_check_type(a->handle, a->p, t->s.value, t->bounds, > + &bad, &a->numbad); > + if (bad) { > + bounds_report(a->handle, a->p, t->s.value, t->bounds, > + bad); > + bounds_destroy_bad(bad); > } > - cond_av_list_destroy(expl); > - avtab_destroy(&expa); > } > > - return 0; > + return rc; > +} > + > +int bounds_check_types(sepol_handle_t *handle, policydb_t *p) > +{ > + int rc; > + struct bounds_args args; > + > + args.handle = handle; > + args.p = p; > + args.numbad = 0; > + > + rc = hashtab_map(p->p_types.table, bounds_check_type_callback, &args); > + if (rc) goto exit; > + > + if (args.numbad > 0) { > + ERR(handle, "%d errors found during type bounds check", > + args.numbad); > + rc = SEPOL_ERR; > + } > > - oom: > - ERR(args->handle, "out of memory on conditional av list expansion"); > - return 1; > +exit: > + return rc; > } > > -/* The role hierarchy is defined as: a child role cannot have more types than it's parent. > - * This function should be called with hashtab_map, it will return 0 on success, 1 on > - * constraint violation and -1 on error > +/* The role bounds is defined as: a child role cannot have a type that > + * its parent doesn't have. > */ > -static int check_role_hierarchy_callback(hashtab_key_t k > - __attribute__ ((unused)), > - hashtab_datum_t d, void *args) > +static int bounds_check_role_callback(hashtab_key_t k __attribute__ ((unused)), > + hashtab_datum_t d, void *args) > { > - hierarchy_args_t *a; > - role_datum_t *r, *rp; > + struct bounds_args *a = (struct bounds_args *)args; > + role_datum_t *r = (role_datum_t *) d; > + role_datum_t *rp = NULL; > > - a = (hierarchy_args_t *) args; > - r = (role_datum_t *) d; > + if (!r->bounds) > + return 0; > > - if (find_parent_role(a, r, &rp) < 0) > - return -1; > + rp = a->p->role_val_to_struct[r->bounds - 1]; > > if (rp && !ebitmap_contains(&rp->types.types, &r->types.types)) { > - /* hierarchical constraint violation, return error */ > - ERR(a->handle, "Role hierarchy violation, %s exceeds %s", > - (char *) k, a->p->p_role_val_to_name[rp->s.value - 1]); > - a->numerr++; > + ERR(a->handle, "Role bounds violation, %s exceeds %s", > + (char *)k, a->p->p_role_val_to_name[rp->s.value - 1]); > + a->numbad++; > + } > + > + return 0; > +} > + > +int bounds_check_roles(sepol_handle_t *handle, policydb_t *p) > +{ > + struct bounds_args args; > + > + args.handle = handle; > + args.p = p; > + args.numbad = 0; > + > + hashtab_map(p->p_roles.table, bounds_check_role_callback, &args); > + > + if (args.numbad > 0) { > + ERR(handle, "%d errors found during role bounds check", > + args.numbad); > + return SEPOL_ERR; > } > + > return 0; > } > > -/* The user hierarchy is defined as: a child user cannot have a role that > - * its parent doesn't have. This function should be called with hashtab_map, > - * it will return 0 on success, 1 on constraint violation and -1 on error. > +/* The user bounds is defined as: a child user cannot have a role that > + * its parent doesn't have. > */ > -static int check_user_hierarchy_callback(hashtab_key_t k > - __attribute__ ((unused)), > - hashtab_datum_t d, void *args) > +static int bounds_check_user_callback(hashtab_key_t k __attribute__ ((unused)), > + hashtab_datum_t d, void *args) > { > - hierarchy_args_t *a; > - user_datum_t *u, *up; > + struct bounds_args *a = (struct bounds_args *)args; > + user_datum_t *u = (user_datum_t *) d; > + user_datum_t *up = NULL; > > - a = (hierarchy_args_t *) args; > - u = (user_datum_t *) d; > + if (!u->bounds) > + return 0; > > - if (find_parent_user(a, u, &up) < 0) > - return -1; > + up = a->p->user_val_to_struct[u->bounds - 1]; > > if (up && !ebitmap_contains(&up->roles.roles, &u->roles.roles)) { > - /* hierarchical constraint violation, return error */ > - ERR(a->handle, "User hierarchy violation, %s exceeds %s", > + ERR(a->handle, "User bounds violation, %s exceeds %s", > (char *) k, a->p->p_user_val_to_name[up->s.value - 1]); > - a->numerr++; > + a->numbad++; > } > + > return 0; > } > > -int hierarchy_check_constraints(sepol_handle_t * handle, policydb_t * p) > +int bounds_check_users(sepol_handle_t *handle, policydb_t *p) > { > - hierarchy_args_t args; > - avtab_t expa; > - > - if (avtab_init(&expa)) > - goto oom; > - if (expand_avtab(p, &p->te_avtab, &expa)) { > - avtab_destroy(&expa); > - goto oom; > - } > + struct bounds_args args; > > - args.p = p; > - args.expa = &expa; > - args.opt_cond_list = NULL; > args.handle = handle; > - args.numerr = 0; > + args.p = p; > + args.numbad = 0; > > - if (hashtab_map(p->p_types.table, check_type_hierarchy_callback, &args)) > - goto bad; > + hashtab_map(p->p_users.table, bounds_check_user_callback, &args); > > - if (pullup_unconditional_perms(p->cond_list, &args)) > - return -1; > + if (args.numbad > 0) { > + ERR(handle, "%d errors found during user bounds check", > + args.numbad); > + return SEPOL_ERR; > + } > > - if (avtab_map(&expa, check_avtab_hierarchy_callback, &args)) > - goto bad; > + return 0; > +} > > - if (check_cond_avtab_hierarchy(p->cond_list, &args)) > - goto bad; > +#define add_hierarchy_callback_template(prefix) \ > + int hierarchy_add_##prefix##_callback(hashtab_key_t k __attribute__ ((unused)), \ > + hashtab_datum_t d, void *args) \ > +{ \ > + struct bounds_args *a = (struct bounds_args *)args; \ > + sepol_handle_t *handle = a->handle; \ > + policydb_t *p = a->p; \ > + prefix##_datum_t *datum = (prefix##_datum_t *)d; \ > + prefix##_datum_t *parent; \ > + char *parent_name, *datum_name, *tmp; \ > + \ > + if (!datum->bounds) { \ > + datum_name = p->p_##prefix##_val_to_name[datum->s.value - 1]; \ > + \ > + tmp = strrchr(datum_name, '.'); \ > + /* no '.' means it has no parent */ \ > + if (!tmp) return 0; \ > + \ > + parent_name = strdup(datum_name); \ > + if (!parent_name) { \ > + ERR(handle, "Insufficient memory"); \ > + return SEPOL_ENOMEM; \ > + } \ > + parent_name[tmp - datum_name] = '\0'; \ > + \ > + parent = hashtab_search(p->p_##prefix##s.table, parent_name); \ > + if (!parent) { \ > + /* Orphan type/role/user */ \ > + ERR(handle, "%s doesn't exist, %s is an orphan",\ > + parent_name, \ > + p->p_##prefix##_val_to_name[datum->s.value - 1]); \ > + free(parent_name); \ > + a->numbad++; \ > + return 0; \ > + } \ > + datum->bounds = parent->s.value; \ > + free(parent_name); \ > + } \ > + \ > + return 0; \ > +} \ > + > +static add_hierarchy_callback_template(type) > +static add_hierarchy_callback_template(role) > +static add_hierarchy_callback_template(user) > > - if (hashtab_map(p->p_roles.table, check_role_hierarchy_callback, &args)) > - goto bad; > +int hierarchy_add_bounds(sepol_handle_t *handle, policydb_t *p) > +{ > + int rc = 0; > + struct bounds_args args; > + > + args.handle = handle; > + args.p = p; > + args.numbad = 0; > + > + rc = hashtab_map(p->p_users.table, hierarchy_add_user_callback, &args); > + if (rc) goto exit; > + > + rc = hashtab_map(p->p_roles.table, hierarchy_add_role_callback, &args); > + if (rc) goto exit; > > - if (hashtab_map(p->p_users.table, check_user_hierarchy_callback, &args)) > - goto bad; > + rc = hashtab_map(p->p_types.table, hierarchy_add_type_callback, &args); > + if (rc) goto exit; > > - if (args.numerr) { > - ERR(handle, "%d total errors found during hierarchy check", > - args.numerr); > - goto bad; > + if (args.numbad > 0) { > + ERR(handle, "%d errors found while adding hierarchies", > + args.numbad); > + rc = SEPOL_ERR; > } > > - avtab_destroy(&expa); > - return 0; > +exit: > + return rc; > +} > + > +int hierarchy_check_constraints(sepol_handle_t * handle, policydb_t * p) > +{ > + int rc = 0; > + int violation = 0; > + > + rc = hierarchy_add_bounds(handle, p); > + if (rc) goto exit; > + > + rc = bounds_check_users(handle, p); > + if (rc) > + violation = 1; > + > + rc = bounds_check_roles(handle, p); > + if (rc) > + violation = 1; > + > + rc = bounds_check_types(handle, p); > + if (rc) { > + if (rc == SEPOL_ERR) > + violation = 1; > + else > + goto exit; > + } > > - bad: > - avtab_destroy(&expa); > - return -1; > + if (violation) > + rc = SEPOL_ERR; > > - oom: > - ERR(handle, "Out of memory"); > - return -1; > +exit: > + return rc; > } > _______________________________________________ Selinux mailing list Selinux@xxxxxxxxxxxxx To unsubscribe, send email to Selinux-leave@xxxxxxxxxxxxx. To get help, send an email containing "help" to Selinux-request@xxxxxxxxxxxxx.