diff options
Diffstat (limited to 'libsefs/src')
-rw-r--r-- | libsefs/src/Makefile.am | 52 | ||||
-rw-r--r-- | libsefs/src/db.cc | 1304 | ||||
-rw-r--r-- | libsefs/src/entry.cc | 213 | ||||
-rw-r--r-- | libsefs/src/fcfile.cc | 691 | ||||
-rw-r--r-- | libsefs/src/fclist.cc | 766 | ||||
-rw-r--r-- | libsefs/src/filesystem.cc | 733 | ||||
-rw-r--r-- | libsefs/src/libsefs.map | 29 | ||||
-rw-r--r-- | libsefs/src/new_ftw.c | 749 | ||||
-rw-r--r-- | libsefs/src/new_ftw.h | 183 | ||||
-rw-r--r-- | libsefs/src/query.cc | 431 | ||||
-rw-r--r-- | libsefs/src/sefs_internal.hh | 78 | ||||
-rw-r--r-- | libsefs/src/util.c | 46 |
12 files changed, 5275 insertions, 0 deletions
diff --git a/libsefs/src/Makefile.am b/libsefs/src/Makefile.am new file mode 100644 index 0000000..a9e61bc --- /dev/null +++ b/libsefs/src/Makefile.am @@ -0,0 +1,52 @@ +lib_LIBRARIES = libsefs.a + +sefsso_DATA = libsefs.so.@libsefs_version@ +sefssodir = $(libdir) + +AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \ + @QPOL_CFLAGS@ @APOL_CFLAGS@ @SQLITE3_CFLAGS@ -I$(srcdir)/../include -fpic +AM_CXXFLAGS = @DEBUGCXXFLAGS@ @WARNCXXFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \ + @QPOL_CFLAGS@ @APOL_CFLAGS@ @SQLITE3_CFLAGS@ -I$(srcdir)/../include -fpic +AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ + +libsefs_a_SOURCES = \ + db.cc \ + entry.cc \ + fcfile.cc \ + fclist.cc \ + filesystem.cc \ + new_ftw.c new_ftw.h \ + query.cc \ + sefs_internal.hh \ + util.c + +libsefs_a_DEPENDENCIES = \ + $(top_builddir)/libapol/src/libapol.so \ + $(top_builddir)/libqpol/src/libqpol.so + +libsefs_so_OBJS = $(patsubst %.c,%.o,$(filter %.c,$(libsefs_a_SOURCES))) $(patsubst %.cc,%.o,$(filter %.cc,$(libsefs_a_SOURCES))) +libsefs_so_OBJS += $(patsubst %.c,libsqlite_a-%.o,$(filter %.c,$(notdir $(libsqlite_a_SOURCES)))) + +LIBSEFS_SONAME = @libsefs_soname@ + +dist_noinst_DATA = libsefs.map + +$(sefsso_DATA): $(libsefs_so_OBJS) libsefs.map + $(CXX) -shared -o $@ $(libsefs_so_OBJS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(LIBSEFS_SONAME),--version-script=$(srcdir)/libsefs.map,-z,defs $(top_builddir)/libqpol/src/libqpol.so $(top_builddir)/libapol/src/libapol.so $(SQLITE3_LIBS) -lselinux -lsepol + $(LN_S) -f $@ @libsefs_soname@ + $(LN_S) -f $@ libsefs.so + +libsefs.so: $(sefso_DATA) + +$(top_builddir)/libapol/src/libapol.so: + $(MAKE) -C $(top_builddir)/libapol/src $(notdir $@) + +install-data-hook: + cd $(DESTDIR)$(sefssodir) && $(LN_S) -f $(sefsso_DATA) @libsefs_soname@ + cd $(DESTDIR)$(sefssodir) && $(LN_S) -f $(sefsso_DATA) libsefs.so + +mostlyclean-local: + -rm -rf *.gcno *.gcda *.gprof *.gcov libsefs.so @libsefs_soname@ $(sefsso_DATA) + +uninstall-local: + -rm -rf $(DESTDIR)$(sefssodir)/$(sefsso_DATA) $(DESTDIR)$(sefssodir)/@libsefs_soname@ $(DESTDIR)$(sefssodir)/libsefs.so diff --git a/libsefs/src/db.cc b/libsefs/src/db.cc new file mode 100644 index 0000000..88ad588 --- /dev/null +++ b/libsefs/src/db.cc @@ -0,0 +1,1304 @@ +/** + * @file + * + * Routines for creating, saving, and loading a sqlite3 database + * containing paths + file contexts. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2003-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/db.hh> +#include <sefs/filesystem.hh> +#include <sefs/entry.hh> +#include <apol/util.h> + +#include <sqlite3.h> + +#include <assert.h> +#include <errno.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> + +#define DB_MAX_VERSION "2" + +#define DB_SCHEMA_NONMLS \ + "CREATE TABLE users (user_id INTEGER PRIMARY KEY, user_name varchar (24));" \ + "CREATE TABLE roles (role_id INTEGER PRIMARY KEY, role_name varchar (24));" \ + "CREATE TABLE types (type_id INTEGER PRIMARY KEY, type_name varchar (48));" \ + "CREATE TABLE devs (dev_id INTEGER PRIMARY KEY, dev_name varchar (32));" \ + "CREATE TABLE paths (path varchar (128) PRIMARY KEY, ino int(64), dev int, user int, role int, type int, range int, obj_class int, symlink_target varchar (128));" \ + "CREATE TABLE info (key varchar, value varchar);" + +#define DB_SCHEMA_MLS DB_SCHEMA_NONMLS \ + "CREATE TABLE mls (mls_id INTEGER PRIMARY KEY, mls_range varchar (64));" + +// wrapper functions to go between non-OO land into OO member functions + +inline struct sefs_context_node *db_get_context(sefs_db * db, const char *user, const char *role, const char *type, + const char *range) throw(std::bad_alloc) +{ + return db->getContext(user, role, type, range); +} + +inline sefs_entry *db_get_entry(sefs_db * db, const struct sefs_context_node * node, uint32_t objClass, + const char *path, ino64_t inode, const char *dev)throw(std::bad_alloc) +{ + return db->getEntry(node, objClass, path, inode, dev); +} + +/******************** sqlite3 callback functions ********************/ + +struct db_callback_arg +{ + struct sqlite3 *db; + char *errmsg; + const char *source_db, *target_db; +}; + +struct db_query_arg +{ + sefs_db *db; + char *user, *role, *type, *range, *path, *dev; + bool regex, db_is_mls; + regex_t *reuser, *rerole, *retype, *rerange, *repath, *redev; + int rangeMatch; + sefs_fclist_map_fn_t fn; + void *data; + apol_vector_t *type_list; + apol_mls_range_t *apol_range; + apol_policy_t *policy; + bool aborted; + int retval; +}; + +/** + * Callback invoked when selecting names of tables from a database. + */ +static int db_copy_schema(void *arg, int argc __attribute__ ((unused)), char *argv[], char *column_names[] __attribute__ ((unused))) +{ + // argv[0] contains a SQL statement that, if executed against a + // db, will create a table + struct db_callback_arg *db = static_cast < struct db_callback_arg *>(arg); + if (sqlite3_exec(db->db, argv[0], NULL, NULL, &(db->errmsg)) != SQLITE_OK) + { + return -1; + } + return 0; +} + +/** + * Callback invoked when selecting each row from a table. + */ +static int db_copy_table(void *arg, int argc __attribute__ ((unused)), char *argv[], char *column_names[] __attribute__ ((unused))) +{ + // argv[0] contains the name of a table + struct db_callback_arg *db = static_cast < struct db_callback_arg *>(arg); + char *insert = NULL; + if (asprintf(&insert, "INSERT INTO %s%s SELECT * FROM %s%s", db->target_db, argv[0], db->source_db, argv[0]) < 0) + { + db->errmsg = strdup(strerror(errno)); + return -1; + } + int rc = sqlite3_exec(db->db, insert, NULL, NULL, &(db->errmsg)); + free(insert); + if (rc != SQLITE_OK) + { + return -1; + } + return 0; +} + +/** + * Callback invoked when selecting a user (for a sefs_query). + */ +static void db_user_compare(sqlite3_context * context, int argc __attribute__ ((unused)), sqlite3_value ** argv) +{ + void *arg = sqlite3_user_data(context); + struct db_query_arg *q = static_cast < struct db_query_arg *>(arg); + assert(sqlite3_value_type(argv[0]) == SQLITE_TEXT); + const char *text = reinterpret_cast < const char *>(sqlite3_value_text(argv[0])); + bool retval = query_str_compare(text, q->user, q->reuser, q->regex); + sqlite3_result_int(context, (retval ? 1 : 0)); +} + +/** + * Callback invoked when selecting a role (for a sefs_query). + */ +static void db_role_compare(sqlite3_context * context, int argc __attribute__ ((unused)), sqlite3_value ** argv) +{ + void *arg = sqlite3_user_data(context); + struct db_query_arg *q = static_cast < struct db_query_arg *>(arg); + assert(sqlite3_value_type(argv[0]) == SQLITE_TEXT); + const char *text = reinterpret_cast < const char *>(sqlite3_value_text(argv[0])); + bool retval = query_str_compare(text, q->role, q->rerole, q->regex); + sqlite3_result_int(context, (retval ? 1 : 0)); +} + +/** + * Callback invoked when selecting a type (for a sefs_query). + */ +static void db_type_compare(sqlite3_context * context, int argc __attribute__ ((unused)), sqlite3_value ** argv) +{ + void *arg = sqlite3_user_data(context); + struct db_query_arg *q = static_cast < struct db_query_arg *>(arg); + assert(sqlite3_value_type(argv[0]) == SQLITE_TEXT); + const char *text = reinterpret_cast < const char *>(sqlite3_value_text(argv[0])); + bool retval; + if (q->type_list != NULL) + { + assert(q->policy != NULL); + size_t index; + retval = (apol_vector_get_index(q->type_list, text, apol_str_strcmp, NULL, &index) >= 0); + if (retval) + { + sqlite3_result_int(context, 1); + return; + } + } + retval = query_str_compare(text, q->type, q->retype, q->regex); + sqlite3_result_int(context, (retval ? 1 : 0)); +} + +/** + * Callback invoked when selecting a range (for a sefs_query). + */ +static void db_range_compare(sqlite3_context * context, int argc __attribute__ ((unused)), sqlite3_value ** argv) +{ + void *arg = sqlite3_user_data(context); + struct db_query_arg *q = static_cast < struct db_query_arg *>(arg); + assert(sqlite3_value_type(argv[0]) == SQLITE_TEXT); + const char *text = reinterpret_cast < const char *>(sqlite3_value_text(argv[0])); + bool retval; + if (q->apol_range == NULL) + { + retval = query_str_compare(text, q->range, q->rerange, q->regex); + } + else + { + assert(q->policy != NULL); + apol_mls_range_t *db_range = apol_mls_range_create_from_string(q->policy, text); + int ret; + ret = apol_mls_range_compare(q->policy, q->apol_range, db_range, q->rangeMatch); + apol_mls_range_destroy(&db_range); + retval = (ret > 0); + } + sqlite3_result_int(context, (retval ? 1 : 0)); +} + +/** + * Callback invoked when selecting a path (for a sefs_query). + */ +static void db_path_compare(sqlite3_context * context, int argc __attribute__ ((unused)), sqlite3_value ** argv) +{ + void *arg = sqlite3_user_data(context); + struct db_query_arg *q = static_cast < struct db_query_arg *>(arg); + assert(sqlite3_value_type(argv[0]) == SQLITE_TEXT); + const char *text = reinterpret_cast < const char *>(sqlite3_value_text(argv[0])); + bool retval = query_str_compare(text, q->path, q->repath, q->regex); + sqlite3_result_int(context, (retval ? 1 : 0)); +} + +/** + * Callback invoked when selecting a device name (for a sefs_query). + */ +static void db_dev_compare(sqlite3_context * context, int argc __attribute__ ((unused)), sqlite3_value ** argv) +{ + void *arg = sqlite3_user_data(context); + struct db_query_arg *q = static_cast < struct db_query_arg *>(arg); + assert(sqlite3_value_type(argv[0]) == SQLITE_TEXT); + const char *text = reinterpret_cast < const char *>(sqlite3_value_text(argv[0])); + bool retval = query_str_compare(text, q->dev, q->redev, q->regex); + sqlite3_result_int(context, (retval ? 1 : 0)); +} + +/** + * Callback invoked when selecting rows during a query. + */ +static int db_query_callback(void *arg, int argc, char *argv[], char *column_names[] __attribute__ ((unused))) +{ + struct db_query_arg *q = static_cast < struct db_query_arg *>(arg); + assert(argc == (q->db_is_mls ? 9 : 8)); + char *path = argv[0]; + ino64_t ino = static_cast < ino64_t > (strtoul(argv[1], NULL, 10)); + char *dev = argv[2]; + char *user = argv[3]; + char *role = argv[4]; + char *type = argv[5]; + char *range, *objclass_str, *link_target; + + if (q->db_is_mls) + { + range = argv[6]; + objclass_str = argv[7]; + link_target = argv[8]; + } + else + { + range = NULL; + objclass_str = argv[6]; + link_target = argv[7]; + } + struct sefs_context_node *node = NULL; + try + { + node = db_get_context(q->db, user, role, type, range); + } + catch(...) + { + return -1; + } + + uint32_t objClass = static_cast < uint32_t > (atoi(objclass_str)); + sefs_entry *entry = NULL; + try + { + entry = db_get_entry(q->db, node, objClass, path, ino, dev); + } + catch(...) + { + return -1; + } + + // invoke real callback (not just the sqlite3 exec callback) + q->retval = q->fn(q->db, entry, q->data); + delete entry; + if (q->retval < 0) + { + q->aborted = true; + return -1; + } + return 0; +} + +/** + * Callback invoked when checking if there exists any row with the + * given select parameters. + */ +static int db_row_exist_callback(void *arg, + int argc __attribute__ ((unused)), + char **argv __attribute__ ((unused)), char **col_names __attribute__ ((unused))) +{ + bool *answer = static_cast < bool * >(arg); + *answer = true; + return 0; +} + +/** + * Callback invoked when obtaining the ctime value from the database. + */ +static int db_ctime_callback(void *arg, int argc __attribute__ ((unused)), char **argv, char **col_names __attribute__ ((unused))) +{ + time_t *ctime = static_cast < time_t * >(arg); + // argv has the result of a call to ctime_r(); convert the string + // back to a time_t value + struct tm t; + memset(&t, 0, sizeof(t)); + if (strptime(argv[0], "%a %b %d %T %Y", &t) == NULL) + { + return -1; + } + *ctime = mktime(&t); + return 0; +} + +/** + * Callback invoked to determine how many rows match a particular + * select statement. + */ +static int db_count_callback(void *arg, int argc __attribute__ ((unused)), char **argv, char **column_names + __attribute__ ((unused))) +{ + int *count = static_cast < int *>(arg); + *count = atoi(argv[0]); + return 0; +} + +/******************** convert from a filesystem to a db ********************/ + +struct strindex +{ + const char *str; + int id; +}; + +static int db_strindex_comp(const void *a, const void *b, void *arg __attribute__ ((unused))) +{ + const struct strindex *n1 = static_cast < const struct strindex *>(a); + const struct strindex *n2 = static_cast < const struct strindex *>(b); + return strcmp(n1->str, n2->str); +} + +class db_convert +{ + public: + db_convert(sefs_db * db, struct sqlite3 * &target_db)throw(std::runtime_error) + { + _db = db; + _target_db = target_db; + _user = _role = _type = _range = _dev = NULL; + _user_id = _role_id = _type_id = _range_id = _dev_id = 0; + _errmsg = NULL; + try + { + if ((_user = apol_bst_create(db_strindex_comp, free)) == NULL) + { + SEFS_ERR(_db, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if ((_role = apol_bst_create(db_strindex_comp, free)) == NULL) + { + SEFS_ERR(_db, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if ((_type = apol_bst_create(db_strindex_comp, free)) == NULL) + { + SEFS_ERR(_db, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if ((_range = apol_bst_create(db_strindex_comp, free)) == NULL) + { + SEFS_ERR(_db, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if ((_dev = apol_bst_create(db_strindex_comp, free)) == NULL) + { + SEFS_ERR(_db, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + } + catch(...) + { + apol_bst_destroy(&_user); + apol_bst_destroy(&_role); + apol_bst_destroy(&_type); + apol_bst_destroy(&_range); + apol_bst_destroy(&_dev); + throw; + } + } + ~db_convert() + { + apol_bst_destroy(&_user); + apol_bst_destroy(&_role); + apol_bst_destroy(&_type); + apol_bst_destroy(&_range); + apol_bst_destroy(&_dev); + sqlite3_free(_errmsg); + } + int getID(const char *sym, apol_bst_t * tree, int &id, const char *table) throw(std::bad_alloc) + { + struct strindex st = { sym, -1 }, *result; + if (apol_bst_get_element(tree, &st, NULL, (void **)&result) == 0) + { + return result->id; + } + if ((result = static_cast < struct strindex * >(malloc(sizeof(*result)))) == NULL) + { + SEFS_ERR(_db, "%s", strerror(errno)); + throw std::bad_alloc(); + } + result->str = sym; + result->id = id++; + if (apol_bst_insert(tree, result, NULL) < 0) + { + SEFS_ERR(_db, "%s", strerror(errno)); + free(result); + throw std::bad_alloc(); + } + char *insert_stmt = NULL; + if (asprintf(&insert_stmt, "INSERT INTO %s VALUES (%d, '%s')", table, result->id, result->str) < 0) + { + SEFS_ERR(_db, "%s", strerror(errno)); + throw std::bad_alloc(); + } + if (sqlite3_exec(_target_db, insert_stmt, NULL, NULL, &_errmsg) != SQLITE_OK) + { + SEFS_ERR(_db, "%s", _errmsg); + free(insert_stmt); + throw std::runtime_error(_errmsg); + } + free(insert_stmt); + return result->id; + } + apol_bst_t *_user, *_role, *_type, *_range, *_dev; + int _user_id, _role_id, _type_id, _range_id, _dev_id; + bool _isMLS; + char *_errmsg; + sefs_db *_db; + struct sqlite3 *_target_db; +}; + +int db_create_from_filesystem(sefs_fclist * fclist __attribute__ ((unused)), const sefs_entry * entry, void *arg) +{ + db_convert *dbc = static_cast < db_convert * >(arg); + + const struct sefs_context_node *context = dbc->_db->getContextNode(entry); + try + { + + // add the user, role, type, range, and dev into the + // target_db if needed + int user_id = dbc->getID(context->user, dbc->_user, dbc->_user_id, "users"); + int role_id = dbc->getID(context->role, dbc->_role, dbc->_role_id, "roles"); + int type_id = dbc->getID(context->type, dbc->_type, dbc->_type_id, "types"); + int range_id = 0; + if (dbc->_isMLS) + { + range_id = dbc->getID(context->range, dbc->_range, dbc->_range_id, "mls"); + } + int dev_id = dbc->getID(entry->dev(), dbc->_dev, dbc->_dev_id, "devs"); + const char *path = entry->path(); + const ino64_t inode = entry->inode(); + const uint32_t objclass = entry->objectClass(); + char link_target[128] = ""; + // determine the link target as necessary + struct stat64 sb; + if (stat64(path, &sb) == -1) + { + SEFS_ERR(dbc->_db, "%s", strerror(errno)); + throw std::bad_alloc(); + } + if (S_ISLNK(sb.st_mode)) + { + if (readlink(path, link_target, 128) == 0) + { + SEFS_ERR(dbc->_db, "%s", strerror(errno)); + throw std::bad_alloc(); + } + link_target[127] = '\0'; + } + + char *insert_stmt = NULL; + if (asprintf + (&insert_stmt, "INSERT INTO paths VALUES ('%s', %lu, %d, %d, %d, %d, %d, %u, '%s')", path, + static_cast < long unsigned int >(inode), dev_id, user_id, role_id, type_id, range_id, objclass, + link_target) < 0) + { + SEFS_ERR(dbc->_db, "%s", strerror(errno)); + throw std::bad_alloc(); + } + if (sqlite3_exec(dbc->_target_db, insert_stmt, NULL, NULL, &dbc->_errmsg) != SQLITE_OK) + { + SEFS_ERR(dbc->_db, "%s", dbc->_errmsg); + free(insert_stmt); + throw std::runtime_error(dbc->_errmsg); + } + free(insert_stmt); + } + catch(...) + { + return -1; + } + return 0; +} + +/******************** public functions below ********************/ + +sefs_db::sefs_db(sefs_filesystem * fs, sefs_callback_fn_t msg_callback, void *varg)throw(std::invalid_argument, std::runtime_error):sefs_fclist + (SEFS_FCLIST_TYPE_DB, msg_callback, + varg) +{ + if (fs == NULL) + { + errno = EINVAL; + SEFS_ERR(this, "%s", strerror(EINVAL)); + throw std::invalid_argument(strerror(EINVAL)); + } + + SEFS_INFO(this, "Reading contexts from filesystem %s.", fs->root()); + char *errmsg = NULL; + try + { + if (sqlite3_open(":memory:", &_db) != SQLITE_OK) + { + SEFS_ERR(this, "%s", sqlite3_errmsg(_db)); + throw std::runtime_error(sqlite3_errmsg(_db)); + } + int rc; + if (fs->isMLS()) + { + rc = sqlite3_exec(_db, DB_SCHEMA_MLS, NULL, 0, &errmsg); + } + else + { + rc = sqlite3_exec(_db, DB_SCHEMA_NONMLS, NULL, 0, &errmsg); + } + if (rc != SQLITE_OK) + { + SEFS_ERR(this, "%s", errmsg); + throw std::runtime_error(errmsg); + } + + db_convert dbc(this, _db); + dbc._isMLS = fs->isMLS(); + if (fs->runQueryMap(NULL, db_create_from_filesystem, &dbc) < 0) + { + throw std::runtime_error(strerror(errno)); + } + + // store metadata about the database + const char *dbversion = DB_MAX_VERSION; + char hostname[64]; + gethostname(hostname, sizeof(hostname)); + hostname[63] = '\0'; + _ctime = time(NULL); + char datetime[32]; + ctime_r(&_ctime, datetime); + + char *info_insert = NULL; + if (asprintf(&info_insert, + "INSERT INTO info (key,value) VALUES ('dbversion','%s');" + "INSERT INTO info (key,value) VALUES ('hostname','%s');" + "INSERT INTO info (key,value) VALUES ('datetime','%s');", dbversion, hostname, datetime) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + rc = sqlite3_exec(_db, info_insert, NULL, NULL, &errmsg); + free(info_insert); + if (rc != SQLITE_OK) + { + SEFS_ERR(this, "%s", errmsg); + throw std::runtime_error(errmsg); + } + + } + catch(...) + { + if (errmsg != NULL) + { + sqlite3_free(errmsg); + } + sqlite3_close(_db); + throw; + } +} + +sefs_db::sefs_db(const char *filename, sefs_callback_fn_t msg_callback, void *varg) throw(std::invalid_argument, + std:: + runtime_error):sefs_fclist + (SEFS_FCLIST_TYPE_DB, msg_callback, varg) +{ + if (filename == NULL) + { + errno = EINVAL; + SEFS_ERR(this, "%s", strerror(EINVAL)); + throw std::invalid_argument(strerror(EINVAL)); + } + + if (!sefs_db::isDB(filename)) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + + if (sqlite3_open(filename, &_db) != SQLITE_OK) + { + SEFS_ERR(this, "%s", sqlite3_errmsg(_db)); + sqlite3_close(_db); + throw std::runtime_error(strerror(errno)); + } + + char *errmsg = NULL; + + const char *select_stmt = "SELECT * FROM info WHERE key = 'dbversion' AND value >= " DB_MAX_VERSION; + bool answer = false; + if (sqlite3_exec(_db, select_stmt, db_row_exist_callback, &answer, &errmsg) != SQLITE_OK) + { + SEFS_ERR(this, "%s", errmsg); + sqlite3_free(errmsg); + sqlite3_close(_db); + throw std::runtime_error(strerror(errno)); + } + if (!answer) + { + SEFS_INFO(this, "Upgrading database %s.", filename); + SEFS_WARN(this, "%s is a pre-libsefs-4.0 database and will be upgraded.", filename); + upgradeToDB2(); + } + + // get ctime from db + _ctime = 0; + const char *ctime_stmt = "SELECT value FROM info WHERE key='datetime'"; + if (sqlite3_exec(_db, ctime_stmt, db_ctime_callback, &_ctime, &errmsg) != SQLITE_OK) + { + SEFS_ERR(this, "%s", errmsg); + sqlite3_free(errmsg); + sqlite3_close(_db); + throw std::runtime_error(strerror(errno)); + } +} + +sefs_db::~sefs_db() +{ + if (_db != NULL) + { + sqlite3_close(_db); + _db = NULL; + } +} + +int sefs_db::runQueryMap(sefs_query * query, sefs_fclist_map_fn_t fn, void *data) throw(std::runtime_error, std::invalid_argument) +{ + // copy the query fields over to the C land struct; this is + // because the query members are private, and thus not accessible + // from a C callback + struct db_query_arg q; + memset(&q, 0, sizeof(q)); + + q.db = this; + if (query != NULL) + { + query->compile(); + if (policy != NULL) + { + if (query->_type != NULL && query->_indirect) + { + q.type_list = + query_create_candidate_type(policy, query->_type, query->_retype, query->_regex, + query->_indirect); + if (q.type_list == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + } + if (query->_range != NULL && query->_rangeMatch != 0) + { + q.apol_range = apol_mls_range_create_from_string(policy, query->_range); + if (q.apol_range == NULL) + { + apol_vector_destroy(&q.type_list); + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + } + } + q.user = query->_user; + q.role = query->_role; + q.type = query->_type; + q.range = query->_range; + q.path = query->_path; + q.dev = query->_dev; + q.regex = query->_regex; + q.reuser = query->_reuser; + q.rerole = query->_rerole; + q.retype = query->_retype; + q.rerange = query->_rerange; + q.repath = query->_repath; + q.redev = query->_redev; + q.rangeMatch = query->_rangeMatch; + } + q.policy = this->policy; + q.db_is_mls = isMLS(); + q.fn = fn; + q.data = data; + q.retval = 0; + q.aborted = false; + + char *select_stmt = NULL, *errmsg = NULL; + size_t len = 0; + + try + { + bool where_added = false; + + if (apol_str_append + (&select_stmt, &len, + "SELECT paths.path, paths.ino, devs.dev_name, users.user_name, roles.role_name, types.type_name") < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (q.db_is_mls && apol_str_append(&select_stmt, &len, ", mls.mls_range") < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (apol_str_append(&select_stmt, &len, + ", paths.obj_class, paths.symlink_target FROM paths, devs, users, roles, types") < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (q.db_is_mls && apol_str_append(&select_stmt, &len, ", mls") < 0) + { + throw std::runtime_error(strerror(errno)); + } + + if (q.user != NULL) + { + if (sqlite3_create_function(_db, "user_compare", 1, SQLITE_UTF8, &q, db_user_compare, NULL, NULL) != + SQLITE_OK) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (apol_str_appendf(&select_stmt, &len, + "%s (user_compare(users.user_name))", (where_added ? " AND" : " WHERE")) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + where_added = true; + } + + if (q.role != NULL) + { + if (sqlite3_create_function(_db, "role_compare", 1, SQLITE_UTF8, &q, db_role_compare, NULL, NULL) != + SQLITE_OK) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (apol_str_appendf(&select_stmt, &len, + "%s (role_compare(roles.role_name))", (where_added ? " AND" : " WHERE")) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + where_added = true; + } + + if (q.type != NULL) + { + if (sqlite3_create_function(_db, "type_compare", 1, SQLITE_UTF8, &q, db_type_compare, NULL, NULL) != + SQLITE_OK) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (apol_str_appendf(&select_stmt, &len, + "%s (type_compare(types.type_name))", (where_added ? " AND" : " WHERE")) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + where_added = true; + } + + if (q.db_is_mls && q.range != NULL) + { + if (sqlite3_create_function(_db, "range_compare", 1, SQLITE_UTF8, &q, db_range_compare, NULL, NULL) != + SQLITE_OK) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (apol_str_appendf(&select_stmt, &len, + "%s (range_compare(mls.mls_range))", (where_added ? " AND" : " WHERE")) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + where_added = true; + } + + if (query->_objclass != 0) + { + if (apol_str_appendf(&select_stmt, &len, + "%s (paths.obj_class = %d)", (where_added ? " AND" : " WHERE"), query->_objclass) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + where_added = true; + } + + if (q.path != NULL) + { + if (sqlite3_create_function(_db, "path_compare", 1, SQLITE_UTF8, &q, db_path_compare, NULL, NULL) != + SQLITE_OK) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (apol_str_appendf(&select_stmt, &len, + "%s (path_compare(paths.path))", (where_added ? " AND" : " WHERE")) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + where_added = true; + } + + if (query->_inode != 0) + { + if (apol_str_appendf(&select_stmt, &len, + "%s (paths.ino = %lu)", (where_added ? " AND" : " WHERE"), + static_cast < long unsigned int >(query->_inode)) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + where_added = true; + } + + if (query->_dev != 0) + { + if (sqlite3_create_function(_db, "dev_compare", 1, SQLITE_UTF8, &q, db_dev_compare, NULL, NULL) != + SQLITE_OK) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (apol_str_appendf(&select_stmt, &len, + "%s (dev_compare(devs.dev_name)", (where_added ? " AND" : " WHERE")) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + where_added = true; + } + + if (apol_str_appendf(&select_stmt, &len, + "%s (paths.user = users.user_id AND paths.role = roles.role_id AND paths.type = types.type_id", + (where_added ? " AND" : " WHERE")) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (q.db_is_mls && apol_str_appendf(&select_stmt, &len, " AND paths.range = mls.mls_id") < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (apol_str_append(&select_stmt, &len, " AND paths.dev = devs.dev_id) ORDER BY paths.path ASC") < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + + int rc = sqlite3_exec(_db, select_stmt, db_query_callback, &q, &errmsg); + if (rc != SQLITE_OK && (rc != SQLITE_ABORT || !q.aborted)) + { + SEFS_ERR(this, "%s", errmsg); + throw std::runtime_error(errmsg); + } + } + catch(...) + { + apol_vector_destroy(&q.type_list); + apol_mls_range_destroy(&q.apol_range); + free(select_stmt); + sqlite3_free(errmsg); + throw; + } + + apol_vector_destroy(&q.type_list); + apol_mls_range_destroy(&q.apol_range); + free(select_stmt); + sqlite3_free(errmsg); + return q.retval; +} + +bool sefs_db::isMLS() const +{ + int rc; + bool answer = false; + char *errmsg = NULL; + const char *select_stmt = "SELECT * FROM sqlite_master WHERE name='mls'"; + rc = sqlite3_exec(_db, select_stmt, db_row_exist_callback, &answer, &errmsg); + if (rc != SQLITE_OK) + { + SEFS_ERR(this, "%s", errmsg); + sqlite3_free(errmsg); + answer = false; + } + return answer; +} + +void sefs_db::save(const char *filename) throw(std::invalid_argument, std::runtime_error) +{ + FILE *fp = NULL; + struct db_callback_arg diskdb; + diskdb.db = NULL; + diskdb.errmsg = NULL; + bool in_transaction = false; + + try + { + if (filename == NULL) + { + errno = EINVAL; + throw std::invalid_argument(strerror(errno)); + } + // check that target file is creatable; this will also + // remove the file if it already exists + if ((fp = fopen(filename, "w")) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + fclose(fp); + fp = NULL; + + // copy database schema from in-memory db to the one on disk + if (sqlite3_open(filename, &(diskdb.db)) != SQLITE_OK) + { + SEFS_ERR(this, "%s", sqlite3_errmsg(diskdb.db)); + throw std::runtime_error(sqlite3_errmsg(diskdb.db)); + } + if (sqlite3_exec(_db, "SELECT sql FROM sqlite_master WHERE sql NOT NULL", db_copy_schema, &diskdb, &diskdb.errmsg) + != SQLITE_OK) + { + SEFS_ERR(this, "%s", diskdb.errmsg); + throw std::runtime_error(diskdb.errmsg); + } + sqlite3_close(diskdb.db); + + char *attach = NULL; + if (asprintf(&attach, "ATTACH '%s' AS diskdb", filename) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + diskdb.db = _db; + diskdb.source_db = ""; + diskdb.target_db = "diskdb."; + int rc = sqlite3_exec(_db, attach, NULL, NULL, &diskdb.errmsg); + free(attach); + if (rc != SQLITE_OK) + { + SEFS_ERR(this, "%s", diskdb.errmsg); + throw std::runtime_error(diskdb.errmsg); + } + + // copy contents from in-memory db to the one on disk + if (sqlite3_exec(_db, "BEGIN TRANSACTION", NULL, NULL, &(diskdb.errmsg)) != SQLITE_OK) + { + SEFS_ERR(this, "%s", diskdb.errmsg); + throw std::runtime_error(diskdb.errmsg); + } + in_transaction = true; + if (sqlite3_exec(_db, "SELECT name FROM sqlite_master WHERE type ='table'", db_copy_table, &diskdb, &diskdb.errmsg) + != SQLITE_OK) + { + SEFS_ERR(this, "%s", diskdb.errmsg); + throw std::runtime_error(diskdb.errmsg); + } + + sqlite3_exec(_db, "DETACH diskdb", NULL, NULL, NULL); + + if (sqlite3_exec(_db, "END TRANSACTION", NULL, 0, &(diskdb.errmsg)) != SQLITE_OK) + { + SEFS_ERR(this, "%s", diskdb.errmsg); + throw std::runtime_error(diskdb.errmsg); + } + in_transaction = false; + } + catch(...) + { + if (fp != NULL) + { + fclose(fp); + } + if (in_transaction) + { + sqlite3_exec(_db, "ROLLBACK TRANSACTION", NULL, NULL, NULL); + } + if (diskdb.db != NULL) + { + sqlite3_close(diskdb.db); + } + sqlite3_free(diskdb.errmsg); + throw; + } + // sqlite3_close(diskdb.db); don't close the database -- it's pointing to this->_db + sqlite3_free(diskdb.errmsg); +} + +time_t sefs_db::getCTime() const +{ + return _ctime; +} + +bool sefs_db::isDB(const char *filename) +{ + if (filename == NULL) + { + errno = EINVAL; + return false; + } + + int rc = access(filename, R_OK); + if (rc != 0) + { + return false; + } + + struct sqlite3 *db = NULL; + rc = sqlite3_open(filename, &db); + if (rc != SQLITE_OK) + { + sqlite3_close(db); + errno = EIO; + return false; + } + + // Run a simple query to check that the database is legal. + int list_size; + char *errmsg = NULL; + rc = sqlite3_exec(db, "SELECT type_name FROM types", db_count_callback, &list_size, &errmsg); + if (rc != SQLITE_OK) + { + sqlite3_close(db); + sqlite3_free(errmsg); + errno = EIO; + return false; + } + sqlite3_close(db); + return true; +} + +/******************** private functions below ********************/ + +const struct sefs_context_node *sefs_db::getContextNode(const sefs_entry * entry) +{ + return entry->_context; +} + +/** + * Callback invoked while upgrading a libsefs database version 1 to + * version 2. Merge the inodes and paths table into one, remap the + * object class value, and explicitly set the role and dev fields to + * zero. + */ +static int db_upgrade_reinsert(void *arg, int argc, char *argv[], char *column_names[]) +{ + struct sqlite3 *db = static_cast < struct sqlite3 *>(arg); + bool mls = (argc == 7); + assert(argc >= 6 && argc <= 7); + uint32_t obj_class = static_cast < uint32_t > (atoi(argv[(mls ? 5 : 4)])); + + switch (obj_class) + { + case 16: + obj_class = QPOL_CLASS_BLK_FILE; + break; + case 8: + obj_class = QPOL_CLASS_CHR_FILE; + break; + case 2: + obj_class = QPOL_CLASS_DIR; + break; + case 64: + obj_class = QPOL_CLASS_FIFO_FILE; + break; + case 1: + obj_class = QPOL_CLASS_FILE; + break; + case 4: + obj_class = QPOL_CLASS_LNK_FILE; + break; + case 32: + obj_class = QPOL_CLASS_SOCK_FILE; + break; + } + + char *insert_stmt = NULL; + if (mls) + { + if (asprintf(&insert_stmt, + "INSERT INTO new_paths (path, ino, dev, user, role, type, range, obj_class, symlink_target) VALUES ('%s', %s, 0, %s, 0, %s, %s, %u, '%s')", + argv[0], argv[1], argv[2], argv[3], argv[4], obj_class, argv[6]) < 0) + { + return -1; + } + } + else + { + if (asprintf(&insert_stmt, + "INSERT INTO new_paths (path, ino, dev, user, role, type, range, obj_class, symlink_target) VALUES ('%s', %s, 0, %s, 0, %s, 0, %u, '%s')", + argv[0], argv[1], argv[2], argv[3], obj_class, argv[5]) < 0) + { + return -1; + } + } + if (sqlite3_exec(db, insert_stmt, NULL, NULL, NULL) != SQLITE_OK) + { + free(insert_stmt); + return -1; + } + free(insert_stmt); + return 0; +} + +void sefs_db::upgradeToDB2() throw(std::runtime_error) +{ + char *errmsg; + + // Add a role field for each inode entry within the database; + // assume that the role is 'object_r'. Also update the object + // class values, from older class values to new ones. Old + // class_id values come from the old libsefs < 4.0 definitions + // that were in fsdata.h; the new style is in + // qpol/genfscon_query.h. + _ctime = time(NULL); + char datetime[32]; + ctime_r(&_ctime, datetime); + char *alter_stmt = NULL; + if (asprintf(&alter_stmt, "BEGIN TRANSACTION;" "CREATE TABLE roles (role_id INTEGER PRIMARY KEY, role_name varchar (24));" // add a roles table + "INSERT INTO roles (role_id, role_name) VALUES (0, 'object_r');" // assume that all previous contexts had as their role 'object_r' + "CREATE TABLE devs (dev_id INTEGER PRIMARY KEY, dev_name varchar (32));" // add a table that maps between device names and some numeric ID + "INSERT INTO devs (dev_id, dev_name) VALUES (0, '<<unknown>>');" // device names were not stored in old DB + "CREATE TABLE new_paths (path varchar (128) PRIMARY KEY, ino int(64), dev int, user int, role int, type int, range int, obj_class int, symlink_target varchar (128));" // create new paths table + "SELECT paths.path, inodes.ino, inodes.user, inodes.type, %sinodes.obj_class, inodes.symlink_target FROM paths, inodes WHERE (inodes.inode_id = paths.inode)", // rebuild new paths table from older tables + isMLS()? "inodes.range, " : "") < 0) + { + SEFS_ERR(this, "%s", errmsg); + sqlite3_free(errmsg); + sqlite3_close(_db); + throw std::runtime_error(strerror(errno)); + } + if (sqlite3_exec(_db, alter_stmt, db_upgrade_reinsert, _db, &errmsg) != SQLITE_OK) + { + SEFS_ERR(this, "%s", errmsg); + free(alter_stmt); + sqlite3_free(errmsg); + sqlite3_close(_db); + throw std::runtime_error(strerror(errno)); + } + + free(alter_stmt); + alter_stmt = NULL; + + if (asprintf(&alter_stmt, "DROP TABLE inodes; DROP TABLE paths;" // drop the old tables + "ALTER TABLE new_paths RENAME TO paths;" // move ver 2 paths table as main table + "UPDATE info SET value = '%s' WHERE key = 'datetime';" + "UPDATE info SET value = '%s' WHERE key = 'dbversion';" + "END TRANSACTION;" "VACUUM", datetime, DB_MAX_VERSION) < 0) + { + SEFS_ERR(this, "%s", errmsg); + sqlite3_free(errmsg); + sqlite3_close(_db); + throw std::runtime_error(strerror(errno)); + } + if (sqlite3_exec(_db, alter_stmt, NULL, NULL, &errmsg) != SQLITE_OK) + { + SEFS_ERR(this, "%s", errmsg); + free(alter_stmt); + sqlite3_free(errmsg); + sqlite3_close(_db); + throw std::runtime_error(strerror(errno)); + } + free(alter_stmt); +} + +sefs_entry *sefs_db::getEntry(const struct sefs_context_node *context, uint32_t objectClass, const char *path, ino64_t inode, + const char *dev) 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 = inode; + + s = NULL; + if ((s = strdup(dev)) == NULL || apol_bst_insert_and_get(dev_tree, (void **)&s, NULL) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + free(s); + throw std::bad_alloc(); + } + e->_dev = dev; + return e; +} + +/******************** C functions below ********************/ + +sefs_fclist_t *sefs_db_create_from_filesystem(sefs_filesystem_t * fs, sefs_callback_fn_t msg_callback, void *varg) +{ + sefs_fclist_t *fc = NULL; + try + { + fc = new sefs_db(fs, msg_callback, varg); + } + catch(...) + { + return NULL; + } + return fc; +} + +sefs_fclist_t *sefs_db_create_from_file(const char *filename, sefs_callback_fn_t msg_callback, void *varg) +{ + sefs_fclist_t *fc = NULL; + try + { + fc = new sefs_db(filename, msg_callback, varg); + } + catch(...) + { + return NULL; + } + return fc; +} + +int sefs_db_save(sefs_db_t * db, const char *filename) +{ + if (db == NULL) + { + SEFS_ERR(NULL, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + try + { + db->save(filename); + } + catch(...) + { + return -1; + } + return 0; +} + +time_t sefs_db_get_ctime(sefs_db_t * db) +{ + if (db == NULL) + { + SEFS_ERR(NULL, "%s", strerror(EINVAL)); + errno = EINVAL; + return static_cast < time_t > (-1); + } + return db->getCTime(); +} + +bool sefs_db_is_db(const char *filename) +{ + return sefs_db::isDB(filename); +} diff --git a/libsefs/src/entry.cc b/libsefs/src/entry.cc new file mode 100644 index 0000000..cf9a5e6 --- /dev/null +++ b/libsefs/src/entry.cc @@ -0,0 +1,213 @@ +/** + * @file + * Implementation of the sefs_entry 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 <qpol/genfscon_query.h> + +#include <assert.h> +#include <errno.h> + +/******************** public functions below ********************/ + +sefs_entry::sefs_entry(const sefs_entry * e) +{ + _fclist = e->_fclist; + _context = e->_context; + _inode = e->_inode; + _dev = e->_dev; + _objectClass = e->_objectClass; + _path = e->_path; + _origin = e->_origin; +} + +sefs_entry::~sefs_entry() +{ + // do nothing +} + +const apol_context_t *sefs_entry::context() const +{ + return _context->context; +} + +ino64_t sefs_entry::inode() const +{ + return _inode; +} + +const char *sefs_entry::dev() const +{ + return _dev; +} + +uint32_t sefs_entry::objectClass() const +{ + return _objectClass; +} + +const char *sefs_entry::path() const +{ + return _path; +} + +const char *sefs_entry::origin() const +{ + return _origin; +} + +char *sefs_entry::toString() const throw(std::bad_alloc) +{ + char *class_str; + + switch (_objectClass) + { + case QPOL_CLASS_ALL: + class_str = " "; + break; + case QPOL_CLASS_BLK_FILE: + class_str = "-b"; + break; + case QPOL_CLASS_CHR_FILE: + class_str = "-c"; + break; + case QPOL_CLASS_DIR: + class_str = "-d"; + break; + case QPOL_CLASS_FIFO_FILE: + class_str = "-p"; + break; + case QPOL_CLASS_FILE: + class_str = "--"; + break; + case QPOL_CLASS_LNK_FILE: + class_str = "-l"; + break; + case QPOL_CLASS_SOCK_FILE: + class_str = "-s"; + break; + default: + // should never get here + assert(0); + class_str = "-?"; + } + + char *s = NULL; + if (asprintf(&s, "%s\t%s\t%s", _path, class_str, _context->context_str) < 0) + { + SEFS_ERR(_fclist, "%s", strerror(errno)); + throw std::bad_alloc(); + } + return s; +} + +/******************** private functions below ********************/ + +sefs_entry::sefs_entry(class sefs_fclist * fclist, const struct sefs_context_node * new_context, uint32_t new_objectClass, + const char *new_path, const char *new_origin) +{ + _fclist = fclist; + _context = new_context; + _objectClass = new_objectClass; + _inode = 0; + _dev = NULL; + _path = new_path; + _origin = new_origin; +} + +/******************** C functions below ********************/ + +const apol_context_t *sefs_entry_get_context(const sefs_entry_t * ent) +{ + if (ent == NULL) + { + errno = EINVAL; + return NULL; + } + return ent->context(); +} + +ino64_t sefs_entry_get_inode(const sefs_entry_t * ent) +{ + if (ent == NULL) + { + errno = EINVAL; + return 0; + } + return ent->inode(); +} + +const char *sefs_entry_get_dev(const sefs_entry_t * ent) +{ + if (ent == NULL) + { + errno = EINVAL; + return 0; + } + return ent->dev(); +} + +uint32_t sefs_entry_get_object_class(const sefs_entry_t * ent) +{ + if (ent == NULL) + { + errno = EINVAL; + return QPOL_CLASS_ALL; + } + return ent->objectClass(); +} + +const char *sefs_entry_get_path(const sefs_entry_t * ent) +{ + if (ent == NULL) + { + errno = EINVAL; + return NULL; + } + return ent->path(); +} + +const char *sefs_entry_get_origin(const sefs_entry_t * ent) +{ + if (ent == NULL) + { + errno = EINVAL; + return NULL; + } + return ent->origin(); +} + +char *sefs_entry_to_string(const sefs_entry_t * ent) +{ + if (ent == NULL) + { + errno = EINVAL; + return NULL; + } + return ent->toString(); +} 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(®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, "<<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(); +} 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; + } +} 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 <config.h> + +#include "sefs_internal.hh" +#include "new_ftw.h" + +#include <sefs/entry.hh> +#include <sefs/filesystem.hh> +#include <apol/util.h> +#include <selinux/context.h> +#include <selinux/selinux.h> +#include <assert.h> +#include <errno.h> +#include <mntent.h> +#include <regex.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> + +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 = "<unknown>"; + 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; +} diff --git a/libsefs/src/libsefs.map b/libsefs/src/libsefs.map new file mode 100644 index 0000000..f6a6b28 --- /dev/null +++ b/libsefs/src/libsefs.map @@ -0,0 +1,29 @@ +VERS_4.0 { + global: + extern "C++" { + # typeinfo exports + *sefs_db; + *sefs_entry; + *sefs_fcfile; + *sefs_fclist; + *sefs_filesystem; + *sefs_query; + *std::invalid_argument; + + sefs_db::*; + sefs_entry::*; + sefs_fcfile::*; + sefs_fclist::*; + sefs_filesystem::*; + sefs_query::*; + }; + sefs_db_*; + sefs_default_file_contexts_get_path; + sefs_entry_*; + sefs_fcfile_*; + sefs_fclist_*; + sefs_filesystem_*; + sefs_query_*; + libsefs_get_version; + local: *; +}; diff --git a/libsefs/src/new_ftw.c b/libsefs/src/new_ftw.c new file mode 100644 index 0000000..5ebaf29 --- /dev/null +++ b/libsefs/src/new_ftw.c @@ -0,0 +1,749 @@ +/* File tree walker functions. + Copyright (C) 1996-2003, 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. + + The GNU C 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. + + The GNU C 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 the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/** + * @file + * + * Implementation of the improved new_ftw() and new_nftw() functions. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if __GNUC__ +# define alloca __builtin_alloca +#else +# if HAVE_ALLOCA_H +# include <alloca.h> +# else +# ifdef _AIX +# pragma alloca +# else +char *alloca(); +# endif +# endif +#endif + +#if defined _LIBC +# include <dirent.h> +# define NAMLEN(dirent) _D_EXACT_NAMLEN (dirent) +#else +# if HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen ((dirent)->d_name) +# else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# if HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# if HAVE_NDIR_H +# include <ndir.h> +# endif +# endif +#endif + +#include <dirent.h> +#undef NAMLEN +#define NAMLEN(dirent) strlen ((dirent)->d_name) + +#include <errno.h> +#include "new_ftw.h" +#include <limits.h> +#include <search.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#if HAVE_SYS_PARAM_H || defined _LIBC +# include <sys/param.h> +#endif +#ifdef _LIBC +# include <include/sys/stat.h> +#else +# include <sys/stat.h> +#endif + +#if ! _LIBC && !HAVE_DECL_STPCPY && !defined stpcpy +char *stpcpy(); +#endif + +#if ! _LIBC && ! defined HAVE_MEMPCPY && ! defined mempcpy +/* Be CAREFUL that there are no side effects in N. */ +# define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N))) +#endif + +/* #define NDEBUG 1 */ +#include <assert.h> + +#ifndef _LIBC +# undef __chdir +# define __chdir chdir +# undef __closedir +# define __closedir closedir +# undef __fchdir +# define __fchdir fchdir +# undef __getcwd +# define __getcwd(P, N) xgetcwd () +extern char *xgetcwd(void); +# undef __mempcpy +# define __mempcpy mempcpy +# undef __opendir +# define __opendir opendir +# undef __readdir64 +# define __readdir64 readdir +# undef __stpcpy +# define __stpcpy stpcpy +# undef __tdestroy +# define __tdestroy tdestroy +# undef __tfind +# define __tfind tfind +# undef __tsearch +# define __tsearch tsearch +# undef internal_function +# define internal_function /* empty */ +# undef dirent64 +# define dirent64 dirent +# undef MAX +# define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +/* Arrange to make lstat calls go through the wrapper function + on systems with an lstat function that does not dereference symlinks + that are specified with a trailing slash. */ +#if ! _LIBC && ! LSTAT_FOLLOWS_SLASHED_SYMLINK +int rpl_lstat(const char *, struct stat *); +# undef lstat +# define lstat(Name, Stat_buf) rpl_lstat(Name, Stat_buf) +#endif + +#ifndef __set_errno +# define __set_errno(Val) errno = (Val) +#endif + +/* Support for the LFS API version. */ +#ifndef NEW_FTW_NAME +# define NEW_FTW_NAME new_ftw +# define NEW_NFTW_NAME new_nftw +# define NEW_NFTW_OLD_NAME __new_old_nftw +# define NEW_NFTW_NEW_NAME __new_new_nftw +# define INO_T ino_t +# define STAT stat +# ifdef _LIBC +# define LXSTAT __lxstat +# define XSTAT __xstat +# else +# define LXSTAT(V,f,sb) lstat (f,sb) +# define XSTAT(V,f,sb) stat (f,sb) +# endif +# define NEW_FTW_FUNC_T __new_ftw_func_t +# define NEW_NFTW_FUNC_T __new_nftw_func_t +#endif + +/* We define PATH_MAX if the system does not provide a definition. + This does not artificially limit any operation. PATH_MAX is simply + used as a guesstimate for the expected maximal path length. + Buffers will be enlarged if necessary. */ +#ifndef PATH_MAX +# define PATH_MAX 1024 +#endif + +struct dir_data +{ + DIR *stream; + char *content; +}; + +struct known_object +{ + dev_t dev; + INO_T ino; +}; + +struct new_ftw_data +{ + /* Array with pointers to open directory streams. */ + struct dir_data **dirstreams; + size_t actdir; + size_t maxdir; + + /* Buffer containing name of currently processed object. */ + char *dirbuf; + size_t dirbufsize; + + /* Passed as fourth argument to `nftw' callback. The `base' member + tracks the content of the `dirbuf'. */ + struct FTW ftw; + + /* Flags passed to `nftw' function. 0 for `ftw'. */ + int flags; + + /* Conversion array for flag values. It is the identity mapping for + `nftw' calls, otherwise it maps the values to those known by + `ftw'. */ + const int *cvt_arr; + + /* Callback function. We always use the `nftw' form. */ + NEW_NFTW_FUNC_T func; + void *data; + + /* Device of starting point. Needed for FTW_MOUNT. */ + dev_t dev; + + /* Data structure for keeping fingerprints of already processed + object. This is needed when not using FTW_PHYS. */ + void *known_objects; +}; + +/* Internally we use the FTW_* constants used for `nftw'. When invoked + as `ftw', map each flag to the subset of values used by `ftw'. */ +static const int nftw_arr[] = { + FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_SL, FTW_DP, FTW_SLN +}; + +static const int ftw_arr[] = { + FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_F, FTW_D, FTW_NS +}; + +/* Forward declarations of local functions. */ +static int ftw_dir(struct new_ftw_data *data, struct STAT *st, struct dir_data *old_dir) internal_function; + +static int object_compare(const void *p1, const void *p2) +{ + /* We don't need a sophisticated and useful comparison. We are only + interested in equality. However, we must be careful not to + accidentally compare `holes' in the structure. */ + const struct known_object *kp1 = p1, *kp2 = p2; + int cmp1; + cmp1 = (kp1->ino > kp2->ino) - (kp1->ino < kp2->ino); + if (cmp1 != 0) + return cmp1; + return (kp1->dev > kp2->dev) - (kp1->dev < kp2->dev); +} + +static inline int add_object(struct new_ftw_data *data, struct STAT *st) +{ + struct known_object *newp = malloc(sizeof(struct known_object)); + if (newp == NULL) + return -1; + newp->dev = st->st_dev; + newp->ino = st->st_ino; + return __tsearch(newp, &data->known_objects, object_compare) ? 0 : -1; +} + +static inline int find_object(struct new_ftw_data *data, struct STAT *st) +{ + struct known_object obj; + obj.dev = st->st_dev; + obj.ino = st->st_ino; + return __tfind(&obj, &data->known_objects, object_compare) != NULL; +} + +static inline int __attribute((always_inline)) open_dir_stream(struct new_ftw_data *data, struct dir_data *dirp) +{ + int result = 0; + + if (data->dirstreams[data->actdir] != NULL) { + /* Oh, oh. We must close this stream. Get all remaining + entries and store them as a list in the `content' member of + the `struct dir_data' variable. */ + size_t bufsize = 1024; + char *buf = malloc(bufsize); + + if (buf == NULL) + result = -1; + else { + DIR *st = data->dirstreams[data->actdir]->stream; + struct dirent64 *d; + size_t actsize = 0; + + while ((d = __readdir64(st)) != NULL) { + size_t this_len = NAMLEN(d); + if (actsize + this_len + 2 >= bufsize) { + char *newp; + bufsize += MAX(1024, 2 * this_len); + newp = (char *)realloc(buf, bufsize); + if (newp == NULL) { + /* No more memory. */ + int save_err = errno; + free(buf); + __set_errno(save_err); + result = -1; + break; + } + buf = newp; + } + + *((char *)__mempcpy(buf + actsize, d->d_name, this_len)) + = '\0'; + actsize += this_len + 1; + } + + /* Terminate the list with an additional NUL byte. */ + buf[actsize++] = '\0'; + + /* Shrink the buffer to what we actually need. */ + data->dirstreams[data->actdir]->content = realloc(buf, actsize); + if (data->dirstreams[data->actdir]->content == NULL) { + int save_err = errno; + free(buf); + __set_errno(save_err); + result = -1; + } else { + __closedir(st); + data->dirstreams[data->actdir]->stream = NULL; + data->dirstreams[data->actdir] = NULL; + } + } + } + + /* Open the new stream. */ + if (result == 0) { + const char *name = ((data->flags & FTW_CHDIR) + ? data->dirbuf + data->ftw.base : data->dirbuf); + assert(data->dirstreams[data->actdir] == NULL); + + dirp->stream = __opendir(name); + if (dirp->stream == NULL) + result = -1; + else { + dirp->content = NULL; + data->dirstreams[data->actdir] = dirp; + + if (++data->actdir == data->maxdir) + data->actdir = 0; + } + } + + return result; +} + +static int internal_function process_entry(struct new_ftw_data *data, struct dir_data *dir, const char *name, size_t namlen) +{ + struct STAT st; + int result = 0; + int flag = 0; + size_t new_buflen; + + if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) + /* Don't process the "." and ".." entries. */ + return 0; + + new_buflen = data->ftw.base + namlen + 2; + if (data->dirbufsize < new_buflen) { + /* Enlarge the buffer. */ + char *newp; + + data->dirbufsize = 2 * new_buflen; + newp = (char *)realloc(data->dirbuf, data->dirbufsize); + if (newp == NULL) + return -1; + data->dirbuf = newp; + } + + *((char *)__mempcpy(data->dirbuf + data->ftw.base, name, namlen)) = '\0'; + + if ((data->flags & FTW_CHDIR) == 0) + name = data->dirbuf; + + if (((data->flags & FTW_PHYS) + ? LXSTAT(_STAT_VER, name, &st) + : XSTAT(_STAT_VER, name, &st)) < 0) { + if (errno != EACCES && errno != ENOENT) + result = -1; + else if (!(data->flags & FTW_PHYS) + && LXSTAT(_STAT_VER, name, &st) == 0 && S_ISLNK(st.st_mode)) + flag = FTW_SLN; + else + flag = FTW_NS; + } else { + if (S_ISDIR(st.st_mode)) + flag = FTW_D; + else if (S_ISLNK(st.st_mode)) + flag = FTW_SL; + else + flag = FTW_F; + } + + if (result == 0 && (flag == FTW_NS || !(data->flags & FTW_MOUNT) || st.st_dev == data->dev)) { + if (flag == FTW_D) { + if ((data->flags & FTW_PHYS) + || (!find_object(data, &st) + /* Remember the object. */ + && (result = add_object(data, &st)) == 0)) + result = ftw_dir(data, &st, dir); + } else + result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag], &data->ftw, data->data); + } + + if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SUBTREE) + result = 0; + + return result; +} + +static int __attribute((noinline)) +internal_function ftw_dir(struct new_ftw_data *data, struct STAT *st, struct dir_data *old_dir) +{ + struct dir_data dir; + struct dirent64 *d; + int previous_base = data->ftw.base; + int result; + char *startp; + + /* Open the stream for this directory. This might require that + another stream has to be closed. */ + result = open_dir_stream(data, &dir); + if (result != 0) { + if (errno == EACCES) + /* We cannot read the directory. Signal this with a special flag. */ + result = (*data->func) (data->dirbuf, st, FTW_DNR, &data->ftw, data->data); + + return result; + } + + /* First, report the directory (if not depth-first). */ + if (!(data->flags & FTW_DEPTH)) { + result = (*data->func) (data->dirbuf, st, FTW_D, &data->ftw, data->data); + if (result != 0) { + int save_err; + fail: + save_err = errno; + __closedir(dir.stream); + __set_errno(save_err); + + if (data->actdir-- == 0) + data->actdir = data->maxdir - 1; + data->dirstreams[data->actdir] = NULL; + return result; + } + } + + /* If necessary, change to this directory. */ + if (data->flags & FTW_CHDIR) { + if (__fchdir(dirfd(dir.stream)) < 0) { + result = -1; + goto fail; + } + } + + /* Next, update the `struct FTW' information. */ + ++data->ftw.level; + startp = strchr(data->dirbuf, '\0'); + /* There always must be a directory name. */ + assert(startp != data->dirbuf); + if (startp[-1] != '/') + *startp++ = '/'; + data->ftw.base = startp - data->dirbuf; + + while (dir.stream != NULL && (d = __readdir64(dir.stream)) != NULL) { + result = process_entry(data, &dir, d->d_name, NAMLEN(d)); + if (result != 0) + break; + } + + if (dir.stream != NULL) { + /* The stream is still open. I.e., we did not need more + descriptors. Simply close the stream now. */ + int save_err = errno; + + assert(dir.content == NULL); + + __closedir(dir.stream); + __set_errno(save_err); + + if (data->actdir-- == 0) + data->actdir = data->maxdir - 1; + data->dirstreams[data->actdir] = NULL; + } else { + int save_err; + char *runp = dir.content; + + while (result == 0 && *runp != '\0') { + char *endp = strchr(runp, '\0'); + + result = process_entry(data, &dir, runp, endp - runp); + + runp = endp + 1; + } + + save_err = errno; + free(dir.content); + __set_errno(save_err); + } + + if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SIBLINGS) + result = 0; + + /* Prepare the return, revert the `struct FTW' information. */ + data->dirbuf[data->ftw.base - 1] = '\0'; + --data->ftw.level; + data->ftw.base = previous_base; + + /* Finally, if we process depth-first report the directory. */ + if (result == 0 && (data->flags & FTW_DEPTH)) + result = (*data->func) (data->dirbuf, st, FTW_DP, &data->ftw, data->data); + + if (old_dir && (data->flags & FTW_CHDIR) + && (result == 0 || ((data->flags & FTW_ACTIONRETVAL) + && (result != -1 && result != FTW_STOP)))) { + /* Change back to the parent directory. */ + int done = 0; + if (old_dir->stream != NULL) + if (__fchdir(dirfd(old_dir->stream)) == 0) + done = 1; + + if (!done) { + if (data->ftw.base == 1) { + if (__chdir("/") < 0) + result = -1; + } else if (__chdir("..") < 0) + result = -1; + } + } + + return result; +} + +static int __attribute((noinline)) +internal_function new_ftw_startup(const char *dir, int is_nftw, void *func, int descriptors, int flags, void *func_data) +{ + struct new_ftw_data data; + struct STAT st; + int result = 0; + int save_err; + char *cwd = NULL; + char *cp; + + /* First make sure the parameters are reasonable. */ + if (dir[0] == '\0') { + __set_errno(ENOENT); + return -1; + } + + data.maxdir = descriptors < 1 ? 1 : descriptors; + data.actdir = 0; + data.dirstreams = (struct dir_data **)alloca(data.maxdir * sizeof(struct dir_data *)); + memset(data.dirstreams, '\0', data.maxdir * sizeof(struct dir_data *)); + + /* PATH_MAX is always defined when we get here. */ + data.dirbufsize = MAX(2 * strlen(dir), PATH_MAX); + data.dirbuf = (char *)malloc(data.dirbufsize); + if (data.dirbuf == NULL) + return -1; + cp = __stpcpy(data.dirbuf, dir); + /* Strip trailing slashes. */ + while (cp > data.dirbuf + 1 && cp[-1] == '/') + --cp; + *cp = '\0'; + + data.ftw.level = 0; + + /* Find basename. */ + while (cp > data.dirbuf && cp[-1] != '/') + --cp; + data.ftw.base = cp - data.dirbuf; + + data.flags = flags; + + /* This assignment might seem to be strange but it is what we want. + The trick is that the first three arguments to the `ftw' and + `nftw' callback functions are equal. Therefore we can call in + every case the callback using the format of the `nftw' version + and get the correct result since the stack layout for a function + call in C allows this. */ + data.func = (NEW_NFTW_FUNC_T) func; + data.data = func_data; + + /* Since we internally use the complete set of FTW_* values we need + to reduce the value range before calling a `ftw' callback. */ + data.cvt_arr = is_nftw ? nftw_arr : ftw_arr; + + /* No object known so far. */ + data.known_objects = NULL; + + /* Now go to the directory containing the initial file/directory. */ + if (flags & FTW_CHDIR) { + /* GNU extension ahead. */ + cwd = __getcwd(NULL, 0); + if (cwd == NULL) + result = -1; + else if (data.ftw.base > 0) { + /* Change to the directory the file is in. In data.dirbuf + we have a writable copy of the file name. Just NUL + terminate it for now and change the directory. */ + if (data.ftw.base == 1) + /* I.e., the file is in the root directory. */ + result = __chdir("/"); + else { + char ch = data.dirbuf[data.ftw.base - 1]; + data.dirbuf[data.ftw.base - 1] = '\0'; + result = __chdir(data.dirbuf); + data.dirbuf[data.ftw.base - 1] = ch; + } + } + } + + /* Get stat info for start directory. */ + if (result == 0) { + const char *name = ((data.flags & FTW_CHDIR) + ? data.dirbuf + data.ftw.base : data.dirbuf); + + if (((flags & FTW_PHYS) + ? LXSTAT(_STAT_VER, name, &st) + : XSTAT(_STAT_VER, name, &st)) < 0) { + if (!(flags & FTW_PHYS) + && errno == ENOENT && LXSTAT(_STAT_VER, name, &st) == 0 && S_ISLNK(st.st_mode)) + result = (*data.func) (data.dirbuf, &st, data.cvt_arr[FTW_SLN], &data.ftw, data.data); + else + /* No need to call the callback since we cannot say anything + about the object. */ + result = -1; + } else { + if (S_ISDIR(st.st_mode)) { + /* Remember the device of the initial directory in case + FTW_MOUNT is given. */ + data.dev = st.st_dev; + + /* We know this directory now. */ + if (!(flags & FTW_PHYS)) + result = add_object(&data, &st); + + if (result == 0) + result = ftw_dir(&data, &st, NULL); + } else { + int flag = S_ISLNK(st.st_mode) ? FTW_SL : FTW_F; + + result = (*data.func) (data.dirbuf, &st, data.cvt_arr[flag], &data.ftw, data.data); + } + } + + if ((flags & FTW_ACTIONRETVAL) + && (result == FTW_SKIP_SUBTREE || result == FTW_SKIP_SIBLINGS)) + result = 0; + } + + /* Return to the start directory (if necessary). */ + if (cwd != NULL) { + int save_err = errno; + __chdir(cwd); + free(cwd); + __set_errno(save_err); + } + + /* Free all memory. */ + save_err = errno; + __tdestroy(data.known_objects, free); + free(data.dirbuf); + __set_errno(save_err); + + return result; +} + +/* Entry points. */ + +int NEW_FTW_NAME(path, func, descriptors, data) +const char *path; +NEW_FTW_FUNC_T func; +int descriptors; +void *data; +{ + return new_ftw_startup(path, 0, func, descriptors, 0, data); +} + +#ifndef _LIBC +int NEW_NFTW_NAME(path, func, descriptors, flags, data) +const char *path; +NEW_NFTW_FUNC_T func; +int descriptors; +int flags; +void *data; +{ + return new_ftw_startup(path, 1, func, descriptors, flags, data); +} +#else + +#include <shlib-compat.h> + +int NEW_NFTW_NEW_NAME(const char *, NEW_NFTW_FUNC_T, int, int, void *); + +int NEW_NFTW_NEW_NAME(path, func, descriptors, flags, data) +const char *path; +NEW_NFTW_FUNC_T func; +int descriptors; +int flags; +void *data; +{ + if (flags & ~(FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH | FTW_ACTIONRETVAL)) { + __set_errno(EINVAL); + return -1; + } + return new_ftw_startup(path, 1, func, descriptors, flags, data); +} + +//versioned_symbol (libc, NFTW_NEW_NAME, NFTW_NAME, GLIBC_2_3_3); + +#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_3_3) + +/* Older nftw* version just ignored all unknown flags. */ + +int NEW_NFTW_OLD_NAME(const char *, NEW_NFTW_FUNC_T, int, int, void *); + +int attribute_compat_text_section NFTW_OLD_NAME(path, func, descriptors, flags, void *) +const char *path; +NFTW_FUNC_T func; +int descriptors; +int flags; +void *data; +{ + flags &= (FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH); + return ftw_startup(path, 1, func, descriptors, flags, data); +} + +compat_symbol(libc, NFTW_OLD_NAME, NFTW_NAME, GLIBC_2_1); +#endif +#endif + +int rpl_lstat(file, sbuf) +const char *file; +struct stat *sbuf; +{ + if (file && *file == 0) { + errno = EINVAL; + return -1; + } + + return lstat(file, sbuf); +} + +#include <stdio.h> + +char *xgetcwd(void) +{ + char *cwd = getcwd(NULL, 0); + if (!cwd && errno == ENOMEM) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + return cwd; +} diff --git a/libsefs/src/new_ftw.h b/libsefs/src/new_ftw.h new file mode 100644 index 0000000..dfc6905 --- /dev/null +++ b/libsefs/src/new_ftw.h @@ -0,0 +1,183 @@ +/* Copyright (C) 1992,1996-1999,2003,2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C 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. + + The GNU C 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 the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/** + * @file + * + * This file looks and acts like /usr/include/ftw.h, with one + * <em>significant</em> difference. The standard ftw() and nftw() + * functions do not permit passing an arbitrary pointer into the + * callback. This greatly limits the functions' usefulness on + * object-oriented systems and in thread-safe libraries. Thus the + * header file declares two new functions, new_ftw() and new_nftw(), + * that act the same as the original, with the addition of an + * arbitrary void pointer as the last argument. This void pointer, + * which may be NULL, is passed into the callback function's last + * parameter. + */ + +/* + * X/Open Portability Guide 4.2: ftw.h + */ + +#ifndef _NEW_FTW_H +#define _NEW_FTW_H 1 + +#include <features.h> + +#include <sys/types.h> +#include <sys/stat.h> + +__BEGIN_DECLS +/* Values for the FLAG argument to the user function passed to `ftw' + and 'nftw'. */ + enum +{ + FTW_F, /* Regular file. */ +#define FTW_F FTW_F + FTW_D, /* Directory. */ +#define FTW_D FTW_D + FTW_DNR, /* Unreadable directory. */ +#define FTW_DNR FTW_DNR + FTW_NS, /* Unstatable file. */ +#define FTW_NS FTW_NS + +#if defined __USE_BSD || defined __USE_XOPEN_EXTENDED + + FTW_SL, /* Symbolic link. */ +# define FTW_SL FTW_SL +#endif + +#ifdef __USE_XOPEN_EXTENDED +/* These flags are only passed from the `nftw' function. */ + FTW_DP, /* Directory, all subdirs have been visited. */ +# define FTW_DP FTW_DP + FTW_SLN /* Symbolic link naming non-existing file. */ +# define FTW_SLN FTW_SLN +#endif /* extended X/Open */ +}; + +#ifdef __USE_XOPEN_EXTENDED +/* Flags for fourth argument of `nftw'. */ +enum +{ + FTW_PHYS = 1, /* Perform physical walk, ignore symlinks. */ +# define FTW_PHYS FTW_PHYS + FTW_MOUNT = 2, /* Report only files on same file system as the + argument. */ +# define FTW_MOUNT FTW_MOUNT + FTW_CHDIR = 4, /* Change to current directory while processing it. */ +# define FTW_CHDIR FTW_CHDIR + FTW_DEPTH = 8 /* Report files in directory before directory itself. */ +# define FTW_DEPTH FTW_DEPTH +# ifdef __USE_GNU + , + FTW_ACTIONRETVAL = 16 /* Assume callback to return FTW_* values instead of + zero to continue and non-zero to terminate. */ +# define FTW_ACTIONRETVAL FTW_ACTIONRETVAL +# endif +}; + +#ifdef __USE_GNU +/* Return values from callback functions. */ +enum +{ + FTW_CONTINUE = 0, /* Continue with next sibling or for FTW_D with the + first child. */ +# define FTW_CONTINUE FTW_CONTINUE + FTW_STOP = 1, /* Return from `ftw' or `nftw' with FTW_STOP as return + value. */ +# define FTW_STOP FTW_STOP + FTW_SKIP_SUBTREE = 2, /* Only meaningful for FTW_D: Don't walk through the + subtree, instead just continue with its next + sibling. */ +# define FTW_SKIP_SUBTREE FTW_SKIP_SUBTREE + FTW_SKIP_SIBLINGS = 3 /* Continue with FTW_DP callback for current directory + (if FTW_DEPTH) and then its siblings. */ +# define FTW_SKIP_SIBLINGS FTW_SKIP_SIBLINGS +}; +#endif + +/* Structure used for fourth argument to callback function for `nftw'. */ +struct FTW +{ + int base; + int level; +}; +#endif /* extended X/Open */ + +/* Convenient types for callback functions. */ +typedef int (*__new_ftw_func_t) (__const char *__filename, __const struct stat * __status, int __flag, void *__data); +#ifdef __USE_LARGEFILE64 +typedef int (*__new_ftw64_func_t) (__const char *__filename, __const struct stat64 * __status, int __flag, void *__data); +#endif +#ifdef __USE_XOPEN_EXTENDED +typedef int (*__new_nftw_func_t) (__const char *__filename, + __const struct stat * __status, int __flag, struct FTW * __info, void *__data); +# ifdef __USE_LARGEFILE64 +typedef int (*__new_nftw64_func_t) (__const char *__filename, + __const struct stat64 * __status, int __flag, struct FTW * __info, void *__data); +# endif +#endif + +/* Call a function on every element in a directory tree. + + This function is a possible cancellation point and therefore not + marked with __THROW. */ +#ifndef __USE_FILE_OFFSET64 +extern int new_ftw(__const char *__dir, __new_ftw_func_t __func, int __descriptors, void *__data) __nonnull((1, 2)); +#else +# ifdef __REDIRECT +extern int __REDIRECT(new_ftw, (__const char *__dir, __new_ftw_func_t __func, + int __descriptors, void *__data), new_ftw64) __nonnull((1, 2)); +# else +# define new_ftw new_ftw64 +# endif +#endif +#ifdef __USE_LARGEFILE64 +extern int new_ftw64(__const char *__dir, __new_ftw64_func_t __func, int __descriptors, void *__data) __nonnull((1, 2)); +#endif + +#ifdef __USE_XOPEN_EXTENDED +/* Call a function on every element in a directory tree. FLAG allows + to specify the behaviour more detailed. + + This function is a possible cancellation point and therefore not + marked with __THROW. */ +# ifndef __USE_FILE_OFFSET64 +extern int new_nftw(__const char *__dir, __new_nftw_func_t __func, int __descriptors, int __flag, void *__data) __nonnull((1, 2)); +# else +# ifdef __REDIRECT +extern int __REDIRECT(new_nftw, (__const char *__dir, __new_nftw_func_t __func, + int __descriptors, int __flag, void *__data), new_nftw64) __nonnull((1, 2)); +# else +# define new_nftw new_nftw64 +# endif +# endif +# ifdef __USE_LARGEFILE64 +extern int new_nftw64(__const char *__dir, __new_nftw64_func_t __func, + int __descriptors, int __flag, void *__data) __nonnull((1, 2)); +# endif +#endif + +/* Compile new_nftw() to always have LFS enabled. */ +extern int __REDIRECT(new_nftw, (__const char *__dir, __new_nftw_func_t __func, + int __descriptors, int __flag, void *__data), new_nftw64) __nonnull((1, 2)); + +__END_DECLS +#endif /* ftw.h */ diff --git a/libsefs/src/query.cc b/libsefs/src/query.cc new file mode 100644 index 0000000..64c4e6b --- /dev/null +++ b/libsefs/src/query.cc @@ -0,0 +1,431 @@ +/** + * @file + * Implementation of the sefs_query 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/query.hh> +#include <apol/util.h> +#include <qpol/genfscon_query.h> + +#include <assert.h> +#include <errno.h> + +/******************** public functions below ********************/ + +sefs_query::sefs_query() +{ + _user = _role = _type = _range = NULL; + _path = _dev = NULL; + _objclass = QPOL_CLASS_ALL; + _indirect = _regex = _recursive = false; + _inode = 0; + _recompiled = false; + _reuser = _rerole = _retype = _rerange = _repath = _redev = NULL; +} + +sefs_query::~sefs_query() +{ + free(_user); + free(_role); + free(_type); + free(_range); + free(_path); + free(_dev); + if (_recompiled) + { + regfree(_reuser); + free(_reuser); + regfree(_rerole); + free(_rerole); + regfree(_retype); + free(_retype); + regfree(_rerange); + free(_rerange); + regfree(_repath); + free(_repath); + regfree(_redev); + free(_redev); + } +} + +void sefs_query::user(const char *name) throw(std::bad_alloc) +{ + if (name != _user) + { + free(_user); + _user = NULL; + if (name != NULL && *name != '\0' && (_user = strdup(name)) == NULL) + { + throw std::bad_alloc(); + } + } +} + +void sefs_query::role(const char *name) throw(std::bad_alloc) +{ + if (name != _role) + { + free(_role); + _role = NULL; + if (name != NULL && *name != '\0' && (_role = strdup(name)) == NULL) + { + throw std::bad_alloc(); + } + } +} + +void sefs_query::type(const char *name, bool indirect) throw(std::bad_alloc) +{ + if (name != _type) + { + free(_type); + _type = NULL; + if (name != NULL && *name != '\0') + { + if ((_type = strdup(name)) == NULL) + { + throw std::bad_alloc(); + } + _indirect = indirect; + } + } +} + +void sefs_query::range(const char *name, int match) throw(std::bad_alloc) +{ + if (name != _range) + { + free(_range); + _range = NULL; + if (name != NULL && *name != '\0') + { + if ((_range = strdup(name)) == NULL) + { + throw std::bad_alloc(); + } + _rangeMatch = match; + } + } +} + +void sefs_query::objectClass(uint32_t objclass) +{ + _objclass = objclass; +} + +void sefs_query::objectClass(const char *name) +{ + if (name == NULL || *name == '\0' || strcmp(name, "any") == 0) + { + _objclass = QPOL_CLASS_ALL; + } + else + { + uint32_t o = apol_str_to_objclass(name); + if (o != QPOL_CLASS_ALL) + { + _objclass = o; + } + } +} + +void sefs_query::path(const char *str) throw(std::bad_alloc) +{ + if (str != _path) + { + free(_path); + _path = NULL; + if (str != NULL && *str != '\0' && (_path = strdup(str)) == NULL) + { + throw std::bad_alloc(); + } + } +} + +void sefs_query::inode(ino64_t ino) +{ + _inode = ino; +} + +void sefs_query::dev(const char *str) throw(std::bad_alloc) +{ + if (str != _dev) + { + free(_dev); + _dev = NULL; + if (str != NULL && *str != '\0' && (_dev = strdup(str)) == NULL) + { + throw std::bad_alloc(); + } + } +} + +void sefs_query::regex(bool r) +{ + _regex = r; +} + +/******************** private functions below ********************/ + +void sefs_query::compile() throw(std::bad_alloc, std::invalid_argument) +{ + if (_recompiled) + { + regfree(_reuser); + regfree(_rerole); + regfree(_retype); + regfree(_rerange); + regfree(_repath); + regfree(_redev); + } + else + { + if ((_reuser = static_cast < regex_t * >(malloc(sizeof(*_reuser)))) == NULL) + { + throw std::bad_alloc(); + } + if ((_rerole = static_cast < regex_t * >(malloc(sizeof(*_rerole)))) == NULL) + { + throw std::bad_alloc(); + } + if ((_retype = static_cast < regex_t * >(malloc(sizeof(*_retype)))) == NULL) + { + throw std::bad_alloc(); + } + if ((_rerange = static_cast < regex_t * >(malloc(sizeof(*_rerange)))) == NULL) + { + throw std::bad_alloc(); + } + if ((_repath = static_cast < regex_t * >(malloc(sizeof(*_repath)))) == NULL) + { + throw std::bad_alloc(); + } + if ((_redev = static_cast < regex_t * >(malloc(sizeof(*_redev)))) == NULL) + { + throw std::bad_alloc(); + } + } + char errbuf[1024] = { '\0' }; + int regretv; + const char *s = (_user == NULL ? "" : _user); + if ((regretv = regcomp(_reuser, s, REG_EXTENDED | REG_NOSUB))) + { + regerror(regretv, _reuser, errbuf, 1024); + throw std::invalid_argument(errbuf); + } + s = (_role == NULL ? "" : _role); + if ((regretv = regcomp(_rerole, s, REG_EXTENDED | REG_NOSUB))) + { + regerror(regretv, _reuser, errbuf, 1024); + throw std::invalid_argument(errbuf); + } + s = (_type == NULL ? "" : _type); + if ((regretv = regcomp(_retype, s, REG_EXTENDED | REG_NOSUB))) + { + regerror(regretv, _reuser, errbuf, 1024); + throw std::invalid_argument(errbuf); + } + s = (_range == NULL ? "" : _range); + if ((regretv = regcomp(_rerange, s, REG_EXTENDED | REG_NOSUB))) + { + regerror(regretv, _reuser, errbuf, 1024); + throw std::invalid_argument(errbuf); + } + s = (_path == NULL ? "" : _path); + if ((regretv = regcomp(_repath, s, REG_EXTENDED | REG_NOSUB))) + { + regerror(regretv, _reuser, errbuf, 1024); + throw std::invalid_argument(errbuf); + } + s = (_dev == NULL ? "" : _dev); + if ((regretv = regcomp(_redev, s, REG_EXTENDED | REG_NOSUB))) + { + regerror(regretv, _reuser, errbuf, 1024); + throw std::invalid_argument(errbuf); + } + _recompiled = true; +} + +/******************** C functions below ********************/ + +sefs_query_t *sefs_query_create() +{ + return new sefs_query(); +} + +void sefs_query_destroy(sefs_query_t ** query) +{ + if (query != NULL && *query != NULL) + { + delete(*query); + *query = NULL; + } +} + +int sefs_query_set_user(sefs_query_t * query, const char *name) +{ + if (query == NULL) + { + errno = EINVAL; + return -1; + } + try + { + query->user(name); + } + catch(...) + { + return -1; + } + return 0; +} + +int sefs_query_set_role(sefs_query_t * query, const char *name) +{ + if (query == NULL) + { + errno = EINVAL; + return -1; + } + try + { + query->role(name); + } + catch(...) + { + return -1; + } + return 0; +} + +int sefs_query_set_type(sefs_query_t * query, const char *name, bool indirect) +{ + if (query == NULL) + { + errno = EINVAL; + return -1; + } + try + { + query->type(name, indirect); + } + catch(...) + { + return -1; + } + return 0; +} + +int sefs_query_set_range(sefs_query_t * query, const char *range, int match) +{ + if (query == NULL) + { + errno = EINVAL; + return -1; + } + query->range(range, match); + return 0; +} + +int sefs_query_set_object_class(sefs_query_t * query, uint32_t objclass) +{ + if (query == NULL) + { + errno = EINVAL; + return -1; + } + query->objectClass(objclass); + return 0; +} + +int sefs_query_set_object_class_str(sefs_query_t * query, const char *name) +{ + if (query == NULL) + { + errno = EINVAL; + return -1; + } + query->objectClass(name); + return 0; +} + +int sefs_query_set_path(sefs_query_t * query, const char *path) +{ + if (query == NULL) + { + errno = EINVAL; + return -1; + } + try + { + query->path(path); + } + catch(...) + { + return -1; + } + return 0; +} + +int sefs_query_set_inode(sefs_query_t * query, ino64_t inode) +{ + if (query == NULL) + { + errno = EINVAL; + return -1; + } + query->inode(inode); + return 0; +} + +int sefs_query_set_dev(sefs_query_t * query, const char *dev) +{ + if (query == NULL) + { + errno = EINVAL; + return -1; + } + try + { + query->dev(dev); + } + catch(...) + { + return -1; + } + return 0; +} + +int sefs_query_set_regex(sefs_query_t * query, bool regex) +{ + if (query == NULL) + { + errno = EINVAL; + return -1; + } + query->regex(regex); + return 0; +} diff --git a/libsefs/src/sefs_internal.hh b/libsefs/src/sefs_internal.hh new file mode 100644 index 0000000..20a2775 --- /dev/null +++ b/libsefs/src/sefs_internal.hh @@ -0,0 +1,78 @@ +/** + * @file + * Additional declarations for use solely by libsefs. + * + * @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 + */ + +#ifndef SEFS_INTERNAL_HH +#define SEFS_INTERNAL_HH + +#include <apol/bst.h> +#include <sefs/fclist.hh> +#include <regex.h> + +/** + * Given a policy containing types, generate and return a vector of + * names (char *) that match the given criteria. + * + * @param policy Policy associated with types. + * @param str Type name to find. + * @param regex If using regexp comparison, the compiled regular + * expression to use. + * @param regex_flag If true, use the compiled regular expression + * instead of str. + * @param indirect If true, do indirect type matching. + * + * @return Vector of strings. The caller is responsible for calling + * apol_vector_destroy() upon the returned value afterwards. + */ +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); + +/** + * Determines if a string matches a target symbol name. If \a + * regex_flag is true, use the compiled regular expression instead of + * \a str. Otherwise do a straight string comparison between \a str + * and \a target. If \a str is NULL and/or empty then the comparison + * always succeeds regardless of \a regex and \a target. Next, if \a + * target is NULL or empty then comparison fails. + * + * @param target Name of target symbol to compare. + * @param str Source string from which to compare. + * @param regex If using regexp comparison, the compiled regular + * expression to use. + * @param regex_flag If true, use the compiled regular expression + * instead. + * + * @return true if comparison succeeds, false if not. + */ +bool query_str_compare(const char *target, const char *str, const regex_t * regex, const bool regex_flag); + +// rather than having each sefs_entry having its own apol_context_t +// object, build a cache of nodes to save space +struct sefs_context_node +{ + apol_context_t *context; // each node owns its apol context + const char *user, *role, *type, *range; // these are pointers into fclists's BSTs + char *context_str; // each node owns the string +}; + +#endif diff --git a/libsefs/src/util.c b/libsefs/src/util.c new file mode 100644 index 0000000..6c27297 --- /dev/null +++ b/libsefs/src/util.c @@ -0,0 +1,46 @@ +/** + * @file + * + * Implementation of utility functions for libsefs. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-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 <string.h> + +#ifdef LIBSELINUX +#include <selinux/selinux.h> +#endif + +const char *libsefs_get_version(void) +{ + return LIBSEFS_VERSION_STRING; +} + +char *sefs_default_file_contexts_get_path(void) +{ +#ifdef LIBSELINUX + return strdup(selinux_file_context_path()); +#else + return strdup(""); +#endif +} |