summaryrefslogtreecommitdiffstats
path: root/libpoldiff/src/user_diff.c
diff options
context:
space:
mode:
Diffstat (limited to 'libpoldiff/src/user_diff.c')
-rw-r--r--libpoldiff/src/user_diff.c789
1 files changed, 789 insertions, 0 deletions
diff --git a/libpoldiff/src/user_diff.c b/libpoldiff/src/user_diff.c
new file mode 100644
index 0000000..e673291
--- /dev/null
+++ b/libpoldiff/src/user_diff.c
@@ -0,0 +1,789 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in users.
+ *
+ * @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 <stdio.h>
+#include <string.h>
+
+struct poldiff_user_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_user
+{
+ char *name;
+ poldiff_form_e form;
+ /* the next three are vector of strings */
+ apol_vector_t *unmodified_roles;
+ apol_vector_t *added_roles;
+ apol_vector_t *removed_roles;
+ /** if not diffing a MLS policy, this will be NULL */
+ poldiff_level_t *orig_default_level;
+ /** if not diffing a MLS policy, this will be NULL; this is
+ also NULL if orig_default_level->form is
+ POLDIFF_FORM_MODIFIED */
+ poldiff_level_t *mod_default_level;
+ /** if not diffing MLS policies then the range is NULL */
+ poldiff_range_t *range;
+};
+
+void poldiff_user_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->user_diffs->num_added;
+ stats[1] = diff->user_diffs->num_removed;
+ stats[2] = diff->user_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+/**
+ * Generate the to_string for a modified user.
+ */
+static char *user_to_modified_string(const poldiff_t * diff, const poldiff_user_t * u)
+{
+ size_t len = 0, i;
+ char *s = NULL, *t = NULL, *role, *range = NULL;
+ size_t num_added_roles = apol_vector_get_size(u->added_roles);
+ size_t num_removed_roles = apol_vector_get_size(u->removed_roles);
+ if (apol_str_appendf(&s, &len, "* %s\n", u->name) < 0) {
+ goto err;
+ }
+ if (num_added_roles > 0 || num_removed_roles > 0) {
+ if (apol_str_append(&s, &len, " roles {") < 0) {
+ goto err;
+ }
+ for (i = 0; i < apol_vector_get_size(u->unmodified_roles); i++) {
+ role = (char *)apol_vector_get_element(u->unmodified_roles, i);
+ if (apol_str_appendf(&s, &len, " %s", role) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < num_added_roles; i++) {
+ role = (char *)apol_vector_get_element(u->added_roles, i);
+ if (apol_str_appendf(&s, &len, " +%s", role) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < num_removed_roles; i++) {
+ role = (char *)apol_vector_get_element(u->removed_roles, i);
+ if (apol_str_appendf(&s, &len, " -%s", role) < 0) {
+ goto err;
+ }
+ }
+ if (apol_str_append(&s, &len, " }\n") < 0) {
+ goto err;
+ }
+ }
+ if ((u->mod_default_level != NULL || u->orig_default_level != NULL) && apol_str_append(&s, &len, " level:\n") < 0) {
+ goto err;
+ }
+ if (u->mod_default_level != NULL) {
+ if ((t = poldiff_level_to_string_brief(diff, u->mod_default_level)) == NULL) {
+ goto err;
+ }
+ if (apol_str_appendf(&s, &len, " %s", t) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto err;
+ }
+ free(t);
+ t = NULL;
+ }
+ if (u->orig_default_level != NULL) {
+ if ((t = poldiff_level_to_string_brief(diff, u->orig_default_level)) == NULL) {
+ goto err;
+ }
+ if (apol_str_appendf(&s, &len, " %s", t) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto err;
+ }
+ free(t);
+ t = NULL;
+ }
+ if (u->range != NULL) {
+ if ((range = poldiff_range_to_string_brief(diff, u->range)) == NULL
+ || (apol_str_appendf(&s, &len, "%s", range) < 0)) {
+ free(range);
+ goto err;
+ }
+ free(range);
+ }
+ return s;
+ err:
+ free(s);
+ free(t);
+ return NULL;
+}
+
+char *poldiff_user_to_string(const poldiff_t * diff, const void *user)
+{
+ poldiff_user_t *u = (poldiff_user_t *) user;
+ size_t len = 0;
+ char *s = NULL;
+ if (diff == NULL || user == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (u->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", u->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", u->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if ((s = user_to_modified_string(diff, u)) == NULL) {
+ 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;
+}
+
+const apol_vector_t *poldiff_get_user_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->user_diffs->diffs;
+}
+
+const char *poldiff_user_get_name(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->name;
+}
+
+poldiff_form_e poldiff_user_get_form(const void *user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return ((const poldiff_user_t *)user)->form;
+}
+
+const apol_vector_t *poldiff_user_get_unmodified_roles(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->unmodified_roles;
+}
+
+const apol_vector_t *poldiff_user_get_added_roles(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->added_roles;
+}
+
+const apol_vector_t *poldiff_user_get_removed_roles(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->removed_roles;
+}
+
+const poldiff_level_t *poldiff_user_get_original_dfltlevel(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->orig_default_level;
+}
+
+const poldiff_level_t *poldiff_user_get_modified_dfltlevel(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->mod_default_level;
+}
+
+const poldiff_range_t *poldiff_user_get_range(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->range;
+}
+
+/*************** protected functions for users ***************/
+
+static void user_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_user_t *u = (poldiff_user_t *) elem;
+ free(u->name);
+ apol_vector_destroy(&u->added_roles);
+ apol_vector_destroy(&u->removed_roles);
+ apol_vector_destroy(&u->unmodified_roles);
+ level_free(u->orig_default_level);
+ level_free(u->mod_default_level);
+ range_destroy(&u->range);
+ free(u);
+ }
+}
+
+poldiff_user_summary_t *user_create(void)
+{
+ poldiff_user_summary_t *us = calloc(1, sizeof(*us));
+ if (us == NULL) {
+ return NULL;
+ }
+ if ((us->diffs = apol_vector_create(user_free)) == NULL) {
+ user_destroy(&us);
+ return NULL;
+ }
+ return us;
+}
+
+void user_destroy(poldiff_user_summary_t ** us)
+{
+ if (us != NULL && *us != NULL) {
+ apol_vector_destroy(&(*us)->diffs);
+ free(*us);
+ *us = NULL;
+ }
+}
+
+int user_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ user_destroy(&diff->user_diffs);
+ diff->user_diffs = user_create();
+ if (diff->user_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Comparison function for two users from the same policy.
+ */
+static int user_name_comp(const void *x, const void *y, void *arg)
+{
+ const qpol_user_t *u1 = x;
+ const qpol_user_t *u2 = y;
+ apol_policy_t *p = (apol_policy_t *) arg;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ const char *name1, *name2;
+ if (qpol_user_get_name(q, u1, &name1) < 0 || qpol_user_get_name(q, u2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+apol_vector_t *user_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+ if (qpol_policy_get_user_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create_from_iter(iter, NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort(v, user_name_comp, (void *)policy);
+ return v;
+}
+
+int user_comp(const void *x, const void *y, const poldiff_t * diff)
+{
+ const qpol_user_t *u1 = x;
+ const qpol_user_t *u2 = y;
+ const char *name1, *name2;
+ if (qpol_user_get_name(diff->orig_qpol, u1, &name1) < 0 || qpol_user_get_name(diff->mod_qpol, u2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+/**
+ * Allocate and return a new user difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the user that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling user_free() upon the returned
+ * value.
+ */
+static poldiff_user_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_user_t *pu;
+ int error;
+ if ((pu = calloc(1, sizeof(*pu))) == NULL ||
+ (pu->name = strdup(name)) == NULL ||
+ (pu->added_roles = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pu->removed_roles = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pu->unmodified_roles = apol_vector_create_with_capacity(1, free)) == NULL) {
+ error = errno;
+ user_free(pu);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pu->form = form;
+ return pu;
+}
+
+/**
+ * Given a user, return a vector of its allowed roles (in the form of
+ * strings).
+ *
+ * @param diff Policy diff error handler.
+ * @param p Policy from which the user came.
+ * @param user User whose roles to get.
+ *
+ * @return Vector of role strings for the user. The caller is
+ * responsible for calling apol_vector_destroy(). On error, return
+ * NULL.
+ */
+static apol_vector_t *user_get_roles(const poldiff_t * diff, const apol_policy_t * p, const qpol_user_t * user)
+{
+ qpol_iterator_t *iter = NULL;
+ const qpol_role_t *role;
+ const char *role_name;
+ char *role_name2;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1, error = 0;
+
+ if ((v = apol_vector_create(free)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (qpol_user_get_role_iter(q, user, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&role) < 0 || qpol_role_get_name(q, role, &role_name)) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((role_name2 = strdup(role_name)) == NULL || apol_vector_append(v, (void *)role_name2) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ free(role_name2);
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ if (retval < 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+/**
+ * Perform a deep diff of the default MLS levels assigned to the two
+ * users.
+ *
+ * @param diff Diff structure containing the original and modified
+ * policies.
+ * @param u1 User from original policy to examine, or NULL if the user
+ * was added.
+ * @param u2 User from modified policy to examine, or NULL if the user
+ * was removed.
+ * @param u Result structure where differences are to be recorded.
+ *
+ * @return Greater than zero if a diff was found, zero if none found,
+ * less than zero for errors.
+ */
+static int user_deep_diff_default_levels(poldiff_t * diff, const qpol_user_t * u1, const qpol_user_t * u2, poldiff_user_t * u)
+{
+ const qpol_mls_level_t *ql1 = NULL, *ql2 = NULL;
+ poldiff_level_t *pl = NULL;
+ apol_mls_level_t *l1 = NULL, *l2 = NULL;
+ int retval = -1;
+ if (u1 != NULL && qpol_user_get_dfltlevel(diff->orig_qpol, u1, &ql1) < 0) {
+ return -1;
+ }
+ if (u2 != NULL && qpol_user_get_dfltlevel(diff->mod_qpol, u2, &ql2) < 0) {
+ return -1;
+ }
+ if (ql1 == NULL && ql2 == NULL) {
+ /* neither policy is MLS */
+ return 0;
+ }
+ if (ql1 == NULL) {
+ if ((l2 = apol_mls_level_create_from_qpol_mls_level(diff->mod_pol, ql2)) == NULL ||
+ (pl = level_create_from_apol_mls_level(l2, POLDIFF_FORM_ADDED)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ u->mod_default_level = pl;
+ retval = 1;
+ } else if (ql2 == NULL) {
+ if ((l1 = apol_mls_level_create_from_qpol_mls_level(diff->orig_pol, ql1)) == NULL ||
+ (pl = level_create_from_apol_mls_level(l1, POLDIFF_FORM_REMOVED)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ u->orig_default_level = pl;
+ retval = 1;
+ } else {
+ if ((l1 = apol_mls_level_create_from_qpol_mls_level(diff->orig_pol, ql1)) == NULL ||
+ (l2 = apol_mls_level_create_from_qpol_mls_level(diff->mod_pol, ql2)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (level_deep_diff_apol_mls_levels(diff, l1, l2, &u->orig_default_level, &u->mod_default_level) < 0) {
+ goto cleanup;
+ }
+ if (u->orig_default_level != NULL) {
+ retval = 1;
+ }
+ }
+ if (retval == -1) {
+ /* if reach this point, then no differences were found */
+ retval = 0;
+ }
+ cleanup:
+ apol_mls_level_destroy(&l1);
+ apol_mls_level_destroy(&l2);
+ if (retval < 0) {
+ level_free(pl);
+ }
+ return retval;
+}
+
+/**
+ * Perform a deep diff of the MLS ranges assigned to the two users.
+ *
+ * @param diff Diff structure containing the original and modified
+ * policies.
+ * @param u1 User from original policy to examine, or NULL if the user
+ * was added.
+ * @param u2 User from modified policy to examine, or NULL if the user
+ * was removed.
+ * @param u Result structure where differences are to be recorded.
+ *
+ * @return Greater than zero if a diff was found, zero if none found,
+ * less than zero for errors.
+ */
+static int user_deep_diff_ranges(poldiff_t * diff, const qpol_user_t * u1, const qpol_user_t * u2, poldiff_user_t * u)
+{
+ const qpol_mls_range_t *r1 = NULL, *r2 = NULL;
+ poldiff_range_t *pr = NULL;
+ int retval = -1;
+ if (u1 != NULL && qpol_user_get_range(diff->orig_qpol, u1, &r1) < 0) {
+ return -1;
+ }
+ if (u2 != NULL && qpol_user_get_range(diff->mod_qpol, u2, &r2) < 0) {
+ return -1;
+ }
+ if (r1 == NULL && r2 == NULL) {
+ /* neither policy is MLS */
+ return 0;
+ }
+ if (r1 == NULL) {
+ if ((pr = range_create(diff, r1, r2, POLDIFF_FORM_ADDED)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ u->range = pr;
+ pr = NULL;
+ retval = 1;
+ } else if (r2 == NULL) {
+ if ((pr = range_create(diff, r1, r2, POLDIFF_FORM_REMOVED)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ u->range = pr;
+ pr = NULL;
+ retval = 1;
+ } else {
+ if ((pr = range_create(diff, r1, r2, POLDIFF_FORM_MODIFIED)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if ((retval = range_deep_diff(diff, pr)) < 0) {
+ goto cleanup;
+ }
+ if (retval > 0) {
+ u->range = pr;
+ pr = NULL;
+ }
+ }
+ cleanup:
+ range_destroy(&pr);
+ return retval;
+}
+
+int user_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const qpol_user_t *u = item;
+ const char *name = NULL;
+ apol_vector_t *v = NULL;
+ poldiff_user_t *pu = NULL;
+ int error = 0, retval = -1;
+ if ((form == POLDIFF_FORM_ADDED &&
+ qpol_user_get_name(diff->mod_qpol, u, &name) < 0) ||
+ ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_user_get_name(diff->orig_qpol, u, &name) < 0))
+ {
+ error = errno;
+ goto cleanup;
+ }
+ if ((pu = make_diff(diff, form, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ apol_vector_destroy(&pu->added_roles);
+ if ((v = user_get_roles(diff, diff->mod_pol, u)) == NULL ||
+ (pu->added_roles = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL ||
+ user_deep_diff_default_levels(diff, NULL, u, pu) < 0 || user_deep_diff_ranges(diff, NULL, u, pu) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ } else {
+ apol_vector_destroy(&pu->removed_roles);
+ if ((v = user_get_roles(diff, diff->orig_pol, u)) == NULL ||
+ (pu->removed_roles = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL ||
+ user_deep_diff_default_levels(diff, u, NULL, pu) < 0 || user_deep_diff_ranges(diff, u, NULL, pu) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_append(diff->user_diffs->diffs, pu) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->user_diffs->num_added++;
+ } else {
+ diff->user_diffs->num_removed++;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v);
+ if (retval < 0) {
+ user_free(pu);
+ errno = error;
+ }
+ return retval;
+}
+
+/**
+ * Perform a deep diff of the roles assigned to the two users.
+ *
+ * @param diff Diff structure containing the original and modified
+ * policies.
+ * @param u1 User from original policy to examine.
+ * @param u2 User from modified policy to examine.
+ * @param u Result structure where differences are to be recorded.
+ *
+ * @return Greater than zero if a diff was found, zero if none found,
+ * less than zero for errors.
+ */
+static int user_deep_diff_roles(poldiff_t * diff, const qpol_user_t * u1, const qpol_user_t * u2, poldiff_user_t * u)
+{
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ char *role1, *role2;
+ size_t i, j;
+ int retval = -1, error = 0, compval;
+
+ if ((v1 = user_get_roles(diff, diff->orig_pol, u1)) == NULL || (v2 = user_get_roles(diff, diff->mod_pol, u2)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ 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 (compval < 0) {
+ if ((role1 = strdup(role1)) == NULL || apol_vector_append(u->removed_roles, role1) < 0) {
+ error = errno;
+ free(role1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ } else if (compval > 0) {
+ if ((role2 = strdup(role2)) == NULL || apol_vector_append(u->added_roles, role2) < 0) {
+ error = errno;
+ free(role2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ j++;
+ } else {
+ if ((role1 = strdup(role1)) == NULL || apol_vector_append(u->unmodified_roles, role1) < 0) {
+ error = errno;
+ free(role1);
+ 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 ((role1 = strdup(role1)) == NULL || apol_vector_append(u->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 ((role2 = strdup(role2)) == NULL || apol_vector_append(u->added_roles, role2) < 0) {
+ error = errno;
+ free(role2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_get_size(u->removed_roles) > 0 || apol_vector_get_size(u->added_roles) > 0) {
+ retval = 1;
+ } else {
+ retval = 0;
+ }
+ cleanup:
+ apol_vector_destroy(&v1);
+ apol_vector_destroy(&v2);
+ errno = error;
+ return retval;
+}
+
+int user_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const qpol_user_t *u1 = x;
+ const qpol_user_t *u2 = y;
+ const char *name;
+ poldiff_user_t *u = NULL;
+ int retval = -1, r1 = 0, r2 = 0, r3 = 0, error = 0;
+ if (qpol_user_get_name(diff->orig_qpol, u1, &name) < 0 || (u = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((r1 = user_deep_diff_roles(diff, u1, u2, u)) < 0 || (r2 = user_deep_diff_default_levels(diff, u1, u2, u)) < 0 ||
+ (r3 = user_deep_diff_ranges(diff, u1, u2, u)) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (r1 > 0 || r2 > 0 || r3 > 0) {
+ if (apol_vector_append(diff->user_diffs->diffs, u) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->user_diffs->num_modified++;
+ } else {
+ /* no differences found */
+ user_free(u);
+ }
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ user_free(u);
+ }
+ errno = error;
+ return retval;
+}