diff options
Diffstat (limited to 'libpoldiff/src/rbac_diff.c')
-rw-r--r-- | libpoldiff/src/rbac_diff.c | 1052 |
1 files changed, 1052 insertions, 0 deletions
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; +} |