#include <cil/android.h> #include <sepol/policydb/hashtab.h> #include <stdlib.h> #include <string.h> #include "cil_build_ast.h" #include "cil_internal.h" #include "cil_strpool.h" #include "cil_symtab.h" #include "cil_tree.h" #define VER_MAP_SZ (1 << 12) /* added to hashmap - currently unused as hashmap is used as a set */ struct version_datum { struct cil_db *db; struct cil_tree_node *ast_node; char *orig_name; }; struct version_args { struct cil_db *db; hashtab_t vers_map; const char *num; }; enum plat_flavor { PLAT_NONE = 0, PLAT_TYPE, PLAT_ATTRIB }; static unsigned int ver_map_hash_val(hashtab_t h, const_hashtab_key_t key) { /* from cil_stpool.c */ char *p, *keyp; size_t size; unsigned int val; val = 0; keyp = (char*)key; size = strlen(keyp); for (p = keyp; ((size_t) (p - keyp)) < size; p++) val = (val << 4 | (val >> (8 * sizeof(unsigned int) - 4))) ^ (*p); return val & (h->size - 1); } static int ver_map_key_cmp(hashtab_t h __attribute__ ((unused)), const_hashtab_key_t key1, const_hashtab_key_t key2) { /* hashtab_key_t is just a const char* underneath */ return strcmp(key1, key2); } /* * version_datum pointers all refer to memory owned elsewhere, so just free the * datum itself. */ static int ver_map_entry_destroy(__attribute__ ((unused))hashtab_key_t k, hashtab_datum_t d, __attribute__ ((unused))void *args) { free(d); return 0; } static void ver_map_destroy(hashtab_t h) { hashtab_map(h, ver_map_entry_destroy, NULL); hashtab_destroy(h); } static int __extract_attributees_helper(struct cil_tree_node *node, uint32_t *finished, void *extra_args) { int rc = SEPOL_ERR; struct version_args *args = (struct version_args *) extra_args; char *key; struct version_datum *datum; if (node == NULL || finished == NULL || extra_args == NULL) { goto exit; } switch (node->flavor) { case CIL_ROLE: cil_log(CIL_ERR, "%s unsupported statement in attributee policy (line %d)\n", CIL_KEY_ROLE, node->line); rc = SEPOL_ERR; break; case CIL_TYPE: case CIL_TYPEATTRIBUTE: datum = cil_malloc(sizeof(*datum)); datum->db = args->db; datum->ast_node = node; datum->orig_name = DATUM(node->data)->name; key = datum->orig_name; if (!strncmp(key, "base_typeattr_", 14)) { /* checkpolicy creates base attributes which are just typeattributesets, of the existing types and attributes. These may be differnt in every checkpolicy output, ignore them here, they'll be dealt with as a special case when attributizing. */ free(datum); } else { rc = hashtab_insert(args->vers_map, (hashtab_key_t) key, (hashtab_datum_t) datum); if (rc != SEPOL_OK) { goto exit; } } break; case CIL_TYPEALIAS: cil_log(CIL_ERR, "%s unsupported statement in attributee policy (line %d)\n", CIL_KEY_TYPEALIAS, node->line); goto exit; break; case CIL_TYPEPERMISSIVE: cil_log(CIL_ERR, "%s unsupported statement in attributee policy (line %d)\n", CIL_KEY_TYPEPERMISSIVE, node->line); goto exit; break; case CIL_NAMETYPETRANSITION: case CIL_TYPE_RULE: cil_log(CIL_ERR, "%s unsupported statement in attributee policy (line %d)\n", CIL_KEY_TYPETRANSITION, node->line); goto exit; break; default: break; } return SEPOL_OK; exit: return rc; } /* * For the given db, with an already-built AST, fill the vers_map hash table * with every encountered type and attribute. This could eventually be expanded * to include other language constructs, such as users and roles, in which case * multiple hash tables would be needed. These tables can then be used by * attributize() to change all references to these types. */ int cil_extract_attributees(struct cil_db *db, hashtab_t vers_map) { /* walk ast. */ int rc = SEPOL_ERR; struct version_args extra_args; extra_args.db = db; extra_args.vers_map = vers_map; extra_args.num = NULL; rc = cil_tree_walk(db->ast->root, __extract_attributees_helper, NULL, NULL, &extra_args); if (rc != SEPOL_OK) { goto exit; } return SEPOL_OK; exit: return rc; } static enum plat_flavor __cil_get_plat_flavor(hashtab_t vers_map, hashtab_key_t key) { enum plat_flavor rc; struct version_datum *vers_datum; vers_datum = (struct version_datum *)hashtab_search(vers_map, key); if (vers_datum == NULL) { return PLAT_NONE; } switch (vers_datum->ast_node->flavor) { case CIL_TYPE: rc = PLAT_TYPE; break; case CIL_TYPEATTRIBUTE: rc = PLAT_ATTRIB; break; default: rc = PLAT_NONE; break; } return rc; } /* * Takes the old name and version string and creates a new strpool entry by * combining them. */ static char *__cil_attrib_get_versname(char *old, const char *vers) { size_t len = 0; char *tmp_new = NULL; char *final; len += strlen(old) + strlen(vers) + 2; tmp_new = cil_malloc(len); snprintf(tmp_new, len, "%s_%s", old, vers); final = cil_strpool_add(tmp_new); free(tmp_new); return final; } /* * Change type to attribute - create new versioned name based on old, create * typeattribute node add to the existing type node. */ static int __cil_attrib_convert_type(struct cil_tree_node *node, struct version_args *args) { int rc = SEPOL_ERR; struct cil_type *type = (struct cil_type *)node->data; struct cil_typeattribute *typeattr = NULL; struct cil_tree_node *new_ast_node = NULL; char *new_key; cil_typeattribute_init(&typeattr); new_key = __cil_attrib_get_versname(type->datum.name, args->num); /* create new tree node to contain typeattribute and add to tree */ cil_tree_node_init(&new_ast_node); new_ast_node->parent = node->parent; new_ast_node->next = node->next; node->next = new_ast_node; rc = cil_gen_node(args->db, new_ast_node, (struct cil_symtab_datum *) typeattr, new_key, CIL_SYM_TYPES, CIL_TYPEATTRIBUTE); if (rc != SEPOL_OK) { goto exit; } return SEPOL_OK; exit: return rc; } /* * Update datum - create new key, remove entry under old key, * update entry, and insert under new key */ static int __cil_attrib_swap_symtab_key(struct cil_tree_node *node, char *old_key, const char *num) { int rc = SEPOL_ERR; char *new_key; symtab_t *symtab; struct cil_symtab_datum *datum = (struct cil_symtab_datum *) node->data; new_key = __cil_attrib_get_versname(old_key, num); symtab = datum->symtab; /* TODO: remove, but what happens to other nodes on this datum ?*/ cil_list_remove(datum->nodes, CIL_NODE, node, 0); cil_symtab_remove_datum(datum); rc = cil_symtab_insert(symtab, new_key, datum, node); if (rc != SEPOL_OK) { goto exit; } return SEPOL_OK; exit: return rc; } /* * expressions may contains strings which are not in the type-attribute * namespace, so this is not a general cil_expr attributizer. * TODO: add support for other types of expressions which may contain types. */ static int cil_attrib_type_expr(struct cil_list *expr_str, struct version_args *args) { int rc = SEPOL_ERR; struct cil_list_item *curr = NULL; char *new; hashtab_key_t key; /* iterate through cil_list, replacing types */ cil_list_for_each(curr, expr_str) { switch(curr->flavor) { case CIL_LIST: rc = cil_attrib_type_expr((struct cil_list *)curr->data, args); if (rc != SEPOL_OK) goto exit; break; case CIL_STRING: key = (hashtab_key_t) curr->data; enum plat_flavor pf = __cil_get_plat_flavor(args->vers_map, key); if (!strncmp(curr->data, "base_typeattr_", 14) || pf == PLAT_TYPE) { new = __cil_attrib_get_versname((char *) curr->data, args->num); curr->data = (void *) new; } break; case CIL_DATUM: cil_log(CIL_ERR, "AST already resolved. Not yet supported.\n"); rc = SEPOL_ERR; goto exit; break; default: break; } } return SEPOL_OK; exit: return rc; } static int cil_attrib_check_context(struct cil_context *ctxt, struct version_args *args) { int rc = SEPOL_ERR; hashtab_key_t key; if (ctxt->type != NULL) { cil_log(CIL_ERR, "AST already resolved. Not yet supported.\n"); goto exit; } key = (hashtab_key_t) ctxt->type_str; if (__cil_get_plat_flavor(args->vers_map, key) != PLAT_NONE) { /* TODO: reinstate check, but leave out for now cil_log(CIL_ERR, "AST contains context with platform public type: %s\n", ctxt->type_str); rc = SEPOL_ERR; goto exit; */ } return SEPOL_OK; exit: return rc; } static int cil_attrib_sidcontext(struct cil_tree_node *node, struct version_args *args) { int rc = SEPOL_ERR; struct cil_sidcontext *sidcon = (struct cil_sidcontext *)node->data; if (sidcon->context_str == NULL) { /* sidcon contains an anon context, which needs to have type checked */ rc = cil_attrib_check_context(sidcon->context, args); if (rc != SEPOL_OK) { goto exit; } } return SEPOL_OK; exit: return rc; } static int cil_attrib_context(struct cil_tree_node *node, struct version_args *args) { struct cil_context *ctxt = (struct cil_context *)node->data; return cil_attrib_check_context(ctxt, args); } static int cil_attrib_roletype(struct cil_tree_node *node, __attribute__((unused)) struct version_args *args) { int rc = SEPOL_ERR; char *key; struct cil_roletype *roletype = (struct cil_roletype *)node->data; if (roletype->role) { cil_log(CIL_ERR, "AST already resolved. !!! Not yet supported.\n"); goto exit; } key = roletype->type_str; if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) { roletype->type_str = __cil_attrib_get_versname(key, args->num); } return SEPOL_OK; exit: return rc; } static int cil_attrib_type(struct cil_tree_node *node, struct version_args *args) { int rc = SEPOL_ERR; struct cil_type *type = (struct cil_type *)node->data; char *key = type->datum.name; if (type->value) { cil_log(CIL_ERR, "AST already resolved. !!! Not yet supported.\n"); goto exit; } if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) { rc = __cil_attrib_convert_type(node, args); if (rc != SEPOL_OK) { goto exit; } } return SEPOL_OK; exit: return rc; } static int cil_attrib_typepermissive(struct cil_tree_node *node, struct version_args *args __attribute__ ((unused))) { struct cil_typepermissive *typeperm = (struct cil_typepermissive *)node->data; if (typeperm->type != NULL) { cil_log(CIL_ERR, "AST already resolved. ### Not yet supported.\n"); return SEPOL_ERR; } return SEPOL_OK; } static int cil_attrib_typeattribute(struct cil_tree_node *node, struct version_args *args) { int rc = SEPOL_ERR; struct cil_typeattribute *typeattr = (struct cil_typeattribute *)node->data; char *key = typeattr->datum.name; if (typeattr->types) { cil_log(CIL_ERR, "AST already resolved. Not yet supported (line %d).\n", node->line); goto exit; } if (!strncmp(key, "base_typeattr_", 14)) { rc = __cil_attrib_swap_symtab_key(node, key, args->num); if (rc != SEPOL_OK) { goto exit; } } return SEPOL_OK; exit: return rc; } static int cil_attrib_typeattributeset(struct cil_tree_node *node, struct version_args *args) { int rc = SEPOL_ERR; char *key; struct cil_typeattributeset *typeattrset = (struct cil_typeattributeset *) node->data; if (typeattrset->datum_expr != NULL) { cil_log(CIL_ERR, "AST already resolved. Not yet supported (line %d).\n", node->line); goto exit; } key = typeattrset->attr_str; /* first check to see if the attribute to which this set belongs is versioned */ if (!strncmp(key, "base_typeattr_", 14)) { typeattrset->attr_str = __cil_attrib_get_versname(key, args->num); } rc = cil_attrib_type_expr(typeattrset->str_expr, args); if (rc != SEPOL_OK) { goto exit; } return SEPOL_OK; exit: return rc; } static int cil_attrib_typealiasactual(struct cil_tree_node *node, struct version_args *args) { int rc = SEPOL_ERR; char *key; struct cil_aliasactual *aliasact = (struct cil_aliasactual *)node->data; key = aliasact->actual_str; if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) != PLAT_NONE) { cil_log(CIL_ERR, "%s with platform public type not allowed (line %d)\n", CIL_KEY_TYPEALIASACTUAL, node->line); goto exit; } return SEPOL_OK; exit: return rc; } static int cil_attrib_nametypetransition(struct cil_tree_node *node, struct version_args *args) { int rc = SEPOL_ERR; char *key; struct cil_nametypetransition *namettrans = (struct cil_nametypetransition *)node->data; if (namettrans->src != NULL) { cil_log(CIL_ERR, "AST already resolved. Not yet supported (line %d).\n", node->line); goto exit; } key = namettrans->src_str; if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) { namettrans->src_str = __cil_attrib_get_versname(key, args->num); } key = namettrans->tgt_str; if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) { namettrans->tgt_str = __cil_attrib_get_versname(key, args->num); } return SEPOL_OK; exit: return rc; } /* * This is exactly the same as cil_attrib_nametypetransition, but the struct * layouts differ, so we can't reuse it. */ static int cil_attrib_type_rule(struct cil_tree_node *node, struct version_args *args) { int rc = SEPOL_ERR; char *key; struct cil_type_rule *type_rule = (struct cil_type_rule *)node->data; if (type_rule->src != NULL) { cil_log(CIL_ERR, "AST already resolved. Not yet supported (line %d).\n", node->line); goto exit; } key = type_rule->src_str; if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) { type_rule->src_str = __cil_attrib_get_versname(key, args->num); } key = type_rule->tgt_str; if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) { type_rule->tgt_str = __cil_attrib_get_versname(key, args->num); } return SEPOL_OK; exit: return rc; } static int cil_attrib_avrule(struct cil_tree_node *node, struct version_args *args) { int rc = SEPOL_ERR; char *key; struct cil_avrule *avrule = (struct cil_avrule *)node->data; if (avrule->src != NULL) { cil_log(CIL_ERR, "AST already resolved. Not yet supported (line %d).\n", node->line); goto exit; } key = avrule->src_str; if (!strncmp(key, "base_typeattr_", 14) || __cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) { avrule->src_str = __cil_attrib_get_versname(key, args->num); } key = avrule->tgt_str; if (!strncmp(key, "base_typeattr_", 14) || __cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) { avrule->tgt_str = __cil_attrib_get_versname(key, args->num); } return SEPOL_OK; exit: return rc; } static int cil_attrib_genfscon(struct cil_tree_node *node, struct version_args *args) { int rc = SEPOL_ERR; struct cil_genfscon *genfscon = (struct cil_genfscon *)node->data; if (genfscon->context_str == NULL) { /* genfscon contains an anon context, which needs to have type checked */ rc = cil_attrib_check_context(genfscon->context, args); if (rc != SEPOL_OK) { goto exit; } } return SEPOL_OK; exit: return rc; } static int cil_attrib_fsuse(struct cil_tree_node *node, struct version_args *args) { int rc = SEPOL_ERR; struct cil_fsuse *fsuse = (struct cil_fsuse *)node->data; if (fsuse->context_str == NULL) { /* fsuse contains an anon context, which needs to have type checked */ rc = cil_attrib_check_context(fsuse->context, args); if (rc != SEPOL_OK) { goto exit; } } return SEPOL_OK; exit: return rc; } static int __attributize_helper(struct cil_tree_node *node, uint32_t *finished, void *extra_args) { int rc = SEPOL_ERR; struct version_args *args = (struct version_args *) extra_args; if (node == NULL || finished == NULL || extra_args == NULL) { goto exit; } switch (node->flavor) { case CIL_SIDCONTEXT: /* contains type, but shouldn't involve an attributized type, maybe add a check on type and error if it conflicts */ rc = cil_attrib_sidcontext(node, args); if (rc != SEPOL_OK) { goto exit; } break; case CIL_ROLE: cil_log(CIL_ERR, "%s declaration illegal non-platform policy (line %d)\n", CIL_KEY_ROLE, node->line); rc = SEPOL_ERR; break; case CIL_ROLETYPE: /* Yes, this is needed if we support roletype in non-platform policy. type_id can be type, typealias or typeattr */ rc = cil_attrib_roletype(node, args); if (rc != SEPOL_OK) { goto exit; } break; case CIL_ROLEATTRIBUTE: /* don't think this is needed, only used for cil_gen_req, and we aren't yet supporting roles in non-platform policy. */ break; case CIL_TYPE: /* conver to attribute if in policy */ rc = cil_attrib_type(node, args); if (rc != SEPOL_OK) { goto exit; } break; case CIL_TYPEPERMISSIVE: rc = cil_attrib_typepermissive(node, args); if (rc != SEPOL_OK) { goto exit; } break; case CIL_TYPEATTRIBUTE: rc = cil_attrib_typeattribute(node, args); if (rc != SEPOL_OK) { goto exit; } break; case CIL_TYPEATTRIBUTESET: rc = cil_attrib_typeattributeset(node, args); if (rc != SEPOL_OK) { goto exit; } break; case CIL_TYPEALIASACTUAL: /* this will break on an attributized type - identify it and throw error */ rc = cil_attrib_typealiasactual(node, args); if (rc != SEPOL_OK) { goto exit; } break; case CIL_NAMETYPETRANSITION: /* not allowed in plat-policy. Types present, throw error if attributee */ rc = cil_attrib_nametypetransition(node, args); if (rc != SEPOL_OK) { goto exit; } break; case CIL_TYPE_RULE: /* not allowed in plat-policy. Types present, throw error if attributee */ rc = cil_attrib_type_rule(node, args); if (rc != SEPOL_OK) { goto exit; } break; case CIL_AVRULE: case CIL_AVRULEX: rc = cil_attrib_avrule(node, args); if (rc != SEPOL_OK) { goto exit; } break; case CIL_CONTEXT: /* not currently found in AOSP policy, but if found would need to be checked to not be attributee */ rc = cil_attrib_context(node, args); if (rc != SEPOL_OK) { goto exit; } break; case CIL_GENFSCON: /* not allowed in plat-policy, but types present, throw error if attributee */ rc = cil_attrib_genfscon(node, args); if (rc != SEPOL_OK) { goto exit; } break; case CIL_FILECON: case CIL_NODECON: case CIL_PORTCON: case CIL_PIRQCON: case CIL_IOMEMCON: case CIL_IOPORTCON: case CIL_PCIDEVICECON: case CIL_DEVICETREECON: case CIL_VALIDATETRANS: case CIL_MLSVALIDATETRANS: case CIL_CALL: case CIL_MACRO: case CIL_OPTIONAL: /* Not currently found in AOSP and not yet properly handled. Return err until support added. */ cil_log(CIL_ERR, "unsupported policy statement (line %d)\n", node->line); rc = SEPOL_ERR; goto exit; case CIL_FSUSE: /* not allowed in plat-policy, but types present, throw error if attributee */ cil_attrib_fsuse(node, args); if (rc != SEPOL_OK) { goto exit; } break; case CIL_CONSTRAIN: case CIL_MLSCONSTRAIN: /* there is type info here, but not sure if we'll allow non-platform code to have this, or whether or not it's in platform policy. Currently assuming that mlsconstrain is private-platform only, and that normal constrain is verboten. */ cil_log(CIL_ERR, "unsupported policy statement (line %d)\n", node->line); rc = SEPOL_ERR; goto exit; default: break; } return SEPOL_OK; exit: return rc; } /* * walk ast, replacing previously identified types and attributes with the * attributized version. Also replace previous references to the attributees * with the versioned type. */ static int cil_attributize(struct cil_db *db, hashtab_t vers_map, const char *num) { int rc = SEPOL_ERR; struct version_args extra_args; extra_args.db = db; extra_args.vers_map = vers_map; extra_args.num = num; rc = cil_tree_walk(db->ast->root, __attributize_helper, NULL, NULL, &extra_args); if (rc != SEPOL_OK) { goto exit; } return SEPOL_OK; exit: return rc; } /* * Create typeattributeset mappings from the attributes generated from the * original types/attributes to the original values. This mapping will provide * the basis for the platform policy's mapping to this public version. * * Add these new typeattributeset nodes to the given cil_db. */ static int cil_build_mappings_tree(hashtab_key_t k, hashtab_datum_t d, void *args) { struct cil_typeattributeset *attrset = NULL; struct cil_typeattribute *typeattr = NULL; struct cil_expandtypeattribute *expandattr = NULL; struct cil_tree_node *ast_node = NULL; struct version_args *verargs = (struct version_args *)args; struct cil_tree_node *ast_parent = verargs->db->ast->root; char *orig_type = (char *) k; struct version_datum *vers_datum = (struct version_datum *) d; char *new_key = __cil_attrib_get_versname(orig_type, verargs->num); if (vers_datum->ast_node->flavor == CIL_TYPEATTRIBUTE) { // platform attributes are not versioned return SEPOL_OK; } /* create typeattributeset datum */ cil_typeattributeset_init(&attrset); cil_list_init(&attrset->str_expr, CIL_TYPE); attrset->attr_str = new_key; cil_list_append(attrset->str_expr, CIL_STRING, orig_type); /* create containing tree node */ cil_tree_node_init(&ast_node); ast_node->data = attrset; ast_node->flavor = CIL_TYPEATTRIBUTESET; /* add to tree */ ast_node->parent = ast_parent; if (ast_parent->cl_head == NULL) ast_parent->cl_head = ast_node; else ast_parent->cl_tail->next = ast_node; ast_parent->cl_tail = ast_node; /* create expandtypeattribute datum */ cil_expandtypeattribute_init(&expandattr); cil_list_init(&expandattr->attr_strs, CIL_TYPE); cil_list_append(expandattr->attr_strs, CIL_STRING, new_key); expandattr->expand = CIL_TRUE; /* create containing tree node */ cil_tree_node_init(&ast_node); ast_node->data = expandattr; ast_node->flavor = CIL_EXPANDTYPEATTRIBUTE; /* add to tree */ ast_node->parent = ast_parent; ast_parent->cl_tail->next = ast_node; ast_parent->cl_tail = ast_node; /* re)declare typeattribute. */ cil_typeattribute_init(&typeattr); typeattr->datum.name = new_key; cil_tree_node_init(&ast_node); ast_node->data = typeattr; ast_node->flavor = CIL_TYPEATTRIBUTE; ast_node->parent = ast_parent; ast_parent->cl_tail->next = ast_node; ast_parent->cl_tail = ast_node; return SEPOL_OK; } /* * Initializes the given db and uses the version mapping generated by * cil_extract_attributees() to fill it with the glue policy required to * connect the attributized policy created by cil_attributize() to the policy * declaring the concrete types. */ static int cil_attrib_mapping(struct cil_db **db, hashtab_t vers_map, const char *num) { int rc = SEPOL_ERR; struct version_args extra_args; cil_db_init(db); /* foreach entry in vers_map, create typeattributeset node and attach to tree */ extra_args.db = *db; extra_args.vers_map = NULL; extra_args.num = num; rc = hashtab_map(vers_map, cil_build_mappings_tree, &extra_args); if (rc != SEPOL_OK) { goto exit; } return SEPOL_OK; exit: return rc; } int cil_android_attrib_mapping(struct cil_db **mdb, struct cil_db *srcdb, const char *num) { int rc = SEPOL_ERR; hashtab_t ver_map_tab = NULL; ver_map_tab = hashtab_create(ver_map_hash_val, ver_map_key_cmp, VER_MAP_SZ); if (!ver_map_tab) { cil_log(CIL_ERR, "Unable to create version mapping table.\n"); goto exit; } rc = cil_build_ast(srcdb, srcdb->parse->root, srcdb->ast->root); if (rc != SEPOL_OK) { cil_log(CIL_ERR, "Unable to build source db AST.\n"); goto exit; } rc = cil_extract_attributees(srcdb, ver_map_tab); if (rc != SEPOL_OK) { cil_log(CIL_ERR, "Unable to extract attributizable elements from source db.\n"); goto exit; } rc = cil_attrib_mapping(mdb, ver_map_tab, num); if (rc != SEPOL_OK) { cil_log(CIL_ERR, "Unable to create mapping db from source db.\n"); goto exit; } exit: ver_map_destroy(ver_map_tab); return rc; } int cil_android_attributize(struct cil_db *tgtdb, struct cil_db *srcdb, const char *num) { int rc = SEPOL_ERR; hashtab_t ver_map_tab = NULL; ver_map_tab = hashtab_create(ver_map_hash_val, ver_map_key_cmp, VER_MAP_SZ); if (!ver_map_tab) { cil_log(CIL_ERR, "Unable to create version mapping table.\n"); goto exit; } rc = cil_build_ast(srcdb, srcdb->parse->root, srcdb->ast->root); if (rc != SEPOL_OK) { cil_log(CIL_ERR, "Unable to build source db AST.\n"); goto exit; } rc = cil_extract_attributees(srcdb, ver_map_tab); if (rc != SEPOL_OK) { cil_log(CIL_ERR, "Unable to extract attributizable elements from source db.\n"); goto exit; } rc = cil_build_ast(tgtdb, tgtdb->parse->root, tgtdb->ast->root); if (rc != SEPOL_OK) { cil_log(CIL_ERR, "Unable to build target db AST.\n"); goto exit; } rc = cil_attributize(tgtdb, ver_map_tab, num); if (rc != SEPOL_OK) { cil_log(CIL_ERR, "Unable to attributize target db.\n"); goto exit; } exit: ver_map_destroy(ver_map_tab); return rc; }