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