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