diff options
Diffstat (limited to 'sechecker/modules/unreachable_doms.c')
-rw-r--r-- | sechecker/modules/unreachable_doms.c | 1053 |
1 files changed, 1053 insertions, 0 deletions
diff --git a/sechecker/modules/unreachable_doms.c b/sechecker/modules/unreachable_doms.c new file mode 100644 index 0000000..4653c2d --- /dev/null +++ b/sechecker/modules/unreachable_doms.c @@ -0,0 +1,1053 @@ +/** + * @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 <config.h> + +#include "unreachable_doms.h" + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <assert.h> + +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 = "<start_type>"; + if (ep_type) + qpol_type_get_name(q, ep_type, &tmp3); + else + tmp3 = "<entrypont>"; + 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; +} |