diff options
author | Miroslav Grepl <mgrepl@redhat.com> | 2014-04-11 09:37:53 +0200 |
---|---|---|
committer | Miroslav Grepl <mgrepl@redhat.com> | 2014-04-11 09:37:53 +0200 |
commit | 47be9ff57e72906660bb62a515222f482131e1fb (patch) | |
tree | 2cb0ef0ba48d73b1df7cc0915754a17e19464bb6 /libsefs/src/fclist.cc | |
download | setools-master.tar.gz setools-master.tar.xz setools-master.zip |
Create setools-3.3.7 git repomaster
Diffstat (limited to 'libsefs/src/fclist.cc')
-rw-r--r-- | libsefs/src/fclist.cc | 766 |
1 files changed, 766 insertions, 0 deletions
diff --git a/libsefs/src/fclist.cc b/libsefs/src/fclist.cc new file mode 100644 index 0000000..051a6f2 --- /dev/null +++ b/libsefs/src/fclist.cc @@ -0,0 +1,766 @@ +/** + * @file + * Implementation of the sefs_fclist class. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 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 "sefs_internal.hh" + +#include <sefs/entry.hh> +#include <apol/util.h> +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <selinux/context.h> +#include <sys/types.h> + +static int fclist_sefs_context_node_comp(const void *a, const void *b, void *arg __attribute__ ((unused))) +{ + const struct sefs_context_node *n1 = static_cast < const struct sefs_context_node *>(a); + const struct sefs_context_node *n2 = static_cast < const struct sefs_context_node *>(b); + if (n1->type != n2->type) + { + return static_cast < int >(reinterpret_cast < ssize_t > (n1->type) - reinterpret_cast < ssize_t > (n2->type)); + } + if (n1->user != n2->user) + { + return static_cast < int >(reinterpret_cast < ssize_t > (n1->user) - reinterpret_cast < ssize_t > (n2->user)); + } + if (n1->role != n2->role) + { + return static_cast < int >(reinterpret_cast < ssize_t > (n1->role) - reinterpret_cast < ssize_t > (n2->role)); + } + return static_cast < int >(reinterpret_cast < ssize_t > (n1->range) - reinterpret_cast < ssize_t > (n2->range)); +} + +static void fclist_sefs_context_node_free(void *elem) +{ + if (elem != NULL) + { + struct sefs_context_node *node = static_cast < struct sefs_context_node *>(elem); + apol_context_destroy(&node->context); + free(node->context_str); + free(node); + } +} + +static int fclist_sefs_node_make_string(struct sefs_context_node *node) +{ + free(node->context_str); + node->context_str = NULL; + if (node->user[0] == '\0' && node->role[0] == '\0' && node->type[0] == '\0' && + (node->range == NULL || node->range[0] == '\0')) + { + if ((node->context_str = strdup("<<none>>")) == NULL) + { + return -1; + } + } + else + { + // instead of calling apol_context_render(), use a custom + // rendering function if no range is set + char *s = NULL; + if (asprintf(&s, "%s:%s:%s", node->user, node->role, node->type) < 0) + { + return -1; + } + if (node->range != NULL) + { + size_t len = strlen(s) + 1; + if (apol_str_appendf(&s, &len, ":%s", node->range) < 0) + { + free(s); + return -1; + } + } + node->context_str = s; + } + return 0; +} + +static int fclist_sefs_node_convert(void *data, void *arg) +{ + struct sefs_context_node *node = static_cast < struct sefs_context_node *>(data); + sefs_fclist *fclist = static_cast < sefs_fclist * >(arg); + apol_policy_t *p = fclist->associatePolicy(); + if (p != NULL) + { + int retval = apol_context_convert(p, node->context); + if (retval < 0) + { + return retval; + } + if ((retval = fclist_sefs_node_make_string(node)) < 0) + { + return retval; + } + } + return 0; +} + +/******************** public functions below ********************/ + +sefs_fclist::~sefs_fclist() +{ + apol_bst_destroy(&user_tree); + apol_bst_destroy(&role_tree); + apol_bst_destroy(&type_tree); + apol_bst_destroy(&range_tree); + apol_bst_destroy(&path_tree); + apol_bst_destroy(&dev_tree); + apol_bst_destroy(&context_tree); +} + +static int map_to_vector(sefs_fclist * fclist, const sefs_entry * entry, void *data) +{ + apol_vector_t *v = static_cast < apol_vector_t * >(data); + sefs_entry *new_entry = new sefs_entry(entry); + if (apol_vector_append(v, new_entry) < 0) + { + return -1; + } + return 0; +} + +static void fclist_entry_free(void *elem) +{ + if (elem != NULL) + { + sefs_entry *entry = static_cast < sefs_entry * >(elem); + delete entry; + } +} + +apol_vector_t *sefs_fclist::runQuery(sefs_query * query) throw(std::bad_alloc, std::runtime_error, std::invalid_argument) +{ + apol_vector_t *v = NULL; + try + { + if ((v = apol_vector_create(fclist_entry_free)) == NULL) + { + throw std::bad_alloc(); + } + if (runQueryMap(query, map_to_vector, v) < 0) + { + throw std::bad_alloc(); + } + } + catch(...) + { + apol_vector_destroy(&v); + throw; + } + return v; +} + +void sefs_fclist::associatePolicy(apol_policy_t * new_policy) +{ + policy = new_policy; + if (policy != NULL) + { + if (apol_bst_inorder_map(context_tree, fclist_sefs_node_convert, policy) < 0) + { + throw new std::bad_alloc(); + } + } +} + +apol_policy_t *sefs_fclist::associatePolicy() const +{ + return policy; +} + +sefs_fclist_type_e sefs_fclist::fclist_type() const +{ + return _fclist_type; +} + +/******************** protected functions below ********************/ + +sefs_fclist::sefs_fclist(sefs_fclist_type_e type, sefs_callback_fn_t callback, void *varg)throw(std::bad_alloc) +{ + _fclist_type = type; + _callback = callback; + _varg = varg; + policy = NULL; + user_tree = role_tree = type_tree = range_tree = path_tree = NULL; + dev_tree = NULL; + context_tree = NULL; + try + { + if ((user_tree = apol_bst_create(apol_str_strcmp, free)) == NULL) + { + throw std::bad_alloc(); + } + if ((role_tree = apol_bst_create(apol_str_strcmp, free)) == NULL) + { + throw std::bad_alloc(); + } + if ((type_tree = apol_bst_create(apol_str_strcmp, free)) == NULL) + { + throw std::bad_alloc(); + } + if ((range_tree = apol_bst_create(apol_str_strcmp, free)) == NULL) + { + throw std::bad_alloc(); + } + if ((path_tree = apol_bst_create(apol_str_strcmp, free)) == NULL) + { + throw std::bad_alloc(); + } + if ((dev_tree = apol_bst_create(apol_str_strcmp, free)) == NULL) + { + throw std::bad_alloc(); + } + if ((context_tree = apol_bst_create(fclist_sefs_context_node_comp, fclist_sefs_context_node_free)) == NULL) + { + throw std::bad_alloc(); + } + } + catch(...) + { + apol_bst_destroy(&user_tree); + apol_bst_destroy(&role_tree); + apol_bst_destroy(&type_tree); + apol_bst_destroy(&range_tree); + apol_bst_destroy(&path_tree); + apol_bst_destroy(&dev_tree); + apol_bst_destroy(&context_tree); + throw; + } +} + +static void sefs_handle_default_callback(void *arg __attribute__ ((unused)), + const sefs_fclist * f + __attribute__ ((unused)), int level, const char *fmt, va_list va_args) +{ + switch (level) + { + case SEFS_MSG_INFO: + { + /* by default do not display these messages */ + return; + } + case SEFS_MSG_WARN: + { + fprintf(stderr, "WARNING: "); + break; + } + case SEFS_MSG_ERR: + default: + { + fprintf(stderr, "ERROR: "); + break; + } + } + vfprintf(stderr, fmt, va_args); + fprintf(stderr, "\n"); +} + +struct sefs_context_node *sefs_fclist::getContext(const char *user, const char *role, const char *type, + const char *range) throw(std::bad_alloc) +{ + char *u = NULL, *r = NULL, *t = NULL, *m = NULL; + if ((u = strdup(user)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (apol_bst_insert_and_get(user_tree, (void **)&u, NULL) < 0) + { + free(u); + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + + if ((r = strdup(role)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (apol_bst_insert_and_get(role_tree, (void **)&r, NULL) < 0) + { + free(r); + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + + if ((t = strdup(type)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (apol_bst_insert_and_get(type_tree, (void **)&t, NULL) < 0) + { + free(t); + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + + if (range == NULL || range[0] == '\0') + { + m = NULL; + } + else + { + if ((m = strdup(range)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (apol_bst_insert_and_get(range_tree, (void **)&m, NULL) < 0) + { + free(m); + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + } + + struct sefs_context_node *node = NULL; + apol_context_t *context = NULL; + try + { + if ((node = static_cast < struct sefs_context_node * >(calloc(1, sizeof(*node)))) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + + node->user = u; + node->role = r; + node->type = t; + node->range = m; + + void *v; + if (apol_bst_get_element(context_tree, node, NULL, &v) == 0) + { + // context already exists + fclist_sefs_context_node_free(node); + return static_cast < struct sefs_context_node *>(v); + } + + apol_mls_range_t *apol_range = NULL; + if (m != NULL) + { + if ((apol_range = apol_mls_range_create_from_literal(m)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::bad_alloc(); + } + } + + if ((context = apol_context_create()) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + apol_mls_range_destroy(&apol_range); + throw std::runtime_error(strerror(errno)); + } + if (apol_context_set_user(NULL, context, u) < 0 || + apol_context_set_role(NULL, context, r) < 0 || apol_context_set_type(NULL, context, t) < 0 || + apol_context_set_range(NULL, context, apol_range) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + apol_mls_range_destroy(&apol_range); + throw std::runtime_error(strerror(errno)); + } + + node->context = context; + context = NULL; + + if (fclist_sefs_node_make_string(node) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + + if (apol_bst_insert(context_tree, node, NULL) != 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + } + catch(...) + { + fclist_sefs_context_node_free(node); + apol_context_destroy(&context); + throw; + } + + return node; +} + +struct sefs_context_node *sefs_fclist::getContext(const security_context_t scon) throw(std::bad_alloc) +{ + context_t con; + if ((con = context_new(scon)) == 0) + { + throw std::bad_alloc(); + } + const char *user = context_user_get(con); + const char *role = context_role_get(con); + const char *type = context_type_get(con); + const char *range = context_range_get(con); + struct sefs_context_node *node = NULL; + try + { + node = getContext(user, role, type, range); + } + catch(...) + { + context_free(con); + throw; + } + context_free(con); + return node; +} + +/******************** private functions below ********************/ + +void sefs_fclist::handleMsg(int level, const char *fmt, va_list va_args) const +{ + if (_callback == NULL) + { + sefs_handle_default_callback(NULL, this, level, fmt, va_args); + } + else + { + _callback(_varg, this, level, fmt, va_args); + } +} + +/******************** C functions below ********************/ + +void sefs_fclist_handleMsg(const struct sefs_fclist *fclist, int level, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (fclist == NULL) + { + sefs_handle_default_callback(NULL, NULL, level, fmt, ap); + } + else + { + fclist->handleMsg(level, fmt, ap); + } + va_end(ap); +} + +void sefs_fclist_destroy(sefs_fclist_t ** fclist) +{ + if (fclist != NULL && *fclist != NULL) + { + delete(*fclist); + *fclist = NULL; + } +} + +int sefs_fclist_run_query_map(sefs_fclist_t * fclist, sefs_query_t * query, sefs_fclist_map_fn_t fn, void *data) +{ + if (fclist == NULL) + { + SEFS_ERR(NULL, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + int retval; + try + { + retval = fclist->runQueryMap(query, fn, data); + } + catch(...) + { + return -1; + } + return retval; +} + +apol_vector_t *sefs_fclist_run_query(sefs_fclist_t * fclist, sefs_query_t * query) +{ + if (fclist == NULL) + { + SEFS_ERR(NULL, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + apol_vector_t *v = NULL; + try + { + v = fclist->runQuery(query); + } + catch(...) + { + return NULL; + } + return v; +} + +bool sefs_fclist_get_is_mls(const sefs_fclist_t * fclist) +{ + if (fclist == NULL) + { + SEFS_ERR(NULL, "%s", strerror(EINVAL)); + return false; + } + return fclist->isMLS(); +} + +void sefs_fclist_associate_policy(sefs_fclist_t * fclist, apol_policy_t * policy) +{ + if (fclist == NULL) + { + SEFS_ERR(NULL, "%s", strerror(EINVAL)); + errno = EINVAL; + } + else + { + fclist->associatePolicy(policy); + } +} + +sefs_fclist_type_e sefs_fclist_get_fclist_type(const sefs_fclist_t * fclist) +{ + if (fclist == NULL) + { + SEFS_ERR(NULL, "%s", strerror(EINVAL)); + return SEFS_FCLIST_TYPE_NONE; + } + return fclist->fclist_type(); +} + +/******************** private static functions below ********************/ + +/** + * Given a type name, obtain its qpol_type_t pointer (relative to a + * policy). If the type is really its alias, get its primary instead. + * (Attributes are considered to be always primary.) + * + * @param p Policy in which to look up types. + * @param type_name Name of type to find. + * + * @return Qpol datum for type, or NULL if not found. + */ +static const qpol_type_t *query_get_type(apol_policy_t * p, const char *type_name) +{ + unsigned char isalias; + const qpol_type_t *type = NULL; + qpol_policy_t *q = apol_policy_get_qpol(p); + if (qpol_policy_get_type_by_name(q, type_name, &type) < 0 || qpol_type_get_isalias(q, type, &isalias) < 0) + { + return NULL; + } + if (isalias) + { + const char *primary_name; + if (qpol_type_get_name(q, type, &primary_name) < 0 || qpol_policy_get_type_by_name(q, primary_name, &type) < 0) + { + return NULL; + } + } + return type; +} + +/** + * Append a non-aliased type name to a vector. If the passed in type + * is an alias, find its primary type and append that name instead. + * + * @param p Policy in which to look up types. + * @param v Vector in which append the non-aliased type name. + * @param type Type or attribute to append. If this is an alias, + * append its primary. + * + * @return 0 on success, < 0 on error. + */ +static int query_append_type(apol_policy_t * p, apol_vector_t * v, const qpol_type_t * type) +{ + qpol_policy_t *q = apol_policy_get_qpol(p); + unsigned char isalias; + const qpol_type_t *real_type = type; + const char *name; + if (qpol_type_get_isattr(q, type, &isalias) < 0) + { + return -1; + } + if (isalias) + { + if (qpol_type_get_name(q, type, &name) < 0 || qpol_policy_get_type_by_name(q, name, &real_type) < 0) + { + return -1; + } + } + if (qpol_type_get_name(q, type, &name) < 0 || + apol_vector_append(v, const_cast < void *>(static_cast < const void *>(name))) < 0) + { + return -1; + } + return 0; +} + +apol_vector_t *query_create_candidate_type(apol_policy_t * policy, const char *str, const regex_t * regex, const bool regex_flag, + const bool indirect) +{ + qpol_policy_t *q = apol_policy_get_qpol(policy); + apol_vector_t *list = apol_vector_create(NULL); + const qpol_type_t *type; + qpol_iterator_t *iter = NULL, *alias_iter = NULL; + const char *type_name; + bool compval; + + try + { + if (list == NULL) + { + throw new std::bad_alloc(); + } + + if (!regex_flag && (type = query_get_type(policy, str)) != NULL) + { + if (query_append_type(policy, list, type) < 0) + { + throw new std::bad_alloc(); + } + } + + if (regex_flag) + { + if (qpol_policy_get_type_iter(q, &iter) < 0) + { + throw new std::bad_alloc(); + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) + { + if (qpol_iterator_get_item(iter, (void **)&type) < 0 || qpol_type_get_name(q, type, &type_name) < 0) + { + throw new std::runtime_error(strerror(errno)); + } + compval = query_str_compare(type_name, str, regex, true); + if (compval) + { + if (query_append_type(policy, list, type) < 0) + { + throw new std::bad_alloc(); + } + continue; + } + if (qpol_type_get_alias_iter(q, type, &alias_iter) < 0) + { + throw new std::bad_alloc(); + } + for (; !qpol_iterator_end(alias_iter); qpol_iterator_next(alias_iter)) + { + if (qpol_iterator_get_item(alias_iter, (void **)&type_name) < 0) + { + throw new std::runtime_error(strerror(errno)); + } + compval = query_str_compare(type_name, str, regex, true); + if (compval) + { + if (query_append_type(policy, list, type)) + { + throw new std::bad_alloc(); + } + break; + } + } + qpol_iterator_destroy(&alias_iter); + } + qpol_iterator_destroy(&iter); + } + + if (indirect) + { + size_t orig_vector_size = apol_vector_get_size(list); + unsigned char isattr, isalias; + for (size_t i = 0; i < orig_vector_size; i++) + { + type_name = static_cast < char *>(apol_vector_get_element(list, i)); + qpol_policy_get_type_by_name(q, type_name, &type); + assert(type != NULL); + if (qpol_type_get_isalias(q, type, &isalias) < 0 || qpol_type_get_isattr(q, type, &isattr) < 0) + { + throw new std::runtime_error(strerror(errno)); + } + if (isalias) + { + continue; + } + if ((isattr && + qpol_type_get_type_iter(q, type, &iter) < 0) || + (!isattr && qpol_type_get_attr_iter(q, type, &iter) < 0)) + { + throw new std::bad_alloc(); + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) + { + if (qpol_iterator_get_item(iter, (void **)&type) < 0) + { + throw new std::runtime_error(strerror(errno)); + } + if (query_append_type(policy, list, type)) + { + throw new std::bad_alloc(); + } + } + qpol_iterator_destroy(&iter); + } + } + + apol_vector_sort_uniquify(list, NULL, NULL); + } + catch(...) + { + apol_vector_destroy(&list); + } + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&alias_iter); + return list; +} + +bool query_str_compare(const char *target, const char *str, const regex_t * regex, const bool regex_flag) +{ + if (str == NULL || str[0] == '\0') + { + return true; + } + if (target == NULL || target[0] == '\0') + { + return false; + } + if (regex_flag) + { + if (regexec(regex, target, 0, NULL, 0) == 0) + { + return true; + } + return false; + } + else + { + if (strcmp(target, str) == 0) + { + return true; + } + return false; + } +} |