summaryrefslogtreecommitdiffstats
path: root/libsefs/src/fcfile.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libsefs/src/fcfile.cc')
-rw-r--r--libsefs/src/fcfile.cc691
1 files changed, 691 insertions, 0 deletions
diff --git a/libsefs/src/fcfile.cc b/libsefs/src/fcfile.cc
new file mode 100644
index 0000000..fcd4096
--- /dev/null
+++ b/libsefs/src/fcfile.cc
@@ -0,0 +1,691 @@
+/**
+ * @file
+ * Implementation of the sefs_fcfile 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 <sefs/fcfile.hh>
+#include <apol/util.h>
+#include <qpol/genfscon_query.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <regex.h>
+#include <stdio.h>
+
+/******************** public functions below ********************/
+
+static void fcfile_entry_free(void *elem)
+{
+ if (elem != NULL)
+ {
+ sefs_entry *entry = static_cast < sefs_entry * >(elem);
+ delete entry;
+ }
+}
+
+sefs_fcfile::sefs_fcfile(sefs_callback_fn_t msg_callback, void *varg) throw(std::bad_alloc):sefs_fclist(SEFS_FCLIST_TYPE_FCFILE,
+ msg_callback, varg)
+{
+ _files = _entries = NULL;
+ _mls_set = false;
+ try
+ {
+ if ((_files = apol_vector_create(free)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ if ((_entries = apol_vector_create(fcfile_entry_free)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ }
+ catch(...)
+ {
+ apol_vector_destroy(&_files);
+ apol_vector_destroy(&_entries);
+ throw;
+ }
+}
+
+sefs_fcfile::sefs_fcfile(const char *file, sefs_callback_fn_t msg_callback, void *varg) throw(std::bad_alloc, std::invalid_argument,
+ std::
+ runtime_error):sefs_fclist
+ (SEFS_FCLIST_TYPE_FCFILE, msg_callback, varg)
+{
+ _files = _entries = NULL;
+ _mls_set = false;
+ try
+ {
+ if ((_files = apol_vector_create_with_capacity(1, free)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ if ((_entries = apol_vector_create(fcfile_entry_free)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ if (appendFile(file) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error("Could not construct fcfile with the given file.");
+ }
+ }
+ catch(...)
+ {
+ apol_vector_destroy(&_files);
+ apol_vector_destroy(&_entries);
+ throw;
+ }
+}
+
+sefs_fcfile::sefs_fcfile(const apol_vector_t * files, sefs_callback_fn_t msg_callback, void *varg) throw(std::bad_alloc,
+ std::invalid_argument,
+ std::
+ runtime_error):sefs_fclist
+ (SEFS_FCLIST_TYPE_FCFILE, msg_callback, varg)
+{
+ _files = _entries = NULL;
+ _mls_set = false;
+ try
+ {
+ if (files == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ throw std::invalid_argument(strerror(EINVAL));
+ }
+ if ((_files = apol_vector_create_with_capacity(apol_vector_get_size(files), free)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ if ((_entries = apol_vector_create(fcfile_entry_free)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ if (appendFileList(files) != apol_vector_get_size(files))
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error("Could not construct fcfile with the given vector.");
+ }
+ }
+ catch(...)
+ {
+ apol_vector_destroy(&_files);
+ apol_vector_destroy(&_entries);
+ throw;
+ }
+}
+
+sefs_fcfile::~sefs_fcfile()
+{
+ apol_vector_destroy(&_files);
+ apol_vector_destroy(&_entries);
+}
+
+int sefs_fcfile::runQueryMap(sefs_query * query, sefs_fclist_map_fn_t fn, void *data) throw(std::runtime_error,
+ std::invalid_argument)
+{
+ apol_vector_t *type_list = NULL;
+ apol_mls_range_t *range = NULL;
+ int retval = 0;
+ try
+ {
+ if (query != NULL)
+ {
+ query->compile();
+ if (policy != NULL)
+ {
+ if (query->_type != NULL && query->_indirect &&
+ (type_list =
+ query_create_candidate_type(policy, query->_type, query->_retype, query->_regex,
+ query->_indirect)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (query->_range != NULL && query->_rangeMatch != 0 &&
+ (range = apol_mls_range_create_from_string(policy, query->_range)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ }
+ }
+
+ for (size_t i = 0; i < apol_vector_get_size(_entries); i++)
+ {
+ sefs_entry *e = static_cast < sefs_entry * >(apol_vector_get_element(_entries, i));
+ if (query != NULL)
+ {
+ const struct sefs_context_node *context = e->_context;
+ if (!query_str_compare(context->user, query->_user, query->_reuser, query->_regex))
+ {
+ continue;
+ }
+ if (!query_str_compare(context->role, query->_role, query->_rerole, query->_regex))
+ {
+ continue;
+ }
+
+ bool str_matched = false, pol_matched = false;
+ str_matched = query_str_compare(context->type, query->_type, query->_retype, query->_regex);
+ if (type_list != NULL && !str_matched)
+ {
+ size_t index;
+ pol_matched =
+ (apol_vector_get_index(type_list, context->type, apol_str_strcmp, NULL, &index) <
+ 0);
+ }
+ if (!str_matched && !pol_matched)
+ {
+ continue;
+ }
+
+ if (isMLS())
+ {
+ if (range == NULL)
+ {
+ if (!query_str_compare
+ (context->range, query->_range, query->_rerange, query->_regex))
+ {
+ continue;
+ }
+ }
+ else
+ {
+ const apol_mls_range_t *context_range = apol_context_get_range(context->context);
+ int ret;
+ ret = apol_mls_range_compare(policy, context_range, range, query->_rangeMatch);
+ if (ret <= 0)
+ {
+ continue;
+ }
+ }
+ }
+
+ if (e->_objectClass != QPOL_CLASS_ALL && query->_objclass != QPOL_CLASS_ALL &&
+ e->_objectClass != query->_objclass)
+ {
+ continue;
+ }
+
+ bool path_matched;
+
+ if (query->_path == NULL || query->_path[0] == '\0')
+ {
+ path_matched = true;
+ }
+ else
+ {
+ path_matched = false;
+ char *anchored_path = NULL;
+ if (asprintf(&anchored_path, "^%s$", e->_path) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+
+ regex_t regex;
+ if (regcomp(&regex, anchored_path, REG_EXTENDED | REG_NOSUB) != 0)
+ {
+ free(anchored_path);
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+
+ bool compval = query_str_compare(query->_path, anchored_path, &regex, true);
+ free(anchored_path);
+ regfree(&regex);
+ if (compval)
+ {
+ path_matched = true;
+ }
+ }
+ if (!path_matched)
+ {
+ continue;
+ }
+ }
+
+ // if reached this point, then all criteria passed, so
+ // invoke the mapping function
+
+ if ((retval = fn(this, e, data)) < 0)
+ {
+ return retval;
+ }
+ }
+ }
+ catch(...)
+ {
+ apol_vector_destroy(&type_list);
+ apol_mls_range_destroy(&range);
+ throw;
+ }
+ apol_vector_destroy(&type_list);
+ return retval;
+}
+
+bool sefs_fcfile::isMLS() const
+{
+ if (_mls_set)
+ {
+ return _mls;
+ }
+ return false;
+}
+
+int sefs_fcfile::appendFile(const char *file) throw(std::bad_alloc, std::invalid_argument, std::runtime_error)
+{
+ FILE *fc_file = NULL;
+ char *line = NULL, *name_dup = NULL;
+ size_t line_len = 0;
+ size_t last_entry = apol_vector_get_size(_entries);
+ int retval, error = 0;
+
+ regex_t line_regex, context_regex;
+ bool is_line_compiled = false;
+ bool is_context_compiled = false;
+
+ try
+ {
+ if (file == NULL)
+ {
+ errno = EINVAL;
+ SEFS_ERR(this, "%s", strerror(EINVAL));
+ throw std::invalid_argument(strerror(EINVAL));
+ }
+
+ fc_file = fopen(file, "r");
+ if (!fc_file)
+ {
+ SEFS_ERR(this, "Unable to open file %s", file);
+ throw std::runtime_error(strerror(error));
+ }
+
+ if ((name_dup = strdup(file)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+
+ if (regcomp(&line_regex, "^([^[:blank:]]+)[[:blank:]]+(-.[[:blank:]]+)?([^-].+)$", REG_EXTENDED) != 0)
+ {
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+ is_line_compiled = true;
+
+ if (regcomp(&context_regex, "^([^:]+):([^:]+):([^:]+):?(.*)$", REG_EXTENDED) != 0)
+ {
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+ is_context_compiled = true;
+
+ while (!feof(fc_file))
+ {
+ if (getline(&line, &line_len, fc_file) == -1)
+ {
+ if (feof(fc_file))
+ {
+ break;
+ }
+ else
+ {
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+ }
+ parse_line(name_dup, line, &line_regex, &context_regex);
+ }
+
+ if (apol_vector_append(_files, name_dup) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+ name_dup = NULL;
+
+ retval = 0;
+ }
+ catch(...)
+ {
+ error = errno;
+ // discard all entries that were read from this file_contexts
+ size_t i = apol_vector_get_size(_entries);
+ for (; i > last_entry; i--)
+ {
+ sefs_entry *e = static_cast < sefs_entry * >(apol_vector_get_element(_entries, i - 1));
+ fcfile_entry_free(e);
+ apol_vector_remove(_entries, i - 1);
+ }
+ retval = -1;
+ }
+
+ if (fc_file != NULL)
+ {
+ fclose(fc_file);
+ }
+ if (is_line_compiled)
+ {
+ regfree(&line_regex);
+ }
+ if (is_context_compiled)
+ {
+ regfree(&context_regex);
+ }
+ free(name_dup);
+ free(line);
+ errno = error;
+ return retval;
+}
+
+size_t sefs_fcfile::appendFileList(const apol_vector_t * files)throw(std::bad_alloc, std::invalid_argument, std::runtime_error)
+{
+ size_t i;
+ if (files == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ throw new std::invalid_argument(strerror(EINVAL));
+ }
+ for (i = 0; i < apol_vector_get_size(files); i++)
+ {
+ if (appendFile(static_cast < char *>(apol_vector_get_element(files, i))) < 0)
+ {
+ return i;
+ }
+ }
+ return i;
+}
+
+const apol_vector_t *sefs_fcfile::fileList() const
+{
+ return _files;
+}
+
+/******************** private functions below ********************/
+
+void sefs_fcfile::parse_line(const char *origin, const char *line, regex_t * line_regex,
+ regex_t * context_regex) throw(std::bad_alloc, std::runtime_error)
+{
+ int error = 0;
+
+ char *s = strdup(line);
+ char *path;
+
+ if (s == NULL)
+ {
+ error = errno;
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+
+ apol_str_trim(s);
+ if (s[0] == '#' || s[0] == '\0')
+ {
+ free(s);
+ return;
+ }
+
+ try
+ {
+ const size_t nmatch = 5;
+ regmatch_t pmatch[nmatch];
+
+ if (regexec(line_regex, s, nmatch, pmatch, 0) != 0)
+ {
+ error = EIO;
+ SEFS_ERR(this, "fcfile line is not legal:\n%s", s);
+ throw std::runtime_error(strerror(error));
+ }
+
+ assert(pmatch[1].rm_so == 0);
+ s[pmatch[1].rm_eo] = '\0';
+ if ((path = strdup(s)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(error));
+ }
+ if (apol_bst_insert_and_get(path_tree, (void **)&path, NULL) < 0)
+ {
+ free(path);
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(error));
+ }
+
+ uint32_t objclass;
+ if (pmatch[2].rm_so != -1)
+ {
+ switch (s[pmatch[2].rm_so + 1])
+ {
+ case '-':
+ objclass = QPOL_CLASS_FILE;
+ break;
+ case 'd':
+ objclass = QPOL_CLASS_DIR;
+ break;
+ case 'c':
+ objclass = QPOL_CLASS_CHR_FILE;
+ break;
+ case 'b':
+ objclass = QPOL_CLASS_BLK_FILE;
+ break;
+ case 'p':
+ objclass = QPOL_CLASS_FIFO_FILE;
+ break;
+ case 'l':
+ objclass = QPOL_CLASS_LNK_FILE;
+ break;
+ case 's':
+ objclass = QPOL_CLASS_SOCK_FILE;
+ break;
+ default:
+ error = EIO;
+ SEFS_ERR(this, "%s", "Invalid file context object class.");
+ throw std::runtime_error(strerror(error));
+ }
+ }
+ else
+ {
+ // no object class explicitly given
+ objclass = QPOL_CLASS_ALL;
+ }
+
+ assert(pmatch[3].rm_so != -1);
+ char *context_str = s + pmatch[3].rm_so;
+ char *user, *role, *type, *range;
+
+ if (strcmp(context_str, "<<none>>") == 0)
+ {
+ user = role = type = range = "";
+ }
+ else
+ {
+ if (regexec(context_regex, context_str, nmatch, pmatch, 0) != 0)
+ {
+ error = EIO;
+ SEFS_ERR(this, "fcfile context is not legal:\n%s", context_str);
+ throw std::runtime_error(strerror(error));
+ }
+
+ assert(pmatch[1].rm_so == 0);
+ context_str[pmatch[1].rm_eo] = '\0';
+ user = context_str;
+
+ assert(pmatch[2].rm_so != -1);
+ context_str[pmatch[2].rm_eo] = '\0';
+ role = context_str + pmatch[2].rm_so;
+
+ assert(pmatch[3].rm_so != -1);
+ context_str[pmatch[3].rm_eo] = '\0';
+ type = context_str + pmatch[3].rm_so;
+
+ range = NULL;
+ if (pmatch[4].rm_so != -1)
+ {
+ range = context_str + pmatch[4].rm_so;
+ }
+ }
+ if (range != NULL & range[0] != '\0')
+ {
+ if (_mls_set && !_mls)
+ {
+ error = EIO;
+ SEFS_ERR(this, "fcfile context is MLS, but fcfile is not:\n%s", context_str);
+ throw std::runtime_error(strerror(error));
+ }
+ _mls = true;
+ _mls_set = true;
+ }
+ else
+ {
+ if (_mls_set && !_mls && strcmp(context_str, "<<none>>") != 0)
+ {
+ error = EIO;
+ SEFS_ERR(this, "fcfile context is not MLS, but fcfile is:\n%s", context_str);
+ throw std::runtime_error(strerror(error));
+ }
+ _mls = true;
+ _mls_set = false;
+ }
+ struct sefs_context_node *context = getContext(user, role, type, range);
+ sefs_entry *entry = new sefs_entry(this, context, objclass, path, origin);
+
+ if (apol_vector_append(_entries, static_cast < void *>(entry)) < 0)
+ {
+ error = errno;
+ delete entry;
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+ }
+
+ catch(...)
+ {
+ free(s);
+ errno = error;
+ throw;
+ }
+
+ free(s);
+}
+
+/******************** C functions below ********************/
+
+sefs_fclist_t *sefs_fcfile_create(sefs_callback_fn_t msg_callback, void *varg)
+{
+ sefs_fclist *fclist;
+ try
+ {
+ fclist = new sefs_fcfile(msg_callback, varg);
+ }
+ catch(...)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return fclist;
+}
+
+sefs_fclist_t *sefs_fcfile_create_from_file(const char *file, sefs_callback_fn_t msg_callback, void *varg)
+{
+ sefs_fclist *fclist;
+ try
+ {
+ fclist = new sefs_fcfile(file, msg_callback, varg);
+ }
+ catch(...)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return fclist;
+}
+
+sefs_fclist_t *sefs_fcfile_create_from_file_list(const apol_vector_t * files, sefs_callback_fn_t msg_callback, void *varg)
+{
+ sefs_fclist *fclist;
+ try
+ {
+ fclist = new sefs_fcfile(files, msg_callback, varg);
+ }
+ catch(...)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return fclist;
+}
+
+int sefs_fcfile_append_file(sefs_fcfile_t * fcfile, const char *file)
+{
+ if (fcfile == NULL)
+ {
+ SEFS_ERR(fcfile, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ try
+ {
+ fcfile->appendFile(file);
+ }
+ catch(...)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+size_t sefs_fcfile_append_file_list(sefs_fcfile_t * fcfile, const apol_vector_t * files)
+{
+ if (fcfile == NULL)
+ {
+ SEFS_ERR(fcfile, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return 0;
+ }
+ return fcfile->appendFileList(files);
+}
+
+const apol_vector_t *sefs_fcfile_get_file_list(const sefs_fcfile_t * fcfile)
+{
+ if (fcfile == NULL)
+ {
+ SEFS_ERR(fcfile, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ return fcfile->fileList();
+}