diff options
Diffstat (limited to 'libpoldiff/src/type_diff.c')
-rw-r--r-- | libpoldiff/src/type_diff.c | 662 |
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; +} |