| /* Authors: Karl MacMillan <kmacmillan@mentalrootkit.com> |
| * Joshua Brindle <jbrindle@tresys.com> |
| * Jason Tang <jtang@tresys.com> |
| * |
| * Copyright (C) 2004-2005 Tresys Technology, LLC |
| * Copyright (C) 2007 Red Hat, Inc. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include <sepol/policydb/policydb.h> |
| #include <sepol/policydb/conditional.h> |
| #include <sepol/policydb/hashtab.h> |
| #include <sepol/policydb/avrule_block.h> |
| #include <sepol/policydb/link.h> |
| #include <sepol/policydb/util.h> |
| |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <assert.h> |
| |
| #include "debug.h" |
| |
| #undef min |
| #define min(a,b) (((a) < (b)) ? (a) : (b)) |
| |
| typedef struct policy_module { |
| policydb_t *policy; |
| uint32_t num_decls; |
| uint32_t *map[SYM_NUM]; |
| uint32_t *avdecl_map; |
| uint32_t **perm_map; |
| uint32_t *perm_map_len; |
| |
| /* a pointer to within the base module's avrule_block chain to |
| * where this module's global now resides */ |
| avrule_block_t *base_global; |
| } policy_module_t; |
| |
| typedef struct link_state { |
| int verbose; |
| policydb_t *base; |
| avrule_block_t *last_avrule_block, *last_base_avrule_block; |
| uint32_t next_decl_id, current_decl_id; |
| |
| /* temporary variables, used during hashtab_map() calls */ |
| policy_module_t *cur; |
| char *cur_mod_name; |
| avrule_decl_t *dest_decl; |
| class_datum_t *src_class, *dest_class; |
| char *dest_class_name; |
| char dest_class_req; /* flag indicating the class was not declared */ |
| uint32_t symbol_num; |
| /* used to report the name of the module if dependancy error occurs */ |
| policydb_t **decl_to_mod; |
| |
| /* error reporting fields */ |
| sepol_handle_t *handle; |
| } link_state_t; |
| |
| typedef struct missing_requirement { |
| uint32_t symbol_type; |
| uint32_t symbol_value; |
| uint32_t perm_value; |
| } missing_requirement_t; |
| |
| static const char *symtab_names[SYM_NUM] = { |
| "common", "class", "role", "type/attribute", "user", |
| "bool", "level", "category" |
| }; |
| |
| /* Deallocates all elements within a module, but NOT the policydb_t |
| * structure within, as well as the pointer itself. */ |
| static void policy_module_destroy(policy_module_t * mod) |
| { |
| unsigned int i; |
| if (mod == NULL) { |
| return; |
| } |
| for (i = 0; i < SYM_NUM; i++) { |
| free(mod->map[i]); |
| } |
| for (i = 0; mod->perm_map != NULL && i < mod->policy->p_classes.nprim; |
| i++) { |
| free(mod->perm_map[i]); |
| } |
| free(mod->perm_map); |
| free(mod->perm_map_len); |
| free(mod->avdecl_map); |
| free(mod); |
| } |
| |
| /***** functions that copy identifiers from a module to base *****/ |
| |
| /* Note: there is currently no scoping for permissions, which causes some |
| * strange side-effects. The current approach is this: |
| * |
| * a) perm is required and the class _and_ perm are declared in base: only add a mapping. |
| * b) perm is required and the class and perm are _not_ declared in base: simply add the permissions |
| * to the object class. This means that the requirements for the decl are the union of the permissions |
| * required for all decls, but who cares. |
| * c) perm is required, the class is declared in base, but the perm is not present. Nothing we can do |
| * here because we can't mark a single permission as required, so we bail with a requirement error |
| * _even_ if we are in an optional. |
| * |
| * A is correct behavior, b is wrong but not too bad, c is totall wrong for optionals. Fixing this requires |
| * a format change. |
| */ |
| static int permission_copy_callback(hashtab_key_t key, hashtab_datum_t datum, |
| void *data) |
| { |
| char *perm_id = key, *new_id = NULL; |
| perm_datum_t *perm, *new_perm = NULL, *dest_perm; |
| link_state_t *state = (link_state_t *) data; |
| |
| class_datum_t *src_class = state->src_class; |
| class_datum_t *dest_class = state->dest_class; |
| policy_module_t *mod = state->cur; |
| uint32_t sclassi = src_class->s.value - 1; |
| int ret; |
| |
| perm = (perm_datum_t *) datum; |
| dest_perm = hashtab_search(dest_class->permissions.table, perm_id); |
| if (dest_perm == NULL && dest_class->comdatum != NULL) { |
| dest_perm = |
| hashtab_search(dest_class->comdatum->permissions.table, |
| perm_id); |
| } |
| |
| if (dest_perm == NULL) { |
| /* If the object class was not declared in the base, add the perm |
| * to the object class. */ |
| if (state->dest_class_req) { |
| /* If the class was required (not declared), insert the new permission */ |
| new_id = strdup(perm_id); |
| if (new_id == NULL) { |
| ERR(state->handle, "Memory error"); |
| ret = SEPOL_ERR; |
| goto err; |
| } |
| new_perm = |
| (perm_datum_t *) calloc(1, sizeof(perm_datum_t)); |
| if (new_perm == NULL) { |
| ERR(state->handle, "Memory error"); |
| ret = SEPOL_ERR; |
| goto err; |
| } |
| ret = hashtab_insert(dest_class->permissions.table, |
| (hashtab_key_t) new_id, |
| (hashtab_datum_t) new_perm); |
| if (ret) { |
| ERR(state->handle, |
| "could not insert permission into class\n"); |
| goto err; |
| } |
| new_perm->s.value = dest_class->permissions.nprim + 1; |
| dest_perm = new_perm; |
| } else { |
| /* this is case c from above */ |
| ERR(state->handle, |
| "Module %s depends on permission %s in class %s, not satisfied", |
| state->cur_mod_name, perm_id, |
| state->dest_class_name); |
| return SEPOL_EREQ; |
| } |
| } |
| |
| /* build the mapping for permissions encompassing this class. |
| * unlike symbols, the permission map translates between |
| * module permission bit to target permission bit. that bit |
| * may have originated from the class -or- it could be from |
| * the class's common parent.*/ |
| if (perm->s.value > mod->perm_map_len[sclassi]) { |
| uint32_t *newmap = calloc(perm->s.value, sizeof(*newmap)); |
| if (newmap == NULL) { |
| ERR(state->handle, "Out of memory!"); |
| return -1; |
| } |
| memcpy(newmap, mod->perm_map[sclassi], |
| mod->perm_map_len[sclassi] * sizeof(*newmap)); |
| free(mod->perm_map[sclassi]); |
| mod->perm_map[sclassi] = newmap; |
| mod->perm_map_len[sclassi] = perm->s.value; |
| } |
| mod->perm_map[sclassi][perm->s.value - 1] = dest_perm->s.value; |
| |
| return 0; |
| err: |
| free(new_id); |
| free(new_perm); |
| return ret; |
| } |
| |
| static int class_copy_default_new_object(link_state_t *state, |
| class_datum_t *olddatum, |
| class_datum_t *newdatum) |
| { |
| if (olddatum->default_user) { |
| if (newdatum->default_user && olddatum->default_user != newdatum->default_user) { |
| ERR(state->handle, "Found conflicting default user definitions"); |
| return SEPOL_ENOTSUP; |
| } |
| newdatum->default_user = olddatum->default_user; |
| } |
| if (olddatum->default_role) { |
| if (newdatum->default_role && olddatum->default_role != newdatum->default_role) { |
| ERR(state->handle, "Found conflicting default role definitions"); |
| return SEPOL_ENOTSUP; |
| } |
| newdatum->default_role = olddatum->default_role; |
| } |
| if (olddatum->default_type) { |
| if (newdatum->default_type && olddatum->default_type != newdatum->default_type) { |
| ERR(state->handle, "Found conflicting default type definitions"); |
| return SEPOL_ENOTSUP; |
| } |
| newdatum->default_type = olddatum->default_type; |
| } |
| if (olddatum->default_range) { |
| if (newdatum->default_range && olddatum->default_range != newdatum->default_range) { |
| ERR(state->handle, "Found conflicting default range definitions"); |
| return SEPOL_ENOTSUP; |
| } |
| newdatum->default_range = olddatum->default_range; |
| } |
| return 0; |
| } |
| |
| static int class_copy_callback(hashtab_key_t key, hashtab_datum_t datum, |
| void *data) |
| { |
| char *id = key, *new_id = NULL; |
| class_datum_t *cladatum, *new_class = NULL; |
| link_state_t *state = (link_state_t *) data; |
| scope_datum_t *scope = NULL; |
| int ret; |
| |
| cladatum = (class_datum_t *) datum; |
| state->dest_class_req = 0; |
| |
| new_class = hashtab_search(state->base->p_classes.table, id); |
| /* If there is not an object class already in the base symtab that means |
| * that either a) a module is trying to declare a new object class (which |
| * the compiler should prevent) or b) an object class was required that is |
| * not in the base. |
| */ |
| if (new_class == NULL) { |
| scope = |
| hashtab_search(state->cur->policy->p_classes_scope.table, |
| id); |
| if (scope == NULL) { |
| ret = SEPOL_ERR; |
| goto err; |
| } |
| if (scope->scope == SCOPE_DECL) { |
| /* disallow declarations in modules */ |
| ERR(state->handle, |
| "%s: Modules may not yet declare new classes.", |
| state->cur_mod_name); |
| ret = SEPOL_ENOTSUP; |
| goto err; |
| } else { |
| /* It would be nice to error early here because the requirement is |
| * not met, but we cannot because the decl might be optional (in which |
| * case we should record the requirement so that it is just turned |
| * off). Note: this will break horribly if modules can declare object |
| * classes because the class numbers will be all wrong (i.e., they |
| * might be assigned in the order they were required rather than the |
| * current scheme which ensures correct numbering by ordering the |
| * declarations properly). This can't be fixed until some infrastructure |
| * for querying the object class numbers is in place. */ |
| state->dest_class_req = 1; |
| new_class = |
| (class_datum_t *) calloc(1, sizeof(class_datum_t)); |
| if (new_class == NULL) { |
| ERR(state->handle, "Memory error\n"); |
| ret = SEPOL_ERR; |
| goto err; |
| } |
| if (symtab_init |
| (&new_class->permissions, PERM_SYMTAB_SIZE)) { |
| ret = SEPOL_ERR; |
| goto err; |
| } |
| new_id = strdup(id); |
| if (new_id == NULL) { |
| ERR(state->handle, "Memory error\n"); |
| symtab_destroy(&new_class->permissions); |
| ret = SEPOL_ERR; |
| goto err; |
| } |
| ret = hashtab_insert(state->base->p_classes.table, |
| (hashtab_key_t) new_id, |
| (hashtab_datum_t) new_class); |
| if (ret) { |
| ERR(state->handle, |
| "could not insert new class into symtab"); |
| symtab_destroy(&new_class->permissions); |
| goto err; |
| } |
| new_class->s.value = ++(state->base->p_classes.nprim); |
| } |
| } |
| |
| state->cur->map[SYM_CLASSES][cladatum->s.value - 1] = |
| new_class->s.value; |
| |
| /* copy permissions */ |
| state->src_class = cladatum; |
| state->dest_class = new_class; |
| state->dest_class_name = (char *)key; |
| |
| /* copy default new object rules */ |
| ret = class_copy_default_new_object(state, cladatum, new_class); |
| if (ret) |
| return ret; |
| |
| ret = |
| hashtab_map(cladatum->permissions.table, permission_copy_callback, |
| state); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| return 0; |
| err: |
| free(new_class); |
| free(new_id); |
| return ret; |
| } |
| |
| static int role_copy_callback(hashtab_key_t key, hashtab_datum_t datum, |
| void *data) |
| { |
| int ret; |
| char *id = key, *new_id = NULL; |
| role_datum_t *role, *base_role, *new_role = NULL; |
| link_state_t *state = (link_state_t *) data; |
| |
| role = (role_datum_t *) datum; |
| |
| base_role = hashtab_search(state->base->p_roles.table, id); |
| if (base_role != NULL) { |
| /* role already exists. check that it is what this |
| * module expected. duplicate declarations (e.g., two |
| * modules both declare role foo_r) is checked during |
| * scope_copy_callback(). */ |
| if (role->flavor == ROLE_ATTRIB |
| && base_role->flavor != ROLE_ATTRIB) { |
| ERR(state->handle, |
| "%s: Expected %s to be a role attribute, but it was already declared as a regular role.", |
| state->cur_mod_name, id); |
| return -1; |
| } else if (role->flavor != ROLE_ATTRIB |
| && base_role->flavor == ROLE_ATTRIB) { |
| ERR(state->handle, |
| "%s: Expected %s to be a regular role, but it was already declared as a role attribute.", |
| state->cur_mod_name, id); |
| return -1; |
| } |
| } else { |
| if (state->verbose) |
| INFO(state->handle, "copying role %s", id); |
| |
| if ((new_id = strdup(id)) == NULL) { |
| goto cleanup; |
| } |
| |
| if ((new_role = |
| (role_datum_t *) malloc(sizeof(*new_role))) == NULL) { |
| goto cleanup; |
| } |
| role_datum_init(new_role); |
| |
| /* new_role's dominates, types and roles field will be copied |
| * during role_fix_callback() */ |
| new_role->flavor = role->flavor; |
| new_role->s.value = state->base->p_roles.nprim + 1; |
| |
| ret = hashtab_insert(state->base->p_roles.table, |
| (hashtab_key_t) new_id, |
| (hashtab_datum_t) new_role); |
| if (ret) { |
| goto cleanup; |
| } |
| state->base->p_roles.nprim++; |
| base_role = new_role; |
| } |
| |
| if (state->dest_decl) { |
| new_id = NULL; |
| if ((new_role = malloc(sizeof(*new_role))) == NULL) { |
| goto cleanup; |
| } |
| role_datum_init(new_role); |
| new_role->flavor = base_role->flavor; |
| new_role->s.value = base_role->s.value; |
| if ((new_id = strdup(id)) == NULL) { |
| goto cleanup; |
| } |
| if (hashtab_insert |
| (state->dest_decl->p_roles.table, new_id, new_role)) { |
| goto cleanup; |
| } |
| state->dest_decl->p_roles.nprim++; |
| } |
| |
| state->cur->map[SYM_ROLES][role->s.value - 1] = base_role->s.value; |
| return 0; |
| |
| cleanup: |
| ERR(state->handle, "Out of memory!"); |
| role_datum_destroy(new_role); |
| free(new_id); |
| free(new_role); |
| return -1; |
| } |
| |
| /* Copy types and attributes from a module into the base module. The |
| * attributes are copied, but the types that make up this attribute |
| * are delayed type_fix_callback(). */ |
| static int type_copy_callback(hashtab_key_t key, hashtab_datum_t datum, |
| void *data) |
| { |
| int ret; |
| char *id = key, *new_id = NULL; |
| type_datum_t *type, *base_type, *new_type = NULL; |
| link_state_t *state = (link_state_t *) data; |
| |
| type = (type_datum_t *) datum; |
| if ((type->flavor == TYPE_TYPE && !type->primary) |
| || type->flavor == TYPE_ALIAS) { |
| /* aliases are handled later, in alias_copy_callback() */ |
| return 0; |
| } |
| |
| base_type = hashtab_search(state->base->p_types.table, id); |
| if (base_type != NULL) { |
| /* type already exists. check that it is what this |
| * module expected. duplicate declarations (e.g., two |
| * modules both declare type foo_t) is checked during |
| * scope_copy_callback(). */ |
| if (type->flavor == TYPE_ATTRIB |
| && base_type->flavor != TYPE_ATTRIB) { |
| ERR(state->handle, |
| "%s: Expected %s to be an attribute, but it was already declared as a type.", |
| state->cur_mod_name, id); |
| return -1; |
| } else if (type->flavor != TYPE_ATTRIB |
| && base_type->flavor == TYPE_ATTRIB) { |
| ERR(state->handle, |
| "%s: Expected %s to be a type, but it was already declared as an attribute.", |
| state->cur_mod_name, id); |
| return -1; |
| } |
| /* permissive should pass to the base type */ |
| base_type->flags |= (type->flags & TYPE_FLAGS_PERMISSIVE); |
| } else { |
| if (state->verbose) |
| INFO(state->handle, "copying type %s", id); |
| |
| if ((new_id = strdup(id)) == NULL) { |
| goto cleanup; |
| } |
| |
| if ((new_type = |
| (type_datum_t *) calloc(1, sizeof(*new_type))) == NULL) { |
| goto cleanup; |
| } |
| new_type->primary = type->primary; |
| new_type->flags = type->flags; |
| new_type->flavor = type->flavor; |
| /* for attributes, the writing of new_type->types is |
| done in type_fix_callback() */ |
| |
| new_type->s.value = state->base->p_types.nprim + 1; |
| |
| ret = hashtab_insert(state->base->p_types.table, |
| (hashtab_key_t) new_id, |
| (hashtab_datum_t) new_type); |
| if (ret) { |
| goto cleanup; |
| } |
| state->base->p_types.nprim++; |
| base_type = new_type; |
| } |
| |
| if (state->dest_decl) { |
| new_id = NULL; |
| if ((new_type = calloc(1, sizeof(*new_type))) == NULL) { |
| goto cleanup; |
| } |
| new_type->primary = type->primary; |
| new_type->flavor = type->flavor; |
| new_type->flags = type->flags; |
| new_type->s.value = base_type->s.value; |
| if ((new_id = strdup(id)) == NULL) { |
| goto cleanup; |
| } |
| if (hashtab_insert |
| (state->dest_decl->p_types.table, new_id, new_type)) { |
| goto cleanup; |
| } |
| state->dest_decl->p_types.nprim++; |
| } |
| |
| state->cur->map[SYM_TYPES][type->s.value - 1] = base_type->s.value; |
| return 0; |
| |
| cleanup: |
| ERR(state->handle, "Out of memory!"); |
| free(new_id); |
| free(new_type); |
| return -1; |
| } |
| |
| static int user_copy_callback(hashtab_key_t key, hashtab_datum_t datum, |
| void *data) |
| { |
| int ret; |
| char *id = key, *new_id = NULL; |
| user_datum_t *user, *base_user, *new_user = NULL; |
| link_state_t *state = (link_state_t *) data; |
| |
| user = (user_datum_t *) datum; |
| |
| base_user = hashtab_search(state->base->p_users.table, id); |
| if (base_user == NULL) { |
| if (state->verbose) |
| INFO(state->handle, "copying user %s", id); |
| |
| if ((new_id = strdup(id)) == NULL) { |
| goto cleanup; |
| } |
| |
| if ((new_user = |
| (user_datum_t *) malloc(sizeof(*new_user))) == NULL) { |
| goto cleanup; |
| } |
| user_datum_init(new_user); |
| /* new_users's roles and MLS fields will be copied during |
| user_fix_callback(). */ |
| |
| new_user->s.value = state->base->p_users.nprim + 1; |
| |
| ret = hashtab_insert(state->base->p_users.table, |
| (hashtab_key_t) new_id, |
| (hashtab_datum_t) new_user); |
| if (ret) { |
| goto cleanup; |
| } |
| state->base->p_users.nprim++; |
| base_user = new_user; |
| } |
| |
| if (state->dest_decl) { |
| new_id = NULL; |
| if ((new_user = malloc(sizeof(*new_user))) == NULL) { |
| goto cleanup; |
| } |
| user_datum_init(new_user); |
| new_user->s.value = base_user->s.value; |
| if ((new_id = strdup(id)) == NULL) { |
| goto cleanup; |
| } |
| if (hashtab_insert |
| (state->dest_decl->p_users.table, new_id, new_user)) { |
| goto cleanup; |
| } |
| state->dest_decl->p_users.nprim++; |
| } |
| |
| state->cur->map[SYM_USERS][user->s.value - 1] = base_user->s.value; |
| return 0; |
| |
| cleanup: |
| ERR(state->handle, "Out of memory!"); |
| user_datum_destroy(new_user); |
| free(new_id); |
| free(new_user); |
| return -1; |
| } |
| |
| static int bool_copy_callback(hashtab_key_t key, hashtab_datum_t datum, |
| void *data) |
| { |
| int ret; |
| char *id = key, *new_id = NULL; |
| cond_bool_datum_t *booldatum, *base_bool, *new_bool = NULL; |
| link_state_t *state = (link_state_t *) data; |
| scope_datum_t *scope; |
| |
| booldatum = (cond_bool_datum_t *) datum; |
| |
| base_bool = hashtab_search(state->base->p_bools.table, id); |
| if (base_bool == NULL) { |
| if (state->verbose) |
| INFO(state->handle, "copying boolean %s", id); |
| |
| if ((new_id = strdup(id)) == NULL) { |
| goto cleanup; |
| } |
| |
| if ((new_bool = |
| (cond_bool_datum_t *) malloc(sizeof(*new_bool))) == NULL) { |
| goto cleanup; |
| } |
| new_bool->s.value = state->base->p_bools.nprim + 1; |
| |
| ret = hashtab_insert(state->base->p_bools.table, |
| (hashtab_key_t) new_id, |
| (hashtab_datum_t) new_bool); |
| if (ret) { |
| goto cleanup; |
| } |
| state->base->p_bools.nprim++; |
| base_bool = new_bool; |
| base_bool->flags = booldatum->flags; |
| base_bool->state = booldatum->state; |
| } 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() or vice versa). |
| * |
| * This is not allowed and bail out with errors */ |
| ERR(state->handle, |
| "%s: Mismatch between boolean/tunable definition " |
| "and usage for %s", state->cur_mod_name, id); |
| return -1; |
| } |
| |
| /* Get the scope info for this boolean to see if this is the declaration, |
| * if so set the state */ |
| scope = hashtab_search(state->cur->policy->p_bools_scope.table, id); |
| if (!scope) |
| return SEPOL_ERR; |
| 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->flags = booldatum->flags; |
| } |
| state->cur->map[SYM_BOOLS][booldatum->s.value - 1] = base_bool->s.value; |
| return 0; |
| |
| cleanup: |
| ERR(state->handle, "Out of memory!"); |
| cond_destroy_bool(new_id, new_bool, NULL); |
| return -1; |
| } |
| |
| static int sens_copy_callback(hashtab_key_t key, hashtab_datum_t datum, |
| void *data) |
| { |
| char *id = key; |
| level_datum_t *level, *base_level; |
| link_state_t *state = (link_state_t *) data; |
| scope_datum_t *scope; |
| |
| level = (level_datum_t *) datum; |
| |
| base_level = hashtab_search(state->base->p_levels.table, id); |
| if (!base_level) { |
| scope = |
| hashtab_search(state->cur->policy->p_sens_scope.table, id); |
| if (!scope) |
| return SEPOL_ERR; |
| if (scope->scope == SCOPE_DECL) { |
| /* disallow declarations in modules */ |
| ERR(state->handle, |
| "%s: Modules may not declare new sensitivities.", |
| state->cur_mod_name); |
| return SEPOL_ENOTSUP; |
| } else if (scope->scope == SCOPE_REQ) { |
| /* unmet requirement */ |
| ERR(state->handle, |
| "%s: Sensitivity %s not declared by base.", |
| state->cur_mod_name, id); |
| return SEPOL_ENOTSUP; |
| } else { |
| ERR(state->handle, |
| "%s: has an unknown scope: %d\n", |
| state->cur_mod_name, scope->scope); |
| return SEPOL_ENOTSUP; |
| } |
| } |
| |
| state->cur->map[SYM_LEVELS][level->level->sens - 1] = |
| base_level->level->sens; |
| |
| return 0; |
| } |
| |
| static int cat_copy_callback(hashtab_key_t key, hashtab_datum_t datum, |
| void *data) |
| { |
| char *id = key; |
| cat_datum_t *cat, *base_cat; |
| link_state_t *state = (link_state_t *) data; |
| scope_datum_t *scope; |
| |
| cat = (cat_datum_t *) datum; |
| |
| base_cat = hashtab_search(state->base->p_cats.table, id); |
| if (!base_cat) { |
| scope = hashtab_search(state->cur->policy->p_cat_scope.table, id); |
| if (!scope) |
| return SEPOL_ERR; |
| if (scope->scope == SCOPE_DECL) { |
| /* disallow declarations in modules */ |
| ERR(state->handle, |
| "%s: Modules may not declare new categories.", |
| state->cur_mod_name); |
| return SEPOL_ENOTSUP; |
| } else if (scope->scope == SCOPE_REQ) { |
| /* unmet requirement */ |
| ERR(state->handle, |
| "%s: Category %s not declared by base.", |
| state->cur_mod_name, id); |
| return SEPOL_ENOTSUP; |
| } else { |
| /* unknown scope? malformed policy? */ |
| ERR(state->handle, |
| "%s: has an unknown scope: %d\n", |
| state->cur_mod_name, scope->scope); |
| return SEPOL_ENOTSUP; |
| } |
| } |
| |
| state->cur->map[SYM_CATS][cat->s.value - 1] = base_cat->s.value; |
| |
| return 0; |
| } |
| |
| static int (*copy_callback_f[SYM_NUM]) (hashtab_key_t key, |
| hashtab_datum_t datum, void *datap) = { |
| NULL, class_copy_callback, role_copy_callback, type_copy_callback, |
| user_copy_callback, bool_copy_callback, sens_copy_callback, |
| cat_copy_callback}; |
| |
| /* |
| * The boundaries have to be copied after the types/roles/users are copied, |
| * because it refers hashtab to lookup destinated objects. |
| */ |
| static int type_bounds_copy_callback(hashtab_key_t key, |
| hashtab_datum_t datum, void *data) |
| { |
| link_state_t *state = (link_state_t *) data; |
| type_datum_t *type = (type_datum_t *) datum; |
| type_datum_t *dest; |
| uint32_t bounds_val; |
| |
| if (!type->bounds) |
| return 0; |
| |
| bounds_val = state->cur->map[SYM_TYPES][type->bounds - 1]; |
| |
| dest = hashtab_search(state->base->p_types.table, key); |
| if (!dest) { |
| ERR(state->handle, |
| "Type lookup failed for %s", (char *)key); |
| return -1; |
| } |
| if (dest->bounds != 0 && dest->bounds != bounds_val) { |
| ERR(state->handle, |
| "Inconsistent boundary for %s", (char *)key); |
| return -1; |
| } |
| dest->bounds = bounds_val; |
| |
| return 0; |
| } |
| |
| static int role_bounds_copy_callback(hashtab_key_t key, |
| hashtab_datum_t datum, void *data) |
| { |
| link_state_t *state = (link_state_t *) data; |
| role_datum_t *role = (role_datum_t *) datum; |
| role_datum_t *dest; |
| uint32_t bounds_val; |
| |
| if (!role->bounds) |
| return 0; |
| |
| bounds_val = state->cur->map[SYM_ROLES][role->bounds - 1]; |
| |
| dest = hashtab_search(state->base->p_roles.table, key); |
| if (!dest) { |
| ERR(state->handle, |
| "Role lookup failed for %s", (char *)key); |
| return -1; |
| } |
| if (dest->bounds != 0 && dest->bounds != bounds_val) { |
| ERR(state->handle, |
| "Inconsistent boundary for %s", (char *)key); |
| return -1; |
| } |
| dest->bounds = bounds_val; |
| |
| return 0; |
| } |
| |
| static int user_bounds_copy_callback(hashtab_key_t key, |
| hashtab_datum_t datum, void *data) |
| { |
| link_state_t *state = (link_state_t *) data; |
| user_datum_t *user = (user_datum_t *) datum; |
| user_datum_t *dest; |
| uint32_t bounds_val; |
| |
| if (!user->bounds) |
| return 0; |
| |
| bounds_val = state->cur->map[SYM_USERS][user->bounds - 1]; |
| |
| dest = hashtab_search(state->base->p_users.table, key); |
| if (!dest) { |
| ERR(state->handle, |
| "User lookup failed for %s", (char *)key); |
| return -1; |
| } |
| if (dest->bounds != 0 && dest->bounds != bounds_val) { |
| ERR(state->handle, |
| "Inconsistent boundary for %s", (char *)key); |
| return -1; |
| } |
| dest->bounds = bounds_val; |
| |
| return 0; |
| } |
| |
| /* The aliases have to be copied after the types and attributes to be |
| * certain that the base symbol table will have the type that the |
| * alias refers. Otherwise, we won't be able to find the type value |
| * for the alias. We can't depend on the declaration ordering because |
| * of the hash table. |
| */ |
| static int alias_copy_callback(hashtab_key_t key, hashtab_datum_t datum, |
| void *data) |
| { |
| char *id = key, *new_id = NULL, *target_id; |
| type_datum_t *type, *base_type, *new_type = NULL, *target_type; |
| link_state_t *state = (link_state_t *) data; |
| policy_module_t *mod = state->cur; |
| int primval; |
| |
| type = (type_datum_t *) datum; |
| /* there are 2 kinds of aliases. Ones with their own value (TYPE_ALIAS) |
| * and ones with the value of their primary (TYPE_TYPE && type->primary = 0) |
| */ |
| if (! |
| (type->flavor == TYPE_ALIAS |
| || (type->flavor == TYPE_TYPE && !type->primary))) { |
| /* ignore types and attributes -- they were handled in |
| * type_copy_callback() */ |
| return 0; |
| } |
| |
| if (type->flavor == TYPE_ALIAS) |
| primval = type->primary; |
| else |
| primval = type->s.value; |
| |
| target_id = mod->policy->p_type_val_to_name[primval - 1]; |
| target_type = hashtab_search(state->base->p_types.table, target_id); |
| if (target_type == NULL) { |
| ERR(state->handle, "%s: Could not find type %s for alias %s.", |
| state->cur_mod_name, target_id, id); |
| return -1; |
| } |
| |
| if (!strcmp(id, target_id)) { |
| ERR(state->handle, "%s: Self aliasing of %s.", |
| state->cur_mod_name, id); |
| return -1; |
| } |
| |
| target_type->flags |= (type->flags & TYPE_FLAGS_PERMISSIVE); |
| |
| base_type = hashtab_search(state->base->p_types.table, id); |
| if (base_type == NULL) { |
| if (state->verbose) |
| INFO(state->handle, "copying alias %s", id); |
| |
| if ((new_type = |
| (type_datum_t *) calloc(1, sizeof(*new_type))) == NULL) { |
| goto cleanup; |
| } |
| /* the linked copy always has TYPE_ALIAS style aliases */ |
| new_type->primary = target_type->s.value; |
| new_type->flags = target_type->flags; |
| new_type->flavor = TYPE_ALIAS; |
| new_type->s.value = state->base->p_types.nprim + 1; |
| if ((new_id = strdup(id)) == NULL) { |
| goto cleanup; |
| } |
| if (hashtab_insert |
| (state->base->p_types.table, new_id, new_type)) { |
| goto cleanup; |
| } |
| state->base->p_types.nprim++; |
| base_type = new_type; |
| } else { |
| |
| /* if this already exists and isn't an alias it was required by another module (or base) |
| * and inserted into the hashtable as a type, fix it up now */ |
| |
| if (base_type->flavor == TYPE_ALIAS) { |
| /* error checking */ |
| assert(base_type->primary == target_type->s.value); |
| assert(base_type->primary == |
| mod->map[SYM_TYPES][primval - 1]); |
| assert(mod->map[SYM_TYPES][type->s.value - 1] == |
| base_type->primary); |
| return 0; |
| } |
| |
| if (base_type->flavor == TYPE_ATTRIB) { |
| ERR(state->handle, |
| "%s is an alias of an attribute, not allowed", id); |
| return -1; |
| } |
| |
| base_type->flavor = TYPE_ALIAS; |
| base_type->primary = target_type->s.value; |
| base_type->flags |= (target_type->flags & TYPE_FLAGS_PERMISSIVE); |
| |
| } |
| /* the aliases map points from its value to its primary so when this module |
| * references this type the value it gets back from the map is the primary */ |
| mod->map[SYM_TYPES][type->s.value - 1] = base_type->primary; |
| |
| return 0; |
| |
| cleanup: |
| ERR(state->handle, "Out of memory!"); |
| free(new_id); |
| free(new_type); |
| return -1; |
| } |
| |
| /*********** callbacks that fix bitmaps ***********/ |
| |
| static int type_set_convert(type_set_t * types, type_set_t * dst, |
| policy_module_t * mod, link_state_t * state |
| __attribute__ ((unused))) |
| { |
| unsigned int i; |
| ebitmap_node_t *tnode; |
| ebitmap_for_each_bit(&types->types, tnode, i) { |
| if (ebitmap_node_get_bit(tnode, i)) { |
| assert(mod->map[SYM_TYPES][i]); |
| if (ebitmap_set_bit |
| (&dst->types, mod->map[SYM_TYPES][i] - 1, 1)) { |
| goto cleanup; |
| } |
| } |
| } |
| ebitmap_for_each_bit(&types->negset, tnode, i) { |
| if (ebitmap_node_get_bit(tnode, i)) { |
| assert(mod->map[SYM_TYPES][i]); |
| if (ebitmap_set_bit |
| (&dst->negset, mod->map[SYM_TYPES][i] - 1, 1)) { |
| goto cleanup; |
| } |
| } |
| } |
| dst->flags = types->flags; |
| return 0; |
| |
| cleanup: |
| return -1; |
| } |
| |
| /* OR 2 typemaps together and at the same time map the src types to |
| * the correct values in the dst typeset. |
| */ |
| static int type_set_or_convert(type_set_t * types, type_set_t * dst, |
| policy_module_t * mod, link_state_t * state) |
| { |
| type_set_t ts_tmp; |
| |
| type_set_init(&ts_tmp); |
| if (type_set_convert(types, &ts_tmp, mod, state) == -1) { |
| goto cleanup; |
| } |
| if (type_set_or_eq(dst, &ts_tmp)) { |
| goto cleanup; |
| } |
| type_set_destroy(&ts_tmp); |
| return 0; |
| |
| cleanup: |
| ERR(state->handle, "Out of memory!"); |
| type_set_destroy(&ts_tmp); |
| return -1; |
| } |
| |
| static int role_set_or_convert(role_set_t * roles, role_set_t * dst, |
| policy_module_t * mod, link_state_t * state) |
| { |
| unsigned int i; |
| ebitmap_t tmp; |
| ebitmap_node_t *rnode; |
| |
| ebitmap_init(&tmp); |
| ebitmap_for_each_bit(&roles->roles, rnode, i) { |
| if (ebitmap_node_get_bit(rnode, i)) { |
| assert(mod->map[SYM_ROLES][i]); |
| if (ebitmap_set_bit |
| (&tmp, mod->map[SYM_ROLES][i] - 1, 1)) { |
| goto cleanup; |
| } |
| } |
| } |
| if (ebitmap_union(&dst->roles, &tmp)) { |
| goto cleanup; |
| } |
| dst->flags |= roles->flags; |
| ebitmap_destroy(&tmp); |
| return 0; |
| cleanup: |
| ERR(state->handle, "Out of memory!"); |
| ebitmap_destroy(&tmp); |
| return -1; |
| } |
| |
| static int mls_level_convert(mls_semantic_level_t * src, mls_semantic_level_t * dst, |
| policy_module_t * mod, link_state_t * state) |
| { |
| mls_semantic_cat_t *src_cat, *new_cat; |
| |
| if (!mod->policy->mls) |
| return 0; |
| |
| /* Required not declared. */ |
| if (!src->sens) |
| return 0; |
| |
| assert(mod->map[SYM_LEVELS][src->sens - 1]); |
| dst->sens = mod->map[SYM_LEVELS][src->sens - 1]; |
| |
| for (src_cat = src->cat; src_cat; src_cat = src_cat->next) { |
| new_cat = |
| (mls_semantic_cat_t *) malloc(sizeof(mls_semantic_cat_t)); |
| if (!new_cat) { |
| ERR(state->handle, "Out of memory"); |
| return -1; |
| } |
| mls_semantic_cat_init(new_cat); |
| |
| new_cat->next = dst->cat; |
| dst->cat = new_cat; |
| |
| assert(mod->map[SYM_CATS][src_cat->low - 1]); |
| dst->cat->low = mod->map[SYM_CATS][src_cat->low - 1]; |
| assert(mod->map[SYM_CATS][src_cat->high - 1]); |
| dst->cat->high = mod->map[SYM_CATS][src_cat->high - 1]; |
| } |
| |
| return 0; |
| } |
| |
| static int mls_range_convert(mls_semantic_range_t * src, mls_semantic_range_t * dst, |
| policy_module_t * mod, link_state_t * state) |
| { |
| int ret; |
| ret = mls_level_convert(&src->level[0], &dst->level[0], mod, state); |
| if (ret) |
| return ret; |
| ret = mls_level_convert(&src->level[1], &dst->level[1], mod, state); |
| if (ret) |
| return ret; |
| return 0; |
| } |
| |
| static int role_fix_callback(hashtab_key_t key, hashtab_datum_t datum, |
| void *data) |
| { |
| unsigned int i; |
| char *id = key; |
| role_datum_t *role, *dest_role = NULL; |
| link_state_t *state = (link_state_t *) data; |
| ebitmap_t e_tmp; |
| policy_module_t *mod = state->cur; |
| ebitmap_node_t *rnode; |
| hashtab_t role_tab; |
| |
| role = (role_datum_t *) datum; |
| if (state->dest_decl == NULL) |
| role_tab = state->base->p_roles.table; |
| else |
| role_tab = state->dest_decl->p_roles.table; |
| |
| dest_role = hashtab_search(role_tab, id); |
| assert(dest_role != NULL); |
| |
| if (state->verbose) { |
| INFO(state->handle, "fixing role %s", id); |
| } |
| |
| ebitmap_init(&e_tmp); |
| ebitmap_for_each_bit(&role->dominates, rnode, i) { |
| if (ebitmap_node_get_bit(rnode, i)) { |
| assert(mod->map[SYM_ROLES][i]); |
| if (ebitmap_set_bit |
| (&e_tmp, mod->map[SYM_ROLES][i] - 1, 1)) { |
| goto cleanup; |
| } |
| } |
| } |
| if (ebitmap_union(&dest_role->dominates, &e_tmp)) { |
| goto cleanup; |
| } |
| if (type_set_or_convert(&role->types, &dest_role->types, mod, state)) { |
| goto cleanup; |
| } |
| ebitmap_destroy(&e_tmp); |
| |
| if (role->flavor == ROLE_ATTRIB) { |
| ebitmap_init(&e_tmp); |
| ebitmap_for_each_bit(&role->roles, rnode, i) { |
| if (ebitmap_node_get_bit(rnode, i)) { |
| assert(mod->map[SYM_ROLES][i]); |
| if (ebitmap_set_bit |
| (&e_tmp, mod->map[SYM_ROLES][i] - 1, 1)) { |
| goto cleanup; |
| } |
| } |
| } |
| if (ebitmap_union(&dest_role->roles, &e_tmp)) { |
| goto cleanup; |
| } |
| ebitmap_destroy(&e_tmp); |
| } |
| |
| return 0; |
| |
| cleanup: |
| ERR(state->handle, "Out of memory!"); |
| ebitmap_destroy(&e_tmp); |
| return -1; |
| } |
| |
| static int type_fix_callback(hashtab_key_t key, hashtab_datum_t datum, |
| void *data) |
| { |
| unsigned int i; |
| char *id = key; |
| type_datum_t *type, *new_type = NULL; |
| link_state_t *state = (link_state_t *) data; |
| ebitmap_t e_tmp; |
| policy_module_t *mod = state->cur; |
| ebitmap_node_t *tnode; |
| symtab_t *typetab; |
| |
| type = (type_datum_t *) datum; |
| |
| if (state->dest_decl == NULL) |
| typetab = &state->base->p_types; |
| else |
| typetab = &state->dest_decl->p_types; |
| |
| /* only fix attributes */ |
| if (type->flavor != TYPE_ATTRIB) { |
| return 0; |
| } |
| |
| new_type = hashtab_search(typetab->table, id); |
| assert(new_type != NULL && new_type->flavor == TYPE_ATTRIB); |
| |
| if (state->verbose) { |
| INFO(state->handle, "fixing attribute %s", id); |
| } |
| |
| ebitmap_init(&e_tmp); |
| ebitmap_for_each_bit(&type->types, tnode, i) { |
| if (ebitmap_node_get_bit(tnode, i)) { |
| assert(mod->map[SYM_TYPES][i]); |
| if (ebitmap_set_bit |
| (&e_tmp, mod->map[SYM_TYPES][i] - 1, 1)) { |
| goto cleanup; |
| } |
| } |
| } |
| if (ebitmap_union(&new_type->types, &e_tmp)) { |
| goto cleanup; |
| } |
| ebitmap_destroy(&e_tmp); |
| return 0; |
| |
| cleanup: |
| ERR(state->handle, "Out of memory!"); |
| ebitmap_destroy(&e_tmp); |
| return -1; |
| } |
| |
| static int user_fix_callback(hashtab_key_t key, hashtab_datum_t datum, |
| void *data) |
| { |
| char *id = key; |
| user_datum_t *user, *new_user = NULL; |
| link_state_t *state = (link_state_t *) data; |
| policy_module_t *mod = state->cur; |
| symtab_t *usertab; |
| |
| user = (user_datum_t *) datum; |
| |
| if (state->dest_decl == NULL) |
| usertab = &state->base->p_users; |
| else |
| usertab = &state->dest_decl->p_users; |
| |
| new_user = hashtab_search(usertab->table, id); |
| assert(new_user != NULL); |
| |
| if (state->verbose) { |
| INFO(state->handle, "fixing user %s", id); |
| } |
| |
| if (role_set_or_convert(&user->roles, &new_user->roles, mod, state)) { |
| goto cleanup; |
| } |
| |
| if (mls_range_convert(&user->range, &new_user->range, mod, state)) |
| goto cleanup; |
| |
| if (mls_level_convert(&user->dfltlevel, &new_user->dfltlevel, mod, state)) |
| goto cleanup; |
| |
| return 0; |
| |
| cleanup: |
| ERR(state->handle, "Out of memory!"); |
| return -1; |
| } |
| |
| static int (*fix_callback_f[SYM_NUM]) (hashtab_key_t key, hashtab_datum_t datum, |
| void *datap) = { |
| NULL, NULL, role_fix_callback, type_fix_callback, user_fix_callback, |
| NULL, NULL, NULL}; |
| |
| /*********** functions that copy AV rules ***********/ |
| |
| static int copy_avrule_list(avrule_t * list, avrule_t ** dst, |
| policy_module_t * module, link_state_t * state) |
| { |
| unsigned int i; |
| avrule_t *cur, *new_rule = NULL, *tail; |
| class_perm_node_t *cur_perm, *new_perm, *tail_perm = NULL; |
| |
| tail = *dst; |
| while (tail && tail->next) { |
| tail = tail->next; |
| } |
| |
| cur = list; |
| while (cur) { |
| if ((new_rule = (avrule_t *) malloc(sizeof(avrule_t))) == NULL) { |
| goto cleanup; |
| } |
| avrule_init(new_rule); |
| |
| new_rule->specified = cur->specified; |
| new_rule->flags = cur->flags; |
| if (type_set_convert |
| (&cur->stypes, &new_rule->stypes, module, state) == -1 |
| || type_set_convert(&cur->ttypes, &new_rule->ttypes, module, |
| state) == -1) { |
| goto cleanup; |
| } |
| |
| cur_perm = cur->perms; |
| tail_perm = NULL; |
| while (cur_perm) { |
| if ((new_perm = (class_perm_node_t *) |
| malloc(sizeof(class_perm_node_t))) == NULL) { |
| goto cleanup; |
| } |
| class_perm_node_init(new_perm); |
| |
| new_perm->tclass = |
| module->map[SYM_CLASSES][cur_perm->tclass - 1]; |
| assert(new_perm->tclass); |
| |
| if (new_rule->specified & AVRULE_AV) { |
| for (i = 0; |
| i < |
| module->perm_map_len[cur_perm->tclass - 1]; |
| i++) { |
| if (!(cur_perm->data & (1U << i))) |
| continue; |
| new_perm->data |= |
| (1U << |
| (module-> |
| perm_map[cur_perm->tclass - 1][i] - |
| 1)); |
| } |
| } else { |
| new_perm->data = |
| module->map[SYM_TYPES][cur_perm->data - 1]; |
| } |
| |
| if (new_rule->perms == NULL) { |
| new_rule->perms = new_perm; |
| } else { |
| assert(tail_perm); |
| tail_perm->next = new_perm; |
| } |
| tail_perm = new_perm; |
| cur_perm = cur_perm->next; |
| } |
| new_rule->line = cur->line; |
| new_rule->source_line = cur->source_line; |
| if (cur->source_filename) { |
| new_rule->source_filename = strdup(cur->source_filename); |
| if (!new_rule->source_filename) |
| goto cleanup; |
| } |
| |
| cur = cur->next; |
| |
| if (*dst == NULL) { |
| *dst = new_rule; |
| } else { |
| tail->next = new_rule; |
| } |
| tail = new_rule; |
| } |
| |
| return 0; |
| cleanup: |
| ERR(state->handle, "Out of memory!"); |
| avrule_destroy(new_rule); |
| free(new_rule); |
| return -1; |
| } |
| |
| static int copy_role_trans_list(role_trans_rule_t * list, |
| role_trans_rule_t ** dst, |
| policy_module_t * module, link_state_t * state) |
| { |
| role_trans_rule_t *cur, *new_rule = NULL, *tail; |
| unsigned int i; |
| ebitmap_node_t *cnode; |
| |
| cur = list; |
| tail = *dst; |
| while (tail && tail->next) { |
| tail = tail->next; |
| } |
| while (cur) { |
| if ((new_rule = |
| (role_trans_rule_t *) malloc(sizeof(role_trans_rule_t))) == |
| NULL) { |
| goto cleanup; |
| } |
| role_trans_rule_init(new_rule); |
| |
| if (role_set_or_convert |
| (&cur->roles, &new_rule->roles, module, state) |
| || type_set_or_convert(&cur->types, &new_rule->types, |
| module, state)) { |
| goto cleanup; |
| } |
| |
| ebitmap_for_each_bit(&cur->classes, cnode, i) { |
| if (ebitmap_node_get_bit(cnode, i)) { |
| assert(module->map[SYM_CLASSES][i]); |
| if (ebitmap_set_bit(&new_rule->classes, |
| module-> |
| map[SYM_CLASSES][i] - 1, |
| 1)) { |
| goto cleanup; |
| } |
| } |
| } |
| |
| new_rule->new_role = module->map[SYM_ROLES][cur->new_role - 1]; |
| |
| if (*dst == NULL) { |
| *dst = new_rule; |
| } else { |
| tail->next = new_rule; |
| } |
| tail = new_rule; |
| cur = cur->next; |
| } |
| return 0; |
| cleanup: |
| ERR(state->handle, "Out of memory!"); |
| role_trans_rule_list_destroy(new_rule); |
| return -1; |
| } |
| |
| static int copy_role_allow_list(role_allow_rule_t * list, |
| role_allow_rule_t ** dst, |
| policy_module_t * module, link_state_t * state) |
| { |
| role_allow_rule_t *cur, *new_rule = NULL, *tail; |
| |
| cur = list; |
| tail = *dst; |
| while (tail && tail->next) { |
| tail = tail->next; |
| } |
| |
| while (cur) { |
| if ((new_rule = |
| (role_allow_rule_t *) malloc(sizeof(role_allow_rule_t))) == |
| NULL) { |
| goto cleanup; |
| } |
| role_allow_rule_init(new_rule); |
| |
| if (role_set_or_convert |
| (&cur->roles, &new_rule->roles, module, state) |
| || role_set_or_convert(&cur->new_roles, |
| &new_rule->new_roles, module, |
| state)) { |
| goto cleanup; |
| } |
| if (*dst == NULL) { |
| *dst = new_rule; |
| } else { |
| tail->next = new_rule; |
| } |
| tail = new_rule; |
| cur = cur->next; |
| } |
| return 0; |
| cleanup: |
| ERR(state->handle, "Out of memory!"); |
| role_allow_rule_list_destroy(new_rule); |
| return -1; |
| } |
| |
| static int copy_filename_trans_list(filename_trans_rule_t * list, |
| filename_trans_rule_t ** dst, |
| policy_module_t * module, |
| link_state_t * state) |
| { |
| filename_trans_rule_t *cur, *new_rule, *tail; |
| |
| cur = list; |
| tail = *dst; |
| while (tail && tail->next) |
| tail = tail->next; |
| |
| while (cur) { |
| new_rule = malloc(sizeof(*new_rule)); |
| if (!new_rule) |
| goto err; |
| |
| filename_trans_rule_init(new_rule); |
| |
| if (*dst == NULL) |
| *dst = new_rule; |
| else |
| tail->next = new_rule; |
| tail = new_rule; |
| |
| new_rule->name = strdup(cur->name); |
| if (!new_rule->name) |
| goto err; |
| |
| if (type_set_or_convert(&cur->stypes, &new_rule->stypes, module, state) || |
| type_set_or_convert(&cur->ttypes, &new_rule->ttypes, module, state)) |
| goto err; |
| |
| new_rule->tclass = module->map[SYM_CLASSES][cur->tclass - 1]; |
| new_rule->otype = module->map[SYM_TYPES][cur->otype - 1]; |
| |
| cur = cur->next; |
| } |
| return 0; |
| err: |
| ERR(state->handle, "Out of memory!"); |
| return -1; |
| } |
| |
| static int copy_range_trans_list(range_trans_rule_t * rules, |
| range_trans_rule_t ** dst, |
| policy_module_t * mod, link_state_t * state) |
| { |
| range_trans_rule_t *rule, *new_rule = NULL; |
| unsigned int i; |
| ebitmap_node_t *cnode; |
| |
| for (rule = rules; rule; rule = rule->next) { |
| new_rule = |
| (range_trans_rule_t *) malloc(sizeof(range_trans_rule_t)); |
| if (!new_rule) |
| goto cleanup; |
| |
| range_trans_rule_init(new_rule); |
| |
| new_rule->next = *dst; |
| *dst = new_rule; |
| |
| if (type_set_convert(&rule->stypes, &new_rule->stypes, |
| mod, state)) |
| goto cleanup; |
| |
| if (type_set_convert(&rule->ttypes, &new_rule->ttypes, |
| mod, state)) |
| goto cleanup; |
| |
| ebitmap_for_each_bit(&rule->tclasses, cnode, i) { |
| if (ebitmap_node_get_bit(cnode, i)) { |
| assert(mod->map[SYM_CLASSES][i]); |
| if (ebitmap_set_bit |
| (&new_rule->tclasses, |
| mod->map[SYM_CLASSES][i] - 1, 1)) { |
| goto cleanup; |
| } |
| } |
| } |
| |
| if (mls_range_convert(&rule->trange, &new_rule->trange, mod, state)) |
| goto cleanup; |
| } |
| return 0; |
| |
| cleanup: |
| ERR(state->handle, "Out of memory!"); |
| range_trans_rule_list_destroy(new_rule); |
| return -1; |
| } |
| |
| static int copy_cond_list(cond_node_t * list, cond_node_t ** dst, |
| policy_module_t * module, link_state_t * state) |
| { |
| unsigned i; |
| cond_node_t *cur, *new_node = NULL, *tail; |
| cond_expr_t *cur_expr; |
| tail = *dst; |
| while (tail && tail->next) |
| tail = tail->next; |
| |
| cur = list; |
| while (cur) { |
| new_node = (cond_node_t *) malloc(sizeof(cond_node_t)); |
| if (!new_node) { |
| goto cleanup; |
| } |
| memset(new_node, 0, sizeof(cond_node_t)); |
| |
| new_node->cur_state = cur->cur_state; |
| new_node->expr = cond_copy_expr(cur->expr); |
| if (!new_node->expr) |
| goto cleanup; |
| /* go back through and remap the expression */ |
| for (cur_expr = new_node->expr; cur_expr != NULL; |
| cur_expr = cur_expr->next) { |
| /* expression nodes don't have a bool value of 0 - don't map them */ |
| if (cur_expr->expr_type != COND_BOOL) |
| continue; |
| assert(module->map[SYM_BOOLS][cur_expr->bool - 1] != 0); |
| cur_expr->bool = |
| module->map[SYM_BOOLS][cur_expr->bool - 1]; |
| } |
| new_node->nbools = cur->nbools; |
| /* FIXME should COND_MAX_BOOLS be used here? */ |
| for (i = 0; i < min(cur->nbools, COND_MAX_BOOLS); i++) { |
| uint32_t remapped_id = |
| module->map[SYM_BOOLS][cur->bool_ids[i] - 1]; |
| assert(remapped_id != 0); |
| new_node->bool_ids[i] = remapped_id; |
| } |
| new_node->expr_pre_comp = cur->expr_pre_comp; |
| |
| if (copy_avrule_list |
| (cur->avtrue_list, &new_node->avtrue_list, module, state) |
| || copy_avrule_list(cur->avfalse_list, |
| &new_node->avfalse_list, module, |
| state)) { |
| goto cleanup; |
| } |
| |
| if (*dst == NULL) { |
| *dst = new_node; |
| } else { |
| tail->next = new_node; |
| } |
| tail = new_node; |
| cur = cur->next; |
| } |
| return 0; |
| cleanup: |
| ERR(state->handle, "Out of memory!"); |
| cond_node_destroy(new_node); |
| free(new_node); |
| return -1; |
| |
| } |
| |
| /*********** functions that copy avrule_decls from module to base ***********/ |
| |
| static int copy_identifiers(link_state_t * state, symtab_t * src_symtab, |
| avrule_decl_t * dest_decl) |
| { |
| int i, ret; |
| |
| state->dest_decl = dest_decl; |
| for (i = 0; i < SYM_NUM; i++) { |
| if (copy_callback_f[i] != NULL) { |
| ret = |
| hashtab_map(src_symtab[i].table, copy_callback_f[i], |
| state); |
| if (ret) { |
| return ret; |
| } |
| } |
| } |
| |
| if (hashtab_map(src_symtab[SYM_TYPES].table, |
| type_bounds_copy_callback, state)) |
| return -1; |
| |
| if (hashtab_map(src_symtab[SYM_TYPES].table, |
| alias_copy_callback, state)) |
| return -1; |
| |
| if (hashtab_map(src_symtab[SYM_ROLES].table, |
| role_bounds_copy_callback, state)) |
| return -1; |
| |
| if (hashtab_map(src_symtab[SYM_USERS].table, |
| user_bounds_copy_callback, state)) |
| return -1; |
| |
| /* then fix bitmaps associated with those newly copied identifiers */ |
| for (i = 0; i < SYM_NUM; i++) { |
| if (fix_callback_f[i] != NULL && |
| hashtab_map(src_symtab[i].table, fix_callback_f[i], |
| state)) { |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| static int copy_scope_index(scope_index_t * src, scope_index_t * dest, |
| policy_module_t * module, link_state_t * state) |
| { |
| unsigned int i, j; |
| uint32_t largest_mapped_class_value = 0; |
| ebitmap_node_t *node; |
| /* copy the scoping information for this avrule decl block */ |
| for (i = 0; i < SYM_NUM; i++) { |
| ebitmap_t *srcmap = src->scope + i; |
| ebitmap_t *destmap = dest->scope + i; |
| if (copy_callback_f[i] == NULL) { |
| continue; |
| } |
| ebitmap_for_each_bit(srcmap, node, j) { |
| if (ebitmap_node_get_bit(node, j)) { |
| assert(module->map[i][j] != 0); |
| if (ebitmap_set_bit |
| (destmap, module->map[i][j] - 1, 1) != 0) { |
| |
| goto cleanup; |
| } |
| if (i == SYM_CLASSES && |
| largest_mapped_class_value < |
| module->map[SYM_CLASSES][j]) { |
| largest_mapped_class_value = |
| module->map[SYM_CLASSES][j]; |
| } |
| } |
| } |
| } |
| |
| /* next copy the enabled permissions data */ |
| if ((dest->class_perms_map = malloc(largest_mapped_class_value * |
| sizeof(*dest->class_perms_map))) == |
| NULL) { |
| goto cleanup; |
| } |
| for (i = 0; i < largest_mapped_class_value; i++) { |
| ebitmap_init(dest->class_perms_map + i); |
| } |
| dest->class_perms_len = largest_mapped_class_value; |
| for (i = 0; i < src->class_perms_len; i++) { |
| ebitmap_t *srcmap = src->class_perms_map + i; |
| ebitmap_t *destmap = |
| dest->class_perms_map + module->map[SYM_CLASSES][i] - 1; |
| ebitmap_for_each_bit(srcmap, node, j) { |
| if (ebitmap_node_get_bit(node, j) && |
| ebitmap_set_bit(destmap, module->perm_map[i][j] - 1, |
| 1)) { |
| goto cleanup; |
| } |
| } |
| } |
| |
| return 0; |
| |
| cleanup: |
| ERR(state->handle, "Out of memory!"); |
| return -1; |
| } |
| |
| static int copy_avrule_decl(link_state_t * state, policy_module_t * module, |
| avrule_decl_t * src_decl, avrule_decl_t * dest_decl) |
| { |
| int ret; |
| |
| /* copy all of the RBAC and TE rules */ |
| if (copy_avrule_list |
| (src_decl->avrules, &dest_decl->avrules, module, state) == -1 |
| || copy_role_trans_list(src_decl->role_tr_rules, |
| &dest_decl->role_tr_rules, module, |
| state) == -1 |
| || copy_role_allow_list(src_decl->role_allow_rules, |
| &dest_decl->role_allow_rules, module, |
| state) == -1 |
| || copy_cond_list(src_decl->cond_list, &dest_decl->cond_list, |
| module, state) == -1) { |
| return -1; |
| } |
| |
| if (copy_filename_trans_list(src_decl->filename_trans_rules, |
| &dest_decl->filename_trans_rules, |
| module, state)) |
| return -1; |
| |
| if (copy_range_trans_list(src_decl->range_tr_rules, |
| &dest_decl->range_tr_rules, module, state)) |
| return -1; |
| |
| /* finally copy any identifiers local to this declaration */ |
| ret = copy_identifiers(state, src_decl->symtab, dest_decl); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| /* then copy required and declared scope indices here */ |
| if (copy_scope_index(&src_decl->required, &dest_decl->required, |
| module, state) == -1 || |
| copy_scope_index(&src_decl->declared, &dest_decl->declared, |
| module, state) == -1) { |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static int copy_avrule_block(link_state_t * state, policy_module_t * module, |
| avrule_block_t * block) |
| { |
| avrule_block_t *new_block = avrule_block_create(); |
| avrule_decl_t *decl, *last_decl = NULL; |
| int ret; |
| |
| if (new_block == NULL) { |
| ERR(state->handle, "Out of memory!"); |
| ret = -1; |
| goto cleanup; |
| } |
| |
| new_block->flags = block->flags; |
| |
| for (decl = block->branch_list; decl != NULL; decl = decl->next) { |
| avrule_decl_t *new_decl = |
| avrule_decl_create(state->next_decl_id); |
| if (new_decl == NULL) { |
| ERR(state->handle, "Out of memory!"); |
| ret = -1; |
| goto cleanup; |
| } |
| |
| if (module->policy->name != NULL) { |
| new_decl->module_name = strdup(module->policy->name); |
| if (new_decl->module_name == NULL) { |
| ERR(state->handle, "Out of memory\n"); |
| avrule_decl_destroy(new_decl); |
| ret = -1; |
| goto cleanup; |
| } |
| } |
| |
| if (last_decl == NULL) { |
| new_block->branch_list = new_decl; |
| } else { |
| last_decl->next = new_decl; |
| } |
| last_decl = new_decl; |
| state->base->decl_val_to_struct[state->next_decl_id - 1] = |
| new_decl; |
| state->decl_to_mod[state->next_decl_id] = module->policy; |
| |
| module->avdecl_map[decl->decl_id] = new_decl->decl_id; |
| |
| ret = copy_avrule_decl(state, module, decl, new_decl); |
| if (ret) { |
| avrule_decl_destroy(new_decl); |
| goto cleanup; |
| } |
| |
| state->next_decl_id++; |
| } |
| state->last_avrule_block->next = new_block; |
| state->last_avrule_block = new_block; |
| return 0; |
| |
| cleanup: |
| avrule_block_list_destroy(new_block); |
| return ret; |
| } |
| |
| static int scope_copy_callback(hashtab_key_t key, hashtab_datum_t datum, |
| void *data) |
| { |
| unsigned int i; |
| int ret; |
| char *id = key, *new_id = NULL; |
| scope_datum_t *scope, *base_scope; |
| link_state_t *state = (link_state_t *) data; |
| uint32_t symbol_num = state->symbol_num; |
| uint32_t *avdecl_map = state->cur->avdecl_map; |
| |
| scope = (scope_datum_t *) datum; |
| |
| /* check if the base already has a scope entry */ |
| base_scope = hashtab_search(state->base->scope[symbol_num].table, id); |
| if (base_scope == NULL) { |
| scope_datum_t *new_scope; |
| if ((new_id = strdup(id)) == NULL) { |
| goto cleanup; |
| } |
| |
| if ((new_scope = |
| (scope_datum_t *) calloc(1, sizeof(*new_scope))) == NULL) { |
| free(new_id); |
| goto cleanup; |
| } |
| ret = hashtab_insert(state->base->scope[symbol_num].table, |
| (hashtab_key_t) new_id, |
| (hashtab_datum_t) new_scope); |
| if (ret) { |
| free(new_id); |
| free(new_scope); |
| goto cleanup; |
| } |
| new_scope->scope = SCOPE_REQ; /* this is reset further down */ |
| base_scope = new_scope; |
| } |
| if (base_scope->scope == SCOPE_REQ && scope->scope == SCOPE_DECL) { |
| /* this module declared symbol, so overwrite the old |
| * list with the new decl ids */ |
| base_scope->scope = SCOPE_DECL; |
| free(base_scope->decl_ids); |
| base_scope->decl_ids = NULL; |
| base_scope->decl_ids_len = 0; |
| for (i = 0; i < scope->decl_ids_len; i++) { |
| if (add_i_to_a(avdecl_map[scope->decl_ids[i]], |
| &base_scope->decl_ids_len, |
| &base_scope->decl_ids) == -1) { |
| goto cleanup; |
| } |
| } |
| } else if (base_scope->scope == SCOPE_DECL && scope->scope == SCOPE_REQ) { |
| /* this module depended on a symbol that now exists, |
| * so don't do anything */ |
| } else if (base_scope->scope == SCOPE_REQ && scope->scope == SCOPE_REQ) { |
| /* symbol is still required, so add to the list */ |
| for (i = 0; i < scope->decl_ids_len; i++) { |
| if (add_i_to_a(avdecl_map[scope->decl_ids[i]], |
| &base_scope->decl_ids_len, |
| &base_scope->decl_ids) == -1) { |
| goto cleanup; |
| } |
| } |
| } else { |
| /* this module declared a symbol, and it was already |
| * declared. only roles and users may be multiply |
| * declared; for all others this is an error. */ |
| if (symbol_num != SYM_ROLES && symbol_num != SYM_USERS) { |
| ERR(state->handle, |
| "%s: Duplicate declaration in module: %s %s", |
| state->cur_mod_name, |
| symtab_names[state->symbol_num], id); |
| return -1; |
| } |
| for (i = 0; i < scope->decl_ids_len; i++) { |
| if (add_i_to_a(avdecl_map[scope->decl_ids[i]], |
| &base_scope->decl_ids_len, |
| &base_scope->decl_ids) == -1) { |
| goto cleanup; |
| } |
| } |
| } |
| return 0; |
| |
| cleanup: |
| ERR(state->handle, "Out of memory!"); |
| return -1; |
| } |
| |
| /* 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. */ |
| static int copy_module(link_state_t * state, policy_module_t * module) |
| { |
| int i, ret; |
| avrule_block_t *cur; |
| state->cur = module; |
| state->cur_mod_name = module->policy->name; |
| |
| /* first copy all of the identifiers */ |
| ret = copy_identifiers(state, module->policy->symtab, NULL); |
| if (ret) { |
| return ret; |
| } |
| |
| /* next copy all of the avrule blocks */ |
| for (cur = module->policy->global; cur != NULL; cur = cur->next) { |
| ret = copy_avrule_block(state, module, cur); |
| if (ret) { |
| return ret; |
| } |
| } |
| |
| /* then copy the scoping tables */ |
| for (i = 0; i < SYM_NUM; i++) { |
| state->symbol_num = i; |
| if (hashtab_map |
| (module->policy->scope[i].table, scope_copy_callback, |
| state)) { |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /***** functions that check requirements and enable blocks in a module ******/ |
| |
| /* borrowed from checkpolicy.c */ |
| |
| struct find_perm_arg { |
| unsigned int valuep; |
| hashtab_key_t key; |
| }; |
| |
| static int find_perm(hashtab_key_t key, hashtab_datum_t datum, void *varg) |
| { |
| |
| struct find_perm_arg *arg = varg; |
| |
| perm_datum_t *perdatum = (perm_datum_t *) datum; |
| if (arg->valuep == perdatum->s.value) { |
| arg->key = key; |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* Check if the requirements are met for a single declaration. If all |
| * are met return 1. For the first requirement found to be missing, |
| * if 'missing_sym_num' and 'missing_value' are both not NULL then |
| * write to them the symbol number and value for the missing |
| * declaration. Then return 0 to indicate a missing declaration. |
| * Note that if a declaration had no requirement at all (e.g., an ELSE |
| * block) this returns 1. */ |
| static int is_decl_requires_met(link_state_t * state, |
| avrule_decl_t * decl, |
| struct missing_requirement *req) |
| { |
| /* (This algorithm is very unoptimized. It performs many |
| * redundant checks. A very obvious improvement is to cache |
| * which symbols have been verified, so that they do not need |
| * to be re-checked.) */ |
| unsigned int i, j; |
| ebitmap_t *bitmap; |
| char *id, *perm_id; |
| policydb_t *pol = state->base; |
| ebitmap_node_t *node; |
| |
| /* check that all symbols have been satisfied */ |
| for (i = 0; i < SYM_NUM; i++) { |
| if (i == SYM_CLASSES) { |
| /* classes will be checked during permissions |
| * checking phase below */ |
| continue; |
| } |
| bitmap = &decl->required.scope[i]; |
| ebitmap_for_each_bit(bitmap, node, j) { |
| if (!ebitmap_node_get_bit(node, j)) { |
| continue; |
| } |
| |
| /* check base's scope table */ |
| id = pol->sym_val_to_name[i][j]; |
| if (!is_id_enabled(id, state->base, i)) { |
| /* this symbol was not found */ |
| if (req != NULL) { |
| req->symbol_type = i; |
| req->symbol_value = j + 1; |
| } |
| return 0; |
| } |
| } |
| } |
| /* check that all classes and permissions have been satisfied */ |
| for (i = 0; i < decl->required.class_perms_len; i++) { |
| |
| bitmap = decl->required.class_perms_map + i; |
| ebitmap_for_each_bit(bitmap, node, j) { |
| struct find_perm_arg fparg; |
| class_datum_t *cladatum; |
| uint32_t perm_value = j + 1; |
| int rc; |
| scope_datum_t *scope; |
| |
| if (!ebitmap_node_get_bit(node, j)) { |
| continue; |
| } |
| id = pol->p_class_val_to_name[i]; |
| cladatum = pol->class_val_to_struct[i]; |
| |
| scope = |
| hashtab_search(state->base->p_classes_scope.table, |
| id); |
| if (scope == NULL) { |
| ERR(state->handle, |
| "Could not find scope information for class %s", |
| id); |
| return -1; |
| } |
| |
| fparg.valuep = perm_value; |
| fparg.key = NULL; |
| |
| (void)hashtab_map(cladatum->permissions.table, find_perm, |
| &fparg); |
| if (fparg.key == NULL && cladatum->comdatum != NULL) { |
| rc = hashtab_map(cladatum->comdatum->permissions.table, |
| find_perm, &fparg); |
| assert(rc == 1); |
| } |
| perm_id = fparg.key; |
| |
| assert(perm_id != NULL); |
| if (!is_perm_enabled(id, perm_id, state->base)) { |
| if (req != NULL) { |
| req->symbol_type = SYM_CLASSES; |
| req->symbol_value = i + 1; |
| req->perm_value = perm_value; |
| } |
| return 0; |
| } |
| } |
| } |
| |
| /* all requirements have been met */ |
| return 1; |
| } |
| |
| static int debug_requirements(link_state_t * state, policydb_t * p) |
| { |
| int ret; |
| avrule_block_t *cur; |
| missing_requirement_t req; |
| memset(&req, 0, sizeof(req)); |
| |
| for (cur = p->global; cur != NULL; cur = cur->next) { |
| if (cur->enabled != NULL) |
| continue; |
| |
| ret = is_decl_requires_met(state, cur->branch_list, &req); |
| if (ret < 0) { |
| return ret; |
| } else if (ret == 0) { |
| const char *mod_name = cur->branch_list->module_name ? |
| cur->branch_list->module_name : "BASE"; |
| if (req.symbol_type == SYM_CLASSES) { |
| struct find_perm_arg fparg; |
| |
| class_datum_t *cladatum; |
| cladatum = p->class_val_to_struct[req.symbol_value - 1]; |
| |
| fparg.valuep = req.perm_value; |
| fparg.key = NULL; |
| (void)hashtab_map(cladatum->permissions.table, |
| find_perm, &fparg); |
| |
| if (cur->flags & AVRULE_OPTIONAL) { |
| ERR(state->handle, |
| "%s[%d]'s optional requirements were not met: class %s, permission %s", |
| mod_name, cur->branch_list->decl_id, |
| p->p_class_val_to_name[req.symbol_value - 1], |
| fparg.key); |
| } else { |
| ERR(state->handle, |
| "%s[%d]'s global requirements were not met: class %s, permission %s", |
| mod_name, cur->branch_list->decl_id, |
| p->p_class_val_to_name[req.symbol_value - 1], |
| fparg.key); |
| } |
| } else { |
| if (cur->flags & AVRULE_OPTIONAL) { |
| ERR(state->handle, |
| "%s[%d]'s optional requirements were not met: %s %s", |
| mod_name, cur->branch_list->decl_id, |
| symtab_names[req.symbol_type], |
| p->sym_val_to_name[req. |
| symbol_type][req. |
| symbol_value |
| - |
| 1]); |
| } else { |
| ERR(state->handle, |
| "%s[%d]'s global requirements were not met: %s %s", |
| mod_name, cur->branch_list->decl_id, |
| symtab_names[req.symbol_type], |
| p->sym_val_to_name[req. |
| symbol_type][req. |
| symbol_value |
| - |
| 1]); |
| } |
| } |
| } |
| } |
| return 0; |
| } |
| |
| static void print_missing_requirements(link_state_t * state, |
| avrule_block_t * cur, |
| missing_requirement_t * req) |
| { |
| policydb_t *p = state->base; |
| const char *mod_name = cur->branch_list->module_name ? |
| cur->branch_list->module_name : "BASE"; |
| |
| if (req->symbol_type == SYM_CLASSES) { |
| |
| struct find_perm_arg fparg; |
| |
| class_datum_t *cladatum; |
| cladatum = p->class_val_to_struct[req->symbol_value - 1]; |
| |
| fparg.valuep = req->perm_value; |
| fparg.key = NULL; |
| (void)hashtab_map(cladatum->permissions.table, find_perm, &fparg); |
| |
| ERR(state->handle, |
| "%s's global requirements were not met: class %s, permission %s", |
| mod_name, |
| p->p_class_val_to_name[req->symbol_value - 1], fparg.key); |
| } else { |
| ERR(state->handle, |
| "%s's global requirements were not met: %s %s", |
| mod_name, |
| symtab_names[req->symbol_type], |
| p->sym_val_to_name[req->symbol_type][req->symbol_value - 1]); |
| } |
| } |
| |
| /* Enable all of the avrule_decl blocks for the policy. This simple |
| * algorithm is the following: |
| * |
| * 1) Enable all of the non-else avrule_decls for all blocks. |
| * 2) Iterate through the non-else decls looking for decls whose requirements |
| * are not met. |
| * 2a) If the decl is non-optional, return immediately with an error. |
| * 2b) If the decl is optional, disable the block and mark changed = 1 |
| * 3) If changed == 1 goto 2. |
| * 4) Iterate through all blocks looking for those that have no enabled |
| * decl. If the block has an else decl, enable. |
| * |
| * This will correctly handle all dependencies, including mutual and |
| * cicular. The only downside is that it is slow. |
| */ |
| static int enable_avrules(link_state_t * state, policydb_t * pol) |
| { |
| int changed = 1; |
| avrule_block_t *block; |
| avrule_decl_t *decl; |
| missing_requirement_t req; |
| int ret = 0, rc; |
| |
| if (state->verbose) { |
| INFO(state->handle, "Determining which avrules to enable."); |
| } |
| |
| /* 1) enable all of the non-else blocks */ |
| for (block = pol->global; block != NULL; block = block->next) { |
| block->enabled = block->branch_list; |
| block->enabled->enabled = 1; |
| for (decl = block->branch_list->next; decl != NULL; |
| decl = decl->next) |
| decl->enabled = 0; |
| } |
| |
| /* 2) Iterate */ |
| while (changed) { |
| changed = 0; |
| for (block = pol->global; block != NULL; block = block->next) { |
| if (block->enabled == NULL) { |
| continue; |
| } |
| decl = block->branch_list; |
| if (state->verbose) { |
| const char *mod_name = decl->module_name ? |
| decl->module_name : "BASE"; |
| INFO(state->handle, "check module %s decl %d\n", |
| mod_name, decl->decl_id); |
| } |
| rc = is_decl_requires_met(state, decl, &req); |
| if (rc < 0) { |
| ret = SEPOL_ERR; |
| goto out; |
| } else if (rc == 0) { |
| decl->enabled = 0; |
| block->enabled = NULL; |
| changed = 1; |
| if (!(block->flags & AVRULE_OPTIONAL)) { |
| print_missing_requirements(state, block, |
| &req); |
| ret = SEPOL_EREQ; |
| goto out; |
| } |
| } |
| } |
| } |
| |
| /* 4) else handling |
| * |
| * Iterate through all of the blocks skipping the first (which is the |
| * global block, is required to be present, and cannot have an else). |
| * If the block is disabled and has an else decl, enable that. |
| * |
| * This code assumes that the second block in the branch list is the else |
| * block. This is currently supported by the compiler. |
| */ |
| for (block = pol->global->next; block != NULL; block = block->next) { |
| if (block->enabled == NULL) { |
| if (block->branch_list->next != NULL) { |
| block->enabled = block->branch_list->next; |
| block->branch_list->next->enabled = 1; |
| } |
| } |
| } |
| |
| out: |
| if (state->verbose) |
| debug_requirements(state, pol); |
| |
| return ret; |
| } |
| |
| /*********** the main linking functions ***********/ |
| |
| /* Given a module's policy, normalize all conditional expressions |
| * within. Return 0 on success, -1 on error. */ |
| static int cond_normalize(policydb_t * p) |
| { |
| avrule_block_t *block; |
| for (block = p->global; block != NULL; block = block->next) { |
| avrule_decl_t *decl; |
| for (decl = block->branch_list; decl != NULL; decl = decl->next) { |
| cond_list_t *cond = decl->cond_list; |
| while (cond) { |
| if (cond_normalize_expr(p, cond) < 0) |
| return -1; |
| cond = cond->next; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| /* Allocate space for the various remapping arrays. */ |
| static int prepare_module(link_state_t * state, policy_module_t * module) |
| { |
| int i; |
| uint32_t items, num_decls = 0; |
| avrule_block_t *cur; |
| |
| /* allocate the maps */ |
| for (i = 0; i < SYM_NUM; i++) { |
| items = module->policy->symtab[i].nprim; |
| if ((module->map[i] = |
| (uint32_t *) calloc(items, |
| sizeof(*module->map[i]))) == NULL) { |
| ERR(state->handle, "Out of memory!"); |
| return -1; |
| } |
| } |
| |
| /* allocate the permissions remap here */ |
| items = module->policy->p_classes.nprim; |
| if ((module->perm_map_len = |
| calloc(items, sizeof(*module->perm_map_len))) == NULL) { |
| ERR(state->handle, "Out of memory!"); |
| return -1; |
| } |
| if ((module->perm_map = |
| calloc(items, sizeof(*module->perm_map))) == NULL) { |
| ERR(state->handle, "Out of memory!"); |
| return -1; |
| } |
| |
| /* allocate a map for avrule_decls */ |
| for (cur = module->policy->global; cur != NULL; cur = cur->next) { |
| avrule_decl_t *decl; |
| for (decl = cur->branch_list; decl != NULL; decl = decl->next) { |
| if (decl->decl_id > num_decls) { |
| num_decls = decl->decl_id; |
| } |
| } |
| } |
| num_decls++; |
| if ((module->avdecl_map = calloc(num_decls, sizeof(uint32_t))) == NULL) { |
| ERR(state->handle, "Out of memory!"); |
| return -1; |
| } |
| module->num_decls = num_decls; |
| |
| /* normalize conditionals within */ |
| if (cond_normalize(module->policy) < 0) { |
| ERR(state->handle, |
| "Error while normalizing conditionals within the module %s.", |
| module->policy->name); |
| return -1; |
| } |
| return 0; |
| } |
| |
| static int prepare_base(link_state_t * state, uint32_t num_mod_decls) |
| { |
| avrule_block_t *cur = state->base->global; |
| assert(cur != NULL); |
| state->next_decl_id = 0; |
| |
| /* iterate through all of the declarations in the base, to |
| determine what the next decl_id should be */ |
| while (cur != NULL) { |
| avrule_decl_t *decl; |
| for (decl = cur->branch_list; decl != NULL; decl = decl->next) { |
| if (decl->decl_id > state->next_decl_id) { |
| state->next_decl_id = decl->decl_id; |
| } |
| } |
| state->last_avrule_block = cur; |
| cur = cur->next; |
| } |
| state->last_base_avrule_block = state->last_avrule_block; |
| state->next_decl_id++; |
| |
| /* allocate the table mapping from base's decl_id to its |
| * avrule_decls and set the initial mappings */ |
| free(state->base->decl_val_to_struct); |
| if ((state->base->decl_val_to_struct = |
| calloc(state->next_decl_id + num_mod_decls, |
| sizeof(*(state->base->decl_val_to_struct)))) == NULL) { |
| ERR(state->handle, "Out of memory!"); |
| return -1; |
| } |
| /* This allocates the decl block to module mapping used for error reporting */ |
| if ((state->decl_to_mod = calloc(state->next_decl_id + num_mod_decls, |
| sizeof(*(state->decl_to_mod)))) == |
| NULL) { |
| ERR(state->handle, "Out of memory!"); |
| return -1; |
| } |
| cur = state->base->global; |
| while (cur != NULL) { |
| avrule_decl_t *decl = cur->branch_list; |
| while (decl != NULL) { |
| state->base->decl_val_to_struct[decl->decl_id - 1] = |
| decl; |
| state->decl_to_mod[decl->decl_id] = state->base; |
| decl = decl->next; |
| } |
| cur = cur->next; |
| } |
| |
| /* normalize conditionals within */ |
| if (cond_normalize(state->base) < 0) { |
| ERR(state->handle, |
| "Error while normalizing conditionals within the base module."); |
| return -1; |
| } |
| return 0; |
| } |
| |
| static int expand_role_attributes(hashtab_key_t key, hashtab_datum_t datum, |
| void * data) |
| { |
| char *id; |
| role_datum_t *role, *sub_attr; |
| link_state_t *state; |
| unsigned int i; |
| ebitmap_node_t *rnode; |
| |
| id = key; |
| role = (role_datum_t *)datum; |
| state = (link_state_t *)data; |
| |
| if (strcmp(id, OBJECT_R) == 0){ |
| /* object_r is never a role attribute by far */ |
| return 0; |
| } |
| |
| if (role->flavor != ROLE_ATTRIB) |
| return 0; |
| |
| if (state->verbose) |
| INFO(state->handle, "expanding role attribute %s", id); |
| |
| restart: |
| ebitmap_for_each_bit(&role->roles, rnode, i) { |
| if (ebitmap_node_get_bit(rnode, i)) { |
| sub_attr = state->base->role_val_to_struct[i]; |
| if (sub_attr->flavor != ROLE_ATTRIB) |
| continue; |
| |
| /* remove the sub role attribute from the parent |
| * role attribute's roles ebitmap */ |
| if (ebitmap_set_bit(&role->roles, i, 0)) |
| return -1; |
| |
| /* loop dependency of role attributes */ |
| if (sub_attr->s.value == role->s.value) |
| continue; |
| |
| /* now go on to expand a sub role attribute |
| * by escalating its roles ebitmap */ |
| if (ebitmap_union(&role->roles, &sub_attr->roles)) { |
| ERR(state->handle, "Out of memory!"); |
| return -1; |
| } |
| |
| /* sub_attr->roles may contain other role attributes, |
| * re-scan the parent role attribute's roles ebitmap */ |
| goto restart; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* For any role attribute in a declaration's local symtab[SYM_ROLES] table, |
| * copy its roles ebitmap into its duplicate's in the base->p_roles.table. |
| */ |
| static int populate_decl_roleattributes(hashtab_key_t key, |
| hashtab_datum_t datum, |
| void *data) |
| { |
| char *id = key; |
| role_datum_t *decl_role, *base_role; |
| link_state_t *state = (link_state_t *)data; |
| |
| decl_role = (role_datum_t *)datum; |
| |
| if (strcmp(id, OBJECT_R) == 0) { |
| /* object_r is never a role attribute by far */ |
| return 0; |
| } |
| |
| if (decl_role->flavor != ROLE_ATTRIB) |
| return 0; |
| |
| base_role = (role_datum_t *)hashtab_search(state->base->p_roles.table, |
| id); |
| assert(base_role != NULL && base_role->flavor == ROLE_ATTRIB); |
| |
| if (ebitmap_union(&base_role->roles, &decl_role->roles)) { |
| ERR(state->handle, "Out of memory!"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static int populate_roleattributes(link_state_t *state, policydb_t *pol) |
| { |
| avrule_block_t *block; |
| avrule_decl_t *decl; |
| |
| if (state->verbose) |
| INFO(state->handle, "Populating role-attribute relationship " |
| "from enabled declarations' local symtab."); |
| |
| /* Iterate through all of the blocks skipping the first(which is the |
| * global block, is required to be present and can't have an else). |
| * If the block is disabled or not having an enabled decl, skip it. |
| */ |
| for (block = pol->global->next; block != NULL; block = block->next) |
| { |
| decl = block->enabled; |
| if (decl == NULL || decl->enabled == 0) |
| continue; |
| |
| if (hashtab_map(decl->symtab[SYM_ROLES].table, |
| populate_decl_roleattributes, state)) |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /* 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 |
| * calling this function. |
| */ |
| int link_modules(sepol_handle_t * handle, |
| policydb_t * b, policydb_t ** mods, int len, int verbose) |
| { |
| int i, ret, retval = -1; |
| policy_module_t **modules = NULL; |
| link_state_t state; |
| uint32_t num_mod_decls = 0; |
| |
| memset(&state, 0, sizeof(state)); |
| state.base = b; |
| state.verbose = verbose; |
| state.handle = handle; |
| |
| if (b->policy_type != POLICY_BASE) { |
| ERR(state.handle, "Target of link was not a base policy."); |
| return -1; |
| } |
| |
| /* first allocate some space to hold the maps from module |
| * symbol's value to the destination symbol value; then do |
| * other preparation work */ |
| if ((modules = |
| (policy_module_t **) calloc(len, sizeof(*modules))) == NULL) { |
| ERR(state.handle, "Out of memory!"); |
| return -1; |
| } |
| for (i = 0; i < len; i++) { |
| if (mods[i]->policy_type != POLICY_MOD) { |
| ERR(state.handle, |
| "Tried to link in a policy that was not a module."); |
| goto cleanup; |
| } |
| |
| if (mods[i]->mls != b->mls) { |
| if (b->mls) |
| ERR(state.handle, |
| "Tried to link in a non-MLS module with an MLS base."); |
| else |
| ERR(state.handle, |
| "Tried to link in an MLS module with a non-MLS base."); |
| goto cleanup; |
| } |
| |
| if ((modules[i] = |
| (policy_module_t *) calloc(1, |
| sizeof(policy_module_t))) == |
| NULL) { |
| ERR(state.handle, "Out of memory!"); |
| goto cleanup; |
| } |
| modules[i]->policy = mods[i]; |
| if (prepare_module(&state, modules[i]) == -1) { |
| goto cleanup; |
| } |
| num_mod_decls += modules[i]->num_decls; |
| } |
| if (prepare_base(&state, num_mod_decls) == -1) { |
| goto cleanup; |
| } |
| |
| /* copy all types, declared and required */ |
| for (i = 0; i < len; i++) { |
| state.cur = modules[i]; |
| state.cur_mod_name = modules[i]->policy->name; |
| ret = |
| hashtab_map(modules[i]->policy->p_types.table, |
| type_copy_callback, &state); |
| if (ret) { |
| retval = ret; |
| goto cleanup; |
| } |
| } |
| |
| /* then copy everything else, including aliases, and fixup attributes */ |
| for (i = 0; i < len; i++) { |
| state.cur = modules[i]; |
| state.cur_mod_name = modules[i]->policy->name; |
| ret = |
| copy_identifiers(&state, modules[i]->policy->symtab, NULL); |
| if (ret) { |
| retval = ret; |
| goto cleanup; |
| } |
| } |
| |
| if (policydb_index_others(state.handle, state.base, 0)) { |
| ERR(state.handle, "Error while indexing others"); |
| goto cleanup; |
| } |
| |
| /* copy and remap the module's data over to base */ |
| for (i = 0; i < len; i++) { |
| state.cur = modules[i]; |
| ret = copy_module(&state, modules[i]); |
| if (ret) { |
| retval = ret; |
| goto cleanup; |
| } |
| } |
| |
| /* re-index base, for symbols were added to symbol tables */ |
| if (policydb_index_classes(state.base)) { |
| ERR(state.handle, "Error while indexing classes"); |
| goto cleanup; |
| } |
| if (policydb_index_others(state.handle, state.base, 0)) { |
| ERR(state.handle, "Error while indexing others"); |
| goto cleanup; |
| } |
| |
| if (enable_avrules(&state, state.base)) { |
| retval = SEPOL_EREQ; |
| goto cleanup; |
| } |
| |
| /* Now that all role attribute's roles ebitmap have been settled, |
| * escalate sub role attribute's roles ebitmap into that of parent. |
| * |
| * First, since some role-attribute relationships could be recorded |
| * in some decl's local symtab(see get_local_role()), we need to |
| * populate them up to the base.p_roles table. */ |
| if (populate_roleattributes(&state, state.base)) { |
| retval = SEPOL_EREQ; |
| goto cleanup; |
| } |
| |
| /* Now do the escalation. */ |
| if (hashtab_map(state.base->p_roles.table, expand_role_attributes, |
| &state)) |
| goto cleanup; |
| |
| retval = 0; |
| cleanup: |
| for (i = 0; modules != NULL && i < len; i++) { |
| policy_module_destroy(modules[i]); |
| } |
| free(modules); |
| free(state.decl_to_mod); |
| return retval; |
| } |