summaryrefslogtreecommitdiffstats
path: root/libapol/src/domain-trans-analysis.c
diff options
context:
space:
mode:
Diffstat (limited to 'libapol/src/domain-trans-analysis.c')
-rw-r--r--libapol/src/domain-trans-analysis.c2076
1 files changed, 2076 insertions, 0 deletions
diff --git a/libapol/src/domain-trans-analysis.c b/libapol/src/domain-trans-analysis.c
new file mode 100644
index 0000000..3fef3b8
--- /dev/null
+++ b/libapol/src/domain-trans-analysis.c
@@ -0,0 +1,2076 @@
+/**
+ * @file
+ *
+ * Routines to perform a domain transition analysis.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+#include "domain-trans-analysis-internal.h"
+#include <apol/domain-trans-analysis.h>
+#include <apol/bst.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+
+/* private data structure definitions */
+struct apol_domain_trans_table
+{
+ apol_bst_t *domain_table;
+ apol_bst_t *entrypoint_table;
+};
+
+typedef struct dom_node
+{
+ const qpol_type_t *type;
+ apol_bst_t *process_transition_tree;
+ apol_bst_t *entrypoint_tree;
+ apol_vector_t *setexec_rules;
+} dom_node_t;
+
+typedef struct ep_node
+{
+ const qpol_type_t *type;
+ apol_bst_t *execute_tree;
+ apol_bst_t *type_transition_tree;
+} ep_node_t;
+
+typedef struct avrule_node
+{
+ const qpol_type_t *type;
+ const qpol_avrule_t *rule;
+ bool used;
+} avrule_node_t;
+
+typedef struct terule_node
+{
+ const qpol_type_t *src;
+ const qpol_type_t *dflt;
+ const qpol_terule_t *rule;
+ bool used;
+} terule_node_t;
+
+/* public data structure definitions */
+struct apol_domain_trans_analysis
+{
+ unsigned char direction;
+ unsigned char valid;
+ char *start_type;
+ char *result;
+ apol_vector_t *access_types;
+ apol_vector_t *access_classes;
+ apol_vector_t *access_perms;
+ regex_t *result_regex;
+};
+
+struct apol_domain_trans_result
+{
+ const qpol_type_t *start_type;
+ const qpol_type_t *ep_type;
+ const qpol_type_t *end_type;
+ apol_vector_t *proc_trans_rules;
+ apol_vector_t *ep_rules;
+ apol_vector_t *exec_rules;
+ apol_vector_t *setexec_rules;
+ apol_vector_t *type_trans_rules;
+ bool valid;
+ /** if access filters used list of rules that satisfy
+ * the filter criteria (of type qpol_avrule_t) */
+ apol_vector_t *access_rules;
+};
+
+/* private functions */
+/* avrule_node */
+static int avrule_node_cmp(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ const avrule_node_t *an = a;
+ const avrule_node_t *bn = b;
+ ssize_t retv = (const char *)an->type - (const char *)bn->type;
+ if (retv > 0)
+ return 1;
+ else if (retv < 0)
+ return -1;
+ retv = (const char *)an->rule - (const char *)bn->rule;
+ if (retv > 0)
+ return 1;
+ else if (retv < 0)
+ return -1;
+ return 0;
+}
+
+static int avrule_node_reset(void *a, void *b __attribute__ ((unused)))
+{
+ avrule_node_t *an = a;
+ if (!a)
+ return -1;
+ an->used = false;
+ return 0;
+}
+
+static avrule_node_t *avrule_node_create(const qpol_type_t * type, const qpol_avrule_t * rule)
+{
+ avrule_node_t *n = calloc(1, sizeof(*n));
+ if (!n)
+ return NULL;
+
+ n->type = type;
+ n->rule = rule;
+
+ return n;
+}
+
+/* terule_node */
+static int terule_node_cmp(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ const terule_node_t *an = a;
+ const terule_node_t *bn = b;
+ ssize_t retv = (const char *)an->src - (const char *)bn->src;
+ if (retv > 0)
+ return 1;
+ else if (retv < 0)
+ return -1;
+ retv = (const char *)an->dflt - (const char *)bn->dflt;
+ if (retv > 0)
+ return 1;
+ else if (retv < 0)
+ return -1;
+ retv = (const char *)an->rule - (const char *)bn->rule;
+ if (retv > 0)
+ return 1;
+ else if (retv < 0)
+ return -1;
+ return 0;
+}
+
+static int terule_node_reset(void *a, void *b __attribute__ ((unused)))
+{
+ terule_node_t *an = a;
+ if (!a)
+ return -1;
+ an->used = false;
+ return 0;
+}
+
+static terule_node_t *terule_node_create(const qpol_type_t * src, const qpol_type_t * dflt, const qpol_terule_t * rule)
+{
+ terule_node_t *n = calloc(1, sizeof(*n));
+ if (!n)
+ return NULL;
+
+ n->src = src;
+ n->dflt = dflt;
+ n->rule = rule;
+
+ return n;
+}
+
+/* dom_node */
+static int dom_node_cmp(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ const dom_node_t *an = a;
+ const dom_node_t *bn = b;
+
+ if ((const char *)(an->type) < (const char *)(bn->type))
+ return -1;
+ else if ((const char *)(an->type) > (const char *)(bn->type))
+ return 1;
+ return 0;
+}
+
+static void dom_node_free(void *x)
+{
+ if (!x)
+ return;
+ apol_bst_destroy(&(((dom_node_t *) x)->process_transition_tree));
+ apol_bst_destroy(&(((dom_node_t *) x)->entrypoint_tree));
+ apol_vector_destroy(&(((dom_node_t *) x)->setexec_rules));
+ free(x);
+}
+
+static int dom_node_reset(void *a, void *b __attribute__ ((unused)))
+{
+ dom_node_t *an = a;
+ if (!a)
+ return -1;
+
+ if (apol_bst_inorder_map(an->process_transition_tree, avrule_node_reset, NULL) < 0)
+ return -1;
+ if (apol_bst_inorder_map(an->entrypoint_tree, avrule_node_reset, NULL) < 0)
+ return -1;
+
+ return 0;
+}
+
+static dom_node_t *dom_node_create(const qpol_type_t * type)
+{
+ dom_node_t *n = calloc(1, sizeof(*n));
+ if (!n)
+ return NULL;
+
+ n->type = type;
+ if (!(n->process_transition_tree = apol_bst_create(avrule_node_cmp, free)) ||
+ !(n->entrypoint_tree = apol_bst_create(avrule_node_cmp, free)) || !(n->setexec_rules = apol_vector_create(NULL))) {
+ apol_bst_destroy(&n->process_transition_tree);
+ apol_bst_destroy(&n->entrypoint_tree);
+ apol_vector_destroy(&n->setexec_rules);
+ free(n);
+ return NULL;
+ }
+
+ return n;
+}
+
+/* ep_node */
+static int ep_node_cmp(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ const ep_node_t *an = a;
+ const ep_node_t *bn = b;
+
+ if ((const char *)(an->type) < (const char *)(bn->type))
+ return -1;
+ else if ((const char *)(an->type) > (const char *)(bn->type))
+ return 1;
+ return 0;
+}
+
+static void ep_node_free(void *x)
+{
+ if (!x)
+ return;
+ apol_bst_destroy(&(((ep_node_t *) x)->type_transition_tree));
+ apol_bst_destroy(&(((ep_node_t *) x)->execute_tree));
+ free(x);
+}
+
+static int ep_node_reset(void *a, void *b __attribute__ ((unused)))
+{
+ ep_node_t *an = a;
+ if (!a)
+ return -1;
+
+ if (apol_bst_inorder_map(an->execute_tree, avrule_node_reset, NULL) < 0)
+ return -1;
+ if (apol_bst_inorder_map(an->type_transition_tree, terule_node_reset, NULL) < 0)
+ return -1;
+ return 0;
+}
+
+static ep_node_t *ep_node_create(const qpol_type_t * type)
+{
+ ep_node_t *n = calloc(1, sizeof(*n));
+ if (!n)
+ return NULL;
+
+ n->type = type;
+ if (!(n->execute_tree = apol_bst_create(avrule_node_cmp, free)) ||
+ !(n->type_transition_tree = apol_bst_create(terule_node_cmp, free))) {
+ apol_bst_destroy(&n->execute_tree);
+ apol_bst_destroy(&n->type_transition_tree);
+ free(n);
+ return NULL;
+ }
+
+ return n;
+}
+
+/* table */
+static apol_domain_trans_table_t *apol_domain_trans_table_new(apol_policy_t * policy)
+{
+ apol_domain_trans_table_t *new_table = NULL;
+ int error;
+
+ if (!policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ new_table = (apol_domain_trans_table_t *) calloc(1, sizeof(apol_domain_trans_table_t));
+ if (!new_table) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ error = ENOMEM;
+ goto cleanup;
+ }
+
+ if (!(new_table->domain_table = apol_bst_create(dom_node_cmp, dom_node_free))) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ error = ENOMEM;
+ goto cleanup;
+ }
+ if (!(new_table->entrypoint_table = apol_bst_create(ep_node_cmp, ep_node_free))) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ error = ENOMEM;
+ goto cleanup;
+ }
+
+ return new_table;
+ cleanup:
+ domain_trans_table_destroy(&new_table);
+ errno = error;
+ return NULL;
+}
+
+static int table_add_avrule(apol_policy_t * policy, apol_domain_trans_table_t * dta_table, const qpol_avrule_t * rule)
+{
+ qpol_policy_t *qp = apol_policy_get_qpol(policy);
+ const qpol_type_t *src;
+ const qpol_type_t *tgt;
+ qpol_avrule_get_source_type(qp, rule, &src);
+ qpol_avrule_get_target_type(qp, rule, &tgt);
+ apol_vector_t *sources = apol_query_expand_type(policy, src);
+ apol_vector_t *targets = apol_query_expand_type(policy, tgt);
+ bool exec = false, ep = false, proc_trans = false, setexec = false;
+ qpol_iterator_t *iter = NULL;
+ int error = 0;
+ qpol_avrule_get_perm_iter(qp, rule, &iter);
+ if (!iter || !sources || !targets) {
+ error = errno;
+ goto err;
+ }
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ void *x;
+ qpol_iterator_get_item(iter, &x);
+ char *perm = x;
+ if (!strcmp("execute", perm))
+ exec = true;
+ if (!strcmp("entrypoint", perm))
+ ep = true;
+ if (!strcmp("transition", perm))
+ proc_trans = true;
+ if (!strcmp("setexec", perm))
+ setexec = true;
+ free(x);
+ }
+ qpol_iterator_destroy(&iter);
+
+ if (proc_trans || ep || setexec) {
+ for (size_t i = 0; i < apol_vector_get_size(sources); i++) {
+ dom_node_t *dnode = NULL;
+ dom_node_t dummy = { apol_vector_get_element(sources, i), NULL, NULL, NULL };
+ if (apol_bst_get_element(dta_table->domain_table, &dummy, NULL, (void **)&dnode)) {
+ dom_node_t *new_dnode = NULL;
+ if (!(new_dnode = dom_node_create(dummy.type)) ||
+ apol_bst_insert(dta_table->domain_table, (void *)new_dnode, NULL)) {
+ error = errno;
+ dom_node_free(new_dnode);
+ goto err;
+ }
+ dnode = new_dnode;
+ }
+ if (setexec) {
+ if (apol_vector_append_unique(dnode->setexec_rules, (void *)rule, NULL, NULL)) {
+ error = errno;
+ goto err;
+ }
+ }
+ for (size_t j = 0; j < apol_vector_get_size(targets); j++) {
+ if (proc_trans) {
+ avrule_node_t *new_node =
+ avrule_node_create((const qpol_type_t *)apol_vector_get_element(targets, j), rule);
+ if (!new_node ||
+ apol_bst_insert_and_get(dnode->process_transition_tree, (void **)&new_node, NULL) < 0) {
+ error = errno;
+ free(new_node);
+ goto err;
+ }
+ }
+ if (ep) {
+ avrule_node_t *new_node =
+ avrule_node_create((const qpol_type_t *)apol_vector_get_element(targets, j), rule);
+ if (!new_node ||
+ apol_bst_insert_and_get(dnode->entrypoint_tree, (void **)&new_node, NULL) < 0) {
+ error = errno;
+ free(new_node);
+ goto err;
+ }
+ }
+ }
+ }
+ }
+ if (exec) {
+ for (size_t i = 0; i < apol_vector_get_size(targets); i++) {
+ ep_node_t *enode = NULL;
+ ep_node_t dummy = { apol_vector_get_element(targets, i), NULL, NULL };
+ if (apol_bst_get_element(dta_table->entrypoint_table, &dummy, NULL, (void **)&enode)) {
+ ep_node_t *new_enode = NULL;
+ if (!(new_enode = ep_node_create(dummy.type)) ||
+ apol_bst_insert(dta_table->entrypoint_table, (void *)new_enode, NULL)) {
+ error = errno;
+ ep_node_free(new_enode);
+ goto err;
+ }
+ enode = new_enode;
+ }
+ for (size_t j = 0; j < apol_vector_get_size(sources); j++) {
+ avrule_node_t *new_node =
+ avrule_node_create((const qpol_type_t *)apol_vector_get_element(sources, j), rule);
+ if (!new_node || apol_bst_insert_and_get(enode->execute_tree, (void **)&new_node, NULL) < 0) {
+ error = errno;
+ free(new_node);
+ goto err;
+ }
+ }
+ }
+ }
+
+ apol_vector_destroy(&sources);
+ apol_vector_destroy(&targets);
+ return 0;
+
+ err:
+ qpol_iterator_destroy(&iter);
+ apol_vector_destroy(&sources);
+ apol_vector_destroy(&targets);
+ errno = error;
+ return -1;
+}
+
+static int table_add_terule(apol_policy_t * policy, apol_domain_trans_table_t * dta_table, const qpol_terule_t * rule)
+{
+ qpol_policy_t *qp = apol_policy_get_qpol(policy);
+ const qpol_type_t *src;
+ const qpol_type_t *tgt;
+ const qpol_type_t *dflt;
+ qpol_terule_get_source_type(qp, rule, &src);
+ qpol_terule_get_target_type(qp, rule, &tgt);
+ qpol_terule_get_default_type(qp, rule, &dflt);
+ apol_vector_t *sources = apol_query_expand_type(policy, src);
+ apol_vector_t *targets = apol_query_expand_type(policy, tgt);
+ int error = 0;
+ for (size_t i = 0; i < apol_vector_get_size(targets); i++) {
+ ep_node_t *enode = NULL;
+ ep_node_t dummy = { apol_vector_get_element(targets, i), NULL, NULL };
+ if (apol_bst_get_element(dta_table->entrypoint_table, &dummy, NULL, (void **)&enode)) {
+ ep_node_t *new_enode = NULL;
+ if (!(new_enode = ep_node_create(dummy.type)) ||
+ apol_bst_insert(dta_table->entrypoint_table, (void *)new_enode, NULL)) {
+ error = errno;
+ ep_node_free(new_enode);
+ goto err;
+ }
+ enode = new_enode;
+ }
+ for (size_t j = 0; j < apol_vector_get_size(sources); j++) {
+ terule_node_t *new_node =
+ terule_node_create((const qpol_type_t *)apol_vector_get_element(sources, j), dflt, rule);
+ if (apol_bst_insert_and_get(enode->type_transition_tree, (void **)&new_node, NULL) < 0) {
+ error = errno;
+ free(new_node);
+ goto err;
+ }
+ }
+ }
+
+ apol_vector_destroy(&sources);
+ apol_vector_destroy(&targets);
+ return 0;
+ err:
+ apol_vector_destroy(&sources);
+ apol_vector_destroy(&targets);
+ errno = error;
+ return -1;
+}
+
+/* result */
+apol_domain_trans_result_t *domain_trans_result_create()
+{
+ apol_domain_trans_result_t *res = calloc(1, sizeof(*res));
+ if (!res)
+ return NULL;
+
+ int error = 0;
+ if (!(res->proc_trans_rules = apol_vector_create(NULL)) || !(res->ep_rules = apol_vector_create(NULL)) ||
+ !(res->exec_rules = apol_vector_create(NULL)) || !(res->setexec_rules = apol_vector_create(NULL)) ||
+ !(res->type_trans_rules = apol_vector_create(NULL))) {
+ error = errno;
+ goto err;
+ }
+
+ return res;
+ err:
+ apol_domain_trans_result_destroy(&res);
+ errno = error;
+ return NULL;
+}
+
+/* public functions */
+/* table */
+int apol_policy_build_domain_trans_table(apol_policy_t * policy)
+{
+ int error = 0;
+ apol_avrule_query_t *avq = NULL;
+ apol_terule_query_t *teq = NULL;
+ apol_vector_t *avrules = NULL;
+ apol_vector_t *terules = NULL;
+
+ if (!policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (policy->domain_trans_table) {
+ return 0; /* already built */
+ }
+
+ apol_domain_trans_table_t *dta_table = policy->domain_trans_table = apol_domain_trans_table_new(policy);
+ if (!policy->domain_trans_table) {
+ error = errno;
+ goto err;
+ }
+
+ avq = apol_avrule_query_create();
+ apol_avrule_query_set_rules(policy, avq, QPOL_RULE_ALLOW);
+ apol_avrule_query_append_class(policy, avq, "file");
+ apol_avrule_query_append_class(policy, avq, "process");
+ apol_avrule_query_append_perm(policy, avq, "execute");
+ apol_avrule_query_append_perm(policy, avq, "entrypoint");
+ apol_avrule_query_append_perm(policy, avq, "transition");
+ apol_avrule_query_append_perm(policy, avq, "setexec");
+ if (apol_avrule_get_by_query(policy, avq, &avrules)) {
+ error = errno;
+ goto err;
+ }
+ apol_avrule_query_destroy(&avq);
+ for (size_t i = 0; i < apol_vector_get_size(avrules); i++) {
+ if (table_add_avrule(policy, dta_table, (const qpol_avrule_t *)apol_vector_get_element(avrules, i))) {
+ error = errno;
+ goto err;
+ }
+ }
+ apol_vector_destroy(&avrules);
+
+ teq = apol_terule_query_create();
+ apol_terule_query_set_rules(policy, teq, QPOL_RULE_TYPE_TRANS);
+ apol_terule_query_append_class(policy, teq, "process");
+ if (apol_terule_get_by_query(policy, teq, &terules)) {
+ error = errno;
+ goto err;
+ }
+ apol_terule_query_destroy(&teq);
+ for (size_t i = 0; i < apol_vector_get_size(terules); i++) {
+ if (table_add_terule(policy, dta_table, (const qpol_terule_t *)apol_vector_get_element(terules, i))) {
+ error = errno;
+ goto err;
+ }
+ }
+ apol_vector_destroy(&terules);
+
+ return 0;
+
+ err:
+ apol_avrule_query_destroy(&avq);
+ apol_vector_destroy(&avrules);
+ apol_terule_query_destroy(&teq);
+ apol_vector_destroy(&terules);
+ domain_trans_table_destroy(&dta_table);
+ policy->domain_trans_table = NULL;
+ errno = error;
+ return -1;
+}
+
+int apol_policy_domain_trans_table_build(apol_policy_t * policy)
+{
+ return apol_policy_build_domain_trans_table(policy);
+}
+
+void domain_trans_table_destroy(apol_domain_trans_table_t ** table)
+{
+ if (!table || !(*table))
+ return;
+
+ apol_bst_destroy(&(*table)->domain_table);
+ apol_bst_destroy(&(*table)->entrypoint_table);
+ free(*table);
+ *table = NULL;
+}
+
+void apol_policy_reset_domain_trans_table(apol_policy_t * policy)
+{
+ if (!policy || !policy->domain_trans_table)
+ return;
+ apol_bst_inorder_map(policy->domain_trans_table->domain_table, dom_node_reset, NULL);
+ apol_bst_inorder_map(policy->domain_trans_table->entrypoint_table, ep_node_reset, NULL);
+ return;
+}
+
+void apol_domain_trans_table_reset(apol_policy_t * policy)
+{
+ apol_policy_reset_domain_trans_table(policy);
+}
+
+/* analysis */
+apol_domain_trans_analysis_t *apol_domain_trans_analysis_create(void)
+{
+ apol_domain_trans_analysis_t *new_dta = NULL;
+ int error = 0;
+
+ if (!(new_dta = calloc(1, sizeof(apol_domain_trans_analysis_t)))) {
+ error = errno;
+ goto err;
+ }
+
+ new_dta->valid = APOL_DOMAIN_TRANS_SEARCH_VALID; /* by default search only valid transitions */
+
+ return new_dta;
+
+ err:
+ apol_domain_trans_analysis_destroy(&new_dta);
+ errno = error;
+ return NULL;
+}
+
+void apol_domain_trans_analysis_destroy(apol_domain_trans_analysis_t ** dta)
+{
+ if (!dta || !(*dta))
+ return;
+
+ free((*dta)->start_type);
+ free((*dta)->result);
+ apol_vector_destroy(&((*dta)->access_types));
+ apol_vector_destroy(&((*dta)->access_classes));
+ apol_vector_destroy(&((*dta)->access_perms));
+ apol_regex_destroy(&((*dta)->result_regex));
+ free(*dta);
+ *dta = NULL;
+}
+
+int apol_domain_trans_analysis_set_direction(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ unsigned char direction)
+{
+ if (!dta || (direction != APOL_DOMAIN_TRANS_DIRECTION_FORWARD && direction != APOL_DOMAIN_TRANS_DIRECTION_REVERSE)) {
+ ERR(policy, "Error setting analysis direction: %s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ dta->direction = direction;
+
+ return 0;
+}
+
+int apol_domain_trans_analysis_set_valid(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, unsigned char valid)
+{
+ if (!dta || valid & ~(APOL_DOMAIN_TRANS_SEARCH_BOTH)) {
+ ERR(policy, "Error setting analysis validity flag: %s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ dta->valid = valid;
+
+ return 0;
+}
+
+int apol_domain_trans_analysis_set_start_type(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ const char *type_name)
+{
+ char *tmp = NULL;
+ int error = 0;
+
+ if (!dta || !type_name) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!(tmp = strdup(type_name))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ free(dta->start_type);
+ dta->start_type = tmp;
+
+ return 0;
+}
+
+int apol_domain_trans_analysis_set_result_regex(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, const char *regex)
+{
+ if (!dta) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!regex) {
+ apol_regex_destroy(&dta->result_regex);
+ return 0;
+ }
+
+ return apol_query_set(policy, &dta->result, &dta->result_regex, regex);
+}
+
+int apol_domain_trans_analysis_append_access_type(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ const char *type_name)
+{
+ char *tmp = NULL;
+ int error = 0;
+
+ if (!dta) {
+ ERR(policy, "Error appending type to analysis: %s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!type_name) {
+ apol_vector_destroy(&dta->access_types);
+ return 0;
+ }
+
+ if (!dta->access_types) {
+ if (!(dta->access_types = apol_vector_create(free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ }
+
+ if (!(tmp = strdup(type_name))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ if (apol_vector_append(dta->access_types, tmp)) {
+ error = errno;
+ free(tmp);
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+int apol_domain_trans_analysis_append_class_perm(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ const char *class_name, const char *perm_name)
+{
+ if (apol_domain_trans_analysis_append_class(policy, dta, class_name))
+ return -1;
+ return apol_domain_trans_analysis_append_perm(policy, dta, perm_name);
+}
+
+int apol_domain_trans_analysis_append_class(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ const char *class_name)
+{
+ char *tmp = NULL;
+ int error = 0;
+
+ if (!dta) {
+ ERR(policy, "Error appending class to analysis: %s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!class_name) {
+ apol_vector_destroy(&dta->access_classes);
+ return 0;
+ }
+
+ if (!dta->access_classes) {
+ if (!(dta->access_classes = apol_vector_create(free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ }
+
+ if (!(tmp = strdup(class_name))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ if (apol_vector_append(dta->access_classes, tmp)) {
+ error = errno;
+ free(tmp);
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+int apol_domain_trans_analysis_append_perm(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, const char *perm_name)
+{
+ char *tmp = NULL;
+ int error = 0;
+
+ if (!dta) {
+ ERR(policy, "Error appending perm to analysis: %s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!perm_name) {
+ apol_vector_destroy(&dta->access_perms);
+ return 0;
+ }
+
+ if (!dta->access_perms) {
+ if (!(dta->access_perms = apol_vector_create(free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ }
+
+ if (!(tmp = strdup(perm_name))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ if (apol_vector_append(dta->access_perms, tmp)) {
+ error = errno;
+ free(tmp);
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+static bool requires_setexec_or_type_trans(apol_policy_t * policy)
+{
+ const qpol_policy_t *qp = apol_policy_get_qpol(policy);
+ unsigned int policy_version = 0;
+ qpol_policy_get_policy_version(qp, &policy_version);
+ int is_modular = qpol_policy_has_capability(policy->p, QPOL_CAP_MODULES);
+ return (policy_version >= 15 || is_modular);
+}
+
+struct rule_map_data
+{
+ const qpol_type_t *search;
+ const qpol_type_t *dflt;
+ apol_vector_t *node_list;
+ bool is_avnode;
+};
+
+static int node_list_map_fn(void *node, void *data)
+{
+ struct rule_map_data *rm = data;
+ if (rm->is_avnode) {
+ avrule_node_t *anode = node;
+ if (anode->type == rm->search && !anode->used)
+ if (apol_vector_append(rm->node_list, node))
+ return -1;
+ return 0;
+ } else {
+ terule_node_t *tnode = node;
+ if ((!rm->search || (rm->search == tnode->src)) && (!rm->dflt || (rm->dflt == tnode->dflt)) &&
+ rm->search != rm->dflt && !tnode->used)
+ if (apol_vector_append(rm->node_list, node))
+ return -1;
+ return 0;
+ }
+}
+
+static apol_vector_t *find_avrules_in_node(void *node, unsigned int rule_type, const qpol_type_t * search)
+{
+ int error = 0;
+ apol_vector_t *rule_nodes = apol_vector_create(NULL); //shallow copies only
+ struct rule_map_data data = { search, NULL, rule_nodes, true };
+ switch (rule_type) {
+ case APOL_DOMAIN_TRANS_RULE_PROC_TRANS:
+ {
+ dom_node_t *dnode = node;
+ if (apol_bst_inorder_map(dnode->process_transition_tree, node_list_map_fn, (void *)&data) < 0) {
+ error = errno;
+ goto err;
+ }
+ break;
+ }
+ case APOL_DOMAIN_TRANS_RULE_ENTRYPOINT:
+ {
+ dom_node_t *dnode = node;
+ if (apol_bst_inorder_map(dnode->entrypoint_tree, node_list_map_fn, (void *)&data) < 0) {
+ error = errno;
+ goto err;
+ }
+ break;
+ }
+ case APOL_DOMAIN_TRANS_RULE_EXEC:
+ {
+ ep_node_t *enode = node;
+ if (apol_bst_inorder_map(enode->execute_tree, node_list_map_fn, (void *)&data) < 0) {
+ error = errno;
+ goto err;
+ }
+ break;
+ }
+ default:
+ {
+ error = EINVAL;
+ goto err;
+ }
+ }
+
+ return rule_nodes;
+
+ err:
+ apol_vector_destroy(&rule_nodes);
+ errno = error;
+ return NULL;
+}
+
+static apol_vector_t *find_terules_in_node(ep_node_t * node, const qpol_type_t * search, const qpol_type_t * dflt)
+{
+ int error = 0;
+ apol_vector_t *rule_nodes = apol_vector_create(NULL); //shallow copies only
+ struct rule_map_data data = { search, dflt, rule_nodes, false };
+ if (apol_bst_inorder_map(node->type_transition_tree, node_list_map_fn, (void *)&data) < 0) {
+ error = errno;
+ goto err;
+ }
+
+ return rule_nodes;
+
+ err:
+ apol_vector_destroy(&rule_nodes);
+ errno = error;
+ return NULL;
+}
+
+static apol_domain_trans_result_t *find_result(apol_vector_t * local_results, const qpol_type_t * src, const qpol_type_t * tgt,
+ const qpol_type_t * dflt)
+{
+ for (size_t i = 0; i < apol_vector_get_size(local_results); i++) {
+ apol_domain_trans_result_t *res = apol_vector_get_element(local_results, i);
+ if (res->start_type == src && res->end_type == dflt && res->ep_type == tgt)
+ return res;
+ }
+ return NULL;
+}
+
+static int domain_trans_table_find_orphan_type_transitions(apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ apol_vector_t * local_results)
+{
+ int error = 0;
+ const qpol_type_t *search = NULL;
+ qpol_policy_get_type_by_name(apol_policy_get_qpol(policy), dta->start_type, &search);
+ apol_domain_trans_result_t *tmp_result = NULL;
+ //walk ep table
+ apol_vector_t *epnodes = apol_bst_get_vector(policy->domain_trans_table->entrypoint_table, 0);
+ if (!epnodes)
+ return -1;
+ for (size_t i = 0; i < apol_vector_get_size(epnodes); i++) {
+ ep_node_t *node = apol_vector_get_element(epnodes, i);
+ //find any unused type transitions
+ apol_vector_t *ttnodes = NULL;
+ if (dta->direction == APOL_DOMAIN_TRANS_DIRECTION_FORWARD)
+ ttnodes = find_terules_in_node(node, search, NULL);
+ else
+ ttnodes = find_terules_in_node(node, NULL, search);
+ for (size_t j = 0; j < apol_vector_get_size(ttnodes); j++) {
+ bool add = false;
+ terule_node_t *tn = apol_vector_get_element(ttnodes, j);
+ tn->used = true;
+ //if missing an entrypoint rule this transition may have already been added to the results
+ tmp_result = find_result(local_results, tn->src, node->type, tn->dflt);
+ if (!tmp_result) {
+ add = true;
+ tmp_result = domain_trans_result_create();
+ }
+ if (!tmp_result) {
+ error = errno;
+ apol_vector_destroy(&ttnodes);
+ goto err;
+ }
+ tmp_result->start_type = tn->src;
+ tmp_result->end_type = tn->dflt;
+ tmp_result->ep_type = node->type;
+ //check for exec
+ apol_vector_t *execrules =
+ find_avrules_in_node((void *)node, APOL_DOMAIN_TRANS_RULE_EXEC, tmp_result->start_type);
+ for (size_t k = 0; k < apol_vector_get_size(execrules); k++) {
+ avrule_node_t *n = apol_vector_get_element(execrules, k);
+ if (apol_vector_append(tmp_result->exec_rules, (void *)n->rule)) {
+ error = errno;
+ apol_vector_destroy(&execrules);
+ if (!add)
+ tmp_result = NULL;
+ goto err;
+ }
+ }
+ apol_vector_destroy(&execrules);
+ //check for proc_trans and setexec
+ dom_node_t dummy = { tmp_result->start_type, NULL, NULL, NULL };
+ dom_node_t *start_node = NULL;
+ apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&dummy, NULL, (void **)&start_node);
+ if (start_node) {
+ //only copy setexec_rules if a new result will be added
+ if (add && apol_vector_get_size(start_node->setexec_rules)) {
+ if (apol_vector_cat(tmp_result->setexec_rules, start_node->setexec_rules)) {
+ error = errno;
+ goto err;
+ }
+ }
+ //add any unused proc_trans rules
+ apol_vector_t *proc_trans_rules =
+ find_avrules_in_node((void *)start_node, APOL_DOMAIN_TRANS_RULE_PROC_TRANS,
+ tmp_result->end_type);
+ for (size_t k = 0; k < apol_vector_get_size(proc_trans_rules); k++) {
+ avrule_node_t *avr = apol_vector_get_element(proc_trans_rules, k);
+ if (apol_vector_append(tmp_result->proc_trans_rules, (void *)avr->rule)) {
+ error = errno;
+ if (!add)
+ tmp_result = NULL;
+ apol_vector_destroy(&proc_trans_rules);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&proc_trans_rules);
+ apol_vector_sort_uniquify(tmp_result->proc_trans_rules, NULL, NULL);
+ }
+ if (add) {
+ if (apol_vector_append(local_results, (void *)tmp_result)) {
+ error = errno;
+ goto err;
+ }
+ }
+ tmp_result = NULL;
+ }
+ apol_vector_destroy(&ttnodes);
+ }
+ apol_vector_destroy(&epnodes);
+
+ return 0;
+
+ err:
+ apol_vector_destroy(&epnodes);
+ apol_domain_trans_result_destroy(&tmp_result);
+ errno = error;
+ return -1;
+}
+
+static int domain_trans_table_get_all_forward_trans(apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ apol_vector_t * local_results, const qpol_type_t * start_type)
+{
+ int error = 0;
+ //create template result this will hold common data for each step and be copied as needed
+ apol_domain_trans_result_t *tmpl_result = domain_trans_result_create();
+ if (!tmpl_result) {
+ error = errno;
+ goto err;
+ }
+ //find start node
+ dom_node_t dummy = { start_type, NULL, NULL, NULL };
+ dom_node_t *start_node = NULL;
+ apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&dummy, NULL, (void **)&start_node);
+ if (start_node) {
+ tmpl_result->start_type = start_type;
+ //if needed and present record setexec
+ if (requires_setexec_or_type_trans(policy) && apol_vector_get_size(start_node->setexec_rules)) {
+ if (apol_vector_cat(tmpl_result->setexec_rules, start_node->setexec_rules)) {
+ error = errno;
+ goto err;
+ }
+ }
+ //check all proc trans to build list of end types
+ apol_vector_t *proc_trans_rules = apol_bst_get_vector(start_node->process_transition_tree, 0);
+ apol_vector_t *potential_end_types = apol_vector_create(NULL);
+ for (size_t i = 0; i < apol_vector_get_size(proc_trans_rules); i++) {
+ avrule_node_t *ptnode = apol_vector_get_element(proc_trans_rules, i);
+ apol_vector_append(potential_end_types, (void *)ptnode->type);
+ }
+ apol_vector_destroy(&proc_trans_rules);
+ apol_vector_sort_uniquify(potential_end_types, NULL, NULL);
+ //for each end check ep
+ for (size_t i = 0; i < apol_vector_get_size(potential_end_types); i++) {
+ dummy.type = tmpl_result->end_type = apol_vector_get_element(potential_end_types, i);
+ dom_node_t *end_node = NULL;
+ apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&dummy, NULL, (void **)&end_node);
+ const qpol_type_t *end_type = dummy.type;
+ if (end_type == start_type)
+ continue;
+ //get all proc trans rules for ths end (may be multiple due to attributes)
+ apol_vector_t *ptrules =
+ find_avrules_in_node((void *)start_node, APOL_DOMAIN_TRANS_RULE_PROC_TRANS, end_type);
+ apol_vector_destroy(&tmpl_result->proc_trans_rules);
+ tmpl_result->proc_trans_rules = apol_vector_create(NULL);
+ for (size_t j = 0; j < apol_vector_get_size(ptrules); j++) {
+ avrule_node_t *pt_ent = apol_vector_get_element(ptrules, j);
+ pt_ent->used = true;
+ if (apol_vector_append(tmpl_result->proc_trans_rules, (void *)pt_ent->rule)) {
+ error = errno;
+ apol_vector_destroy(&ptrules);
+ apol_vector_destroy(&potential_end_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&ptrules);
+ apol_vector_sort_uniquify(tmpl_result->proc_trans_rules, NULL, NULL);
+ if (end_node) {
+ //collect potential entrypoint types
+ apol_vector_t *eprules = apol_bst_get_vector(end_node->entrypoint_tree, 0);
+ apol_vector_t *potential_ep_types = apol_vector_create(NULL);
+ if (!eprules || !potential_ep_types) {
+ error = errno;
+ apol_vector_destroy(&eprules);
+ apol_vector_destroy(&potential_end_types);
+ goto err;
+ }
+ for (size_t j = 0; j < apol_vector_get_size(eprules); j++) {
+ avrule_node_t *epr = apol_vector_get_element(eprules, j);
+ if (apol_vector_append(potential_ep_types, (void *)epr->type)) {
+ error = errno;
+ apol_vector_destroy(&eprules);
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&eprules);
+ apol_vector_sort_uniquify(potential_ep_types, NULL, NULL);
+ //for each ep find exec by start
+ for (size_t j = 0; j < apol_vector_get_size(potential_ep_types); j++) {
+ tmpl_result->ep_type = apol_vector_get_element(potential_ep_types, j);
+ ep_node_t edummy =
+ { (const qpol_type_t *)apol_vector_get_element(potential_ep_types, j), NULL, NULL };
+ ep_node_t *epnode = NULL;
+ apol_bst_get_element(policy->domain_trans_table->entrypoint_table, (void *)&edummy, NULL,
+ (void **)&epnode);
+ //get all entrypoint rules for ths end (may be multiple due to attributes)
+ apol_vector_destroy(&tmpl_result->ep_rules);
+ tmpl_result->ep_rules = apol_vector_create(NULL);
+ if (!tmpl_result->ep_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ eprules = find_avrules_in_node((void *)end_node, APOL_DOMAIN_TRANS_RULE_ENTRYPOINT,
+ tmpl_result->ep_type);
+ for (size_t k = 0; k < apol_vector_get_size(eprules); k++) {
+ avrule_node_t *ep_ent = apol_vector_get_element(eprules, k);
+ ep_ent->used = true;
+ if (apol_vector_append(tmpl_result->ep_rules, (void *)ep_ent->rule)) {
+ error = errno;
+ apol_vector_destroy(&eprules);
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&eprules);
+ apol_vector_sort_uniquify(tmpl_result->ep_rules, NULL, NULL);
+ if (epnode) {
+ //if present find tt
+ apol_vector_destroy(&tmpl_result->type_trans_rules);
+ tmpl_result->type_trans_rules = apol_vector_create(NULL);
+ if (!tmpl_result->type_trans_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ apol_vector_t *ttrules = find_terules_in_node(epnode, start_type, end_type);
+ for (size_t l = 0; l < apol_vector_get_size(ttrules); l++) {
+ terule_node_t *tn = apol_vector_get_element(ttrules, l);
+ if (apol_vector_append(tmpl_result->type_trans_rules, (void *)tn->rule)) {
+ error = errno;
+ apol_vector_destroy(&ttrules);
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&ttrules);
+ apol_vector_sort_uniquify(tmpl_result->type_trans_rules, NULL, NULL);
+ //find execute rules
+ apol_vector_destroy(&tmpl_result->exec_rules);
+ tmpl_result->exec_rules = apol_vector_create(NULL);
+ if (!tmpl_result->exec_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ apol_vector_t *execrules =
+ find_avrules_in_node(epnode, APOL_DOMAIN_TRANS_RULE_EXEC, start_type);
+ if (apol_vector_get_size(execrules)) {
+ for (size_t l = 0; l < apol_vector_get_size(execrules); l++) {
+ avrule_node_t *xnode = apol_vector_get_element(execrules, l);
+ //do not mark xnode as used here; it is valid to re-use it.
+ if (apol_vector_append
+ (tmpl_result->exec_rules, (void *)xnode->rule)) {
+ error = errno;
+ apol_vector_destroy(&execrules);
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&execrules);
+ apol_vector_sort_uniquify(tmpl_result->exec_rules, NULL, NULL);
+ //found everything possible add a result
+ apol_domain_trans_result_t *tmp =
+ apol_domain_trans_result_create_from_domain_trans_result
+ (tmpl_result);
+ if (!tmp || apol_vector_append(local_results, (void *)tmp)) {
+ error = errno;
+ apol_domain_trans_result_destroy(&tmp);
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ //reset execute rules
+ apol_vector_destroy(&tmpl_result->exec_rules);
+ tmpl_result->exec_rules = apol_vector_create(NULL);
+ if (!tmpl_result->exec_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ //reset type transition rules
+ apol_vector_destroy(&tmpl_result->type_trans_rules);
+ tmpl_result->type_trans_rules = apol_vector_create(NULL);
+ if (!tmpl_result->type_trans_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ } else {
+ //have proc_trans and entrypoint but no execute
+ apol_domain_trans_result_t *tmp =
+ apol_domain_trans_result_create_from_domain_trans_result
+ (tmpl_result);
+ if (!tmp || apol_vector_append(local_results, (void *)tmp)) {
+ error = errno;
+ apol_domain_trans_result_destroy(&tmp);
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&execrules);
+ } else {
+ //have proc_trans and entrypoint but no execute
+ apol_domain_trans_result_t *tmp =
+ apol_domain_trans_result_create_from_domain_trans_result(tmpl_result);
+ if (!tmp || apol_vector_append(local_results, (void *)tmp)) {
+ error = errno;
+ apol_domain_trans_result_destroy(&tmp);
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ //reset entrypoint rules
+ apol_vector_destroy(&tmpl_result->ep_rules);
+ tmpl_result->ep_rules = apol_vector_create(NULL);
+ if (!tmpl_result->ep_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&potential_ep_types);
+ } else {
+ //have proc_trans but end has no ep
+ apol_domain_trans_result_t *tmp =
+ apol_domain_trans_result_create_from_domain_trans_result(tmpl_result);
+ if (!tmp || apol_vector_append(local_results, (void *)tmp)) {
+ error = errno;
+ apol_domain_trans_result_destroy(&tmp);
+ goto err;
+ }
+ }
+ }
+ apol_vector_destroy(&potential_end_types);
+ //validate all
+ for (size_t i = 0; i < apol_vector_get_size(local_results); i++) {
+ apol_domain_trans_result_t *res = apol_vector_get_element(local_results, i);
+ if (res->start_type && res->ep_type && res->end_type && apol_vector_get_size(res->proc_trans_rules) &&
+ apol_vector_get_size(res->ep_rules) && apol_vector_get_size(res->exec_rules) &&
+ (requires_setexec_or_type_trans(policy)
+ ? (apol_vector_get_size(res->setexec_rules) || apol_vector_get_size(res->type_trans_rules)) : true)) {
+ res->valid = true;
+ }
+ }
+ }
+ //iff looking for invalid find orphan type_transition rules
+ if (dta->valid & APOL_DOMAIN_TRANS_SEARCH_INVALID) {
+ if (domain_trans_table_find_orphan_type_transitions(policy, dta, local_results)) {
+ error = errno;
+ goto err;
+ }
+ }
+ apol_domain_trans_result_destroy(&tmpl_result);
+
+ return 0;
+ err:
+ apol_domain_trans_result_destroy(&tmpl_result);
+ errno = error;
+ return -1;
+}
+
+static int domain_trans_table_get_all_reverse_trans(apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ apol_vector_t * local_results, const qpol_type_t * end_type)
+{
+ int error = 0;
+ //create template result this will hold common data for each step and be copied as needed
+ apol_domain_trans_result_t *tmpl_result = domain_trans_result_create();
+ if (!tmpl_result) {
+ error = errno;
+ goto err;
+ }
+ //find end node
+ dom_node_t dummy = { end_type, NULL, NULL, NULL };
+ dom_node_t *end_node = NULL;
+ apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&dummy, NULL, (void **)&end_node);
+ if (end_node) {
+ tmpl_result->end_type = end_type;
+ //collect potential entrypoint types
+ apol_vector_t *eprules = apol_bst_get_vector(end_node->entrypoint_tree, 0);
+ apol_vector_t *potential_ep_types = apol_vector_create(NULL);
+ if (!eprules || !potential_ep_types) {
+ error = errno;
+ apol_vector_destroy(&eprules);
+ goto err;
+ }
+ for (size_t j = 0; j < apol_vector_get_size(eprules); j++) {
+ avrule_node_t *epr = apol_vector_get_element(eprules, j);
+ if (apol_vector_append(potential_ep_types, (void *)epr->type)) {
+ error = errno;
+ apol_vector_destroy(&eprules);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&eprules);
+ apol_vector_sort_uniquify(potential_ep_types, NULL, NULL);
+ for (size_t i = 0; i < apol_vector_get_size(potential_ep_types); i++) {
+ tmpl_result->ep_type = apol_vector_get_element(potential_ep_types, i);
+ //get all ep rules for this end (may be multiple due to attributes)
+ eprules = find_avrules_in_node((void *)end_node, APOL_DOMAIN_TRANS_RULE_ENTRYPOINT, tmpl_result->ep_type);
+ apol_vector_destroy(&tmpl_result->ep_rules);
+ tmpl_result->ep_rules = apol_vector_create(NULL);
+ for (size_t j = 0; j < apol_vector_get_size(eprules); j++) {
+ avrule_node_t *ep_ent = apol_vector_get_element(eprules, j);
+ ep_ent->used = true;
+ if (apol_vector_append(tmpl_result->ep_rules, (void *)ep_ent->rule)) {
+ error = errno;
+ apol_vector_destroy(&eprules);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&eprules);
+ apol_vector_sort_uniquify(tmpl_result->ep_rules, NULL, NULL);
+ ep_node_t edummy = { tmpl_result->ep_type, NULL, NULL };
+ ep_node_t *epnode = NULL;
+ apol_bst_get_element(policy->domain_trans_table->entrypoint_table, (void *)&edummy, NULL, (void **)&epnode);
+ //for each ep find exec rules to generate list of potential start types
+ if (epnode) {
+ apol_vector_t *execrules = apol_bst_get_vector(epnode->execute_tree, 0);
+ apol_vector_t *potential_start_types = apol_vector_create(NULL);
+ if (!execrules || !potential_start_types) {
+ error = errno;
+ apol_vector_destroy(&execrules);
+ goto err;
+ }
+ for (size_t k = 0; k < apol_vector_get_size(execrules); k++) {
+ avrule_node_t *n = apol_vector_get_element(execrules, k);
+ if (apol_vector_append(potential_start_types, (void *)n->type)) {
+ error = errno;
+ apol_vector_destroy(&execrules);
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&execrules);
+ apol_vector_sort_uniquify(potential_start_types, NULL, NULL);
+ for (size_t k = 0; k < apol_vector_get_size(potential_start_types); k++) {
+ tmpl_result->start_type = apol_vector_get_element(potential_start_types, k);
+ //no transition to self
+ if (tmpl_result->end_type == tmpl_result->start_type)
+ continue;
+ //get all execute rule for this start type
+ apol_vector_t *exec_rules =
+ find_avrules_in_node((void *)epnode, APOL_DOMAIN_TRANS_RULE_EXEC,
+ tmpl_result->start_type);
+ apol_vector_destroy(&tmpl_result->exec_rules);
+ tmpl_result->exec_rules = apol_vector_create(NULL);
+ for (size_t l = 0; l < apol_vector_get_size(exec_rules); l++) {
+ avrule_node_t *n = apol_vector_get_element(exec_rules, l);
+ n->used = true;
+ if (apol_vector_append(tmpl_result->exec_rules, (void *)n->rule)) {
+ error = errno;
+ apol_vector_destroy(&exec_rules);
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&exec_rules);
+ apol_vector_sort_uniquify(tmpl_result->exec_rules, NULL, NULL);
+ //check for type transition rules
+ apol_vector_t *ttrules =
+ find_terules_in_node(epnode, tmpl_result->start_type, tmpl_result->end_type);
+ apol_vector_destroy(&tmpl_result->type_trans_rules);
+ tmpl_result->type_trans_rules = apol_vector_create(NULL);
+ if (!tmpl_result->type_trans_rules) {
+ error = errno;
+ apol_vector_destroy(&ttrules);
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ for (size_t l = 0; l < apol_vector_get_size(ttrules); l++) {
+ terule_node_t *n = apol_vector_get_element(ttrules, l);
+ n->used = true;
+ if (apol_vector_append(tmpl_result->type_trans_rules, (void *)n->rule)) {
+ error = errno;
+ apol_vector_destroy(&ttrules);
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&ttrules);
+ apol_vector_sort_uniquify(tmpl_result->type_trans_rules, NULL, NULL);
+ dummy.type = tmpl_result->start_type;
+ dom_node_t *start_node = NULL;
+ apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&dummy, NULL,
+ (void **)&start_node);
+ if (start_node) {
+ //for each start check setexec if needed
+ if (requires_setexec_or_type_trans(policy)) {
+ apol_vector_destroy(&tmpl_result->setexec_rules);
+ tmpl_result->setexec_rules = apol_vector_create(NULL);
+ if (!tmpl_result->setexec_rules ||
+ apol_vector_cat(tmpl_result->setexec_rules,
+ start_node->setexec_rules)) {
+ error = errno;
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ //for each start find pt
+ apol_vector_destroy(&tmpl_result->proc_trans_rules);
+ tmpl_result->proc_trans_rules = apol_vector_create(NULL);
+ if (!tmpl_result->proc_trans_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ apol_vector_t *pt_rules = NULL;
+ pt_rules =
+ find_avrules_in_node(start_node, APOL_DOMAIN_TRANS_RULE_PROC_TRANS,
+ tmpl_result->end_type);
+ if (apol_vector_get_size(pt_rules)) {
+ for (size_t l = 0; l < apol_vector_get_size(pt_rules); l++) {
+ avrule_node_t *n = apol_vector_get_element(pt_rules, l);
+ apol_vector_append(tmpl_result->proc_trans_rules, (void *)n->rule);
+ }
+ apol_vector_destroy(&pt_rules);
+ apol_vector_sort_uniquify(tmpl_result->proc_trans_rules, NULL, NULL);
+ // have all possible rules add this entry
+ apol_domain_trans_result_t *tmp =
+ apol_domain_trans_result_create_from_domain_trans_result
+ (tmpl_result);
+ if (!tmp || apol_vector_append(local_results, (void *)tmp)) {
+ error = errno;
+ apol_domain_trans_result_destroy(&tmp);
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ //reset process transition rules
+ apol_vector_destroy(&tmpl_result->proc_trans_rules);
+ tmpl_result->proc_trans_rules = apol_vector_create(NULL);
+ if (!tmpl_result->proc_trans_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ //reset setexec rules
+ apol_vector_destroy(&tmpl_result->setexec_rules);
+ tmpl_result->setexec_rules = apol_vector_create(NULL);
+ if (!tmpl_result->setexec_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ } else {
+ //have entrypoint and execute rules but no process transition rule
+ apol_domain_trans_result_t *tmp =
+ apol_domain_trans_result_create_from_domain_trans_result
+ (tmpl_result);
+ if (!tmp || apol_vector_append(local_results, (void *)tmp)) {
+ error = errno;
+ apol_domain_trans_result_destroy(&tmp);
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ apol_vector_destroy(&pt_rules);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&pt_rules);
+ } else {
+ //have entrypoint and execute rules but no process transition rule
+ apol_domain_trans_result_t *tmp =
+ apol_domain_trans_result_create_from_domain_trans_result(tmpl_result);
+ if (!tmp || apol_vector_append(local_results, (void *)tmp)) {
+ error = errno;
+ apol_domain_trans_result_destroy(&tmp);
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ //reset execute rules
+ apol_vector_destroy(&tmpl_result->exec_rules);
+ tmpl_result->exec_rules = apol_vector_create(NULL);
+ if (!tmpl_result->exec_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ //reset type transition rules
+ apol_vector_destroy(&tmpl_result->type_trans_rules);
+ tmpl_result->type_trans_rules = apol_vector_create(NULL);
+ if (!tmpl_result->type_trans_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&potential_start_types);
+ } else {
+ //have entrypoint but no exec
+ apol_domain_trans_result_t *tmp =
+ apol_domain_trans_result_create_from_domain_trans_result(tmpl_result);
+ if (!tmp || apol_vector_append(local_results, (void *)tmp)) {
+ error = errno;
+ apol_domain_trans_result_destroy(&tmp);
+ goto err;
+ }
+ }
+ }
+ apol_vector_destroy(&potential_ep_types);
+
+ //validate all
+ for (size_t i = 0; i < apol_vector_get_size(local_results); i++) {
+ apol_domain_trans_result_t *res = apol_vector_get_element(local_results, i);
+ if (res->start_type && res->ep_type && res->end_type && apol_vector_get_size(res->proc_trans_rules) &&
+ apol_vector_get_size(res->ep_rules) && apol_vector_get_size(res->exec_rules) &&
+ (requires_setexec_or_type_trans(policy)
+ ? (apol_vector_get_size(res->setexec_rules) || apol_vector_get_size(res->type_trans_rules)) : true)) {
+ res->valid = true;
+ }
+ }
+ }
+ //iff looking for invalid find orphan type_transition rules
+ if (dta->valid & APOL_DOMAIN_TRANS_SEARCH_INVALID) {
+ if (domain_trans_table_find_orphan_type_transitions(policy, dta, local_results)) {
+ error = errno;
+ goto err;
+ }
+ }
+
+ apol_domain_trans_result_destroy(&tmpl_result);
+ return 0;
+
+ err:
+ apol_domain_trans_result_destroy(&tmpl_result);
+ errno = error;
+ return -1;
+}
+
+int apol_domain_trans_analysis_do(apol_policy_t * policy, apol_domain_trans_analysis_t * dta, apol_vector_t ** results)
+{
+ apol_vector_t *local_results = NULL;
+ apol_avrule_query_t *accessq = NULL;
+ int error = 0;
+ if (!results)
+ *results = NULL;
+ if (!policy || !dta || !results) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* build table if not already present */
+ if (!(policy->domain_trans_table)) {
+ if (apol_policy_build_domain_trans_table(policy))
+ return -1; /* errors already reported by build function */
+ }
+
+ /* validate analysis options */
+ if (dta->direction == 0 || dta->valid & ~(APOL_DOMAIN_TRANS_SEARCH_BOTH) || !(dta->start_type)) {
+ error = EINVAL;
+ ERR(policy, "%s", strerror(EINVAL));
+ goto err;
+ }
+ size_t num_atypes = apol_vector_get_size(dta->access_types);
+ size_t num_aclasses = apol_vector_get_size(dta->access_classes);
+ size_t num_aprems = apol_vector_get_size(dta->access_perms);
+ if ((num_atypes == 0 && (num_aclasses != 0 || num_aprems != 0)) ||
+ (num_aclasses == 0 && (num_atypes != 0 || num_aprems != 0)) ||
+ (num_aprems == 0 && (num_aclasses != 0 || num_atypes != 0))) {
+ error = EINVAL;
+ ERR(policy, "%s", strerror(EINVAL));
+ goto err;
+ }
+
+ /* get starting type */
+ const qpol_type_t *start_type = NULL;
+ if (qpol_policy_get_type_by_name(policy->p, dta->start_type, &start_type)) {
+ error = errno;
+ ERR(policy, "Unable to perform analysis: Invalid starting type %s", dta->start_type);
+ goto err;
+ }
+ unsigned char isattr = 0;
+ qpol_type_get_isattr(policy->p, start_type, &isattr);
+ if (isattr) {
+ ERR(policy, "%s", "Attributes are not valid here.");
+ error = EINVAL;
+ goto err;
+ }
+
+ local_results = apol_vector_create(domain_trans_result_free);
+ /* get all transitions for the requested direction */
+ if (dta->direction == APOL_DOMAIN_TRANS_DIRECTION_REVERSE) {
+ if (domain_trans_table_get_all_reverse_trans(policy, dta, local_results, start_type)) {
+ error = errno;
+ goto err;
+ }
+ } else {
+ if (domain_trans_table_get_all_forward_trans(policy, dta, local_results, start_type)) {
+ error = errno;
+ goto err;
+ }
+ }
+
+ /* if requested, filter by validity */
+ if (dta->valid != APOL_DOMAIN_TRANS_SEARCH_BOTH) {
+ for (size_t i = 0; i < apol_vector_get_size(local_results); /* increment later */ ) {
+ apol_domain_trans_result_t *res = apol_vector_get_element(local_results, i);
+ if (res->valid != (dta->valid == APOL_DOMAIN_TRANS_SEARCH_VALID)) {
+ apol_vector_remove(local_results, i);
+ domain_trans_result_free(res);
+ } else {
+ i++;
+ }
+ }
+ }
+
+ /* if filtering by result type, do that now */
+ if (dta->result) {
+ for (size_t i = 0; i < apol_vector_get_size(local_results); /* increment later */ ) {
+ apol_domain_trans_result_t *res = apol_vector_get_element(local_results, i);
+ const qpol_type_t *type = NULL;
+ if (dta->direction == APOL_DOMAIN_TRANS_DIRECTION_REVERSE) {
+ type = res->start_type;
+ } else {
+ type = res->end_type;
+ }
+ int compval = apol_compare_type(policy, type, dta->result, APOL_QUERY_REGEX, &dta->result_regex);
+ if (compval < 0) {
+ error = errno;
+ goto err;
+ } else if (compval > 0) {
+ i++;
+ } else {
+ apol_vector_remove(local_results, i);
+ domain_trans_result_free(res);
+ }
+ }
+ }
+
+ /* finally do access filtering */
+ if (dta->direction == APOL_DOMAIN_TRANS_DIRECTION_FORWARD && num_atypes && num_aclasses && num_aprems) {
+ accessq = apol_avrule_query_create();
+ apol_avrule_query_set_rules(policy, accessq, QPOL_RULE_ALLOW);
+ for (size_t i = 0; i < num_aclasses; i++) {
+ if (apol_avrule_query_append_class
+ (policy, accessq, (char *)apol_vector_get_element(dta->access_classes, i))) {
+ error = errno;
+ goto err;
+ }
+ }
+ for (size_t i = 0; i < num_aprems; i++) {
+ if (apol_avrule_query_append_perm(policy, accessq, (char *)apol_vector_get_element(dta->access_perms, i))) {
+ error = errno;
+ goto err;
+ }
+ }
+ for (size_t i = 0; i < apol_vector_get_size(local_results); /* increment later */ ) {
+ const char *end_name = NULL;
+ apol_domain_trans_result_t *res = apol_vector_get_element(local_results, i);
+ if (qpol_type_get_name(apol_policy_get_qpol(policy), res->end_type, &end_name) ||
+ apol_avrule_query_set_source(policy, accessq, end_name, 1)) {
+ error = errno;
+ goto err;
+ }
+ apol_vector_t *tmp_access = apol_vector_create(NULL);
+ for (size_t j = 0; j < num_atypes; j++) {
+ if (apol_avrule_query_set_target
+ (policy, accessq, (char *)apol_vector_get_element(dta->access_types, j), 1)) {
+ error = errno;
+ apol_vector_destroy(&tmp_access);
+ goto err;
+ }
+ apol_vector_t *cur_tgt_v = NULL;
+ apol_avrule_get_by_query(policy, accessq, &cur_tgt_v);
+ apol_vector_cat(tmp_access, cur_tgt_v);
+ apol_vector_destroy(&cur_tgt_v);
+ }
+ if (apol_vector_get_size(tmp_access)) {
+ res->access_rules = tmp_access;
+ tmp_access = NULL;
+ i++;
+ } else {
+ apol_vector_remove(local_results, i);
+ domain_trans_result_free(res);
+ }
+ apol_vector_destroy(&tmp_access);
+ }
+ apol_avrule_query_destroy(&accessq);
+ }
+
+ *results = apol_vector_create(domain_trans_result_free);
+ if (!(*results)) {
+ error = errno;
+ goto err;
+ }
+ for (size_t i = 0; i < apol_vector_get_size(local_results); i++) {
+ apol_domain_trans_result_t *res =
+ apol_domain_trans_result_create_from_domain_trans_result((apol_domain_trans_result_t *)
+ apol_vector_get_element(local_results, i));
+ if (!res || apol_vector_append(*results, (void *)res)) {
+ error = errno;
+ domain_trans_result_free(res);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&local_results);
+
+ return 0;
+ err:
+ apol_vector_destroy(&local_results);
+ apol_vector_destroy(results);
+ apol_avrule_query_destroy(&accessq);
+ errno = error;
+ return -1;
+}
+
+/* result */
+
+const qpol_type_t *apol_domain_trans_result_get_start_type(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->start_type;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+const qpol_type_t *apol_domain_trans_result_get_entrypoint_type(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->ep_type;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+const qpol_type_t *apol_domain_trans_result_get_end_type(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->end_type;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+const apol_vector_t *apol_domain_trans_result_get_proc_trans_rules(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->proc_trans_rules;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+const apol_vector_t *apol_domain_trans_result_get_entrypoint_rules(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->ep_rules;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+const apol_vector_t *apol_domain_trans_result_get_exec_rules(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->exec_rules;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+const apol_vector_t *apol_domain_trans_result_get_setexec_rules(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->setexec_rules;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+const apol_vector_t *apol_domain_trans_result_get_type_trans_rules(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->type_trans_rules;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+int apol_domain_trans_result_is_trans_valid(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->valid;
+ } else {
+ errno = EINVAL;
+ return 0;
+ }
+}
+
+const apol_vector_t *apol_domain_trans_result_get_access_rules(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->access_rules;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+int apol_domain_trans_table_verify_trans(apol_policy_t * policy, const qpol_type_t * start_dom, const qpol_type_t * ep_type,
+ const qpol_type_t * end_dom)
+{
+ int missing_rules = 0;
+
+ if (!policy || !policy->domain_trans_table) {
+ errno = EINVAL;
+ return -1;
+ }
+ //reset the table
+ apol_policy_reset_domain_trans_table(policy);
+ //find nodes for each type
+ dom_node_t start_dummy = { start_dom, NULL, NULL, NULL };
+ dom_node_t *start_node = NULL;
+ if (start_dom)
+ apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&start_dummy, NULL, (void **)&start_node);
+ ep_node_t ep_dummy = { ep_type, NULL, NULL };
+ ep_node_t *ep_node = NULL;
+ if (ep_type)
+ apol_bst_get_element(policy->domain_trans_table->entrypoint_table, (void *)&ep_dummy, NULL, (void **)&ep_node);
+ dom_node_t end_dummy = { end_dom, NULL, NULL, NULL };
+ dom_node_t *end_node = NULL;
+ if (end_dom)
+ apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&end_dummy, NULL, (void **)&end_node);
+
+ bool tt = false, sx = false, ex = false, pt = false, ep = false;
+
+ //find process transition rule
+ if (start_node && end_dom) {
+ apol_vector_t *v = find_avrules_in_node(start_node, APOL_DOMAIN_TRANS_RULE_PROC_TRANS, end_dom);
+ if (apol_vector_get_size(v))
+ pt = true;
+ apol_vector_destroy(&v);
+ }
+ //find execute rule
+ if (start_dom && ep_node) {
+ apol_vector_t *v = find_avrules_in_node(ep_node, APOL_DOMAIN_TRANS_RULE_EXEC, start_dom);
+ if (apol_vector_get_size(v))
+ ex = true;
+ apol_vector_destroy(&v);
+ }
+ //find entrypoint rules
+ if (end_node && ep_type) {
+ apol_vector_t *v = find_avrules_in_node(end_node, APOL_DOMAIN_TRANS_RULE_ENTRYPOINT, ep_type);
+ if (apol_vector_get_size(v))
+ ep = true;
+ apol_vector_destroy(&v);
+ }
+ if (requires_setexec_or_type_trans(policy)) {
+ //find setexec rule
+ if (start_node)
+ if (apol_vector_get_size(start_node->setexec_rules))
+ sx = true;
+ //find type_transition rule
+ if (ep_node && start_dom && end_dom) {
+ apol_vector_t *v = find_terules_in_node(ep_node, start_dom, end_dom);
+ if (apol_vector_get_size(v)) {
+ tt = true;
+ }
+ apol_vector_destroy(&v);
+ }
+ } else {
+ //old policy version - pretend these exist
+ tt = sx = true;
+ }
+
+ if (!(pt && ep && ex && (tt || sx))) {
+ if (!pt)
+ missing_rules |= APOL_DOMAIN_TRANS_RULE_PROC_TRANS;
+ if (!ep)
+ missing_rules |= APOL_DOMAIN_TRANS_RULE_ENTRYPOINT;
+ if (!ex)
+ missing_rules |= APOL_DOMAIN_TRANS_RULE_EXEC;
+ if (!tt && !sx) {
+ missing_rules |= APOL_DOMAIN_TRANS_RULE_SETEXEC;
+ //do not report type_transition as missing if there is one for another entrypoint as this would be invalid
+ const char *start_name = NULL, *end_name = NULL;
+ qpol_type_get_name(apol_policy_get_qpol(policy), start_dom, &start_name);
+ qpol_type_get_name(apol_policy_get_qpol(policy), end_dom, &end_name);
+ apol_terule_query_t *tq = NULL;
+ if (!start_name || !end_name || !(tq = apol_terule_query_create())) {
+ return -1;
+ }
+ apol_terule_query_set_rules(policy, tq, QPOL_RULE_TYPE_TRANS);
+ apol_terule_query_set_source(policy, tq, start_name, 1);
+ apol_terule_query_set_default(policy, tq, end_name);
+ apol_vector_t *v = NULL;
+ if (apol_terule_get_by_query(policy, tq, &v)) {
+ apol_terule_query_destroy(&tq);
+ return -1;
+ }
+ apol_terule_query_destroy(&tq);
+ if (!apol_vector_get_size(v))
+ missing_rules |= APOL_DOMAIN_TRANS_RULE_TYPE_TRANS;
+ apol_vector_destroy(&v);
+ }
+ }
+
+ return missing_rules;
+}
+
+apol_domain_trans_result_t *apol_domain_trans_result_create_from_domain_trans_result(const apol_domain_trans_result_t * result)
+{
+ apol_domain_trans_result_t *new_r = NULL;
+ int retval = -1;
+ if ((new_r = calloc(1, sizeof(*new_r))) == NULL) {
+ goto cleanup;
+ }
+ if (result->proc_trans_rules != NULL &&
+ (new_r->proc_trans_rules = apol_vector_create_from_vector(result->proc_trans_rules, NULL, NULL, NULL)) == NULL) {
+ goto cleanup;
+ }
+ if (result->ep_rules != NULL
+ && (new_r->ep_rules = apol_vector_create_from_vector(result->ep_rules, NULL, NULL, NULL)) == NULL) {
+ goto cleanup;
+ }
+ if (result->exec_rules != NULL
+ && (new_r->exec_rules = apol_vector_create_from_vector(result->exec_rules, NULL, NULL, NULL)) == NULL) {
+ goto cleanup;
+ }
+ if (result->setexec_rules != NULL
+ && (new_r->setexec_rules = apol_vector_create_from_vector(result->setexec_rules, NULL, NULL, NULL)) == NULL) {
+ goto cleanup;
+ }
+ if (result->type_trans_rules != NULL &&
+ (new_r->type_trans_rules = apol_vector_create_from_vector(result->type_trans_rules, NULL, NULL, NULL)) == NULL) {
+ goto cleanup;
+ }
+ if (result->access_rules != NULL
+ && (new_r->access_rules = apol_vector_create_from_vector(result->access_rules, NULL, NULL, NULL)) == NULL) {
+ goto cleanup;
+ }
+ new_r->start_type = result->start_type;
+ new_r->ep_type = result->ep_type;
+ new_r->end_type = result->end_type;
+ new_r->valid = result->valid;
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ domain_trans_result_free(new_r);
+ return NULL;
+ }
+ return new_r;
+}
+
+/******************** protected functions ********************/
+
+void domain_trans_result_free(void *dtr)
+{
+ apol_domain_trans_result_t *res = (apol_domain_trans_result_t *) dtr;
+
+ if (!res)
+ return;
+
+ apol_vector_destroy(&res->proc_trans_rules);
+ apol_vector_destroy(&res->ep_rules);
+ apol_vector_destroy(&res->exec_rules);
+ apol_vector_destroy(&res->setexec_rules);
+ apol_vector_destroy(&res->type_trans_rules);
+ apol_vector_destroy(&res->access_rules);
+ free(res);
+}
+
+void apol_domain_trans_result_destroy(apol_domain_trans_result_t ** res)
+{
+ if (!res || !(*res))
+ return;
+ domain_trans_result_free((void *)*res);
+ *res = NULL;
+}