summaryrefslogtreecommitdiffstats
path: root/libapol/src/relabel-analysis.c
diff options
context:
space:
mode:
Diffstat (limited to 'libapol/src/relabel-analysis.c')
-rw-r--r--libapol/src/relabel-analysis.c813
1 files changed, 813 insertions, 0 deletions
diff --git a/libapol/src/relabel-analysis.c b/libapol/src/relabel-analysis.c
new file mode 100644
index 0000000..d1cab99
--- /dev/null
+++ b/libapol/src/relabel-analysis.c
@@ -0,0 +1,813 @@
+/**
+ * @file
+ * Implementation of the direct relabelling 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 <errno.h>
+#include <string.h>
+
+/* defines for mode */
+#define APOL_RELABEL_MODE_OBJ 0x01
+#define APOL_RELABEL_MODE_SUBJ 0x02
+
+struct apol_relabel_analysis
+{
+ unsigned int mode, direction;
+ char *type, *result;
+ apol_vector_t *classes, *subjects;
+ regex_t *result_regex;
+};
+
+/**
+ * Results are in the form of a list of apol_relabel_result_t nodes.
+ * Each node has three sublists of apol_relabel_result_pair_t.
+ */
+struct apol_relabel_result
+{
+ apol_vector_t *to;
+ apol_vector_t *from;
+ apol_vector_t *both;
+ const qpol_type_t *type;
+};
+
+struct apol_relabel_result_pair
+{
+ const qpol_avrule_t *ruleA, *ruleB;
+ const qpol_type_t *intermed;
+};
+
+#define PERM_RELABELTO "relabelto"
+#define PERM_RELABELFROM "relabelfrom"
+
+/******************** actual analysis rountines ********************/
+
+/**
+ * Given an avrule, determine which relabel direction it has (to,
+ * from, or both).
+ *
+ * @param p Policy containing avrule.
+ * @param avrule Rule to examine.
+ *
+ * @return One of APOL_RELABEL_DIR_TO, APOL_RELABEL_DIR_FROM,
+ * APOL_RELABEL_DIR_BOTH, or < 0 if direction could not be determined.
+ */
+static int relabel_analysis_get_direction(const apol_policy_t * p, const qpol_avrule_t * avrule)
+{
+ qpol_iterator_t *iter;
+ int to = 0, from = 0, retval = -1;
+
+ if (qpol_avrule_get_perm_iter(p->p, avrule, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ char *perm;
+ if (qpol_iterator_get_item(iter, (void **)&perm) < 0) {
+ goto cleanup;
+ }
+ if (strcmp(perm, PERM_RELABELTO) == 0) {
+ to = 1;
+ } else if (strcmp(perm, PERM_RELABELFROM) == 0) {
+ from = 1;
+ }
+ free(perm);
+ perm = NULL;
+ }
+ if (to && from) {
+ retval = APOL_RELABEL_DIR_BOTH;
+ } else if (to) {
+ retval = APOL_RELABEL_DIR_TO;
+ } else if (from) {
+ retval = APOL_RELABEL_DIR_FROM;
+ }
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Given an apol_relabel_result_t node and a qpol_type_t, determine if
+ * the two match.
+ *
+ * @param a Pointer to a apol_relabel_result_t.
+ * @param b Pointer to a type.
+ * @param data (Unused).
+ *
+ * @return 0 if the result node's type matches the given type,
+ * non-zero if not.
+ */
+static int relabel_result_comp_func(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ apol_relabel_result_t *r = (apol_relabel_result_t *) a;
+ qpol_type_t *t = (qpol_type_t *) b;
+ return (int)((char *)r->type - (char *)t);
+}
+
+static void relabel_result_free(void *result)
+{
+ if (result != NULL) {
+ apol_relabel_result_t *r = (apol_relabel_result_t *) result;
+ apol_vector_destroy(&r->to);
+ apol_vector_destroy(&r->from);
+ apol_vector_destroy(&r->both);
+ free(result);
+ }
+}
+
+/**
+ * Given a qpol_type_t pointer, find and return the first
+ * apol_relabel_result_t node within vector v that matches the type.
+ * If there does not exist a node with that type, then allocate a new
+ * one, append it to the vector, and return it. The caller is
+ * expected to eventually call apol_vector_destroy() upon the vector.
+ *
+ * @param p Policy, used for error handling.
+ * @param results A vector of apol_relabel_result_t nodes.
+ * @param type Target type to find.
+ *
+ * @return An apol_relabel_result_t node from which to append results,
+ * or NULL upon error.
+ */
+static apol_relabel_result_t *relabel_result_get_node(const apol_policy_t * p, apol_vector_t * results, const qpol_type_t * type)
+{
+ apol_relabel_result_t *result;
+ size_t i;
+ if (apol_vector_get_index(results, type, relabel_result_comp_func, NULL, &i) == 0) {
+ return (apol_relabel_result_t *) apol_vector_get_element(results, i);
+ }
+ /* make a new result node */
+ if ((result = calloc(1, sizeof(*result))) == NULL ||
+ (result->to = apol_vector_create(free)) == NULL ||
+ (result->from = apol_vector_create(free)) == NULL ||
+ (result->both = apol_vector_create(free)) == NULL || apol_vector_append(results, result) < 0) {
+ ERR(p, "%s", strerror(errno));
+ relabel_result_free(result);
+ return NULL;
+ }
+ result->type = type;
+ return result;
+}
+
+/**
+ * Given a vector of strings representing type names, allocate and
+ * return a vector of qpol_type_t pointers into the given policy for
+ * those types. If a type name is really an alias, obtain and store
+ * its primary instead.
+ *
+ * @param p Policy to which look up types
+ * @param v Vector of strings.
+ *
+ * @return A newly allocated apol_vector_t, which the caller must free
+ * with apol_vector_destroy(). If a type name was not found or upon
+ * other error return NULL.
+ */
+static apol_vector_t *relabel_analysis_get_type_vector(const apol_policy_t * p, const apol_vector_t * v)
+{
+ apol_vector_t *types = NULL;
+ size_t i;
+ int retval = -1;
+
+ if ((types = apol_vector_create_with_capacity(apol_vector_get_size(v), NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ char *s = (char *)apol_vector_get_element(v, i);
+ const qpol_type_t *type;
+ if (apol_query_get_type(p, s, &type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_append(types, (void *)type)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval == -1) {
+ apol_vector_destroy(&types);
+ return NULL;
+ }
+ return types;
+}
+
+/**
+ * Given a type, see if it is an element within a vector of
+ * qpol_type_t pointers. If the type is really an attribute, also
+ * check if any of the attribute's types are a member of v. If v is
+ * NULL then the comparison always succeeds.
+ *
+ * @param p Policy to which look up types.
+ * @param v Target vector of qpol_type_t pointers.
+ * @param type Source type to find.
+ *
+ * @return 1 if type is a member of v, 0 if not, < 0 on error.
+ */
+static int relabel_analysis_compare_type_to_vector(const apol_policy_t * p, const apol_vector_t * v, const qpol_type_t * type)
+{
+ size_t i;
+ unsigned char isattr;
+ qpol_iterator_t *iter = NULL;
+ int retval = -1;
+ if (v == NULL || apol_vector_get_index(v, type, NULL, NULL, &i) == 0) {
+ retval = 1; /* found it */
+ goto cleanup;
+ }
+ if (qpol_type_get_isattr(p->p, type, &isattr) < 0) {
+ goto cleanup;
+ }
+ if (!isattr) { /* not an attribute, so comparison failed */
+ retval = 0;
+ goto cleanup;
+ }
+ if (qpol_type_get_type_iter(p->p, type, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_type_t *t;
+ if (qpol_iterator_get_item(iter, (void **)&t) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(v, t, NULL, NULL, &i) == 0) {
+ retval = 1;
+ goto cleanup;
+ }
+ }
+ retval = 0; /* no matches */
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Given two avrules, possbily append it to the object results vector
+ * onto the appropriate rules vector. The decision to actually append
+ * or not is dependent upon the filtering options stored within the
+ * relabel analysis object.
+ *
+ * @param p Policy containing avrule.
+ * @param r Relabel analysis query object, containing filtering options.
+ * @param ruleA First AV rule to add.
+ * @param ruleB Other AV rule to add.
+ * @param result Results vector being built.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int append_avrules_to_object_vector(const apol_policy_t * p,
+ apol_relabel_analysis_t * r,
+ const qpol_avrule_t * ruleA, const qpol_avrule_t * ruleB, apol_vector_t * results)
+{
+ const qpol_type_t *sourceA, *sourceB, *target, *intermed;
+ unsigned char isattrA, isattrB;
+ apol_vector_t *target_v = NULL, *result_list;
+ size_t i;
+ apol_relabel_result_t *result;
+ apol_relabel_result_pair_t *pair = NULL;
+ int retval = -1, compval, dirA, dirB;
+ if (qpol_avrule_get_target_type(p->p, ruleB, &target) < 0 || (target_v = apol_query_expand_type(p, target)) == NULL) {
+ goto cleanup;
+ }
+ if (qpol_avrule_get_source_type(p->p, ruleA, &sourceA) < 0 ||
+ qpol_avrule_get_source_type(p->p, ruleB, &sourceB) < 0 ||
+ qpol_type_get_isattr(p->p, sourceA, &isattrA) < 0 || qpol_type_get_isattr(p->p, sourceB, &isattrB) < 0) {
+ goto cleanup;
+ }
+ /* If both rules use the same attribute, retain the attribute
+ * to minimize the number of results and to indicate that all
+ * types with that attribute have the permission to relabel. */
+ if ((isattrA && isattrB) || !isattrA) {
+ intermed = sourceA;
+ } else {
+ intermed = sourceB;
+ }
+ for (i = 0; i < apol_vector_get_size(target_v); i++) {
+ target = (qpol_type_t *) apol_vector_get_element(target_v, i);
+ /* exclude if B(t) does not match search criteria */
+ compval = apol_compare_type(p, target, r->type, 0, NULL);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 1) {
+ continue; /* don't care about relabels to itself */
+ }
+ compval = apol_compare_type(p, target, r->result, APOL_QUERY_REGEX, &r->result_regex);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ if ((result = relabel_result_get_node(p, results, target)) == NULL) {
+ goto cleanup;
+ }
+ if ((dirA = relabel_analysis_get_direction(p, ruleA)) < 0 || (dirB = relabel_analysis_get_direction(p, ruleB)) < 0) {
+ goto cleanup;
+ }
+ if ((pair = calloc(1, sizeof(*pair))) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (dirA == APOL_RELABEL_DIR_BOTH && dirB == APOL_RELABEL_DIR_BOTH) {
+ result_list = result->both;
+ pair->ruleA = ruleA;
+ pair->ruleB = ruleB;
+ } else if (dirA == APOL_RELABEL_DIR_FROM || dirB == APOL_RELABEL_DIR_TO) {
+ result_list = result->to;
+ pair->ruleA = ruleA;
+ pair->ruleB = ruleB;
+ } else {
+ result_list = result->from;
+ pair->ruleA = ruleB;
+ pair->ruleB = ruleA;
+ }
+ pair->intermed = intermed;
+ if ((apol_vector_append(result_list, pair)) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ pair = NULL;
+ }
+ retval = 0;
+ cleanup:
+ free(pair);
+ apol_vector_destroy(&target_v);
+ return retval;
+}
+
+/**
+ * Search through sets av and bv, finding pairs of avrules that
+ * satisfy a relabel and adding those pairs to result vector v.
+ *
+ * @param p Policy containing avrules.
+ * @param r Relabel analysis query object.
+ * @param v Vector of apol_relabel_result_t nodes.
+ * @param av Vector of qpol_avrule_t pointers.
+ * @param bv Vector of qpol_avrule_t pointers.
+ * @param subjects_v Vector of permitted qpol_type_t subjects, or NULL
+ * to allow all types.
+ *
+ * @return 0 on success, < 0 upon error.
+ */
+static int relabel_analysis_matchup(const apol_policy_t * p,
+ apol_relabel_analysis_t * r,
+ apol_vector_t * av, apol_vector_t * bv, const apol_vector_t * subjects_v, apol_vector_t * v)
+{
+ const qpol_avrule_t *a_avrule, *b_avrule;
+ const qpol_type_t *a_source, *a_target, *b_source, *b_target, *start_type;
+ const qpol_class_t *a_class, *b_class;
+ apol_vector_t *start_v = NULL;
+ size_t i, j;
+ int compval, retval = -1;
+
+ if (apol_query_get_type(p, r->type, &start_type) < 0) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(av); i++) {
+ a_avrule = apol_vector_get_element(av, i);
+ if (qpol_avrule_get_source_type(p->p, a_avrule, &a_source) < 0 ||
+ qpol_avrule_get_target_type(p->p, a_avrule, &a_target) < 0 ||
+ qpol_avrule_get_object_class(p->p, a_avrule, &a_class) < 0) {
+ goto cleanup;
+ }
+ compval = relabel_analysis_compare_type_to_vector(p, subjects_v, a_source);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ if ((start_v = apol_query_expand_type(p, a_source)) == NULL) {
+ goto cleanup;
+ }
+
+ /* check if there exists a B s.t. B(s) = source and
+ * B(t) != r->type and B(o) = A(o) */
+ for (j = 0; j < apol_vector_get_size(bv); j++) {
+ b_avrule = apol_vector_get_element(bv, j);
+ if (qpol_avrule_get_source_type(p->p, b_avrule, &b_source) < 0 ||
+ qpol_avrule_get_target_type(p->p, b_avrule, &b_target) < 0 ||
+ qpol_avrule_get_object_class(p->p, b_avrule, &b_class) < 0) {
+ goto cleanup;
+ }
+ if (relabel_analysis_compare_type_to_vector(p, start_v, b_source) != 1 ||
+ b_target == start_type || a_class != b_class) {
+ continue;
+ }
+ if (append_avrules_to_object_vector(p, r, a_avrule, b_avrule, v) < 0) {
+ goto cleanup;
+ }
+ }
+ apol_vector_destroy(&start_v);
+ }
+
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&start_v);
+ return retval;
+}
+
+/**
+ * Get a list of allow rules, whose target type matches r->type and
+ * whose permission is <i>opposite</i> of the direction given (e.g.,
+ * relabelfrom if given DIR_TO). Only include rules whose class is a
+ * member of r->classes and whose source is a member of subjects_v.
+ *
+ * @param p Policy to which look up rules.
+ * @param r Structure containing parameters for subject relabel analysis.
+ * @param v Target vector to which append discovered rules.
+ * @param direction Relabelling direction to search.
+ * @param subjects_v If not NULL, then a vector of qpol_type_t pointers.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int relabel_analysis_object(const apol_policy_t * p,
+ apol_relabel_analysis_t * r,
+ apol_vector_t * v, unsigned int direction, const apol_vector_t * subjects_v)
+{
+ apol_avrule_query_t *a = NULL, *b = NULL;
+ apol_vector_t *a_rules = NULL, *b_rules = NULL;
+ char *perm1, *perm2;
+ size_t i;
+ int retval = -1;
+
+ if (direction == APOL_RELABEL_DIR_TO) {
+ perm1 = PERM_RELABELFROM;
+ perm2 = PERM_RELABELTO;
+ } else {
+ perm1 = PERM_RELABELTO;
+ perm2 = PERM_RELABELFROM;
+ }
+
+ if ((a = apol_avrule_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_avrule_query_set_rules(p, a, QPOL_RULE_ALLOW) < 0 ||
+ apol_avrule_query_set_target(p, a, r->type, 1) < 0 || apol_avrule_query_append_perm(p, a, perm1) < 0) {
+ goto cleanup;
+ }
+ for (i = 0; r->classes != NULL && i < apol_vector_get_size(r->classes); i++) {
+ if (apol_avrule_query_append_class(p, a, apol_vector_get_element(r->classes, i)) < 0) {
+ goto cleanup;
+ }
+ }
+ if (apol_avrule_get_by_query(p, a, &a_rules) < 0) {
+ goto cleanup;
+ }
+
+ if ((b = apol_avrule_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_avrule_query_set_rules(p, b, QPOL_RULE_ALLOW) < 0 || apol_avrule_query_append_perm(p, b, perm2) < 0) {
+ goto cleanup;
+ }
+ for (i = 0; r->classes != NULL && i < apol_vector_get_size(r->classes); i++) {
+ if (apol_avrule_query_append_class(p, b, apol_vector_get_element(r->classes, i)) < 0) {
+ goto cleanup;
+ }
+ }
+ if (apol_avrule_get_by_query(p, b, &b_rules) < 0) {
+ goto cleanup;
+ }
+
+ if (relabel_analysis_matchup(p, r, a_rules, b_rules, subjects_v, v) < 0) {
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ apol_avrule_query_destroy(&a);
+ apol_vector_destroy(&a_rules);
+ apol_avrule_query_destroy(&b);
+ apol_vector_destroy(&b_rules);
+ return retval;
+}
+
+/**
+ * Given an avrule, possbily append it to the subject results vector
+ * onto the appropriate rules vector. The decision to actually append
+ * or not is dependent upon the filtering options stored within the
+ * relabel analysis object.
+ *
+ * @param p Policy containing avrule.
+ * @param r Relabel analysis query object, containing filtering options.
+ * @param avrule AV rule to add.
+ * @param result Results vector being built.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int append_avrule_to_subject_vector(const apol_policy_t * p,
+ apol_relabel_analysis_t * r, const qpol_avrule_t * avrule, apol_vector_t * results)
+{
+ const qpol_type_t *target;
+ apol_vector_t *target_v = NULL, *result_list = NULL;
+ size_t i;
+ apol_relabel_result_t *result;
+ apol_relabel_result_pair_t *pair = NULL;
+ int retval = -1, dir, compval;
+ if ((dir = relabel_analysis_get_direction(p, avrule)) < 0) {
+ goto cleanup;
+ }
+ if (qpol_avrule_get_target_type(p->p, avrule, &target) < 0 || (target_v = apol_query_expand_type(p, target)) == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(target_v); i++) {
+ target = (qpol_type_t *) apol_vector_get_element(target_v, i);
+ compval = apol_compare_type(p, target, r->type, 0, NULL);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 1) {
+ continue; /* don't care about relabels to itself */
+ }
+ compval = apol_compare_type(p, target, r->result, APOL_QUERY_REGEX, &r->result_regex);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ if ((result = relabel_result_get_node(p, results, target)) == NULL) {
+ goto cleanup;
+ }
+ if ((pair = calloc(1, sizeof(*pair))) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ pair->ruleA = avrule;
+ pair->ruleB = NULL;
+ pair->intermed = NULL;
+ switch (dir) {
+ case APOL_RELABEL_DIR_TO:
+ result_list = result->to;
+ break;
+ case APOL_RELABEL_DIR_FROM:
+ result_list = result->from;
+ break;
+ case APOL_RELABEL_DIR_BOTH:
+ result_list = result->both;
+ break;
+ }
+ if ((apol_vector_append(result_list, pair)) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ pair = NULL;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&target_v);
+ free(pair);
+ return retval;
+}
+
+/**
+ * Get a list of all allow rules, whose source type matches r->type
+ * and whose permission list has either "relabelto" or "relabelfrom".
+ * Only include rules whose class is a member of r->classes. Add
+ * instances of those to the result vector.
+ *
+ * @param p Policy to which look up rules.
+ * @param r Structure containing parameters for subject relabel analysis.
+ * @param v Target vector to which append discovered rules.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int relabel_analysis_subject(const apol_policy_t * p, apol_relabel_analysis_t * r, apol_vector_t * v)
+{
+ apol_avrule_query_t *a = NULL;
+ apol_vector_t *avrules_v = NULL;
+ const qpol_avrule_t *avrule;
+ size_t i;
+ int retval = -1;
+
+ if ((a = apol_avrule_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_avrule_query_set_rules(p, a, QPOL_RULE_ALLOW) < 0 ||
+ apol_avrule_query_set_source(p, a, r->type, 1) < 0 ||
+ apol_avrule_query_append_perm(p, a, PERM_RELABELTO) < 0 || apol_avrule_query_append_perm(p, a, PERM_RELABELFROM) < 0) {
+ goto cleanup;
+ }
+ for (i = 0; r->classes != NULL && i < apol_vector_get_size(r->classes); i++) {
+ if (apol_avrule_query_append_class(p, a, apol_vector_get_element(r->classes, i)) < 0) {
+ goto cleanup;
+ }
+ }
+ if (apol_avrule_get_by_query(p, a, &avrules_v) < 0) {
+ goto cleanup;
+ }
+
+ for (i = 0; i < apol_vector_get_size(avrules_v); i++) {
+ avrule = (qpol_avrule_t *) apol_vector_get_element(avrules_v, i);
+ if (append_avrule_to_subject_vector(p, r, avrule, v) < 0) {
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ apol_avrule_query_destroy(&a);
+ apol_vector_destroy(&avrules_v);
+ return retval;
+}
+
+/******************** public functions below ********************/
+
+int apol_relabel_analysis_do(const apol_policy_t * p, apol_relabel_analysis_t * r, apol_vector_t ** v)
+{
+ apol_vector_t *subjects_v = NULL;
+ const qpol_type_t *start_type;
+ int retval = -1;
+ *v = NULL;
+
+ if (r->mode == 0 || r->type == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+ if (apol_query_get_type(p, r->type, &start_type) < 0) {
+ goto cleanup;
+ }
+
+ if ((*v = apol_vector_create(relabel_result_free)) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+
+ if (r->mode == APOL_RELABEL_MODE_OBJ) {
+ if (r->subjects != NULL && (subjects_v = relabel_analysis_get_type_vector(p, r->subjects)) == NULL) {
+ goto cleanup;
+ }
+ if ((r->direction & APOL_RELABEL_DIR_TO) && relabel_analysis_object(p, r, *v, APOL_RELABEL_DIR_TO, subjects_v) < 0) {
+ goto cleanup;
+ }
+ if ((r->direction & APOL_RELABEL_DIR_FROM) &&
+ relabel_analysis_object(p, r, *v, APOL_RELABEL_DIR_FROM, subjects_v) < 0) {
+ goto cleanup;
+ }
+ } else {
+ if (relabel_analysis_subject(p, r, *v) < 0) {
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&subjects_v);
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ return retval;
+}
+
+apol_relabel_analysis_t *apol_relabel_analysis_create(void)
+{
+ return calloc(1, sizeof(apol_relabel_analysis_t));
+}
+
+void apol_relabel_analysis_destroy(apol_relabel_analysis_t ** r)
+{
+ if (r != NULL && *r != NULL) {
+ free((*r)->type);
+ free((*r)->result);
+ apol_vector_destroy(&(*r)->classes);
+ apol_vector_destroy(&(*r)->subjects);
+ apol_regex_destroy(&(*r)->result_regex);
+ free(*r);
+ *r = NULL;
+ }
+}
+
+int apol_relabel_analysis_set_dir(const apol_policy_t * p, apol_relabel_analysis_t * r, unsigned int dir)
+{
+ if (p == NULL || r == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+
+ switch (dir) {
+ case APOL_RELABEL_DIR_BOTH:
+ case APOL_RELABEL_DIR_TO:
+ case APOL_RELABEL_DIR_FROM:
+ {
+ r->mode = APOL_RELABEL_MODE_OBJ;
+ r->direction = dir;
+ break;
+ }
+ case APOL_RELABEL_DIR_SUBJECT:
+ {
+ r->mode = APOL_RELABEL_MODE_SUBJ;
+ r->direction = APOL_RELABEL_DIR_BOTH;
+ break;
+ }
+ default:
+ {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_relabel_analysis_set_type(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *name)
+{
+ if (p == NULL || r == NULL || name == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ return apol_query_set(p, &r->type, NULL, name);
+}
+
+int apol_relabel_analysis_append_class(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *obj_class)
+{
+ char *s;
+ if (p == NULL || r == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ if (obj_class == NULL) {
+ apol_vector_destroy(&r->classes);
+ } else if ((s = strdup(obj_class)) == NULL || (r->classes == NULL && (r->classes = apol_vector_create(free)) == NULL)
+ || apol_vector_append(r->classes, s) < 0) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int apol_relabel_analysis_append_subject(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *subject)
+{
+ char *s;
+ if (p == NULL || r == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ if (subject == NULL) {
+ apol_vector_destroy(&r->subjects);
+ } else if ((s = strdup(subject)) == NULL ||
+ (r->subjects == NULL && (r->subjects = apol_vector_create(free)) == NULL) ||
+ apol_vector_append(r->subjects, s) < 0) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int apol_relabel_analysis_set_result_regex(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *result)
+{
+ return apol_query_set(p, &r->result, &r->result_regex, result);
+}
+
+/******************** functions to access relabel results ********************/
+
+const apol_vector_t *apol_relabel_result_get_to(const apol_relabel_result_t * r)
+{
+ return r->to;
+}
+
+const apol_vector_t *apol_relabel_result_get_from(const apol_relabel_result_t * r)
+{
+ return r->from;
+}
+
+const apol_vector_t *apol_relabel_result_get_both(const apol_relabel_result_t * r)
+{
+ return r->both;
+}
+
+const qpol_type_t *apol_relabel_result_get_result_type(const apol_relabel_result_t * r)
+{
+ return r->type;
+}
+
+const qpol_avrule_t *apol_relabel_result_pair_get_ruleA(const apol_relabel_result_pair_t * p)
+{
+ return p->ruleA;
+}
+
+const qpol_avrule_t *apol_relabel_result_pair_get_ruleB(const apol_relabel_result_pair_t * p)
+{
+ return p->ruleB;
+}
+
+const qpol_type_t *apol_relabel_result_pair_get_intermediate_type(const apol_relabel_result_pair_t * p)
+{
+ return p->intermed;
+}