/** * @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 #include "poldiff_internal.h" #include #include #include 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; }