From 8fdb2255215a1f1488b613737b5fbffb873d8376 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Wed, 23 Nov 2016 09:45:20 -0500 Subject: [PATCH] libsepol,checkpolicy: convert rangetrans and filenametrans to hashtabs range transition and name-based type transition rules were originally simple unordered lists. They were converted to hashtabs in the kernel by commit 2f3e82d694d3d7a2db019db1bb63385fbc1066f3 ("selinux: convert range transition list to a hashtab") and by commit 2463c26d50adc282d19317013ba0ff473823ca47 ("SELinux: put name based create rules in a hashtable"), but left unchanged in libsepol and checkpolicy. Convert libsepol and checkpolicy to use the same hashtabs as the kernel for the range transitions and name-based type transitions. With this change and the preceding one, it is possible to directly compare a policy file generated by libsepol/checkpolicy and the kernel-generated /sys/fs/selinux/policy pseudo file after normalizing them both through checkpolicy. To do so, you can run the following sequence of commands: checkpolicy -M -b /etc/selinux/targeted/policy/policy.30 -o policy.1 checkpolicy -M -b /sys/fs/selinux/policy -o policy.2 cmp policy.1 policy.2 Normalizing the two files via checkpolicy is still necessary to ensure consistent ordering of the avtab entries. There may still be potential for other areas of difference, e.g. xperms entries may lack a well-defined order. Signed-off-by: Stephen Smalley --- checkpolicy/policy_define.c | 57 ++-- checkpolicy/test/dispol.c | 36 ++- libsepol/cil/src/cil_binary.c | 79 +++-- libsepol/include/sepol/policydb/context.h | 40 +++ libsepol/include/sepol/policydb/policydb.h | 18 +- libsepol/src/expand.c | 150 +++++----- libsepol/src/mls.c | 52 ++-- libsepol/src/policydb.c | 327 ++++++++++++++------- libsepol/src/write.c | 177 +++++++---- 9 files changed, 606 insertions(+), 330 deletions(-) diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c index 8b56a293..86478899 100644 --- a/checkpolicy/policy_define.c +++ b/checkpolicy/policy_define.c @@ -3220,11 +3220,12 @@ int define_filename_trans(void) ebitmap_t e_tclasses; ebitmap_node_t *snode, *tnode, *cnode; filename_trans_t *ft; + filename_trans_datum_t *ftdatum; filename_trans_rule_t *ftr; type_datum_t *typdatum; uint32_t otype; unsigned int c, s, t; - int add; + int add, rc; if (pass == 1) { /* stype */ @@ -3308,40 +3309,44 @@ int define_filename_trans(void) ebitmap_for_each_bit(&e_ttypes, tnode, t) { if (!ebitmap_node_get_bit(tnode, t)) continue; - - for (ft = policydbp->filename_trans; ft; ft = ft->next) { - if (ft->stype == (s + 1) && - ft->ttype == (t + 1) && - ft->tclass == (c + 1) && - !strcmp(ft->name, name)) { - yyerror2("duplicate filename transition for: filename_trans %s %s %s:%s", - name, - policydbp->p_type_val_to_name[s], - policydbp->p_type_val_to_name[t], - policydbp->p_class_val_to_name[c]); - goto bad; - } - } - - ft = malloc(sizeof(*ft)); + + ft = calloc(1, sizeof(*ft)); if (!ft) { yyerror("out of memory"); goto bad; } - memset(ft, 0, sizeof(*ft)); - - ft->next = policydbp->filename_trans; - policydbp->filename_trans = ft; - + ft->stype = s+1; + ft->ttype = t+1; + ft->tclass = c+1; ft->name = strdup(name); if (!ft->name) { yyerror("out of memory"); goto bad; } - ft->stype = s + 1; - ft->ttype = t + 1; - ft->tclass = c + 1; - ft->otype = otype; + + ftdatum = hashtab_search(policydbp->filename_trans, + (hashtab_key_t)ft); + if (ftdatum) { + yyerror2("duplicate filename transition for: filename_trans %s %s %s:%s", + name, + policydbp->p_type_val_to_name[s], + policydbp->p_type_val_to_name[t], + policydbp->p_class_val_to_name[c]); + goto bad; + } + + ftdatum = calloc(1, sizeof(*ftdatum)); + if (!ftdatum) { + yyerror("out of memory"); + goto bad; + } + rc = hashtab_insert(policydbp->filename_trans, + (hashtab_key_t)ft, + ftdatum); + if (rc) { + yyerror("out of memory"); + goto bad; + } } } diff --git a/checkpolicy/test/dispol.c b/checkpolicy/test/dispol.c index a78ce818..d91fafdb 100644 --- a/checkpolicy/test/dispol.c +++ b/checkpolicy/test/dispol.c @@ -330,18 +330,38 @@ static void display_role_trans(policydb_t *p, FILE *fp) } } +struct filenametr_display_args { + policydb_t *p; + FILE *fp; +}; + +static int filenametr_display(hashtab_key_t key, + hashtab_datum_t datum, + void *ptr) +{ + struct filename_trans *ft = (struct filename_trans *)key; + struct filename_trans_datum *ftdatum = datum; + struct filenametr_display_args *args = ptr; + policydb_t *p = args->p; + FILE *fp = args->fp; + + display_id(p, fp, SYM_TYPES, ft->stype - 1, ""); + display_id(p, fp, SYM_TYPES, ft->ttype - 1, ""); + display_id(p, fp, SYM_CLASSES, ft->tclass - 1, ":"); + display_id(p, fp, SYM_TYPES, ftdatum->otype - 1, ""); + fprintf(fp, " %s\n", ft->name); + return 0; +} + + static void display_filename_trans(policydb_t *p, FILE *fp) { - filename_trans_t *ft; + struct filenametr_display_args args; fprintf(fp, "filename_trans rules:\n"); - for (ft = p->filename_trans; ft; ft = ft->next) { - display_id(p, fp, SYM_TYPES, ft->stype - 1, ""); - display_id(p, fp, SYM_TYPES, ft->ttype - 1, ""); - display_id(p, fp, SYM_CLASSES, ft->tclass - 1, ":"); - display_id(p, fp, SYM_TYPES, ft->otype - 1, ""); - fprintf(fp, " %s\n", ft->name); - } + args.p = p; + args.fp = fp; + hashtab_map(p->filename_trans, filenametr_display, &args); } int menu(void) diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c index a813201f..d33981b4 100644 --- a/libsepol/cil/src/cil_binary.c +++ b/libsepol/cil/src/cil_binary.c @@ -1131,13 +1131,13 @@ int __cil_typetransition_to_avtab(policydb_t *pdb, const struct cil_db *db, stru class_datum_t *sepol_obj = NULL; struct cil_list *class_list; type_datum_t *sepol_result = NULL; - filename_trans_t *new = NULL; + filename_trans_t *newkey = NULL; + filename_trans_datum_t *newdatum = NULL, *otype = NULL; ebitmap_t src_bitmap, tgt_bitmap; ebitmap_node_t *node1, *node2; unsigned int i, j; struct cil_list_item *c; char *name = DATUM(typetrans->name)->name; - uint32_t *otype = NULL; if (name == CIL_KEY_STAR) { struct cil_type_rule trans; @@ -1177,20 +1177,20 @@ int __cil_typetransition_to_avtab(policydb_t *pdb, const struct cil_db *db, stru rc = __cil_get_sepol_class_datum(pdb, DATUM(c->data), &sepol_obj); if (rc != SEPOL_OK) goto exit; - new = cil_malloc(sizeof(*new)); - memset(new, 0, sizeof(*new)); - new->stype = sepol_src->s.value; - new->ttype = sepol_tgt->s.value; - new->tclass = sepol_obj->s.value; - new->otype = sepol_result->s.value; - new->name = cil_strdup(name); + newkey = cil_calloc(1, sizeof(*newkey)); + newdatum = cil_calloc(1, sizeof(*newdatum)); + newkey->stype = sepol_src->s.value; + newkey->ttype = sepol_tgt->s.value; + newkey->tclass = sepol_obj->s.value; + newkey->name = cil_strdup(name); + newdatum->otype = sepol_result->s.value; - rc = hashtab_insert(filename_trans_table, (hashtab_key_t)new, &(new->otype)); + rc = hashtab_insert(filename_trans_table, (hashtab_key_t)newkey, newdatum); if (rc != SEPOL_OK) { if (rc == SEPOL_EEXIST) { add = CIL_FALSE; - otype = hashtab_search(filename_trans_table, (hashtab_key_t)new); - if (new->otype != *otype) { + otype = hashtab_search(filename_trans_table, (hashtab_key_t)newkey); + if (newdatum->otype != otype->otype) { cil_log(CIL_ERR, "Conflicting name type transition rules\n"); } else { rc = SEPOL_OK; @@ -1201,11 +1201,17 @@ int __cil_typetransition_to_avtab(policydb_t *pdb, const struct cil_db *db, stru } if (add == CIL_TRUE) { - new->next = pdb->filename_trans; - pdb->filename_trans = new; + rc = hashtab_insert(pdb->filename_trans, + (hashtab_key_t)newkey, + newdatum); + if (rc != SEPOL_OK) { + cil_log(CIL_ERR, "Out of memory\n"); + goto exit; + } } else { - free(new->name); - free(new); + free(newkey->name); + free(newkey); + free(newdatum); if (rc != SEPOL_OK) { goto exit; } @@ -2943,7 +2949,8 @@ int cil_rangetransition_to_policydb(policydb_t *pdb, const struct cil_db *db, st type_datum_t *sepol_tgt = NULL; class_datum_t *sepol_class = NULL; struct cil_list *class_list; - range_trans_t *new; + range_trans_t *newkey = NULL; + struct mls_range *newdatum = NULL; ebitmap_t src_bitmap, tgt_bitmap; ebitmap_node_t *node1, *node2; unsigned int i, j; @@ -2975,24 +2982,25 @@ int cil_rangetransition_to_policydb(policydb_t *pdb, const struct cil_db *db, st rc = __cil_get_sepol_class_datum(pdb, DATUM(c->data), &sepol_class); if (rc != SEPOL_OK) goto exit; - new = cil_malloc(sizeof(*new)); - memset(new, 0, sizeof(range_trans_t)); - new->source_type = sepol_src->s.value; - new->target_type = sepol_tgt->s.value; - new->target_class = sepol_class->s.value; - rc = __cil_levelrange_to_mls_range(pdb, rangetrans->range, &new->target_range); + newkey = cil_calloc(1, sizeof(*newkey)); + newdatum = cil_calloc(1, sizeof(*newdatum)); + newkey->source_type = sepol_src->s.value; + newkey->target_type = sepol_tgt->s.value; + newkey->target_class = sepol_class->s.value; + rc = __cil_levelrange_to_mls_range(pdb, rangetrans->range, newdatum); if (rc != SEPOL_OK) { - free(new); + free(newkey); + free(newdatum); goto exit; } rc = SEPOL_OK; - rc = hashtab_insert(range_trans_table, (hashtab_key_t)new, &(new->target_range)); + rc = hashtab_insert(range_trans_table, (hashtab_key_t)newkey, newdatum); if (rc != SEPOL_OK) { if (rc == SEPOL_EEXIST) { add = CIL_FALSE; - o_range = hashtab_search(range_trans_table, (hashtab_key_t)new); - if (!mls_range_eq(&new->target_range, o_range)) { + o_range = hashtab_search(range_trans_table, (hashtab_key_t)newkey); + if (!mls_range_eq(newdatum, o_range)) { cil_log(CIL_ERR, "Conflicting Range transition rules\n"); } else { rc = SEPOL_OK; @@ -3003,11 +3011,20 @@ int cil_rangetransition_to_policydb(policydb_t *pdb, const struct cil_db *db, st } if (add == CIL_TRUE) { - new->next = pdb->range_tr; - pdb->range_tr = new; + rc = hashtab_insert(pdb->range_tr, + (hashtab_key_t)newkey, + newdatum); + if (rc != SEPOL_OK) { + mls_range_destroy(newdatum); + free(newdatum); + free(newkey); + cil_log(CIL_ERR, "Out of memory\n"); + goto exit; + } } else { - mls_range_destroy(&new->target_range); - free(new); + mls_range_destroy(newdatum); + free(newdatum); + free(newkey); if (rc != SEPOL_OK) { goto exit; } diff --git a/libsepol/include/sepol/policydb/context.h b/libsepol/include/sepol/policydb/context.h index dbb7c3ec..c844c37c 100644 --- a/libsepol/include/sepol/policydb/context.h +++ b/libsepol/include/sepol/policydb/context.h @@ -50,6 +50,46 @@ static inline int mls_context_cpy(context_struct_t * dst, return 0; } +/* + * Sets both levels in the MLS range of 'dst' to the low level of 'src'. + */ +static inline int mls_context_cpy_low(context_struct_t *dst, context_struct_t *src) +{ + int rc; + + dst->range.level[0].sens = src->range.level[0].sens; + rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat); + if (rc) + goto out; + + dst->range.level[1].sens = src->range.level[0].sens; + rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[0].cat); + if (rc) + ebitmap_destroy(&dst->range.level[0].cat); +out: + return rc; +} + +/* + * Sets both levels in the MLS range of 'dst' to the high level of 'src'. + */ +static inline int mls_context_cpy_high(context_struct_t *dst, context_struct_t *src) +{ + int rc; + + dst->range.level[0].sens = src->range.level[1].sens; + rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[1].cat); + if (rc) + goto out; + + dst->range.level[1].sens = src->range.level[1].sens; + rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[1].cat); + if (rc) + ebitmap_destroy(&dst->range.level[0].cat); +out: + return rc; +} + static inline int mls_context_cmp(context_struct_t * c1, context_struct_t * c2) { return (mls_level_eq(&c1->range.level[0], &c2->range.level[0]) && diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h index 77e46fbe..31cdd76a 100644 --- a/libsepol/include/sepol/policydb/policydb.h +++ b/libsepol/include/sepol/policydb/policydb.h @@ -162,10 +162,12 @@ typedef struct filename_trans { uint32_t ttype; uint32_t tclass; char *name; - uint32_t otype; - struct filename_trans *next; } filename_trans_t; +typedef struct filename_trans_datum { + uint32_t otype; /* expected of new object */ +} filename_trans_datum_t; + /* Type attributes */ typedef struct type_datum { symtab_datum_t s; @@ -218,8 +220,6 @@ typedef struct range_trans { uint32_t source_type; uint32_t target_type; uint32_t target_class; - mls_range_t target_range; - struct range_trans *next; } range_trans_t; /* Boolean data type */ @@ -555,9 +555,6 @@ typedef struct policydb { /* role transitions */ role_trans_t *role_tr; - /* type transition rules with a 'name' component */ - filename_trans_t *filename_trans; - /* role allows */ role_allow_t *role_allow; @@ -570,8 +567,11 @@ typedef struct policydb { fixed labeling behavior. */ genfs_t *genfs; - /* range transitions */ - range_trans_t *range_tr; + /* range transitions table (range_trans_key -> mls_range) */ + hashtab_t range_tr; + + /* file transitions with the last path component */ + hashtab_t filename_trans; ebitmap_t *type_attr_map; diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c index 7244e01f..315fc65c 100644 --- a/libsepol/src/expand.c +++ b/libsepol/src/expand.c @@ -1385,10 +1385,12 @@ static int copy_role_trans(expand_state_t * state, role_trans_rule_t * rules) static int expand_filename_trans(expand_state_t *state, filename_trans_rule_t *rules) { unsigned int i, j; - filename_trans_t *new_trans, *cur_trans; + filename_trans_t key, *new_trans; + filename_trans_datum_t *otype; filename_trans_rule_t *cur_rule; ebitmap_t stypes, ttypes; ebitmap_node_t *snode, *tnode; + int rc; cur_rule = rules; while (cur_rule) { @@ -1418,40 +1420,32 @@ static int expand_filename_trans(expand_state_t *state, filename_trans_rule_t *r if (!ebitmap_node_get_bit(tnode, j)) continue; - cur_trans = state->out->filename_trans; - while (cur_trans) { - if ((cur_trans->stype == i + 1) && - (cur_trans->ttype == j + 1) && - (cur_trans->tclass == cur_rule->tclass) && - (!strcmp(cur_trans->name, cur_rule->name))) { - /* duplicate rule, who cares */ - if (cur_trans->otype == mapped_otype) - break; + key.stype = i + 1; + key.ttype = j + 1; + key.tclass = cur_rule->tclass; + key.name = cur_rule->name; + otype = hashtab_search(state->out->filename_trans, + (hashtab_key_t) &key); + if (otype) { + /* duplicate rule, ignore */ + if (otype->otype == mapped_otype) + continue; - ERR(state->handle, "Conflicting name-based type_transition %s %s:%s \"%s\": %s vs %s", - state->out->p_type_val_to_name[i], - state->out->p_type_val_to_name[j], - state->out->p_class_val_to_name[cur_trans->tclass - 1], - cur_trans->name, - state->out->p_type_val_to_name[cur_trans->otype - 1], - state->out->p_type_val_to_name[mapped_otype - 1]); - - return -1; - } - cur_trans = cur_trans->next; + ERR(state->handle, "Conflicting name-based type_transition %s %s:%s \"%s\": %s vs %s", + state->out->p_type_val_to_name[i], + state->out->p_type_val_to_name[j], + state->out->p_class_val_to_name[cur_rule->tclass - 1], + cur_rule->name, + state->out->p_type_val_to_name[otype->otype - 1], + state->out->p_type_val_to_name[mapped_otype - 1]); + return -1; } - /* duplicate rule, who cares */ - if (cur_trans) - continue; - new_trans = malloc(sizeof(*new_trans)); + new_trans = calloc(1, sizeof(*new_trans)); if (!new_trans) { ERR(state->handle, "Out of memory!"); return -1; } - memset(new_trans, 0, sizeof(*new_trans)); - new_trans->next = state->out->filename_trans; - state->out->filename_trans = new_trans; new_trans->name = strdup(cur_rule->name); if (!new_trans->name) { @@ -1461,7 +1455,21 @@ static int expand_filename_trans(expand_state_t *state, filename_trans_rule_t *r new_trans->stype = i + 1; new_trans->ttype = j + 1; new_trans->tclass = cur_rule->tclass; - new_trans->otype = mapped_otype; + + otype = calloc(1, sizeof(*otype)); + if (!otype) { + ERR(state->handle, "Out of memory!"); + return -1; + } + otype->otype = mapped_otype; + + rc = hashtab_insert(state->out->filename_trans, + (hashtab_key_t)new_trans, + otype); + if (rc) { + ERR(state->handle, "Out of memory!"); + return -1; + } } } @@ -1477,63 +1485,67 @@ static int exp_rangetr_helper(uint32_t stype, uint32_t ttype, uint32_t tclass, mls_semantic_range_t * trange, expand_state_t * state) { - range_trans_t *rt, *check_rt = state->out->range_tr; - mls_range_t exp_range; + range_trans_t *rt = NULL, key; + mls_range_t *r, *exp_range = NULL; int rc = -1; - if (mls_semantic_range_expand(trange, &exp_range, state->out, + exp_range = calloc(1, sizeof(*exp_range)); + if (!exp_range) { + ERR(state->handle, "Out of memory!"); + return -1; + } + + if (mls_semantic_range_expand(trange, exp_range, state->out, state->handle)) - goto out; + goto err; /* check for duplicates/conflicts */ - while (check_rt) { - if ((check_rt->source_type == stype) && - (check_rt->target_type == ttype) && - (check_rt->target_class == tclass)) { - if (mls_range_eq(&check_rt->target_range, &exp_range)) { - /* duplicate */ - break; - } else { - /* conflict */ - ERR(state->handle, - "Conflicting range trans rule %s %s : %s", - state->out->p_type_val_to_name[stype - 1], - state->out->p_type_val_to_name[ttype - 1], - state->out->p_class_val_to_name[tclass - - 1]); - goto out; - } + key.source_type = stype; + key.target_type = ttype; + key.target_class = tclass; + r = hashtab_search(state->out->range_tr, (hashtab_key_t) &key); + if (r) { + if (mls_range_eq(r, exp_range)) { + /* duplicate, ignore */ + mls_range_destroy(exp_range); + free(exp_range); + return 0; } - check_rt = check_rt->next; - } - if (check_rt) { - /* this is a dup - skip */ - rc = 0; - goto out; + + /* conflict */ + ERR(state->handle, + "Conflicting range trans rule %s %s : %s", + state->out->p_type_val_to_name[stype - 1], + state->out->p_type_val_to_name[ttype - 1], + state->out->p_class_val_to_name[tclass - 1]); + goto err; } - rt = (range_trans_t *) calloc(1, sizeof(range_trans_t)); + rt = calloc(1, sizeof(*rt)); if (!rt) { ERR(state->handle, "Out of memory!"); - goto out; + goto err; } - - rt->next = state->out->range_tr; - state->out->range_tr = rt; - rt->source_type = stype; rt->target_type = ttype; rt->target_class = tclass; - if (mls_range_cpy(&rt->target_range, &exp_range)) { + + rc = hashtab_insert(state->out->range_tr, (hashtab_key_t) rt, + exp_range); + if (rc) { ERR(state->handle, "Out of memory!"); - goto out; + goto err; + } - rc = 0; - - out: - mls_range_destroy(&exp_range); - return rc; + return 0; +err: + free(rt); + if (exp_range) { + mls_range_destroy(exp_range); + free(exp_range); + } + return -1; } static int expand_range_trans(expand_state_t * state, diff --git a/libsepol/src/mls.c b/libsepol/src/mls.c index 8047d914..be854750 100644 --- a/libsepol/src/mls.c +++ b/libsepol/src/mls.c @@ -610,22 +610,45 @@ int mls_compute_sid(policydb_t * policydb, sepol_security_class_t tclass, uint32_t specified, context_struct_t * newcontext) { - range_trans_t *rtr; + range_trans_t rtr; + struct mls_range *r; + struct class_datum *cladatum; + int default_range = 0; + if (!policydb->mls) return 0; switch (specified) { case AVTAB_TRANSITION: /* Look for a range transition rule. */ - for (rtr = policydb->range_tr; rtr; rtr = rtr->next) { - if (rtr->source_type == scontext->type && - rtr->target_type == tcontext->type && - rtr->target_class == tclass) { - /* Set the range from the rule */ - return mls_range_set(newcontext, - &rtr->target_range); - } + rtr.source_type = scontext->type; + rtr.target_type = tcontext->type; + rtr.target_class = tclass; + r = hashtab_search(policydb->range_tr, (hashtab_key_t) &rtr); + if (r) + return mls_range_set(newcontext, r); + + if (tclass && tclass <= policydb->p_classes.nprim) { + cladatum = policydb->class_val_to_struct[tclass - 1]; + if (cladatum) + default_range = cladatum->default_range; } + + switch (default_range) { + case DEFAULT_SOURCE_LOW: + return mls_context_cpy_low(newcontext, scontext); + case DEFAULT_SOURCE_HIGH: + return mls_context_cpy_high(newcontext, scontext); + case DEFAULT_SOURCE_LOW_HIGH: + return mls_context_cpy(newcontext, scontext); + case DEFAULT_TARGET_LOW: + return mls_context_cpy_low(newcontext, tcontext); + case DEFAULT_TARGET_HIGH: + return mls_context_cpy_high(newcontext, tcontext); + case DEFAULT_TARGET_LOW_HIGH: + return mls_context_cpy(newcontext, tcontext); + } + /* Fallthrough */ case AVTAB_CHANGE: if (tclass == SECCLASS_PROCESS) @@ -635,15 +658,8 @@ int mls_compute_sid(policydb_t * policydb, /* Use the process effective MLS attributes. */ return mls_scopy_context(newcontext, scontext); case AVTAB_MEMBER: - /* Only polyinstantiate the MLS attributes if - the type is being polyinstantiated */ - if (newcontext->type != tcontext->type) { - /* Use the process effective MLS attributes. */ - return mls_scopy_context(newcontext, scontext); - } else { - /* Use the related object MLS attributes. */ - return mls_copy_context(newcontext, tcontext); - } + /* Use the process effective MLS attributes. */ + return mls_context_cpy_low(newcontext, scontext); default: return -EINVAL; } diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c index f9b2ec37..142bad8f 100644 --- a/libsepol/src/policydb.c +++ b/libsepol/src/policydb.c @@ -721,6 +721,77 @@ static int roles_init(policydb_t * p) goto out; } +static inline unsigned long +partial_name_hash(unsigned long c, unsigned long prevhash) +{ + return (prevhash + (c << 4) + (c >> 4)) * 11; +} + +static unsigned int filenametr_hash(hashtab_t h, hashtab_key_t k) +{ + const struct filename_trans *ft = (const struct filename_trans *)k; + unsigned long hash; + unsigned int byte_num; + unsigned char focus; + + hash = ft->stype ^ ft->ttype ^ ft->tclass; + + byte_num = 0; + while ((focus = ft->name[byte_num++])) + hash = partial_name_hash(focus, hash); + return hash & (h->size - 1); +} + +static int filenametr_cmp(hashtab_t h __attribute__ ((unused)), + hashtab_key_t k1, hashtab_key_t k2) +{ + const struct filename_trans *ft1 = (const struct filename_trans *)k1; + const struct filename_trans *ft2 = (const struct filename_trans *)k2; + int v; + + v = ft1->stype - ft2->stype; + if (v) + return v; + + v = ft1->ttype - ft2->ttype; + if (v) + return v; + + v = ft1->tclass - ft2->tclass; + if (v) + return v; + + return strcmp(ft1->name, ft2->name); + +} + +static unsigned int rangetr_hash(hashtab_t h, hashtab_key_t k) +{ + const struct range_trans *key = (const struct range_trans *)k; + return (key->source_type + (key->target_type << 3) + + (key->target_class << 5)) & (h->size - 1); +} + +static int rangetr_cmp(hashtab_t h __attribute__ ((unused)), + hashtab_key_t k1, hashtab_key_t k2) +{ + const struct range_trans *key1 = (const struct range_trans *)k1; + const struct range_trans *key2 = (const struct range_trans *)k2; + int v; + + v = key1->source_type - key2->source_type; + if (v) + return v; + + v = key1->target_type - key2->target_type; + if (v) + return v; + + v = key1->target_class - key2->target_class; + + return v; +} + /* * Initialize a policy database structure. */ @@ -730,50 +801,62 @@ int policydb_init(policydb_t * p) memset(p, 0, sizeof(policydb_t)); - ebitmap_init(&p->policycaps); - - ebitmap_init(&p->permissive_map); - for (i = 0; i < SYM_NUM; i++) { p->sym_val_to_name[i] = NULL; rc = symtab_init(&p->symtab[i], symtab_sizes[i]); if (rc) - goto out_free_symtab; + goto err; } /* initialize the module stuff */ for (i = 0; i < SYM_NUM; i++) { if (symtab_init(&p->scope[i], symtab_sizes[i])) { - goto out_free_symtab; + goto err; } } if ((p->global = avrule_block_create()) == NULL || (p->global->branch_list = avrule_decl_create(1)) == NULL) { - goto out_free_symtab; + goto err; } p->decl_val_to_struct = NULL; rc = avtab_init(&p->te_avtab); if (rc) - goto out_free_symtab; + goto err; rc = roles_init(p); if (rc) - goto out_free_symtab; + goto err; rc = cond_policydb_init(p); if (rc) - goto out_free_symtab; - out: - return rc; + goto err; - out_free_symtab: + p->filename_trans = hashtab_create(filenametr_hash, filenametr_cmp, (1 << 10)); + if (!p->filename_trans) { + rc = -ENOMEM; + goto err; + } + + p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256); + if (!p->range_tr) { + rc = -ENOMEM; + goto err; + } + + ebitmap_init(&p->policycaps); + ebitmap_init(&p->permissive_map); + + return 0; +err: + hashtab_destroy(p->filename_trans); + hashtab_destroy(p->range_tr); for (i = 0; i < SYM_NUM; i++) { hashtab_destroy(p->symtab[i].table); hashtab_destroy(p->scope[i].table); } avrule_block_list_destroy(p->global); - goto out; + return rc; } int policydb_role_cache(hashtab_key_t key @@ -1242,6 +1325,27 @@ static int (*destroy_f[SYM_NUM]) (hashtab_key_t key, hashtab_datum_t datum, common_destroy, class_destroy, role_destroy, type_destroy, user_destroy, cond_destroy_bool, sens_destroy, cat_destroy,}; +static int filenametr_destroy(hashtab_key_t key, hashtab_datum_t datum, + void *p __attribute__ ((unused))) +{ + struct filename_trans *ft = (struct filename_trans *)key; + free(ft->name); + free(key); + free(datum); + return 0; +} + +static int range_tr_destroy(hashtab_key_t key, hashtab_datum_t datum, + void *p __attribute__ ((unused))) +{ + struct mls_range *rt = (struct mls_range *)datum; + free(key); + ebitmap_destroy(&rt->level[0].cat); + ebitmap_destroy(&rt->level[1].cat); + free(datum); + return 0; +} + void ocontext_selinux_free(ocontext_t **ocontexts) { ocontext_t *c, *ctmp; @@ -1291,8 +1395,6 @@ void policydb_destroy(policydb_t * p) unsigned int i; role_allow_t *ra, *lra = NULL; role_trans_t *tr, *ltr = NULL; - range_trans_t *rt, *lrt = NULL; - filename_trans_t *ft, *nft; if (!p) return; @@ -1358,14 +1460,6 @@ void policydb_destroy(policydb_t * p) if (ltr) free(ltr); - ft = p->filename_trans; - while (ft) { - nft = ft->next; - free(ft->name); - free(ft); - ft = nft; - } - for (ra = p->role_allow; ra; ra = ra->next) { if (lra) free(lra); @@ -1374,19 +1468,11 @@ void policydb_destroy(policydb_t * p) if (lra) free(lra); - for (rt = p->range_tr; rt; rt = rt->next) { - if (lrt) { - ebitmap_destroy(&lrt->target_range.level[0].cat); - ebitmap_destroy(&lrt->target_range.level[1].cat); - free(lrt); - } - lrt = rt; - } - if (lrt) { - ebitmap_destroy(&lrt->target_range.level[0].cat); - ebitmap_destroy(&lrt->target_range.level[1].cat); - free(lrt); - } + hashtab_map(p->filename_trans, filenametr_destroy, NULL); + hashtab_destroy(p->filename_trans); + + hashtab_map(p->range_tr, range_tr_destroy, NULL); + hashtab_destroy(p->range_tr); if (p->type_attr_map) { for (i = 0; i < p->p_types.nprim; i++) { @@ -2428,11 +2514,12 @@ int role_allow_read(role_allow_t ** r, struct policy_file *fp) return 0; } -int filename_trans_read(filename_trans_t **t, struct policy_file *fp) +int filename_trans_read(policydb_t *p, struct policy_file *fp) { unsigned int i; uint32_t buf[4], nel, len; - filename_trans_t *ft, *lft; + filename_trans_t *ft; + filename_trans_datum_t *otype; int rc; char *name; @@ -2441,43 +2528,73 @@ int filename_trans_read(filename_trans_t **t, struct policy_file *fp) return -1; nel = le32_to_cpu(buf[0]); - lft = NULL; for (i = 0; i < nel; i++) { - ft = calloc(1, sizeof(struct filename_trans)); + ft = NULL; + otype = NULL; + name = NULL; + + ft = calloc(1, sizeof(*ft)); if (!ft) - return -1; - if (lft) - lft->next = ft; - else - *t = ft; - lft = ft; + goto err; + otype = calloc(1, sizeof(*otype)); + if (!ft) + goto err; rc = next_entry(buf, fp, sizeof(uint32_t)); if (rc < 0) - return -1; + goto err; len = le32_to_cpu(buf[0]); if (zero_or_saturated(len)) - return -1; + goto err; name = calloc(len + 1, sizeof(*name)); if (!name) - return -1; + goto err; ft->name = name; rc = next_entry(name, fp, len); if (rc < 0) - return -1; + goto err; rc = next_entry(buf, fp, sizeof(uint32_t) * 4); if (rc < 0) - return -1; + goto err; ft->stype = le32_to_cpu(buf[0]); ft->ttype = le32_to_cpu(buf[1]); ft->tclass = le32_to_cpu(buf[2]); - ft->otype = le32_to_cpu(buf[3]); + otype->otype = le32_to_cpu(buf[3]); + + rc = hashtab_insert(p->filename_trans, (hashtab_key_t) ft, + otype); + if (rc) { + if (rc != SEPOL_EEXIST) + goto err; + /* + * Some old policies were wrongly generated with + * duplicate filename transition rules. For backward + * compatibility, do not reject such policies, just + * issue a warning and ignore the duplicate. + */ + WARN(fp->handle, + "Duplicate name-based type_transition %s %s:%s \"%s\": %s, ignoring", + p->p_type_val_to_name[ft->stype - 1], + p->p_type_val_to_name[ft->ttype - 1], + p->p_class_val_to_name[ft->tclass - 1], + ft->name, + p->p_type_val_to_name[otype->otype - 1]); + free(ft); + free(name); + free(otype); + /* continue, ignoring this one */ + } } return 0; +err: + free(ft); + free(otype); + free(name); + return -1; } static int ocontext_read_xen(struct policydb_compat_info *info, @@ -3129,8 +3246,9 @@ static avrule_t *avrule_read(policydb_t * p static int range_read(policydb_t * p, struct policy_file *fp) { uint32_t buf[2], nel; - range_trans_t *rt, *lrt; - range_trans_rule_t *rtr, *lrtr = NULL; + range_trans_t *rt = NULL; + struct mls_range *r = NULL; + range_trans_rule_t *rtr = NULL, *lrtr = NULL; unsigned int i; int new_rangetr = (p->policy_type == POLICY_KERN && p->policyvers >= POLICYDB_VERSION_RANGETRANS); @@ -3140,84 +3258,79 @@ static int range_read(policydb_t * p, struct policy_file *fp) if (rc < 0) return -1; nel = le32_to_cpu(buf[0]); - lrt = NULL; for (i = 0; i < nel; i++) { rt = calloc(1, sizeof(range_trans_t)); if (!rt) return -1; - if (lrt) - lrt->next = rt; - else - p->range_tr = rt; rc = next_entry(buf, fp, (sizeof(uint32_t) * 2)); if (rc < 0) - return -1; + goto err; rt->source_type = le32_to_cpu(buf[0]); rt->target_type = le32_to_cpu(buf[1]); if (new_rangetr) { rc = next_entry(buf, fp, (sizeof(uint32_t))); if (rc < 0) - return -1; + goto err; rt->target_class = le32_to_cpu(buf[0]); } else rt->target_class = SECCLASS_PROCESS; - if (mls_read_range_helper(&rt->target_range, fp)) - return -1; - lrt = rt; - } + r = calloc(1, sizeof(*r)); + if (!r) + goto err; + if (mls_read_range_helper(r, fp)) + goto err; - /* if this is a kernel policy, we are done - otherwise we need to - * convert these structs to range_trans_rule_ts */ - if (p->policy_type == POLICY_KERN) - return 0; - - /* create range_trans_rules_ts that correspond to the range_trans_ts - * that were just read in from an older policy */ - for (rt = p->range_tr; rt; rt = rt->next) { - rtr = malloc(sizeof(range_trans_rule_t)); - if (!rtr) { - return -1; + if (p->policy_type == POLICY_KERN) { + rc = hashtab_insert(p->range_tr, (hashtab_key_t)rt, r); + if (rc) + goto err; + rt = NULL; + r = NULL; + continue; } + + /* Module policy: convert to range_trans_rule and discard. */ + rtr = malloc(sizeof(range_trans_rule_t)); + if (!rtr) + goto err; range_trans_rule_init(rtr); + if (ebitmap_set_bit(&rtr->stypes.types, rt->source_type - 1, 1)) + goto err; + + if (ebitmap_set_bit(&rtr->ttypes.types, rt->target_type - 1, 1)) + goto err; + + if (ebitmap_set_bit(&rtr->tclasses, rt->target_class - 1, 1)) + goto err; + + if (mls_range_to_semantic(r, &rtr->trange)) + goto err; + if (lrtr) lrtr->next = rtr; else p->global->enabled->range_tr_rules = rtr; - if (ebitmap_set_bit(&rtr->stypes.types, rt->source_type - 1, 1)) - return -1; - - if (ebitmap_set_bit(&rtr->ttypes.types, rt->target_type - 1, 1)) - return -1; - - if (ebitmap_set_bit(&rtr->tclasses, rt->target_class - 1, 1)) - return -1; - - if (mls_range_to_semantic(&rt->target_range, &rtr->trange)) - return -1; - + free(rt); + rt = NULL; + free(r); + r = NULL; lrtr = rtr; } - /* now destroy the range_trans_ts */ - lrt = NULL; - for (rt = p->range_tr; rt; rt = rt->next) { - if (lrt) { - ebitmap_destroy(&lrt->target_range.level[0].cat); - ebitmap_destroy(&lrt->target_range.level[1].cat); - free(lrt); - } - lrt = rt; - } - if (lrt) { - ebitmap_destroy(&lrt->target_range.level[0].cat); - ebitmap_destroy(&lrt->target_range.level[1].cat); - free(lrt); - } - p->range_tr = NULL; - return 0; +err: + free(rt); + if (r) { + mls_range_destroy(r); + free(r); + } + if (rtr) { + range_trans_rule_destroy(rtr); + free(rtr); + } + return -1; } int avrule_read_list(policydb_t * p, avrule_t ** avrules, @@ -3904,7 +4017,7 @@ int policydb_read(policydb_t * p, struct policy_file *fp, unsigned verbose) if (role_allow_read(&p->role_allow, fp)) goto bad; if (r_policyvers >= POLICYDB_VERSION_FILENAME_TRANS && - filename_trans_read(&p->filename_trans, fp)) + filename_trans_read(p, fp)) goto bad; } else { /* first read the AV rule blocks, then the scope tables */ diff --git a/libsepol/src/write.c b/libsepol/src/write.c index fbc6dad4..c4c84ac4 100644 --- a/libsepol/src/write.c +++ b/libsepol/src/write.c @@ -563,40 +563,55 @@ static int role_allow_write(role_allow_t * r, struct policy_file *fp) return POLICYDB_SUCCESS; } -static int filename_trans_write(filename_trans_t * r, struct policy_file *fp) +static int filename_write_helper(hashtab_key_t key, void *data, void *ptr) { - filename_trans_t *ft; uint32_t buf[4]; - size_t nel, items, len; + size_t items, len; + struct filename_trans *ft = (struct filename_trans *)key; + struct filename_trans_datum *otype = data; + void *fp = ptr; - nel = 0; - for (ft = r; ft; ft = ft->next) - nel++; + len = strlen(ft->name); + buf[0] = cpu_to_le32(len); + items = put_entry(buf, sizeof(uint32_t), 1, fp); + if (items != 1) + return POLICYDB_ERROR; + + items = put_entry(ft->name, sizeof(char), len, fp); + if (items != len) + return POLICYDB_ERROR; + + buf[0] = cpu_to_le32(ft->stype); + buf[1] = cpu_to_le32(ft->ttype); + buf[2] = cpu_to_le32(ft->tclass); + buf[3] = cpu_to_le32(otype->otype); + items = put_entry(buf, sizeof(uint32_t), 4, fp); + if (items != 4) + return POLICYDB_ERROR; + + return 0; +} + +static int filename_trans_write(struct policydb *p, void *fp) +{ + size_t nel, items; + uint32_t buf[1]; + int rc; + + if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS) + return 0; + + nel = p->filename_trans->nel; buf[0] = cpu_to_le32(nel); items = put_entry(buf, sizeof(uint32_t), 1, fp); if (items != 1) return POLICYDB_ERROR; - for (ft = r; ft; ft = ft->next) { - len = strlen(ft->name); - buf[0] = cpu_to_le32(len); - items = put_entry(buf, sizeof(uint32_t), 1, fp); - if (items != 1) - return POLICYDB_ERROR; - items = put_entry(ft->name, sizeof(char), len, fp); - if (items != len) - return POLICYDB_ERROR; + rc = hashtab_map(p->filename_trans, filename_write_helper, fp); + if (rc) + return rc; - buf[0] = cpu_to_le32(ft->stype); - buf[1] = cpu_to_le32(ft->ttype); - buf[2] = cpu_to_le32(ft->tclass); - buf[3] = cpu_to_le32(ft->otype); - items = put_entry(buf, sizeof(uint32_t), 4, fp); - if (items != 4) - return POLICYDB_ERROR; - } - - return POLICYDB_SUCCESS; + return 0; } static int role_set_write(role_set_t * x, struct policy_file *fp) @@ -1512,51 +1527,89 @@ static int genfs_write(policydb_t * p, struct policy_file *fp) return POLICYDB_SUCCESS; } + +struct rangetrans_write_args { + size_t nel; + int new_rangetr; + struct policy_file *fp; +}; + +static int rangetrans_count(hashtab_key_t key, + void *data __attribute__ ((unused)), + void *ptr) +{ + struct range_trans *rt = (struct range_trans *)key; + struct rangetrans_write_args *args = ptr; + + /* all range_transitions are written for the new format, only + process related range_transitions are written for the old + format, so count accordingly */ + if (args->new_rangetr || rt->target_class == SECCLASS_PROCESS) + args->nel++; + return 0; +} + +static int range_write_helper(hashtab_key_t key, void *data, void *ptr) +{ + uint32_t buf[2]; + struct range_trans *rt = (struct range_trans *)key; + struct mls_range *r = data; + struct rangetrans_write_args *args = ptr; + struct policy_file *fp = args->fp; + int new_rangetr = args->new_rangetr; + size_t items; + static int warning_issued = 0; + int rc; + + if (!new_rangetr && rt->target_class != SECCLASS_PROCESS) { + if (!warning_issued) + WARN(fp->handle, "Discarding range_transition " + "rules for security classes other than " + "\"process\""); + warning_issued = 1; + return 0; + } + + buf[0] = cpu_to_le32(rt->source_type); + buf[1] = cpu_to_le32(rt->target_type); + items = put_entry(buf, sizeof(uint32_t), 2, fp); + if (items != 2) + return POLICYDB_ERROR; + if (new_rangetr) { + buf[0] = cpu_to_le32(rt->target_class); + items = put_entry(buf, sizeof(uint32_t), 1, fp); + if (items != 1) + return POLICYDB_ERROR; + } + rc = mls_write_range_helper(r, fp); + if (rc) + return rc; + + return 0; +} + static int range_write(policydb_t * p, struct policy_file *fp) { - size_t nel, items; - struct range_trans *rt; + size_t items; uint32_t buf[2]; int new_rangetr = (p->policy_type == POLICY_KERN && p->policyvers >= POLICYDB_VERSION_RANGETRANS); - int warning_issued = 0; + struct rangetrans_write_args args; + int rc; - nel = 0; - for (rt = p->range_tr; rt; rt = rt->next) { - /* all range_transitions are written for the new format, only - process related range_transitions are written for the old - format, so count accordingly */ - if (new_rangetr || rt->target_class == SECCLASS_PROCESS) - nel++; - } - buf[0] = cpu_to_le32(nel); + args.nel = 0; + args.new_rangetr = new_rangetr; + args.fp = fp; + rc = hashtab_map(p->range_tr, rangetrans_count, &args); + if (rc) + return rc; + + buf[0] = cpu_to_le32(args.nel); items = put_entry(buf, sizeof(uint32_t), 1, fp); if (items != 1) return POLICYDB_ERROR; - for (rt = p->range_tr; rt; rt = rt->next) { - if (!new_rangetr && rt->target_class != SECCLASS_PROCESS) { - if (!warning_issued) - WARN(fp->handle, "Discarding range_transition " - "rules for security classes other than " - "\"process\""); - warning_issued = 1; - continue; - } - buf[0] = cpu_to_le32(rt->source_type); - buf[1] = cpu_to_le32(rt->target_type); - items = put_entry(buf, sizeof(uint32_t), 2, fp); - if (items != 2) - return POLICYDB_ERROR; - if (new_rangetr) { - buf[0] = cpu_to_le32(rt->target_class); - items = put_entry(buf, sizeof(uint32_t), 1, fp); - if (items != 1) - return POLICYDB_ERROR; - } - if (mls_write_range_helper(&rt->target_range, fp)) - return POLICYDB_ERROR; - } - return POLICYDB_SUCCESS; + + return hashtab_map(p->range_tr, range_write_helper, &args); } /************** module writing functions below **************/ @@ -2136,7 +2189,7 @@ int policydb_write(policydb_t * p, struct policy_file *fp) if (role_allow_write(p->role_allow, fp)) return POLICYDB_ERROR; if (p->policyvers >= POLICYDB_VERSION_FILENAME_TRANS) { - if (filename_trans_write(p->filename_trans, fp)) + if (filename_trans_write(p, fp)) return POLICYDB_ERROR; } else { if (p->filename_trans)