summaryrefslogtreecommitdiffstats
path: root/libsefs/src
diff options
context:
space:
mode:
Diffstat (limited to 'libsefs/src')
-rw-r--r--libsefs/src/Makefile.am52
-rw-r--r--libsefs/src/db.cc1304
-rw-r--r--libsefs/src/entry.cc213
-rw-r--r--libsefs/src/fcfile.cc691
-rw-r--r--libsefs/src/fclist.cc766
-rw-r--r--libsefs/src/filesystem.cc733
-rw-r--r--libsefs/src/libsefs.map29
-rw-r--r--libsefs/src/new_ftw.c749
-rw-r--r--libsefs/src/new_ftw.h183
-rw-r--r--libsefs/src/query.cc431
-rw-r--r--libsefs/src/sefs_internal.hh78
-rw-r--r--libsefs/src/util.c46
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(&regex, anchored_path, REG_EXTENDED | REG_NOSUB) != 0)
+ {
+ free(anchored_path);
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+
+ bool compval = query_str_compare(query->_path, anchored_path, &regex, true);
+ free(anchored_path);
+ regfree(&regex);
+ if (compval)
+ {
+ path_matched = true;
+ }
+ }
+ if (!path_matched)
+ {
+ continue;
+ }
+ }
+
+ // if reached this point, then all criteria passed, so
+ // invoke the mapping function
+
+ if ((retval = fn(this, e, data)) < 0)
+ {
+ return retval;
+ }
+ }
+ }
+ catch(...)
+ {
+ apol_vector_destroy(&type_list);
+ apol_mls_range_destroy(&range);
+ throw;
+ }
+ apol_vector_destroy(&type_list);
+ return retval;
+}
+
+bool sefs_fcfile::isMLS() const
+{
+ if (_mls_set)
+ {
+ return _mls;
+ }
+ return false;
+}
+
+int sefs_fcfile::appendFile(const char *file) throw(std::bad_alloc, std::invalid_argument, std::runtime_error)
+{
+ FILE *fc_file = NULL;
+ char *line = NULL, *name_dup = NULL;
+ size_t line_len = 0;
+ size_t last_entry = apol_vector_get_size(_entries);
+ int retval, error = 0;
+
+ regex_t line_regex, context_regex;
+ bool is_line_compiled = false;
+ bool is_context_compiled = false;
+
+ try
+ {
+ if (file == NULL)
+ {
+ errno = EINVAL;
+ SEFS_ERR(this, "%s", strerror(EINVAL));
+ throw std::invalid_argument(strerror(EINVAL));
+ }
+
+ fc_file = fopen(file, "r");
+ if (!fc_file)
+ {
+ SEFS_ERR(this, "Unable to open file %s", file);
+ throw std::runtime_error(strerror(error));
+ }
+
+ if ((name_dup = strdup(file)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+
+ if (regcomp(&line_regex, "^([^[:blank:]]+)[[:blank:]]+(-.[[:blank:]]+)?([^-].+)$", REG_EXTENDED) != 0)
+ {
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+ is_line_compiled = true;
+
+ if (regcomp(&context_regex, "^([^:]+):([^:]+):([^:]+):?(.*)$", REG_EXTENDED) != 0)
+ {
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+ is_context_compiled = true;
+
+ while (!feof(fc_file))
+ {
+ if (getline(&line, &line_len, fc_file) == -1)
+ {
+ if (feof(fc_file))
+ {
+ break;
+ }
+ else
+ {
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+ }
+ parse_line(name_dup, line, &line_regex, &context_regex);
+ }
+
+ if (apol_vector_append(_files, name_dup) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+ name_dup = NULL;
+
+ retval = 0;
+ }
+ catch(...)
+ {
+ error = errno;
+ // discard all entries that were read from this file_contexts
+ size_t i = apol_vector_get_size(_entries);
+ for (; i > last_entry; i--)
+ {
+ sefs_entry *e = static_cast < sefs_entry * >(apol_vector_get_element(_entries, i - 1));
+ fcfile_entry_free(e);
+ apol_vector_remove(_entries, i - 1);
+ }
+ retval = -1;
+ }
+
+ if (fc_file != NULL)
+ {
+ fclose(fc_file);
+ }
+ if (is_line_compiled)
+ {
+ regfree(&line_regex);
+ }
+ if (is_context_compiled)
+ {
+ regfree(&context_regex);
+ }
+ free(name_dup);
+ free(line);
+ errno = error;
+ return retval;
+}
+
+size_t sefs_fcfile::appendFileList(const apol_vector_t * files)throw(std::bad_alloc, std::invalid_argument, std::runtime_error)
+{
+ size_t i;
+ if (files == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ throw new std::invalid_argument(strerror(EINVAL));
+ }
+ for (i = 0; i < apol_vector_get_size(files); i++)
+ {
+ if (appendFile(static_cast < char *>(apol_vector_get_element(files, i))) < 0)
+ {
+ return i;
+ }
+ }
+ return i;
+}
+
+const apol_vector_t *sefs_fcfile::fileList() const
+{
+ return _files;
+}
+
+/******************** private functions below ********************/
+
+void sefs_fcfile::parse_line(const char *origin, const char *line, regex_t * line_regex,
+ regex_t * context_regex) throw(std::bad_alloc, std::runtime_error)
+{
+ int error = 0;
+
+ char *s = strdup(line);
+ char *path;
+
+ if (s == NULL)
+ {
+ error = errno;
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+
+ apol_str_trim(s);
+ if (s[0] == '#' || s[0] == '\0')
+ {
+ free(s);
+ return;
+ }
+
+ try
+ {
+ const size_t nmatch = 5;
+ regmatch_t pmatch[nmatch];
+
+ if (regexec(line_regex, s, nmatch, pmatch, 0) != 0)
+ {
+ error = EIO;
+ SEFS_ERR(this, "fcfile line is not legal:\n%s", s);
+ throw std::runtime_error(strerror(error));
+ }
+
+ assert(pmatch[1].rm_so == 0);
+ s[pmatch[1].rm_eo] = '\0';
+ if ((path = strdup(s)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(error));
+ }
+ if (apol_bst_insert_and_get(path_tree, (void **)&path, NULL) < 0)
+ {
+ free(path);
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(error));
+ }
+
+ uint32_t objclass;
+ if (pmatch[2].rm_so != -1)
+ {
+ switch (s[pmatch[2].rm_so + 1])
+ {
+ case '-':
+ objclass = QPOL_CLASS_FILE;
+ break;
+ case 'd':
+ objclass = QPOL_CLASS_DIR;
+ break;
+ case 'c':
+ objclass = QPOL_CLASS_CHR_FILE;
+ break;
+ case 'b':
+ objclass = QPOL_CLASS_BLK_FILE;
+ break;
+ case 'p':
+ objclass = QPOL_CLASS_FIFO_FILE;
+ break;
+ case 'l':
+ objclass = QPOL_CLASS_LNK_FILE;
+ break;
+ case 's':
+ objclass = QPOL_CLASS_SOCK_FILE;
+ break;
+ default:
+ error = EIO;
+ SEFS_ERR(this, "%s", "Invalid file context object class.");
+ throw std::runtime_error(strerror(error));
+ }
+ }
+ else
+ {
+ // no object class explicitly given
+ objclass = QPOL_CLASS_ALL;
+ }
+
+ assert(pmatch[3].rm_so != -1);
+ char *context_str = s + pmatch[3].rm_so;
+ char *user, *role, *type, *range;
+
+ if (strcmp(context_str, "<<none>>") == 0)
+ {
+ user = role = type = range = "";
+ }
+ else
+ {
+ if (regexec(context_regex, context_str, nmatch, pmatch, 0) != 0)
+ {
+ error = EIO;
+ SEFS_ERR(this, "fcfile context is not legal:\n%s", context_str);
+ throw std::runtime_error(strerror(error));
+ }
+
+ assert(pmatch[1].rm_so == 0);
+ context_str[pmatch[1].rm_eo] = '\0';
+ user = context_str;
+
+ assert(pmatch[2].rm_so != -1);
+ context_str[pmatch[2].rm_eo] = '\0';
+ role = context_str + pmatch[2].rm_so;
+
+ assert(pmatch[3].rm_so != -1);
+ context_str[pmatch[3].rm_eo] = '\0';
+ type = context_str + pmatch[3].rm_so;
+
+ range = NULL;
+ if (pmatch[4].rm_so != -1)
+ {
+ range = context_str + pmatch[4].rm_so;
+ }
+ }
+ if (range != NULL & range[0] != '\0')
+ {
+ if (_mls_set && !_mls)
+ {
+ error = EIO;
+ SEFS_ERR(this, "fcfile context is MLS, but fcfile is not:\n%s", context_str);
+ throw std::runtime_error(strerror(error));
+ }
+ _mls = true;
+ _mls_set = true;
+ }
+ else
+ {
+ if (_mls_set && !_mls && strcmp(context_str, "<<none>>") != 0)
+ {
+ error = EIO;
+ SEFS_ERR(this, "fcfile context is not MLS, but fcfile is:\n%s", context_str);
+ throw std::runtime_error(strerror(error));
+ }
+ _mls = true;
+ _mls_set = false;
+ }
+ struct sefs_context_node *context = getContext(user, role, type, range);
+ sefs_entry *entry = new sefs_entry(this, context, objclass, path, origin);
+
+ if (apol_vector_append(_entries, static_cast < void *>(entry)) < 0)
+ {
+ error = errno;
+ delete entry;
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+ }
+
+ catch(...)
+ {
+ free(s);
+ errno = error;
+ throw;
+ }
+
+ free(s);
+}
+
+/******************** C functions below ********************/
+
+sefs_fclist_t *sefs_fcfile_create(sefs_callback_fn_t msg_callback, void *varg)
+{
+ sefs_fclist *fclist;
+ try
+ {
+ fclist = new sefs_fcfile(msg_callback, varg);
+ }
+ catch(...)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return fclist;
+}
+
+sefs_fclist_t *sefs_fcfile_create_from_file(const char *file, sefs_callback_fn_t msg_callback, void *varg)
+{
+ sefs_fclist *fclist;
+ try
+ {
+ fclist = new sefs_fcfile(file, msg_callback, varg);
+ }
+ catch(...)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return fclist;
+}
+
+sefs_fclist_t *sefs_fcfile_create_from_file_list(const apol_vector_t * files, sefs_callback_fn_t msg_callback, void *varg)
+{
+ sefs_fclist *fclist;
+ try
+ {
+ fclist = new sefs_fcfile(files, msg_callback, varg);
+ }
+ catch(...)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return fclist;
+}
+
+int sefs_fcfile_append_file(sefs_fcfile_t * fcfile, const char *file)
+{
+ if (fcfile == NULL)
+ {
+ SEFS_ERR(fcfile, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ try
+ {
+ fcfile->appendFile(file);
+ }
+ catch(...)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+size_t sefs_fcfile_append_file_list(sefs_fcfile_t * fcfile, const apol_vector_t * files)
+{
+ if (fcfile == NULL)
+ {
+ SEFS_ERR(fcfile, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return 0;
+ }
+ return fcfile->appendFileList(files);
+}
+
+const apol_vector_t *sefs_fcfile_get_file_list(const sefs_fcfile_t * fcfile)
+{
+ if (fcfile == NULL)
+ {
+ SEFS_ERR(fcfile, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ return fcfile->fileList();
+}
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
+}