summaryrefslogtreecommitdiffstats
path: root/libpoldiff/src/rbac_diff.c
diff options
context:
space:
mode:
Diffstat (limited to 'libpoldiff/src/rbac_diff.c')
-rw-r--r--libpoldiff/src/rbac_diff.c1052
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;
+}