diff options
Diffstat (limited to 'libapol/src')
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(<ime) == (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(<ime)) < 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, ®ex); + 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, ®ex); + 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, ®ex); + 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, ®ex); + 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, ®ex); + 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; +} |