summaryrefslogtreecommitdiffstats
path: root/libpoldiff/src/type_diff.c
diff options
context:
space:
mode:
Diffstat (limited to 'libpoldiff/src/type_diff.c')
-rw-r--r--libpoldiff/src/type_diff.c662
1 files changed, 662 insertions, 0 deletions
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;
+}