From 47be9ff57e72906660bb62a515222f482131e1fb Mon Sep 17 00:00:00 2001 From: Miroslav Grepl Date: Fri, 11 Apr 2014 09:37:53 +0200 Subject: Create setools-3.3.7 git repo --- libsefs/src/filesystem.cc | 733 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 733 insertions(+) create mode 100644 libsefs/src/filesystem.cc (limited to 'libsefs/src/filesystem.cc') diff --git a/libsefs/src/filesystem.cc b/libsefs/src/filesystem.cc new file mode 100644 index 0000000..8dfd32b --- /dev/null +++ b/libsefs/src/filesystem.cc @@ -0,0 +1,733 @@ +/** + * @file + * Implementation of the sefs_filesystem 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 "new_ftw.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int lgetfilecon_raw(const char *, security_context_t *) __attribute__ ((weak)); + +/** + * As that setools must work with older libselinux versions that may + * not have the _raw() functions, declare them as weak. If libselinux + * does indeed have the new functions then use them; otherwise + * fallback to the originals. + */ +static int filesystem_lgetfilecon(const char *path, security_context_t * context) +{ + if (lgetfilecon_raw != NULL) + { + return lgetfilecon_raw(path, context); + } + else + { + return lgetfilecon(path, context); + } +} + +#if 0 + +/** + * Given a directory, find all bounded mounted filesystems within that + * directory (or subdirectory within.) This function consults the + * entries written to /etc/mtab to determine if something is mounted + * or not and if it has the "bind" option. Note that if \a dir itself + * is a mount, it will not be reported; a subdirectory might. + * + * Note that the returned vector in never actually used by this + * library. This function existed in previous versions of libsefs, + * but was never documented why it existed. Rather than eliminate + * this function, it is retained (but effectively unused), in case a + * future revision of libsefs necessitates finding bind mounts. + * + * @param dir Directory to begin search. + * + * @return An allocated vector containing pathnames (type char *) to + * each mounted location with the "bind" option. The caller is + * responsible for calling apol_vector_destroy() afterwards. + */ +static apol_vector_t *filesystem_find_mount_points(const char *dir) throw(std::bad_alloc, std::runtime_error) +{ + char *dirdup = NULL; + apol_vector_t *v = NULL; + FILE *mtab = NULL; + struct mntent *entry; + + try + { + if ((dirdup = strdup(dir)) == NULL) + { + throw std::bad_alloc(); + } + size_t len = strlen(dirdup); + if (len > 1 && dirdup[len - 1] == '/') + { + dirdup[len - 1] = '\0'; + } + if ((v = apol_vector_create(free)) == NULL) + { + throw std::bad_alloc(); + } + if ((mtab = fopen("/etc/mtab", "r")) == NULL) + { + throw std::runtime_error(strerror(errno)); + } + // note non thread-safeness below + while ((entry = getmntent(mtab)) != NULL) + { + if (strstr(entry->mnt_dir, dir) != entry->mnt_dir) + { + continue; + } + if (strcmp(entry->mnt_dir, dirdup) == 0) + { + continue; + } + if (strstr(entry->mnt_opts, "bind") != NULL) + { + char *s = strdup(entry->mnt_dir); + if (s == NULL) + { + throw std::bad_alloc(); + } + if (apol_vector_append(v, s) < 0) + { + free(s); + throw std::bad_alloc(); + } + } + } + + } + catch(...) + { + free(dirdup); + apol_vector_destroy(&v); + if (mtab != NULL) + { + fclose(mtab); + } + throw; + } + free(dirdup); + fclose(mtab); + return v; +} + +#endif + +/******************** public functions below ********************/ + +sefs_filesystem::sefs_filesystem(const char *new_root, sefs_callback_fn_t msg_callback, void *varg)throw(std::bad_alloc, std::invalid_argument, std::runtime_error):sefs_fclist(SEFS_FCLIST_TYPE_FILESYSTEM, + msg_callback, + varg) +{ + if (new_root == NULL) + { + SEFS_ERR(this, "%s", strerror(EINVAL)); + errno = EINVAL; + throw std::invalid_argument(strerror(EINVAL)); + } + _root = NULL; + _mls = false; + try + { + // check that root exists and is readable + struct stat64 sb; + if (stat64(new_root, &sb) != 0 && !S_ISDIR(sb.st_mode)) + { + SEFS_ERR(this, "%s", strerror(EINVAL)); + errno = EINVAL; + throw std::invalid_argument(strerror(EINVAL)); + } + + // determine if filesystem is MLS or not + security_context_t scon; + if (filesystem_lgetfilecon(new_root, &scon) < 0) + { + SEFS_ERR(this, "Could not read SELinux file context for %s.", new_root); + throw std::runtime_error(strerror(errno)); + } + context_t con; + if ((con = context_new(scon)) == 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + freecon(scon); + throw std::runtime_error(strerror(errno)); + } + freecon(scon); + const char *range = context_range_get(con); + if (range != NULL && range[0] != '\0') + { + _mls = true; + } + context_free(con); + + if ((_root = strdup(new_root)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::bad_alloc(); + } + } + catch(...) + { + free(_root); + throw; + } +} + +sefs_filesystem::~sefs_filesystem() +{ + free(_root); +} + +struct filesystem_ftw_struct +{ + sefs_filesystem *fs; + sefs_query *query; + apol_vector_t *dev_map; //< vector of filesystem_dev entries + apol_vector_t *type_list; + apol_mls_range_t *range; + sefs_fclist_map_fn_t fn; + void *data; + bool aborted; + int retval; +}; + +// wrapper functions to go between non-OO land into OO member functions + +inline struct sefs_context_node *filesystem_get_context(sefs_filesystem * fs, security_context_t scon) throw(std::bad_alloc) +{ + return fs->getContext(scon); +} + +inline sefs_entry *filesystem_get_entry(sefs_filesystem * fs, const struct sefs_context_node * node, uint32_t objClass, + const char *path, ino64_t ino, const char *dev_name)throw(std::bad_alloc) +{ + return fs->getEntry(node, objClass, path, ino, dev_name); +} + +inline bool filesystem_is_query_match(sefs_filesystem * fs, const sefs_query * query, const char *path, const char *dev, + const struct stat64 * sb, apol_vector_t * type_list, + apol_mls_range_t * range)throw(std::runtime_error) +{ + return fs->isQueryMatch(query, path, dev, sb, type_list, range); +} + +static uint32_t filesystem_stat_to_objclass(const struct stat64 *sb) +{ + if (S_ISREG(sb->st_mode)) + { + return QPOL_CLASS_FILE; + } + if (S_ISDIR(sb->st_mode)) + { + return QPOL_CLASS_DIR; + } + if (S_ISCHR(sb->st_mode)) + { + return QPOL_CLASS_CHR_FILE; + } + if (S_ISBLK(sb->st_mode)) + { + return QPOL_CLASS_BLK_FILE; + } + if (S_ISFIFO(sb->st_mode)) + { + return QPOL_CLASS_FIFO_FILE; + } + if (S_ISLNK(sb->st_mode)) + { + return QPOL_CLASS_LNK_FILE; + } + if (S_ISSOCK(sb->st_mode)) + { + return QPOL_CLASS_SOCK_FILE; + } + assert(0); // should never get here + return 0; +} + +struct filesystem_dev +{ + dev_t dev; + char *dev_name; //< pointer into the dev_tree +}; + +static int filesystem_dev_cmp(const void *a, const void *b __attribute__ ((unused)), void *arg) +{ + const struct filesystem_dev *d1 = static_cast < const struct filesystem_dev *>(a); + dev_t *d2 = static_cast < dev_t * >(arg); + if (d1->dev < *d2) + { + return -1; + } + else if (d1->dev > *d2) + { + return 1; + } + return 0; +} + +static int filesystem_ftw_handler(const char *fpath, const struct stat64 *sb, int typeflag + __attribute__ ((unused)), struct FTW *ftwbuf __attribute__ ((unused)), void *data) +{ + struct filesystem_ftw_struct *s = static_cast < struct filesystem_ftw_struct *>(data); + + size_t i; + void *dev_num = const_cast < void *>(static_cast < const void *>(&(sb->st_dev))); + int rc = apol_vector_get_index(s->dev_map, NULL, filesystem_dev_cmp, dev_num, &i); + const char *dev = ""; + if (rc == 0) + { + // if the device number was discovered in buildDevMap + // then store the device name within the entry + struct filesystem_dev *d = static_cast < struct filesystem_dev *>(apol_vector_get_element(s->dev_map, i)); + dev = d->dev_name; + } + else + { + SEFS_WARN(s->fs, "Unknown device for %s.", fpath); + } + try + { + if (!filesystem_is_query_match(s->fs, s->query, fpath, dev, sb, s->type_list, s->range)) + { + return 0; + } + } + catch(...) + { + return -1; + } + + security_context_t scon; + if (filesystem_lgetfilecon(fpath, &scon) < 0) + { + SEFS_ERR(s->fs, "Could not read SELinux file context for %s.", fpath); + return -1; + } + struct sefs_context_node *node = NULL; + try + { + node = filesystem_get_context(s->fs, scon); + } + catch(...) + { + freecon(scon); + return -1; + } + freecon(scon); + + uint32_t objClass = filesystem_stat_to_objclass(sb); + + sefs_entry *entry = NULL; + try + { + entry = filesystem_get_entry(s->fs, node, objClass, fpath, sb->st_ino, dev); + } + catch(...) + { + return -1; + } + + // invoke real callback (not just the nftw handler) + s->retval = s->fn(s->fs, entry, s->data); + delete entry; + if (s->retval < 0) + { + s->aborted = true; + return s->retval; + } + + return 0; +} + +int sefs_filesystem::runQueryMap(sefs_query * query, sefs_fclist_map_fn_t fn, void *data) throw(std::runtime_error, + std::invalid_argument) +{ + struct filesystem_ftw_struct s; + s.dev_map = NULL; + s.type_list = NULL; + s.range = NULL; + try + { + s.dev_map = buildDevMap(); + if (query != NULL) + { + query->compile(); + if (policy != NULL) + { + if (query->_type != NULL && query->_indirect && + (s.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 && + (s.range = apol_mls_range_create_from_string(policy, query->_range)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + } + } + } + catch(...) + { + apol_vector_destroy(&s.dev_map); + apol_vector_destroy(&s.type_list); + apol_mls_range_destroy(&s.range); + throw; + } + s.fs = this; + s.query = query; + s.fn = fn; + s.data = data; + s.aborted = false; + s.retval = 0; + + int retval = new_nftw64(_root, filesystem_ftw_handler, 1024, 0, &s); + apol_vector_destroy(&s.dev_map); + apol_vector_destroy(&s.type_list); + apol_mls_range_destroy(&s.range); + if (retval != 0 && !s.aborted) + { + // error was generated by new_nftw64() itself, not + // from callback + return retval; + } + return s.retval; +} + +bool sefs_filesystem::isMLS() const +{ + return _mls; +} + +const char *sefs_filesystem::root() const +{ + return _root; +} + +/******************** private functions below ********************/ + +static void filesystem_dev_free(void *elem) +{ + if (elem != NULL) + { + struct filesystem_dev *d = static_cast < struct filesystem_dev *>(elem); + // don't free the device name pointer, because it's pointing + // into the dev_tree BST + free(d); + } +} + +const char *sefs_filesystem::getDevName(const dev_t dev) throw(std::runtime_error) +{ + apol_vector_t *dev_map = buildDevMap(); + size_t i; + void *devp = const_cast < dev_t * >(&dev); + int rc = apol_vector_get_index(dev_map, NULL, filesystem_dev_cmp, devp, &i); + if (rc < 0) + { + apol_vector_destroy(&dev_map); + return NULL; + } + struct filesystem_dev *d = static_cast < struct filesystem_dev *>(apol_vector_get_element(dev_map, i)); + const char *dev_name = d->dev_name; // this is pointing into this->_dev_tree + apol_vector_destroy(&dev_map); + return dev_name; +} + +/** + * For each entry in /etc/mtab, record the device number and the name + * of the mounted file system. This provides the mapping between a + * device number and its source device. + * + * @return Vector of filesystem_dev entries. The caller must call + * apol_vector_destroy() upon the vector afterwards. + * @exception If error allocating space, unable to open /etc/mtab, or + * unable to parse mtab file. + */ +apol_vector_t *sefs_filesystem::buildDevMap(void)throw(std::runtime_error) +{ + apol_vector_t *dev_map; + if ((dev_map = apol_vector_create(filesystem_dev_free)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + FILE *f = NULL; + try + { + if ((f = fopen("/etc/mtab", "r")) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + char buf[256]; + struct mntent mntbuf; + while (getmntent_r(f, &mntbuf, buf, 256) != NULL) + { + struct stat sb; + if (stat(mntbuf.mnt_dir, &sb) == -1) + { + // could not open this device, so skip + // it (and hope it won't be examined + // during runQuery()) + continue; + } + else + { + struct filesystem_dev *d = static_cast < struct filesystem_dev *>(calloc(1, sizeof(*d))); + if (d == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (apol_vector_append(dev_map, d) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + filesystem_dev_free(d); + throw std::runtime_error(strerror(errno)); + } + d->dev = sb.st_dev; + char *mnt_fsname = strdup(mntbuf.mnt_fsname); + if (mnt_fsname == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (apol_bst_insert_and_get(dev_tree, (void **)&mnt_fsname, NULL) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + free(mnt_fsname); + throw std::runtime_error(strerror(errno)); + } + d->dev_name = mnt_fsname; + } + } + } + catch(...) + { + apol_vector_destroy(&dev_map); + if (f != NULL) + { + fclose(f); + } + throw; + } + fclose(f); + return dev_map; +} + +bool sefs_filesystem::isQueryMatch(const sefs_query * query, const char *path, const char *dev, const struct stat64 * sb, + apol_vector_t * type_list, apol_mls_range_t * range)throw(std::runtime_error) +{ + if (query == NULL) + { + return true; + } + security_context_t scon; + if (filesystem_lgetfilecon(path, &scon) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + context_t con; + if ((con = context_new(scon)) == 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + freecon(scon); + throw std::runtime_error(strerror(errno)); + } + freecon(scon); + + if (!query_str_compare(context_user_get(con), query->_user, query->_reuser, query->_regex)) + { + context_free(con); + return false; + } + if (!query_str_compare(context_role_get(con), query->_role, query->_rerole, query->_regex)) + { + context_free(con); + return false; + } + + bool str_matched = false, pol_matched = false; + str_matched = query_str_compare(context_type_get(con), 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_get(con), apol_str_strcmp, NULL, &index) < 0); + } + if (!str_matched && !pol_matched) + { + context_free(con); + return false; + } + + if (isMLS()) + { + if (range == NULL) + { + if (!query_str_compare(context_range_get(con), query->_range, query->_rerange, query->_regex)) + { + context_free(con); + return false; + } + } + else + { + assert(policy != NULL); + apol_mls_range_t *context_range = apol_mls_range_create_from_string(policy, context_range_get(con)); + if (context_range == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + context_free(con); + throw std::runtime_error(strerror(errno)); + } + int ret; + ret = apol_mls_range_compare(policy, range, context_range, query->_rangeMatch); + apol_mls_range_destroy(&context_range); + if (ret <= 0) + { + context_free(con); + return false; + } + } + } + + context_free(con); + + if (query->_objclass != 0 && query->_objclass != filesystem_stat_to_objclass(sb)) + { + return false; + } + + if (!query_str_compare(path, query->_path, query->_repath, query->_regex)) + { + return false; + } + + if (query->_inode != 0 && query->_inode != sb->st_ino) + { + return false; + } + + if (!query_str_compare(dev, query->_dev, query->_redev, query->_regex)) + { + return false; + } + + return true; +} + +sefs_entry *sefs_filesystem::getEntry(const struct sefs_context_node * context, uint32_t objectClass, + const char *path, ino64_t ino, const char *dev_name)throw(std::bad_alloc) +{ + char *s = strdup(path); + if (s == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::bad_alloc(); + } + if (apol_bst_insert_and_get(path_tree, (void **)&s, NULL) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + free(s); + throw std::bad_alloc(); + } + sefs_entry *e = new sefs_entry(this, context, objectClass, s); + e->_inode = ino; + e->_dev = dev_name; + return e; +} + +/******************** C functions below ********************/ + +sefs_filesystem_t *sefs_filesystem_create(const char *root, sefs_callback_fn_t msg_callback, void *varg) +{ + sefs_filesystem_t *fs; + try + { + fs = new sefs_filesystem(root, msg_callback, varg); + } + catch(...) + { + errno = ENOMEM; + return NULL; + } + return fs; +} + +const char *sefs_filesystem_get_root(const sefs_filesystem_t * fs) +{ + if (fs == NULL) + { + SEFS_ERR(NULL, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + return fs->root(); +} + +extern const char *sefs_filesystem_get_dev_name(sefs_filesystem_t * fs, const dev_t dev) +{ + if (fs == NULL) + { + SEFS_ERR(NULL, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + const char *dev_name = NULL; + try + { + dev_name = fs->getDevName(dev); + } + catch(...) + { + return NULL; + } + return dev_name; +} -- cgit