summaryrefslogtreecommitdiffstats
path: root/libapol/src
diff options
context:
space:
mode:
Diffstat (limited to 'libapol/src')
-rw-r--r--libapol/src/Makefile.am75
-rw-r--r--libapol/src/avrule-query.c1209
-rw-r--r--libapol/src/bool-query.c111
-rw-r--r--libapol/src/bst.c352
-rw-r--r--libapol/src/class-perm-query.c327
-rw-r--r--libapol/src/condrule-query.c182
-rw-r--r--libapol/src/constraint-query.c217
-rw-r--r--libapol/src/context-query.c477
-rw-r--r--libapol/src/domain-trans-analysis-internal.h36
-rw-r--r--libapol/src/domain-trans-analysis.c2076
-rw-r--r--libapol/src/fscon-query.c437
-rw-r--r--libapol/src/infoflow-analysis-internal.h49
-rw-r--r--libapol/src/infoflow-analysis.c2245
-rw-r--r--libapol/src/isid-query.c123
-rw-r--r--libapol/src/libapol.map86
-rw-r--r--libapol/src/mls-query.c248
-rw-r--r--libapol/src/mls_level.c771
-rw-r--r--libapol/src/mls_range.c641
-rw-r--r--libapol/src/netcon-query.c592
-rw-r--r--libapol/src/perm-map.c697
-rw-r--r--libapol/src/permissive-query.c107
-rw-r--r--libapol/src/polcap-query.c107
-rw-r--r--libapol/src/policy-path.c409
-rw-r--r--libapol/src/policy-query-internal.h511
-rw-r--r--libapol/src/policy-query.c903
-rw-r--r--libapol/src/policy.c217
-rw-r--r--libapol/src/queue.c125
-rw-r--r--libapol/src/queue.h86
-rw-r--r--libapol/src/range_trans-query.c318
-rw-r--r--libapol/src/rbacrule-query.c417
-rw-r--r--libapol/src/relabel-analysis.c813
-rw-r--r--libapol/src/render.c158
-rw-r--r--libapol/src/role-query.c167
-rw-r--r--libapol/src/terule-query.c1049
-rw-r--r--libapol/src/type-query.c202
-rw-r--r--libapol/src/types-relation-analysis.c1143
-rw-r--r--libapol/src/user-query.c198
-rw-r--r--libapol/src/util.c659
-rw-r--r--libapol/src/vector-internal.h36
-rw-r--r--libapol/src/vector.c457
40 files changed, 19033 insertions, 0 deletions
diff --git a/libapol/src/Makefile.am b/libapol/src/Makefile.am
new file mode 100644
index 0000000..3fa4f06
--- /dev/null
+++ b/libapol/src/Makefile.am
@@ -0,0 +1,75 @@
+lib_LIBRARIES = libapol.a
+setoolsdir = @setoolsdir@
+
+apolso_DATA = libapol.so.@libapol_version@
+apolsodir = $(libdir)
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ -fpic -I$(srcdir)/../include \
+ -DAPOL_INSTALL_DIR='"${setoolsdir}"' \
+ -DLIBAPOL_POLICY_INSTALL_DIR='"@selinux_policy_dir@/policy"' \
+ -DLIBAPOL_DEFAULT_POLICY='"@selinux_default_policy@"'
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@
+
+libapol_a_SOURCES = \
+ avrule-query.c \
+ bool-query.c \
+ bst.c \
+ class-perm-query.c \
+ condrule-query.c \
+ constraint-query.c \
+ context-query.c \
+ domain-trans-analysis.c domain-trans-analysis-internal.h \
+ fscon-query.c \
+ infoflow-analysis.c infoflow-analysis-internal.h \
+ isid-query.c \
+ mls-query.c \
+ mls_level.c \
+ mls_range.c \
+ netcon-query.c \
+ perm-map.c \
+ permissive-query.c \
+ polcap-query.c \
+ policy.c \
+ policy-path.c \
+ policy-query.c \
+ queue.c \
+ range_trans-query.c \
+ rbacrule-query.c \
+ relabel-analysis.c \
+ render.c \
+ role-query.c \
+ terule-query.c \
+ type-query.c \
+ types-relation-analysis.c \
+ user-query.c \
+ util.c \
+ vector.c vector-internal.h \
+ policy-query-internal.h queue.h
+
+libapol_a_DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so
+
+libapol_so_OBJS = $(patsubst %.c,%.o,$(filter %.c,$(libapol_a_SOURCES)))
+LIBAPOL_SONAME = @libapol_soname@
+
+dist_noinst_DATA = libapol.map
+
+$(apolso_DATA): $(libapol_so_OBJS) libapol.map
+ $(CC) -shared -o $@ $(libapol_so_OBJS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(LIBAPOL_SONAME),--version-script=$(srcdir)/libapol.map,-z,defs $(top_builddir)/libqpol/src/libqpol.so
+ $(LN_S) -f $@ @libapol_soname@
+ $(LN_S) -f $@ libapol.so
+
+libapol.so: $(apolso_DATA)
+
+$(top_builddir)/libqpol/src/libqpol.so:
+ $(MAKE) -C $(top_builddir)/libqpol/src $(notdir $@)
+
+install-data-hook:
+ cd $(DESTDIR)$(apolsodir) && $(LN_S) -f $(apolso_DATA) @libapol_soname@
+ cd $(DESTDIR)$(apolsodir) && $(LN_S) -f $(apolso_DATA) libapol.so
+
+mostlyclean-local:
+ -rm -rf *.gcno *.gcda *.gprof *.gcov libapol.so @libapol_soname@ $(apolso_DATA)
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(apolsodir)/$(apolso_DATA) $(DESTDIR)$(apolsodir)/@libapol_soname@ $(DESTDIR)$(apolsodir)/libapol.so
diff --git a/libapol/src/avrule-query.c b/libapol/src/avrule-query.c
new file mode 100644
index 0000000..c409e30
--- /dev/null
+++ b/libapol/src/avrule-query.c
@@ -0,0 +1,1209 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about access vector
+ * rules within a policy. The caller obtains a query object, fills in
+ * its parameters, and then runs the query; it obtains a vector of
+ * results. Searches are conjunctive -- all fields of the search
+ * query must match for a datum to be added to the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2008 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 <apol/bst.h>
+#include <qpol/policy_extend.h>
+#include <errno.h>
+#include <string.h>
+
+struct apol_avrule_query
+{
+ char *source, *target, *bool_name;
+ apol_vector_t *classes, *perms;
+ unsigned int rules;
+ unsigned int flags;
+};
+
+/**
+ * Common semantic rule selection routine used in get*rule_by_query.
+ * @param p Policy to search.
+ * @param v Vector of rules to populate (of type qpol_avrule_t).
+ * @param rule_type Mask of rules to search.
+ * @param flags Query options as specified by the apol_avrule_query.
+ * @param source_list If non-NULL, list of types to use as source.
+ * If NULL, accept all types.
+ * @param target_list If non-NULL, list of types to use as target.
+ * If NULL, accept all types.
+ * @param class_list If non-NULL, list of classes to use.
+ * If NULL, accept all classes.
+ * @param perm_list If non-NULL, list of permisions to use.
+ * If NULL, accept all permissions.
+ * @param bool_name If non-NULL, find conditional rules affected by this boolean.
+ * If NULL, all rules will be considered (including unconditional rules).
+ * @return 0 on success and < 0 on failure.
+ */
+static int rule_select(const apol_policy_t * p, apol_vector_t * v, uint32_t rule_type, unsigned int flags,
+ const apol_vector_t * source_list, const apol_vector_t * target_list, const apol_vector_t * class_list,
+ const apol_vector_t * perm_list, const char *bool_name)
+{
+ qpol_iterator_t *iter = NULL, *perm_iter = NULL;
+ const int only_enabled = flags & APOL_QUERY_ONLY_ENABLED;
+ const int is_regex = flags & APOL_QUERY_REGEX;
+ const int source_as_any = flags & APOL_QUERY_SOURCE_AS_ANY;
+ size_t num_perms_to_match = 1;
+ int retv = -1;
+ regex_t *bool_regex = NULL;
+
+ if ((flags & APOL_QUERY_MATCH_ALL_PERMS) && perm_list != NULL) {
+ num_perms_to_match = apol_vector_get_size(perm_list);
+ }
+ if (qpol_policy_get_avrule_iter(p->p, rule_type, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_avrule_t *rule;
+ uint32_t is_enabled;
+ const qpol_cond_t *cond = NULL;
+ int match_source = 0, match_target = 0, match_bool = 0;
+ size_t match_perm = 0, i;
+ if (qpol_iterator_get_item(iter, (void **)&rule) < 0) {
+ goto cleanup;
+ }
+
+ if (qpol_avrule_get_is_enabled(p->p, rule, &is_enabled) < 0) {
+ goto cleanup;
+ }
+ if (!is_enabled && only_enabled) {
+ continue;
+ }
+
+ if (bool_name != NULL) {
+ if (qpol_avrule_get_cond(p->p, rule, &cond) < 0) {
+ goto cleanup;
+ }
+ if (cond == NULL) {
+ continue; /* skip unconditional rule */
+ }
+ match_bool = apol_compare_cond_expr(p, cond, bool_name, is_regex, &bool_regex);
+ if (match_bool < 0) {
+ goto cleanup;
+ } else if (match_bool == 0) {
+ continue;
+ }
+ }
+
+ if (source_list == NULL) {
+ match_source = 1;
+ } else {
+ const qpol_type_t *source_type;
+ if (qpol_avrule_get_source_type(p->p, rule, &source_type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(source_list, source_type, NULL, NULL, &i) == 0) {
+ match_source = 1;
+ }
+ }
+
+ /* if source did not match, but treating source symbol
+ * as any field, then delay rejecting this rule until
+ * the target has been checked */
+ if (!source_as_any && !match_source) {
+ continue;
+ }
+
+ if (target_list == NULL || (source_as_any && match_source)) {
+ match_target = 1;
+ } else {
+ const qpol_type_t *target_type;
+ if (qpol_avrule_get_target_type(p->p, rule, &target_type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(target_list, target_type, NULL, NULL, &i) == 0) {
+ match_target = 1;
+ }
+ }
+
+ if (!match_target) {
+ continue;
+ }
+
+ if (class_list != NULL) {
+ const qpol_class_t *obj_class;
+ if (qpol_avrule_get_object_class(p->p, rule, &obj_class) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(class_list, obj_class, NULL, NULL, &i) < 0) {
+ continue;
+ }
+ }
+
+ if (perm_list != NULL) {
+ for (i = 0; i < apol_vector_get_size(perm_list) && match_perm < num_perms_to_match; i++) {
+ char *perm = (char *)apol_vector_get_element(perm_list, i);
+ if (qpol_avrule_get_perm_iter(p->p, rule, &perm_iter) < 0) {
+ goto cleanup;
+ }
+ int match = apol_compare_iter(p, perm_iter, perm, 0, NULL, 1);
+ if (match < 0) {
+ goto cleanup;
+ } else if (match > 0) {
+ match_perm++;
+ }
+ qpol_iterator_destroy(&perm_iter);
+ }
+ } else {
+ match_perm = num_perms_to_match;
+ }
+ if (match_perm < num_perms_to_match) {
+ continue;
+ }
+
+ if (apol_vector_append(v, rule)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retv = 0;
+ cleanup:
+ apol_regex_destroy(&bool_regex);
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&perm_iter);
+ return retv;
+}
+
+int apol_avrule_get_by_query(const apol_policy_t * p, const apol_avrule_query_t * a, apol_vector_t ** v)
+{
+ apol_vector_t *source_list = NULL, *target_list = NULL, *class_list = NULL, *perm_list = NULL;
+ int retval = -1, source_as_any = 0, is_regex = 0;
+ char *bool_name = NULL;
+ *v = NULL;
+ unsigned int flags = 0;
+
+ uint32_t rule_type = QPOL_RULE_ALLOW | QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT;
+// if (qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_NEVERALLOW)) {
+ rule_type |= QPOL_RULE_NEVERALLOW;
+// }
+ if (a != NULL) {
+ if (a->rules != 0) {
+ rule_type &= a->rules;
+ }
+ flags = a->flags;
+ is_regex = a->flags & APOL_QUERY_REGEX;
+ bool_name = a->bool_name;
+ if (a->source != NULL &&
+ (source_list =
+ apol_query_create_candidate_type_list(p, a->source, is_regex,
+ a->flags & APOL_QUERY_SOURCE_INDIRECT,
+ ((a->flags & (APOL_QUERY_SOURCE_TYPE | APOL_QUERY_SOURCE_ATTRIBUTE)) /
+ APOL_QUERY_SOURCE_TYPE))) == NULL) {
+ goto cleanup;
+ }
+ if ((a->flags & APOL_QUERY_SOURCE_AS_ANY) && a->source != NULL) {
+ target_list = source_list;
+ source_as_any = 1;
+ } else if (a->target != NULL &&
+ (target_list =
+ apol_query_create_candidate_type_list(p, a->target, is_regex,
+ a->flags & APOL_QUERY_TARGET_INDIRECT,
+ ((a->
+ flags & (APOL_QUERY_TARGET_TYPE | APOL_QUERY_TARGET_ATTRIBUTE))
+ / APOL_QUERY_TARGET_TYPE))) == NULL) {
+ goto cleanup;
+ }
+ if (a->classes != NULL &&
+ apol_vector_get_size(a->classes) > 0 &&
+ (class_list = apol_query_create_candidate_class_list(p, a->classes)) == NULL) {
+ goto cleanup;
+ }
+ if (a->perms != NULL && apol_vector_get_size(a->perms) > 0) {
+ perm_list = a->perms;
+ }
+ }
+
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ if (rule_select(p, *v, rule_type, flags, source_list, target_list, class_list, perm_list, bool_name)) {
+ goto cleanup;
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ apol_vector_destroy(&source_list);
+ if (!source_as_any) {
+ apol_vector_destroy(&target_list);
+ }
+ apol_vector_destroy(&class_list);
+ /* don't destroy perm_list - it points to query's permission list */
+ return retval;
+}
+
+int apol_syn_avrule_get_by_query(const apol_policy_t * p, const apol_avrule_query_t * a, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL, *perm_iter = NULL;
+ apol_vector_t *source_list = NULL, *target_list = NULL, *class_list = NULL, *perm_list = NULL, *syn_v = NULL;
+ apol_vector_t *target_types_list = NULL;
+ int retval = -1, source_as_any = 0, is_regex = 0;
+ char *bool_name = NULL;
+ regex_t *bool_regex = NULL;
+ *v = NULL;
+ size_t i;
+ unsigned int flags = 0;
+
+ if (!p || !qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_SYN_RULES)) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+
+ uint32_t rule_type = QPOL_RULE_ALLOW | QPOL_RULE_NEVERALLOW | QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT;
+ if (a != NULL) {
+ if (a->rules != 0) {
+ rule_type &= a->rules;
+ }
+ flags = a->flags;
+ is_regex = a->flags & APOL_QUERY_REGEX;
+ bool_name = a->bool_name;
+ if (a->source != NULL &&
+ (source_list =
+ apol_query_create_candidate_syn_type_list(p, a->source, is_regex,
+ a->flags & APOL_QUERY_SOURCE_INDIRECT,
+ ((a->flags & (APOL_QUERY_SOURCE_TYPE |
+ APOL_QUERY_SOURCE_ATTRIBUTE)) /
+ APOL_QUERY_SOURCE_TYPE))) == NULL) {
+ goto cleanup;
+ }
+ if ((a->flags & APOL_QUERY_SOURCE_AS_ANY) && a->source != NULL) {
+ target_list = source_list;
+ source_as_any = 1;
+ } else if (a->target != NULL &&
+ (target_list =
+ apol_query_create_candidate_syn_type_list(p, a->target, is_regex,
+ a->flags & APOL_QUERY_TARGET_INDIRECT,
+ ((a->flags & (APOL_QUERY_TARGET_TYPE |
+ APOL_QUERY_TARGET_ATTRIBUTE))
+ / APOL_QUERY_TARGET_TYPE))) == NULL) {
+ goto cleanup;
+ }
+ if (a->classes != NULL &&
+ apol_vector_get_size(a->classes) > 0 &&
+ (class_list = apol_query_create_candidate_class_list(p, a->classes)) == NULL) {
+ goto cleanup;
+ }
+ if (a->perms != NULL && apol_vector_get_size(a->perms) > 0) {
+ perm_list = a->perms;
+ }
+ }
+
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ if (rule_select(p, *v, rule_type, flags, source_list, target_list, class_list, perm_list, bool_name)) {
+ goto cleanup;
+ }
+
+ syn_v = apol_avrule_list_to_syn_avrules(p, *v, perm_list);
+ if (!syn_v) {
+ goto cleanup;
+ }
+ apol_vector_destroy(v);
+ *v = syn_v;
+ syn_v = NULL;
+
+ /* if both fields are indirect skip post filtering type sets */
+ if ((a->flags & APOL_QUERY_SOURCE_INDIRECT) && (a->flags & (APOL_QUERY_TARGET_INDIRECT | APOL_QUERY_SOURCE_AS_ANY))) {
+ retval = 0;
+ goto cleanup;
+ }
+ /* if not searching by source or target we are done */
+ if (!source_list && !target_list) {
+ retval = 0;
+ goto cleanup;
+ }
+
+ if (source_list && !(a->flags & APOL_QUERY_SOURCE_INDIRECT)) {
+ apol_vector_destroy(&source_list);
+ source_list =
+ apol_query_create_candidate_type_list(p, a->source, is_regex, 0,
+ ((a->flags & (APOL_QUERY_SOURCE_TYPE | APOL_QUERY_SOURCE_ATTRIBUTE)) /
+ APOL_QUERY_SOURCE_TYPE));
+ if (!source_list)
+ goto cleanup;
+ }
+ if (target_list && (source_as_any || !(a->flags & APOL_QUERY_TARGET_INDIRECT))) {
+ if (source_as_any) {
+ target_list = source_list;
+ } else {
+ apol_vector_destroy(&target_list);
+ target_list =
+ apol_query_create_candidate_type_list(p, a->target, is_regex, 0,
+ ((a->flags & (APOL_QUERY_SOURCE_TYPE |
+ APOL_QUERY_SOURCE_ATTRIBUTE)) /
+ APOL_QUERY_SOURCE_TYPE));
+ if (!target_list)
+ goto cleanup;
+ }
+ }
+ if (target_list) {
+ target_types_list = apol_vector_create_from_vector(target_list, NULL, NULL, NULL);
+ if (!target_types_list) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ qpol_type_t *type = NULL;
+ for (i = 0; i < apol_vector_get_size(target_types_list); i++) {
+ type = apol_vector_get_element(target_types_list, i);
+ unsigned char isattr = 0;
+ qpol_type_get_isattr(p->p, type, &isattr);
+ if (isattr) {
+ apol_vector_remove(target_types_list, i);
+ i--;
+ }
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(*v); i++) {
+ qpol_syn_avrule_t *srule = apol_vector_get_element(*v, i);
+ const qpol_type_set_t *stypes = NULL, *ttypes = NULL;
+ int uses_source = 0, uses_target = 0;
+ uint32_t is_self = 0;
+ qpol_syn_avrule_get_source_type_set(p->p, srule, &stypes);
+ qpol_syn_avrule_get_target_type_set(p->p, srule, &ttypes);
+ qpol_syn_avrule_get_is_target_self(p->p, srule, &is_self);
+ if (source_list && !(a->flags & APOL_QUERY_SOURCE_INDIRECT)) {
+ uses_source = apol_query_type_set_uses_types_directly(p, stypes, source_list);
+ if (uses_source < 0)
+ goto cleanup;
+ } else if (source_list && a->flags & APOL_QUERY_SOURCE_INDIRECT) {
+ uses_source = 1;
+ } else if (!source_list) {
+ uses_source = 1;
+ }
+
+ if (target_list
+ && !((a->flags & APOL_QUERY_TARGET_INDIRECT) || (source_as_any && a->flags & APOL_QUERY_SOURCE_INDIRECT))) {
+ uses_target = apol_query_type_set_uses_types_directly(p, ttypes, target_list);
+ if (uses_target < 0)
+ goto cleanup;
+ if (is_self) {
+ uses_target |= apol_query_type_set_uses_types_directly(p, stypes, target_types_list);
+ if (uses_target < 0)
+ goto cleanup;
+ }
+ } else if (target_list && ((a->flags & APOL_QUERY_TARGET_INDIRECT)
+ || (source_as_any && a->flags & APOL_QUERY_SOURCE_INDIRECT))) {
+ uses_target = 1;
+ } else if (!target_list) {
+ uses_target = 1;
+ }
+
+ if (!((uses_source && uses_target) || (source_as_any && (uses_source || uses_target)))) {
+ apol_vector_remove(*v, i);
+ i--;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ apol_vector_destroy(&syn_v);
+ apol_vector_destroy(&source_list);
+ apol_vector_destroy(&target_types_list);
+ if (!source_as_any) {
+ apol_vector_destroy(&target_list);
+ }
+ apol_vector_destroy(&class_list);
+ /* don't destroy perm_list - it points to query's permission list */
+ apol_regex_destroy(&bool_regex);
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&perm_iter);
+ return retval;
+}
+
+apol_avrule_query_t *apol_avrule_query_create(void)
+{
+ apol_avrule_query_t *a = calloc(1, sizeof(apol_avrule_query_t));
+ if (a != NULL) {
+ a->rules = ~0U;
+ a->flags =
+ (APOL_QUERY_SOURCE_TYPE | APOL_QUERY_SOURCE_ATTRIBUTE | APOL_QUERY_TARGET_TYPE |
+ APOL_QUERY_TARGET_ATTRIBUTE);
+ }
+ return a;
+}
+
+void apol_avrule_query_destroy(apol_avrule_query_t ** a)
+{
+ if (*a != NULL) {
+ free((*a)->source);
+ free((*a)->target);
+ free((*a)->bool_name);
+ apol_vector_destroy(&(*a)->classes);
+ apol_vector_destroy(&(*a)->perms);
+ free(*a);
+ *a = NULL;
+ }
+}
+
+int apol_avrule_query_set_rules(const apol_policy_t * p __attribute__ ((unused)), apol_avrule_query_t * a, unsigned int rules)
+{
+ if (rules != 0) {
+ a->rules = rules;
+ } else {
+ a->rules = ~0U;
+ }
+ return 0;
+}
+
+int apol_avrule_query_set_source(const apol_policy_t * p, apol_avrule_query_t * a, const char *symbol, int is_indirect)
+{
+ apol_query_set_flag(p, &a->flags, is_indirect, APOL_QUERY_SOURCE_INDIRECT);
+ return apol_query_set(p, &a->source, NULL, symbol);
+}
+
+int apol_avrule_query_set_source_component(const apol_policy_t * p, apol_avrule_query_t * a, unsigned int component)
+{
+ if (!a || !(component & APOL_QUERY_SYMBOL_IS_BOTH)) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ apol_query_set_flag(p, &a->flags, component & APOL_QUERY_SYMBOL_IS_TYPE, APOL_QUERY_SOURCE_TYPE);
+ apol_query_set_flag(p, &a->flags, component & APOL_QUERY_SYMBOL_IS_ATTRIBUTE, APOL_QUERY_SOURCE_ATTRIBUTE);
+ return 0;
+}
+
+int apol_avrule_query_set_target(const apol_policy_t * p, apol_avrule_query_t * a, const char *symbol, int is_indirect)
+{
+ apol_query_set_flag(p, &a->flags, is_indirect, APOL_QUERY_TARGET_INDIRECT);
+ return apol_query_set(p, &a->target, NULL, symbol);
+}
+
+int apol_avrule_query_set_target_component(const apol_policy_t * p, apol_avrule_query_t * a, unsigned int component)
+{
+ if (!a || !(component && APOL_QUERY_SYMBOL_IS_BOTH)) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ apol_query_set_flag(p, &a->flags, component & APOL_QUERY_SYMBOL_IS_TYPE, APOL_QUERY_TARGET_TYPE);
+ apol_query_set_flag(p, &a->flags, component & APOL_QUERY_SYMBOL_IS_ATTRIBUTE, APOL_QUERY_TARGET_ATTRIBUTE);
+ return 0;
+}
+
+int apol_avrule_query_append_class(const apol_policy_t * p, apol_avrule_query_t * a, const char *obj_class)
+{
+ char *s = NULL;
+ if (obj_class == NULL) {
+ apol_vector_destroy(&a->classes);
+ } else if ((s = strdup(obj_class)) == NULL || (a->classes == NULL && (a->classes = apol_vector_create(free)) == NULL)
+ || apol_vector_append(a->classes, s) < 0) {
+ ERR(p, "%s", strerror(errno));
+ free(s);
+ return -1;
+ }
+ return 0;
+}
+
+int apol_avrule_query_append_perm(const apol_policy_t * p, apol_avrule_query_t * a, const char *perm)
+{
+ char *s;
+ if (perm == NULL) {
+ apol_vector_destroy(&a->perms);
+ } else if ((s = strdup(perm)) == NULL ||
+ (a->perms == NULL && (a->perms = apol_vector_create(free)) == NULL) || apol_vector_append(a->perms, s) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ return 0;
+}
+
+int apol_avrule_query_set_bool(const apol_policy_t * p, apol_avrule_query_t * a, const char *bool_name)
+{
+ return apol_query_set(p, &a->bool_name, NULL, bool_name);
+}
+
+int apol_avrule_query_set_enabled(const apol_policy_t * p, apol_avrule_query_t * a, int is_enabled)
+{
+ return apol_query_set_flag(p, &a->flags, is_enabled, APOL_QUERY_ONLY_ENABLED);
+}
+
+int apol_avrule_query_set_all_perms(const apol_policy_t * p, apol_avrule_query_t * a, int match_all)
+{
+ return apol_query_set_flag(p, &a->flags, match_all, APOL_QUERY_MATCH_ALL_PERMS);
+}
+
+int apol_avrule_query_set_source_any(const apol_policy_t * p, apol_avrule_query_t * a, int is_any)
+{
+ return apol_query_set_flag(p, &a->flags, is_any, APOL_QUERY_SOURCE_AS_ANY);
+}
+
+int apol_avrule_query_set_regex(const apol_policy_t * p, apol_avrule_query_t * a, int is_regex)
+{
+ return apol_query_set_regex(p, &a->flags, is_regex);
+}
+
+/**
+ * Comparison function for two syntactic avrules. Will return -1 if
+ * a's line number is before b's, 1 if b is greater.
+ */
+static int apol_syn_avrule_comp(const void *a, const void *b, void *data)
+{
+ qpol_syn_avrule_t *r1 = (qpol_syn_avrule_t *) a;
+ qpol_syn_avrule_t *r2 = (qpol_syn_avrule_t *) b;
+ apol_policy_t *p = (apol_policy_t *) data;
+ unsigned long num1, num2;
+ if (qpol_syn_avrule_get_lineno(p->p, r1, &num1) < 0 || qpol_syn_avrule_get_lineno(p->p, r2, &num2) < 0) {
+ return 0;
+ }
+ if (num1 != num2) {
+ return (int)num1 - (int)num2;
+ }
+ return (int)((char *)r1 - (char *)r2);
+}
+
+apol_vector_t *apol_avrule_to_syn_avrules(const apol_policy_t * p, const qpol_avrule_t * rule, const apol_vector_t * perms)
+{
+ apol_vector_t *v = NULL;
+ qpol_iterator_t *iter = NULL, *perm_iter = NULL;
+ qpol_syn_avrule_t *syn_avrule;
+ char *perm;
+ size_t i;
+ int retval = -1, error = 0, found_perm = 0;
+ if (qpol_avrule_get_syn_avrule_iter(p->p, rule, &iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&syn_avrule) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ found_perm = 0;
+ if (perms != NULL && apol_vector_get_size(perms) > 0) {
+ if (qpol_syn_avrule_get_perm_iter(p->p, syn_avrule, &perm_iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) {
+ if (qpol_iterator_get_item(perm_iter, (void **)&perm) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (apol_vector_get_index(perms, perm, apol_str_strcmp, NULL, &i) == 0) {
+ found_perm = 1;
+ break;
+ }
+ }
+ } else {
+ found_perm = 1;
+ }
+ if (found_perm && apol_vector_append(v, syn_avrule) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ /* explicit cast to void* since vector's arbitrary data is non-const */
+ apol_vector_sort_uniquify(v, apol_syn_avrule_comp, (void *)p);
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&perm_iter);
+ if (retval != 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+apol_vector_t *apol_avrule_list_to_syn_avrules(const apol_policy_t * p, const apol_vector_t * rules, const apol_vector_t * perms)
+{
+ apol_bst_t *b = NULL;
+ qpol_avrule_t *rule;
+ qpol_iterator_t *iter = NULL;
+ qpol_syn_avrule_t *syn_avrule;
+ char *perm;
+ apol_vector_t *tmp_v = NULL, *v = NULL;
+ size_t i, x;
+ int retval = -1, error = 0, found_perm = 0;
+
+ if ((b = apol_bst_create(apol_syn_avrule_comp, NULL)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(rules); i++) {
+ rule = apol_vector_get_element(rules, i);
+ if (qpol_avrule_get_syn_avrule_iter(p->p, rule, &iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&syn_avrule) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ /* explicit cast to void* since bst's arbitrary data is non-const */
+ if (apol_bst_insert(b, syn_avrule, (void *)p) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ if ((tmp_v = apol_bst_get_vector(b, 1)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (perms == NULL || apol_vector_get_size(perms) == 0) {
+ v = tmp_v;
+ tmp_v = NULL;
+ } else {
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(tmp_v); i++) {
+ syn_avrule = apol_vector_get_element(tmp_v, i);
+ found_perm = 0;
+ if (qpol_syn_avrule_get_perm_iter(p->p, syn_avrule, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&perm) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (apol_vector_get_index(perms, perm, apol_str_strcmp, NULL, &x) == 0) {
+ found_perm = 1;
+ break;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ if (found_perm && apol_vector_append(v, syn_avrule) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_bst_destroy(&b);
+ qpol_iterator_destroy(&iter);
+ apol_vector_destroy(&tmp_v);
+ if (retval != 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+char *apol_avrule_render(const apol_policy_t * policy, const qpol_avrule_t * rule)
+{
+ char *tmp = NULL;
+ const char *rule_type_str, *tmp_name = NULL;
+ int error = 0;
+ uint32_t rule_type = 0;
+ const qpol_type_t *type = NULL;
+ const qpol_class_t *obj_class = NULL;
+ qpol_iterator_t *iter = NULL;
+ size_t tmp_sz = 0, num_perms = 0;
+
+ if (!policy || !rule) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* rule type */
+ if (qpol_avrule_get_rule_type(policy->p, rule, &rule_type)) {
+ return NULL;
+ }
+ if (!(rule_type &= (QPOL_RULE_ALLOW | QPOL_RULE_NEVERALLOW | QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT))) {
+ ERR(policy, "%s", "Invalid AV rule type");
+ errno = EINVAL;
+ return NULL;
+ }
+ if (!(rule_type_str = apol_rule_type_to_str(rule_type))) {
+ ERR(policy, "%s", "Could not get AV rule type's string");
+ errno = EINVAL;
+ return NULL;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", rule_type_str)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* source type */
+ if (qpol_avrule_get_source_type(policy->p, rule, &type)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* target type */
+ if (qpol_avrule_get_target_type(policy->p, rule, &type)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s : ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* object class */
+ if (qpol_avrule_get_object_class(policy->p, rule, &obj_class)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_class_get_name(policy->p, obj_class, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* perms */
+ if (qpol_avrule_get_perm_iter(policy->p, rule, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_iterator_get_size(iter, &num_perms)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (num_perms > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "{ ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ char *perm_name = NULL;
+ if (qpol_iterator_get_item(iter, (void **)&perm_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", perm_name)) {
+ error = errno;
+ free(perm_name);
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ free(perm_name);
+ tmp_name = NULL;
+ }
+ if (num_perms > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "} ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+
+ if (apol_str_append(&tmp, &tmp_sz, ";")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ qpol_iterator_destroy(&iter);
+ return tmp;
+
+ err:
+ free(tmp);
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+}
+
+char *apol_syn_avrule_render(const apol_policy_t * policy, const qpol_syn_avrule_t * rule)
+{
+ char *tmp = NULL;
+ const char *rule_type_str, *tmp_name = NULL;
+ int error = 0;
+ uint32_t rule_type = 0, star = 0, comp = 0, self = 0;
+ const qpol_type_t *type = NULL;
+ const qpol_class_t *obj_class = NULL;
+ qpol_iterator_t *iter = NULL, *iter2 = NULL;
+ size_t tmp_sz = 0, iter_sz = 0, iter2_sz = 0;
+ const qpol_type_set_t *set = NULL;
+
+ if (!policy || !rule) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* rule type */
+ if (qpol_syn_avrule_get_rule_type(policy->p, rule, &rule_type)) {
+ return NULL;
+ }
+ if (!(rule_type &= (QPOL_RULE_ALLOW | QPOL_RULE_NEVERALLOW | QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT))) {
+ ERR(policy, "%s", "Invalid AV rule type");
+ errno = EINVAL;
+ return NULL;
+ }
+ if (!(rule_type_str = apol_rule_type_to_str(rule_type))) {
+ ERR(policy, "%s", "Could not get AV rule type's string");
+ errno = EINVAL;
+ return NULL;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", rule_type_str)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* source type set */
+ if (qpol_syn_avrule_get_source_type_set(policy->p, rule, &set)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_set_get_is_star(policy->p, set, &star)) {
+ error = errno;
+ goto err;
+ }
+ if (star) {
+ if (apol_str_append(&tmp, &tmp_sz, "* ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ } else {
+ if (qpol_type_set_get_is_comp(policy->p, set, &comp)) {
+ error = errno;
+ goto err;
+ }
+ if (comp) {
+ if (apol_str_append(&tmp, &tmp_sz, "~")) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto err;
+ }
+ }
+ if (qpol_type_set_get_included_types_iter(policy->p, set, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_set_get_subtracted_types_iter(policy->p, set, &iter2)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_iterator_get_size(iter, &iter_sz) || qpol_iterator_get_size(iter2, &iter2_sz)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (iter_sz + iter2_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "{ ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter2); qpol_iterator_next(iter2)) {
+ if (qpol_iterator_get_item(iter2, (void **)&type)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "-%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&iter2);
+ if (iter_sz + iter2_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "} ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ }
+
+ /* target type set */
+ if (qpol_syn_avrule_get_target_type_set(policy->p, rule, &set)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_set_get_is_star(policy->p, set, &star)) {
+ error = errno;
+ goto err;
+ }
+ if (star) {
+ if (apol_str_append(&tmp, &tmp_sz, "* ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ } else {
+ if (qpol_type_set_get_is_comp(policy->p, set, &comp)) {
+ error = errno;
+ goto err;
+ }
+ if (comp) {
+ if (apol_str_append(&tmp, &tmp_sz, "~")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ if (qpol_type_set_get_included_types_iter(policy->p, set, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_set_get_subtracted_types_iter(policy->p, set, &iter2)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_iterator_get_size(iter, &iter_sz) || qpol_iterator_get_size(iter2, &iter2_sz)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_syn_avrule_get_is_target_self(policy->p, rule, &self)) {
+ error = errno;
+ goto err;
+ }
+ if (iter_sz + iter2_sz + self > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "{ ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter2); qpol_iterator_next(iter2)) {
+ if (qpol_iterator_get_item(iter2, (void **)&type)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "-%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&iter2);
+ if (self) {
+ if (apol_str_append(&tmp, &tmp_sz, "self ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ if (iter_sz + iter2_sz + self > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "} ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ }
+
+ if (apol_str_append(&tmp, &tmp_sz, ": ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* object classes */
+ if (qpol_syn_avrule_get_class_iter(policy->p, rule, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_iterator_get_size(iter, &iter_sz)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (iter_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "{ ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&obj_class)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_class_get_name(policy->p, obj_class, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ if (iter_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "} ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+
+ /* permissions */
+ if (qpol_syn_avrule_get_perm_iter(policy->p, rule, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_iterator_get_size(iter, &iter_sz)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (iter_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "{ ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ if (iter_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "} ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+
+ if (apol_str_append(&tmp, &tmp_sz, ";")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ return tmp;
+
+ err:
+ free(tmp);
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&iter2);
+ errno = error;
+ return NULL;
+}
diff --git a/libapol/src/bool-query.c b/libapol/src/bool-query.c
new file mode 100644
index 0000000..8147c3a
--- /dev/null
+++ b/libapol/src/bool-query.c
@@ -0,0 +1,111 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about conditional
+ * booleans within a policy. The caller obtains a query object, fills
+ * in its parameters, and then runs the query; it obtains a vector of
+ * results. Searches are conjunctive -- all fields of the search
+ * query must match for a datum to be added to the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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>
+
+struct apol_bool_query
+{
+ char *bool_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+/******************** booleans queries ********************/
+
+int apol_bool_get_by_query(const apol_policy_t * p, apol_bool_query_t * b, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1;
+ *v = NULL;
+ if (qpol_policy_get_bool_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_bool_t *qbool;
+ if (qpol_iterator_get_item(iter, (void **)&qbool) < 0) {
+ goto cleanup;
+ }
+ if (b != NULL) {
+ const char *bool_name;
+ int compval;
+ if (qpol_bool_get_name(p->p, qbool, &bool_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, bool_name, b->bool_name, b->flags, &(b->regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, qbool)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_bool_query_t *apol_bool_query_create(void)
+{
+ return calloc(1, sizeof(apol_bool_query_t));
+}
+
+void apol_bool_query_destroy(apol_bool_query_t ** b)
+{
+ if (*b != NULL) {
+ free((*b)->bool_name);
+ apol_regex_destroy(&(*b)->regex);
+ free(*b);
+ *b = NULL;
+ }
+}
+
+int apol_bool_query_set_bool(const apol_policy_t * p, apol_bool_query_t * b, const char *name)
+{
+ return apol_query_set(p, &b->bool_name, &b->regex, name);
+}
+
+int apol_bool_query_set_regex(const apol_policy_t * p, apol_bool_query_t * b, int is_regex)
+{
+ return apol_query_set_regex(p, &b->flags, is_regex);
+}
diff --git a/libapol/src/bst.c b/libapol/src/bst.c
new file mode 100644
index 0000000..df0beb6
--- /dev/null
+++ b/libapol/src/bst.c
@@ -0,0 +1,352 @@
+/**
+ * @file
+ * Contains the implementation of a generic binary search tree. The
+ * tree is implemented as a red-black tree, as inspired by Julienne
+ * Walker (http://eternallyconfuzzled.com/tuts/redblack.html).
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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 <apol/bst.h>
+#include <apol/vector.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vector-internal.h"
+
+typedef struct bst_node
+{
+ void *elem;
+ int is_red;
+ struct bst_node *child[2];
+} bst_node_t;
+
+/**
+ * Generic binary search tree structure. Stores elements as void*.
+ */
+struct apol_bst
+{
+ /** Comparison function for nodes. */
+ apol_bst_comp_func *cmp;
+ /** Destroy function for the nodes, or NULL to not free each node. */
+ apol_bst_free_func *fr;
+ /** The number of elements currently stored in the bst. */
+ size_t size;
+ /** Pointer to top of the tree. */
+ bst_node_t *head;
+};
+
+apol_bst_t *apol_bst_create(apol_bst_comp_func * cmp, apol_bst_free_func * fr)
+{
+ apol_bst_t *b = NULL;
+ if ((b = calloc(1, sizeof(*b))) == NULL) {
+ return NULL;
+ }
+ b->cmp = cmp;
+ b->fr = fr;
+ return b;
+}
+
+/**
+ * Free the data stored within a bst node, recurse through the node's
+ * children, and then the node itself.
+ *
+ * @param node Node to free. If NULL then do stop recursing.
+ * @param fr Callback to free a node's data. If NULL then do not free
+ * the data.
+ */
+static void bst_node_free(bst_node_t * node, apol_bst_free_func * fr)
+{
+ if (node != NULL) {
+ if (fr != NULL) {
+ fr(node->elem);
+ }
+ bst_node_free(node->child[0], fr);
+ bst_node_free(node->child[1], fr);
+ free(node);
+ }
+}
+
+void apol_bst_destroy(apol_bst_t ** b)
+{
+ if (!b || !(*b))
+ return;
+ bst_node_free((*b)->head, (*b)->fr);
+ (*b)->head = NULL;
+ free(*b);
+ *b = NULL;
+}
+
+/**
+ * Given a BST node, traverse the node infix, appending the node's
+ * element to vector v.
+ *
+ * @param node BST node to recurse.
+ * @param v Vector to which append.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int bst_node_to_vector(bst_node_t * node, apol_vector_t * v)
+{
+ int retval;
+ if (node == NULL) {
+ return 0;
+ }
+ if ((retval = bst_node_to_vector(node->child[0], v)) < 0) {
+ return retval;
+ }
+ if ((retval = apol_vector_append(v, node->elem)) < 0) {
+ return retval;
+ }
+ return bst_node_to_vector(node->child[1], v);
+}
+
+apol_vector_t *apol_bst_get_vector(apol_bst_t * b, int change_owner)
+{
+ apol_vector_t *v = NULL;
+ if (!b) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((v = apol_vector_create_with_capacity(b->size, NULL)) == NULL) {
+ return NULL;
+ }
+ if (bst_node_to_vector(b->head, v) < 0) {
+ int error = errno;
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ if (change_owner) {
+ vector_set_free_func(v, b->fr);
+ b->fr = NULL;
+ }
+ return v;
+}
+
+size_t apol_bst_get_size(const apol_bst_t * b)
+{
+ if (!b) {
+ errno = EINVAL;
+ return 0;
+ } else {
+ return b->size;
+ }
+}
+
+int apol_bst_get_element(const apol_bst_t * b, const void *elem, void *data, void **result)
+{
+ bst_node_t *node;
+ int compval;
+ if (!b || !result) {
+ errno = EINVAL;
+ return -1;
+ }
+ node = b->head;
+ while (node != NULL) {
+ if (b->cmp != NULL) {
+ compval = b->cmp(node->elem, elem, data);
+ } else {
+ char *p1 = (char *)node->elem;
+ char *p2 = (char *)elem;
+ if (p1 < p2) {
+ compval = -1;
+ } else if (p1 > p2) {
+ compval = 1;
+ } else {
+ compval = 0;
+ }
+ }
+ if (compval == 0) {
+ *result = node->elem;
+ return 0;
+ } else if (compval > 0) {
+ node = node->child[0];
+ } else {
+ node = node->child[1];
+ }
+ }
+ return -1;
+}
+
+/**
+ * Allocate and return a new BST node, with data set to elem and color
+ * to red. Also increment the tree's size.
+ *
+ * @param b BST size to increment.
+ * @param elem Value for the node.
+ *
+ * @return Allocated BST node, which the caller must insert, or NULL
+ * on error.
+ */
+static bst_node_t *bst_node_make(apol_bst_t * b, void *elem)
+{
+ bst_node_t *new_node;
+ if ((new_node = calloc(1, sizeof(*new_node))) == NULL) {
+ return NULL;
+ }
+ new_node->elem = elem;
+ new_node->is_red = 1;
+ b->size++;
+ return new_node;
+}
+
+/**
+ * Determines if a node is red or not.
+ *
+ * @param node Node to check. If NULL then treat the node as black.
+ *
+ * @return 0 if the node is black, 1 if red.
+ */
+static int bst_node_is_red(bst_node_t * node)
+{
+ return node != NULL && node->is_red;
+}
+
+static bst_node_t *bst_rotate_single(bst_node_t * root, int dir)
+{
+ bst_node_t *save = root->child[!dir];
+ root->child[!dir] = save->child[dir];
+ save->child[dir] = root;
+ root->is_red = 1;
+ save->is_red = 0;
+ return save;
+}
+
+static bst_node_t *bst_rotate_double(bst_node_t * root, int dir)
+{
+ root->child[!dir] = bst_rotate_single(root->child[!dir], !dir);
+ return bst_rotate_single(root, dir);
+}
+
+static bst_node_t *bst_insert_recursive(apol_bst_t * b, bst_node_t * root, void **elem, void *data, apol_bst_free_func * fr,
+ int *not_uniq)
+{
+ int compval, dir;
+ if (root == NULL) {
+ if ((root = bst_node_make(b, *elem)) == NULL) {
+ *not_uniq = -1;
+ return NULL;
+ }
+ *not_uniq = 0;
+ } else {
+ if (b->cmp != NULL) {
+ compval = b->cmp(root->elem, *elem, data);
+ } else {
+ char *p1 = (char *)root->elem;
+ char *p2 = (char *)(*elem);
+ if (p1 < p2) {
+ compval = -1;
+ } else if (p1 > p2) {
+ compval = 1;
+ } else {
+ compval = 0;
+ }
+ }
+ if (compval == 0) {
+ /* already exists */
+ if (fr != NULL) {
+ fr(*elem);
+ }
+ *elem = root->elem;
+ *not_uniq = 1;
+ return root;
+ } else if (compval > 0) {
+ dir = 0;
+ } else {
+ dir = 1;
+ }
+ root->child[dir] = bst_insert_recursive(b, root->child[dir], elem, data, fr, not_uniq);
+ if (*not_uniq != 0) {
+ return root;
+ }
+
+ /* rebalance tree */
+ if (bst_node_is_red(root->child[dir])) {
+ if (bst_node_is_red(root->child[!dir])) {
+ /* recolor myself and children. note
+ * that this can't be reached if a
+ * child is NULL */
+ root->is_red = 1;
+ root->child[0]->is_red = 0;
+ root->child[1]->is_red = 0;
+ } else {
+ if (bst_node_is_red(root->child[dir]->child[dir])) {
+ root = bst_rotate_single(root, !dir);
+ } else if (bst_node_is_red(root->child[dir]->child[!dir])) {
+ root = bst_rotate_double(root, !dir);
+ }
+ }
+ }
+ }
+ return root;
+}
+
+int apol_bst_insert(apol_bst_t * b, void *elem, void *data)
+{
+ int retval = -1;
+ if (!b || !elem) {
+ errno = EINVAL;
+ return -1;
+ }
+ b->head = bst_insert_recursive(b, b->head, &elem, data, NULL, &retval);
+ if (retval >= 0) {
+ b->head->is_red = 0;
+ }
+ return retval;
+}
+
+int apol_bst_insert_and_get(apol_bst_t * b, void **elem, void *data)
+{
+ int retval = -1;
+ if (!b || !elem) {
+ errno = EINVAL;
+ return -1;
+ }
+ b->head = bst_insert_recursive(b, b->head, elem, data, b->fr, &retval);
+ if (retval >= 0) {
+ b->head->is_red = 0;
+ }
+ return retval;
+}
+
+static int bst_inorder_map(const bst_node_t * node, int (*fn) (void *, void *), void *data)
+{
+ int retval;
+ if (node == NULL) {
+ return 0;
+ }
+ if ((retval = bst_inorder_map(node->child[0], fn, data)) < 0) {
+ return retval;
+ }
+ if ((retval = fn(node->elem, data)) < 0) {
+ return retval;
+ }
+ return bst_inorder_map(node->child[1], fn, data);
+}
+
+int apol_bst_inorder_map(const apol_bst_t * b, int (*fn) (void *, void *), void *data)
+{
+ if (b == NULL || fn == NULL)
+ return -1;
+ return bst_inorder_map(b->head, fn, data);
+}
diff --git a/libapol/src/class-perm-query.c b/libapol/src/class-perm-query.c
new file mode 100644
index 0000000..938994d
--- /dev/null
+++ b/libapol/src/class-perm-query.c
@@ -0,0 +1,327 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about classes, commons,
+ * and permissions within a policy. The caller obtains a query
+ * object, fills in its parameters, and then runs the query; it
+ * obtains a vector of results. Searches are conjunctive -- all
+ * fields of the search query must match for a datum to be added to
+ * the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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>
+
+struct apol_class_query
+{
+ char *class_name, *common_name;
+ unsigned int flags;
+ regex_t *class_regex, *common_regex;
+};
+
+struct apol_common_query
+{
+ char *common_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+struct apol_perm_query
+{
+ char *perm_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+/******************** class queries ********************/
+
+int apol_class_get_by_query(const apol_policy_t * p, apol_class_query_t * c, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL, *perm_iter = NULL;
+ int retval = -1, append_class;
+ *v = NULL;
+ if (qpol_policy_get_class_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_class_t *class_datum;
+ if (qpol_iterator_get_item(iter, (void **)&class_datum) < 0) {
+ goto cleanup;
+ }
+ append_class = 1;
+ if (c != NULL) {
+ const char *class_name, *common_name = NULL;
+ const qpol_common_t *common_datum;
+ int compval;
+ if (qpol_class_get_name(p->p, class_datum, &class_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, class_name, c->class_name, c->flags, &(c->class_regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ if (qpol_class_get_common(p->p, class_datum, &common_datum) < 0) {
+ goto cleanup;
+ }
+ if (common_datum == NULL) {
+ if (c->common_name != NULL && c->common_name[0] != '\0') {
+ continue;
+ }
+ } else {
+ if (qpol_common_get_name(p->p, common_datum, &common_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, common_name, c->common_name, c->flags, &(c->common_regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ }
+ }
+ if (append_class && apol_vector_append(*v, class_datum)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&perm_iter);
+ return retval;
+}
+
+apol_class_query_t *apol_class_query_create(void)
+{
+ return calloc(1, sizeof(apol_class_query_t));
+}
+
+void apol_class_query_destroy(apol_class_query_t ** c)
+{
+ if (*c != NULL) {
+ free((*c)->class_name);
+ free((*c)->common_name);
+ apol_regex_destroy(&(*c)->class_regex);
+ apol_regex_destroy(&(*c)->common_regex);
+ free(*c);
+ *c = NULL;
+ }
+}
+
+int apol_class_query_set_class(const apol_policy_t * p, apol_class_query_t * c, const char *name)
+{
+ return apol_query_set(p, &c->class_name, &c->class_regex, name);
+}
+
+int apol_class_query_set_common(const apol_policy_t * p, apol_class_query_t * c, const char *name)
+{
+ return apol_query_set(p, &c->common_name, &c->common_regex, name);
+}
+
+int apol_class_query_set_regex(const apol_policy_t * p, apol_class_query_t * c, int is_regex)
+{
+ return apol_query_set_regex(p, &c->flags, is_regex);
+}
+
+/******************** common queries ********************/
+
+int apol_common_get_by_query(const apol_policy_t * p, apol_common_query_t * c, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL;
+ int retval = -1;
+ *v = NULL;
+ if (qpol_policy_get_common_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_common_t *common_datum;
+ if (qpol_iterator_get_item(iter, (void **)&common_datum) < 0) {
+ goto cleanup;
+ }
+ if (c != NULL) {
+ const char *common_name = NULL;
+ int compval;
+ if (qpol_common_get_name(p->p, common_datum, &common_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, common_name, c->common_name, c->flags, &(c->regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, common_datum)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_common_query_t *apol_common_query_create(void)
+{
+ return calloc(1, sizeof(apol_common_query_t));
+}
+
+void apol_common_query_destroy(apol_common_query_t ** c)
+{
+ if (*c != NULL) {
+ free((*c)->common_name);
+ apol_regex_destroy(&(*c)->regex);
+ free(*c);
+ *c = NULL;
+ }
+}
+
+int apol_common_query_set_common(const apol_policy_t * p, apol_common_query_t * c, const char *name)
+{
+ return apol_query_set(p, &c->common_name, &c->regex, name);
+}
+
+int apol_common_query_set_regex(const apol_policy_t * p, apol_common_query_t * c, int is_regex)
+{
+ return apol_query_set_regex(p, &c->flags, is_regex);
+}
+
+/******************** permission queries ********************/
+
+int apol_perm_get_by_query(const apol_policy_t * p, apol_perm_query_t * pq, apol_vector_t ** v)
+{
+ qpol_iterator_t *class_iter = NULL, *common_iter = NULL, *perm_iter = NULL;
+ int retval = -1, compval;
+ char *perm_name;
+ *v = NULL;
+ if (qpol_policy_get_class_iter(p->p, &class_iter) < 0 || qpol_policy_get_common_iter(p->p, &common_iter) < 0) {
+ goto cleanup;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(class_iter); qpol_iterator_next(class_iter)) {
+ qpol_class_t *class_datum;
+ if (qpol_iterator_get_item(class_iter, (void **)&class_datum) < 0 ||
+ qpol_class_get_perm_iter(p->p, class_datum, &perm_iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) {
+ if (qpol_iterator_get_item(perm_iter, (void **)&perm_name) < 0) {
+ goto cleanup;
+ }
+ if (pq == NULL) {
+ compval = 1;
+ } else {
+ compval = apol_compare(p, perm_name, pq->perm_name, pq->flags, &(pq->regex));
+ }
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 1 && apol_vector_append_unique(*v, perm_name, apol_str_strcmp, NULL) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ qpol_iterator_destroy(&perm_iter);
+ }
+
+ for (; !qpol_iterator_end(common_iter); qpol_iterator_next(common_iter)) {
+ qpol_common_t *common_datum;
+ if (qpol_iterator_get_item(common_iter, (void **)&common_datum) < 0 ||
+ qpol_common_get_perm_iter(p->p, common_datum, &perm_iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) {
+ if (qpol_iterator_get_item(perm_iter, (void **)&perm_name) < 0) {
+ goto cleanup;
+ }
+ if (pq == NULL) {
+ compval = 1;
+ } else {
+ compval = apol_compare(p, perm_name, pq->perm_name, pq->flags, &(pq->regex));
+ }
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 1 && apol_vector_append_unique(*v, perm_name, apol_str_strcmp, NULL) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ qpol_iterator_destroy(&perm_iter);
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&class_iter);
+ qpol_iterator_destroy(&common_iter);
+ qpol_iterator_destroy(&perm_iter);
+ return retval;
+}
+
+apol_perm_query_t *apol_perm_query_create(void)
+{
+ return calloc(1, sizeof(apol_perm_query_t));
+}
+
+void apol_perm_query_destroy(apol_perm_query_t ** pq)
+{
+ if (*pq != NULL) {
+ free((*pq)->perm_name);
+ apol_regex_destroy(&(*pq)->regex);
+ free(*pq);
+ *pq = NULL;
+ }
+}
+
+int apol_perm_query_set_perm(const apol_policy_t * p, apol_perm_query_t * pq, const char *name)
+{
+ return apol_query_set(p, &pq->perm_name, &pq->regex, name);
+}
+
+int apol_perm_query_set_regex(const apol_policy_t * p, apol_perm_query_t * pq, int is_regex)
+{
+ return apol_query_set_regex(p, &pq->flags, is_regex);
+}
diff --git a/libapol/src/condrule-query.c b/libapol/src/condrule-query.c
new file mode 100644
index 0000000..451b617
--- /dev/null
+++ b/libapol/src/condrule-query.c
@@ -0,0 +1,182 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about conditional
+ * expressions rules within a policy. The caller obtains a query
+ * object, fills in its parameters, and then runs the query; it
+ * obtains a vector of results. Searches are conjunctive -- all
+ * fields of the search query must match for a datum to be added to
+ * the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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>
+
+struct apol_cond_query
+{
+ char *bool_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+int apol_cond_get_by_query(const apol_policy_t * p, apol_cond_query_t * c, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL;
+ int retval = -1;
+ *v = NULL;
+
+ if (qpol_policy_get_cond_iter(p->p, &iter) < 0) {
+ goto cleanup;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_cond_t *cond;
+ if (qpol_iterator_get_item(iter, (void **)&cond) < 0) {
+ goto cleanup;
+ }
+ if (c != NULL) {
+ int keep_cond = apol_compare_cond_expr(p, cond, c->bool_name, c->flags, &c->regex);
+ if (keep_cond < 0) {
+ goto cleanup;
+ } else if (keep_cond == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, cond)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_cond_query_t *apol_cond_query_create(void)
+{
+ return calloc(1, sizeof(apol_cond_query_t));
+}
+
+void apol_cond_query_destroy(apol_cond_query_t ** c)
+{
+ if (*c != NULL) {
+ free((*c)->bool_name);
+ apol_regex_destroy(&(*c)->regex);
+ free(*c);
+ *c = NULL;
+ }
+}
+
+int apol_cond_query_set_bool(const apol_policy_t * p, apol_cond_query_t * c, const char *name)
+{
+ return apol_query_set(p, &c->bool_name, &c->regex, name);
+}
+
+int apol_cond_query_set_regex(const apol_policy_t * p, apol_cond_query_t * c, int is_regex)
+{
+ return apol_query_set_regex(p, &c->flags, is_regex);
+}
+
+char *apol_cond_expr_render(const apol_policy_t * p, const qpol_cond_t * cond)
+{
+ qpol_iterator_t *iter = NULL;
+ qpol_cond_expr_node_t *expr = NULL;
+ char *tmp = NULL;
+ const char *bool_name = NULL;
+ int error = 0;
+ size_t tmp_sz = 0, i;
+ uint32_t expr_type = 0;
+ qpol_bool_t *cond_bool = NULL;
+
+ if (!p || !cond) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ if (qpol_cond_get_expr_node_iter(p->p, cond, &iter) < 0) {
+ error = errno;
+ goto err;
+ }
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&expr)) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_cond_expr_node_get_expr_type(p->p, expr, &expr_type)) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ if (expr_type != QPOL_COND_EXPR_BOOL) {
+ if (apol_str_append(&tmp, &tmp_sz, apol_cond_expr_type_to_str(expr_type))) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ } else {
+ if (qpol_cond_expr_node_get_bool(p->p, expr, &cond_bool)) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_bool_get_name(p->p, cond_bool, &bool_name)) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ if (apol_str_append(&tmp, &tmp_sz, bool_name)) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ }
+ if (apol_str_append(&tmp, &tmp_sz, " ")) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ }
+
+ /* remove trailing space */
+ i = strlen(tmp);
+ if (i > 1) {
+ tmp[i - 1] = '\0';
+ }
+ qpol_iterator_destroy(&iter);
+ return tmp;
+
+ err:
+ qpol_iterator_destroy(&iter);
+ free(tmp);
+ errno = error;
+ return NULL;
+}
diff --git a/libapol/src/constraint-query.c b/libapol/src/constraint-query.c
new file mode 100644
index 0000000..5495975
--- /dev/null
+++ b/libapol/src/constraint-query.c
@@ -0,0 +1,217 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about constraint and
+ * validatetrans statements within a policy. The caller obtains a
+ * query object, fills in its parameters, and then runs the query; it
+ * obtains a vector of results. Searches are conjunctive -- all
+ * fields of the search query must match for a datum to be added to
+ * the results.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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>
+
+struct apol_constraint_query
+{
+ char *class_name, *perm_name;
+ unsigned int flags;
+ regex_t *class_regex, *perm_regex;
+};
+
+struct apol_validatetrans_query
+{
+ char *class_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+/******************** constraint queries ********************/
+
+int apol_constraint_get_by_query(const apol_policy_t * p, apol_constraint_query_t * c, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL, *perm_iter = NULL;
+ int retval = -1;
+ *v = NULL;
+ if (qpol_policy_get_constraint_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_constraint_t *constraint;
+ if (qpol_iterator_get_item(iter, (void **)&constraint) < 0) {
+ goto cleanup;
+ }
+ if (c != NULL) {
+ const qpol_class_t *class_datum;
+ const char *class_name;
+ int compval;
+ if (qpol_constraint_get_class(p->p, constraint, &class_datum) < 0 ||
+ qpol_class_get_name(p->p, class_datum, &class_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, class_name, c->class_name, c->flags, &(c->class_regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ free(constraint);
+ continue;
+ }
+
+ if (qpol_constraint_get_perm_iter(p->p, constraint, &perm_iter) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare_iter(p, perm_iter, c->perm_name, c->flags, &(c->perm_regex), 1);
+ qpol_iterator_destroy(&perm_iter);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ free(constraint);
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, constraint)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&perm_iter);
+ return retval;
+}
+
+apol_constraint_query_t *apol_constraint_query_create(void)
+{
+ return calloc(1, sizeof(apol_constraint_query_t));
+}
+
+void apol_constraint_query_destroy(apol_constraint_query_t ** c)
+{
+ if (*c != NULL) {
+ free((*c)->class_name);
+ free((*c)->perm_name);
+ apol_regex_destroy(&(*c)->class_regex);
+ apol_regex_destroy(&(*c)->perm_regex);
+ free(*c);
+ *c = NULL;
+ }
+}
+
+int apol_constraint_query_set_class(const apol_policy_t * p, apol_constraint_query_t * c, const char *name)
+{
+ return apol_query_set(p, &c->class_name, &c->class_regex, name);
+}
+
+int apol_constraint_query_set_perm(const apol_policy_t * p, apol_constraint_query_t * c, const char *name)
+{
+ return apol_query_set(p, &c->perm_name, &c->perm_regex, name);
+}
+
+int apol_constraint_query_set_regex(const apol_policy_t * p, apol_constraint_query_t * c, int is_regex)
+{
+ return apol_query_set_regex(p, &c->flags, is_regex);
+}
+
+/******************** validatetrans queries ********************/
+
+int apol_validatetrans_get_by_query(const apol_policy_t * p, apol_validatetrans_query_t * vt, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL;
+ int retval = -1;
+ *v = NULL;
+ if (qpol_policy_get_validatetrans_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_validatetrans_t *validatetrans;
+ if (qpol_iterator_get_item(iter, (void **)&validatetrans) < 0) {
+ goto cleanup;
+ }
+ if (vt != NULL) {
+ const qpol_class_t *class_datum;
+ const char *class_name;
+ int compval;
+ if (qpol_validatetrans_get_class(p->p, validatetrans, &class_datum) < 0 ||
+ qpol_class_get_name(p->p, class_datum, &class_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, class_name, vt->class_name, vt->flags, &(vt->regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ free(validatetrans);
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, validatetrans)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_validatetrans_query_t *apol_validatetrans_query_create(void)
+{
+ return calloc(1, sizeof(apol_validatetrans_query_t));
+}
+
+void apol_validatetrans_query_destroy(apol_validatetrans_query_t ** vt)
+{
+ if (*vt != NULL) {
+ free((*vt)->class_name);
+ apol_regex_destroy(&(*vt)->regex);
+ free(*vt);
+ *vt = NULL;
+ }
+}
+
+int apol_validatetrans_query_set_class(const apol_policy_t * p, apol_validatetrans_query_t * vt, const char *name)
+{
+ return apol_query_set(p, &vt->class_name, &vt->regex, name);
+}
+
+int apol_validatetrans_query_set_regex(const apol_policy_t * p, apol_validatetrans_query_t * vt, int is_regex)
+{
+ return apol_query_set_regex(p, &vt->flags, is_regex);
+}
diff --git a/libapol/src/context-query.c b/libapol/src/context-query.c
new file mode 100644
index 0000000..90c7fbe
--- /dev/null
+++ b/libapol/src/context-query.c
@@ -0,0 +1,477 @@
+/**
+ * @file
+ * Implementation for querying aspects of a context.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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 <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <apol/render.h>
+
+struct apol_context
+{
+ char *user, *role, *type;
+ apol_mls_range_t *range;
+};
+
+apol_context_t *apol_context_create(void)
+{
+ return calloc(1, sizeof(apol_context_t));
+}
+
+apol_context_t *apol_context_create_from_qpol_context(const apol_policy_t * p, const qpol_context_t * context)
+{
+ apol_context_t *c = NULL;
+ const qpol_user_t *user;
+ const qpol_role_t *role;
+ const qpol_type_t *type;
+ const qpol_mls_range_t *range;
+ const char *user_name, *role_name, *type_name;
+ apol_mls_range_t *apol_range = NULL;
+ if ((c = apol_context_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto err;
+ }
+ if (qpol_context_get_user(p->p, context, &user) < 0 ||
+ qpol_context_get_role(p->p, context, &role) < 0 ||
+ qpol_context_get_type(p->p, context, &type) < 0 || qpol_context_get_range(p->p, context, &range) < 0) {
+ goto err;
+ }
+ if (qpol_user_get_name(p->p, user, &user_name) < 0 ||
+ qpol_role_get_name(p->p, role, &role_name) < 0 || qpol_type_get_name(p->p, type, &type_name) < 0) {
+ goto err;
+ }
+ if (qpol_policy_has_capability(p->p, QPOL_CAP_MLS)) {
+ /* if the policy is MLS then convert the range, else
+ * rely upon the default value of NULL */
+ if ((apol_range = apol_mls_range_create_from_qpol_mls_range(p, range)) == NULL) {
+ goto err;
+ }
+ }
+ if (apol_context_set_user(p, c, user_name) < 0 ||
+ apol_context_set_role(p, c, role_name) < 0 ||
+ apol_context_set_type(p, c, type_name) < 0 || apol_context_set_range(p, c, apol_range) < 0) {
+ goto err;
+ }
+ return c;
+ err:
+ apol_mls_range_destroy(&apol_range);
+ apol_context_destroy(&c);
+ return NULL;
+}
+
+apol_context_t *apol_context_create_from_literal(const char *context_string)
+{
+ apol_context_t *c = NULL;
+ bool is_context_compiled = false;
+ regex_t context_regex;
+ const size_t nmatch = 5;
+ regmatch_t pmatch[nmatch];
+
+ if ((c = apol_context_create()) == NULL) {
+ goto err;
+ }
+
+ if (regcomp(&context_regex, "^([^:]*):([^:]*):([^:]*):?(.*)$", REG_EXTENDED) != 0) {
+ goto err;
+ }
+ is_context_compiled = true;
+
+ if (regexec(&context_regex, context_string, nmatch, pmatch, 0) != 0) {
+ errno = EIO;
+ goto err;
+ }
+
+ const char *s;
+ size_t len;
+
+ assert(pmatch[1].rm_so == 0);
+ s = context_string + pmatch[1].rm_so;
+ len = pmatch[1].rm_eo - pmatch[1].rm_so; // no +1 to avoid copying colon
+ if (len != 0 && *s != '*' && (c->user = strndup(s, len)) == NULL) {
+ goto err;
+ }
+
+ assert(pmatch[2].rm_so != -1);
+ s = context_string + pmatch[2].rm_so;
+ len = pmatch[2].rm_eo - pmatch[2].rm_so; // no +1 to avoid copying colon
+ if (len != 0 && *s != '*' && (c->role = strndup(s, len)) == NULL) {
+ goto err;
+ }
+
+ assert(pmatch[3].rm_so != -1);
+ s = context_string + pmatch[3].rm_so;
+ len = pmatch[3].rm_eo - pmatch[3].rm_so; // no +1 to avoid copying colon
+ if (len != 0 && *s != '*' && (c->type = strndup(s, len)) == NULL) {
+ goto err;
+ }
+
+ if (pmatch[4].rm_so != -1) {
+ s = context_string + pmatch[4].rm_so;
+ len = pmatch[4].rm_eo - pmatch[4].rm_so;
+ if (len != 0 && *s != '*' && (c->range = apol_mls_range_create_from_literal(s)) == NULL) {
+ goto err;
+ }
+ }
+
+ regfree(&context_regex);
+ return c;
+
+ err:
+ apol_context_destroy(&c);
+ if (is_context_compiled) {
+ regfree(&context_regex);
+ }
+ return NULL;
+}
+
+void apol_context_destroy(apol_context_t ** context)
+{
+ if (*context != NULL) {
+ free((*context)->user);
+ free((*context)->role);
+ free((*context)->type);
+ apol_mls_range_destroy(&((*context)->range));
+ free(*context);
+ *context = NULL;
+ }
+}
+
+int apol_context_set_user(const apol_policy_t * p, apol_context_t * context, const char *user)
+{
+ if (context == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (user != context->user) {
+ free(context->user);
+ context->user = NULL;
+ if (user != NULL && (context->user = strdup(user)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_context_set_role(const apol_policy_t * p, apol_context_t * context, const char *role)
+{
+ if (context == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (role != context->role) {
+ free(context->role);
+ context->role = NULL;
+ if (role != NULL && (context->role = strdup(role)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_context_set_type(const apol_policy_t * p, apol_context_t * context, const char *type)
+{
+ if (context == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (type != context->type) {
+ free(context->type);
+ context->type = NULL;
+ if (type != NULL && (context->type = strdup(type)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_context_set_range(const apol_policy_t * p, apol_context_t * context, apol_mls_range_t * range)
+{
+ if (context == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (range != context->range) {
+ apol_mls_range_destroy(&(context->range));
+ context->range = range;
+ }
+ return 0;
+}
+
+const char *apol_context_get_user(const apol_context_t * context)
+{
+ if (context == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return context->user;
+}
+
+const char *apol_context_get_role(const apol_context_t * context)
+{
+ if (context == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return context->role;
+}
+
+const char *apol_context_get_type(const apol_context_t * context)
+{
+ if (context == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return context->type;
+}
+
+const apol_mls_range_t *apol_context_get_range(const apol_context_t * context)
+{
+ if (context == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return context->range;
+}
+
+int apol_context_compare(const apol_policy_t * p, const apol_context_t * target, const apol_context_t * search,
+ unsigned int range_compare_type)
+{
+ uint32_t value0, value1;
+ if (p == NULL || target == NULL || search == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (target->user != NULL && search->user != NULL) {
+ const qpol_user_t *user0, *user1;
+ if (qpol_policy_get_user_by_name(p->p,
+ target->user, &user0) < 0 ||
+ qpol_policy_get_user_by_name(p->p,
+ search->user, &user1) < 0 ||
+ qpol_user_get_value(p->p, user0, &value0) < 0 || qpol_user_get_value(p->p, user1, &value1) < 0) {
+ return -1;
+ }
+ if (value0 != value1) {
+ return 0;
+ }
+ }
+ if (target->role != NULL && search->role != NULL) {
+ const qpol_role_t *role0, *role1;
+ if (qpol_policy_get_role_by_name(p->p,
+ target->role, &role0) < 0 ||
+ qpol_policy_get_role_by_name(p->p,
+ search->role, &role1) < 0 ||
+ qpol_role_get_value(p->p, role0, &value0) < 0 || qpol_role_get_value(p->p, role1, &value1) < 0) {
+ return -1;
+ }
+ if (value0 != value1) {
+ return 0;
+ }
+ }
+ if (target->type != NULL && search->type != NULL) {
+ const qpol_type_t *type0, *type1;
+ if (qpol_policy_get_type_by_name(p->p,
+ target->type, &type0) < 0 ||
+ qpol_policy_get_type_by_name(p->p,
+ search->type, &type1) < 0 ||
+ qpol_type_get_value(p->p, type0, &value0) < 0 || qpol_type_get_value(p->p, type1, &value1) < 0) {
+ return -1;
+ }
+ if (value0 != value1) {
+ return 0;
+ }
+ }
+ if (target->range != NULL && search->range != NULL) {
+ return apol_mls_range_compare(p, target->range, search->range, range_compare_type);
+ }
+ return 1;
+}
+
+int apol_context_validate(const apol_policy_t * p, const apol_context_t * context)
+{
+ if (context == NULL ||
+ context->user == NULL ||
+ context->role == NULL || context->type == NULL || (apol_policy_is_mls(p) && context->range == NULL)) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ return apol_context_validate_partial(p, context);
+}
+
+int apol_context_validate_partial(const apol_policy_t * p, const apol_context_t * context)
+{
+ apol_user_query_t *user_query = NULL;
+ apol_role_query_t *role_query = NULL;
+ apol_vector_t *user_v = NULL, *role_v = NULL;
+ const qpol_user_t *user;
+ const qpol_type_t *type;
+ const qpol_mls_range_t *user_range;
+ apol_mls_range_t *user_apol_range = NULL;
+ int retval = -1, retval2;
+
+ if (context == NULL) {
+ return 1;
+ }
+ if (context->user != NULL) {
+ if ((user_query = apol_user_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ }
+ if (apol_user_query_set_user(p, user_query, context->user) < 0 ||
+ (context->role != NULL && apol_user_query_set_role(p, user_query, context->role) < 0) ||
+ apol_user_get_by_query(p, user_query, &user_v) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_size(user_v) == 0) {
+ retval = 0;
+ goto cleanup;
+ }
+ }
+ if (context->role != NULL) {
+ if ((role_query = apol_role_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ }
+ if (apol_role_query_set_role(p, role_query, context->role) < 0 ||
+ (context->type != NULL && apol_role_query_set_type(p, role_query, context->type) < 0) ||
+ apol_role_get_by_query(p, role_query, &role_v) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_size(role_v) == 0) {
+ retval = 0;
+ goto cleanup;
+ }
+ }
+ if (context->type != NULL) {
+ if (qpol_policy_get_type_by_name(p->p, context->type, &type) < 0) {
+ retval = 0;
+ goto cleanup;
+ }
+ }
+ if (apol_policy_is_mls(p) && context->range != NULL) {
+ retval2 = apol_mls_range_validate(p, context->range);
+ if (retval2 != 1) {
+ retval = retval2;
+ goto cleanup;
+ }
+ /* next check that the user has access to this context */
+ if (context->user != NULL) {
+ if (qpol_policy_get_user_by_name(p->p, context->user, &user) < 0 ||
+ qpol_user_get_range(p->p, user, &user_range) < 0) {
+ goto cleanup;
+ }
+ user_apol_range = apol_mls_range_create_from_qpol_mls_range(p, user_range);
+ if (user_apol_range == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ retval2 = apol_mls_range_compare(p, user_apol_range, context->range, APOL_QUERY_SUB);
+ if (retval2 != 1) {
+ retval = retval2;
+ goto cleanup;
+ }
+ }
+ }
+ retval = 1;
+ cleanup:
+ apol_user_query_destroy(&user_query);
+ apol_role_query_destroy(&role_query);
+ apol_vector_destroy(&user_v);
+ apol_vector_destroy(&role_v);
+ apol_mls_range_destroy(&user_apol_range);
+ return retval;
+}
+
+char *apol_context_render(const apol_policy_t * p, const apol_context_t * context)
+{
+ char *buf = NULL, *range_str = NULL;
+ size_t buf_sz = 0;
+
+ if (context == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ if (p == NULL && !apol_mls_range_is_literal(context->range)) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ if (apol_str_appendf(&buf, &buf_sz, "%s:", (context->user != NULL ? context->user : "*")) != 0) {
+ ERR(p, "%s", strerror(errno));
+ goto err_return;
+ }
+ if (apol_str_appendf(&buf, &buf_sz, "%s:", (context->role != NULL ? context->role : "*")) != 0) {
+ ERR(p, "%s", strerror(errno));
+ goto err_return;
+ }
+ if (apol_str_append(&buf, &buf_sz, (context->type != NULL ? context->type : "*")) != 0) {
+ ERR(p, "%s", strerror(errno));
+ goto err_return;
+ }
+ if ((p != NULL && apol_policy_is_mls(p)) || (p == NULL)) {
+ if (context->range == NULL) {
+ range_str = strdup("*");
+ } else {
+ range_str = apol_mls_range_render(p, context->range);
+ }
+ if (range_str == NULL) {
+ goto err_return;
+ }
+ if (apol_str_appendf(&buf, &buf_sz, ":%s", range_str) != 0) {
+ ERR(p, "%s", strerror(errno));
+ goto err_return;
+ }
+ free(range_str);
+ }
+ return buf;
+
+ err_return:
+ free(buf);
+ free(range_str);
+ return NULL;
+}
+
+int apol_context_convert(const apol_policy_t * p, apol_context_t * context)
+{
+ if (p == NULL || context == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (context->range != NULL) {
+ return apol_mls_range_convert(p, context->range);
+ }
+ return 0;
+}
diff --git a/libapol/src/domain-trans-analysis-internal.h b/libapol/src/domain-trans-analysis-internal.h
new file mode 100644
index 0000000..2c49bed
--- /dev/null
+++ b/libapol/src/domain-trans-analysis-internal.h
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * Protected routines for 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
+ */
+
+#ifndef APOL_DOMAIN_TRANS_ANALYSIS_INTERNAL_H
+#define APOL_DOMAIN_TRANS_ANALYSIS_INTERNAL_H
+
+/**
+ * Free all memory associated with a domain transition result, including
+ * the pointer itself. This function does nothing if the result is NULL.
+ * @param dtr Pointer to a domain transition result structure to free.
+ */
+void domain_trans_result_free(void *dtr);
+
+#endif
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;
+}
diff --git a/libapol/src/fscon-query.c b/libapol/src/fscon-query.c
new file mode 100644
index 0000000..be3c7d3
--- /dev/null
+++ b/libapol/src/fscon-query.c
@@ -0,0 +1,437 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about genfscons and
+ * fs_use statements within a policy. The caller obtains a query
+ * object, fills in its parameters, and then runs the query; it
+ * obtains a vector of results. Searches are conjunctive -- all
+ * fields of the search query must match for a datum to be added to
+ * the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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 <apol/render.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+struct apol_genfscon_query
+{
+ char *fs, *path;
+ uint32_t objclass;
+ bool objclass_set;
+ apol_context_t *context;
+ unsigned int flags;
+};
+
+struct apol_fs_use_query
+{
+ char *fs;
+ uint32_t behavior;
+ bool behavior_set;
+ apol_context_t *context;
+ unsigned int flags;
+};
+
+/******************** genfscon queries ********************/
+
+int apol_genfscon_get_by_query(const apol_policy_t * p, const apol_genfscon_query_t * g, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1, retval2;
+ qpol_genfscon_t *genfscon = NULL;
+ *v = NULL;
+ if (qpol_policy_get_genfscon_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&genfscon) < 0) {
+ goto cleanup;
+ }
+ if (g != NULL) {
+ const char *fs, *path;
+ uint32_t objclass;
+ const qpol_context_t *context;
+ if (qpol_genfscon_get_name(p->p, genfscon, &fs) < 0 ||
+ qpol_genfscon_get_path(p->p, genfscon, &path) < 0 ||
+ qpol_genfscon_get_class(p->p, genfscon, &objclass) < 0 ||
+ qpol_genfscon_get_context(p->p, genfscon, &context) < 0) {
+ goto cleanup;
+ }
+ retval2 = apol_compare(p, fs, g->fs, 0, NULL);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ free(genfscon);
+ continue;
+ }
+ retval2 = apol_compare(p, path, g->path, 0, NULL);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ free(genfscon);
+ continue;
+ }
+ if (g->objclass_set && g->objclass != objclass) {
+ free(genfscon);
+ continue;
+ }
+ retval2 = apol_compare_context(p, context, g->context, g->flags);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ free(genfscon);
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, genfscon)) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ free(genfscon);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_genfscon_query_t *apol_genfscon_query_create(void)
+{
+ apol_genfscon_query_t *g = calloc(1, sizeof(apol_genfscon_query_t));
+ if (g != NULL) {
+ g->objclass = -1;
+ }
+ return g;
+}
+
+void apol_genfscon_query_destroy(apol_genfscon_query_t ** g)
+{
+ if (*g != NULL) {
+ free((*g)->fs);
+ free((*g)->path);
+ apol_context_destroy(&((*g)->context));
+ free(*g);
+ *g = NULL;
+ }
+}
+
+int apol_genfscon_query_set_filesystem(const apol_policy_t * p, apol_genfscon_query_t * g, const char *fs)
+{
+ return apol_query_set(p, &g->fs, NULL, fs);
+}
+
+int apol_genfscon_query_set_path(const apol_policy_t * p, apol_genfscon_query_t * g, const char *path)
+{
+ int tmp = apol_query_set(p, &g->path, NULL, path);
+ if (!tmp && g->path) {
+ if (strlen(g->path) > 1 && g->path[strlen(g->path) - 1] == '/')
+ g->path[strlen(g->path) - 1] = 0;
+ }
+ return tmp;
+}
+
+int apol_genfscon_query_set_objclass(const apol_policy_t * p, apol_genfscon_query_t * g, int objclass)
+{
+ if (objclass < 0) {
+ g->objclass = 0;
+ g->objclass_set = false;
+ } else {
+ switch (objclass) {
+ case QPOL_CLASS_BLK_FILE:
+ case QPOL_CLASS_CHR_FILE:
+ case QPOL_CLASS_DIR:
+ case QPOL_CLASS_FIFO_FILE:
+ case QPOL_CLASS_FILE:
+ case QPOL_CLASS_LNK_FILE:
+ case QPOL_CLASS_SOCK_FILE:
+ case QPOL_CLASS_ALL:
+ {
+ g->objclass = objclass;
+ g->objclass_set = true;
+ break;
+ }
+ default:
+ ERR(p, "%s", "Invalid object class given.");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_genfscon_query_set_context(const apol_policy_t * p __attribute__ ((unused)),
+ apol_genfscon_query_t * g, apol_context_t * context, unsigned int range_match)
+{
+ if (g->context != NULL) {
+ apol_context_destroy(&g->context);
+ }
+ g->context = context;
+ g->flags = (g->flags & ~APOL_QUERY_FLAGS) | range_match;
+ return 0;
+}
+
+char *apol_genfscon_render(const apol_policy_t * p, const qpol_genfscon_t * genfscon)
+{
+ char *line = NULL, *retval = NULL;
+ const qpol_context_t *ctxt = NULL;
+ char *context_str = NULL;
+ const char *type_str = NULL;
+ const char *name = NULL, *path = NULL;
+ uint32_t fclass;
+
+ if (!genfscon || !p)
+ goto cleanup;
+
+ if (qpol_genfscon_get_name(p->p, genfscon, &name))
+ goto cleanup;
+ if (qpol_genfscon_get_path(p->p, genfscon, &path))
+ goto cleanup;
+ if (qpol_genfscon_get_class(p->p, genfscon, &fclass))
+ return NULL;
+ if (qpol_genfscon_get_context(p->p, genfscon, &ctxt))
+ goto cleanup;
+
+ switch (fclass) {
+ case QPOL_CLASS_DIR:
+ type_str = " -d ";
+ break;
+ case QPOL_CLASS_CHR_FILE:
+ type_str = " -c ";
+ break;
+ case QPOL_CLASS_BLK_FILE:
+ type_str = " -b ";
+ break;
+ case QPOL_CLASS_FILE:
+ type_str = " -- ";
+ break;
+ case QPOL_CLASS_FIFO_FILE:
+ type_str = " -p ";
+ break;
+ case QPOL_CLASS_LNK_FILE:
+ type_str = " -l ";
+ break;
+ case QPOL_CLASS_SOCK_FILE:
+ type_str = " -s ";
+ break;
+ case QPOL_CLASS_ALL:
+ type_str = " ";
+ break;
+ default:
+ goto cleanup;
+ break;
+ }
+ context_str = apol_qpol_context_render(p, ctxt);
+ if (!context_str)
+ goto cleanup;
+
+ if (asprintf(&line, "genfscon %s %s %s %s", name, path, type_str, context_str) < 0) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ retval = line;
+ cleanup:
+ free(context_str);
+ if (retval != line) {
+ free(line);
+ }
+ return retval;
+}
+
+/******************** fs_use queries ********************/
+
+int apol_fs_use_get_by_query(const apol_policy_t * p, const apol_fs_use_query_t * f, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1, retval2;
+ const qpol_fs_use_t *fs_use = NULL;
+ *v = NULL;
+ if (qpol_policy_get_fs_use_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&fs_use) < 0) {
+ goto cleanup;
+ }
+ if (f != NULL) {
+ const char *fs;
+ uint32_t behavior;
+ const qpol_context_t *context = NULL;
+ if (qpol_fs_use_get_name(p->p, fs_use, &fs) < 0 || qpol_fs_use_get_behavior(p->p, fs_use, &behavior) < 0) {
+ goto cleanup;
+ }
+ if (behavior != QPOL_FS_USE_PSID && qpol_fs_use_get_context(p->p, fs_use, &context) < 0) {
+ goto cleanup;
+ }
+ retval2 = apol_compare(p, fs, f->fs, 0, NULL);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ continue;
+ }
+ if (f->behavior_set && f->behavior != behavior) {
+ continue;
+ }
+ /* recall that fs_use_psid statements do not
+ * have contexts */
+ if (f->context != NULL && behavior == QPOL_FS_USE_PSID) {
+ retval2 = 0;
+ } else {
+ retval2 = apol_compare_context(p, context, f->context, f->flags);
+ if (retval2 < 0) {
+ goto cleanup;
+ }
+ }
+ if (retval2 == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, (void *)fs_use)) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_fs_use_query_t *apol_fs_use_query_create(void)
+{
+ apol_fs_use_query_t *f = calloc(1, sizeof(apol_fs_use_query_t));
+ if (f != NULL) {
+ f->behavior = -1;
+ }
+ return f;
+}
+
+void apol_fs_use_query_destroy(apol_fs_use_query_t ** f)
+{
+ if (*f != NULL) {
+ free((*f)->fs);
+ apol_context_destroy(&((*f)->context));
+ free(*f);
+ *f = NULL;
+ }
+}
+
+int apol_fs_use_query_set_filesystem(const apol_policy_t * p, apol_fs_use_query_t * f, const char *fs)
+{
+ return apol_query_set(p, &f->fs, NULL, fs);
+}
+
+int apol_fs_use_query_set_behavior(const apol_policy_t * p, apol_fs_use_query_t * f, int behavior)
+{
+ if (behavior < 0) {
+ f->behavior = 0;
+ f->behavior_set = false;
+ } else {
+ switch (behavior) {
+ case QPOL_FS_USE_XATTR:
+ case QPOL_FS_USE_TASK:
+ case QPOL_FS_USE_TRANS:
+ case QPOL_FS_USE_GENFS:
+ case QPOL_FS_USE_NONE:
+ case QPOL_FS_USE_PSID:
+ {
+ f->behavior = behavior;
+ f->behavior_set = true;
+ break;
+ }
+ default:
+ ERR(p, "%s", "Invalid fs_use behavior given.");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_fs_use_query_set_context(const apol_policy_t * p __attribute__ ((unused)),
+ apol_fs_use_query_t * f, apol_context_t * context, unsigned int range_match)
+{
+ if (f->context != NULL) {
+ apol_context_destroy(&f->context);
+ }
+ f->context = context;
+ f->flags = (f->flags & ~APOL_QUERY_FLAGS) | range_match;
+ return 0;
+}
+
+char *apol_fs_use_render(const apol_policy_t * p, const qpol_fs_use_t * fsuse)
+{
+ char *context_str = NULL;
+ char *line = NULL, *retval = NULL;
+ const char *behavior_str = NULL;
+ const char *fsname = NULL;
+ const qpol_context_t *ctxt = NULL;
+ uint32_t behavior;
+
+ if (qpol_fs_use_get_behavior(p->p, fsuse, &behavior))
+ goto cleanup;
+ if ((behavior_str = apol_fs_use_behavior_to_str(behavior)) == NULL) {
+ ERR(p, "%s", "Could not get behavior string.");
+ goto cleanup;
+ }
+
+ if (qpol_fs_use_get_name(p->p, fsuse, &fsname))
+ goto cleanup;
+
+ if (behavior == QPOL_FS_USE_PSID) {
+ context_str = strdup("");
+ } else {
+ if (qpol_fs_use_get_context(p->p, fsuse, &ctxt))
+ goto cleanup;
+ context_str = apol_qpol_context_render(p, ctxt);
+ if (!context_str) {
+ goto cleanup;
+ }
+ }
+ if (asprintf(&line, "%s %s %s", behavior_str, fsname, context_str) < 0) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+
+ retval = line;
+ cleanup:
+ free(context_str);
+ if (retval != line) {
+ free(line);
+ }
+ return retval;
+}
diff --git a/libapol/src/infoflow-analysis-internal.h b/libapol/src/infoflow-analysis-internal.h
new file mode 100644
index 0000000..61fdd85
--- /dev/null
+++ b/libapol/src/infoflow-analysis-internal.h
@@ -0,0 +1,49 @@
+/**
+ * @file
+ *
+ * Protected routines for information flow analysis.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-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
+ */
+
+#ifndef APOL_INFOFLOW_ANALYSIS_INTERNAL_H
+#define APOL_INFOFLOW_ANALYSIS_INTERNAL_H
+
+/**
+ * Do a deep copy (i.e., a clone) of an apol_infoflow_result_t object.
+ * The caller is responsible for calling apol_infoflow_result_free()
+ * upon the returned value.
+ *
+ * @param result Pointer to an infoflow result structure to destroy.
+ *
+ * @return A clone of the passed in result node, or NULL upon error.
+ */
+extern apol_infoflow_result_t *infoflow_result_create_from_infoflow_result(const apol_infoflow_result_t * result);
+
+/**
+ * Free all memory associated with an information flow analysis
+ * result, including the pointer itself. This function does nothing
+ * if the result is already NULL.
+ *
+ * @param result Pointer to an infoflow result structure to destroy.
+ */
+extern void infoflow_result_free(void *result);
+
+#endif
diff --git a/libapol/src/infoflow-analysis.c b/libapol/src/infoflow-analysis.c
new file mode 100644
index 0000000..dad9a34
--- /dev/null
+++ b/libapol/src/infoflow-analysis.c
@@ -0,0 +1,2245 @@
+/**
+ * @file
+ * Implementation of the information flow analysis.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-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 "infoflow-analysis-internal.h"
+#include "queue.h"
+#include <apol/bst.h>
+#include <apol/perm-map.h>
+
+#include <assert.h>
+#include <config.h>
+#include <errno.h>
+#include <limits.h>
+#include <time.h>
+
+/*
+ * Nodes in the graph represent either a type used in the source
+ * of an allow rule or the target: these defines are used to
+ * represent which.
+ */
+#define APOL_INFOFLOW_NODE_SOURCE 0x1
+#define APOL_INFOFLOW_NODE_TARGET 0x2
+
+/*
+ * These defines are used to color nodes in the graph algorithms.
+ */
+#define APOL_INFOFLOW_COLOR_WHITE 0
+#define APOL_INFOFLOW_COLOR_GREY 1
+#define APOL_INFOFLOW_COLOR_BLACK 2
+#define APOL_INFOFLOW_COLOR_RED 3
+
+typedef struct apol_infoflow_node apol_infoflow_node_t;
+typedef struct apol_infoflow_edge apol_infoflow_edge_t;
+
+struct apol_infoflow_graph
+{
+ /** vector of apol_infoflow_node_t */
+ apol_vector_t *nodes;
+ /** vector of apol_infoflow_edge_t */
+ apol_vector_t *edges;
+ /** temporary BST of apol_infoflow_node_t used while building
+ * the graph */
+ apol_bst_t *nodes_bst;
+
+ unsigned int mode, direction;
+ regex_t *regex;
+
+ /** vector of apol_infoflow_node_t, used for random restarts
+ * for further transitive analysis */
+ apol_vector_t *further_start;
+ /** vector of apol_infoflow_node_t of targets, used for
+ * further transitive analysis */
+ apol_vector_t *further_end;
+ size_t current_start;
+#ifdef HAVE_RAND_R
+ unsigned int seed;
+#endif
+};
+
+struct apol_infoflow_node
+{
+ const qpol_type_t *type;
+ /** one of APOL_INFOFLOW_NODE_SOURCE or APOL_INFOFLOW_NODE_TARGET */
+ int node_type;
+ /** vector of apol_infoflow_edge_t, pointing into the graph */
+ apol_vector_t *in_edges;
+ /** vector of apol_infoflow_edge_t, pointing into the graph */
+ apol_vector_t *out_edges;
+ unsigned char color;
+ apol_infoflow_node_t *parent;
+ int distance;
+};
+
+struct apol_infoflow_edge
+{
+ /** vector of qpol_avrule_t, pointing into the policy */
+ apol_vector_t *rules;
+ /** pointer into a node within the graph */
+ apol_infoflow_node_t *start_node;
+ /** pointer into a node within the graph */
+ apol_infoflow_node_t *end_node;
+ int length;
+};
+
+/**
+ * apol_infoflow_analysis_h encapsulates all of the paramaters of a
+ * query. It should always be allocated with
+ * apol_infoflow_analysis_create() and deallocated with
+ * apol_infoflow_analysis_destroy(). Limiting by ending_types,
+ * obj_classes, intermed types, obj_class permissions is optional - if
+ * the vector is empty then no limiting is done.
+ *
+ * All of the vectors except end_types should contain the items that
+ * you want to not appear in the results. end_types lists the types
+ * that you do want to appear.
+ */
+struct apol_infoflow_analysis
+{
+ unsigned int mode, direction;
+ char *type, *result;
+ apol_vector_t *intermed, *class_perms;
+ int min_weight;
+};
+
+/**
+ * The results of running an infoflow, either direct or transitive, is
+ * a path from start_type to end_type. The path consists of a vector
+ * of intermediate steps.
+ */
+struct apol_infoflow_result
+{
+ const qpol_type_t *start_type, *end_type;
+ /** vector of apol_infoflow_step_t */
+ apol_vector_t *steps;
+ unsigned int direction;
+ unsigned int length;
+};
+
+/**
+ * Each result consists of multiple steps, representing the steps
+ * taken from the original start to end types. Along each step there
+ * is a vector of rules. For a direct infoflow analysis there will be
+ * exactly one step, and that flow's start type is the same as the
+ * original result's start_type. Likewise the end_types will be the
+ * same.
+ */
+struct apol_infoflow_step
+{
+ const qpol_type_t *start_type, *end_type;
+ /** vector of qpol_avrule_t */
+ apol_vector_t *rules;
+ int weight;
+};
+
+/**
+ * Deallocate all space associated with an apol_infoflow_step_t,
+ * including the pointer itself. Does nothing if the pointer is
+ * already NULL.
+ *
+ * @param step Infoflow step to free.
+ */
+static void apol_infoflow_step_free(void *step)
+{
+ if (step != NULL) {
+ apol_infoflow_step_t *s = (apol_infoflow_step_t *) step;
+ apol_vector_destroy(&s->rules);
+ free(s);
+ }
+}
+
+/******************** random number routines ********************/
+
+/**
+ * Initialize the pseudo-random number generator to be used during
+ * further transitive analysis.
+ *
+ * @param g Transitive infoflow graph.
+ */
+static void apol_infoflow_srand(apol_infoflow_graph_t * g)
+{
+#ifdef HAVE_RAND_R
+ g->seed = (int)time(NULL);
+#else
+ srand((int)time(NULL));
+#endif
+}
+
+/**
+ * Return a pseudo-random integer between 0 and RAND_MAX, for use
+ * during further transitive analysis. If the system supports it,
+ * this function will use rand_r() so that this library remains
+ * reentrant and thread-safe.
+ *
+ * @param g Transitive infoflow graph.
+ *
+ * @return Integer between 0 and RAND_MAX.
+ */
+static int apol_infoflow_rand(apol_infoflow_graph_t * g)
+{
+#ifdef HAVE_RAND_R
+ return rand_r(&g->seed);
+#else
+ return rand();
+#endif
+}
+
+/******************** infoflow graph node routines ********************/
+
+/**
+ * Given a pointer to an apol_infoflow_node_t, free its space
+ * including the pointer itself. Does nothing if the pointer is
+ * already NULL.
+ *
+ * @param data Node to free.
+ */
+static void apol_infoflow_node_free(void *data)
+{
+ apol_infoflow_node_t *node = (apol_infoflow_node_t *) data;
+ if (node != NULL) {
+ /* the edges themselves are owned by the graph, not by
+ * the node */
+ apol_vector_destroy(&node->in_edges);
+ apol_vector_destroy(&node->out_edges);
+ free(node);
+ }
+}
+
+struct apol_infoflow_node_key
+{
+ const qpol_type_t *type;
+ int node_type;
+};
+
+/**
+ * Given an infoflow node and a key, returns 0 if they are the same,
+ * non-zero if not.
+ *
+ * @param a Existing node within the infoflow graph.
+ * @param b <i>Unused.</i>
+ * @param data Pointer to a struct infoflow_node_key.
+ *
+ * @return 0 if the key matches a, non-zero if not.
+ */
+static int apol_infoflow_node_compare(const void *a, const void *b __attribute__ ((unused)), void *data)
+{
+ apol_infoflow_node_t *node = (apol_infoflow_node_t *) a;
+ struct apol_infoflow_node_key *key = (struct apol_infoflow_node_key *)data;
+ if (node->type != key->type) {
+ return (int)((char *)node->type - (char *)key->type);
+ }
+ return node->node_type - key->node_type;
+}
+
+/**
+ * Attempt to allocate a new node, add it to the infoflow graph, and
+ * return a pointer to it. If there already exists a node with the
+ * same type then reuse that node.
+ *
+ * @param p Policy handler, for reporting error.
+ * @param g Infoflow to which add the node.
+ * @param type Type for the new node.
+ * @param node_type Node type, one of APOL_INFOFLOW_NODE_SOURCE or
+ * APOL_INFOFLOW_NODE_TARGET.
+ *
+ * @return Pointer an allocated node within the infoflow graph, or
+ * NULL upon error.
+ */
+static apol_infoflow_node_t *apol_infoflow_graph_create_node(const apol_policy_t * p,
+ apol_infoflow_graph_t * g, const qpol_type_t * type, int node_type)
+{
+ struct apol_infoflow_node_key key = { type, node_type };
+ apol_infoflow_node_t *node = NULL;
+ if (apol_bst_get_element(g->nodes_bst, NULL, &key, (void **)&node) == 0) {
+ return node;
+ }
+ if ((node = calloc(1, sizeof(*node))) == NULL ||
+ (node->in_edges = apol_vector_create(NULL)) == NULL || (node->out_edges = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ apol_infoflow_node_free(node);
+ return NULL;
+ }
+ node->type = type;
+ node->node_type = node_type;
+ if (apol_bst_insert(g->nodes_bst, node, &key) != 0) {
+ ERR(p, "%s", strerror(errno));
+ apol_infoflow_node_free(node);
+ return NULL;
+ }
+ return node;
+}
+
+/**
+ * Attempt to allocate a new node, add it to the infoflow graph, and
+ * return a pointer to it. If there already exists a node with the
+ * same type then reuse that node.
+ *
+ * @param p Policy handler, for reporting error.
+ * @param g Infoflow to which add the node.
+ * @param type Type for the new node. If this is an attribute then it
+ * will be expanded into its component types.
+ * @param types If non-NULL, a BST of qpol_type_t pointers. Only
+ * create and return nodes which are members of this tree.
+ * @param node_type Node type, one of APOL_INFOFLOW_NODE_SOURCE or
+ * APOL_INFOFLOW_NODE_TARGET.
+ *
+ * @return Vector of nodes (type apol_infoflow_node_t *) within the
+ * infoflow graph, or NULL upon error. The caller is responsible for
+ * calling apol_vector_destroy() upon the return value.
+ */
+static apol_vector_t *apol_infoflow_graph_create_nodes(const apol_policy_t * p,
+ apol_infoflow_graph_t * g, const qpol_type_t * type, apol_bst_t * types,
+ int node_type)
+{
+ unsigned char isattr;
+ apol_vector_t *v = NULL;
+ apol_infoflow_node_t *node = NULL;
+ if (qpol_type_get_isattr(p->p, type, &isattr) < 0) {
+ return NULL;
+ }
+ if (isattr && g->mode != APOL_INFOFLOW_MODE_DIRECT) {
+ qpol_iterator_t *iter = NULL;
+ qpol_type_t *t;
+ size_t len;
+ if (qpol_type_get_type_iter(p->p, type, &iter) < 0 ||
+ qpol_iterator_get_size(iter, &len) < 0 || (v = apol_vector_create_with_capacity(len, NULL)) == NULL) {
+ qpol_iterator_destroy(&iter);
+ apol_vector_destroy(&v);
+ return NULL;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)&t);
+ void *result;
+ if (types != NULL && apol_bst_get_element(types, t, NULL, &result) < 0) {
+ continue;
+ }
+ if ((node = apol_infoflow_graph_create_node(p, g, t, node_type)) == NULL || apol_vector_append(v, node) < 0) {
+ qpol_iterator_destroy(&iter);
+ apol_vector_destroy(&v);
+ return NULL;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ } else {
+ /* for a direct search, do not expand types; the
+ * algorithm will do that with
+ * apol_infoflow_graph_get_nodes_for_type() and
+ * apol_infoflow_analysis_direct_expand(). for
+ * transitive searches the \a types BST was checked in
+ * apol_infoflow_graph_check_types() if \a type is
+ * just a type.
+ */
+ if ((v = apol_vector_create_with_capacity(1, NULL)) == NULL) {
+ return NULL;
+ }
+ if ((node = apol_infoflow_graph_create_node(p, g, type, node_type)) == NULL || apol_vector_append(v, node) < 0) {
+ apol_vector_destroy(&v);
+ return NULL;
+ }
+ }
+ return v;
+}
+
+/******************** infoflow graph edge routines ********************/
+
+/**
+ * Given a pointer to an apol_infoflow_edge_t, free its space
+ * including the pointer itself. Does nothing if the pointer is
+ * already NULL.
+ *
+ * @param data Edge to free.
+ */
+static void apol_infoflow_edge_free(void *data)
+{
+ apol_infoflow_edge_t *edge = (apol_infoflow_edge_t *) data;
+ if (edge != NULL) {
+ apol_vector_destroy(&edge->rules);
+ free(edge);
+ }
+}
+
+struct apol_infoflow_edge_key
+{
+ apol_infoflow_node_t *start_node, *end_node;
+};
+
+/**
+ * Given an infoflow edge and a key, returns 0 if they are the same,
+ * non-zero if not.
+ *
+ * @param a Existing edge within the infoflow graph.
+ * @param b <i>Unused.</i>
+ * @param data Pointer to a struct infoflow_edge_key.
+ *
+ * @return 0 if the key matches a, non-zero if not.
+ */
+static int apol_infoflow_edge_compare(const void *a, const void *b __attribute__ ((unused)), void *data)
+{
+ apol_infoflow_edge_t *edge = (apol_infoflow_edge_t *) a;
+ struct apol_infoflow_edge_key *key = (struct apol_infoflow_edge_key *)data;
+ if (key->start_node != NULL && edge->start_node != key->start_node) {
+ return (int)((char *)edge->start_node - (char *)key->start_node);
+ }
+ if (key->end_node != NULL && edge->end_node != key->end_node) {
+ return (int)((char *)edge->end_node - (char *)key->end_node);
+ }
+ return 0;
+}
+
+/**
+ * Attempt to allocate a new edge, add it to the infoflow graph, and
+ * return a pointer to it. If there already exists a edge from the
+ * start node to the end node then reuse that edge.
+ *
+ * @param p Policy handler, for reporting errors.
+ * @param g Infoflow graph to which add the edge.
+ * @param start_node Starting node for the edge.
+ * @param end_node Ending node for the edge.
+ * @param len Length of edge (proportionally inverse of permission weight)
+ *
+ * @return Pointer an allocated node within the infoflow graph, or
+ * NULL upon error.
+ */
+static apol_infoflow_edge_t *apol_infoflow_graph_create_edge(const apol_policy_t * p,
+ apol_infoflow_graph_t * g __attribute__ ((unused)),
+ apol_infoflow_node_t * start_node,
+ apol_infoflow_node_t * end_node, int len)
+{
+ struct apol_infoflow_edge_key key = { NULL, end_node };
+ size_t i;
+ apol_infoflow_edge_t *edge = NULL;
+ if (apol_vector_get_index(start_node->out_edges, NULL, apol_infoflow_edge_compare, &key, &i) == 0) {
+ edge = (apol_infoflow_edge_t *) apol_vector_get_element(start_node->out_edges, i);
+ if (edge->length < len) {
+ edge->length = len;
+ }
+ return edge;
+ }
+ if ((edge = calloc(1, sizeof(*edge))) == NULL || (edge->rules = apol_vector_create(NULL)) == NULL ||
+ apol_vector_append(g->edges, edge) < 0) {
+ ERR(p, "%s", strerror(errno));
+ apol_infoflow_edge_free(edge);
+ return NULL;
+ }
+ edge->start_node = start_node;
+ edge->end_node = end_node;
+ edge->length = len;
+ if (apol_vector_append(start_node->out_edges, edge) < 0 || apol_vector_append(end_node->in_edges, edge) < 0) {
+ /* don't free the edge -- it is owned by the graph */
+ ERR(p, "%s", strerror(errno));
+ return NULL;
+ }
+ return edge;
+}
+
+/******************** infoflow graph creation routines ********************/
+
+/**
+ * Take an avrule within a policy and possibly add it to the infoflow
+ * graph. The rule's source and target type sets are expanded. If
+ * the rule is to be added, then add its end nodes as necessary, and
+ * an edge connecting those nodes as necessary, and then add the rule
+ * to the edge.
+ *
+ * @param p Policy containing rules.
+ * @param g Information flow graph being created.
+ * @param rule AV rule to use.
+ * @param types BST of qpol_type_t pointers; while adding avrules to
+ * the graph, only add those whose source and/or target is a member of
+ * \a types, if \a types is non-NULL.
+ * @param found_read Non-zero to indicate that this rule performs a
+ * read operation.
+ * @param read_len Length of the edge to create (proportionally
+ * inverse of permission weight).
+ * @param found_write Non-zero to indicate that this rule performs a
+ * write operation.
+ * @param write_len Length of the edge to create (proportionally
+ * inverse of permission weight).
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_graph_connect_nodes(const apol_policy_t * p,
+ apol_infoflow_graph_t * g,
+ const qpol_avrule_t * rule,
+ apol_bst_t * types, int found_read, int read_len, int found_write, int write_len)
+{
+ const qpol_type_t *src_type, *tgt_type;
+ apol_vector_t *src_nodes = NULL, *tgt_nodes = NULL;
+ size_t i, j;
+ apol_infoflow_node_t *src_node, *tgt_node;
+ apol_infoflow_edge_t *edge;
+ int retval = -1;
+
+ if (qpol_avrule_get_source_type(p->p, rule, &src_type) < 0 || qpol_avrule_get_target_type(p->p, rule, &tgt_type) < 0) {
+ goto cleanup;
+ }
+
+ if ((src_nodes = apol_infoflow_graph_create_nodes(p, g, src_type, types, APOL_INFOFLOW_NODE_SOURCE)) == NULL) {
+ goto cleanup;
+ }
+ if ((tgt_nodes = apol_infoflow_graph_create_nodes(p, g, tgt_type, types, APOL_INFOFLOW_NODE_TARGET)) == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(src_nodes); i++) {
+ src_node = apol_vector_get_element(src_nodes, i);
+ for (j = 0; j < apol_vector_get_size(tgt_nodes); j++) {
+ tgt_node = apol_vector_get_element(tgt_nodes, j);
+ if (found_read) {
+ if ((edge = apol_infoflow_graph_create_edge(p, g, tgt_node, src_node, read_len)) == NULL) {
+ goto cleanup;
+ }
+ if (apol_vector_append(edge->rules, (void *)rule) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ if (found_write) {
+ if ((edge = apol_infoflow_graph_create_edge(p, g, src_node, tgt_node, write_len)) == NULL) {
+ goto cleanup;
+ }
+ if (apol_vector_append(edge->rules, (void *)rule) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&src_nodes);
+ apol_vector_destroy(&tgt_nodes);
+ return retval;
+}
+
+/**
+ * Given a policy and a partially completed infoflow graph, create the
+ * nodes and edges associated with a particular rule.
+ *
+ * @param p Policy from which to create the infoflow graph.
+ * @param g Infoflow graph being created.
+ * @param rule AV rule to add.
+ * @param types BST of qpol_type_t pointers; while adding avrules to
+ * the graph, only add those whose source and/or target is a member of
+ * \a types, if \a types is non-NULL.
+ * @param max_len Maximum permission length (i.e., inverse of
+ * permission weight) to consider when deciding to add this rule or
+ * not.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_graph_create_avrule(const apol_policy_t * p, apol_infoflow_graph_t * g, const qpol_avrule_t * rule,
+ apol_bst_t * types, int max_len)
+{
+ const qpol_class_t *obj_class;
+ qpol_iterator_t *perm_iter = NULL;
+ const char *obj_class_name;
+ char *perm_name;
+ int found_read = 0, found_write = 0, perm_error = 0;
+ int read_len = INT_MAX, write_len = INT_MAX;
+ int retval = -1;
+ if (qpol_avrule_get_object_class(p->p, rule, &obj_class) < 0 ||
+ qpol_class_get_name(p->p, obj_class, &obj_class_name) < 0 || qpol_avrule_get_perm_iter(p->p, rule, &perm_iter) < 0) {
+ goto cleanup;
+ }
+
+ /* find read or write flows for each object class/perm pair */
+ for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) {
+ int perm_map, perm_weight, len;
+
+ if (qpol_iterator_get_item(perm_iter, (void **)&perm_name) < 0) {
+ goto cleanup;
+ }
+ if (apol_policy_get_permmap(p, obj_class_name, perm_name, &perm_map, &perm_weight) < 0) {
+ goto cleanup;
+ }
+ free(perm_name);
+ if (perm_map == APOL_PERMMAP_UNMAPPED) {
+ perm_error = 1;
+ continue;
+ }
+ len = APOL_PERMMAP_MAX_WEIGHT - perm_weight + 1;
+ if (len < APOL_PERMMAP_MIN_WEIGHT) {
+ len = APOL_PERMMAP_MIN_WEIGHT;
+ } else if (len > APOL_PERMMAP_MAX_WEIGHT) {
+ len = APOL_PERMMAP_MAX_WEIGHT;
+ }
+ if (perm_map & APOL_PERMMAP_READ) {
+ if (len < read_len && len <= max_len) {
+ found_read = 1;
+ read_len = len;
+ }
+ }
+ if (perm_map & APOL_PERMMAP_WRITE) {
+ if (len < write_len && len <= max_len) {
+ found_write = 1;
+ write_len = len;
+ }
+ }
+ }
+
+ /* if we have found any flows then connect them within the graph */
+ if ((found_read || found_write) &&
+ apol_infoflow_graph_connect_nodes(p, g, rule, types, found_read, read_len, found_write, write_len) < 0) {
+ goto cleanup;
+ }
+ if (perm_error) {
+ WARN(p, "%s", "Not all of the permissions found had associated permission maps.");
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&perm_iter);
+ return retval;
+}
+
+/**
+ * Given a vector of strings representing types, return a BST of
+ * qpol_type_t pointers consisting of those types, those types'
+ * attributes, and those types' aliases.
+ *
+ * @param p Policy within which to look up types,
+ * @param v Vector of type strings.
+ *
+ * @return BST of qpol_type_t pointers, or NULL on error. The caller
+ * is responsible for calling apol_bst_destroy() upon the returned
+ * value.
+ */
+static apol_bst_t *apol_infoflow_graph_create_required_types(const apol_policy_t * p, const apol_vector_t * v)
+{
+ apol_bst_t *types = NULL;
+ apol_vector_t *expanded_types = NULL;
+ size_t i;
+ char *s;
+ int retval = -1;
+ if ((types = apol_bst_create(NULL, NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ s = (char *)apol_vector_get_element(v, i);
+ expanded_types = apol_query_create_candidate_type_list(p, s, 0, 1, APOL_QUERY_SYMBOL_IS_BOTH);
+ if (expanded_types == NULL) {
+ goto cleanup;
+ }
+ for (size_t j = 0; j < apol_vector_get_size(expanded_types); j++) {
+ qpol_type_t *t = (qpol_type_t *) apol_vector_get_element(expanded_types, j);
+ if (apol_bst_insert(types, t, NULL) < 0) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ apol_vector_destroy(&expanded_types);
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&expanded_types);
+ if (retval != 0) {
+ apol_bst_destroy(&types);
+ }
+ return types;
+}
+
+/**
+ * Determine if an av rule matches a list of qpol_type_t pointers.
+ * Both the source and target of the rule must be in the list.
+ *
+ * @param p Policy to which look up classes and permissions.
+ * @param rule AV rule to check.
+ * @param types BST of qpol_type_t, of which both the source and
+ * target types must be members. If NULL allow all types.
+ *
+ * @return 1 if rule matches, 0 if not, < 0 on error.
+ */
+static int apol_infoflow_graph_check_types(const apol_policy_t * p, const qpol_avrule_t * rule, const apol_bst_t * types)
+{
+ const qpol_type_t *source, *target;
+ void *result;
+ int retval = -1;
+ if (types == NULL) {
+ retval = 1;
+ goto cleanup;
+ }
+ if (qpol_avrule_get_source_type(p->p, rule, &source) < 0 || qpol_avrule_get_target_type(p->p, rule, &target) < 0) {
+ goto cleanup;
+ }
+ if (apol_bst_get_element(types, source, NULL, &result) < 0 || apol_bst_get_element(types, target, NULL, &result) < 0) {
+ retval = 0;
+ goto cleanup;
+ }
+ retval = 1;
+ cleanup:
+ return retval;
+}
+
+/**
+ * Determine if an av rule matches a list of apol_obj_perm_t. The
+ * rule's class must match at least one item in the list, and at least
+ * one of the rule's permissions must be on the list.
+ *
+ * @param p Policy to which look up classes and permissions.
+ * @param rule AV rule to check.
+ * @param class_perms Vector of apol_obj_perm_t, of which rule's class
+ * and permissions must be a member. If NULL or empty then allow all
+ * classes and permissions.
+ *
+ * @return 1 if rule matches, 0 if not, < 0 on error.
+ */
+static int apol_infoflow_graph_check_class_perms(const apol_policy_t * p, const qpol_avrule_t * rule,
+ const apol_vector_t * class_perms)
+{
+ const qpol_class_t *obj_class;
+ const char *obj_name;
+ char *perm;
+ qpol_iterator_t *iter = NULL;
+ apol_obj_perm_t *obj_perm = NULL;
+ apol_vector_t *obj_perm_v = NULL;
+ size_t i;
+ int retval = -1;
+
+ if (class_perms == NULL || apol_vector_get_size(class_perms) == 0) {
+ retval = 1;
+ goto cleanup;
+ }
+ if (qpol_avrule_get_object_class(p->p, rule, &obj_class) < 0 || qpol_class_get_name(p->p, obj_class, &obj_name) < 0) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(class_perms); i++) {
+ obj_perm = (apol_obj_perm_t *) apol_vector_get_element(class_perms, i);
+ if (strcmp(apol_obj_perm_get_obj_name(obj_perm), obj_name) == 0) {
+ obj_perm_v = apol_obj_perm_get_perm_vector(obj_perm);
+ break;
+ }
+ }
+ if (i >= apol_vector_get_size(class_perms)) {
+ retval = 0; /* no matching class */
+ goto cleanup;
+ }
+ if (qpol_avrule_get_perm_iter(p->p, rule, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&perm) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(obj_perm_v, perm, apol_str_strcmp, NULL, &i) == 0) {
+ free(perm);
+ retval = 1;
+ goto cleanup;
+ }
+ free(perm);
+ }
+ retval = 0; /* no matching perm */
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Given a particular information flow analysis object, generate an
+ * infoflow graph relative to a particular policy. This graph is
+ * customized for the particular analysis.
+ *
+ * @param p Policy from which to create the infoflow graph.
+ * @param ia Parameters to tune the created graph.
+ * @param g Reference to where to store the graph. The caller is
+ * responsible for calling apol_infoflow_graph_destroy() upon this.
+ *
+ * @return 0 if the graph was created, < 0 on error. Upon error *g
+ * will be set to NULL.
+ */
+static int apol_infoflow_graph_create(const apol_policy_t * p, const apol_infoflow_analysis_t * ia, apol_infoflow_graph_t ** g)
+{
+ apol_bst_t *types = NULL;
+ qpol_iterator_t *iter = NULL;
+ int max_len = APOL_PERMMAP_MAX_WEIGHT - ia->min_weight + 1;
+ int compval, retval = -1;
+
+ *g = NULL;
+ if (p->pmap == NULL) {
+ ERR(p, "%s", "A permission map must be loaded prior to building the infoflow graph.");
+ goto cleanup;
+ }
+
+ INFO(p, "%s", "Generating information flow graph.");
+ if (ia->mode == APOL_INFOFLOW_MODE_TRANS && ia->intermed != NULL &&
+ (types = apol_infoflow_graph_create_required_types(p, ia->intermed)) == NULL) {
+ goto cleanup;
+ }
+
+ if ((*g = calloc(1, sizeof(**g))) == NULL ||
+ ((*g)->nodes_bst = apol_bst_create(apol_infoflow_node_compare, apol_infoflow_node_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ (*g)->mode = ia->mode;
+ (*g)->direction = ia->direction;
+ if (ia->result != NULL && ia->result[0] != '\0') {
+ if (((*g)->regex = malloc(sizeof(regex_t))) == NULL || regcomp((*g)->regex, ia->result, REG_EXTENDED | REG_NOSUB)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ if (((*g)->edges = apol_vector_create(apol_infoflow_edge_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ if (qpol_policy_get_avrule_iter(p->p, QPOL_RULE_ALLOW, &iter) < 0) {
+ goto cleanup;
+ }
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_avrule_t *rule;
+ if (qpol_iterator_get_item(iter, (void **)&rule) < 0) {
+ goto cleanup;
+ }
+ compval = apol_infoflow_graph_check_types(p, rule, types);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ compval = apol_infoflow_graph_check_class_perms(p, rule, ia->class_perms);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ if (apol_infoflow_graph_create_avrule(p, *g, rule, types, max_len) < 0) {
+ goto cleanup;
+ }
+ }
+
+ if (((*g)->nodes = apol_bst_get_vector((*g)->nodes_bst, 1)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ apol_bst_destroy(&(*g)->nodes_bst);
+ retval = 0;
+ cleanup:
+ apol_bst_destroy(&types);
+ qpol_iterator_destroy(&iter);
+ if (retval < 0) {
+ apol_infoflow_graph_destroy(g);
+ }
+ return retval;
+}
+
+void apol_infoflow_graph_destroy(apol_infoflow_graph_t ** g)
+{
+ if (g != NULL && *g != NULL) {
+ apol_bst_destroy(&(*g)->nodes_bst);
+ apol_vector_destroy(&(*g)->nodes);
+ apol_vector_destroy(&(*g)->edges);
+ apol_vector_destroy(&(*g)->further_start);
+ apol_vector_destroy(&(*g)->further_end);
+ apol_regex_destroy(&(*g)->regex);
+ free(*g);
+ *g = NULL;
+ }
+}
+
+/*************** infoflow graph direct analysis routines ***************/
+
+/**
+ * Given a graph and a target type, append to vector v all nodes
+ * (apol_infoflow_node_t) within the graph that use that type, one of
+ * that type's aliases, or one of that type's attributes. This will
+ * also implicitly permutate across all of the type's object classes.
+ *
+ * @param p Error reporting handler.
+ * @param g Information flow graph containing nodes.
+ * @param type Target type name to find.
+ * @param v Initialized vector to which append nodes.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_graph_get_nodes_for_type(const apol_policy_t * p, const apol_infoflow_graph_t * g, const char *type,
+ apol_vector_t * v)
+{
+ size_t i, j;
+ apol_vector_t *cand_list = NULL;
+ int retval = -1;
+ if ((cand_list = apol_query_create_candidate_type_list(p, type, 0, 1, APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(g->nodes); i++) {
+ apol_infoflow_node_t *node;
+ node = (apol_infoflow_node_t *) apol_vector_get_element(g->nodes, i);
+ if (apol_vector_get_index(cand_list, node->type, NULL, NULL, &j) == 0 && apol_vector_append(v, node) < 0) {
+ goto cleanup;
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&cand_list);
+ return retval;
+}
+
+/**
+ * Return a usable infoflow result object. If there already exists a
+ * result object within vector v with the same start and ending type
+ * then reuse that object. Otherwise allocate and return a new
+ * infoflow result with its start and end type fields set.
+ *
+ * @param p Policy handler, for reporting errors.
+ * @param v Non-null vector of infoflow results.
+ * @param start_type Starting type for returned infoflow result object.
+ * @param end_type Starting type for returned infoflow result object.
+ *
+ * @return A usable infoflow result object, or NULL upon error.
+ */
+static apol_infoflow_result_t *apol_infoflow_direct_get_result(const apol_policy_t * p,
+ apol_vector_t * v, const qpol_type_t * start_type,
+ const qpol_type_t * end_type)
+{
+ size_t i;
+ apol_infoflow_result_t *r;
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ r = (apol_infoflow_result_t *) apol_vector_get_element(v, i);
+ if (r->start_type == start_type && r->end_type == end_type) {
+ return r;
+ }
+ }
+ if ((r = calloc(1, sizeof(*r))) == NULL || (r->steps = apol_vector_create(apol_infoflow_step_free)) == NULL
+ || apol_vector_append(v, r) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ infoflow_result_free(r);
+ return NULL;
+ }
+ r->start_type = start_type;
+ r->end_type = end_type;
+ r->length = INT_MAX;
+ return r;
+}
+
+/**
+ * Append the rules on an edge to a direct infoflow result.
+ *
+ * @param p Policy containing rules.
+ * @param edge Infoflow edge containing rules.
+ * @param direction Direction of flow, one of APOL_INFOFLOW_IN, etc.
+ * @param result Infoflow result to modify.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_direct_define(const apol_policy_t * p,
+ const apol_infoflow_edge_t * edge, unsigned int direction, apol_infoflow_result_t * result)
+{
+ apol_infoflow_step_t *step = NULL;
+ if (apol_vector_get_size(result->steps) == 0) {
+ if ((step = calloc(1, sizeof(*step))) == NULL ||
+ (step->rules = apol_vector_create(NULL)) == NULL || apol_vector_append(result->steps, step) < 0) {
+ apol_infoflow_step_free(step);
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ step->start_type = result->start_type;
+ step->end_type = result->end_type;
+ step->weight = 0;
+ } else {
+ step = (apol_infoflow_step_t *) apol_vector_get_element(result->steps, 0);
+ }
+ if (apol_vector_cat(step->rules, edge->rules) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ result->direction |= direction;
+ //TODO: check that edge->lenght can be safely unsigned
+ if (edge->length < (int)result->length) {
+ result->length = edge->length;
+ }
+ return 0;
+}
+
+/**
+ * Given the regular expression compiled into the graph object and a
+ * type, determine if that regex matches that type or any of the
+ * type's aliases.
+ *
+ * @param p Policy containing type names.
+ * @param g Graph object containing regex.
+ * @param type Type to check against.
+ *
+ * @return 1 if comparison succeeds, 0 if not, < 0 on error.
+ */
+static int apol_infoflow_graph_compare(const apol_policy_t * p, apol_infoflow_graph_t * g, const qpol_type_t * type)
+{
+ const char *type_name;
+ qpol_iterator_t *alias_iter = NULL;
+ int compval = 0;
+ if (g->regex == NULL) {
+ return 1;
+ }
+ if (qpol_type_get_name(p->p, type, &type_name) < 0) {
+ return -1;
+ }
+ if (regexec(g->regex, type_name, 0, NULL, 0) == 0) {
+ return 1;
+ }
+ /* also check for matches against any of target's aliases */
+ if (qpol_type_get_alias_iter(p->p, type, &alias_iter) < 0) {
+ return -1;
+ }
+ for (; !qpol_iterator_end(alias_iter); qpol_iterator_next(alias_iter)) {
+ char *iter_name;
+ if (qpol_iterator_get_item(alias_iter, (void **)&iter_name) < 0) {
+ compval = -1;
+ break;
+ }
+ if (regexec(g->regex, iter_name, 0, NULL, 0) == 0) {
+ compval = 1;
+ break;
+ }
+ }
+ qpol_iterator_destroy(&alias_iter);
+ return compval;
+}
+
+/**
+ * For each result object in vector working_results, append a
+ * duplicate of it to vector results if (a) the infoflow analysis
+ * object direction is not BOTH or (b) the result object's direction
+ * is BOTH. Regardless of success or error, it is safe to destroy
+ * either vector without concern of double-free()ing things.
+ *
+ * @param p Policy handler, for reporting errors.
+ * @param working_results Vector of infoflow results to check.
+ * @param direction Direction of search.
+ * @param results Vector to which append duplicated infoflow results.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_results_check_both(const apol_policy_t * p,
+ const apol_vector_t * working_results, unsigned int direction, apol_vector_t * results)
+{
+ size_t i;
+ apol_infoflow_result_t *r, *new_r;
+ for (i = 0; i < apol_vector_get_size(working_results); i++) {
+ r = (apol_infoflow_result_t *) apol_vector_get_element(working_results, i);
+ if (direction != APOL_INFOFLOW_BOTH || r->direction == APOL_INFOFLOW_BOTH) {
+ if ((new_r = calloc(1, sizeof(*new_r))) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ memcpy(new_r, r, sizeof(*new_r));
+ r->steps = NULL;
+ if (apol_vector_append(results, new_r) < 0) {
+ infoflow_result_free(new_r);
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * Given a start node, an edge, and flow direction, add an infoflow
+ * results to a vector. If the node on the other end of the edge is
+ * an attribute, first expand the attribute to its component types.
+ * If a regular expression is compiled into the infoflow graph, apply
+ * that regex match against candidate end node types prior to creating
+ * result nodes.
+ *
+ * @param p Policy to analyze.
+ * @param g Information flow graph to analyze.
+ * @param start_node Starting node.
+ * @param edge An edge from start_node.
+ * @param flow_dir Direction of search, either APOL_INFOFLOW_IN or
+ * APOL_INFOFLOW_OUT.
+ * @param results Non-NULL vector to which append infoflow results.
+ * The caller is responsible for calling apol_infoflow_results_free()
+ * upon each element afterwards.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_analysis_direct_expand(const apol_policy_t * p,
+ apol_infoflow_graph_t * g,
+ apol_infoflow_node_t * start_node,
+ apol_infoflow_edge_t * edge, unsigned int flow_dir, apol_vector_t * results)
+{
+ apol_infoflow_node_t *end_node;
+ unsigned char isattr;
+ qpol_iterator_t *iter = NULL;
+ const qpol_type_t *type;
+ apol_infoflow_result_t *r;
+ int retval = -1, compval;
+
+ if (edge->start_node == start_node) {
+ end_node = edge->end_node;
+ } else {
+ end_node = edge->start_node;
+ }
+ if (qpol_type_get_isattr(p->p, end_node->type, &isattr) < 0) {
+ goto cleanup;
+ }
+ if (isattr) {
+ if (qpol_type_get_type_iter(p->p, end_node->type, &iter) < 0) {
+ goto cleanup;
+ }
+ if (qpol_iterator_end(iter)) {
+ retval = 0;
+ goto cleanup;
+ }
+ }
+ /* always do this loop once, either if end_node is an attribute or not */
+ do {
+ if (isattr) {
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0) {
+ goto cleanup;
+ }
+ qpol_iterator_next(iter);
+ } else {
+ type = end_node->type;
+ }
+ compval = apol_infoflow_graph_compare(p, g, type);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ if ((r = apol_infoflow_direct_get_result(p, results, start_node->type, type)) == NULL ||
+ apol_infoflow_direct_define(p, edge, flow_dir, r) < 0) {
+ goto cleanup;
+ }
+ } while (isattr && !qpol_iterator_end(iter));
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Perform a direct information flow analysis upon the given infoflow
+ * graph.
+ *
+ * @param p Policy to analyze.
+ * @param g Information flow graph to analyze.
+ * @param start_type Type from which to begin search.
+ * @param results Non-NULL vector to which append infoflow results.
+ * The caller is responsible for calling apol_infoflow_results_free()
+ * upon each element afterwards.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_analysis_direct(const apol_policy_t * p,
+ apol_infoflow_graph_t * g, const char *start_type, apol_vector_t * results)
+{
+ apol_vector_t *nodes = NULL;
+ size_t i, j;
+ apol_infoflow_node_t *node;
+ apol_infoflow_edge_t *edge;
+ apol_vector_t *working_results = NULL;
+ int retval = -1;
+
+ if ((nodes = apol_vector_create(NULL)) == NULL || (working_results = apol_vector_create(infoflow_result_free)) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_infoflow_graph_get_nodes_for_type(p, g, start_type, nodes) < 0) {
+ goto cleanup;
+ }
+
+ if (g->direction == APOL_INFOFLOW_IN || g->direction == APOL_INFOFLOW_EITHER || g->direction == APOL_INFOFLOW_BOTH) {
+ for (i = 0; i < apol_vector_get_size(nodes); i++) {
+ node = (apol_infoflow_node_t *) apol_vector_get_element(nodes, i);
+ for (j = 0; j < apol_vector_get_size(node->in_edges); j++) {
+ edge = (apol_infoflow_edge_t *) apol_vector_get_element(node->in_edges, j);
+ if (apol_infoflow_analysis_direct_expand(p, g, node, edge, APOL_INFOFLOW_IN, working_results) < 0) {
+ goto cleanup;
+ }
+ }
+ }
+ }
+ if (g->direction == APOL_INFOFLOW_OUT || g->direction == APOL_INFOFLOW_EITHER || g->direction == APOL_INFOFLOW_BOTH) {
+ for (i = 0; i < apol_vector_get_size(nodes); i++) {
+ node = (apol_infoflow_node_t *) apol_vector_get_element(nodes, i);
+ for (j = 0; j < apol_vector_get_size(node->out_edges); j++) {
+ edge = (apol_infoflow_edge_t *) apol_vector_get_element(node->out_edges, j);
+ if (apol_infoflow_analysis_direct_expand(p, g, node, edge, APOL_INFOFLOW_OUT, working_results) < 0) {
+ goto cleanup;
+ }
+ }
+ }
+ }
+
+ if (apol_infoflow_results_check_both(p, working_results, g->direction, results) < 0) {
+ goto cleanup;
+ }
+
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&nodes);
+ apol_vector_destroy(&working_results);
+ return retval;
+}
+
+/*************** infoflow graph transitive analysis routines ***************/
+
+/**
+ * Prepare an infoflow graph for a transitive analysis by coloring its
+ * nodes and setting its parent and distance. For the start node
+ * color it red; for all others color them white.
+ *
+ * @param p Policy handler, for reporting errors.
+ * @param g Infoflow graph to initialize.
+ * @param start Node from which to begin analysis.
+ * @param q Queue of apol_infoflow_node_t pointers to which search.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_graph_trans_init(const apol_policy_t * p,
+ apol_infoflow_graph_t * g, apol_infoflow_node_t * start, apol_queue_t * q)
+{
+ size_t i;
+ apol_infoflow_node_t *node;
+ for (i = 0; i < apol_vector_get_size(g->nodes); i++) {
+ node = (apol_infoflow_node_t *) apol_vector_get_element(g->nodes, i);
+ node->parent = NULL;
+ if (node == start) {
+ node->color = APOL_INFOFLOW_COLOR_RED;
+ node->distance = 0;
+ if (apol_queue_insert(q, node) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ } else {
+ node->color = APOL_INFOFLOW_COLOR_WHITE;
+ node->distance = INT_MAX;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Prepare an infoflow graph for furher transitive analysis by
+ * coloring its nodes and setting its parent and distance. For the
+ * start node color it grey; for all others color them white.
+ *
+ * @param p Policy handler, for reporting errors.
+ * @param g Infoflow graph to initialize.
+ * @param start Node from which to begin analysis.
+ * @param q Queue of apol_infoflow_node_t pointers to which search.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_graph_trans_further_init(const apol_policy_t * p,
+ apol_infoflow_graph_t * g, apol_infoflow_node_t * start, apol_queue_t * q)
+{
+ size_t i;
+ apol_infoflow_node_t *node;
+ for (i = 0; i < apol_vector_get_size(g->nodes); i++) {
+ node = (apol_infoflow_node_t *) apol_vector_get_element(g->nodes, i);
+ node->parent = NULL;
+ if (node == start) {
+ node->color = APOL_INFOFLOW_COLOR_GREY;
+ node->distance = 0;
+ if (apol_queue_insert(q, node) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ } else {
+ node->color = APOL_INFOFLOW_COLOR_WHITE;
+ node->distance = -1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Given a colored infoflow graph from apol_infoflow_analysis_trans(),
+ * find the shortest path from the end node to the start node.
+ * Allocate and return a vector of apol_infoflow_node_t that lists the
+ * nodes from the end to start.
+ *
+ * @param p Policy from which infoflow graph was generated.
+ * @param g Infoflow graph that has been colored.
+ * @param start_node Starting node for the path
+ * @param end_node Ending node to which to find a path.
+ * @param path Reference to a vector that will be allocated and filled
+ * with apol_infoflow_node_t pointers from the graph. The path will
+ * be in reverse order (i.e., from end node to a start node). Upon
+ * error this will be set to NULL.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_trans_path(const apol_policy_t * p,
+ apol_infoflow_graph_t * g,
+ apol_infoflow_node_t * start_node, apol_infoflow_node_t * end_node, apol_vector_t ** path)
+{
+ int retval = -1;
+ apol_infoflow_node_t *next_node = end_node;
+ if ((*path = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ while (1) {
+ if (apol_vector_append(*path, next_node) < 0) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (next_node == start_node) {
+ break;
+ }
+ if (next_node == NULL || apol_vector_get_size(*path) >= apol_vector_get_size(g->nodes)) {
+ ERR(p, "%s", "Infinite loop in trans_path.");
+ errno = EPERM;
+ goto cleanup;
+ }
+ next_node = next_node->parent;
+ }
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(path);
+ }
+ return retval;
+}
+
+/**
+ * Given a node within an infoflow graph, return the edge that
+ * connects it to next_node.
+ *
+ * @param p Policy handler, for reporting errors.
+ * @param g Infoflow graph from which to find edge.
+ * @param node Starting node.
+ * @param next_node Ending node.
+ *
+ * @return Edge connecting node to next_node, or NULL on error.
+ */
+static apol_infoflow_edge_t *apol_infoflow_trans_find_edge(const apol_policy_t * p,
+ apol_infoflow_graph_t * g,
+ apol_infoflow_node_t * node, apol_infoflow_node_t * next_node)
+{
+ apol_vector_t *v;
+ apol_infoflow_edge_t *edge;
+ size_t i;
+
+ if (g->direction == APOL_INFOFLOW_OUT) {
+ v = node->out_edges;
+ } else {
+ v = node->in_edges;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ edge = (apol_infoflow_edge_t *) apol_vector_get_element(v, i);
+ if (g->direction == APOL_INFOFLOW_OUT) {
+ if (edge->start_node == node && edge->end_node == next_node) {
+ return edge;
+ }
+ } else {
+ if (edge->end_node == node && edge->start_node == next_node) {
+ return edge;
+ }
+
+ }
+ }
+ ERR(p, "%s", "Did not find an edge.");
+ return NULL;
+}
+
+/**
+ * Given a path of nodes, define a new infoflow result that represents
+ * that path. The given path is a list of nodes that must be in
+ * reverse order (i.e., from end node to start node) and must have at
+ * least 2 elements within.
+ *
+ * @param p Policy handler, for reporting errors.
+ * @param g Graph from which the node path originated.
+ * @param path Vector of apol_infoflow_node_t representing an infoflow
+ * path.
+ * @param end_type Ending type for the path.
+ * @param result Reference pointer to where to store result. The
+ * caller is responsible for calling apol_infoflow_result_free() upon
+ * the returned value. Upon error this will be set to NULL.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_trans_define(const apol_policy_t * p,
+ apol_infoflow_graph_t * g,
+ apol_vector_t * path, const qpol_type_t * end_type, apol_infoflow_result_t ** result)
+{
+ apol_infoflow_step_t *step = NULL;
+ size_t path_len = apol_vector_get_size(path), i;
+ apol_infoflow_node_t *node, *next_node;
+ apol_infoflow_edge_t *edge;
+ int retval = -1, length = 0;
+ *result = NULL;
+
+ if (((*result) = calloc(1, sizeof(**result))) == NULL ||
+ ((*result)->steps = apol_vector_create_with_capacity(path_len, apol_infoflow_step_free)) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ (*result)->end_type = end_type;
+ /* build in reverse order because path is from end node to
+ * start node */
+ node = (apol_infoflow_node_t *) apol_vector_get_element(path, path_len - 1);
+ (*result)->start_type = node->type;
+ (*result)->direction = g->direction;
+ for (i = path_len - 1; i > 0; i--, node = next_node) {
+ next_node = (apol_infoflow_node_t *) apol_vector_get_element(path, i - 1);
+ edge = apol_infoflow_trans_find_edge(p, g, node, next_node);
+ if (edge == NULL) {
+ goto cleanup;
+ }
+ length += edge->length;
+ if ((step = calloc(1, sizeof(*step))) == NULL ||
+ (step->rules = apol_vector_create_from_vector(edge->rules, NULL, NULL, NULL)) == NULL ||
+ apol_vector_append((*result)->steps, step) < 0) {
+ apol_infoflow_step_free(step);
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ step->start_type = edge->start_node->type;
+ step->end_type = edge->end_node->type;
+ step->weight = APOL_PERMMAP_MAX_WEIGHT - edge->length + 1;
+ }
+ (*result)->length = length;
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ infoflow_result_free(*result);
+ *result = NULL;
+ }
+ return retval;
+}
+
+/**
+ * Compares two apol_infoflow_step_t objects, returning 0 if they have
+ * the same contents, non-zero or not. This is a callback function to
+ * apol_vector_compare().
+ *
+ * @param a First apol_infoflow_step_t to compare.
+ * @param b Other apol_infoflow_step_t to compare.
+ * @param data Unused.
+ *
+ * @return 0 if the steps are the same, non-zero if different.
+ */
+static int apol_infoflow_trans_step_comp(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ const apol_infoflow_step_t *step_a = (const apol_infoflow_step_t *)a;
+ const apol_infoflow_step_t *step_b = (const apol_infoflow_step_t *)b;
+ size_t i;
+ if (step_a->start_type != step_b->start_type) {
+ return (int)((char *)step_a->start_type - (char *)step_b->start_type);
+ }
+ if (step_a->end_type != step_b->end_type) {
+ return (int)((char *)step_a->end_type - (char *)step_b->end_type);
+ }
+ return apol_vector_compare(step_a->rules, step_b->rules, NULL, NULL, &i);
+}
+
+/**
+ * Given a path, append to the results vector a new
+ * apol_infoflow_result object - but only if there is not already a
+ * result describing the same path.
+ *
+ * @param p Policy handler, for reporting errors.
+ * @param g Infoflow graph to which create results.
+ * @param path Vector of apol_infoflow_node_t describing a path from
+ * an end node to a starting node.
+ * @param end_type Ending type for the path.
+ * @param results Vector of apol_infoflow_result_t to possibly append
+ * a new result.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_trans_append(const apol_policy_t * p,
+ apol_infoflow_graph_t * g,
+ apol_vector_t * path, const qpol_type_t * end_type, apol_vector_t * results)
+{
+ apol_infoflow_result_t *new_r = NULL, *r;
+ size_t i, j;
+ int compval, retval = -1;
+
+ if (apol_infoflow_trans_define(p, g, path, end_type, &new_r) < 0) {
+ goto cleanup;
+ }
+
+ /* First we look for duplicate paths */
+ for (i = 0; i < apol_vector_get_size(results); i++) {
+ r = (apol_infoflow_result_t *) apol_vector_get_element(results, i);
+ if (r->end_type != end_type ||
+ r->direction != new_r->direction || apol_vector_get_size(r->steps) != apol_vector_get_size(new_r->steps)) {
+ break;
+ }
+ compval = apol_vector_compare(r->steps, new_r->steps, apol_infoflow_trans_step_comp, NULL, &j);
+ /* found a dup TODO - make certain all of the object
+ * class / rules are kept */
+ if (compval == 0) {
+ infoflow_result_free(new_r);
+ new_r = NULL;
+ retval = 0;
+ goto cleanup;
+ }
+ }
+
+ /* If we are here the newly built path is unique. */
+ if (apol_vector_append(results, new_r) < 0) {
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ infoflow_result_free(new_r);
+ }
+ return retval;
+}
+
+/**
+ * Given a start and end node, add a trans infoflow results to a
+ * vector. If a regular expression is compiled into the infoflow
+ * graph, apply that regex match against candidate end node types
+ * prior to creating result nodes.
+ *
+ * @param p Policy to analyze.
+ * @param g Information flow graph to analyze.
+ * @param start_node Starting node.
+ * @param end_node Ending node.
+ * @param results Non-NULL vector to which append infoflow result.
+ * The caller is responsible for calling apol_infoflow_results_free()
+ * upon each element afterwards.
+ *
+ * @return 0 on success (including no result actually added), or < 0
+ * on error.
+ */
+static int apol_infoflow_analysis_trans_expand(const apol_policy_t * p,
+ apol_infoflow_graph_t * g,
+ apol_infoflow_node_t * start_node,
+ apol_infoflow_node_t * end_node, apol_vector_t * results)
+{
+ unsigned char isattr;
+ apol_vector_t *path = NULL;
+ int retval = -1, compval;
+
+ if (qpol_type_get_isattr(p->p, end_node->type, &isattr) < 0) {
+ goto cleanup;
+ }
+ assert(isattr == 0);
+ if (start_node->type == end_node->type) {
+ return 0;
+ }
+ compval = apol_infoflow_graph_compare(p, g, end_node->type);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ return 0;
+ }
+ if (apol_infoflow_trans_path(p, g, start_node, end_node, &path) < 0 ||
+ apol_infoflow_trans_append(p, g, path, end_node->type, results) < 0) {
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&path);
+ return retval;
+}
+
+/**
+ * Perform a transitive information flow analysis upon the given
+ * infoflow graph starting from some particular node within the graph.
+ *
+ * This is a label correcting shortest path algorithm; see Bertsekas,
+ * D. P., "A Simple and Fast Label Correcting Algorithm for Shortest
+ * Paths," Networks, Vol. 23, pp. 703-709, 1993. for more information.
+ * A label correcting algorithm is needed instead of the more common
+ * Dijkstra label setting algorithm to correctly handle the the cycles
+ * that are possible in these graphs.
+ *
+ * This algorithm finds the shortest path between a given start node
+ * and all other nodes in the graph. Any paths that it finds it
+ * appends to the iflow_transitive_t structure. This is a basic label
+ * correcting algorithm with 1 optimization. It uses the D'Esopo-Pape
+ * method for node selection in the node queue. Why is this faster?
+ * The paper referenced above says "No definitive explanation has been
+ * given." They have fancy graphs to show that it is faster though
+ * and the important part is that the worst case isn't much worse that
+ * N^2 - much better than an n^3 transitive closure. Additionally,
+ * most normal sparse graphs are significantly better than the worst
+ * case.
+ *
+ * @param p Policy to analyze.
+ * @param g Information flow graph to analyze.
+ * @param start Node from which to begin search.
+ * @param results Non-NULL vector to which append infoflow results.
+ * The caller is responsible for calling apol_infoflow_results_free()
+ * upon each element afterwards.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_analysis_trans_shortest_path(const apol_policy_t * p,
+ apol_infoflow_graph_t * g,
+ apol_infoflow_node_t * start, apol_vector_t * results)
+{
+ apol_vector_t *edge_list;
+ apol_queue_t *queue = NULL;
+ apol_infoflow_node_t *node, *cur_node;
+ apol_infoflow_edge_t *edge;
+ size_t i;
+ int retval = -1;
+
+ if ((queue = apol_queue_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_infoflow_graph_trans_init(p, g, start, queue) < 0) {
+ goto cleanup;
+ }
+
+ while ((cur_node = apol_queue_remove(queue)) != NULL) {
+ cur_node->color = APOL_INFOFLOW_COLOR_GREY;
+ if (g->direction == APOL_INFOFLOW_OUT) {
+ edge_list = cur_node->out_edges;
+ } else {
+ edge_list = cur_node->in_edges;
+ }
+ for (i = 0; i < apol_vector_get_size(edge_list); i++) {
+ edge = (apol_infoflow_edge_t *) apol_vector_get_element(edge_list, i);
+ if (g->direction == APOL_INFOFLOW_OUT) {
+ node = edge->end_node;
+ } else {
+ node = edge->start_node;
+ }
+ if (node == start) {
+ continue;
+ }
+
+ if (node->distance > cur_node->distance + edge->length) {
+ node->distance = cur_node->distance + edge->length;
+ node->parent = cur_node;
+ /* If this node has been inserted into
+ * the queue before insert it at the
+ * beginning, otherwise it goes to the
+ * end. See the comment at the
+ * beginning of the function for
+ * why. */
+ if (node->color != APOL_INFOFLOW_COLOR_RED) {
+ if (node->color == APOL_INFOFLOW_COLOR_GREY) {
+ if (apol_queue_push(queue, node) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ } else {
+ if (apol_queue_insert(queue, node) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ node->color = APOL_INFOFLOW_COLOR_RED;
+ }
+ }
+ }
+ }
+
+ /* Find all of the paths and add them to the results vector */
+ for (i = 0; i < apol_vector_get_size(g->nodes); i++) {
+ cur_node = (apol_infoflow_node_t *) apol_vector_get_element(g->nodes, i);
+ if (cur_node->parent == NULL || cur_node == start) {
+ continue;
+ }
+ if (apol_infoflow_analysis_trans_expand(p, g, start, cur_node, results) < 0) {
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ apol_queue_destroy(&queue);
+ return retval;
+}
+
+/**
+ * Perform a transitive information flow analysis upon the given
+ * infoflow graph.
+ *
+ * @param p Policy to analyze.
+ * @param g Information flow graph to analyze.
+ * @param start_type Type from which to begin search.
+ * @param results Non-NULL vector to which append infoflow results.
+ * The caller is responsible for calling apol_infoflow_results_free()
+ * upon each element afterwards.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_analysis_trans(const apol_policy_t * p,
+ apol_infoflow_graph_t * g, const char *start_type, apol_vector_t * results)
+{
+ apol_vector_t *start_nodes = NULL;
+ apol_infoflow_node_t *start_node;
+ size_t i;
+ int retval = -1;
+
+ if (g->direction != APOL_INFOFLOW_IN && g->direction != APOL_INFOFLOW_OUT) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+ if ((start_nodes = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (apol_infoflow_graph_get_nodes_for_type(p, g, start_type, start_nodes) < 0) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(start_nodes); i++) {
+ start_node = (apol_infoflow_node_t *) apol_vector_get_element(start_nodes, i);
+ if (apol_infoflow_analysis_trans_shortest_path(p, g, start_node, results) < 0) {
+ goto cleanup;
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&start_nodes);
+ return retval;
+}
+
+/**
+ * Given a vector, allocate and return a new vector with the elements
+ * shuffled about. This will make a shallow copy of the original
+ * vector's elements.
+ *
+ * @param p Policy handler, for error reporting.
+ * @param g Transitive infoflow graph containing PRNG object.
+ * @param v Vector to shuffle.
+ *
+ * @return A newly allocated vector with shuffled elements, or NULL
+ * upon error. The caller must call apol_vector_destroy() upon the
+ * returned value.
+ */
+static apol_vector_t *apol_infoflow_trans_further_shuffle(const apol_policy_t * p, apol_infoflow_graph_t * g, apol_vector_t * v)
+{
+ size_t i, j, size;
+ void **deck = NULL, *tmp;
+ apol_vector_t *new_v = NULL;
+ int retval = -1;
+ size = apol_vector_get_size(v);
+ if ((new_v = apol_vector_create_with_capacity(size, NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (size == 0) {
+ retval = 0;
+ goto cleanup;
+ }
+ if ((deck = malloc(size * sizeof(*deck))) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (i = 0; i < size; i++) {
+ deck[i] = apol_vector_get_element(v, i);
+ }
+ for (i = size - 1; i > 0; i--) {
+ j = (size_t) ((apol_infoflow_rand(g) / (RAND_MAX + 1.0)) * i);
+ tmp = deck[i];
+ deck[i] = deck[j];
+ deck[j] = tmp;
+ }
+ for (i = 0; i < size; i++) {
+ if (apol_vector_append(new_v, deck[i]) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ retval = 0;
+ cleanup:
+ free(deck);
+ if (retval != 0) {
+ apol_vector_destroy(&new_v);
+ }
+ return new_v;
+}
+
+static int apol_infoflow_analysis_trans_further(const apol_policy_t * p,
+ apol_infoflow_graph_t * g, apol_infoflow_node_t * start, apol_vector_t * results)
+{
+ apol_vector_t *edge_list = NULL;
+ apol_queue_t *queue = NULL;
+ apol_infoflow_node_t *node, *cur_node;
+ apol_infoflow_edge_t *edge;
+ size_t i;
+ int retval = -1;
+
+ if ((queue = apol_queue_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_infoflow_graph_trans_further_init(p, g, start, queue) < 0) {
+ goto cleanup;
+ }
+
+ while ((cur_node = apol_queue_remove(queue)) != NULL) {
+ if (cur_node != start &&
+ apol_vector_get_index(g->further_end, cur_node, NULL, NULL, &i) == 0 &&
+ apol_infoflow_analysis_trans_expand(p, g, start, cur_node, results) < 0) {
+ goto cleanup;
+ }
+ cur_node->color = APOL_INFOFLOW_COLOR_BLACK;
+ if (g->direction == APOL_INFOFLOW_OUT) {
+ edge_list = cur_node->out_edges;
+ } else {
+ edge_list = cur_node->in_edges;
+ }
+ edge_list = apol_infoflow_trans_further_shuffle(p, g, edge_list);
+ if (edge_list == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(edge_list); i++) {
+ edge = (apol_infoflow_edge_t *) apol_vector_get_element(edge_list, i);
+ if (g->direction == APOL_INFOFLOW_OUT) {
+ node = edge->end_node;
+ } else {
+ node = edge->start_node;
+ }
+ if (node->color == APOL_INFOFLOW_COLOR_WHITE) {
+ node->color = APOL_INFOFLOW_COLOR_GREY;
+ node->distance = cur_node->distance + 1;
+ node->parent = cur_node;
+ if (apol_queue_push(queue, node) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ }
+ apol_vector_destroy(&edge_list);
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&edge_list);
+ apol_queue_destroy(&queue);
+ return retval;
+}
+
+/******************** infoflow analysis object routines ********************/
+
+int apol_infoflow_analysis_do(const apol_policy_t * p, const apol_infoflow_analysis_t * ia, apol_vector_t ** v,
+ apol_infoflow_graph_t ** g)
+{
+ int retval = -1;
+ if (v != NULL) {
+ *v = NULL;
+ }
+ if (g != NULL) {
+ *g = NULL;
+ }
+ if (p == NULL || ia == NULL || v == NULL || g == NULL || ia->mode == 0 || ia->direction == 0) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+ if (apol_infoflow_graph_create(p, ia, g) < 0) {
+ goto cleanup;
+ }
+ INFO(p, "%s", "Searching information flow graph.");
+ retval = apol_infoflow_analysis_do_more(p, *g, ia->type, v);
+ cleanup:
+ if (retval != 0) {
+ apol_infoflow_graph_destroy(g);
+ }
+ return retval;
+}
+
+int apol_infoflow_analysis_do_more(const apol_policy_t * p, apol_infoflow_graph_t * g, const char *type, apol_vector_t ** v)
+{
+ const qpol_type_t *start_type;
+ int retval = -1;
+ if (v != NULL) {
+ *v = NULL;
+ }
+ if (p == NULL || g == NULL || type == NULL || v == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+
+ if (apol_query_get_type(p, type, &start_type) < 0) {
+ goto cleanup;
+ }
+
+ if ((*v = apol_vector_create(infoflow_result_free)) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+
+ if ((g->mode == APOL_INFOFLOW_MODE_DIRECT &&
+ apol_infoflow_analysis_direct(p, g, type, *v) < 0) ||
+ (g->mode == APOL_INFOFLOW_MODE_TRANS && apol_infoflow_analysis_trans(p, g, type, *v) < 0)) {
+ goto cleanup;
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ return retval;
+}
+
+int apol_infoflow_analysis_trans_further_prepare(const apol_policy_t * p,
+ apol_infoflow_graph_t * g, const char *start_type, const char *end_type)
+{
+ const qpol_type_t *stype, *etype;
+ int retval = -1;
+
+ apol_infoflow_srand(g);
+ if (apol_query_get_type(p, start_type, &stype) < 0 || apol_query_get_type(p, end_type, &etype) < 0) {
+ goto cleanup;
+ }
+ if (g->mode != APOL_INFOFLOW_MODE_TRANS) {
+ ERR(p, "%s", "May only perform further infoflow analysis when the graph is transitive.");
+ goto cleanup;
+ }
+ apol_vector_destroy(&g->further_start);
+ apol_vector_destroy(&g->further_end);
+ if ((g->further_start = apol_vector_create(NULL)) == NULL || (g->further_end = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (apol_infoflow_graph_get_nodes_for_type(p, g, start_type, g->further_start) < 0 ||
+ apol_infoflow_graph_get_nodes_for_type(p, g, end_type, g->further_end) < 0) {
+ goto cleanup;
+ }
+ g->current_start = 0;
+ retval = 0;
+ cleanup:
+ return retval;
+}
+
+int apol_infoflow_analysis_trans_further_next(const apol_policy_t * p, apol_infoflow_graph_t * g, apol_vector_t ** v)
+{
+ apol_infoflow_node_t *start_node;
+ int retval = -1;
+ if (p == NULL || g == NULL || v == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (*v == NULL) {
+ *v = apol_vector_create(infoflow_result_free);
+ }
+ if (g->further_start == NULL) {
+ ERR(p, "%s", "Infoflow graph was not prepared yet.");
+ goto cleanup;
+ }
+ start_node = apol_vector_get_element(g->further_start, g->current_start);
+ if (apol_infoflow_analysis_trans_further(p, g, start_node, *v) < 0) {
+ goto cleanup;
+ }
+ g->current_start++;
+ if (g->current_start >= apol_vector_get_size(g->further_start)) {
+ g->current_start = 0;
+ }
+ retval = 0;
+ cleanup:
+ return retval;
+}
+
+apol_infoflow_analysis_t *apol_infoflow_analysis_create(void)
+{
+ return calloc(1, sizeof(apol_infoflow_analysis_t));
+}
+
+void apol_infoflow_analysis_destroy(apol_infoflow_analysis_t ** ia)
+{
+ if (*ia != NULL) {
+ free((*ia)->type);
+ free((*ia)->result);
+ apol_vector_destroy(&(*ia)->intermed);
+ apol_vector_destroy(&(*ia)->class_perms);
+ free(*ia);
+ *ia = NULL;
+ }
+}
+
+int apol_infoflow_analysis_set_mode(const apol_policy_t * p, apol_infoflow_analysis_t * ia, unsigned int mode)
+{
+ switch (mode) {
+ case APOL_INFOFLOW_MODE_DIRECT:
+ case APOL_INFOFLOW_MODE_TRANS:
+ {
+ ia->mode = mode;
+ break;
+ }
+ default:
+ {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_infoflow_analysis_set_dir(const apol_policy_t * p, apol_infoflow_analysis_t * ia, unsigned int dir)
+{
+ switch (dir) {
+ case APOL_INFOFLOW_IN:
+ case APOL_INFOFLOW_OUT:
+ case APOL_INFOFLOW_BOTH:
+ case APOL_INFOFLOW_EITHER:
+ {
+ ia->direction = dir;
+ break;
+ }
+ default:
+ {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_infoflow_analysis_set_type(const apol_policy_t * p, apol_infoflow_analysis_t * ia, const char *name)
+{
+ if (name == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ return apol_query_set(p, &ia->type, NULL, name);
+}
+
+static int compare_class_perm_by_class_name(const void *in_op, const void *class_name, void *unused __attribute__ ((unused)))
+{
+ const apol_obj_perm_t *op = (const apol_obj_perm_t *)in_op;
+ const char *name = (const char *)class_name;
+
+ return strcmp(apol_obj_perm_get_obj_name(op), name);
+}
+
+int apol_infoflow_analysis_append_intermediate(const apol_policy_t * policy, apol_infoflow_analysis_t * ia, const char *type)
+{
+ char *tmp = NULL;
+ if (type == NULL) {
+ apol_vector_destroy(&ia->intermed);
+ return 0;
+ }
+ if (ia->intermed == NULL && (ia->intermed = apol_vector_create(free)) == NULL) {
+ ERR(policy, "Error appending type to analysis: %s", strerror(ENOMEM));
+ return -1;
+ }
+ if ((tmp = strdup(type)) == NULL || apol_vector_append(ia->intermed, tmp) < 0) {
+ free(tmp);
+ ERR(policy, "Error appending type to analysis: %s", strerror(ENOMEM));
+ return -1;
+ }
+ return 0;
+}
+
+int apol_infoflow_analysis_append_class_perm(const apol_policy_t * p,
+ apol_infoflow_analysis_t * ia, const char *class_name, const char *perm_name)
+{
+ apol_obj_perm_t *op = NULL;
+ size_t i;
+
+ if (p == NULL || ia == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (class_name == NULL) {
+ apol_vector_destroy(&ia->class_perms);
+ return 0;
+ }
+ if (perm_name == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (ia->class_perms == NULL && (ia->class_perms = apol_vector_create(apol_obj_perm_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+
+ if (apol_vector_get_index(ia->class_perms, (void *)class_name, compare_class_perm_by_class_name, NULL, &i) < 0) {
+ if (perm_name) {
+ if ((op = apol_obj_perm_create()) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+ if (apol_obj_perm_set_obj_name(op, class_name) ||
+ apol_obj_perm_append_perm(op, perm_name) || apol_vector_append(ia->class_perms, op)) {
+ ERR(p, "%s", strerror(errno));
+ apol_obj_perm_free(op);
+ return -1;
+ }
+ } else {
+ return 0; /* nothing to clear; done */
+ }
+ } else {
+ op = apol_vector_get_element(ia->class_perms, i);
+ if (apol_obj_perm_append_perm(op, perm_name)) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_infoflow_analysis_set_min_weight(const apol_policy_t * p
+ __attribute__ ((unused)), apol_infoflow_analysis_t * ia, int min_weight)
+{
+ if (min_weight <= 0) {
+ ia->min_weight = 0;
+ } else if (min_weight >= APOL_PERMMAP_MAX_WEIGHT) {
+ ia->min_weight = APOL_PERMMAP_MAX_WEIGHT;
+ } else {
+ ia->min_weight = min_weight;
+ }
+ return 0;
+}
+
+int apol_infoflow_analysis_set_result_regex(const apol_policy_t * p, apol_infoflow_analysis_t * ia, const char *result)
+{
+ return apol_query_set(p, &ia->result, NULL, result);
+}
+
+/*************** functions to access infoflow results ***************/
+
+unsigned int apol_infoflow_result_get_dir(const apol_infoflow_result_t * result)
+{
+ if (!result) {
+ errno = EINVAL;
+ return 0;
+ }
+ return result->direction;
+}
+
+const qpol_type_t *apol_infoflow_result_get_start_type(const apol_infoflow_result_t * result)
+{
+ if (!result) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return result->start_type;
+}
+
+const qpol_type_t *apol_infoflow_result_get_end_type(const apol_infoflow_result_t * result)
+{
+ if (!result) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return result->end_type;
+}
+
+unsigned int apol_infoflow_result_get_length(const apol_infoflow_result_t * result)
+{
+ if (!result) {
+ errno = EINVAL;
+ return 0;
+ }
+ assert(result->length != 0);
+ return result->length;
+}
+
+const apol_vector_t *apol_infoflow_result_get_steps(const apol_infoflow_result_t * result)
+{
+ if (!result) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return result->steps;
+}
+
+const qpol_type_t *apol_infoflow_step_get_start_type(const apol_infoflow_step_t * step)
+{
+ if (!step) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return step->start_type;
+}
+
+const qpol_type_t *apol_infoflow_step_get_end_type(const apol_infoflow_step_t * step)
+{
+ if (!step) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return step->end_type;
+}
+
+int apol_infoflow_step_get_weight(const apol_infoflow_step_t * step)
+{
+ if (!step) {
+ errno = EINVAL;
+ return -1;
+ }
+ return step->weight;
+}
+
+const apol_vector_t *apol_infoflow_step_get_rules(const apol_infoflow_step_t * step)
+{
+ if (!step) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return step->rules;
+}
+
+/******************** protected functions ********************/
+
+apol_infoflow_result_t *infoflow_result_create_from_infoflow_result(const apol_infoflow_result_t * result)
+{
+ apol_infoflow_result_t *new_r = NULL;
+ apol_infoflow_step_t *step, *new_step;
+ size_t i;
+ int retval = -1;
+
+ if ((new_r = calloc(1, sizeof(*new_r))) == NULL ||
+ (new_r->steps = apol_vector_create_with_capacity(apol_vector_get_size(result->steps), apol_infoflow_step_free)) == NULL)
+ {
+ goto cleanup;
+ }
+ new_r->start_type = result->start_type;
+ new_r->end_type = result->end_type;
+ new_r->direction = result->direction;
+ new_r->length = result->length;
+ for (i = 0; i < apol_vector_get_size(result->steps); i++) {
+ step = (apol_infoflow_step_t *) apol_vector_get_element(result->steps, i);
+ if ((new_step = calloc(1, sizeof(*new_step))) == NULL ||
+ (new_step->rules = apol_vector_create_from_vector(step->rules, NULL, NULL, NULL)) == NULL ||
+ apol_vector_append(new_r->steps, new_step) < 0) {
+ apol_infoflow_step_free(new_step);
+ goto cleanup;
+ }
+ new_step->start_type = step->start_type;
+ new_step->end_type = step->end_type;
+ new_step->weight = step->weight;
+ }
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ infoflow_result_free(new_r);
+ return NULL;
+ }
+ return new_r;
+}
+
+void infoflow_result_free(void *result)
+{
+ if (result != NULL) {
+ apol_infoflow_result_t *r = (apol_infoflow_result_t *) result;
+ apol_vector_destroy(&r->steps);
+ free(r);
+ }
+}
diff --git a/libapol/src/isid-query.c b/libapol/src/isid-query.c
new file mode 100644
index 0000000..9cc6211
--- /dev/null
+++ b/libapol/src/isid-query.c
@@ -0,0 +1,123 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about initial SIDs
+ * within a policy. The caller obtains a query object, fills in its
+ * parameters, and then runs the query; it obtains a vector of
+ * results. Searches are conjunctive -- all fields of the search
+ * query must match for a datum to be added to the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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>
+
+struct apol_isid_query
+{
+ char *name;
+ apol_context_t *context;
+ unsigned int flags;
+};
+
+/******************** genfscon queries ********************/
+
+int apol_isid_get_by_query(const apol_policy_t * p, const apol_isid_query_t * i, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1, retval2;
+ const qpol_isid_t *isid = NULL;
+ *v = NULL;
+ if (qpol_policy_get_isid_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&isid) < 0) {
+ goto cleanup;
+ }
+ if (i != NULL) {
+ const char *name;
+ const qpol_context_t *context;
+ if (qpol_isid_get_name(p->p, isid, &name) < 0 || qpol_isid_get_context(p->p, isid, &context) < 0) {
+ goto cleanup;
+ }
+ retval2 = apol_compare(p, name, i->name, 0, NULL);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ continue;
+ }
+ retval2 = apol_compare_context(p, context, i->context, i->flags);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, (void *)isid)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_isid_query_t *apol_isid_query_create(void)
+{
+ return calloc(1, sizeof(apol_isid_query_t));
+}
+
+void apol_isid_query_destroy(apol_isid_query_t ** i)
+{
+ if (*i != NULL) {
+ free((*i)->name);
+ apol_context_destroy(&((*i)->context));
+ free(*i);
+ *i = NULL;
+ }
+}
+
+int apol_isid_query_set_name(const apol_policy_t * p, apol_isid_query_t * i, const char *name)
+{
+ return apol_query_set(p, &i->name, NULL, name);
+}
+
+int apol_isid_query_set_context(const apol_policy_t * p __attribute__ ((unused)),
+ apol_isid_query_t * i, apol_context_t * context, unsigned int range_match)
+{
+ if (i->context != NULL) {
+ apol_context_destroy(&i->context);
+ }
+ i->context = context;
+ i->flags = (i->flags & ~APOL_QUERY_FLAGS) | range_match;
+ return 0;
+}
diff --git a/libapol/src/libapol.map b/libapol/src/libapol.map
new file mode 100644
index 0000000..4894374
--- /dev/null
+++ b/libapol/src/libapol.map
@@ -0,0 +1,86 @@
+VERS_4.0{
+ global:
+ apol_attr_*;
+ apol_avrule_*;
+ apol_bool_*;
+ apol_bst_*;
+ apol_cat_*;
+ apol_class_*;
+ apol_common_*;
+ apol_cond_*;
+ apol_config_*;
+ apol_constraint_*;
+ apol_context_*;
+ apol_domain_*;
+ apol_file_*;
+ apol_fs_use_*;
+ apol_genfscon_*;
+ apol_get_*;
+ apol_handle_msg;
+ apol_infoflow_*;
+ apol_ipv4_addr_render;
+ apol_ipv6_addr_render;
+ apol_isid_*;
+ apol_level_*;
+ apol_mls_*;
+ apol_netifcon_*;
+ apol_nodecon_*;
+ apol_objclass_to_str;
+ apol_perm_*;
+ apol_permmap_*;
+ apol_policy_*;
+ apol_policy_path_*;
+ apol_portcon_*;
+ apol_protocol_to_str;
+ apol_qpol_context_render;
+ apol_range_trans_*;
+ apol_relabel_*;
+ apol_role_*;
+ apol_role_allow_*;
+ apol_role_trans_*;
+ apol_rule_type_to_str;
+ apol_str_*;
+ apol_syn_*;
+ apol_terule_*;
+ apol_type_*;
+ apol_types_relation_*;
+ apol_user_*;
+ apol_validatetrans_*;
+ apol_vector_*;
+ libapol_get_version;
+ local: *;
+};
+
+VERS_4.1{
+ global:
+ apol_avrule_query_set_all_perms;
+ apol_bst_inorder_map;
+ apol_context_convert;
+ apol_context_create_from_literal;
+ apol_domain_trans_analysis_append_class;
+ apol_domain_trans_analysis_append_perm;
+ apol_mls_level_convert;
+ apol_mls_level_create_from_literal;
+ apol_mls_level_is_literal;
+ apol_mls_level_validate;
+ apol_mls_range_convert;
+ apol_mls_range_create_from_literal;
+ apol_mls_range_create_from_string;
+ apol_mls_range_is_literal;
+ apol_nodecon_query_set_protocol;
+ apol_policy_build_domain_trans_table;
+ apol_policy_get_permmap;
+ apol_policy_open_permmap;
+ apol_policy_reset_domain_trans_table;
+ apol_policy_save_permmap;
+ apol_policy_set_permmap;
+ apol_portcon_query_set_protocol;
+ apol_str_to_protocol;
+ apol_str_to_objclass;
+} VERS_4.0;
+
+VERS_4.2{
+ global:
+ apol_permissive_*;
+ apol_polcap_*;
+} VERS_4.1;
diff --git a/libapol/src/mls-query.c b/libapol/src/mls-query.c
new file mode 100644
index 0000000..6fccc54
--- /dev/null
+++ b/libapol/src/mls-query.c
@@ -0,0 +1,248 @@
+/**
+ * @file
+ * Implementation for querying MLS components.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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 <config.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <regex.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <qpol/iterator.h>
+
+#include "policy-query-internal.h"
+#include <apol/vector.h>
+
+struct apol_level_query
+{
+ char *sens_name, *cat_name;
+ unsigned int flags;
+ regex_t *sens_regex, *cat_regex;
+};
+
+struct apol_cat_query
+{
+ char *cat_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+int apol_mls_sens_compare(const apol_policy_t * p, const char *sens1, const char *sens2)
+{
+ const qpol_level_t *level_datum1, *level_datum2;
+ if (qpol_policy_get_level_by_name(p->p, sens1, &level_datum1) < 0 ||
+ qpol_policy_get_level_by_name(p->p, sens2, &level_datum2) < 0) {
+ return -1;
+ }
+ if (level_datum1 == level_datum2) {
+ return 1;
+ }
+ return 0;
+}
+
+int apol_mls_cats_compare(const apol_policy_t * p, const char *cat1, const char *cat2)
+{
+ const qpol_cat_t *qcat1, *qcat2;
+ if (qpol_policy_get_cat_by_name(p->p, cat1, &qcat1) < 0 || qpol_policy_get_cat_by_name(p->p, cat2, &qcat2) < 0) {
+ return -1;
+ }
+ if (qcat1 == qcat2) {
+ return 1;
+ }
+ return 0;
+}
+
+/******************** level queries ********************/
+
+int apol_level_get_by_query(const apol_policy_t * p, apol_level_query_t * l, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter, *cat_iter = NULL;
+ int retval = -1, append_level;
+ *v = NULL;
+ if (qpol_policy_get_level_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_level_t *level;
+ unsigned char isalias;
+ if (qpol_iterator_get_item(iter, (void **)&level) < 0 || qpol_level_get_isalias(p->p, level, &isalias) < 0) {
+ goto cleanup;
+ }
+ if (isalias) {
+ continue;
+ }
+ append_level = 1;
+ if (l != NULL) {
+ int compval = apol_compare_level(p,
+ level, l->sens_name,
+ l->flags, &(l->sens_regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ if (qpol_level_get_cat_iter(p->p, level, &cat_iter) < 0) {
+ goto cleanup;
+ }
+ append_level = 0;
+ for (; !qpol_iterator_end(cat_iter); qpol_iterator_next(cat_iter)) {
+ qpol_cat_t *cat;
+ if (qpol_iterator_get_item(cat_iter, (void **)&cat) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare_cat(p, cat, l->cat_name, l->flags, &(l->cat_regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 1) {
+ append_level = 1;
+ break;
+ }
+ }
+ qpol_iterator_destroy(&cat_iter);
+ }
+ if (append_level && apol_vector_append(*v, level)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&cat_iter);
+ return retval;
+}
+
+apol_level_query_t *apol_level_query_create(void)
+{
+ return calloc(1, sizeof(apol_level_query_t));
+}
+
+void apol_level_query_destroy(apol_level_query_t ** l)
+{
+ if (*l != NULL) {
+ free((*l)->sens_name);
+ free((*l)->cat_name);
+ apol_regex_destroy(&(*l)->sens_regex);
+ apol_regex_destroy(&(*l)->cat_regex);
+ free(*l);
+ *l = NULL;
+ }
+}
+
+int apol_level_query_set_sens(const apol_policy_t * p, apol_level_query_t * l, const char *name)
+{
+ return apol_query_set(p, &l->sens_name, &l->sens_regex, name);
+}
+
+int apol_level_query_set_cat(const apol_policy_t * p, apol_level_query_t * l, const char *name)
+{
+ return apol_query_set(p, &l->cat_name, &l->cat_regex, name);
+}
+
+int apol_level_query_set_regex(const apol_policy_t * p, apol_level_query_t * l, int is_regex)
+{
+ return apol_query_set_regex(p, &l->flags, is_regex);
+}
+
+/******************** category queries ********************/
+
+int apol_cat_get_by_query(const apol_policy_t * p, apol_cat_query_t * c, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1;
+ *v = NULL;
+ if (qpol_policy_get_cat_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_cat_t *cat;
+ unsigned char isalias;
+ if (qpol_iterator_get_item(iter, (void **)&cat) < 0 || qpol_cat_get_isalias(p->p, cat, &isalias) < 0) {
+ goto cleanup;
+ }
+ if (isalias) {
+ continue;
+ }
+ if (c != NULL) {
+ int compval = apol_compare_cat(p,
+ cat, c->cat_name,
+ c->flags, &(c->regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, cat)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_cat_query_t *apol_cat_query_create(void)
+{
+ return calloc(1, sizeof(apol_cat_query_t));
+}
+
+void apol_cat_query_destroy(apol_cat_query_t ** c)
+{
+ if (*c != NULL) {
+ free((*c)->cat_name);
+ apol_regex_destroy(&(*c)->regex);
+ free(*c);
+ *c = NULL;
+ }
+}
+
+int apol_cat_query_set_cat(const apol_policy_t * p, apol_cat_query_t * c, const char *name)
+{
+ return apol_query_set(p, &c->cat_name, &c->regex, name);
+}
+
+int apol_cat_query_set_regex(const apol_policy_t * p, apol_cat_query_t * c, int is_regex)
+{
+ return apol_query_set_regex(p, &c->flags, is_regex);
+}
diff --git a/libapol/src/mls_level.c b/libapol/src/mls_level.c
new file mode 100644
index 0000000..26a1469
--- /dev/null
+++ b/libapol/src/mls_level.c
@@ -0,0 +1,771 @@
+/**
+ * @file
+ * Implementation of apol_mls_level class.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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 <config.h>
+
+#include <apol/mls_level.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "policy-query-internal.h"
+
+#include <qpol/iterator.h>
+#include <apol/vector.h>
+
+struct apol_mls_level
+{
+ char *sens;
+ apol_vector_t *cats; // if NULL, then level is incomplete
+ char *literal_cats;
+};
+
+/********************* miscellaneous routines *********************/
+
+/* Given a category datum and a category name, returns < 0 if a has
+ * higher value than b, > 0 if b is higher according to the given
+ * policy. If the two are equal or upon error, return 0.
+ */
+static int apol_mls_cat_vector_compare(const void *a, const void *b, void *data)
+{
+ const qpol_cat_t *cat1 = a;
+ const char *cat2_name = b;
+ apol_policy_t *p = (apol_policy_t *) data;
+ const qpol_cat_t *cat2;
+ uint32_t cat_value1, cat_value2;
+ if (qpol_policy_get_cat_by_name(p->p, cat2_name, &cat2) < 0) {
+ return 0;
+ }
+ if (qpol_cat_get_value(p->p, cat1, &cat_value1) < 0 || qpol_cat_get_value(p->p, cat2, &cat_value2) < 0) {
+ return 0;
+ }
+ return (cat_value2 - cat_value1);
+}
+
+/**
+ * Given two category names, returns < 0 if a has higher value than b,
+ * > 0 if b is higher. The comparison is against the categories'
+ * values according to the supplied policy. If the two are equal or
+ * upon error, return 0.
+ *
+ * @param a First category name to compare.
+ * @param b Other name to compare.
+ * @param data Pointer to a policy to which use for comparison.
+ *
+ * @return <0, 0, or >0 if a is less than, equal, or greater than b,
+ * respectively.
+ */
+static int apol_mls_cat_name_compare(const void *a, const void *b, void *data)
+{
+ const char *cat1 = a;
+ const char *cat2 = b;
+ apol_policy_t *p = (apol_policy_t *) data;
+ const qpol_cat_t *qcat1, *qcat2;
+ uint32_t cat_value1, cat_value2;
+ if (qpol_policy_get_cat_by_name(p->p, cat1, &qcat1) < 0 || qpol_policy_get_cat_by_name(p->p, cat2, &qcat2) < 0) {
+ return 0;
+ }
+ if (qpol_cat_get_value(p->p, qcat1, &cat_value1) < 0 || qpol_cat_get_value(p->p, qcat2, &cat_value2) < 0) {
+ return 0;
+ }
+ return (cat_value1 - cat_value2);
+}
+
+/********************* level *********************/
+
+apol_mls_level_t *apol_mls_level_create(void)
+{
+ apol_mls_level_t *l;
+ if ((l = calloc(1, sizeof(*l))) == NULL || (l->cats = apol_vector_create(free)) == NULL) {
+ apol_mls_level_destroy(&l);
+ return NULL;
+ }
+ return l;
+}
+
+apol_mls_level_t *apol_mls_level_create_from_mls_level(const apol_mls_level_t * level)
+{
+ apol_mls_level_t *l;
+ if ((l = calloc(1, sizeof(*l))) == NULL) {
+ return NULL;
+ }
+ if (level != NULL) {
+ if ((level->sens != NULL) && (l->sens = strdup(level->sens)) == NULL) {
+ apol_mls_level_destroy(&l);
+ return NULL;
+ }
+ if ((level->cats != NULL) &&
+ (l->cats = apol_vector_create_from_vector(level->cats, apol_str_strdup, NULL, free)) == NULL) {
+ apol_mls_level_destroy(&l);
+ return NULL;
+ }
+ if ((level->literal_cats != NULL) && (l->literal_cats = strdup(level->literal_cats)) == NULL) {
+ apol_mls_level_destroy(&l);
+ return NULL;
+ }
+ }
+ return l;
+}
+
+apol_mls_level_t *apol_mls_level_create_from_string(const apol_policy_t * p, const char *mls_level_string)
+{
+ if (p == NULL || mls_level_string == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ apol_mls_level_t *l = apol_mls_level_create_from_literal(mls_level_string);
+ if (l == NULL) {
+ ERR(p, "%s", strerror(errno));
+ return NULL;
+ }
+
+ if (apol_mls_level_convert(p, l) < 0) {
+ apol_mls_level_destroy(&l);
+ return NULL;
+ }
+ free(l->literal_cats);
+ l->literal_cats = NULL;
+ return l;
+}
+
+apol_mls_level_t *apol_mls_level_create_from_literal(const char *mls_level_string)
+{
+ apol_mls_level_t *l;
+ char *colon;
+ if (mls_level_string == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((l = calloc(1, sizeof(*l))) == NULL) {
+ return NULL;
+ }
+ if ((colon = strchr(mls_level_string, ':')) != NULL) {
+ // both a sensitivity and 1 or more categories
+ if (colon == mls_level_string) {
+ apol_mls_level_destroy(&l);
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((l->sens = strndup(mls_level_string, colon - mls_level_string)) == NULL) {
+ apol_mls_level_destroy(&l);
+ return NULL;
+ }
+ // store everything after the colon as the category string
+ if ((l->literal_cats = strdup(colon + 1)) == NULL) {
+ apol_mls_level_destroy(&l);
+ return NULL;
+ }
+ apol_str_trim(l->literal_cats);
+ } else {
+ // no category, just a sensitivity
+ if ((l->sens = strdup(mls_level_string)) == NULL || (l->literal_cats = strdup("")) == NULL) {
+ apol_mls_level_destroy(&l);
+ return NULL;
+ }
+ }
+ apol_str_trim(l->sens);
+ return l;
+}
+
+apol_mls_level_t *apol_mls_level_create_from_qpol_mls_level(const apol_policy_t * p, const qpol_mls_level_t * qpol_level)
+{
+ apol_mls_level_t *lvl = NULL;
+ qpol_iterator_t *iter = NULL;
+ const qpol_cat_t *tmp_cat = NULL;
+ const char *tmp = NULL;
+ int error = 0;
+
+ if (!p || !qpol_level) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ goto err;
+ }
+
+ if ((lvl = apol_mls_level_create()) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_mls_level_get_sens_name(p->p, qpol_level, &tmp) || qpol_mls_level_get_cat_iter(p->p, qpol_level, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_mls_level_set_sens(p, lvl, tmp) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&tmp_cat) < 0 || qpol_cat_get_name(p->p, tmp_cat, &tmp) < 0) {
+ error = errno;
+ goto err;
+ }
+ if (apol_mls_level_append_cats(p, lvl, tmp) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ }
+
+ qpol_iterator_destroy(&iter);
+ return lvl;
+
+ err:
+ apol_mls_level_destroy(&lvl);
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+}
+
+apol_mls_level_t *apol_mls_level_create_from_qpol_level_datum(const apol_policy_t * p, const qpol_level_t * qpol_level)
+{
+ apol_mls_level_t *lvl = NULL;
+ qpol_iterator_t *iter = NULL;
+ const qpol_cat_t *tmp_cat = NULL;
+ const char *tmp = NULL;
+ int error = 0;
+
+ if (!p || !qpol_level) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((lvl = apol_mls_level_create()) == NULL) {
+ ERR(p, "%s", strerror(error));
+ return NULL;
+ }
+ if (qpol_level_get_name(p->p, qpol_level, &tmp)) {
+ error = errno;
+ goto err;
+ }
+ if ((lvl->sens = strdup(tmp)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+
+ if (qpol_level_get_cat_iter(p->p, qpol_level, &iter)) {
+ error = errno;
+ goto err;
+ }
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&tmp_cat)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_cat_get_name(p->p, tmp_cat, &tmp)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_mls_level_append_cats(p, lvl, tmp)) {
+ error = errno;
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ return lvl;
+
+ err:
+ apol_mls_level_destroy(&lvl);
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+}
+
+static void mls_level_free(void *level)
+{
+ if (level != NULL) {
+ apol_mls_level_t *l = level;
+ free(l->sens);
+ apol_vector_destroy(&l->cats);
+ free(l->literal_cats);
+ free(l);
+ }
+}
+
+void apol_mls_level_destroy(apol_mls_level_t ** level)
+{
+ if (!level || !(*level))
+ return;
+ mls_level_free(*level);
+ *level = NULL;
+}
+
+int apol_mls_level_set_sens(const apol_policy_t * p, apol_mls_level_t * level, const char *sens)
+{
+ if (!level) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ return apol_query_set(p, &level->sens, NULL, sens);
+}
+
+const char *apol_mls_level_get_sens(const apol_mls_level_t * level)
+{
+ if (!level) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return level->sens;
+}
+
+int apol_mls_level_append_cats(const apol_policy_t * p, apol_mls_level_t * level, const char *cats)
+{
+ char *new_cat = NULL;
+ if (!level || !cats || level->cats == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (level->cats == NULL && (level->cats = apol_vector_create(free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+ if ((new_cat = strdup(cats)) == NULL || apol_vector_append(level->cats, (void *)new_cat) < 0) {
+ ERR(p, "%s", strerror(errno));
+ free(new_cat);
+ return -1;
+ }
+ apol_vector_sort(level->cats, apol_str_strcmp, NULL);
+ return 0;
+}
+
+const apol_vector_t *apol_mls_level_get_cats(const apol_mls_level_t * level)
+{
+ if (!level || level->cats == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return level->cats;
+}
+
+int apol_mls_level_compare(const apol_policy_t * p, const apol_mls_level_t * l1, const apol_mls_level_t * l2)
+{
+ const qpol_level_t *level_datum1, *level_datum2;
+ int level1_sens, level2_sens, sens_cmp;
+ size_t l1_size, l2_size, i, j;
+ int m_list, ucat = 0;
+ apol_vector_t *cat_list_master, *cat_list_subset;
+ if (l2 == NULL) {
+ return APOL_MLS_EQ;
+ }
+ if ((l1 != NULL && l1->cats == NULL) || (l2->cats == NULL)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (qpol_policy_get_level_by_name(p->p, l1->sens, &level_datum1) < 0 ||
+ qpol_policy_get_level_by_name(p->p, l2->sens, &level_datum2) < 0) {
+ return -1;
+ }
+
+ /* compare the level's senstitivity value */
+ if (qpol_level_get_value(p->p, level_datum1, (uint32_t *) (&level1_sens)) < 0 ||
+ qpol_level_get_value(p->p, level_datum2, (uint32_t *) (&level2_sens)) < 0) {
+ return -1;
+ }
+ sens_cmp = level1_sens - level2_sens;
+
+ /* determine if all the categories in one level are in the other set */
+ l1_size = apol_vector_get_size(l1->cats);
+ l2_size = apol_vector_get_size(l2->cats);
+ if (l1_size < l2_size) {
+ m_list = 2;
+ cat_list_master = l2->cats;
+ cat_list_subset = l1->cats;
+ } else {
+ m_list = 1;
+ cat_list_master = l1->cats;
+ cat_list_subset = l2->cats;
+ }
+ for (i = 0; i < apol_vector_get_size(cat_list_subset); i++) {
+ char *cat = (char *)apol_vector_get_element(cat_list_subset, i);
+ if (apol_vector_get_index(cat_list_master, cat, apol_mls_cat_name_compare, (void *)p, &j) < 0) {
+ ucat = 1;
+ break;
+ }
+ }
+
+ if (!sens_cmp && !ucat && l1_size == l2_size)
+ return APOL_MLS_EQ;
+ if (sens_cmp >= 0 && m_list == 1 && !ucat)
+ return APOL_MLS_DOM;
+ if (sens_cmp <= 0 && (m_list == 2 || l1_size == l2_size) && !ucat)
+ return APOL_MLS_DOMBY;
+ return APOL_MLS_INCOMP;
+}
+
+int apol_mls_level_validate(const apol_policy_t * p, const apol_mls_level_t * level)
+{
+ const qpol_level_t *level_datum;
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *cat_vector;
+ int retval = -1;
+ size_t i, j;
+
+ if (p == NULL || level == NULL || level->cats == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (level->sens == NULL) {
+ return 0;
+ }
+ if (qpol_policy_get_level_by_name(p->p, level->sens, &level_datum) < 0 ||
+ qpol_level_get_cat_iter(p->p, level_datum, &iter) < 0) {
+ return -1;
+ }
+ if ((cat_vector = apol_vector_create_from_iter(iter, NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ for (i = 0; i < apol_vector_get_size(level->cats); i++) {
+ char *cat_name = (char *)apol_vector_get_element(level->cats, i);
+ if (apol_vector_get_index(cat_vector, cat_name, apol_mls_cat_vector_compare, (void *)p, &j) < 0) {
+ retval = 0;
+ goto cleanup;
+ }
+ }
+
+ retval = 1;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ apol_vector_destroy(&cat_vector);
+ return retval;
+}
+
+char *apol_mls_level_render(const apol_policy_t * p, const apol_mls_level_t * level)
+{
+ char *rt = NULL;
+ const char *name = NULL, *sens_name = NULL, *cat_name = NULL;
+ char *retval = NULL;
+ int cur;
+ const qpol_cat_t *cur_cat = NULL, *next_cat = NULL;
+ uint32_t cur_cat_val, next_cat_val, far_cat_val;
+ apol_vector_t *cats = NULL;
+ size_t sz = 0, n_cats = 0, i;
+
+ if (!level || (p == NULL && level->cats != NULL)) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ goto cleanup;
+ }
+
+ sens_name = level->sens;
+ if (!sens_name)
+ goto cleanup;
+ if (apol_str_append(&rt, &sz, sens_name)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ if (level->cats != NULL) {
+ if ((cats = apol_vector_create_from_vector(level->cats, apol_str_strdup, NULL, free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ n_cats = apol_vector_get_size(cats);
+ }
+ if (n_cats == 0) {
+ if (level->literal_cats != NULL && level->literal_cats[0] != '\0') {
+ if (apol_str_appendf(&rt, &sz, ":%s", level->literal_cats)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ }
+ retval = rt;
+ goto cleanup;
+ }
+ apol_vector_sort(cats, apol_mls_cat_name_compare, (void *)p);
+
+ cat_name = (char *)apol_vector_get_element(cats, 0);
+ if (!cat_name)
+ goto cleanup;
+
+ if (apol_str_appendf(&rt, &sz, ":%s", cat_name)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ cur = 0; /* current value to compare with cat[i] */
+ for (i = 1; i < n_cats; i++) { /* we've already appended the first category */
+ /* get the value of cats[cur] */
+ cat_name = (char *)apol_vector_get_element(cats, cur);
+ if (qpol_policy_get_cat_by_name(p->p, cat_name, &cur_cat))
+ goto cleanup;
+ if (qpol_cat_get_value(p->p, cur_cat, &cur_cat_val))
+ goto cleanup;
+
+ /* get the value of cats[i] */
+ cat_name = (char *)apol_vector_get_element(cats, i);
+ if (qpol_policy_get_cat_by_name(p->p, cat_name, &next_cat))
+ goto cleanup;
+ if (qpol_cat_get_value(p->p, next_cat, &next_cat_val))
+ goto cleanup;
+
+ if (next_cat_val == cur_cat_val + 1) {
+ if (i + 1 == n_cats) { /* last category is next; append "." */
+ if (qpol_cat_get_name(p->p, next_cat, &name))
+ goto cleanup;
+ if (apol_str_appendf(&rt, &sz, ".%s", name)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ cur = i;
+ } else {
+ const qpol_cat_t *far_cat = NULL; /* category 2 in front of cur */
+ cat_name = (char *)apol_vector_get_element(cats, i + 1);
+ if (qpol_policy_get_cat_by_name(p->p, cat_name, &far_cat))
+ goto cleanup;
+ if (qpol_cat_get_value(p->p, far_cat, &far_cat_val))
+ goto cleanup;
+ if (far_cat_val == cur_cat_val + 2) {
+ cur++;
+ } else { /* far_cat isn't consecutive wrt cur/next_cat; append it */
+ if (qpol_cat_get_name(p->p, next_cat, &name))
+ goto cleanup;
+ if (apol_str_appendf(&rt, &sz, ".%s", name)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ cur = i;
+ }
+ }
+ } else { /* next_cat isn't consecutive to cur_cat; append it */
+ if (qpol_cat_get_name(p->p, next_cat, &name))
+ goto cleanup;
+ if (apol_str_appendf(&rt, &sz, ", %s", name)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ cur = i;
+ }
+ }
+
+ retval = rt;
+ cleanup:
+ apol_vector_destroy(&cats);
+ if (retval != rt) {
+ free(rt);
+ }
+ return retval;
+}
+
+int apol_mls_level_convert(const apol_policy_t * p, apol_mls_level_t * level)
+{
+ const char *tmp, *cat_name;
+ char **tokens = NULL, *next = NULL;
+ size_t num_tokens = 1, i;
+ qpol_iterator_t *iter = NULL;
+ const qpol_level_t *sens = NULL;
+ const qpol_cat_t *cat1 = NULL, *cat2 = NULL, *tmp_cat = NULL;
+ uint32_t val1 = 0, val2 = 0, tmp_val = 0;
+ unsigned char tmp_isalias = 0;
+
+ int error = 0;
+ if (p == NULL || level == NULL || level->literal_cats == NULL) {
+ error = EINVAL;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+
+ apol_vector_destroy(&level->cats);
+ if (level->literal_cats[0] == '\0') {
+ if ((level->cats = apol_vector_create_with_capacity(1, free)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ return 0;
+ }
+
+ for (tmp = level->literal_cats; *tmp; tmp++) {
+ if ((next = strchr(tmp, ','))) {
+ tmp = next;
+ num_tokens++;
+ }
+ }
+ tokens = calloc(num_tokens, sizeof(char *));
+ if (!tokens) {
+ error = errno;
+ ERR(p, "%s", strerror(ENOMEM));
+ goto err;
+ }
+ if ((level->cats = apol_vector_create_with_capacity(num_tokens, free)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+
+ for (tmp = level->literal_cats, i = 0; *tmp && i < num_tokens; tmp++) {
+ if (isspace(*tmp))
+ continue;
+ next = strchr(tmp, ',');
+ if (next) {
+ tokens[i] = strndup(tmp, next - tmp);
+ if (!tokens[i]) {
+ error = errno;
+ goto err;
+ }
+ tmp = next;
+ next = NULL;
+ i++;
+ } else {
+ tokens[i] = strdup(tmp);
+ if (!tokens[i]) {
+ error = errno;
+ ERR(p, "%s", strerror(ENOMEM));
+ goto err;
+ }
+ i++;
+ if (i != num_tokens) {
+ error = EIO;
+ goto err;
+ }
+ }
+ }
+
+ if (qpol_policy_get_level_by_name(p->p, level->sens, &sens)) {
+ error = errno;
+ goto err;
+ }
+
+ for (i = 0; i < num_tokens; i++) {
+ next = strchr(tokens[i], '.');
+ if (next) {
+ *next = '\0';
+ next++;
+
+ /* get end points of cat range */
+ if (qpol_policy_get_cat_by_name(p->p, tokens[i], &cat1)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_policy_get_cat_by_name(p->p, next, &cat2)) {
+ error = errno;
+ goto err;
+ }
+
+ /* get end point values */
+ if (qpol_cat_get_value(p->p, cat1, &val1)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_cat_get_value(p->p, cat2, &val2)) {
+ error = errno;
+ goto err;
+ }
+ if (val1 >= val2) {
+ error = EINVAL;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ if (apol_mls_level_append_cats(p, level, tokens[i])) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_policy_get_cat_iter(p->p, &iter)) {
+ error = errno;
+ goto err;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&tmp_cat)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_cat_get_isalias(p->p, tmp_cat, &tmp_isalias)) {
+ error = errno;
+ goto err;
+ }
+ if (tmp_isalias)
+ continue;
+ if (qpol_cat_get_value(p->p, tmp_cat, &tmp_val)) {
+ error = errno;
+ goto err;
+ }
+ if (tmp_val > val1 && tmp_val < val2) {
+ if (qpol_cat_get_name(p->p, tmp_cat, &cat_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_mls_level_append_cats(p, level, cat_name)) {
+ error = errno;
+ goto err;
+ }
+ }
+ }
+ if (apol_mls_level_append_cats(p, level, next)) {
+ error = errno;
+ goto err;
+ }
+ } else {
+ if (qpol_policy_get_cat_by_name(p->p, tokens[i], &cat1)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_mls_level_append_cats(p, level, tokens[i])) {
+ error = errno;
+ goto err;
+ }
+ }
+ }
+
+ if (tokens) {
+ for (i = 0; i < num_tokens; i++)
+ free(tokens[i]);
+ free(tokens);
+ }
+
+ qpol_iterator_destroy(&iter);
+ return 0;
+
+ err:
+ if (tokens) {
+ for (i = 0; i < num_tokens; i++)
+ free(tokens[i]);
+ free(tokens);
+ }
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return -1;
+}
+
+int apol_mls_level_is_literal(const apol_mls_level_t * level)
+{
+ if (level == NULL) {
+ return -1;
+ }
+ if (level->literal_cats != NULL) {
+ return 1;
+ }
+ return 0;
+}
diff --git a/libapol/src/mls_range.c b/libapol/src/mls_range.c
new file mode 100644
index 0000000..cefe8ac
--- /dev/null
+++ b/libapol/src/mls_range.c
@@ -0,0 +1,641 @@
+/**
+ * @file
+ * Implementation of apol_mls_range class.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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 <config.h>
+
+#include <apol/mls_range.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "policy-query-internal.h"
+
+#include <qpol/iterator.h>
+#include <apol/vector.h>
+
+struct apol_mls_range
+{
+ apol_mls_level_t *low, *high;
+};
+
+apol_mls_range_t *apol_mls_range_create(void)
+{
+ return calloc(1, sizeof(apol_mls_range_t));
+}
+
+apol_mls_range_t *apol_mls_range_create_from_mls_range(const apol_mls_range_t * range)
+{
+ apol_mls_range_t *r;
+ if ((r = apol_mls_range_create()) == NULL) {
+ return NULL;
+ }
+ if (range != NULL &&
+ ((r->low = apol_mls_level_create_from_mls_level(range->low)) == NULL ||
+ (r->high = apol_mls_level_create_from_mls_level(range->high)) == NULL)) {
+ apol_mls_range_destroy(&r);
+ return NULL;
+ }
+ return r;
+}
+
+apol_mls_range_t *apol_mls_range_create_from_string(const apol_policy_t * p, const char *mls_range_string)
+{
+ if (p == NULL || mls_range_string == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ apol_mls_range_t *r = apol_mls_range_create();
+ if (r == NULL) {
+ ERR(p, "%s", strerror(errno));
+ return NULL;
+ }
+ char *dash;
+ if ((dash = strchr(mls_range_string, '-')) == NULL) {
+ // just a low level
+ apol_mls_level_t *l = apol_mls_level_create_from_string(p, mls_range_string);
+ if (l == NULL) {
+ ERR(p, "%s", strerror(errno));
+ apol_mls_range_destroy(&r);
+ return NULL;
+ }
+ r->low = l;
+ } else {
+ // both a low and a high level
+ if (dash == mls_range_string) {
+ apol_mls_range_destroy(&r);
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ char *s = strndup(mls_range_string, dash - mls_range_string);
+ if (s == NULL) {
+ ERR(p, "%s", strerror(errno));
+ apol_mls_range_destroy(&r);
+ return NULL;
+ }
+ apol_mls_level_t *l = apol_mls_level_create_from_string(p, s);
+ if (l == NULL) {
+ ERR(p, "%s", strerror(errno));
+ apol_mls_range_destroy(&r);
+ free(s);
+ return NULL;
+ }
+ r->low = l;
+ free(s);
+ l = NULL;
+
+ if ((l = apol_mls_level_create_from_string(p, dash + 1)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ apol_mls_range_destroy(&r);
+ return NULL;
+ }
+ r->high = l;
+ }
+
+ if (apol_mls_range_validate(p, r) <= 0) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ apol_mls_range_destroy(&r);
+ return NULL;
+ }
+ return r;
+}
+
+apol_mls_range_t *apol_mls_range_create_from_literal(const char *mls_range_string)
+{
+ if (mls_range_string == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ apol_mls_range_t *r = apol_mls_range_create();
+ if (r == NULL) {
+ return NULL;
+ }
+ char *dash;
+ if ((dash = strchr(mls_range_string, '-')) == NULL) {
+ // just a low level
+ apol_mls_level_t *l = apol_mls_level_create_from_literal(mls_range_string);
+ if (l == NULL) {
+ apol_mls_range_destroy(&r);
+ return NULL;
+ }
+ r->low = l;
+ } else {
+ // both a low and a high level
+ if (dash == mls_range_string) {
+ apol_mls_range_destroy(&r);
+ errno = EINVAL;
+ return NULL;
+ }
+ char *s = strndup(mls_range_string, dash - mls_range_string);
+ if (s == NULL) {
+ apol_mls_range_destroy(&r);
+ return NULL;
+ }
+ apol_mls_level_t *l = apol_mls_level_create_from_literal(s);
+ if (l == NULL) {
+ apol_mls_range_destroy(&r);
+ free(s);
+ return NULL;
+ }
+ r->low = l;
+ free(s);
+ l = NULL;
+
+ if ((l = apol_mls_level_create_from_literal(dash + 1)) == NULL) {
+ apol_mls_range_destroy(&r);
+ return NULL;
+ }
+ r->high = l;
+ }
+ return r;
+}
+
+apol_mls_range_t *apol_mls_range_create_from_qpol_mls_range(const apol_policy_t * p, const qpol_mls_range_t * qpol_range)
+{
+ apol_mls_range_t *apol_range = NULL;
+ const qpol_mls_level_t *tmp = NULL;
+ apol_mls_level_t *tmp_lvl = NULL;
+ int error = 0;
+
+ if (!p || !qpol_range) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ apol_range = calloc(1, sizeof(apol_mls_range_t));
+ if (!apol_range) {
+ ERR(p, "%s", strerror(ENOMEM));
+ return NULL;
+ }
+
+ /* low */
+ if (qpol_mls_range_get_low_level(p->p, qpol_range, &tmp) ||
+ !(tmp_lvl = apol_mls_level_create_from_qpol_mls_level(p, tmp)) || apol_mls_range_set_low(p, apol_range, tmp_lvl)) {
+ error = errno;
+ apol_mls_level_destroy(&tmp_lvl);
+ goto err;
+ }
+ tmp_lvl = NULL;
+
+ /* high */
+ if (qpol_mls_range_get_high_level(p->p, qpol_range, &tmp) ||
+ !(tmp_lvl = apol_mls_level_create_from_qpol_mls_level(p, tmp)) || apol_mls_range_set_high(p, apol_range, tmp_lvl)) {
+ error = errno;
+ apol_mls_level_destroy(&tmp_lvl);
+ goto err;
+ }
+
+ return apol_range;
+
+ err:
+ apol_mls_range_destroy(&apol_range);
+ errno = error;
+ return NULL;
+}
+
+void apol_mls_range_destroy(apol_mls_range_t ** range)
+{
+ if (!range || !(*range))
+ return;
+
+ if ((*range)->low != (*range)->high) {
+ apol_mls_level_destroy(&((*range)->high));
+ }
+ apol_mls_level_destroy(&((*range)->low));
+ free(*range);
+ *range = NULL;
+}
+
+int apol_mls_range_set_low(const apol_policy_t * p, apol_mls_range_t * range, apol_mls_level_t * level)
+{
+ if (!range) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (range->low != level) {
+ apol_mls_level_destroy(&(range->low));
+ range->low = level;
+ }
+ return 0;
+}
+
+int apol_mls_range_set_high(const apol_policy_t * p, apol_mls_range_t * range, apol_mls_level_t * level)
+{
+ if (!range) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (range->high != level) {
+ if (range->low != range->high) {
+ apol_mls_level_destroy(&(range->high));
+ }
+ range->high = level;
+ }
+ return 0;
+}
+
+const apol_mls_level_t *apol_mls_range_get_low(const apol_mls_range_t * range)
+{
+ if (!range) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range->low;
+}
+
+const apol_mls_level_t *apol_mls_range_get_high(const apol_mls_range_t * range)
+{
+ if (!range) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range->high;
+}
+
+int apol_mls_range_compare(const apol_policy_t * p, const apol_mls_range_t * target, const apol_mls_range_t * search,
+ unsigned int range_compare_type)
+{
+ int ans1 = -1, ans2 = -1;
+ if (search == NULL) {
+ return 1;
+ }
+ if (p == NULL || target == NULL || target->low == NULL || search->low == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ /* FIX ME: intersect does not work */
+ if ((range_compare_type & APOL_QUERY_SUB) || (range_compare_type & APOL_QUERY_INTERSECT)) {
+ ans1 = apol_mls_range_contain_subrange(p, target, search);
+ if (ans1 < 0) {
+ return -1;
+ }
+ }
+ if ((range_compare_type & APOL_QUERY_SUPER) || (range_compare_type & APOL_QUERY_INTERSECT)) {
+ ans2 = apol_mls_range_contain_subrange(p, search, target);
+ if (ans2 < 0) {
+ return -1;
+ }
+ }
+ /* EXACT has to come first because its bits are both SUB and SUPER */
+ if ((range_compare_type & APOL_QUERY_EXACT) == APOL_QUERY_EXACT) {
+ return (ans1 && ans2);
+ } else if (range_compare_type & APOL_QUERY_SUB) {
+ return ans1;
+ } else if (range_compare_type & APOL_QUERY_SUPER) {
+ return ans2;
+ } else if (range_compare_type & APOL_QUERY_INTERSECT) {
+ return (ans1 || ans2);
+ }
+ ERR(p, "%s", "Invalid range compare type argument.");
+ errno = EINVAL;
+ return -1;
+}
+
+static int apol_mls_range_does_include_level(const apol_policy_t * p, const apol_mls_range_t * range,
+ const apol_mls_level_t * level)
+{
+ int high_cmp = -1, low_cmp = -1;
+
+ if (range->low != range->high) {
+ low_cmp = apol_mls_level_compare(p, range->low, level);
+ if (low_cmp < 0) {
+ return -1;
+ }
+ }
+ const apol_mls_level_t *high_level = (range->high != NULL ? range->high : range->low);
+ high_cmp = apol_mls_level_compare(p, high_level, level);
+ if (high_cmp < 0) {
+ return -1;
+ }
+
+ if (high_cmp == APOL_MLS_EQ || high_cmp == APOL_MLS_DOM) {
+ if ((low_cmp == APOL_MLS_EQ || low_cmp == APOL_MLS_DOMBY) && range->low != high_level) {
+ return 1;
+ } else if (range->low == high_level) {
+ return apol_mls_sens_compare(p, apol_mls_level_get_sens(range->low), apol_mls_level_get_sens(level));
+ }
+ }
+
+ return 0;
+}
+
+int apol_mls_range_contain_subrange(const apol_policy_t * p, const apol_mls_range_t * range, const apol_mls_range_t * subrange)
+{
+ if (p == NULL || apol_mls_range_validate(p, subrange) != 1) {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ /* parent range validity will be checked via
+ * apol_mls_range_include_level() */
+
+ if (apol_mls_range_does_include_level(p, range, subrange->low)) {
+ if (subrange->high == NULL || apol_mls_range_does_include_level(p, range, subrange->high)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int apol_mls_range_validate(const apol_policy_t * p, const apol_mls_range_t * range)
+{
+ int retv;
+
+ if (p == NULL || range == NULL || range->low == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((retv = apol_mls_level_validate(p, range->low)) != 1) {
+ return retv;
+ }
+
+ if (range->high == NULL) {
+ return retv;
+ }
+ if (range->high != range->low && (retv = apol_mls_level_validate(p, range->high)) != 1) {
+ return retv;
+ }
+
+ /* both low and high levels exist, so now check that high
+ * dominates low */
+ retv = apol_mls_level_compare(p, range->low, range->high);
+ if (retv < 0) {
+ return -1;
+ } else if (retv != APOL_MLS_EQ && retv != APOL_MLS_DOMBY) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int mls_range_comp(const void *a, const void *b, void *data)
+{
+ const apol_mls_level_t *l1 = a;
+ const apol_mls_level_t *l2 = b;
+ qpol_policy_t *q = (qpol_policy_t *) data;
+ const qpol_level_t *l;
+ uint32_t low_value, high_value;
+ qpol_policy_get_level_by_name(q, apol_mls_level_get_sens(l1), &l);
+ qpol_level_get_value(q, l, &low_value);
+ qpol_policy_get_level_by_name(q, apol_mls_level_get_sens(l2), &l);
+ qpol_level_get_value(q, l, &high_value);
+ assert(low_value != 0 && high_value != 0);
+ return low_value - high_value;
+}
+
+static int mls_level_name_to_cat_comp(const void *a, const void *b, void *data)
+{
+ const qpol_cat_t *cat = a;
+ const char *name = (const char *)b;
+ qpol_policy_t *q = (qpol_policy_t *) data;
+ const char *cat_name = "";
+ qpol_cat_get_name(q, cat, &cat_name);
+ return strcmp(name, cat_name);
+}
+
+static void mls_level_free(void *elem)
+{
+ apol_mls_level_t *level = elem;
+ apol_mls_level_destroy(&level);
+}
+
+apol_vector_t *apol_mls_range_get_levels(const apol_policy_t * p, const apol_mls_range_t * range)
+{
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ apol_vector_t *v = NULL, *catv = NULL;
+ const qpol_level_t *l;
+ uint32_t low_value, high_value, value;
+ int error = 0;
+ qpol_iterator_t *iter = NULL, *catiter = NULL;
+
+ if (p == NULL || range == NULL || range->low == NULL) {
+ error = EINVAL;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ apol_mls_level_t *low_level, *high_level;
+ low_level = range->low;
+ if (range->high == NULL) {
+ high_level = low_level;
+ } else {
+ high_level = range->high;
+ }
+ if (qpol_policy_get_level_by_name(q, apol_mls_level_get_sens(low_level), &l) < 0 ||
+ qpol_level_get_value(q, l, &low_value) < 0) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_policy_get_level_by_name(q, apol_mls_level_get_sens(high_level), &l) < 0 ||
+ qpol_level_get_value(q, l, &high_value) < 0) {
+ error = errno;
+ goto err;
+ }
+ assert(low_value <= high_value);
+ if ((v = apol_vector_create(mls_level_free)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_policy_get_level_iter(q, &iter) < 0) {
+ error = errno;
+ goto err;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ const char *name;
+ apol_mls_level_t *ml;
+ if (qpol_iterator_get_item(iter, (void **)&l) < 0 ||
+ qpol_level_get_value(q, l, &value) < 0 || qpol_level_get_name(q, l, &name) < 0) {
+ error = errno;
+ goto err;
+ }
+ if (value < low_value || value > high_value) {
+ continue;
+ }
+ if ((ml = apol_mls_level_create()) == NULL || (apol_mls_level_set_sens(p, ml, name) < 0)) {
+ error = errno;
+ apol_mls_level_destroy(&ml);
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+
+ if (qpol_level_get_cat_iter(q, l, &catiter) < 0 || (catv = apol_vector_create_from_iter(catiter, NULL)) == NULL) {
+ error = errno;
+ goto err;
+ }
+
+ const apol_vector_t *high_cats = apol_mls_level_get_cats(high_level);
+ for (size_t i = 0; i < apol_vector_get_size(high_cats); i++) {
+ char *cat_name = apol_vector_get_element(high_cats, i);
+
+ size_t j;
+ /* do not add categories that are not members of
+ the level */
+ if (apol_vector_get_index(catv, cat_name, mls_level_name_to_cat_comp, q, &j) < 0) {
+ /* this category is not legal under the given policy */
+ continue;
+ }
+ if (apol_mls_level_append_cats(p, ml, cat_name) < 0) {
+ error = errno;
+ apol_mls_level_destroy(&ml);
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ }
+
+ qpol_iterator_destroy(&catiter);
+ apol_vector_destroy(&catv);
+
+ if (apol_vector_append(v, ml) < 0) {
+ error = errno;
+ apol_mls_level_destroy(&ml);
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ }
+ apol_vector_sort(v, mls_range_comp, q);
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&catiter);
+ apol_vector_destroy(&catv);
+ return v;
+ err:
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&catiter);
+ apol_vector_destroy(&v);
+ apol_vector_destroy(&catv);
+ errno = error;
+ return NULL;
+}
+
+char *apol_mls_range_render(const apol_policy_t * p, const apol_mls_range_t * range)
+{
+ char *rt = NULL, *retval = NULL;
+ char *sub_str = NULL;
+ int retv;
+ size_t sz = 0;
+
+ if (!range || range->low == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ goto cleanup;
+ }
+ if (p == NULL && apol_mls_range_is_literal(range) != 1) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ goto cleanup;
+ }
+
+ if ((sub_str = apol_mls_level_render(p, range->low)) == NULL) {
+ goto cleanup;
+ }
+ if (apol_str_append(&rt, &sz, sub_str)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ free(sub_str);
+ sub_str = NULL;
+ if (range->high == NULL) {
+ /* no high level set, so skip the rest of this render
+ * function */
+ retval = rt;
+ goto cleanup;
+ }
+ if (p == NULL) {
+ // no policy, so assume that high level dominates low level
+ retv = APOL_MLS_DOM;
+ } else {
+ retv = apol_mls_level_compare(p, range->low, range->high);
+ if (retv < 0) {
+ goto cleanup;
+ }
+ }
+ /* if (high level != low level) */
+ if ((retv == APOL_MLS_DOM || retv == APOL_MLS_DOMBY) && range->high != NULL) {
+ sub_str = apol_mls_level_render(p, range->high);
+ if (!sub_str)
+ goto cleanup;
+ if (apol_str_appendf(&rt, &sz, " - %s", sub_str)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ retval = rt;
+ cleanup:
+ if (retval != rt) {
+ free(rt);
+ }
+ free(sub_str);
+ return retval;
+}
+
+int apol_mls_range_convert(const apol_policy_t * p, apol_mls_range_t * range)
+{
+ if (p == NULL || range == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ apol_mls_level_t *low = range->low;
+ apol_mls_level_t *high = range->high;
+ int retval;
+ if (low != NULL) {
+ retval = apol_mls_level_convert(p, low);
+ if (retval < 0) {
+ return retval;
+ }
+ }
+ if (high != NULL && high != low) {
+ retval = apol_mls_level_convert(p, high);
+ if (retval < 0) {
+ return retval;
+ }
+ }
+ return 0;
+}
+
+int apol_mls_range_is_literal(const apol_mls_range_t * range)
+{
+ if (range == NULL) {
+ return -1;
+ }
+ int ret;
+ if ((ret = apol_mls_level_is_literal(range->low)) != 0) {
+ return ret;
+ }
+ if (range->high != NULL) {
+ ret = apol_mls_level_is_literal(range->high);
+ }
+ return ret;
+}
diff --git a/libapol/src/netcon-query.c b/libapol/src/netcon-query.c
new file mode 100644
index 0000000..7faf0de
--- /dev/null
+++ b/libapol/src/netcon-query.c
@@ -0,0 +1,592 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about portcons,
+ * netifcons, and nodecons within a policy. The caller obtains a
+ * query object, fills in its parameters, and then runs the query; it
+ * obtains a vector of results. Searches are conjunctive -- all
+ * fields of the search query must match for a datum to be added to
+ * the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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 <apol/render.h>
+
+#include <errno.h>
+#include <string.h>
+
+struct apol_portcon_query
+{
+ int proto;
+ int low, high;
+ apol_context_t *context;
+ unsigned int flags;
+};
+
+struct apol_netifcon_query
+{
+ char *dev;
+ apol_context_t *if_context, *msg_context;
+ unsigned int if_flags, msg_flags;
+};
+
+struct apol_nodecon_query
+{
+ char proto, addr_proto, mask_proto;
+ uint32_t addr[4], mask[4];
+ apol_context_t *context;
+ unsigned int flags;
+};
+
+/******************** portcon queries ********************/
+
+int apol_portcon_get_by_query(const apol_policy_t * p, const apol_portcon_query_t * po, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1, retval2;
+ *v = NULL;
+ if (qpol_policy_get_portcon_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_portcon_t *portcon;
+ if (qpol_iterator_get_item(iter, (void **)&portcon) < 0) {
+ goto cleanup;
+ }
+ if (po != NULL) {
+ uint16_t low, high;
+ uint8_t proto;
+ const qpol_context_t *context;
+ if (qpol_portcon_get_low_port(p->p,
+ portcon, &low) < 0 ||
+ qpol_portcon_get_high_port(p->p,
+ portcon, &high) < 0 ||
+ qpol_portcon_get_protocol(p->p,
+ portcon, &proto) < 0 || qpol_portcon_get_context(p->p, portcon, &context) < 0)
+ {
+ goto cleanup;
+ }
+ if ((po->low >= 0 && ((uint16_t) po->low) != low) ||
+ (po->high >= 0 && ((uint16_t) po->high) != high) || (po->proto >= 0 && ((uint8_t) po->proto) != proto))
+ {
+ continue;
+ }
+ retval2 = apol_compare_context(p, context, po->context, po->flags);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, portcon)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_portcon_query_t *apol_portcon_query_create(void)
+{
+ apol_portcon_query_t *po = calloc(1, sizeof(*po));
+ if (po == NULL) {
+ return NULL;
+ }
+ po->proto = po->low = po->high = -1;
+ return po;
+}
+
+void apol_portcon_query_destroy(apol_portcon_query_t ** po)
+{
+ if (*po != NULL) {
+ apol_context_destroy(&((*po)->context));
+ free(*po);
+ *po = NULL;
+ }
+}
+
+int apol_portcon_query_set_protocol(const apol_policy_t * p __attribute__ ((unused)), apol_portcon_query_t * po, int proto)
+{
+ po->proto = proto;
+ return 0;
+}
+
+/**
+ * @deprecated Use apol_portcon_query_set_protocol() instead.
+ */
+int apol_portcon_query_set_proto(apol_policy_t * p, apol_portcon_query_t * po, int proto)
+{
+ return apol_portcon_query_set_protocol(p, po, proto);
+}
+int apol_portcon_query_set_proto(apol_policy_t * p, apol_portcon_query_t * po, int proto) __attribute__ ((deprecated));
+
+int apol_portcon_query_set_low(const apol_policy_t * p __attribute__ ((unused)), apol_portcon_query_t * po, int low)
+{
+ po->low = low;
+ return 0;
+}
+
+int apol_portcon_query_set_high(const apol_policy_t * p __attribute__ ((unused)), apol_portcon_query_t * po, int high)
+{
+ po->high = high;
+ return 0;
+}
+
+int apol_portcon_query_set_context(const apol_policy_t * p __attribute__ ((unused)),
+ apol_portcon_query_t * po, apol_context_t * context, unsigned int range_match)
+{
+ if (po->context != NULL) {
+ apol_context_destroy(&po->context);
+ }
+ po->context = context;
+ po->flags = (po->flags & ~APOL_QUERY_FLAGS) | range_match;
+ return 0;
+}
+
+char *apol_portcon_render(const apol_policy_t * p, const qpol_portcon_t * portcon)
+{
+ char *line = NULL, *retval = NULL;
+ char *buff = NULL;
+ const char *proto_str = NULL;
+ char *context_str = NULL;
+ const qpol_context_t *ctxt = NULL;
+ uint16_t low_port, high_port;
+ uint8_t proto;
+
+ const size_t bufflen = 50; /* arbitrary size big enough to hold port no. */
+ if (!portcon || !p)
+ goto cleanup;
+
+ buff = (char *)calloc(bufflen + 1, sizeof(char));
+ if (!buff) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+
+ if (qpol_portcon_get_protocol(p->p, portcon, &proto))
+ goto cleanup;
+
+ if ((proto_str = apol_protocol_to_str(proto)) == NULL) {
+ ERR(p, "%s", "Could not get protocol string.");
+ goto cleanup;
+ }
+ if (qpol_portcon_get_low_port(p->p, portcon, &low_port))
+ goto cleanup;
+ if (qpol_portcon_get_high_port(p->p, portcon, &high_port))
+ goto cleanup;
+ if (low_port == high_port)
+ snprintf(buff, bufflen, "%d", low_port);
+ else
+ snprintf(buff, bufflen, "%d-%d", low_port, high_port);
+
+ if (qpol_portcon_get_context(p->p, portcon, &ctxt))
+ goto cleanup;
+ context_str = apol_qpol_context_render(p, ctxt);
+ if (!context_str)
+ goto cleanup;
+
+ line = (char *)calloc(4 + strlen("portcon") + strlen(proto_str) + strlen(buff) + strlen(context_str), sizeof(char));
+ if (!line) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+
+ sprintf(line, "portcon %s %s %s", proto_str, buff, context_str);
+
+ retval = line;
+ cleanup:
+ free(buff);
+ free(context_str);
+ if (retval != line) {
+ free(line);
+ }
+ return retval;
+}
+
+/******************** netifcon queries ********************/
+
+int apol_netifcon_get_by_query(const apol_policy_t * p, const apol_netifcon_query_t * n, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1, retval2;
+ *v = NULL;
+ if (qpol_policy_get_netifcon_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ const qpol_netifcon_t *netifcon;
+ if (qpol_iterator_get_item(iter, (void **)&netifcon) < 0) {
+ goto cleanup;
+ }
+ if (n != NULL) {
+ const char *name;
+ const qpol_context_t *ifcon, *msgcon;
+ if (qpol_netifcon_get_name(p->p, netifcon, &name) < 0 ||
+ qpol_netifcon_get_if_con(p->p, netifcon, &ifcon) < 0 ||
+ qpol_netifcon_get_msg_con(p->p, netifcon, &msgcon) < 0) {
+ goto cleanup;
+ }
+ retval2 = apol_compare(p, name, n->dev, 0, NULL);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ continue;
+ }
+ retval2 = apol_compare_context(p, ifcon, n->if_context, n->if_flags);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ continue;
+ }
+ retval2 = apol_compare_context(p, msgcon, n->msg_context, n->msg_flags);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, (void *)netifcon)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_netifcon_query_t *apol_netifcon_query_create(void)
+{
+ return calloc(1, sizeof(apol_netifcon_query_t));
+}
+
+void apol_netifcon_query_destroy(apol_netifcon_query_t ** n)
+{
+ if (*n != NULL) {
+ free((*n)->dev);
+ apol_context_destroy(&((*n)->if_context));
+ apol_context_destroy(&((*n)->msg_context));
+ free(*n);
+ *n = NULL;
+ }
+}
+
+int apol_netifcon_query_set_device(const apol_policy_t * p, apol_netifcon_query_t * n, const char *dev)
+{
+ return apol_query_set(p, &n->dev, NULL, dev);
+}
+
+int apol_netifcon_query_set_if_context(const apol_policy_t * p __attribute__ ((unused)),
+ apol_netifcon_query_t * n, apol_context_t * context, unsigned int range_match)
+{
+ if (n->if_context != NULL) {
+ apol_context_destroy(&n->if_context);
+ }
+ n->if_context = context;
+ n->if_flags = (n->if_flags & ~APOL_QUERY_FLAGS) | range_match;
+ return 0;
+}
+
+int apol_netifcon_query_set_msg_context(const apol_policy_t * p __attribute__ ((unused)),
+ apol_netifcon_query_t * n, apol_context_t * context, unsigned int range_match)
+{
+ if (n->msg_context != NULL) {
+ apol_context_destroy(&n->msg_context);
+ }
+ n->msg_context = context;
+ n->msg_flags = (n->msg_flags & ~APOL_QUERY_FLAGS) | range_match;
+ return 0;
+}
+
+char *apol_netifcon_render(const apol_policy_t * p, const qpol_netifcon_t * netifcon)
+{
+ char *line = NULL, *retval = NULL;
+ char *devcon_str = NULL;
+ char *pktcon_str = NULL;
+ const char *iface_str = NULL;
+ const qpol_context_t *ctxt = NULL;
+
+ if (!netifcon || !p)
+ goto cleanup;
+
+ if (qpol_netifcon_get_if_con(p->p, netifcon, &ctxt))
+ goto cleanup;
+ devcon_str = apol_qpol_context_render(p, ctxt);
+ if (!devcon_str)
+ goto cleanup;
+
+ if (qpol_netifcon_get_msg_con(p->p, netifcon, &ctxt))
+ goto cleanup;
+ pktcon_str = apol_qpol_context_render(p, ctxt);
+ if (!pktcon_str) {
+ goto cleanup;
+ }
+
+ if (qpol_netifcon_get_name(p->p, netifcon, &iface_str))
+ return NULL;
+ line = (char *)calloc(4 + strlen(iface_str) + strlen(devcon_str) + strlen(pktcon_str) + strlen("netifcon"), sizeof(char));
+ if (!line) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ sprintf(line, "netifcon %s %s %s", iface_str, devcon_str, pktcon_str);
+
+ retval = line;
+ cleanup:
+ free(devcon_str);
+ free(pktcon_str);
+ return retval;
+}
+
+/******************** nodecon queries ********************/
+
+int apol_nodecon_get_by_query(const apol_policy_t * p, const apol_nodecon_query_t * n, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1, retval2;
+ qpol_nodecon_t *nodecon = NULL;
+ *v = NULL;
+ if (qpol_policy_get_nodecon_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&nodecon) < 0) {
+ goto cleanup;
+ }
+ if (n != NULL) {
+ unsigned char proto, proto_a, proto_m;
+ uint32_t *addr, *mask;
+ const qpol_context_t *con;
+ if (qpol_nodecon_get_protocol(p->p, nodecon, &proto) < 0 ||
+ qpol_nodecon_get_addr(p->p, nodecon, &addr, &proto_a) < 0 ||
+ qpol_nodecon_get_mask(p->p, nodecon, &mask, &proto_m) < 0 ||
+ qpol_nodecon_get_context(p->p, nodecon, &con) < 0) {
+ goto cleanup;
+ }
+ if (n->proto >= 0 && n->proto != proto) {
+ free(nodecon);
+ continue;
+ }
+ if (n->addr_proto >= 0 &&
+ (n->addr_proto != proto_a ||
+ (proto_a == QPOL_IPV4 && memcmp(n->addr, addr, 1 * sizeof(uint32_t)) != 0) ||
+ (proto_a == QPOL_IPV6 && memcmp(n->addr, addr, 4 * sizeof(uint32_t)) != 0))) {
+ free(nodecon);
+ continue;
+ }
+ if (n->mask_proto >= 0 &&
+ (n->mask_proto != proto_m ||
+ (proto_m == QPOL_IPV4 && memcmp(n->mask, mask, 1 * sizeof(uint32_t)) != 0) ||
+ (proto_m == QPOL_IPV6 && memcmp(n->mask, mask, 4 * sizeof(uint32_t)) != 0))) {
+ free(nodecon);
+ continue;
+ }
+ retval2 = apol_compare_context(p, con, n->context, n->flags);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ free(nodecon);
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, nodecon)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ free(nodecon);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_nodecon_query_t *apol_nodecon_query_create(void)
+{
+ apol_nodecon_query_t *n = calloc(1, sizeof(apol_nodecon_query_t));
+ if (n != NULL) {
+ n->proto = n->addr_proto = n->mask_proto = -1;
+ }
+ return n;
+}
+
+void apol_nodecon_query_destroy(apol_nodecon_query_t ** n)
+{
+ if (*n != NULL) {
+ apol_context_destroy(&((*n)->context));
+ free(*n);
+ *n = NULL;
+ }
+}
+
+int apol_nodecon_query_set_protocol(const apol_policy_t * p, apol_nodecon_query_t * n, int proto)
+{
+ if (proto == QPOL_IPV4 || proto == QPOL_IPV6) {
+ n->proto = (char)proto;
+ } else if (proto < 0) {
+ n->proto = -1;
+ } else {
+ ERR(p, "Invalid protocol value %d.", proto);
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * @deprecated Use apol_nodecon_query_set_protocol() instead.
+ */
+int apol_nodecon_query_set_proto(apol_policy_t * p, apol_nodecon_query_t * n, int proto)
+{
+ return apol_nodecon_query_set_protocol(p, n, proto);
+}
+int apol_nodecon_query_set_proto(apol_policy_t * p, apol_nodecon_query_t * n, int proto) __attribute__ ((deprecated));
+
+int apol_nodecon_query_set_addr(const apol_policy_t * p, apol_nodecon_query_t * n, uint32_t * addr, int proto)
+{
+ if (addr == NULL) {
+ n->addr_proto = -1;
+ } else {
+ if (proto == QPOL_IPV4) {
+ memcpy(n->addr, addr, 1 * sizeof(uint32_t));
+ } else if (proto == QPOL_IPV6) {
+ memcpy(n->addr, addr, 4 * sizeof(uint32_t));
+ } else {
+ ERR(p, "Invalid protocol value %d.", proto);
+ return -1;
+ }
+ n->addr_proto = (char)proto;
+ }
+ return 0;
+}
+
+int apol_nodecon_query_set_mask(const apol_policy_t * p, apol_nodecon_query_t * n, uint32_t * mask, int proto)
+{
+ if (mask == NULL) {
+ n->mask_proto = -1;
+ } else {
+ if (proto == QPOL_IPV4) {
+ memcpy(n->mask, mask, 1 * sizeof(uint32_t));
+ } else if (proto == QPOL_IPV6) {
+ memcpy(n->mask, mask, 4 * sizeof(uint32_t));
+ } else {
+ ERR(p, "Invalid protocol value %d.", proto);
+ return -1;
+ }
+ n->mask_proto = (char)proto;
+ }
+ return 0;
+}
+
+int apol_nodecon_query_set_context(const apol_policy_t * p __attribute__ ((unused)),
+ apol_nodecon_query_t * n, apol_context_t * context, unsigned int range_match)
+{
+ if (n->context != NULL) {
+ apol_context_destroy(&n->context);
+ }
+ n->context = context;
+ n->flags = (n->flags & ~APOL_QUERY_FLAGS) | range_match;
+ return 0;
+}
+
+char *apol_nodecon_render(const apol_policy_t * p, const qpol_nodecon_t * nodecon)
+{
+ char *line = NULL, *retval = NULL;
+ char *context_str = NULL;
+ char *addr_str = NULL;
+ char *mask_str = NULL;
+ const qpol_context_t *ctxt = NULL;
+ unsigned char protocol, addr_proto, mask_proto;
+ uint32_t *addr = NULL, *mask = NULL;
+
+ if (!nodecon || !p)
+ goto cleanup;
+
+ if (qpol_nodecon_get_protocol(p->p, nodecon, &protocol))
+ goto cleanup;
+ if (qpol_nodecon_get_addr(p->p, nodecon, &addr, &addr_proto))
+ goto cleanup;
+ if (qpol_nodecon_get_mask(p->p, nodecon, &mask, &mask_proto))
+ goto cleanup;
+ switch (protocol) {
+ case QPOL_IPV4:
+ if ((addr_str = apol_ipv4_addr_render(p, addr)) == NULL || (mask_str = apol_ipv4_addr_render(p, mask)) == NULL) {
+ goto cleanup;
+ }
+ break;
+ case QPOL_IPV6:
+ if ((addr_str = apol_ipv6_addr_render(p, addr)) == NULL || (mask_str = apol_ipv6_addr_render(p, mask)) == NULL) {
+ goto cleanup;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (qpol_nodecon_get_context(p->p, nodecon, &ctxt))
+ goto cleanup;
+ context_str = apol_qpol_context_render(p, ctxt);
+ if (!context_str)
+ goto cleanup;
+
+ line = (char *)calloc(4 + strlen("nodecon") + strlen(addr_str) + strlen(mask_str) + strlen(context_str), sizeof(char));
+ if (!line) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+
+ sprintf(line, "nodecon %s %s %s", addr_str, mask_str, context_str);
+
+ retval = line;
+ cleanup:
+ free(addr_str);
+ free(mask_str);
+ free(context_str);
+ return retval;
+}
diff --git a/libapol/src/perm-map.c b/libapol/src/perm-map.c
new file mode 100644
index 0000000..01c4c32
--- /dev/null
+++ b/libapol/src/perm-map.c
@@ -0,0 +1,697 @@
+/**
+ * @file
+ *
+ * Implementation of permission mapping routines.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-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 <apol/perm-map.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+/* use 8k line size */
+#define APOL_LINE_SZ 8192
+
+struct apol_permmap
+{
+ unsigned char mapped; /* true if this class's permissions
+ * were mapped from a file, false if
+ * using default values */
+ apol_vector_t *classes; /* list of apol_permmap_class_t */
+};
+
+/* There is one apol_permmap_class per object class. */
+typedef struct apol_permmap_class
+{
+ unsigned char mapped; /* mask */
+ /** pointer to within a qpol_policy_t that represents this class */
+ const qpol_class_t *c;
+ /** vector of apol_permmap_perm, an element for each permission bit */
+ apol_vector_t *perms;
+} apol_permmap_class_t;
+
+/**
+ * Permission maps: For each object class we need to map all permisions
+ * to either read and/or write, or non similar as is done for the MLS stuff.
+ * This allows us to determine information flow. These mappings will be
+ * loadable so that users can re-map them as they see fit.
+ */
+typedef struct apol_permmap_perm
+{
+ /** name of permission */
+ char *name;
+ /** one of APOL_PERMMAP_READ, etc. */
+ unsigned char map;
+ /** the weight (importance) of this perm. (least) 1 - 10 (most) */
+ int weight;
+} apol_permmap_perm_t;
+
+/* some perms unmapped */
+#define APOL_PERMMAP_RET_UNMAPPED_PERM 0x01
+/* some objects unmapped */
+#define APOL_PERMMAP_RET_UNMAPPED_OBJ 0x02
+/* some perms from file unknown and ignored */
+#define APOL_PERMMAP_RET_UNKNOWN_PERM 0x04
+/* some object from file unknown and ignored */
+#define APOL_PERMMAP_RET_UNKNOWN_OBJ 0x08
+/* not enough classes/perms */
+#define APOL_PERMMAP_RET_NOT_ENOUGH 0x10
+
+/**
+ * Deallocate all space used by an apol_permmap_perm_t, including the
+ * pointer itself.
+ *
+ * @param elem Pointer to free. If NULL then do nothing.
+ */
+static void permmap_perm_free(void *elem)
+{
+ if (elem != NULL) {
+ apol_permmap_perm_t *p = (apol_permmap_perm_t *) elem;
+ free(p->name);
+ free(p);
+ }
+}
+
+/**
+ * Deallocate all space used by an apol_permmap_class_t, including the
+ * pointer itself.
+ *
+ * @param elem Pointer to free. If NULL then do nothing.
+ */
+static void permmap_class_free(void *elem)
+{
+ if (elem != NULL) {
+ apol_permmap_class_t *c = (apol_permmap_class_t *) elem;
+ apol_vector_destroy(&c->perms);
+ free(c);
+ }
+}
+
+/**
+ * Allocate and return a new apol_permmap_perm_t.
+ *
+ * @param name Name of the permission. This function will duplicate
+ * the string.
+ * @param map Direction of information flow. This must be one of
+ * APOL_PERMMAP_UNMAPPED, APOL_PERMMAP_READ, etc.
+ * @param weight Weight of the permission. This must be an integer
+ * from APOL_PERMMAP_MIN_WEIGHT to APOL_PERMMAP_MAX_WEIGHT, inclusive.
+ *
+ * @return A newly allocated apol_permmap_perm_t, or NULL on out of
+ * memory. The caller is responsible for deallocating this pointer
+ * via apol_permmap_perm_free().
+ */
+static apol_permmap_perm_t *apol_permmap_perm_create(const char *name, unsigned char map, int weight)
+{
+ apol_permmap_perm_t *pp;
+ if ((pp = calloc(1, sizeof(*pp))) == NULL) {
+ return NULL;
+ }
+ if ((pp->name = strdup(name)) == NULL) {
+ free(pp);
+ return NULL;
+ }
+ pp->map = map;
+ pp->weight = weight;
+ return pp;
+}
+
+/**
+ * Allocate and return a new permission map from a policy, and
+ * allocates space for defined object classes.
+ *
+ * @param p Policy from which to create permission map.
+ *
+ * @return A newly allocated map, or NULL on error. The caller is
+ * responsible for deallocating this pointer via permmap_destroy().
+ */
+static apol_permmap_t *apol_permmap_create_from_policy(const apol_policy_t * p)
+{
+ apol_permmap_t *t = NULL;
+ qpol_iterator_t *class_iter = NULL, *perm_iter = NULL, *common_iter = NULL;
+ size_t num_obj_classes;
+ int retval = -1;
+
+ if (p == NULL) {
+ goto cleanup;
+ }
+
+ if ((t = (apol_permmap_t *) calloc(1, sizeof(*t))) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (qpol_policy_get_class_iter(p->p, &class_iter) < 0 || qpol_iterator_get_size(class_iter, &num_obj_classes) < 0) {
+ goto cleanup;
+ }
+ t->mapped = 0;
+ if ((t->classes = apol_vector_create_with_capacity(num_obj_classes, permmap_class_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(class_iter); qpol_iterator_next(class_iter)) {
+ const qpol_class_t *c;
+ const qpol_common_t *common;
+ apol_permmap_class_t *pc = NULL;
+ apol_permmap_perm_t *pp = NULL;
+ size_t num_unique_perms, num_common_perms = 0;
+ char *name;
+ if (qpol_iterator_get_item(class_iter, (void **)&c) < 0 ||
+ qpol_class_get_perm_iter(p->p, c, &perm_iter) < 0 ||
+ qpol_iterator_get_size(perm_iter, &num_unique_perms) < 0 || qpol_class_get_common(p->p, c, &common) < 0) {
+ goto cleanup;
+ }
+ if (common != NULL &&
+ (qpol_common_get_perm_iter(p->p, common, &common_iter) < 0 ||
+ qpol_iterator_get_size(common_iter, &num_common_perms) < 0)) {
+ goto cleanup;
+ }
+ if ((pc = calloc(1, sizeof(*pc))) == NULL || apol_vector_append(t->classes, pc) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ permmap_class_free(pc);
+ goto cleanup;
+ }
+ pc->mapped = 0;
+ pc->c = c;
+ if ((pc->perms = apol_vector_create_with_capacity(num_unique_perms + num_common_perms, permmap_perm_free)) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ /* initialize with all the class's unique permissions
+ * from provided policy */
+ for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) {
+ if (qpol_iterator_get_item(perm_iter, (void **)&name) < 0) {
+ goto cleanup;
+ }
+ if ((pp = apol_permmap_perm_create(name, 0, (char)APOL_PERMMAP_MIN_WEIGHT)) == NULL ||
+ apol_vector_append(pc->perms, pp) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ permmap_perm_free(pp);
+ goto cleanup;
+ }
+ }
+ /* next initialize with common permissions */
+ for (; common_iter != NULL && !qpol_iterator_end(common_iter); qpol_iterator_next(common_iter)) {
+ if (qpol_iterator_get_item(common_iter, (void **)&name) < 0) {
+ goto cleanup;
+ }
+ if ((pp = apol_permmap_perm_create(name, 0, (char)APOL_PERMMAP_MIN_WEIGHT)) == NULL ||
+ apol_vector_append(pc->perms, pp) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ permmap_perm_free(pp);
+ goto cleanup;
+ }
+ }
+ qpol_iterator_destroy(&perm_iter);
+ qpol_iterator_destroy(&common_iter);
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&class_iter);
+ qpol_iterator_destroy(&perm_iter);
+ qpol_iterator_destroy(&common_iter);
+ if (retval < 0) {
+ permmap_destroy(&t);
+ }
+ return t;
+}
+
+void permmap_destroy(apol_permmap_t ** p)
+{
+ if (p == NULL || *p == NULL)
+ return;
+ apol_vector_destroy(&(*p)->classes);
+ free(*p);
+ *p = NULL;
+}
+
+/**
+ * Searches through the permission map within a policy, returning the
+ * record for a given object class.
+ *
+ * @param p Policy containing permission map.
+ * @param target Target class name.
+ *
+ * @return Pointer to the class within the permission map, or NULL if
+ * not found or on error.
+ */
+static apol_permmap_class_t *find_permmap_class(const apol_policy_t * p, const char *target)
+{
+ size_t i;
+ const qpol_class_t *target_class;
+ if (qpol_policy_get_class_by_name(p->p, target, &target_class) < 0) {
+ return NULL;
+ }
+ for (i = 0; i < apol_vector_get_size(p->pmap->classes); i++) {
+ apol_permmap_class_t *pc = apol_vector_get_element(p->pmap->classes, i);
+ if (pc->c == target_class) {
+ return pc;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Searches through the permission map's class, returning the record
+ * for a given permission.
+ *
+ * @param p Policy to use, for error handling.
+ * @param pc Permission map class to search.
+ * @param target Target class name.
+ *
+ * @return Pointer to the permission record within the class, or NULL
+ * if not found or on error.
+ */
+static apol_permmap_perm_t *find_permmap_perm(const apol_policy_t * p
+ __attribute__ ((unused)), const apol_permmap_class_t * pc, const char *target)
+{
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(pc->perms); i++) {
+ apol_permmap_perm_t *pp = apol_vector_get_element(pc->perms, i);
+ if (strcmp(pp->name, target) == 0) {
+ return pp;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Given a character representation, return its numerical permission
+ * map type.
+ *
+ * @param p Policy containing error handler.
+ * @param perm_name Name of the permission.
+ * @param mapid Character representing map type.
+ *
+ * @return One of APOL_PERMMAP_READ, etc, or APOL_PERMMAP_UNMAPPED on error.
+ */
+static char convert_map_char(const apol_policy_t * p, const char *perm_name, char mapid)
+{
+ switch (mapid) {
+ case 'r':
+ case 'R':
+ return APOL_PERMMAP_READ;
+ case 'w':
+ case 'W':
+ return APOL_PERMMAP_WRITE;
+ case 'b':
+ case 'B':
+ return APOL_PERMMAP_BOTH;
+ case 'n':
+ case 'N':
+ return APOL_PERMMAP_NONE;
+ default:
+ ERR(p, "Invalid map character '%c' for permission %s; permission will be unmapped.", mapid, perm_name);
+ return APOL_PERMMAP_UNMAPPED;
+ }
+}
+
+/**
+ * Goes through a policy's permission map to check that all classes
+ * had an entry within the recently read permission map file.
+ *
+ * @param p Policy containing permission map to check.
+ *
+ * @return 1 if all classes had entries, 0 if any did not.
+ */
+static int are_all_classes_mapped(const apol_policy_t * p)
+{
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(p->pmap->classes); i++) {
+ apol_permmap_class_t *pc = apol_vector_get_element(p->pmap->classes, i);
+ if (pc->mapped == 0) {
+ const char *class_name;
+ if (qpol_class_get_name(p->p, pc->c, &class_name) < 0) {
+ return 0;
+ }
+ WARN(p, "Some permissions were unmapped for class %s.", class_name);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/**
+ * Goes through a class's permissions to check that had an entry
+ * within the recently read permission map file.
+ *
+ * @param p Policy containing permission map to check.
+ * @param pc Class to check.
+ *
+ * @return 1 if all permissions had entries, 0 if any did not.
+ */
+static int are_all_perms_mapped(const apol_policy_t * p, const apol_permmap_class_t * pc)
+{
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(pc->perms); i++) {
+ apol_permmap_perm_t *pp = apol_vector_get_element(pc->perms, i);
+ if (pp->map == 0) {
+ const char *class_name;
+ if (qpol_class_get_name(p->p, pc->c, &class_name) < 0) {
+ return 0;
+ }
+ WARN(p, "Permission %s was unmapped for class %s.", pp->name, class_name);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/**
+ * Parse the individual permission definitions for a given class. If
+ * pc is not NULL then store them into a apol_permmap_class_t,
+ * otherwise discard the read values. If a permission was read, but
+ * does not have an entry within pc, then generate a warning and
+ * continue. Finally, check that all permissions for the given class
+ * have been mapped; if any have not then generate a warning.
+ *
+ * @param p Policy containing permission map.
+ * @param fp File pointer that contains permission map data.
+ * @param num_perms Number of permissions expected to be read.
+ * @param pc Destination to store data; if NULL then do not store data.
+ *
+ * @return 0 on success, > 0 on success but with warnings, < 0 on
+ * error.
+ */
+static int parse_permmap_class(apol_policy_t * p, FILE * fp, size_t num_perms, apol_permmap_class_t * pc)
+{
+ char line[APOL_LINE_SZ], perm_name[APOL_LINE_SZ], *line_ptr = NULL;
+ size_t perms_read = 0;
+ int retval = 0;
+
+ while (fgets(line, sizeof(line), fp) != NULL && perms_read < num_perms) {
+ char mapid;
+ int perm_weight, new_weight;
+ apol_permmap_perm_t *pp;
+
+ line_ptr = line;
+ apol_str_trim(line_ptr);
+ if (line_ptr[0] == '#' || apol_str_is_only_white_space(line_ptr))
+ continue;
+ perms_read++;
+ if (sscanf(line_ptr, "%s %c %d", perm_name, &mapid, &perm_weight) != 3) {
+ /* This may be a perm map file w/o perm weighting. */
+ if (sscanf(line_ptr, "%s %c", perm_name, &mapid) != 2) {
+ ERR(p, "Permission map has an invalid line: \"%s\"", line_ptr);
+ return -1;
+ }
+ perm_weight = APOL_PERMMAP_MAX_WEIGHT;
+ }
+ if (strcmp(perm_name, "class") == 0) {
+ ERR(p, "There were supposed to be %zu permissions, but only %zu were found.", num_perms, perms_read);
+ return -1;
+ }
+ new_weight = perm_weight;
+ if (perm_weight > APOL_PERMMAP_MAX_WEIGHT) {
+ new_weight = APOL_PERMMAP_MAX_WEIGHT;
+ } else if (perm_weight < APOL_PERMMAP_MIN_WEIGHT) {
+ new_weight = APOL_PERMMAP_MIN_WEIGHT;
+ }
+ if (new_weight != perm_weight) {
+ WARN(p, "Permission %s's weight %d is invalid. Setting it to %d instead.", perm_name, perm_weight,
+ new_weight);
+ perm_weight = new_weight;
+ }
+ if (pc != NULL) {
+ if ((pp = find_permmap_perm(p, pc, perm_name)) == NULL) {
+ WARN(p,
+ "Permission %s was defined in the permission map file but not within the policy. It will be ignored.",
+ perm_name);
+ retval |= APOL_PERMMAP_RET_UNKNOWN_PERM;
+ } else {
+ pp->weight = perm_weight;
+ pp->map = convert_map_char(p, perm_name, mapid);
+ }
+ }
+ }
+ if (perms_read != num_perms) {
+ WARN(p, "There were supposed to be %zu permissions, but only %zu were found.", num_perms, perms_read);
+ retval |= APOL_PERMMAP_RET_NOT_ENOUGH;
+ }
+ if (pc != NULL && !are_all_perms_mapped(p, pc)) {
+ retval |= APOL_PERMMAP_RET_UNMAPPED_PERM;
+ }
+ return retval;
+}
+
+/**
+ * Parse the permission map found within a file pointer, storing the
+ * information into the map within a policy. If there is a non-fatal
+ * error while loading (e.g., file declared an object class that does
+ * not exist within the policy) then generate a warning string and
+ * send it to the error handler stored within the policy.
+ *
+ * @param p Policy containing a newly allocated permission map.
+ * @param fp File pointer that contains permission map data.
+ *
+ * @return 0 on success, > 0 on success but with warnings, < 0 on
+ * error.
+ */
+static int parse_permmap(apol_policy_t * p, FILE * fp)
+{
+ char line[APOL_LINE_SZ], class_name[APOL_LINE_SZ], *line_ptr = NULL;
+ size_t num_classes = 0, num_perms = 0;
+ size_t i;
+ int retval = 0;
+
+ /* first read number of classes */
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ line_ptr = line;;
+ apol_str_trim(line_ptr);
+ if (line_ptr[0] != '#' && (sscanf(line_ptr, "%zu", &num_classes) == 1)) {
+ break;
+ }
+ }
+ if (num_classes == 0) {
+ ERR(p, "%s", "No object classes were defined in the permission map file.");
+ return -1;
+ }
+
+ /* next read each class */
+ for (i = 0; i < num_classes; i++) {
+ apol_permmap_class_t *pc;
+ int found_class_decl = 0, rt;
+ while (fgets(line, APOL_LINE_SZ, fp) != NULL) {
+ line_ptr = line;
+ apol_str_trim(line_ptr);
+ if (line_ptr[0] != '#' && (sscanf(line_ptr, "%*s %s %zu", class_name, &num_perms) == 2)) {
+ found_class_decl = 1;
+ break;
+ }
+ }
+ if (!found_class_decl) {
+ WARN(p, "Permission map file was supposed to have %zu classes, but only %zu were found.", num_classes, i);
+ return APOL_PERMMAP_RET_NOT_ENOUGH;
+ }
+ if ((pc = find_permmap_class(p, class_name)) == NULL) {
+ WARN(p,
+ "Object class %s was defined in the permission map file but not within the policy. It will be ignored.",
+ class_name);
+ /* skip to next record */
+ parse_permmap_class(p, fp, num_perms, NULL);
+ retval |= APOL_PERMMAP_RET_UNKNOWN_OBJ;
+ } else {
+ if ((rt = parse_permmap_class(p, fp, num_perms, pc)) < 0) {
+ return -1;
+ }
+ pc->mapped = 1;
+ retval |= rt;
+ }
+ }
+ return retval;
+}
+
+int apol_policy_open_permmap(apol_policy_t * p, const char *filename)
+{
+ FILE *outfile = NULL;
+ int retval = -1, rt = 0;
+
+ if (p == NULL || filename == NULL) {
+ goto cleanup;
+ }
+ permmap_destroy(&p->pmap);
+ if ((p->pmap = apol_permmap_create_from_policy(p)) == NULL) {
+ goto cleanup;
+ }
+
+ if ((outfile = fopen(filename, "r")) == NULL) {
+ ERR(p, "Could not open permission map %s for reading: %s", filename, strerror(errno));
+ goto cleanup;
+ }
+
+ if ((rt = parse_permmap(p, outfile)) < 0) {
+ goto cleanup;
+ }
+
+ /* check that all classes have been mapped */
+ if (rt == 0 && !are_all_classes_mapped(p)) {
+ rt = APOL_PERMMAP_RET_UNMAPPED_OBJ;
+ }
+ p->pmap->mapped = 1;
+
+ retval = rt;
+ cleanup:
+ if (outfile != NULL) {
+ fclose(outfile);
+ }
+ return retval;
+}
+
+int apol_permmap_load(apol_policy_t * p, const char *filename)
+{
+ return apol_policy_open_permmap(p, filename);
+}
+
+int apol_policy_save_permmap(const apol_policy_t * p, const char *filename)
+{
+ time_t ltime;
+ size_t i, j;
+ FILE *outfile = NULL;
+ int retval = -1;
+
+ if (p == NULL || p->pmap == NULL || filename == NULL)
+ goto cleanup;
+
+ if ((outfile = fopen(filename, "w")) == NULL) {
+ ERR(p, "Could not open permission map %s for writing: %s", filename, strerror(errno));
+ goto cleanup;
+ }
+
+ if (time(&ltime) == (time_t) - 1) {
+ ERR(p, "Could not get time: %s", strerror(errno));
+ goto cleanup;
+ }
+ if (fprintf(outfile, "# Auto-generated by apol on %s\n", ctime(&ltime)) < 0 ||
+ fprintf(outfile, "#\n# permission map file\n\n\n") < 0 ||
+ fprintf(outfile, "Number of classes (mapped?: %s):\n", (p->pmap->mapped ? "yes" : "no")) < 0 ||
+ fprintf(outfile, "%zu\n", apol_vector_get_size(p->pmap->classes)) < 0) {
+ ERR(p, "Write error: %s", strerror(errno));
+ goto cleanup;
+ }
+
+ for (i = 0; i < apol_vector_get_size(p->pmap->classes); i++) {
+ apol_permmap_class_t *pc = apol_vector_get_element(p->pmap->classes, i);
+ const char *class_name;
+ if (qpol_class_get_name(p->p, pc->c, &class_name) < 0) {
+ goto cleanup;
+ }
+ if (fprintf(outfile, "\nclass %s %zu\n", class_name, apol_vector_get_size(pc->perms)) < 0) {
+ ERR(p, "Write error: %s", strerror(errno));
+ goto cleanup;
+ }
+
+ for (j = 0; j < apol_vector_get_size(pc->perms); j++) {
+ apol_permmap_perm_t *pp = apol_vector_get_element(pc->perms, j);
+ char *s;
+ if (fprintf(outfile, "%s%18s ", pp->map & APOL_PERMMAP_UNMAPPED ? "#" : "", pp->name) < 0) {
+ ERR(p, "Write error: %s", strerror(errno));
+ goto cleanup;
+ }
+ switch (pp->map) {
+ case APOL_PERMMAP_READ:
+ s = "r";
+ break;
+ case APOL_PERMMAP_WRITE:
+ s = "w";
+ break;
+ case APOL_PERMMAP_BOTH:
+ s = "b";
+ break;
+ case APOL_PERMMAP_NONE:
+ s = "n";
+ break;
+ case APOL_PERMMAP_UNMAPPED:
+ s = "u";
+ break;
+ default:
+ s = "?";
+ }
+ if (fprintf(outfile, "%s %10d\n", s, pp->weight) < 0) {
+ ERR(p, "Write error: %s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (outfile != NULL) {
+ fclose(outfile);
+ }
+ return retval;
+}
+
+int apol_permmap_save(apol_policy_t * p, const char *filename)
+{
+ return apol_policy_save_permmap(p, filename);
+}
+
+int apol_policy_get_permmap(const apol_policy_t * p, const char *class_name, const char *perm_name, int *map, int *weight)
+{
+ apol_permmap_class_t *pc;
+ apol_permmap_perm_t *pp;
+ if (p == NULL || p->pmap == NULL) {
+ return -1;
+ }
+ if ((pc = find_permmap_class(p, class_name)) == NULL || (pp = find_permmap_perm(p, pc, perm_name)) == NULL) {
+ ERR(p, "Could not find permission %s in class %s.", perm_name, class_name);
+ return -1;
+ }
+ *map = pp->map;
+ *weight = pp->weight;
+ return 0;
+}
+
+int apol_permmap_get(apol_policy_t * p, const char *class_name, const char *perm_name, int *map, int *weight)
+{
+ return apol_policy_get_permmap(p, class_name, perm_name, map, weight);
+}
+
+int apol_policy_set_permmap(apol_policy_t * p, const char *class_name, const char *perm_name, int map, int weight)
+{
+ apol_permmap_class_t *pc;
+ apol_permmap_perm_t *pp;
+ if (p == NULL || p->pmap == NULL) {
+ return -1;
+ }
+ if ((pc = find_permmap_class(p, class_name)) == NULL || (pp = find_permmap_perm(p, pc, perm_name)) == NULL) {
+ ERR(p, "Could not find permission %s in class %s.", perm_name, class_name);
+ return -1;
+ }
+ pp->map = map;
+ if (weight > APOL_PERMMAP_MAX_WEIGHT) {
+ weight = APOL_PERMMAP_MAX_WEIGHT;
+ } else if (weight < APOL_PERMMAP_MIN_WEIGHT) {
+ weight = APOL_PERMMAP_MIN_WEIGHT;
+ }
+ pp->weight = weight;
+ return 0;
+}
+
+int apol_permmap_set(apol_policy_t * p, const char *class_name, const char *perm_name, int map, int weight)
+{
+ return apol_policy_set_permmap(p, class_name, perm_name, map, weight);
+}
diff --git a/libapol/src/permissive-query.c b/libapol/src/permissive-query.c
new file mode 100644
index 0000000..1279ee7
--- /dev/null
+++ b/libapol/src/permissive-query.c
@@ -0,0 +1,107 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about permissive types
+ * within a policy. The caller obtains a query object,
+ * fills in its parameters, and then runs the query; it obtains a
+ * vector of results. Searches are conjunctive -- all fields of the
+ * search query must match for a datum to be added to the results
+ * query.
+ *
+ * @author Steve Lawrence slawrence@tresys.com
+ *
+ * Copyright (C) 2006-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>
+
+struct apol_permissive_query
+{
+ char *permissive_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+int apol_permissive_get_by_query(const apol_policy_t * p, apol_permissive_query_t * q, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1;
+ *v = NULL;
+ if (qpol_policy_get_permissive_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ const qpol_permissive_t *permissive;
+ if (qpol_iterator_get_item(iter, (void **)&permissive) < 0) {
+ goto cleanup;
+ }
+ if (q != NULL) {
+ int compval = apol_compare_permissive(p,
+ permissive, q->permissive_name,
+ q->flags, &(q->regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, (void *)permissive)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_permissive_query_t *apol_permissive_query_create(void)
+{
+ return calloc(1, sizeof(apol_permissive_query_t));
+}
+
+void apol_permissive_query_destroy(apol_permissive_query_t ** q)
+{
+ if (*q != NULL) {
+ free((*q)->permissive_name);
+ apol_regex_destroy(&(*q)->regex);
+ free(*q);
+ *q = NULL;
+ }
+}
+
+int apol_permissive_query_set_name(const apol_policy_t * p, apol_permissive_query_t * q, const char *name)
+{
+ return apol_query_set(p, &q->permissive_name, &q->regex, name);
+}
+
+int apol_permissive_query_set_regex(const apol_policy_t * p, apol_permissive_query_t * q, int is_regex)
+{
+ return apol_query_set_regex(p, &q->flags, is_regex);
+}
+
diff --git a/libapol/src/polcap-query.c b/libapol/src/polcap-query.c
new file mode 100644
index 0000000..3491485
--- /dev/null
+++ b/libapol/src/polcap-query.c
@@ -0,0 +1,107 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about policy capabilities
+ * within a policy. The caller obtains a query object,
+ * fills in its parameters, and then runs the query; it obtains a
+ * vector of results. Searches are conjunctive -- all fields of the
+ * search query must match for a datum to be added to the results
+ * query.
+ *
+ * @author Steve Lawrence slawrence@tresys.com
+ *
+ * Copyright (C) 2006-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>
+
+struct apol_polcap_query
+{
+ char *polcap_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+int apol_polcap_get_by_query(const apol_policy_t * p, apol_polcap_query_t * q, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1;
+ *v = NULL;
+ if (qpol_policy_get_polcap_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ const qpol_polcap_t *polcap;
+ if (qpol_iterator_get_item(iter, (void **)&polcap) < 0) {
+ goto cleanup;
+ }
+ if (q != NULL) {
+ int compval = apol_compare_polcap(p,
+ polcap, q->polcap_name,
+ q->flags, &(q->regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, (void *)polcap)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_polcap_query_t *apol_polcap_query_create(void)
+{
+ return calloc(1, sizeof(apol_polcap_query_t));
+}
+
+void apol_polcap_query_destroy(apol_polcap_query_t ** q)
+{
+ if (*q != NULL) {
+ free((*q)->polcap_name);
+ apol_regex_destroy(&(*q)->regex);
+ free(*q);
+ *q = NULL;
+ }
+}
+
+int apol_polcap_query_set_name(const apol_policy_t * p, apol_polcap_query_t * q, const char *name)
+{
+ return apol_query_set(p, &q->polcap_name, &q->regex, name);
+}
+
+int apol_polcap_query_set_regex(const apol_policy_t * p, apol_polcap_query_t * q, int is_regex)
+{
+ return apol_query_set_regex(p, &q->flags, is_regex);
+}
+
diff --git a/libapol/src/policy-path.c b/libapol/src/policy-path.c
new file mode 100644
index 0000000..3633ebd
--- /dev/null
+++ b/libapol/src/policy-path.c
@@ -0,0 +1,409 @@
+/**
+ * @file
+ *
+ * Implementation of policy path object.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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 <config.h>
+
+#include <apol/policy-path.h>
+#include <apol/util.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+static const char *POLICY_PATH_MAGIC = "policy_list";
+static const int POLICY_PATH_MAX_VERSION = 1;
+
+struct apol_policy_path
+{
+ apol_policy_path_type_e path_type;
+ char *base;
+ apol_vector_t *modules;
+};
+
+apol_policy_path_t *apol_policy_path_create(apol_policy_path_type_e path_type, const char *path, const apol_vector_t * modules)
+{
+ apol_policy_path_t *p = NULL;
+
+ if (path == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((p = calloc(1, sizeof(*p))) == NULL) {
+ return NULL;
+ }
+ p->path_type = path_type;
+ if ((p->base = strdup(path)) == NULL) {
+ apol_policy_path_destroy(&p);
+ return NULL;
+ }
+ if (p->path_type == APOL_POLICY_PATH_TYPE_MODULAR) {
+ if (modules == NULL) {
+ p->modules = apol_vector_create(free);
+ } else {
+ p->modules = apol_vector_create_from_vector(modules, apol_str_strdup, NULL, free);
+ }
+ if (p->modules == NULL) {
+ apol_policy_path_destroy(&p);
+ return NULL;
+ }
+ apol_vector_sort_uniquify(p->modules, apol_str_strcmp, NULL);
+ }
+ return p;
+}
+
+apol_policy_path_t *apol_policy_path_create_from_policy_path(const apol_policy_path_t * path)
+{
+ apol_policy_path_t *p;
+ if (path == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ p = apol_policy_path_create(path->path_type, path->base, path->modules);
+ return p;
+}
+
+apol_policy_path_t *apol_policy_path_create_from_file(const char *filename)
+{
+ FILE *f = NULL;
+ apol_policy_path_t *path = NULL;
+ apol_policy_path_type_e path_type;
+ char *line = NULL, *s;
+ apol_vector_t *header_tokens = NULL;
+ size_t len;
+ int read_base = 0, retval = -1, error = 0;
+
+ if (filename == NULL) {
+ error = EINVAL;
+ goto cleanup;
+ }
+ if ((f = fopen(filename, "r")) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+
+ if (getline(&line, &len, f) < 0) {
+ error = EIO;
+ goto cleanup;
+ }
+ apol_str_trim(line);
+ if (strncmp(line, POLICY_PATH_MAGIC, strlen(POLICY_PATH_MAGIC)) != 0) {
+ error = EIO;
+ goto cleanup;
+ }
+
+ apol_str_trim(line);
+ if ((header_tokens = apol_str_split(line, " ")) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_get_size(header_tokens) < 3) {
+ error = EIO;
+ goto cleanup;
+ }
+ s = apol_vector_get_element(header_tokens, 1);
+ if (atoi(s) == 0 || atoi(s) > POLICY_PATH_MAX_VERSION) {
+ error = ENOTSUP;
+ goto cleanup;
+ }
+ s = apol_vector_get_element(header_tokens, 2);
+ if (strcmp(s, "monolithic") == 0) {
+ path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC;
+ } else if (strcmp(s, "modular") == 0) {
+ path_type = APOL_POLICY_PATH_TYPE_MODULAR;
+ } else {
+ error = EIO;
+ goto cleanup;
+ }
+
+ while (getline(&line, &len, f) >= 0) {
+ apol_str_trim(line);
+ if (line[0] == '#') {
+ continue;
+ }
+ if (!read_base) {
+ /* trying to parse a base policy / monolithic policy line */
+ if ((path = apol_policy_path_create(path_type, line, NULL)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ read_base = 1;
+ } else {
+ /* trying to parse a module line */
+ if (path_type == APOL_POLICY_PATH_TYPE_MONOLITHIC) {
+ error = EIO;
+ goto cleanup;
+ } else {
+ if ((s = strdup(line)) == NULL || apol_vector_append(path->modules, s) < 0) {
+ error = errno;
+ free(s);
+ goto cleanup;
+ }
+ }
+ }
+ }
+ if (read_base == 0) {
+ error = EIO;
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ if (f != NULL) {
+ fclose(f);
+ }
+ free(line);
+ apol_vector_destroy(&header_tokens);
+ if (retval != 0) {
+ apol_policy_path_destroy(&path);
+ errno = error;
+ }
+ return path;
+}
+
+apol_policy_path_t *apol_policy_path_create_from_string(const char *path_string)
+{
+ apol_policy_path_t *p = NULL;
+ apol_vector_t *tokens = NULL;
+ apol_policy_path_type_e path_type;
+ char *s;
+ size_t i;
+ if (path_string == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((tokens = apol_str_split(path_string, ":")) == NULL) {
+ return NULL;
+ }
+
+ /* first token identifies the path type */
+ if (apol_vector_get_size(tokens) < 2) {
+ apol_vector_destroy(&tokens);
+ return NULL;
+ }
+ s = apol_vector_get_element(tokens, 0);
+ if (strcmp(s, "monolithic") == 0) {
+ path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC;
+ } else if (strcmp(s, "modular") == 0) {
+ path_type = APOL_POLICY_PATH_TYPE_MODULAR;
+ } else {
+ apol_vector_destroy(&tokens);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* second token identifies gives base path */
+ s = apol_vector_get_element(tokens, 1);
+ if ((p = apol_policy_path_create(path_type, s, NULL)) == NULL) {
+ apol_vector_destroy(&tokens);
+ return NULL;
+ }
+
+ if (path_type == APOL_POLICY_PATH_TYPE_MODULAR) {
+ /* remainder are module paths */
+ for (i = 2; i < apol_vector_get_size(tokens); i++) {
+ s = apol_vector_get_element(tokens, i);
+ if ((s = strdup(s)) == NULL || apol_vector_append(p->modules, s) < 0) {
+ free(s);
+ apol_vector_destroy(&tokens);
+ apol_policy_path_destroy(&p);
+ return NULL;
+ }
+ }
+ apol_vector_sort_uniquify(p->modules, apol_str_strcmp, NULL);
+ }
+ return p;
+}
+
+void apol_policy_path_destroy(apol_policy_path_t ** path)
+{
+ if (path != NULL && *path != NULL) {
+ free((*path)->base);
+ apol_vector_destroy(&(*path)->modules);
+ free(*path);
+ *path = NULL;
+ }
+}
+
+int apol_policy_path_compare(const apol_policy_path_t * a, const apol_policy_path_t * b)
+{
+ int cmp;
+ if (a == NULL || b == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ if ((cmp = a->path_type - b->path_type) != 0) {
+ return cmp;
+ }
+ if ((cmp = strcmp(a->base, b->base)) != 0) {
+ return cmp;
+ }
+ if (a->path_type == APOL_POLICY_PATH_TYPE_MODULAR) {
+ /* only compare module vector if that field is relevant */
+ size_t i;
+ cmp = apol_vector_compare(a->modules, b->modules, apol_str_strcmp, NULL, &i);
+ if (cmp != 0) {
+ return cmp;
+ }
+ }
+ return 0;
+}
+
+apol_policy_path_type_e apol_policy_path_get_type(const apol_policy_path_t * path)
+{
+ if (path == NULL) {
+ errno = EINVAL;
+ return APOL_POLICY_PATH_TYPE_MONOLITHIC;
+ }
+ return path->path_type;
+}
+
+const char *apol_policy_path_get_primary(const apol_policy_path_t * path)
+{
+ if (path == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return path->base;
+}
+
+const apol_vector_t *apol_policy_path_get_modules(const apol_policy_path_t * path)
+{
+ if (path == NULL || path->path_type != APOL_POLICY_PATH_TYPE_MODULAR) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return path->modules;
+}
+
+int apol_policy_path_to_file(const apol_policy_path_t * path, const char *filename)
+{
+ FILE *f = NULL;
+ char *path_type;
+ size_t i;
+ int retval = -1, error = 0;
+ if (path == NULL || filename == NULL) {
+ errno = EINVAL;
+ goto cleanup;
+ }
+ if ((f = fopen(filename, "w")) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (path->path_type == APOL_POLICY_PATH_TYPE_MODULAR) {
+ path_type = "modular";
+ } else {
+ path_type = "monolithic";
+ }
+ if (fprintf(f, "%s %d %s\n", POLICY_PATH_MAGIC, POLICY_PATH_MAX_VERSION, path_type) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (fprintf(f, "%s\n", path->base) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (path->path_type == APOL_POLICY_PATH_TYPE_MODULAR) {
+ for (i = 0; i < apol_vector_get_size(path->modules); i++) {
+ char *m = apol_vector_get_element(path->modules, i);
+ if (fprintf(f, "%s\n", m) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (f != NULL) {
+ fclose(f);
+ }
+ if (retval != 0) {
+ error = errno;
+ }
+ return retval;
+}
+
+char *apol_policy_path_to_string(const apol_policy_path_t * path)
+{
+ char *path_type;
+ char *s = NULL;
+ size_t len = 0, i;
+ if (path == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (path->path_type == APOL_POLICY_PATH_TYPE_MODULAR) {
+ path_type = "modular";
+ } else {
+ path_type = "monolithic";
+ }
+ if (apol_str_appendf(&s, &len, "%s:%s", path_type, path->base) < 0) {
+ return NULL;
+ }
+ if (path->path_type == APOL_POLICY_PATH_TYPE_MODULAR) {
+ for (i = 0; i < apol_vector_get_size(path->modules); i++) {
+ char *m = apol_vector_get_element(path->modules, i);
+ if (apol_str_appendf(&s, &len, ":%s", m) < 0) {
+ return NULL;
+ }
+ }
+ }
+ return s;
+}
+
+int apol_file_is_policy_path_list(const char *filename)
+{
+ FILE *f = NULL;
+ char *line = NULL;
+ size_t len = 0;
+ int retval = -1, error = 0;
+
+ if (filename == NULL) {
+ error = EINVAL;
+ goto cleanup;
+ }
+ if ((f = fopen(filename, "r")) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+
+ if (getline(&line, &len, f) < 0) {
+ error = EIO;
+ goto cleanup;
+ }
+ apol_str_trim(line);
+ if (strncmp(line, POLICY_PATH_MAGIC, strlen(POLICY_PATH_MAGIC)) != 0) {
+ retval = 0;
+ goto cleanup;
+ }
+ retval = 1;
+
+ cleanup:
+ if (f)
+ fclose(f);
+ free(line);
+ if (retval < 0)
+ errno = error;
+ return retval;
+}
diff --git a/libapol/src/policy-query-internal.h b/libapol/src/policy-query-internal.h
new file mode 100644
index 0000000..657c815
--- /dev/null
+++ b/libapol/src/policy-query-internal.h
@@ -0,0 +1,511 @@
+/**
+ * @file
+ *
+ * Header for routines shared among libapol's queries and analyses.
+ * These routines are declared hidden within the library by way of the
+ * linking map.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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
+ */
+
+#ifndef APOL_POLICY_QUERY_INTERNAL_H
+#define APOL_POLICY_QUERY_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <config.h>
+
+#include <apol/policy.h>
+#include <apol/policy-query.h>
+#include <apol/util.h>
+#include <apol/vector.h>
+
+#include <regex.h>
+#include <stdlib.h>
+#include <qpol/policy.h>
+
+/* forward declaration. the definition resides within perm-map.c */
+ struct apol_permmap;
+
+/* forward declaration. the definition resides within domain-trans-analysis.c */
+ typedef struct apol_domain_trans_table apol_domain_trans_table_t;
+
+/* declared in perm-map.c */
+ typedef struct apol_permmap apol_permmap_t;
+
+ struct apol_policy
+ {
+ qpol_policy_t *p;
+ apol_callback_fn_t msg_callback;
+ void *msg_callback_arg;
+ int policy_type;
+ /** permission mapping for this policy; mappings loaded as needed */
+ struct apol_permmap *pmap;
+ /** for domain trans analysis; table built as needed */
+ struct apol_domain_trans_table *domain_trans_table;
+ };
+
+/** Every query allows the treatment of strings as regular expressions
+ * instead. Within the query structure are flags; if the first bit
+ * is set then use regex matching instead. */
+#define APOL_QUERY_REGEX 0x01
+
+#define APOL_QUERY_ONLY_ENABLED 0x10
+#define APOL_QUERY_SOURCE_AS_ANY 0x20
+#define APOL_QUERY_SOURCE_INDIRECT 0x40
+#define APOL_QUERY_TARGET_INDIRECT 0x80
+
+#define APOL_QUERY_SYMBOL_IS_BOTH (APOL_QUERY_SYMBOL_IS_TYPE|APOL_QUERY_SYMBOL_IS_ATTRIBUTE)
+#define APOL_QUERY_SOURCE_TYPE 0x100
+#define APOL_QUERY_SOURCE_ATTRIBUTE 0x200
+#define APOL_QUERY_TARGET_TYPE 0x400
+#define APOL_QUERY_TARGET_ATTRIBUTE 0x800
+
+#define APOL_QUERY_MATCH_ALL_PERMS 0x1000
+
+/**
+ * Destroy a compiled regular expression, setting it to NULL
+ * afterwards. Does nothing if the reference is NULL.
+ * @param regex Regular expression to destroy.
+ */
+ void apol_regex_destroy(regex_t ** regex);
+
+/**
+ * Sets a string field within a query, clearing its old contents and
+ * cached regex first. The search name will be duplicated.
+ *
+ * @param p Policy handler.
+ * @param search_name Reference to where to store duplicated name.
+ * @param regex Reference to cached regex; this will be cleared by the
+ * function.
+ * @param name New name to set, or NULL to just clear the field.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ int apol_query_set(const apol_policy_t * p, char **query_name, regex_t ** regex, const char *name);
+
+/**
+ * Sets an arbitrary flag for a query structure.
+ *
+ * @param p Policy handler.
+ * @param flags Reference to a flag bitmap.
+ * @param is_flag If non-zero, set flag. Otherwise unset it.
+ * @param flag_value Flag value to set.
+ *
+ * @return Always returns 0.
+ */
+ int apol_query_set_flag(const apol_policy_t * p, unsigned int *flags, const int is_flag, int flag_value);
+
+/**
+ * Sets the regular expression flag for a query structure.
+ *
+ * @param p Policy handler.
+ * @param flags Reference to the regular expression flag.
+ * @param is_regex If non-zero, set regex flag. Otherwise unset it.
+ *
+ * @return Always returns 0.
+ */
+ int apol_query_set_regex(const apol_policy_t * p, unsigned int *flags, const int is_regex);
+
+/**
+ * Determines if a name matches a target symbol name. If flags has
+ * the APOL_QUERY_REGEX bit set, then (1) compile the regular
+ * expression if NULL, and (2) apply it to target. Otherwise do a
+ * string comparison between name and target. If name is NULL and/or
+ * empty then the comparison always succeeds regardless of flags and
+ * regex.
+ *
+ * @param p Policy handler.
+ * @param target Name of target symbol to compare.
+ * @param name Source target from which to compare.
+ * @param flags If APOL_QUERY_REGEX bit is set, treat name as a
+ * regular expression.
+ * @param regex If using regexp comparison, the compiled regular
+ * expression to use; the pointer will be allocated space if regexp is
+ * legal. If NULL, then compile the regexp pattern given by name and
+ * cache it here.
+ *
+ * @return 1 If comparison succeeds, 0 if not; < 0 on error.
+ */
+ int apol_compare(const apol_policy_t * p, const char *target, const char *name, unsigned int flags, regex_t ** regex);
+
+/**
+ * Given an iterator of strings, checks if name matches any element
+ * within it. If there is a match, either literally or by regular
+ * expression, then return 1. If there are no matches then return 0.
+ *
+ * @param p Policy handler.
+ * @param iter Iterator of strings to match.
+ * @param name Source target from which to compare.
+ * @param flags If APOL_QUERY_REGEX bit is set, treat name as a
+ * regular expression.
+ * @param regex If using regexp comparison, the compiled regular
+ * expression to use; the pointer will be allocated space if regexp is
+ * legal. If NULL, then compile the regexp pattern given by name and
+ * cache it here.
+ * @param do_free If non-zero free the strings returned by the iterator.
+ *
+ * @return 1 If comparison succeeds, 0 if not; < 0 on error.
+ */
+ int apol_compare_iter(const apol_policy_t * p, qpol_iterator_t * iter, const char *name,
+ unsigned int flags, regex_t ** regex, int do_free);
+
+/**
+ * Determines if a (partial) type query matches a qpol_type_t,
+ * either the type name or any of its aliases.
+ *
+ * @param p Policy within which to look up types.
+ * @param type Type datum to compare against.
+ * @param name Source target from which to compare.
+ * @param flags If APOL_QUERY_REGEX bit is set, treat name as a
+ * regular expression.
+ * @param regex If using regexp comparison, the compiled regular
+ * expression to use; the pointer will be allocated space if regexp is
+ * legal. If NULL, then compile the regexp pattern given by name and
+ * cache it here.
+ *
+ * @return 1 If comparison succeeds, 0 if not; < 0 on error.
+ */
+ int apol_compare_type(const apol_policy_t * p, const qpol_type_t * type, const char *name, unsigned int flags,
+ regex_t ** type_regex);
+
+/**
+ * Determines if a (partial) permissive query matches a qpol_permissive_t,
+ * by name.
+ *
+ * @param p Policy within which to look up types.
+ * @param type Permissive datum to compare against.
+ * @param name Source target from which to compare.
+ * @param flags If APOL_QUERY_REGEX bit is set, treat name as a
+ * regular expression.
+ * @param regex If using regexp comparison, the compiled regular
+ * expression to use; the pointer will be allocated space if regexp is
+ * legal. If NULL, then compile the regexp pattern given by name and
+ * cache it here.
+ *
+ * @return 1 If comparison succeeds, 0 if not; < 0 on error.
+ */
+ int apol_compare_permissive(const apol_policy_t * p, const qpol_permissive_t * permissive, const char *name, unsigned int flags,
+ regex_t ** type_regex);
+
+/**
+ * Determines if a (partial) polcap query matches a qpol_polcap_t,
+ * by name.
+ *
+ * @param p Policy within which to look up types.
+ * @param type Polcap datum to compare against.
+ * @param name Source target from which to compare.
+ * @param flags If APOL_QUERY_REGEX bit is set, treat name as a
+ * regular expression.
+ * @param regex If using regexp comparison, the compiled regular
+ * expression to use; the pointer will be allocated space if regexp is
+ * legal. If NULL, then compile the regexp pattern given by name and
+ * cache it here.
+ *
+ * @return 1 If comparison succeeds, 0 if not; < 0 on error.
+ */
+ int apol_compare_polcap(const apol_policy_t * p, const qpol_polcap_t * polcap, const char *name, unsigned int flags,
+ regex_t ** type_regex);
+
+/**
+ * Determines if a boolean is used within a particual conditional.
+ *
+ * @param p Policy within which to look up types.
+ * @param cond Conditional to compare against.
+ * @param name Source boolean name from which to compare.
+ * @param flags If APOL_QUERY_REGEX bit is set, treat name as a
+ * regular expression.
+ * @param regex If using regexp comparison, the compiled regular
+ * expression to use; the pointer will be allocated space if regexp is
+ * legal. If NULL, then compile the regexp pattern given by name and
+ * cache it here.
+ *
+ * @return 1 If comparison succeeds, 0 if not; < 0 on error.
+ */
+ int apol_compare_cond_expr(const apol_policy_t * p, const qpol_cond_t * cond, const char *name, unsigned int flags,
+ regex_t ** bool_regex);
+
+/**
+ * Determines if a level query matches a qpol_level_t, either
+ * the sensitivity name or any of its aliases.
+ *
+ * @param p Policy within which to look up types.
+ * @param level level datum to compare against.
+ * @param name Source target from which to compare.
+ * @param flags If APOL_QUERY_REGEX bit is set, treat name as a
+ * regular expression.
+ * @param regex If using regexp comparison, the compiled regular
+ * expression to use; the pointer will be allocated space if regexp is
+ * legal. If NULL, then compile the regexp pattern given by name and
+ * cache it here.
+ *
+ * @return 1 If comparison succeeds, 0 if not; < 0 on error.
+ */
+ int apol_compare_level(const apol_policy_t * p, const qpol_level_t * level, const char *name, unsigned int flags,
+ regex_t ** level_regex);
+
+/**
+ * Determines if a category query matches a qpol_cat_t, either
+ * the category name or any of its aliases.
+ *
+ * @param p Policy within which to look up types.
+ * @param cat category datum to compare against.
+ * @param name Source target from which to compare.
+ * @param flags If APOL_QUERY_REGEX bit is set, treat name as a
+ * regular expression.
+ * @param regex If using regexp comparison, the compiled regular
+ * expression to use; the pointer will be allocated space if regexp is
+ * legal. If NULL, then compile the regexp pattern given by name and
+ * cache it here.
+ *
+ * @return 1 If comparison succeeds, 0 if not; < 0 on error.
+ */
+ int apol_compare_cat(const apol_policy_t * p, const qpol_cat_t * cat, const char *name, unsigned int flags,
+ regex_t ** cat_regex);
+
+/**
+ * Convenience function that compares a qpol_context_t to a
+ * apol_context_t, based upon the MLS range match given by flags. If
+ * search is NULL then the comparison always succeeds.
+ *
+ * @param p Policy within which to look up types.
+ * @param target Target context to compare.
+ * @param name Source context from which to compare.
+ * @param flags Gives how to match MLS ranges within the contexts.
+ *
+ * @return 1 If comparison succeeds, 0 if not; < 0 on error.
+ */
+ int apol_compare_context(const apol_policy_t * p, const qpol_context_t * target, const apol_context_t * search,
+ unsigned int flags);
+
+/**
+ * Given a type name, obtain its qpol_type_t pointer (relative to a
+ * policy). If the type is really its alias, get its primary instead.
+ * (Attributes are considered to be always primary.)
+ *
+ * @param p Policy in which to look up types.
+ * @param type_name Name of type to find.
+ * @param type Reference to where to store resulting pointer.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ int apol_query_get_type(const apol_policy_t * p, const char *type_name, const qpol_type_t ** type);
+
+/**
+ * Given a symbol name (a type, attribute, alias, or a regular
+ * expression string), determine all types/attributes it matches.
+ * Return a vector of qpol_type_t that match. If regex is enabled,
+ * include all types/attributes that match the expression. If
+ * indirect is enabled, expand the candidiates within the vector (all
+ * attributes for a type, all types for an attribute), and then
+ * uniquify the vector.
+ *
+ * @param p Policy in which to look up types.
+ * @param symbol A string describing one or more type/attribute to
+ * which match.
+ * @param do_regex If non-zero, then treat symbol as a regular expression.
+ * @param do_indirect If non-zero, expand types to their attributes
+ * and attributes to their types.
+ * @param ta_flag Bit-wise or of (APOL_QUERY_SYMBOL_IS_TYPE,
+ * APOL_QUERY_SYMBOL_IS_ATTRIBUTE, APOL_QUERY_SYMBOL_IS_BOTH) whether
+ * symbol should be matched against type names or attribute names.
+ *
+ * @return Vector of unique qpol_type_t pointers (relative to policy
+ * within p), or NULL upon error. Caller is responsible for calling
+ * apol_vector_destroy() afterwards.
+ */
+ apol_vector_t *apol_query_create_candidate_type_list(const apol_policy_t * p, const char *symbol, int do_regex,
+ int do_indirect, unsigned int ta_flag);
+
+/**
+ * Given a symbol name (a type, attribute, alias, or a regular
+ * expression string), determine all types/attributes it matches.
+ * Return a vector of qpol_type_t that match. If regex is enabled,
+ * include all types/attributes that match the expression. If
+ * indirect is enabled, expand the candidiates within the vector (all
+ * attributes for a type, all types for an attribute), and then
+ * uniquify the vector. The list will include types needed for syntactic
+ * rule searching.
+ *
+ * @param p Policy in which to look up types. <b>Must be a source policy.</b>
+ * @param symbol A string describing one or more type/attribute to
+ * which match.
+ * @param do_regex If non-zero, then treat symbol as a regular expression.
+ * @param do_indirect If non-zero, expand types to their attributes
+ * and attributes to their types.
+ * @param ta_flag Bit-wise or of (APOL_QUERY_SYMBOL_IS_TYPE,
+ * APOL_QUERY_SYMBOL_IS_ATTRIBUTE, APOL_QUERY_SYMBOL_IS_BOTH) whether
+ * symbol should be matched against type names or attribute names.
+ *
+ * @return Vector of unique qpol_type_t pointers (relative to policy
+ * within p), or NULL upon error. Caller is responsible for calling
+ * apol_vector_destroy() afterwards.
+ */
+ apol_vector_t *apol_query_create_candidate_syn_type_list(const apol_policy_t * p, const char *symbol, int do_regex,
+ int do_indirect, unsigned int ta_flag);
+
+/**
+ * Given a symbol name (a role or a regular expression string),
+ * determine all roles it matches. Return a vector of qpol_role_t
+ * that match. If regex is enabled, include all role that
+ * match the expression.
+ *
+ * @param p Policy in which to look up roles.
+ * @param symbol A string describing one or more role to match.
+ * @param do_regex If non-zero, then treat symbol as a regular expression.
+ *
+ * @return Vector of unique qpol_role_t pointers (relative to policy
+ * within p), or NULL upon error. Caller is responsible for calling
+ * apol_vector_destroy() afterwards.
+ */
+ apol_vector_t *apol_query_create_candidate_role_list(const apol_policy_t * p, char *symbol, int do_regex);
+
+/**
+ * Given a vector of object class strings, determine all of the
+ * classes it matches within the policy. Returns a vector of
+ * qpol_class_t that match. If a string does not match an object
+ * class within the policy then it is ignored.
+ *
+ * @param p Policy in which to look up classes.
+ * @param classes Vector of class strings to convert.
+ *
+ * @return Vector of unique qpol_class_t pointers (relative to policy
+ * within p), or NULL upon error. Caller is responsible for calling
+ * apol_vector_destroy() afterwards.
+ */
+ apol_vector_t *apol_query_create_candidate_class_list(const apol_policy_t * p, apol_vector_t * classes);
+
+/**
+ * Given a type, return a vector of qpol_type_t pointers to which the
+ * type expands. If the type is just a type or an alias, the vector
+ * will have one element, pointing to the type's primary. If it was
+ * an attribute, the vector will have that attribute's types (but not
+ * the attribute itself).
+ *
+ * @param p Policy in which to look up types.
+ * @param t Type to expand.
+ *
+ * @return Vector of qpol_type_t pointers, or NULL upon error. Caller
+ * is responsible for calling apol_vector_destroy() afterwards.
+ */
+ apol_vector_t *apol_query_expand_type(const apol_policy_t * p, const qpol_type_t * t);
+
+/**
+ * Object class and permission set.
+ * Contains the name of a class and a list of permissions
+ * used by analyses and complex searches to allow permissions
+ * to be specified on a per class basis.
+ */
+ typedef struct apol_obj_perm apol_obj_perm_t;
+
+/**
+ * Allocate and return a new object permission set.
+ * @return a newly allocated object permission set or NULL on error.
+ * Caller is responsible for calling apol_obj_perm_free() to free
+ * memory used.
+ */
+ apol_obj_perm_t *apol_obj_perm_create(void);
+
+/**
+ * Free the memory used by an object permission set.
+ * @param op the object permission set to free.
+ */
+ void apol_obj_perm_free(void *op);
+
+/**
+ * Set the object class name for an object permission set.
+ * If already set free the previous name.
+ * @param op The object permission set for which to set the object name.
+ * @param obj_name New object name to set; this string will be duplicated
+ * by this call. If NULL only free existing name (if any).
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and the original object permission set will be unchanged.
+ */
+ int apol_obj_perm_set_obj_name(apol_obj_perm_t * op, const char *obj_name);
+
+/**
+ * Get the object class name from an object permission set.
+ * @param op The object permission set from which to get the class name.
+ * @return The class name or NULL if not set or error. The caller <b>should
+ * NOT</b> free the returned string.
+ */
+ char *apol_obj_perm_get_obj_name(const apol_obj_perm_t * op);
+
+/**
+ * Add a permission to the permission list of an object permission set.
+ * @param op The object permission set to which to add the permission.
+ * @param perm Name of the permission to add, this string will be duplicated.
+ * If NULL clear all permissions. If the permission is already in the list
+ * nothing is done;
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and the original object permission set will be unchanged.
+ */
+ int apol_obj_perm_append_perm(apol_obj_perm_t * op, const char *perm);
+
+/**
+ * Get a vector of the permissions in an object permission set.
+ * @param op The object permission set from which to get the permissions.
+ * @return Vector (of type char *) of permission names; the caller
+ * <b>should NOT</b> destroy this vector.
+ */
+ apol_vector_t *apol_obj_perm_get_perm_vector(const apol_obj_perm_t * op);
+
+/**
+ * Comparision function for use with vectors of object permission sets.
+ * @param a first object permission set.
+ * @param b second object permission set.
+ * @param policy apol policy from which the objects and permissions come.
+ * @return < 0, 0, or > 0 if the value of the class of a is less than, equal
+ * to, or greater than that of b respectively.
+ */
+ int apol_obj_perm_compare_class(const void *a, const void *b, void *policy);
+
+/**
+ * Determine if a syntactic type set directly uses any of the types in v.
+ * @param p Policy from which the type set and types come.
+ * @param set Syntactic type set to check.
+ * @param v Vector of types (qpol_type_t) to find in set.
+ * @return 0 if no types in v appear in set, > 0 if at least one type
+ * was found, and < 0 if an error occurred.
+ */
+ int apol_query_type_set_uses_types_directly(const apol_policy_t * p, const qpol_type_set_t * set, const apol_vector_t * v);
+
+/**
+ * Deallocate all space associated with a particular policy's permmap,
+ * including the pointer itself. Afterwards set the pointer to NULL.
+ *
+ * @param p Reference to an apol_permmap_t to destroy.
+ */
+ void permmap_destroy(apol_permmap_t ** p);
+
+/**
+ * Destroy the domain transition table freeing all memory used.
+ * @param table Reference pointer to the table to be destroyed.
+ */
+ void domain_trans_table_destroy(apol_domain_trans_table_t ** table);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/src/policy-query.c b/libapol/src/policy-query.c
new file mode 100644
index 0000000..18152fb
--- /dev/null
+++ b/libapol/src/policy-query.c
@@ -0,0 +1,903 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about different
+ * components of a policy. The caller obtains a query object, fills
+ * in its parameters, and then runs the query; it obtains a vector of
+ * results. Searches are conjunctive -- all fields of the search
+ * query must match for a datum to be added to the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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 <regex.h>
+#include <stdlib.h>
+#include <string.h>
+
+/******************** misc helpers ********************/
+
+void apol_regex_destroy(regex_t ** regex)
+{
+ if (*regex != NULL) {
+ regfree(*regex);
+ free(*regex);
+ *regex = NULL;
+ }
+}
+
+int apol_query_set(const apol_policy_t * p, char **query_name, regex_t ** regex, const char *name)
+{
+ if (*query_name != name) {
+ if (regex != NULL) {
+ apol_regex_destroy(regex);
+ }
+ free(*query_name);
+ *query_name = NULL;
+ if (name != NULL && name[0] != '\0' && ((*query_name) = strdup(name)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_query_set_flag(const apol_policy_t * p __attribute__ ((unused)), unsigned int *flags, const int is_flag, int flag_value)
+{
+ if (is_flag) {
+ *flags |= flag_value;
+ } else {
+ *flags &= ~flag_value;
+ }
+ return 0;
+}
+
+int apol_query_set_regex(const apol_policy_t * p, unsigned int *flags, const int is_regex)
+{
+ return apol_query_set_flag(p, flags, is_regex, APOL_QUERY_REGEX);
+}
+
+/********************* comparison helpers *********************/
+
+int apol_compare(const apol_policy_t * p, const char *target, const char *name, unsigned int flags, regex_t ** regex)
+{
+ if (name == NULL || *name == '\0') {
+ return 1;
+ }
+ char errbuf[1024] = { '\0' };
+ if ((flags & APOL_QUERY_REGEX) && regex != NULL) {
+ if (*regex == NULL) {
+ if ((*regex = malloc(sizeof(**regex))) == NULL) {
+ free(*regex);
+ *regex = NULL;
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ int regretv = regcomp(*regex, name, REG_EXTENDED | REG_NOSUB);
+ if (regretv) {
+ regerror(regretv, *regex, errbuf, 1024);
+ free(*regex);
+ *regex = NULL;
+ ERR(p, "%s", errbuf);
+ return -1;
+ }
+ }
+ if (regexec(*regex, target, 0, NULL, 0) == 0) {
+ return 1;
+ }
+ return 0;
+ } else {
+ if (strcmp(target, name) == 0) {
+ return 1;
+ }
+ return 0;
+ }
+}
+
+int apol_compare_iter(const apol_policy_t * p, qpol_iterator_t * iter,
+ const char *name, unsigned int flags, regex_t ** regex, int do_free)
+{
+ int compval;
+ if (name == NULL || *name == '\0') {
+ return 1;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ char *iter_name;
+ if (qpol_iterator_get_item(iter, (void **)&iter_name) < 0) {
+ return -1;
+ }
+ compval = apol_compare(p, iter_name, name, flags, regex);
+ if (do_free)
+ free(iter_name);
+ if (compval != 0) {
+ /* matched at least one name, or error */
+ return compval;
+ }
+ }
+ /* no matches */
+ return 0;
+}
+
+int apol_compare_type(const apol_policy_t * p, const qpol_type_t * type, const char *name, unsigned int flags,
+ regex_t ** type_regex)
+{
+ const char *type_name;
+ int compval;
+ qpol_iterator_t *alias_iter = NULL;
+ if (qpol_type_get_name(p->p, type, &type_name) < 0) {
+ return -1;
+ }
+ compval = apol_compare(p, type_name, name, flags, type_regex);
+ if (compval != 0) {
+ return compval;
+ }
+ /* also check if the matches against one of the type's
+ * aliases */
+ if (qpol_type_get_alias_iter(p->p, type, &alias_iter) < 0) {
+ return -1;
+ }
+ compval = apol_compare_iter(p, alias_iter, name, flags, type_regex, 0);
+ qpol_iterator_destroy(&alias_iter);
+ return compval;
+}
+
+int apol_compare_permissive(const apol_policy_t * p, const qpol_permissive_t * permissive, const char *name, unsigned int flags,
+ regex_t ** permissive_regex)
+{
+ const char *permissive_name;
+ int compval;
+
+ if (qpol_permissive_get_name(p->p, permissive, &permissive_name) < 0) {
+ return -1;
+ }
+ compval = apol_compare(p, permissive_name, name, flags, permissive_regex);
+
+ return compval;
+}
+
+int apol_compare_polcap(const apol_policy_t * p, const qpol_polcap_t * polcap, const char *name, unsigned int flags,
+ regex_t ** polcap_regex)
+{
+ const char *polcap_name;
+ int compval;
+
+ if (qpol_polcap_get_name(p->p, polcap, &polcap_name) < 0) {
+ return -1;
+ }
+ compval = apol_compare(p, polcap_name, name, flags, polcap_regex);
+
+ return compval;
+}
+
+int apol_compare_cond_expr(const apol_policy_t * p, const qpol_cond_t * cond, const char *name, unsigned int flags,
+ regex_t ** bool_regex)
+{
+ qpol_iterator_t *expr_iter = NULL;
+ int compval = -1;
+ if (qpol_cond_get_expr_node_iter(p->p, cond, &expr_iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(expr_iter); qpol_iterator_next(expr_iter)) {
+ qpol_cond_expr_node_t *expr;
+ uint32_t expr_type;
+ qpol_bool_t *qbool;
+ const char *bool_name;
+ if (qpol_iterator_get_item(expr_iter, (void **)&expr) < 0 ||
+ qpol_cond_expr_node_get_expr_type(p->p, expr, &expr_type) < 0) {
+ goto cleanup;
+ }
+ if (expr_type != QPOL_COND_EXPR_BOOL) {
+ continue;
+ }
+ if (qpol_cond_expr_node_get_bool(p->p, expr, &qbool) < 0 || qpol_bool_get_name(p->p, qbool, &bool_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, bool_name, name, flags, bool_regex);
+ if (compval != 0) { /* catches both errors and success */
+ goto cleanup;
+ }
+ }
+ compval = 0;
+ cleanup:
+ qpol_iterator_destroy(&expr_iter);
+ return compval;
+}
+
+int apol_compare_level(const apol_policy_t * p, const qpol_level_t * level, const char *name, unsigned int flags,
+ regex_t ** level_regex)
+{
+ const char *level_name;
+ int compval;
+ qpol_iterator_t *alias_iter = NULL;
+ if (qpol_level_get_name(p->p, level, &level_name) < 0) {
+ return -1;
+ }
+ compval = apol_compare(p, level_name, name, flags, level_regex);
+ if (compval != 0) {
+ return compval;
+ }
+ /* also check if the matches against one of the sensitivity's
+ * aliases */
+ if (qpol_level_get_alias_iter(p->p, level, &alias_iter) < 0) {
+ return -1;
+ }
+ compval = apol_compare_iter(p, alias_iter, name, flags, level_regex, 0);
+ qpol_iterator_destroy(&alias_iter);
+ return compval;
+}
+
+int apol_compare_cat(const apol_policy_t * p, const qpol_cat_t * cat, const char *name, unsigned int flags, regex_t ** cat_regex)
+{
+ const char *cat_name;
+ int compval;
+ qpol_iterator_t *alias_iter = NULL;
+ if (qpol_cat_get_name(p->p, cat, &cat_name) < 0) {
+ return -1;
+ }
+ compval = apol_compare(p, cat_name, name, flags, cat_regex);
+ if (compval != 0) {
+ return compval;
+ }
+ /* also check if the matches against one of the category's
+ * aliases */
+ if (qpol_cat_get_alias_iter(p->p, cat, &alias_iter) < 0) {
+ return -1;
+ }
+ compval = apol_compare_iter(p, alias_iter, name, flags, cat_regex, 0);
+ qpol_iterator_destroy(&alias_iter);
+ return compval;
+}
+
+int apol_compare_context(const apol_policy_t * p, const qpol_context_t * target, const apol_context_t * search, unsigned int flags)
+{
+ apol_context_t *apol_context;
+ int retval;
+ if (search == NULL) {
+ return 1;
+ }
+ apol_context = apol_context_create_from_qpol_context(p, target);
+ retval = apol_context_compare(p, apol_context, search, flags);
+ apol_context_destroy(&apol_context);
+ return retval;
+}
+
+/******************** other helpers ********************/
+
+int apol_query_get_type(const apol_policy_t * p, const char *type_name, const qpol_type_t ** type)
+{
+ unsigned char isalias;
+ if (qpol_policy_get_type_by_name(p->p, type_name, type) < 0 || qpol_type_get_isalias(p->p, *type, &isalias) < 0) {
+ return -1;
+ }
+ if (isalias) {
+ const char *primary_name;
+ if (qpol_type_get_name(p->p, *type, &primary_name) < 0 ||
+ qpol_policy_get_type_by_name(p->p, primary_name, type) < 0) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Append a non-aliased type to a vector. If the passed in type is an
+ * alias, find its primary type and append that instead.
+ *
+ * @param p Policy in which to look up types.
+ * @param v Vector in which append the non-aliased type.
+ * @param type Type or attribute to append. If this is an alias,
+ * append its primary.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_query_append_type(const apol_policy_t * p, apol_vector_t * v, const qpol_type_t * type)
+{
+ unsigned char isalias;
+ const qpol_type_t *real_type = type;
+ if (qpol_type_get_isalias(p->p, type, &isalias) < 0) {
+ return -1;
+ }
+ if (isalias) {
+ const char *primary_name;
+ if (qpol_type_get_name(p->p, type, &primary_name) < 0 ||
+ qpol_policy_get_type_by_name(p->p, primary_name, &real_type) < 0) {
+ return -1;
+ }
+ }
+ if (apol_vector_append(v, (void *)real_type) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ return 0;
+}
+
+apol_vector_t *apol_query_create_candidate_type_list(const apol_policy_t * p, const char *symbol, int do_regex, int do_indirect,
+ unsigned int ta_flag)
+{
+ apol_vector_t *list = apol_vector_create(NULL);
+ const qpol_type_t *type;
+ regex_t *regex = NULL;
+ qpol_iterator_t *iter = NULL, *alias_iter = NULL;
+ int retval = -1, error = 0;
+ unsigned char isalias, isattr;
+ const char *type_name;
+ int compval;
+ size_t i, orig_vector_size;
+
+ if (list == NULL) {
+ error = EINVAL;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ if (ta_flag == 0 || (ta_flag & ~APOL_QUERY_SYMBOL_IS_BOTH)) {
+ error = EINVAL;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ if (!do_regex && apol_query_get_type(p, symbol, &type) == 0) {
+ if (apol_query_append_type(p, list, type) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+
+ if (do_regex) {
+ if (qpol_policy_get_type_iter(p->p, &iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0 || qpol_type_get_name(p->p, type, &type_name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ compval = apol_compare(p, type_name, symbol, APOL_QUERY_REGEX, &regex);
+ if (compval < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (compval && apol_query_append_type(p, list, type)) {
+ error = errno;
+ goto cleanup;
+ }
+ if (compval)
+ continue;
+ if (qpol_type_get_alias_iter(p->p, type, &alias_iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(alias_iter); qpol_iterator_next(alias_iter)) {
+ if (qpol_iterator_get_item(alias_iter, (void **)&type_name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ compval = apol_compare(p, type_name, symbol, APOL_QUERY_REGEX, &regex);
+ if (compval < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (compval && apol_query_append_type(p, list, type)) {
+ error = errno;
+ goto cleanup;
+ }
+ if (compval)
+ break;
+ }
+ qpol_iterator_destroy(&alias_iter);
+ }
+ qpol_iterator_destroy(&iter);
+ }
+
+ /* prune to match ta_flag */
+ for (i = 0; i < apol_vector_get_size(list); i++) {
+ type = (qpol_type_t *) apol_vector_get_element(list, i);
+ if (qpol_type_get_isattr(p->p, type, &isattr) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((isattr && !(ta_flag & APOL_QUERY_SYMBOL_IS_ATTRIBUTE)) || (!isattr && !(ta_flag & APOL_QUERY_SYMBOL_IS_TYPE))) {
+ apol_vector_remove(list, i);
+ i--;
+ }
+ }
+
+ if (do_indirect) {
+ orig_vector_size = apol_vector_get_size(list);
+ for (i = 0; i < orig_vector_size; i++) {
+ type = (qpol_type_t *) apol_vector_get_element(list, i);
+ if (qpol_type_get_isalias(p->p, type, &isalias) < 0 || qpol_type_get_isattr(p->p, type, &isattr) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (isalias) {
+ continue;
+ }
+ if ((isattr &&
+ qpol_type_get_type_iter(p->p, type, &iter) < 0) ||
+ (!isattr && qpol_type_get_attr_iter(p->p, type, &iter) < 0)) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_query_append_type(p, list, type)) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ }
+
+ apol_vector_sort_uniquify(list, NULL, NULL);
+ retval = 0;
+ cleanup:
+ if (regex != NULL) {
+ regfree(regex);
+ free(regex);
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&alias_iter);
+ if (retval < 0) {
+ apol_vector_destroy(&list);
+ errno = error;
+ }
+ return list;
+}
+
+apol_vector_t *apol_query_create_candidate_syn_type_list(const apol_policy_t * p, const char *symbol, int do_regex, int do_indirect,
+ unsigned int ta_flag)
+{
+ apol_vector_t *list = apol_vector_create(NULL);
+ const qpol_type_t *type;
+ regex_t *regex = NULL;
+ qpol_iterator_t *iter = NULL, *alias_iter = NULL;
+ int retval = -1, error = 0;
+ unsigned char isalias, isattr;
+ const char *type_name;
+ int compval;
+ size_t i, orig_vector_size;
+
+ if (list == NULL) {
+ error = EINVAL;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ if (!p || !qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_ATTRIB_NAMES)
+ || !qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_SYN_RULES)) {
+ error = EINVAL;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ if (ta_flag == 0 || (ta_flag & ~APOL_QUERY_SYMBOL_IS_BOTH)) {
+ error = EINVAL;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ if (!do_regex && apol_query_get_type(p, symbol, &type) == 0) {
+ if (apol_query_append_type(p, list, type) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+
+ if (do_regex) {
+ if (qpol_policy_get_type_iter(p->p, &iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0 || qpol_type_get_name(p->p, type, &type_name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ compval = apol_compare(p, type_name, symbol, APOL_QUERY_REGEX, &regex);
+ if (compval < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (compval && apol_query_append_type(p, list, type)) {
+ error = errno;
+ goto cleanup;
+ }
+ if (compval)
+ continue;
+ if (qpol_type_get_alias_iter(p->p, type, &alias_iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(alias_iter); qpol_iterator_next(alias_iter)) {
+ if (qpol_iterator_get_item(alias_iter, (void **)&type_name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ compval = apol_compare(p, type_name, symbol, APOL_QUERY_REGEX, &regex);
+ if (compval < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (compval && apol_query_append_type(p, list, type)) {
+ error = errno;
+ goto cleanup;
+ }
+ if (compval)
+ break;
+ }
+ qpol_iterator_destroy(&alias_iter);
+ }
+ qpol_iterator_destroy(&iter);
+ }
+
+ /* prune to match ta_flag */
+ for (i = 0; i < apol_vector_get_size(list); i++) {
+ type = (qpol_type_t *) apol_vector_get_element(list, i);
+ if (qpol_type_get_isattr(p->p, type, &isattr) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((isattr && !(ta_flag & APOL_QUERY_SYMBOL_IS_ATTRIBUTE)) || (!isattr && !(ta_flag & APOL_QUERY_SYMBOL_IS_TYPE))) {
+ apol_vector_remove(list, i);
+ i--;
+ }
+ }
+
+ orig_vector_size = apol_vector_get_size(list);
+ for (i = 0; i < orig_vector_size; i++) {
+ type = (qpol_type_t *) apol_vector_get_element(list, i);
+ if (qpol_type_get_isalias(p->p, type, &isalias) < 0 || qpol_type_get_isattr(p->p, type, &isattr) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (isalias) {
+ continue;
+ }
+ if (!do_indirect && !isattr)
+ continue;
+ if ((isattr && qpol_type_get_type_iter(p->p, type, &iter) < 0) ||
+ (!isattr && qpol_type_get_attr_iter(p->p, type, &iter) < 0)) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_query_append_type(p, list, type)) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+
+ apol_vector_sort_uniquify(list, NULL, NULL);
+ retval = 0;
+ cleanup:
+ if (regex != NULL) {
+ regfree(regex);
+ free(regex);
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&alias_iter);
+ if (retval < 0) {
+ apol_vector_destroy(&list);
+ list = NULL;
+ errno = error;
+ }
+ return list;
+}
+
+apol_vector_t *apol_query_create_candidate_role_list(const apol_policy_t * p, char *symbol, int do_regex)
+{
+ apol_vector_t *list = apol_vector_create(NULL);
+ const qpol_role_t *role;
+ regex_t *regex = NULL;
+ qpol_iterator_t *iter = NULL;
+ int retval = -1;
+
+ if (list == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ if (!do_regex && qpol_policy_get_role_by_name(p->p, symbol, &role) == 0) {
+ if (apol_vector_append(list, (void *)role) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ if (do_regex) {
+ if (qpol_policy_get_role_iter(p->p, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ const char *role_name;
+ int compval;
+ if (qpol_iterator_get_item(iter, (void **)&role) < 0 || qpol_role_get_name(p->p, role, &role_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, role_name, symbol, APOL_QUERY_REGEX, &regex);
+ if (compval < 0) {
+ goto cleanup;
+ }
+ if (compval && apol_vector_append(list, (void *)role)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_sort_uniquify(list, NULL, NULL);
+ retval = 0;
+ cleanup:
+ if (regex != NULL) {
+ regfree(regex);
+ free(regex);
+ }
+ qpol_iterator_destroy(&iter);
+ if (retval < 0) {
+ apol_vector_destroy(&list);
+ list = NULL;
+ }
+ return list;
+}
+
+apol_vector_t *apol_query_create_candidate_class_list(const apol_policy_t * p, apol_vector_t * classes)
+{
+ apol_vector_t *list = apol_vector_create(NULL);
+ size_t i;
+ int retval = -1;
+
+ if (list == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ for (i = 0; i < apol_vector_get_size(classes); i++) {
+ char *class_string = (char *)apol_vector_get_element(classes, i);
+ const qpol_class_t *class;
+ if (qpol_policy_get_class_by_name(p->p, class_string, &class) == 0) {
+ if (apol_vector_append(list, (void *)class) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ }
+ apol_vector_sort_uniquify(list, NULL, NULL);
+ retval = 0;
+ cleanup:
+ if (retval < 0) {
+ apol_vector_destroy(&list);
+ list = NULL;
+ }
+ return list;
+}
+
+apol_vector_t *apol_query_expand_type(const apol_policy_t * p, const qpol_type_t * t)
+{
+ apol_vector_t *v = NULL;
+ int retval = -1;
+ unsigned char isattr;
+ qpol_iterator_t *iter = NULL;
+
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (qpol_type_get_isattr(p->p, t, &isattr) < 0) {
+ goto cleanup;
+ }
+ if (!isattr) {
+ if (apol_vector_append(v, (void *)t) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ } else {
+ if (qpol_type_get_type_iter(p->p, t, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_type_t *type;
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_append(v, type) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ }
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ if (retval != 0) {
+ apol_vector_destroy(&v);
+ return NULL;
+ }
+ return v;
+}
+
+/******** apol_obj_perm - set of an object with a list of permissions ********/
+
+struct apol_obj_perm
+{
+ char *obj_class; /* name of object class */
+ apol_vector_t *perms; /* vector of permission names */
+};
+
+apol_obj_perm_t *apol_obj_perm_create(void)
+{
+ apol_obj_perm_t *op = calloc(1, sizeof(apol_obj_perm_t));
+ if (!op)
+ return NULL;
+
+ op->perms = apol_vector_create(free);
+ if (!(op->perms)) {
+ free(op);
+ return NULL;
+ }
+
+ return op;
+}
+
+void apol_obj_perm_free(void *op)
+{
+ apol_obj_perm_t *inop = (apol_obj_perm_t *) op;
+ if (inop != NULL) {
+ free(inop->obj_class);
+ apol_vector_destroy(&inop->perms);
+ free(inop);
+ }
+}
+
+int apol_obj_perm_set_obj_name(apol_obj_perm_t * op, const char *obj_name)
+{
+ char *tmp = NULL;
+
+ if (!op) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (obj_name) {
+ if (!(tmp = strdup(obj_name)))
+ return -1;
+ free(op->obj_class);
+ op->obj_class = tmp;
+ } else {
+ free(op->obj_class);
+ op->obj_class = NULL;
+ }
+
+ return 0;
+}
+
+char *apol_obj_perm_get_obj_name(const apol_obj_perm_t * op)
+{
+ if (!op) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return op->obj_class;
+}
+
+int apol_obj_perm_append_perm(apol_obj_perm_t * op, const char *perm)
+{
+ char *tmp = NULL;
+
+ if (!op) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (perm) {
+ if ((tmp = strdup(perm)) == NULL || (op->perms == NULL && (op->perms = apol_vector_create(free)) == NULL)) {
+ free(tmp);
+ return -1;
+ }
+ if (apol_vector_append_unique(op->perms, tmp, apol_str_strcmp, NULL) < 0) {
+ free(tmp);
+ return -1;
+ }
+ } else {
+ apol_vector_destroy(&op->perms);
+ }
+
+ return 0;
+}
+
+apol_vector_t *apol_obj_perm_get_perm_vector(const apol_obj_perm_t * op)
+{
+ if (!op) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return op->perms;
+}
+
+int apol_obj_perm_compare_class(const void *a, const void *b, void *policy)
+{
+ const apol_obj_perm_t *opa = (const apol_obj_perm_t *)a;
+ const apol_obj_perm_t *opb = (const apol_obj_perm_t *)b;
+ apol_policy_t *p = (apol_policy_t *) policy;
+ const qpol_class_t *obja = NULL, *objb = NULL;
+ uint32_t a_val = 0, b_val = 0;
+
+ qpol_policy_get_class_by_name(p->p, opa->obj_class, &obja);
+ qpol_policy_get_class_by_name(p->p, opb->obj_class, &objb);
+ qpol_class_get_value(p->p, obja, &a_val);
+ qpol_class_get_value(p->p, objb, &b_val);
+
+ return (int)(a_val - b_val);
+}
+
+int apol_query_type_set_uses_types_directly(const apol_policy_t * p, const qpol_type_set_t * set, const apol_vector_t * v)
+{
+ qpol_iterator_t *iter = NULL;
+ qpol_type_t *type = NULL;
+ size_t i;
+ uint32_t comp;
+
+ if (!p || !set) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (!v || !apol_vector_get_size(v))
+ return 0;
+
+ if (qpol_type_set_get_is_comp(p->p, set, &comp)) {
+ return -1;
+ }
+ if (comp) {
+ if (qpol_type_set_get_subtracted_types_iter(p->p, set, &iter)) {
+ return -1;
+ }
+ } else {
+ if (qpol_type_set_get_included_types_iter(p->p, set, &iter)) {
+ return -1;
+ }
+ }
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)&type);
+ if (!apol_vector_get_index(v, (void *)type, NULL, NULL, &i)) {
+ qpol_iterator_destroy(&iter);
+ return 1;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+
+ return 0;
+}
diff --git a/libapol/src/policy.c b/libapol/src/policy.c
new file mode 100644
index 0000000..95ab7cd
--- /dev/null
+++ b/libapol/src/policy.c
@@ -0,0 +1,217 @@
+/**
+ * @file
+ *
+ * Public interface for SELinux policies.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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 <apol/perm-map.h>
+#include <apol/domain-trans-analysis.h>
+
+#include <qpol/policy.h>
+#include <qpol/policy_extend.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void apol_handle_default_callback(void *varg __attribute__ ((unused)), const apol_policy_t * p
+ __attribute__ ((unused)), int level, const char *fmt, va_list va_args)
+{
+ switch (level) {
+ case APOL_MSG_INFO:
+ {
+ /* by default do not display these messages */
+ return;
+ }
+ case APOL_MSG_WARN:
+ {
+ fprintf(stderr, "WARNING: ");
+ break;
+ }
+ case APOL_MSG_ERR:
+ default:
+ {
+ fprintf(stderr, "ERROR: ");
+ break;
+ }
+ }
+ vfprintf(stderr, fmt, va_args);
+ fprintf(stderr, "\n");
+}
+
+static void qpol_handle_route_to_callback(void *varg, const qpol_policy_t * policy
+ __attribute__ ((unused)), int level, const char *fmt, va_list ap)
+{
+ apol_policy_t *p = (apol_policy_t *) varg;
+ if (p == NULL) {
+ apol_handle_default_callback(NULL, NULL, level, fmt, ap);
+ } else if (p->msg_callback != NULL) {
+ p->msg_callback(p->msg_callback_arg, p, level, fmt, ap);
+ }
+}
+
+apol_policy_t *apol_policy_create_from_policy_path(const apol_policy_path_t * path, const int options,
+ apol_callback_fn_t msg_callback, void *varg)
+{
+ apol_policy_t *policy;
+ const char *primary_path;
+ int policy_type;
+ if (!path) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (!(policy = calloc(1, sizeof(apol_policy_t)))) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ return NULL; /* errno set by calloc */
+ }
+ if (msg_callback != NULL) {
+ policy->msg_callback = msg_callback;
+ } else {
+ policy->msg_callback = apol_handle_default_callback;
+ }
+ policy->msg_callback_arg = varg;
+ primary_path = apol_policy_path_get_primary(path);
+ INFO(policy, "Loading policy %s.", primary_path);
+ policy_type = qpol_policy_open_from_file(primary_path, &policy->p, qpol_handle_route_to_callback, policy, options);
+ if (policy_type < 0) {
+ ERR(policy, "Unable to open policy %s.", primary_path);
+ apol_policy_destroy(&policy);
+ return NULL; /* qpol sets errno */
+ }
+ policy->policy_type = policy_type;
+
+ if (apol_policy_path_get_type(path) == APOL_POLICY_PATH_TYPE_MODULAR) {
+ if (!qpol_policy_has_capability(policy->p, QPOL_CAP_MODULES)) {
+ INFO(policy, "%s is not a base policy.", primary_path);
+ return policy;
+ }
+ const apol_vector_t *modules = apol_policy_path_get_modules(path);
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(modules); i++) {
+ const char *module_path = apol_vector_get_element(modules, i);
+ qpol_module_t *mod = NULL;
+ INFO(policy, "Loading module %s.", module_path);
+ if (qpol_module_create_from_file(module_path, &mod)) {
+ ERR(policy, "Error loading module %s.", module_path);
+ apol_policy_destroy(&policy);
+ return NULL;
+ }
+ if (qpol_policy_append_module(policy->p, mod)) {
+ ERR(policy, "Error loading module %s.", module_path);
+ apol_policy_destroy(&policy);
+ qpol_module_destroy(&mod);
+ return NULL;
+ }
+ }
+ INFO(policy, "%s", "Linking modules into base policy.");
+ if (qpol_policy_rebuild(policy->p, options)) {
+ apol_policy_destroy(&policy);
+ return NULL;
+ }
+ }
+ return policy;
+}
+
+void apol_policy_destroy(apol_policy_t ** policy)
+{
+ if (policy != NULL && *policy != NULL) {
+ qpol_policy_destroy(&((*policy)->p));
+ permmap_destroy(&(*policy)->pmap);
+ domain_trans_table_destroy(&(*policy)->domain_trans_table);
+ free(*policy);
+ *policy = NULL;
+ }
+}
+
+int apol_policy_get_policy_type(const apol_policy_t * policy)
+{
+ if (policy == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return policy->policy_type;
+}
+
+qpol_policy_t *apol_policy_get_qpol(const apol_policy_t * policy)
+{
+ if (policy == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return policy->p;
+}
+
+int apol_policy_is_mls(const apol_policy_t * p)
+{
+ if (p == NULL) {
+ return -1;
+ }
+ return qpol_policy_has_capability(p->p, QPOL_CAP_MLS);
+}
+
+char *apol_policy_get_version_type_mls_str(const apol_policy_t * p)
+{
+ unsigned int version;
+ char *policy_type, *mls, buf[64];
+ if (qpol_policy_get_policy_version(p->p, &version) < 0) {
+ return NULL;
+ }
+ switch (p->policy_type) {
+ case QPOL_POLICY_KERNEL_SOURCE:
+ policy_type = "source";
+ break;
+ case QPOL_POLICY_KERNEL_BINARY:
+ policy_type = "binary";
+ break;
+ case QPOL_POLICY_MODULE_BINARY:
+ policy_type = "modular";
+ break;
+ default:
+ policy_type = "unknown";
+ break;
+ }
+ if (qpol_policy_has_capability(p->p, QPOL_CAP_MLS)) {
+ mls = "mls";
+ } else {
+ mls = "non-mls";
+ }
+ if (snprintf(buf, sizeof(buf), "v.%u (%s, %s)", version, policy_type, mls) == -1) {
+ return NULL;
+ }
+ return strdup(buf);
+}
+
+void apol_handle_msg(const apol_policy_t * p, int level, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ if (p == NULL) {
+ apol_handle_default_callback(NULL, NULL, level, fmt, ap);
+ } else if (p->msg_callback != NULL) {
+ p->msg_callback(p->msg_callback_arg, p, level, fmt, ap);
+ }
+ va_end(ap);
+}
diff --git a/libapol/src/queue.c b/libapol/src/queue.c
new file mode 100644
index 0000000..1a3de23
--- /dev/null
+++ b/libapol/src/queue.c
@@ -0,0 +1,125 @@
+/**
+ * @file
+ *
+ * This file is a copy of queue.h from NSA's CVS repository. It has
+ * been modified to follow the setools naming conventions.
+ *
+ * Author : Stephen Smalley (NSA), <sds@epoch.ncsc.mil>
+ *
+ * Implementation of the double-ended queue type.
+ */
+
+#include <stdlib.h>
+#include "queue.h"
+
+apol_queue_t *apol_queue_create(void)
+{
+ apol_queue_t *q;
+
+ q = (apol_queue_t *) malloc(sizeof(apol_queue_t));
+ if (q == NULL)
+ return NULL;
+
+ q->head = q->tail = NULL;
+
+ return q;
+}
+
+int apol_queue_insert(apol_queue_t * q, void *element)
+{
+ apol_queue_node_t *newnode;
+
+ if (!q)
+ return -1;
+
+ newnode = (apol_queue_node_t *) malloc(sizeof(struct apol_queue_node));
+ if (newnode == NULL)
+ return -1;
+
+ newnode->element = element;
+ newnode->next = NULL;
+
+ if (q->head == NULL) {
+ q->head = q->tail = newnode;
+ } else {
+ q->tail->next = newnode;
+ q->tail = newnode;
+ }
+
+ return 0;
+}
+
+int apol_queue_push(apol_queue_t * q, void *element)
+{
+ apol_queue_node_t *newnode;
+
+ if (!q)
+ return -1;
+
+ newnode = (apol_queue_node_t *) malloc(sizeof(apol_queue_node_t));
+ if (newnode == NULL)
+ return -1;
+
+ newnode->element = element;
+ newnode->next = NULL;
+
+ if (q->head == NULL) {
+ q->head = q->tail = newnode;
+ } else {
+ newnode->next = q->head;
+ q->head = newnode;
+ }
+
+ return 0;
+}
+
+void *apol_queue_remove(apol_queue_t * q)
+{
+ apol_queue_node_t *node;
+ void *element;
+
+ if (!q)
+ return NULL;
+
+ if (q->head == NULL)
+ return NULL;
+
+ node = q->head;
+ q->head = q->head->next;
+ if (q->head == NULL)
+ q->tail = NULL;
+
+ element = node->element;
+ free(node);
+
+ return element;
+}
+
+void *apol_queue_head(apol_queue_t * q)
+{
+ if (!q)
+ return NULL;
+
+ if (q->head == NULL)
+ return NULL;
+
+ return q->head->element;
+}
+
+void apol_queue_destroy(apol_queue_t ** q)
+{
+ apol_queue_node_t *p, *temp;
+
+ if (!q || *q == NULL)
+ return;
+
+ p = (*q)->head;
+ while (p != NULL) {
+ temp = p;
+ p = p->next;
+ free(temp);
+ }
+
+ free(*q);
+ *q = NULL;
+}
diff --git a/libapol/src/queue.h b/libapol/src/queue.h
new file mode 100644
index 0000000..56eb97b
--- /dev/null
+++ b/libapol/src/queue.h
@@ -0,0 +1,86 @@
+/**
+ * @file
+ *
+ * This file is a copy of queue.h from NSA's CVS repository. It has
+ * been modified to follow the setools naming conventions.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ *
+ * A double-ended queue is a singly linked list of
+ * elements of arbitrary type that may be accessed
+ * at either end.
+ */
+
+#ifndef APOL_QUEUE_H
+#define APOL_QUEUE_H
+
+typedef struct apol_queue_node
+{
+ void *element;
+ struct apol_queue_node *next;
+} apol_queue_node_t;
+
+typedef struct apol_queue
+{
+ apol_queue_node_t *head;
+ apol_queue_node_t *tail;
+} apol_queue_t;
+
+/**
+ * Allocate and return a new queue. The caller is responsible for
+ * calling apol_queue_destroy() upon the return value.
+ *
+ * @return A newly allocated queue, or NULL upon error.
+ */
+apol_queue_t *apol_queue_create(void);
+
+/**
+ * Adds an element to the end of a queue.
+ *
+ * @param q Queue to modify.
+ * @param element Element to append to the end.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+int apol_queue_insert(apol_queue_t * q, void *element);
+
+/**
+ * Adds an element to the beginning of a queue.
+ *
+ * @param q Queue to modify.
+ * @param element Element to prepend to the beginning.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+int apol_queue_push(apol_queue_t * q, void *element);
+
+/**
+ * Remove the first element from a queue and return the data; the
+ * queue is advanced afterwards. If the queue was empty then return
+ * NULL.
+ *
+ * @return First element of a queue, or NULL if nothing is there.
+ */
+void *apol_queue_remove(apol_queue_t * q);
+
+/**
+ * Return the data within the first element, but do not remove it from
+ * the queue. If the queue was empty then return NULL.
+ *
+ * @return First element of a queue, or NULL if nothing is there.
+ */
+void *apol_queue_head(apol_queue_t * q);
+
+/**
+ * Destroy the referenced queue, but <i>do not</i> attempt to free the
+ * data stored within. (The caller is responsible for doing that.)
+ * Afterwards set the referenced variable to NULL. If the variable is
+ * NULL then do nothing.
+ *
+ * @param Reference to a queue to destroy.
+ */
+void apol_queue_destroy(apol_queue_t ** q);
+
+#endif
+
+/* FLASK */
diff --git a/libapol/src/range_trans-query.c b/libapol/src/range_trans-query.c
new file mode 100644
index 0000000..6ba80b7
--- /dev/null
+++ b/libapol/src/range_trans-query.c
@@ -0,0 +1,318 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about range transition
+ * rules within a policy. The caller obtains a query object, fills in
+ * its parameters, and then runs the query; it obtains a vector of
+ * results. Searches are conjunctive -- all fields of the search
+ * query must match for a datum to be added to the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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>
+
+struct apol_range_trans_query
+{
+ char *source, *target;
+ apol_vector_t *classes;
+ apol_mls_range_t *range;
+ unsigned int flags;
+};
+
+int apol_range_trans_get_by_query(const apol_policy_t * p, const apol_range_trans_query_t * r, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *source_list = NULL, *target_list = NULL, *class_list = NULL;
+ apol_mls_range_t *range = NULL;
+ int retval = -1, source_as_any = 0;
+ *v = NULL;
+
+ if (r != NULL) {
+ if (r->source != NULL &&
+ (source_list =
+ apol_query_create_candidate_type_list(p, r->source, r->flags & APOL_QUERY_REGEX,
+ r->flags & APOL_QUERY_SOURCE_INDIRECT,
+ APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) {
+ goto cleanup;
+ }
+ if ((r->flags & APOL_QUERY_SOURCE_AS_ANY) && r->source != NULL) {
+ target_list = source_list;
+ source_as_any = 1;
+ } else if (r->target != NULL &&
+ (target_list =
+ apol_query_create_candidate_type_list(p, r->target, r->flags & APOL_QUERY_REGEX,
+ r->flags & APOL_QUERY_TARGET_INDIRECT,
+ APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) {
+ goto cleanup;
+ }
+ if (r->classes != NULL &&
+ apol_vector_get_size(r->classes) > 0 &&
+ (class_list = apol_query_create_candidate_class_list(p, r->classes)) == NULL) {
+ goto cleanup;
+ }
+ }
+
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (qpol_policy_get_range_trans_iter(p->p, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_range_trans_t *rule;
+ const qpol_mls_range_t *mls_range;
+ int match_source = 0, match_target = 0, compval;
+ size_t i;
+ if (qpol_iterator_get_item(iter, (void **)&rule) < 0) {
+ goto cleanup;
+ }
+ if (source_list == NULL) {
+ match_source = 1;
+ } else {
+ const qpol_type_t *source_type;
+ if (qpol_range_trans_get_source_type(p->p, rule, &source_type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(source_list, source_type, NULL, NULL, &i) == 0) {
+ match_source = 1;
+ }
+ }
+
+ /* if source did not match, but treating source symbol
+ * as any field, then delay rejecting this rule until
+ * the target has been checked */
+ if (!source_as_any && !match_source) {
+ continue;
+ }
+
+ if (target_list == NULL || (source_as_any && match_source)) {
+ match_target = 1;
+ } else {
+ const qpol_type_t *target_type;
+ if (qpol_range_trans_get_target_type(p->p, rule, &target_type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(target_list, target_type, NULL, NULL, &i) == 0) {
+ match_target = 1;
+ }
+ }
+
+ if (!match_target) {
+ continue;
+ }
+
+ if (class_list != NULL) {
+ const qpol_class_t *obj_class;
+ if (qpol_range_trans_get_target_class(p->p, rule, &obj_class) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(class_list, obj_class, NULL, NULL, &i) < 0) {
+ continue;
+ }
+ }
+
+ if (qpol_range_trans_get_range(p->p, rule, &mls_range) < 0 ||
+ (range = apol_mls_range_create_from_qpol_mls_range(p, mls_range)) == NULL) {
+ goto cleanup;
+ }
+ if (r)
+ compval = apol_mls_range_compare(p, range, r->range, r->flags);
+ else
+ compval = 1;
+ apol_mls_range_destroy(&range);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+
+ if (apol_vector_append(*v, rule)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ apol_vector_destroy(&source_list);
+ if (!source_as_any) {
+ apol_vector_destroy(&target_list);
+ }
+ apol_vector_destroy(&class_list);
+ qpol_iterator_destroy(&iter);
+ apol_mls_range_destroy(&range);
+ return retval;
+}
+
+apol_range_trans_query_t *apol_range_trans_query_create(void)
+{
+ return calloc(1, sizeof(apol_range_trans_query_t));
+}
+
+void apol_range_trans_query_destroy(apol_range_trans_query_t ** r)
+{
+ if (*r != NULL) {
+ free((*r)->source);
+ free((*r)->target);
+ apol_vector_destroy(&(*r)->classes);
+ apol_mls_range_destroy(&((*r)->range));
+ free(*r);
+ *r = NULL;
+ }
+}
+
+int apol_range_trans_query_set_source(const apol_policy_t * p, apol_range_trans_query_t * r, const char *symbol, int is_indirect)
+{
+ apol_query_set_flag(p, &r->flags, is_indirect, APOL_QUERY_SOURCE_INDIRECT);
+ return apol_query_set(p, &r->source, NULL, symbol);
+}
+
+int apol_range_trans_query_set_target(const apol_policy_t * p, apol_range_trans_query_t * r, const char *symbol, int is_indirect)
+{
+ apol_query_set_flag(p, &r->flags, is_indirect, APOL_QUERY_TARGET_INDIRECT);
+ return apol_query_set(p, &r->target, NULL, symbol);
+}
+
+int apol_range_trans_query_append_class(const apol_policy_t * p, apol_range_trans_query_t * r, const char *obj_class)
+{
+ char *s = NULL;
+ 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));
+ free(s);
+ return -1;
+ }
+ return 0;
+}
+
+int apol_range_trans_query_set_range(const apol_policy_t * p __attribute__ ((unused)),
+ apol_range_trans_query_t * r, apol_mls_range_t * range, unsigned int range_match)
+{
+ if (r->range != NULL) {
+ apol_mls_range_destroy(&r->range);
+ }
+ r->range = range;
+ r->flags = (r->flags & ~APOL_QUERY_FLAGS) | range_match;
+ return 0;
+}
+
+int apol_range_trans_query_set_source_any(const apol_policy_t * p, apol_range_trans_query_t * r, int is_any)
+{
+ return apol_query_set_flag(p, &r->flags, is_any, APOL_QUERY_SOURCE_AS_ANY);
+}
+
+int apol_range_trans_query_set_regex(const apol_policy_t * p, apol_range_trans_query_t * r, int is_regex)
+{
+ return apol_query_set_regex(p, &r->flags, is_regex);
+}
+
+char *apol_range_trans_render(const apol_policy_t * policy, const qpol_range_trans_t * rule)
+{
+ char *tmp = NULL;
+ const char *tmp_name = NULL;
+ int error = 0;
+ size_t tmp_sz = 0;
+ const qpol_type_t *type = NULL;
+ const qpol_class_t *target_class = NULL;
+ const qpol_mls_range_t *range = NULL;
+ apol_mls_range_t *arange = NULL;
+
+ if (!policy || !rule) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* range_transition */
+ if (apol_str_append(&tmp, &tmp_sz, "range_transition ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ return NULL;
+ }
+
+ /* source type */
+ if (qpol_range_trans_get_source_type(policy->p, rule, &type) ||
+ qpol_type_get_name(policy->p, type, &tmp_name) ||
+ apol_str_append(&tmp, &tmp_sz, tmp_name) || apol_str_append(&tmp, &tmp_sz, " ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* target type */
+ if (qpol_range_trans_get_target_type(policy->p, rule, &type) ||
+ qpol_type_get_name(policy->p, type, &tmp_name) ||
+ apol_str_append(&tmp, &tmp_sz, tmp_name) || apol_str_append(&tmp, &tmp_sz, " : ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* target class */
+ if (qpol_range_trans_get_target_class(policy->p, rule, &target_class) ||
+ qpol_class_get_name(policy->p, target_class, &tmp_name) ||
+ apol_str_append(&tmp, &tmp_sz, tmp_name) || apol_str_append(&tmp, &tmp_sz, " ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* range */
+ if (qpol_range_trans_get_range(policy->p, rule, &range)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (!(arange = apol_mls_range_create_from_qpol_mls_range(policy, range))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ char *tmp_range_str = NULL;
+ if (!(tmp_range_str = apol_mls_range_render(policy, arange))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ apol_mls_range_destroy(&arange);
+ if (apol_str_append(&tmp, &tmp_sz, tmp_range_str) || apol_str_append(&tmp, &tmp_sz, ";")) {
+ free(tmp_range_str);
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ free(tmp_range_str);
+ return tmp;
+
+ err:
+ apol_mls_range_destroy(&arange);
+ free(tmp);
+ errno = error;
+ return NULL;
+}
diff --git a/libapol/src/rbacrule-query.c b/libapol/src/rbacrule-query.c
new file mode 100644
index 0000000..81b0c53
--- /dev/null
+++ b/libapol/src/rbacrule-query.c
@@ -0,0 +1,417 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about type enforcement
+ * rules within a policy. The caller obtains a query object, fills in
+ * its parameters, and then runs the query; it obtains a vector of
+ * results. Searches are conjunctive -- all fields of the search
+ * query must match for a datum to be added to the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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>
+
+struct apol_role_allow_query
+{
+ char *source, *target;
+ unsigned int flags;
+};
+
+struct apol_role_trans_query
+{
+ char *source, *target, *default_role;
+ unsigned int flags;
+};
+
+/******************** (role) allow queries ********************/
+
+int apol_role_allow_get_by_query(const apol_policy_t * p, const apol_role_allow_query_t * r, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *source_list = NULL, *target_list = NULL;
+ int retval = -1, source_as_any = 0;
+ *v = NULL;
+
+ if (r != NULL) {
+ if (r->source != NULL &&
+ (source_list = apol_query_create_candidate_role_list(p, r->source, r->flags & APOL_QUERY_REGEX)) == NULL) {
+ goto cleanup;
+ }
+ if ((r->flags & APOL_QUERY_SOURCE_AS_ANY) && r->source != NULL) {
+ target_list = source_list;
+ source_as_any = 1;
+ } else if (r->target != NULL &&
+ (target_list = apol_query_create_candidate_role_list(p, r->target, r->flags & APOL_QUERY_REGEX)) == NULL)
+ {
+ goto cleanup;
+ }
+ }
+ if (qpol_policy_get_role_allow_iter(p->p, &iter) < 0) {
+ goto cleanup;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_role_allow_t *rule;
+ int match_source = 0, match_target = 0;
+ size_t i;
+ if (qpol_iterator_get_item(iter, (void **)&rule) < 0) {
+ goto cleanup;
+ }
+
+ if (source_list == NULL) {
+ match_source = 1;
+ } else {
+ const qpol_role_t *source_role;
+ if (qpol_role_allow_get_source_role(p->p, rule, &source_role) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(source_list, source_role, NULL, NULL, &i) == 0) {
+ match_source = 1;
+ }
+ }
+
+ /* if source did not match, but treating source symbol
+ * as any field, then delay rejecting this rule until
+ * the target has been checked */
+ if (!source_as_any && !match_source) {
+ continue;
+ }
+
+ if (target_list == NULL || (source_as_any && match_source)) {
+ match_target = 1;
+ } else {
+ const qpol_role_t *target_role;
+ if (qpol_role_allow_get_target_role(p->p, rule, &target_role) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(target_list, target_role, NULL, NULL, &i) == 0) {
+ match_target = 1;
+ }
+ }
+ if (!match_target) {
+ continue;
+ }
+
+ if (apol_vector_append(*v, rule)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ apol_vector_destroy(&source_list);
+ if (!source_as_any) {
+ apol_vector_destroy(&target_list);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_role_allow_query_t *apol_role_allow_query_create(void)
+{
+ return calloc(1, sizeof(apol_role_allow_query_t));
+}
+
+void apol_role_allow_query_destroy(apol_role_allow_query_t ** r)
+{
+ if (r != NULL && *r != NULL) {
+ free((*r)->source);
+ free((*r)->target);
+ free(*r);
+ *r = NULL;
+ }
+}
+
+int apol_role_allow_query_set_source(const apol_policy_t * p, apol_role_allow_query_t * r, const char *role)
+{
+ return apol_query_set(p, &r->source, NULL, role);
+}
+
+int apol_role_allow_query_set_target(const apol_policy_t * p, apol_role_allow_query_t * r, const char *role)
+{
+ return apol_query_set(p, &r->target, NULL, role);
+}
+
+int apol_role_allow_query_set_source_any(const apol_policy_t * p, apol_role_allow_query_t * r, int is_any)
+{
+ return apol_query_set_flag(p, &r->flags, is_any, APOL_QUERY_SOURCE_AS_ANY);
+}
+
+int apol_role_allow_query_set_regex(const apol_policy_t * p, apol_role_allow_query_t * r, int is_regex)
+{
+ return apol_query_set_regex(p, &r->flags, is_regex);
+}
+
+char *apol_role_allow_render(const apol_policy_t * policy, const qpol_role_allow_t * rule)
+{
+ char *tmp = NULL;
+ const char *source_name = NULL, *target_name = NULL;
+ const qpol_role_t *role = NULL;
+
+ if (!policy || !rule) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* source role */
+ if (qpol_role_allow_get_source_role(policy->p, rule, &role)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+ if (qpol_role_get_name(policy->p, role, &source_name)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+
+ /* target role */
+ if (qpol_role_allow_get_target_role(policy->p, rule, &role)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+ if (qpol_role_get_name(policy->p, role, &target_name)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+
+ if (asprintf(&tmp, "allow %s %s;", source_name, target_name) < 0) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+
+ return tmp;
+}
+
+/******************** role_transition queries ********************/
+
+int apol_role_trans_get_by_query(const apol_policy_t * p, const apol_role_trans_query_t * r, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *source_list = NULL, *target_list = NULL, *default_list = NULL;
+ int retval = -1, source_as_any = 0;
+ *v = NULL;
+
+ if (r != NULL) {
+ if (r->source != NULL &&
+ (source_list = apol_query_create_candidate_role_list(p, r->source, r->flags & APOL_QUERY_REGEX)) == NULL) {
+ goto cleanup;
+ }
+ if (r->target != NULL &&
+ (target_list =
+ apol_query_create_candidate_type_list(p, r->target, r->flags & APOL_QUERY_REGEX,
+ r->flags & APOL_QUERY_TARGET_INDIRECT,
+ APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) {
+ goto cleanup;
+ }
+ if ((r->flags & APOL_QUERY_SOURCE_AS_ANY) && r->source != NULL) {
+ default_list = source_list;
+ source_as_any = 1;
+ } else if (r->default_role != NULL &&
+ (default_list =
+ apol_query_create_candidate_role_list(p, r->default_role, r->flags & APOL_QUERY_REGEX)) == NULL) {
+ goto cleanup;
+ }
+ }
+ if (qpol_policy_get_role_trans_iter(p->p, &iter) < 0) {
+ goto cleanup;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_role_trans_t *rule;
+ int match_source = 0, match_target = 0, match_default = 0;
+ size_t i;
+ if (qpol_iterator_get_item(iter, (void **)&rule) < 0) {
+ goto cleanup;
+ }
+
+ if (source_list == NULL) {
+ match_source = 1;
+ } else {
+ const qpol_role_t *source_role;
+ if (qpol_role_trans_get_source_role(p->p, rule, &source_role) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(source_list, source_role, NULL, NULL, &i) == 0) {
+ match_source = 1;
+ }
+ }
+
+ /* if source did not match, but treating source symbol
+ * as any field, then delay rejecting this rule until
+ * the target and default have been checked */
+ if (!source_as_any && !match_source) {
+ continue;
+ }
+
+ if (target_list == NULL) {
+ match_target = 1;
+ } else {
+ const qpol_type_t *target_type;
+ if (qpol_role_trans_get_target_type(p->p, rule, &target_type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(target_list, target_type, NULL, NULL, &i) == 0) {
+ match_target = 1;
+ }
+ }
+ if (!match_target) {
+ continue;
+ }
+
+ if (default_list == NULL || (source_as_any && match_source)) {
+ match_default = 1;
+ } else {
+ const qpol_role_t *default_role;
+ if (qpol_role_trans_get_default_role(p->p, rule, &default_role) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(default_list, default_role, NULL, NULL, &i) == 0) {
+ match_default = 1;
+ }
+ }
+ if (!match_default) {
+ continue;
+ }
+
+ if (apol_vector_append(*v, rule)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ apol_vector_destroy(&source_list);
+ apol_vector_destroy(&target_list);
+ if (!source_as_any) {
+ apol_vector_destroy(&default_list);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_role_trans_query_t *apol_role_trans_query_create(void)
+{
+ return calloc(1, sizeof(apol_role_trans_query_t));
+}
+
+void apol_role_trans_query_destroy(apol_role_trans_query_t ** r)
+{
+ if (r != NULL && *r != NULL) {
+ free((*r)->source);
+ free((*r)->target);
+ free((*r)->default_role);
+ free(*r);
+ *r = NULL;
+ }
+}
+
+int apol_role_trans_query_set_source(const apol_policy_t * p, apol_role_trans_query_t * r, const char *role)
+{
+ return apol_query_set(p, &r->source, NULL, role);
+}
+
+int apol_role_trans_query_set_target(const apol_policy_t * p, apol_role_trans_query_t * r, const char *type, int is_indirect)
+{
+ apol_query_set_flag(p, &r->flags, is_indirect, APOL_QUERY_TARGET_INDIRECT);
+ return apol_query_set(p, &r->target, NULL, type);
+}
+
+int apol_role_trans_query_set_default(const apol_policy_t * p, apol_role_trans_query_t * r, const char *role)
+{
+ return apol_query_set(p, &r->default_role, NULL, role);
+}
+
+int apol_role_trans_query_set_source_any(const apol_policy_t * p, apol_role_trans_query_t * r, int is_any)
+{
+ return apol_query_set_flag(p, &r->flags, is_any, APOL_QUERY_SOURCE_AS_ANY);
+}
+
+int apol_role_trans_query_set_regex(const apol_policy_t * p, apol_role_trans_query_t * r, int is_regex)
+{
+ return apol_query_set_regex(p, &r->flags, is_regex);
+}
+
+char *apol_role_trans_render(const apol_policy_t * policy, const qpol_role_trans_t * rule)
+{
+ char *tmp = NULL;
+ const char *source_name = NULL, *target_name = NULL, *default_name = NULL;
+ const qpol_role_t *role = NULL;
+ const qpol_type_t *type = NULL;
+
+ if (!policy || !rule) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* source role */
+ if (qpol_role_trans_get_source_role(policy->p, rule, &role)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+ if (qpol_role_get_name(policy->p, role, &source_name)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+
+ /* target type */
+ if (qpol_role_trans_get_target_type(policy->p, rule, &type)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+ if (qpol_type_get_name(policy->p, type, &target_name)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+
+ /* default role */
+ if (qpol_role_trans_get_default_role(policy->p, rule, &role)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+ if (qpol_role_get_name(policy->p, role, &default_name)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+
+ if (asprintf(&tmp, "role_transition %s %s %s;", source_name, target_name, default_name) < 0) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+ return tmp;
+}
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;
+}
diff --git a/libapol/src/render.c b/libapol/src/render.c
new file mode 100644
index 0000000..4bc19b6
--- /dev/null
+++ b/libapol/src/render.c
@@ -0,0 +1,158 @@
+/**
+ * @file
+ *
+ * Routines to render various data structures used by libapol.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2003-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 <config.h>
+
+#include <apol/context-query.h>
+#include <apol/policy.h>
+#include <apol/render.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifndef WORDS_BIGENDIAN
+extern void swab(const void *from, void *to, ssize_t n);
+#endif
+
+#if LINK_SHARED == 1
+__asm__(".symver apol_ipv4_addr_render_old,apol_ipv4_addr_render@");
+__asm__(".symver apol_ipv4_addr_render_new,apol_ipv4_addr_render@@VERS_4.1");
+#endif
+
+/**
+ * @brief Internal version of apol_ipv4_addr_render() version 4.1
+ *
+ * Implementation of the exported function apol_ipv4_addr_render()
+ * for version 4.1; this symbol name is not exported.
+ */
+char *apol_ipv4_addr_render_new(const apol_policy_t * policydb, uint32_t addr[4])
+{
+ char buf[40], *b;
+ unsigned char *p = (unsigned char *)&(addr[0]);
+ snprintf(buf, sizeof(buf), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+ if ((b = strdup(buf)) == NULL) {
+ ERR(policydb, "%s", strerror(ENOMEM));
+ }
+ return b;
+}
+
+#if LINK_SHARED == 0
+char *apol_ipv4_addr_render(const apol_policy_t * policydb, uint32_t addr[4])
+{
+ return apol_ipv4_addr_render_new(policydb, addr);
+}
+#endif
+
+/**
+ * @brief Internal version of apol_ipv4_addr_render() version 4.0 or earlier
+ * @deprecated use the 4.1 version.
+ * @see apol_ipv4_addr_render()
+ */
+char *apol_ipv4_addr_render_old(apol_policy_t * policydb, uint32_t addr)
+{
+ char buf[40], *b;
+ unsigned char *p = (unsigned char *)&addr;
+ snprintf(buf, sizeof(buf), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+ if ((b = strdup(buf)) == NULL) {
+ ERR(policydb, "%s", strerror(ENOMEM));
+ }
+ return b;
+}
+
+char *apol_ipv6_addr_render(const apol_policy_t * policydb, uint32_t addr[4])
+{
+ uint16_t tmp[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ int i, sz = 0, retv;
+ char buf[40], *b; /* 8 * 4 hex digits + 7 * ':' + '\0' == max size of string */
+ int contract = 0, prev_contr = 0, contr_idx_end = -1;
+ for (i = 0; i < 4; i++) {
+ uint32_t a;
+#ifdef WORDS_BIGENDIAN
+ a = addr[i];
+#else
+ swab(addr + i, &a, sizeof(a));
+#endif
+ /* have to use division and mod here, so as to ignore
+ * host system's byte ordering */
+ tmp[2 * i] = a % (1 << 16);
+ tmp[2 * i + 1] = a / (1 << 16);
+ }
+
+ for (i = 0; i < 8; i++) {
+ if (tmp[i] == 0) {
+ contract++;
+ if (i == 7 && contr_idx_end == -1)
+ contr_idx_end = 8;
+ } else {
+ if (contract > prev_contr) {
+ contr_idx_end = i;
+ }
+ prev_contr = contract;
+ contract = 0;
+ }
+ }
+
+ if (prev_contr > contract)
+ contract = prev_contr;
+
+ for (i = 0; i < 8; i++) {
+ if (i == contr_idx_end - contract) {
+ retv = snprintf(buf + sz, 40 - sz, i ? ":" : "::");
+ sz += retv;
+ } else if (i > contr_idx_end - contract && i < contr_idx_end) {
+ continue;
+ } else {
+ retv = snprintf(buf + sz, 40 - sz, i == 7 ? "%04x" : "%04x:", tmp[i]);
+ sz += retv;
+ }
+ }
+
+ buf[sz] = '\0';
+ if ((b = strdup(buf)) == NULL) {
+ ERR(policydb, "%s", strerror(ENOMEM));
+ }
+ return b;
+}
+
+char *apol_qpol_context_render(const apol_policy_t * p, const qpol_context_t * context)
+{
+ apol_context_t *c = NULL;
+ char *rendered_context;
+
+ if (p == NULL || context == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((c = apol_context_create_from_qpol_context(p, context)) == NULL) {
+ return NULL;
+ }
+ rendered_context = apol_context_render(p, c);
+ apol_context_destroy(&c);
+ return rendered_context;
+}
diff --git a/libapol/src/role-query.c b/libapol/src/role-query.c
new file mode 100644
index 0000000..aaafe65
--- /dev/null
+++ b/libapol/src/role-query.c
@@ -0,0 +1,167 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about roles within a
+ * policy. The caller obtains a query object, fills in its
+ * parameters, and then runs the query; it obtains a vector of
+ * results. Searches are conjunctive -- all fields of the search
+ * query must match for a datum to be added to the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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>
+
+struct apol_role_query
+{
+ char *role_name, *type_name;
+ unsigned int flags;
+ regex_t *role_regex, *type_regex;
+};
+
+/******************** role queries ********************/
+
+int apol_role_get_by_query(const apol_policy_t * p, apol_role_query_t * r, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL, *type_iter = NULL;
+ int retval = -1, append_role;
+ *v = NULL;
+ if (qpol_policy_get_role_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_role_t *role;
+ if (qpol_iterator_get_item(iter, (void **)&role) < 0) {
+ goto cleanup;
+ }
+ append_role = 1;
+ if (r != NULL) {
+ const char *role_name;
+ int compval;
+ if (qpol_role_get_name(p->p, role, &role_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, role_name, r->role_name, r->flags, &(r->role_regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ if (r->type_name == NULL || r->type_name[0] == '\0') {
+ goto end_of_query;
+ }
+ if (qpol_role_get_type_iter(p->p, role, &type_iter) < 0) {
+ goto cleanup;
+ }
+ append_role = 0;
+ for (; !qpol_iterator_end(type_iter); qpol_iterator_next(type_iter)) {
+ qpol_type_t *type;
+ if (qpol_iterator_get_item(type_iter, (void **)&type) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare_type(p, type, r->type_name, r->flags, &(r->type_regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 1) {
+ append_role = 1;
+ break;
+ }
+ }
+ qpol_iterator_destroy(&type_iter);
+ }
+ end_of_query:
+ if (append_role && apol_vector_append(*v, role)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&type_iter);
+ return retval;
+}
+
+apol_role_query_t *apol_role_query_create(void)
+{
+ return calloc(1, sizeof(apol_role_query_t));
+}
+
+void apol_role_query_destroy(apol_role_query_t ** r)
+{
+ if (*r != NULL) {
+ free((*r)->role_name);
+ free((*r)->type_name);
+ apol_regex_destroy(&(*r)->role_regex);
+ apol_regex_destroy(&(*r)->type_regex);
+ free(*r);
+ *r = NULL;
+ }
+}
+
+int apol_role_query_set_role(const apol_policy_t * p, apol_role_query_t * r, const char *name)
+{
+ return apol_query_set(p, &r->role_name, &r->role_regex, name);
+}
+
+int apol_role_query_set_type(const apol_policy_t * p, apol_role_query_t * r, const char *name)
+{
+ return apol_query_set(p, &r->type_name, &r->type_regex, name);
+}
+
+int apol_role_query_set_regex(const apol_policy_t * p, apol_role_query_t * r, int is_regex)
+{
+ return apol_query_set_regex(p, &r->flags, is_regex);
+}
+
+int apol_role_has_type(const apol_policy_t * p, const qpol_role_t * r, const qpol_type_t * t)
+{
+ qpol_iterator_t *iter = NULL;
+ qpol_type_t *tmp_type;
+ uint32_t type_value, t_type_value;
+ int retval = -1;
+
+ if (qpol_type_get_value(p->p, t, &t_type_value) < 0 || qpol_role_get_type_iter(p->p, r, &iter) < 0) {
+ goto cleanup;
+ }
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)(&tmp_type));
+ qpol_type_get_value(p->p, tmp_type, &type_value);
+ if (t_type_value == type_value) {
+ retval = 1;
+ goto cleanup;
+ }
+ }
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
diff --git a/libapol/src/terule-query.c b/libapol/src/terule-query.c
new file mode 100644
index 0000000..80a7eff
--- /dev/null
+++ b/libapol/src/terule-query.c
@@ -0,0 +1,1049 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about type enforcement
+ * rules within a policy. The caller obtains a query object, fills in
+ * its parameters, and then runs the query; it obtains a vector of
+ * results. Searches are conjunctive -- all fields of the search
+ * query must match for a datum to be added to the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2008 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 <apol/bst.h>
+#include <qpol/policy_extend.h>
+#include <errno.h>
+#include <string.h>
+
+struct apol_terule_query
+{
+ char *source, *target, *default_type, *bool_name;
+ apol_vector_t *classes;
+ unsigned int rules;
+ unsigned int flags;
+};
+
+/**
+ * Common semantic rule selection routine used in get*rule_by_query.
+ * @param p Policy to search.
+ * @param v Vector of rules to populate (of type qpol_terule_t).
+ * @param rule_type Mask of rules to search.
+ * @param flags Query options as specified by the apol_terule_query.
+ * @param source_list If non-NULL, list of types to use as source.
+ * If NULL, accept all types.
+ * @param target_list If non-NULL, list of types to use as target.
+ * If NULL, accept all types.
+ * @param class_list If non-NULL, list of classes to use.
+ * If NULL, accept all classes.
+ * @param default_list If non-NULL, list of types to use as default.
+ * If NULL, accept all types.
+ * @param bool_name If non-NULL, find conditional rules affected by this boolean.
+ * If NULL, all rules will be considered (including unconditional rules).
+ * @return 0 on success and < 0 on failure.
+ */
+static int rule_select(const apol_policy_t * p, apol_vector_t * v, uint32_t rule_type, unsigned int flags,
+ const apol_vector_t * source_list, const apol_vector_t * target_list, const apol_vector_t * class_list,
+ const apol_vector_t * default_list, const char *bool_name)
+{
+ qpol_iterator_t *iter = NULL;
+ int only_enabled = flags & APOL_QUERY_ONLY_ENABLED;
+ int is_regex = flags & APOL_QUERY_REGEX;
+ int source_as_any = flags & APOL_QUERY_SOURCE_AS_ANY;
+ int retv = -1;
+ regex_t *bool_regex = NULL;
+
+ if (qpol_policy_get_terule_iter(p->p, rule_type, &iter) < 0) {
+ goto cleanup;
+ }
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_terule_t *rule;
+ uint32_t is_enabled;
+ const qpol_cond_t *cond = NULL;
+ int match_source = 0, match_target = 0, match_default = 0, match_bool = 0;
+ size_t i;
+ if (qpol_iterator_get_item(iter, (void **)&rule) < 0) {
+ goto cleanup;
+ }
+
+ if (qpol_terule_get_is_enabled(p->p, rule, &is_enabled) < 0) {
+ goto cleanup;
+ }
+ if (!is_enabled && only_enabled) {
+ continue;
+ }
+
+ if (bool_name != NULL) {
+ if (qpol_terule_get_cond(p->p, rule, &cond) < 0) {
+ goto cleanup;
+ }
+ if (cond == NULL) {
+ continue; /* skip unconditional rule */
+ }
+ match_bool = apol_compare_cond_expr(p, cond, bool_name, is_regex, &bool_regex);
+ if (match_bool < 0) {
+ goto cleanup;
+ } else if (match_bool == 0) {
+ continue;
+ }
+ }
+
+ if (source_list == NULL) {
+ match_source = 1;
+ } else {
+ const qpol_type_t *source_type;
+ if (qpol_terule_get_source_type(p->p, rule, &source_type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(source_list, source_type, NULL, NULL, &i) == 0) {
+ match_source = 1;
+ }
+ }
+
+ /* if source did not match, but treating source symbol
+ * as any field, then delay rejecting this rule until
+ * the target and default have been checked */
+ if (!source_as_any && !match_source) {
+ continue;
+ }
+
+ if (target_list == NULL || (source_as_any && match_source)) {
+ match_target = 1;
+ } else {
+ const qpol_type_t *target_type;
+ if (qpol_terule_get_target_type(p->p, rule, &target_type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(target_list, target_type, NULL, NULL, &i) == 0) {
+ match_target = 1;
+ }
+ }
+
+ if (!source_as_any && !match_target) {
+ continue;
+ }
+
+ if (default_list == NULL || (source_as_any && match_source) || (source_as_any && match_target)) {
+ match_default = 1;
+ } else {
+ const qpol_type_t *default_type;
+ if (qpol_terule_get_default_type(p->p, rule, &default_type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(default_list, default_type, NULL, NULL, &i) == 0) {
+ match_default = 1;
+ }
+ }
+
+ if (!source_as_any && !match_default) {
+ continue;
+ }
+ /* at least one thing must match if source_as_any was given */
+ if (source_as_any && (!match_source && !match_target && !match_default)) {
+ continue;
+ }
+
+ if (class_list != NULL) {
+ const qpol_class_t *obj_class;
+ if (qpol_terule_get_object_class(p->p, rule, &obj_class) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(class_list, obj_class, NULL, NULL, &i) < 0) {
+ continue;
+ }
+ }
+
+ if (apol_vector_append(v, rule)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retv = 0;
+
+ cleanup:
+ apol_regex_destroy(&bool_regex);
+ qpol_iterator_destroy(&iter);
+ return retv;
+}
+
+int apol_terule_get_by_query(const apol_policy_t * p, const apol_terule_query_t * t, apol_vector_t ** v)
+{
+ apol_vector_t *source_list = NULL, *target_list = NULL, *class_list = NULL, *default_list = NULL;
+ int retval = -1, source_as_any = 0, is_regex = 0;
+ char *bool_name = NULL;
+ *v = NULL;
+ unsigned int flags = 0;
+
+ uint32_t rule_type = QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_MEMBER | QPOL_RULE_TYPE_CHANGE;
+ if (t != NULL) {
+ if (t->rules != 0) {
+ rule_type &= t->rules;
+ }
+ flags = t->flags;
+ is_regex = t->flags & APOL_QUERY_REGEX;
+ bool_name = t->bool_name;
+ if (t->source != NULL &&
+ (source_list =
+ apol_query_create_candidate_type_list(p, t->source, is_regex,
+ t->flags & APOL_QUERY_SOURCE_INDIRECT,
+ ((t->flags & (APOL_QUERY_SOURCE_TYPE | APOL_QUERY_SOURCE_ATTRIBUTE)) /
+ APOL_QUERY_SOURCE_TYPE))) == NULL) {
+ goto cleanup;
+ }
+ if ((t->flags & APOL_QUERY_SOURCE_AS_ANY) && t->source != NULL) {
+ default_list = target_list = source_list;
+ source_as_any = 1;
+ } else {
+ if (t->target != NULL &&
+ (target_list =
+ apol_query_create_candidate_type_list(p, t->target, is_regex,
+ t->flags & APOL_QUERY_TARGET_INDIRECT,
+ ((t->
+ flags & (APOL_QUERY_TARGET_TYPE | APOL_QUERY_TARGET_ATTRIBUTE))
+ / APOL_QUERY_TARGET_TYPE))) == NULL) {
+ goto cleanup;
+ }
+ if (t->default_type != NULL &&
+ (default_list =
+ apol_query_create_candidate_type_list(p, t->default_type, is_regex, 0,
+ APOL_QUERY_SYMBOL_IS_TYPE)) == NULL) {
+ goto cleanup;
+ }
+ }
+ if (t->classes != NULL &&
+ apol_vector_get_size(t->classes) > 0 &&
+ (class_list = apol_query_create_candidate_class_list(p, t->classes)) == NULL) {
+ goto cleanup;
+ }
+ }
+
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ if (rule_select(p, *v, rule_type, flags, source_list, target_list, class_list, default_list, bool_name)) {
+ goto cleanup;
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ apol_vector_destroy(&source_list);
+ if (!source_as_any) {
+ apol_vector_destroy(&target_list);
+ apol_vector_destroy(&default_list);
+ }
+ apol_vector_destroy(&class_list);
+ return retval;
+}
+
+int apol_syn_terule_get_by_query(const apol_policy_t * p, const apol_terule_query_t * t, apol_vector_t ** v)
+{
+ apol_vector_t *source_list = NULL, *target_list = NULL, *class_list = NULL, *default_list = NULL, *syn_v = NULL;
+ int retval = -1, source_as_any = 0, is_regex = 0;
+ char *bool_name = NULL;
+ *v = NULL;
+ size_t i;
+ unsigned int flags = 0;
+
+ if (!p || !qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_SYN_RULES)) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+
+ uint32_t rule_type = QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_MEMBER | QPOL_RULE_TYPE_CHANGE;
+ if (t != NULL) {
+ if (t->rules != 0) {
+ rule_type &= t->rules;
+ }
+ flags = t->flags;
+ is_regex = t->flags & APOL_QUERY_REGEX;
+ bool_name = t->bool_name;
+ if (t->source != NULL &&
+ (source_list =
+ apol_query_create_candidate_syn_type_list(p, t->source, is_regex,
+ t->flags & APOL_QUERY_SOURCE_INDIRECT,
+ ((t->flags & (APOL_QUERY_SOURCE_TYPE |
+ APOL_QUERY_SOURCE_ATTRIBUTE)) /
+ APOL_QUERY_SOURCE_TYPE))) == NULL) {
+ goto cleanup;
+ }
+ if ((t->flags & APOL_QUERY_SOURCE_AS_ANY) && t->source != NULL) {
+ default_list = target_list = source_list;
+ source_as_any = 1;
+ } else {
+ if (t->target != NULL &&
+ (target_list =
+ apol_query_create_candidate_syn_type_list(p, t->target, is_regex,
+ t->flags & APOL_QUERY_TARGET_INDIRECT,
+ ((t->flags & (APOL_QUERY_TARGET_TYPE |
+ APOL_QUERY_TARGET_ATTRIBUTE))
+ / APOL_QUERY_TARGET_TYPE))) == NULL) {
+ goto cleanup;
+ }
+ if (t->default_type != NULL &&
+ (default_list =
+ apol_query_create_candidate_type_list(p, t->default_type, is_regex, 0,
+ APOL_QUERY_SYMBOL_IS_TYPE)) == NULL) {
+ goto cleanup;
+ }
+ }
+ if (t->classes != NULL &&
+ apol_vector_get_size(t->classes) > 0 &&
+ (class_list = apol_query_create_candidate_class_list(p, t->classes)) == NULL) {
+ goto cleanup;
+ }
+ }
+
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ if (rule_select(p, *v, rule_type, flags, source_list, target_list, class_list, default_list, bool_name)) {
+ goto cleanup;
+ }
+
+ syn_v = apol_terule_list_to_syn_terules(p, *v);
+ if (!syn_v) {
+ goto cleanup;
+ }
+ apol_vector_destroy(v);
+ *v = syn_v;
+ syn_v = NULL;
+
+ /* if source and target are indirect skip post filtering type sets */
+ if ((t->flags & APOL_QUERY_SOURCE_INDIRECT) && (t->flags & (APOL_QUERY_TARGET_INDIRECT | APOL_QUERY_SOURCE_AS_ANY))) {
+ retval = 0;
+ goto cleanup;
+ }
+ /* if not searching by source, target, or default we are done */
+ if (!source_list && !target_list && !default_list) {
+ retval = 0;
+ goto cleanup;
+ }
+
+ if (source_list && !(t->flags & APOL_QUERY_SOURCE_INDIRECT)) {
+ apol_vector_destroy(&source_list);
+ source_list =
+ apol_query_create_candidate_type_list(p, t->source, is_regex, 0,
+ ((t->flags & (APOL_QUERY_SOURCE_TYPE | APOL_QUERY_SOURCE_ATTRIBUTE)) /
+ APOL_QUERY_SOURCE_TYPE));
+ if (!source_list)
+ goto cleanup;
+ }
+ if (target_list && (source_as_any || !(t->flags & APOL_QUERY_TARGET_INDIRECT))) {
+ if (source_as_any) {
+ target_list = source_list;
+ } else {
+ apol_vector_destroy(&target_list);
+ target_list =
+ apol_query_create_candidate_type_list(p, t->target, is_regex, 0,
+ ((t->flags & (APOL_QUERY_SOURCE_TYPE |
+ APOL_QUERY_SOURCE_ATTRIBUTE)) /
+ APOL_QUERY_SOURCE_TYPE));
+ if (!target_list)
+ goto cleanup;
+ }
+ }
+ if (source_as_any) {
+ default_list = source_list;
+ }
+
+ for (i = 0; i < apol_vector_get_size(*v); i++) {
+ qpol_syn_terule_t *srule = apol_vector_get_element(*v, i);
+ const qpol_type_set_t *stypes = NULL, *ttypes = NULL;
+ const qpol_type_t *dflt = NULL;
+ size_t j;
+ int uses_source = 0, uses_target = 0, uses_default = 0;
+ qpol_syn_terule_get_source_type_set(p->p, srule, &stypes);
+ qpol_syn_terule_get_target_type_set(p->p, srule, &ttypes);
+ if (source_list && !(t->flags & APOL_QUERY_SOURCE_INDIRECT)) {
+ uses_source = apol_query_type_set_uses_types_directly(p, stypes, source_list);
+ if (uses_source < 0)
+ goto cleanup;
+ } else if (source_list && (t->flags & APOL_QUERY_SOURCE_INDIRECT)) {
+ uses_source = 1;
+ } else if (!source_list) {
+ uses_source = 1;
+ }
+
+ if (target_list
+ && !(t->flags & APOL_QUERY_TARGET_INDIRECT || (source_as_any && t->flags & APOL_QUERY_SOURCE_INDIRECT))) {
+ uses_target = apol_query_type_set_uses_types_directly(p, ttypes, target_list);
+ if (uses_target < 0)
+ goto cleanup;
+ } else if (target_list
+ && (t->flags & APOL_QUERY_TARGET_INDIRECT || (source_as_any && t->flags & APOL_QUERY_SOURCE_INDIRECT))) {
+ uses_target = 1;
+ } else if (!target_list) {
+ uses_target = 1;
+ }
+
+ if (default_list) {
+ qpol_syn_terule_get_default_type(p->p, srule, &dflt);
+ if (!apol_vector_get_index(default_list, (void *)dflt, NULL, NULL, &j))
+ uses_default = 1;
+ } else if (!default_list) {
+ uses_default = 1;
+ }
+
+ if (!((uses_source && uses_target && uses_default)
+ || (source_as_any && (uses_source || uses_target || uses_default)))) {
+ apol_vector_remove(*v, i);
+ i--;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ apol_vector_destroy(&syn_v);
+ apol_vector_destroy(&source_list);
+ if (!source_as_any) {
+ apol_vector_destroy(&target_list);
+ apol_vector_destroy(&default_list);
+ }
+ apol_vector_destroy(&class_list);
+ return retval;
+}
+
+apol_terule_query_t *apol_terule_query_create(void)
+{
+ apol_terule_query_t *t = calloc(1, sizeof(apol_terule_query_t));
+ if (t != NULL) {
+ t->rules = ~0U;
+ t->flags =
+ (APOL_QUERY_SOURCE_TYPE | APOL_QUERY_SOURCE_ATTRIBUTE | APOL_QUERY_TARGET_TYPE |
+ APOL_QUERY_TARGET_ATTRIBUTE);
+ }
+ return t;
+}
+
+void apol_terule_query_destroy(apol_terule_query_t ** t)
+{
+ if (*t != NULL) {
+ free((*t)->source);
+ free((*t)->target);
+ free((*t)->default_type);
+ free((*t)->bool_name);
+ apol_vector_destroy(&(*t)->classes);
+ free(*t);
+ *t = NULL;
+ }
+}
+
+int apol_terule_query_set_rules(const apol_policy_t * p __attribute__ ((unused)), apol_terule_query_t * t, unsigned int rules)
+{
+ if (rules != 0) {
+ t->rules = rules;
+ } else {
+ t->rules = ~0U;
+ }
+ return 0;
+}
+
+int apol_terule_query_set_source(const apol_policy_t * p, apol_terule_query_t * t, const char *symbol, int is_indirect)
+{
+ apol_query_set_flag(p, &t->flags, is_indirect, APOL_QUERY_SOURCE_INDIRECT);
+ return apol_query_set(p, &t->source, NULL, symbol);
+}
+
+int apol_terule_query_set_source_component(const apol_policy_t * p, apol_terule_query_t * t, unsigned int component)
+{
+ if (!t || !(component & APOL_QUERY_SYMBOL_IS_BOTH)) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ apol_query_set_flag(p, &t->flags, component & APOL_QUERY_SYMBOL_IS_TYPE, APOL_QUERY_SOURCE_TYPE);
+ apol_query_set_flag(p, &t->flags, component & APOL_QUERY_SYMBOL_IS_ATTRIBUTE, APOL_QUERY_SOURCE_ATTRIBUTE);
+ return 0;
+}
+
+int apol_terule_query_set_target(const apol_policy_t * p, apol_terule_query_t * t, const char *symbol, int is_indirect)
+{
+ apol_query_set_flag(p, &t->flags, is_indirect, APOL_QUERY_TARGET_INDIRECT);
+ return apol_query_set(p, &t->target, NULL, symbol);
+}
+
+int apol_terule_query_set_target_component(const apol_policy_t * p, apol_terule_query_t * t, unsigned int component)
+{
+ if (!t || !(component & APOL_QUERY_SYMBOL_IS_BOTH)) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ apol_query_set_flag(p, &t->flags, component & APOL_QUERY_SYMBOL_IS_TYPE, APOL_QUERY_TARGET_TYPE);
+ apol_query_set_flag(p, &t->flags, component & APOL_QUERY_SYMBOL_IS_ATTRIBUTE, APOL_QUERY_TARGET_ATTRIBUTE);
+ return 0;
+}
+
+int apol_terule_query_set_default(const apol_policy_t * p, apol_terule_query_t * t, const char *symbol)
+{
+ return apol_query_set(p, &t->default_type, NULL, symbol);
+}
+
+int apol_terule_query_append_class(const apol_policy_t * p, apol_terule_query_t * t, const char *obj_class)
+{
+ char *s = NULL;
+ if (obj_class == NULL) {
+ apol_vector_destroy(&t->classes);
+ } else if ((s = strdup(obj_class)) == NULL || (t->classes == NULL && (t->classes = apol_vector_create(free)) == NULL)
+ || apol_vector_append(t->classes, s) < 0) {
+ ERR(p, "%s", strerror(errno));
+ free(s);
+ return -1;
+ }
+ return 0;
+}
+
+int apol_terule_query_set_bool(const apol_policy_t * p, apol_terule_query_t * t, const char *bool_name)
+{
+ return apol_query_set(p, &t->bool_name, NULL, bool_name);
+}
+
+int apol_terule_query_set_enabled(const apol_policy_t * p, apol_terule_query_t * t, int is_enabled)
+{
+ return apol_query_set_flag(p, &t->flags, is_enabled, APOL_QUERY_ONLY_ENABLED);
+}
+
+int apol_terule_query_set_source_any(const apol_policy_t * p, apol_terule_query_t * t, int is_any)
+{
+ return apol_query_set_flag(p, &t->flags, is_any, APOL_QUERY_SOURCE_AS_ANY);
+}
+
+int apol_terule_query_set_regex(const apol_policy_t * p, apol_terule_query_t * t, int is_regex)
+{
+ return apol_query_set_regex(p, &t->flags, is_regex);
+}
+
+/**
+ * Comparison function for two syntactic terules. Will return -1 if
+ * a's line number is before b's, 1 if b is greater.
+ */
+static int apol_syn_terule_comp(const void *a, const void *b, void *data)
+{
+ qpol_syn_terule_t *r1 = (qpol_syn_terule_t *) a;
+ qpol_syn_terule_t *r2 = (qpol_syn_terule_t *) b;
+ apol_policy_t *p = (apol_policy_t *) data;
+ unsigned long num1, num2;
+ if (qpol_syn_terule_get_lineno(p->p, r1, &num1) < 0 || qpol_syn_terule_get_lineno(p->p, r2, &num2) < 0) {
+ return 0;
+ }
+ if (num1 != num2) {
+ return (int)num1 - (int)num2;
+ }
+ return (int)((char *)r1 - (char *)r2);
+}
+
+apol_vector_t *apol_terule_to_syn_terules(const apol_policy_t * p, const qpol_terule_t * rule)
+{
+ apol_vector_t *v = NULL;
+ qpol_iterator_t *iter = NULL;
+ qpol_syn_terule_t *syn_terule;
+ int retval = -1, error = 0;
+ if (qpol_terule_get_syn_terule_iter(p->p, rule, &iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&syn_terule) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (apol_vector_append(v, syn_terule) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ apol_vector_sort_uniquify(v, apol_syn_terule_comp, (void *)p);
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ if (retval != 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+apol_vector_t *apol_terule_list_to_syn_terules(const apol_policy_t * p, const apol_vector_t * rules)
+{
+ apol_bst_t *b = NULL;
+ qpol_terule_t *rule;
+ qpol_iterator_t *iter = NULL;
+ qpol_syn_terule_t *syn_terule;
+ apol_vector_t *v = NULL;
+ size_t i;
+ int retval = -1, error = 0;
+
+ if ((b = apol_bst_create(apol_syn_terule_comp, NULL)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(rules); i++) {
+ rule = apol_vector_get_element(rules, i);
+ if (qpol_terule_get_syn_terule_iter(p->p, rule, &iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&syn_terule) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (apol_bst_insert(b, syn_terule, (void *)p) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ if ((v = apol_bst_get_vector(b, 1)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ apol_bst_destroy(&b);
+ qpol_iterator_destroy(&iter);
+ if (retval != 0) {
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+char *apol_terule_render(const apol_policy_t * policy, const qpol_terule_t * rule)
+{
+ char *tmp = NULL;
+ const char *tmp_name = NULL;
+ const char *rule_type_str;
+ int error = 0;
+ size_t tmp_sz = 0;
+ uint32_t rule_type = 0;
+ const qpol_type_t *type = NULL;
+ const qpol_class_t *obj_class = NULL;
+
+ if (!policy || !rule) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* rule type */
+ if (qpol_terule_get_rule_type(policy->p, rule, &rule_type)) {
+ return NULL;
+ }
+ if (!(rule_type &= (QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_CHANGE | QPOL_RULE_TYPE_MEMBER))) {
+ ERR(policy, "%s", "Invalid TE rule type");
+ errno = EINVAL;
+ return NULL;
+ }
+ if (!(rule_type_str = apol_rule_type_to_str(rule_type))) {
+ ERR(policy, "%s", "Could not get TE rule type's string");
+ errno = EINVAL;
+ return NULL;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", rule_type_str)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* source type */
+ if (qpol_terule_get_source_type(policy->p, rule, &type)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* target type */
+ if (qpol_terule_get_target_type(policy->p, rule, &type)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s : ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* object class */
+ if (qpol_terule_get_object_class(policy->p, rule, &obj_class)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_class_get_name(policy->p, obj_class, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* default type */
+ if (qpol_terule_get_default_type(policy->p, rule, &type)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s;", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ return tmp;
+
+ err:
+ free(tmp);
+ errno = error;
+ return NULL;
+}
+
+char *apol_syn_terule_render(const apol_policy_t * policy, const qpol_syn_terule_t * rule)
+{
+ char *tmp = NULL;
+ const char *tmp_name = NULL;
+ const char *rule_type_str;
+ int error = 0;
+ uint32_t rule_type = 0, star = 0, comp = 0;
+ const qpol_type_t *type = NULL;
+ const qpol_class_t *obj_class = NULL;
+ qpol_iterator_t *iter = NULL, *iter2 = NULL;
+ size_t tmp_sz = 0, iter_sz = 0, iter2_sz = 0;
+ const qpol_type_set_t *set = NULL;
+
+ if (!policy || !rule) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* rule type */
+ if (qpol_syn_terule_get_rule_type(policy->p, rule, &rule_type)) {
+ return NULL;
+ }
+ if (!(rule_type &= (QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_CHANGE | QPOL_RULE_TYPE_MEMBER))) {
+ ERR(policy, "%s", "Invalid TE rule type");
+ errno = EINVAL;
+ return NULL;
+ }
+ if (!(rule_type_str = apol_rule_type_to_str(rule_type))) {
+ ERR(policy, "%s", "Could not get TE rule type's string");
+ errno = EINVAL;
+ return NULL;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", rule_type_str)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* source type set */
+ if (qpol_syn_terule_get_source_type_set(policy->p, rule, &set)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_set_get_is_star(policy->p, set, &star)) {
+ error = errno;
+ goto err;
+ }
+ if (star) {
+ if (apol_str_append(&tmp, &tmp_sz, "* ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ } else {
+ if (qpol_type_set_get_is_comp(policy->p, set, &comp)) {
+ error = errno;
+ goto err;
+ }
+ if (comp) {
+ if (apol_str_append(&tmp, &tmp_sz, "~")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ if (qpol_type_set_get_included_types_iter(policy->p, set, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_set_get_subtracted_types_iter(policy->p, set, &iter2)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_iterator_get_size(iter, &iter_sz) || qpol_iterator_get_size(iter2, &iter2_sz)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (iter_sz + iter2_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "{ ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter2); qpol_iterator_next(iter2)) {
+ if (qpol_iterator_get_item(iter2, (void **)&type)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "-%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&iter2);
+ if (iter_sz + iter2_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "} ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ }
+
+ /* target type set */
+ if (qpol_syn_terule_get_target_type_set(policy->p, rule, &set)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_set_get_is_star(policy->p, set, &star)) {
+ error = errno;
+ goto err;
+ }
+ if (star) {
+ if (apol_str_append(&tmp, &tmp_sz, "* ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ } else {
+ if (qpol_type_set_get_is_comp(policy->p, set, &comp)) {
+ error = errno;
+ goto err;
+ }
+ if (comp) {
+ if (apol_str_append(&tmp, &tmp_sz, "~")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ if (qpol_type_set_get_included_types_iter(policy->p, set, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_set_get_subtracted_types_iter(policy->p, set, &iter2)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_iterator_get_size(iter, &iter_sz) || qpol_iterator_get_size(iter2, &iter2_sz)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (iter_sz + iter2_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "{ ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter2); qpol_iterator_next(iter2)) {
+ if (qpol_iterator_get_item(iter2, (void **)&type)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "-%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&iter2);
+ if (iter_sz + iter2_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "} ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ }
+
+ if (apol_str_append(&tmp, &tmp_sz, ": ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* object classes */
+ if (qpol_syn_terule_get_class_iter(policy->p, rule, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_iterator_get_size(iter, &iter_sz)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (iter_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "{ ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&obj_class)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_class_get_name(policy->p, obj_class, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ if (iter_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "} ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+
+ /* default type */
+ if (qpol_syn_terule_get_default_type(policy->p, rule, &type)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s;", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ return tmp;
+
+ err:
+ free(tmp);
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&iter2);
+ errno = error;
+ return NULL;
+}
diff --git a/libapol/src/type-query.c b/libapol/src/type-query.c
new file mode 100644
index 0000000..7ead836
--- /dev/null
+++ b/libapol/src/type-query.c
@@ -0,0 +1,202 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about types and
+ * attributes within a policy. The caller obtains a query object,
+ * fills in its parameters, and then runs the query; it obtains a
+ * vector of results. Searches are conjunctive -- all fields of the
+ * search query must match for a datum to be added to the results
+ * query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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>
+
+struct apol_type_query
+{
+ char *type_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+struct apol_attr_query
+{
+ char *attr_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+/******************** type queries ********************/
+
+int apol_type_get_by_query(const apol_policy_t * p, apol_type_query_t * t, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1;
+ *v = NULL;
+ if (qpol_policy_get_type_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ const qpol_type_t *type;
+ unsigned char isattr, isalias;
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0) {
+ goto cleanup;
+ }
+ if (qpol_type_get_isattr(p->p, type, &isattr) < 0 || qpol_type_get_isalias(p->p, type, &isalias) < 0) {
+ goto cleanup;
+ }
+ if (isattr || isalias) {
+ continue;
+ }
+ if (t != NULL) {
+ int compval = apol_compare_type(p,
+ type, t->type_name,
+ t->flags, &(t->regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, (void *)type)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_type_query_t *apol_type_query_create(void)
+{
+ return calloc(1, sizeof(apol_type_query_t));
+}
+
+void apol_type_query_destroy(apol_type_query_t ** t)
+{
+ if (*t != NULL) {
+ free((*t)->type_name);
+ apol_regex_destroy(&(*t)->regex);
+ free(*t);
+ *t = NULL;
+ }
+}
+
+int apol_type_query_set_type(const apol_policy_t * p, apol_type_query_t * t, const char *name)
+{
+ return apol_query_set(p, &t->type_name, &t->regex, name);
+}
+
+int apol_type_query_set_regex(const apol_policy_t * p, apol_type_query_t * t, int is_regex)
+{
+ return apol_query_set_regex(p, &t->flags, is_regex);
+}
+
+/******************** attribute queries ********************/
+
+int apol_attr_get_by_query(const apol_policy_t * p, apol_attr_query_t * a, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1;
+ *v = NULL;
+ if (qpol_policy_get_type_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_type_t *type;
+ unsigned char isattr, isalias;
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0) {
+ goto cleanup;
+ }
+ if (qpol_type_get_isattr(p->p, type, &isattr) < 0 || qpol_type_get_isalias(p->p, type, &isalias) < 0) {
+ goto cleanup;
+ }
+ if (!isattr || isalias) {
+ continue;
+ }
+ if (a != NULL) {
+ const char *attr_name;
+ int compval;
+ if (qpol_type_get_name(p->p, type, &attr_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, attr_name, a->attr_name, a->flags, &(a->regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, type)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_attr_query_t *apol_attr_query_create(void)
+{
+ return calloc(1, sizeof(apol_attr_query_t));
+}
+
+void apol_attr_query_destroy(apol_attr_query_t ** a)
+{
+ if (*a != NULL) {
+ free((*a)->attr_name);
+ apol_regex_destroy(&(*a)->regex);
+ free(*a);
+ *a = NULL;
+ }
+}
+
+int apol_attr_query_set_attr(const apol_policy_t * p, apol_attr_query_t * a, const char *name)
+{
+ return apol_query_set(p, &a->attr_name, &a->regex, name);
+}
+
+int apol_attr_query_set_regex(const apol_policy_t * p, apol_attr_query_t * a, int is_regex)
+{
+ return apol_query_set_regex(p, &a->flags, is_regex);
+}
diff --git a/libapol/src/types-relation-analysis.c b/libapol/src/types-relation-analysis.c
new file mode 100644
index 0000000..79203d9
--- /dev/null
+++ b/libapol/src/types-relation-analysis.c
@@ -0,0 +1,1143 @@
+/**
+ * @file
+ * Implementation of the two-types relationship 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 "infoflow-analysis-internal.h"
+
+#include <errno.h>
+#include <string.h>
+
+struct apol_types_relation_analysis
+{
+ char *typeA, *typeB;
+ unsigned int analyses;
+};
+
+struct apol_types_relation_result
+{
+ /** vector of qpol_type_t pointers */
+ apol_vector_t *attribs;
+ /** vector of qpol_role_t pointers */
+ apol_vector_t *roles;
+ /** vector of qpol_user_t pointers */
+ apol_vector_t *users;
+ /** vector af apol_types_relation_access, rules that A has in common with B */
+ apol_vector_t *simA;
+ /** vector af apol_types_relation_access, rules that B has in common with A */
+ apol_vector_t *simB;
+ /** vector af apol_types_relation_access, types that A has that B does not */
+ apol_vector_t *disA;
+ /** vector af apol_types_relation_access, types that B has that A does not */
+ apol_vector_t *disB;
+ /** vector of qpol_avrule_t pointers */
+ apol_vector_t *allows;
+ /** vector of qpol_terule_t pointers */
+ apol_vector_t *types;
+ /** vector of apol_infoflow_result_t */
+ apol_vector_t *dirflows;
+ /** vector of apol_infoflow_result_t from type A to B */
+ apol_vector_t *transAB;
+ /** vector of apol_infoflow_result_t from type B to A */
+ apol_vector_t *transBA;
+ /** vector of apol_domain_trans_result_t from type A to B */
+ apol_vector_t *domsAB;
+ /** vector of apol_domain_trans_result_t from type B to A */
+ apol_vector_t *domsBA;
+};
+
+struct apol_types_relation_access
+{
+ const qpol_type_t *type;
+ /** vector of qpol_avrule_t pointers */
+ apol_vector_t *rules;
+};
+
+/******************** actual analysis rountines ********************/
+
+/**
+ * Find the attributes that both typeA and typeB have. Create a
+ * vector of those attributes (as represented as qpol_type_t pointers
+ * relative to the provided policy) and set r->attribs to that vector.
+ *
+ * @param p Policy containing types' information.
+ * @param typeA First type to check.
+ * @param typeB Other type to check.
+ * @param r Result structure to fill.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_common_attribs(const apol_policy_t * p,
+ const qpol_type_t * typeA, const qpol_type_t * typeB,
+ apol_types_relation_result_t * r)
+{
+ qpol_iterator_t *iA = NULL, *iB = NULL;
+ apol_vector_t *vA = NULL, *vB = NULL;
+ int retval = -1;
+
+ if (qpol_type_get_attr_iter(p->p, typeA, &iA) < 0 || qpol_type_get_attr_iter(p->p, typeB, &iB) < 0) {
+ goto cleanup;
+ }
+ if ((vA = apol_vector_create_from_iter(iA, NULL)) == NULL ||
+ (vB = apol_vector_create_from_iter(iB, NULL)) == NULL ||
+ (r->attribs = apol_vector_create_from_intersection(vA, vB, NULL, NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iA);
+ qpol_iterator_destroy(&iB);
+ apol_vector_destroy(&vA);
+ apol_vector_destroy(&vB);
+ return retval;
+}
+
+/**
+ * Find the roles whose allowed types include both typeA and typeB.
+ * Create a vector of those roles (as represented as qpol_role_t
+ * pointers relative to the provided policy) and set r->roles to that
+ * vector.
+ *
+ * @param p Policy containing types' information.
+ * @param typeA First type to check.
+ * @param typeB Other type to check.
+ * @param r Result structure to fill.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_common_roles(const apol_policy_t * p,
+ const qpol_type_t * typeA, const qpol_type_t * typeB, apol_types_relation_result_t * r)
+{
+ const char *nameA, *nameB;
+ apol_role_query_t *rq = NULL;
+ apol_vector_t *vA = NULL, *vB = NULL;
+ int retval = -1;
+
+ if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) {
+ goto cleanup;
+ }
+ if ((rq = apol_role_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_role_query_set_type(p, rq, nameA) < 0 ||
+ apol_role_get_by_query(p, rq, &vA) < 0 ||
+ apol_role_query_set_type(p, rq, nameB) < 0 || apol_role_get_by_query(p, rq, &vB) < 0) {
+ goto cleanup;
+ }
+ if ((r->roles = apol_vector_create_from_intersection(vA, vB, NULL, NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ }
+
+ retval = 0;
+ cleanup:
+ apol_role_query_destroy(&rq);
+ apol_vector_destroy(&vA);
+ apol_vector_destroy(&vB);
+ return retval;
+}
+
+/**
+ * Find the users whose roles have as their allowed types both typeA
+ * and typeB. Create a vector of those users (as represented as
+ * qpol_user_t pointers relative to the provided policy) and set
+ * r->users to that vector.
+ *
+ * @param p Policy containing types' information.
+ * @param typeA First type to check.
+ * @param typeB Other type to check.
+ * @param r Result structure to fill.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_common_users(const apol_policy_t * p,
+ const qpol_type_t * typeA, const qpol_type_t * typeB, apol_types_relation_result_t * r)
+{
+ const char *nameA, *nameB;
+ apol_role_query_t *rq = NULL;
+ apol_vector_t *vA = NULL, *vB = NULL;
+ qpol_iterator_t *iter = NULL, *riter = NULL;
+ int retval = -1;
+
+ if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) {
+ goto cleanup;
+ }
+ if ((rq = apol_role_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_role_query_set_type(p, rq, nameA) < 0 ||
+ apol_role_get_by_query(p, rq, &vA) < 0 ||
+ apol_role_query_set_type(p, rq, nameB) < 0 || apol_role_get_by_query(p, rq, &vB) < 0) {
+ goto cleanup;
+ }
+
+ if ((r->users = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (qpol_policy_get_user_iter(p->p, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_user_t *user;
+ size_t i;
+ int inA = 0, inB = 0;
+ if (qpol_iterator_get_item(iter, (void **)&user) < 0) {
+ goto cleanup;
+ }
+ if (qpol_user_get_role_iter(p->p, user, &riter) < 0) {
+ goto cleanup;
+ }
+ for (; (!inA || !inB) && !qpol_iterator_end(riter); qpol_iterator_next(riter)) {
+ qpol_role_t *role;
+ if (qpol_iterator_get_item(riter, (void **)&role) < 0) {
+ goto cleanup;
+ }
+ if (!inA && apol_vector_get_index(vA, role, NULL, NULL, &i) == 0) {
+ inA = 1;
+ }
+ if (!inB && apol_vector_get_index(vB, role, NULL, NULL, &i) == 0) {
+ inB = 1;
+ }
+ }
+ qpol_iterator_destroy(&riter);
+ if (inA && inB && apol_vector_append(r->users, user) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ apol_role_query_destroy(&rq);
+ apol_vector_destroy(&vA);
+ apol_vector_destroy(&vB);
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&riter);
+ return retval;
+}
+
+/**
+ * Comparison function for a vector of apol_types_relation_access_t
+ * pointers. Returns 0 if the access type at a matches the type b.
+ *
+ * @param a Pointer to an existing apol_types_relation_access_t.
+ * @param b Pointer to a qpol_type_t.
+ * @param data Unused.
+ *
+ * @return 0 if a's type matchs b, non-zero if not.
+ */
+static int apol_types_relation_access_compfunc(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ apol_types_relation_access_t *access = (apol_types_relation_access_t *) a;
+ qpol_type_t *t = (qpol_type_t *) b;
+ return (int)((char *)access->type - (char *)t);
+}
+
+/**
+ * Comparison function for a vector of apol_types_relation_access_t
+ * pointers. Returns 0 if the access type a matches the access type b.
+ *
+ * @param a Pointer to an existing apol_types_relation_access_t.
+ * @param b Pointer to another existing apol_types_relation_access_t.
+ * @param data Unused.
+ *
+ * @return 0 if a's type matchs b, non-zero if not.
+ */
+static int apol_types_relation_access_compfunc2(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ apol_types_relation_access_t *accessA = (apol_types_relation_access_t *) a;
+ apol_types_relation_access_t *accessB = (apol_types_relation_access_t *) b;
+ return (int)((char *)accessA->type - (char *)accessB->type);
+}
+
+/**
+ * Deallocate all space associated with a types relation access node,
+ * including the pointer itself. Does nothing if the pointer is
+ * alread NULL.
+ *
+ * @param data Pointer to an access node to free.
+ */
+static void apol_types_relation_access_free(void *data)
+{
+ apol_types_relation_access_t *a = (apol_types_relation_access_t *) data;
+ if (a != NULL) {
+ apol_vector_destroy(&a->rules);
+ free(a);
+ }
+}
+
+/**
+ * Adds a rule to a vector of apol_types_relation_access_t pointers.
+ * Expands the rule's target type, appending new entries as necessary.
+ *
+ * @param p Policy from which rule originated.
+ * @param r Rule to expand and append.
+ * @param access Vector of apol_types_relation_access_t.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_access_append_rule(const apol_policy_t * p, const qpol_avrule_t * r, apol_vector_t * access)
+{
+ const qpol_type_t *t;
+ apol_vector_t *expanded = NULL;
+ size_t i, j;
+ apol_types_relation_access_t *a;
+ int retval = -1;
+ if (qpol_avrule_get_target_type(p->p, r, &t) < 0 || (expanded = apol_query_expand_type(p, t)) == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(expanded); i++) {
+ t = apol_vector_get_element(expanded, i);
+ if (apol_vector_get_index(access, t, apol_types_relation_access_compfunc, NULL, &j) == 0) {
+ a = (apol_types_relation_access_t *) apol_vector_get_element(access, j);
+ } else {
+ if ((a = calloc(1, sizeof(*a))) == NULL ||
+ (a->rules = apol_vector_create(NULL)) == NULL || apol_vector_append(access, a) < 0) {
+ ERR(p, "%s", strerror(errno));
+ apol_types_relation_access_free(a);
+ goto cleanup;
+ }
+ a->type = t;
+ }
+ if (apol_vector_append(a->rules, (void *)r) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&expanded);
+ return retval;
+}
+
+/**
+ * The following builds separate databases to hold rules for typeA and
+ * typeB respectively. The database holds a vector of pointers to
+ * apol_types_relation_access_t objects. Then compare access lists
+ * for typeA and typeB, determine common and unique access and have
+ * easy access to the relevant rules.
+ *
+ * @param p Policy to look up av rules.
+ * @param typeA First type to build access list.
+ * @param typeB Other type to build access list.
+ * @param accessesA Vector of apol_types_relation_access_t for typeA.
+ * @param accessesB Vector of apol_types_relation_access_t for typeB.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_create_access_pools(const apol_policy_t * p,
+ const qpol_type_t * typeA,
+ const qpol_type_t * typeB, apol_vector_t * accessesA, apol_vector_t * accessesB)
+{
+ const char *nameA, *nameB;
+ apol_avrule_query_t *aq = NULL;
+ apol_vector_t *vA = NULL, *vB = NULL;
+ size_t i;
+ int retval = -1;
+
+ if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) {
+ goto cleanup;
+ }
+ if ((aq = apol_avrule_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_avrule_query_set_rules(p, aq, QPOL_RULE_ALLOW) < 0 ||
+ apol_avrule_query_set_source(p, aq, nameA, 1) < 0 ||
+ apol_avrule_get_by_query(p, aq, &vA) < 0 ||
+ apol_avrule_query_set_source(p, aq, nameB, 1) < 0 || apol_avrule_get_by_query(p, aq, &vB) < 0) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(vA); i++) {
+ qpol_avrule_t *r = (qpol_avrule_t *) apol_vector_get_element(vA, i);
+ if (apol_types_relation_access_append_rule(p, r, accessesA) < 0) {
+ goto cleanup;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(vB); i++) {
+ qpol_avrule_t *r = (qpol_avrule_t *) apol_vector_get_element(vB, i);
+ if (apol_types_relation_access_append_rule(p, r, accessesB) < 0) {
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ apol_avrule_query_destroy(&aq);
+ apol_vector_destroy(&vA);
+ apol_vector_destroy(&vB);
+ return retval;
+}
+
+/**
+ * Allocate a new apol_types_relation_access_t and append it to a
+ * vector. The new access node's type will be set to a's type. The
+ * rules will be a clone of a's rules.
+ *
+ * @param p Policy from which rule originated.
+ * @param a Access node to duplicate.
+ * @param access Vector of apol_types_relation_access_t to append.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_access_append(const apol_policy_t * p, const apol_types_relation_access_t * a,
+ apol_vector_t * access)
+{
+ apol_types_relation_access_t *new_a;
+ int retval = -1;
+ if ((new_a = calloc(1, sizeof(*new_a))) == NULL
+ || (new_a->rules = apol_vector_create_from_vector(a->rules, NULL, NULL, NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ new_a->type = a->type;
+ if (apol_vector_append(access, new_a) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_types_relation_access_free(new_a);
+ }
+ return retval;
+}
+
+/**
+ * Find accesses, both similar and dissimilar, between both typeA and
+ * typeB.
+ *
+ * @param p Policy containing types' information.
+ * @param typeA First type to check.
+ * @param typeB Other type to check.
+ * @param do_similar 1 if to calculate similar accesses, 0 to skip.
+ * @param do_dissimilar 1 if to calculate dissimilar accesses, 0 to skip.
+ * @param r Result structure to fill.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_accesses(const apol_policy_t * p,
+ const qpol_type_t * typeA,
+ const qpol_type_t * typeB, int do_similar, int do_dissimilar,
+ apol_types_relation_result_t * r)
+{
+ apol_vector_t *accessesA = NULL, *accessesB = NULL;
+ apol_types_relation_access_t *a, *b;
+ size_t i, j;
+ int retval = -1;
+
+ if ((accessesA = apol_vector_create(apol_types_relation_access_free)) == NULL
+ || (accessesB = apol_vector_create(apol_types_relation_access_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (apol_types_relation_create_access_pools(p, typeA, typeB, accessesA, accessesB) < 0) {
+ goto cleanup;
+ }
+ apol_vector_sort(accessesA, apol_types_relation_access_compfunc2, NULL);
+ apol_vector_sort(accessesB, apol_types_relation_access_compfunc2, NULL);
+
+ if (do_similar) {
+ if ((r->simA = apol_vector_create(apol_types_relation_access_free)) == NULL
+ || (r->simB = apol_vector_create(apol_types_relation_access_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ if (do_dissimilar) {
+ if ((r->disA = apol_vector_create(apol_types_relation_access_free)) == NULL
+ || (r->disB = apol_vector_create(apol_types_relation_access_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+
+ /* Step through each element for each access sorted list. If
+ * their types match and if do_similiar, then append the union
+ * of the access rules to the results. If their types do not
+ * match and if do_similar then add to results.
+ */
+ for (i = j = 0; i < apol_vector_get_size(accessesA) && j < apol_vector_get_size(accessesB);) {
+ a = (apol_types_relation_access_t *) apol_vector_get_element(accessesA, i);
+ b = (apol_types_relation_access_t *) apol_vector_get_element(accessesB, j);
+ if (a->type == b->type) {
+ if (do_similar &&
+ (apol_types_relation_access_append(p, a, r->simA) < 0 ||
+ apol_types_relation_access_append(p, b, r->simB) < 0)) {
+ goto cleanup;
+ }
+ i++;
+ j++;
+ } else {
+ if (a->type < b->type) {
+ if (do_dissimilar && apol_types_relation_access_append(p, a, r->disA) < 0) {
+ goto cleanup;
+ }
+ i++;
+ } else {
+ if (do_dissimilar && apol_types_relation_access_append(p, b, r->disB) < 0) {
+ goto cleanup;
+ }
+ j++;
+ }
+ }
+ }
+ for (; do_dissimilar && i < apol_vector_get_size(accessesA); i++) {
+ a = (apol_types_relation_access_t *) apol_vector_get_element(accessesA, i);
+ if (apol_types_relation_access_append(p, a, r->disA) < 0) {
+ goto cleanup;
+ }
+ }
+ for (; do_dissimilar && j < apol_vector_get_size(accessesB); j++) {
+ b = (apol_types_relation_access_t *) apol_vector_get_element(accessesB, j);
+ if (apol_types_relation_access_append(p, b, r->disB) < 0) {
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&accessesA);
+ apol_vector_destroy(&accessesB);
+ return retval;
+}
+
+/**
+ * Find all allow rules that involve both types. Create a vector of
+ * those rules (as represented as qpol_avrule_t pointers relative to
+ * the provided policy) and set r->allows to that vector.
+ *
+ * @param p Policy containing types' information.
+ * @param typeA First type to check.
+ * @param typeB Other type to check.
+ * @param r Result structure to fill.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_allows(const apol_policy_t * p, const qpol_type_t * typeA, const qpol_type_t * typeB,
+ apol_types_relation_result_t * r)
+{
+ const char *nameA, *nameB;
+ apol_avrule_query_t *aq = NULL;
+ apol_vector_t *v = NULL;
+ int retval = -1;
+
+ if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) {
+ goto cleanup;
+ }
+ if ((aq = apol_avrule_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_avrule_query_set_rules(p, aq, QPOL_RULE_ALLOW) < 0 ||
+ apol_avrule_query_set_source(p, aq, nameA, 1) < 0 ||
+ apol_avrule_query_set_target(p, aq, nameB, 1) < 0 || apol_avrule_get_by_query(p, aq, &r->allows) < 0) {
+ goto cleanup;
+ }
+ if (apol_avrule_query_set_source(p, aq, nameB, 1) < 0 ||
+ apol_avrule_query_set_target(p, aq, nameA, 1) < 0 || apol_avrule_get_by_query(p, aq, &v) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_cat(r->allows, v) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ apol_avrule_query_destroy(&aq);
+ apol_vector_destroy(&v);
+ return retval;
+}
+
+/**
+ * Find all type transition / type change rules that involve both
+ * types. Create a vector of those rules (as represented as
+ * qpol_terule_t pointers relative to the provided policy) and set
+ * r->types to that vector.
+ *
+ * @param p Policy containing types' information.
+ * @param typeA First type to check.
+ * @param typeB Other type to check.
+ * @param r Result structure to fill.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_types(const apol_policy_t * p, const qpol_type_t * typeA, const qpol_type_t * typeB,
+ apol_types_relation_result_t * r)
+{
+ const char *nameA, *nameB;
+ apol_terule_query_t *tq = NULL;
+ apol_vector_t *v = NULL, *candidate_types = NULL;
+ const qpol_terule_t *rule;
+ const qpol_type_t *target, *default_type;
+ size_t i, j;
+ int retval = -1;
+
+ if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) {
+ goto cleanup;
+ }
+ if ((r->types = apol_vector_create(NULL)) == NULL || (tq = apol_terule_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_terule_query_set_rules(p, tq, QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_CHANGE) < 0 ||
+ apol_terule_query_set_source(p, tq, nameA, 1) < 0 ||
+ apol_terule_get_by_query(p, tq, &v) < 0 ||
+ (candidate_types = apol_query_create_candidate_type_list(p, nameB, 0, 1, APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ rule = (qpol_terule_t *) apol_vector_get_element(v, i);
+ if (qpol_terule_get_target_type(p->p, rule, &target) < 0 ||
+ qpol_terule_get_default_type(p->p, rule, &default_type) < 0) {
+ goto cleanup;
+ }
+ if ((apol_vector_get_index(candidate_types, target, NULL, NULL, &j) == 0 ||
+ apol_vector_get_index(candidate_types, default_type, NULL, NULL, &j) == 0) &&
+ apol_vector_append(r->types, (void *)rule) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ apol_vector_destroy(&v);
+ apol_vector_destroy(&candidate_types);
+ if (apol_terule_query_set_source(p, tq, nameB, 1) < 0 ||
+ apol_terule_get_by_query(p, tq, &v) < 0 ||
+ (candidate_types = apol_query_create_candidate_type_list(p, nameA, 0, 1, APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ rule = (qpol_terule_t *) apol_vector_get_element(v, i);
+ if (qpol_terule_get_target_type(p->p, rule, &target) < 0 ||
+ qpol_terule_get_default_type(p->p, rule, &default_type) < 0) {
+ goto cleanup;
+ }
+ if ((apol_vector_get_index(candidate_types, target, NULL, NULL, &j) == 0 ||
+ apol_vector_get_index(candidate_types, default_type, NULL, NULL, &j) == 0) &&
+ apol_vector_append(r->types, (void *)rule) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ apol_terule_query_destroy(&tq);
+ apol_vector_destroy(&v);
+ apol_vector_destroy(&candidate_types);
+ return retval;
+}
+
+/**
+ * Given a vector of apol_infoflow_result_t objects, deep copy to the
+ * results vector those infoflow results whose target type matches
+ * target_name (or any of target_name's attributes or aliases).
+ *
+ * @param p Policy within which to lookup types.
+ * @param v Vector of existing apol_infoflow_result_t.
+ * @param target_name Target type name.
+ * @param results Vector to which clone matching infoflow results.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_clone_infoflow(const apol_policy_t * p, const apol_vector_t * v, const char *target_name,
+ apol_vector_t * results)
+{
+ apol_vector_t *candidate_types = NULL;
+ const qpol_type_t *target;
+ apol_infoflow_result_t *res, *new_res;
+ size_t i, j;
+ int retval = -1;
+ if ((candidate_types = apol_query_create_candidate_type_list(p, target_name, 0, 1, APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ res = (apol_infoflow_result_t *) apol_vector_get_element(v, i);
+ target = apol_infoflow_result_get_end_type(res);
+ if (apol_vector_get_index(candidate_types, target, NULL, NULL, &j) == 0) {
+ if ((new_res = infoflow_result_create_from_infoflow_result(res)) == NULL ||
+ apol_vector_append(results, new_res) < 0) {
+ infoflow_result_free(new_res);
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&candidate_types);
+ return retval;
+}
+
+/**
+ * Find all direct information flows between the two types. Create a
+ * vector of apol_infoflow_result_t and set r->dirflows to that vector.
+ *
+ * @param p Policy containing types' information.
+ * @param typeA First type to check.
+ * @param typeB Other type to check.
+ * @param r Result structure to fill.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_directflow(const apol_policy_t * p,
+ const qpol_type_t * typeA, const qpol_type_t * typeB, apol_types_relation_result_t * r)
+{
+ const char *nameA, *nameB;
+ apol_infoflow_analysis_t *ia = NULL;
+ apol_vector_t *v = NULL;
+ apol_infoflow_graph_t *g = NULL;
+ int retval = -1;
+
+ if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) {
+ goto cleanup;
+ }
+ if ((r->dirflows = apol_vector_create(infoflow_result_free)) == NULL || (ia = apol_infoflow_analysis_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_infoflow_analysis_set_mode(p, ia, APOL_INFOFLOW_MODE_DIRECT) < 0 ||
+ apol_infoflow_analysis_set_dir(p, ia, APOL_INFOFLOW_EITHER) < 0 ||
+ apol_infoflow_analysis_set_type(p, ia, nameA) < 0 || apol_infoflow_analysis_do(p, ia, &v, &g) < 0) {
+ goto cleanup;
+ }
+ if (apol_types_relation_clone_infoflow(p, v, nameB, r->dirflows) < 0) {
+ goto cleanup;
+ }
+
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v);
+ apol_infoflow_analysis_destroy(&ia);
+ apol_infoflow_graph_destroy(&g);
+ return retval;
+}
+
+/**
+ * Find (some) transitive information flows between the two types.
+ *
+ * @param p Policy containing types' information.
+ * @param typeA First type to check.
+ * @param typeB Other type to check.
+ * @param do_transAB 1 if to find paths from type A to B, 0 to skip.
+ * @param do_transBA 1 if to find paths from type B to A, 0 to skip.
+ * @param r Result structure to fill.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_transflow(const apol_policy_t * p,
+ const qpol_type_t * typeA,
+ const qpol_type_t * typeB,
+ unsigned int do_transAB, unsigned int do_transBA, apol_types_relation_result_t * r)
+{
+ const char *nameA, *nameB;
+ apol_infoflow_analysis_t *ia = NULL;
+ apol_vector_t *v = NULL;
+ apol_infoflow_graph_t *g = NULL;
+ int retval = -1;
+
+ if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) {
+ goto cleanup;
+ }
+ if ((ia = apol_infoflow_analysis_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_infoflow_analysis_set_mode(p, ia, APOL_INFOFLOW_MODE_TRANS) < 0 ||
+ apol_infoflow_analysis_set_dir(p, ia, APOL_INFOFLOW_OUT) < 0) {
+ goto cleanup;
+ }
+ if (do_transAB) {
+ if (apol_infoflow_analysis_set_type(p, ia, nameA) < 0 || apol_infoflow_analysis_do(p, ia, &v, &g) < 0) {
+ goto cleanup;
+ }
+ if ((r->transAB = apol_vector_create(infoflow_result_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (apol_types_relation_clone_infoflow(p, v, nameB, r->transAB) < 0) {
+ goto cleanup;
+ }
+ }
+ if (do_transBA) {
+ apol_vector_destroy(&v);
+ if ((do_transAB &&
+ apol_infoflow_analysis_do_more(p, g, nameB, &v) < 0) ||
+ (!do_transAB &&
+ (apol_infoflow_analysis_set_type(p, ia, nameB) < 0 || apol_infoflow_analysis_do(p, ia, &v, &g) < 0))) {
+ goto cleanup;
+ }
+ if ((r->transBA = apol_vector_create(infoflow_result_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (apol_types_relation_clone_infoflow(p, v, nameA, r->transBA) < 0) {
+ goto cleanup;
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v);
+ apol_infoflow_analysis_destroy(&ia);
+ apol_infoflow_graph_destroy(&g);
+ return retval;
+}
+
+/**
+ * Given a vector of apol_domain_trans_result_t objects, deep copy to
+ * the results vector those domain transition results whose target
+ * type matches target_name (or any of target_name's attributes or
+ * aliases).
+ *
+ * @param p Policy within which to lookup types.
+ * @param v Vector of existing apol_domain_trans_result_t.
+ * @param target_name Target type name.
+ * @param results Vector to which clone matching domain transition
+ * results.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_clone_domaintrans(const apol_policy_t * p, const apol_vector_t * v, const char *target_name,
+ apol_vector_t * results)
+{
+ apol_vector_t *candidate_types = NULL;
+ const qpol_type_t *target;
+ apol_domain_trans_result_t *res, *new_res;
+ size_t i, j;
+ int retval = -1;
+ if ((candidate_types = apol_query_create_candidate_type_list(p, target_name, 0, 1, APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ res = (apol_domain_trans_result_t *) apol_vector_get_element(v, i);
+ target = apol_domain_trans_result_get_end_type(res);
+ if (apol_vector_get_index(candidate_types, target, NULL, NULL, &j) == 0) {
+ if ((new_res = apol_domain_trans_result_create_from_domain_trans_result(res)) == NULL ||
+ apol_vector_append(results, new_res) < 0) {
+ domain_trans_result_free(new_res);
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&candidate_types);
+ return retval;
+}
+
+/**
+ * Find domain transitions between the two types.
+ *
+ * @param p Policy containing types' information.
+ * @param typeA First type to check.
+ * @param typeB Other type to check.
+ * @param do_domainAB 1 if to find transitions from type A to B, 0 to skip.
+ * @param do_domainBA 1 if to find transitions from type B to A, 0 to skip.
+ * @param r Result structure to fill.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_domain(apol_policy_t * p,
+ const qpol_type_t * typeA,
+ const qpol_type_t * typeB,
+ unsigned int do_domainsAB, unsigned int do_domainsBA, apol_types_relation_result_t * r)
+{
+ const char *nameA, *nameB;
+ apol_domain_trans_analysis_t *dta = NULL;
+ apol_vector_t *v = NULL;
+ int retval = -1;
+
+ if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) {
+ goto cleanup;
+ }
+ if ((dta = apol_domain_trans_analysis_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_policy_build_domain_trans_table(p) < 0 ||
+ apol_domain_trans_analysis_set_direction(p, dta, APOL_DOMAIN_TRANS_DIRECTION_FORWARD) < 0) {
+ goto cleanup;
+ }
+ if (do_domainsAB) {
+ apol_policy_reset_domain_trans_table(p);
+ if (apol_domain_trans_analysis_set_start_type(p, dta, nameA) < 0 || apol_domain_trans_analysis_do(p, dta, &v) < 0) {
+ goto cleanup;
+ }
+ if ((r->domsAB = apol_vector_create(domain_trans_result_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (apol_types_relation_clone_domaintrans(p, v, nameB, r->domsAB) < 0) {
+ goto cleanup;
+ }
+ }
+ if (do_domainsBA) {
+ apol_vector_destroy(&v);
+ apol_policy_reset_domain_trans_table(p);
+ if (apol_domain_trans_analysis_set_start_type(p, dta, nameB) < 0 || apol_domain_trans_analysis_do(p, dta, &v) < 0) {
+ goto cleanup;
+ }
+ if ((r->domsBA = apol_vector_create(domain_trans_result_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (apol_types_relation_clone_domaintrans(p, v, nameA, r->domsBA) < 0) {
+ goto cleanup;
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v);
+ apol_domain_trans_analysis_destroy(&dta);
+ return retval;
+}
+
+/******************** public functions below ********************/
+
+int apol_types_relation_analysis_do(apol_policy_t * p, const apol_types_relation_analysis_t * tr, apol_types_relation_result_t ** r)
+{
+ const qpol_type_t *typeA, *typeB;
+ unsigned char isattrA, isattrB;
+ unsigned int do_similar_access, do_dissimilar_access;
+ unsigned int do_transAB, do_transBA;
+ unsigned int do_domainAB, do_domainBA;
+ int retval = -1;
+ *r = NULL;
+
+ if (tr->typeA == NULL || tr->typeB == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+ if (apol_query_get_type(p, tr->typeA, &typeA) < 0 ||
+ apol_query_get_type(p, tr->typeB, &typeB) < 0 ||
+ qpol_type_get_isattr(p->p, typeA, &isattrA) < 0 || qpol_type_get_isattr(p->p, typeB, &isattrB) < 0) {
+ goto cleanup;
+ }
+ if (isattrA) {
+ ERR(p, "Symbol %s is an attribute.", tr->typeA);
+ goto cleanup;
+ }
+ if (isattrB) {
+ ERR(p, "Symbol %s is an attribute.", tr->typeB);
+ goto cleanup;
+ }
+ if ((*r = calloc(1, sizeof(**r))) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if ((tr->analyses & APOL_TYPES_RELATION_COMMON_ATTRIBS) && apol_types_relation_common_attribs(p, typeA, typeB, *r) < 0) {
+ goto cleanup;
+ }
+ if ((tr->analyses & APOL_TYPES_RELATION_COMMON_ROLES) && apol_types_relation_common_roles(p, typeA, typeB, *r) < 0) {
+ goto cleanup;
+ }
+ if ((tr->analyses & APOL_TYPES_RELATION_COMMON_USERS) && apol_types_relation_common_users(p, typeA, typeB, *r) < 0) {
+ goto cleanup;
+ }
+ do_similar_access = tr->analyses & APOL_TYPES_RELATION_SIMILAR_ACCESS;
+ do_dissimilar_access = tr->analyses & APOL_TYPES_RELATION_DISSIMILAR_ACCESS;
+ if ((do_similar_access || do_dissimilar_access) &&
+ apol_types_relation_accesses(p, typeA, typeB, do_similar_access, do_dissimilar_access, *r) < 0) {
+ goto cleanup;
+ }
+ if ((tr->analyses & APOL_TYPES_RELATION_ALLOW_RULES) && apol_types_relation_allows(p, typeA, typeB, *r) < 0) {
+ goto cleanup;
+ }
+ if ((tr->analyses & APOL_TYPES_RELATION_TYPE_RULES) && apol_types_relation_types(p, typeA, typeB, *r) < 0) {
+ goto cleanup;
+ }
+ if ((tr->analyses & APOL_TYPES_RELATION_DIRECT_FLOW) && apol_types_relation_directflow(p, typeA, typeB, *r) < 0) {
+ goto cleanup;
+ }
+ do_transAB = tr->analyses & APOL_TYPES_RELATION_TRANS_FLOW_AB;
+ do_transBA = tr->analyses & APOL_TYPES_RELATION_TRANS_FLOW_BA;
+ if ((do_transAB || do_transBA) && apol_types_relation_transflow(p, typeA, typeB, do_transAB, do_transBA, *r) < 0) {
+ goto cleanup;
+ }
+ do_domainAB = tr->analyses & APOL_TYPES_RELATION_DOMAIN_TRANS_AB;
+ do_domainBA = tr->analyses & APOL_TYPES_RELATION_DOMAIN_TRANS_BA;
+ if ((do_domainAB || do_domainBA) && apol_types_relation_domain(p, typeA, typeB, do_domainAB, do_domainBA, *r) < 0) {
+ goto cleanup;
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_types_relation_result_destroy(r);
+ }
+ return retval;
+}
+
+apol_types_relation_analysis_t *apol_types_relation_analysis_create(void)
+{
+ return calloc(1, sizeof(apol_types_relation_analysis_t));
+}
+
+void apol_types_relation_analysis_destroy(apol_types_relation_analysis_t ** tr)
+{
+ if (*tr != NULL) {
+ free((*tr)->typeA);
+ free((*tr)->typeB);
+ free(*tr);
+ *tr = NULL;
+ }
+}
+
+int apol_types_relation_analysis_set_first_type(const apol_policy_t * p, apol_types_relation_analysis_t * tr, const char *name)
+{
+ if (name == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ return apol_query_set(p, &tr->typeA, NULL, name);
+}
+
+int apol_types_relation_analysis_set_other_type(const apol_policy_t * p, apol_types_relation_analysis_t * tr, const char *name)
+{
+ if (name == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ return apol_query_set(p, &tr->typeB, NULL, name);
+}
+
+int apol_types_relation_analysis_set_analyses(const apol_policy_t * p __attribute__ ((unused)),
+ apol_types_relation_analysis_t * tr, unsigned int analyses)
+{
+ if (analyses != 0) {
+ tr->analyses = analyses;
+ } else {
+ tr->analyses = ~0U;
+ }
+ return 0;
+}
+
+/*************** functions to access type relation results ***************/
+
+void apol_types_relation_result_destroy(apol_types_relation_result_t ** result)
+{
+ if (*result != NULL) {
+ apol_vector_destroy(&(*result)->attribs);
+ apol_vector_destroy(&(*result)->roles);
+ apol_vector_destroy(&(*result)->users);
+ apol_vector_destroy(&(*result)->simA);
+ apol_vector_destroy(&(*result)->simB);
+ apol_vector_destroy(&(*result)->disA);
+ apol_vector_destroy(&(*result)->disB);
+ apol_vector_destroy(&(*result)->allows);
+ apol_vector_destroy(&(*result)->types);
+ apol_vector_destroy(&(*result)->dirflows);
+ apol_vector_destroy(&(*result)->transAB);
+ apol_vector_destroy(&(*result)->transBA);
+ apol_vector_destroy(&(*result)->domsAB);
+ apol_vector_destroy(&(*result)->domsBA);
+ free(*result);
+ *result = NULL;
+ }
+}
+
+const apol_vector_t *apol_types_relation_result_get_attributes(const apol_types_relation_result_t * result)
+{
+ return result->attribs;
+}
+
+const apol_vector_t *apol_types_relation_result_get_roles(const apol_types_relation_result_t * result)
+{
+ return result->roles;
+}
+
+const apol_vector_t *apol_types_relation_result_get_users(const apol_types_relation_result_t * result)
+{
+ return result->users;
+}
+
+const apol_vector_t *apol_types_relation_result_get_similar_first(const apol_types_relation_result_t * result)
+{
+ return result->simA;
+}
+
+const apol_vector_t *apol_types_relation_result_get_similar_other(const apol_types_relation_result_t * result)
+{
+ return result->simB;
+}
+
+const apol_vector_t *apol_types_relation_result_get_dissimilar_first(const apol_types_relation_result_t * result)
+{
+ return result->disA;
+}
+
+const apol_vector_t *apol_types_relation_result_get_dissimilar_other(const apol_types_relation_result_t * result)
+{
+ return result->disB;
+}
+
+const apol_vector_t *apol_types_relation_result_get_allowrules(const apol_types_relation_result_t * result)
+{
+ return result->allows;
+}
+
+const apol_vector_t *apol_types_relation_result_get_typerules(const apol_types_relation_result_t * result)
+{
+ return result->types;
+}
+
+const apol_vector_t *apol_types_relation_result_get_directflows(const apol_types_relation_result_t * result)
+{
+ return result->dirflows;
+}
+
+const apol_vector_t *apol_types_relation_result_get_transflowsAB(const apol_types_relation_result_t * result)
+{
+ return result->transAB;
+}
+
+const apol_vector_t *apol_types_relation_result_get_transflowsBA(const apol_types_relation_result_t * result)
+{
+ return result->transBA;
+}
+
+const apol_vector_t *apol_types_relation_result_get_domainsAB(const apol_types_relation_result_t * result)
+{
+ return result->domsAB;
+}
+
+const apol_vector_t *apol_types_relation_result_get_domainsBA(const apol_types_relation_result_t * result)
+{
+ return result->domsBA;
+}
+
+const qpol_type_t *apol_types_relation_access_get_type(const apol_types_relation_access_t * a)
+{
+ return a->type;
+}
+
+const apol_vector_t *apol_types_relation_access_get_rules(const apol_types_relation_access_t * a)
+{
+ return a->rules;
+}
diff --git a/libapol/src/user-query.c b/libapol/src/user-query.c
new file mode 100644
index 0000000..066e005
--- /dev/null
+++ b/libapol/src/user-query.c
@@ -0,0 +1,198 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about users within a
+ * policy. The caller obtains a query object, fills in its
+ * parameters, and then runs the query; it obtains a vector of
+ * results. Searches are conjunctive -- all fields of the search
+ * query must match for a datum to be added to the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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>
+
+struct apol_user_query
+{
+ char *user_name, *role_name;
+ apol_mls_level_t *default_level;
+ apol_mls_range_t *range;
+ unsigned int flags;
+ regex_t *user_regex, *role_regex;
+};
+
+/******************** user queries ********************/
+
+int apol_user_get_by_query(const apol_policy_t * p, apol_user_query_t * u, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL, *role_iter = NULL;
+ apol_mls_level_t *default_level = NULL;
+ apol_mls_range_t *range = NULL;
+ int retval = -1, append_user;
+ *v = NULL;
+ if (qpol_policy_get_user_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_user_t *user;
+ if (qpol_iterator_get_item(iter, (void **)&user) < 0) {
+ goto cleanup;
+ }
+ append_user = 1;
+ if (u != NULL) {
+ const char *user_name;
+ int compval;
+ const qpol_mls_level_t *mls_default_level;
+ const qpol_mls_range_t *mls_range;
+
+ qpol_iterator_destroy(&role_iter);
+ apol_mls_level_destroy(&default_level);
+ apol_mls_range_destroy(&range);
+
+ if (qpol_user_get_name(p->p, user, &user_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, user_name, u->user_name, u->flags, &(u->user_regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ if (qpol_user_get_role_iter(p->p, user, &role_iter) < 0) {
+ goto cleanup;
+ }
+ if (u->role_name != NULL && u->role_name[0] != '\0') {
+ append_user = 0;
+ for (; !qpol_iterator_end(role_iter); qpol_iterator_next(role_iter)) {
+ qpol_role_t *role;
+ const char *role_name;
+ if (qpol_iterator_get_item(role_iter, (void **)&role) < 0 ||
+ qpol_role_get_name(p->p, role, &role_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, role_name, u->role_name, u->flags, &(u->role_regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 1) {
+ append_user = 1;
+ break;
+ }
+ }
+ }
+ if (apol_policy_is_mls(p)) {
+ if (qpol_user_get_dfltlevel(p->p, user, &mls_default_level) < 0 ||
+ (default_level = apol_mls_level_create_from_qpol_mls_level(p, mls_default_level)) == NULL) {
+ goto cleanup;
+ }
+ compval = apol_mls_level_compare(p, default_level, u->default_level);
+ apol_mls_level_destroy(&default_level);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval != APOL_MLS_EQ) {
+ continue;
+ }
+
+ if (qpol_user_get_range(p->p, user, &mls_range) < 0 ||
+ (range = apol_mls_range_create_from_qpol_mls_range(p, mls_range)) == NULL) {
+ goto cleanup;
+ }
+ compval = apol_mls_range_compare(p, range, u->range, u->flags);
+ apol_mls_range_destroy(&range);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ }
+ }
+ if (append_user && apol_vector_append(*v, user)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&role_iter);
+ apol_mls_level_destroy(&default_level);
+ apol_mls_range_destroy(&range);
+ return retval;
+}
+
+apol_user_query_t *apol_user_query_create(void)
+{
+ return calloc(1, sizeof(apol_user_query_t));
+}
+
+void apol_user_query_destroy(apol_user_query_t ** u)
+{
+ if (*u != NULL) {
+ free((*u)->user_name);
+ free((*u)->role_name);
+ apol_mls_level_destroy(&((*u)->default_level));
+ apol_mls_range_destroy(&((*u)->range));
+ apol_regex_destroy(&(*u)->user_regex);
+ apol_regex_destroy(&(*u)->role_regex);
+ free(*u);
+ *u = NULL;
+ }
+}
+
+int apol_user_query_set_user(const apol_policy_t * p, apol_user_query_t * u, const char *name)
+{
+ return apol_query_set(p, &u->user_name, &u->user_regex, name);
+}
+
+int apol_user_query_set_role(const apol_policy_t * p, apol_user_query_t * u, const char *role)
+{
+ return apol_query_set(p, &u->role_name, &u->role_regex, role);
+}
+
+int apol_user_query_set_default_level(const apol_policy_t * p
+ __attribute__ ((unused)), apol_user_query_t * u, apol_mls_level_t * level)
+{
+ u->default_level = level;
+ return 0;
+}
+
+int apol_user_query_set_range(const apol_policy_t * p __attribute__ ((unused)),
+ apol_user_query_t * u, apol_mls_range_t * range, unsigned int range_match)
+{
+ if (u->range != NULL) {
+ apol_mls_range_destroy(&u->range);
+ }
+ u->range = range;
+ u->flags = (u->flags & ~APOL_QUERY_FLAGS) | range_match;
+ return 0;
+}
+
+int apol_user_query_set_regex(const apol_policy_t * p, apol_user_query_t * u, int is_regex)
+{
+ return apol_query_set_regex(p, &u->flags, is_regex);
+}
diff --git a/libapol/src/util.c b/libapol/src/util.c
new file mode 100644
index 0000000..dd6d300
--- /dev/null
+++ b/libapol/src/util.c
@@ -0,0 +1,659 @@
+/**
+ * @file
+ *
+ * Implementation of utility functions.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2001-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 <config.h>
+
+#include <apol/util.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+/* these are needed for nodecons and IPv4 and IPv6 */
+#include <qpol/nodecon_query.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h> /* needed for portcon's protocol */
+
+/* use 8k line size */
+#define APOL_LINE_SZ 8192
+#define APOL_ENVIRON_VAR_NAME "APOL_INSTALL_DIR"
+
+const char *libapol_get_version(void)
+{
+ return LIBAPOL_VERSION_STRING;
+}
+
+int apol_str_to_internal_ip(const char *str, uint32_t ip[4])
+{
+ bool ipv4 = false;
+ bool ipv6 = false;
+
+ if (!str || !ip) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ip[0] = ip[1] = ip[2] = ip[3] = 0;
+
+ if (strchr(str, '.'))
+ ipv4 = true;
+
+ if (strchr(str, ':'))
+ ipv6 = true;
+
+ if (ipv4 == ipv6) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (ipv4) {
+ unsigned char *p = (unsigned char *)&(ip[0]);
+ int seg = 0;
+ uint32_t val = 0; /* value of current segment of address */
+ size_t len = strlen(str), i;
+ for (i = 0; i <= len; i++) {
+ if (str[i] == '.' || str[i] == '\0') {
+ if (val > 255) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ p[seg] = (unsigned char)(0xff & val);
+ seg++;
+ val = 0;
+ if (seg == 4)
+ break;
+ } else if (isdigit(str[i])) {
+ char tmp[2] = { str[i], 0 };
+ val = val * 10 + atoi(tmp);
+ } else {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ } else {
+ struct in6_addr addr;
+ if (inet_pton(AF_INET6, str, &addr) <= 0) {
+ return -1;
+ }
+ memcpy(ip, addr.s6_addr32, 16);
+ }
+
+ return ipv4 ? QPOL_IPV4 : QPOL_IPV6;
+}
+
+const char *apol_objclass_to_str(uint32_t objclass)
+{
+ switch (objclass) {
+ case QPOL_CLASS_BLK_FILE:
+ return "block";
+ case QPOL_CLASS_CHR_FILE:
+ return "char";
+ case QPOL_CLASS_DIR:
+ return "dir";
+ case QPOL_CLASS_FIFO_FILE:
+ return "fifo";
+ case QPOL_CLASS_FILE:
+ return "file";
+ case QPOL_CLASS_LNK_FILE:
+ return "link";
+ case QPOL_CLASS_SOCK_FILE:
+ return "sock";
+ case QPOL_CLASS_ALL:
+ return "any";
+ }
+ return NULL;
+}
+
+uint32_t apol_str_to_objclass(const char *objclass)
+{
+ if (objclass == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ if (strcmp(objclass, "block") == 0) {
+ return QPOL_CLASS_BLK_FILE;
+ }
+ if (strcmp(objclass, "char") == 0) {
+ return QPOL_CLASS_CHR_FILE;
+ }
+ if (strcmp(objclass, "dir") == 0) {
+ return QPOL_CLASS_DIR;
+ }
+ if (strcmp(objclass, "fifo") == 0) {
+ return QPOL_CLASS_FIFO_FILE;
+ }
+ if (strcmp(objclass, "file") == 0) {
+ return QPOL_CLASS_FILE;
+ }
+ if (strcmp(objclass, "link") == 0) {
+ return QPOL_CLASS_LNK_FILE;
+ }
+ if (strcmp(objclass, "sock") == 0) {
+ return QPOL_CLASS_SOCK_FILE;
+ }
+ if (strcmp(objclass, "any") == 0) {
+ return QPOL_CLASS_ALL;
+ }
+ return 0;
+}
+
+const char *apol_protocol_to_str(uint8_t protocol)
+{
+ switch (protocol) {
+ case IPPROTO_TCP:
+ return "tcp";
+ case IPPROTO_UDP:
+ return "udp";
+ default:
+ errno = EPROTONOSUPPORT;
+ return NULL;
+ }
+}
+
+uint8_t apol_str_to_protocol(const char *protocol_str)
+{
+ if (protocol_str == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ if (strcmp(protocol_str, "tcp") == 0 || strcmp(protocol_str, "TCP") == 0) {
+ return IPPROTO_TCP;
+ }
+ if (strcmp(protocol_str, "udp") == 0 || strcmp(protocol_str, "UDP") == 0) {
+ return IPPROTO_UDP;
+ }
+ errno = EPROTONOSUPPORT;
+ return 0;
+}
+
+const char *apol_fs_use_behavior_to_str(uint32_t behavior)
+{
+ switch (behavior) {
+ case QPOL_FS_USE_XATTR:
+ return "fs_use_xattr";
+ case QPOL_FS_USE_TASK:
+ return "fs_use_task";
+ case QPOL_FS_USE_TRANS:
+ return "fs_use_trans";
+ case QPOL_FS_USE_GENFS:
+ return "fs_use_genfs";
+ case QPOL_FS_USE_NONE:
+ return "fs_use_none";
+ case QPOL_FS_USE_PSID:
+ return "fs_use_psid";
+ }
+ return NULL;
+}
+
+int apol_str_to_fs_use_behavior(const char *behavior)
+{
+ if (strcmp(behavior, "fs_use_xattr") == 0) {
+ return QPOL_FS_USE_XATTR;
+ } else if (strcmp(behavior, "fs_use_task") == 0) {
+ return QPOL_FS_USE_TASK;
+ } else if (strcmp(behavior, "fs_use_trans") == 0) {
+ return QPOL_FS_USE_TRANS;
+ } else if (strcmp(behavior, "fs_use_genfs") == 0) {
+ return QPOL_FS_USE_GENFS;
+ } else if (strcmp(behavior, "fs_use_none") == 0) {
+ return QPOL_FS_USE_NONE;
+ } else if (strcmp(behavior, "fs_use_psid") == 0) {
+ return QPOL_FS_USE_PSID;
+ }
+ return -1;
+}
+
+const char *apol_rule_type_to_str(uint32_t rule_type)
+{
+ switch (rule_type) {
+ case QPOL_RULE_ALLOW:
+ return "allow";
+ case QPOL_RULE_NEVERALLOW:
+ return "neverallow";
+ case QPOL_RULE_AUDITALLOW:
+ return "auditallow";
+ case QPOL_RULE_DONTAUDIT:
+ return "dontaudit";
+ case QPOL_RULE_TYPE_TRANS:
+ return "type_transition";
+ case QPOL_RULE_TYPE_CHANGE:
+ return "type_change";
+ case QPOL_RULE_TYPE_MEMBER:
+ return "type_member";
+ }
+ return NULL;
+}
+
+const char *apol_cond_expr_type_to_str(uint32_t expr_type)
+{
+ switch (expr_type) {
+ case QPOL_COND_EXPR_BOOL:
+ return "";
+ case QPOL_COND_EXPR_NOT:
+ return "!";
+ case QPOL_COND_EXPR_OR:
+ return "||";
+ case QPOL_COND_EXPR_AND:
+ return "&&";
+ case QPOL_COND_EXPR_XOR:
+ return "^";
+ case QPOL_COND_EXPR_EQ:
+ return "==";
+ case QPOL_COND_EXPR_NEQ:
+ return "!=";
+ }
+ return NULL;
+}
+
+char *apol_file_find(const char *file_name)
+{
+ char *file = NULL, *var = NULL, *dirs[3];
+ size_t i;
+ int rt;
+
+ if (file_name == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* check current directory, environment variable, and then
+ * installed directory */
+ dirs[0] = ".";
+ dirs[1] = getenv(APOL_ENVIRON_VAR_NAME);
+ dirs[2] = APOL_INSTALL_DIR;
+ for (i = 0; i < 3; i++) {
+ if ((var = dirs[i]) != NULL) {
+ if (asprintf(&file, "%s/%s", var, file_name) < 0) {
+ return NULL;
+ }
+ rt = access(file, R_OK);
+ free(file);
+ if (rt == 0) {
+ return strdup(var);
+ }
+ }
+ }
+
+ /* didn't find it */
+ return NULL;
+}
+
+char *apol_file_find_path(const char *file_name)
+{
+ char *file = NULL, *var = NULL, *dirs[3];
+ size_t i;
+ int rt;
+
+ if (file_name == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* check current directory, environment variable, and then
+ * installed directory */
+ dirs[0] = ".";
+ dirs[1] = getenv(APOL_ENVIRON_VAR_NAME);
+ dirs[2] = APOL_INSTALL_DIR;
+ for (i = 0; i < 3; i++) {
+ if ((var = dirs[i]) != NULL) {
+ if (asprintf(&file, "%s/%s", var, file_name) < 0) {
+ return NULL;
+ }
+ rt = access(file, R_OK);
+ if (rt == 0) {
+ return file;
+ }
+ free(file);
+ }
+ }
+
+ /* didn't find it */
+ return NULL;
+}
+
+char *apol_file_find_user_config(const char *file_name)
+{
+ char *file, *var;
+ int rt;
+
+ if (file_name == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ var = getenv("HOME");
+ if (var) {
+ if (asprintf(&file, "%s/%s", var, file_name) < 0) {
+ return NULL;
+ }
+ rt = access(file, R_OK);
+ if (rt == 0) {
+ return file;
+ } else {
+ free(file);
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+int apol_file_read_to_buffer(const char *fname, char **buf, size_t * len)
+{
+ FILE *file = NULL;
+ const size_t BUF_SIZE = 1024;
+ size_t size = 0, r;
+ char *bufp, *b;
+
+ assert(*buf == NULL);
+ assert(len);
+ *len = 0;
+ while (1) {
+ size += BUF_SIZE;
+ r = 0;
+ b = (char *)realloc(*buf, size * sizeof(char));
+ if (b == NULL) {
+ free(*buf);
+ *buf = NULL;
+ *len = 0;
+ if (file)
+ fclose(file);
+ return -1;
+ }
+ *buf = b;
+ if (!file) {
+ file = fopen(fname, "rb");
+ if (!file) {
+ free(*buf);
+ *buf = NULL;
+ *len = 0;
+ return -1;
+ }
+ }
+ bufp = &((*buf)[size - BUF_SIZE]);
+ r = fread(bufp, sizeof(char), BUF_SIZE, file);
+ *len += r;
+ if (r < BUF_SIZE) {
+ if (feof(file)) {
+ fclose(file);
+ break;
+ } else {
+ free(*buf);
+ *buf = NULL;
+ *len = 0;
+ fclose(file);
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+char *apol_config_get_var(const char *var, FILE * fp)
+{
+ char line[APOL_LINE_SZ], t1[APOL_LINE_SZ], t2[APOL_LINE_SZ];
+ char *line_ptr = NULL;
+
+ if (var == NULL || fp == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ rewind(fp);
+ while (fgets(line, APOL_LINE_SZ, fp) != NULL) {
+ if ((line_ptr = strdup(line)) == NULL) {
+ return NULL;
+ }
+ apol_str_trim(line_ptr);
+ if (line_ptr[0] == '#' || sscanf(line_ptr, "%s %[^\n]", t1, t2) != 2 || strcasecmp(var, t1) != 0) {
+ free(line_ptr);
+ continue;
+ } else {
+ free(line_ptr);
+ return strdup(t2);
+ }
+ }
+ return NULL;
+}
+
+apol_vector_t *apol_str_split(const char *s, const char *delim)
+{
+ char *orig_s = NULL, *dup_s = NULL, *v, *token;
+ apol_vector_t *list = NULL;
+ int error = 0;
+
+ if (s == NULL || delim == NULL) {
+ error = EINVAL;
+ goto cleanup;
+ }
+ if ((list = apol_vector_create(free)) == NULL || (orig_s = strdup(s)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ v = orig_s;
+ while ((token = strsep(&v, delim)) != NULL) {
+ if (strcmp(token, "") != 0 && !apol_str_is_only_white_space(token)) {
+ if ((dup_s = strdup(token)) == NULL || apol_vector_append(list, dup_s) < 0) {
+ error = errno;
+ free(dup_s);
+ goto cleanup;
+ }
+ }
+ }
+ cleanup:
+ free(orig_s);
+ if (error != 0) {
+ apol_vector_destroy(&list);
+ errno = error;
+ return NULL;
+ }
+ return list;
+}
+
+char *apol_str_join(const apol_vector_t * list, const char *delim)
+{
+ char *val, *s;
+ size_t i, len;
+
+ if (list == NULL || delim == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (apol_vector_get_size(list) == 0) {
+ return strdup("");
+ }
+ s = apol_vector_get_element(list, 0);
+ if ((val = strdup(s)) == NULL) {
+ return NULL;
+ }
+ len = strlen(val) + 1;
+ for (i = 1; i < apol_vector_get_size(list); i++) {
+ s = apol_vector_get_element(list, i);
+ if (apol_str_appendf(&val, &len, "%s%s", delim, s) < 0) {
+ return NULL;
+ }
+ }
+ return val;
+}
+
+/**
+ * Given a string, if the string begins with whitespace then allocate
+ * a new string that does not contain those whitespaces.
+ *
+ * @param str String to modify.
+ */
+static void trim_leading_whitespace(char *str)
+{
+ size_t i, len;
+ for (i = 0; str[i] != '\0' && isspace(str[i]); i++) ;
+ len = strlen(str + i);
+ memmove(str, str + i, len + 1);
+}
+
+/**
+ * Given a mutable string, replace trailing whitespace characters with
+ * null characters.
+ *
+ * @param str String to modify.
+ */
+static void trim_trailing_whitespace(char *str)
+{
+ size_t length;
+ length = strlen(str);
+ while (length > 0 && isspace(str[length - 1])) {
+ str[length - 1] = '\0';
+ length--;
+ }
+}
+
+void apol_str_trim(char *str)
+{
+ if (str == NULL) {
+ errno = EINVAL;
+ return;
+ }
+ trim_leading_whitespace(str);
+ trim_trailing_whitespace(str);
+}
+
+int apol_str_append(char **tgt, size_t * tgt_sz, const char *str)
+{
+ size_t str_len;
+ if (str == NULL || (str_len = strlen(str)) == 0)
+ return 0;
+ if (tgt == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ str_len++;
+ /* target is currently empty */
+ if (*tgt == NULL || *tgt_sz == 0) {
+ *tgt = (char *)malloc(str_len);
+ if (*tgt == NULL) {
+ *tgt_sz = 0;
+ return -1;
+ }
+ *tgt_sz = str_len;
+ strcpy(*tgt, str);
+ return 0;
+ } else {
+ /* tgt has some memory */
+ char *t = (char *)realloc(*tgt, *tgt_sz + str_len);
+ if (t == NULL) {
+ int error = errno;
+ free(*tgt);
+ *tgt = NULL;
+ *tgt_sz = 0;
+ errno = error;
+ return -1;
+ }
+ *tgt = t;
+ *tgt_sz += str_len;
+ strcat(*tgt, str);
+ return 0;
+ }
+}
+
+int apol_str_appendf(char **tgt, size_t * tgt_sz, const char *fmt, ...)
+{
+ va_list ap;
+ int error;
+ if (fmt == NULL || strlen(fmt) == 0)
+ return 0;
+ if (tgt == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ va_start(ap, fmt);
+ /* target is currently empty */
+ if (*tgt == NULL || *tgt_sz == 0) {
+ if (vasprintf(tgt, fmt, ap) < 0) {
+ error = errno;
+ *tgt = NULL;
+ *tgt_sz = 0;
+ va_end(ap);
+ errno = error;
+ return -1;
+ }
+ *tgt_sz = strlen(*tgt) + 1;
+ va_end(ap);
+ return 0;
+ } else {
+ /* tgt has some memory */
+ char *t, *u;
+ size_t str_len;
+ if (vasprintf(&t, fmt, ap) < 0) {
+ error = errno;
+ free(*tgt);
+ *tgt_sz = 0;
+ va_end(ap);
+ errno = error;
+ return -1;
+ }
+ va_end(ap);
+ str_len = strlen(t);
+ if ((u = (char *)realloc(*tgt, *tgt_sz + str_len)) == NULL) {
+ error = errno;
+ free(t);
+ free(*tgt);
+ *tgt_sz = 0;
+ errno = error;
+ return -1;
+ }
+ *tgt = u;
+ *tgt_sz += str_len;
+ strcat(*tgt, t);
+ free(t);
+ return 0;
+ }
+}
+
+int apol_str_is_only_white_space(const char *str)
+{
+ size_t len, i;
+ if (str == NULL)
+ return 0;
+ len = strlen(str);
+ for (i = 0; i < len; i++) {
+ if (!isspace(str[i]))
+ return 0;
+ }
+ return 1;
+}
+
+int apol_str_strcmp(const void *a, const void *b, void *unused __attribute__ ((unused)))
+{
+ return strcmp((const char *)a, (const char *)b);
+}
+
+void *apol_str_strdup(const void *elem, void *unused __attribute__ ((unused)))
+{
+ return strdup((const char *)elem);
+}
diff --git a/libapol/src/vector-internal.h b/libapol/src/vector-internal.h
new file mode 100644
index 0000000..88c4378
--- /dev/null
+++ b/libapol/src/vector-internal.h
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * Protected routines for the vector class.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 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
+ */
+
+#ifndef APOL_VECTOR_INTERNAL_H
+#define APOL_VECTOR_INTERNAL_H
+
+/**
+ * Change the free function of a vector. Currently, this function is
+ * friends with the BST class; otherwise consider this to be a private
+ * function.
+ */
+void vector_set_free_func(apol_vector_t * v, apol_vector_free_func * fr);
+
+#endif
diff --git a/libapol/src/vector.c b/libapol/src/vector.c
new file mode 100644
index 0000000..28fc897
--- /dev/null
+++ b/libapol/src/vector.c
@@ -0,0 +1,457 @@
+/**
+ * @file
+ * Contains the implementation of a generic vector.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-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 <apol/vector.h>
+#include "vector-internal.h"
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+/** The default initial capacity of a vector; must be a positive integer */
+#define APOL_VECTOR_DFLT_INIT_CAP 10
+
+/**
+ * Generic vector structure. Stores elements as void*.
+ */
+struct apol_vector
+{
+ /** The array of element pointers, which will be resized as needed. */
+ void **array;
+ /** The number of elements currently stored in array. */
+ size_t size;
+ /** The actual amount of space in array. This amount will always
+ * be >= size and will grow exponentially as needed. */
+ size_t capacity;
+ apol_vector_free_func *fr;
+};
+
+apol_vector_t *apol_vector_create(apol_vector_free_func * fr)
+{
+ return apol_vector_create_with_capacity(APOL_VECTOR_DFLT_INIT_CAP, fr);
+}
+
+apol_vector_t *apol_vector_create_with_capacity(size_t cap, apol_vector_free_func * fr)
+{
+ apol_vector_t *v = NULL;
+ int error;
+
+ if (cap < 1) {
+ cap = 1;
+ }
+ v = calloc(1, sizeof(apol_vector_t));
+ if (!v)
+ return NULL;
+ v->array = calloc((v->capacity = cap), sizeof(void *));
+ if (!(v->array)) {
+ error = errno;
+ free(v);
+ errno = error;
+ return NULL;
+ }
+ v->fr = fr;
+ return v;
+}
+
+apol_vector_t *apol_vector_create_from_iter(qpol_iterator_t * iter, apol_vector_free_func * fr)
+{
+ size_t iter_size;
+ apol_vector_t *v;
+ void *item;
+ int error;
+ if (qpol_iterator_get_size(iter, &iter_size) < 0 || (v = apol_vector_create_with_capacity(iter_size, fr)) == NULL) {
+ return NULL;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, &item)) {
+ error = errno;
+ free(v);
+ errno = error;
+ return NULL;
+ }
+ apol_vector_append(v, item);
+ }
+ return v;
+}
+
+apol_vector_t *apol_vector_create_from_vector(const apol_vector_t * v, apol_vector_dup_func * dup, void *data,
+ apol_vector_free_func * fr)
+{
+ apol_vector_t *new_v;
+ size_t i;
+ if (v == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((new_v = apol_vector_create_with_capacity(v->capacity, fr)) == NULL) {
+ return NULL;
+ }
+ if (dup == NULL) {
+ memcpy(new_v->array, v->array, v->size * sizeof(void *));
+ } else {
+ for (i = 0; i < v->size; i++) {
+ new_v->array[i] = dup(v->array[i], data);
+ }
+ }
+ new_v->size = v->size;
+ return new_v;
+}
+
+apol_vector_t *apol_vector_create_from_intersection(const apol_vector_t * v1,
+ const apol_vector_t * v2, apol_vector_comp_func * cmp, void *data)
+{
+ apol_vector_t *new_v;
+ size_t i, j;
+ if (v1 == NULL || v2 == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((new_v = apol_vector_create(NULL)) == NULL) {
+ return NULL;
+ }
+ for (i = 0; i < v1->size; i++) {
+ for (j = 0; j < v2->size; j++) {
+ if ((cmp != NULL && cmp(v1->array[i], v2->array[j], data) == 0) ||
+ (cmp == NULL && v1->array[i] == v2->array[j])) {
+ if (apol_vector_append(new_v, v1->array[i]) < 0) {
+ apol_vector_destroy(&new_v);
+ return NULL;
+ }
+ break;
+ }
+ }
+ }
+ return new_v;
+}
+
+void apol_vector_destroy(apol_vector_t ** v)
+{
+ size_t i = 0;
+
+ if (!v || !(*v))
+ return;
+
+ if ((*v)->fr) {
+ for (i = 0; i < (*v)->size; i++) {
+ (*v)->fr((*v)->array[i]);
+ }
+ }
+ free((*v)->array);
+ (*v)->array = NULL;
+ free(*v);
+ *v = NULL;
+}
+
+size_t apol_vector_get_size(const apol_vector_t * v)
+{
+ if (!v) {
+ errno = EINVAL;
+ return 0;
+ } else {
+ return v->size;
+ }
+}
+
+size_t apol_vector_get_capacity(const apol_vector_t * v)
+{
+ if (!v) {
+ errno = EINVAL;
+ return 0;
+ } else {
+ return v->capacity;
+ }
+}
+
+void *apol_vector_get_element(const apol_vector_t * v, size_t idx)
+{
+ if (!v || !(v->array)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (idx >= v->size) {
+ errno = ERANGE;
+ return NULL;
+ }
+
+ return v->array[idx];
+}
+
+/**
+ * Grows a vector, by reallocating additional space for it.
+ *
+ * @param v Vector to which increase its size.
+ *
+ * @return 0 on success, -1 on error.
+ */
+static int apol_vector_grow(apol_vector_t * v)
+{
+ void **tmp;
+ size_t new_capacity = v->capacity;
+ if (new_capacity >= 128) {
+ new_capacity += 128;
+ } else {
+ new_capacity *= 2;
+ }
+ tmp = realloc(v->array, new_capacity * sizeof(void *));
+ if (!tmp) {
+ return -1;
+ }
+ v->capacity = new_capacity;
+ v->array = tmp;
+ return 0;
+}
+
+int apol_vector_get_index(const apol_vector_t * v, const void *elem, apol_vector_comp_func * cmp, void *data, size_t * i)
+{
+ if (!v || !i) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ for (*i = 0; *i < v->size; (*i)++) {
+ if ((cmp != NULL && cmp(v->array[*i], elem, data) == 0) || (cmp == NULL && elem == v->array[*i])) {
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int apol_vector_append(apol_vector_t * v, void *elem)
+{
+ if (!v) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (v->size >= v->capacity && apol_vector_grow(v)) {
+ return -1;
+ }
+
+ v->array[v->size] = elem;
+ v->size++;
+
+ return 0;
+}
+
+int apol_vector_append_unique(apol_vector_t * v, void *elem, apol_vector_comp_func * cmp, void *data)
+{
+ size_t i;
+ if (apol_vector_get_index(v, elem, cmp, data, &i) < 0) {
+ return apol_vector_append(v, elem);
+ }
+ errno = EEXIST;
+ return 1;
+}
+
+int apol_vector_compare(const apol_vector_t * a, const apol_vector_t * b, apol_vector_comp_func * cmp, void *data, size_t * i)
+{
+ int compval;
+ if (a == NULL || b == NULL || i == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ size_t a_len = apol_vector_get_size(a);
+ size_t b_len = apol_vector_get_size(b);
+ for (*i = 0; *i < a_len && *i < b_len; (*i)++) {
+ if (cmp != NULL) {
+ compval = cmp(a->array[*i], b->array[*i], data);
+ } else {
+ compval = (int)((char *)a->array[*i] - (char *)b->array[*i]);
+ }
+ if (compval != 0) {
+ return compval;
+ }
+ }
+ if (a_len == b_len) {
+ return 0;
+ } else if (a_len < b_len) {
+ return -1;
+ } else {
+ return 1;
+ }
+}
+
+static size_t vector_qsort_partition(void **data, size_t first, size_t last, apol_vector_comp_func * cmp, void *arg)
+{
+ void *pivot = data[last];
+ size_t i = first, j = last;
+ while (i < j) {
+ if (cmp(data[i], pivot, arg) <= 0) {
+ i++;
+ } else {
+ data[j] = data[i];
+ data[i] = data[j - 1];
+ j--;
+ }
+ }
+ data[j] = pivot;
+ return j;
+}
+
+static void vector_qsort(void **data, size_t first, size_t last, apol_vector_comp_func * cmp, void *arg)
+{
+ if (first < last) {
+ size_t i = vector_qsort_partition(data, first, last, cmp, arg);
+ /* need this explicit check here, because i is an
+ * unsigned integer, and subtracting 1 from 0 is
+ * bad */
+ if (i > 0) {
+ vector_qsort(data, first, i - 1, cmp, arg);
+ }
+ vector_qsort(data, i + 1, last, cmp, arg);
+ }
+}
+
+/**
+ * Generic comparison function, which treats elements of the vector as
+ * unsigned integers.
+ */
+static int vector_int_comp(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ char *i = (char *)a;
+ char *j = (char *)b;
+ if (i < j) {
+ return -1;
+ } else if (i > j) {
+ return 1;
+ }
+ return 0;
+}
+
+/* implemented as an in-place quicksort */
+void apol_vector_sort(apol_vector_t * v, apol_vector_comp_func * cmp, void *data)
+{
+ if (!v) {
+ errno = EINVAL;
+ return;
+ }
+ if (cmp == NULL) {
+ cmp = vector_int_comp;
+ }
+ if (v->size > 1) {
+ vector_qsort(v->array, 0, v->size - 1, cmp, data);
+ }
+}
+
+void apol_vector_sort_uniquify(apol_vector_t * v, apol_vector_comp_func * cmp, void *data)
+{
+ if (!v) {
+ errno = EINVAL;
+ return;
+ }
+ if (cmp == NULL) {
+ cmp = vector_int_comp;
+ }
+ if (v->size > 1) {
+ size_t i, j = 0;
+ void **new_array;
+ /* sweep through the array, do a quick compaction,
+ * then sort */
+ for (i = 1; i < v->size; i++) {
+ if (cmp(v->array[i], v->array[j], data) != 0) {
+ /* found a unique element */
+ j++;
+ v->array[j] = v->array[i];
+ } else {
+ /* found a non-unique element */
+ if (v->fr != NULL) {
+ v->fr(v->array[i]);
+ }
+ }
+ }
+ v->size = j + 1;
+
+ apol_vector_sort(v, cmp, data);
+ j = 0;
+ for (i = 1; i < v->size; i++) {
+ if (cmp(v->array[i], v->array[j], data) != 0) {
+ /* found a unique element */
+ j++;
+ v->array[j] = v->array[i];
+ } else {
+ /* found a non-unique element */
+ if (v->fr != NULL) {
+ v->fr(v->array[i]);
+ }
+ }
+ }
+ /* try to realloc vector to save space */
+ v->size = j + 1;
+ if ((new_array = realloc(v->array, v->size * sizeof(void *))) != NULL) {
+ v->array = new_array;
+ v->capacity = v->size;
+ }
+ }
+}
+
+int apol_vector_cat(apol_vector_t * dest, const apol_vector_t * src)
+{
+ size_t i, orig_size, cap;
+ void **a;
+ if (!src || !apol_vector_get_size(src)) {
+ return 0; /* nothing to append */
+ }
+
+ if (!dest) {
+ errno = EINVAL;
+ return -1;
+ }
+ orig_size = apol_vector_get_size(dest);
+ for (i = 0; i < apol_vector_get_size(src); i++)
+ if (apol_vector_append(dest, apol_vector_get_element(src, i))) {
+ /* revert if possible */
+ if (orig_size == 0) {
+ cap = 1;
+ } else {
+ cap = orig_size;
+ }
+ a = realloc(dest->array, cap * sizeof(*a));
+ if (a != NULL) {
+ dest->array = a;
+ }
+ dest->size = orig_size;
+ dest->capacity = cap;
+ return -1;
+ }
+
+ return 0;
+}
+
+int apol_vector_remove(apol_vector_t * v, const size_t idx)
+{
+ if (v == NULL || idx >= v->size) {
+ errno = EINVAL;
+ return -1;
+ }
+ memmove(v->array + idx, v->array + idx + 1, sizeof(v->array[0]) * (v->size - idx - 1));
+ v->size--;
+ return 0;
+}
+
+/******************** friend function below ********************/
+
+void vector_set_free_func(apol_vector_t * v, apol_vector_free_func * fr)
+{
+ v->fr = fr;
+}