summaryrefslogtreecommitdiffstats
path: root/sechecker/modules/unreachable_doms.c
diff options
context:
space:
mode:
Diffstat (limited to 'sechecker/modules/unreachable_doms.c')
-rw-r--r--sechecker/modules/unreachable_doms.c1053
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;
+}