diff options
Diffstat (limited to 'libpoldiff/src')
34 files changed, 14678 insertions, 0 deletions
diff --git a/libpoldiff/src/Makefile.am b/libpoldiff/src/Makefile.am new file mode 100644 index 0000000..8275309 --- /dev/null +++ b/libpoldiff/src/Makefile.am @@ -0,0 +1,57 @@ +lib_LIBRARIES = libpoldiff.a + +poldiffso_DATA = libpoldiff.so.@libpoldiff_version@ +poldiffsodir = $(libdir) + +AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ \ + @APOL_CFLAGS@ @QPOL_CFLAGS@ -I$(srcdir)/../include -fpic +AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ + +libpoldiff_a_SOURCES = \ + poldiff.c \ + attrib_diff.c attrib_internal.h \ + avrule_diff.c avrule_internal.h \ + bool_diff.c bool_internal.h \ + cat_diff.c cat_internal.h \ + class_diff.c class_internal.h \ + level_diff.c level_internal.h \ + range_diff.c range_internal.h \ + range_trans_diff.c range_trans_internal.h \ + rbac_diff.c rbac_internal.h \ + role_diff.c role_internal.h \ + terule_diff.c terule_internal.h \ + type_diff.c type_internal.h \ + user_diff.c user_internal.h \ + poldiff_internal.h \ + type_map.c type_map_internal.h \ + util.c + +libpoldiff_a_DEPENDENCIES = $(top_builddir)/libapol/src/libapol.so + +libpoldiff_so_OBJS = $(patsubst %.c,%.o,$(filter %.c,$(libpoldiff_a_SOURCES))) +LIBPOLDIFF_SONAME = @libpoldiff_soname@ + +dist_noinst_DATA = libpoldiff.map writing-diffs-HOWTO + +$(poldiffso_DATA): $(libpoldiff_so_OBJS) libpoldiff.map + $(CC) -shared -o $@ $(libpoldiff_so_OBJS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(LIBPOLDIFF_SONAME),--version-script=$(srcdir)/libpoldiff.map,-z,defs $(top_builddir)/libqpol/src/libqpol.so $(top_builddir)/libapol/src/libapol.so + $(LN_S) -f $@ @libpoldiff_soname@ + $(LN_S) -f $@ libpoldiff.so + +libpoldiff.so: $(poldiffso_DATA) + +$(top_builddir)/libapol/src/libapol.so: + $(MAKE) -C $(top_builddir)/libapol/src $(notdir $@) + +$(top_builddir)/libqpol/src/libqpol.so: + $(MAKE) -C $(top_builddir)/libqpol/src $(notdir $@) + +install-data-hook: + cd $(DESTDIR)$(poldiffsodir) && $(LN_S) -f $(poldiffso_DATA) @libpoldiff_soname@ + cd $(DESTDIR)$(poldiffsodir) && $(LN_S) -f $(poldiffso_DATA) libpoldiff.so + +mostlyclean-local: + -rm -rf *.gcno *.gcda *.gprof *.gcov libpoldiff.so @libpoldiff_soname@ $(poldiffso_DATA) + +uninstall-local: + -rm -rf $(DESTDIR)$(poldiffsodir)/$(poldiffso_DATA) $(DESTDIR)$(poldiffsodir)/@libpoldiff_soname@ $(DESTDIR)$(poldiffsodir)/libpoldiff.so diff --git a/libpoldiff/src/attrib_diff.c b/libpoldiff/src/attrib_diff.c new file mode 100644 index 0000000..a9802df --- /dev/null +++ b/libpoldiff/src/attrib_diff.c @@ -0,0 +1,544 @@ +/** + * @file + * Implementation for computing semantic differences in attributes. + * + * @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 "poldiff_internal.h" + +#include <apol/util.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +struct poldiff_attrib_summary +{ + size_t num_added; + size_t num_removed; + size_t num_modified; + apol_vector_t *diffs; +}; + +struct poldiff_attrib +{ + char *name; + poldiff_form_e form; + apol_vector_t *added_types; + apol_vector_t *removed_types; +}; + +void poldiff_attrib_get_stats(const poldiff_t * diff, size_t stats[5]) +{ + if (diff == NULL || stats == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return; + } + stats[0] = diff->attrib_diffs->num_added; + stats[1] = diff->attrib_diffs->num_removed; + stats[2] = diff->attrib_diffs->num_modified; + stats[3] = 0; + stats[4] = 0; +} + +char *poldiff_attrib_to_string(const poldiff_t * diff, const void *attrib) +{ + poldiff_attrib_t *at = (poldiff_attrib_t *) attrib; + size_t num_added, num_removed, len = 0, i; + char *s = NULL, *type; + if (diff == NULL || attrib == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + num_added = apol_vector_get_size(at->added_types); + num_removed = apol_vector_get_size(at->removed_types); + switch (at->form) { + case POLDIFF_FORM_ADDED: + { + if (apol_str_appendf(&s, &len, "+ %s", at->name) < 0) { + break; + } + return s; + } + case POLDIFF_FORM_REMOVED: + { + if (apol_str_appendf(&s, &len, "- %s", at->name) < 0) { + break; + } + return s; + } + case POLDIFF_FORM_MODIFIED: + { + if (apol_str_appendf(&s, &len, "* %s (", at->name) < 0) { + break; + } + if (num_added > 0 && apol_str_appendf(&s, &len, "%zd Added Type%s", num_added, (num_added == 1 ? "" : "s")) < 0) { + break; + } + if (num_removed > 0 + && apol_str_appendf(&s, &len, "%s%zd Removed Type%s", (num_added > 0 ? ", " : ""), num_removed, + (num_removed == 1 ? "" : "s")) < 0) { + break; + } + if (apol_str_append(&s, &len, ")\n") < 0) { + break; + } + for (i = 0; i < apol_vector_get_size(at->added_types); i++) { + type = (char *)apol_vector_get_element(at->added_types, i); + if (apol_str_appendf(&s, &len, "\t+ %s\n", type) < 0) { + goto err; + } + } + for (i = 0; i < apol_vector_get_size(at->removed_types); i++) { + type = (char *)apol_vector_get_element(at->removed_types, i); + if (apol_str_appendf(&s, &len, "\t- %s\n", type) < 0) { + goto err; + } + } + return s; + } + default: + { + ERR(diff, "%s", strerror(ENOTSUP)); + errno = ENOTSUP; + return NULL; + } + } + err: + /* if this is reached then an error occurred */ + free(s); + ERR(diff, "%s", strerror(ENOMEM)); + errno = ENOMEM; + return NULL; +} + +const apol_vector_t *poldiff_get_attrib_vector(const poldiff_t * diff) +{ + if (diff == NULL) { + errno = EINVAL; + return NULL; + } + return diff->attrib_diffs->diffs; +} + +const char *poldiff_attrib_get_name(const poldiff_attrib_t * attrib) +{ + if (attrib == NULL) { + errno = EINVAL; + return NULL; + } + return attrib->name; +} + +poldiff_form_e poldiff_attrib_get_form(const void *attrib) +{ + if (attrib == NULL) { + errno = EINVAL; + return 0; + } + return ((const poldiff_attrib_t *)attrib)->form; +} + +const apol_vector_t *poldiff_attrib_get_added_types(const poldiff_attrib_t * attrib) +{ + if (attrib == NULL) { + errno = EINVAL; + return NULL; + } + return attrib->added_types; +} + +const apol_vector_t *poldiff_attrib_get_removed_types(const poldiff_attrib_t * attrib) +{ + if (attrib == NULL) { + errno = EINVAL; + return NULL; + } + return attrib->removed_types; +} + +/*************** protected functions for attribs ***************/ + +static void attrib_free(void *elem) +{ + if (elem != NULL) { + poldiff_attrib_t *t = (poldiff_attrib_t *) elem; + free(t->name); + apol_vector_destroy(&t->added_types); + apol_vector_destroy(&t->removed_types); + free(t); + } +} + +poldiff_attrib_summary_t *attrib_summary_create(void) +{ + poldiff_attrib_summary_t *rs = calloc(1, sizeof(*rs)); + if (rs == NULL) { + return NULL; + } + if ((rs->diffs = apol_vector_create(attrib_free)) == NULL) { + attrib_summary_destroy(&rs); + return NULL; + } + return rs; +} + +void attrib_summary_destroy(poldiff_attrib_summary_t ** rs) +{ + if (rs != NULL && *rs != NULL) { + apol_vector_destroy(&(*rs)->diffs); + free(*rs); + *rs = NULL; + } +} + +int attrib_reset(poldiff_t * diff) +{ + int error = 0; + + if (diff == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + attrib_summary_destroy(&diff->attrib_diffs); + diff->attrib_diffs = attrib_summary_create(); + if (diff->attrib_diffs == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + errno = error; + return -1; + } + + return 0; +} + +/** + * Comparison function for two attribs from the same policy. + */ +static int attrib_name_comp(const void *x, const void *y, void *arg) +{ + const qpol_type_t *r1 = x; + const qpol_type_t *r2 = y; + apol_policy_t *p = (apol_policy_t *) arg; + qpol_policy_t *q = apol_policy_get_qpol(p); + const char *name1, *name2; + if (qpol_type_get_name(q, r1, &name1) < 0 || qpol_type_get_name(q, r2, &name2) < 0) { + return 0; + } + return strcmp(name1, name2); +} + +apol_vector_t *attrib_get_items(poldiff_t * diff, const apol_policy_t * policy) +{ + qpol_iterator_t *iter = NULL; + apol_vector_t *v = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policy); + int error = 0; + if (qpol_policy_get_type_iter(q, &iter) < 0) { + return NULL; + } + v = apol_vector_create(NULL); + if (v == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + qpol_iterator_destroy(&iter); + errno = error; + return NULL; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + unsigned char isattr; + qpol_type_t *type; + qpol_iterator_get_item(iter, (void **)&type); + qpol_type_get_isattr(q, type, &isattr); + if (isattr) { + apol_vector_append(v, type); + } + } + qpol_iterator_destroy(&iter); + apol_vector_sort(v, attrib_name_comp, (void *)policy); + return v; +} + +int attrib_comp(const void *x, const void *y, const poldiff_t * diff) +{ + const qpol_type_t *r1 = x; + const qpol_type_t *r2 = y; + const char *name1, *name2; + if (qpol_type_get_name(diff->orig_qpol, r1, &name1) < 0 || qpol_type_get_name(diff->mod_qpol, r2, &name2) < 0) { + return 0; + } + return strcmp(name1, name2); +} + +/** + * Allocate and return a new attrib difference object. + * + * @param diff Policy diff error handler. + * @param form Form of the difference. + * @param name Name of the attrib that is different. + * + * @return A newly allocated and initialized diff, or NULL upon error. + * The caller is responsible for calling attrib_free() upon the returned + * value. + */ +static poldiff_attrib_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name) +{ + poldiff_attrib_t *pr; + int error; + if ((pr = calloc(1, sizeof(*pr))) == NULL || + (pr->name = strdup(name)) == NULL || + (pr->added_types = apol_vector_create_with_capacity(1, free)) == NULL || + (pr->removed_types = apol_vector_create_with_capacity(1, free)) == NULL) { + error = errno; + attrib_free(pr); + ERR(diff, "%s", strerror(error)); + errno = error; + return NULL; + } + pr->form = form; + return pr; +} + +int attrib_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + const qpol_type_t *r = item; + const char *name = NULL; + poldiff_attrib_t *pr; + int error; + if ((form == POLDIFF_FORM_ADDED && + qpol_type_get_name(diff->mod_qpol, r, &name) < 0) || + ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_type_get_name(diff->orig_qpol, r, &name) < 0)) + { + return -1; + } + pr = make_diff(diff, form, name); + if (pr == NULL) { + return -1; + } + if (apol_vector_append(diff->attrib_diffs->diffs, pr) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + attrib_free(pr); + errno = error; + return -1; + } + if (form == POLDIFF_FORM_ADDED) { + diff->attrib_diffs->num_added++; + } else { + diff->attrib_diffs->num_removed++; + } + return 0; +} + +/** + * Given a attrib, return an unsorted vector of its allowed types (in + * the form of uint32_t corresponding to pseudo-type values). + * + * @param diff Policy diff error handler. + * @param attrib Attrib whose types to get. + * @param which Which policy, one of POLDIFF_POLICY_ORIG or + * POLDIFF_POLICY_MOD. + * + * @return Vector of pseudo-type values. The caller is responsible + * for calling apol_vector_destroy(). On error, return NULL. + */ +static apol_vector_t *attrib_get_types(const poldiff_t * diff, const qpol_type_t * attrib, int which) +{ + qpol_iterator_t *iter = NULL; + const qpol_type_t *type; + uint32_t new_val; + apol_vector_t *v = NULL; + int retval = -1, error = 0; + + if ((v = apol_vector_create(NULL)) == NULL) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + if (which == POLDIFF_POLICY_ORIG) { + if (qpol_type_get_type_iter(diff->orig_qpol, attrib, &iter) < 0) { + goto cleanup; + } + } else { + if (qpol_type_get_type_iter(diff->mod_qpol, attrib, &iter) < 0) { + goto cleanup; + } + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&type) < 0 || (new_val = type_map_lookup(diff, type, which)) == 0) { + error = errno; + goto cleanup; + } + if (apol_vector_append(v, (void *)((size_t) new_val)) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + if (retval < 0) { + apol_vector_destroy(&v); + errno = error; + return NULL; + } + return v; +} + +int attrib_deep_diff(poldiff_t * diff, const void *x, const void *y) +{ + const qpol_type_t *r1 = x; + const qpol_type_t *r2 = y; + apol_vector_t *v1 = NULL, *v2 = NULL; + apol_vector_t *added_types = NULL, *removed_types = NULL; + const apol_vector_t *reverse_v; + const char *name; + char *new_name; + uint32_t t1, t2; + poldiff_attrib_t *r = NULL; + qpol_type_t *t; + size_t i, j; + int retval = -1, error = 0; + if (qpol_type_get_name(diff->orig_qpol, r1, &name) < 0 || + (v1 = attrib_get_types(diff, r1, POLDIFF_POLICY_ORIG)) == NULL || + (v2 = attrib_get_types(diff, r2, POLDIFF_POLICY_MOD)) == NULL) { + error = errno; + goto cleanup; + } + apol_vector_sort_uniquify(v1, NULL, NULL); + apol_vector_sort_uniquify(v2, NULL, NULL); + if ((added_types = apol_vector_create(NULL)) == NULL || (removed_types = apol_vector_create(NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + for (i = j = 0; i < apol_vector_get_size(v1);) { + if (j >= apol_vector_get_size(v2)) + break; + t1 = (uint32_t) ((size_t) apol_vector_get_element(v1, i)); + t2 = (uint32_t) ((size_t) apol_vector_get_element(v2, j)); + if (t2 > t1) { + if (apol_vector_append(removed_types, (void *)((size_t) t1)) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + i++; + } else if (t1 > t2) { + if (apol_vector_append(added_types, (void *)((size_t) t2)) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + j++; + } else { + i++; + j++; + } + } + for (; i < apol_vector_get_size(v1); i++) { + t1 = (uint32_t) ((size_t) apol_vector_get_element(v1, i)); + if (apol_vector_append(removed_types, (void *)((size_t) t1)) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + for (; j < apol_vector_get_size(v2); j++) { + t2 = (uint32_t) ((size_t) apol_vector_get_element(v2, j)); + if (apol_vector_append(added_types, (void *)((size_t) t2)) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + if (apol_vector_get_size(added_types) > 0 || apol_vector_get_size(removed_types) > 0) { + if ((r = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) { + error = errno; + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(removed_types); i++) { + t1 = (uint32_t) ((size_t) apol_vector_get_element(removed_types, i)); + if ((reverse_v = type_map_lookup_reverse(diff, t1, POLDIFF_POLICY_ORIG)) == NULL) { + error = errno; + goto cleanup; + } + for (j = 0; j < apol_vector_get_size(reverse_v); j++) { + t = (qpol_type_t *) apol_vector_get_element(reverse_v, j); + if (qpol_type_get_name(diff->orig_qpol, t, &name) < 0) { + error = errno; + goto cleanup; + } + if ((new_name = strdup(name)) == NULL || apol_vector_append(r->removed_types, new_name) < 0) { + error = errno; + free(new_name); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + } + for (i = 0; i < apol_vector_get_size(added_types); i++) { + t2 = (uint32_t) ((size_t) apol_vector_get_element(added_types, i)); + if ((reverse_v = type_map_lookup_reverse(diff, t2, POLDIFF_POLICY_MOD)) == NULL) { + error = errno; + goto cleanup; + } + for (j = 0; j < apol_vector_get_size(reverse_v); j++) { + t = (qpol_type_t *) apol_vector_get_element(reverse_v, j); + if (qpol_type_get_name(diff->mod_qpol, t, &name) < 0) { + error = errno; + goto cleanup; + } + if ((new_name = strdup(name)) == NULL || apol_vector_append(r->added_types, new_name) < 0) { + error = errno; + free(new_name); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + } + apol_vector_sort(r->removed_types, apol_str_strcmp, NULL); + apol_vector_sort(r->added_types, apol_str_strcmp, NULL); + if (apol_vector_append(diff->attrib_diffs->diffs, r) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + diff->attrib_diffs->num_modified++; + } + retval = 0; + cleanup: + apol_vector_destroy(&v1); + apol_vector_destroy(&v2); + apol_vector_destroy(&added_types); + apol_vector_destroy(&removed_types); + if (retval != 0) { + attrib_free(r); + } + errno = error; + return retval; +} diff --git a/libpoldiff/src/attrib_internal.h b/libpoldiff/src/attrib_internal.h new file mode 100644 index 0000000..75d1e1d --- /dev/null +++ b/libpoldiff/src/attrib_internal.h @@ -0,0 +1,121 @@ +/** + * @file + * Protected interface for attribute differences. + * + * @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 POLDIFF_ATTRIB_INTERNAL_H +#define POLDIFF_ATTRIB_INTERNAL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct poldiff_attrib_summary poldiff_attrib_summary_t; + +/** + * Allocate and return a new poldiff_attrib_summary_t object. + * + * @return A new attrib summary. The caller must call attrib_destroy() + * afterwards. On error, return NULL and set errno. + */ + poldiff_attrib_summary_t *attrib_summary_create(void); + +/** + * Deallocate all space associated with a poldiff_attrib_summary_t + * object, including the pointer itself. If the pointer is already + * NULL then do nothing. + * + * @param us Reference to a attrib summary to destroy. The pointer + * will be set to NULL afterwards. + */ + void attrib_summary_destroy(poldiff_attrib_summary_t ** us); + +/** + * Reset the state of all attribute differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int attrib_reset(poldiff_t * diff); + +/** + * Get a vector of all attribs from the given policy, sorted by name. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * + * @return A newly allocated vector of all attribs. The caller is + * responsible for calling apol_vector_destroy() afterwards. On + * error, return NULL and set errno. + */ + apol_vector_t *attrib_get_items(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Compare two qpol_attrib_t objects, determining if they have the same + * name or not. + * + * @param x The attrib from the original policy. + * @param y The attrib from the modified policy. + * @param diff The policy difference structure associated with both + * policies. + * + * @return < 0, 0, or > 0 if attrib x is respectively less than, equal + * to, or greater than attrib y. + */ + int attrib_comp(const void *x, const void *y, const poldiff_t * diff); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a attrib. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int attrib_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Compute the semantic difference of two attribs for which the compare + * callback returns 0. If a difference is found then allocate, + * initialize, and insert a new semantic difference entry for that + * attrib. + * + * @param diff The policy difference structure associated with both + * attribs and to which to add an entry if needed. + * @param x The attrib from the original policy. + * @param y The attrib from the modified policy. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int attrib_deep_diff(poldiff_t * diff, const void *x, const void *y); + +#ifdef __cplusplus +} +#endif + +#endif /* POLDIFF_ATTRIB_INTERNAL_H */ diff --git a/libpoldiff/src/avrule_diff.c b/libpoldiff/src/avrule_diff.c new file mode 100644 index 0000000..a1b61b1 --- /dev/null +++ b/libpoldiff/src/avrule_diff.c @@ -0,0 +1,1636 @@ +/** + * @file + * Implementation for computing semantic differences in AV and Type + * rules. + * + * @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 "poldiff_internal.h" + +#include <apol/policy-query.h> +#include <apol/util.h> +#include <qpol/policy_extend.h> +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +struct poldiff_avrule_summary +{ + size_t num_added; + size_t num_removed; + size_t num_modified; + size_t num_added_type; + size_t num_removed_type; + int diffs_sorted; + /** vector of poldiff_avrule_t */ + apol_vector_t *diffs; +}; + +struct poldiff_avrule +{ + uint32_t spec; + /* pointer into policy's symbol table */ + const char *source, *target; + /** the class string is pointer into the class_bst BST */ + char *cls; + poldiff_form_e form; + /** vector of pointers into the perm_bst BST (char *) */ + apol_vector_t *unmodified_perms; + /** vector of pointers into the perm_bst BST (char *) */ + apol_vector_t *added_perms; + /** vector of pointers into the perm_bst BST (char *) */ + apol_vector_t *removed_perms; + /** pointer into policy's conditional list, needed to render + * conditional expressions */ + const qpol_cond_t *cond; + uint32_t branch; + /** vector of unsigned longs of line numbers from original policy */ + apol_vector_t *orig_linenos; + /** vector of unsigned longs of line numbers from modified policy */ + apol_vector_t *mod_linenos; + /** array of pointers for original rules */ + qpol_avrule_t **orig_rules; + size_t num_orig_rules; + /** array of pointers for modified rules */ + qpol_avrule_t **mod_rules; + size_t num_mod_rules; +}; + +typedef struct pseudo_avrule +{ + uint32_t spec; + /** pseudo-type values */ + uint32_t source, target; + /** pointer into the class_bst BST */ + char *cls; + /** array of pointers into the perm_bst BST */ + /* (use an array here to save space) */ + char **perms; + size_t num_perms; + /** array of pointers into the bool_bst BST */ + char *bools[5]; + uint32_t bool_val; + uint32_t branch; + /** pointer into policy's conditional list, needed to render + * conditional expressions */ + const qpol_cond_t *cond; + /** array of qpol_avrule_t pointers, for showing line numbers */ + const qpol_avrule_t **rules; + size_t num_rules; +} pseudo_avrule_t; + +/******************** public avrule functions ********************/ + +/** + * Get an array of statistics for the number of differences of each + * form for av rules. + * + * @param diff The policy difference structure from which to get the + * stats. + * @param stats Array into which to write the numbers (array must be + * pre-allocated). The order of the values written to the array is + * as follows: number of items of form POLDIFF_FORM_ADDED, number of + * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of + * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE. + * @param idx Index into the avrule differences specifying which + * avrule type to get, one of AVRULE_OFFSET_ALLOW, etc. + */ +static void poldiff_avrule_get_stats(const poldiff_t * diff, size_t stats[5], avrule_offset_e idx) +{ + if (diff == NULL || stats == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return; + } + stats[0] = diff->avrule_diffs[idx]->num_added; + stats[1] = diff->avrule_diffs[idx]->num_removed; + stats[2] = diff->avrule_diffs[idx]->num_modified; + stats[3] = diff->avrule_diffs[idx]->num_added_type; + stats[4] = diff->avrule_diffs[idx]->num_removed_type; +} + +void poldiff_avrule_get_stats_allow(const poldiff_t * diff, size_t stats[5]) +{ + poldiff_avrule_get_stats(diff, stats, AVRULE_OFFSET_ALLOW); +} + +void poldiff_avrule_get_stats_neverallow(const poldiff_t * diff, size_t stats[5]) +{ + poldiff_avrule_get_stats(diff, stats, AVRULE_OFFSET_NEVERALLOW); +} + +void poldiff_avrule_get_stats_dontaudit(const poldiff_t * diff, size_t stats[5]) +{ + poldiff_avrule_get_stats(diff, stats, AVRULE_OFFSET_DONTAUDIT); +} + +void poldiff_avrule_get_stats_auditallow(const poldiff_t * diff, size_t stats[5]) +{ + poldiff_avrule_get_stats(diff, stats, AVRULE_OFFSET_AUDITALLOW); +} + +char *poldiff_avrule_to_string(const poldiff_t * diff, const void *avrule) +{ + const poldiff_avrule_t *pa = (const poldiff_avrule_t *)avrule; + apol_policy_t *p; + const char *rule_type; + char *diff_char = "", *s = NULL, *perm_name, *cond_expr = NULL; + size_t i, len = 0; + int show_perm_sym = 0, error; + if (diff == NULL || avrule == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + switch (pa->form) { + case POLDIFF_FORM_ADDED: + case POLDIFF_FORM_ADD_TYPE: + { + diff_char = "+"; + p = diff->mod_pol; + break; + } + case POLDIFF_FORM_REMOVED: + case POLDIFF_FORM_REMOVE_TYPE: + { + diff_char = "-"; + p = diff->orig_pol; + break; + } + case POLDIFF_FORM_MODIFIED: + { + diff_char = "*"; + p = diff->orig_pol; + show_perm_sym = 1; + break; + } + default: + { + ERR(diff, "%s", strerror(ENOTSUP)); + errno = ENOTSUP; + return NULL; + } + } + rule_type = apol_rule_type_to_str(pa->spec); + if (apol_str_appendf(&s, &len, "%s %s %s %s : %s {", diff_char, rule_type, pa->source, pa->target, pa->cls) < 0) { + error = errno; + goto err; + } + for (i = 0; pa->unmodified_perms != NULL && i < apol_vector_get_size(pa->unmodified_perms); i++) { + perm_name = (char *)apol_vector_get_element(pa->unmodified_perms, i); + if (apol_str_appendf(&s, &len, " %s", perm_name) < 0) { + error = errno; + goto err; + } + } + for (i = 0; pa->added_perms != NULL && i < apol_vector_get_size(pa->added_perms); i++) { + perm_name = (char *)apol_vector_get_element(pa->added_perms, i); + if (apol_str_appendf(&s, &len, " %s%s", (show_perm_sym ? "+" : ""), perm_name) < 0) { + error = errno; + goto err; + } + } + for (i = 0; pa->removed_perms != NULL && i < apol_vector_get_size(pa->removed_perms); i++) { + perm_name = (char *)apol_vector_get_element(pa->removed_perms, i); + if (apol_str_appendf(&s, &len, " %s%s", (show_perm_sym ? "-" : ""), perm_name) < 0) { + error = errno; + goto err; + } + } + if (apol_str_append(&s, &len, " };") < 0) { + error = errno; + goto err; + } + if (pa->cond != NULL) { + if ((cond_expr = apol_cond_expr_render(p, pa->cond)) == NULL) { + error = errno; + goto err; + } + if (apol_str_appendf(&s, &len, " [%s]:%s", cond_expr, (pa->branch ? "TRUE" : "FALSE")) < 0) { + error = errno; + goto err; + } + free(cond_expr); + } + return s; + err: + free(s); + free(cond_expr); + ERR(diff, "%s", strerror(error)); + errno = error; + return NULL; +} + +/** + * Sort poldiff_avrule diff results in a mostly alphabetical order. + */ +static int poldiff_avrule_cmp(const void *x, const void *y, void *data __attribute__ ((unused))) +{ + const poldiff_avrule_t *a = (const poldiff_avrule_t *)x; + const poldiff_avrule_t *b = (const poldiff_avrule_t *)y; + int compval; + if (a->spec != b->spec) { + const char *rule_type1 = apol_rule_type_to_str(a->spec); + const char *rule_type2 = apol_rule_type_to_str(b->spec); + compval = strcmp(rule_type1, rule_type2); + if (compval != 0) { + return compval; + } + } + if ((compval = strcmp(a->source, b->source)) != 0) { + return compval; + } + if ((compval = strcmp(a->target, b->target)) != 0) { + return compval; + } + if ((compval = strcmp(a->cls, b->cls)) != 0) { + return compval; + } + if (a->cond != b->cond) { + return (char *)a->cond - (char *)b->cond; + } + /* sort true branch before false branch */ + return b->branch - a->branch; +} + +/** + * Get the vector of av rule differences from the av rule difference + * summary. + * + * @param diff The policy difference structure associated with the av + * rule difference summary. + * @param idx Index into the avrule differences specifying which + * avrule type to get, one of AVRULE_OFFSET_ALLOW, etc. + * + * @return A vector of elements of type poldiff_avrule_t, or NULL on + * error. The caller should <b>not</b> destroy the vector returned. + * If the call fails, errno will be set. + */ +static const apol_vector_t *poldiff_get_avrule_vector(const poldiff_t * diff, avrule_offset_e idx) +{ + if (diff == NULL) { + errno = EINVAL; + return NULL; + } + if (diff->avrule_diffs[idx]->diffs_sorted == 0) { + apol_vector_sort(diff->avrule_diffs[idx]->diffs, poldiff_avrule_cmp, NULL); + diff->avrule_diffs[idx]->diffs_sorted = 1; + } + return diff->avrule_diffs[idx]->diffs; +} + +const apol_vector_t *poldiff_get_avrule_vector_allow(const poldiff_t * diff) +{ + return poldiff_get_avrule_vector(diff, AVRULE_OFFSET_ALLOW); +} + +const apol_vector_t *poldiff_get_avrule_vector_auditallow(const poldiff_t * diff) +{ + return poldiff_get_avrule_vector(diff, AVRULE_OFFSET_AUDITALLOW); +} + +const apol_vector_t *poldiff_get_avrule_vector_dontaudit(const poldiff_t * diff) +{ + return poldiff_get_avrule_vector(diff, AVRULE_OFFSET_DONTAUDIT); +} + +const apol_vector_t *poldiff_get_avrule_vector_neverallow(const poldiff_t * diff) +{ + return poldiff_get_avrule_vector(diff, AVRULE_OFFSET_NEVERALLOW); +} + +poldiff_form_e poldiff_avrule_get_form(const void *avrule) +{ + if (avrule == NULL) { + errno = EINVAL; + return 0; + } + return ((const poldiff_avrule_t *)avrule)->form; +} + +uint32_t poldiff_avrule_get_rule_type(const poldiff_avrule_t * avrule) +{ + if (avrule == NULL) { + errno = EINVAL; + return 0; + } + return avrule->spec; +} + +const char *poldiff_avrule_get_source_type(const poldiff_avrule_t * avrule) +{ + if (avrule == NULL) { + errno = EINVAL; + return 0; + } + return avrule->source; +} + +const char *poldiff_avrule_get_target_type(const poldiff_avrule_t * avrule) +{ + if (avrule == NULL) { + errno = EINVAL; + return 0; + } + return avrule->target; +} + +const char *poldiff_avrule_get_object_class(const poldiff_avrule_t * avrule) +{ + if (avrule == NULL) { + errno = EINVAL; + return 0; + } + return avrule->cls; +} + +void poldiff_avrule_get_cond(const poldiff_t * diff, const poldiff_avrule_t * avrule, + const qpol_cond_t ** cond, uint32_t * which_list, const apol_policy_t ** p) +{ + if (diff == NULL || avrule == NULL || cond == NULL || p == NULL) { + errno = EINVAL; + return; + } + *cond = avrule->cond; + if (*cond == NULL) { + *which_list = 1; + *p = NULL; + } else if (avrule->form == POLDIFF_FORM_ADDED || avrule->form == POLDIFF_FORM_ADD_TYPE) { + *which_list = avrule->branch; + *p = diff->mod_pol; + } else { + *which_list = avrule->branch; + *p = diff->orig_pol; + } +} + +const apol_vector_t *poldiff_avrule_get_unmodified_perms(const poldiff_avrule_t * avrule) +{ + if (avrule == NULL) { + errno = EINVAL; + return NULL; + } + return avrule->unmodified_perms; +} + +const apol_vector_t *poldiff_avrule_get_added_perms(const poldiff_avrule_t * avrule) +{ + if (avrule == NULL) { + errno = EINVAL; + return NULL; + } + return avrule->added_perms; +} + +const apol_vector_t *poldiff_avrule_get_removed_perms(const poldiff_avrule_t * avrule) +{ + if (avrule == NULL) { + errno = EINVAL; + return NULL; + } + return avrule->removed_perms; +} + +const apol_vector_t *poldiff_avrule_get_orig_line_numbers(const poldiff_avrule_t * avrule) +{ + if (avrule == NULL) { + errno = EINVAL; + return NULL; + } + return avrule->orig_linenos; +} + +const apol_vector_t *poldiff_avrule_get_mod_line_numbers(const poldiff_avrule_t * avrule) +{ + if (avrule == NULL) { + errno = EINVAL; + return NULL; + } + return avrule->mod_linenos; +} + +/** + * Get the line numbers from an array of qpol_avrule_t that contain + * the given permission. + */ +static apol_vector_t *avrule_get_line_numbers_for_perm(const poldiff_t * diff, const char *perm, const qpol_policy_t * q, + qpol_avrule_t ** rules, const size_t num_rules) +{ + apol_vector_t *v = NULL; + qpol_iterator_t *syn_iter = NULL, *perm_iter = NULL; + size_t i; + int error = 0; + + if ((v = apol_vector_create(NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + for (i = 0; i < num_rules; i++) { + if (qpol_avrule_get_syn_avrule_iter(q, rules[i], &syn_iter) < 0) { + error = errno; + goto cleanup; + } + for (; !qpol_iterator_end(syn_iter); qpol_iterator_next(syn_iter)) { + qpol_syn_avrule_t *syn_rule; + qpol_iterator_get_item(syn_iter, (void **)&syn_rule); + if (qpol_syn_avrule_get_perm_iter(q, syn_rule, &perm_iter) < 0) { + error = errno; + goto cleanup; + } + for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) { + char *syn_perm; + qpol_iterator_get_item(perm_iter, (void **)&syn_perm); + if (strcmp(perm, syn_perm) == 0) { + unsigned long lineno; + qpol_syn_avrule_get_lineno(q, syn_rule, &lineno); + if (apol_vector_append(v, (void *)lineno) < 0) { + ERR(diff, "%s", strerror(errno)); + } + break; + } + } + qpol_iterator_destroy(&perm_iter); + } + qpol_iterator_destroy(&syn_iter); + } + apol_vector_sort_uniquify(v, NULL, NULL); + cleanup: + qpol_iterator_destroy(&syn_iter); + qpol_iterator_destroy(&perm_iter); + if (error != 0) { + apol_vector_destroy(&v); + errno = error; + return NULL; + } + return v; +} + +apol_vector_t *poldiff_avrule_get_orig_line_numbers_for_perm(const poldiff_t * diff, const poldiff_avrule_t * avrule, + const char *perm) +{ + if (diff == NULL || avrule == NULL || perm == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + if (!diff->line_numbers_enabled || avrule->form == POLDIFF_FORM_ADDED || avrule->form == POLDIFF_FORM_ADD_TYPE) { + return NULL; + } + if (avrule->num_orig_rules == 0) { + return NULL; + } + return avrule_get_line_numbers_for_perm(diff, perm, diff->orig_qpol, avrule->orig_rules, avrule->num_orig_rules); +} + +apol_vector_t *poldiff_avrule_get_mod_line_numbers_for_perm(const poldiff_t * diff, const poldiff_avrule_t * avrule, + const char *perm) +{ + if (diff == NULL || avrule == NULL || perm == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + if (!diff->line_numbers_enabled || avrule->form == POLDIFF_FORM_REMOVED || avrule->form == POLDIFF_FORM_REMOVE_TYPE) { + return NULL; + } + if (avrule->num_mod_rules == 0) { + return NULL; + } + return avrule_get_line_numbers_for_perm(diff, perm, diff->mod_qpol, avrule->mod_rules, avrule->num_mod_rules); +} + +/******************** protected functions below ********************/ + +/** + * Free all space used by a poldiff_avrule_t, including the pointer + * itself. Does nothing if the pointer is already NULL. + * + * @param elem Pointer to a poldiff_avrule_t. + */ +static void poldiff_avrule_free(void *elem) +{ + if (elem != NULL) { + poldiff_avrule_t *a = (poldiff_avrule_t *) elem; + apol_vector_destroy(&a->unmodified_perms); + apol_vector_destroy(&a->added_perms); + apol_vector_destroy(&a->removed_perms); + apol_vector_destroy(&a->orig_linenos); + apol_vector_destroy(&a->mod_linenos); + free(a->orig_rules); + free(a->mod_rules); + free(a); + } +} + +poldiff_avrule_summary_t *avrule_create(void) +{ + poldiff_avrule_summary_t *rs = calloc(1, sizeof(*rs)); + if (rs == NULL) { + return NULL; + } + if ((rs->diffs = apol_vector_create(poldiff_avrule_free)) == NULL) { + avrule_destroy(&rs); + return NULL; + } + return rs; +} + +void avrule_destroy(poldiff_avrule_summary_t ** rs) +{ + if (rs != NULL && *rs != NULL) { + apol_vector_destroy(&(*rs)->diffs); + free(*rs); + *rs = NULL; + } +} + +/** + * Reset the state of an AV rule differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @param idx Index into the avrule diffs array indicating which rule + * type to reset, one of AVRULE_OFFSET_ALLOW, etc. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ +static int avrule_reset(poldiff_t * diff, avrule_offset_e idx) +{ + int error = 0; + + avrule_destroy(&diff->avrule_diffs[idx]); + diff->avrule_diffs[idx] = avrule_create(); + if (diff->avrule_diffs[idx] == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + errno = error; + return -1; + } + + return 0; +} + +int avrule_reset_allow(poldiff_t * diff) +{ + return avrule_reset(diff, AVRULE_OFFSET_ALLOW); +} + +int avrule_reset_auditallow(poldiff_t * diff) +{ + return avrule_reset(diff, AVRULE_OFFSET_AUDITALLOW); +} + +int avrule_reset_dontaudit(poldiff_t * diff) +{ + return avrule_reset(diff, AVRULE_OFFSET_DONTAUDIT); +} + +int avrule_reset_neverallow(poldiff_t * diff) +{ + return avrule_reset(diff, AVRULE_OFFSET_NEVERALLOW); +} + +static void avrule_free_item(void *item) +{ + pseudo_avrule_t *a = (pseudo_avrule_t *) item; + if (item != NULL) { + free(a->perms); + free(a->rules); + free(a); + } +} + +/** + * Apply an ordering scheme to two pseudo-av rules. + * + * <ul> + * <li>Sort by target pseudo-type value, + * <li>Then by source pseudo-type value, + * <li>Then by object class's BST's pointer value, + * <li>Then by rule specified (allow, neverallow, etc.), + * <li>Then choose unconditional rules over conditional rules, + * <li>Then by conditional expression's BST's boolean pointer value. + * </ul> + * + * If this function is being used for sorting (via avrule_get_items()) + * then sort by truth value, and then by branch (true branch, then + * false branch). Otherwise, when comparing rules (via avrule_comp()) + * then by truth value, inverting rule2's value if in the other + * branch. + */ +static int pseudo_avrule_comp(const pseudo_avrule_t * rule1, const pseudo_avrule_t * rule2, int is_sorting) +{ + size_t i; + uint32_t bool_val; + if (rule1->target != rule2->target) { + return rule1->target - rule2->target; + } + if (rule1->source != rule2->source) { + return rule1->source - rule2->source; + } + if (rule1->cls != rule2->cls) { + return (int)(rule1->cls - rule2->cls); + } + if (rule1->spec != rule2->spec) { + return rule1->spec - rule2->spec; + } + if (rule1->bools[0] == NULL && rule2->bools[0] == NULL) { + /* both rules are unconditional */ + return 0; + } else if (rule1->bools[0] == NULL && rule2->bools[0] != NULL) { + /* unconditional rules come before conditional */ + return -1; + } else if (rule1->bools[0] != NULL && rule2->bools[0] == NULL) { + /* unconditional rules come before conditional */ + return 1; + } + for (i = 0; i < (sizeof(rule1->bools) / sizeof(rule1->bools[0])); i++) { + if (rule1->bools[i] != rule2->bools[i]) { + return (int)(rule1->bools[i] - rule2->bools[i]); + } + } + if (is_sorting) { + if (rule1->branch != rule2->branch) { + return rule1->branch - rule2->branch; + } + return (int)rule1->bool_val - (int)rule2->bool_val; + } else { + if (rule1->branch == rule2->branch) { + bool_val = rule2->bool_val; + } else { + bool_val = ~rule2->bool_val; + } + if (rule1->bool_val < bool_val) { + return -1; + } else if (rule1->bool_val > bool_val) { + return 1; + } + return 0; + } +} + +static int avrule_bst_comp(const void *x, const void *y, void *data __attribute__ ((unused))) +{ + const pseudo_avrule_t *r1 = (const pseudo_avrule_t *)x; + const pseudo_avrule_t *r2 = (const pseudo_avrule_t *)y; + return pseudo_avrule_comp(r1, r2, 1); +} + +/** + * Given a conditional expression, convert its booleans to a sorted + * array of pseudo-boolean values, assign that array to the + * pseudo-avrule key, and then derive the truth table. + * + * @param diff Policy difference structure. + * @param p Policy containing conditional. + * @param cond Conditional expression to convert. + * @param key Location to write converted expression. + */ +static int avrule_build_cond(poldiff_t * diff, const apol_policy_t * p, const qpol_cond_t * cond, pseudo_avrule_t * key) +{ + qpol_iterator_t *iter = NULL; + qpol_cond_expr_node_t *node; + uint32_t expr_type, truthiness; + qpol_bool_t *bools[5] = { NULL, NULL, NULL, NULL, NULL }, *qbool; + size_t i, j; + size_t num_bools = 0; + const char *bool_name; + char *pseudo_bool, *t; + qpol_policy_t *q = apol_policy_get_qpol(p); + int retval = -1, error = 0, compval; + if (qpol_cond_get_expr_node_iter(q, cond, &iter) < 0) { + error = errno; + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&node) < 0 || qpol_cond_expr_node_get_expr_type(q, node, &expr_type) < 0) { + error = errno; + goto cleanup; + } + if (expr_type != QPOL_COND_EXPR_BOOL) { + continue; + } + if (qpol_cond_expr_node_get_bool(q, node, &qbool) < 0) { + error = errno; + goto cleanup; + } + for (i = 0; i < num_bools; i++) { + if (bools[i] == qbool) { + break; + } + } + if (i >= num_bools) { + assert(i < 5); + bools[i] = qbool; + num_bools++; + } + } + for (i = 0; i < num_bools; i++) { + if (qpol_bool_get_name(q, bools[i], &bool_name) < 0) { + error = errno; + goto cleanup; + } + if (apol_bst_get_element(diff->bool_bst, (void *)bool_name, NULL, (void **)&pseudo_bool) < 0) { + error = EBADRQC; /* should never get here */ + ERR(diff, "%s", strerror(error)); + assert(0); + goto cleanup; + } + key->bools[i] = pseudo_bool; + } + + /* bubble sorth the pseudo bools (not bad because there are at + * most five elements */ + for (i = num_bools; i > 1; i--) { + for (j = 1; j < i; j++) { + compval = strcmp(key->bools[j - 1], key->bools[j]); + if (compval > 0) { + t = key->bools[j]; + key->bools[j] = key->bools[j - 1]; + key->bools[j - 1] = t; + qbool = bools[j]; + bools[j] = bools[j - 1]; + bools[j - 1] = qbool; + } + } + } + + /* now compute the truth table for the booleans */ + key->bool_val = 0; + for (i = 0; i < 32; i++) { + for (j = 0; j < num_bools; j++) { + int state = ((i & (1 << j)) ? 1 : 0); + if (qpol_bool_set_state_no_eval(q, bools[j], state) < 0) { + error = errno; + goto cleanup; + } + } + if (qpol_cond_eval(q, cond, &truthiness) < 0) { + error = errno; + goto cleanup; + } + key->bool_val = (key->bool_val << 1) | truthiness; + } + + key->cond = cond; + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + return retval; +} + +/** + * Bubble sort the permissions within a pseudo-avrule, sorted by + * pointer value. (Bubble-sort is fine because the number of + * permissions will usually be less than 10.) Then uniquify the list. + * + * @param key Rule whose permissions to sort. + */ +static void sort_and_uniquify_perms(pseudo_avrule_t * key) +{ + size_t i, j; + char *t; + for (i = key->num_perms; i > 1; i--) { + for (j = 1; j < i; j++) { + if (key->perms[j - 1] > key->perms[j]) { + t = key->perms[j]; + key->perms[j] = key->perms[j - 1]; + key->perms[j - 1] = t; + } + } + } + for (i = 1; i < key->num_perms; i++) { + if (key->perms[i] == key->perms[i - 1]) { + memmove(key->perms + i, key->perms + i + 1, (key->num_perms - i - 1) * sizeof(key->perms[0])); + key->num_perms--; + } + } +} + +/** + * Given a rule, construct a new pseudo-avrule and insert it into the + * BST if not already there. + * + * @param diff Policy difference structure. + * @param p Policy from which the rule came. + * @param rule AV rule to insert. + * @param source Source pseudo-type value. + * @param target Target pseudo-type value. + * @param b BST containing pseudo-avrules. + * + * @return 0 on success, < 0 on error. + */ +static int avrule_add_to_bst(poldiff_t * diff, const apol_policy_t * p, + const qpol_avrule_t * rule, uint32_t source, uint32_t target, apol_bst_t * b) +{ + pseudo_avrule_t *key, *inserted_key; + const qpol_class_t *obj_class; + qpol_iterator_t *perm_iter = NULL; + const char *class_name; + char *perm_name, *pseudo_perm, **t; + size_t num_perms; + const qpol_cond_t *cond; + qpol_policy_t *q = apol_policy_get_qpol(p); + int retval = -1, error = 0, compval; + if ((key = calloc(1, sizeof(*key))) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + if (qpol_avrule_get_rule_type(q, rule, &(key->spec)) < 0 || + qpol_avrule_get_object_class(q, rule, &obj_class) < 0 || + qpol_avrule_get_perm_iter(q, rule, &perm_iter) < 0 || qpol_avrule_get_cond(q, rule, &cond) < 0) { + error = errno; + goto cleanup; + } + if (qpol_class_get_name(q, obj_class, &class_name) < 0) { + error = errno; + goto cleanup; + } + if (apol_bst_get_element(diff->class_bst, (void *)class_name, NULL, (void **)&key->cls) < 0) { + error = EBADRQC; /* should never get here */ + ERR(diff, "%s", strerror(error)); + assert(0); + goto cleanup; + } + key->source = source; + key->target = target; + if (cond != NULL && (qpol_avrule_get_which_list(q, rule, &(key->branch)) < 0 || avrule_build_cond(diff, p, cond, key) < 0)) { + error = errno; + goto cleanup; + } + + /* insert this pseudo into the tree if not already there */ + if ((compval = apol_bst_insert_and_get(b, (void **)&key, NULL)) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + inserted_key = key; + key = NULL; + + /* append and uniquify this rule's permissions */ + if (qpol_iterator_get_size(perm_iter, &num_perms) < 0) { + error = errno; + goto cleanup; + } + if ((t = realloc(inserted_key->perms, (inserted_key->num_perms + num_perms) * sizeof(*t))) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + inserted_key->perms = t; + for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) { + if (qpol_iterator_get_item(perm_iter, (void *)&perm_name) < 0) { + error = errno; + goto cleanup; + } + if (apol_bst_get_element(diff->perm_bst, perm_name, NULL, (void **)&pseudo_perm) < 0) { + error = EBADRQC; /* should never get here */ + ERR(diff, "%s", strerror(error)); + assert(0); + free(perm_name); + goto cleanup; + } + free(perm_name); + inserted_key->perms[(inserted_key->num_perms)++] = pseudo_perm; + } + sort_and_uniquify_perms(inserted_key); + + /* store the rule pointer, to be used for showing line numbers */ + if (qpol_policy_has_capability(q, QPOL_CAP_LINE_NUMBERS)) { + const qpol_avrule_t **a = realloc(inserted_key->rules, + (inserted_key->num_rules + 1) * sizeof(*a)); + if (a == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + inserted_key->rules = a; + inserted_key->rules[inserted_key->num_rules++] = rule; + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&perm_iter); + if (retval < 0) { + avrule_free_item(key); + } + errno = error; + return retval; +} + +/** + * Given a rule, expand its source and target types into individual + * pseudo-type values. Then add the expanded rules to the BST. This + * is needed for when the source and/or target is an attribute. + * + * @param diff Policy difference structure. + * @param p Policy from which the rule came. + * @param rule AV rule to insert. + * @param b BST containing pseudo-avrules. + * + * @return 0 on success, < 0 on error. + */ +static int avrule_expand(poldiff_t * diff, const apol_policy_t * p, const qpol_avrule_t * rule, apol_bst_t * b) +{ + const qpol_type_t *source, *orig_target, *target; + unsigned char source_attr, target_attr; + qpol_iterator_t *source_iter = NULL, *target_iter = NULL; + uint32_t source_val, target_val; + qpol_policy_t *q = apol_policy_get_qpol(p); + int which = (p == diff->orig_pol ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD); + int retval = -1, error = 0; + if (qpol_avrule_get_source_type(q, rule, &source) < 0 || + qpol_avrule_get_target_type(q, rule, &orig_target) < 0 || + qpol_type_get_isattr(q, source, &source_attr) < 0 || qpol_type_get_isattr(q, orig_target, &target_attr)) { + error = errno; + goto cleanup; + } +#ifdef SETOOLS_DEBUG + const char *orig_source_name, *orig_target_name; + qpol_type_get_name(q, source, &orig_source_name); + qpol_type_get_name(q, orig_target, &orig_target_name); +#endif + + if (source_attr) { + if (qpol_type_get_type_iter(q, source, &source_iter) < 0) { + error = errno; + goto cleanup; + } + /* handle situation where a rule has as its source an + * attribute without any types */ + if (qpol_iterator_end(source_iter)) { + retval = 0; + goto cleanup; + } + } + do { + if (source_attr) { + if (qpol_iterator_get_item(source_iter, (void **)&source) < 0) { + error = errno; + goto cleanup; + } + qpol_iterator_next(source_iter); + } + if (target_attr) { + if (qpol_type_get_type_iter(q, orig_target, &target_iter) < 0) { + error = errno; + goto cleanup; + } + /* handle situation where a rule has as its + * target an attribute without any types */ + if (qpol_iterator_end(target_iter)) { + retval = 0; + goto cleanup; + } + } else { + target = orig_target; + } + do { + if (target_attr) { + if (qpol_iterator_get_item(target_iter, (void **)&target) < 0) { + error = errno; + goto cleanup; + } + qpol_iterator_next(target_iter); + } +#ifdef SETOOLS_DEBUG + const char *n1, *n2; + qpol_type_get_name(q, source, &n1); + qpol_type_get_name(q, target, &n2); +#endif + if ((source_val = type_map_lookup(diff, source, which)) == 0 || + (target_val = type_map_lookup(diff, target, which)) == 0 || + avrule_add_to_bst(diff, p, rule, source_val, target_val, b) < 0) { + error = errno; + goto cleanup; + } + } while (target_attr && !qpol_iterator_end(target_iter)); + qpol_iterator_destroy(&target_iter); + } while (source_attr && !qpol_iterator_end(source_iter)); + retval = 0; + cleanup: + qpol_iterator_destroy(&source_iter); + qpol_iterator_destroy(&target_iter); + errno = error; + return retval; +} + +/** + * Get a vector of avrules from the given policy, sorted. This + * function will remap source and target types to their pseudo-type + * value equivalents. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * @param which Kind of rule to get, one of QPOL_RULE_ALLOW, etc. + * + * @return A newly allocated vector of all av rules (of type + * pseudo_avrule_t). The caller is responsible for calling + * apol_vector_destroy() afterwards. On error, return NULL and set + * errno. + */ +static apol_vector_t *avrule_get_items(poldiff_t * diff, const apol_policy_t * policy, const unsigned int which) +{ + apol_vector_t *bools = NULL, *bool_states = NULL; + size_t i, num_rules, j; + apol_bst_t *b = NULL; + apol_vector_t *v = NULL; + qpol_iterator_t *iter = NULL; + const qpol_avrule_t *rule; + qpol_policy_t *q = apol_policy_get_qpol(policy); + int retval = -1, error = 0; + + /* special case: if getting neverallow rules if the policy + does not support it, then return an empty vector */ + if (which == QPOL_RULE_NEVERALLOW && !qpol_policy_has_capability(q, QPOL_CAP_NEVERALLOW)) { + v = apol_vector_create_with_capacity(1, avrule_free_item); + if (v == NULL) { + ERR(diff, "%s", strerror(error)); + } + return v; + } + + if (poldiff_build_bsts(diff) < 0) { + error = errno; + goto cleanup; + } + + /* store original boolean values */ + if (apol_bool_get_by_query(policy, NULL, &bools) < 0) { + error = errno; + goto cleanup; + } + if ((bool_states = apol_vector_create_with_capacity(apol_vector_get_size(bools), NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(bools); i++) { + qpol_bool_t *qbool = apol_vector_get_element(bools, i); + int state; + if (qpol_bool_get_state(q, qbool, &state) < 0) { + error = errno; + goto cleanup; + } + if (apol_vector_append(bool_states, (void *)((size_t) state)) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + if ((b = apol_bst_create(avrule_bst_comp, avrule_free_item)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + if (qpol_policy_get_avrule_iter(q, which, &iter) < 0) { + + error = errno; + goto cleanup; + } + qpol_iterator_get_size(iter, &num_rules); + for (j = 0; !qpol_iterator_end(iter); qpol_iterator_next(iter), j++) { + if (qpol_iterator_get_item(iter, (void **)&rule) < 0 || avrule_expand(diff, policy, rule, b) < 0) { + error = errno; + goto cleanup; + } + if (!(j % 1024)) { + int percent = 50 * j / num_rules + (policy == diff->mod_pol ? 50 : 0); + INFO(diff, "Computing AV rule difference: %02d%% complete", percent); + } + } + if ((v = apol_bst_get_vector(b, 1)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + retval = 0; + cleanup: + /* restore boolean states */ + for (i = 0; i < apol_vector_get_size(bools); i++) { + qpol_bool_t *qbool = apol_vector_get_element(bools, i); + int state = (int)((size_t) apol_vector_get_element(bool_states, i)); + qpol_bool_set_state_no_eval(q, qbool, state); + } + qpol_policy_reevaluate_conds(q); + apol_vector_destroy(&bools); + apol_vector_destroy(&bool_states); + apol_bst_destroy(&b); + qpol_iterator_destroy(&iter); + if (retval < 0) { + apol_vector_destroy(&v); + errno = error; + return NULL; + } + return v; +} + +apol_vector_t *avrule_get_items_allow(poldiff_t * diff, const apol_policy_t * policy) +{ + return avrule_get_items(diff, policy, QPOL_RULE_ALLOW); +} + +apol_vector_t *avrule_get_items_auditallow(poldiff_t * diff, const apol_policy_t * policy) +{ + return avrule_get_items(diff, policy, QPOL_RULE_AUDITALLOW); +} + +apol_vector_t *avrule_get_items_dontaudit(poldiff_t * diff, const apol_policy_t * policy) +{ + return avrule_get_items(diff, policy, QPOL_RULE_DONTAUDIT); +} + +apol_vector_t *avrule_get_items_neverallow(poldiff_t * diff, const apol_policy_t * policy) +{ + return avrule_get_items(diff, policy, QPOL_RULE_NEVERALLOW); +} + +int avrule_comp(const void *x, const void *y, const poldiff_t * diff __attribute__ ((unused))) +{ + const pseudo_avrule_t *r1 = (const pseudo_avrule_t *)x; + const pseudo_avrule_t *r2 = (const pseudo_avrule_t *)y; + return pseudo_avrule_comp(r1, r2, 0); +} + +/** + * Allocate and return a new avrule difference object. If the + * pseudo-avrule's source and/or target expands to multiple read + * types, then just choose the first one for display. + * + * @param diff Policy diff error handler. + * @param form Form of the difference. + * @param rule Pseudo avrule that changed. + * + * @return A newly allocated and initialized diff, or NULL upon error. + * The caller is responsible for calling poldiff_avrule_free() upon + * the returned value. + */ +static poldiff_avrule_t *make_avdiff(poldiff_t * diff, poldiff_form_e form, pseudo_avrule_t * rule) +{ + poldiff_avrule_t *pa = NULL; + const char *n1, *n2; + int error = 0; + if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) { + n1 = type_map_get_name(diff, rule->source, POLDIFF_POLICY_MOD); + n2 = type_map_get_name(diff, rule->target, POLDIFF_POLICY_MOD); + } else { + n1 = type_map_get_name(diff, rule->source, POLDIFF_POLICY_ORIG); + n2 = type_map_get_name(diff, rule->target, POLDIFF_POLICY_ORIG); + } + assert(n1 != NULL && n2 != NULL); + if ((pa = calloc(1, sizeof(*pa))) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + pa->spec = rule->spec; + pa->source = n1; + pa->target = n2; + pa->cls = rule->cls; + pa->form = form; + pa->cond = rule->cond; + pa->branch = rule->branch; + cleanup: + if (error != 0) { + poldiff_avrule_free(pa); + errno = error; + return NULL; + } + return pa; +} + +/** + * Create, initialize, and insert a new semantic difference entry for + * a pseudo-av rule. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * @param idx Index into the avrule differences specifying into which + * to place the constructed pseudo-av rule. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ +static int avrule_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item, avrule_offset_e idx) +{ + pseudo_avrule_t *rule = (pseudo_avrule_t *) item; + poldiff_avrule_t *pa = NULL; + const apol_vector_t *v1, *v2; + apol_vector_t **target; + apol_policy_t *p; + size_t i; + int retval = -1, error = errno; + + /* check if form should really become ADD_TYPE / REMOVE_TYPE, + * by seeing if the /other/ policy's reverse lookup is + * empty */ + if (form == POLDIFF_FORM_ADDED) { + if ((v1 = type_map_lookup_reverse(diff, rule->source, POLDIFF_POLICY_ORIG)) == NULL || + (v2 = type_map_lookup_reverse(diff, rule->target, POLDIFF_POLICY_ORIG)) == NULL) { + error = errno; + goto cleanup; + } + if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) { + form = POLDIFF_FORM_ADD_TYPE; + } + p = diff->mod_pol; + } else { + if ((v1 = type_map_lookup_reverse(diff, rule->source, POLDIFF_POLICY_MOD)) == NULL || + (v2 = type_map_lookup_reverse(diff, rule->target, POLDIFF_POLICY_MOD)) == NULL) { + error = errno; + goto cleanup; + } + if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) { + form = POLDIFF_FORM_REMOVE_TYPE; + } + p = diff->orig_pol; + } + + pa = make_avdiff(diff, form, rule); + if (pa == NULL) { + return -1; + } + + if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) { + if ((pa->removed_perms = apol_vector_create_with_capacity(1, NULL)) == NULL || + (pa->unmodified_perms = apol_vector_create_with_capacity(1, NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + target = &pa->added_perms; + } else { + if ((pa->added_perms = apol_vector_create_with_capacity(1, NULL)) == NULL || + (pa->unmodified_perms = apol_vector_create_with_capacity(1, NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + target = &pa->removed_perms; + } + if ((*target = apol_vector_create_with_capacity(rule->num_perms, NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + for (i = 0; i < rule->num_perms; i++) { + if (apol_vector_append(*target, rule->perms[i]) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + apol_vector_sort(*target, apol_str_strcmp, NULL); + + if (qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_LINE_NUMBERS)) { + /* calculate line numbers */ + apol_vector_t *vl = NULL; + if ((vl = apol_vector_create(NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) { + pa->mod_linenos = vl; + } else { + pa->orig_linenos = vl; + } + + /* copy rule pointers for delayed line number claculation */ + if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) { + pa->num_mod_rules = rule->num_rules; + pa->mod_rules = calloc(rule->num_rules, sizeof(qpol_avrule_t *)); + if (!pa->mod_rules) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + memcpy(pa->mod_rules, rule->rules, rule->num_rules * sizeof(qpol_avrule_t *)); + } else { + pa->num_orig_rules = rule->num_rules; + pa->orig_rules = calloc(rule->num_rules, sizeof(qpol_avrule_t *)); + if (!pa->orig_rules) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + memcpy(pa->orig_rules, rule->rules, rule->num_rules * sizeof(qpol_avrule_t *)); + } + } + + if (apol_vector_append(diff->avrule_diffs[idx]->diffs, pa) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + switch (form) { + case POLDIFF_FORM_ADDED: + diff->avrule_diffs[idx]->num_added++; + break; + case POLDIFF_FORM_ADD_TYPE: + diff->avrule_diffs[idx]->num_added_type++; + break; + case POLDIFF_FORM_REMOVED: + diff->avrule_diffs[idx]->num_removed++; + break; + case POLDIFF_FORM_REMOVE_TYPE: + diff->avrule_diffs[idx]->num_removed_type++; + break; + default: + error = EBADRQC; /* should never get here */ + ERR(diff, "%s", strerror(error)); + assert(0); + goto cleanup; + } + diff->avrule_diffs[idx]->diffs_sorted = 0; + retval = 0; + cleanup: + if (retval < 0) { + poldiff_avrule_free(pa); + } + errno = error; + return retval; +} + +int avrule_new_diff_allow(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + return avrule_new_diff(diff, form, item, AVRULE_OFFSET_ALLOW); +} + +int avrule_new_diff_auditallow(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + return avrule_new_diff(diff, form, item, AVRULE_OFFSET_AUDITALLOW); +} + +int avrule_new_diff_dontaudit(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + return avrule_new_diff(diff, form, item, AVRULE_OFFSET_DONTAUDIT); +} + +int avrule_new_diff_neverallow(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + return avrule_new_diff(diff, form, item, AVRULE_OFFSET_NEVERALLOW); +} + +/** + * Compute the semantic difference of two pseudo-av rules for which + * the compare callback returns 0. If a difference is found then + * allocate, initialize, and insert a new semantic difference entry + * for that pseudo-av rule. + * + * @param diff The policy difference structure associated with both + * pseudo-av rules and to which to add an entry if needed. + * @param x The pseudo-av rule from the original policy. + * @param y The pseudo-av rule from the modified policy. + * @param idx Index into the avrule differences specifying into which + * to place the constructed pseudo-av rule. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ +static int avrule_deep_diff(poldiff_t * diff, const void *x, const void *y, avrule_offset_e idx) +{ + pseudo_avrule_t *r1 = (pseudo_avrule_t *) x; + pseudo_avrule_t *r2 = (pseudo_avrule_t *) y; + apol_vector_t *unmodified_perms = NULL, *added_perms = NULL, *removed_perms = NULL; + size_t i, j; + char *perm1, *perm2; + poldiff_avrule_t *pa = NULL; + int retval = -1, error = 0; + + if ((unmodified_perms = apol_vector_create(NULL)) == NULL || + (added_perms = apol_vector_create(NULL)) == NULL || (removed_perms = apol_vector_create(NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + for (i = j = 0; i < r1->num_perms;) { + if (j >= r2->num_perms) + break; + perm1 = r1->perms[i]; + perm2 = r2->perms[j]; + if (perm2 > perm1) { + if (apol_vector_append(removed_perms, perm1) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + i++; + } else if (perm1 > perm2) { + if (apol_vector_append(added_perms, perm2) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + j++; + } else { + if (apol_vector_append(unmodified_perms, perm1) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + i++; + j++; + } + } + + for (; i < r1->num_perms; i++) { + perm1 = r1->perms[i]; + if (apol_vector_append(removed_perms, perm1) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + for (; j < r2->num_perms; j++) { + perm2 = r2->perms[j]; + if (apol_vector_append(added_perms, perm2) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + if (apol_vector_get_size(added_perms) > 0 || apol_vector_get_size(removed_perms) > 0) { + if ((pa = make_avdiff(diff, POLDIFF_FORM_MODIFIED, r1)) == NULL) { + error = errno; + goto cleanup; + } + pa->unmodified_perms = unmodified_perms; + pa->added_perms = added_perms; + pa->removed_perms = removed_perms; + unmodified_perms = NULL; + added_perms = NULL; + removed_perms = NULL; + apol_vector_sort(pa->unmodified_perms, apol_str_strcmp, NULL); + apol_vector_sort(pa->added_perms, apol_str_strcmp, NULL); + apol_vector_sort(pa->removed_perms, apol_str_strcmp, NULL); + + /* calculate line numbers */ + if (qpol_policy_has_capability(apol_policy_get_qpol(diff->orig_pol), QPOL_CAP_LINE_NUMBERS)) { + if ((pa->orig_linenos = apol_vector_create(NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + + /* copy rule pointers for delayed line number claculation */ + pa->num_orig_rules = r1->num_rules; + pa->orig_rules = calloc(r1->num_rules, sizeof(qpol_avrule_t *)); + if (!pa->orig_rules) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + memcpy(pa->orig_rules, r1->rules, r1->num_rules * sizeof(qpol_avrule_t *)); + } + if (qpol_policy_has_capability(apol_policy_get_qpol(diff->mod_pol), QPOL_CAP_LINE_NUMBERS)) { + if ((pa->mod_linenos = apol_vector_create(NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + + /* copy rule pointers for delayed line number claculation */ + pa->num_mod_rules = r2->num_rules; + pa->mod_rules = calloc(r2->num_rules, sizeof(qpol_avrule_t *)); + if (!pa->mod_rules) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + memcpy(pa->mod_rules, r2->rules, r2->num_rules * sizeof(qpol_avrule_t *)); + } + if (apol_vector_append(diff->avrule_diffs[idx]->diffs, pa) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + diff->avrule_diffs[idx]->num_modified++; + diff->avrule_diffs[idx]->diffs_sorted = 0; + } + retval = 0; + cleanup: + apol_vector_destroy(&unmodified_perms); + apol_vector_destroy(&added_perms); + apol_vector_destroy(&removed_perms); + if (retval != 0) { + poldiff_avrule_free(pa); + } + errno = error; + return retval; +} + +int avrule_deep_diff_allow(poldiff_t * diff, const void *x, const void *y) +{ + return avrule_deep_diff(diff, x, y, AVRULE_OFFSET_ALLOW); +} + +int avrule_deep_diff_auditallow(poldiff_t * diff, const void *x, const void *y) +{ + return avrule_deep_diff(diff, x, y, AVRULE_OFFSET_AUDITALLOW); +} + +int avrule_deep_diff_dontaudit(poldiff_t * diff, const void *x, const void *y) +{ + return avrule_deep_diff(diff, x, y, AVRULE_OFFSET_DONTAUDIT); +} + +int avrule_deep_diff_neverallow(poldiff_t * diff, const void *x, const void *y) +{ + return avrule_deep_diff(diff, x, y, AVRULE_OFFSET_NEVERALLOW); +} + +int avrule_enable_line_numbers(poldiff_t * diff, avrule_offset_e idx) +{ + const apol_vector_t *av = NULL; + poldiff_avrule_t *avrule = NULL; + size_t i, j; + qpol_iterator_t *iter = NULL; + qpol_syn_avrule_t *sav = NULL; + int error = 0; + unsigned long lineno = 0; + + av = poldiff_get_avrule_vector(diff, idx); + + for (i = 0; i < apol_vector_get_size(av); i++) { + avrule = apol_vector_get_element(av, i); + if (apol_vector_get_size(avrule->mod_linenos) || apol_vector_get_size(avrule->orig_linenos)) + continue; + for (j = 0; j < avrule->num_orig_rules; j++) { + if (qpol_avrule_get_syn_avrule_iter(diff->orig_qpol, avrule->orig_rules[j], &iter)) { + error = errno; + goto err; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&sav) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + if (qpol_syn_avrule_get_lineno(diff->orig_qpol, sav, &lineno) < 0) { + error = errno; + goto err; + } + if (apol_vector_append(avrule->orig_linenos, (void *)lineno) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + } + qpol_iterator_destroy(&iter); + } + apol_vector_sort_uniquify(avrule->orig_linenos, NULL, NULL); + for (j = 0; j < avrule->num_mod_rules; j++) { + if (qpol_avrule_get_syn_avrule_iter(diff->mod_qpol, avrule->mod_rules[j], &iter)) { + error = errno; + goto err; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&sav) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + if (qpol_syn_avrule_get_lineno(diff->mod_qpol, sav, &lineno) < 0) { + error = errno; + goto err; + } + if (apol_vector_append(avrule->mod_linenos, (void *)lineno) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + } + qpol_iterator_destroy(&iter); + } + apol_vector_sort_uniquify(avrule->mod_linenos, NULL, NULL); + } + return 0; + err: + qpol_iterator_destroy(&iter); + return -1; +} diff --git a/libpoldiff/src/avrule_internal.h b/libpoldiff/src/avrule_internal.h new file mode 100644 index 0000000..9bdf517 --- /dev/null +++ b/libpoldiff/src/avrule_internal.h @@ -0,0 +1,296 @@ +/** + * @file + * Protected interface for AV rule differences. + * + * @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 POLDIFF_AVRULE_INTERNAL_H +#define POLDIFF_AVRULE_INTERNAL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct poldiff_avrule_summary poldiff_avrule_summary_t; + +/** + * Allocate and return a new poldiff_terule_summary_t object, used by + * AV rule searches. + * + * @return A new rule summary. The caller must call avrule_destroy() + * afterwards. On error, return NULL and set errno. + */ + poldiff_avrule_summary_t *avrule_create(void); + +/** + * Deallocate all space associated with a poldiff_avrule_summary_t + * object, including the pointer itself. If the pointer is already + * NULL then do nothing. + * + * @param rs Reference to an rule summary to destroy. The pointer + * will be set to NULL afterwards. + */ + void avrule_destroy(poldiff_avrule_summary_t ** rs); + +/** + * Reset the state of AV allow rule differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int avrule_reset_allow(poldiff_t * diff); + +/** + * Reset the state of AV auditallow rule differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int avrule_reset_auditallow(poldiff_t * diff); + +/** + * Reset the state of AV dontaudit rule differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int avrule_reset_dontaudit(poldiff_t * diff); + +/** + * Reset the state of AV neverallow rule differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int avrule_reset_neverallow(poldiff_t * diff); + +/** + * Get a vector of AV allow rules from the given policy, sorted. This + * function will remap source and target types to their pseudo-type + * value equivalents. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * + * @return A newly allocated vector of av allow rules (of type + * pseudo_avrule_t). The caller is responsible for calling + * apol_vector_destroy() afterwards. On error, return NULL and set + * errno. + */ + apol_vector_t *avrule_get_items_allow(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Get a vector of AV auditallow rules from the given policy, sorted. + * This function will remap source and target types to their + * pseudo-type value equivalents. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * + * @return A newly allocated vector of av auditallow rules (of type + * pseudo_avrule_t). The caller is responsible for calling + * apol_vector_destroy() afterwards. On error, return NULL and set + * errno. + */ + apol_vector_t *avrule_get_items_auditallow(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Get a vector of AV dontaudit rules from the given policy, sorted. + * This function will remap source and target types to their + * pseudo-type value equivalents. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * + * @return A newly allocated vector of av dontaudit rules (of type + * pseudo_avrule_t). The caller is responsible for calling + * apol_vector_destroy() afterwards. On error, return NULL and set + * errno. + */ + apol_vector_t *avrule_get_items_dontaudit(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Get a vector of AV neverallow rules from the given policy, sorted. + * This function will remap source and target types to their + * pseudo-type value equivalents. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * + * @return A newly allocated vector of av neverallow rules (of type + * pseudo_avrule_t). The caller is responsible for calling + * apol_vector_destroy() afterwards. On error, return NULL and set + * errno. + */ + apol_vector_t *avrule_get_items_neverallow(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Compare two pseudo_avrule_t objects, determining if they have the + * same key (specified + source + target + class + conditional + * expression). + * + * @param x The pseudo-av rule from the original policy. + * @param y The pseudo-av rule from the modified policy. + * @param diff The policy difference structure associated with both + * policies. + * + * @return < 0, 0, or > 0 if av rule x is respectively less than, + * equal to, or greater than av rule y. + */ + int avrule_comp(const void *x, const void *y, const poldiff_t * diff); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a pseudo-av rule that was originally an allow rule. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int avrule_new_diff_allow(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a pseudo-av rule that was originally an auditallow rule. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int avrule_new_diff_auditallow(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a pseudo-av rule that was originally a dontaudit rule. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int avrule_new_diff_dontaudit(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a pseudo-av rule that was originally a neverallow rule. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int avrule_new_diff_neverallow(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Compute the semantic difference of two pseudo-av rules (that were + * allow rules) for which the compare callback returns 0. If a + * difference is found then allocate, initialize, and insert a new + * semantic difference entry for that pseudo-av rule. + * + * @param diff The policy difference structure associated with both + * pseudo-av rules and to which to add an entry if needed. + * @param x The pseudo-av rule from the original policy. + * @param y The pseudo-av rule from the modified policy. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int avrule_deep_diff_allow(poldiff_t * diff, const void *x, const void *y); + +/** + * Compute the semantic difference of two pseudo-av rules (that were + * auditallow rules) for which the compare callback returns 0. If a + * difference is found then allocate, initialize, and insert a new + * semantic difference entry for that pseudo-av rule. + * + * @param diff The policy difference structure associated with both + * pseudo-av rules and to which to add an entry if needed. + * @param x The pseudo-av rule from the original policy. + * @param y The pseudo-av rule from the modified policy. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int avrule_deep_diff_auditallow(poldiff_t * diff, const void *x, const void *y); + +/** + * Compute the semantic difference of two pseudo-av rules (that were + * dontaudit rules) for which the compare callback returns 0. If a + * difference is found then allocate, initialize, and insert a new + * semantic difference entry for that pseudo-av rule. + * + * @param diff The policy difference structure associated with both + * pseudo-av rules and to which to add an entry if needed. + * @param x The pseudo-av rule from the original policy. + * @param y The pseudo-av rule from the modified policy. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int avrule_deep_diff_dontaudit(poldiff_t * diff, const void *x, const void *y); + +/** + * Compute the semantic difference of two pseudo-av rules (that were + * neverallow rules) for which the compare callback returns 0. If a + * difference is found then allocate, initialize, and insert a new + * semantic difference entry for that pseudo-av rule. + * + * @param diff The policy difference structure associated with both + * pseudo-av rules and to which to add an entry if needed. + * @param x The pseudo-av rule from the original policy. + * @param y The pseudo-av rule from the modified policy. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int avrule_deep_diff_neverallow(poldiff_t * diff, const void *x, const void *y); + +/** + * Iterate through an AV rule difference, filling in its line numbers. + * + * @param diff Diff structure containing avrule differences. + * @param idx Index into the avrule differences specifying which line + * number table to enable. + * + * @return 0 on success, < 0 on errno. + */ + int avrule_enable_line_numbers(poldiff_t * diff, avrule_offset_e idx); + +#ifdef __cplusplus +} +#endif + +#endif /* POLDIFF_AVRULE_INTERNAL_H */ diff --git a/libpoldiff/src/bool_diff.c b/libpoldiff/src/bool_diff.c new file mode 100644 index 0000000..3db1e46 --- /dev/null +++ b/libpoldiff/src/bool_diff.c @@ -0,0 +1,333 @@ +/** + * @file + * Implementation for computing semantic differences in booleans. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Randy Wicks rwicks@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 "poldiff_internal.h" + +#include <apol/util.h> +#include <apol/vector.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdbool.h> + +struct poldiff_bool_summary +{ + size_t num_added; + size_t num_removed; + size_t num_modified; + apol_vector_t *diffs; +}; + +struct poldiff_bool +{ + char *name; + poldiff_form_e form; + bool state; +}; + +void poldiff_bool_get_stats(const poldiff_t * diff, size_t stats[5]) +{ + if (diff == NULL || stats == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return; + } + stats[0] = diff->bool_diffs->num_added; + stats[1] = diff->bool_diffs->num_removed; + stats[2] = diff->bool_diffs->num_modified; + stats[3] = 0; + stats[4] = 0; +} + +char *poldiff_bool_to_string(const poldiff_t * diff, const void *boolean) +{ + poldiff_bool_t *b = (poldiff_bool_t *) boolean; + size_t len = 0; + char *s = NULL; + if (diff == NULL || boolean == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + switch (b->form) { + case POLDIFF_FORM_ADDED: + { + if (apol_str_appendf(&s, &len, "+ %s", b->name) < 0) { + break; + } + return s; + } + case POLDIFF_FORM_REMOVED: + { + if (apol_str_appendf(&s, &len, "- %s", b->name) < 0) { + break; + } + return s; + } + case POLDIFF_FORM_MODIFIED: + { + if (apol_str_appendf + (&s, &len, "* %s (changed from %s)", b->name, (b->state ? "false to true" : "true to false")) < 0) { + break; + } + return s; + } + default: + { + ERR(diff, "%s", strerror(ENOTSUP)); + errno = ENOTSUP; + return NULL; + } + } + errno = ENOMEM; + return NULL; +} + +const apol_vector_t *poldiff_get_bool_vector(const poldiff_t * diff) +{ + if (diff == NULL) { + errno = EINVAL; + return NULL; + } + return diff->bool_diffs->diffs; +} + +const char *poldiff_bool_get_name(const poldiff_bool_t * boolean) +{ + if (boolean == NULL) { + errno = EINVAL; + return NULL; + } + return boolean->name; +} + +poldiff_form_e poldiff_bool_get_form(const void *boolean) +{ + if (boolean == NULL) { + errno = EINVAL; + return 0; + } + return ((const poldiff_bool_t *)boolean)->form; +} + +/******************** protected functions ********************/ + +static void bool_free(void *elem) +{ + if (elem != NULL) { + poldiff_bool_t *b = (poldiff_bool_t *) elem; + free(b->name); + free(b); + } +} + +poldiff_bool_summary_t *bool_create(void) +{ + poldiff_bool_summary_t *bs = calloc(1, sizeof(*bs)); + if (bs == NULL) { + return NULL; + } + if ((bs->diffs = apol_vector_create(bool_free)) == NULL) { + bool_destroy(&bs); + return NULL; + } + return bs; +} + +void bool_destroy(poldiff_bool_summary_t ** bs) +{ + if (bs != NULL && *bs != NULL) { + apol_vector_destroy(&(*bs)->diffs); + free(*bs); + *bs = NULL; + } +} + +int bool_reset(poldiff_t * diff) +{ + int error = 0; + + if (diff == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + bool_destroy(&diff->bool_diffs); + diff->bool_diffs = bool_create(); + if (diff->bool_diffs == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + errno = error; + return -1; + } + + return 0; +} + +/** + * Comparison function for two bools from the same policy. + */ +static int bool_name_comp(const void *x, const void *y, void *arg) +{ + qpol_bool_t *c1 = (qpol_bool_t *) x; + qpol_bool_t *c2 = (qpol_bool_t *) y; + apol_policy_t *p = (apol_policy_t *) arg; + qpol_policy_t *q = apol_policy_get_qpol(p); + const char *name1, *name2; + if (qpol_bool_get_name(q, c1, &name1) < 0 || qpol_bool_get_name(q, c2, &name2) < 0) { + return 0; + } + return strcmp(name1, name2); +} + +apol_vector_t *bool_get_items(poldiff_t * diff, const apol_policy_t * policy) +{ + qpol_iterator_t *iter = NULL; + apol_vector_t *v = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policy); + int error = 0; + if (qpol_policy_get_bool_iter(q, &iter) < 0) { + return NULL; + } + v = apol_vector_create_from_iter(iter, NULL); + if (v == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + qpol_iterator_destroy(&iter); + errno = error; + return NULL; + } + qpol_iterator_destroy(&iter); + apol_vector_sort(v, bool_name_comp, (void *)policy); + return v; +} + +int bool_comp(const void *x, const void *y, const poldiff_t * diff) +{ + qpol_bool_t *c1 = (qpol_bool_t *) x; + qpol_bool_t *c2 = (qpol_bool_t *) y; + const char *name1, *name2; + if (qpol_bool_get_name(diff->orig_qpol, c1, &name1) < 0 || qpol_bool_get_name(diff->mod_qpol, c2, &name2) < 0) { + return 0; + } + return strcmp(name1, name2); +} + +/** + * Allocate and return a new bool difference object. + * + * @param diff Policy diff error handler. + * @param form Form of the difference. + * @param name Name of the bool that is different. + * + * @return A newly allocated and initialized diff, or NULL upon error. + * The caller is responsible for calling bool_free() upon the + * returned value. + */ +static poldiff_bool_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name) +{ + poldiff_bool_t *pb; + int error; + if ((pb = calloc(1, sizeof(*pb))) == NULL || (pb->name = strdup(name)) == NULL) { + error = errno; + bool_free(pb); + ERR(diff, "%s", strerror(error)); + errno = error; + return NULL; + } + pb->form = form; + return pb; +} + +int bool_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + qpol_bool_t *c = (qpol_bool_t *) item; + const char *name = NULL; + poldiff_bool_t *pb; + int error; + if ((form == POLDIFF_FORM_ADDED && + qpol_bool_get_name(diff->mod_qpol, c, &name) < 0) || + ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_bool_get_name(diff->orig_qpol, c, &name) < 0)) + { + return -1; + } + pb = make_diff(diff, form, name); + if (pb == NULL) { + return -1; + } + if (apol_vector_append(diff->bool_diffs->diffs, pb) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + bool_free(pb); + errno = error; + return -1; + } + if (form == POLDIFF_FORM_ADDED) + diff->bool_diffs->num_added++; + else + diff->bool_diffs->num_removed++; + return 0; +} + +int bool_deep_diff(poldiff_t * diff, const void *x, const void *y) +{ + qpol_bool_t *b1 = (qpol_bool_t *) x; + qpol_bool_t *b2 = (qpol_bool_t *) y; + const char *name; + int s1, s2; + poldiff_bool_t *b = NULL; + int retval = -1, error = 0; + + if (qpol_bool_get_name(diff->orig_qpol, b1, &name) < 0 || + qpol_bool_get_state(diff->orig_qpol, b1, &s1) < 0 || qpol_bool_get_state(diff->mod_qpol, b2, &s2) < 0) { + error = errno; + goto cleanup; + } + if (s1 != s2) { + if ((b = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) { + error = errno; + goto cleanup; + } + if (s2) + b->state = true; + else + b->state = false; + } + if (b != NULL) { + if (apol_vector_append(diff->bool_diffs->diffs, b) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + diff->bool_diffs->num_modified++; + } + retval = 0; + cleanup: + errno = error; + return retval; +} diff --git a/libpoldiff/src/bool_internal.h b/libpoldiff/src/bool_internal.h new file mode 100644 index 0000000..744419f --- /dev/null +++ b/libpoldiff/src/bool_internal.h @@ -0,0 +1,122 @@ +/** + * @file + * Protected interface for boolean differences. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Randy Wicks rwicks@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 POLDIFF_BOOL_INTERNAL_H +#define POLDIFF_BOOL_INTERNAL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct poldiff_bool_summary poldiff_bool_summary_t; + +/** + * Allocate and return a new poldiff_bool_summary_t object. + * + * @return A new bool summary. The caller must call bool_destroy() + * afterwards. On error, return NULL and set errno. + */ + poldiff_bool_summary_t *bool_create(void); + +/** + * Deallocate all space associated with a poldiff_bool_summary_t + * object, including the pointer itself. If the pointer is already + * NULL then do nothing. + * + * @param bs Reference to a bool summary to destroy. The pointer + * will be set to NULL afterwards. + */ + void bool_destroy(poldiff_bool_summary_t ** bs); + +/** + * Reset the state of all boolean differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int bool_reset(poldiff_t * diff); + +/** + * Get a vector of all bools from the given policy, sorted by name. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * + * @return a newly allocated vector of all bools. The caller is + * responsible for calling apol_vector_destroy() afterwards. On + * error, return NULL and set errno. + */ + apol_vector_t *bool_get_items(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Compare two qpol_bool_t objects, determining if they have the same + * name or not. + * + * @param x The bool from the original policy. + * @param y The bool from the modified policy. + * @param diff The policy difference structure associated with both + * policies. + * + * @return < 0, 0, or > 0 if bool x is respectively less than, equal + * to, or greater than bool y. + */ + int bool_comp(const void *x, const void *y, const poldiff_t * diff); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a bool. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int bool_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Compute the semantic difference of two bools for which the + * compare callback returns 0. If a difference is found then + * allocate, initialize, and insert an new semantic difference entry + * for that bool. + * + * @param diff The policy difference structure associated with both + * bools and to which to add an entry if needed. + * @param x The bool from the original policy. + * @param y The bool from the modified policy. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int bool_deep_diff(poldiff_t * diff, const void *x, const void *y); + +#ifdef __cplusplus +} +#endif + +#endif /* POLDIFF_BOOL_INTERNAL_H */ diff --git a/libpoldiff/src/cat_diff.c b/libpoldiff/src/cat_diff.c new file mode 100644 index 0000000..2f6ab17 --- /dev/null +++ b/libpoldiff/src/cat_diff.c @@ -0,0 +1,289 @@ +/** + * @file + * Implementation for computing semantic differences in categories. + * + * @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 + */ + +#include <config.h> + +#include "poldiff_internal.h" + +#include <apol/util.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +struct poldiff_cat_summary +{ + size_t num_added; + size_t num_removed; + apol_vector_t *diffs; +}; + +struct poldiff_cat +{ + char *name; + poldiff_form_e form; +}; + +void poldiff_cat_get_stats(const poldiff_t * diff, size_t stats[5]) +{ + if (diff == NULL || stats == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return; + } + stats[0] = diff->cat_diffs->num_added; + stats[1] = diff->cat_diffs->num_removed; + stats[2] = 0; + stats[3] = 0; + stats[4] = 0; +} + +const apol_vector_t *poldiff_get_cat_vector(const poldiff_t * diff) +{ + if (diff == NULL) { + errno = EINVAL; + return NULL; + } + return diff->cat_diffs->diffs; +} + +char *poldiff_cat_to_string(const poldiff_t * diff, const void *cat) +{ + poldiff_cat_t *c = (poldiff_cat_t *) cat; + size_t len = 0; + char *s = NULL; + if (diff == NULL || cat == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + switch (c->form) { + case POLDIFF_FORM_ADDED: + { + if (apol_str_appendf(&s, &len, "+ %s", c->name) < 0) { + break; + } + return s; + } + case POLDIFF_FORM_REMOVED: + { + if (apol_str_appendf(&s, &len, "- %s", c->name) < 0) { + break; + } + return s; + } + case POLDIFF_FORM_MODIFIED: + default: + { + ERR(diff, "%s", strerror(ENOTSUP)); + errno = ENOTSUP; + return NULL; + } + } + return NULL; +} + +const char *poldiff_cat_get_name(const poldiff_cat_t * cat) +{ + if (cat == NULL) { + errno = EINVAL; + return NULL; + } + + return cat->name; +} + +poldiff_form_e poldiff_cat_get_form(const void *cat) +{ + if (cat == NULL) { + errno = EINVAL; + return POLDIFF_FORM_NONE; + } + + return ((const poldiff_cat_t *)cat)->form; +} + +static void cat_free(void *elem) +{ + poldiff_cat_t *s = elem; + if (!elem) + return; + free(s->name); + free(s); +} + +poldiff_cat_summary_t *cat_create(void) +{ + poldiff_cat_summary_t *cs = calloc(1, sizeof(poldiff_cat_summary_t)); + if (cs == NULL) + return NULL; + if ((cs->diffs = apol_vector_create(cat_free)) == NULL) { + cat_destroy(&cs); + return NULL; + } + return cs; +} + +void cat_destroy(poldiff_cat_summary_t ** cs) +{ + if (cs == NULL || *cs == NULL) + return; + apol_vector_destroy(&(*cs)->diffs); + free(*cs); + *cs = NULL; +} + +int cat_reset(poldiff_t * diff) +{ + int error = 0; + + if (diff == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + cat_destroy(&diff->cat_diffs); + diff->cat_diffs = cat_create(); + if (diff->cat_diffs == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + errno = error; + return -1; + } + + return 0; +} + +/** + * Comparison function for two categories from the same policy. + */ +static int cat_name_comp(const void *x, const void *y, void *arg) +{ + const qpol_cat_t *c1 = x; + const qpol_cat_t *c2 = y; + apol_policy_t *p = arg; + qpol_policy_t *q = apol_policy_get_qpol(p); + const char *name1, *name2; + + if (qpol_cat_get_name(q, c1, &name1) < 0 || qpol_cat_get_name(q, c2, &name2) < 0) + return 0; + return strcmp(name1, name2); +} + +apol_vector_t *cat_get_items(poldiff_t * diff, const apol_policy_t * policy) +{ + qpol_iterator_t *iter = NULL; + apol_vector_t *v = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policy); + int error = 0; + if (qpol_policy_get_cat_iter(q, &iter) < 0) { + return NULL; + } + v = apol_vector_create_from_iter(iter, NULL); + if (v == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + qpol_iterator_destroy(&iter); + errno = error; + return NULL; + } + qpol_iterator_destroy(&iter); + apol_vector_sort(v, cat_name_comp, (void *)policy); + return v; +} + +int cat_comp(const void *x, const void *y, const poldiff_t * diff) +{ + const qpol_cat_t *c1 = x; + const qpol_cat_t *c2 = y; + const char *name1, *name2; + if (qpol_cat_get_name(diff->orig_qpol, c1, &name1) < 0 || qpol_cat_get_name(diff->mod_qpol, c2, &name2) < 0) { + return 0; + } + return strcmp(name1, name2); +} + +/** + * Allocate and return a new category difference object. + * + * @param diff Policy diff error handler. + * @param form Form of the difference. + * @param name Name of the category that is different. + * + * @return A newly allocated and initialized diff, or NULL upon error. + * The caller is responsible for calling cat_free() upon the returned + * value. + */ +static poldiff_cat_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name) +{ + poldiff_cat_t *pl; + int error; + if ((pl = calloc(1, sizeof(*pl))) == NULL || (pl->name = strdup(name)) == NULL) { + error = errno; + cat_free(pl); + ERR(diff, "%s", strerror(error)); + errno = error; + return NULL; + } + pl->form = form; + return pl; +} + +int cat_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + const qpol_cat_t *c = item; + const char *name = NULL; + poldiff_cat_t *pl; + int error; + if ((form == POLDIFF_FORM_ADDED && + qpol_cat_get_name(diff->mod_qpol, c, &name) < 0) || + ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_cat_get_name(diff->orig_qpol, c, &name) < 0)) { + return -1; + } + pl = make_diff(diff, form, name); + if (pl == NULL) { + return -1; + } + if (apol_vector_append(diff->cat_diffs->diffs, pl) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + cat_free(pl); + errno = error; + return -1; + } + if (form == POLDIFF_FORM_ADDED) { + diff->cat_diffs->num_added++; + } else { + diff->cat_diffs->num_removed++; + } + return 0; +} + +int cat_deep_diff(poldiff_t * diff __attribute__ ((unused)), const void *x __attribute__ ((unused)), const void *y + __attribute__ ((unused))) +{ + /* Categories cannot be modified only added or removed. + * This call back simply returns 0 to satisfy the generic diff algorithm. */ + return 0; +} diff --git a/libpoldiff/src/cat_internal.h b/libpoldiff/src/cat_internal.h new file mode 100644 index 0000000..4c5714e --- /dev/null +++ b/libpoldiff/src/cat_internal.h @@ -0,0 +1,120 @@ +/** + * @file + * Protected interface for computing semantic differences in + * categories. + * + * @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 POLDIFF_CAT_INTERNAL_H +#define POLDIFF_CAT_INTERNAL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct poldiff_cat_summary poldiff_cat_summary_t; + +/** + * Allocate and return a new poldiff_cat_summary_t object. + * + * @return A new category summary. The caller must call cat_destroy() + * afterwards. On error, return NULL and set errno. + */ + poldiff_cat_summary_t *cat_create(void); + +/** + * Deallocate all space associated with a poldiff_cat_summary_t + * object, including the pointer itself. If the pointer is already + * NULL then do nothing. + * + * @param cs Reference to a category summary to destroy. The pointer + * will be set to NULL afterwards. + */ + void cat_destroy(poldiff_cat_summary_t ** cs); + +/** + * Reset the state of all category differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int cat_reset(poldiff_t * diff); + +/** + * Get a vector of all categoriess from the given policy, sorted by name. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * + * @return a newly allocated vector of all categories. The caller is + * responsible for calling apol_vector_destroy() afterwards. On + * error, return NULL and set errno. + */ + apol_vector_t *cat_get_items(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Compare two qpol_cat_t objects, determining if they have the same + * level name or not. + * + * @param x The category from the original policy. + * @param y The category from the modified policy. + * @param diff The policy difference structure associated with both + * policies. + * + * @return < 0, 0, or > 0 if category x is respectively less than, equal + * to, or greater than category y. + */ + int cat_comp(const void *x, const void *y, const poldiff_t * diff); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a category. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int cat_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Compute the semantic difference of two categories for which the compare + * callback returns 0. Categories cannot be modified only added or removed. + * This function always returns 0. + * + * @param diff The policy difference structure associated with both + * categories. + * @param x The category from the original policy. + * @param y The category from the modified policy. + * + * @return always 0. + */ + int cat_deep_diff(poldiff_t * diff, const void *x, const void *y); + +#ifdef __cplusplus +} +#endif + +#endif /* POLDIFF_CAT_INTERNAL_H */ diff --git a/libpoldiff/src/class_diff.c b/libpoldiff/src/class_diff.c new file mode 100644 index 0000000..1c4541c --- /dev/null +++ b/libpoldiff/src/class_diff.c @@ -0,0 +1,990 @@ +/** + * @file + * Implementation for computing semantic differences in classes and + * commons. + * + * @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 "poldiff_internal.h" + +#include <apol/util.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +/******************** object classes ********************/ + +struct poldiff_class_summary +{ + size_t num_added; + size_t num_removed; + size_t num_modified; + apol_vector_t *diffs; +}; + +struct poldiff_class +{ + char *name; + poldiff_form_e form; + apol_vector_t *added_perms; + apol_vector_t *removed_perms; +}; + +void poldiff_class_get_stats(const poldiff_t * diff, size_t stats[5]) +{ + if (diff == NULL || stats == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return; + } + stats[0] = diff->class_diffs->num_added; + stats[1] = diff->class_diffs->num_removed; + stats[2] = diff->class_diffs->num_modified; + stats[3] = 0; + stats[4] = 0; +} + +char *poldiff_class_to_string(const poldiff_t * diff, const void *cls) +{ + poldiff_class_t *c = (poldiff_class_t *) cls; + size_t num_added, num_removed, len = 0, i; + char *s = NULL, *perm; + if (diff == NULL || cls == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + num_added = apol_vector_get_size(c->added_perms); + num_removed = apol_vector_get_size(c->removed_perms); + switch (c->form) { + case POLDIFF_FORM_ADDED: + { + if (apol_str_appendf(&s, &len, "+ %s", c->name) < 0) { + break; + } + return s; + } + case POLDIFF_FORM_REMOVED: + { + if (apol_str_appendf(&s, &len, "- %s", c->name) < 0) { + break; + } + return s; + } + case POLDIFF_FORM_MODIFIED: + { + if (apol_str_appendf(&s, &len, "* %s (", c->name) < 0) { + s = NULL; + break; + } + if (num_added > 0) { + if (apol_str_appendf(&s, &len, "%zd Added Permission%s", num_added, (num_added == 1 ? "" : "s")) < 0) { + break; + } + } + if (num_removed > 0) { + if (apol_str_appendf + (&s, &len, "%s%zd Removed Permission%s", (num_added > 0 ? ", " : ""), num_removed, + (num_removed == 1 ? "" : "s")) < 0) { + break; + } + } + if (apol_str_append(&s, &len, ")\n") < 0) { + break; + } + for (i = 0; i < apol_vector_get_size(c->added_perms); i++) { + perm = (char *)apol_vector_get_element(c->added_perms, i); + if (apol_str_appendf(&s, &len, "\t+ %s\n", perm) < 0) { + goto err; + } + } + for (i = 0; i < apol_vector_get_size(c->removed_perms); i++) { + perm = (char *)apol_vector_get_element(c->removed_perms, i); + if (apol_str_appendf(&s, &len, "\t- %s\n", perm) < 0) { + goto err; + } + } + return s; + } + default: + { + ERR(diff, "%s", strerror(ENOTSUP)); + errno = ENOTSUP; + return NULL; + } + } + err: + /* if this is reached then an error occurred */ + free(s); + ERR(diff, "%s", strerror(ENOMEM)); + errno = ENOMEM; + return NULL; +} + +const apol_vector_t *poldiff_get_class_vector(const poldiff_t * diff) +{ + if (diff == NULL) { + errno = EINVAL; + return NULL; + } + return diff->class_diffs->diffs; +} + +const char *poldiff_class_get_name(const poldiff_class_t * cls) +{ + if (cls == NULL) { + errno = EINVAL; + return NULL; + } + return cls->name; +} + +poldiff_form_e poldiff_class_get_form(const void *cls) +{ + if (cls == NULL) { + errno = EINVAL; + return 0; + } + return ((const poldiff_class_t *)cls)->form; +} + +const apol_vector_t *poldiff_class_get_added_perms(const poldiff_class_t * cls) +{ + if (cls == NULL) { + errno = EINVAL; + return NULL; + } + return cls->added_perms; +} + +const apol_vector_t *poldiff_class_get_removed_perms(const poldiff_class_t * cls) +{ + if (cls == NULL) { + errno = EINVAL; + return NULL; + } + return cls->removed_perms; +} + +/*************** protected functions for object classes ***************/ + +static void class_free(void *elem) +{ + if (elem != NULL) { + poldiff_class_t *c = (poldiff_class_t *) elem; + free(c->name); + apol_vector_destroy(&c->added_perms); + apol_vector_destroy(&c->removed_perms); + free(c); + } +} + +poldiff_class_summary_t *class_create(void) +{ + poldiff_class_summary_t *cs = calloc(1, sizeof(*cs)); + if (cs == NULL) { + return NULL; + } + if ((cs->diffs = apol_vector_create(class_free)) == NULL) { + class_destroy(&cs); + return NULL; + } + return cs; +} + +void class_destroy(poldiff_class_summary_t ** cs) +{ + if (cs != NULL && *cs != NULL) { + apol_vector_destroy(&(*cs)->diffs); + free(*cs); + *cs = NULL; + } +} + +int class_reset(poldiff_t * diff) +{ + int error = 0; + + if (diff == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + class_destroy(&diff->class_diffs); + diff->class_diffs = class_create(); + if (diff->class_diffs == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + errno = error; + return -1; + } + + return 0; +} + +/** + * Comparison function for two classes from the same policy. + */ +static int class_name_comp(const void *x, const void *y, void *arg) +{ + const qpol_class_t *c1 = x; + const qpol_class_t *c2 = y; + apol_policy_t *p = (apol_policy_t *) arg; + qpol_policy_t *q = apol_policy_get_qpol(p); + const char *name1, *name2; + if (qpol_class_get_name(q, c1, &name1) < 0 || qpol_class_get_name(q, c2, &name2) < 0) { + return 0; + } + return strcmp(name1, name2); +} + +apol_vector_t *class_get_items(poldiff_t * diff, const apol_policy_t * policy) +{ + qpol_iterator_t *iter = NULL; + apol_vector_t *v = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policy); + int error = 0; + if (qpol_policy_get_class_iter(q, &iter) < 0) { + return NULL; + } + v = apol_vector_create_from_iter(iter, NULL); + if (v == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + qpol_iterator_destroy(&iter); + errno = error; + return NULL; + } + qpol_iterator_destroy(&iter); + apol_vector_sort(v, class_name_comp, (void *)policy); + return v; +} + +int class_comp(const void *x, const void *y, const poldiff_t * diff) +{ + const qpol_class_t *c1 = x; + const qpol_class_t *c2 = y; + const char *name1, *name2; + if (qpol_class_get_name(diff->orig_qpol, c1, &name1) < 0 || qpol_class_get_name(diff->mod_qpol, c2, &name2) < 0) { + return 0; + } + return strcmp(name1, name2); +} + +/** + * Allocate and return a new class difference object. + * + * @param diff Policy diff error handler. + * @param form Form of the difference. + * @param name Name of the class that is different. + * + * @return A newly allocated and initialized diff, or NULL upon error. + * The caller is responsible for calling class_free() upon the + * returned value. + */ +static poldiff_class_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name) +{ + poldiff_class_t *pc; + int error; + if ((pc = calloc(1, sizeof(*pc))) == NULL || + (pc->name = strdup(name)) == NULL || + (pc->added_perms = apol_vector_create_with_capacity(1, free)) == NULL || + (pc->removed_perms = apol_vector_create_with_capacity(1, free)) == NULL) { + error = errno; + class_free(pc); + ERR(diff, "%s", strerror(error)); + errno = error; + return NULL; + } + pc->form = form; + return pc; +} + +int class_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + const qpol_class_t *c = item; + const char *name = NULL; + poldiff_class_t *pc; + int error; + if ((form == POLDIFF_FORM_ADDED && + qpol_class_get_name(diff->mod_qpol, c, &name) < 0) || + ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_class_get_name(diff->orig_qpol, c, &name) < 0)) + { + return -1; + } + pc = make_diff(diff, form, name); + if (pc == NULL) { + return -1; + } + if (apol_vector_append(diff->class_diffs->diffs, pc) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + class_free(pc); + errno = error; + return -1; + } + if (form == POLDIFF_FORM_ADDED) { + diff->class_diffs->num_added++; + } else { + diff->class_diffs->num_removed++; + } + return 0; +} + +/** + * Given an object class, return a vector of its permissions (in the + * form of strings). These permissions include those inherited from + * the class's common, if present. + * + * @param diff Policy diff error handler. + * @param p Policy from which the class came. + * @param class Class whose permissions to get. + * + * @return Vector of permissions strings for the class. The caller is + * responsible for calling apol_vector_destroy(). On error, return + * NULL. + */ +static apol_vector_t *class_get_perms(const poldiff_t * diff, const apol_policy_t * p, const qpol_class_t * class) +{ + const qpol_common_t *common; + qpol_iterator_t *perm_iter = NULL, *common_iter = NULL; + char *perm; + apol_vector_t *v = NULL; + qpol_policy_t *q = apol_policy_get_qpol(p); + int retval = -1; + + if ((v = apol_vector_create(NULL)) == NULL) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + if (qpol_class_get_common(q, class, &common) < 0 || qpol_class_get_perm_iter(q, class, &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) { + goto cleanup; + } + if (apol_vector_append(v, perm) < 0) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + } + if (common != NULL) { + if (qpol_common_get_perm_iter(q, common, &common_iter) < 0) { + goto cleanup; + } + for (; !qpol_iterator_end(common_iter); qpol_iterator_next(common_iter)) { + if (qpol_iterator_get_item(common_iter, (void **)&perm) < 0) { + goto cleanup; + } + if (apol_vector_append(v, perm) < 0) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + } + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&perm_iter); + qpol_iterator_destroy(&common_iter); + if (retval < 0) { + apol_vector_destroy(&v); + return NULL; + } + return v; +} + +int class_deep_diff(poldiff_t * diff, const void *x, const void *y) +{ + const qpol_class_t *c1 = x; + const qpol_class_t *c2 = y; + apol_vector_t *v1 = NULL, *v2 = NULL; + char *perm1 = NULL, *perm2 = NULL; + const char *name; + poldiff_class_t *c = NULL; + size_t i, j; + int retval = -1, error = 0, compval; + + if (qpol_class_get_name(diff->orig_qpol, c1, &name) < 0 || + (v1 = class_get_perms(diff, diff->orig_pol, c1)) == NULL || (v2 = class_get_perms(diff, diff->mod_pol, c2)) == NULL) { + error = errno; + goto cleanup; + } + apol_vector_sort(v1, apol_str_strcmp, NULL); + apol_vector_sort(v2, apol_str_strcmp, NULL); + for (i = j = 0; i < apol_vector_get_size(v1);) { + if (j >= apol_vector_get_size(v2)) + break; + perm1 = (char *)apol_vector_get_element(v1, i); + perm2 = (char *)apol_vector_get_element(v2, j); + compval = strcmp(perm1, perm2); + if (compval != 0 && c == NULL) { + if ((c = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) { + error = errno; + goto cleanup; + } + } + if (compval < 0) { + if ((perm1 = strdup(perm1)) == NULL || apol_vector_append(c->removed_perms, perm1) < 0) { + error = errno; + free(perm1); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + i++; + } else if (compval > 0) { + if ((perm2 = strdup(perm2)) == NULL || apol_vector_append(c->added_perms, perm2) < 0) { + error = errno; + free(perm2); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + j++; + } else { + i++; + j++; + } + } + for (; i < apol_vector_get_size(v1); i++) { + perm1 = (char *)apol_vector_get_element(v1, i); + if (c == NULL) { + if ((c = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) { + error = errno; + goto cleanup; + } + } + if ((perm1 = strdup(perm1)) == NULL || apol_vector_append(c->removed_perms, perm1) < 0) { + error = errno; + free(perm1); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + for (; j < apol_vector_get_size(v2); j++) { + perm2 = (char *)apol_vector_get_element(v2, j); + if (c == NULL) { + if ((c = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) { + error = errno; + goto cleanup; + } + } + if ((perm2 = strdup(perm2)) == NULL || apol_vector_append(c->added_perms, perm2) < 0) { + error = errno; + free(perm2); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + if (c != NULL) { + apol_vector_sort(c->removed_perms, apol_str_strcmp, NULL); + apol_vector_sort(c->added_perms, apol_str_strcmp, NULL); + if (apol_vector_append(diff->class_diffs->diffs, c) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + diff->class_diffs->num_modified++; + } + retval = 0; + cleanup: + apol_vector_destroy(&v1); + apol_vector_destroy(&v2); + if (retval != 0) { + class_free(c); + } + errno = error; + return retval; +} + +/******************** common classes ********************/ + +struct poldiff_common_summary +{ + size_t num_added; + size_t num_removed; + size_t num_modified; + apol_vector_t *diffs; +}; + +struct poldiff_common +{ + char *name; + poldiff_form_e form; + apol_vector_t *added_perms; + apol_vector_t *removed_perms; +}; + +void poldiff_common_get_stats(const poldiff_t * diff, size_t stats[5]) +{ + if (diff == NULL || stats == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return; + } + stats[0] = diff->common_diffs->num_added; + stats[1] = diff->common_diffs->num_removed; + stats[2] = diff->common_diffs->num_modified; + stats[3] = 0; + stats[4] = 0; +} + +char *poldiff_common_to_string(const poldiff_t * diff, const void *cls) +{ + poldiff_common_t *c = (poldiff_common_t *) cls; + size_t num_added, num_removed, len = 0, i; + char *s = NULL, *perm; + if (diff == NULL || cls == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + num_added = apol_vector_get_size(c->added_perms); + num_removed = apol_vector_get_size(c->removed_perms); + switch (c->form) { + case POLDIFF_FORM_ADDED: + { + if (apol_str_appendf(&s, &len, "+ %s", c->name) < 0) { + s = NULL; + break; + } + return s; + } + case POLDIFF_FORM_REMOVED: + { + if (apol_str_appendf(&s, &len, "- %s", c->name) < 0) { + s = NULL; + break; + } + return s; + } + case POLDIFF_FORM_MODIFIED: + { + if (apol_str_appendf(&s, &len, "* %s (", c->name) < 0) { + s = NULL; + break; + } + if (num_added > 0) { + if (apol_str_appendf(&s, &len, "%zd Added Permission%s", num_added, (num_added == 1 ? "" : "s")) < 0) { + break; + } + } + if (num_removed > 0) { + if (apol_str_appendf + (&s, &len, "%s%zd Removed Permission%s", (num_added > 0 ? ", " : ""), num_removed, + (num_removed == 1 ? "" : "s")) < 0) { + break; + } + } + if (apol_str_append(&s, &len, ")\n") < 0) { + break; + } + for (i = 0; i < apol_vector_get_size(c->added_perms); i++) { + perm = (char *)apol_vector_get_element(c->added_perms, i); + if (apol_str_appendf(&s, &len, "\t+ %s\n", perm) < 0) { + goto err; + } + } + for (i = 0; i < apol_vector_get_size(c->removed_perms); i++) { + perm = (char *)apol_vector_get_element(c->removed_perms, i); + if (apol_str_appendf(&s, &len, "\t- %s\n", perm) < 0) { + goto err; + } + } + return s; + } + default: + { + ERR(diff, "%s", strerror(ENOTSUP)); + errno = ENOTSUP; + return NULL; + } + } + err: + /* if this is reached then an error occurred */ + free(s); + ERR(diff, "%s", strerror(ENOMEM)); + errno = ENOMEM; + return NULL; +} + +const apol_vector_t *poldiff_get_common_vector(const poldiff_t * diff) +{ + if (diff == NULL) { + errno = EINVAL; + return NULL; + } + return diff->common_diffs->diffs; +} + +const char *poldiff_common_get_name(const poldiff_common_t * cls) +{ + if (cls == NULL) { + errno = EINVAL; + return NULL; + } + return cls->name; +} + +poldiff_form_e poldiff_common_get_form(const void *cls) +{ + if (cls == NULL) { + errno = EINVAL; + return 0; + } + return ((const poldiff_common_t *)cls)->form; +} + +const apol_vector_t *poldiff_common_get_added_perms(const poldiff_common_t * cls) +{ + if (cls == NULL) { + errno = EINVAL; + return NULL; + } + return cls->added_perms; +} + +const apol_vector_t *poldiff_common_get_removed_perms(const poldiff_common_t * cls) +{ + if (cls == NULL) { + errno = EINVAL; + return NULL; + } + return cls->removed_perms; +} + +/*************** protected functions for common classes ***************/ + +static void common_free(void *elem) +{ + if (elem != NULL) { + poldiff_common_t *c = (poldiff_common_t *) elem; + free(c->name); + apol_vector_destroy(&c->added_perms); + apol_vector_destroy(&c->removed_perms); + free(c); + } +} + +poldiff_common_summary_t *common_create(void) +{ + poldiff_common_summary_t *cs = calloc(1, sizeof(*cs)); + if (cs == NULL) { + return NULL; + } + if ((cs->diffs = apol_vector_create(common_free)) == NULL) { + common_destroy(&cs); + return NULL; + } + return cs; +} + +void common_destroy(poldiff_common_summary_t ** cs) +{ + if (cs != NULL && *cs != NULL) { + apol_vector_destroy(&(*cs)->diffs); + free(*cs); + *cs = NULL; + } +} + +int common_reset(poldiff_t * diff) +{ + int error = 0; + + if (diff == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + common_destroy(&diff->common_diffs); + diff->common_diffs = common_create(); + if (diff->common_diffs == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + errno = error; + return -1; + } + + return 0; +} + +/** + * Comparison function for two commons from the same policy. + */ +static int common_name_comp(const void *x, const void *y, void *arg) +{ + const qpol_common_t *c1 = x; + const qpol_common_t *c2 = y; + apol_policy_t *p = (apol_policy_t *) arg; + qpol_policy_t *q = apol_policy_get_qpol(p); + const char *name1, *name2; + if (qpol_common_get_name(q, c1, &name1) < 0 || qpol_common_get_name(q, c2, &name2) < 0) { + return 0; + } + return strcmp(name1, name2); +} + +apol_vector_t *common_get_items(poldiff_t * diff, const apol_policy_t * policy) +{ + qpol_iterator_t *iter = NULL; + apol_vector_t *v = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policy); + int error = 0; + if (qpol_policy_get_common_iter(q, &iter) < 0) { + return NULL; + } + v = apol_vector_create_from_iter(iter, NULL); + if (v == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + qpol_iterator_destroy(&iter); + errno = error; + return NULL; + } + qpol_iterator_destroy(&iter); + apol_vector_sort(v, common_name_comp, (void *)policy); + return v; +} + +int common_comp(const void *x, const void *y, const poldiff_t * diff) +{ + const qpol_common_t *c1 = x; + const qpol_common_t *c2 = y; + const char *name1, *name2; + if (qpol_common_get_name(diff->orig_qpol, c1, &name1) < 0 || qpol_common_get_name(diff->mod_qpol, c2, &name2) < 0) { + return 0; + } + return strcmp(name1, name2); +} + +/** + * Allocate and return a new common difference object. + * + * @param diff Policy diff error handler. + * @param form Form of the difference. + * @param name Name of the common that is different. + * + * @return A newly allocated and initialized diff, or NULL upon error. + * The caller is responsible for calling common_free() upon the + * returned value. + */ +static poldiff_common_t *make_common_diff(const poldiff_t * diff, poldiff_form_e form, const char *name) +{ + poldiff_common_t *pc; + int error; + if ((pc = calloc(1, sizeof(*pc))) == NULL || + (pc->name = strdup(name)) == NULL || + (pc->added_perms = apol_vector_create_with_capacity(1, free)) == NULL || + (pc->removed_perms = apol_vector_create_with_capacity(1, free)) == NULL) { + error = errno; + common_free(pc); + ERR(diff, "%s", strerror(error)); + errno = error; + return NULL; + } + pc->form = form; + return pc; +} + +int common_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + const qpol_common_t *c = item; + const char *name = NULL; + poldiff_common_t *pc; + int error; + if ((form == POLDIFF_FORM_ADDED && + qpol_common_get_name(diff->mod_qpol, c, &name) < 0) || + ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && + qpol_common_get_name(diff->orig_qpol, c, &name) < 0)) { + return -1; + } + pc = make_common_diff(diff, form, name); + if (pc == NULL) { + return -1; + } + if (apol_vector_append(diff->common_diffs->diffs, pc) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + common_free(pc); + errno = error; + return -1; + } + if (form == POLDIFF_FORM_ADDED) { + diff->common_diffs->num_added++; + } else { + diff->common_diffs->num_removed++; + } + return 0; +} + +/** + * Given a common class, return a vector of its permissions (in the + * form of strings). + * + * @param diff Policy diff error handler. + * @param p Policy from which the common came. + * @param common Common whose permissions to get. + * + * @return Vector of permissions strings for the common. The caller + * is responsible for calling apol_vector_destroy(). On error, return + * NULL. + */ +static apol_vector_t *common_get_perms(const poldiff_t * diff, const apol_policy_t * p, const qpol_common_t * common) +{ + qpol_iterator_t *perm_iter = NULL; + char *perm; + apol_vector_t *v = NULL; + qpol_policy_t *q = apol_policy_get_qpol(p); + int retval = -1; + + if ((v = apol_vector_create(NULL)) == NULL) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + if (qpol_common_get_perm_iter(q, common, &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) { + goto cleanup; + } + if (apol_vector_append(v, perm) < 0) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&perm_iter); + if (retval < 0) { + apol_vector_destroy(&v); + return NULL; + } + return v; +} + +int common_deep_diff(poldiff_t * diff, const void *x, const void *y) +{ + const qpol_common_t *c1 = x; + const qpol_common_t *c2 = y; + apol_vector_t *v1 = NULL, *v2 = NULL; + char *perm1 = NULL, *perm2 = NULL; + const char *name; + poldiff_common_t *c = NULL; + size_t i, j; + int retval = -1, error = 0, compval; + + if (qpol_common_get_name(diff->orig_qpol, c1, &name) < 0 || + (v1 = common_get_perms(diff, diff->orig_pol, c1)) == NULL || (v2 = common_get_perms(diff, diff->mod_pol, c2)) == NULL) { + error = errno; + goto cleanup; + } + apol_vector_sort(v1, apol_str_strcmp, NULL); + apol_vector_sort(v2, apol_str_strcmp, NULL); + for (i = j = 0; i < apol_vector_get_size(v1);) { + if (j >= apol_vector_get_size(v2)) + break; + perm1 = (char *)apol_vector_get_element(v1, i); + perm2 = (char *)apol_vector_get_element(v2, j); + compval = strcmp(perm1, perm2); + if (compval != 0 && c == NULL) { + if ((c = make_common_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) { + error = errno; + goto cleanup; + } + } + if (compval < 0) { + if ((perm1 = strdup(perm1)) == NULL || apol_vector_append(c->removed_perms, perm1) < 0) { + error = errno; + free(perm1); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + i++; + } else if (compval > 0) { + if ((perm2 = strdup(perm2)) == NULL || apol_vector_append(c->added_perms, perm2) < 0) { + error = errno; + free(perm2); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + j++; + } else { + i++; + j++; + } + } + for (; i < apol_vector_get_size(v1); i++) { + perm1 = (char *)apol_vector_get_element(v1, i); + if (c == NULL) { + if ((c = make_common_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) { + error = errno; + goto cleanup; + } + } + if ((perm1 = strdup(perm1)) == NULL || apol_vector_append(c->removed_perms, perm1) < 0) { + error = errno; + free(perm1); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + for (; j < apol_vector_get_size(v2); j++) { + perm2 = (char *)apol_vector_get_element(v2, j); + if (c == NULL) { + if ((c = make_common_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) { + error = errno; + goto cleanup; + } + } + if ((perm2 = strdup(perm2)) == NULL || apol_vector_append(c->added_perms, perm2) < 0) { + error = errno; + free(perm2); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + if (c != NULL) { + apol_vector_sort(c->removed_perms, apol_str_strcmp, NULL); + apol_vector_sort(c->added_perms, apol_str_strcmp, NULL); + if (apol_vector_append(diff->common_diffs->diffs, c) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + diff->common_diffs->num_modified++; + } + retval = 0; + cleanup: + apol_vector_destroy(&v1); + apol_vector_destroy(&v2); + if (retval != 0) { + common_free(c); + } + errno = error; + return retval; +} diff --git a/libpoldiff/src/class_internal.h b/libpoldiff/src/class_internal.h new file mode 100644 index 0000000..69f6fc0 --- /dev/null +++ b/libpoldiff/src/class_internal.h @@ -0,0 +1,212 @@ +/** + * @file + * Protected interface for class and common differences. + * + * @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 POLDIFF_CLASS_INTERNAL_H +#define POLDIFF_CLASS_INTERNAL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************** object classes ********************/ + + typedef struct poldiff_class_summary poldiff_class_summary_t; + +/** + * Allocate and return a new poldiff_class_summary_t object. + * + * @return A new class summary. The caller must call class_destroy() + * afterwards. On error, return NULL and set errno. + */ + poldiff_class_summary_t *class_create(void); + +/** + * Deallocate all space associated with a poldiff_class_summary_t + * object, including the pointer itself. If the pointer is already + * NULL then do nothing. + * + * @param cs Reference to a class summary to destroy. The pointer + * will be set to NULL afterwards. + */ + void class_destroy(poldiff_class_summary_t ** cs); + +/** + * Reset the state of all class differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int class_reset(poldiff_t * diff); + +/** + * Get a vector of all object classes (type qpol_class_t) from the + * given policy, sorted by name. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * + * @return a newly allocated vector of all classes. The caller is + * responsible for calling apol_vector_destroy() afterwards. On + * error, return NULL and set errno. + */ + apol_vector_t *class_get_items(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Compare two qpol_class_t objects, determining if they have the same + * name or not. + * + * @param x The class from the original policy. + * @param y The class from the modified policy. + * @param diff The policy difference structure associated with both + * policies. + * + * @return < 0, 0, or > 0 if class x is respectively less than, equal + * to, or greater than class y. + */ + int class_comp(const void *x, const void *y, const poldiff_t * diff); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a class. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int class_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Compute the semantic difference of two classes for which the + * compare callback returns 0. If a difference is found then + * allocate, initialize, and insert a new semantic difference entry + * for that class. + * + * @param diff The policy difference structure associated with both + * classes and to which to add an entry if needed. + * @param x The class from the original policy. + * @param y The class from the modified policy. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int class_deep_diff(poldiff_t * diff, const void *x, const void *y); + +/******************** common classes ********************/ + + typedef struct poldiff_common_summary poldiff_common_summary_t; + +/** + * Allocate and return a new poldiff_common_summary_t object. + * + * @return A new common summary. The caller must call + * common_destroy() afterwards. On error, return NULL and set errno. + */ + poldiff_common_summary_t *common_create(void); + +/** + * Deallocate all space associated with a poldiff_common_summary_t + * object, including the pointer itself. If the pointer is already + * NULL then do nothing. + * + * @param cs Reference to a common summary to destroy. The pointer + * will be set to NULL afterwards. + */ + void common_destroy(poldiff_common_summary_t ** cs); + +/** + * Reset the state of all common differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int common_reset(poldiff_t * diff); + +/** + * Get a vector of all common classes (type qpol_common_t) from the + * given policy, sorted by name. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * + * @return a newly allocated vector of all commons. The caller is + * responsible for calling apol_vector_destroy() afterwards. On + * error, return NULL and set errno. + */ + apol_vector_t *common_get_items(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Compare two qpol_common_t objects, determining if they have the + * same name or not. + * + * @param x The common from the original policy. + * @param y The common from the modified policy. + * @param diff The policy difference structure associated with both + * policies. + * + * @return < 0, 0, or > 0 if common x is respectively less than, equal + * to, or greater than common y. + */ + int common_comp(const void *x, const void *y, const poldiff_t * diff); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a common. + * + * @param diff The policy difference structure to which to add the + * entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int common_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Compute the semantic difference of two commons for which the + * compare callback returns 0. If a difference is found then + * allocate, initialize, and insert a new semantic difference entry + * for that common. + * + * @param diff The policy difference structure associated with both + * commons and to which to add an entry if needed. + * @param x The common from the original policy. + * @param y The common from the modified policy. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int common_deep_diff(poldiff_t * diff, const void *x, const void *y); + +#ifdef __cplusplus +} +#endif + +#endif /* POLDIFF_CLASS_INTERNAL_H */ diff --git a/libpoldiff/src/level_diff.c b/libpoldiff/src/level_diff.c new file mode 100644 index 0000000..b384519 --- /dev/null +++ b/libpoldiff/src/level_diff.c @@ -0,0 +1,769 @@ +/** + * @file + * Implementation for computing semantic differences in levels. + * + * @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 + */ + +#include <config.h> + +#include "poldiff_internal.h" + +#include <apol/util.h> +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +struct poldiff_level_summary +{ + size_t num_added; + size_t num_removed; + size_t num_modified; + apol_vector_t *diffs; +}; + +void poldiff_level_get_stats(const poldiff_t * diff, size_t stats[5]) +{ + if (diff == NULL || stats == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return; + } + stats[0] = diff->level_diffs->num_added; + stats[1] = diff->level_diffs->num_removed; + stats[2] = diff->level_diffs->num_modified; + stats[3] = 0; + stats[4] = 0; +} + +const apol_vector_t *poldiff_get_level_vector(const poldiff_t * diff) +{ + if (diff == NULL) { + errno = EINVAL; + return NULL; + } + return diff->level_diffs->diffs; +} + +char *poldiff_level_to_string(const poldiff_t * diff, const void *level) +{ + poldiff_level_t *l = (poldiff_level_t *) level; + size_t num_added, num_removed, len = 0, i; + char *s = NULL, *cat; + if (diff == NULL || level == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + num_added = apol_vector_get_size(l->added_cats); + num_removed = apol_vector_get_size(l->removed_cats); + switch (l->form) { + case POLDIFF_FORM_ADDED: + { + if (apol_str_appendf(&s, &len, "+ %s", l->name) < 0) { + break; + } + return s; + } + case POLDIFF_FORM_REMOVED: + { + if (apol_str_appendf(&s, &len, "- %s", l->name) < 0) { + break; + } + return s; + } + case POLDIFF_FORM_MODIFIED: + { + if (apol_str_appendf(&s, &len, "* %s (", l->name) < 0) { + break; + } + if (num_added > 0) { + if (apol_str_appendf(&s, &len, "%zd Added %s", num_added, (num_added == 1 ? "Category" : "Categories")) < 0) { + break; + } + } + if (num_removed > 0) { + if (apol_str_appendf + (&s, &len, "%s%zd Removed %s", (num_added > 0 ? ", " : ""), num_removed, + (num_removed == 1 ? "Category" : "Categories")) < 0) { + break; + } + } + if (apol_str_append(&s, &len, ")\n") < 0) { + break; + } + for (i = 0; i < apol_vector_get_size(l->added_cats); i++) { + cat = (char *)apol_vector_get_element(l->added_cats, i); + if (apol_str_appendf(&s, &len, "\t+ %s\n", cat) < 0) { + goto err; + } + } + for (i = 0; i < apol_vector_get_size(l->removed_cats); i++) { + cat = (char *)apol_vector_get_element(l->removed_cats, i); + if (apol_str_appendf(&s, &len, "\t- %s\n", cat) < 0) { + goto err; + } + } + return s; + } + default: + { + ERR(diff, "%s", strerror(ENOTSUP)); + errno = ENOTSUP; + return NULL; + } + } + err: + /* if this is reached then an error occurred */ + free(s); + ERR(diff, "%s", strerror(ENOMEM)); + errno = ENOMEM; + return NULL; +} + +char *poldiff_level_to_string_brief(const poldiff_t * diff, const poldiff_level_t * level) +{ + char *s = NULL, t, *cat, *sep = ""; + int show_cat_sym = 0; + size_t len = 0, i; + switch (level->form) { + case POLDIFF_FORM_ADDED: + t = '+'; + break; + case POLDIFF_FORM_REMOVED: + t = '-'; + break; + case POLDIFF_FORM_MODIFIED: + t = '*'; + show_cat_sym = 1; + break; + default: + /* don't show unmodified levels */ + if ((s = strdup("")) == NULL) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + return s; + } + if (apol_str_appendf(&s, &len, "%c %s", t, level->name) < 0) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + if ((level->unmodified_cats != NULL && apol_vector_get_size(level->unmodified_cats) > 0) || + (level->added_cats != NULL && apol_vector_get_size(level->added_cats) > 0) || + (level->removed_cats != NULL && apol_vector_get_size(level->removed_cats) > 0)) { + if (apol_str_append(&s, &len, " : ") < 0) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + for (i = 0; level->unmodified_cats != NULL && i < apol_vector_get_size(level->unmodified_cats); i++) { + cat = apol_vector_get_element(level->unmodified_cats, i); + if (apol_str_appendf(&s, &len, "%s%s", sep, cat) < 0) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + sep = ","; + } + for (i = 0; level->added_cats != NULL && i < apol_vector_get_size(level->added_cats); i++) { + cat = apol_vector_get_element(level->added_cats, i); + if (apol_str_appendf(&s, &len, "%s%s%s", sep, (show_cat_sym ? "+" : ""), cat) < 0) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + sep = ","; + } + for (i = 0; level->removed_cats != NULL && i < apol_vector_get_size(level->removed_cats); i++) { + cat = apol_vector_get_element(level->removed_cats, i); + if (apol_str_appendf(&s, &len, "%s%s%s", sep, (show_cat_sym ? "-" : ""), cat) < 0) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + sep = ","; + } + } + if (apol_str_append(&s, &len, "\n") < 0) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + return s; +} + +const char *poldiff_level_get_name(const poldiff_level_t * level) +{ + if (level == NULL) { + errno = EINVAL; + return NULL; + } + + return level->name; +} + +poldiff_form_e poldiff_level_get_form(const void *level) +{ + if (level == NULL) { + errno = EINVAL; + return POLDIFF_FORM_NONE; + } + + return ((const poldiff_level_t *)level)->form; +} + +const apol_vector_t *poldiff_level_get_added_cats(const poldiff_level_t * level) +{ + if (level == NULL) { + errno = EINVAL; + return NULL; + } + return level->added_cats; +} + +const apol_vector_t *poldiff_level_get_removed_cats(const poldiff_level_t * level) +{ + if (level == NULL) { + errno = EINVAL; + return NULL; + } + + return level->removed_cats; +} + +const apol_vector_t *poldiff_level_get_unmodified_cats(const poldiff_level_t * level) +{ + if (level == NULL) { + errno = EINVAL; + return NULL; + } + + return level->unmodified_cats; +} + +poldiff_level_summary_t *level_create(void) +{ + poldiff_level_summary_t *ls = calloc(1, sizeof(poldiff_level_summary_t)); + if (ls == NULL) + return NULL; + if ((ls->diffs = apol_vector_create(level_free)) == NULL) { + level_destroy(&ls); + return NULL; + } + return ls; +} + +void level_destroy(poldiff_level_summary_t ** ls) +{ + if (ls == NULL || *ls == NULL) + return; + apol_vector_destroy(&(*ls)->diffs); + free(*ls); + *ls = NULL; +} + +int level_reset(poldiff_t * diff) +{ + int error = 0; + + if (diff == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + level_destroy(&diff->level_diffs); + diff->level_diffs = level_create(); + if (diff->level_diffs == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + errno = error; + return -1; + } + + return 0; +} + +/** + * Comparison function for two levels from the same policy. + */ +static int level_name_comp(const void *x, const void *y, void *arg) +{ + const qpol_level_t *s1 = x; + const qpol_level_t *s2 = y; + apol_policy_t *p = arg; + qpol_policy_t *q = apol_policy_get_qpol(p); + const char *name1, *name2; + + if (qpol_level_get_name(q, s1, &name1) < 0 || qpol_level_get_name(q, s2, &name2) < 0) + return 0; + return strcmp(name1, name2); +} + +apol_vector_t *level_get_items(poldiff_t * diff, const apol_policy_t * policy) +{ + qpol_iterator_t *iter = NULL; + apol_vector_t *v = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policy); + int error = 0; + if (qpol_policy_get_level_iter(q, &iter) < 0) { + return NULL; + } + v = apol_vector_create_from_iter(iter, NULL); + if (v == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + qpol_iterator_destroy(&iter); + errno = error; + return NULL; + } + qpol_iterator_destroy(&iter); + apol_vector_sort(v, level_name_comp, (void *)policy); + return v; +} + +int level_comp(const void *x, const void *y, const poldiff_t * diff) +{ + const qpol_level_t *l1 = x; + const qpol_level_t *l2 = y; + const char *name1, *name2; + if (qpol_level_get_name(diff->orig_qpol, l1, &name1) < 0 || qpol_level_get_name(diff->mod_qpol, l2, &name2) < 0) { + return 0; + } + return strcmp(name1, name2); +} + +/** + * Allocate and return a new level difference object. + * + * @param diff Policy diff error handler. + * @param form Form of the difference. + * @param name Name of the level that is different. + * + * @return A newly allocated and initialized diff, or NULL upon error. + * The caller is responsible for calling level_free() upon the returned + * value. + */ +static poldiff_level_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name) +{ + poldiff_level_t *pl; + int error; + if ((pl = calloc(1, sizeof(*pl))) == NULL || (pl->name = strdup(name)) == NULL || + (pl->added_cats = apol_vector_create(free)) == NULL || + (pl->removed_cats = apol_vector_create(free)) == NULL || (pl->unmodified_cats = apol_vector_create(free)) == NULL) { + error = errno; + level_free(pl); + ERR(diff, "%s", strerror(error)); + errno = error; + return NULL; + } + pl->form = form; + return pl; +} + +/** + * Given a level, return a vector of its allowed categories (in the + * form of strings). These will be sorted in policy order. + * + * @param diff Policy diff error handler. + * @param p Policy from which the level came. + * @param level Level whose categories to get. + * + * @return Vector of category strings for the level. The caller is + * responsible for calling apol_vector_destroy(). On error, return + * NULL. + */ +static apol_vector_t *level_get_cats(const poldiff_t * diff, const apol_policy_t * p, const qpol_level_t * level) +{ + qpol_iterator_t *iter = NULL; + const qpol_cat_t *cat; + const char *cat_name; + apol_vector_t *v = NULL; + qpol_policy_t *q = apol_policy_get_qpol(p); + int retval = -1, error = 0; + + if ((v = apol_vector_create(NULL)) == NULL) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + if (qpol_level_get_cat_iter(q, level, &iter) < 0) { + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&cat) < 0 || qpol_cat_get_name(q, cat, &cat_name)) { + error = errno; + goto cleanup; + } + if (apol_vector_append(v, (void *)cat_name) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + if (retval < 0) { + apol_vector_destroy(&v); + errno = error; + return NULL; + } + return v; +} + +int level_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + const qpol_level_t *l = item; + const char *name = NULL; + poldiff_level_t *pl = NULL; + apol_policy_t *p; + qpol_policy_t *q; + apol_vector_t *v = NULL; + int error = 0, retval = -1; + if (form == POLDIFF_FORM_ADDED) { + p = diff->mod_pol; + q = diff->mod_qpol; + } else { + p = diff->orig_pol; + q = diff->orig_qpol; + } + if (qpol_level_get_name(q, l, &name) < 0 || (pl = make_diff(diff, form, name)) == NULL) { + error = errno; + goto cleanup; + } + if ((v = level_get_cats(diff, p, l)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + if (form == POLDIFF_FORM_ADDED) { + apol_vector_destroy(&pl->added_cats); + if ((pl->added_cats = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } else if (form == POLDIFF_FORM_REMOVED) { + apol_vector_destroy(&pl->removed_cats); + if ((pl->removed_cats = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + if (apol_vector_append(diff->level_diffs->diffs, pl) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + if (form == POLDIFF_FORM_ADDED) { + diff->level_diffs->num_added++; + } else { + diff->level_diffs->num_removed++; + } + retval = 0; + cleanup: + apol_vector_destroy(&v); + if (retval < 0) { + level_free(pl); + errno = error; + } + return retval; +} + +/** + * Comparison function for two category names from the same policy. + * + * @param a Name of a category. + * @param b Name of another category. + * @param data qpol policy from which the categories originate. + * + * @return Less than zero, zero, or greater than zero based upon the + * categories' order within the policy. + */ +static int level_cat_comp(const void *a, const void *b, void *data) +{ + const char *name1 = (const char *)a; + const char *name2 = (const char *)b; + qpol_policy_t *q = (qpol_policy_t *) data; + const qpol_cat_t *cat1, *cat2; + qpol_policy_get_cat_by_name(q, name1, &cat1); + qpol_policy_get_cat_by_name(q, name2, &cat2); + assert(cat1 != NULL && cat2 != NULL); + uint32_t val1, val2; + qpol_cat_get_value(q, cat1, &val1); + qpol_cat_get_value(q, cat2, &val2); + return val1 - val2; +} + +int level_deep_diff(poldiff_t * diff, const void *x, const void *y) +{ + const qpol_level_t *l1 = x; + const qpol_level_t *l2 = y; + apol_vector_t *v1 = NULL, *v2 = NULL; + apol_vector_t *added = NULL, *removed = NULL, *unmodified = NULL; + const char *name; + poldiff_level_t *l = NULL; + int retval = -1, error = 0, compval; + + if (qpol_level_get_name(diff->orig_qpol, l1, &name) < 0 || + (v1 = level_get_cats(diff, diff->orig_pol, l1)) == NULL || (v2 = level_get_cats(diff, diff->mod_pol, l2)) == NULL) { + error = errno; + goto cleanup; + } + apol_vector_sort(v1, apol_str_strcmp, NULL); + apol_vector_sort(v2, apol_str_strcmp, NULL); + compval = level_deep_diff_cats(diff, v1, v2, &added, &removed, &unmodified); + if (compval < 0) { + error = errno; + goto cleanup; + } else if (compval > 0) { + if ((l = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) { + error = errno; + goto cleanup; + } + apol_vector_destroy(&l->added_cats); + apol_vector_destroy(&l->removed_cats); + apol_vector_destroy(&l->unmodified_cats); + if ((l->added_cats = apol_vector_create_from_vector(added, apol_str_strdup, NULL, free)) == NULL || + (l->removed_cats = apol_vector_create_from_vector(removed, apol_str_strdup, NULL, free)) == NULL || + (l->unmodified_cats = apol_vector_create_from_vector(unmodified, apol_str_strdup, NULL, free)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + apol_vector_sort(l->removed_cats, level_cat_comp, diff->orig_qpol); + apol_vector_sort(l->added_cats, level_cat_comp, diff->mod_qpol); + apol_vector_sort(l->unmodified_cats, level_cat_comp, diff->orig_qpol); + if (apol_vector_append(diff->level_diffs->diffs, l) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + diff->level_diffs->num_modified++; + } + retval = 0; + cleanup: + apol_vector_destroy(&v1); + apol_vector_destroy(&v2); + apol_vector_destroy(&added); + apol_vector_destroy(&removed); + apol_vector_destroy(&unmodified); + if (retval != 0) { + level_free(l); + } + errno = error; + return retval; +} + +poldiff_level_t *level_create_from_apol_mls_level(const apol_mls_level_t * level, poldiff_form_e form) +{ + const char *sens = apol_mls_level_get_sens(level); + const apol_vector_t *cats = apol_mls_level_get_cats(level); + poldiff_level_t *pl = NULL; + apol_vector_t **target; + if ((pl = calloc(1, sizeof(*pl))) == NULL || + (pl->name = strdup(sens)) == NULL || (pl->unmodified_cats = apol_vector_create_with_capacity(1, free)) == NULL) { + level_free(pl); + return NULL;; + } + pl->form = form; + if (form == POLDIFF_FORM_ADDED) { + if ((pl->removed_cats = apol_vector_create_with_capacity(1, free)) == NULL) { + level_free(pl); + return NULL; + } + target = &pl->added_cats; + } else if (form == POLDIFF_FORM_REMOVED) { + if ((pl->added_cats = apol_vector_create_with_capacity(1, free)) == NULL) { + level_free(pl); + return NULL; + } + target = &pl->removed_cats; + } else { + if ((pl->added_cats = apol_vector_create_with_capacity(1, free)) == NULL || + (pl->removed_cats = apol_vector_create_with_capacity(1, free)) == NULL) { + level_free(pl); + return NULL; + } + return pl; + } + if ((*target = apol_vector_create_from_vector(cats, apol_str_strdup, NULL, free)) == NULL) { + level_free(pl); + return NULL; + } + return pl; +} + +void level_free(void *elem) +{ + poldiff_level_t *s = elem; + if (!elem) + return; + free(s->name); + apol_vector_destroy(&s->added_cats); + apol_vector_destroy(&s->removed_cats); + apol_vector_destroy(&s->unmodified_cats); + free(s); +} + +int level_deep_diff_apol_mls_levels(poldiff_t * diff, const apol_mls_level_t * level1, const apol_mls_level_t * level2, + poldiff_level_t ** orig_pl, poldiff_level_t ** mod_pl) +{ + poldiff_level_t *u1 = NULL, *u2 = NULL; + apol_vector_t *added = NULL, *removed = NULL, *unmodified = NULL; + const char *sens1 = apol_mls_level_get_sens(level1); + const apol_vector_t *cats1 = apol_mls_level_get_cats(level1); + const char *sens2 = apol_mls_level_get_sens(level2); + const apol_vector_t *cats2 = apol_mls_level_get_cats(level2); + int retval = -1, compval; + + *orig_pl = *mod_pl = NULL; + if (strcmp(sens1, sens2) != 0) { + /* sensitivities do not match, so don't check categories */ + if ((u1 = make_diff(diff, POLDIFF_FORM_REMOVED, sens1)) == NULL || + (u2 = make_diff(diff, POLDIFF_FORM_ADDED, sens2)) == NULL) { + ERR(diff, "%s", strerror(errno)); + level_free(u1); + level_free(u2); + return -1; + } + apol_vector_destroy(&u1->removed_cats); + apol_vector_destroy(&u2->added_cats); + if ((u1->removed_cats = apol_vector_create_from_vector(cats1, apol_str_strdup, NULL, free)) == NULL || + (u2->added_cats = apol_vector_create_from_vector(cats2, apol_str_strdup, NULL, free)) == NULL) { + ERR(diff, "%s", strerror(errno)); + level_free(u1); + level_free(u2); + return -1; + } + apol_vector_sort(u1->removed_cats, level_cat_comp, diff->orig_qpol); + apol_vector_sort(u2->added_cats, level_cat_comp, diff->mod_qpol); + *orig_pl = u1; + *mod_pl = u2; + return 0; + } + + compval = level_deep_diff_cats(diff, cats1, cats2, &added, &removed, &unmodified); + if (compval < 0) { + goto cleanup; + } else if (compval > 0) { + if ((u1 = calloc(1, sizeof(*u1))) == NULL || (u1->name = strdup(sens1)) == NULL || + (u1->added_cats = apol_vector_create_from_vector(added, apol_str_strdup, NULL, free)) == NULL || + (u1->removed_cats = apol_vector_create_from_vector(removed, apol_str_strdup, NULL, free)) == NULL || + (u1->unmodified_cats = apol_vector_create_from_vector(unmodified, apol_str_strdup, NULL, free)) == NULL) { + ERR(diff, "%s", strerror(errno)); + level_free(u1); + goto cleanup; + } + apol_vector_sort(u1->added_cats, level_cat_comp, diff->mod_qpol); + apol_vector_sort(u1->removed_cats, level_cat_comp, diff->orig_qpol); + apol_vector_sort(u1->unmodified_cats, level_cat_comp, diff->orig_qpol); + u1->form = POLDIFF_FORM_MODIFIED; + *orig_pl = u1; + } + retval = 0; + cleanup: + apol_vector_destroy(&added); + apol_vector_destroy(&removed); + apol_vector_destroy(&unmodified); + return retval; +} + +int level_deep_diff_cats(poldiff_t * diff, const apol_vector_t * v1, const apol_vector_t * v2, apol_vector_t ** added, + apol_vector_t ** removed, apol_vector_t ** unmodified) +{ + size_t i, j; + char *cat1, *cat2; + int compval, retval = -1, error = 0; + *added = *removed = *unmodified = NULL; + if ((*added = apol_vector_create(free)) == NULL || + (*removed = apol_vector_create(free)) == NULL || (*unmodified = apol_vector_create(free)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + for (i = j = 0; i < apol_vector_get_size(v1);) { + if (j >= apol_vector_get_size(v2)) { + break; + } + cat1 = (char *)apol_vector_get_element(v1, i); + cat2 = (char *)apol_vector_get_element(v2, j); + compval = strcmp(cat1, cat2); + if (compval < 0) { + if ((cat1 = strdup(cat1)) == NULL || apol_vector_append(*removed, cat1) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + free(cat1); + goto cleanup; + } + i++; + } else if (compval > 0) { + if ((cat2 = strdup(cat2)) == NULL || apol_vector_append(*added, cat2) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + free(cat2); + goto cleanup; + } + j++; + } else { + if ((cat1 = strdup(cat1)) == NULL || apol_vector_append(*unmodified, cat1) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + free(cat1); + goto cleanup; + } + i++; + j++; + } + } + for (; i < apol_vector_get_size(v1); i++) { + cat1 = (char *)apol_vector_get_element(v1, i); + if ((cat1 = strdup(cat1)) == NULL || apol_vector_append(*removed, cat1) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + free(cat1); + goto cleanup; + } + } + for (; j < apol_vector_get_size(v2); j++) { + cat2 = (char *)apol_vector_get_element(v2, j); + if ((cat2 = strdup(cat2)) == NULL || apol_vector_append(*added, cat2) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + free(cat2); + goto cleanup; + } + } + if (apol_vector_get_size(*added) > 0 || apol_vector_get_size(*removed) > 0) { + retval = 1; + } else { + retval = 0; + } + cleanup: + if (retval <= 0) { + /* if no differences found, then destroy all vectors */ + apol_vector_destroy(added); + apol_vector_destroy(removed); + apol_vector_destroy(unmodified); + } + if (retval < 0) { + error = errno; + } + return retval; +} diff --git a/libpoldiff/src/level_internal.h b/libpoldiff/src/level_internal.h new file mode 100644 index 0000000..4a0f603 --- /dev/null +++ b/libpoldiff/src/level_internal.h @@ -0,0 +1,208 @@ +/** + * @file + * Protected interface for computing semantic differences in levels, + * either from level declarations, user's default level, user's + * permitted range, or a range_transition's target range. + * + * @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 POLDIFF_LEVEL_INTERNAL_H +#define POLDIFF_LEVEL_INTERNAL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct poldiff_level_summary poldiff_level_summary_t; + + struct poldiff_level + { + char *name; + poldiff_form_e form; + apol_vector_t *added_cats; + apol_vector_t *removed_cats; + apol_vector_t *unmodified_cats; + }; + +/** + * Allocate and return a new poldiff_level_summary_t object. + * + * @return A new level summary. The caller must call level_destroy() + * afterwards. On error, return NULL and set errno. + */ + poldiff_level_summary_t *level_create(void); + +/** + * Deallocate all space associated with a poldiff_level_summary_t + * object, including the pointer itself. If the pointer is already + * NULL then do nothing. + * + * @param ls Reference to a level summary to destroy. The pointer + * will be set to NULL afterwards. + */ + void level_destroy(poldiff_level_summary_t ** ls); + +/** + * Reset the state of all level differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int level_reset(poldiff_t * diff); + +/** + * Get a vector of all levels from the given policy, sorted by name. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * + * @return a newly allocated vector of all levels. The caller is + * responsible for calling apol_vector_destroy() afterwards. On + * error, return NULL and set errno. + */ + apol_vector_t *level_get_items(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Compare two qpol_level_t objects, determining if they have the same + * level name or not. + * + * @param x The level from the original policy. + * @param y The level from the modified policy. + * @param diff The policy difference structure associated with both + * policies. + * + * @return < 0, 0, or > 0 if level x is respectively less than, equal + * to, or greater than level y. + */ + int level_comp(const void *x, const void *y, const poldiff_t * diff); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a level. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int level_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Compute the semantic difference of two levels for which the compare + * callback returns 0. If a difference is found then allocate, + * initialize, and insert a new semantic difference entry for that + * level. + * + * @param diff The policy difference structure associated with both + * levels and to which to add an entry if needed. + * @param x The level from the original policy. + * @param y The level from the modified policy. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int level_deep_diff(poldiff_t * diff, const void *x, const void *y); + +/********************* + * The remainder are protected functions that operate upon a single + * poldiff_level_t. These are used by user's default level, user's + * assigned range, etc. + *********************/ + +/** + * Allocate and return a poldiff_level_t object. If the form is added + * or removed, set that respective vector to be all of the categories + * from the given level. + * + * @param level Level object to use as a template. + * @param form Form of difference for the level. + * + * @return Initialized level, or NULL upon error. Caller must call + * level_free() upon the returned value. + */ + poldiff_level_t *level_create_from_apol_mls_level(const apol_mls_level_t * level, poldiff_form_e form); + +/** + * Deallocate all space associated with a poldiff_level_t, including + * the pointer itself. + * + * @param elem Pointer to a poldiff_level_t object. If NULL then do + * nothing. + */ + void level_free(void *elem); + +/** + * Perform a deep diff of two levels. This will first compare the + * sensitivity names; if they match then it compares the vectors of + * category names. If the sensitivities do not match, then generate + * two poldiff_level_ts, one for the original level and one for + * modified level. If they do match then create just one + * poldiff_level_t and write it to orig_pl. + * + * @param diff Poldiff object, used for error reporting and for + * sorting the categories to policy order. + * @param level1 Original level. + * @param level2 Modified level. + * @param orig_pl Destination to where to write the poldiff_level_t, + * if the sensitivites do not match or if the categories do not match. + * @param mod_pl Destination to where to write the poldiff_level_t, + * if the sensitivities do not match. + * + * @return 0 on success, < 0 on error. + */ + int level_deep_diff_apol_mls_levels(poldiff_t * diff, const apol_mls_level_t * level1, const apol_mls_level_t * level2, + poldiff_level_t ** orig_pl, poldiff_level_t ** mod_pl); + +/** + * Calculate the differences between two sorted vectors of category + * names. Allocate the vectors added, removed, and unmodified; fill + * them with appropriate category names. The returned vectors' + * categories will be sorted alphabetically. + * + * @param diff Error handler. + * @param v1 First vector of category names, sorted alphabetically. + * @param v2 Other vector of category names, sorted alphabetically. + * @param added Reference to where to store added categories. The + * caller is responsible for calling apol_vector_destroy() upon the + * value. If no differences are found then this will be set to NULL. + * @param removed Reference to where to store removed categories. The + * caller is responsible for calling apol_vector_destroy() upon the + * value. If no differences are found then this will be set to NULL. + * @param unmodified Reference to where to store unmodified + * categories. The caller is responsible for calling + * apol_vector_destroy() upon the value. If no differences are found + * then this will be set to NULL. + * + * @return Greater than zero if a difference was found, zero upon no + * differences, less than zero on error. + */ + int level_deep_diff_cats(poldiff_t * diff, const apol_vector_t * v1, const apol_vector_t * v2, apol_vector_t ** added, + apol_vector_t ** removed, apol_vector_t ** unmodified); + +#ifdef __cplusplus +} +#endif + +#endif /* POLDIFF_LEVEL_INTERNAL_H */ diff --git a/libpoldiff/src/libpoldiff.map b/libpoldiff/src/libpoldiff.map new file mode 100644 index 0000000..29950c0 --- /dev/null +++ b/libpoldiff/src/libpoldiff.map @@ -0,0 +1,50 @@ +VERS_1.2{ + global: + poldiff_create; + poldiff_destroy; + poldiff_run; + poldiff_is_run; + poldiff_type_remap_*; + poldiff_get_*; + poldiff_attrib_*; + poldiff_avrule_*; + poldiff_bool_*; + poldiff_cat_*; + poldiff_class_*; + poldiff_common_*; + poldiff_level_*; + poldiff_range_*; + poldiff_range_trans_*; + poldiff_role_*; + poldiff_role_allow_*; + poldiff_role_trans_*; + poldiff_terule_*; + poldiff_type_*; + poldiff_user_*; + libpoldiff_get_version; + poldiff_enable_line_numbers; + local: *; +}; + +VERS_1.3{ + global: + poldiff_avrule_get_stats_allow; + poldiff_avrule_get_stats_auditallow; + poldiff_avrule_get_stats_dontaudit; + poldiff_avrule_get_stats_neverallow; + poldiff_get_avrule_vector_allow; + poldiff_get_avrule_vector_auditallow; + poldiff_get_avrule_vector_dontaudit; + poldiff_get_avrule_vector_neverallow; + poldiff_component_record_*; + poldiff_range_get_min_added_cats; + poldiff_range_get_min_removed_cats; + poldiff_range_get_min_unmodified_cats; + poldiff_role_allow_get_unmodified_roles; + poldiff_terule_get_stats_change; + poldiff_terule_get_stats_member; + poldiff_terule_get_stats_trans; + poldiff_get_terule_vector_change; + poldiff_get_terule_vector_member; + poldiff_get_terule_vector_trans; +} VERS_1.2; diff --git a/libpoldiff/src/poldiff.c b/libpoldiff/src/poldiff.c new file mode 100644 index 0000000..cce4fc4 --- /dev/null +++ b/libpoldiff/src/poldiff.c @@ -0,0 +1,814 @@ +/** + * @file + * Implementation for computing a semantic policy difference. + * + * @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 "poldiff_internal.h" +#include <poldiff/component_record.h> + +#include <apol/util.h> +#include <qpol/policy_extend.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +/** + * All policy items (object classes, types, rules, etc.) must + * implement at least these functions. Next, a record should be + * appended to the array 'component_records' below. + */ +struct poldiff_component_record +{ + const char *item_name; + uint32_t flag_bit; + poldiff_get_item_stats_fn_t get_stats; + poldiff_get_result_items_fn_t get_results; + poldiff_item_get_form_fn_t get_form; + poldiff_item_to_string_fn_t to_string; + poldiff_reset_fn_t reset; + poldiff_get_items_fn_t get_items; + poldiff_item_comp_fn_t comp; + poldiff_new_diff_fn_t new_diff; + poldiff_deep_diff_fn_t deep_diff; +}; + +static const poldiff_component_record_t component_records[] = { + { + "attribute", + POLDIFF_DIFF_ATTRIBS, + poldiff_attrib_get_stats, + poldiff_get_attrib_vector, + poldiff_attrib_get_form, + poldiff_attrib_to_string, + attrib_reset, + attrib_get_items, + attrib_comp, + attrib_new_diff, + attrib_deep_diff, + }, + { + "Allow Rules", + POLDIFF_DIFF_AVALLOW, + poldiff_avrule_get_stats_allow, + poldiff_get_avrule_vector_allow, + poldiff_avrule_get_form, + poldiff_avrule_to_string, + avrule_reset_allow, + avrule_get_items_allow, + avrule_comp, + avrule_new_diff_allow, + avrule_deep_diff_allow, + }, + { + "Audit Allow Rules", + POLDIFF_DIFF_AVAUDITALLOW, + poldiff_avrule_get_stats_auditallow, + poldiff_get_avrule_vector_auditallow, + poldiff_avrule_get_form, + poldiff_avrule_to_string, + avrule_reset_auditallow, + avrule_get_items_auditallow, + avrule_comp, + avrule_new_diff_auditallow, + avrule_deep_diff_auditallow, + }, + { + "Don't Audit Rules", + POLDIFF_DIFF_AVDONTAUDIT, + poldiff_avrule_get_stats_dontaudit, + poldiff_get_avrule_vector_dontaudit, + poldiff_avrule_get_form, + poldiff_avrule_to_string, + avrule_reset_dontaudit, + avrule_get_items_dontaudit, + avrule_comp, + avrule_new_diff_dontaudit, + avrule_deep_diff_dontaudit, + }, + { + "Never Allow Rules", + POLDIFF_DIFF_AVNEVERALLOW, + poldiff_avrule_get_stats_neverallow, + poldiff_get_avrule_vector_neverallow, + poldiff_avrule_get_form, + poldiff_avrule_to_string, + avrule_reset_neverallow, + avrule_get_items_neverallow, + avrule_comp, + avrule_new_diff_neverallow, + avrule_deep_diff_neverallow, + }, + { + "bool", + POLDIFF_DIFF_BOOLS, + poldiff_bool_get_stats, + poldiff_get_bool_vector, + poldiff_bool_get_form, + poldiff_bool_to_string, + bool_reset, + bool_get_items, + bool_comp, + bool_new_diff, + bool_deep_diff, + }, + { + "category", + POLDIFF_DIFF_CATS, + poldiff_cat_get_stats, + poldiff_get_cat_vector, + poldiff_cat_get_form, + poldiff_cat_to_string, + cat_reset, + cat_get_items, + cat_comp, + cat_new_diff, + cat_deep_diff, + }, + { + "class", + POLDIFF_DIFF_CLASSES, + poldiff_class_get_stats, + poldiff_get_class_vector, + poldiff_class_get_form, + poldiff_class_to_string, + class_reset, + class_get_items, + class_comp, + class_new_diff, + class_deep_diff, + }, + { + "common", + POLDIFF_DIFF_COMMONS, + poldiff_common_get_stats, + poldiff_get_common_vector, + poldiff_common_get_form, + poldiff_common_to_string, + common_reset, + common_get_items, + common_comp, + common_new_diff, + common_deep_diff, + }, + { + "level", + POLDIFF_DIFF_LEVELS, + poldiff_level_get_stats, + poldiff_get_level_vector, + poldiff_level_get_form, + poldiff_level_to_string, + level_reset, + level_get_items, + level_comp, + level_new_diff, + level_deep_diff, + }, + { + "range transition", + POLDIFF_DIFF_RANGE_TRANS, + poldiff_range_trans_get_stats, + poldiff_get_range_trans_vector, + poldiff_range_trans_get_form, + poldiff_range_trans_to_string, + range_trans_reset, + range_trans_get_items, + range_trans_comp, + range_trans_new_diff, + range_trans_deep_diff, + }, + { + "role", + POLDIFF_DIFF_ROLES, + poldiff_role_get_stats, + poldiff_get_role_vector, + poldiff_role_get_form, + poldiff_role_to_string, + role_reset, + role_get_items, + role_comp, + role_new_diff, + role_deep_diff, + }, + { + "role_allow", + POLDIFF_DIFF_ROLE_ALLOWS, + poldiff_role_allow_get_stats, + poldiff_get_role_allow_vector, + poldiff_role_allow_get_form, + poldiff_role_allow_to_string, + role_allow_reset, + role_allow_get_items, + role_allow_comp, + role_allow_new_diff, + role_allow_deep_diff, + }, + { + "role_transition", + POLDIFF_DIFF_ROLE_TRANS, + poldiff_role_trans_get_stats, + poldiff_get_role_trans_vector, + poldiff_role_trans_get_form, + poldiff_role_trans_to_string, + role_trans_reset, + role_trans_get_items, + role_trans_comp, + role_trans_new_diff, + role_trans_deep_diff, + }, + { + "Type Change rules", + POLDIFF_DIFF_TECHANGE, + poldiff_terule_get_stats_change, + poldiff_get_terule_vector_change, + poldiff_terule_get_form, + poldiff_terule_to_string, + terule_reset_change, + terule_get_items_change, + terule_comp, + terule_new_diff_change, + terule_deep_diff_change, + }, + { + "Type Member Rules", + POLDIFF_DIFF_TEMEMBER, + poldiff_terule_get_stats_member, + poldiff_get_terule_vector_member, + poldiff_terule_get_form, + poldiff_terule_to_string, + terule_reset_member, + terule_get_items_member, + terule_comp, + terule_new_diff_member, + terule_deep_diff_member, + }, + { + "Type Transition Rules", + POLDIFF_DIFF_TETRANS, + poldiff_terule_get_stats_trans, + poldiff_get_terule_vector_trans, + poldiff_terule_get_form, + poldiff_terule_to_string, + terule_reset_trans, + terule_get_items_trans, + terule_comp, + terule_new_diff_trans, + terule_deep_diff_trans, + }, + { + "type", + POLDIFF_DIFF_TYPES, + poldiff_type_get_stats, + poldiff_get_type_vector, + poldiff_type_get_form, + poldiff_type_to_string, + type_reset, + type_get_items, + type_comp, + type_new_diff, + type_deep_diff, + }, + { + "user", + POLDIFF_DIFF_USERS, + poldiff_user_get_stats, + poldiff_get_user_vector, + poldiff_user_get_form, + poldiff_user_to_string, + user_reset, + user_get_items, + user_comp, + user_new_diff, + user_deep_diff, + }, +}; + +const poldiff_component_record_t *poldiff_get_component_record(uint32_t which) +{ + size_t i = 0; + size_t num_items; + + num_items = sizeof(component_records) / sizeof(poldiff_component_record_t); + for (i = 0; i < num_items; i++) { + if (component_records[i].flag_bit == which) + return &component_records[i]; + } + return NULL; +} + +poldiff_t *poldiff_create(apol_policy_t * orig_policy, apol_policy_t * mod_policy, poldiff_handle_fn_t fn, void *callback_arg) +{ + poldiff_t *diff = NULL; + int error; + + if (!orig_policy || !mod_policy) { + ERR(NULL, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + + if (!(diff = calloc(1, sizeof(poldiff_t)))) { + ERR(NULL, "%s", strerror(ENOMEM)); + errno = ENOMEM; + return NULL; + } + diff->orig_pol = orig_policy; + diff->mod_pol = mod_policy; + diff->orig_qpol = apol_policy_get_qpol(diff->orig_pol); + diff->mod_qpol = apol_policy_get_qpol(diff->mod_pol); + diff->fn = fn; + diff->handle_arg = callback_arg; + if ((diff->type_map = type_map_create()) == NULL) { + ERR(diff, "%s", strerror(ENOMEM)); + poldiff_destroy(&diff); + errno = ENOMEM; + return NULL; + } + if (type_map_infer(diff) < 0) { + error = errno; + poldiff_destroy(&diff); + errno = error; + return NULL; + } + + if ((diff->attrib_diffs = attrib_summary_create()) == NULL || + (diff->avrule_diffs[AVRULE_OFFSET_ALLOW] = avrule_create()) == NULL || + (diff->avrule_diffs[AVRULE_OFFSET_AUDITALLOW] = avrule_create()) == NULL || + (diff->avrule_diffs[AVRULE_OFFSET_DONTAUDIT] = avrule_create()) == NULL || + (diff->avrule_diffs[AVRULE_OFFSET_NEVERALLOW] = avrule_create()) == NULL || + (diff->bool_diffs = bool_create()) == NULL || + (diff->cat_diffs = cat_create()) == NULL || + (diff->class_diffs = class_create()) == NULL || + (diff->common_diffs = common_create()) == NULL || + (diff->level_diffs = level_create()) == NULL || + (diff->range_trans_diffs = range_trans_create()) == NULL || + (diff->role_diffs = role_create()) == NULL || + (diff->role_allow_diffs = role_allow_create()) == NULL || + (diff->role_trans_diffs = role_trans_create()) == NULL || + (diff->terule_diffs[TERULE_OFFSET_CHANGE] = terule_create()) == NULL || + (diff->terule_diffs[TERULE_OFFSET_MEMBER] = terule_create()) == NULL || + (diff->terule_diffs[TERULE_OFFSET_TRANS] = terule_create()) == NULL || + (diff->type_diffs = type_summary_create()) == NULL || (diff->user_diffs = user_create()) == NULL) { + ERR(diff, "%s", strerror(ENOMEM)); + poldiff_destroy(&diff); + errno = ENOMEM; + return NULL; + } + + diff->policy_opts = QPOL_POLICY_OPTION_NO_RULES | QPOL_POLICY_OPTION_NO_NEVERALLOWS; + return diff; +} + +void poldiff_destroy(poldiff_t ** diff) +{ + if (!diff || !(*diff)) + return; + apol_policy_destroy(&(*diff)->orig_pol); + apol_policy_destroy(&(*diff)->mod_pol); + apol_bst_destroy(&(*diff)->class_bst); + apol_bst_destroy(&(*diff)->perm_bst); + apol_bst_destroy(&(*diff)->bool_bst); + + type_map_destroy(&(*diff)->type_map); + attrib_summary_destroy(&(*diff)->attrib_diffs); + avrule_destroy(&(*diff)->avrule_diffs[AVRULE_OFFSET_ALLOW]); + avrule_destroy(&(*diff)->avrule_diffs[AVRULE_OFFSET_AUDITALLOW]); + avrule_destroy(&(*diff)->avrule_diffs[AVRULE_OFFSET_DONTAUDIT]); + avrule_destroy(&(*diff)->avrule_diffs[AVRULE_OFFSET_NEVERALLOW]); + bool_destroy(&(*diff)->bool_diffs); + cat_destroy(&(*diff)->cat_diffs); + class_destroy(&(*diff)->class_diffs); + common_destroy(&(*diff)->common_diffs); + level_destroy(&(*diff)->level_diffs); + range_trans_destroy(&(*diff)->range_trans_diffs); + role_destroy(&(*diff)->role_diffs); + role_allow_destroy(&(*diff)->role_allow_diffs); + role_trans_destroy(&(*diff)->role_trans_diffs); + user_destroy(&(*diff)->user_diffs); + terule_destroy(&(*diff)->terule_diffs[TERULE_OFFSET_CHANGE]); + terule_destroy(&(*diff)->terule_diffs[TERULE_OFFSET_MEMBER]); + terule_destroy(&(*diff)->terule_diffs[TERULE_OFFSET_TRANS]); + type_summary_destroy(&(*diff)->type_diffs); + free(*diff); + *diff = NULL; +} + +/** + * Given a particular policy item record (e.g., one for object + * classes), (re-)perform a diff of them between the two policies + * listed in the poldiff_t structure. Upon success, set the status + * flag within 'diff' to indicate that this diff is done. + * + * @param diff The policy difference structure containing the policies + * to compare and to populate with the item differences. + * @param component_record Item record containg callbacks to perform each + * step of the computation for a particular kind of item. + * + * @return 0 on success and < 0 on error; if the call fails; errno + * will be set and the only defined operation on the policy difference + * structure will be poldiff_destroy(). + */ +static int poldiff_do_item_diff(poldiff_t * diff, const poldiff_component_record_t * component_record) +{ + apol_vector_t *p1_v = NULL, *p2_v = NULL; + int error = 0, retv; + size_t x = 0, y = 0; + void *item_x = NULL, *item_y = NULL; + + if (!diff || !component_record) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + diff->diff_status &= (~component_record->flag_bit); + + INFO(diff, "Getting %s items from original policy.", component_record->item_name); + p1_v = component_record->get_items(diff, diff->orig_pol); + if (!p1_v) { + error = errno; + goto err; + } + + INFO(diff, "Getting %s items from modified policy.", component_record->item_name); + p2_v = component_record->get_items(diff, diff->mod_pol); + if (!p2_v) { + error = errno; + goto err; + } + + INFO(diff, "Finding differences in %s.", component_record->item_name); + for (x = 0, y = 0; x < apol_vector_get_size(p1_v);) { + if (y >= apol_vector_get_size(p2_v)) + break; + item_x = apol_vector_get_element(p1_v, x); + item_y = apol_vector_get_element(p2_v, y); + retv = component_record->comp(item_x, item_y, diff); + if (retv < 0) { + if (component_record->new_diff(diff, POLDIFF_FORM_REMOVED, item_x)) { + error = errno; + goto err; + } + x++; + } else if (retv > 0) { + if (component_record->new_diff(diff, POLDIFF_FORM_ADDED, item_y)) { + error = errno; + goto err; + } + y++; + } else { + if (component_record->deep_diff(diff, item_x, item_y)) { + error = errno; + goto err; + } + x++; + y++; + } + } + for (; x < apol_vector_get_size(p1_v); x++) { + item_x = apol_vector_get_element(p1_v, x); + if (component_record->new_diff(diff, POLDIFF_FORM_REMOVED, item_x)) { + error = errno; + goto err; + } + } + for (; y < apol_vector_get_size(p2_v); y++) { + item_y = apol_vector_get_element(p2_v, y); + if (component_record->new_diff(diff, POLDIFF_FORM_ADDED, item_y)) { + error = errno; + goto err; + } + } + + apol_vector_destroy(&p1_v); + apol_vector_destroy(&p2_v); + diff->diff_status |= component_record->flag_bit; + return 0; + err: + apol_vector_destroy(&p1_v); + apol_vector_destroy(&p2_v); + errno = error; + return -1; +} + +int poldiff_run(poldiff_t * diff, uint32_t flags) +{ + size_t i, num_items; + + if (!flags) + return 0; /* nothing to do */ + + if (!diff) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + int policy_opts = diff->policy_opts; + if (flags & (POLDIFF_DIFF_AVRULES | POLDIFF_DIFF_TERULES)) { + policy_opts &= ~(QPOL_POLICY_OPTION_NO_RULES); + } + if (flags & POLDIFF_DIFF_AVNEVERALLOW) { + policy_opts &= ~(QPOL_POLICY_OPTION_NO_NEVERALLOWS); + } + if (policy_opts != diff->policy_opts) { + INFO(diff, "%s", "Loading rules from original policy."); + if (qpol_policy_rebuild(diff->orig_qpol, policy_opts)) { + return -1; + } + INFO(diff, "%s", "Loading rules from modified policy."); + if (qpol_policy_rebuild(diff->mod_qpol, policy_opts)) { + return -1; + } + // force flushing of existing pointers into policies + diff->remapped = 1; + diff->policy_opts = policy_opts; + } + + num_items = sizeof(component_records) / sizeof(poldiff_component_record_t); + if (diff->remapped) { + for (i = 0; i < num_items; i++) { + if (component_records[i].flag_bit & POLDIFF_DIFF_REMAPPED) { + INFO(diff, "Resetting %s diff.", component_records[i].item_name); + if (component_records[i].reset(diff)) + return -1; + } + } + diff->diff_status &= ~(POLDIFF_DIFF_REMAPPED); + diff->remapped = 0; + } + + INFO(diff, "%s", "Building type map."); + if (type_map_build(diff)) { + return -1; + } + + diff->line_numbers_enabled = 0; + for (i = 0; i < num_items; i++) { + /* item requested but not yet run */ + if ((flags & component_records[i].flag_bit) && !(component_records[i].flag_bit & diff->diff_status)) { + INFO(diff, "Running %s diff.", component_records[i].item_name); + if (poldiff_do_item_diff(diff, &(component_records[i]))) { + return -1; + } + } + } + + return 0; +} + +int poldiff_is_run(const poldiff_t * diff, uint32_t flags) +{ + if (!flags) + return 1; /* nothing to do */ + + if (!diff) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + if ((diff->diff_status & flags) == flags) { + return 1; + } + return 0; +} + +int poldiff_get_stats(const poldiff_t * diff, uint32_t flags, size_t stats[5]) +{ + size_t i, j, num_items, tmp_stats[5] = { 0, 0, 0, 0, 0 }; + + if (!diff || !flags) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + stats[0] = stats[1] = stats[2] = stats[3] = stats[4] = 0; + + num_items = sizeof(component_records) / sizeof(poldiff_component_record_t); + for (i = 0; i < num_items; i++) { + if (flags & component_records[i].flag_bit) { + component_records[i].get_stats(diff, tmp_stats); + for (j = 0; j < 5; j++) + stats[j] += tmp_stats[j]; + } + } + + return 0; +} + +int poldiff_enable_line_numbers(poldiff_t * diff) +{ + int retval; + if (diff == NULL) { + errno = EINVAL; + return -1; + } + if (!diff->line_numbers_enabled) { + if (qpol_policy_build_syn_rule_table(diff->orig_qpol)) + return -1; + if (qpol_policy_build_syn_rule_table(diff->mod_qpol)) + return -1; + if ((retval = avrule_enable_line_numbers(diff, AVRULE_OFFSET_ALLOW)) < 0) { + return retval; + } + if ((retval = avrule_enable_line_numbers(diff, AVRULE_OFFSET_AUDITALLOW)) < 0) { + return retval; + } + if ((retval = avrule_enable_line_numbers(diff, AVRULE_OFFSET_DONTAUDIT)) < 0) { + return retval; + } + if ((retval = avrule_enable_line_numbers(diff, AVRULE_OFFSET_NEVERALLOW)) < 0) { + return retval; + } + if ((retval = terule_enable_line_numbers(diff, TERULE_OFFSET_CHANGE)) < 0) { + return retval; + } + if ((retval = terule_enable_line_numbers(diff, TERULE_OFFSET_MEMBER)) < 0) { + return retval; + } + if ((retval = terule_enable_line_numbers(diff, TERULE_OFFSET_TRANS)) < 0) { + return retval; + } + diff->line_numbers_enabled = 1; + } + return 0; +} + +int poldiff_build_bsts(poldiff_t * diff) +{ + apol_vector_t *classes[2] = { NULL, NULL }; + apol_vector_t *perms[2] = { NULL, NULL }; + apol_vector_t *bools[2] = { NULL, NULL }; + size_t i, j; + const qpol_class_t *cls; + qpol_bool_t *qbool; + const char *name; + char *new_name; + int retval = -1, error = 0; + if (diff->class_bst != NULL) { + return 0; + } + if ((diff->class_bst = apol_bst_create(apol_str_strcmp, free)) == NULL || + (diff->perm_bst = apol_bst_create(apol_str_strcmp, free)) == NULL || + (diff->bool_bst = apol_bst_create(apol_str_strcmp, free)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + for (i = 0; i < 2; i++) { + apol_policy_t *p = (i == 0 ? diff->orig_pol : diff->mod_pol); + qpol_policy_t *q = apol_policy_get_qpol(p); + if (apol_class_get_by_query(p, NULL, &classes[i]) < 0 || + apol_perm_get_by_query(p, NULL, &perms[i]) < 0 || apol_bool_get_by_query(p, NULL, &bools[i]) < 0) { + error = errno; + goto cleanup; + } + for (j = 0; j < apol_vector_get_size(classes[i]); j++) { + cls = apol_vector_get_element(classes[i], j); + if (qpol_class_get_name(q, cls, &name) < 0) { + error = errno; + goto cleanup; + } + if ((new_name = strdup(name)) == NULL || + apol_bst_insert_and_get(diff->class_bst, (void **)&new_name, NULL) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + for (j = 0; j < apol_vector_get_size(perms[i]); j++) { + name = (char *)apol_vector_get_element(perms[i], j); + if ((new_name = strdup(name)) == NULL || + apol_bst_insert_and_get(diff->perm_bst, (void **)&new_name, NULL) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + for (j = 0; j < apol_vector_get_size(bools[i]); j++) { + qbool = (qpol_bool_t *) apol_vector_get_element(bools[i], j); + if (qpol_bool_get_name(q, qbool, &name) < 0) { + error = errno; + goto cleanup; + } + if ((new_name = strdup(name)) == NULL || + apol_bst_insert_and_get(diff->bool_bst, (void **)&new_name, NULL) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + } + retval = 0; + cleanup: + apol_vector_destroy(&classes[0]); + apol_vector_destroy(&classes[1]); + apol_vector_destroy(&perms[0]); + apol_vector_destroy(&perms[1]); + apol_vector_destroy(&bools[0]); + apol_vector_destroy(&bools[1]); + errno = error; + return retval; +} + +static void poldiff_handle_default_callback(void *arg __attribute__ ((unused)), + poldiff_t * p __attribute__ ((unused)), int level, const char *fmt, va_list va_args) +{ + switch (level) { + case POLDIFF_MSG_INFO: + { + /* by default do not display these messages */ + return; + } + case POLDIFF_MSG_WARN: + { + fprintf(stderr, "WARNING: "); + break; + } + case POLDIFF_MSG_ERR: + default: + { + fprintf(stderr, "ERROR: "); + break; + } + } + vfprintf(stderr, fmt, va_args); + fprintf(stderr, "\n"); +} + +void poldiff_handle_msg(const poldiff_t * p, int level, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (p == NULL || p->fn == NULL) { + poldiff_handle_default_callback(NULL, NULL, level, fmt, ap); + } else { + p->fn(p->handle_arg, p, level, fmt, ap); + } + va_end(ap); +} + +poldiff_item_get_form_fn_t poldiff_component_record_get_form_fn(const poldiff_component_record_t * diff) +{ + if (!diff) { + errno = EINVAL; + return NULL; + } + return diff->get_form; +} + +poldiff_item_to_string_fn_t poldiff_component_record_get_to_string_fn(const poldiff_component_record_t * diff) +{ + if (!diff) { + errno = EINVAL; + return NULL; + } + return diff->to_string; +} + +poldiff_get_item_stats_fn_t poldiff_component_record_get_stats_fn(const poldiff_component_record_t * diff) +{ + if (!diff) { + errno = EINVAL; + return NULL; + } + return diff->get_stats; +} + +poldiff_get_result_items_fn_t poldiff_component_record_get_results_fn(const poldiff_component_record_t * diff) +{ + if (!diff) { + errno = EINVAL; + return NULL; + } + return diff->get_results; +} + +const char *poldiff_component_record_get_label(const poldiff_component_record_t * diff) +{ + if (!diff) { + errno = EINVAL; + return NULL; + } + return diff->item_name; +} diff --git a/libpoldiff/src/poldiff_internal.h b/libpoldiff/src/poldiff_internal.h new file mode 100644 index 0000000..e84cdb7 --- /dev/null +++ b/libpoldiff/src/poldiff_internal.h @@ -0,0 +1,239 @@ +/** + * @file + * Protected interface for computing semantic policy difference. + * + * @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 POLDIFF_POLDIFF_INTERNAL_H +#define POLDIFF_POLDIFF_INTERNAL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <poldiff/poldiff.h> +#include <apol/bst.h> + + typedef enum + { + AVRULE_OFFSET_ALLOW = 0, AVRULE_OFFSET_AUDITALLOW, + AVRULE_OFFSET_DONTAUDIT, AVRULE_OFFSET_NEVERALLOW, + AVRULE_OFFSET_MAX + } avrule_offset_e; + + typedef enum + { + TERULE_OFFSET_CHANGE = 0, TERULE_OFFSET_MEMBER, + TERULE_OFFSET_TRANS, + TERULE_OFFSET_MAX + } terule_offset_e; + +#include "attrib_internal.h" +#include "avrule_internal.h" +#include "bool_internal.h" +#include "cat_internal.h" +#include "class_internal.h" +#include "level_internal.h" +#include "range_internal.h" +#include "range_trans_internal.h" +#include "rbac_internal.h" +#include "role_internal.h" +#include "terule_internal.h" +#include "user_internal.h" +#include "type_internal.h" + +#include "type_map_internal.h" + +/* forward declarations */ + struct poldiff_attrib_summary; + struct poldiff_avrule_summary; + struct poldiff_bool_summary; + struct poldiff_cat_summary; + struct poldiff_class_summary; + struct poldiff_common_summary; + struct poldiff_level_summary; + struct poldiff_range_trans_summary; + struct poldiff_role_summary; + struct poldiff_role_allow_summary; + struct poldiff_role_trans_summary; + struct poldiff_terule_summary; + struct poldiff_type_summary; + struct poldiff_user_summary; +/* and so forth for ocon_summary structs */ + + struct poldiff + { + /** the "original" policy */ + apol_policy_t *orig_pol; + /** the "modified" policy */ + apol_policy_t *mod_pol; + /** pointer to original's qpol policy within orig_pol */ + qpol_policy_t *orig_qpol; + /** pointer to modified's qpol policy within mod_pol */ + qpol_policy_t *mod_qpol; + /** non-zero if rules' line numbers are accurate */ + int line_numbers_enabled; + /** BST of duplicated strings, used when making pseudo-rules */ + apol_bst_t *class_bst; + /** BST of duplicated strings, used when making pseudo-rules */ + apol_bst_t *perm_bst; + /** BST of duplicated strings, used when making pseudo-rules */ + apol_bst_t *bool_bst; + poldiff_handle_fn_t fn; + void *handle_arg; + /** set of POLDIF_DIFF_* bits for diffs run */ + uint32_t diff_status; + struct poldiff_attrib_summary *attrib_diffs; + struct poldiff_avrule_summary *avrule_diffs[AVRULE_OFFSET_MAX]; + struct poldiff_bool_summary *bool_diffs; + struct poldiff_cat_summary *cat_diffs; + struct poldiff_class_summary *class_diffs; + struct poldiff_common_summary *common_diffs; + struct poldiff_level_summary *level_diffs; + struct poldiff_range_trans_summary *range_trans_diffs; + struct poldiff_role_summary *role_diffs; + struct poldiff_role_allow_summary *role_allow_diffs; + struct poldiff_role_trans_summary *role_trans_diffs; + struct poldiff_terule_summary *terule_diffs[TERULE_OFFSET_MAX]; + struct poldiff_type_summary *type_diffs; + struct poldiff_user_summary *user_diffs; + /* and so forth if we want ocon_diffs */ + type_map_t *type_map; + /** most recently used flags to open the two policies */ + int policy_opts; + /** set if type mapping was changed since last run */ + int remapped; + }; + +/** + * Callback function signature for getting a vector of all unique + * items of a given kind in a policy. The vector must be sorted + * prior to returning from this function. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * @return a newly allocated vector of all unique items of the + * appropriate kind on success, or NULL on error; if the call fails, + * errno will be set. + */ + typedef apol_vector_t *(*poldiff_get_items_fn_t) (poldiff_t * diff, const apol_policy_t * policy); + +/** + * Callback function signature for quickly comparing two items to + * determine if they are semantically the same item. This operation + * should quickly determine if the two are obviously different or + * not. + * + * @param x The item from the original policy. + * @param y The item from the modified policy. + * @param diff The policy difference structure associated with both + * items. + * + * @return Expected return value from this function is < 0, 0, or > 0 + * if item x is respectively less than, equal to, or greater than item y. + * This must be able to return a defined stable ordering for all items + * not semantically equivalent. + */ + typedef int (*poldiff_item_comp_fn_t) (const void *x, const void *y, const poldiff_t * diff); + +/** + * Callback function signature for creating, initializing and inserting + * a new semantic difference entry for an item. + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference, one of POLDIFF_FORM_ADDED or + * POLDIFF_FORM_REMOVED. + * @param item Item for which the entry is being created. + * @return Expected return value from this function is 0 on success and + * < 0 on error; if the call fails, it is expected to set errno and to + * leave the policy difference structure unchanged. + */ + typedef int (*poldiff_new_diff_fn_t) (poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Callback function signature for computing the semantic difference of + * two items for which the compare callback returns 0. This function should + * calculate the difference of any properties of the items and if a difference + * is found to allocate, initialize, and insert an new semantic difference + * entry for that item. + * @param diff The policy difference structure associated with both items and + * to which to add an entry if needed. + * @param x The item from the original policy. + * @param y The item from the modified policy. + * @return Expected return value from this function is 0 on success and + * < 0 on error; if the call fails, it is expected to set errno and to + * leave the policy difference structure unchanged. + */ + typedef int (*poldiff_deep_diff_fn_t) (poldiff_t * diff, const void *x, const void *y); + +/** + * Callback function signature for resetting the diff results for an + * item. called when mapping of the symbols used by the diff change. + * @param diff The policy difference structure containing the diffs + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * it is expected to set errno. + */ + typedef int (*poldiff_reset_fn_t) (poldiff_t * diff); + +/******************** error handling code below ********************/ + +#define POLDIFF_MSG_ERR 1 +#define POLDIFF_MSG_WARN 2 +#define POLDIFF_MSG_INFO 3 + +/** + * Write a message to the callback stored within a poldiff error + * handler. If the msg_callback field is empty then suppress the + * message. + * + * @param p Error reporting handler. If NULL then write message to + * stderr. + * @param level Severity of message, one of POLDIFF_MSG_ERR, + * POLDIFF_MSG_WARN, or POLDIFF_MSG_INFO. + * @param fmt Format string to print, using syntax of printf(3). + */ + __attribute__ ((format(printf, 3, 4))) extern void poldiff_handle_msg(const poldiff_t * p, int level, const char *fmt, ...); + +#undef ERR +#undef WARN +#undef INFO + +#define ERR(handle, format, ...) poldiff_handle_msg(handle, POLDIFF_MSG_ERR, format, __VA_ARGS__) +#define WARN(handle, format, ...) poldiff_handle_msg(handle, POLDIFF_MSG_WARN, format, __VA_ARGS__) +#define INFO(handle, format, ...) poldiff_handle_msg(handle, POLDIFF_MSG_INFO, format, __VA_ARGS__) + +/** + * Build the BST for classes, permissions, and booleans if the + * policies have changed. This effectively provides a partial mapping + * of rules from one policy to the other. + * + * @param diff Policy difference structure containing policies to diff. + * + * @return 0 on success, < 0 on error. + */ + int poldiff_build_bsts(poldiff_t * diff); + +#ifdef __cplusplus +} +#endif + +#endif /* POLDIFF_POLDIFF_INTERNAL_H */ diff --git a/libpoldiff/src/range_diff.c b/libpoldiff/src/range_diff.c new file mode 100644 index 0000000..e6c7c3a --- /dev/null +++ b/libpoldiff/src/range_diff.c @@ -0,0 +1,420 @@ +/** + * @file + * Implementation for computing semantic differences in ranges. + * + * @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 + */ + +#include <config.h> + +#include "poldiff_internal.h" + +#include <apol/util.h> +#include <assert.h> +#include <errno.h> + +struct poldiff_range +{ + apol_mls_range_t *orig_range; + apol_mls_range_t *mod_range; + /** a vector of poldiff_level_t */ + apol_vector_t *levels; + apol_vector_t *min_added_cats; + apol_vector_t *min_removed_cats; + apol_vector_t *min_unmodified_cats; +}; + +apol_vector_t *poldiff_range_get_levels(const poldiff_range_t * range) +{ + if (range == NULL) { + errno = EINVAL; + return NULL; + } + return range->levels; +} + +const apol_mls_range_t *poldiff_range_get_original_range(const poldiff_range_t * range) +{ + if (range == NULL) { + errno = EINVAL; + return NULL; + } + return range->orig_range; +} + +const apol_mls_range_t *poldiff_range_get_modified_range(const poldiff_range_t * range) +{ + if (range == NULL) { + errno = EINVAL; + return NULL; + } + return range->mod_range; +} + +apol_vector_t *poldiff_range_get_min_added_cats(const poldiff_range_t * range) +{ + if (range == NULL) { + errno = EINVAL; + return NULL; + } + return range->min_added_cats; +} + +apol_vector_t *poldiff_range_get_min_removed_cats(const poldiff_range_t * range) +{ + if (range == NULL) { + errno = EINVAL; + return NULL; + } + return range->min_removed_cats; +} + +apol_vector_t *poldiff_range_get_min_unmodified_cats(const poldiff_range_t * range) +{ + if (range == NULL) { + errno = EINVAL; + return NULL; + } + return range->min_unmodified_cats; +} + +char *poldiff_range_to_string_brief(const poldiff_t * diff, const poldiff_range_t * range) +{ + char *r1 = NULL, *r2 = NULL; + char *s = NULL, *t = NULL, *sep = "", *cat; + size_t len = 0, i; + if (range->orig_range != NULL && (r1 = apol_mls_range_render(diff->orig_pol, range->orig_range)) == NULL) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + if (range->mod_range != NULL && (r2 = apol_mls_range_render(diff->mod_pol, range->mod_range)) == NULL) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + assert(r1 != NULL || r2 != NULL); + if (r1 == NULL) { + if (apol_str_appendf(&s, &len, " range: %s\n", r2) < 0) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + } else if (r2 == NULL) { + if (apol_str_appendf(&s, &len, " range: %s\n", r1) < 0) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + } else { + if (apol_str_appendf(&s, &len, " range: %s --> %s\n", r1, r2) < 0) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + } + if ((range->min_added_cats != NULL && apol_vector_get_size(range->min_added_cats) > 0) || + (range->min_removed_cats != NULL && apol_vector_get_size(range->min_removed_cats) > 0) || + (range->min_unmodified_cats != NULL && apol_vector_get_size(range->min_unmodified_cats) > 0)) { + if (apol_str_append(&s, &len, " minimum categories: ") < 0) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + for (i = 0; range->min_unmodified_cats != NULL && i < apol_vector_get_size(range->min_unmodified_cats); i++) { + cat = apol_vector_get_element(range->min_unmodified_cats, i); + if (apol_str_appendf(&s, &len, "%s%s", sep, cat) < 0) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + sep = ","; + } + for (i = 0; range->min_added_cats != NULL && i < apol_vector_get_size(range->min_added_cats); i++) { + cat = apol_vector_get_element(range->min_added_cats, i); + if (apol_str_appendf(&s, &len, "%s+%s", sep, cat) < 0) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + sep = ","; + } + for (i = 0; range->min_removed_cats != NULL && i < apol_vector_get_size(range->min_removed_cats); i++) { + cat = apol_vector_get_element(range->min_removed_cats, i); + if (apol_str_appendf(&s, &len, "%s-%s", sep, cat) < 0) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + sep = ","; + } + if (apol_str_append(&s, &len, "\n") < 0) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + } + for (i = 0; i < apol_vector_get_size(range->levels); i++) { + poldiff_level_t *level = apol_vector_get_element(range->levels, i); + if ((t = poldiff_level_to_string_brief(diff, level)) == NULL) { + goto cleanup; + } + if (apol_str_appendf(&s, &len, " %s", t) < 0) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + free(t); + t = NULL; + } + cleanup: + free(r1); + free(r2); + free(t); + return s; +} + +poldiff_range_t *range_create(const poldiff_t * diff, const qpol_mls_range_t * orig_range, const qpol_mls_range_t * mod_range, + poldiff_form_e form) +{ + poldiff_range_t *pr = NULL; + apol_policy_t *p; + apol_mls_range_t *range; + apol_vector_t *levels = NULL; + poldiff_level_t *pl = NULL; + size_t i; + int retval = -1; + if ((pr = calloc(1, sizeof(*pr))) == NULL || (pr->levels = apol_vector_create(level_free)) == NULL) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + if (orig_range != NULL && (pr->orig_range = apol_mls_range_create_from_qpol_mls_range(diff->orig_pol, orig_range)) == NULL) { + goto cleanup; + } + if (mod_range != NULL && (pr->mod_range = apol_mls_range_create_from_qpol_mls_range(diff->mod_pol, mod_range)) == NULL) { + goto cleanup; + } + if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) { + p = diff->mod_pol; + range = pr->mod_range; + } else if (form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_REMOVE_TYPE) { + p = diff->orig_pol; + range = pr->orig_range; + } else if (form == POLDIFF_FORM_MODIFIED) { + /* don't fill in the range's levels here */ + return pr; + } else { + /* should never get here */ + assert(0); + return pr; + } + if ((levels = apol_mls_range_get_levels(p, range)) == NULL) { + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(levels); i++) { + apol_mls_level_t *l = apol_vector_get_element(levels, i); + const char *sens = apol_mls_level_get_sens(l); + const apol_vector_t *cats = apol_mls_level_get_cats(l); + if ((pl = calloc(1, sizeof(*pl))) == NULL || + (pl->name = strdup(sens)) == NULL || (pl->unmodified_cats = apol_vector_create_with_capacity(1, free)) == NULL) + { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + if (form == POLDIFF_FORM_ADDED) { + if ((pl->added_cats = apol_vector_create_from_vector(cats, apol_str_strdup, NULL, free)) == NULL || + (pl->removed_cats = apol_vector_create_with_capacity(1, free)) == NULL) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + } else if (form == POLDIFF_FORM_REMOVED) { + if ((pl->added_cats = apol_vector_create_with_capacity(1, free)) == NULL || + (pl->removed_cats = apol_vector_create_from_vector(cats, apol_str_strdup, NULL, free)) == NULL) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + } + if (apol_vector_append(pr->levels, pl) < 0) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + pl = NULL; + } + retval = 0; + cleanup: + apol_vector_destroy(&levels); + if (retval != 0) { + level_free(pl); + range_destroy(&pr); + return NULL; + } + return pr; +} + +void range_destroy(poldiff_range_t ** range) +{ + if (range != NULL && *range != NULL) { + apol_mls_range_destroy(&(*range)->orig_range); + apol_mls_range_destroy(&(*range)->mod_range); + apol_vector_destroy(&(*range)->levels); + apol_vector_destroy(&(*range)->min_added_cats); + apol_vector_destroy(&(*range)->min_removed_cats); + apol_vector_destroy(&(*range)->min_unmodified_cats); + free(*range); + *range = NULL; + } +} + +/** + * Comparison function for two apol_mls_level_t objects from the same + * apol_mls_range_t. Sorts the levels in alphabetical order according + * to sensitivity. + */ +static int range_comp_alphabetize(const void *a, const void *b, void *data __attribute__ ((unused))) +{ + const apol_mls_level_t *l1 = a; + const apol_mls_level_t *l2 = b; + const char *sens1 = apol_mls_level_get_sens(l1); + const char *sens2 = apol_mls_level_get_sens(l2); + return strcmp(sens1, sens2); +} + +/** + * Comparison function for two levels from the same poldiff_range_t. + * Sorts the levels by form; within each form sort them by policy + * order. + */ +static int range_comp(const void *a, const void *b, void *data) +{ + const poldiff_level_t *l1 = a; + const poldiff_level_t *l2 = b; + poldiff_t *diff = data; + qpol_policy_t *q; + const qpol_level_t *ql1, *ql2; + uint32_t v1, v2; + if (l1->form != l2->form) { + return l1->form - l2->form; + } + if (l1->form == POLDIFF_FORM_ADDED) { + q = diff->mod_qpol; + } else { + q = diff->orig_qpol; + } + qpol_policy_get_level_by_name(q, l1->name, &ql1); + qpol_policy_get_level_by_name(q, l2->name, &ql2); + qpol_level_get_value(q, ql1, &v1); + qpol_level_get_value(q, ql2, &v2); + assert(v1 != 0 && v2 != 0); + return v1 - v2; +} + +int range_deep_diff(poldiff_t * diff, poldiff_range_t * range) +{ + apol_vector_t *orig_levels = NULL, *mod_levels = NULL; + apol_vector_t *added = NULL, *removed = NULL, *unmodified = NULL; + apol_mls_level_t *l1, *l2; + poldiff_level_t *pl1, *pl2; + size_t i, j; + int retval = -1, differences_found = 0, compval; + if ((orig_levels = apol_mls_range_get_levels(diff->orig_pol, range->orig_range)) == NULL || + (mod_levels = apol_mls_range_get_levels(diff->mod_pol, range->mod_range)) == NULL) { + goto cleanup; + } + apol_vector_sort(orig_levels, range_comp_alphabetize, NULL); + apol_vector_sort(mod_levels, range_comp_alphabetize, NULL); + for (i = j = 0; i < apol_vector_get_size(orig_levels);) { + if (j >= apol_vector_get_size(mod_levels)) + break; + l1 = (apol_mls_level_t *) apol_vector_get_element(orig_levels, i); + l2 = (apol_mls_level_t *) apol_vector_get_element(mod_levels, j); + pl1 = pl2 = NULL; + const char *sens1 = apol_mls_level_get_sens(l1); + const char *sens2 = apol_mls_level_get_sens(l2); + compval = strcmp(sens1, sens2); + if (compval < 0) { + if ((pl1 = level_create_from_apol_mls_level(l1, POLDIFF_FORM_REMOVED)) == NULL + || apol_vector_append(range->levels, pl1) < 0) { + level_free(pl1); + goto cleanup; + } + differences_found = 1; + i++; + } else if (compval > 0) { + if ((pl2 = level_create_from_apol_mls_level(l2, POLDIFF_FORM_ADDED)) == NULL + || apol_vector_append(range->levels, pl2) < 0) { + level_free(pl2); + goto cleanup; + } + differences_found = 1; + j++; + } else { + if (level_deep_diff_apol_mls_levels(diff, l1, l2, &pl1, &pl2) < 0) { + goto cleanup; + } + assert(pl2 == NULL); + if (pl1 != NULL) { + if (apol_vector_append(range->levels, pl1) < 0) { + level_free(pl1); + goto cleanup; + } + differences_found = 1; + } + i++; + j++; + } + } + for (; i < apol_vector_get_size(orig_levels); i++) { + l1 = (apol_mls_level_t *) apol_vector_get_element(orig_levels, i); + if ((pl1 = level_create_from_apol_mls_level(l1, POLDIFF_FORM_REMOVED)) == NULL + || apol_vector_append(range->levels, pl1) < 0) { + level_free(pl1); + goto cleanup; + } + differences_found = 1; + } + for (; j < apol_vector_get_size(mod_levels); j++) { + l2 = (apol_mls_level_t *) apol_vector_get_element(mod_levels, j); + if ((pl2 = level_create_from_apol_mls_level(l2, POLDIFF_FORM_ADDED)) == NULL + || apol_vector_append(range->levels, pl2) < 0) { + level_free(pl2); + goto cleanup; + } + differences_found = 1; + } + /* now check minimum category sets */ + const apol_mls_level_t *low1 = apol_mls_range_get_low(range->orig_range); + const apol_vector_t *cats1 = apol_mls_level_get_cats(low1); + const apol_mls_level_t *low2 = apol_mls_range_get_low(range->mod_range); + const apol_vector_t *cats2 = apol_mls_level_get_cats(low2); + compval = level_deep_diff_cats(diff, cats1, cats2, &added, &removed, &unmodified); + if (compval < 0) { + goto cleanup; + } else if (compval > 0) { + differences_found = 1; + range->min_added_cats = added; + range->min_removed_cats = removed; + range->min_unmodified_cats = unmodified; + added = NULL; + removed = NULL; + unmodified = NULL; + } + if (differences_found) { + apol_vector_sort(range->levels, range_comp, diff); + retval = 1; + } else { + retval = 0; + } + cleanup: + apol_vector_destroy(&orig_levels); + apol_vector_destroy(&mod_levels); + apol_vector_destroy(&added); + apol_vector_destroy(&removed); + apol_vector_destroy(&unmodified); + return retval; +} diff --git a/libpoldiff/src/range_internal.h b/libpoldiff/src/range_internal.h new file mode 100644 index 0000000..bf4d296 --- /dev/null +++ b/libpoldiff/src/range_internal.h @@ -0,0 +1,80 @@ +/** + * @file + * Protected interface for range differences. + * + * @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 POLDIFF_RANGE_INTERNAL_H +#define POLDIFF_RANGE_INTERNAL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * Allocate and return a poldiff_range_t object. This will fill in + * the orig_range and mod_range strings. If the form is modified, + * then this will allocate the levels vector but leave it empty. + * Otherwise the levels vector will be filled with the levels that + * were added/removed. + * + * @param diff Diff object containing policies. + * @param orig_range Range from original policy, or NULL if there is + * no original range. + * @param mod_range Range from modified policy, or NULL if there is no + * modified range. + * @param form Form of the range. + * + * @return An initialized range, or NULL upon error. Caller must call + * range_destroy() upon the returned value. + */ + poldiff_range_t *range_create(const poldiff_t * diff, const qpol_mls_range_t * orig_range, + const qpol_mls_range_t * mod_range, poldiff_form_e form); + +/** + * Deallocate all space for a range, including the pointer itself. + * Afterwards set the pointer to NULL. + * + * @param range Reference to a range to destroy. + */ + void range_destroy(poldiff_range_t ** range); + +/** + * Calculate the differences between two ranges (that are stored + * within the poldiff_range_t object). This involves two things: + * changes in the expanded levels, and changes to minimum category + * sets. If differences are found then the range's levels vector will + * be filled with those differences. + * + * @param diff Diff object containing policies. + * @param range Range object to diff. + * + * @return Greater than zero if a diff was found, zero if none found, + * less than zero for errors. + */ + int range_deep_diff(poldiff_t * diff, poldiff_range_t * range); + +#ifdef __cplusplus +} +#endif + +#endif /* POLDIFF_RANGE_INTERNAL_H */ diff --git a/libpoldiff/src/range_trans_diff.c b/libpoldiff/src/range_trans_diff.c new file mode 100644 index 0000000..7394c89 --- /dev/null +++ b/libpoldiff/src/range_trans_diff.c @@ -0,0 +1,520 @@ +/** + * @file + * Implementation for computing a semantic differences in range + * transition rules. + * + * @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 + */ + +#include <config.h> + +#include "poldiff_internal.h" + +#include <apol/mls-query.h> +#include <apol/util.h> +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +struct poldiff_range_trans_summary +{ + size_t num_added; + size_t num_removed; + size_t num_modified; + size_t num_added_type; + size_t num_removed_type; + apol_vector_t *diffs; +}; + +struct poldiff_range_trans +{ + char *source; + char *target; + char *target_class; + poldiff_form_e form; + poldiff_range_t *range; +}; + +void poldiff_range_trans_get_stats(const poldiff_t * diff, size_t stats[5]) +{ + if (diff == NULL || stats == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return; + } + stats[0] = diff->range_trans_diffs->num_added; + stats[1] = diff->range_trans_diffs->num_removed; + stats[2] = diff->range_trans_diffs->num_modified; + stats[3] = diff->range_trans_diffs->num_added_type; + stats[4] = diff->range_trans_diffs->num_removed_type; +} + +char *poldiff_range_trans_to_string(const poldiff_t * diff, const void *range_trans) +{ + const poldiff_range_trans_t *rt = range_trans; + const poldiff_range_t *range = poldiff_range_trans_get_range(rt); + const apol_mls_range_t *orig_range = poldiff_range_get_original_range(range); + const apol_mls_range_t *mod_range = poldiff_range_get_modified_range(range); + size_t len = 0; + char *s = NULL; + if (diff == NULL || range_trans == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + switch (rt->form) { + case POLDIFF_FORM_ADDED: + case POLDIFF_FORM_ADD_TYPE: + { + char *t = NULL; + if ((t = apol_mls_range_render(diff->mod_pol, mod_range)) == NULL || + apol_str_appendf(&s, &len, "+ range_transition %s %s : %s %s;", rt->source, rt->target, + rt->target_class, t) < 0) { + free(t); + goto cleanup; + } + free(t); + return s; + } + case POLDIFF_FORM_REMOVED: + case POLDIFF_FORM_REMOVE_TYPE: + { + char *t = NULL; + if ((t = apol_mls_range_render(diff->orig_pol, orig_range)) == NULL || + apol_str_appendf(&s, &len, "- range_transition %s %s : %s %s;", rt->source, rt->target, + rt->target_class, t) < 0) { + free(t); + goto cleanup; + } + free(t); + return s; + } + case POLDIFF_FORM_MODIFIED: + { + char *t; + if ((t = poldiff_range_to_string_brief(diff, range)) == NULL || + apol_str_appendf(&s, &len, "* range_transition %s %s : %s\n%s", rt->source, rt->target, + rt->target_class, t) < 0) { + free(t); + goto cleanup; + } + free(t); + return s; + } + default: + { + ERR(diff, "%s", strerror(ENOTSUP)); + errno = ENOTSUP; + return NULL; + } + } + cleanup: + /* if this is reached then an error occurred */ + ERR(diff, "%s", strerror(ENOMEM)); + free(s); + errno = ENOMEM; + return NULL; +} + +const apol_vector_t *poldiff_get_range_trans_vector(const poldiff_t * diff) +{ + if (diff == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + return diff->range_trans_diffs->diffs; +} + +const char *poldiff_range_trans_get_source_type(const poldiff_range_trans_t * range_trans) +{ + if (range_trans == NULL) { + errno = EINVAL; + return NULL; + } + return range_trans->source; +} + +const char *poldiff_range_trans_get_target_type(const poldiff_range_trans_t * range_trans) +{ + if (range_trans == NULL) { + errno = EINVAL; + return NULL; + } + return range_trans->target; +} + +const char *poldiff_range_trans_get_target_class(const poldiff_range_trans_t * range_trans) +{ + if (range_trans == NULL) { + errno = EINVAL; + return NULL; + } + return range_trans->target_class; +} + +const poldiff_range_t *poldiff_range_trans_get_range(const poldiff_range_trans_t * range_trans) +{ + if (range_trans == NULL) { + errno = EINVAL; + return NULL; + } + return range_trans->range; +} + +poldiff_form_e poldiff_range_trans_get_form(const void *range_trans) +{ + if (range_trans == NULL) { + errno = EINVAL; + return POLDIFF_FORM_NONE; + } + return ((const poldiff_range_trans_t *)range_trans)->form; +} + +/** + * Destroy all space used by a poldiff_range_trans_t, including the + * pointer itself. + */ +static void range_trans_free(void *elem) +{ + if (elem != NULL) { + poldiff_range_trans_t *rt = (poldiff_range_trans_t *) elem; + free(rt->source); + free(rt->target); + free(rt->target_class); + range_destroy(&rt->range); + free(rt); + } +} + +poldiff_range_trans_summary_t *range_trans_create(void) +{ + poldiff_range_trans_summary_t *rts = calloc(1, sizeof(*rts)); + if (rts == NULL) { + return NULL; + } + if ((rts->diffs = apol_vector_create(range_trans_free)) == NULL) { + range_trans_destroy(&rts); + return NULL; + } + return rts; +} + +void range_trans_destroy(poldiff_range_trans_summary_t ** rts) +{ + if (rts != NULL && *rts != NULL) { + apol_vector_destroy(&(*rts)->diffs); + free(*rts); + *rts = NULL; + } +} + +typedef struct pseudo_range_trans +{ + uint32_t source_type, target_type; + /* pointer into a policy's class's symbol table */ + const char *target_class; + const qpol_mls_range_t *range; +} pseudo_range_trans_t; + +static void range_trans_free_item(void *item) +{ + if (item != NULL) { + pseudo_range_trans_t *prt = item; + free(prt); + } +} + +int range_trans_comp(const void *x, const void *y, const poldiff_t * diff __attribute__ ((unused))) +{ + const pseudo_range_trans_t *p1 = x; + const pseudo_range_trans_t *p2 = y; + + if (p1->source_type != p2->source_type) { + return p1->source_type - p2->source_type; + } + if (p1->target_type != p2->target_type) { + return p1->target_type - p2->target_type; + } + return strcmp(p1->target_class, p2->target_class); +} + +int range_trans_reset(poldiff_t * diff) +{ + int error = 0; + + if (diff == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + range_trans_destroy(&diff->range_trans_diffs); + diff->range_trans_diffs = range_trans_create(); + if (diff->range_trans_diffs == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + errno = error; + return -1; + } + + return 0; +} + +/** + * Allocate and return a new range trans difference object. If the + * pseudo-range trans's source and/or target expands to multiple read + * types, then just choose the first one for display. + */ +static poldiff_range_trans_t *make_range_trans_diff(const poldiff_t * diff, poldiff_form_e form, const pseudo_range_trans_t * prt) +{ + poldiff_range_trans_t *rt = NULL; + const char *n1, *n2; + int error; + if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) { + n1 = type_map_get_name(diff, prt->source_type, POLDIFF_POLICY_MOD); + n2 = type_map_get_name(diff, prt->target_type, POLDIFF_POLICY_MOD); + } else { + n1 = type_map_get_name(diff, prt->source_type, POLDIFF_POLICY_ORIG); + n2 = type_map_get_name(diff, prt->target_type, POLDIFF_POLICY_ORIG); + } + assert(n1 != NULL && n2 != NULL); + if ((rt = calloc(1, sizeof(*rt))) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + errno = error; + return NULL; + } + if ((rt->source = strdup(n1)) == NULL || + (rt->target = strdup(n2)) == NULL || (rt->target_class = strdup(prt->target_class)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(errno)); + range_trans_free(rt); + errno = error; + return NULL; + } + rt->form = form; + return rt; +} + +int range_trans_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + const pseudo_range_trans_t *prt = (const pseudo_range_trans_t *)item; + const apol_vector_t *v1, *v2; + const qpol_mls_range_t *orig_range = NULL, *mod_range = NULL; + poldiff_range_trans_t *rt = NULL; + int error; + + /* check if form should really become ADD_TYPE / REMOVE_TYPE, + * by seeing if the /other/ policy's reverse lookup is + * empty */ + if (form == POLDIFF_FORM_ADDED) { + if ((v1 = type_map_lookup_reverse(diff, prt->source_type, POLDIFF_POLICY_ORIG)) == NULL || + (v2 = type_map_lookup_reverse(diff, prt->target_type, POLDIFF_POLICY_ORIG)) == NULL) { + error = errno; + goto cleanup; + } + if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) { + form = POLDIFF_FORM_ADD_TYPE; + } + mod_range = prt->range; + } else { + if ((v1 = type_map_lookup_reverse(diff, prt->source_type, POLDIFF_POLICY_MOD)) == NULL || + (v2 = type_map_lookup_reverse(diff, prt->target_type, POLDIFF_POLICY_MOD)) == NULL) { + error = errno; + goto cleanup; + } + if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) { + form = POLDIFF_FORM_REMOVE_TYPE; + } + orig_range = prt->range; + } + if ((rt = make_range_trans_diff(diff, form, prt)) == NULL || + (rt->range = range_create(diff, orig_range, mod_range, form)) == NULL) { + error = errno; + goto cleanup; + } + if (apol_vector_append(diff->range_trans_diffs->diffs, rt) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + /* increment appropriate counter */ + switch (form) { + case POLDIFF_FORM_ADDED: + { + diff->range_trans_diffs->num_added++; + break; + } + case POLDIFF_FORM_ADD_TYPE: + { + diff->range_trans_diffs->num_added_type++; + break; + } + case POLDIFF_FORM_REMOVED: + { + diff->range_trans_diffs->num_removed++; + break; + } + case POLDIFF_FORM_REMOVE_TYPE: + { + diff->range_trans_diffs->num_removed_type++; + break; + } + default: + { + /* not reachable */ + assert(0); + } + } + return 0; + cleanup: + range_trans_free(rt); + errno = error; + return -1; +} + +/** + * Compare two pseudo range transition rules from the same policy. + * Compares the pseudo source type, pseudo target type, and target + * class. + * + * @param x A pseudo_range_trans_t entry. + * @param y A pseudo_range_trans_t entry. + * @param arg The policy difference structure. + * + * @return < 0, 0, or > 0 if the first rule is respectively less than, + * equal to, or greater than the second. If the return value would be 0 + * but the default role is different a warning is issued. + */ +static int pseudo_range_trans_comp(const void *x, const void *y, void *arg) +{ + const pseudo_range_trans_t *a = x; + const pseudo_range_trans_t *b = y; + poldiff_t *diff = arg; + int retval = range_trans_comp(a, b, diff); + return retval; +} + +apol_vector_t *range_trans_get_items(poldiff_t * diff, const apol_policy_t * policy) +{ + apol_vector_t *v = NULL; + qpol_iterator_t *iter = NULL; + const qpol_range_trans_t *qrt = NULL; + const qpol_type_t *source_type, *target_type; + const qpol_class_t *target_class; + const char *class_name; + const qpol_mls_range_t *range; + pseudo_range_trans_t *prt = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policy); + int error = 0, which_pol; + + which_pol = (policy == diff->orig_pol ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD); + if (qpol_policy_get_range_trans_iter(q, &iter)) { + error = errno; + goto err; + } + if ((v = apol_vector_create(range_trans_free_item)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&qrt) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + if (qpol_range_trans_get_source_type(q, qrt, &source_type) < 0 || + qpol_range_trans_get_target_type(q, qrt, &target_type) < 0 || + qpol_range_trans_get_target_class(q, qrt, &target_class) < 0 || + qpol_class_get_name(q, target_class, &class_name) < 0 || qpol_range_trans_get_range(q, qrt, &range) < 0) { + error = errno; + goto err; + } + if (!(prt = calloc(1, sizeof(*prt)))) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + prt->source_type = type_map_lookup(diff, source_type, which_pol); + prt->target_type = type_map_lookup(diff, target_type, which_pol); + prt->target_class = class_name; + prt->range = range; + if (apol_vector_append(v, prt)) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + prt = NULL; + } + qpol_iterator_destroy(&iter); + apol_vector_sort_uniquify(v, pseudo_range_trans_comp, diff); + return v; + + err: + qpol_iterator_destroy(&iter); + apol_vector_destroy(&v); + free(prt); + errno = error; + return NULL; +} + +int range_trans_deep_diff(poldiff_t * diff, const void *x, const void *y) +{ + const pseudo_range_trans_t *prt1 = x; + const pseudo_range_trans_t *prt2 = y; + poldiff_range_t *range = NULL; + poldiff_range_trans_t *rt = NULL; + int error = 0, retval = -1; + + if ((range = range_create(diff, prt1->range, prt2->range, POLDIFF_FORM_MODIFIED)) == NULL) { + error = errno; + goto cleanup; + } + if ((retval = range_deep_diff(diff, range)) < 0) { + error = errno; + goto cleanup; + } + if (retval > 0) { + if ((rt = make_range_trans_diff(diff, POLDIFF_FORM_MODIFIED, prt1)) == NULL) { + error = errno; + goto cleanup; + } + rt->range = range; + range = NULL; + if (apol_vector_append(diff->range_trans_diffs->diffs, rt) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + diff->range_trans_diffs->num_modified++; + rt = NULL; + } + retval = 0; + cleanup: + range_destroy(&range); + range_trans_free(rt); + if (retval != 0) { + errno = error; + } + return retval; +} diff --git a/libpoldiff/src/range_trans_internal.h b/libpoldiff/src/range_trans_internal.h new file mode 100644 index 0000000..e1bfe11 --- /dev/null +++ b/libpoldiff/src/range_trans_internal.h @@ -0,0 +1,124 @@ +/** + * @file + * Protected interface for range transition rule differences. + * + * @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 POLDIFF_RANGE_TRANS_INTERNAL_H +#define POLDIFF_RANGE_TRANS_INTERNAL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct poldiff_range_trans_summary poldiff_range_trans_summary_t; + +/** + * Allocate and return a new poldiff_range_trans_summary_t object. + * + * @return A new range transition summary. The caller must call + * range_trans_destroy() afterwards. On error, return NULL and set + * errno. + */ + poldiff_range_trans_summary_t *range_trans_create(void); + +/** + * Deallocate all space associated with a + * poldiff_range_trans_summary_t object, including the pointer itself. + * If the pointer is already NULL then do nothing. + * + * @param rts Reference to a range transition summary to destroy. The + * pointer will be set to NULL afterwards. + */ + void range_trans_destroy(poldiff_range_trans_summary_t ** rts); + +/** + * Reset the state of all range transition rule differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int range_trans_reset(poldiff_t * diff); + +/** + * Get a vector of all range transition rules from the given policy, + * sorted by source type. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * + * @return A newly allocated vector of all range transition rules (of + * type pseudo_range_trans_t). The caller is responsible for calling + * apol_vector_destroy() afterwards. On error, return NULL and set + * errno. + */ + apol_vector_t *range_trans_get_items(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Compare two pseudo_range_trans_t objects, determining if they have + * the same source type, target type, and target class or not. + * + * @param x The range transition from the original policy. + * @param y The range transition from the modified policy. + * @param diff The policy difference structure associated with both + * policies. + * + * @return < 0, 0, or > 0 if x is respectively less than, equal to, or + * greater than source role of y. + */ + int range_trans_comp(const void *x, const void *y, const poldiff_t * diff); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a range transition rule. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int range_trans_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Compute the semantic difference of two range transition rules for + * which the compare callback returns 0. If a difference is found + * then allocate, initialize, and insert a new semantic difference + * entry for that range transition rule. + * + * @param diff The policy difference structure associated with both + * rules and to which to add an entry if needed. + * @param x The range transition rule from the original policy. + * @param y The range transition rule from the modified policy. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int range_trans_deep_diff(poldiff_t * diff, const void *x, const void *y); + +#ifdef __cplusplus +} +#endif + +#endif /* POLDIFF_RANGE_TRANS_INTERNAL_H */ diff --git a/libpoldiff/src/rbac_diff.c b/libpoldiff/src/rbac_diff.c new file mode 100644 index 0000000..bc47ba7 --- /dev/null +++ b/libpoldiff/src/rbac_diff.c @@ -0,0 +1,1052 @@ +/** + * @file + * Implementation for computing semantic differences in role allow + * rules and role_transition rules. + * + * @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 "poldiff_internal.h" + +#include <apol/bst.h> +#include <apol/util.h> +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +struct poldiff_role_allow_summary +{ + size_t num_added; + size_t num_removed; + size_t num_modified; + apol_vector_t *diffs; +}; + +struct poldiff_role_trans_summary +{ + size_t num_added; + size_t num_removed; + size_t num_modified; + size_t num_added_type; + size_t num_removed_type; + apol_vector_t *diffs; +}; + +struct poldiff_role_allow +{ + const char *source_role; + poldiff_form_e form; + apol_vector_t *orig_roles; + apol_vector_t *added_roles; + apol_vector_t *removed_roles; +}; + +struct poldiff_role_trans +{ + const char *source_role; + char *target_type; + const char *orig_default; + const char *mod_default; + poldiff_form_e form; +}; + +/**************** role allow functions *******************/ + +void poldiff_role_allow_get_stats(const poldiff_t * diff, size_t stats[5]) +{ + if (diff == NULL || stats == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return; + } + stats[0] = diff->role_allow_diffs->num_added; + stats[1] = diff->role_allow_diffs->num_removed; + stats[2] = diff->role_allow_diffs->num_modified; + stats[3] = 0; + stats[4] = 0; +} + +char *poldiff_role_allow_to_string(const poldiff_t * diff, const void *role_allow) +{ + const poldiff_role_allow_t *ra = role_allow; + size_t len = 0, i; + char *s = NULL, *role; + if (diff == NULL || role_allow == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + switch (ra->form) { + case POLDIFF_FORM_ADDED: + { + if (apol_str_appendf(&s, &len, "+ allow %s { ", ra->source_role) < 0) { + s = NULL; + break; + } + for (i = 0; i < apol_vector_get_size(ra->added_roles); i++) { + role = apol_vector_get_element(ra->added_roles, i); + if (apol_str_appendf(&s, &len, "%s ", role) < 0) { + goto err; + } + } + if (apol_str_append(&s, &len, "};") < 0) { + break; + } + return s; + } + case POLDIFF_FORM_REMOVED: + { + if (apol_str_appendf(&s, &len, "- allow %s { ", ra->source_role) < 0) { + break; + } + for (i = 0; i < apol_vector_get_size(ra->removed_roles); i++) { + role = apol_vector_get_element(ra->removed_roles, i); + if (apol_str_appendf(&s, &len, "%s ", role) < 0) { + goto err; + } + } + if (apol_str_append(&s, &len, "};") < 0) { + break; + } + return s; + } + case POLDIFF_FORM_MODIFIED: + { + if (apol_str_appendf(&s, &len, "* allow %s { ", ra->source_role) < 0) { + s = NULL; + break; + } + for (i = 0; i < apol_vector_get_size(ra->orig_roles); i++) { + role = apol_vector_get_element(ra->orig_roles, i); + if (apol_str_appendf(&s, &len, "%s ", role) < 0) { + goto err; + } + } + for (i = 0; i < apol_vector_get_size(ra->added_roles); i++) { + role = apol_vector_get_element(ra->added_roles, i); + if (apol_str_appendf(&s, &len, "+%s ", role) < 0) { + goto err; + } + } + for (i = 0; i < apol_vector_get_size(ra->removed_roles); i++) { + role = apol_vector_get_element(ra->removed_roles, i); + if (apol_str_appendf(&s, &len, "-%s ", role) < 0) { + goto err; + } + } + if (apol_str_append(&s, &len, "};") < 0) { + break; + } + return s; + } + default: + { + ERR(diff, "%s", strerror(ENOTSUP)); + errno = ENOTSUP; + return NULL; + } + } + /* if this is reached then an error occurred */ + err: + free(s); + ERR(diff, "%s", strerror(ENOMEM)); + errno = ENOMEM; + return NULL; +} + +const apol_vector_t *poldiff_get_role_allow_vector(const poldiff_t * diff) +{ + if (diff == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + return diff->role_allow_diffs->diffs; +} + +const char *poldiff_role_allow_get_name(const poldiff_role_allow_t * role_allow) +{ + if (role_allow == NULL) { + errno = EINVAL; + return NULL; + } + return role_allow->source_role; +} + +poldiff_form_e poldiff_role_allow_get_form(const void *role_allow) +{ + if (role_allow == NULL) { + errno = EINVAL; + return POLDIFF_FORM_NONE; + } + return ((const poldiff_role_allow_t *)role_allow)->form; +} + +const apol_vector_t *poldiff_role_allow_get_unmodified_roles(const poldiff_role_allow_t * role_allow) +{ + if (role_allow == NULL) { + errno = EINVAL; + return NULL; + } + return role_allow->orig_roles; +} + +const apol_vector_t *poldiff_role_allow_get_added_roles(const poldiff_role_allow_t * role_allow) +{ + if (role_allow == NULL) { + errno = EINVAL; + return NULL; + } + return role_allow->added_roles; +} + +const apol_vector_t *poldiff_role_allow_get_removed_roles(const poldiff_role_allow_t * role_allow) +{ + if (role_allow == NULL) { + errno = EINVAL; + return NULL; + } + return role_allow->removed_roles; +} + +static void role_allow_free(void *elem) +{ + if (elem != NULL) { + poldiff_role_allow_t *r = (poldiff_role_allow_t *) elem; + apol_vector_destroy(&r->orig_roles); + apol_vector_destroy(&r->added_roles); + apol_vector_destroy(&r->removed_roles); + free(r); + } +} + +poldiff_role_allow_summary_t *role_allow_create(void) +{ + poldiff_role_allow_summary_t *ras = calloc(1, sizeof(*ras)); + if (ras == NULL) { + return NULL; + } + if ((ras->diffs = apol_vector_create(role_allow_free)) == NULL) { + role_allow_destroy(&ras); + return NULL; + } + return ras; +} + +void role_allow_destroy(poldiff_role_allow_summary_t ** ras) +{ + if (ras != NULL && *ras != NULL) { + apol_vector_destroy(&(*ras)->diffs); + free(*ras); + *ras = NULL; + } +} + +typedef struct pseudo_role_allow +{ + const char *source_role; + apol_vector_t *target_roles; +} pseudo_role_allow_t; + +static void role_allow_free_item(void *item) +{ + pseudo_role_allow_t *pra = item; + + if (!item) + return; + + /* no need to free source name or target role names */ + apol_vector_destroy(&pra->target_roles); + free(item); +} + +static int role_allow_source_comp(const void *x, const void *y, void *arg __attribute__ ((unused))) +{ + const pseudo_role_allow_t *p1 = x; + const pseudo_role_allow_t *p2 = y; + + return strcmp(p1->source_role, p2->source_role); +} + +apol_vector_t *role_allow_get_items(poldiff_t * diff, const apol_policy_t * policy) +{ + qpol_iterator_t *iter = NULL; + apol_vector_t *tmp = NULL, *v = NULL; + int error = 0, retv; + size_t i; + apol_bst_t *bst = NULL; + pseudo_role_allow_t *pra = NULL; + const qpol_role_t *sr = NULL, *tr = NULL; + const char *sr_name = NULL, *tr_name = NULL; + const qpol_role_allow_t *qra = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policy); + + if (qpol_policy_get_role_allow_iter(q, &iter) < 0) { + return NULL; + } + + tmp = apol_vector_create_from_iter(iter, NULL); + if (tmp == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + qpol_iterator_destroy(&iter); + errno = error; + return NULL; + } + qpol_iterator_destroy(&iter); + + bst = apol_bst_create(role_allow_source_comp, role_allow_free_item); + + for (i = 0; i < apol_vector_get_size(tmp); i++) { + qra = apol_vector_get_element(tmp, i); + if (!(pra = calloc(1, sizeof(*pra))) || (!(pra->target_roles = apol_vector_create_with_capacity(1, NULL)))) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + if (qpol_role_allow_get_source_role(q, qra, &sr) || qpol_role_get_name(q, sr, &sr_name)) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + sr = NULL; + if (qpol_role_allow_get_target_role(q, qra, &tr) || qpol_role_get_name(q, tr, &tr_name)) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + tr = NULL; + pra->source_role = sr_name; + retv = apol_bst_insert_and_get(bst, (void **)&pra, NULL); + if (retv < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + apol_vector_append_unique(pra->target_roles, (void *)tr_name, apol_str_strcmp, NULL); + pra = NULL; + } + apol_vector_destroy(&tmp); + + v = apol_bst_get_vector(bst, 1); + if (!v) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + apol_bst_destroy(&bst); + + return v; + + err: + role_allow_free_item(pra); + apol_bst_destroy(&bst); + errno = error; + return NULL; +} + +int role_allow_comp(const void *x, const void *y, const poldiff_t * diff __attribute__ ((unused))) +{ + const pseudo_role_allow_t *p1 = x; + const pseudo_role_allow_t *p2 = y; + + return strcmp(p1->source_role, p2->source_role); +} + +int role_allow_reset(poldiff_t * diff) +{ + int error = 0; + + if (diff == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + role_allow_destroy(&diff->role_allow_diffs); + diff->role_allow_diffs = role_allow_create(); + if (diff->role_allow_diffs == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + errno = error; + return -1; + } + + return 0; +} + +/** + * Allocate and return a new role allow rule difference object. + * + * @param diff Policy diff error handler. + * @param form Form of the difference. + * @param source_role Name of the source role in the role allow rule. + * + * @return A newly allocated and initialized diff, or NULL upon error. + * The caller is responsible for calling role_allow_free() upon the returned + * value. + */ +static poldiff_role_allow_t *make_ra_diff(const poldiff_t * diff, poldiff_form_e form, const char *source_role) +{ + poldiff_role_allow_t *ra = NULL; + int error = 0; + if ((ra = calloc(1, sizeof(*ra))) == NULL || + (ra->source_role = source_role) == NULL || + (ra->added_roles = apol_vector_create_with_capacity(1, NULL)) == NULL || + (ra->orig_roles = apol_vector_create_with_capacity(1, NULL)) == NULL || + (ra->removed_roles = apol_vector_create_with_capacity(1, NULL)) == NULL) { + error = errno; + role_allow_free(ra); + ERR(diff, "%s", strerror(error)); + errno = error; + return NULL; + } + ra->form = form; + return ra; +} + +int role_allow_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + pseudo_role_allow_t *ra = (pseudo_role_allow_t *) item; + poldiff_role_allow_t *pra; + int error; + + pra = make_ra_diff(diff, form, ra->source_role); + if (pra == NULL) { + return -1; + } + int rt; + if (form == POLDIFF_FORM_ADDED) { + rt = apol_vector_cat(pra->added_roles, ra->target_roles); + } else { + rt = apol_vector_cat(pra->removed_roles, ra->target_roles); + } + if (rt < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + role_allow_free(pra); + errno = error; + return -1; + } + if (apol_vector_append(diff->role_allow_diffs->diffs, pra) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + role_allow_free(pra); + errno = error; + return -1; + } + if (form == POLDIFF_FORM_ADDED) { + diff->role_allow_diffs->num_added++; + } else { + diff->role_allow_diffs->num_removed++; + } + return 0; +} + +int role_allow_deep_diff(poldiff_t * diff, const void *x, const void *y) +{ + const pseudo_role_allow_t *p1 = x; + const pseudo_role_allow_t *p2 = y; + apol_vector_t *v1 = NULL, *v2 = NULL; + char *role1, *role2; + poldiff_role_allow_t *pra = NULL; + size_t i, j; + int retval = -1, error = 0, compval; + + v1 = p1->target_roles; + v2 = p2->target_roles; + + apol_vector_sort(v1, apol_str_strcmp, NULL); + apol_vector_sort(v2, apol_str_strcmp, NULL); + for (i = j = 0; i < apol_vector_get_size(v1);) { + if (j >= apol_vector_get_size(v2)) + break; + role1 = (char *)apol_vector_get_element(v1, i); + role2 = (char *)apol_vector_get_element(v2, j); + compval = strcmp(role1, role2); + if (pra == NULL) { + if ((pra = make_ra_diff(diff, POLDIFF_FORM_MODIFIED, p1->source_role)) == NULL) { + error = errno; + goto cleanup; + } + } + if (compval < 0) { + if (apol_vector_append(pra->removed_roles, role1) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + i++; + } else if (compval > 0) { + if (apol_vector_append(pra->added_roles, role2) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + j++; + } else { + if (apol_vector_append(pra->orig_roles, role1) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + i++; + j++; + } + } + for (; i < apol_vector_get_size(v1); i++) { + role1 = (char *)apol_vector_get_element(v1, i); + if (pra == NULL) { + if ((pra = make_ra_diff(diff, POLDIFF_FORM_MODIFIED, p1->source_role)) == NULL) { + error = errno; + goto cleanup; + } + } + if (apol_vector_append(pra->removed_roles, role1) < 0) { + error = errno; + free(role1); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + for (; j < apol_vector_get_size(v2); j++) { + role2 = (char *)apol_vector_get_element(v2, j); + if (pra == NULL) { + if ((pra = make_ra_diff(diff, POLDIFF_FORM_MODIFIED, p1->source_role)) == NULL) { + error = errno; + goto cleanup; + } + } + if (apol_vector_append(pra->added_roles, role2) < 0) { + error = errno; + free(role2); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + if (apol_vector_get_size(pra->added_roles) || apol_vector_get_size(pra->removed_roles)) { + apol_vector_sort(pra->removed_roles, apol_str_strcmp, NULL); + apol_vector_sort(pra->added_roles, apol_str_strcmp, NULL); + apol_vector_sort(pra->orig_roles, apol_str_strcmp, NULL); + if (apol_vector_append(diff->role_allow_diffs->diffs, pra) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + diff->role_allow_diffs->num_modified++; + } else { + role_allow_free(pra); + pra = NULL; + } + retval = 0; + cleanup: + if (retval != 0) { + role_allow_free(pra); + } + errno = error; + return retval; +} + +/**************** role_transition functions *******************/ + +void poldiff_role_trans_get_stats(const poldiff_t * diff, size_t stats[5]) +{ + if (diff == NULL || stats == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return; + } + stats[0] = diff->role_trans_diffs->num_added; + stats[1] = diff->role_trans_diffs->num_removed; + stats[2] = diff->role_trans_diffs->num_modified; + stats[3] = diff->role_trans_diffs->num_added_type; + stats[4] = diff->role_trans_diffs->num_removed_type; +} + +extern char *poldiff_role_trans_to_string(const poldiff_t * diff, const void *role_trans) +{ + const poldiff_role_trans_t *rt = role_trans; + char *s = NULL; + + if (diff == NULL || role_trans == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + switch (rt->form) { + case POLDIFF_FORM_ADDED: + case POLDIFF_FORM_ADD_TYPE: + { + if (asprintf(&s, "+ role_transition %s %s %s;", rt->source_role, rt->target_type, rt->mod_default) < 0) + break; + return s; + } + case POLDIFF_FORM_REMOVED: + case POLDIFF_FORM_REMOVE_TYPE: + { + if (asprintf(&s, "- role_transition %s %s %s;", rt->source_role, rt->target_type, rt->orig_default) < 0) + break; + return s; + } + case POLDIFF_FORM_MODIFIED: + { + if (asprintf + (&s, "* role_transition %s %s { +%s -%s };", rt->source_role, rt->target_type, rt->mod_default, + rt->orig_default) < 0) + break; + return s; + } + case POLDIFF_FORM_NONE: + default: + { + ERR(diff, "%s", strerror(ENOTSUP)); + errno = ENOTSUP; + return NULL; + } + } + /* if this is reached then an error occurred */ + free(s); + ERR(diff, "%s", strerror(ENOMEM)); + errno = ENOMEM; + return NULL; +} + +const apol_vector_t *poldiff_get_role_trans_vector(const poldiff_t * diff) +{ + if (diff == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + return diff->role_trans_diffs->diffs; +} + +extern const char *poldiff_role_trans_get_source_role(const poldiff_role_trans_t * role_trans) +{ + if (role_trans == NULL) { + errno = EINVAL; + return NULL; + } + return role_trans->source_role; +} + +extern const char *poldiff_role_trans_get_target_type(const poldiff_role_trans_t * role_trans) +{ + if (role_trans == NULL) { + errno = EINVAL; + return NULL; + } + return role_trans->target_type; +} + +extern poldiff_form_e poldiff_role_trans_get_form(const void *role_trans) +{ + if (role_trans == NULL) { + errno = EINVAL; + return POLDIFF_FORM_NONE; + } + return ((const poldiff_role_trans_t *)role_trans)->form; +} + +extern const char *poldiff_role_trans_get_original_default(const poldiff_role_trans_t * role_trans) +{ + if (role_trans == NULL) { + errno = EINVAL; + return NULL; + } + return role_trans->orig_default; +} + +extern const char *poldiff_role_trans_get_modified_default(const poldiff_role_trans_t * role_trans) +{ + if (role_trans == NULL) { + errno = EINVAL; + return NULL; + } + return role_trans->mod_default; +} + +static void role_trans_free(void *elem) +{ + if (elem != NULL) { + poldiff_role_trans_t *rt = elem; + free(rt->target_type); + free(rt); + } +} + +poldiff_role_trans_summary_t *role_trans_create(void) +{ + poldiff_role_trans_summary_t *rts = calloc(1, sizeof(*rts)); + if (rts == NULL) { + return NULL; + } + if ((rts->diffs = apol_vector_create(role_trans_free)) == NULL) { + role_trans_destroy(&rts); + return NULL; + } + return rts; +} + +void role_trans_destroy(poldiff_role_trans_summary_t ** rts) +{ + if (rts != NULL && *rts != NULL) { + apol_vector_destroy(&(*rts)->diffs); + free(*rts); + *rts = NULL; + } +} + +int role_trans_reset(poldiff_t * diff) +{ + int error = 0; + + if (diff == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + role_trans_destroy(&diff->role_trans_diffs); + diff->role_trans_diffs = role_trans_create(); + if (diff->role_trans_diffs == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + errno = error; + return -1; + } + + return 0; +} + +typedef struct pseudo_role_trans +{ + const char *source_role; + uint32_t pseudo_target; + const char *default_role; +} pseudo_role_trans_t; + +/** + * Compare two pseudo role_transition rules from the same policy. + * Compares the source role name and then pseudo type value of the target. + * + * @param x A pseudo_role_trans_t entry. + * @param y A pseudo_role_trans_t entry. + * @param arg The policy difference structure. + * + * @return < 0, 0, or > 0 if the first rule is respectively less than, + * equal to, or greater than the second. If the return value would be 0 + * but the default role is different a warning is issued. + */ +static int pseudo_role_trans_comp(const void *x, const void *y, void *arg) +{ + int retv = 0; + const pseudo_role_trans_t *a = x; + const pseudo_role_trans_t *b = y; + poldiff_t *diff = arg; + + retv = strcmp(a->source_role, b->source_role); + if (!retv) + retv = a->pseudo_target - b->pseudo_target; + else + return retv; + if (!retv && strcmp(a->default_role, b->default_role)) + WARN(diff, "Multiple role_transition rules for %s %s with different default roles.", a->source_role, + type_map_get_name(diff, a->pseudo_target, POLDIFF_POLICY_ORIG)); + return retv; +} + +static void role_trans_free_item(void *item) +{ + /* no need to free members of a pseudo role_transition */ + free(item); +} + +apol_vector_t *role_trans_get_items(poldiff_t * diff, const apol_policy_t * policy) +{ + qpol_iterator_t *iter = NULL, *attr_types = NULL; + apol_vector_t *v = NULL; + const qpol_role_trans_t *qrt = NULL; + pseudo_role_trans_t *tmp_prt = NULL; + const char *tmp_name = NULL; + const qpol_role_t *tmp_role = NULL; + const qpol_type_t *tmp_type = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policy); + int error = 0, which_pol; + unsigned char isattr = 0; + + which_pol = (policy == diff->orig_pol ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD); + if (qpol_policy_get_role_trans_iter(q, &iter)) { + error = errno; + goto err; + } + v = apol_vector_create(role_trans_free_item); + if (!v) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + isattr = 0; + if (qpol_iterator_get_item(iter, (void **)&qrt) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + if (qpol_role_trans_get_target_type(q, qrt, &tmp_type) < 0) { + error = errno; + goto err; + } + qpol_type_get_isattr(q, tmp_type, &isattr); + if (isattr) { + qpol_type_get_type_iter(q, tmp_type, &attr_types); + for (; !qpol_iterator_end(attr_types); qpol_iterator_next(attr_types)) { + qpol_iterator_get_item(attr_types, (void **)&tmp_type); + if (!(tmp_prt = calloc(1, sizeof(*tmp_prt)))) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + tmp_prt->pseudo_target = type_map_lookup(diff, tmp_type, which_pol); + qpol_role_trans_get_source_role(q, qrt, &tmp_role); + qpol_role_get_name(q, tmp_role, &tmp_name); + tmp_prt->source_role = tmp_name; + qpol_role_trans_get_default_role(q, qrt, &tmp_role); + qpol_role_get_name(q, tmp_role, &tmp_name); + tmp_prt->default_role = tmp_name; + if (apol_vector_append(v, tmp_prt)) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + tmp_prt = NULL; + } + qpol_iterator_destroy(&attr_types); + } else { + if (!(tmp_prt = calloc(1, sizeof(*tmp_prt)))) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + tmp_prt->pseudo_target = type_map_lookup(diff, tmp_type, which_pol); + qpol_role_trans_get_source_role(q, qrt, &tmp_role); + qpol_role_get_name(q, tmp_role, &tmp_name); + tmp_prt->source_role = tmp_name; + qpol_role_trans_get_default_role(q, qrt, &tmp_role); + qpol_role_get_name(q, tmp_role, &tmp_name); + tmp_prt->default_role = tmp_name; + if (apol_vector_append(v, tmp_prt)) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + tmp_prt = NULL; + } + } + qpol_iterator_destroy(&iter); + apol_vector_sort_uniquify(v, pseudo_role_trans_comp, diff); + + return v; + + err: + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&attr_types); + apol_vector_destroy(&v); + free(tmp_prt); + errno = error; + return NULL; +} + +int role_trans_comp(const void *x, const void *y, const poldiff_t * diff __attribute__ ((unused))) +{ + int retv = 0; + const pseudo_role_trans_t *a = x; + const pseudo_role_trans_t *b = y; + + retv = strcmp(a->source_role, b->source_role); + if (!retv) + return a->pseudo_target - b->pseudo_target; + else + return retv; +} + +/** + * Allocate and return a new role_transition rule difference object. + * + * @param diff Policy difference error handler. + * @param form Form of the difference. + * @param src Name of the source role. + * @param tgt Name of the target type. + * + * @return A newly allocated and initialised diff or NULL upon error. + * The caller is responsible for calling free() upon the returned + * value. + */ +static poldiff_role_trans_t *make_rt_diff(const poldiff_t * diff, poldiff_form_e form, const char *src, const char *tgt) +{ + poldiff_role_trans_t *rt = NULL; + int error = 0; + if ((rt = calloc(1, sizeof(*rt))) == NULL || (rt->source_role = src) == NULL || (rt->target_type = strdup(tgt)) == NULL) { + error = errno; + role_trans_free(rt); + ERR(diff, "%s", strerror(error)); + errno = error; + return NULL; + } + rt->form = form; + return rt; +} + +int role_trans_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + const pseudo_role_trans_t *rt = item; + poldiff_role_trans_t *prt = NULL; + const char *tgt_name = NULL; + int error = 0; + + /* get tgt_name from type_map */ + switch (form) { + case POLDIFF_FORM_ADDED: + { + tgt_name = type_map_get_name(diff, rt->pseudo_target, POLDIFF_POLICY_MOD); + if (type_map_get_name(diff, rt->pseudo_target, POLDIFF_POLICY_ORIG) == NULL) { + form = POLDIFF_FORM_ADD_TYPE; + } + break; + } + case POLDIFF_FORM_REMOVED: + { + tgt_name = type_map_get_name(diff, rt->pseudo_target, POLDIFF_POLICY_ORIG); + if (type_map_get_name(diff, rt->pseudo_target, POLDIFF_POLICY_MOD) == NULL) { + form = POLDIFF_FORM_REMOVE_TYPE; + } + break; + } + case POLDIFF_FORM_MODIFIED: /* not supported here */ + case POLDIFF_FORM_NONE: + default: + { + assert(0); + return -1; + } + } + assert(tgt_name != NULL); + + /* create a new diff */ + prt = make_rt_diff(diff, form, rt->source_role, tgt_name); + if (!prt) + return -1; + + /* set the appropriate default */ + switch (form) { + case POLDIFF_FORM_ADDED: + case POLDIFF_FORM_ADD_TYPE: + { + prt->mod_default = rt->default_role; + break; + } + case POLDIFF_FORM_REMOVED: + case POLDIFF_FORM_REMOVE_TYPE: + { + prt->orig_default = rt->default_role; + break; + } + default: + { + /* not reachable */ + assert(0); + } + } + if (apol_vector_append(diff->role_trans_diffs->diffs, prt)) { + error = errno; + ERR(diff, "%s", strerror(error)); + role_trans_free(prt); + errno = error; + return -1; + }; + + /* increment appropriate counter */ + switch (form) { + case POLDIFF_FORM_ADDED: + { + diff->role_trans_diffs->num_added++; + break; + } + case POLDIFF_FORM_ADD_TYPE: + { + diff->role_trans_diffs->num_added_type++; + break; + } + case POLDIFF_FORM_REMOVED: + { + diff->role_trans_diffs->num_removed++; + break; + } + case POLDIFF_FORM_REMOVE_TYPE: + { + diff->role_trans_diffs->num_removed_type++; + break; + } + default: + { + /* not reachable */ + assert(0); + } + } + + return 0; +} + +int role_trans_deep_diff(poldiff_t * diff, const void *x, const void *y) +{ + const pseudo_role_trans_t *prt1 = x; + const pseudo_role_trans_t *prt2 = y; + const char *default1 = NULL, *default2 = NULL; + poldiff_role_trans_t *rt = NULL; + const char *tgt = NULL; + int error = 0; + + default1 = prt1->default_role; + default2 = prt2->default_role; + + if (!strcmp(default1, default2)) + return 0; /* no difference */ + + tgt = type_map_get_name(diff, prt1->pseudo_target, POLDIFF_POLICY_ORIG); + assert(tgt != NULL); + rt = make_rt_diff(diff, POLDIFF_FORM_MODIFIED, prt1->source_role, tgt); + if (!rt) + return -1; /* errors already reported */ + rt->orig_default = default1; + rt->mod_default = default2; + if (apol_vector_append(diff->role_trans_diffs->diffs, rt)) { + error = errno; + ERR(diff, "%s", strerror(error)); + role_trans_free(rt); + errno = error; + return -1; + }; + diff->role_trans_diffs->num_modified++; + + return 0; +} diff --git a/libpoldiff/src/rbac_internal.h b/libpoldiff/src/rbac_internal.h new file mode 100644 index 0000000..6c89234 --- /dev/null +++ b/libpoldiff/src/rbac_internal.h @@ -0,0 +1,209 @@ +/** + * @file + * Protected interface for role allow rule and role_transition rule + * differences. + * + * @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 POLDIFF_RBAC_INTERNAL_H +#define POLDIFF_RBAC_INTERNAL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct poldiff_role_allow_summary poldiff_role_allow_summary_t; + typedef struct poldiff_role_trans_summary poldiff_role_trans_summary_t; + +/** + * Allocate and return a new poldiff_role_allow_summary_t object. + * + * @return A new role allow summary. The caller must call role_allow_destroy() + * afterwards. On error, return NULL and set errno. + */ + poldiff_role_allow_summary_t *role_allow_create(void); + +/** + * Deallocate all space associated with a poldiff_role_allow_summary_t + * object, including the pointer itself. If the pointer is already + * NULL then do nothing. + * + * @param ras Reference to a role allow summary to destroy. The pointer + * will be set to NULL afterwards. + */ + void role_allow_destroy(poldiff_role_allow_summary_t ** ras); + +/** + * Reset the state of all role allow rule differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int role_allow_reset(poldiff_t * diff); + +/** + * Get a vector of all role allow rules from the given policy, + * sorted by source name. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * + * @return A newly allocated vector of all role allow rules (of type + * pseudo_role_allow_t). The caller is responsible for calling + * apol_vector_destroy() afterwards. On error, return NULL and set + * errno. + */ + apol_vector_t *role_allow_get_items(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Compare two pseudo_role_allow_t objects, determining if they have the same + * source name or not. + * + * @param x The role allow from the original policy. + * @param y The role allow from the modified policy. + * @param diff The policy difference structure associated with both + * policies. + * + * @return < 0, 0, or > 0 if source role of x is respectively less than, equal + * to, or greater than source role of y. + */ + int role_allow_comp(const void *x, const void *y, const poldiff_t * diff); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a role allow rule. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int role_allow_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Compute the semantic difference of two role allow rules for which the + * compare callback returns 0. If a difference is found then allocate, + * initialize, and insert a new semantic difference entry for that role allow + * rule. + * + * @param diff The policy difference structure associated with both + * rules and to which to add an entry if needed. + * @param x The role allow rule from the original policy. + * @param y The role allow rule from the modified policy. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int role_allow_deep_diff(poldiff_t * diff, const void *x, const void *y); + +/** + * Allocate and return a new poldiff_role_trans_summary_t object. + * + * @return A new role transition summary. The caller must call + * role_trans_destroy() afterwards. On error, return NULL and set errno. + */ + poldiff_role_trans_summary_t *role_trans_create(void); + +/** + * Deallocate all space associated with a poldiff_role_trans_summary_t + * object, including the pointer itself. If the pointer is already + * NULL then do nothing. + * + * @param rts Reference to a role transition summary to destroy. The pointer + * will be set to NULL afterwards. + */ + void role_trans_destroy(poldiff_role_trans_summary_t ** rts); + +/** + * Reset the state of all role_transition rule differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int role_trans_reset(poldiff_t * diff); + +/** + * Get a vector of all role_transition rules from the given policy, + * sorted by source name. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * + * @return A newly allocated vector of all role_transition rules (of + * type pseudo_role_trans_t). The caller is responsible for calling + * apol_vector_destroy() afterwards. On error, return NULL and set + * errno. + */ + apol_vector_t *role_trans_get_items(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Compare two pseudo_role_trans_t objects, determining if they have the same + * source name and target or not. + * + * @param x The role_transition from the original policy. + * @param y The role_transition from the modified policy. + * @param diff The policy difference structure associated with both + * policies. + * + * @return < 0, 0, or > 0 if source role of x is respectively less than, equal + * to, or greater than source role of y. + */ + int role_trans_comp(const void *x, const void *y, const poldiff_t * diff); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a role_transition rule. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int role_trans_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Compute the semantic difference of two role_transition rules for which the + * compare callback returns 0. If a difference is found then allocate, + * initialize, and insert a new semantic difference entry for that + * role_transition rule. + * + * @param diff The policy difference structure associated with both + * rules and to which to add an entry if needed. + * @param x The role_transition rule from the original policy. + * @param y The role_transition rule from the modified policy. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int role_trans_deep_diff(poldiff_t * diff, const void *x, const void *y); + +#ifdef __cplusplus +} +#endif + +#endif /* POLDIFF_RBAC_INTERNAL_H */ diff --git a/libpoldiff/src/role_diff.c b/libpoldiff/src/role_diff.c new file mode 100644 index 0000000..bac2062 --- /dev/null +++ b/libpoldiff/src/role_diff.c @@ -0,0 +1,543 @@ +/** + * @file + * Implementation for computing semantic differences in roles. + * + * @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 "poldiff_internal.h" + +#include <apol/util.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +struct poldiff_role_summary +{ + size_t num_added; + size_t num_removed; + size_t num_modified; + apol_vector_t *diffs; +}; + +struct poldiff_role +{ + char *name; + poldiff_form_e form; + apol_vector_t *added_types; + apol_vector_t *removed_types; +}; + +void poldiff_role_get_stats(const poldiff_t * diff, size_t stats[5]) +{ + if (diff == NULL || stats == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return; + } + stats[0] = diff->role_diffs->num_added; + stats[1] = diff->role_diffs->num_removed; + stats[2] = diff->role_diffs->num_modified; + stats[3] = 0; + stats[4] = 0; +} + +char *poldiff_role_to_string(const poldiff_t * diff, const void *role) +{ + const poldiff_role_t *r = role; + size_t num_added, num_removed, len = 0, i; + char *s = NULL, *type; + if (diff == NULL || role == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + num_added = apol_vector_get_size(r->added_types); + num_removed = apol_vector_get_size(r->removed_types); + switch (r->form) { + case POLDIFF_FORM_ADDED: + { + if (apol_str_appendf(&s, &len, "+ %s", r->name) < 0) { + s = NULL; + break; + } + return s; + } + case POLDIFF_FORM_REMOVED: + { + if (apol_str_appendf(&s, &len, "- %s", r->name) < 0) { + s = NULL; + break; + } + return s; + } + case POLDIFF_FORM_MODIFIED: + { + if (apol_str_appendf(&s, &len, "* %s (", r->name) < 0) { + s = NULL; + break; + } + if (num_added > 0) { + if (apol_str_appendf(&s, &len, "%zd Added Type%s", num_added, (num_added == 1 ? "" : "s")) < 0) { + break; + } + } + if (num_removed > 0) { + if (apol_str_appendf + (&s, &len, "%s%zd Removed Type%s", (num_added > 0 ? ", " : ""), num_removed, + (num_removed == 1 ? "" : "s")) < 0) { + break; + } + } + if (apol_str_append(&s, &len, ")\n") < 0) { + break; + } + for (i = 0; i < apol_vector_get_size(r->added_types); i++) { + type = (char *)apol_vector_get_element(r->added_types, i); + if (apol_str_appendf(&s, &len, "\t+ %s\n", type) < 0) { + goto err; + } + } + for (i = 0; i < apol_vector_get_size(r->removed_types); i++) { + type = (char *)apol_vector_get_element(r->removed_types, i); + if (apol_str_appendf(&s, &len, "\t- %s\n", type) < 0) { + goto err; + } + } + return s; + } + default: + { + ERR(diff, "%s", strerror(ENOTSUP)); + errno = ENOTSUP; + return NULL; + } + } + err: + /* if this is reached then an error occurred */ + free(s); + ERR(diff, "%s", strerror(ENOMEM)); + errno = ENOMEM; + return NULL; +} + +const apol_vector_t *poldiff_get_role_vector(const poldiff_t * diff) +{ + if (diff == NULL) { + errno = EINVAL; + return NULL; + } + return diff->role_diffs->diffs; +} + +const char *poldiff_role_get_name(const poldiff_role_t * role) +{ + if (role == NULL) { + errno = EINVAL; + return NULL; + } + return role->name; +} + +poldiff_form_e poldiff_role_get_form(const void *role) +{ + if (role == NULL) { + errno = EINVAL; + return 0; + } + return ((const poldiff_role_t *)role)->form; +} + +const apol_vector_t *poldiff_role_get_added_types(const poldiff_role_t * role) +{ + if (role == NULL) { + errno = EINVAL; + return NULL; + } + return role->added_types; +} + +const apol_vector_t *poldiff_role_get_removed_types(const poldiff_role_t * role) +{ + if (role == NULL) { + errno = EINVAL; + return NULL; + } + return role->removed_types; +} + +/*************** protected functions for roles ***************/ + +static void role_free(void *elem) +{ + if (elem != NULL) { + poldiff_role_t *r = (poldiff_role_t *) elem; + free(r->name); + apol_vector_destroy(&r->added_types); + apol_vector_destroy(&r->removed_types); + free(r); + } +} + +poldiff_role_summary_t *role_create(void) +{ + poldiff_role_summary_t *rs = calloc(1, sizeof(*rs)); + if (rs == NULL) { + return NULL; + } + if ((rs->diffs = apol_vector_create(role_free)) == NULL) { + role_destroy(&rs); + return NULL; + } + return rs; +} + +void role_destroy(poldiff_role_summary_t ** rs) +{ + if (rs != NULL && *rs != NULL) { + apol_vector_destroy(&(*rs)->diffs); + free(*rs); + *rs = NULL; + } +} + +int role_reset(poldiff_t * diff) +{ + int error = 0; + + if (diff == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + role_destroy(&diff->role_diffs); + diff->role_diffs = role_create(); + if (diff->role_diffs == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + errno = error; + return -1; + } + + return 0; +} + +/** + * Comparison function for two roles from the same policy. + */ +static int role_name_comp(const void *x, const void *y, void *arg) +{ + const qpol_role_t *r1 = x; + const qpol_role_t *r2 = y; + apol_policy_t *p = (apol_policy_t *) arg; + qpol_policy_t *q = apol_policy_get_qpol(p); + const char *name1, *name2; + if (qpol_role_get_name(q, r1, &name1) < 0 || qpol_role_get_name(q, r2, &name2) < 0) { + return 0; + } + return strcmp(name1, name2); +} + +apol_vector_t *role_get_items(poldiff_t * diff, const apol_policy_t * policy) +{ + qpol_iterator_t *iter = NULL; + apol_vector_t *v = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policy); + int error = 0; + if (qpol_policy_get_role_iter(q, &iter) < 0) { + return NULL; + } + v = apol_vector_create_from_iter(iter, NULL); + if (v == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + qpol_iterator_destroy(&iter); + errno = error; + return NULL; + } + qpol_iterator_destroy(&iter); + apol_vector_sort(v, role_name_comp, (void *)policy); + return v; +} + +int role_comp(const void *x, const void *y, const poldiff_t * diff) +{ + const qpol_role_t *r1 = x; + const qpol_role_t *r2 = y; + const char *name1, *name2; + if (qpol_role_get_name(diff->orig_qpol, r1, &name1) < 0 || qpol_role_get_name(diff->mod_qpol, r2, &name2) < 0) { + return 0; + } + return strcmp(name1, name2); +} + +/** + * Allocate and return a new role difference object. + * + * @param diff Policy diff error handler. + * @param form Form of the difference. + * @param name Name of the role that is different. + * + * @return A newly allocated and initialized diff, or NULL upon error. + * The caller is responsible for calling role_free() upon the returned + * value. + */ +static poldiff_role_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name) +{ + poldiff_role_t *pr; + int error; + if ((pr = calloc(1, sizeof(*pr))) == NULL || + (pr->name = strdup(name)) == NULL || + (pr->added_types = apol_vector_create_with_capacity(1, free)) == NULL || + (pr->removed_types = apol_vector_create_with_capacity(1, free)) == NULL) { + error = errno; + role_free(pr); + ERR(diff, "%s", strerror(error)); + errno = error; + return NULL; + } + pr->form = form; + return pr; +} + +int role_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + const qpol_role_t *r = item; + const char *name = NULL; + poldiff_role_t *pr; + int error; + if ((form == POLDIFF_FORM_ADDED && + qpol_role_get_name(diff->mod_qpol, r, &name) < 0) || + ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_role_get_name(diff->orig_qpol, r, &name) < 0)) + { + return -1; + } + pr = make_diff(diff, form, name); + if (pr == NULL) { + return -1; + } + if (apol_vector_append(diff->role_diffs->diffs, pr) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + role_free(pr); + errno = error; + return -1; + } + if (form == POLDIFF_FORM_ADDED) { + diff->role_diffs->num_added++; + } else { + diff->role_diffs->num_removed++; + } + return 0; +} + +/** + * Given a role, return an unsorted vector of its allowed types (in + * the form of uint32_t corresponding to pseudo-type values). + * + * @param diff Policy diff error handler. + * @param role Role whose roles to get. + * @param which Which policy, one of POLDIFF_POLICY_ORIG or + * POLDIFF_POLICY_MOD. + * + * @return Vector of pseudo-type values. The caller is responsible + * for calling apol_vector_destroy(). On error, return NULL. + */ +static apol_vector_t *role_get_types(const poldiff_t * diff, const qpol_role_t * role, int which) +{ + qpol_iterator_t *iter = NULL; + const qpol_type_t *type; + uint32_t new_val; + apol_vector_t *v = NULL; + int retval = -1, error = 0; + + if ((v = apol_vector_create(NULL)) == NULL) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + if (which == POLDIFF_POLICY_ORIG) { + if (qpol_role_get_type_iter(diff->orig_qpol, role, &iter) < 0) { + goto cleanup; + } + } else { + if (qpol_role_get_type_iter(diff->mod_qpol, role, &iter) < 0) { + goto cleanup; + } + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&type) < 0 || (new_val = type_map_lookup(diff, type, which)) == 0) { + error = errno; + goto cleanup; + } + if (apol_vector_append(v, (void *)((size_t) new_val)) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + if (retval < 0) { + apol_vector_destroy(&v); + errno = error; + return NULL; + } + return v; +} + +int role_deep_diff(poldiff_t * diff, const void *x, const void *y) +{ + const qpol_role_t *r1 = x; + const qpol_role_t *r2 = y; + apol_vector_t *v1 = NULL, *v2 = NULL; + apol_vector_t *added_types = NULL, *removed_types = NULL; + const apol_vector_t *reverse_v; + const char *name; + char *new_name; + uint32_t t1, t2; + poldiff_role_t *r = NULL; + qpol_type_t *t; + size_t i, j; + int retval = -1, error = 0; + + if (qpol_role_get_name(diff->orig_qpol, r1, &name) < 0 || + (v1 = role_get_types(diff, r1, POLDIFF_POLICY_ORIG)) == NULL || + (v2 = role_get_types(diff, r2, POLDIFF_POLICY_MOD)) == NULL) { + error = errno; + goto cleanup; + } + apol_vector_sort_uniquify(v1, NULL, NULL); + apol_vector_sort_uniquify(v2, NULL, NULL); + if ((added_types = apol_vector_create(NULL)) == NULL || (removed_types = apol_vector_create(NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + for (i = j = 0; i < apol_vector_get_size(v1);) { + if (j >= apol_vector_get_size(v2)) + break; + t1 = (uint32_t) ((size_t) apol_vector_get_element(v1, i)); + t2 = (uint32_t) ((size_t) apol_vector_get_element(v2, j)); + if (t2 > t1) { + if (apol_vector_append(removed_types, (void *)((size_t) t1)) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + i++; + } else if (t1 > t2) { + if (apol_vector_append(added_types, (void *)((size_t) t2)) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + j++; + } else { + i++; + j++; + } + } + for (; i < apol_vector_get_size(v1); i++) { + t1 = (uint32_t) ((size_t) apol_vector_get_element(v1, i)); + if (apol_vector_append(removed_types, (void *)((size_t) t1)) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + for (; j < apol_vector_get_size(v2); j++) { + t2 = (uint32_t) ((size_t) apol_vector_get_element(v2, j)); + if (apol_vector_append(added_types, (void *)((size_t) t2)) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + if (apol_vector_get_size(added_types) > 0 || apol_vector_get_size(removed_types) > 0) { + if ((r = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) { + error = errno; + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(removed_types); i++) { + t1 = (uint32_t) ((size_t) apol_vector_get_element(removed_types, i)); + if ((reverse_v = type_map_lookup_reverse(diff, t1, POLDIFF_POLICY_ORIG)) == NULL) { + error = errno; + goto cleanup; + } + for (j = 0; j < apol_vector_get_size(reverse_v); j++) { + t = (qpol_type_t *) apol_vector_get_element(reverse_v, j); + if (qpol_type_get_name(diff->orig_qpol, t, &name) < 0) { + error = errno; + goto cleanup; + } + if ((new_name = strdup(name)) == NULL || apol_vector_append(r->removed_types, new_name) < 0) { + error = errno; + free(new_name); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + } + for (i = 0; i < apol_vector_get_size(added_types); i++) { + t2 = (uint32_t) ((size_t) apol_vector_get_element(added_types, i)); + if ((reverse_v = type_map_lookup_reverse(diff, t2, POLDIFF_POLICY_MOD)) == NULL) { + error = errno; + goto cleanup; + } + for (j = 0; j < apol_vector_get_size(reverse_v); j++) { + t = (qpol_type_t *) apol_vector_get_element(reverse_v, j); + if (qpol_type_get_name(diff->mod_qpol, t, &name) < 0) { + error = errno; + goto cleanup; + } + if ((new_name = strdup(name)) == NULL || apol_vector_append(r->added_types, new_name) < 0) { + error = errno; + free(new_name); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + } + apol_vector_sort(r->removed_types, apol_str_strcmp, NULL); + apol_vector_sort(r->added_types, apol_str_strcmp, NULL); + if (apol_vector_append(diff->role_diffs->diffs, r) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + diff->role_diffs->num_modified++; + } + retval = 0; + cleanup: + apol_vector_destroy(&v1); + apol_vector_destroy(&v2); + apol_vector_destroy(&added_types); + apol_vector_destroy(&removed_types); + if (retval != 0) { + role_free(r); + } + errno = error; + return retval; +} diff --git a/libpoldiff/src/role_internal.h b/libpoldiff/src/role_internal.h new file mode 100644 index 0000000..7c2aa90 --- /dev/null +++ b/libpoldiff/src/role_internal.h @@ -0,0 +1,121 @@ +/** + * @file + * Protected interface for role differences. + * + * @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 POLDIFF_ROLE_INTERNAL_H +#define POLDIFF_ROLE_INTERNAL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct poldiff_role_summary poldiff_role_summary_t; + +/** + * Allocate and return a new poldiff_role_summary_t object. + * + * @return A new role summary. The caller must call role_destroy() + * afterwards. On error, return NULL and set errno. + */ + poldiff_role_summary_t *role_create(void); + +/** + * Deallocate all space associated with a poldiff_role_summary_t + * object, including the pointer itself. If the pointer is already + * NULL then do nothing. + * + * @param us Reference to a role summary to destroy. The pointer + * will be set to NULL afterwards. + */ + void role_destroy(poldiff_role_summary_t ** us); + +/** + * Reset the state of all role differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int role_reset(poldiff_t * diff); + +/** + * Get a vector of all roles from the given policy, sorted by name. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * + * @return a newly allocated vector of all roles. The caller is + * responsible for calling apol_vector_destroy() afterwards. On + * error, return NULL and set errno. + */ + apol_vector_t *role_get_items(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Compare two qpol_role_t objects, determining if they have the same + * name or not. + * + * @param x The role from the original policy. + * @param y The role from the modified policy. + * @param diff The policy difference structure associated with both + * policies. + * + * @return < 0, 0, or > 0 if role x is respectively less than, equal + * to, or greater than role y. + */ + int role_comp(const void *x, const void *y, const poldiff_t * diff); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a role. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int role_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Compute the semantic difference of two roles for which the compare + * callback returns 0. If a difference is found then allocate, + * initialize, and insert a new semantic difference entry for that + * role. + * + * @param diff The policy difference structure associated with both + * roles and to which to add an entry if needed. + * @param x The role from the original policy. + * @param y The role from the modified policy. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int role_deep_diff(poldiff_t * diff, const void *x, const void *y); + +#ifdef __cplusplus +} +#endif + +#endif /* POLDIFF_ROLE_INTERNAL_H */ diff --git a/libpoldiff/src/terule_diff.c b/libpoldiff/src/terule_diff.c new file mode 100644 index 0000000..2857403 --- /dev/null +++ b/libpoldiff/src/terule_diff.c @@ -0,0 +1,1329 @@ +/** + * @file + * Implementation for computing semantic differences in AV and Type + * rules. + * + * @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 "poldiff_internal.h" + +#include <apol/policy-query.h> +#include <apol/util.h> +#include <qpol/policy_extend.h> +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +struct poldiff_terule_summary +{ + size_t num_added; + size_t num_removed; + size_t num_modified; + size_t num_added_type; + size_t num_removed_type; + int diffs_sorted; + /** vector of poldiff_terule_t */ + apol_vector_t *diffs; +}; + +struct poldiff_terule +{ + uint32_t spec; + /* pointer into policy's symbol table */ + const char *source, *target; + /** the class string is pointer into the class_bst BST */ + const char *cls; + poldiff_form_e form; + /* pointer into policy's symbol table */ + const char *orig_default, *mod_default; + /** pointer into policy's conditional list, needed to render + * conditional expressions */ + const qpol_cond_t *cond; + uint32_t branch; + /** vector of unsigned longs of line numbers from original policy */ + apol_vector_t *orig_linenos; + /** vector of unsigned longs of line numbers from modified policy */ + apol_vector_t *mod_linenos; + /** array of pointers for original rules */ + qpol_terule_t **orig_rules; + size_t num_orig_rules; + /** array of pointers for modified rules */ + qpol_terule_t **mod_rules; + size_t num_mod_rules; +}; + +typedef struct pseudo_terule +{ + uint32_t spec; + /** pseudo-type values */ + uint32_t source, target, default_type; + /** pointer into the class_bst BST */ + const char *cls; + /** array of pointers into the bool_bst BST */ + const char *bools[5]; + uint32_t bool_val; + uint32_t branch; + /** pointer into policy's conditional list, needed to render + * conditional expressions */ + const qpol_cond_t *cond; + /** array of qpol_terule_t pointers, for showing line numbers */ + const qpol_terule_t **rules; + size_t num_rules; +} pseudo_terule_t; + +/******************** public terule functions ********************/ + +/** + * Get an array of statistics for the number of differences of each + * form for te rules. + * + * @param diff The policy difference structure from which to get the + * stats. + * @param stats Array into which to write the numbers (array must be + * pre-allocated). The order of the values written to the array is + * as follows: number of items of form POLDIFF_FORM_ADDED, number of + * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of + * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE. + * @param idx Index into the terule diffs array indicating which rule + * type to reset, one of TERULE_OFFSET_MEMBER, etc. + */ +static void poldiff_terule_get_stats(const poldiff_t * diff, size_t stats[5], unsigned int idx) +{ + if (diff == NULL || stats == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return; + } + stats[0] = diff->terule_diffs[idx]->num_added; + stats[1] = diff->terule_diffs[idx]->num_removed; + stats[2] = diff->terule_diffs[idx]->num_modified; + stats[3] = diff->terule_diffs[idx]->num_added_type; + stats[4] = diff->terule_diffs[idx]->num_removed_type; +} + +void poldiff_terule_get_stats_change(const poldiff_t * diff, size_t stats[5]) +{ + poldiff_terule_get_stats(diff, stats, TERULE_OFFSET_CHANGE); +} + +void poldiff_terule_get_stats_member(const poldiff_t * diff, size_t stats[5]) +{ + poldiff_terule_get_stats(diff, stats, TERULE_OFFSET_MEMBER); +} + +void poldiff_terule_get_stats_trans(const poldiff_t * diff, size_t stats[5]) +{ + poldiff_terule_get_stats(diff, stats, TERULE_OFFSET_TRANS); +} + +char *poldiff_terule_to_string(const poldiff_t * diff, const void *terule) +{ + const poldiff_terule_t *pt = (const poldiff_terule_t *)terule; + apol_policy_t *p; + const char *rule_type; + char *diff_char = "", *s = NULL, *cond_expr = NULL; + size_t len = 0; + int error; + if (diff == NULL || terule == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + switch (pt->form) { + case POLDIFF_FORM_ADDED: + case POLDIFF_FORM_ADD_TYPE: + { + diff_char = "+"; + p = diff->mod_pol; + break; + } + case POLDIFF_FORM_REMOVED: + case POLDIFF_FORM_REMOVE_TYPE: + { + diff_char = "-"; + p = diff->orig_pol; + break; + } + case POLDIFF_FORM_MODIFIED: + { + diff_char = "*"; + p = diff->orig_pol; + break; + } + default: + { + ERR(diff, "%s", strerror(ENOTSUP)); + errno = ENOTSUP; + return NULL; + } + } + rule_type = apol_rule_type_to_str(pt->spec); + if (apol_str_appendf(&s, &len, "%s %s %s %s : %s ", diff_char, rule_type, pt->source, pt->target, pt->cls) < 0) { + error = errno; + s = NULL; + goto err; + } + switch (pt->form) { + case POLDIFF_FORM_ADDED: + case POLDIFF_FORM_ADD_TYPE: + { + if (apol_str_append(&s, &len, pt->mod_default) < 0) { + error = errno; + goto err; + } + break; + } + case POLDIFF_FORM_REMOVED: + case POLDIFF_FORM_REMOVE_TYPE: + { + if (apol_str_append(&s, &len, pt->orig_default) < 0) { + error = errno; + goto err; + } + break; + } + case POLDIFF_FORM_MODIFIED: + { + if (apol_str_appendf(&s, &len, "{ -%s +%s }", pt->orig_default, pt->mod_default) < 0) { + error = errno; + goto err; + } + break; + } + default: + { + ERR(diff, "%s", strerror(ENOTSUP)); + errno = ENOTSUP; + return NULL; + } + } + if (apol_str_append(&s, &len, ";") < 0) { + error = errno; + goto err; + } + if (pt->cond != NULL) { + if ((cond_expr = apol_cond_expr_render(p, pt->cond)) == NULL) { + error = errno; + goto err; + } + if (apol_str_appendf(&s, &len, " [%s]:%s", cond_expr, (pt->branch ? "TRUE" : "FALSE")) < 0) { + error = errno; + goto err; + } + free(cond_expr); + } + return s; + err: + free(s); + free(cond_expr); + ERR(diff, "%s", strerror(error)); + errno = error; + return NULL; +} + +/** + * Sort poldiff_terule diff results in a mostly alphabetical order. + */ +static int poldiff_terule_cmp(const void *x, const void *y, void *data __attribute__ ((unused))) +{ + const poldiff_terule_t *a = (const poldiff_terule_t *)x; + const poldiff_terule_t *b = (const poldiff_terule_t *)y; + int compval; + if (a->spec != b->spec) { + const char *rule_type1 = apol_rule_type_to_str(a->spec); + const char *rule_type2 = apol_rule_type_to_str(b->spec); + compval = strcmp(rule_type1, rule_type2); + if (compval != 0) { + return compval; + } + } + if ((compval = strcmp(a->source, b->source)) != 0) { + return compval; + } + if ((compval = strcmp(a->target, b->target)) != 0) { + return compval; + } + if ((compval = strcmp(a->cls, b->cls)) != 0) { + return compval; + } + if (a->cond != b->cond) { + return (int)((char *)a->cond - (char *)b->cond); + } + /* sort true branch before false branch */ + return b->branch - a->branch; +} + +/** + * Get the vector of te rule differences from the te rule difference + * summary. + * + * @param diff The policy difference structure associated with the te + * rule difference summary. + * @param idx Index into the terule diffs array indicating which rule + * type to reset, one of TERULE_OFFSET_MEMBER, etc. + * + * @return A vector of elements of type poldiff_terule_t, or NULL on + * error. The caller should <b>not</b> destroy the vector returned. + * If the call fails, errno will be set. + */ +static const apol_vector_t *poldiff_get_terule_vector(const poldiff_t * diff, terule_offset_e idx) +{ + if (diff == NULL) { + errno = EINVAL; + return NULL; + } + if (diff->terule_diffs[idx]->diffs_sorted == 0) { + apol_vector_sort(diff->terule_diffs[idx]->diffs, poldiff_terule_cmp, NULL); + diff->terule_diffs[idx]->diffs_sorted = 1; + } + return diff->terule_diffs[idx]->diffs; +} + +const apol_vector_t *poldiff_get_terule_vector_member(const poldiff_t * diff) +{ + return poldiff_get_terule_vector(diff, TERULE_OFFSET_MEMBER); +} + +const apol_vector_t *poldiff_get_terule_vector_change(const poldiff_t * diff) +{ + return poldiff_get_terule_vector(diff, TERULE_OFFSET_CHANGE); +} + +const apol_vector_t *poldiff_get_terule_vector_trans(const poldiff_t * diff) +{ + return poldiff_get_terule_vector(diff, TERULE_OFFSET_TRANS); +} + +poldiff_form_e poldiff_terule_get_form(const void *terule) +{ + if (terule == NULL) { + errno = EINVAL; + return 0; + } + return ((const poldiff_terule_t *)terule)->form; +} + +uint32_t poldiff_terule_get_rule_type(const poldiff_terule_t * terule) +{ + if (terule == NULL) { + errno = EINVAL; + return 0; + } + return terule->spec; +} + +const char *poldiff_terule_get_source_type(const poldiff_terule_t * terule) +{ + if (terule == NULL) { + errno = EINVAL; + return 0; + } + return terule->source; +} + +const char *poldiff_terule_get_target_type(const poldiff_terule_t * terule) +{ + if (terule == NULL) { + errno = EINVAL; + return 0; + } + return terule->target; +} + +const char *poldiff_terule_get_object_class(const poldiff_terule_t * terule) +{ + if (terule == NULL) { + errno = EINVAL; + return 0; + } + return terule->cls; +} + +void poldiff_terule_get_cond(const poldiff_t * diff, const poldiff_terule_t * terule, + const qpol_cond_t ** cond, uint32_t * which_list, const apol_policy_t ** p) +{ + if (diff == NULL || terule == NULL || cond == NULL || p == NULL) { + errno = EINVAL; + return; + } + *cond = terule->cond; + if (*cond == NULL) { + *which_list = 1; + *p = NULL; + } else if (terule->form == POLDIFF_FORM_ADDED || terule->form == POLDIFF_FORM_ADD_TYPE) { + *which_list = terule->branch; + *p = diff->mod_pol; + } else { + *which_list = terule->branch; + *p = diff->orig_pol; + } +} + +const char *poldiff_terule_get_original_default(const poldiff_terule_t * terule) +{ + if (terule == NULL) { + errno = EINVAL; + return 0; + } + return terule->orig_default; +} + +const char *poldiff_terule_get_modified_default(const poldiff_terule_t * terule) +{ + if (terule == NULL) { + errno = EINVAL; + return 0; + } + return terule->mod_default; +} + +apol_vector_t *poldiff_terule_get_orig_line_numbers(const poldiff_terule_t * terule) +{ + if (terule == NULL) { + errno = EINVAL; + return NULL; + } + return terule->orig_linenos; +} + +apol_vector_t *poldiff_terule_get_mod_line_numbers(const poldiff_terule_t * terule) +{ + if (terule == NULL) { + errno = EINVAL; + return NULL; + } + return terule->mod_linenos; +} + +/*************** protected functions ***************/ + +/** + * Free all space used by a poldiff_terule_t, including the pointer + * itself. Does nothing if the pointer is already NULL. + * + * @param elem Pointer to a poldiff_terule_t. + */ +static void poldiff_terule_free(void *elem) +{ + if (elem != NULL) { + poldiff_terule_t *t = elem; + apol_vector_destroy(&t->orig_linenos); + apol_vector_destroy(&t->mod_linenos); + free(t->orig_rules); + free(t->mod_rules); + free(elem); + } +} + +poldiff_terule_summary_t *terule_create(void) +{ + poldiff_terule_summary_t *rs = calloc(1, sizeof(*rs)); + if (rs == NULL) { + return NULL; + } + if ((rs->diffs = apol_vector_create(poldiff_terule_free)) == NULL) { + terule_destroy(&rs); + return NULL; + } + return rs; +} + +void terule_destroy(poldiff_terule_summary_t ** rs) +{ + if (rs != NULL && *rs != NULL) { + apol_vector_destroy(&(*rs)->diffs); + free(*rs); + *rs = NULL; + } +} + +/** + * Reset the state of all TE rule differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @param idx Index into the terule diffs array indicating which rule + * type to reset, one of TERULE_OFFSET_CHANGE, etc. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ +static int terule_reset(poldiff_t * diff, terule_offset_e idx) +{ + int error = 0; + terule_destroy(&diff->terule_diffs[idx]); + diff->terule_diffs[idx] = terule_create(); + if (diff->terule_diffs[idx] == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + errno = error; + return -1; + } + return 0; +} + +int terule_reset_change(poldiff_t * diff) +{ + return terule_reset(diff, TERULE_OFFSET_CHANGE); +} + +int terule_reset_member(poldiff_t * diff) +{ + return terule_reset(diff, TERULE_OFFSET_MEMBER); +} + +int terule_reset_trans(poldiff_t * diff) +{ + return terule_reset(diff, TERULE_OFFSET_TRANS); +} + +static void terule_free_item(void *item) +{ + pseudo_terule_t *t = (pseudo_terule_t *) item; + if (item != NULL) { + free(t->rules); + free(t); + } +} + +/** + * Apply an ordering scheme to two pseudo-te rules. + * + * <ul> + * <li>Sort by target pseudo-type value, + * <li>Then by source pseudo-type value, + * <li>Then by object class's BST's pointer value, + * <li>Then by rule specified (allow, neverallow, etc.), + * <li>Then choose unconditional rules over conditional rules, + * <li>Then by conditional expression's BST's boolean pointer value. + * </ul> + * + * If this function is being used for sorting (via terule_get_items()) + * then sort by truth value, and then by branch (true branch, then + * false branch). Otherwise, when comparing rules (via terule_comp()) + * then by truth value, inverting rule2's value if in the other + * branch. + */ +static int pseudo_terule_comp(const pseudo_terule_t * rule1, const pseudo_terule_t * rule2, int is_sorting) +{ + size_t i; + uint32_t bool_val; + if (rule1->target != rule2->target) { + return rule1->target - rule2->target; + } + if (rule1->source != rule2->source) { + return rule1->source - rule2->source; + } + if (rule1->cls != rule2->cls) { + return (int)(rule1->cls - rule2->cls); + } + if (rule1->spec != rule2->spec) { + return rule1->spec - rule2->spec; + } + if (rule1->bools[0] == NULL && rule2->bools[0] == NULL) { + /* both rules are unconditional */ + return 0; + } else if (rule1->bools[0] == NULL && rule2->bools[0] != NULL) { + /* unconditional rules come before conditional */ + return -1; + } else if (rule1->bools[0] != NULL && rule2->bools[0] == NULL) { + /* unconditional rules come before conditional */ + return 1; + } + for (i = 0; i < (sizeof(rule1->bools) / sizeof(rule1->bools[0])); i++) { + if (rule1->bools[i] != rule2->bools[i]) { + return (int)(rule1->bools[i] - rule2->bools[i]); + } + } + if (is_sorting) { + if (rule1->branch != rule2->branch) { + return rule1->branch - rule2->branch; + } + return (int)rule1->bool_val - (int)rule2->bool_val; + } else { + if (rule1->branch == rule2->branch) { + bool_val = rule2->bool_val; + } else { + bool_val = ~rule2->bool_val; + } + if (rule1->bool_val < bool_val) { + return -1; + } else if (rule1->bool_val > bool_val) { + return 1; + } + return 0; + } +} + +static int terule_bst_comp(const void *x, const void *y, void *data) +{ + const pseudo_terule_t *r1 = (const pseudo_terule_t *)x; + const pseudo_terule_t *r2 = (const pseudo_terule_t *)y; + poldiff_t *diff = data; + int retv; + retv = pseudo_terule_comp(r1, r2, 1); + if (!retv && r1->default_type != r2->default_type) + WARN(diff, "Multiple %s rules for %s %s %s with different default types", apol_rule_type_to_str(r1->spec), + type_map_get_name(diff, r1->source, POLDIFF_POLICY_ORIG), type_map_get_name(diff, r1->target, + POLDIFF_POLICY_ORIG), r1->cls); + return retv; +} + +/** + * Given a conditional expression, convert its booleans to a sorted + * array of pseudo-boolean values, assign that array to the + * pseudo-terule key, and then derive the truth table. + * + * @param diff Policy difference structure. + * @param p Policy containing conditional. + * @param cond Conditional expression to convert. + * @param key Location to write converted expression. + */ +static int terule_build_cond(const poldiff_t * diff, const apol_policy_t * p, const qpol_cond_t * cond, pseudo_terule_t * key) +{ + qpol_iterator_t *iter = NULL; + qpol_cond_expr_node_t *node; + uint32_t expr_type, truthiness; + qpol_bool_t *bools[5] = { NULL, NULL, NULL, NULL, NULL }, *qbool; + size_t i, j; + size_t num_bools = 0; + const char *bool_name, *pseudo_bool, *t; + qpol_policy_t *q = apol_policy_get_qpol(p); + int retval = -1, error = 0, compval; + if (qpol_cond_get_expr_node_iter(q, cond, &iter) < 0) { + error = errno; + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&node) < 0 || qpol_cond_expr_node_get_expr_type(q, node, &expr_type) < 0) { + error = errno; + goto cleanup; + } + if (expr_type != QPOL_COND_EXPR_BOOL) { + continue; + } + if (qpol_cond_expr_node_get_bool(q, node, &qbool) < 0) { + error = errno; + goto cleanup; + } + for (i = 0; i < num_bools; i++) { + if (bools[i] == qbool) { + break; + } + } + if (i >= num_bools) { + assert(i < 5); + bools[i] = qbool; + num_bools++; + } + } + for (i = 0; i < num_bools; i++) { + if (qpol_bool_get_name(q, bools[i], &bool_name) < 0) { + error = errno; + goto cleanup; + } + if (apol_bst_get_element(diff->bool_bst, (void *)bool_name, NULL, (void **)&pseudo_bool) < 0) { + error = EBADRQC; /* should never get here */ + ERR(diff, "%s", strerror(error)); + assert(0); + goto cleanup; + } + key->bools[i] = pseudo_bool; + } + + /* bubble sorth the pseudo bools (not bad because there are at + * most five elements */ + for (i = num_bools; i > 1; i--) { + for (j = 1; j < i; j++) { + compval = strcmp(key->bools[j - 1], key->bools[j]); + if (compval > 0) { + t = key->bools[j]; + key->bools[j] = key->bools[j - 1]; + key->bools[j - 1] = t; + qbool = bools[j]; + bools[j] = bools[j - 1]; + bools[j - 1] = qbool; + } + } + } + + /* now compute the truth table for the booleans */ + key->bool_val = 0; + for (i = 0; i < 32; i++) { + for (j = 0; j < num_bools; j++) { + int state = ((i & (1 << j)) ? 1 : 0); + if (qpol_bool_set_state_no_eval(q, bools[j], state) < 0) { + error = errno; + goto cleanup; + } + } + if (qpol_cond_eval(q, cond, &truthiness) < 0) { + error = errno; + goto cleanup; + } + key->bool_val = (key->bool_val << 1) | truthiness; + } + + key->cond = cond; + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + return retval; +} + +/** + * Given a rule, construct a new pseudo-terule and insert it into the + * BST if not already there. + * + * @param diff Policy difference structure. + * @param p Policy from which the rule came. + * @param rule TE rule to insert. + * @param source Source pseudo-type value. + * @param target Target pseudo-type value. + * @param b BST containing pseudo-terules. + * + * @return 0 on success, < 0 on error. + */ +static int terule_add_to_bst(poldiff_t * diff, const apol_policy_t * p, + const qpol_terule_t * rule, uint32_t source, uint32_t target, apol_bst_t * b) +{ + pseudo_terule_t *key, *inserted_key; + const qpol_class_t *obj_class; + const qpol_type_t *default_type; + const char *class_name; + const qpol_cond_t *cond; + qpol_policy_t *q = apol_policy_get_qpol(p); + int retval = -1, error = 0, compval; + int which = (p == diff->orig_pol ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD); + if ((key = calloc(1, sizeof(*key))) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + if (qpol_terule_get_rule_type(q, rule, &(key->spec)) < 0 || + qpol_terule_get_object_class(q, rule, &obj_class) < 0 || + qpol_terule_get_default_type(q, rule, &default_type) < 0 || qpol_terule_get_cond(q, rule, &cond) < 0) { + error = errno; + goto cleanup; + } + if (qpol_class_get_name(q, obj_class, &class_name) < 0) { + error = errno; + goto cleanup; + } + if (apol_bst_get_element(diff->class_bst, (void *)class_name, NULL, (void **)&key->cls) < 0) { + error = EBADRQC; /* should never get here */ + ERR(diff, "%s", strerror(error)); + assert(0); + goto cleanup; + } + if ((key->default_type = type_map_lookup(diff, default_type, which)) == 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + key->source = source; + key->target = target; + if (cond != NULL && (qpol_terule_get_which_list(q, rule, &(key->branch)) < 0 || terule_build_cond(diff, p, cond, key) < 0)) { + error = errno; + goto cleanup; + } + + /* insert this pseudo into the tree if not already there */ + if ((compval = apol_bst_insert_and_get(b, (void **)&key, diff)) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + inserted_key = key; + key = NULL; + + /* store the rule pointer, to be used for showing line numbers */ + if (qpol_policy_has_capability(q, QPOL_CAP_LINE_NUMBERS)) { + const qpol_terule_t **t = realloc(inserted_key->rules, + (inserted_key->num_rules + 1) * sizeof(*t)); + if (t == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + inserted_key->rules = t; + inserted_key->rules[inserted_key->num_rules++] = rule; + } + + retval = 0; + cleanup: + if (retval < 0) { + terule_free_item(key); + } + errno = error; + return retval; +} + +/** + * Given a rule, expand its source and target types into individual + * pseudo-type values. Then add the expanded rule to the BST. This + * is needed for when the source and/or target is an attribute. + * + * @param diff Policy difference structure. + * @param p Policy from which the rule came. + * @param rule TE rule to insert. + * @param b BST containing pseudo-terules. + * + * @return 0 on success, < 0 on error. + */ +static int terule_expand(poldiff_t * diff, const apol_policy_t * p, const qpol_terule_t * rule, apol_bst_t * b) +{ + const qpol_type_t *source, *orig_target, *target; + unsigned char source_attr, target_attr; + qpol_iterator_t *source_iter = NULL, *target_iter = NULL; + uint32_t source_val, target_val; + qpol_policy_t *q = apol_policy_get_qpol(p); + int which = (p == diff->orig_pol ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD); + int retval = -1, error = 0; + if (qpol_terule_get_source_type(q, rule, &source) < 0 || + qpol_terule_get_target_type(q, rule, &orig_target) < 0 || + qpol_type_get_isattr(q, source, &source_attr) < 0 || qpol_type_get_isattr(q, orig_target, &target_attr)) { + error = errno; + goto cleanup; + } + if (source_attr && qpol_type_get_type_iter(q, source, &source_iter) < 0) { + error = errno; + goto cleanup; + } + do { + if (source_attr) { + if (qpol_iterator_get_item(source_iter, (void **)&source) < 0) { + error = errno; + goto cleanup; + } + qpol_iterator_next(source_iter); + } + if (target_attr) { + if (qpol_type_get_type_iter(q, orig_target, &target_iter) < 0) { + error = errno; + goto cleanup; + } + } else { + target = orig_target; + } + do { + if (target_attr) { + if (qpol_iterator_get_item(target_iter, (void **)&target) < 0) { + error = errno; + goto cleanup; + } + qpol_iterator_next(target_iter); + } + const char *n1, *n2; + qpol_type_get_name(q, source, &n1); + qpol_type_get_name(q, target, &n2); + if ((source_val = type_map_lookup(diff, source, which)) == 0 || + (target_val = type_map_lookup(diff, target, which)) == 0 || + terule_add_to_bst(diff, p, rule, source_val, target_val, b) < 0) { + error = errno; + goto cleanup; + } + } while (target_attr && !qpol_iterator_end(target_iter)); + qpol_iterator_destroy(&target_iter); + } while (source_attr && !qpol_iterator_end(source_iter)); + retval = 0; + cleanup: + qpol_iterator_destroy(&source_iter); + qpol_iterator_destroy(&target_iter); + errno = error; + return retval; +} + +/** + * Get a vector of terules from the given policy, sorted. This + * function will remap source and target types to their pseudo-type + * value equivalents. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * @param which Kind of rule to get, one of QPOL_RULE_TYPE_TRANS, etc. + * + * @return A newly allocated vector of all te rules (of type + * pseudo_terule_t). The caller is responsible for calling + * apol_vector_destroy() afterwards. On error, return NULL and set + * errno. + */ +static apol_vector_t *terule_get_items(poldiff_t * diff, const apol_policy_t * policy, unsigned int which) +{ + apol_vector_t *bools = NULL, *bool_states = NULL; + size_t i, num_rules, j; + apol_bst_t *b = NULL; + apol_vector_t *v = NULL; + qpol_iterator_t *iter = NULL; + qpol_terule_t *rule; + qpol_policy_t *q = apol_policy_get_qpol(policy); + int retval = -1, error = 0; + if (poldiff_build_bsts(diff) < 0) { + error = errno; + goto cleanup; + } + + /* store original boolean values */ + if (apol_bool_get_by_query(policy, NULL, &bools) < 0) { + error = errno; + goto cleanup; + } + if ((bool_states = apol_vector_create_with_capacity(apol_vector_get_size(bools), NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(bools); i++) { + qpol_bool_t *qbool = apol_vector_get_element(bools, i); + int state; + if (qpol_bool_get_state(q, qbool, &state) < 0) { + error = errno; + goto cleanup; + } + if (apol_vector_append(bool_states, (void *)((size_t) state)) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + if ((b = apol_bst_create(terule_bst_comp, terule_free_item)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + if (qpol_policy_get_terule_iter(q, which, &iter) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + qpol_iterator_get_size(iter, &num_rules); + for (j = 0; !qpol_iterator_end(iter); qpol_iterator_next(iter), j++) { + if (qpol_iterator_get_item(iter, (void **)&rule) < 0 || terule_expand(diff, policy, rule, b) < 0) { + error = errno; + goto cleanup; + } + if (!(j % 1024)) { + int percent = 50 * j / num_rules + (policy == diff->mod_pol ? 50 : 0); + INFO(diff, "Computing TE rule difference: %02d%% complete", percent); + } + } + if ((v = apol_bst_get_vector(b, 1)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + retval = 0; + cleanup: + /* restore boolean states */ + for (i = 0; bools != NULL && i < apol_vector_get_size(bools); i++) { + qpol_bool_t *qbool = apol_vector_get_element(bools, i); + int state = (int)((size_t) apol_vector_get_element(bool_states, i)); + qpol_bool_set_state_no_eval(q, qbool, state); + } + apol_vector_destroy(&bools); + apol_vector_destroy(&bool_states); + qpol_policy_reevaluate_conds(q); + apol_bst_destroy(&b); + qpol_iterator_destroy(&iter); + if (retval < 0) { + apol_vector_destroy(&v); + errno = error; + return NULL; + } + return v; +} + +apol_vector_t *terule_get_items_change(poldiff_t * diff, const apol_policy_t * policy) +{ + return terule_get_items(diff, policy, QPOL_RULE_TYPE_CHANGE); +} + +apol_vector_t *terule_get_items_member(poldiff_t * diff, const apol_policy_t * policy) +{ + return terule_get_items(diff, policy, QPOL_RULE_TYPE_MEMBER); +} + +apol_vector_t *terule_get_items_trans(poldiff_t * diff, const apol_policy_t * policy) +{ + return terule_get_items(diff, policy, QPOL_RULE_TYPE_TRANS); +} + +int terule_comp(const void *x, const void *y, const poldiff_t * diff __attribute__ ((unused))) +{ + const pseudo_terule_t *r1 = (const pseudo_terule_t *)x; + const pseudo_terule_t *r2 = (const pseudo_terule_t *)y; + return pseudo_terule_comp(r1, r2, 0); +} + +/** + * Allocate and return a new terule difference object. If the + * pseudo-terule's source and/or target expands to multiple read + * types, then just choose the first one for display. + * + * @param diff Policy diff error handler. + * @param form Form of the difference. + * @param rule Pseudo terule that changed. + * + * @return A newly allocated and initialized diff, or NULL upon error. + * The caller is responsible for calling poldiff_terule_free() upon + * the returned value. + */ +static poldiff_terule_t *make_tediff(const poldiff_t * diff, poldiff_form_e form, const pseudo_terule_t * rule) +{ + poldiff_terule_t *pt; + const char *n1, *n2; + int error; + if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) { + n1 = type_map_get_name(diff, rule->source, POLDIFF_POLICY_MOD); + n2 = type_map_get_name(diff, rule->target, POLDIFF_POLICY_MOD); + } else { + n1 = type_map_get_name(diff, rule->source, POLDIFF_POLICY_ORIG); + n2 = type_map_get_name(diff, rule->target, POLDIFF_POLICY_ORIG); + } + assert(n1 != NULL && n2 != NULL); + if ((pt = calloc(1, sizeof(*pt))) == NULL) { + error = errno; + poldiff_terule_free(pt); + ERR(diff, "%s", strerror(error)); + errno = error; + return NULL; + } + pt->spec = rule->spec; + pt->source = n1; + pt->target = n2; + pt->cls = rule->cls; + pt->form = form; + pt->cond = rule->cond; + pt->branch = rule->branch; + return pt; +} + +/** + * Create, initialize, and insert a new semantic difference entry for + * a pseudo-te rule. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * @param idx Index into the terule differences specifying int which + * to place the constructed pseudo-te rule. + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ +static int terule_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item, terule_offset_e idx) +{ + pseudo_terule_t *rule = (pseudo_terule_t *) item; + poldiff_terule_t *pt = NULL; + const apol_vector_t *v1, *v2; + apol_policy_t *p; + const char *orig_default = NULL, *mod_default = NULL; + int retval = -1, error = errno; + + /* check if form should really become ADD_TYPE / REMOVE_TYPE, + * by seeing if the /other/ policy's reverse lookup is + * empty */ + if (form == POLDIFF_FORM_ADDED) { + if ((v1 = type_map_lookup_reverse(diff, rule->source, POLDIFF_POLICY_ORIG)) == NULL || + (v2 = type_map_lookup_reverse(diff, rule->target, POLDIFF_POLICY_ORIG)) == NULL || + (mod_default = type_map_get_name(diff, rule->default_type, POLDIFF_POLICY_MOD)) == NULL) { + error = errno; + goto cleanup; + } + if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) { + form = POLDIFF_FORM_ADD_TYPE; + } + p = diff->mod_pol; + } else { + if ((v1 = type_map_lookup_reverse(diff, rule->source, POLDIFF_POLICY_MOD)) == NULL || + (v2 = type_map_lookup_reverse(diff, rule->target, POLDIFF_POLICY_MOD)) == NULL || + (orig_default = type_map_get_name(diff, rule->default_type, POLDIFF_POLICY_ORIG)) == NULL) { + error = errno; + goto cleanup; + } + if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) { + form = POLDIFF_FORM_REMOVE_TYPE; + } + p = diff->orig_pol; + } + + pt = make_tediff(diff, form, rule); + if (pt == NULL) { + return -1; + } + pt->orig_default = orig_default; + pt->mod_default = mod_default; + + /* calculate line numbers */ + if (qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_LINE_NUMBERS)) { + apol_vector_t *vl = NULL; + if ((vl = apol_vector_create(NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) { + pt->mod_linenos = vl; + } else { + pt->orig_linenos = vl; + } + + /* copy rule pointers for delayed line number claculation */ + if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) { + pt->num_mod_rules = rule->num_rules; + pt->mod_rules = calloc(rule->num_rules, sizeof(qpol_terule_t *)); + if (!pt->mod_rules) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + memcpy(pt->mod_rules, rule->rules, rule->num_rules * sizeof(qpol_terule_t *)); + } else { + pt->num_orig_rules = rule->num_rules; + pt->orig_rules = calloc(rule->num_rules, sizeof(qpol_terule_t *)); + if (!pt->orig_rules) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + memcpy(pt->orig_rules, rule->rules, rule->num_rules * sizeof(qpol_terule_t *)); + } + } + + if (apol_vector_append(diff->terule_diffs[idx]->diffs, pt) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + switch (form) { + case POLDIFF_FORM_ADDED: + diff->terule_diffs[idx]->num_added++; + break; + case POLDIFF_FORM_ADD_TYPE: + diff->terule_diffs[idx]->num_added_type++; + break; + case POLDIFF_FORM_REMOVED: + diff->terule_diffs[idx]->num_removed++; + break; + case POLDIFF_FORM_REMOVE_TYPE: + diff->terule_diffs[idx]->num_removed_type++; + break; + default: + error = EBADRQC; /* should never get here */ + ERR(diff, "%s", strerror(error)); + assert(0); + goto cleanup; + } + diff->terule_diffs[idx]->diffs_sorted = 0; + retval = 0; + cleanup: + if (retval < 0) { + poldiff_terule_free(pt); + } + errno = error; + return retval; +} + +int terule_new_diff_change(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + return terule_new_diff(diff, form, item, TERULE_OFFSET_CHANGE); +} + +int terule_new_diff_member(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + return terule_new_diff(diff, form, item, TERULE_OFFSET_MEMBER); +} + +int terule_new_diff_trans(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + return terule_new_diff(diff, form, item, TERULE_OFFSET_TRANS); +} + +/** + * Compute the semantic difference of two pseudo-te rules for which + * the compare callback returns 0. If a difference is found then + * allocate, initialize, and insert a new semantic difference entry + * for that pseudo-te rule. + * + * @param diff The policy difference structure associated with both + * pseudo-te rules and to which to add an entry if needed. + * @param x The pseudo-te rule from the original policy. + * @param y The pseudo-te rule from the modified policy. + * @param idx Index into the terule differences specifying into which + * to place the constructed pseudo-ate rule. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ +static int terule_deep_diff(poldiff_t * diff, const void *x, const void *y, terule_offset_e idx) +{ + pseudo_terule_t *r1 = (pseudo_terule_t *) x; + pseudo_terule_t *r2 = (pseudo_terule_t *) y; + poldiff_terule_t *pt = NULL; + int retval = -1, error = 0; + + if (r1->default_type != r2->default_type) { + if ((pt = make_tediff(diff, POLDIFF_FORM_MODIFIED, r1)) == NULL) { + error = errno; + goto cleanup; + } + pt->orig_default = type_map_get_name(diff, r1->default_type, POLDIFF_POLICY_ORIG); + pt->mod_default = type_map_get_name(diff, r2->default_type, POLDIFF_POLICY_MOD); + + /* calculate line numbers */ + if (qpol_policy_has_capability(apol_policy_get_qpol(diff->orig_pol), QPOL_CAP_LINE_NUMBERS)) { + if ((pt->orig_linenos = apol_vector_create(NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + + /* copy rule pointers for delayed line number claculation */ + pt->num_orig_rules = r1->num_rules; + pt->orig_rules = calloc(r1->num_rules, sizeof(qpol_terule_t *)); + if (!pt->orig_rules) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + memcpy(pt->orig_rules, r1->rules, r1->num_rules * sizeof(qpol_terule_t *)); + } + if (qpol_policy_has_capability(apol_policy_get_qpol(diff->mod_pol), QPOL_CAP_LINE_NUMBERS)) { + if ((pt->mod_linenos = apol_vector_create(NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + + /* copy rule pointers for delayed line number claculation */ + pt->num_mod_rules = r2->num_rules; + pt->mod_rules = calloc(r2->num_rules, sizeof(qpol_terule_t *)); + if (!pt->mod_rules) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + memcpy(pt->mod_rules, r2->rules, r2->num_rules * sizeof(qpol_terule_t *)); + } + + if (apol_vector_append(diff->terule_diffs[idx]->diffs, pt) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + diff->terule_diffs[idx]->num_modified++; + diff->terule_diffs[idx]->diffs_sorted = 0; + } + retval = 0; + cleanup: + if (retval != 0) { + poldiff_terule_free(pt); + } + errno = error; + return retval; +} + +int terule_deep_diff_change(poldiff_t * diff, const void *x, const void *y) +{ + return terule_deep_diff(diff, x, y, TERULE_OFFSET_CHANGE); +} + +int terule_deep_diff_member(poldiff_t * diff, const void *x, const void *y) +{ + return terule_deep_diff(diff, x, y, TERULE_OFFSET_MEMBER); +} + +int terule_deep_diff_trans(poldiff_t * diff, const void *x, const void *y) +{ + return terule_deep_diff(diff, x, y, TERULE_OFFSET_TRANS); +} + +int terule_enable_line_numbers(poldiff_t * diff, terule_offset_e idx) +{ + const apol_vector_t *te = NULL; + poldiff_terule_t *terule = NULL; + size_t i, j; + qpol_iterator_t *iter = NULL; + qpol_syn_terule_t *ste = NULL; + int error = 0; + unsigned long lineno = 0; + + te = poldiff_get_terule_vector(diff, idx); + + for (i = 0; i < apol_vector_get_size(te); i++) { + terule = apol_vector_get_element(te, i); + if (apol_vector_get_size(terule->mod_linenos) || apol_vector_get_size(terule->orig_linenos)) + continue; + for (j = 0; j < terule->num_orig_rules; j++) { + if (qpol_terule_get_syn_terule_iter(diff->orig_qpol, terule->orig_rules[j], &iter)) { + error = errno; + goto err; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&ste) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + if (qpol_syn_terule_get_lineno(diff->orig_qpol, ste, &lineno) < 0) { + error = errno; + goto err; + } + if (apol_vector_append(terule->orig_linenos, (void *)lineno) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + } + qpol_iterator_destroy(&iter); + } + apol_vector_sort_uniquify(terule->orig_linenos, NULL, NULL); + for (j = 0; j < terule->num_mod_rules; j++) { + if (qpol_terule_get_syn_terule_iter(diff->mod_qpol, terule->mod_rules[j], &iter)) { + error = errno; + goto err; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&ste) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + if (qpol_syn_terule_get_lineno(diff->mod_qpol, ste, &lineno) < 0) { + error = errno; + goto err; + } + if (apol_vector_append(terule->mod_linenos, (void *)lineno) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto err; + } + } + qpol_iterator_destroy(&iter); + } + apol_vector_sort_uniquify(terule->mod_linenos, NULL, NULL); + } + + return 0; + err: + qpol_iterator_destroy(&iter); + return -1; +} diff --git a/libpoldiff/src/terule_internal.h b/libpoldiff/src/terule_internal.h new file mode 100644 index 0000000..79b4441 --- /dev/null +++ b/libpoldiff/src/terule_internal.h @@ -0,0 +1,244 @@ +/** + * @file + * Protected interface for rule differences, both AV and Type rules. + * + * @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 POLDIFF_TERULE_INTERNAL_H +#define POLDIFF_TERULE_INTERNAL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct poldiff_terule_summary poldiff_terule_summary_t; + +/** + * Allocate and return a new poldiff_terule_summary_t object, used by + * TE rule searches. + * + * @return A new rule summary. The caller must call terule_destroy() + * afterwards. On error, return NULL and set errno. + */ + poldiff_terule_summary_t *terule_create(void); + +/** + * Deallocate all space associated with a poldiff_terule_summary_t + * object, including the pointer itself. If the pointer is already + * NULL then do nothing. + * + * @param rs Reference to an rule summary to destroy. The pointer + * will be set to NULL afterwards. + */ + void terule_destroy(poldiff_terule_summary_t ** rs); + +/** + * Reset the state of TE rule type_change differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int terule_reset_change(poldiff_t * diff); + +/** + * Reset the state of TE rule type_member differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int terule_reset_member(poldiff_t * diff); + +/** + * Reset the state of TE rule type_transition differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int terule_reset_trans(poldiff_t * diff); + +/** + * Get a vector of type_change rules from the given policy, sorted. + * This function will remap source and target types to their + * pseudo-type value equivalents. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * + * @return A newly allocated vector of type_change rules (of type + * pseudo_terule_t). The caller is responsible for calling + * apol_vector_destroy() afterwards. On error, return NULL and set + * errno. + */ + apol_vector_t *terule_get_items_change(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Get a vector of type_member rules from the given policy, sorted. + * This function will remap source and target types to their + * pseudo-type value equivalents. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * @param flags Kind of rule to get, one of QPOL_RULE_TYPE_TRANS, etc. + * + * @return A newly allocated vector of type_member rules (of type + * pseudo_terule_t). The caller is responsible for calling + * apol_vector_destroy() afterwards. On error, return NULL and set + * errno. + */ + apol_vector_t *terule_get_items_member(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Get a vector of type_transition rules from the given policy, + * sorted. This function will remap source and target types to their + * pseudo-type value equivalents. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * + * @return A newly allocated vector of type_transition rules (of type + * pseudo_terule_t). The caller is responsible for calling + * apol_vector_destroy() afterwards. On error, return NULL and set + * errno. + */ + apol_vector_t *terule_get_items_trans(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Compare two pseudo_terule_t objects, determining if they have the + * same key (specified + source + target + class + conditional + * expression). + * + * @param x The pseudo-te rule from the original policy. + * @param y The pseudo-te rule from the modified policy. + * @param diff The policy difference structure associated with both + * policies. + * + * @return < 0, 0, or > 0 if te rule x is respectively less than, + * equal to, or greater than te rule y. + */ + int terule_comp(const void *x, const void *y, const poldiff_t * diff); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a pseudo-te rule that was originally from a type_change rule. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int terule_new_diff_change(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a pseudo-te rule that was originally from a type_member rule. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int terule_new_diff_member(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a pseudo-te rule that was originally from a type_transition rule. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int terule_new_diff_trans(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Compute the semantic difference of two pseudo-te rules (that were + * type_change rules) for which the compare callback returns 0. If a + * difference is found then allocate, initialize, and insert a new + * semantic difference entry for that pseudo-te rule. + * + * @param diff The policy difference structure associated with both + * pseudo-te rules and to which to add an entry if needed. + * @param x The pseudo-te rule from the original policy. + * @param y The pseudo-te rule from the modified policy. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int terule_deep_diff_change(poldiff_t * diff, const void *x, const void *y); + +/** + * Compute the semantic difference of two pseudo-te rules (that were + * type_member rules) for which the compare callback returns 0. If a + * difference is found then allocate, initialize, and insert a new + * semantic difference entry for that pseudo-te rule. + * + * @param diff The policy difference structure associated with both + * pseudo-te rules and to which to add an entry if needed. + * @param x The pseudo-te rule from the original policy. + * @param y The pseudo-te rule from the modified policy. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int terule_deep_diff_member(poldiff_t * diff, const void *x, const void *y); + +/** + * Compute the semantic difference of two pseudo-te rules (that were + * type_transition rules) for which the compare callback returns 0. + * If a difference is found then allocate, initialize, and insert a + * new semantic difference entry for that pseudo-te rule. + * + * @param diff The policy difference structure associated with both + * pseudo-te rules and to which to add an entry if needed. + * @param x The pseudo-te rule from the original policy. + * @param y The pseudo-te rule from the modified policy. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int terule_deep_diff_trans(poldiff_t * diff, const void *x, const void *y); + +/** + * Iterate through a TE rule differences, filling in its line numbers. + * + * @param diff Diff structure containing avrule differences. + * @param idx Index into the terule differences specifying which line + * number table to enable. + * + * @return 0 on success, < 0 on errno. + */ + int terule_enable_line_numbers(poldiff_t * diff, unsigned int idx); + +#ifdef __cplusplus +} +#endif + +#endif /* POLDIFF_TERULE_INTERNAL_H */ diff --git a/libpoldiff/src/type_diff.c b/libpoldiff/src/type_diff.c new file mode 100644 index 0000000..32c7a17 --- /dev/null +++ b/libpoldiff/src/type_diff.c @@ -0,0 +1,662 @@ +/** + * @file + * Implementation for computing a semantic differences in types. + * + * @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 "poldiff_internal.h" + +#include <apol/util.h> +#include <assert.h> +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +/******************** types ********************/ + +struct poldiff_type_summary +{ + size_t num_added; + size_t num_removed; + size_t num_modified; + int are_diffs_sorted; + apol_vector_t *diffs; +}; + +struct poldiff_type +{ + char *name; + poldiff_form_e form; + apol_vector_t *added_attribs; + apol_vector_t *removed_attribs; +}; + +void poldiff_type_get_stats(const poldiff_t * diff, size_t stats[5]) +{ + if (diff == NULL || stats == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return; + } + stats[0] = diff->type_diffs->num_added; + stats[1] = diff->type_diffs->num_removed; + stats[2] = diff->type_diffs->num_modified; + stats[3] = 0; + stats[4] = 0; +} + +char *poldiff_type_to_string(const poldiff_t * diff, const void *type) +{ + poldiff_type_t *t = (poldiff_type_t *) type; + size_t num_added, num_removed, len = 0, i; + char *s = NULL, *attrib; + + if (diff == NULL || type == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + num_added = apol_vector_get_size(t->added_attribs); + num_removed = apol_vector_get_size(t->removed_attribs); + switch (t->form) { + case POLDIFF_FORM_ADDED: + { + if (apol_str_appendf(&s, &len, "+ %s", t->name) < 0) { + break; + } + return s; + } + case POLDIFF_FORM_REMOVED: + { + if (apol_str_appendf(&s, &len, "- %s", t->name) < 0) { + break; + } + return s; + } + case POLDIFF_FORM_MODIFIED: + { + if (apol_str_appendf(&s, &len, "* %s (", t->name) < 0) { + break; + } + if (num_added > 0) { + if (apol_str_appendf(&s, &len, "%zd Added Attribute%s", num_added, (num_added == 1 ? "" : "s")) < 0) { + break; + } + } + if (num_removed > 0) { + if (apol_str_appendf + (&s, &len, "%s%zd Removed Attribute%s", (num_added > 0 ? ", " : ""), num_removed, + (num_removed == 1 ? "" : "s")) < 0) { + break; + } + } + if (apol_str_append(&s, &len, ")\n") < 0) { + break; + } + for (i = 0; i < apol_vector_get_size(t->added_attribs); i++) { + attrib = (char *)apol_vector_get_element(t->added_attribs, i); + if (apol_str_appendf(&s, &len, "\t+ %s\n", attrib) < 0) { + goto err; + } + } + for (i = 0; i < apol_vector_get_size(t->removed_attribs); i++) { + attrib = (char *)apol_vector_get_element(t->removed_attribs, i); + if (apol_str_appendf(&s, &len, "\t- %s\n", attrib) < 0) { + goto err; + } + } + return s; + } + default: + { + ERR(diff, "%s", strerror(ENOTSUP)); + errno = ENOTSUP; + return NULL; + } + } + err: + /* if this is reached then an error occurred */ + free(s); + ERR(diff, "%s", strerror(ENOMEM)); + errno = ENOMEM; + return NULL; +} + +static int poldiff_type_comp(const void *a, const void *b, void *data __attribute__ ((unused))) +{ + const poldiff_type_t *t1 = a; + const poldiff_type_t *t2 = b; + return strcmp(t1->name, t2->name); +} + +const apol_vector_t *poldiff_get_type_vector(const poldiff_t * diff) +{ + if (diff == NULL) { + errno = EINVAL; + return NULL; + } + /* the elements of the results vector are not sorted by name, + * but by pseudo-type value. thus sort them by name as + * necessary */ + if (!diff->type_diffs->are_diffs_sorted) { + apol_vector_sort(diff->type_diffs->diffs, poldiff_type_comp, NULL); + diff->type_diffs->are_diffs_sorted = 1; + } + return diff->type_diffs->diffs; +} + +const char *poldiff_type_get_name(const poldiff_type_t * type) +{ + if (type == NULL) { + errno = EINVAL; + return NULL; + } + return type->name; +} + +poldiff_form_e poldiff_type_get_form(const void *type) +{ + if (type == NULL) { + errno = EINVAL; + return POLDIFF_FORM_NONE; + } + return ((const poldiff_type_t *)type)->form; +} + +const apol_vector_t *poldiff_type_get_added_attribs(const poldiff_type_t * type) +{ + if (type == NULL) { + errno = EINVAL; + return NULL; + } + return type->added_attribs; +} + +const apol_vector_t *poldiff_type_get_removed_attribs(const poldiff_type_t * type) +{ + if (type == NULL) { + errno = EINVAL; + return NULL; + } + return type->removed_attribs; +} + +/*************** protected functions for types ***************/ + +/** + * Destroy a specified type + * @param type The type to destroy (a poldiff_type_t object) + */ +static void type_destroy(void *type) +{ + poldiff_type_t *t; + if (type == NULL) + return; + t = (poldiff_type_t *) type; + free(t->name); + apol_vector_destroy(&(t->added_attribs)); + apol_vector_destroy(&(t->removed_attribs)); + free(t); +} + +poldiff_type_summary_t *type_summary_create(void) +{ + poldiff_type_summary_t *type = calloc(1, sizeof(*type)); + if (type == NULL) { + return NULL; + } + if ((type->diffs = apol_vector_create(type_destroy)) == NULL) { + type_summary_destroy(&type); + return NULL; + } + return type; +} + +void type_summary_destroy(poldiff_type_summary_t ** type) +{ + if (type != NULL && *type != NULL) { + apol_vector_destroy(&(*type)->diffs); + free(*type); + *type = NULL; + } +} + +apol_vector_t *type_get_items(poldiff_t * diff, const apol_policy_t * policy) +{ + qpol_iterator_t *iter = NULL; + apol_vector_t *v = NULL; + int error = 0; + qpol_type_t *t; + unsigned char isattr, isalias; + qpol_policy_t *q = apol_policy_get_qpol(policy); + uint32_t val; + + if (diff == NULL || policy == NULL) { + error = errno = EINVAL; + ERR(diff, "%s", strerror(error)); + errno = error; + return NULL; + } + if (qpol_policy_get_type_iter(q, &iter) < 0) { + return NULL; + } + v = apol_vector_create(NULL); + if (v == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + qpol_iterator_destroy(&iter); + errno = error; + return NULL; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_iterator_get_item(iter, (void **)&t); + qpol_type_get_isalias(q, t, &isalias); + qpol_type_get_isattr(q, t, &isattr); + if (isattr || isalias) + continue; + val = type_map_lookup(diff, t, policy == diff->orig_pol ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD); + apol_vector_append(v, (void *)((size_t) val)); + } + qpol_iterator_destroy(&iter); + apol_vector_sort_uniquify(v, NULL, NULL); + return v; +} + +int type_reset(poldiff_t * diff) +{ + int error = 0; + + if (diff == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + type_summary_destroy(&diff->type_diffs); + diff->type_diffs = type_summary_create(); + if (diff->type_diffs == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + errno = error; + return -1; + } + + return 0; +} + +/** + * Compare two type map values + * @param x The first type to compare, a (uint32_t) value + * @param y The second type to compare, a (uint32_t) value + * @param diff The policy difference structure + * + * @return < 0, 0, or > 0, if x is respectively less than + * equal to, or greater than y. + */ +int type_comp(const void *x, const void *y, const poldiff_t * diff __attribute__ ((unused))) +{ + uint32_t p1val = (uint32_t) ((size_t) x); + uint32_t p2val = (uint32_t) ((size_t) y); + + /* p1val == p2val means the types are semantically equivalent */ + return p1val - p2val; +} + +/** + * Allocate and return a new type difference object. + * + * @param diff Policy diff error handler. + * @param form Form of the difference. + * @param name Name of the type that is different. + * + * @return A newly allocated and initialized diff, or NULL upon error. + * The caller is responsible for calling type_destroy() upon the + * returned value. + */ +static poldiff_type_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name) +{ + poldiff_type_t *pt; + int error; + + if ((pt = calloc(1, sizeof(poldiff_type_t))) == NULL || + (pt->name = strdup(name)) == NULL || + (pt->added_attribs = apol_vector_create_with_capacity(1, free)) == NULL || + (pt->removed_attribs = apol_vector_create_with_capacity(1, free)) == NULL) { + error = errno; + type_destroy(pt); + ERR(diff, "%s", strerror(error)); + errno = error; + return NULL; + } + pt->form = form; + return pt; +} + +/** + * Allocate and return a string representing the type that is + * different. If the type was not remapped then it is simply that + * type's name within the policy. Otherwise the string will be of the + * form "orig_name1, orig_name2, ... -> mod_name1, mod_name2, ..." + * + * @param diff Diff structure containing type remaps. + * @param tval Pseudo-type value whose name to get. + * + * @return Allocated string name for the type, or NULL upon error. + * The caller must call free() upon the returned value. + */ +static char *type_get_name(const poldiff_t * diff, uint32_t tval) +{ + const apol_vector_t *v1, *v2; + size_t sv1, sv2; + size_t i, len = 0; + const qpol_type_t *qtype; + const char *name = NULL; + char *ret = NULL; + int error = 0; + + /* names mapped from the first policy */ + v1 = type_map_lookup_reverse(diff, tval, POLDIFF_POLICY_ORIG); + sv1 = apol_vector_get_size(v1); + /* names mapped from the second policy */ + v2 = type_map_lookup_reverse(diff, tval, POLDIFF_POLICY_MOD); + sv2 = apol_vector_get_size(v2); + + if (sv1 == 1 && sv2 == 0) { + /* return the name in v1 */ + qtype = apol_vector_get_element(v1, 0); + if (qpol_type_get_name(diff->orig_qpol, qtype, &name) < 0 || (ret = strdup(name)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } else if (sv1 == 0 && sv2 == 1) { + /* return the name in v2 */ + qtype = apol_vector_get_element(v2, 0); + if (qpol_type_get_name(diff->mod_qpol, qtype, &name) < 0 || (ret = strdup(name)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } else { + /* if the single name in v1 and v2 is the same return that name */ + if (sv1 == sv2 && sv2 == 1) { + const char *name2; + qpol_type_t *qtype2; + qtype = apol_vector_get_element(v1, 0); + qtype2 = apol_vector_get_element(v2, 0); + if (qpol_type_get_name(diff->orig_qpol, qtype, &name) < 0 || + qpol_type_get_name(diff->mod_qpol, qtype2, &name2) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + if (strcmp(name, name2) == 0) { + if ((ret = strdup(name)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + } + goto cleanup; + } + } + /* build and return the composite name */ + for (i = 0; i < sv1; i++) { + qtype = apol_vector_get_element(v1, i); + if (qpol_type_get_name(diff->orig_qpol, qtype, &name) < 0 || + apol_str_appendf(&ret, &len, "%s%s", (i > 0 ? ", " : ""), name) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + apol_str_append(&ret, &len, " -> "); + for (i = 0; i < sv2; i++) { + qtype = apol_vector_get_element(v2, i); + if (qpol_type_get_name(diff->mod_qpol, qtype, &name) < 0 || + apol_str_appendf(&ret, &len, "%s%s", (i > 0 ? ", " : ""), name) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + } + cleanup: + if (error != 0) { + free(ret); + errno = error; + return NULL; + } + return ret; +} + +int type_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + uint32_t tval = (uint32_t) ((size_t) item); + char *name = NULL; + poldiff_type_t *pt; + int error; + + if ((name = type_get_name(diff, tval)) == NULL || (pt = make_diff(diff, form, name)) == NULL) { + error = errno; + free(name); + errno = error; + return -1; + } + free(name); + if (apol_vector_append(diff->type_diffs->diffs, pt) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + type_destroy(pt); + errno = error; + return -1; + } + diff->type_diffs->are_diffs_sorted = 0; + if (form == POLDIFF_FORM_ADDED) { + diff->type_diffs->num_added++; + } else { + diff->type_diffs->num_removed++; + } + return 0; +} + +/** + * Given an type, return a vector of its attributes (in the form of + * strings). + * + * @param diff Policy diff error handler. + * @param p Policy from which the type came. + * @param type Type whose attributes to get. + * + * @return Vector of attribute strings for the type. The caller is + * responsible for calling apol_vector_destroy(). On error, return + * NULL. + */ +static apol_vector_t *type_get_attrib_names(const poldiff_t * diff, const apol_policy_t * p, uint32_t type) +{ + qpol_iterator_t *attrib_iter = NULL; + const char *attrib; + char *new_attrib; + const apol_vector_t *v = NULL; + apol_vector_t *ret = NULL; + qpol_type_t *qt = NULL; + qpol_policy_t *q = apol_policy_get_qpol(p); + int retval = -1; + size_t i; + + if ((ret = apol_vector_create(free)) == NULL) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + + /* get the qpol_type_t objects for the specified type value + * and policy */ + v = type_map_lookup_reverse(diff, type, (diff->orig_pol == p ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD)); + if (apol_vector_get_size(v) == 0) { + assert(false); + return NULL; + } + /* append the attributes for each qpol_type_t to the vector we return */ + for (i = 0; i < apol_vector_get_size(v); i++) { + qt = apol_vector_get_element(v, i); + if (qt == NULL) { + assert(false); + return NULL; + } + qpol_type_get_attr_iter(q, qt, &attrib_iter); + for (; !qpol_iterator_end(attrib_iter); qpol_iterator_next(attrib_iter)) { + + if (qpol_iterator_get_item(attrib_iter, (void **)&qt) < 0) { + goto cleanup; + } + qpol_type_get_name(q, qt, &attrib); + if ((new_attrib = strdup(attrib)) == NULL || apol_vector_append(ret, new_attrib) < 0) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + } + } + apol_vector_sort_uniquify(ret, &apol_str_strcmp, NULL); + retval = 0; + cleanup: + qpol_iterator_destroy(&attrib_iter); + if (retval < 0) { + apol_vector_destroy(&ret); + return NULL; + } + return ret; +} + +int type_deep_diff(poldiff_t * diff, const void *x, const void *y) +{ + uint32_t tval1 = (uint32_t) ((size_t) x); + uint32_t tval2 = (uint32_t) ((size_t) y); + apol_vector_t *v1 = NULL, *v2 = NULL; + char *attrib1 = NULL, *attrib2 = NULL, *name = NULL; + poldiff_type_t *t = NULL; + size_t i, j; + int retval = -1, error = 0, compval; + + assert(tval1 == tval2); + /* can't do a deep diff of type if either policy does not retain attribute + * names because the fake attribute names are bogus */ + if (!(qpol_policy_has_capability(apol_policy_get_qpol(diff->orig_pol), QPOL_CAP_ATTRIB_NAMES)) + || !(qpol_policy_has_capability(apol_policy_get_qpol(diff->mod_pol), QPOL_CAP_ATTRIB_NAMES))) { + return 0; + } + v1 = type_get_attrib_names(diff, diff->orig_pol, tval1); + v2 = type_get_attrib_names(diff, diff->mod_pol, tval2); + apol_vector_sort(v1, apol_str_strcmp, NULL); + apol_vector_sort(v2, apol_str_strcmp, NULL); + for (i = j = 0; i < apol_vector_get_size(v1);) { + if (j >= apol_vector_get_size(v2)) + break; + attrib1 = (char *)apol_vector_get_element(v1, i); + attrib2 = (char *)apol_vector_get_element(v2, j); + compval = strcmp(attrib1, attrib2); + if (compval != 0 && t == NULL) { + name = type_get_name(diff, tval1); + if ((t = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) { + error = errno; + goto cleanup; + } + free(name); + name = NULL; + } + if (compval < 0) { + if ((attrib1 = strdup(attrib1)) == NULL || apol_vector_append(t->removed_attribs, attrib1) < 0) { + error = errno; + free(attrib1); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + i++; + } else if (compval > 0) { + if ((attrib2 = strdup(attrib2)) == NULL || apol_vector_append(t->added_attribs, attrib2) < 0) { + error = errno; + free(attrib2); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + j++; + } else { + i++; + j++; + } + } + for (; i < apol_vector_get_size(v1); i++) { + attrib1 = (char *)apol_vector_get_element(v1, i); + if (t == NULL) { + name = type_get_name(diff, tval1); + if ((t = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) { + error = errno; + goto cleanup; + } + free(name); + name = NULL; + } + if ((attrib1 = strdup(attrib1)) == NULL || apol_vector_append(t->removed_attribs, attrib1) < 0) { + error = errno; + free(attrib1); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + for (; j < apol_vector_get_size(v2); j++) { + attrib2 = (char *)apol_vector_get_element(v2, j); + if (t == NULL) { + name = type_get_name(diff, tval1); + if ((t = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) { + error = errno; + goto cleanup; + } + free(name); + name = NULL; + } + if ((attrib2 = strdup(attrib2)) == NULL || apol_vector_append(t->added_attribs, attrib2) < 0) { + error = errno; + free(attrib2); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + if (t != NULL) { + if (apol_vector_append(diff->type_diffs->diffs, t) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + diff->type_diffs->are_diffs_sorted = 0; + diff->type_diffs->num_modified++; + } + retval = 0; + cleanup: + apol_vector_destroy(&v1); + apol_vector_destroy(&v2); + free(name); + if (retval != 0) { + type_destroy(t); + } + errno = error; + return retval; + return 0; +} diff --git a/libpoldiff/src/type_internal.h b/libpoldiff/src/type_internal.h new file mode 100644 index 0000000..8c9cb44 --- /dev/null +++ b/libpoldiff/src/type_internal.h @@ -0,0 +1,125 @@ +/** + * @file + * Protected Interface for type differences. + * + * @author Kevin Carr kcarr@tresys.com + * @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 POLDIFF_TYPE_INTERNAL_H +#define POLDIFF_TYPE_INTERNAL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************** types ********************/ + + typedef struct poldiff_type_summary poldiff_type_summary_t; + +/** + * Allocate and return a new poldiff_type_summary_t object. + * + * @return A new type summary. The caller must call type_destroy() + * afterwards. On error, return NULL and set errno. + */ + poldiff_type_summary_t *type_summary_create(void); + +/** + * Deallocate all space associated with a poldiff_type_summary_t + * object, including the pointer itself. If the pointer is already + * NULL then do nothing. + * + * @param type Reference to a type summary to destroy. The pointer + * will be set to NULL afterwards. + */ + void type_summary_destroy(poldiff_type_summary_t ** type); + +/** + * Reset the state of all type differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int type_reset(poldiff_t * diff); + +/** + * Get a vector of all type (type qpol_type_t) from the + * given policy, sorted by name. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * + * @return a newly allocated vector of all typees. The caller is + * responsible for calling apol_vector_destroy() afterwards. On + * error, return NULL and set errno. + */ + apol_vector_t *type_get_items(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Compare two qpol_type_t objects, determining if they have the same + * name or not. + * + * @param x The type from the original policy. + * @param y The type from the modified policy. + * @param diff The policy difference structure associated with both + * policies. + * + * @return < 0, 0, or > 0 if type x is respectively less than, equal + * to, or greater than type y. + */ + int type_comp(const void *x, const void *y, const poldiff_t * diff); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a type. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int type_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Compute the semantic difference of two types for which the + * compare callback returns 0. If a difference is found then + * allocate, initialize, and insert a new semantic difference entry + * for that type. + * + * @param diff The policy difference structure associated with both + * types and to which to add an entry if needed. + * @param x The type from the original policy. + * @param y The type from the modified policy. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int type_deep_diff(poldiff_t * diff, const void *x, const void *y); + +#ifdef __cplusplus +} +#endif + +#endif /* POLDIFF_TYPE_INTERNAL_H */ diff --git a/libpoldiff/src/type_map.c b/libpoldiff/src/type_map.c new file mode 100644 index 0000000..ff97c43 --- /dev/null +++ b/libpoldiff/src/type_map.c @@ -0,0 +1,985 @@ +/** + * @file + * Implementation of type equivalence mapping for semantic + * difference calculations. + * The mapping of types is handled by creating a list of pseudo type + * values to represent the set of all semantically unique types in + * both the original and modified policies. This mapping takes into + * account both inferred and user specified mappings of types and may + * contain holes where a type does not exist in one of the 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 "poldiff_internal.h" + +#include <apol/policy-query.h> +#include <apol/util.h> +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +/** + * A poldiff's type map consists of maps between policies' types to a + * unified pseudo-type value. + */ +struct type_map +{ + /** array of size num_orig_types mapping types by (value - 1) + to pseudo value */ + uint32_t *orig_to_pseudo; + /** array of size num_mod_types mapping types by (value - 1) + to pseudo value */ + uint32_t *mod_to_pseudo; + /** vector of vector of qpol_type_t that reverse map pseudo + value to orig_pol value(s) */ + apol_vector_t *pseudo_to_orig; + /** vector of vector of qpol_type_t that reverse map pseudo + value to mod_pol value(s) */ + apol_vector_t *pseudo_to_mod; + size_t num_orig_types; + size_t num_mod_types; + /** vector of poldiff_type_remap_entry_t */ + apol_vector_t *remap; +}; + +/** + * Each map entry consists of 2 vectors, each vector being a list of + * qpol_type_t. + */ +struct poldiff_type_remap_entry +{ + /** vector of names of qpol_type_t in original qpolicy */ + apol_vector_t *orig_types; + /** vector of names of qpol_type_t in the modified qpolicy */ + apol_vector_t *mod_types; + int inferred; + int enabled; +}; + +/** + * Free the space associated with a singly type remap entry. + * + * @param elem Pointer to a type remap entry to free. If NULL then do + * nothing. + */ +static void type_remap_entry_free(void *elem) +{ + poldiff_type_remap_entry_t *entry = (poldiff_type_remap_entry_t *) elem; + if (entry != NULL) { + apol_vector_destroy(&entry->orig_types); + apol_vector_destroy(&entry->mod_types); + free(entry); + } +} + +/** + * Allocate a new poldiff type remap entry, append it to the current + * type remap vector, enable it, and return the entry. + * + * @param diff Policy diff structure containing remap vector. + * + * @return a new entry, or NULL on error. + */ +static poldiff_type_remap_entry_t *poldiff_type_remap_entry_create(poldiff_t * diff) +{ + poldiff_type_remap_entry_t *e = NULL; + if ((e = calloc(1, sizeof(*e))) == NULL || + (e->orig_types = apol_vector_create_with_capacity(1, free)) == NULL || + (e->mod_types = apol_vector_create_with_capacity(1, free)) == NULL || apol_vector_append(diff->type_map->remap, e) < 0) + { + type_remap_entry_free(e); + return NULL; + } + diff->remapped = 1; + e->enabled = 1; + return e; +} + +int poldiff_type_remap_create(poldiff_t * diff, const apol_vector_t * orig_names, const apol_vector_t * mod_names) +{ + poldiff_type_remap_entry_t *entry = NULL; + size_t i; + char *name; + const qpol_type_t *type; + unsigned char isalias, isattr; + int retval = -1, error = 0; + if (diff == NULL || orig_names == NULL || mod_names == NULL) { + error = EINVAL; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + if (apol_vector_get_size(orig_names) == 0 || + apol_vector_get_size(mod_names) == 0 || (apol_vector_get_size(orig_names) > 1 && apol_vector_get_size(mod_names) > 1)) { + error = EINVAL; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + if ((entry = calloc(1, sizeof(*entry))) == NULL || + (entry->orig_types = apol_vector_create_with_capacity(1, free)) == NULL || + (entry->mod_types = apol_vector_create_with_capacity(1, free)) == NULL) { + error = ENOMEM; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(orig_names); i++) { + name = (char *)apol_vector_get_element(orig_names, i); + if (qpol_policy_get_type_by_name(diff->orig_qpol, name, &type) < 0 || + qpol_type_get_isalias(diff->orig_qpol, type, &isalias) < 0 || + qpol_type_get_isattr(diff->orig_qpol, type, &isattr) < 0) { + error = errno; + goto cleanup; + } + if (isalias || isattr) { + error = EINVAL; + ERR(diff, "%s is not a primary type.", name); + goto cleanup; + } + if ((name = strdup(name)) == NULL || apol_vector_append(entry->orig_types, (void *)name) < 0) { + error = ENOMEM; + free(name); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + apol_vector_sort_uniquify(entry->orig_types, apol_str_strcmp, NULL); + for (i = 0; i < apol_vector_get_size(mod_names); i++) { + name = (char *)apol_vector_get_element(mod_names, i); + if (qpol_policy_get_type_by_name(diff->mod_qpol, name, &type) < 0 || + qpol_type_get_isalias(diff->mod_qpol, type, &isalias) < 0 || + qpol_type_get_isattr(diff->mod_qpol, type, &isattr) < 0) { + error = errno; + goto cleanup; + } + if (isalias || isattr) { + error = EINVAL; + ERR(diff, "%s is not a primary type.", name); + goto cleanup; + } + if ((name = strdup(name)) == NULL || apol_vector_append(entry->mod_types, (void *)name) < 0) { + error = ENOMEM; + free(name); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + apol_vector_sort_uniquify(entry->mod_types, apol_str_strcmp, NULL); + entry->enabled = 1; + if (apol_vector_append(diff->type_map->remap, entry) < 0) { + error = ENOMEM; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + retval = 0; + diff->remapped = 1; + cleanup: + if (retval < 0) { + type_remap_entry_free(entry); + } + errno = error; + return retval; +} + +apol_vector_t *poldiff_type_remap_get_entries(const poldiff_t * diff) +{ + if (diff == NULL || diff->type_map == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + return diff->type_map->remap; +} + +void poldiff_type_remap_entry_remove(poldiff_t * diff, poldiff_type_remap_entry_t * entry) +{ + size_t idx; + if (diff == NULL || entry == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return; + } + if (apol_vector_get_index(diff->type_map->remap, entry, NULL, NULL, &idx) < 0) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return; + } + apol_vector_remove(diff->type_map->remap, idx); + diff->remapped = 1; +} + +apol_vector_t *poldiff_type_remap_entry_get_original_types(const poldiff_t * diff, const poldiff_type_remap_entry_t * entry) +{ + if (diff == NULL || entry == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + return apol_vector_create_from_vector(entry->orig_types, NULL, NULL, NULL); +} + +apol_vector_t *poldiff_type_remap_entry_get_modified_types(const poldiff_t * diff, const poldiff_type_remap_entry_t * entry) +{ + if (diff == NULL || entry == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + return apol_vector_create_from_vector(entry->mod_types, NULL, NULL, NULL); +} + +int poldiff_type_remap_entry_get_is_inferred(const poldiff_type_remap_entry_t * entry) +{ + if (entry == NULL) { + errno = EINVAL; + return -1; + } + return entry->inferred; +} + +int poldiff_type_remap_entry_get_is_enabled(const poldiff_type_remap_entry_t * entry) +{ + if (entry == NULL) { + errno = EINVAL; + return -1; + } + return entry->enabled; +} + +void poldiff_type_remap_entry_set_enabled(poldiff_type_remap_entry_t * entry, int enabled) +{ + if (entry == NULL) { + errno = EINVAL; + return; + } + if (enabled) { + entry->enabled = 1; + } else { + entry->enabled = 0; + } +} + +type_map_t *type_map_create(void) +{ + type_map_t *map = calloc(1, sizeof(*map)); + if (map == NULL) { + return NULL; + } + if ((map->remap = apol_vector_create(type_remap_entry_free)) == NULL) { + type_map_destroy(&map); + return NULL; + } + return map; +} + +void type_map_destroy(type_map_t ** map) +{ + if (map != NULL && *map != NULL) { + free((*map)->orig_to_pseudo); + free((*map)->mod_to_pseudo); + apol_vector_destroy(&(*map)->pseudo_to_orig); + apol_vector_destroy(&(*map)->pseudo_to_mod); + apol_vector_destroy(&(*map)->remap); + free(*map); + *map = NULL; + } +} + +/** + * If --enable-debug is given, then dump to stdout the type map from + * policy's types -> pseudo-types. + */ +static void type_map_dump(poldiff_t * diff) +{ +#ifdef SETOOLS_DEBUG + size_t i, j; + apol_vector_t *v; + const qpol_type_t *t; + const char *name; + printf("# type map debug dump (qpol_type_t -> pseudo-type):\norig:\n"); + for (i = 0; i < diff->type_map->num_orig_types; i++) { + printf("%3zd:%5d", i, diff->type_map->orig_to_pseudo[i]); + if ((i + 1) % 5 == 0) { + printf("\n"); + } else { + printf("\t"); + } + } + for (i = 0; i < apol_vector_get_size(diff->type_map->pseudo_to_orig); i++) { + v = apol_vector_get_element(diff->type_map->pseudo_to_orig, i); + printf("\n%3zd->", i); + for (j = 0; j < apol_vector_get_size(v); j++) { + t = apol_vector_get_element(v, j); + qpol_type_get_name(diff->orig_qpol, t, &name); + printf(" %s", name); + } + } + printf("\nmod:\n"); + for (i = 0; i < diff->type_map->num_mod_types; i++) { + printf("%3zd:%5d", i, diff->type_map->mod_to_pseudo[i]); + if ((i + 1) % 5 == 0) { + printf("\n"); + } else { + printf("\t"); + } + } + for (i = 0; i < apol_vector_get_size(diff->type_map->pseudo_to_mod); i++) { + v = apol_vector_get_element(diff->type_map->pseudo_to_mod, i); + printf("\n%3zd->", i); + for (j = 0; j < apol_vector_get_size(v); j++) { + t = apol_vector_get_element(v, j); + qpol_type_get_name(diff->mod_qpol, t, &name); + printf(" %s", name); + } + } + printf("\n"); +#endif +} + +/** + * Free a vector of qpol_type_t pointers. + */ +static void type_map_vector_free(void *elem) +{ + apol_vector_t *v = (apol_vector_t *) elem; + if (v != NULL) { + apol_vector_destroy(&v); + } +} + +int type_map_build(poldiff_t * diff) +{ + type_map_t *map; + apol_vector_t *ov = NULL, *mv = NULL; + int retval = -1, error = 0; + size_t i, j; + const qpol_type_t *t; + uint32_t val, max_val, next_val; + apol_vector_t *reverse_v = NULL; + + map = diff->type_map; + free(map->orig_to_pseudo); + map->orig_to_pseudo = NULL; + map->num_orig_types = 0; + free(map->mod_to_pseudo); + map->mod_to_pseudo = NULL; + map->num_mod_types = 0; + apol_vector_destroy(&map->pseudo_to_orig); + apol_vector_destroy(&map->pseudo_to_mod); + + if (apol_type_get_by_query(diff->orig_pol, NULL, &ov) < 0 || apol_type_get_by_query(diff->mod_pol, NULL, &mv) < 0) { + error = errno; + goto cleanup; + } + + /* there is no guarantee that the number of types is equal to + * the highest type value (because a policy could have + * attributes), so calculate them here */ + max_val = 0; + for (i = 0; i < apol_vector_get_size(ov); i++) { + t = (qpol_type_t *) apol_vector_get_element(ov, i); + if (qpol_type_get_value(diff->orig_qpol, t, &val) < 0) { + error = errno; + goto cleanup; + } + if (val > max_val) { + max_val = val; + } + } + if ((map->orig_to_pseudo = calloc(max_val, sizeof(*(map->orig_to_pseudo)))) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + map->num_orig_types = max_val; + max_val = 0; + for (i = 0; i < apol_vector_get_size(mv); i++) { + t = (qpol_type_t *) apol_vector_get_element(mv, i); + if (qpol_type_get_value(diff->mod_qpol, t, &val) < 0) { + error = errno; + goto cleanup; + } + if (val > max_val) { + max_val = val; + } + } + if ((map->mod_to_pseudo = calloc(max_val, sizeof(*(map->mod_to_pseudo)))) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + map->num_mod_types = max_val; + + if ((map->pseudo_to_orig = apol_vector_create(type_map_vector_free)) == NULL + || (map->pseudo_to_mod = apol_vector_create(type_map_vector_free)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + + next_val = 1; + for (i = 0; i < apol_vector_get_size(map->remap); i++) { + poldiff_type_remap_entry_t *e; + const char *name; + e = (poldiff_type_remap_entry_t *) apol_vector_get_element(map->remap, i); + if (!e->enabled) { + continue; + } + + if ((reverse_v = apol_vector_create_with_capacity(1, NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + for (j = 0; j < apol_vector_get_size(e->orig_types); j++) { + name = (const char *)apol_vector_get_element(e->orig_types, j); + if (qpol_policy_get_type_by_name(diff->orig_qpol, name, &t) < 0 || + qpol_type_get_value(diff->orig_qpol, t, &val) < 0) { + error = errno; + goto cleanup; + } + if (map->orig_to_pseudo[val - 1] != 0) { + error = EINVAL; + ERR(diff, "Type %s is already remapped.", name); + goto cleanup; + } + map->orig_to_pseudo[val - 1] = next_val; + if (apol_vector_append(reverse_v, (void *)t) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + if (apol_vector_append(map->pseudo_to_orig, reverse_v) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + reverse_v = NULL; + + if ((reverse_v = apol_vector_create_with_capacity(1, NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + for (j = 0; j < apol_vector_get_size(e->mod_types); j++) { + name = (const char *)apol_vector_get_element(e->mod_types, j); + if (qpol_policy_get_type_by_name(diff->mod_qpol, name, &t) < 0 || + qpol_type_get_value(diff->mod_qpol, t, &val) < 0) { + error = errno; + goto cleanup; + } + if (map->mod_to_pseudo[val - 1] != 0) { + error = EINVAL; + ERR(diff, "Type %s is already remapped.", name); + goto cleanup; + } + map->mod_to_pseudo[val - 1] = next_val; + if (apol_vector_append(reverse_v, (void *)t) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + if (apol_vector_append(map->pseudo_to_mod, reverse_v) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + reverse_v = NULL; + + next_val++; + } + + /* all remaining types (both from orig and mod) get their own + * values */ + for (i = 0; i < apol_vector_get_size(ov); i++) { + t = apol_vector_get_element(ov, i); + if (qpol_type_get_value(diff->orig_qpol, t, &val) < 0) { + error = errno; + goto cleanup; + } + if (map->orig_to_pseudo[val - 1] == 0) { + map->orig_to_pseudo[val - 1] = next_val; + if ((reverse_v = apol_vector_create_with_capacity(1, NULL)) == NULL || + apol_vector_append(reverse_v, (void *)t) < 0 || + apol_vector_append(map->pseudo_to_orig, reverse_v) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + reverse_v = NULL; + if ((reverse_v = apol_vector_create_with_capacity(1, NULL)) == NULL || + apol_vector_append(map->pseudo_to_mod, reverse_v) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + reverse_v = NULL; + next_val++; + } + } + for (i = 0; i < apol_vector_get_size(mv); i++) { + t = apol_vector_get_element(mv, i); + if (qpol_type_get_value(diff->mod_qpol, t, &val) < 0) { + error = errno; + goto cleanup; + } + if (map->mod_to_pseudo[val - 1] == 0) { + map->mod_to_pseudo[val - 1] = next_val; + if ((reverse_v = apol_vector_create_with_capacity(1, NULL)) == NULL || + apol_vector_append(reverse_v, (void *)t) < 0 || apol_vector_append(map->pseudo_to_mod, reverse_v) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + reverse_v = NULL; + if ((reverse_v = apol_vector_create_with_capacity(1, NULL)) == NULL || + apol_vector_append(map->pseudo_to_orig, reverse_v) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + reverse_v = NULL; + next_val++; + } + } + + type_map_dump(diff); + + retval = 0; + cleanup: + apol_vector_destroy(&ov); + apol_vector_destroy(&mv); + apol_vector_destroy(&reverse_v); + error = errno; + return retval; +} + +void poldiff_type_remap_flush(poldiff_t * diff) +{ + if (diff == NULL || diff->type_map == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return; + } + apol_vector_destroy(&(diff->type_map->remap)); + /* no error checking below */ + diff->type_map->remap = apol_vector_create(type_remap_entry_free); + diff->remapped = 1; +} + +/** + * Convenience struct for comparing elements within arrays of primary types. + */ +struct type_map_comp +{ + poldiff_t *diff; + /** from which policy the first element came, either + * POLDIFF_POLICY_ORIG or POLDIFF_POLICY_MOD */ + int dir; +}; + +/** + * Given two qpol_type_t pointers, both of which are primary types, + * compare their names for equivalence. + * + * @param a Pointer to a qpol_type_t from a policy. + * @param b Pointer to a qpol_type_t from a policy. + * @param data Pointer to a type_map_comp struct. + * + * @return 0 if the names match, non-zero if not. + */ +static int type_map_primary_comp(const void *a, const void *b, void *data) +{ + const qpol_type_t *ta = a; + const qpol_type_t *tb = b; + struct type_map_comp *c = (struct type_map_comp *)data; + poldiff_t *diff = c->diff; + int dir = c->dir; + const char *na, *nb; + if (dir == POLDIFF_POLICY_ORIG) { + if (qpol_type_get_name(diff->orig_qpol, ta, &na) < 0 || qpol_type_get_name(diff->mod_qpol, tb, &nb) < 0) { + return -1; + } + } else { + if (qpol_type_get_name(diff->mod_qpol, ta, &na) < 0 || qpol_type_get_name(diff->orig_qpol, tb, &nb) < 0) { + return -1; + } + } + return strcmp(na, nb); +} + +/** + * Given two qpol_type_t pointers, both of which are primary types, + * see if the first type matches any of the other type's aliases. + * + * @param a Pointer to a qpol_type_t from a policy. + * @param b Pointer to a qpol_type_t from a policy. + * @param data Pointer to a type_map_comp struct. + * + * @return 0 if b is a member of a's aliases, non-zero if not. + */ +static int type_map_prim_alias_comp(const void *a, const void *b, void *data) +{ + const qpol_type_t *ta = a; + const qpol_type_t *tb = b; + struct type_map_comp *c = (struct type_map_comp *)data; + poldiff_t *diff = c->diff; + int dir = c->dir; + const char *prim, *alias; + qpol_iterator_t *iter = NULL; + if (dir == POLDIFF_POLICY_ORIG) { + if (qpol_type_get_alias_iter(diff->orig_qpol, ta, &iter) < 0 || qpol_type_get_name(diff->mod_qpol, tb, &prim) < 0) { + qpol_iterator_destroy(&iter); + return -1; + } + } else { + if (qpol_type_get_alias_iter(diff->mod_qpol, ta, &iter) < 0 || qpol_type_get_name(diff->orig_qpol, tb, &prim) < 0) { + qpol_iterator_destroy(&iter); + return -1; + } + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&alias) < 0) { + qpol_iterator_destroy(&iter); + return -1; + } + if (strcmp(prim, alias) == 0) { + qpol_iterator_destroy(&iter); + return 0; + } + } + qpol_iterator_destroy(&iter); + return -1; +} + +/** + * Given two qpol_type_t pointers, both of which are primary types, + * see if the first type's aliases matches the second type's aliases. + * + * @param a Pointer to a qpol_type_t from a policy. + * @param b Pointer to a qpol_type_t from a policy. + * @param data Pointer to a type_map_comp struct. + * + * @return 0 if b is a member of a's aliases, non-zero if not. + */ +static int type_map_prim_aliases_comp(const void *a, const void *b, void *data) +{ + qpol_type_t *ta = (qpol_type_t *) a; + qpol_type_t *tb = (qpol_type_t *) b; + struct type_map_comp *c = (struct type_map_comp *)data; + poldiff_t *diff = c->diff; + int dir = c->dir; + qpol_policy_t *p1, *p2; + qpol_iterator_t *iter1 = NULL, *iter2 = NULL; + apol_vector_t *v1 = NULL, *v2 = NULL; + size_t i; + int retval = -1, error = 0; + if (dir == POLDIFF_POLICY_ORIG) { + p1 = diff->orig_qpol; + p2 = diff->mod_qpol; + } else { + p1 = diff->mod_qpol; + p2 = diff->orig_qpol; + } + if (qpol_type_get_alias_iter(p1, ta, &iter1) < 0) { + error = errno; + goto cleanup; + } + if ((v1 = apol_vector_create_from_iter(iter1, NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + if (qpol_type_get_alias_iter(p2, tb, &iter2) < 0) { + error = errno; + goto cleanup; + } + if ((v2 = apol_vector_create_from_iter(iter2, NULL)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) { + retval = 1; + goto cleanup; + } else { + apol_vector_sort_uniquify(v1, apol_str_strcmp, NULL); + apol_vector_sort_uniquify(v2, apol_str_strcmp, NULL); + retval = apol_vector_compare(v1, v2, apol_str_strcmp, NULL, &i); + } + cleanup: + qpol_iterator_destroy(&iter1); + qpol_iterator_destroy(&iter2); + apol_vector_destroy(&v1); + apol_vector_destroy(&v2); + errno = error; + return retval; +} + +/** + * If --enable-debug is given, then dump to stdout the type-map from + * pseudo-types to the policy's type(s). + */ +static void type_remap_vector_dump(poldiff_t * diff) +{ +#ifdef SETOOLS_DEBUG + apol_vector_t *v, *w; + size_t i, j; + poldiff_type_remap_entry_t *e; + char *name; + printf("# type remap vector debug dump (pseudo-type -> qpol_type_t(s):\n"); + v = poldiff_type_remap_get_entries(diff); + for (i = 0; i < apol_vector_get_size(v); i++) { + e = apol_vector_get_element(v, i); + printf("%zd\t%s\t", i, poldiff_type_remap_entry_get_is_enabled(e) ? "en" : "dis"); + w = poldiff_type_remap_entry_get_original_types(diff, e); + for (j = 0; j < apol_vector_get_size(w); j++) { + name = apol_vector_get_element(w, j); + printf("%s ", name); + } + apol_vector_destroy(&w); + printf("-> "); + w = poldiff_type_remap_entry_get_modified_types(diff, e); + for (j = 0; j < apol_vector_get_size(w); j++) { + name = apol_vector_get_element(w, j); + printf("%s ", name); + } + apol_vector_destroy(&w); + printf("\n"); + } +#endif +} + +static int type_map_entry_append_qtypes(poldiff_t * diff, poldiff_type_remap_entry_t * entry, const qpol_type_t * t, + const qpol_type_t * u) +{ + const char *name; + char *dup_name; + if (qpol_type_get_name(diff->orig_qpol, t, &name) < 0) { + return -1; + } + if ((dup_name = strdup(name)) == NULL || apol_vector_append(entry->orig_types, (void *)dup_name) < 0) { + free(dup_name); + return -1; + } + + if (qpol_type_get_name(diff->mod_qpol, u, &name) < 0) { + return -1; + } + if ((dup_name = strdup(name)) == NULL || apol_vector_append(entry->mod_types, (void *)dup_name) < 0) { + free(dup_name); + return -1; + } + return 0; +} + +int type_map_infer(poldiff_t * diff) +{ + apol_vector_t *ov = NULL, *mv = NULL; + char *orig_done = NULL, *mod_done = NULL; + size_t num_orig, num_mod, i, j; + qpol_type_t *t, *u; + struct type_map_comp c = { diff, 0 }; + poldiff_type_remap_entry_t *entry = NULL; + int retval = -1, error = 0; + + INFO(diff, "%s", "Inferring type remap."); + if (apol_type_get_by_query(diff->orig_pol, NULL, &ov) < 0 || apol_type_get_by_query(diff->mod_pol, NULL, &mv) < 0) { + error = errno; + goto cleanup; + } + num_orig = apol_vector_get_size(ov); + num_mod = apol_vector_get_size(mv); + if ((orig_done = calloc(1, num_orig)) == NULL || (mod_done = calloc(1, num_mod)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + + /* first map primary <--> primary */ + c.dir = POLDIFF_POLICY_MOD; + for (i = 0; i < num_orig; i++) { + t = (qpol_type_t *) apol_vector_get_element(ov, i); + if (apol_vector_get_index(mv, t, type_map_primary_comp, &c, &j) < 0) { + continue; + } + assert(!mod_done[j]); + u = (qpol_type_t *) apol_vector_get_element(mv, j); + if ((entry = poldiff_type_remap_entry_create(diff)) == NULL || type_map_entry_append_qtypes(diff, entry, t, u) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + entry->inferred = 1; + orig_done[i] = 1; + mod_done[j] = 1; + } + + /* now map primary -> primary's alias */ + c.dir = POLDIFF_POLICY_MOD; + for (i = 0; i < num_orig; i++) { + if (orig_done[i]) { + continue; + } + t = (qpol_type_t *) apol_vector_get_element(ov, i); + u = NULL; + for (j = 0; j < num_mod; j++) { + if (mod_done[j]) { + continue; + } + u = (qpol_type_t *) apol_vector_get_element(mv, j); + if (type_map_prim_alias_comp(u, t, &c) == 0) { + break; + } + } + if (j >= num_mod) { + continue; + } + if ((entry = poldiff_type_remap_entry_create(diff)) == NULL || type_map_entry_append_qtypes(diff, entry, t, u) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + entry->inferred = 1; + orig_done[i] = 1; + mod_done[j] = 1; + } + + /* then map primary's alias <- primary */ + c.dir = POLDIFF_POLICY_ORIG; + for (j = 0; j < num_mod; j++) { + if (mod_done[j]) { + continue; + } + u = (qpol_type_t *) apol_vector_get_element(mv, j); + t = NULL; + for (i = 0; i < num_orig; i++) { + if (orig_done[i]) { + continue; + } + t = (qpol_type_t *) apol_vector_get_element(ov, i); + if (type_map_prim_alias_comp(t, u, &c) == 0) { + break; + } + } + if (i >= num_orig) { + continue; + } + if ((entry = poldiff_type_remap_entry_create(diff)) == NULL || type_map_entry_append_qtypes(diff, entry, t, u) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + entry->inferred = 1; + orig_done[i] = 1; + mod_done[j] = 1; + } + + /* map alias <-> alias */ + c.dir = POLDIFF_POLICY_MOD; + for (i = 0; i < num_orig; i++) { + if (orig_done[i]) { + continue; + } + t = (qpol_type_t *) apol_vector_get_element(ov, i); + u = NULL; + for (j = 0; j < num_mod; j++) { + if (mod_done[j]) { + continue; + } + u = (qpol_type_t *) apol_vector_get_element(mv, j); + if (type_map_prim_aliases_comp(u, t, &c) == 0) { + break; + } + } + if (j >= num_mod) { + continue; + } + if ((entry = poldiff_type_remap_entry_create(diff)) == NULL || type_map_entry_append_qtypes(diff, entry, t, u) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + entry->inferred = 1; + orig_done[i] = 1; + mod_done[j] = 1; + } + + type_remap_vector_dump(diff); + + retval = 0; + diff->remapped = 1; + cleanup: + apol_vector_destroy(&ov); + apol_vector_destroy(&mv); + free(orig_done); + free(mod_done); + errno = error; + return retval; +} + +uint32_t type_map_lookup(const poldiff_t * diff, const qpol_type_t * type, int which_pol) +{ + uint32_t val; + if (which_pol == POLDIFF_POLICY_ORIG) { + if (qpol_type_get_value(diff->orig_qpol, type, &val) < 0) { + return 0; + } + assert(val <= diff->type_map->num_orig_types); + assert(diff->type_map->orig_to_pseudo[val - 1] != 0); + return diff->type_map->orig_to_pseudo[val - 1]; + } else { + if (qpol_type_get_value(diff->mod_qpol, type, &val) < 0) { + return 0; + } + assert(val <= diff->type_map->num_mod_types); + assert(diff->type_map->mod_to_pseudo[val - 1] != 0); + return diff->type_map->mod_to_pseudo[val - 1]; + } +} + +const apol_vector_t *type_map_lookup_reverse(const poldiff_t * diff, uint32_t val, int which_pol) +{ + if (which_pol == POLDIFF_POLICY_ORIG) { + return apol_vector_get_element(diff->type_map->pseudo_to_orig, val - 1); + } else { + return apol_vector_get_element(diff->type_map->pseudo_to_mod, val - 1); + } +} + +const char *type_map_get_name(const poldiff_t * diff, const uint32_t pseudo_val, int pol) +{ + const apol_vector_t *v = NULL; + const char *name = NULL; + const qpol_type_t *t; + + v = type_map_lookup_reverse(diff, pseudo_val, pol); + if (apol_vector_get_size(v) == 0) { + return NULL; + } + t = apol_vector_get_element(v, 0); + if (pol == POLDIFF_POLICY_ORIG) + qpol_type_get_name(diff->orig_qpol, t, &name); + else + qpol_type_get_name(diff->mod_qpol, t, &name); + return name; +} diff --git a/libpoldiff/src/type_map_internal.h b/libpoldiff/src/type_map_internal.h new file mode 100644 index 0000000..786f38d --- /dev/null +++ b/libpoldiff/src/type_map_internal.h @@ -0,0 +1,171 @@ +/** + * @file + * Protected interface for type equivalence mapping for semantic + * difference calculations. + * + * @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 POLDIFF_TYPE_MAP_INTERNAL_H +#define POLDIFF_TYPE_MAP_INTERNAL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <apol/vector.h> +#include <qpol/policy.h> + + typedef struct type_map type_map_t; + +#define POLDIFF_POLICY_ORIG 1 +#define POLDIFF_POLICY_MOD 2 + +/** + * Allocate and return a new type_map_t object. + * + * @return a new type map object. The caller must call + * type_map_destroy() afterwards. On error, return NULL and set + * errno. + */ + type_map_t *type_map_create(void); + +/** + * Free all memory used by the type map. + * + * @param map Reference pointer to the type map to destroy. This + * pointer will be set to NULL afterwards. + */ + void type_map_destroy(type_map_t ** map); + +/** + * Build the type map for a policy difference structure, using all + * enabled poldiff_type_remap_entry entries as hints for the + * mappings. This function should be called by poldiff_run() before + * each run. + * + * @param diff The policy difference structure containing the + * policies from which to construct the type map. + * @return 0 on success and < 0 on error, if the call fails, errno will + * be set and the policy difference structure will be unchanged. + */ + int type_map_build(poldiff_t * diff); + +/** + * Clear away all type remap entries within the type map. This + * function should be called some time after type_map_create(). + * + * @param diff The policy difference structure containing the + * policies from which to construct the type map. + */ + void poldiff_type_remap_flush(poldiff_t * diff); + +/** + * Infer type remappings and append them to the current type remap + * vector. The vector should probably be first flushed via + * poldiff_type_remap_flush(). Generated entries will have their + * 'inferred' and 'enabled' flags set. + * + * The heuristic for determining type remaps is as follow. + * <ol> + * + * <li>If any type name exists as a primary in both policies then map + * it. + * + * <li>For all remaining unmapped primary types in the original + * policy, if that type name appears as an alias to an unmapped + * primary in the modified then map it. + * + * <li>For all remaining unmapped primary types in the modified + * policy, if that type name appears as an alias to an unmapped + * primary in the original then map it. + * + * <li>For all remaining unmapped primary types in both policies, if + * all of the aliases of one type are exactly the same as another + * type's aliases then map it. + * + * <li>All remaining types are left as unmapped. + * + * </ol> + * + * A side-effect of this heuristic is that it is reversible; the same + * inferences are made regardless of the order of policies. + * + * @param diff The policy difference structure containing the + * policies from which to construct the type map. + * + * @return 0 on success, < 0 on error and errno will be set. + */ + int type_map_infer(poldiff_t * diff); + +/** + * Given a qpol_type_t and a flag indicating from which the policy + * the type originated, return its remapped value. (type_map_build() + * must have been first called.) + * + * @param diff The policy difference structure assocated with the + * types. + * @param type Type to lookup. + * @param which_pol One of POLDIFF_POLICY_ORIG or POLDIFF_POLICY_MOD. + * + * @return The type's remapped value. On error this will be 0 and + * errno will be set. + */ + uint32_t type_map_lookup(const poldiff_t * diff, const qpol_type_t * type, int which_pol); + +/** + * Given a pseudo-type's value and a flag indicating for which policy + * to look up, return a vector of qpol_type_t pointers to reference + * back to the unmapped types. (type_map_build() must have been + * first called.) Note that the returned vector could be empty for + * the situation where a type was added or removed. + * + * @param diff The policy difference structure assocated with the + * types. + * @param val Pseudo-type value to lookup. + * @param which_pol One of POLDIFF_POLICY_ORIG or POLDIFF_POLICY_MOD. + * + * @return A vector of qpol_type_t pointers. The caller should not + * free this vector. If the call fails, NULL will be returned and + * errno will be set. + */ + const apol_vector_t *type_map_lookup_reverse(const poldiff_t * diff, uint32_t val, int which_pol); + +/** + * Get the first name that can be found for a pseudo type value. + * + * @param diff Policy difference structure associated with the value. + * @param pseudo_val Value for which to get a name. + * @param pol The policy to use, either POLDIFF_POLICY_ORIG or + * POLDIFF_POLICY_MOD. + * + * @return A valid name of a type from either policy that maps to the + * specified value. If the type does not exist then return NULL. Do + * not modify the string; it is a pointer into the policy's symbol + * table. + */ + const char *type_map_get_name(const poldiff_t * diff, const uint32_t pseudo_val, int pol); + +#ifdef __cplusplus +} +#endif + +#endif /* POLDIFF_TYPE_MAP_INTERNAL_H */ diff --git a/libpoldiff/src/user_diff.c b/libpoldiff/src/user_diff.c new file mode 100644 index 0000000..e673291 --- /dev/null +++ b/libpoldiff/src/user_diff.c @@ -0,0 +1,789 @@ +/** + * @file + * Implementation for computing semantic differences in users. + * + * @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 "poldiff_internal.h" + +#include <apol/util.h> +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +struct poldiff_user_summary +{ + size_t num_added; + size_t num_removed; + size_t num_modified; + apol_vector_t *diffs; +}; + +struct poldiff_user +{ + char *name; + poldiff_form_e form; + /* the next three are vector of strings */ + apol_vector_t *unmodified_roles; + apol_vector_t *added_roles; + apol_vector_t *removed_roles; + /** if not diffing a MLS policy, this will be NULL */ + poldiff_level_t *orig_default_level; + /** if not diffing a MLS policy, this will be NULL; this is + also NULL if orig_default_level->form is + POLDIFF_FORM_MODIFIED */ + poldiff_level_t *mod_default_level; + /** if not diffing MLS policies then the range is NULL */ + poldiff_range_t *range; +}; + +void poldiff_user_get_stats(const poldiff_t * diff, size_t stats[5]) +{ + if (diff == NULL || stats == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return; + } + stats[0] = diff->user_diffs->num_added; + stats[1] = diff->user_diffs->num_removed; + stats[2] = diff->user_diffs->num_modified; + stats[3] = 0; + stats[4] = 0; +} + +/** + * Generate the to_string for a modified user. + */ +static char *user_to_modified_string(const poldiff_t * diff, const poldiff_user_t * u) +{ + size_t len = 0, i; + char *s = NULL, *t = NULL, *role, *range = NULL; + size_t num_added_roles = apol_vector_get_size(u->added_roles); + size_t num_removed_roles = apol_vector_get_size(u->removed_roles); + if (apol_str_appendf(&s, &len, "* %s\n", u->name) < 0) { + goto err; + } + if (num_added_roles > 0 || num_removed_roles > 0) { + if (apol_str_append(&s, &len, " roles {") < 0) { + goto err; + } + for (i = 0; i < apol_vector_get_size(u->unmodified_roles); i++) { + role = (char *)apol_vector_get_element(u->unmodified_roles, i); + if (apol_str_appendf(&s, &len, " %s", role) < 0) { + goto err; + } + } + for (i = 0; i < num_added_roles; i++) { + role = (char *)apol_vector_get_element(u->added_roles, i); + if (apol_str_appendf(&s, &len, " +%s", role) < 0) { + goto err; + } + } + for (i = 0; i < num_removed_roles; i++) { + role = (char *)apol_vector_get_element(u->removed_roles, i); + if (apol_str_appendf(&s, &len, " -%s", role) < 0) { + goto err; + } + } + if (apol_str_append(&s, &len, " }\n") < 0) { + goto err; + } + } + if ((u->mod_default_level != NULL || u->orig_default_level != NULL) && apol_str_append(&s, &len, " level:\n") < 0) { + goto err; + } + if (u->mod_default_level != NULL) { + if ((t = poldiff_level_to_string_brief(diff, u->mod_default_level)) == NULL) { + goto err; + } + if (apol_str_appendf(&s, &len, " %s", t) < 0) { + ERR(diff, "%s", strerror(errno)); + goto err; + } + free(t); + t = NULL; + } + if (u->orig_default_level != NULL) { + if ((t = poldiff_level_to_string_brief(diff, u->orig_default_level)) == NULL) { + goto err; + } + if (apol_str_appendf(&s, &len, " %s", t) < 0) { + ERR(diff, "%s", strerror(errno)); + goto err; + } + free(t); + t = NULL; + } + if (u->range != NULL) { + if ((range = poldiff_range_to_string_brief(diff, u->range)) == NULL + || (apol_str_appendf(&s, &len, "%s", range) < 0)) { + free(range); + goto err; + } + free(range); + } + return s; + err: + free(s); + free(t); + return NULL; +} + +char *poldiff_user_to_string(const poldiff_t * diff, const void *user) +{ + poldiff_user_t *u = (poldiff_user_t *) user; + size_t len = 0; + char *s = NULL; + if (diff == NULL || user == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + switch (u->form) { + case POLDIFF_FORM_ADDED: + { + if (apol_str_appendf(&s, &len, "+ %s", u->name) < 0) { + break; + } + return s; + } + case POLDIFF_FORM_REMOVED: + { + if (apol_str_appendf(&s, &len, "- %s", u->name) < 0) { + break; + } + return s; + } + case POLDIFF_FORM_MODIFIED: + { + if ((s = user_to_modified_string(diff, u)) == NULL) { + goto err; + } + return s; + } + default: + { + ERR(diff, "%s", strerror(ENOTSUP)); + errno = ENOTSUP; + return NULL; + } + } + err: + /* if this is reached then an error occurred */ + free(s); + ERR(diff, "%s", strerror(ENOMEM)); + errno = ENOMEM; + return NULL; +} + +const apol_vector_t *poldiff_get_user_vector(const poldiff_t * diff) +{ + if (diff == NULL) { + errno = EINVAL; + return NULL; + } + return diff->user_diffs->diffs; +} + +const char *poldiff_user_get_name(const poldiff_user_t * user) +{ + if (user == NULL) { + errno = EINVAL; + return NULL; + } + return user->name; +} + +poldiff_form_e poldiff_user_get_form(const void *user) +{ + if (user == NULL) { + errno = EINVAL; + return 0; + } + return ((const poldiff_user_t *)user)->form; +} + +const apol_vector_t *poldiff_user_get_unmodified_roles(const poldiff_user_t * user) +{ + if (user == NULL) { + errno = EINVAL; + return NULL; + } + return user->unmodified_roles; +} + +const apol_vector_t *poldiff_user_get_added_roles(const poldiff_user_t * user) +{ + if (user == NULL) { + errno = EINVAL; + return NULL; + } + return user->added_roles; +} + +const apol_vector_t *poldiff_user_get_removed_roles(const poldiff_user_t * user) +{ + if (user == NULL) { + errno = EINVAL; + return NULL; + } + return user->removed_roles; +} + +const poldiff_level_t *poldiff_user_get_original_dfltlevel(const poldiff_user_t * user) +{ + if (user == NULL) { + errno = EINVAL; + return NULL; + } + return user->orig_default_level; +} + +const poldiff_level_t *poldiff_user_get_modified_dfltlevel(const poldiff_user_t * user) +{ + if (user == NULL) { + errno = EINVAL; + return NULL; + } + return user->mod_default_level; +} + +const poldiff_range_t *poldiff_user_get_range(const poldiff_user_t * user) +{ + if (user == NULL) { + errno = EINVAL; + return NULL; + } + return user->range; +} + +/*************** protected functions for users ***************/ + +static void user_free(void *elem) +{ + if (elem != NULL) { + poldiff_user_t *u = (poldiff_user_t *) elem; + free(u->name); + apol_vector_destroy(&u->added_roles); + apol_vector_destroy(&u->removed_roles); + apol_vector_destroy(&u->unmodified_roles); + level_free(u->orig_default_level); + level_free(u->mod_default_level); + range_destroy(&u->range); + free(u); + } +} + +poldiff_user_summary_t *user_create(void) +{ + poldiff_user_summary_t *us = calloc(1, sizeof(*us)); + if (us == NULL) { + return NULL; + } + if ((us->diffs = apol_vector_create(user_free)) == NULL) { + user_destroy(&us); + return NULL; + } + return us; +} + +void user_destroy(poldiff_user_summary_t ** us) +{ + if (us != NULL && *us != NULL) { + apol_vector_destroy(&(*us)->diffs); + free(*us); + *us = NULL; + } +} + +int user_reset(poldiff_t * diff) +{ + int error = 0; + + if (diff == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + user_destroy(&diff->user_diffs); + diff->user_diffs = user_create(); + if (diff->user_diffs == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + errno = error; + return -1; + } + + return 0; +} + +/** + * Comparison function for two users from the same policy. + */ +static int user_name_comp(const void *x, const void *y, void *arg) +{ + const qpol_user_t *u1 = x; + const qpol_user_t *u2 = y; + apol_policy_t *p = (apol_policy_t *) arg; + qpol_policy_t *q = apol_policy_get_qpol(p); + const char *name1, *name2; + if (qpol_user_get_name(q, u1, &name1) < 0 || qpol_user_get_name(q, u2, &name2) < 0) { + return 0; + } + return strcmp(name1, name2); +} + +apol_vector_t *user_get_items(poldiff_t * diff, const apol_policy_t * policy) +{ + qpol_iterator_t *iter = NULL; + apol_vector_t *v = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policy); + int error = 0; + if (qpol_policy_get_user_iter(q, &iter) < 0) { + return NULL; + } + v = apol_vector_create_from_iter(iter, NULL); + if (v == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + qpol_iterator_destroy(&iter); + errno = error; + return NULL; + } + qpol_iterator_destroy(&iter); + apol_vector_sort(v, user_name_comp, (void *)policy); + return v; +} + +int user_comp(const void *x, const void *y, const poldiff_t * diff) +{ + const qpol_user_t *u1 = x; + const qpol_user_t *u2 = y; + const char *name1, *name2; + if (qpol_user_get_name(diff->orig_qpol, u1, &name1) < 0 || qpol_user_get_name(diff->mod_qpol, u2, &name2) < 0) { + return 0; + } + return strcmp(name1, name2); +} + +/** + * Allocate and return a new user difference object. + * + * @param diff Policy diff error handler. + * @param form Form of the difference. + * @param name Name of the user that is different. + * + * @return A newly allocated and initialized diff, or NULL upon error. + * The caller is responsible for calling user_free() upon the returned + * value. + */ +static poldiff_user_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name) +{ + poldiff_user_t *pu; + int error; + if ((pu = calloc(1, sizeof(*pu))) == NULL || + (pu->name = strdup(name)) == NULL || + (pu->added_roles = apol_vector_create_with_capacity(1, free)) == NULL || + (pu->removed_roles = apol_vector_create_with_capacity(1, free)) == NULL || + (pu->unmodified_roles = apol_vector_create_with_capacity(1, free)) == NULL) { + error = errno; + user_free(pu); + ERR(diff, "%s", strerror(error)); + errno = error; + return NULL; + } + pu->form = form; + return pu; +} + +/** + * Given a user, return a vector of its allowed roles (in the form of + * strings). + * + * @param diff Policy diff error handler. + * @param p Policy from which the user came. + * @param user User whose roles to get. + * + * @return Vector of role strings for the user. The caller is + * responsible for calling apol_vector_destroy(). On error, return + * NULL. + */ +static apol_vector_t *user_get_roles(const poldiff_t * diff, const apol_policy_t * p, const qpol_user_t * user) +{ + qpol_iterator_t *iter = NULL; + const qpol_role_t *role; + const char *role_name; + char *role_name2; + apol_vector_t *v = NULL; + qpol_policy_t *q = apol_policy_get_qpol(p); + int retval = -1, error = 0; + + if ((v = apol_vector_create(free)) == NULL) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + if (qpol_user_get_role_iter(q, user, &iter) < 0) { + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&role) < 0 || qpol_role_get_name(q, role, &role_name)) { + error = errno; + goto cleanup; + } + if ((role_name2 = strdup(role_name)) == NULL || apol_vector_append(v, (void *)role_name2) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + free(role_name2); + goto cleanup; + } + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + if (retval < 0) { + apol_vector_destroy(&v); + errno = error; + return NULL; + } + return v; +} + +/** + * Perform a deep diff of the default MLS levels assigned to the two + * users. + * + * @param diff Diff structure containing the original and modified + * policies. + * @param u1 User from original policy to examine, or NULL if the user + * was added. + * @param u2 User from modified policy to examine, or NULL if the user + * was removed. + * @param u Result structure where differences are to be recorded. + * + * @return Greater than zero if a diff was found, zero if none found, + * less than zero for errors. + */ +static int user_deep_diff_default_levels(poldiff_t * diff, const qpol_user_t * u1, const qpol_user_t * u2, poldiff_user_t * u) +{ + const qpol_mls_level_t *ql1 = NULL, *ql2 = NULL; + poldiff_level_t *pl = NULL; + apol_mls_level_t *l1 = NULL, *l2 = NULL; + int retval = -1; + if (u1 != NULL && qpol_user_get_dfltlevel(diff->orig_qpol, u1, &ql1) < 0) { + return -1; + } + if (u2 != NULL && qpol_user_get_dfltlevel(diff->mod_qpol, u2, &ql2) < 0) { + return -1; + } + if (ql1 == NULL && ql2 == NULL) { + /* neither policy is MLS */ + return 0; + } + if (ql1 == NULL) { + if ((l2 = apol_mls_level_create_from_qpol_mls_level(diff->mod_pol, ql2)) == NULL || + (pl = level_create_from_apol_mls_level(l2, POLDIFF_FORM_ADDED)) == NULL) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + u->mod_default_level = pl; + retval = 1; + } else if (ql2 == NULL) { + if ((l1 = apol_mls_level_create_from_qpol_mls_level(diff->orig_pol, ql1)) == NULL || + (pl = level_create_from_apol_mls_level(l1, POLDIFF_FORM_REMOVED)) == NULL) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + u->orig_default_level = pl; + retval = 1; + } else { + if ((l1 = apol_mls_level_create_from_qpol_mls_level(diff->orig_pol, ql1)) == NULL || + (l2 = apol_mls_level_create_from_qpol_mls_level(diff->mod_pol, ql2)) == NULL) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + if (level_deep_diff_apol_mls_levels(diff, l1, l2, &u->orig_default_level, &u->mod_default_level) < 0) { + goto cleanup; + } + if (u->orig_default_level != NULL) { + retval = 1; + } + } + if (retval == -1) { + /* if reach this point, then no differences were found */ + retval = 0; + } + cleanup: + apol_mls_level_destroy(&l1); + apol_mls_level_destroy(&l2); + if (retval < 0) { + level_free(pl); + } + return retval; +} + +/** + * Perform a deep diff of the MLS ranges assigned to the two users. + * + * @param diff Diff structure containing the original and modified + * policies. + * @param u1 User from original policy to examine, or NULL if the user + * was added. + * @param u2 User from modified policy to examine, or NULL if the user + * was removed. + * @param u Result structure where differences are to be recorded. + * + * @return Greater than zero if a diff was found, zero if none found, + * less than zero for errors. + */ +static int user_deep_diff_ranges(poldiff_t * diff, const qpol_user_t * u1, const qpol_user_t * u2, poldiff_user_t * u) +{ + const qpol_mls_range_t *r1 = NULL, *r2 = NULL; + poldiff_range_t *pr = NULL; + int retval = -1; + if (u1 != NULL && qpol_user_get_range(diff->orig_qpol, u1, &r1) < 0) { + return -1; + } + if (u2 != NULL && qpol_user_get_range(diff->mod_qpol, u2, &r2) < 0) { + return -1; + } + if (r1 == NULL && r2 == NULL) { + /* neither policy is MLS */ + return 0; + } + if (r1 == NULL) { + if ((pr = range_create(diff, r1, r2, POLDIFF_FORM_ADDED)) == NULL) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + u->range = pr; + pr = NULL; + retval = 1; + } else if (r2 == NULL) { + if ((pr = range_create(diff, r1, r2, POLDIFF_FORM_REMOVED)) == NULL) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + u->range = pr; + pr = NULL; + retval = 1; + } else { + if ((pr = range_create(diff, r1, r2, POLDIFF_FORM_MODIFIED)) == NULL) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + if ((retval = range_deep_diff(diff, pr)) < 0) { + goto cleanup; + } + if (retval > 0) { + u->range = pr; + pr = NULL; + } + } + cleanup: + range_destroy(&pr); + return retval; +} + +int user_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + const qpol_user_t *u = item; + const char *name = NULL; + apol_vector_t *v = NULL; + poldiff_user_t *pu = NULL; + int error = 0, retval = -1; + if ((form == POLDIFF_FORM_ADDED && + qpol_user_get_name(diff->mod_qpol, u, &name) < 0) || + ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_user_get_name(diff->orig_qpol, u, &name) < 0)) + { + error = errno; + goto cleanup; + } + if ((pu = make_diff(diff, form, name)) == NULL) { + error = errno; + goto cleanup; + } + if (form == POLDIFF_FORM_ADDED) { + apol_vector_destroy(&pu->added_roles); + if ((v = user_get_roles(diff, diff->mod_pol, u)) == NULL || + (pu->added_roles = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL || + user_deep_diff_default_levels(diff, NULL, u, pu) < 0 || user_deep_diff_ranges(diff, NULL, u, pu) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } else { + apol_vector_destroy(&pu->removed_roles); + if ((v = user_get_roles(diff, diff->orig_pol, u)) == NULL || + (pu->removed_roles = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL || + user_deep_diff_default_levels(diff, u, NULL, pu) < 0 || user_deep_diff_ranges(diff, u, NULL, pu) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + if (apol_vector_append(diff->user_diffs->diffs, pu) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + if (form == POLDIFF_FORM_ADDED) { + diff->user_diffs->num_added++; + } else { + diff->user_diffs->num_removed++; + } + retval = 0; + cleanup: + apol_vector_destroy(&v); + if (retval < 0) { + user_free(pu); + errno = error; + } + return retval; +} + +/** + * Perform a deep diff of the roles assigned to the two users. + * + * @param diff Diff structure containing the original and modified + * policies. + * @param u1 User from original policy to examine. + * @param u2 User from modified policy to examine. + * @param u Result structure where differences are to be recorded. + * + * @return Greater than zero if a diff was found, zero if none found, + * less than zero for errors. + */ +static int user_deep_diff_roles(poldiff_t * diff, const qpol_user_t * u1, const qpol_user_t * u2, poldiff_user_t * u) +{ + apol_vector_t *v1 = NULL, *v2 = NULL; + char *role1, *role2; + size_t i, j; + int retval = -1, error = 0, compval; + + if ((v1 = user_get_roles(diff, diff->orig_pol, u1)) == NULL || (v2 = user_get_roles(diff, diff->mod_pol, u2)) == NULL) { + error = errno; + goto cleanup; + } + apol_vector_sort(v1, apol_str_strcmp, NULL); + apol_vector_sort(v2, apol_str_strcmp, NULL); + for (i = j = 0; i < apol_vector_get_size(v1);) { + if (j >= apol_vector_get_size(v2)) + break; + role1 = (char *)apol_vector_get_element(v1, i); + role2 = (char *)apol_vector_get_element(v2, j); + compval = strcmp(role1, role2); + if (compval < 0) { + if ((role1 = strdup(role1)) == NULL || apol_vector_append(u->removed_roles, role1) < 0) { + error = errno; + free(role1); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + i++; + } else if (compval > 0) { + if ((role2 = strdup(role2)) == NULL || apol_vector_append(u->added_roles, role2) < 0) { + error = errno; + free(role2); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + j++; + } else { + if ((role1 = strdup(role1)) == NULL || apol_vector_append(u->unmodified_roles, role1) < 0) { + error = errno; + free(role1); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + i++; + j++; + } + } + for (; i < apol_vector_get_size(v1); i++) { + role1 = (char *)apol_vector_get_element(v1, i); + if ((role1 = strdup(role1)) == NULL || apol_vector_append(u->removed_roles, role1) < 0) { + error = errno; + free(role1); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + for (; j < apol_vector_get_size(v2); j++) { + role2 = (char *)apol_vector_get_element(v2, j); + if ((role2 = strdup(role2)) == NULL || apol_vector_append(u->added_roles, role2) < 0) { + error = errno; + free(role2); + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + if (apol_vector_get_size(u->removed_roles) > 0 || apol_vector_get_size(u->added_roles) > 0) { + retval = 1; + } else { + retval = 0; + } + cleanup: + apol_vector_destroy(&v1); + apol_vector_destroy(&v2); + errno = error; + return retval; +} + +int user_deep_diff(poldiff_t * diff, const void *x, const void *y) +{ + const qpol_user_t *u1 = x; + const qpol_user_t *u2 = y; + const char *name; + poldiff_user_t *u = NULL; + int retval = -1, r1 = 0, r2 = 0, r3 = 0, error = 0; + if (qpol_user_get_name(diff->orig_qpol, u1, &name) < 0 || (u = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) { + error = errno; + goto cleanup; + } + if ((r1 = user_deep_diff_roles(diff, u1, u2, u)) < 0 || (r2 = user_deep_diff_default_levels(diff, u1, u2, u)) < 0 || + (r3 = user_deep_diff_ranges(diff, u1, u2, u)) < 0) { + error = errno; + goto cleanup; + } + if (r1 > 0 || r2 > 0 || r3 > 0) { + if (apol_vector_append(diff->user_diffs->diffs, u) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + diff->user_diffs->num_modified++; + } else { + /* no differences found */ + user_free(u); + } + retval = 0; + cleanup: + if (retval != 0) { + user_free(u); + } + errno = error; + return retval; +} diff --git a/libpoldiff/src/user_internal.h b/libpoldiff/src/user_internal.h new file mode 100644 index 0000000..f94d46b --- /dev/null +++ b/libpoldiff/src/user_internal.h @@ -0,0 +1,121 @@ +/** + * @file + * Protected interface for user differences. + * + * @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 POLDIFF_USER_INTERNAL_H +#define POLDIFF_USER_INTERNAL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct poldiff_user_summary poldiff_user_summary_t; + +/** + * Allocate and return a new poldiff_user_summary_t object. + * + * @return A new user summary. The caller must call user_destroy() + * afterwards. On error, return NULL and set errno. + */ + poldiff_user_summary_t *user_create(void); + +/** + * Deallocate all space associated with a poldiff_user_summary_t + * object, including the pointer itself. If the pointer is already + * NULL then do nothing. + * + * @param us Reference to a user summary to destroy. The pointer + * will be set to NULL afterwards. + */ + void user_destroy(poldiff_user_summary_t ** us); + +/** + * Reset the state of all user differences. + * @param diff The policy difference structure containing the differences + * to reset. + * @return 0 on success and < 0 on error; if the call fails, + * errno will be set and the user should call poldiff_destroy() on diff. + */ + int user_reset(poldiff_t * diff); + +/** + * Get a vector of all users from the given policy, sorted by name. + * + * @param diff Policy diff error handler. + * @param policy The policy from which to get the items. + * + * @return a newly allocated vector of all users. The caller is + * responsible for calling apol_vector_destroy() afterwards. On + * error, return NULL and set errno. + */ + apol_vector_t *user_get_items(poldiff_t * diff, const apol_policy_t * policy); + +/** + * Compare two qpol_user_t objects, determining if they have the same + * name or not. + * + * @param x The user from the original policy. + * @param y The user from the modified policy. + * @param diff The policy difference structure associated with both + * policies. + * + * @return < 0, 0, or > 0 if user x is respectively less than, equal + * to, or greater than user y. + */ + int user_comp(const void *x, const void *y, const poldiff_t * diff); + +/** + * Create, initialize, and insert a new semantic difference entry for + * a user. + * + * @param diff The policy difference structure to which to add the entry. + * @param form The form of the difference. + * @param item Item for which the entry is being created. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int user_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item); + +/** + * Compute the semantic difference of two users for which the compare + * callback returns 0. If a difference is found then allocate, + * initialize, and insert a new semantic difference entry for that + * user. + * + * @param diff The policy difference structure associated with both + * users and to which to add an entry if needed. + * @param x The user from the original policy. + * @param y The user from the modified policy. + * + * @return 0 on success and < 0 on error; if the call fails, set errno + * and leave the policy difference structure unchanged. + */ + int user_deep_diff(poldiff_t * diff, const void *x, const void *y); + +#ifdef __cplusplus +} +#endif + +#endif /* POLDIFF_USER_INTERNAL_H */ diff --git a/libpoldiff/src/util.c b/libpoldiff/src/util.c new file mode 100644 index 0000000..059dadf --- /dev/null +++ b/libpoldiff/src/util.c @@ -0,0 +1,32 @@ +/** + * @file + * + * Implementation of utility functions for libpoldiff. + * + * @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 <poldiff/util.h> + +const char *libpoldiff_get_version(void) +{ + return LIBPOLDIFF_VERSION_STRING; +} diff --git a/libpoldiff/src/writing-diffs-HOWTO b/libpoldiff/src/writing-diffs-HOWTO new file mode 100644 index 0000000..4980492 --- /dev/null +++ b/libpoldiff/src/writing-diffs-HOWTO @@ -0,0 +1,351 @@ +Writing New Diff Modules for libpoldiff HOWTO +August 1, 2007 + + +0. Introduction + +libpoldiff is a library to be used in conjunction with libapol to find +"semantic" differences between policies. For the purposes of this +HOWTO, the term "semantic" refers to how the SELinux kernel would +enforce accesses. If two policies could ever be enforced differently +then they are defined to be semantically different. + +libpoldiff operates by breaking a policy into various 'policy items'. +Examples of items are users, object classes, and type enforcement +rules. These items correspond to flags passed into the sediff +program. So as to be extensible, libpoldiff was designed to allow one +to add new diff modules (and hence additional flags to sediff). These +modules should operate independent of each other and without regard to +ordering of modules. + + +1. Library Overview + +libpoldiff implements what we term "the generic diffing algorithm", +akin to a merge sort. The algorithm takes two ordered lists of items +and successively picks the first item from the lists. If the two +match, according to the items' comparison function, then they are +deemed the same. Otherwise the same ordering used to generate the +lists may also be used to determine if one was added or removed from +the policy. + +As an example, consider a diff of users for two hypothetical SELinux +policies, "orig.policy" and "updated.policy". Within orig.policy are +users adam_u, charlie_u, and dave_u. updated.policy has the users adam_u, +bob_u, and charlie_u. The first step of the generic diffing algorithm +is to get the ordered list of items. Let the ordering algorithm be +alphabetical order; thus the lists would be: + + orig.policy -> {adam_u, charlie_u, dave_u} + updated.policy -> {adam_u, bob_u, charlie_u} + +The algorithm picks the first item from each list and compares them. +As that "adam_u" is the same as "adam_u", the algorithm accepts them +as the same and continues. The next two items are "charlie_u" and +"bob_u". The ordering functions finds "bob_u" to be "earlier" than +"charlie_u" (because it appears earlier alphabetically), "bob_u" is +marked as item added to updated.policy. The algorithm keeps +"charlie_u" from the first policy but advances the pointer for the +second list. These "charlie_u" are the same. The remaining type, +"dave_u", has no complement in the second list and is thus marked as +removed. + +Of course, finding differences for users is not as simple as comparing +their names. In addition one must also examine the roles assigned to +them as well. Comparing names was a "shallow diff"; checking roles is +a "deep diff". The deep diff must look at all aspects of the two +items to determine if they are the same, modified, +modified-by-adding-a-type, or modified-by-removing-a-type. (At this +time the users' allowed MLS ranges and default range would also be +checked.) + +To complicate things, consider the aspect of type remapping. Type +names may be changed between the policies; they could also be joined +into a new type and conversely split. Thus one must be careful how +the ordered lists of types are generated. The functions +type_map_lookup() and type_map_lookup_reverse() will prove essential. + + +2. Reporting Differences + +If a difference was found, either via a shallow diff or a deep diff, +then an "item diff" struct must be created. If it the difference was +'added' or 'removed' then libpoldiff's poldiff_do_item_diff() will +call the item diff creation function. If instead the difference was +found within the deep diff comparison function then that function is +responsible for creating the item diff struct. The item diff struct +is used by each item's to_string() function to create a human-readable +report. + + +3. Complete Walkthrough + +The following walkthrough describes the process for writing a diff for +items between the original and modified policies. The shallow +diff is to see if a type in the original policy exists in the modified +policy, with respect to the type map. The deep diff determines if the +types have the same set of attributes. + + +3a. Public Header + +Create a new file, libpoldiff/include/poldiff/type_diff.h, to declare +publicly accessible functions. At least three functions must exist +here: + + extern void poldiff_type_get_stats(poldiff_t *diff, size_t stats[5]); + extern apol_vector_t *poldiff_get_type_vector(poldiff_t *diff); + extern poldiff_form_e *poldiff_type_get_form(const void *type); + extern char *poldiff_type_to_string(poldiff_t *diff, const void *type); + +(The reason for using a void * in poldiff_type_to_string() will become +apparant in section 3d.) + +Also in this file, declare an opaque object to hold a single type +difference: + + typedef struct poldiff_type poldiff_type_t; + +Once a user gets a vector of poldiff_type_t objects, via +poldiff_get_type_vector(), he may want to do a number of things with +it. He could print it via poldiff_type_to_string() or get its form; +or just get the type's name, list of added attributes, or list of +removed attributes. Therefore add these three functions to +type_diff.h: + + extern const char *poldiff_type_get_name(poldiff_type_t *type); + extern apol_vector_t *poldiff_type_get_added_attribs(poldiff_type_t *type); + extern apol_vector_t *poldiff_type_get_removed_attribs(poldiff_type_t *type); + +As a convenience to developers, one should only need to #include the +public poldiff.h to pick up all diff modules. Modify +libpoldiff/include/poldiff/poldiff.h in the vicinity of line 74: + + #include <poldiff/type_diff.h> + +Finally, modify libpoldiff/include/poldiff/Makefile.am by adding an +entry for type_diff.h. + +3b. Protected Header + +There will be functions accessible only between library files (i.e., +protected functions). To distinguish public functions from those that +are protected, do not prefix these with 'poldiff_'. Create a new +file, libpoldiff/src/type_internal.h, that declares protected +routines. libpoldiff requires these four functions to exist: + + apol_vector_t *type_get_items(poldiff_t *diff, apol_policy_t *policy); + int type_new_diff(poldiff_t *diff, poldiff_form_e form, const void *item); + int type_comp(const void *x, const void *y, poldiff_t *diff); + int type_deep_diff(poldiff_t *diff, const void *x, const void *y); + +Associated with the computed list of poldiff_type_t objects is a +summary structure. Check that libpoldiff/src/poldiff_internal.h has +declare a 'struct poldiff_type_summary', then add the following line +to the protected header: + + typedef struct poldiff_type_summary poldiff_type_summary_t; + +As with all other poldiff objects, you will need a constructor and a +destructor: + + poldiff_type_summary_t *type_create(void); + void type_destroy(poldiff_type_summary_t **ts); + +As a convenience to developers, one should only need to #include the +protected poldiff_internal.h to pick up all diff modules. Modify +libpoldiff/src/poldiff_internal.h in the vicinity of line 50: + + #include "type_internal.h" + +Finally, modify libpoldiff/src/Makefile.am by adding an entry for +type_internal.h. + +3c. Implementing Functions + +Create a new file, libpoldiff/src/type_diff.c, to implement all +public, protected, and any necessary private functions. First declare +the contents of the structures: + + struct poldiff_type_summary { + size_t num_added; + size_t num_removed; + size_t num_modified; + apol_vector_t *diffs; /* vector of poldiff_type_t */ + }; + struct poldiff_type { + char *name; + poldiff_form_e form; + apol_vector_t *added_attribs; /* vector of char* */ + apol_vector_t *removed_attribs; /* vector of char* */ + }; + +The public functions are easy to write. + + * poldiff_type_get_stats() fetches the fields + diff->type_diffs->num_added, diff->type_diffs->num_removed, and + diff->type_diffs->num_modified. + + * poldiff_type_get_form() fetches an individual result's form. Note + that you must first cast the second parameter from void* to a + poldiff_type_t*, because this function operates upon items + returned by poldiff_get_type_vector(). + + * poldiff_type_to_string() returns an allocated string akin to + poldiff_user_to_string(). Note that you must first cast the + second parameter from void* to a poldiff_type_t*, because this + function operates upon items returned by + poldiff_get_type_vector(). + + * poldiff_get_type_vector() returns diff->type_diffs->diffs. + +The rest of the public functions are accessors into a poldiff_type_t +object. + +The protected functions are more difficult. + + * type_create() and type_destroy() affect a poldiff_type_summary_t + object. + + * The other protected functions are described in section 3e. + +Finally, modify libpoldiff/src/Makefile.am by adding an entry for +type_diff.c. + +3d. Adding Hooks to Diff Module + +The main library now needs to create and destroy the +poldiff_type_summary_t object and to actually diff types. Make these +changes to libpoldiff/src/poldiff.c: + + * Create a poldiff_type_summary_t object by calling type_create() + within poldiff_create(). + + * Destroy the summary by calling type_destroy() within + poldiff_destroy(). + + * To enforce that all diff modules have the requisite public and + protected functions, one must fulfill the requirements as given by + a poldiff_item_record, as defined on line 41. The first four + callbacks are satisfied by the first four public functions, the + remainder are met by protected functions. Thus add a new record + to the item_records[] array like so: + + /* ... */ + { + "type", + POLDIFF_DIFF_TYPES, + poldiff_type_get_stats, + poldiff_get_type_vector, + poldiff_type_get_form, + poldiff_type_to_string, + type_reset, + type_get_items, + type_comp, + type_new_diff, + type_deep_diff + }, + /* ... */ + +Finally, for the public functions to be accessible through +libpoldiff.so, add this line to libpoldiff/src/libpoldiff.map under +the 'global' category: + + poldiff_type_*; + +3e. General Idea for Diffing Types + +Rather than comparing qpol_type_t pointers from one policy to another, +it is more convenient to convert those pointers to "pseudo-type +values", which are represented as uint32_ts. These new values handle +the type mappings between policies. Whenever a difference is found, +convert those pseudo-type values back to the component qpol_type_t +pointers. With type splits and type joins, a single pseudo-type value +may map to multiple qpol_type_t pointers. + +First look at type_get_items(). Its job is to return a sorted list of +unique items. Write it like this: + + apol_vector_t *type_get_items(poldiff_t *diff, apol_policy_t *policy) + { + get an iterator of types from the policy + allocate a new vector v + for each item in the iterator, + convert qpol_type_t* to uint32_t via type_map_lookup() + append that uint32_t to v + sort and unquify v + return v + } + +type_map_lookup() needs a third parameter that says from which policy +the type originated. Use these lines to calculate the parameter: + + if (policy == diff->orig_pol) + which_pol = POLDIFF_POLICY_ORIG; + else + which_pol = POLDIFF_POLICY_MOD; + +Now that you have a vector of pseudo-type values, all further +functions will need to be in terms of these values. libpoldiff will +pass elements from the type_get_items() vector into your protected +functions. In this walkthrough you will thus need to cast all values +from void* to uint32_t because type_get_items() returned a vector of +uint32_ts. For example: + + int type_comp(const void *x, const void *y, + poldiff_t *diff __attribute__((unused))) + { + uint32_t t1 = (uint32_t) x; + uint32_t t2 = (uint32_t) y; + return t1 - t2; + } + + int type_deep_diff(poldiff_t *diff, const void *x, const void *y) + { + uint32_t t1 = (uint32_t) x; + uint32_t t2 = (uint32_t) y; + apol_vector_t *v1 = type_map_lookup_reverse(diff, t1, + POLDIFF_POLICY_ORIG); + apol_vector_t *v2 = type_map_lookup_reverse(diff, t2, + POLDIFF_POLICY_ORIG); + apol_vector_t *added_attribs, *removed_attribs; + let vector a1 = union of all attributes for all types in vector v1 + let vector a2 = union of all attributes for all types in vector v2 + sort and uniquify a1 + sort and uniquify a2 + for all attributes in a1 not in a2, + append to removed_attribs those attributes + for all attributes in a2 not in a1, + append to added_attribs those attributes + if removed_attribs is not empty or added_attribs is not empty, + then foreach type in v1, + create a new poldiff_type_t + set the poldiff_type_t's name to the type's name + set the form to POLDIFF_FORM_MODIFIED + clone added_attribs + clone removed_attribs + append the poldiff_type_t to diff->type_diffs->diffs + if no poldiff_type_t was created + return 0 + else + return non-zero + } + + int type_new_diff(poldiff_t *diff, poldiff_form_e form, const void *item) + { + uint32_t t = (uint32_t) item; + apol_vector_t *v; + if (form == POLDIFF_FORM_REMOVED) + v = type_map_lookup(diff, t, POLDIFF_POLICY_ORIG); + else + v = type_map_lookup(diff, t, POLDIFF_POLICY_MOD); + foreach type in v, + create a new poldiff_type_t + set the poldiff_type_t's name to the type's name + set the form to form + append the poldiff_type_t to diff->type_diffs->diffs + } + +One implementation of creating temporary vectors similar to +added_attribs and removed_attribs may be found at +libpoldiff/src/role_diff.c:role_deep_diff(). |