summaryrefslogtreecommitdiffstats
path: root/libsefs/src/fclist.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libsefs/src/fclist.cc')
-rw-r--r--libsefs/src/fclist.cc766
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;
+ }
+}