/** * @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 #include "sefs_internal.hh" #include #include #include #include #include #include #include #include #include /******************** 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(®ex, 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, ®ex, true); free(anchored_path); regfree(®ex); 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, "<>") == 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, "<>") != 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(); }