summaryrefslogtreecommitdiffstats
path: root/libsefs
diff options
context:
space:
mode:
Diffstat (limited to 'libsefs')
-rw-r--r--libsefs/Makefile.am8
-rw-r--r--libsefs/include/Makefile.am1
-rw-r--r--libsefs/include/sefs/Makefile.am10
-rw-r--r--libsefs/include/sefs/db.hh213
-rw-r--r--libsefs/include/sefs/entry.hh222
-rw-r--r--libsefs/include/sefs/fcfile.hh261
-rw-r--r--libsefs/include/sefs/fclist.hh323
-rw-r--r--libsefs/include/sefs/filesystem.hh181
-rw-r--r--libsefs/include/sefs/query.hh329
-rw-r--r--libsefs/include/sefs/util.h56
-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
-rw-r--r--libsefs/swig/Makefile.am15
-rw-r--r--libsefs/swig/java/MANIFEST.MF.in14
-rw-r--r--libsefs/swig/java/Makefile.am85
-rw-r--r--libsefs/swig/python/Makefile.am39
-rw-r--r--libsefs/swig/sefs.i162
-rw-r--r--libsefs/swig/tcl/Makefile.am37
-rw-r--r--libsefs/tests/Makefile.am17
-rw-r--r--libsefs/tests/attic/fuse_non_mls.c195
-rwxr-xr-xlibsefs/tests/attic/launch-libsefs-tests.sh12
-rw-r--r--libsefs/tests/fcfile-tests.cc333
-rw-r--r--libsefs/tests/fcfile-tests.hh35
-rw-r--r--libsefs/tests/file_contexts.broken3
-rw-r--r--libsefs/tests/file_contexts.confed24
-rw-r--r--libsefs/tests/file_contexts.union16
-rw-r--r--libsefs/tests/libsefs-tests.cc52
37 files changed, 7918 insertions, 0 deletions
diff --git a/libsefs/Makefile.am b/libsefs/Makefile.am
new file mode 100644
index 0000000..5a0aa81
--- /dev/null
+++ b/libsefs/Makefile.am
@@ -0,0 +1,8 @@
+if DO_SWIGIFY
+ MAYBE_SWIG = swig
+endif
+
+SUBDIRS = src include tests $(MAYBE_SWIG)
+
+libsefs.a libsefs.so:
+ $(MAKE) -C src $@
diff --git a/libsefs/include/Makefile.am b/libsefs/include/Makefile.am
new file mode 100644
index 0000000..6f6c411
--- /dev/null
+++ b/libsefs/include/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = sefs
diff --git a/libsefs/include/sefs/Makefile.am b/libsefs/include/sefs/Makefile.am
new file mode 100644
index 0000000..f38dc4b
--- /dev/null
+++ b/libsefs/include/sefs/Makefile.am
@@ -0,0 +1,10 @@
+sefsdir = $(includedir)/sefs
+
+sefs_HEADERS = \
+ db.hh \
+ entry.hh \
+ fcfile.hh \
+ fclist.hh \
+ filesystem.hh \
+ query.hh \
+ util.h
diff --git a/libsefs/include/sefs/db.hh b/libsefs/include/sefs/db.hh
new file mode 100644
index 0000000..2355547
--- /dev/null
+++ b/libsefs/include/sefs/db.hh
@@ -0,0 +1,213 @@
+/**
+ * @file
+ * Defines the public interface for the database fc list object.
+ *
+ * @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_DB_H
+#define SEFS_DB_H
+
+#include <sefs/fclist.hh>
+#include <sefs/filesystem.hh>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include <time.h>
+#include <apol/bst.h>
+#include <apol/vector.h>
+
+#ifdef __cplusplus
+}
+
+#include <stdexcept>
+
+/**
+ * This class represents a database that maps files to their SELinux
+ * file contexts.
+ */
+class sefs_db:public sefs_fclist
+{
+#ifndef SWIG_FRIENDS
+ // private functions -- do not call these directly from
+ // outside of the library
+ friend int db_create_from_filesystem(sefs_fclist * fclist __attribute__ ((unused)), const sefs_entry * entry, void *arg);
+ friend struct sefs_context_node *db_get_context(sefs_db *, const char *, const char *, const char *,
+ const char *) throw(std::bad_alloc);
+ friend sefs_entry *db_get_entry(sefs_db *, const struct sefs_context_node *, uint32_t, const char *, ino64_t,
+ const char *) throw(std::bad_alloc);
+#endif
+
+ public:
+
+ /**
+ * Allocate and return a new sefs database initialized with
+ * entries from the filesystem \a fs.
+ * @param fs Sefs filesystem from which to create the database.
+ * @param msg_callback Callback to invoke as errors/warnings are
+ * generated. If NULL, write messages to standard error.
+ * @param varg Value to be passed as the first parameter to the
+ * callback function.
+ * @exception std::invalid_argument Filesystem does not exist.
+ * @exception std::runtime_error Error while reading the
+ * database.
+ */
+ sefs_db(sefs_filesystem * fs, sefs_callback_fn_t msg_callback, void *varg) throw(std::invalid_argument,
+ std::runtime_error);
+
+ /**
+ * Allocate and return a new sefs database, loading the
+ * entries from an existing database stored at \a path.
+ * @param filename Name of a sefs database from which to load.
+ * @param msg_callback Callback to invoke as errors/warnings
+ * are generated. If NULL, write messages to standard error.
+ * @param varg Value to be passed as the first parameter to
+ * the callback function.
+ * @exception std::invalid_argument Database does not exist.
+ * @exception std::runtime_error Error while reading the
+ * database.
+ */
+ sefs_db(const char *filename, sefs_callback_fn_t msg_callback, void *varg) throw(std::invalid_argument,
+ std::runtime_error);
+
+ ~sefs_db();
+
+ /**
+ * Perform a sefs query on this database object, and then
+ * invoke a callback upon each matching entry. Entries will
+ * be returned in alphabetical order by path.
+ * @param query Query object containing search parameters. If
+ * NULL, invoke the callback on all entries.
+ * @param fn Function to invoke upon matching entries. This
+ * function will be called with three parameters: a pointer to
+ * this database, pointer to a matching entry, and an
+ * arbitrary data pointer. It should return a non-negative
+ * value upon success, negative value upon error and to abort
+ * the mapping.
+ * @param data Arbitrary pointer to be passed into \fn as a
+ * third parameter.
+ * @return Last value returned by fn() (i.e., >= on success, <
+ * 0 on failure). If the database has no entries then
+ * return 0.
+ * @exception std::runtime_error Error while reading contexts
+ * from the database.
+ * @exception std::invalid_argument One or more query arguments
+ * is invalid.
+ */
+ int runQueryMap(sefs_query * query, sefs_fclist_map_fn_t fn, void *data) throw(std::runtime_error, std::invalid_argument);
+
+ /**
+ * Determine if the contexts stored in this database contain
+ * MLS fields.
+ * @return \a true if MLS fields are present, \a false if not
+ * or undeterminable.
+ */
+ bool isMLS() const;
+
+ /**
+ * Write a database to disk, overwriting any existing file.
+ * The database may then be read by calling the appropriate
+ * constructor.
+ * @param filename Name of file to which write.
+ * @exception std::invalid_argument No filename given.
+ * @exception std::runtime_error Error while writing the
+ * database.
+ */
+ void save(const char *filename) throw(std::invalid_argument, std::runtime_error);
+
+ /**
+ * Get the creation time of a sefs database.
+ * @return Creation time of the database, or 0 on error.
+ */
+ time_t getCTime() const;
+
+ /**
+ * Determine if the given file is a valid sefs_db. This
+ * does not thoroughly load the file, rather just the header
+ * of the file.
+ * @param filename Name of file to check.
+ * @return True if the file appears to be a database, false if not.
+ */
+ static bool isDB(const char *filename);
+
+ private:
+ /**
+ * Upgrade an existing version 1 database to version 2.
+ */
+ void upgradeToDB2() throw(std::runtime_error);
+
+ const struct sefs_context_node *getContextNode(const sefs_entry * entry);
+ sefs_entry *getEntry(const struct sefs_context_node *context, uint32_t objectClass, const char *path, ino64_t inode,
+ const char *dev) throw(std::bad_alloc);
+ struct sqlite3 *_db;
+ time_t _ctime;
+};
+
+extern "C"
+{
+#endif
+
+//we do not want to wrap two copies of everything so have SWIG ignore
+//the compatibility section.
+#ifndef SWIG
+
+ typedef struct sefs_db sefs_db_t;
+ typedef struct sefs_filesystem sefs_filesystem_t;
+
+/**
+ * Allocate and return a new sefs database from the filesystem \a fs.
+ * @see sefs_db::sefs_db(const sefs_filesystem &fs, sefs_callback_fn_t msg_callback, void *varg)
+ */
+ extern sefs_fclist_t *sefs_db_create_from_filesystem(sefs_filesystem_t * fs, sefs_callback_fn_t msg_callback, void *varg);
+
+/**
+ * Allocate and return a new sefs database, loading the entries from
+ * the saved database \a path.
+ * @see sefs_db::sefs_db(const char *filename, sefs_callback_fn_t msg_callback, void *varg)
+ */
+ extern sefs_fclist_t *sefs_db_create_from_file(const char *path, sefs_callback_fn_t msg_callback, void *varg);
+
+/**
+ * Write a database to disk, overwriting any existing file.
+ * @see sefs_db::save()
+ */
+ extern int sefs_db_save(sefs_db_t * db, const char *filename);
+
+/**
+ * Get the creation time of a sefs database.
+ * @see sefs_db::getCTime()
+ */
+ extern time_t sefs_db_get_ctime(sefs_db_t * db);
+
+/**
+ * Determine if the given file is a valid sefs_db.
+ * @see sefs_db::isDB()
+ */
+ extern bool sefs_db_is_db(const char *filename);
+
+#endif /* SWIG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SEFS_DB_H */
diff --git a/libsefs/include/sefs/entry.hh b/libsefs/include/sefs/entry.hh
new file mode 100644
index 0000000..34fae73
--- /dev/null
+++ b/libsefs/include/sefs/entry.hh
@@ -0,0 +1,222 @@
+/**
+ * @file
+ * Defines the public interface for file context entries.
+ *
+ * @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_ENTRY_H
+#define SEFS_ENTRY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <sys/types.h>
+#include <apol/context-query.h>
+#include <apol/vector.h>
+
+#ifdef __cplusplus
+}
+
+#include <stdexcept>
+
+class sefs_fclist;
+struct sefs_context_node;
+
+/**
+ * This class represents an individual entry within a list an fcfile
+ * object. Note that the entry's contents (even upon a
+ * copy-constructed version of the entry) are always tied to its
+ * fclist, so do not access entries whose fclist has been destroyed.
+ */
+class sefs_entry
+{
+ friend class sefs_db;
+ friend class sefs_fcfile;
+ friend class sefs_filesystem;
+
+ public:
+
+ /**
+ * Perform a deep copy of an entry object.
+ */
+ sefs_entry(const sefs_entry * e);
+
+ ~sefs_entry();
+
+ /**
+ * Get the context from a sefs entry. If the entry has no
+ * context (such as being marked <tt>&lt;&lt;none&gt;&gt;</tt>
+ * in a file_contexts file) then apol_context_get_user() and
+ * others will return an empty string.
+ * @return A pointer to the context, or NULL on error. The
+ * caller should not modify or destroy the returned context.
+ */
+ const apol_context_t *context() const;
+
+ /**
+ * Get the inode number associated with a sefs entry.
+ * @return Inode number associated with the entry or 0 on
+ * error. Entries originating from a file_contexts object
+ * will have no inode and thus return 0.
+ */
+ ino64_t inode() const;
+
+ /**
+ * Get the device name associated with a sefs entry. For
+ * example, if /dev/sda5 is mounted as /home, the device name
+ * for entry "/home/gburdell" will be "/dev/sda5".
+ * @return Device number associated with the entry or NULL on
+ * error. Do not free() this value. Entries originating from
+ * a file_contexts object will have no device name and thus
+ * return NULL.
+ */
+ const char *dev() const;
+
+ /**
+ * Get the object class associated with a sefs entry. The
+ * returned value will be one of one of QPOL_CLASS_ALL,
+ * QPOL_CLASS_FILE, etc., as defined in
+ * <qpol/genfscon_query.h>. If this returns QPOL_CLASS_ALL
+ * then the entry is associated with all object classes.
+ * @return Entry's object class. Upon error return
+ * QPOL_CLASS_ALL.
+ * @see apol_objclass_to_str() to convert the value to a
+ * string.
+ */
+ uint32_t objectClass() const;
+
+ /**
+ * Get the paths associated with a sefs entry.
+ * @return Path for the entry.If the entry came from a
+ * file_contexts object the paths will be a regular expression
+ * rather than literal paths. Do not free() this pointer.
+ */
+ const char *path() const;
+
+ /**
+ * Get the file from which a sefs entry originated.
+ * This function is only meaningful when entries are returned
+ * from a query on a modular file context file.
+ * @return The path of the file (policy package or source
+ * file) providing the entry or NULL if the entry is not from
+ * a module. Do not free() this pointer.
+ */
+ const char *origin() const;
+
+ /**
+ * Return a string representation of this entry. The string
+ * is suitable for printing to the screen or to a
+ * file_contexts file.
+ * @return An allocated string representation. The caller is
+ * responsibily for free()ing the string afterwards.
+ * @exception std::bad_alloc Out of memory.
+ */
+ char *toString() const throw(std::bad_alloc);
+
+ private:
+ /**
+ * Create a blank entry. The entity creating this entry is
+ * responsible for setting additional values as needed.
+ * @param fclist List that will contain this entry. This
+ * constructor will not add itself to the fclist.
+ * @param new_context Context node containing the SELinux
+ * context.
+ * @param new_objectClass Object class for the entry.
+ * @param new_path Path to this entry. The entry will share
+ * this pointer.
+ * @param new_origin Name of file_contexts file from which
+ * this entry originated. The entry will share this pointer.
+ * @exception std::bad_alloc Out of memory.
+ */
+ 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 = NULL);
+
+ // note that entry does not own any of these pointers; they
+ // are shallow copies into the fclist's BST
+ class sefs_fclist *_fclist;
+ const struct sefs_context_node *_context;
+ ino64_t _inode;
+ const char *_dev;
+ uint32_t _objectClass;
+ const char *_path, *_origin;
+};
+
+extern "C"
+{
+#endif
+
+//we do not want to wrap two copies of everything so have SWIG ignore
+//the compatibility section.
+#ifndef SWIG
+
+ typedef struct sefs_entry sefs_entry_t;
+
+/**
+ * Get the context from a sefs entry.
+ * @see sefs_entry::context()
+ */
+ extern const apol_context_t *sefs_entry_get_context(const sefs_entry_t * ent);
+
+/**
+ * Get the inode number associated with a sefs entry.
+ * @see sefs_entry::inode()
+ */
+ extern ino64_t sefs_entry_get_inode(const sefs_entry_t * ent);
+
+/**
+ * Get the device number associated with a sefs entry.
+ * @see sefs_entry::dev()
+ */
+ extern const char *sefs_entry_get_dev(const sefs_entry_t * ent);
+
+/**
+ * Get the object class associated with a sefs entry.
+ * @see sefs_entry::objectClass()
+ */
+ extern uint32_t sefs_entry_get_object_class(const sefs_entry_t * ent);
+
+/**
+ * Get the path associated with a sefs entry.
+ * @see sefs_entry::path()
+ */
+ extern const char *sefs_entry_get_path(const sefs_entry_t * ent);
+
+/**
+ * Get the file from which a sefs entry originated.
+ * @see sefs_entry::origin()
+ */
+ extern const char *sefs_entry_get_origin(const sefs_entry_t * ent);
+
+/**
+ * Return a string representation of this entry.
+ * @see sefs_entry::toString()
+ */
+ extern char *sefs_entry_to_string(const sefs_entry_t * ent);
+
+#endif /* SWIG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SEFS_ENTRY_H */
diff --git a/libsefs/include/sefs/fcfile.hh b/libsefs/include/sefs/fcfile.hh
new file mode 100644
index 0000000..280a957
--- /dev/null
+++ b/libsefs/include/sefs/fcfile.hh
@@ -0,0 +1,261 @@
+/**
+ * @file
+ * Defines the public interface for the file_context set fc list object.
+ *
+ * @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_FCFILE_H
+#define SEFS_FCFILE_H
+
+#include <sefs/fclist.hh>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdlib.h>
+
+#include <apol/vector.h>
+
+#ifdef __cplusplus
+}
+
+#include <stdexcept>
+
+/**
+ * This class represents file contexts entry as read from a file,
+ * typically name file_contexts.
+ */
+class sefs_fcfile:public sefs_fclist
+{
+ public:
+
+ /**
+ * Allocate and return a new (and empty) sefs file_context set
+ * structure.
+ * @param msg_callback Callback to invoke as errors/warnings
+ * are generated. If NULL, write messages to standard error.
+ * @param varg Value to be passed as the first parameter to
+ * the callback function.
+ * @exception std::bad_alloc if out of memory
+ */
+ sefs_fcfile(sefs_callback_fn_t msg_callback, void *varg) throw(std::bad_alloc);
+
+ /**
+ * Allocate and return a new sefs file_context set structure
+ * from a single file_contexts file.
+ * @param file File contexts file to read.
+ * @param msg_callback Callback to invoke as errors/warnings
+ * are generated. If NULL, write messages to standard error.
+ * @param varg Value to be passed as the first parameter to
+ * the callback function.
+ * @exception std::bad_alloc if out of memory
+ * @exception std::invalid_argument if the vector is NULL
+ * @exception std::runtime_error if the give file could not be
+ * read or is the wrong format
+ */
+ sefs_fcfile(const char *file, sefs_callback_fn_t msg_callback, void *varg) throw(std::bad_alloc, std::invalid_argument,
+ std::runtime_error);
+
+ /**
+ * Allocate and return a new sefs file_context set structure
+ * from a list of file_context files.
+ * @param files Vector of file contexts filenames (of type
+ * char *) to read.
+ * @param msg_callback Callback to invoke as errors/warnings
+ * are generated. If NULL, write messages to standard error.
+ * @param varg Value to be passed as the first parameter to
+ * the callback function.
+ * @exception std::bad_alloc if out of memory
+ * @exception std::invalid_argument if the vector is NULL
+ * @exception std::runtime_error if a given file could not
+ * be read or is the wrong format
+ */
+ 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_fcfile();
+
+ /**
+ * Perform a sefs query on this fcfile object, and then invoke
+ * a callback upon each matching entry. Mapping occurs in the
+ * order of entries as given by the file_contexts, and in the
+ * order that file_contexts were appended (via appendFile())
+ * to this object.
+ * @param query Query object containing search parameters. If
+ * NULL, invoke the callback on all entries.
+ * @param fn Function to invoke upon matching entries. This
+ * function will be called with three parameters: a pointer to
+ * this fclist, pointer to a matching entry, and an arbitrary
+ * data pointer. It should return a non-negative value upon
+ * success, negative value upon error and to abort the
+ * mapping.
+ * @param data Arbitrary pointer to be passed into \fn as a
+ * third parameter.
+ * @return Last value returned by fn() (i.e., >= on success, <
+ * 0 on failure). If the fcfile has no entries then return 0.
+ * @exception std::runtime_error Error while reading contexts
+ * from the fclist.
+ * @exception std::invalid_argument One or more query arguments
+ * is invalid.
+ */
+ int runQueryMap(sefs_query * query, sefs_fclist_map_fn_t fn, void *data) throw(std::runtime_error, std::invalid_argument);
+
+ /**
+ * Determine if the contexts in the fcfile contain MLS fields.
+ * @return \a true if MLS fields are present, \a false if not
+ * or undeterminable.
+ */
+ bool isMLS() const;
+
+ /**
+ * Append a file_contexts file to a sefs file contexts file
+ * set. If the fcfile already has a non-MLS file, subsequent
+ * appends must also be to non-MLS files. Likewise, if the
+ * fcfile already has an MLS file the file to be append must
+ * also be MLS.
+ * @param file File containing entries to append.
+ * @return 0 on success or < 0 on failure; if the call fails,
+ * the fcfile will be unchanged.
+ * @exception std::bad_alloc if out of memory
+ * @exception std::invalid_argument if the file name is NULL
+ * @exception std::runtime_error if a given file could not
+ * be read or is the wrong format
+ */
+ int appendFile(const char *file) throw(std::bad_alloc, std::invalid_argument, std::runtime_error);
+
+ /**
+ * Append a list of file_context files to a sefs file contexts
+ * file set. If the fcfile already has a non-MLS file,
+ * subsequent appends must also be to non-MLS files.
+ * Likewise, if the fcfile already has an MLS file the file to
+ * be append must also be MLS.
+ * @param files Vector of filenames (type char *) to append;
+ * these files will be appended in the order they appear in
+ * the vector.
+ * @return The number of files successfully appended. If the
+ * value returned is less than the size of the vector, then
+ * file at index (returned value) failed. If append fails for
+ * any file, the operation stops at that file; it is safe to
+ * attempt to append the files remaining after the
+ * unsuccessful file.
+ * @exception std::bad_alloc if out of memory
+ * @exception std::invalid_argument if the vector is NULL
+ * @exception std::runtime_error if a given file could not
+ * be read or is the wrong format
+ */
+ size_t appendFileList(const apol_vector_t * files) throw(std::bad_alloc, std::invalid_argument, std::runtime_error);
+
+ /**
+ * Get a list of all files contributing to the entries in a
+ * sefs file_contexts set.
+ * @return Vector of file paths (char *) of all files
+ * contributing to the set; the caller should not destroy or
+ * otherwise modify the returned vector.
+ */
+ const apol_vector_t *fileList() const;
+
+ private:
+
+ /**
+ * Parse a single line from a file_contexts file (or from any
+ * other source of file contexts information), and then add
+ * the resulting sefs_entry into the vector of entries.
+ * @param origin File from which this line originated.
+ * @param line File contexts line to parse.
+ * @param line_regex Compiled regular expression pattern for
+ * an entire line.
+ * @param context_regex Compiled regular expression pattern
+ * for the SELinux portion of a line.
+ * @exception std::bad_alloc if out of memory
+ * @exception std::runtime_error if the give file could not be
+ * read or is the wrong format
+ */
+ void parse_line(const char *origin, const char *line, regex_t * line_regex, regex_t * context_regex) throw(std::bad_alloc,
+ std::
+ runtime_error);
+
+ apol_vector_t *_files, *_entries;
+ bool _mls, _mls_set;
+};
+
+extern "C"
+{
+#endif
+
+//we do not want to wrap two copies of everything so have SWIG ignore
+//the compatibility section.
+#ifndef SWIG
+
+ typedef struct sefs_fcfile sefs_fcfile_t;
+
+/**
+ * Allocate and return a new sefs file_context set structure.
+ * @see sefs_fcfile::sefs_fcfile(sefs_callback_fn_t msg_callback, void *varg)
+ */
+ extern sefs_fclist_t *sefs_fcfile_create(sefs_callback_fn_t msg_callback, void *varg);
+
+/**
+ * Allocate and return a new sefs file_context set structure from a
+ * single file_contexts file.
+ * @see sefs_fcfile::sefs_fcfile(const char *file, sefs_callback_fn_t msg_callback, void *varg)
+ */
+ extern sefs_fclist_t *sefs_fcfile_create_from_file(const char *file, sefs_callback_fn_t msg_callback, void *varg);
+
+/**
+ * Allocate and return a new sefs file_context set structure from a
+ * list of file_context files.
+ * @see sefs_fcfile::sefs_fcfile(const apol_vector_t * files, sefs_callback_fn_t msg_callback, void *varg)
+ */
+ extern sefs_fclist_t *sefs_fcfile_create_from_file_list(const apol_vector_t * files, sefs_callback_fn_t msg_callback,
+ void *varg);
+
+/**
+ * Append a file_contexts file to a sefs file contexts file set.
+ * @return 0 on success or < 0 on failure; if the call fails, the
+ * fcfile will be unchanged.
+ * @see sefs_fcfile::appendFile()
+ */
+ extern int sefs_fcfile_append_file(sefs_fcfile_t * fcfile, const char *file);
+
+/**
+ * Append a list of file_context files to a sefs file contexts file
+ * set.
+ * @see sefs_fcfile::appendFileList()
+ */
+ extern size_t sefs_fcfile_append_file_list(sefs_fcfile_t * fcfile, const apol_vector_t * files);
+
+/**
+ * Get a list of all files contributing to the entries in a sefs
+ * file_contexts set.
+ * @see sefs_fcfile::fileList()
+ */
+ extern const apol_vector_t *sefs_fcfile_get_file_list(const sefs_fcfile_t * fcfile);
+
+#endif /* SWIG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SEFS_FCFILE_H */
diff --git a/libsefs/include/sefs/fclist.hh b/libsefs/include/sefs/fclist.hh
new file mode 100644
index 0000000..63eeeba
--- /dev/null
+++ b/libsefs/include/sefs/fclist.hh
@@ -0,0 +1,323 @@
+/**
+ * @file
+ * Defines the public interface for the file context list abstract
+ * object. A user must call a constructor for one of sefs_fcfile_t,
+ * sefs_db_t, or sefs_filesystem_t to create a sefs_fclist_t object.
+ *
+ * @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_FCLIST_H
+#define SEFS_FCLIST_H
+
+#include <sefs/entry.hh>
+#include <sefs/query.hh>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <selinux/selinux.h>
+#include <stdarg.h>
+
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+#include <apol/policy.h>
+
+#define SEFS_MSG_ERR 1 /*!< Message describes a fatal error. */
+#define SEFS_MSG_WARN 2 /*!< Message is issued as a warning but does not represent a fatal error. */
+#define SEFS_MSG_INFO 3 /*!< Message is issued for inormational reasons and does not represent an atypical state. */
+
+ struct sefs_fclist;
+
+ typedef void (*sefs_callback_fn_t) (void *varg, const struct sefs_fclist * fclist, int level, const char *fmt,
+ va_list argp);
+
+/**
+ * Possible types of fclist for use with sefs_fclist_get_data().
+ */
+ typedef enum sefs_fclist_type
+ {
+ SEFS_FCLIST_TYPE_NONE = 0, /*!< Not an actual type, used for error conditions */
+ SEFS_FCLIST_TYPE_FILESYSTEM, /*!< get_data returns sefs_filesystem_t, a representation of a file system */
+ SEFS_FCLIST_TYPE_FCFILE, /*!< get_data returns sefs_fcfile_t, a representation of a collection of file_context files */
+ SEFS_FCLIST_TYPE_DB /*!< get_data returns sefs_db_t, a representation of a database of file system contexts */
+ } sefs_fclist_type_e;
+
+/**
+ * Invoke a sefs_fclist_t's callback for an error, passing it a format
+ * string and arguments.
+ */
+#define SEFS_ERR(fclist, format, ...) sefs_fclist_handleMsg(fclist, SEFS_MSG_ERR, format, __VA_ARGS__)
+
+/**
+ * Invoke a sefs_fclist_t's callback for a warning, passing it a
+ * format string and arguments.
+ */
+#define SEFS_WARN(fclist, format, ...) sefs_fclist_handleMsg(fclist, SEFS_MSG_WARN, format, __VA_ARGS__)
+
+/**
+ * Invoke a sefs_fclist's callback for an informational message,
+ * passing it a format string and arguments.
+ */
+#define SEFS_INFO(fclist, format, ...) sefs_fclist_handleMsg(fclist, SEFS_MSG_INFO, format, __VA_ARGS__)
+
+ extern void sefs_fclist_handleMsg(const struct sefs_fclist *fclist, int level, const char *fmt, ...);
+
+ __attribute__ ((format(printf, 3, 4))) void sefs_fclist_handleMsg(const struct sefs_fclist *fclist, int level,
+ const char *fmt, ...);
+
+#ifdef __cplusplus
+}
+
+#include <stdexcept>
+
+struct apol_bst;
+struct context_node;
+
+#define SEFS_MAP_FUNC_DEFINED
+
+/**
+ * Function invoked upon each matching file context entry during a query.
+ */
+typedef int (*sefs_fclist_map_fn_t) (sefs_fclist *, const sefs_entry *, void *);
+/**
+ * An abstract class the represents a list of file contexts. Contexts
+ * may be read from a filesystem, inferred from a file_contexts file,
+ * or read from a database.
+ */
+class sefs_fclist
+{
+#ifndef SWIG_FRIENDS
+ friend void sefs_fclist_handleMsg(const sefs_fclist * fclist, int level, const char *fmt, ...);
+ friend class sefs_entry;
+#endif
+
+ public:
+ virtual ~sefs_fclist();
+
+ /**
+ * Perform a sefs query on the given file context list object,
+ * and then invoke a callback upon each matching entry.
+ * Mapping occurs in the order of entries as specified by the
+ * file context list.
+ * @param query Query object containing search parameters. If
+ * NULL, invoke the callback on all entries.
+ * @param fn Function to invoke upon matching entries. This
+ * function will be called with three parameters: a pointer to
+ * this fclist, pointer to a matching entry, and an arbitrary
+ * data pointer. It should return a non-negative value upon
+ * success, negative value upon error and to abort the
+ * mapping. Be aware that the entry may go out of scope upon
+ * conclusion of runQueryMap(), so \a fn will need to clone
+ * the entry if it needs it later.
+ * <p>
+ * <b>This function must not throw any exceptions.</b> Doing
+ * so will most likely corrupt fclist's internal state.
+ * Instead, return a negative value to abort processing.
+ * @param data Arbitrary pointer to be passed into \a fn as a
+ * third parameter.
+ * @return Last value returned by fn() (i.e., >= on success, <
+ * 0 on failure). If the fclist has no entries then return 0.
+ * @exception std::runtime_error Error while reading contexts
+ * from the fclist.
+ * @exception std::invalid_argument One or more query arguments
+ * is invalid.
+ */
+ virtual int runQueryMap(sefs_query * query, sefs_fclist_map_fn_t fn, void *data) throw(std::runtime_error,
+ std::invalid_argument) = 0;
+
+ /**
+ * Perform a sefs query on the given file context list object
+ * and return a list of matching entries.
+ * @param query Query object containing search parameters. If
+ * NULL, return all contexts.
+ * @return A newly allocated unsorted vector (of class
+ * sefs_entry *) containing all entries matching the query.
+ * Do not modify the returned entries. Note that the vector
+ * may be empty. The caller is responsible for calling
+ * apol_vector_destroy() on the returned vector.
+ * @exception std::bad_alloc Out of memory.
+ * @exception std::runtime_error Error while reading contexts
+ * from the fclist.
+ * @exception std::invalid_argument One or more query arguments
+ * is invalid.
+ */
+ apol_vector_t *runQuery(sefs_query * query) throw(std::bad_alloc, std::runtime_error, std::invalid_argument);
+
+ /**
+ * Determine if the contexts in the fclist contain MLS fields.
+ * @return \a true if MLS fields are present, \a false if not
+ * or undeterminable.
+ */
+ virtual bool isMLS() const = 0;
+
+ /**
+ * Associate a policy with the fclist. This is needed to
+ * resolve attributes and MLS ranges in queries. If a policy
+ * is already associated, then calling this function removes
+ * that previous association.
+ * @param policy Policy to associate with \a fclist. If NULL,
+ * remove any policy association. While \a policy is
+ * associated with \a fclist the caller should not destroy \a
+ * policy.
+ * @see sefs_query_set_type()
+ * @see sefs_query_set_range()
+ */
+ void associatePolicy(apol_policy_t * new_policy);
+
+ /**
+ * Return the policy currently associated with this fclist.
+ * Do not destroy the policy without first unassociating it
+ * (via call to sefs_fclist::associatePolicy(NULL)).
+ * @return Currently associated policy, or NULL if none is
+ * set.
+ */
+ apol_policy_t *associatePolicy() const;
+
+ /**
+ * Get the type of fclist object represented by \a fclist.
+ * @return The type of fclist object or SEFS_FCLIST_TYPE_NONE
+ * on error.
+ */
+ sefs_fclist_type_e fclist_type() const;
+
+ protected:
+ sefs_fclist(sefs_fclist_type_e type, sefs_callback_fn_t callback, void *varg) throw(std::bad_alloc);
+
+ /**
+ * Given the parts of a context, return a context node (which
+ * would contain an apol_context_t). If the context already
+ * exists, then a pointer to the existing one is returned.
+ *
+ * @param user User component of the context. The string will
+ * be duplicated.
+ * @param role Role component of the context. The string will
+ * be duplicated.
+ * @param type Type component of the context. The string will
+ * be duplicated.
+ * @param range Range component of the context. The string
+ * will be duplicated, or NULL if no range exists.
+ *
+ * @return A context node. Do not free() it.
+ */
+ struct sefs_context_node *getContext(const char *user, const char *role, const char *type,
+ const char *range) throw(std::bad_alloc);
+
+ /**
+ * Given a SELinux security context, return a context node
+ * (which would contain an apol_context_t). If the context
+ * already exists, then a pointer to the existing one is
+ * returned.
+ *
+ * @param scon Security context from which to obtain a node.
+ *
+ * @return A context node. Do not free() it.
+ */
+ struct sefs_context_node *getContext(const security_context_t scon) throw(std::bad_alloc);
+
+ apol_policy_t *policy;
+ struct apol_bst *user_tree, *role_tree, *type_tree, *range_tree, *path_tree;
+ struct apol_bst *dev_tree;
+ struct apol_bst *context_tree;
+
+ private:
+
+ /**
+ * Write a message to the callback stored within a fclist
+ * error handler. If the msg_callback field is empty, then
+ * the default message callback will be used.
+ * @param level Severity of message, one of SEFS_MSG_*.
+ * @param fmt Format string to print, using syntax of
+ * printf(3).
+ */
+ void handleMsg(int level, const char *fmt, va_list va_args) const;
+
+ sefs_callback_fn_t _callback;
+ void *_varg;
+ sefs_fclist_type_e _fclist_type;
+};
+
+extern "C"
+{
+#endif
+
+//we do not want to wrap two copies of everything so have SWIG ignore
+//the compatibility section.
+#ifndef SWIG
+
+ typedef struct sefs_fclist sefs_fclist_t;
+
+#ifndef SEFS_MAP_FUNC_DEFINED
+ struct sefs_fclist;
+ struct sefs_entry;
+ typedef int (*sefs_fclist_map_fn_t) (struct sefs_fclist *, const struct sefs_entry *, void *);
+#endif
+
+/**
+ * Deallocate all memory associated with the referenced fclist object,
+ * and then set it to NULL. This function does nothing if the fclist
+ * object is already NULL.
+ * @param Reference to a fclist object to destroy.
+ */
+ extern void sefs_fclist_destroy(sefs_fclist_t ** fclist);
+
+/**
+ * Perform a sefs query on the given file context list object.
+ * @see sefs_fclist::runQueryMap()
+ */
+ extern int sefs_fclist_run_query_map(sefs_fclist_t * fclist, sefs_query_t * query, sefs_fclist_map_fn_t fn, void *data);
+
+/**
+ * Perform a sefs query on the given file context list object.
+ * @see sefs_fclist::runQuery()
+ */
+ extern apol_vector_t *sefs_fclist_run_query(sefs_fclist_t * fclist, sefs_query_t * query);
+
+/**
+ * Determine if the contexts in the fclist contain MLS fields.
+ * @see sefs_fclist::isMLS()
+ */
+ extern bool sefs_fclist_get_is_mls(const sefs_fclist_t * fclist);
+
+/**
+ * Associate a policy with the fclist.
+ * @see sefs_fclist::associatePolicy()
+ * @see sefs_query_set_type()
+ * @see sefs_query_set_range()
+ */
+ extern void sefs_fclist_associate_policy(sefs_fclist_t * fclist, apol_policy_t * policy);
+
+/**
+ * Get the type of fclist object represented by \a fclist.
+ * @see sefs_fclist::fclist_type()
+ */
+ extern sefs_fclist_type_e sefs_fclist_get_fclist_type(const sefs_fclist_t * fclist);
+
+#endif /* SWIG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SEFS_FCLIST_H */
diff --git a/libsefs/include/sefs/filesystem.hh b/libsefs/include/sefs/filesystem.hh
new file mode 100644
index 0000000..1d2c9ed
--- /dev/null
+++ b/libsefs/include/sefs/filesystem.hh
@@ -0,0 +1,181 @@
+/**
+ * @file
+ * Defines the public interface for the filesystem fc list object.
+ *
+ * @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_FILESYSTEM_H
+#define SEFS_FILESYSTEM_H
+
+#include <sefs/fclist.hh>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+
+#ifdef __cplusplus
+}
+
+#include <stdexcept>
+
+/**
+ * This class represents the SELinux file contexts on a local on-disk
+ * filesystem. Be aware that the object will recurse beginning from
+ * the root directory, so if there are circular mounts (e.g., via
+ * something mounted with the 'bind' option) then queries against the
+ * filesystem will never terminate.
+ */
+class sefs_filesystem:public sefs_fclist
+{
+#ifndef SWIG_FRIENDS
+ // private functions -- do not call these directly from
+ // outside of the library
+ friend struct sefs_context_node *filesystem_get_context(sefs_filesystem *, security_context_t) throw(std::bad_alloc);
+ friend sefs_entry *filesystem_get_entry(sefs_filesystem *, const struct sefs_context_node *, uint32_t,
+ const char *, ino64_t, const char *) throw(std::bad_alloc);
+ friend bool filesystem_is_query_match(sefs_filesystem *, const sefs_query *, const char *, const char *,
+ const struct stat64 *, apol_vector_t *, apol_mls_range_t *) throw(std::runtime_error);
+#endif
+
+ public:
+
+ /**
+ * Allocate and return a new sefs filesystem structure
+ * representing the filesystem rooted at directory \a root.
+ * <b>Be aware that the constructor is not thread-safe.</b>
+ * @param new_root Directory to use as the root of the
+ * filesystem. This object represents this directory and all
+ * subdirectories, including other mounted filesystems.
+ * @param msg_callback Callback to invoke as errors/warnings
+ * are generated. If NULL, write messages to standard error.
+ * @param varg Value to be passed as the first parameter to
+ * the callback function.
+ * @exception bad_alloc Out of memory.
+ * @exception invalid_argument Root directory does not exist.
+ * @exception runtime_error Could not open root directory or
+ * /etc/mtab.
+ */
+ 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_filesystem();
+
+ /**
+ * Perform a sefs query on this filesystem object, and then
+ * invoke a callback upon each matching entry. Mapping is in
+ * pre-order (i.e., directories will be mapped prior to files
+ * and subdirectories they contain.)
+ * @param query Query object containing search parameters. If
+ * NULL, invoke the callback on all entries.
+ * @param fn Function to invoke upon matching entries. This
+ * function will be called with three parameters: a pointer to
+ * this filesystem, pointer to a matching entry, and an
+ * arbitrary data pointer. It should return a non-negative
+ * value upon success, negative value upon error and to abort
+ * the mapping.
+ * @param data Arbitrary pointer to be passed into \fn as a
+ * third parameter.
+ * @return Last value returned by fn() (i.e., >= on success, <
+ * 0 on failure). If the filesystem has no entries then
+ * return 0.
+ * @exception std::runtime_error Error while reading contexts
+ * from the filesystem.
+ * @exception std::invalid_argument One or more query arguments
+ * is invalid.
+ */
+ int runQueryMap(sefs_query * query, sefs_fclist_map_fn_t fn, void *data) throw(std::runtime_error, std::invalid_argument);
+
+ /**
+ * Determine if the contexts stored in this filesystem contain
+ * MLS fields.
+ * @return \a true if MLS fields are present, \a false if not
+ * or undeterminable.
+ */
+ bool isMLS() const;
+
+ /**
+ * Get the root directory of a sefs filesystem structure.
+ * @return The root directory of the filesystem or NULL on
+ * error. Do not free() this string.
+ */
+ const char *root() const;
+
+ /**
+ * Look up the given device number on the currently running
+ * system, and convert it to its device name.
+ * @param dev Device number to look up.
+ * @return Name of the device, or NULL if the device number
+ * was not found. Do not free() this pointer.
+ * @exception std::runtime_error Error while querying system.
+ */
+ const char *getDevName(const dev_t dev) throw(std::runtime_error);
+
+ private:
+ apol_vector_t * buildDevMap(void) throw(std::runtime_error);
+ bool 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);
+ sefs_entry *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 *_root;
+ bool _rw, _mls;
+};
+
+extern "C"
+{
+#endif
+
+//we do not want to wrap two copies of everything so have SWIG ignore
+//the compatibility section.
+#ifndef SWIG
+
+ typedef struct sefs_filesystem sefs_filesystem_t;
+
+/**
+ * Allocate and return a new sefs filesystem structure representing
+ * the filesystem rooted at directory \a root.
+ * @see sefs_filesystem::sefs_filesystem()
+ */
+ extern sefs_filesystem_t *sefs_filesystem_create(const char *root, sefs_callback_fn_t msg_callback, void *varg);
+
+/**
+ * Get the root directory of a sefs filesystem structure.
+ * @see sefs_filesystem::root()
+ */
+ extern const char *sefs_filesystem_get_root(const sefs_filesystem_t * fs);
+
+/**
+ * Look up the given device number on the currently running
+ * system, and convert it to its device name.
+ * @see sefs_filesystem::ged_dev_name()
+ */
+ extern const char *sefs_filesystem_get_dev_name(sefs_filesystem_t * fs, const dev_t dev);
+
+#endif /* SWIG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SEFS_FILESYSTEM_H */
diff --git a/libsefs/include/sefs/query.hh b/libsefs/include/sefs/query.hh
new file mode 100644
index 0000000..c0e8921
--- /dev/null
+++ b/libsefs/include/sefs/query.hh
@@ -0,0 +1,329 @@
+/**
+ * @file
+ * Defines the public interface for file context queries.
+ *
+ * @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_QUERY_H
+#define SEFS_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <sys/types.h>
+#include <regex.h>
+
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+#include <apol/context-query.h>
+#include <apol/mls-query.h>
+#include <apol/policy-query.h>
+#include <apol/vector.h>
+
+#ifdef __cplusplus
+}
+
+#include <stdexcept>
+
+/**
+ * This class represents a query into a (subclass of) fclist. Create
+ * a query, fill in all accessors are needed, and then run the query.
+ * All fields must match for an entry to be returned. Where a fclist
+ * does not support a particular criterion (e.g., inode numbers for
+ * fcfile) that portion of the query is considered to be matching.
+ */
+class sefs_query
+{
+ friend class sefs_db;
+ friend class sefs_fcfile;
+ friend class sefs_filesystem;
+
+ public:
+
+ /**
+ * Allocate and return a new sefs query structure. All fields
+ * are initialized, such that running this blank query results
+ * in returning all entries within a fclist.
+ */
+ sefs_query();
+
+ ~sefs_query();
+
+ /**
+ * Set a sefs query to match only entries with contexts with
+ * the user \a name.
+ * @param name Limit query to only contexts with this user, or
+ * NULL to clear this field. The string will be duplicated.
+ * @exception std::bad_alloc Out of memory.
+ */
+ void user(const char *name) throw(std::bad_alloc);
+
+ /**
+ * Set a sefs query to match only entries with contexts with
+ * the role \a name.
+ * @param name Limit query to only contexts with this role, or
+ * NULL to clear this field. The string will be duplicated.
+ * @exception std::bad_alloc Out of memory.
+ */
+ void role(const char *name) throw(std::bad_alloc);
+
+ /**
+ * Set a sefs query to match only entries with contexts with
+ * the type \a name.
+ * @param name Limit query to only contexts with this type, or
+ * NULL to clear this field. The string will be duplicated.
+ * @param indirect If true and if the fclist queried has
+ * access to a policy, also match contexts with types in
+ * attribute \a name or types which are an alias for \a name.
+ * If a policy is not available, this field is ignored, and
+ * exact string matching is used instead. This paramater is
+ * ignored if \a name is NULL.
+ * @exception std::bad_alloc Out of memory.
+ * @see sefs_fclist::associatePolicy() to associate a policy
+ * with a fclist.
+ */
+ void type(const char *name, bool indirect) throw(std::bad_alloc);
+
+ /**
+ * Set a sefs query to match only entries with contexts with a
+ * range of \a range. If the fclist is not MLS then \a name
+ * and \a match will be ignored.
+ * @param name Limit query to only contexts matching this
+ * string representing the MLS range, or NULL to clear this
+ * field. The string will be duplicated.
+ * @param match If non-zero and the fclist queried has access
+ * to a policy, match the range using the specified semantics;
+ * this should be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or
+ * APOL_QUERY_EXACT. (The range string will be converted
+ * automatically into an apol_mls_range_t object.) If a
+ * policy is not available or \a match is zero, exact string
+ * matching is used instead. Note, if a policy is available
+ * the regex flag is ignored if \a match is non-zero. This
+ * parameter is ignored if \a range is NULL.
+ * @exception std::bad_alloc Out of memory.
+ * @see sefs_fclist::associatePolicy() to associate a policy
+ * with a fclist.
+ */
+ void range(const char *name, int match) throw(std::bad_alloc);
+
+ /**
+ * Set a sefs query to match only entries with object class \a
+ * objclass.
+ *
+ * <em>Note:</em> If the query is run against a fcfile, then
+ * entries without explicit object classes (i.e., no explicit
+ * <tt>--</tt>, <tt>-d</tt>, etc.) will always match
+ * irrespective of the query's object class field.
+ *
+ * @param Numeric identifier for an objclass, one of
+ * QPOL_CLASS_FILE, QPOL_CLASS_DIR, etc., as defined in
+ * <qpol/genfscon_query.h>. Use QPOL_CLASS_ALL to match all
+ * object classes.
+ */
+ void objectClass(uint32_t objclass);
+
+ /**
+ * Set a sefs query to match only entries with object class \a
+ * name. The \a name parameter is not affected by regex().
+ *
+ * @param name Limit query to only entries with this object
+ * class, or NULL to clear this field. The incoming string
+ * must be legal according to apol_str_to_objclass().
+ *
+ * @see objectClass(uint32_t) for note about fcfiles.
+ */
+ void objectClass(const char *name);
+
+ /**
+ * Set a sefs query to match only entries with path \a path.
+ *
+ * <em>Note:</em> If the query is run against a fcfile, the
+ * behavior of matching paths is slightly different. For each
+ * of fcfile's entries, that entry's regular expression is
+ * matched against \a path. This is the reverse for other
+ * types of fclist, where \a path matches an entry's path if
+ * \a path is a substring. (If sefs_query::regex() is set to
+ * true, \a path is instead treated as a regular expression.)
+ *
+ * @param str Limit query to only entries containing this
+ * path, or NULL to clear this field. The string will be
+ * duplicated.
+ * @exception std::bad_alloc Out of memory.
+ */
+ void path(const char *str) throw(std::bad_alloc);
+
+ /**
+ * Set a sefs query to match only entries with a given inode
+ * number.
+ * @param ino Limit query to only entries with this inode
+ * number, or 0 to clear this field.
+ */
+ void inode(ino64_t ino);
+
+ /**
+ * Set a sefs query to match only entries with a given device
+ * name.
+ * @param str Limit query to only entries with this device
+ * name, or NULL to clear this string. The string will be
+ * duplicated.
+ * @exception std::bad_alloc Out of memory.
+ * @see sefs_filesystem::getDevName() to convert between dev_t
+ * and a name.
+ */
+ void dev(const char *str) throw(std::bad_alloc);
+
+ /**
+ * Set a sefs query to use regular expression matching for
+ * string fields.
+ * @param r If true then use regular expression matching;
+ * otherwise use only exact string matching.
+ */
+ void regex(bool r);
+
+ private:
+ /**
+ * Compile the regular expressions stored within this query
+ * object. It is safe to call this function multiple times.
+ *
+ * @exception std::bad_alloc Out of memory.
+ * @exception std::invalid_argument One or more invalid regular
+ * expressions is invalid.
+ */
+ void compile() throw(std::bad_alloc, std::invalid_argument);
+
+ char *_user, *_role, *_type, *_range, *_path, *_dev;
+ uint32_t _objclass;
+ bool _indirect, _regex, _recursive;
+ int _rangeMatch;
+ ino64_t _inode;
+ bool _recompiled;
+ regex_t *_reuser, *_rerole, *_retype, *_rerange, *_repath, *_redev;
+};
+
+extern "C"
+{
+#endif
+
+//we do not want to wrap two copies of everything so have SWIG ignore
+//the compatibility section.
+#ifndef SWIG
+
+ typedef struct sefs_query sefs_query_t;
+
+/**
+ * Allocate and return a new sefs query structure.
+ * @see sefs_query::sefs_query()
+ */
+ extern sefs_query_t *sefs_query_create();
+
+/**
+ * Deallocate all memory associated with the referenced sefs query,
+ * and then set it to NULL. This function does nothing if the query
+ * is already NULL.
+ * @param query Reference to a sefs query structure to destroy.
+ */
+ extern void sefs_query_destroy(sefs_query_t ** query);
+
+/**
+ * Set a sefs query to match only entries with contexts with the user
+ * \a name.
+ * @see sefs_query::user()
+ */
+ extern int sefs_query_set_user(sefs_query_t * query, const char *name);
+
+/**
+ * Set a sefs query to match only entries with contexts with the role
+ * \a name.
+ * @see sefs_query::role()
+ */
+ extern int sefs_query_set_role(sefs_query_t * query, const char *name);
+
+/**
+ * Set a sefs query to match only entries with contexts with the type
+ * \a name.
+ * @see sefs_query::type()
+ * @see sefs_fclist_associate_policy() to associate a policy with a
+ * fclist.
+ */
+ extern int sefs_query_set_type(sefs_query_t * query, const char *name, bool indirect);
+
+/**
+ * Set a sefs query to match only entries with contexts with a range
+ * of \a range.
+ * @see sefs_query::range()
+ * @see sefs_fclist_associate_policy() to associate a policy with a
+ * fclist.
+ */
+ extern int sefs_query_set_range(sefs_query_t * query, const char *range, int match);
+
+/**
+ * Set a sefs query to match only entries with object class \a
+ * objclass.
+ * @return Always 0.
+ * @see sefs_query::objectClass(uint32_t)
+ */
+ extern int sefs_query_set_object_class(sefs_query_t * query, uint32_t objclass);
+
+/**
+ * Set a sefs query to match only entries with object class \a name.
+ * @return Always 0.
+ * @see sefs_query::objectClass(const char *)
+ */
+ extern int sefs_query_set_object_class_str(sefs_query_t * query, const char *name);
+
+/**
+ * Set a sefs query to match only entries with path \a path.
+ * @see sefs_query::path()
+ */
+ extern int sefs_query_set_path(sefs_query_t * query, const char *path);
+
+/**
+ * Set a sefs query to match only entries with a given inode number.
+ * @return Always 0.
+ * @see sefs_query::inode()
+ */
+ extern int sefs_query_set_inode(sefs_query_t * query, ino64_t inode);
+
+/**
+ * Set a sefs query to match only entries with a given device number.
+ * @see sefs_query::dev()
+ */
+ extern int sefs_query_set_dev(sefs_query_t * query, const char *dev);
+
+/**
+ * Set a sefs query to use regular expression matching for string
+ * fields.
+ * @return Always 0.
+ * @see sefs_query::regex()
+ */
+ extern int sefs_query_set_regex(sefs_query_t * query, bool regex);
+
+#endif /* SWIG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SEFS_QUERY_H */
diff --git a/libsefs/include/sefs/util.h b/libsefs/include/sefs/util.h
new file mode 100644
index 0000000..24480ea
--- /dev/null
+++ b/libsefs/include/sefs/util.h
@@ -0,0 +1,56 @@
+/**
+ * @file
+ *
+ * Miscellaneous, uncategorized 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
+ */
+
+#ifndef SEFS_UTIL_H
+#define SEFS_UTIL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * Return an immutable string describing this library's version.
+ *
+ * @return String describing this library.
+ */
+ extern const char *libsefs_get_version(void);
+
+/**
+ * Return the name (path + filename) of the file_contexts file for the
+ * currently running SELinux system. If the system is not running
+ * SELinux then return an empty string ("").
+ *
+ * @return The name of the default file_contexts file (if system is
+ * running SELinux), an empty string (if not SELinux), or NULL upon
+ * error. The caller must free() the string afterwards.
+ */
+ extern char *sefs_default_file_contexts_get_path(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
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
+}
diff --git a/libsefs/swig/Makefile.am b/libsefs/swig/Makefile.am
new file mode 100644
index 0000000..6af1d63
--- /dev/null
+++ b/libsefs/swig/Makefile.am
@@ -0,0 +1,15 @@
+if DO_SWIGIFY_PYTHON
+ MAYBE_PYSWIG = python
+endif
+
+if DO_SWIGIFY_JAVA
+ MAYBE_JSWIG = java
+endif
+
+if DO_SWIGIFY_TCL
+ MAYBE_TCLSWIG = tcl
+endif
+
+SUBDIRS = $(MAYBE_PYSWIG) $(MAYBE_JSWIG) $(MAYBE_TCLSWIG)
+
+dist_noinst_DATA = sefs.i
diff --git a/libsefs/swig/java/MANIFEST.MF.in b/libsefs/swig/java/MANIFEST.MF.in
new file mode 100644
index 0000000..0ac9628
--- /dev/null
+++ b/libsefs/swig/java/MANIFEST.MF.in
@@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+
+Name: com/tresys/setools/
+Specification-Title: "SETools Java Libraries"
+Specification-Version: "@VERSION@"
+Specification-Vendor: "Tresys Technology"
+Implementation-Title: "com.tresys.setools.sefs"
+Implementation-Version: "@libsefs_version@"
+Implementation-Vendor: "Tresys Technology"
+Extension-List: qpol apol
+qpol-Extension-Name: com.tresys.setools.qpol
+qpol-Implementation-Version: @libqpol_version@
+apol-Extension-Name: com.tresys.setools.apol
+apol-Implementation-Version: @libapol_version@
diff --git a/libsefs/swig/java/Makefile.am b/libsefs/swig/java/Makefile.am
new file mode 100644
index 0000000..088b61b
--- /dev/null
+++ b/libsefs/swig/java/Makefile.am
@@ -0,0 +1,85 @@
+wrappedso_DATA = libjsefs.so.@libsefs_version@
+wrappedso_SONAME = @libsefs_jswig_soname@
+short_name = libjsefs.so
+wrappedsodir = $(libdir)
+
+package_name = com.tresys.setools.sefs
+package_dir = $(dir $(subst .,/,$(package_name)))sefs
+
+wrappedjar_DATA = sefs.jar
+wrappedjardir = $(setoolsdir)
+
+dist_noinst_DATA = $(srcdir)/../sefs.i
+BUILT_SOURCES = sefs_wrap.cc \
+ sefsConstants.java \
+ sefs_db.java \
+ sefs_entry.java \
+ sefs_fcfile.java \
+ sefs_fclist.java \
+ sefs_fclist_type_e.java \
+ sefs_filesystem.java \
+ sefs.java \
+ sefsJNI.java \
+ sefs_query.java \
+ SWIGTYPE_p_dev_t.java \
+ SWIGTYPE_p_f_p_sefs_fclist_p_q_const__sefs_entry_p_void__int.java \
+ SWIGTYPE_p_f_p_void_p_q_const__sefs_fclist_int_p_q_const__char_va_list__void.java \
+ SWIGTYPE_p_ino64_t.java \
+ SWIGTYPE_p_void.java
+
+AM_CXXFLAGS = @DEBUGCXXFLAGS@ @WARNCXXFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libsefs/include
+AM_JFLAGS = @DEBUGJFLAGS@ @WARNJFLAGS@ \
+ -classpath $(top_builddir)/libqpol/swig/java/qpol.jar:$(top_builddir)/libapol/swig/java/apol.jar
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ \
+ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @SEFS_LIB_FLAG@
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libsefs/src/libsefs.so
+
+$(firstword $(BUILT_SOURCES)): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) -c++ $(SWIG_JAVA_OPT) -package $(package_name) -o $@ \
+ -I$(top_srcdir)/libsefs/include -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libqpol/include \
+ -I$(top_srcdir)/libqpol/swig -I$(top_srcdir)/libapol/swig $<
+
+$(wordlist 2,$(words $(BUILT_SOURCES)), $(BUILT_SOURCES)): $(firstword $(BUILT_SOURCES))
+
+$(wrappedso_DATA): $(filter %.cc, $(BUILT_SOURCES))
+ $(CXX) -shared -o $@ $^ $(AM_CXXFLAGS) $(CXXFLAGS) $(SWIG_JAVA_CFLAGS) -DSWIGJAVA=1 $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ $(short_name)
+
+# Intentionally do not include SWIGTYPE_p_void.java below so that the
+# Java compiler uses the one created in package
+# com.tresys.setools.qpol instead of the one from package
+# com.tresys.setools.sefs.
+java_files = $(filter-out SWIGTYPE_p_void.java, $(filter %.java, $(BUILT_SOURCES)))
+
+classes = $(patsubst %.java, $(package_dir)/%.class, $(java_files))
+
+# Because the Java compiler can generate multiple class files from the
+# same source .java file, putting all of the classes below will result
+# in repeated invocations of javac. Therefore, an alternative is to
+# just depend upon the first class file, and let the Java compiler
+# create the rest of them.
+$(firstword $(classes)): $(java_files)
+ $(JAVAC) $(AM_JFLAGS) $(JAVAFLAGS) -d . $^
+
+$(wordlist 2,$(words $(classes)),$(classes)): $(firstword $(classes))
+
+$(wrappedjar_DATA): MANIFEST.MF
+
+$(wrappedjar_DATA): $(classes)
+ $(JAR) cfm $@ MANIFEST.MF $^
+
+install-data-hook:
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(wrappedso_SONAME)
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(short_name)
+ $(mkdir_p) $(DESTDIR)$(javadir) && cd $(DESTDIR)$(javadir) && $(LN_S) -f $(wrappedjardir)/$(wrappedjar_DATA)
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(wrappedsodir)/$(wrappedso_SONAME) $(DESTDIR)$(wrappedsodir)/$(short_name)
+ -rm -f $(DESTDIR)$(javadir)/$(wrappedjar_DATA)
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(classes) $(wrappedso_DATA) $(wrappedjar_DATA) $(wrappedso_SONAME) $(short_name)
diff --git a/libsefs/swig/python/Makefile.am b/libsefs/swig/python/Makefile.am
new file mode 100644
index 0000000..f7fb7b3
--- /dev/null
+++ b/libsefs/swig/python/Makefile.am
@@ -0,0 +1,39 @@
+wrappedso_DATA = _sefs.so.@libsefs_version@
+wrappedso_SONAME = @libsefs_pyswig_soname@
+wrappedsodir = $(pkgpyexecdir)
+
+wrappedpy_DATA = sefs.py
+wrappedpydir = $(pkgpyexecdir)
+
+dist_noinst_DATA = $(srcdir)/../sefs.i
+BUILT_SOURCES = sefs_wrap.cc
+
+AM_CXXFLAGS = @DEBUGCXXFLAGS@ @WARNCXXFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libsefs/include
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ @PYTHON_LDFLAGS@ \
+ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @SEFS_LIB_FLAG@ @XML_LIBS@
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libsefs/src/libsefs.so
+
+$(BUILT_SOURCES): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) -c++ $(SWIG_PYTHON_OPT) -o $@ \
+ -I$(top_srcdir)/libsefs/include -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libqpol/include \
+ -I$(top_srcdir)/libqpol/swig -I$(top_srcdir)/libapol/swig $<
+
+$(wrappedso_DATA): $(BUILT_SOURCES)
+ $(CXX) -shared -o $@ $^ $(AM_CXXFLAGS) $(CXXFLAGS) $(SWIG_PYTHON_CPPFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ _sefs.so
+
+$(wrappedpy_DATA): $(BUILT_SOURCES)
+
+install-data-hook:
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(wrappedso_SONAME)
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) _sefs.so
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(wrappedsodir)/$(wrappedso_SONAME) $(DESTDIR)$(wrappedsodir)/_sefs.so
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(wrappedso_DATA) $(wrappedpy_DATA) $(wrappedso_SONAME) _sefs.so sefs.pyc
diff --git a/libsefs/swig/sefs.i b/libsefs/swig/sefs.i
new file mode 100644
index 0000000..adc5d7e
--- /dev/null
+++ b/libsefs/swig/sefs.i
@@ -0,0 +1,162 @@
+/**
+ * @file
+ * SWIG declarations 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
+ */
+
+%module sefs
+
+%{
+#include <sefs/db.hh>
+#include <sefs/entry.hh>
+#include <sefs/fcfile.hh>
+#include <sefs/fclist.hh>
+#include <sefs/filesystem.hh>
+#include <sefs/query.hh>
+#include <sefs/util.h>
+%}
+
+%import apol.i
+
+%exception;
+
+%include std_except.i
+
+%{
+#undef BEGIN_EXCEPTION
+#undef END_EXCEPTION
+%}
+
+%inline %{
+ typedef struct apol_string_vector apol_string_vector_t;
+%}
+
+#ifdef SWIGPYTHON
+
+%typemap(out) time_t {
+ $result = PyInt_FromLong((long) $1);
+}
+
+#endif // end of python specific code
+
+#ifdef SWIGJAVA
+
+/* handle size_t correctly in java as architecture independent */
+%typemap(jni) size_t "jlong"
+%typemap(jtype) size_t "long"
+%typemap(jstype) size_t "long"
+%typemap("javaimports") SWIGTYPE, FILE* %{
+import com.tresys.setools.qpol.*;
+import com.tresys.setools.apol.*;
+%}
+/* the following handles the dependencies on qpol and apol */
+%pragma(java) jniclassimports=%{
+import com.tresys.setools.qpol.*;
+import com.tresys.setools.apol.*;
+%}
+%pragma(java) jniclasscode=%{
+ static {
+ try
+ {
+ libsefs_get_version ();
+ }
+ catch (UnsatisfiedLinkError ule)
+ {
+ System.loadLibrary("jsefs");
+ }
+ }
+%}
+%pragma(java) moduleimports=%{
+import com.tresys.setools.qpol.*;
+import com.tresys.setools.apol.*;
+%}
+
+%apply long { time_t }
+
+%javaconst(1);
+
+#else
+/* not in java so handle size_t as architecture dependent */
+#ifdef SWIGWORDSIZE64
+typedef uint64_t size_t;
+#else
+typedef uint32_t size_t;
+#endif
+
+#endif // end of Java specific code
+
+#ifdef SWIGTCL
+
+%wrapper %{
+/* Tcl module's initialization routine is expected to be named
+ * Sefs_Init(), but the output file will be called libtsefs.so instead
+ * of libsefs.so. Therefore add an alias from Tsefs_Init() to the
+ * real Sefs_Init().
+ */
+SWIGEXPORT int Tsefs_Init(Tcl_Interp *interp) {
+ return SWIG_init(interp);
+}
+%}
+
+%typemap(out) time_t {
+ Tcl_SetObjResult(interp, Tcl_NewLongObj((long) $1));
+}
+
+#endif // end of Tcl specific code
+
+
+%nodefaultctor;
+
+#define __attribute__(x)
+
+%ignore sefs_fcfile::fileList() const;
+
+// don't wrap private friend functions
+#define SWIG_FRIENDS
+
+%newobject sefs_fclist::runQuery(sefs_query * query);
+%newobject sefs_entry::toString();
+%newobject sefs_default_file_contexts_get_path();
+
+%include <sefs/fclist.hh>
+%include <sefs/db.hh>
+%include <sefs/entry.hh>
+%include <sefs/fcfile.hh>
+%include <sefs/filesystem.hh>
+%include <sefs/query.hh>
+
+const char *libsefs_get_version (void);
+char *sefs_default_file_contexts_get_path(void);
+
+%inline %{
+ // needed to convert from the results of runQuery() to the entry
+ sefs_entry *sefs_entry_from_void(void *v) {
+ return static_cast<sefs_entry *>(v);
+ }
+%}
+
+%extend sefs_fcfile {
+ const apol_string_vector_t *fileListStrs() const
+ {
+ const apol_vector_t *v = self->fileList();
+ return reinterpret_cast<const apol_string_vector_t*>(v);
+ }
+}
diff --git a/libsefs/swig/tcl/Makefile.am b/libsefs/swig/tcl/Makefile.am
new file mode 100644
index 0000000..7964e52
--- /dev/null
+++ b/libsefs/swig/tcl/Makefile.am
@@ -0,0 +1,37 @@
+wrappedso_DATA = libtsefs.so.@libsefs_version@
+wrappedso_SONAME = @libsefs_tswig_soname@
+short_name = libtsefs.so
+wrappedsodir = $(libdir)/setools/sefs
+
+package_SCRIPTS = pkgIndex.tcl
+packagedir = $(wrappedsodir)
+
+dist_noinst_DATA = $(srcdir)/../sefs.i
+BUILT_SOURCES = sefs_wrap.cc
+
+AM_CXXFLAGS = @DEBUGCXXFLAGS@ @WARNCXXFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libsefs/include
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ @TCL_LIB_SPEC@ \
+ @SEFS_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libsefs/src/libsefs.so
+
+$(BUILT_SOURCES): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) -c++ $(SWIG_TCL_OPT) -pkgversion @libsefs_version@ -o $@ -I$(top_srcdir)/libsefs/include -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libapol/swig -I$(top_srcdir)/libqpol/swig $<
+
+$(wrappedso_DATA): $(BUILT_SOURCES)
+ $(CXX) -shared -o $@ $^ $(AM_CXXFLAGS) $(CXXFLAGS) $(SWIG_TCL_CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ $(short_name)
+
+$(package_SCRIPTS): $(wrappedso_DATA)
+ echo "pkg_mkIndex . $^" | LD_LIBRARY_PATH=$(top_builddir)/libqpol/src:$(top_builddir)/libapol/src:$(top_builddir)/libsefs/src $(TCLSH_PROG)
+ chmod 644 $@
+ $(mkdir_p) sefs
+ cp $(wrappedso_DATA) $@ sefs
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(wrappedso_DATA) $(wrappedso_SONAME) $(short_name) $(package_DATA) sefs/$(wrappedso_DATA) sefs/$(package_SCRIPTS)
+
+CLEANFILES = $(package_SCRIPTS)
diff --git a/libsefs/tests/Makefile.am b/libsefs/tests/Makefile.am
new file mode 100644
index 0000000..50f7af0
--- /dev/null
+++ b/libsefs/tests/Makefile.am
@@ -0,0 +1,17 @@
+TESTS = libsefs-tests
+check_PROGRAMS = libsefs-tests
+
+libsefs_tests_SOURCES = \
+ fcfile-tests.cc fcfile-tests.hh \
+ libsefs-tests.cc
+
+EXTRA_DIST = file_contexts.confed file_contexts.union file_contexts.broken
+
+AM_CXXFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ @SEFS_CFLAGS@ -DSRCDIR="\"$(srcdir)\""
+
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@
+
+LDADD = @SELINUX_LIB_FLAG@ @SEFS_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @CUNIT_LIB_FLAG@
+
+libsefs_tests_DEPENDENCIES = ../src/libsefs.so \ No newline at end of file
diff --git a/libsefs/tests/attic/fuse_non_mls.c b/libsefs/tests/attic/fuse_non_mls.c
new file mode 100644
index 0000000..30b3df4
--- /dev/null
+++ b/libsefs/tests/attic/fuse_non_mls.c
@@ -0,0 +1,195 @@
+/*
+ * The approach described below does not actually work. Apparantly,
+ * SELinux will assign a context based upond the underlying policy
+ * (typically from a fs_use statement); the operating system will not
+ * invoke this file's fuse_getxattr() function at all. Thus it is
+ * not possible to use FUSE to create a virtual filesystem with
+ * arbitrary file contexts.
+ */
+
+/**
+ * @file
+ *
+ * Use the FUSE (filesystem in userspace) to create a virtual
+ * filesystem for libsefs tests. This particular filesystem will not
+ * contain MLS contexts.
+ *
+ * @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>
+
+#define FUSE_USE_VERSION 25
+
+#define XATTR_NAME_SELINUX "security.selinux"
+
+#include <apol/bst.h>
+#include <fuse.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+/*************** definition of the virtual filesystem ***************/
+
+static apol_bst_t *bst = NULL;
+
+struct fuse_entry
+{
+ const char *path;
+ mode_t mode;
+ const char *context;
+};
+
+static struct fuse_entry fs[] = {
+ {"/", S_IFDIR, "user_r:object_r:system_t"},
+ {"/foo", S_IFREG, "user_r:object_r:system_t"},
+ {NULL, 0, NULL}
+};
+
+static int fuse_comp(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ const struct fuse_entry *f1 = (const struct fuse_entry *)a;
+ const struct fuse_entry *f2 = (const struct fuse_entry *)b;
+ return strcmp(f1->path, f2->path);
+}
+
+/*************** required fuse functions ***************/
+
+static int fuse_getattr(const char *path, struct stat *stbuf)
+{
+ struct fuse_entry key = { path, 0, NULL };
+ struct fuse_entry *e;
+ memset(stbuf, 0, sizeof(*stbuf));
+ if (apol_bst_get_element(bst, &key, NULL, (void **)&e) < 0) {
+ return -ENOENT;
+ }
+ if (e->mode == S_IFDIR) {
+ stbuf->st_mode = S_IFDIR | 0755;
+ stbuf->st_nlink = 2;
+ } else {
+ stbuf->st_mode = e->mode | 0444;
+ stbuf->st_nlink = 1;
+ }
+ return 0;
+}
+
+static int fuse_getxattr(const char *path, const char *attrib_name, char *buf, size_t buflen)
+{
+ struct fuse_entry key = { path, 0, NULL };
+ struct fuse_entry *e;
+ if (apol_bst_get_element(bst, &key, NULL, (void **)&e) < 0) {
+ return -ENOENT;
+ }
+ if (strcmp(attrib_name, XATTR_NAME_SELINUX) != 0) {
+ return -ENOSYS;
+ }
+ strncpy(buf, e->context, buflen);
+ return strlen(e->context) + 1;
+}
+
+static int fuse_open(const char *path, struct fuse_file_info *fi __attribute__ ((unused)))
+{
+ struct fuse_entry key = { path, 0, NULL };
+ struct fuse_entry *e;
+ if (apol_bst_get_element(bst, &key, NULL, (void **)&e) < 0) {
+ return -ENOENT;
+ }
+
+ return -EACCES;
+}
+
+static int fuse_read(const char *path, char *buf __attribute__ ((unused)), size_t size, off_t offset __attribute__ ((unused)),
+ struct fuse_file_info *fi __attribute__ ((unused)))
+{
+ struct fuse_entry key = { path, 0, NULL };
+ struct fuse_entry *e;
+ if (apol_bst_get_element(bst, &key, NULL, (void **)&e) < 0) {
+ return -ENOENT;
+ }
+ return size;
+}
+
+struct stem_data
+{
+ const char *stem;
+ void *buf;
+ fuse_fill_dir_t filler;
+};
+
+static int fuse_stem_match(void *a, void *data)
+{
+ const struct fuse_entry *e = (const struct fuse_entry *)a;
+ struct stem_data *sd = (struct stem_data *)data;
+
+ size_t e_len = strlen(e->path);
+ size_t stem_len = strlen(sd->stem);
+ if (e_len <= stem_len) {
+ /* entry's path is too longer than the requested path */
+ return 0;
+ }
+ if (strncmp(e->path, sd->stem, stem_len) != 0) {
+ /* stem is not the beginning of entry's path */
+ return 0;
+ }
+ const char *file = e->path + 1;
+ if (strchr(file, '/') != NULL) {
+ /* member of a subdirectory of stem */
+ return 0;
+ }
+ return -sd->filler(sd->buf, file, NULL, 0);
+}
+
+static int fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset __attribute__ ((unused)), struct fuse_file_info *fi __attribute__ ((unused)))
+{
+ struct fuse_entry key = { path, 0, NULL };
+ struct fuse_entry *e;
+ if (apol_bst_get_element(bst, &key, NULL, (void **)&e) < 0) {
+ return -ENOENT;
+ }
+
+ filler(buf, ".", NULL, 0);
+ filler(buf, "..", NULL, 0);
+ struct stem_data sd = { path, buf, filler };
+ return -apol_bst_inorder_map(bst, fuse_stem_match, &sd);
+}
+
+int main(int argc, char *argv[])
+{
+ if ((bst = apol_bst_create(fuse_comp, NULL)) < 0) {
+ return 1;
+ }
+ for (size_t i = 0; fs[i].path != NULL; i++) {
+ if (apol_bst_insert(bst, &fs[i], NULL) < 0) {
+ return 1;
+ }
+ }
+
+ struct fuse_operations non_mls_oper = {
+ .getattr = fuse_getattr,
+ .getxattr = fuse_getxattr,
+ .open = fuse_open,
+ .read = fuse_read,
+ .readdir = fuse_readdir,
+ };
+ return fuse_main(argc, argv, &non_mls_oper);
+}
diff --git a/libsefs/tests/attic/launch-libsefs-tests.sh b/libsefs/tests/attic/launch-libsefs-tests.sh
new file mode 100755
index 0000000..94bc873
--- /dev/null
+++ b/libsefs/tests/attic/launch-libsefs-tests.sh
@@ -0,0 +1,12 @@
+# Mount the virtual filesystems, execute the the real test, then
+# unmount those filesystems.
+
+mkdir -p non-mls
+mkdir -p mls
+./fuse_non_mls non-mls
+./libsefs-tests
+result=$?
+fusermount -u non-mls
+rmdir non-mls
+rmdir mls
+exit ${result}
diff --git a/libsefs/tests/fcfile-tests.cc b/libsefs/tests/fcfile-tests.cc
new file mode 100644
index 0000000..153d111
--- /dev/null
+++ b/libsefs/tests/fcfile-tests.cc
@@ -0,0 +1,333 @@
+/**
+ * @file
+ *
+ * Test the fcfile parsing and querying, introduced in SETools 3.3.
+ *
+ * @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 <CUnit/CUnit.h>
+#include <sefs/fcfile.hh>
+#include <string.h>
+
+#define FC_CONFED SRCDIR "/file_contexts.confed"
+#define FC_UNION SRCDIR "/file_contexts.union"
+#define FC_BROKEN SRCDIR "/file_contexts.broken"
+
+static void fcfile_open()
+{
+ sefs_fcfile *fc1 = NULL, *fc2 = NULL;
+ bool open_failed = false;
+ apol_vector_t *files = apol_vector_create(NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(files);
+ apol_vector_append(files, (void *)(FC_CONFED));
+
+ try
+ {
+ fc1 = new sefs_fcfile(NULL, NULL);
+ fc2 = new sefs_fcfile(files, NULL, NULL);
+ }
+ catch(...)
+ {
+ CU_ASSERT(0);
+ open_failed = true;
+ }
+ delete fc1;
+ fc1 = NULL;
+ if (open_failed)
+ {
+ delete fc2;
+ return;
+ }
+
+ apol_vector_destroy(&files);
+ files = apol_vector_create(NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(files);
+ apol_vector_append(files, (void *)(FC_UNION));
+ apol_vector_append(files, (void *)(FC_BROKEN));
+ size_t num_matches = 0;
+ try
+ {
+ num_matches = fc2->appendFileList(files);
+ CU_ASSERT(num_matches == 1);
+ }
+ catch(...)
+ {
+ CU_ASSERT(0);
+ }
+ apol_vector_destroy(&files);
+
+ CU_ASSERT_FALSE(fc2->isMLS());
+ CU_ASSERT(fc2->fclist_type() == SEFS_FCLIST_TYPE_FCFILE);
+
+ const apol_vector_t *fileList = fc2->fileList();
+ CU_ASSERT(apol_vector_get_size(fileList) == 2);
+ if (apol_vector_get_size(fileList) >= 1)
+ {
+ CU_ASSERT_STRING_EQUAL(apol_vector_get_element(fileList, 0), FC_CONFED);
+ }
+ if (apol_vector_get_size(fileList) >= 2)
+ {
+ CU_ASSERT_STRING_EQUAL(apol_vector_get_element(fileList, 1), FC_UNION);
+ }
+
+ delete fc2;
+}
+
+int fcfile_query_map_user_lee(sefs_fclist * fc __attribute__ ((unused)), const sefs_entry * e, void *data)
+{
+ const apol_context_t *con = e->context();
+ if (strcmp(apol_context_get_user(con), "lee_u") == 0)
+ {
+ CU_ASSERT(1);
+ int *num_matches = static_cast < int *>(data);
+ (*num_matches)++;
+ CU_ASSERT_STRING_EQUAL(e->origin(), FC_CONFED);
+ return 0;
+ }
+ else
+ {
+ CU_ASSERT(0);
+ return -1;
+ }
+}
+
+static void fcfile_query()
+{
+ sefs_fcfile *fc = NULL;
+ int retval;
+ try
+ {
+ fc = new sefs_fcfile(FC_CONFED, NULL, NULL);
+ retval = fc->appendFile(FC_UNION);
+ CU_ASSERT(retval == 0);
+ }
+ catch(...)
+ {
+ CU_ASSERT_FATAL(0);
+ }
+
+ sefs_query *q = new sefs_query();
+ q->user("lee_u");
+ int num_matches = 0;
+ retval = fc->runQueryMap(q, fcfile_query_map_user_lee, &num_matches);
+ CU_ASSERT(retval == 0 && num_matches == 2);
+
+ q->user(NULL);
+ q->role("location_r");
+ apol_vector_t *entries = fc->runQuery(q);
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 11);
+ 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));
+ const apol_context_t *con = e->context();
+ const char *t = apol_context_get_type(con);
+ CU_ASSERT(strcmp(t, "city_t") == 0 || strcmp(t, "state_t") == 0 || strcmp(t, "terrain_t") == 0);
+ char *s = e->toString();
+ printf("%s\n", s);
+ CU_ASSERT_PTR_NOT_NULL(s);
+ free(s);
+ }
+ apol_vector_destroy(&entries);
+
+ q->type("city_t", false);
+ entries = fc->runQuery(q); // both role and type are set
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 3);
+ bool found_boonsboro = false, found_sharpsburg = false, found_harpers_ferry = false;
+ 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));
+ const char *p = e->path();
+ if (strcmp(p, "/antietam/boonsboro") == 0)
+ {
+ found_boonsboro = true;
+ }
+ else if (strcmp(p, "/sharpsburg") == 0)
+ {
+ found_sharpsburg = true;
+ }
+ else if (strcmp(p, "/harpers_ferry") == 0)
+ {
+ found_harpers_ferry = true;
+ }
+ else
+ {
+ CU_ASSERT(0);
+ }
+ }
+ CU_ASSERT(found_boonsboro && found_sharpsburg && found_harpers_ferry);
+ apol_vector_destroy(&entries);
+
+ q->role(NULL);
+ q->type(NULL, false);
+ q->objectClass(QPOL_CLASS_LNK_FILE);
+ entries = fc->runQuery(q);
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 6); // 2 link files, 4 entries without explicit class
+ 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));
+ uint32_t objclass = e->objectClass();
+ CU_ASSERT(objclass == QPOL_CLASS_LNK_FILE || objclass == QPOL_CLASS_ALL);
+ }
+ apol_vector_destroy(&entries);
+
+ q->objectClass("file");
+ entries = fc->runQuery(q);
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 8); // 4 files, 4 entries without explicit class
+ size_t num_files = 0, num_alls = 0;
+ 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));
+ uint32_t objclass = e->objectClass();
+ if (objclass == QPOL_CLASS_FILE)
+ {
+ num_files++;
+ }
+ else if (objclass == QPOL_CLASS_ALL)
+ {
+ num_alls++;
+ }
+ else
+ {
+ CU_ASSERT(0);
+ }
+ }
+ CU_ASSERT(num_files == 4 && num_alls == 4);
+ apol_vector_destroy(&entries);
+
+ // setting any of these should have no effect upon results
+ q->range("s0", APOL_QUERY_EXACT);
+ q->inode(42);
+ q->dev("first_maryland_campaign");
+ entries = fc->runQuery(q);
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 8);
+ num_files = 0, num_alls = 0;
+ 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));
+ uint32_t objclass = e->objectClass();
+ if (objclass == QPOL_CLASS_FILE)
+ {
+ num_files++;
+ }
+ else if (objclass == QPOL_CLASS_ALL)
+ {
+ num_alls++;
+ }
+ else
+ {
+ CU_ASSERT(0);
+ }
+ }
+ CU_ASSERT(num_files == 4 && num_alls == 4);
+ apol_vector_destroy(&entries);
+
+ q->objectClass((const char *)NULL);
+ q->path("/sharpsburg/main_street");
+ entries = fc->runQuery(q);
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 0);
+ apol_vector_destroy(&entries);
+
+ q->path("/sharpsburg/east_woods/hooker");
+ entries = fc->runQuery(q);
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 2);
+ bool found_east_woods = false, found_hooker = false;
+ 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));
+ const char *path = e->path();
+ if (strcmp(path, "/sharpsburg/east_woods(/.*)?") == 0)
+ {
+ found_east_woods = true;
+ }
+ else if (strcmp(path, "/sharpsburg/east_woods/hooker") == 0)
+ {
+ found_hooker = true;
+ }
+ else
+ {
+ CU_ASSERT(0);
+ }
+ }
+ CU_ASSERT(found_east_woods && found_hooker);
+ apol_vector_destroy(&entries);
+
+ q->path(NULL);
+ q->user("hill");
+ q->regex(true);
+ entries = fc->runQuery(q);
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 2);
+ apol_vector_destroy(&entries);
+
+ q->user("hilly");
+ entries = fc->runQuery(q);
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 0);
+ apol_vector_destroy(&entries);
+
+ q->user(NULL);
+ q->path("/sharpsburg/dunker");
+ entries = fc->runQuery(q);
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 1);
+ 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));
+ const apol_context_t *con = e->context();
+ CU_ASSERT_PTR_NOT_NULL(con);
+ CU_ASSERT_STRING_EQUAL(apol_context_get_user(con), "");
+ CU_ASSERT_STRING_EQUAL(apol_context_get_role(con), "");
+ CU_ASSERT_STRING_EQUAL(apol_context_get_type(con), "");
+ }
+ apol_vector_destroy(&entries);
+
+ delete q;
+ delete fc;
+}
+
+// alse test: load MLS in non-MLS; load non-MLS in MLS; MLS in MLS
+
+CU_TestInfo fcfile_tests[] = {
+ {"fcfile opening files", fcfile_open}
+ ,
+ {"fcfile queries", fcfile_query}
+ ,
+ CU_TEST_INFO_NULL
+};
+
+int fcfile_init()
+{
+ return 0;
+}
+
+int fcfile_cleanup()
+{
+ return 0;
+}
diff --git a/libsefs/tests/fcfile-tests.hh b/libsefs/tests/fcfile-tests.hh
new file mode 100644
index 0000000..e3aa8ed
--- /dev/null
+++ b/libsefs/tests/fcfile-tests.hh
@@ -0,0 +1,35 @@
+/**
+ * @file
+ *
+ * Declarations for libsefs file_contexts file (fcfile) tests.
+ *
+ * @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 FCFILE_TESTS_H
+#define FCFILE_TESTS_H
+
+#include <CUnit/CUnit.h>
+
+extern CU_TestInfo fcfile_tests[];
+extern int fcfile_init();
+extern int fcfile_cleanup();
+
+#endif
diff --git a/libsefs/tests/file_contexts.broken b/libsefs/tests/file_contexts.broken
new file mode 100644
index 0000000..b74dd72
--- /dev/null
+++ b/libsefs/tests/file_contexts.broken
@@ -0,0 +1,3 @@
+# An intentionally broken file_contexts file.
+
+/antietam/burnside -? burnside_u:union_r:general_t
diff --git a/libsefs/tests/file_contexts.confed b/libsefs/tests/file_contexts.confed
new file mode 100644
index 0000000..01d48a1
--- /dev/null
+++ b/libsefs/tests/file_contexts.confed
@@ -0,0 +1,24 @@
+# Confederate placement as of morning of September 17, 1861.
+
+/ -d maryland_u:location_r:state_t
+/sharpsburg -d sharpsburg_u:location_r:city_t
+/sharpsburg/lee -l lee_u:confed_r:general_t
+/sharpsburg/nicodemus -d sharpsburg_u:location_r:terrain_t
+/sharpsburg/nicodemus/jackson(/.*)? -- jackson_u:confed_r:infantry_t
+/sharpsburg/nicodemus/stuart -- stuart_u:confed_r:artillery_t
+/sharpsburg/west_woods -d sharpsburg_u:location_r:terrain_t
+/sharpsburg/west_woods/jackson(/.*)? -c jackson_u:confed_r:infantry_t
+/sharpsburg/west_woods/jones -c jones_u:confed_r:infantry_t
+/sharpsburg/west_woods/lee -c lee_u:confed_r:artillery_t
+/sharpsburg/dunker <<none>>
+/sharpsburg/hill -p hill_u:confed_r:infantry_t
+/antietam -d sharpsburg_u:location_r:terrain_t
+/antietam/cemetary_hill -d sharpsburg_u:location_r:terrain_t
+/antietam/cemetary_hill/artillery_ridge -d sharpsburg_u:location_r:terrain_t
+/antietam/cemetary_hill/artillery_ridge/anderson(/.*)? -s anderson_u:confed_r:infantry_t
+/antietam/cemetary_hill/artillery_ridge/jones(/.*)? -s jones_u:confed_r:infantry_t
+/antietam/cemetary_hill/artillery_ridge/walker(/.*)? -s walker_u:confed_r:infantry_t
+/antietam/cemetary_hill/artillery_ridge/toombs(/.*)? -s toombs_u:confed_r:artillery_t
+
+/harpers_ferry virginia_u:location_r:city_t
+/harpers_ferry/.* hill_u:confed_r:infantry_t
diff --git a/libsefs/tests/file_contexts.union b/libsefs/tests/file_contexts.union
new file mode 100644
index 0000000..4e93e9f
--- /dev/null
+++ b/libsefs/tests/file_contexts.union
@@ -0,0 +1,16 @@
+# Union forces as of morning of September 17, 1861.
+
+/antietam/mcclellan -l mcclellan_u:union_r:general_t
+/antietam/boonsboro -- maryland_u:location_r:city_t
+/sharpsburg/north_woods -d sharpsburg_u:location_r:terrain_t
+/sharpsburg/north_woods/doubleday -b doubleday_u:union_r:infantry_t
+/sharpsburg/north_woods/meade.* -b meade_u:union_r:artillery_t
+/sharpsburg/north_woods/ricketts -b ricketts_u:union_r:cavalry_t
+/sharpsburg/east_woods(/.*)? sharpsburg_u:location_r:terrain_t
+/sharpsburg/east_woods/hooker -p hooker_u:union_r:infantry_t
+/sharpsburg/east_woods/mansfield -p mansfield_u:union_r:artillery_t
+/sharpsburg/east_woods/sedgwick -p sedgwick_u:union_r:infantry_t
+/sharpsburg/corn_field -- <<none>>
+/antietam/middle_bridge -b <<none>>
+/antietam/lower_bridge -b <<none>>
+/antietam/snavely -p <<none>>
diff --git a/libsefs/tests/libsefs-tests.cc b/libsefs/tests/libsefs-tests.cc
new file mode 100644
index 0000000..9104d41
--- /dev/null
+++ b/libsefs/tests/libsefs-tests.cc
@@ -0,0 +1,52 @@
+/**
+ * @file
+ *
+ * CUnit testing framework for 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
+ */
+
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <CUnit/Basic.h>
+
+#include "fcfile-tests.hh"
+
+int main(void)
+{
+ if (CU_initialize_registry() != CUE_SUCCESS)
+ {
+ return CU_get_error();
+ }
+
+ CU_SuiteInfo suites[] = {
+ {"fcfile", fcfile_init, fcfile_cleanup, fcfile_tests}
+ ,
+ CU_SUITE_INFO_NULL
+ };
+
+ CU_register_suites(suites);
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ unsigned int num_failures = CU_get_number_of_failure_records();
+ CU_cleanup_registry();
+ return (int)num_failures;
+}