summaryrefslogtreecommitdiffstats
path: root/libpoldiff/src
diff options
context:
space:
mode:
Diffstat (limited to 'libpoldiff/src')
-rw-r--r--libpoldiff/src/Makefile.am57
-rw-r--r--libpoldiff/src/attrib_diff.c544
-rw-r--r--libpoldiff/src/attrib_internal.h121
-rw-r--r--libpoldiff/src/avrule_diff.c1636
-rw-r--r--libpoldiff/src/avrule_internal.h296
-rw-r--r--libpoldiff/src/bool_diff.c333
-rw-r--r--libpoldiff/src/bool_internal.h122
-rw-r--r--libpoldiff/src/cat_diff.c289
-rw-r--r--libpoldiff/src/cat_internal.h120
-rw-r--r--libpoldiff/src/class_diff.c990
-rw-r--r--libpoldiff/src/class_internal.h212
-rw-r--r--libpoldiff/src/level_diff.c769
-rw-r--r--libpoldiff/src/level_internal.h208
-rw-r--r--libpoldiff/src/libpoldiff.map50
-rw-r--r--libpoldiff/src/poldiff.c814
-rw-r--r--libpoldiff/src/poldiff_internal.h239
-rw-r--r--libpoldiff/src/range_diff.c420
-rw-r--r--libpoldiff/src/range_internal.h80
-rw-r--r--libpoldiff/src/range_trans_diff.c520
-rw-r--r--libpoldiff/src/range_trans_internal.h124
-rw-r--r--libpoldiff/src/rbac_diff.c1052
-rw-r--r--libpoldiff/src/rbac_internal.h209
-rw-r--r--libpoldiff/src/role_diff.c543
-rw-r--r--libpoldiff/src/role_internal.h121
-rw-r--r--libpoldiff/src/terule_diff.c1329
-rw-r--r--libpoldiff/src/terule_internal.h244
-rw-r--r--libpoldiff/src/type_diff.c662
-rw-r--r--libpoldiff/src/type_internal.h125
-rw-r--r--libpoldiff/src/type_map.c985
-rw-r--r--libpoldiff/src/type_map_internal.h171
-rw-r--r--libpoldiff/src/user_diff.c789
-rw-r--r--libpoldiff/src/user_internal.h121
-rw-r--r--libpoldiff/src/util.c32
-rw-r--r--libpoldiff/src/writing-diffs-HOWTO351
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().