diff options
Diffstat (limited to 'libapol/src/mls_level.c')
-rw-r--r-- | libapol/src/mls_level.c | 771 |
1 files changed, 771 insertions, 0 deletions
diff --git a/libapol/src/mls_level.c b/libapol/src/mls_level.c new file mode 100644 index 0000000..26a1469 --- /dev/null +++ b/libapol/src/mls_level.c @@ -0,0 +1,771 @@ +/** + * @file + * Implementation of apol_mls_level class. + * + * @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 <apol/mls_level.h> + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "policy-query-internal.h" + +#include <qpol/iterator.h> +#include <apol/vector.h> + +struct apol_mls_level +{ + char *sens; + apol_vector_t *cats; // if NULL, then level is incomplete + char *literal_cats; +}; + +/********************* miscellaneous routines *********************/ + +/* Given a category datum and a category name, returns < 0 if a has + * higher value than b, > 0 if b is higher according to the given + * policy. If the two are equal or upon error, return 0. + */ +static int apol_mls_cat_vector_compare(const void *a, const void *b, void *data) +{ + const qpol_cat_t *cat1 = a; + const char *cat2_name = b; + apol_policy_t *p = (apol_policy_t *) data; + const qpol_cat_t *cat2; + uint32_t cat_value1, cat_value2; + if (qpol_policy_get_cat_by_name(p->p, cat2_name, &cat2) < 0) { + return 0; + } + if (qpol_cat_get_value(p->p, cat1, &cat_value1) < 0 || qpol_cat_get_value(p->p, cat2, &cat_value2) < 0) { + return 0; + } + return (cat_value2 - cat_value1); +} + +/** + * Given two category names, returns < 0 if a has higher value than b, + * > 0 if b is higher. The comparison is against the categories' + * values according to the supplied policy. If the two are equal or + * upon error, return 0. + * + * @param a First category name to compare. + * @param b Other name to compare. + * @param data Pointer to a policy to which use for comparison. + * + * @return <0, 0, or >0 if a is less than, equal, or greater than b, + * respectively. + */ +static int apol_mls_cat_name_compare(const void *a, const void *b, void *data) +{ + const char *cat1 = a; + const char *cat2 = b; + apol_policy_t *p = (apol_policy_t *) data; + const qpol_cat_t *qcat1, *qcat2; + uint32_t cat_value1, cat_value2; + if (qpol_policy_get_cat_by_name(p->p, cat1, &qcat1) < 0 || qpol_policy_get_cat_by_name(p->p, cat2, &qcat2) < 0) { + return 0; + } + if (qpol_cat_get_value(p->p, qcat1, &cat_value1) < 0 || qpol_cat_get_value(p->p, qcat2, &cat_value2) < 0) { + return 0; + } + return (cat_value1 - cat_value2); +} + +/********************* level *********************/ + +apol_mls_level_t *apol_mls_level_create(void) +{ + apol_mls_level_t *l; + if ((l = calloc(1, sizeof(*l))) == NULL || (l->cats = apol_vector_create(free)) == NULL) { + apol_mls_level_destroy(&l); + return NULL; + } + return l; +} + +apol_mls_level_t *apol_mls_level_create_from_mls_level(const apol_mls_level_t * level) +{ + apol_mls_level_t *l; + if ((l = calloc(1, sizeof(*l))) == NULL) { + return NULL; + } + if (level != NULL) { + if ((level->sens != NULL) && (l->sens = strdup(level->sens)) == NULL) { + apol_mls_level_destroy(&l); + return NULL; + } + if ((level->cats != NULL) && + (l->cats = apol_vector_create_from_vector(level->cats, apol_str_strdup, NULL, free)) == NULL) { + apol_mls_level_destroy(&l); + return NULL; + } + if ((level->literal_cats != NULL) && (l->literal_cats = strdup(level->literal_cats)) == NULL) { + apol_mls_level_destroy(&l); + return NULL; + } + } + return l; +} + +apol_mls_level_t *apol_mls_level_create_from_string(const apol_policy_t * p, const char *mls_level_string) +{ + if (p == NULL || mls_level_string == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + + apol_mls_level_t *l = apol_mls_level_create_from_literal(mls_level_string); + if (l == NULL) { + ERR(p, "%s", strerror(errno)); + return NULL; + } + + if (apol_mls_level_convert(p, l) < 0) { + apol_mls_level_destroy(&l); + return NULL; + } + free(l->literal_cats); + l->literal_cats = NULL; + return l; +} + +apol_mls_level_t *apol_mls_level_create_from_literal(const char *mls_level_string) +{ + apol_mls_level_t *l; + char *colon; + if (mls_level_string == NULL) { + errno = EINVAL; + return NULL; + } + if ((l = calloc(1, sizeof(*l))) == NULL) { + return NULL; + } + if ((colon = strchr(mls_level_string, ':')) != NULL) { + // both a sensitivity and 1 or more categories + if (colon == mls_level_string) { + apol_mls_level_destroy(&l); + errno = EINVAL; + return NULL; + } + if ((l->sens = strndup(mls_level_string, colon - mls_level_string)) == NULL) { + apol_mls_level_destroy(&l); + return NULL; + } + // store everything after the colon as the category string + if ((l->literal_cats = strdup(colon + 1)) == NULL) { + apol_mls_level_destroy(&l); + return NULL; + } + apol_str_trim(l->literal_cats); + } else { + // no category, just a sensitivity + if ((l->sens = strdup(mls_level_string)) == NULL || (l->literal_cats = strdup("")) == NULL) { + apol_mls_level_destroy(&l); + return NULL; + } + } + apol_str_trim(l->sens); + return l; +} + +apol_mls_level_t *apol_mls_level_create_from_qpol_mls_level(const apol_policy_t * p, const qpol_mls_level_t * qpol_level) +{ + apol_mls_level_t *lvl = NULL; + qpol_iterator_t *iter = NULL; + const qpol_cat_t *tmp_cat = NULL; + const char *tmp = NULL; + int error = 0; + + if (!p || !qpol_level) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + goto err; + } + + if ((lvl = apol_mls_level_create()) == NULL) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + if (qpol_mls_level_get_sens_name(p->p, qpol_level, &tmp) || qpol_mls_level_get_cat_iter(p->p, qpol_level, &iter)) { + error = errno; + goto err; + } + if (apol_mls_level_set_sens(p, lvl, tmp) < 0) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&tmp_cat) < 0 || qpol_cat_get_name(p->p, tmp_cat, &tmp) < 0) { + error = errno; + goto err; + } + if (apol_mls_level_append_cats(p, lvl, tmp) < 0) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + } + + qpol_iterator_destroy(&iter); + return lvl; + + err: + apol_mls_level_destroy(&lvl); + qpol_iterator_destroy(&iter); + errno = error; + return NULL; +} + +apol_mls_level_t *apol_mls_level_create_from_qpol_level_datum(const apol_policy_t * p, const qpol_level_t * qpol_level) +{ + apol_mls_level_t *lvl = NULL; + qpol_iterator_t *iter = NULL; + const qpol_cat_t *tmp_cat = NULL; + const char *tmp = NULL; + int error = 0; + + if (!p || !qpol_level) { + errno = EINVAL; + return NULL; + } + + if ((lvl = apol_mls_level_create()) == NULL) { + ERR(p, "%s", strerror(error)); + return NULL; + } + if (qpol_level_get_name(p->p, qpol_level, &tmp)) { + error = errno; + goto err; + } + if ((lvl->sens = strdup(tmp)) == NULL) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + + if (qpol_level_get_cat_iter(p->p, qpol_level, &iter)) { + error = errno; + goto err; + } + + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&tmp_cat)) { + error = errno; + goto err; + } + if (qpol_cat_get_name(p->p, tmp_cat, &tmp)) { + error = errno; + goto err; + } + if (apol_mls_level_append_cats(p, lvl, tmp)) { + error = errno; + goto err; + } + } + qpol_iterator_destroy(&iter); + return lvl; + + err: + apol_mls_level_destroy(&lvl); + qpol_iterator_destroy(&iter); + errno = error; + return NULL; +} + +static void mls_level_free(void *level) +{ + if (level != NULL) { + apol_mls_level_t *l = level; + free(l->sens); + apol_vector_destroy(&l->cats); + free(l->literal_cats); + free(l); + } +} + +void apol_mls_level_destroy(apol_mls_level_t ** level) +{ + if (!level || !(*level)) + return; + mls_level_free(*level); + *level = NULL; +} + +int apol_mls_level_set_sens(const apol_policy_t * p, apol_mls_level_t * level, const char *sens) +{ + if (!level) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + return apol_query_set(p, &level->sens, NULL, sens); +} + +const char *apol_mls_level_get_sens(const apol_mls_level_t * level) +{ + if (!level) { + errno = EINVAL; + return NULL; + } + return level->sens; +} + +int apol_mls_level_append_cats(const apol_policy_t * p, apol_mls_level_t * level, const char *cats) +{ + char *new_cat = NULL; + if (!level || !cats || level->cats == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + if (level->cats == NULL && (level->cats = apol_vector_create(free)) == NULL) { + ERR(p, "%s", strerror(errno)); + return -1; + } + if ((new_cat = strdup(cats)) == NULL || apol_vector_append(level->cats, (void *)new_cat) < 0) { + ERR(p, "%s", strerror(errno)); + free(new_cat); + return -1; + } + apol_vector_sort(level->cats, apol_str_strcmp, NULL); + return 0; +} + +const apol_vector_t *apol_mls_level_get_cats(const apol_mls_level_t * level) +{ + if (!level || level->cats == NULL) { + errno = EINVAL; + return NULL; + } + return level->cats; +} + +int apol_mls_level_compare(const apol_policy_t * p, const apol_mls_level_t * l1, const apol_mls_level_t * l2) +{ + const qpol_level_t *level_datum1, *level_datum2; + int level1_sens, level2_sens, sens_cmp; + size_t l1_size, l2_size, i, j; + int m_list, ucat = 0; + apol_vector_t *cat_list_master, *cat_list_subset; + if (l2 == NULL) { + return APOL_MLS_EQ; + } + if ((l1 != NULL && l1->cats == NULL) || (l2->cats == NULL)) { + errno = EINVAL; + return -1; + } + if (qpol_policy_get_level_by_name(p->p, l1->sens, &level_datum1) < 0 || + qpol_policy_get_level_by_name(p->p, l2->sens, &level_datum2) < 0) { + return -1; + } + + /* compare the level's senstitivity value */ + if (qpol_level_get_value(p->p, level_datum1, (uint32_t *) (&level1_sens)) < 0 || + qpol_level_get_value(p->p, level_datum2, (uint32_t *) (&level2_sens)) < 0) { + return -1; + } + sens_cmp = level1_sens - level2_sens; + + /* determine if all the categories in one level are in the other set */ + l1_size = apol_vector_get_size(l1->cats); + l2_size = apol_vector_get_size(l2->cats); + if (l1_size < l2_size) { + m_list = 2; + cat_list_master = l2->cats; + cat_list_subset = l1->cats; + } else { + m_list = 1; + cat_list_master = l1->cats; + cat_list_subset = l2->cats; + } + for (i = 0; i < apol_vector_get_size(cat_list_subset); i++) { + char *cat = (char *)apol_vector_get_element(cat_list_subset, i); + if (apol_vector_get_index(cat_list_master, cat, apol_mls_cat_name_compare, (void *)p, &j) < 0) { + ucat = 1; + break; + } + } + + if (!sens_cmp && !ucat && l1_size == l2_size) + return APOL_MLS_EQ; + if (sens_cmp >= 0 && m_list == 1 && !ucat) + return APOL_MLS_DOM; + if (sens_cmp <= 0 && (m_list == 2 || l1_size == l2_size) && !ucat) + return APOL_MLS_DOMBY; + return APOL_MLS_INCOMP; +} + +int apol_mls_level_validate(const apol_policy_t * p, const apol_mls_level_t * level) +{ + const qpol_level_t *level_datum; + qpol_iterator_t *iter = NULL; + apol_vector_t *cat_vector; + int retval = -1; + size_t i, j; + + if (p == NULL || level == NULL || level->cats == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + if (level->sens == NULL) { + return 0; + } + if (qpol_policy_get_level_by_name(p->p, level->sens, &level_datum) < 0 || + qpol_level_get_cat_iter(p->p, level_datum, &iter) < 0) { + return -1; + } + if ((cat_vector = apol_vector_create_from_iter(iter, NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + + for (i = 0; i < apol_vector_get_size(level->cats); i++) { + char *cat_name = (char *)apol_vector_get_element(level->cats, i); + if (apol_vector_get_index(cat_vector, cat_name, apol_mls_cat_vector_compare, (void *)p, &j) < 0) { + retval = 0; + goto cleanup; + } + } + + retval = 1; + cleanup: + qpol_iterator_destroy(&iter); + apol_vector_destroy(&cat_vector); + return retval; +} + +char *apol_mls_level_render(const apol_policy_t * p, const apol_mls_level_t * level) +{ + char *rt = NULL; + const char *name = NULL, *sens_name = NULL, *cat_name = NULL; + char *retval = NULL; + int cur; + const qpol_cat_t *cur_cat = NULL, *next_cat = NULL; + uint32_t cur_cat_val, next_cat_val, far_cat_val; + apol_vector_t *cats = NULL; + size_t sz = 0, n_cats = 0, i; + + if (!level || (p == NULL && level->cats != NULL)) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + goto cleanup; + } + + sens_name = level->sens; + if (!sens_name) + goto cleanup; + if (apol_str_append(&rt, &sz, sens_name)) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + + if (level->cats != NULL) { + if ((cats = apol_vector_create_from_vector(level->cats, apol_str_strdup, NULL, free)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + n_cats = apol_vector_get_size(cats); + } + if (n_cats == 0) { + if (level->literal_cats != NULL && level->literal_cats[0] != '\0') { + if (apol_str_appendf(&rt, &sz, ":%s", level->literal_cats)) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + + } + retval = rt; + goto cleanup; + } + apol_vector_sort(cats, apol_mls_cat_name_compare, (void *)p); + + cat_name = (char *)apol_vector_get_element(cats, 0); + if (!cat_name) + goto cleanup; + + if (apol_str_appendf(&rt, &sz, ":%s", cat_name)) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + cur = 0; /* current value to compare with cat[i] */ + for (i = 1; i < n_cats; i++) { /* we've already appended the first category */ + /* get the value of cats[cur] */ + cat_name = (char *)apol_vector_get_element(cats, cur); + if (qpol_policy_get_cat_by_name(p->p, cat_name, &cur_cat)) + goto cleanup; + if (qpol_cat_get_value(p->p, cur_cat, &cur_cat_val)) + goto cleanup; + + /* get the value of cats[i] */ + cat_name = (char *)apol_vector_get_element(cats, i); + if (qpol_policy_get_cat_by_name(p->p, cat_name, &next_cat)) + goto cleanup; + if (qpol_cat_get_value(p->p, next_cat, &next_cat_val)) + goto cleanup; + + if (next_cat_val == cur_cat_val + 1) { + if (i + 1 == n_cats) { /* last category is next; append "." */ + if (qpol_cat_get_name(p->p, next_cat, &name)) + goto cleanup; + if (apol_str_appendf(&rt, &sz, ".%s", name)) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + cur = i; + } else { + const qpol_cat_t *far_cat = NULL; /* category 2 in front of cur */ + cat_name = (char *)apol_vector_get_element(cats, i + 1); + if (qpol_policy_get_cat_by_name(p->p, cat_name, &far_cat)) + goto cleanup; + if (qpol_cat_get_value(p->p, far_cat, &far_cat_val)) + goto cleanup; + if (far_cat_val == cur_cat_val + 2) { + cur++; + } else { /* far_cat isn't consecutive wrt cur/next_cat; append it */ + if (qpol_cat_get_name(p->p, next_cat, &name)) + goto cleanup; + if (apol_str_appendf(&rt, &sz, ".%s", name)) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + cur = i; + } + } + } else { /* next_cat isn't consecutive to cur_cat; append it */ + if (qpol_cat_get_name(p->p, next_cat, &name)) + goto cleanup; + if (apol_str_appendf(&rt, &sz, ", %s", name)) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + cur = i; + } + } + + retval = rt; + cleanup: + apol_vector_destroy(&cats); + if (retval != rt) { + free(rt); + } + return retval; +} + +int apol_mls_level_convert(const apol_policy_t * p, apol_mls_level_t * level) +{ + const char *tmp, *cat_name; + char **tokens = NULL, *next = NULL; + size_t num_tokens = 1, i; + qpol_iterator_t *iter = NULL; + const qpol_level_t *sens = NULL; + const qpol_cat_t *cat1 = NULL, *cat2 = NULL, *tmp_cat = NULL; + uint32_t val1 = 0, val2 = 0, tmp_val = 0; + unsigned char tmp_isalias = 0; + + int error = 0; + if (p == NULL || level == NULL || level->literal_cats == NULL) { + error = EINVAL; + ERR(p, "%s", strerror(error)); + goto err; + } + + apol_vector_destroy(&level->cats); + if (level->literal_cats[0] == '\0') { + if ((level->cats = apol_vector_create_with_capacity(1, free)) == NULL) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + return 0; + } + + for (tmp = level->literal_cats; *tmp; tmp++) { + if ((next = strchr(tmp, ','))) { + tmp = next; + num_tokens++; + } + } + tokens = calloc(num_tokens, sizeof(char *)); + if (!tokens) { + error = errno; + ERR(p, "%s", strerror(ENOMEM)); + goto err; + } + if ((level->cats = apol_vector_create_with_capacity(num_tokens, free)) == NULL) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + + for (tmp = level->literal_cats, i = 0; *tmp && i < num_tokens; tmp++) { + if (isspace(*tmp)) + continue; + next = strchr(tmp, ','); + if (next) { + tokens[i] = strndup(tmp, next - tmp); + if (!tokens[i]) { + error = errno; + goto err; + } + tmp = next; + next = NULL; + i++; + } else { + tokens[i] = strdup(tmp); + if (!tokens[i]) { + error = errno; + ERR(p, "%s", strerror(ENOMEM)); + goto err; + } + i++; + if (i != num_tokens) { + error = EIO; + goto err; + } + } + } + + if (qpol_policy_get_level_by_name(p->p, level->sens, &sens)) { + error = errno; + goto err; + } + + for (i = 0; i < num_tokens; i++) { + next = strchr(tokens[i], '.'); + if (next) { + *next = '\0'; + next++; + + /* get end points of cat range */ + if (qpol_policy_get_cat_by_name(p->p, tokens[i], &cat1)) { + error = errno; + goto err; + } + if (qpol_policy_get_cat_by_name(p->p, next, &cat2)) { + error = errno; + goto err; + } + + /* get end point values */ + if (qpol_cat_get_value(p->p, cat1, &val1)) { + error = errno; + goto err; + } + if (qpol_cat_get_value(p->p, cat2, &val2)) { + error = errno; + goto err; + } + if (val1 >= val2) { + error = EINVAL; + ERR(p, "%s", strerror(error)); + goto err; + } + if (apol_mls_level_append_cats(p, level, tokens[i])) { + error = errno; + goto err; + } + if (qpol_policy_get_cat_iter(p->p, &iter)) { + error = errno; + goto err; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&tmp_cat)) { + error = errno; + goto err; + } + if (qpol_cat_get_isalias(p->p, tmp_cat, &tmp_isalias)) { + error = errno; + goto err; + } + if (tmp_isalias) + continue; + if (qpol_cat_get_value(p->p, tmp_cat, &tmp_val)) { + error = errno; + goto err; + } + if (tmp_val > val1 && tmp_val < val2) { + if (qpol_cat_get_name(p->p, tmp_cat, &cat_name)) { + error = errno; + goto err; + } + if (apol_mls_level_append_cats(p, level, cat_name)) { + error = errno; + goto err; + } + } + } + if (apol_mls_level_append_cats(p, level, next)) { + error = errno; + goto err; + } + } else { + if (qpol_policy_get_cat_by_name(p->p, tokens[i], &cat1)) { + error = errno; + goto err; + } + if (apol_mls_level_append_cats(p, level, tokens[i])) { + error = errno; + goto err; + } + } + } + + if (tokens) { + for (i = 0; i < num_tokens; i++) + free(tokens[i]); + free(tokens); + } + + qpol_iterator_destroy(&iter); + return 0; + + err: + if (tokens) { + for (i = 0; i < num_tokens; i++) + free(tokens[i]); + free(tokens); + } + qpol_iterator_destroy(&iter); + errno = error; + return -1; +} + +int apol_mls_level_is_literal(const apol_mls_level_t * level) +{ + if (level == NULL) { + return -1; + } + if (level->literal_cats != NULL) { + return 1; + } + return 0; +} |