/** * @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 #include "sefs_internal.hh" #include #include #include #include #include #include #include #include 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("<>")) == 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; } }