diff --git a/libsepol/include/sepol/policydb/conditional.h b/libsepol/include/sepol/policydb/conditional.h index a8ed694..1fd1638 100644 --- a/libsepol/include/sepol/policydb/conditional.h +++ b/libsepol/include/sepol/policydb/conditional.h @@ -77,15 +77,16 @@ typedef struct cond_node { /* these true/false lists point into te_avtab when that is used */ cond_av_list_t *true_list; cond_av_list_t *false_list; - /* and these are using during parsing and for modules */ + /* and these are used during parsing and for modules */ avrule_t *avtrue_list; avrule_t *avfalse_list; /* these fields are not written to binary policy */ unsigned int nbools; uint32_t bool_ids[COND_MAX_BOOLS]; uint32_t expr_pre_comp; - /* */ struct cond_node *next; +#define COND_NODE_FLAGS_TUNABLE 0x01 /* a tunable conditional */ + uint32_t flags; } cond_node_t; extern int cond_evaluate_expr(policydb_t * p, cond_expr_t * expr); diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h index 5320bc8..1848a7b 100644 --- a/libsepol/include/sepol/policydb/policydb.h +++ b/libsepol/include/sepol/policydb/policydb.h @@ -210,6 +210,8 @@ typedef struct range_trans { typedef struct cond_bool_datum { symtab_datum_t s; int state; +#define COND_BOOL_FLAGS_TUNABLE 0x01 /* is this a tunable? */ + uint32_t flags; } cond_bool_datum_t; struct cond_node; @@ -683,9 +685,10 @@ extern int policydb_set_target_platform(policydb_t *p, int platform); #define MOD_POLICYDB_VERSION_FILENAME_TRANS 11 #define MOD_POLICYDB_VERSION_ROLETRANS 12 #define MOD_POLICYDB_VERSION_ROLEATTRIB 13 +#define MOD_POLICYDB_VERSION_TUNABLE_SEP 14 #define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE -#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_ROLEATTRIB +#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_TUNABLE_SEP #define POLICYDB_CONFIG_MLS 1 diff --git a/libsepol/src/conditional.c b/libsepol/src/conditional.c index 1482387..ea47cdd 100644 --- a/libsepol/src/conditional.c +++ b/libsepol/src/conditional.c @@ -160,6 +160,7 @@ cond_node_t *cond_node_create(policydb_t * p, cond_node_t * node) for (i = 0; i < min(node->nbools, COND_MAX_BOOLS); i++) new_node->bool_ids[i] = node->bool_ids[i]; new_node->expr_pre_comp = node->expr_pre_comp; + new_node->flags = node->flags; } return new_node; @@ -563,8 +564,8 @@ static int bool_isvalid(cond_bool_datum_t * b) return 1; } -int cond_read_bool(policydb_t * p - __attribute__ ((unused)), hashtab_t h, +int cond_read_bool(policydb_t * p, + hashtab_t h, struct policy_file *fp) { char *key = 0; @@ -596,6 +597,15 @@ int cond_read_bool(policydb_t * p if (rc < 0) goto err; key[len] = 0; + + if (p->policy_type != POLICY_KERN && + p->policyvers >= MOD_POLICYDB_VERSION_TUNABLE_SEP) { + rc = next_entry(buf, fp, sizeof(uint32_t)); + if (rc < 0) + goto err; + booldatum->flags = le32_to_cpu(buf[0]); + } + if (hashtab_insert(h, key, booldatum)) goto err; @@ -810,6 +820,14 @@ static int cond_read_node(policydb_t * p, cond_node_t * node, void *fp) goto err; } + if (p->policy_type != POLICY_KERN && + p->policyvers >= MOD_POLICYDB_VERSION_TUNABLE_SEP) { + rc = next_entry(buf, fp, sizeof(uint32_t)); + if (rc < 0) + goto err; + node->flags = le32_to_cpu(buf[0]); + } + return 0; err: cond_node_destroy(node); diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c index 06f11f4..4458de6 100644 --- a/libsepol/src/expand.c +++ b/libsepol/src/expand.c @@ -1014,6 +1014,11 @@ static int bool_copy_callback(hashtab_key_t key, hashtab_datum_t datum, return 0; } + if (bool->flags & COND_BOOL_FLAGS_TUNABLE) { + /* Skip tunables */ + return 0; + } + if (state->verbose) INFO(state->handle, "copying boolean %s", id); @@ -1046,6 +1051,7 @@ static int bool_copy_callback(hashtab_key_t key, hashtab_datum_t datum, state->boolmap[bool->s.value - 1] = new_bool->s.value; new_bool->state = bool->state; + new_bool->flags = bool->flags; return 0; } @@ -1940,6 +1946,13 @@ static int cond_node_copy(expand_state_t * state, cond_node_t * cn) if (cond_node_copy(state, cn->next)) { return -1; } + + /* If current cond_node_t is of tunable, its effective branch + * has been appended to its home decl->avrules list during link + * and now we should just skip it. */ + if (cn->flags & COND_NODE_FLAGS_TUNABLE) + return 0; + if (cond_normalize_expr(state->base, cn)) { ERR(state->handle, "Error while normalizing conditional"); return -1; diff --git a/libsepol/src/libsepol.map b/libsepol/src/libsepol.map index 719e5b7..4044977 100644 --- a/libsepol/src/libsepol.map +++ b/libsepol/src/libsepol.map @@ -1,5 +1,6 @@ { global: + expand_module_avrules; sepol_module_package_*; sepol_link_modules; sepol_expand_module; sepol_link_packages; sepol_bool_*; sepol_genbools*; sepol_context_*; sepol_mls_*; sepol_check_context; diff --git a/libsepol/src/link.c b/libsepol/src/link.c index 421c47b..4b0fd16 100644 --- a/libsepol/src/link.c +++ b/libsepol/src/link.c @@ -587,7 +587,17 @@ static int bool_copy_callback(hashtab_key_t key, hashtab_datum_t datum, } state->base->p_bools.nprim++; base_bool = new_bool; - + base_bool->flags = booldatum->flags; + } else if ((booldatum->flags & COND_BOOL_FLAGS_TUNABLE) != + (base_bool->flags & COND_BOOL_FLAGS_TUNABLE)) { + /* A mismatch between boolean/tunable declaration + * and usage(for example, a boolean used in the + * tunable_policy macro), then the tunables would + * be filtered out and only the effective branch + * of the cond_node would be preserved. */ + INFO(state->handle, + "%s: Mismatch between boolean/tunable definition " + "and usage for %s", state->cur_mod_name, id); } /* Get the scope info for this boolean to see if this is the declaration, @@ -595,9 +605,12 @@ static int bool_copy_callback(hashtab_key_t key, hashtab_datum_t datum, scope = hashtab_search(state->cur->policy->p_bools_scope.table, id); if (!scope) return SEPOL_ERR; - if (scope->scope == SCOPE_DECL) + if (scope->scope == SCOPE_DECL) { base_bool->state = booldatum->state; - + /* Only the declaration rather than requirement + * decides if it is a boolean or tunable. */ + base_bool->state = booldatum->state; + } state->cur->map[SYM_BOOLS][booldatum->s.value - 1] = base_bool->s.value; return 0; @@ -2451,6 +2464,92 @@ static int populate_roleattributes(link_state_t *state, policydb_t *pol) return 0; } +static void separate_tunables(link_state_t *state, policydb_t *pol) +{ + avrule_block_t *block; + avrule_decl_t *decl; + cond_node_t *cur_node; + cond_expr_t *cur_expr; + int cur_state; + avrule_t *tail, *to_be_appended; + + if (state->verbose) + INFO(state->handle, "Separating tunables from booleans."); + + /* Iterate through all cond_node of all enabled decls, if a cond_node + * is about tunable, caculate its state value and concatenate one of + * its avrule list to the current decl->avrules list. + * + * Note, such tunable cond_node would be skipped over in expansion, + * so we won't have to worry about removing it from decl->cond_list + * here :-) + * + * If tunables and booleans co-exist in the expression of a cond_node, + * then tunables would be "transformed" as booleans. + */ + for (block = pol->global; block != NULL; block = block->next) { + decl = block->enabled; + if (decl == NULL || decl->enabled == 0) + continue; + + tail = decl->avrules; + while (tail && tail->next) + tail = tail->next; + + for (cur_node = decl->cond_list; cur_node != NULL; + cur_node = cur_node->next) { + int booleans, tunables, i; + cond_bool_datum_t *booldatum; + cond_bool_datum_t *tmp[COND_EXPR_MAXDEPTH]; + + booleans = tunables = 0; + memset(tmp, 0, sizeof(cond_bool_datum_t *) * COND_EXPR_MAXDEPTH); + + for (cur_expr = cur_node->expr; cur_expr != NULL; + cur_expr = cur_expr->next) { + if (cur_expr->expr_type != COND_BOOL) + continue; + booldatum = pol->bool_val_to_struct[cur_expr->bool - 1]; + if (booldatum->flags & COND_BOOL_FLAGS_TUNABLE) + tmp[tunables++] = booldatum; + else + booleans++; + } + + if (tunables && booleans) { + /* Tunable mixed with boolean */ + for (i = 0; i < tunables; i++) + tmp[i]->flags &= ~COND_BOOL_FLAGS_TUNABLE; + } else if (tunables && !booleans) { + /* Pure tunable conditional */ + cur_node->flags |= COND_NODE_FLAGS_TUNABLE; + cur_state = cond_evaluate_expr(pol, cur_node->expr); + if (cur_state == -1) { + printf("Expression result was " + "undefined, skipping all" + "rules\n"); + continue; + } + + to_be_appended = (cur_state == 1) ? + cur_node->avtrue_list : cur_node->avfalse_list; + + if (tail) + tail->next = to_be_appended; + else + tail = decl->avrules = to_be_appended; + + /* Update the tail of decl->avrules for + * further concatenation */ + while (tail && tail->next) + tail = tail->next; + + cur_node->avtrue_list = cur_node->avfalse_list = NULL; + } + } + } +} + /* Link a set of modules into a base module. This process is somewhat * similar to an actual compiler: it requires a set of order dependent * steps. The base and every module must have been indexed prior to @@ -2587,6 +2686,11 @@ int link_modules(sepol_handle_t * handle, &state)) goto cleanup; + /* Append tunable's avtrue_list or avfalse_list to the avrules list + * of its home decl depending on its state value, so that the effect + * rules of a tunable would be added to te_avtab permanently. */ + separate_tunables(&state, state.base); + retval = 0; cleanup: for (i = 0; modules != NULL && i < len; i++) { diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c index 017aeca..136b450 100644 --- a/libsepol/src/policydb.c +++ b/libsepol/src/policydb.c @@ -221,6 +221,13 @@ static struct policydb_compat_info policydb_compat[] = { .target_platform = SEPOL_TARGET_SELINUX, }, { + .type = POLICY_BASE, + .version = MOD_POLICYDB_VERSION_TUNABLE_SEP, + .sym_num = SYM_NUM, + .ocon_num = OCON_NODE6 + 1, + .target_platform = SEPOL_TARGET_SELINUX, + }, + { .type = POLICY_MOD, .version = MOD_POLICYDB_VERSION_BASE, .sym_num = SYM_NUM, @@ -290,6 +297,13 @@ static struct policydb_compat_info policydb_compat[] = { .ocon_num = 0, .target_platform = SEPOL_TARGET_SELINUX, }, + { + .type = POLICY_MOD, + .version = MOD_POLICYDB_VERSION_TUNABLE_SEP, + .sym_num = SYM_NUM, + .ocon_num = 0, + .target_platform = SEPOL_TARGET_SELINUX, + }, }; #if 0 diff --git a/libsepol/src/write.c b/libsepol/src/write.c index 290e036..e34ab52 100644 --- a/libsepol/src/write.c +++ b/libsepol/src/write.c @@ -607,6 +607,7 @@ static int cond_write_bool(hashtab_key_t key, hashtab_datum_t datum, void *ptr) unsigned int items, items2; struct policy_data *pd = ptr; struct policy_file *fp = pd->fp; + struct policydb *p = pd->p; booldatum = (cond_bool_datum_t *) datum; @@ -621,6 +622,15 @@ static int cond_write_bool(hashtab_key_t key, hashtab_datum_t datum, void *ptr) items = put_entry(key, 1, len, fp); if (items != len) return POLICYDB_ERROR; + + if (p->policy_type != POLICY_KERN && + p->policyvers >= MOD_POLICYDB_VERSION_TUNABLE_SEP) { + buf[0] = cpu_to_le32(booldatum->flags); + items = put_entry(buf, sizeof(uint32_t), 1, fp); + if (items != 1) + return POLICYDB_ERROR; + } + return POLICYDB_SUCCESS; } @@ -727,6 +737,14 @@ static int cond_write_node(policydb_t * p, return POLICYDB_ERROR; } + if (p->policy_type != POLICY_KERN && + p->policyvers >= MOD_POLICYDB_VERSION_TUNABLE_SEP) { + buf[0] = cpu_to_le32(node->flags); + items = put_entry(buf, sizeof(uint32_t), 1, fp); + if (items != 1) + return POLICYDB_ERROR; + } + return POLICYDB_SUCCESS; } @@ -972,6 +990,19 @@ static int role_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr) role = (role_datum_t *) datum; + /* + * Role attributes are redundant for policy.X, skip them + * when writing the roles symbol table. They are also skipped + * when pp is downgraded. + * + * Their numbers would be deducted in policydb_write(). + */ + if ((role->flavor == ROLE_ATTRIB) && + ((p->policy_type == POLICY_KERN) || + (p->policy_type != POLICY_KERN && + p->policyvers < MOD_POLICYDB_VERSION_ROLEATTRIB))) + return POLICYDB_SUCCESS; + len = strlen(key); items = 0; buf[items++] = cpu_to_le32(len); @@ -1795,6 +1826,19 @@ static int type_attr_uncount(hashtab_key_t key __attribute__ ((unused)), return 0; } +static int role_attr_uncount(hashtab_key_t key __attribute__ ((unused)), + hashtab_datum_t datum, void *args) +{ + role_datum_t *role = datum; + uint32_t *p_nel = args; + + if (role->flavor == ROLE_ATTRIB) { + /* uncount attribute from total number of roles */ + (*p_nel)--; + } + return 0; +} + /* * Write the configuration data in a policy database * structure to a policy database binary representation @@ -1926,7 +1970,7 @@ int policydb_write(policydb_t * p, struct policy_file *fp) num_syms = info->sym_num; for (i = 0; i < num_syms; i++) { buf[0] = cpu_to_le32(p->symtab[i].nprim); - buf[1] = cpu_to_le32(p->symtab[i].table->nel); + buf[1] = p->symtab[i].table->nel; /* * A special case when writing type/attribute symbol table. @@ -1939,6 +1983,20 @@ int policydb_write(policydb_t * p, struct policy_file *fp) p->policy_type == POLICY_KERN) { hashtab_map(p->symtab[i].table, type_attr_uncount, &buf[1]); } + + /* + * Another special case when writing role/attribute symbol + * table, role attributes are redundant for policy.X, or + * when the pp's version is not big enough. So deduct + * their numbers from p_roles.table->nel. + */ + if ((i == SYM_ROLES) && + ((p->policy_type == POLICY_KERN) || + (p->policy_type != POLICY_KERN && + p->policyvers < MOD_POLICYDB_VERSION_ROLEATTRIB))) + hashtab_map(p->symtab[i].table, role_attr_uncount, &buf[1]); + + buf[1] = cpu_to_le32(buf[1]); items = put_entry(buf, sizeof(uint32_t), 2, fp); if (items != 2) return POLICYDB_ERROR;