/** * @file * Implementation of the unreachable domains module. * * @author Kevin Carr kcarr@tresys.com * @author Jeremy A. Mowery jmowery@tresys.com * @author Jason Tang jtang@tresys.com * @author David Windsor dwindsor@tresys.com * * Copyright (C) 2005-2007 Tresys Technology, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "unreachable_doms.h" #include #include #include #include #include static bool parse_default_contexts(const char *ctx_file_path, apol_vector_t * ctx_vector, apol_policy_t * policy); static bool in_isid_ctx(const char *type_name, apol_policy_t * policy); static bool in_def_ctx(const char *type_name, unreachable_doms_data_t * datum); /* for some reason we have to define this here to remove compile warnings */ extern ssize_t getline(char **lineptr, size_t * n, FILE * stream); /* This string is the name f the module and should match the stem * of the file name; it should also match the prefix of all functions * defined in this module and the private data storage structure */ static const char *const mod_name = "unreachable_doms"; int unreachable_doms_register(sechk_lib_t * lib) { sechk_module_t *mod = NULL; sechk_fn_t *fn_struct = NULL; if (!lib) { ERR(NULL, "%s", "No library"); errno = EINVAL; return -1; } /* Modules are declared by the config file and their name and options * are stored in the module array. The name is looked up to determine * where to store the function structures */ mod = sechk_lib_get_module(mod_name, lib); if (!mod) { ERR(NULL, "%s", "Module unknown"); errno = EINVAL; return -1; } mod->parent_lib = lib; /* assign the descriptions */ mod->brief_description = "unreachable domains"; mod->detailed_description = "--------------------------------------------------------------------------------\n" "This module finds all domains in a policy which are unreachable. A domain is\n" "unreachable if any of the following apply:\n" " 1) There is insufficient type enforcement policy to allow a transition,\n" " 2) There is insufficient RBAC policy to allow a transition,\n" " 3) There are no users with proper roles to allow a transition.\n" "However, if any of the above rules indicate an unreachable domain, yet the\n" "domain appears in the system default contexts file, it is considered reachable.\n"; mod->opt_description = " Module requirements:\n" " attribute names\n" #ifdef LIBSELINUX " default_contexts file\n" #endif " Module dependencies:\n" " find_domains module\n" " Module options:\n" " none\n"; mod->severity = SECHK_SEV_MED; /* assign requirements */ if (apol_vector_append(mod->requirements, sechk_name_value_new(SECHK_REQ_POLICY_CAP, SECHK_REQ_CAP_ATTRIB_NAMES)) < 0) { ERR(NULL, "%s", strerror(ENOMEM)); errno = ENOMEM; return -1; } #ifdef LIBSELINUX if (apol_vector_append(mod->requirements, sechk_name_value_new(SECHK_REQ_DEFAULT_CONTEXTS, NULL)) < 0) { ERR(NULL, "%s", strerror(ENOMEM)); errno = ENOMEM; return -1; } #endif /* assign dependencies */ if (apol_vector_append(mod->dependencies, sechk_name_value_new("module", "find_domains")) < 0) { ERR(NULL, "%s", strerror(ENOMEM)); errno = ENOMEM; return -1; } /* register functions */ fn_struct = sechk_fn_new(); if (!fn_struct) { ERR(NULL, "%s", strerror(ENOMEM)); errno = ENOMEM; return -1; } fn_struct->name = strdup(SECHK_MOD_FN_INIT); if (!fn_struct->name) { ERR(NULL, "%s", strerror(ENOMEM)); errno = ENOMEM; return -1; } fn_struct->fn = unreachable_doms_init; if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) { ERR(NULL, "%s", strerror(ENOMEM)); errno = ENOMEM; return -1; } fn_struct = sechk_fn_new(); if (!fn_struct) { ERR(NULL, "%s", strerror(ENOMEM)); errno = ENOMEM; return -1; } fn_struct->name = strdup(SECHK_MOD_FN_RUN); if (!fn_struct->name) { ERR(NULL, "%s", strerror(ENOMEM)); errno = ENOMEM; return -1; } fn_struct->fn = unreachable_doms_run; if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) { ERR(NULL, "%s", strerror(ENOMEM)); errno = ENOMEM; return -1; } mod->data_free = unreachable_doms_data_free; fn_struct = sechk_fn_new(); if (!fn_struct) { ERR(NULL, "%s", strerror(ENOMEM)); errno = ENOMEM; return -1; } fn_struct->name = strdup(SECHK_MOD_FN_PRINT); if (!fn_struct->name) { ERR(NULL, "%s", strerror(ENOMEM)); errno = ENOMEM; return -1; } fn_struct->fn = unreachable_doms_print; if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) { ERR(NULL, "%s", strerror(ENOMEM)); errno = ENOMEM; return -1; } return 0; } /* The init function creates the module's private data storage object * and initializes its values based on the options parsed in the config * file. */ int unreachable_doms_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused))) { unreachable_doms_data_t *datum = NULL; const char *ctx_file_path = NULL; if (!mod || !policy) { ERR(policy, "%s", "Invalid parameters"); errno = EINVAL; return -1; } if (strcmp(mod_name, mod->name)) { ERR(policy, "Wrong module (%s)", mod->name); errno = EINVAL; return -1; } datum = unreachable_doms_data_new(); if (!datum) { ERR(policy, "%s", strerror(ENOMEM)); errno = ENOMEM; return -1; } mod->data = datum; /* Parse default contexts file */ if (!(datum->ctx_vector = apol_vector_create(free))) { ERR(policy, "%s", strerror(ENOMEM)); errno = ENOMEM; return -1; } #ifdef LIBSELINUX ctx_file_path = selinux_default_context_path(); if (!ctx_file_path) { ERR(policy, "%s", "Unable to find default contexts file"); errno = ENOENT; return -1; } else { bool retv = parse_default_contexts(ctx_file_path, datum->ctx_vector, policy); if (!retv) { ERR(policy, "%s", "Unable to parse default contexts file"); errno = EIO; return -1; } } #endif return 0; } typedef enum dom_need { KEEP_SEARCHING = 0, /* keep checking way to reach not found yet */ USER, /* missing a user for role(s) associated with the type */ COMMON_USER, /* missing user for the role in a transition to that type */ ROLE_TRANS, /* transition is valid but need a role transition as well */ ROLE_ALLOW, /* transition is valid and has a role_transition but not role allow */ RBAC, /* there is a transition but insufficient RBAC rules to permit or to determie a user */ VALID_TRANS, /* only transitions to the type are invalid ones needs one or more rules to complete */ ROLE, /* type has no associated role */ TRANSITION, /* no transition exists */ DONE /* done searching a valid way to reach the type has been found */ } dom_need_e; /** * Finds user witn at least one role from each vector. * @param policy The policy. * @param src_roles The first set of roles. * @param tgt_roles The second set of roles. * @return 1 if a common user can be found 0 other wise. */ static int exists_common_user(apol_policy_t * policy, apol_vector_t * src_roles, apol_vector_t * tgt_roles, const qpol_role_t ** which_sr, const qpol_role_t ** which_tr, const qpol_user_t ** which_u) { int retv = 0; apol_user_query_t *uq; const char *name = NULL; const qpol_role_t *role = NULL; const qpol_user_t *user = NULL; qpol_iterator_t *iter = NULL; apol_vector_t *user_v = NULL; qpol_policy_t *q = apol_policy_get_qpol(policy); size_t i, j, k; if (!policy || !src_roles || !tgt_roles) return 0; if (which_sr) *which_sr = NULL; if (which_tr) *which_tr = NULL; if (which_u) *which_u = NULL; if (!(uq = apol_user_query_create())) return 0; for (i = 0; i < apol_vector_get_size(src_roles); i++) { role = apol_vector_get_element(src_roles, i); if (which_sr) *which_sr = role; qpol_role_get_name(q, role, &name); apol_user_query_set_role(policy, uq, name); apol_user_get_by_query(policy, uq, &user_v); for (j = 0; j < apol_vector_get_size(user_v); j++) { user = apol_vector_get_element(user_v, j); qpol_user_get_role_iter(q, user, &iter); for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { qpol_iterator_get_item(iter, (void **)&role); if (!apol_vector_get_index(tgt_roles, role, NULL, NULL, &k)) { retv = 1; if (which_tr) *which_tr = role; if (which_u) *which_u = user; goto exists_done; } } qpol_iterator_destroy(&iter); } apol_vector_destroy(&user_v); } exists_done: qpol_iterator_destroy(&iter); apol_vector_destroy(&user_v); apol_user_query_destroy(&uq); return retv; } /* The run function performs the check. This function runs only once even if * called multiple times. This function allocates the result structure and * fills in all relevant item and proof data. * Return Values: * -1 System error * 0 The module "succeeded" - no negative results found * 1 The module "failed" - some negative results found */ int unreachable_doms_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused))) { unreachable_doms_data_t *datum; sechk_name_value_t *dep = NULL; sechk_result_t *res = NULL, *find_domains_res = NULL; sechk_item_t *item = NULL; sechk_proof_t *proof = NULL; size_t i, j, k, l; sechk_mod_fn_t run_fn = NULL; int error = 0, trans_missing = 0; apol_vector_t *dom_vector = NULL, *dom_results = NULL, *dom_roles = NULL; apol_vector_t *valid_rev_trans = NULL, *invalid_rev_trans = NULL; apol_vector_t *start_roles = NULL, *intersect_roles = NULL; apol_vector_t *tmp_users = NULL, *role_users = NULL; apol_vector_t *role_trans_vector = NULL, *role_allow_vector = NULL; apol_domain_trans_analysis_t *dta = NULL; apol_domain_trans_result_t *dtr = NULL; const char *tmp_name = NULL, *cur_dom_name = NULL; const char *tmp2 = NULL, *tmp3 = NULL; const qpol_type_t *cur_dom = NULL, *ep_type = NULL, *start_type = NULL; const qpol_type_t *last_type = NULL; const qpol_role_t *last_role = NULL, *src_role = NULL, *dflt_role = NULL; const qpol_role_t *last_dflt = NULL; const qpol_user_t *last_user = NULL; dom_need_e need = KEEP_SEARCHING; apol_role_query_t *role_q = NULL; apol_user_query_t *user_q = NULL; apol_role_trans_query_t *rtq = NULL; apol_role_allow_query_t *raq = NULL; const qpol_role_trans_t *role_trans = NULL; qpol_policy_t *q = apol_policy_get_qpol(policy); if (!mod || !policy) { ERR(policy, "%s", "Invalid parameters"); errno = EINVAL; return -1; } if (strcmp(mod_name, mod->name)) { ERR(policy, "Wrong module (%s)", mod->name); errno = EINVAL; return -1; } /* if already run return */ if (mod->result) return 0; datum = (unreachable_doms_data_t *) mod->data; res = sechk_result_new(); if (!res) { ERR(policy, "%s", strerror(ENOMEM)); errno = ENOMEM; return -1; } res->test_name = strdup(mod_name); if (!res->test_name) { error = errno; ERR(policy, "%s", strerror(ENOMEM)); goto unreachable_doms_run_fail; } res->item_type = SECHK_ITEM_TYPE; if (!(res->items = apol_vector_create(sechk_item_free))) { error = errno; ERR(policy, "%s", strerror(ENOMEM)); goto unreachable_doms_run_fail; } /* run dependencies and get results */ for (i = 0; i < apol_vector_get_size(mod->dependencies); i++) { dep = apol_vector_get_element(mod->dependencies, i); run_fn = sechk_lib_get_module_function(dep->value, SECHK_MOD_FN_RUN, mod->parent_lib); run_fn(sechk_lib_get_module(dep->value, mod->parent_lib), policy, NULL); } find_domains_res = sechk_lib_get_module_result("find_domains", mod->parent_lib); dom_results = find_domains_res->items; if (!(dom_vector = apol_vector_create(NULL))) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } for (i = 0; i < apol_vector_get_size(dom_results); i++) { item = apol_vector_get_element(dom_results, i); if (apol_vector_append(dom_vector, (void *)(item->item))) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } } item = NULL; dom_results = NULL; /* no need to destroy, belongs to another module. */ /* initialize query objects */ dta = apol_domain_trans_analysis_create(); if (!dta) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } apol_domain_trans_analysis_set_direction(policy, dta, APOL_DOMAIN_TRANS_DIRECTION_REVERSE); role_q = apol_role_query_create(); if (!role_q) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } user_q = apol_user_query_create(); if (!user_q) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } rtq = apol_role_trans_query_create(); if (!rtq) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } raq = apol_role_allow_query_create(); if (!raq) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } /* dom_vector now contains all types considered domains */ for (i = 0; i < apol_vector_get_size(dom_vector); i++) { cur_dom = apol_vector_get_element(dom_vector, i); qpol_type_get_name(q, cur_dom, &cur_dom_name); need = KEEP_SEARCHING; if ( #ifdef LIBSELINUX in_def_ctx(cur_dom_name, datum) || #endif in_isid_ctx(cur_dom_name, policy)) continue; /* collect information about roles and transitions to this domain */ apol_role_query_set_type(policy, role_q, cur_dom_name); apol_role_get_by_query(policy, role_q, &dom_roles); apol_policy_reset_domain_trans_table(policy); apol_domain_trans_analysis_set_start_type(policy, dta, cur_dom_name); apol_domain_trans_analysis_set_valid(policy, dta, APOL_DOMAIN_TRANS_SEARCH_VALID); apol_domain_trans_analysis_do(policy, dta, &valid_rev_trans); apol_policy_reset_domain_trans_table(policy); apol_domain_trans_analysis_set_valid(policy, dta, APOL_DOMAIN_TRANS_SEARCH_INVALID); apol_domain_trans_analysis_do(policy, dta, &invalid_rev_trans); /* for valid transitions - validate RBAC, and then users */ for (j = 0; j < apol_vector_get_size(valid_rev_trans); j++) { dtr = apol_vector_get_element(valid_rev_trans, j); start_type = apol_domain_trans_result_get_start_type(dtr); ep_type = apol_domain_trans_result_get_entrypoint_type(dtr); qpol_type_get_name(q, start_type, &tmp_name); apol_role_query_set_type(policy, role_q, tmp_name); apol_role_get_by_query(policy, role_q, &start_roles); intersect_roles = apol_vector_create_from_intersection(dom_roles, start_roles, NULL, NULL); if (apol_vector_get_size(intersect_roles) > 0) { /* find user with role in intersect */ role_users = apol_vector_create(NULL); for (k = 0; k < apol_vector_get_size(intersect_roles); k++) { last_role = apol_vector_get_element(intersect_roles, k); qpol_role_get_name(q, last_role, &tmp_name); apol_user_query_set_role(policy, user_q, tmp_name); apol_user_get_by_query(policy, user_q, &tmp_users); if (apol_vector_cat(role_users, tmp_users)) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } apol_vector_destroy(&tmp_users); } if (apol_vector_get_size(role_users) > 0) need = DONE; else need = USER; apol_vector_destroy(&role_users); } if (need == DONE) break; /* look for role_transitions */ qpol_type_get_name(q, ep_type, &tmp_name); apol_role_trans_query_set_target(policy, rtq, tmp_name, 1); apol_role_trans_get_by_query(policy, rtq, &role_trans_vector); for (k = 0; need != DONE && k < apol_vector_get_size(role_trans_vector); k++) { role_trans = apol_vector_get_element(role_trans_vector, k); qpol_role_trans_get_source_role(q, role_trans, &src_role); qpol_role_trans_get_default_role(q, role_trans, &dflt_role); if (apol_vector_get_index(start_roles, src_role, NULL, NULL, &l) || apol_vector_get_index(dom_roles, dflt_role, NULL, NULL, &l)) continue; /* start domain must have the source role and cur_dom must have default role or transition does not apply */ if (exists_common_user(policy, start_roles, dom_roles, NULL, NULL, NULL)) { qpol_role_get_name(q, src_role, &tmp_name); apol_role_allow_query_set_source(policy, raq, tmp_name); qpol_role_get_name(q, dflt_role, &tmp_name); apol_role_allow_query_set_target(policy, raq, tmp_name); apol_role_allow_get_by_query(policy, raq, &role_allow_vector); if (apol_vector_get_size(role_allow_vector) > 0) { need = DONE; } else { need = ROLE_ALLOW; last_role = src_role; last_dflt = dflt_role; } apol_vector_destroy(&role_allow_vector); } else { need = COMMON_USER; last_role = src_role; last_dflt = dflt_role; } } /* no roles usable in intersection and no transitions so pick first set with common user */ if (apol_vector_get_size(role_trans_vector) == 0) { if (exists_common_user(policy, start_roles, dom_roles, &src_role, &dflt_role, &last_user)) { need = ROLE_TRANS; last_role = src_role; last_dflt = dflt_role; last_type = ep_type; } else { need = RBAC; } } apol_vector_destroy(&role_trans_vector); if (need == DONE) break; } /* if no valid transition found - check what is needed to complete invalid ones */ if (need == KEEP_SEARCHING) { for (j = 0; j < apol_vector_get_size(invalid_rev_trans); j++) { dtr = apol_vector_get_element(invalid_rev_trans, j); start_type = apol_domain_trans_result_get_start_type(dtr); ep_type = apol_domain_trans_result_get_entrypoint_type(dtr); trans_missing = apol_domain_trans_table_verify_trans(policy, start_type, ep_type, cur_dom); need = VALID_TRANS; /* since incomplete transitions can be missing types break if we * found one with all three types specified, else keep the last one */ if (start_type && ep_type) break; } } /* if no transition exists (valid or otherwise) check that at least one role and user pair is valid */ if (need == KEEP_SEARCHING) { role_users = apol_vector_create(NULL); for (j = 0; j < apol_vector_get_size(dom_roles); j++) { last_role = apol_vector_get_element(dom_roles, j); qpol_role_get_name(q, last_role, &tmp_name); apol_user_query_set_role(policy, user_q, tmp_name); apol_user_get_by_query(policy, user_q, &tmp_users); apol_vector_cat(role_users, tmp_users); apol_vector_destroy(&tmp_users); } if (apol_vector_get_size(dom_roles) == 0) { need = ROLE; } else if (apol_vector_get_size(role_users) == 0) { need = USER; } else { need = TRANSITION; } apol_vector_destroy(&role_users); } /* if something needs to be reported do so now */ if (need != DONE) { assert(need != KEEP_SEARCHING); item = sechk_item_new(NULL); if (!item) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } item->item = (void *)cur_dom; item->test_result = (unsigned char)need; item->proof = apol_vector_create(sechk_proof_free); if (!item->proof) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } proof = sechk_proof_new(NULL); if (!proof) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } proof->type = SECHK_ITEM_NONE; proof->elem = NULL; switch (need) { case USER: { qpol_role_get_name(q, last_role, &tmp_name); if (asprintf(&proof->text, "No user associated with role %s for %s", tmp_name, cur_dom_name) < 0) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } break; } case COMMON_USER: { qpol_role_get_name(q, last_role, &tmp_name); qpol_role_get_name(q, last_dflt, &tmp2); if (asprintf (&proof->text, "Role transition required but no user associated with role %s and %s", tmp_name, tmp2) < 0) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } break; } case ROLE_TRANS: { qpol_role_get_name(q, last_role, &tmp_name); qpol_role_get_name(q, last_dflt, &tmp2); qpol_type_get_name(q, last_type, &tmp3); if (asprintf(&proof->text, "Missing: role_transition %s %s %s;", tmp_name, tmp3, tmp2) < 0) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } break; } case ROLE_ALLOW: { qpol_role_get_name(q, last_role, &tmp_name); qpol_role_get_name(q, last_dflt, &tmp2); if (asprintf (&proof->text, "Role transition required but missing role allow rule.\n\tMissing: allow %s %s;", tmp_name, tmp2) < 0) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } break; } case RBAC: { if (asprintf (&proof->text, "Valid domain transition to %s exists but indufficient RBAC rules to permit it.", cur_dom_name) < 0) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } break; } case VALID_TRANS: { if (start_type) qpol_type_get_name(q, start_type, &tmp2); else tmp2 = ""; if (ep_type) qpol_type_get_name(q, ep_type, &tmp3); else tmp3 = ""; if (asprintf (&proof->text, "Partial transition to %s found:\n\t%s: allow %s %s : process transition;\n\t%s: allow %s %s : file execute;\n\t%s: allow %s %s : file entrypoint;\n\t%s one of:\n\tallow %s self : process setexec;\n\ttype_transition %s %s : process %s;", cur_dom_name, ((trans_missing & APOL_DOMAIN_TRANS_RULE_PROC_TRANS) ? "Missing" : "Has"), tmp2, cur_dom_name, ((trans_missing & APOL_DOMAIN_TRANS_RULE_EXEC) ? "Missing" : "Has"), tmp2, tmp3, ((trans_missing & APOL_DOMAIN_TRANS_RULE_ENTRYPOINT) ? "Missing" : "Has"), cur_dom_name, tmp3, ((trans_missing & (APOL_DOMAIN_TRANS_RULE_TYPE_TRANS | APOL_DOMAIN_TRANS_RULE_SETEXEC)) ? "May need" : "Has"), cur_dom_name, tmp2, tmp3, cur_dom_name) < 0) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } break; } case ROLE: { if (asprintf(&proof->text, "No role associated with domain %s", cur_dom_name) < 0) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } break; } case TRANSITION: { if (asprintf(&proof->text, "There are no transitions to domain %s", cur_dom_name) < 0) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } break; } case DONE: case KEEP_SEARCHING: default: { assert(0); error = EDOM; goto unreachable_doms_run_fail; } } if (apol_vector_append(item->proof, (void *)proof) < 0) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } proof = NULL; if (apol_vector_append(res->items, (void *)item) < 0) { error = errno; ERR(policy, "%s", strerror(error)); goto unreachable_doms_run_fail; } item = NULL; } apol_vector_destroy(&dom_roles); apol_vector_destroy(&valid_rev_trans); apol_vector_destroy(&invalid_rev_trans); } apol_vector_destroy(&dom_vector); apol_domain_trans_analysis_destroy(&dta); apol_role_query_destroy(&role_q); apol_user_query_destroy(&user_q); apol_role_trans_query_destroy(&rtq); apol_role_allow_query_destroy(&raq); mod->result = res; if (apol_vector_get_size(res->items)) return 1; return 0; unreachable_doms_run_fail: apol_vector_destroy(&dom_vector); apol_domain_trans_analysis_destroy(&dta); apol_role_query_destroy(&role_q); apol_user_query_destroy(&user_q); apol_role_trans_query_destroy(&rtq); apol_role_allow_query_destroy(&raq); apol_vector_destroy(&dom_roles); apol_vector_destroy(&valid_rev_trans); apol_vector_destroy(&invalid_rev_trans); apol_vector_destroy(&tmp_users); apol_vector_destroy(&role_users); apol_vector_destroy(&role_trans_vector); sechk_proof_free(proof); sechk_item_free(item); sechk_result_destroy(&res); errno = error; return -1; } /* The free function frees the private data of a module */ void unreachable_doms_data_free(void *data) { unreachable_doms_data_t *d = data; if (!data) return; free(d->ctx_file_path); apol_vector_destroy(&d->ctx_vector); free(data); } /* The print function generates the text and prints the results to stdout. */ int unreachable_doms_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused))) { unreachable_doms_data_t *datum = NULL; unsigned char outformat = 0x00; sechk_item_t *item = NULL; sechk_proof_t *proof = NULL; size_t i = 0, j = 0, k, l, num_items; const qpol_type_t *type; qpol_policy_t *q = apol_policy_get_qpol(policy); const char *type_name; if (!mod || !policy) { ERR(policy, "%s", strerror(EINVAL)); errno = EINVAL; return -1; } if (strcmp(mod_name, mod->name)) { ERR(policy, "Wrong module (%s)", mod->name); errno = EINVAL; return -1; } datum = (unreachable_doms_data_t *) mod->data; outformat = mod->outputformat; num_items = apol_vector_get_size(mod->result->items); if (!mod->result) { ERR(policy, "%s", "Module has not been run"); errno = EINVAL; return -1; } if (!outformat || (outformat & SECHK_OUT_QUIET)) return 0; /* not an error - no output is requested */ if (outformat & SECHK_OUT_STATS) { printf("Found %zd unreachable domains.\n", num_items); } if (outformat & SECHK_OUT_LIST) { printf("\n"); for (i = 0; i < num_items; i++) { j++; j %= 4; item = apol_vector_get_element(mod->result->items, i); type = (qpol_type_t *) item->item; qpol_type_get_name(q, type, &type_name); printf("%s%s", type_name, (char *)((j && i != num_items - 1) ? ", " : "\n")); } printf("\n"); } if (outformat & SECHK_OUT_PROOF) { if (apol_vector_get_size(datum->ctx_vector) > 0) { printf("Found %zd domains in %s:\n", apol_vector_get_size(datum->ctx_vector), selinux_default_context_path()); for (j = 0; j < apol_vector_get_size(datum->ctx_vector); j++) { type_name = apol_vector_get_element(datum->ctx_vector, j); printf("\t%s\n", type_name); } } printf("\n"); for (k = 0; k < num_items; k++) { item = apol_vector_get_element(mod->result->items, k); if (item) { type = item->item; qpol_type_get_name(q, type, &type_name); printf("%s\n", (char *)type_name); for (l = 0; l < apol_vector_get_size(item->proof); l++) { proof = apol_vector_get_element(item->proof, l); if (proof) printf("\t%s\n", proof->text); } } printf("\n"); } } return 0; } /* The unreachable_doms_data_new function allocates and returns an initialized * private data storage structure for this module. */ unreachable_doms_data_t *unreachable_doms_data_new(void) { unreachable_doms_data_t *datum = NULL; datum = (unreachable_doms_data_t *) calloc(1, sizeof(unreachable_doms_data_t)); return datum; } /* Parses default_contexts and adds source domains to datum->ctx_list. * The vector will contain newly allocated strings. */ static bool parse_default_contexts(const char *ctx_file_path, apol_vector_t * ctx_vector, apol_policy_t * policy) { int str_sz, i, charno, error = 0, retv; FILE *ctx_file; char *line = NULL, *src_role = NULL, *src_dom = NULL, *dst_role = NULL, *dst_dom = NULL; size_t line_len = 0; bool uses_mls = false; printf("Using default contexts: %s\n", ctx_file_path); ctx_file = fopen(ctx_file_path, "r"); if (!ctx_file) { error = errno; ERR(policy, "Opening default contexts file %s", ctx_file_path); goto parse_default_contexts_fail; } while (!feof(ctx_file)) { retv = getline(&line, &line_len, ctx_file); if (retv == -1) { if (feof(ctx_file)) { break; } else { error = errno; ERR(policy, "%s", "Reading default contexts file"); goto parse_default_contexts_fail; } } uses_mls = false; str_sz = APOL_STR_SZ + 128; i = 0; /* source role */ src_role = malloc(str_sz); if (!src_role) { error = errno; ERR(policy, "%s", strerror(ENOMEM)); goto parse_default_contexts_fail; } memset(src_role, 0x0, str_sz); charno = 0; while (line[i] != ':') { if (!isspace(line[i])) { src_role[i] = line[i]; charno++; } i++; } i++; /* skip ':' */ /* source type */ src_dom = malloc(str_sz); if (!src_dom) { error = errno; ERR(policy, "%s", strerror(ENOMEM)); goto parse_default_contexts_fail; } memset(src_dom, 0x0, str_sz); charno = 0; while (1) { if (isspace(line[i])) break; /* Check for MLS */ if (line[i] == ':') { uses_mls = true; i++; /* skip ':' */ while (!isspace(line[i])) i++; } if (uses_mls) break; src_dom[charno] = line[i]; charno++; i++; } /* dest role */ dst_role = malloc(str_sz); if (!dst_role) { error = errno; ERR(policy, "%s", strerror(ENOMEM)); goto parse_default_contexts_fail; } memset(dst_role, 0x0, str_sz); charno = 0; while (line[i] != ':') { if (!isspace(line[i])) { dst_role[charno] = line[i]; charno++; } i++; } i++; /* skip ':' */ /* dest type */ dst_dom = malloc(str_sz); if (!dst_dom) { error = errno; ERR(policy, "%s", strerror(ENOMEM)); goto parse_default_contexts_fail; } memset(dst_dom, 0x0, str_sz); charno = 0; while (line[i]) { if (uses_mls) if (line[i] == ':') break; if (!isspace(line[i])) dst_dom[charno] = line[i]; charno++; i++; } if (apol_vector_append(ctx_vector, (void *)strdup(src_dom)) < 0) { error = errno; ERR(policy, "%s", strerror(ENOMEM)); goto parse_default_contexts_fail; } free(line); line = NULL; free(src_role); free(src_dom); free(dst_role); free(dst_dom); } free(line); fclose(ctx_file); return true; parse_default_contexts_fail: if (ctx_file != NULL) { fclose(ctx_file); } free(line); free(src_role); free(src_dom); free(dst_role); free(dst_dom); errno = error; return false; } /* Returns true if type_idx is in datum->ctx_list */ static bool in_def_ctx(const char *type_name, unreachable_doms_data_t * datum) { size_t i; if (apol_vector_get_index(datum->ctx_vector, type_name, apol_str_strcmp, NULL, &i) < 0) { return false; } return true; } /* Returns true if type is a type assigned to an isid */ static bool in_isid_ctx(const char *type_name, apol_policy_t * policy) { qpol_iterator_t *iter = NULL; qpol_policy_t *q = apol_policy_get_qpol(policy); qpol_policy_get_isid_iter(q, &iter); for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { const qpol_isid_t *isid; const qpol_context_t *ocon; const qpol_type_t *context_type; const char *context_type_name; qpol_iterator_get_item(iter, (void **)&isid); qpol_isid_get_context(q, isid, &ocon); qpol_context_get_type(q, ocon, &context_type); qpol_type_get_name(q, context_type, &context_type_name); if (!strcmp(type_name, context_type_name)) { qpol_iterator_destroy(&iter); return true; } } qpol_iterator_destroy(&iter); return false; }