From 47be9ff57e72906660bb62a515222f482131e1fb Mon Sep 17 00:00:00 2001 From: Miroslav Grepl Date: Fri, 11 Apr 2014 09:37:53 +0200 Subject: Create setools-3.3.7 git repo --- libsefs/Makefile.am | 8 + libsefs/include/Makefile.am | 1 + libsefs/include/sefs/Makefile.am | 10 + libsefs/include/sefs/db.hh | 213 +++++ libsefs/include/sefs/entry.hh | 222 +++++ libsefs/include/sefs/fcfile.hh | 261 ++++++ libsefs/include/sefs/fclist.hh | 323 +++++++ libsefs/include/sefs/filesystem.hh | 181 ++++ libsefs/include/sefs/query.hh | 329 +++++++ libsefs/include/sefs/util.h | 56 ++ libsefs/src/Makefile.am | 52 ++ libsefs/src/db.cc | 1304 +++++++++++++++++++++++++++ libsefs/src/entry.cc | 213 +++++ libsefs/src/fcfile.cc | 691 ++++++++++++++ libsefs/src/fclist.cc | 766 ++++++++++++++++ libsefs/src/filesystem.cc | 733 +++++++++++++++ libsefs/src/libsefs.map | 29 + libsefs/src/new_ftw.c | 749 +++++++++++++++ libsefs/src/new_ftw.h | 183 ++++ libsefs/src/query.cc | 431 +++++++++ libsefs/src/sefs_internal.hh | 78 ++ libsefs/src/util.c | 46 + libsefs/swig/Makefile.am | 15 + libsefs/swig/java/MANIFEST.MF.in | 14 + libsefs/swig/java/Makefile.am | 85 ++ libsefs/swig/python/Makefile.am | 39 + libsefs/swig/sefs.i | 162 ++++ libsefs/swig/tcl/Makefile.am | 37 + libsefs/tests/Makefile.am | 17 + libsefs/tests/attic/fuse_non_mls.c | 195 ++++ libsefs/tests/attic/launch-libsefs-tests.sh | 12 + libsefs/tests/fcfile-tests.cc | 333 +++++++ libsefs/tests/fcfile-tests.hh | 35 + libsefs/tests/file_contexts.broken | 3 + libsefs/tests/file_contexts.confed | 24 + libsefs/tests/file_contexts.union | 16 + libsefs/tests/libsefs-tests.cc | 52 ++ 37 files changed, 7918 insertions(+) create mode 100644 libsefs/Makefile.am create mode 100644 libsefs/include/Makefile.am create mode 100644 libsefs/include/sefs/Makefile.am create mode 100644 libsefs/include/sefs/db.hh create mode 100644 libsefs/include/sefs/entry.hh create mode 100644 libsefs/include/sefs/fcfile.hh create mode 100644 libsefs/include/sefs/fclist.hh create mode 100644 libsefs/include/sefs/filesystem.hh create mode 100644 libsefs/include/sefs/query.hh create mode 100644 libsefs/include/sefs/util.h create mode 100644 libsefs/src/Makefile.am create mode 100644 libsefs/src/db.cc create mode 100644 libsefs/src/entry.cc create mode 100644 libsefs/src/fcfile.cc create mode 100644 libsefs/src/fclist.cc create mode 100644 libsefs/src/filesystem.cc create mode 100644 libsefs/src/libsefs.map create mode 100644 libsefs/src/new_ftw.c create mode 100644 libsefs/src/new_ftw.h create mode 100644 libsefs/src/query.cc create mode 100644 libsefs/src/sefs_internal.hh create mode 100644 libsefs/src/util.c create mode 100644 libsefs/swig/Makefile.am create mode 100644 libsefs/swig/java/MANIFEST.MF.in create mode 100644 libsefs/swig/java/Makefile.am create mode 100644 libsefs/swig/python/Makefile.am create mode 100644 libsefs/swig/sefs.i create mode 100644 libsefs/swig/tcl/Makefile.am create mode 100644 libsefs/tests/Makefile.am create mode 100644 libsefs/tests/attic/fuse_non_mls.c create mode 100755 libsefs/tests/attic/launch-libsefs-tests.sh create mode 100644 libsefs/tests/fcfile-tests.cc create mode 100644 libsefs/tests/fcfile-tests.hh create mode 100644 libsefs/tests/file_contexts.broken create mode 100644 libsefs/tests/file_contexts.confed create mode 100644 libsefs/tests/file_contexts.union create mode 100644 libsefs/tests/libsefs-tests.cc (limited to 'libsefs') 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 +#include + +#ifdef __cplusplus +extern "C" +{ +#endif +#include +#include +#include + +#ifdef __cplusplus +} + +#include + +/** + * 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 +#include +#include + +#ifdef __cplusplus +} + +#include + +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 <<none>> + * 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 + * . 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 + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +#include + +#ifdef __cplusplus +} + +#include + +/** + * 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 +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +#ifndef __cplusplus +#include +#endif + +#include + +#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 + +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. + *

+ * This function must not throw any exceptions. 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 + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +#ifdef __cplusplus +} + +#include + +/** + * 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. + * Be aware that the constructor is not thread-safe. + * @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 +#include + +#ifndef __cplusplus +#include +#endif + +#include +#include +#include +#include + +#ifdef __cplusplus +} + +#include + +/** + * 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. + * + * Note: If the query is run against a fcfile, then + * entries without explicit object classes (i.e., no explicit + * --, -d, 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 + * . 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. + * + * Note: 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 + +#include "sefs_internal.hh" + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#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, '<>');" // 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 + +#include "sefs_internal.hh" + +#include +#include +#include + +#include +#include + +/******************** 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 + +#include "sefs_internal.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/******************** public functions below ********************/ + +static void fcfile_entry_free(void *elem) +{ + if (elem != NULL) + { + sefs_entry *entry = static_cast < sefs_entry * >(elem); + delete entry; + } +} + +sefs_fcfile::sefs_fcfile(sefs_callback_fn_t msg_callback, void *varg) throw(std::bad_alloc):sefs_fclist(SEFS_FCLIST_TYPE_FCFILE, + msg_callback, varg) +{ + _files = _entries = NULL; + _mls_set = false; + try + { + if ((_files = apol_vector_create(free)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::bad_alloc(); + } + if ((_entries = apol_vector_create(fcfile_entry_free)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::bad_alloc(); + } + } + catch(...) + { + apol_vector_destroy(&_files); + apol_vector_destroy(&_entries); + throw; + } +} + +sefs_fcfile::sefs_fcfile(const char *file, sefs_callback_fn_t msg_callback, void *varg) throw(std::bad_alloc, std::invalid_argument, + std:: + runtime_error):sefs_fclist + (SEFS_FCLIST_TYPE_FCFILE, msg_callback, varg) +{ + _files = _entries = NULL; + _mls_set = false; + try + { + if ((_files = apol_vector_create_with_capacity(1, free)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::bad_alloc(); + } + if ((_entries = apol_vector_create(fcfile_entry_free)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::bad_alloc(); + } + if (appendFile(file) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error("Could not construct fcfile with the given file."); + } + } + catch(...) + { + apol_vector_destroy(&_files); + apol_vector_destroy(&_entries); + throw; + } +} + +sefs_fcfile::sefs_fcfile(const apol_vector_t * files, sefs_callback_fn_t msg_callback, void *varg) throw(std::bad_alloc, + std::invalid_argument, + std:: + runtime_error):sefs_fclist + (SEFS_FCLIST_TYPE_FCFILE, msg_callback, varg) +{ + _files = _entries = NULL; + _mls_set = false; + try + { + if (files == NULL) + { + SEFS_ERR(this, "%s", strerror(EINVAL)); + errno = EINVAL; + throw std::invalid_argument(strerror(EINVAL)); + } + if ((_files = apol_vector_create_with_capacity(apol_vector_get_size(files), free)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::bad_alloc(); + } + if ((_entries = apol_vector_create(fcfile_entry_free)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::bad_alloc(); + } + if (appendFileList(files) != apol_vector_get_size(files)) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error("Could not construct fcfile with the given vector."); + } + } + catch(...) + { + apol_vector_destroy(&_files); + apol_vector_destroy(&_entries); + throw; + } +} + +sefs_fcfile::~sefs_fcfile() +{ + apol_vector_destroy(&_files); + apol_vector_destroy(&_entries); +} + +int sefs_fcfile::runQueryMap(sefs_query * query, sefs_fclist_map_fn_t fn, void *data) throw(std::runtime_error, + std::invalid_argument) +{ + apol_vector_t *type_list = NULL; + apol_mls_range_t *range = NULL; + int retval = 0; + try + { + if (query != NULL) + { + query->compile(); + if (policy != NULL) + { + if (query->_type != NULL && query->_indirect && + (type_list = + query_create_candidate_type(policy, query->_type, query->_retype, query->_regex, + query->_indirect)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (query->_range != NULL && query->_rangeMatch != 0 && + (range = apol_mls_range_create_from_string(policy, query->_range)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + } + } + + for (size_t i = 0; i < apol_vector_get_size(_entries); i++) + { + sefs_entry *e = static_cast < sefs_entry * >(apol_vector_get_element(_entries, i)); + if (query != NULL) + { + const struct sefs_context_node *context = e->_context; + if (!query_str_compare(context->user, query->_user, query->_reuser, query->_regex)) + { + continue; + } + if (!query_str_compare(context->role, query->_role, query->_rerole, query->_regex)) + { + continue; + } + + bool str_matched = false, pol_matched = false; + str_matched = query_str_compare(context->type, query->_type, query->_retype, query->_regex); + if (type_list != NULL && !str_matched) + { + size_t index; + pol_matched = + (apol_vector_get_index(type_list, context->type, apol_str_strcmp, NULL, &index) < + 0); + } + if (!str_matched && !pol_matched) + { + continue; + } + + if (isMLS()) + { + if (range == NULL) + { + if (!query_str_compare + (context->range, query->_range, query->_rerange, query->_regex)) + { + continue; + } + } + else + { + const apol_mls_range_t *context_range = apol_context_get_range(context->context); + int ret; + ret = apol_mls_range_compare(policy, context_range, range, query->_rangeMatch); + if (ret <= 0) + { + continue; + } + } + } + + if (e->_objectClass != QPOL_CLASS_ALL && query->_objclass != QPOL_CLASS_ALL && + e->_objectClass != query->_objclass) + { + continue; + } + + bool path_matched; + + if (query->_path == NULL || query->_path[0] == '\0') + { + path_matched = true; + } + else + { + path_matched = false; + char *anchored_path = NULL; + if (asprintf(&anchored_path, "^%s$", e->_path) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + + regex_t regex; + if (regcomp(®ex, anchored_path, REG_EXTENDED | REG_NOSUB) != 0) + { + free(anchored_path); + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + + bool compval = query_str_compare(query->_path, anchored_path, ®ex, true); + free(anchored_path); + regfree(®ex); + if (compval) + { + path_matched = true; + } + } + if (!path_matched) + { + continue; + } + } + + // if reached this point, then all criteria passed, so + // invoke the mapping function + + if ((retval = fn(this, e, data)) < 0) + { + return retval; + } + } + } + catch(...) + { + apol_vector_destroy(&type_list); + apol_mls_range_destroy(&range); + throw; + } + apol_vector_destroy(&type_list); + return retval; +} + +bool sefs_fcfile::isMLS() const +{ + if (_mls_set) + { + return _mls; + } + return false; +} + +int sefs_fcfile::appendFile(const char *file) throw(std::bad_alloc, std::invalid_argument, std::runtime_error) +{ + FILE *fc_file = NULL; + char *line = NULL, *name_dup = NULL; + size_t line_len = 0; + size_t last_entry = apol_vector_get_size(_entries); + int retval, error = 0; + + regex_t line_regex, context_regex; + bool is_line_compiled = false; + bool is_context_compiled = false; + + try + { + if (file == NULL) + { + errno = EINVAL; + SEFS_ERR(this, "%s", strerror(EINVAL)); + throw std::invalid_argument(strerror(EINVAL)); + } + + fc_file = fopen(file, "r"); + if (!fc_file) + { + SEFS_ERR(this, "Unable to open file %s", file); + throw std::runtime_error(strerror(error)); + } + + if ((name_dup = strdup(file)) == NULL) + { + SEFS_ERR(this, "%s", strerror(error)); + throw std::bad_alloc(); + } + + if (regcomp(&line_regex, "^([^[:blank:]]+)[[:blank:]]+(-.[[:blank:]]+)?([^-].+)$", REG_EXTENDED) != 0) + { + SEFS_ERR(this, "%s", strerror(error)); + throw std::bad_alloc(); + } + is_line_compiled = true; + + if (regcomp(&context_regex, "^([^:]+):([^:]+):([^:]+):?(.*)$", REG_EXTENDED) != 0) + { + SEFS_ERR(this, "%s", strerror(error)); + throw std::bad_alloc(); + } + is_context_compiled = true; + + while (!feof(fc_file)) + { + if (getline(&line, &line_len, fc_file) == -1) + { + if (feof(fc_file)) + { + break; + } + else + { + SEFS_ERR(this, "%s", strerror(error)); + throw std::bad_alloc(); + } + } + parse_line(name_dup, line, &line_regex, &context_regex); + } + + if (apol_vector_append(_files, name_dup) < 0) + { + SEFS_ERR(this, "%s", strerror(error)); + throw std::bad_alloc(); + } + name_dup = NULL; + + retval = 0; + } + catch(...) + { + error = errno; + // discard all entries that were read from this file_contexts + size_t i = apol_vector_get_size(_entries); + for (; i > last_entry; i--) + { + sefs_entry *e = static_cast < sefs_entry * >(apol_vector_get_element(_entries, i - 1)); + fcfile_entry_free(e); + apol_vector_remove(_entries, i - 1); + } + retval = -1; + } + + if (fc_file != NULL) + { + fclose(fc_file); + } + if (is_line_compiled) + { + regfree(&line_regex); + } + if (is_context_compiled) + { + regfree(&context_regex); + } + free(name_dup); + free(line); + errno = error; + return retval; +} + +size_t sefs_fcfile::appendFileList(const apol_vector_t * files)throw(std::bad_alloc, std::invalid_argument, std::runtime_error) +{ + size_t i; + if (files == NULL) + { + SEFS_ERR(this, "%s", strerror(EINVAL)); + errno = EINVAL; + throw new std::invalid_argument(strerror(EINVAL)); + } + for (i = 0; i < apol_vector_get_size(files); i++) + { + if (appendFile(static_cast < char *>(apol_vector_get_element(files, i))) < 0) + { + return i; + } + } + return i; +} + +const apol_vector_t *sefs_fcfile::fileList() const +{ + return _files; +} + +/******************** private functions below ********************/ + +void sefs_fcfile::parse_line(const char *origin, const char *line, regex_t * line_regex, + regex_t * context_regex) throw(std::bad_alloc, std::runtime_error) +{ + int error = 0; + + char *s = strdup(line); + char *path; + + if (s == NULL) + { + error = errno; + SEFS_ERR(this, "%s", strerror(error)); + throw std::bad_alloc(); + } + + apol_str_trim(s); + if (s[0] == '#' || s[0] == '\0') + { + free(s); + return; + } + + try + { + const size_t nmatch = 5; + regmatch_t pmatch[nmatch]; + + if (regexec(line_regex, s, nmatch, pmatch, 0) != 0) + { + error = EIO; + SEFS_ERR(this, "fcfile line is not legal:\n%s", s); + throw std::runtime_error(strerror(error)); + } + + assert(pmatch[1].rm_so == 0); + s[pmatch[1].rm_eo] = '\0'; + if ((path = strdup(s)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(error)); + } + if (apol_bst_insert_and_get(path_tree, (void **)&path, NULL) < 0) + { + free(path); + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(error)); + } + + uint32_t objclass; + if (pmatch[2].rm_so != -1) + { + switch (s[pmatch[2].rm_so + 1]) + { + case '-': + objclass = QPOL_CLASS_FILE; + break; + case 'd': + objclass = QPOL_CLASS_DIR; + break; + case 'c': + objclass = QPOL_CLASS_CHR_FILE; + break; + case 'b': + objclass = QPOL_CLASS_BLK_FILE; + break; + case 'p': + objclass = QPOL_CLASS_FIFO_FILE; + break; + case 'l': + objclass = QPOL_CLASS_LNK_FILE; + break; + case 's': + objclass = QPOL_CLASS_SOCK_FILE; + break; + default: + error = EIO; + SEFS_ERR(this, "%s", "Invalid file context object class."); + throw std::runtime_error(strerror(error)); + } + } + else + { + // no object class explicitly given + objclass = QPOL_CLASS_ALL; + } + + assert(pmatch[3].rm_so != -1); + char *context_str = s + pmatch[3].rm_so; + char *user, *role, *type, *range; + + if (strcmp(context_str, "<>") == 0) + { + user = role = type = range = ""; + } + else + { + if (regexec(context_regex, context_str, nmatch, pmatch, 0) != 0) + { + error = EIO; + SEFS_ERR(this, "fcfile context is not legal:\n%s", context_str); + throw std::runtime_error(strerror(error)); + } + + assert(pmatch[1].rm_so == 0); + context_str[pmatch[1].rm_eo] = '\0'; + user = context_str; + + assert(pmatch[2].rm_so != -1); + context_str[pmatch[2].rm_eo] = '\0'; + role = context_str + pmatch[2].rm_so; + + assert(pmatch[3].rm_so != -1); + context_str[pmatch[3].rm_eo] = '\0'; + type = context_str + pmatch[3].rm_so; + + range = NULL; + if (pmatch[4].rm_so != -1) + { + range = context_str + pmatch[4].rm_so; + } + } + if (range != NULL & range[0] != '\0') + { + if (_mls_set && !_mls) + { + error = EIO; + SEFS_ERR(this, "fcfile context is MLS, but fcfile is not:\n%s", context_str); + throw std::runtime_error(strerror(error)); + } + _mls = true; + _mls_set = true; + } + else + { + if (_mls_set && !_mls && strcmp(context_str, "<>") != 0) + { + error = EIO; + SEFS_ERR(this, "fcfile context is not MLS, but fcfile is:\n%s", context_str); + throw std::runtime_error(strerror(error)); + } + _mls = true; + _mls_set = false; + } + struct sefs_context_node *context = getContext(user, role, type, range); + sefs_entry *entry = new sefs_entry(this, context, objclass, path, origin); + + if (apol_vector_append(_entries, static_cast < void *>(entry)) < 0) + { + error = errno; + delete entry; + SEFS_ERR(this, "%s", strerror(error)); + throw std::bad_alloc(); + } + } + + catch(...) + { + free(s); + errno = error; + throw; + } + + free(s); +} + +/******************** C functions below ********************/ + +sefs_fclist_t *sefs_fcfile_create(sefs_callback_fn_t msg_callback, void *varg) +{ + sefs_fclist *fclist; + try + { + fclist = new sefs_fcfile(msg_callback, varg); + } + catch(...) + { + errno = ENOMEM; + return NULL; + } + return fclist; +} + +sefs_fclist_t *sefs_fcfile_create_from_file(const char *file, sefs_callback_fn_t msg_callback, void *varg) +{ + sefs_fclist *fclist; + try + { + fclist = new sefs_fcfile(file, msg_callback, varg); + } + catch(...) + { + errno = ENOMEM; + return NULL; + } + return fclist; +} + +sefs_fclist_t *sefs_fcfile_create_from_file_list(const apol_vector_t * files, sefs_callback_fn_t msg_callback, void *varg) +{ + sefs_fclist *fclist; + try + { + fclist = new sefs_fcfile(files, msg_callback, varg); + } + catch(...) + { + errno = ENOMEM; + return NULL; + } + return fclist; +} + +int sefs_fcfile_append_file(sefs_fcfile_t * fcfile, const char *file) +{ + if (fcfile == NULL) + { + SEFS_ERR(fcfile, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + try + { + fcfile->appendFile(file); + } + catch(...) + { + return -1; + } + return 0; +} + +size_t sefs_fcfile_append_file_list(sefs_fcfile_t * fcfile, const apol_vector_t * files) +{ + if (fcfile == NULL) + { + SEFS_ERR(fcfile, "%s", strerror(EINVAL)); + errno = EINVAL; + return 0; + } + return fcfile->appendFileList(files); +} + +const apol_vector_t *sefs_fcfile_get_file_list(const sefs_fcfile_t * fcfile) +{ + if (fcfile == NULL) + { + SEFS_ERR(fcfile, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + return fcfile->fileList(); +} 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 + +#include "sefs_internal.hh" + +#include +#include +#include +#include +#include +#include +#include +#include + +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("<>")) == 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 + +#include "sefs_internal.hh" +#include "new_ftw.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int lgetfilecon_raw(const char *, security_context_t *) __attribute__ ((weak)); + +/** + * As that setools must work with older libselinux versions that may + * not have the _raw() functions, declare them as weak. If libselinux + * does indeed have the new functions then use them; otherwise + * fallback to the originals. + */ +static int filesystem_lgetfilecon(const char *path, security_context_t * context) +{ + if (lgetfilecon_raw != NULL) + { + return lgetfilecon_raw(path, context); + } + else + { + return lgetfilecon(path, context); + } +} + +#if 0 + +/** + * Given a directory, find all bounded mounted filesystems within that + * directory (or subdirectory within.) This function consults the + * entries written to /etc/mtab to determine if something is mounted + * or not and if it has the "bind" option. Note that if \a dir itself + * is a mount, it will not be reported; a subdirectory might. + * + * Note that the returned vector in never actually used by this + * library. This function existed in previous versions of libsefs, + * but was never documented why it existed. Rather than eliminate + * this function, it is retained (but effectively unused), in case a + * future revision of libsefs necessitates finding bind mounts. + * + * @param dir Directory to begin search. + * + * @return An allocated vector containing pathnames (type char *) to + * each mounted location with the "bind" option. The caller is + * responsible for calling apol_vector_destroy() afterwards. + */ +static apol_vector_t *filesystem_find_mount_points(const char *dir) throw(std::bad_alloc, std::runtime_error) +{ + char *dirdup = NULL; + apol_vector_t *v = NULL; + FILE *mtab = NULL; + struct mntent *entry; + + try + { + if ((dirdup = strdup(dir)) == NULL) + { + throw std::bad_alloc(); + } + size_t len = strlen(dirdup); + if (len > 1 && dirdup[len - 1] == '/') + { + dirdup[len - 1] = '\0'; + } + if ((v = apol_vector_create(free)) == NULL) + { + throw std::bad_alloc(); + } + if ((mtab = fopen("/etc/mtab", "r")) == NULL) + { + throw std::runtime_error(strerror(errno)); + } + // note non thread-safeness below + while ((entry = getmntent(mtab)) != NULL) + { + if (strstr(entry->mnt_dir, dir) != entry->mnt_dir) + { + continue; + } + if (strcmp(entry->mnt_dir, dirdup) == 0) + { + continue; + } + if (strstr(entry->mnt_opts, "bind") != NULL) + { + char *s = strdup(entry->mnt_dir); + if (s == NULL) + { + throw std::bad_alloc(); + } + if (apol_vector_append(v, s) < 0) + { + free(s); + throw std::bad_alloc(); + } + } + } + + } + catch(...) + { + free(dirdup); + apol_vector_destroy(&v); + if (mtab != NULL) + { + fclose(mtab); + } + throw; + } + free(dirdup); + fclose(mtab); + return v; +} + +#endif + +/******************** public functions below ********************/ + +sefs_filesystem::sefs_filesystem(const char *new_root, sefs_callback_fn_t msg_callback, void *varg)throw(std::bad_alloc, std::invalid_argument, std::runtime_error):sefs_fclist(SEFS_FCLIST_TYPE_FILESYSTEM, + msg_callback, + varg) +{ + if (new_root == NULL) + { + SEFS_ERR(this, "%s", strerror(EINVAL)); + errno = EINVAL; + throw std::invalid_argument(strerror(EINVAL)); + } + _root = NULL; + _mls = false; + try + { + // check that root exists and is readable + struct stat64 sb; + if (stat64(new_root, &sb) != 0 && !S_ISDIR(sb.st_mode)) + { + SEFS_ERR(this, "%s", strerror(EINVAL)); + errno = EINVAL; + throw std::invalid_argument(strerror(EINVAL)); + } + + // determine if filesystem is MLS or not + security_context_t scon; + if (filesystem_lgetfilecon(new_root, &scon) < 0) + { + SEFS_ERR(this, "Could not read SELinux file context for %s.", new_root); + throw std::runtime_error(strerror(errno)); + } + context_t con; + if ((con = context_new(scon)) == 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + freecon(scon); + throw std::runtime_error(strerror(errno)); + } + freecon(scon); + const char *range = context_range_get(con); + if (range != NULL && range[0] != '\0') + { + _mls = true; + } + context_free(con); + + if ((_root = strdup(new_root)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::bad_alloc(); + } + } + catch(...) + { + free(_root); + throw; + } +} + +sefs_filesystem::~sefs_filesystem() +{ + free(_root); +} + +struct filesystem_ftw_struct +{ + sefs_filesystem *fs; + sefs_query *query; + apol_vector_t *dev_map; //< vector of filesystem_dev entries + apol_vector_t *type_list; + apol_mls_range_t *range; + sefs_fclist_map_fn_t fn; + void *data; + bool aborted; + int retval; +}; + +// wrapper functions to go between non-OO land into OO member functions + +inline struct sefs_context_node *filesystem_get_context(sefs_filesystem * fs, security_context_t scon) throw(std::bad_alloc) +{ + return fs->getContext(scon); +} + +inline sefs_entry *filesystem_get_entry(sefs_filesystem * fs, const struct sefs_context_node * node, uint32_t objClass, + const char *path, ino64_t ino, const char *dev_name)throw(std::bad_alloc) +{ + return fs->getEntry(node, objClass, path, ino, dev_name); +} + +inline bool filesystem_is_query_match(sefs_filesystem * fs, const sefs_query * query, const char *path, const char *dev, + const struct stat64 * sb, apol_vector_t * type_list, + apol_mls_range_t * range)throw(std::runtime_error) +{ + return fs->isQueryMatch(query, path, dev, sb, type_list, range); +} + +static uint32_t filesystem_stat_to_objclass(const struct stat64 *sb) +{ + if (S_ISREG(sb->st_mode)) + { + return QPOL_CLASS_FILE; + } + if (S_ISDIR(sb->st_mode)) + { + return QPOL_CLASS_DIR; + } + if (S_ISCHR(sb->st_mode)) + { + return QPOL_CLASS_CHR_FILE; + } + if (S_ISBLK(sb->st_mode)) + { + return QPOL_CLASS_BLK_FILE; + } + if (S_ISFIFO(sb->st_mode)) + { + return QPOL_CLASS_FIFO_FILE; + } + if (S_ISLNK(sb->st_mode)) + { + return QPOL_CLASS_LNK_FILE; + } + if (S_ISSOCK(sb->st_mode)) + { + return QPOL_CLASS_SOCK_FILE; + } + assert(0); // should never get here + return 0; +} + +struct filesystem_dev +{ + dev_t dev; + char *dev_name; //< pointer into the dev_tree +}; + +static int filesystem_dev_cmp(const void *a, const void *b __attribute__ ((unused)), void *arg) +{ + const struct filesystem_dev *d1 = static_cast < const struct filesystem_dev *>(a); + dev_t *d2 = static_cast < dev_t * >(arg); + if (d1->dev < *d2) + { + return -1; + } + else if (d1->dev > *d2) + { + return 1; + } + return 0; +} + +static int filesystem_ftw_handler(const char *fpath, const struct stat64 *sb, int typeflag + __attribute__ ((unused)), struct FTW *ftwbuf __attribute__ ((unused)), void *data) +{ + struct filesystem_ftw_struct *s = static_cast < struct filesystem_ftw_struct *>(data); + + size_t i; + void *dev_num = const_cast < void *>(static_cast < const void *>(&(sb->st_dev))); + int rc = apol_vector_get_index(s->dev_map, NULL, filesystem_dev_cmp, dev_num, &i); + const char *dev = ""; + if (rc == 0) + { + // if the device number was discovered in buildDevMap + // then store the device name within the entry + struct filesystem_dev *d = static_cast < struct filesystem_dev *>(apol_vector_get_element(s->dev_map, i)); + dev = d->dev_name; + } + else + { + SEFS_WARN(s->fs, "Unknown device for %s.", fpath); + } + try + { + if (!filesystem_is_query_match(s->fs, s->query, fpath, dev, sb, s->type_list, s->range)) + { + return 0; + } + } + catch(...) + { + return -1; + } + + security_context_t scon; + if (filesystem_lgetfilecon(fpath, &scon) < 0) + { + SEFS_ERR(s->fs, "Could not read SELinux file context for %s.", fpath); + return -1; + } + struct sefs_context_node *node = NULL; + try + { + node = filesystem_get_context(s->fs, scon); + } + catch(...) + { + freecon(scon); + return -1; + } + freecon(scon); + + uint32_t objClass = filesystem_stat_to_objclass(sb); + + sefs_entry *entry = NULL; + try + { + entry = filesystem_get_entry(s->fs, node, objClass, fpath, sb->st_ino, dev); + } + catch(...) + { + return -1; + } + + // invoke real callback (not just the nftw handler) + s->retval = s->fn(s->fs, entry, s->data); + delete entry; + if (s->retval < 0) + { + s->aborted = true; + return s->retval; + } + + return 0; +} + +int sefs_filesystem::runQueryMap(sefs_query * query, sefs_fclist_map_fn_t fn, void *data) throw(std::runtime_error, + std::invalid_argument) +{ + struct filesystem_ftw_struct s; + s.dev_map = NULL; + s.type_list = NULL; + s.range = NULL; + try + { + s.dev_map = buildDevMap(); + if (query != NULL) + { + query->compile(); + if (policy != NULL) + { + if (query->_type != NULL && query->_indirect && + (s.type_list = + query_create_candidate_type(policy, query->_type, query->_retype, query->_regex, + query->_indirect)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (query->_range != NULL && query->_rangeMatch != 0 && + (s.range = apol_mls_range_create_from_string(policy, query->_range)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + } + } + } + catch(...) + { + apol_vector_destroy(&s.dev_map); + apol_vector_destroy(&s.type_list); + apol_mls_range_destroy(&s.range); + throw; + } + s.fs = this; + s.query = query; + s.fn = fn; + s.data = data; + s.aborted = false; + s.retval = 0; + + int retval = new_nftw64(_root, filesystem_ftw_handler, 1024, 0, &s); + apol_vector_destroy(&s.dev_map); + apol_vector_destroy(&s.type_list); + apol_mls_range_destroy(&s.range); + if (retval != 0 && !s.aborted) + { + // error was generated by new_nftw64() itself, not + // from callback + return retval; + } + return s.retval; +} + +bool sefs_filesystem::isMLS() const +{ + return _mls; +} + +const char *sefs_filesystem::root() const +{ + return _root; +} + +/******************** private functions below ********************/ + +static void filesystem_dev_free(void *elem) +{ + if (elem != NULL) + { + struct filesystem_dev *d = static_cast < struct filesystem_dev *>(elem); + // don't free the device name pointer, because it's pointing + // into the dev_tree BST + free(d); + } +} + +const char *sefs_filesystem::getDevName(const dev_t dev) throw(std::runtime_error) +{ + apol_vector_t *dev_map = buildDevMap(); + size_t i; + void *devp = const_cast < dev_t * >(&dev); + int rc = apol_vector_get_index(dev_map, NULL, filesystem_dev_cmp, devp, &i); + if (rc < 0) + { + apol_vector_destroy(&dev_map); + return NULL; + } + struct filesystem_dev *d = static_cast < struct filesystem_dev *>(apol_vector_get_element(dev_map, i)); + const char *dev_name = d->dev_name; // this is pointing into this->_dev_tree + apol_vector_destroy(&dev_map); + return dev_name; +} + +/** + * For each entry in /etc/mtab, record the device number and the name + * of the mounted file system. This provides the mapping between a + * device number and its source device. + * + * @return Vector of filesystem_dev entries. The caller must call + * apol_vector_destroy() upon the vector afterwards. + * @exception If error allocating space, unable to open /etc/mtab, or + * unable to parse mtab file. + */ +apol_vector_t *sefs_filesystem::buildDevMap(void)throw(std::runtime_error) +{ + apol_vector_t *dev_map; + if ((dev_map = apol_vector_create(filesystem_dev_free)) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + FILE *f = NULL; + try + { + if ((f = fopen("/etc/mtab", "r")) == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + char buf[256]; + struct mntent mntbuf; + while (getmntent_r(f, &mntbuf, buf, 256) != NULL) + { + struct stat sb; + if (stat(mntbuf.mnt_dir, &sb) == -1) + { + // could not open this device, so skip + // it (and hope it won't be examined + // during runQuery()) + continue; + } + else + { + struct filesystem_dev *d = static_cast < struct filesystem_dev *>(calloc(1, sizeof(*d))); + if (d == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (apol_vector_append(dev_map, d) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + filesystem_dev_free(d); + throw std::runtime_error(strerror(errno)); + } + d->dev = sb.st_dev; + char *mnt_fsname = strdup(mntbuf.mnt_fsname); + if (mnt_fsname == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + if (apol_bst_insert_and_get(dev_tree, (void **)&mnt_fsname, NULL) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + free(mnt_fsname); + throw std::runtime_error(strerror(errno)); + } + d->dev_name = mnt_fsname; + } + } + } + catch(...) + { + apol_vector_destroy(&dev_map); + if (f != NULL) + { + fclose(f); + } + throw; + } + fclose(f); + return dev_map; +} + +bool sefs_filesystem::isQueryMatch(const sefs_query * query, const char *path, const char *dev, const struct stat64 * sb, + apol_vector_t * type_list, apol_mls_range_t * range)throw(std::runtime_error) +{ + if (query == NULL) + { + return true; + } + security_context_t scon; + if (filesystem_lgetfilecon(path, &scon) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::runtime_error(strerror(errno)); + } + context_t con; + if ((con = context_new(scon)) == 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + freecon(scon); + throw std::runtime_error(strerror(errno)); + } + freecon(scon); + + if (!query_str_compare(context_user_get(con), query->_user, query->_reuser, query->_regex)) + { + context_free(con); + return false; + } + if (!query_str_compare(context_role_get(con), query->_role, query->_rerole, query->_regex)) + { + context_free(con); + return false; + } + + bool str_matched = false, pol_matched = false; + str_matched = query_str_compare(context_type_get(con), query->_type, query->_retype, query->_regex); + if (type_list != NULL && !str_matched) + { + size_t index; + pol_matched = (apol_vector_get_index(type_list, context_type_get(con), apol_str_strcmp, NULL, &index) < 0); + } + if (!str_matched && !pol_matched) + { + context_free(con); + return false; + } + + if (isMLS()) + { + if (range == NULL) + { + if (!query_str_compare(context_range_get(con), query->_range, query->_rerange, query->_regex)) + { + context_free(con); + return false; + } + } + else + { + assert(policy != NULL); + apol_mls_range_t *context_range = apol_mls_range_create_from_string(policy, context_range_get(con)); + if (context_range == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + context_free(con); + throw std::runtime_error(strerror(errno)); + } + int ret; + ret = apol_mls_range_compare(policy, range, context_range, query->_rangeMatch); + apol_mls_range_destroy(&context_range); + if (ret <= 0) + { + context_free(con); + return false; + } + } + } + + context_free(con); + + if (query->_objclass != 0 && query->_objclass != filesystem_stat_to_objclass(sb)) + { + return false; + } + + if (!query_str_compare(path, query->_path, query->_repath, query->_regex)) + { + return false; + } + + if (query->_inode != 0 && query->_inode != sb->st_ino) + { + return false; + } + + if (!query_str_compare(dev, query->_dev, query->_redev, query->_regex)) + { + return false; + } + + return true; +} + +sefs_entry *sefs_filesystem::getEntry(const struct sefs_context_node * context, uint32_t objectClass, + const char *path, ino64_t ino, const char *dev_name)throw(std::bad_alloc) +{ + char *s = strdup(path); + if (s == NULL) + { + SEFS_ERR(this, "%s", strerror(errno)); + throw std::bad_alloc(); + } + if (apol_bst_insert_and_get(path_tree, (void **)&s, NULL) < 0) + { + SEFS_ERR(this, "%s", strerror(errno)); + free(s); + throw std::bad_alloc(); + } + sefs_entry *e = new sefs_entry(this, context, objectClass, s); + e->_inode = ino; + e->_dev = dev_name; + return e; +} + +/******************** C functions below ********************/ + +sefs_filesystem_t *sefs_filesystem_create(const char *root, sefs_callback_fn_t msg_callback, void *varg) +{ + sefs_filesystem_t *fs; + try + { + fs = new sefs_filesystem(root, msg_callback, varg); + } + catch(...) + { + errno = ENOMEM; + return NULL; + } + return fs; +} + +const char *sefs_filesystem_get_root(const sefs_filesystem_t * fs) +{ + if (fs == NULL) + { + SEFS_ERR(NULL, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + return fs->root(); +} + +extern const char *sefs_filesystem_get_dev_name(sefs_filesystem_t * fs, const dev_t dev) +{ + if (fs == NULL) + { + SEFS_ERR(NULL, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + const char *dev_name = NULL; + try + { + dev_name = fs->getDevName(dev); + } + catch(...) + { + return NULL; + } + return dev_name; +} 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 , 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 +#endif + +#if __GNUC__ +# define alloca __builtin_alloca +#else +# if HAVE_ALLOCA_H +# include +# else +# ifdef _AIX +# pragma alloca +# else +char *alloca(); +# endif +# endif +#endif + +#if defined _LIBC +# include +# define NAMLEN(dirent) _D_EXACT_NAMLEN (dirent) +#else +# if HAVE_DIRENT_H +# include +# define NAMLEN(dirent) strlen ((dirent)->d_name) +# else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include +# endif +# if HAVE_SYS_DIR_H +# include +# endif +# if HAVE_NDIR_H +# include +# endif +# endif +#endif + +#include +#undef NAMLEN +#define NAMLEN(dirent) strlen ((dirent)->d_name) + +#include +#include "new_ftw.h" +#include +#include +#include +#include +#include +#if HAVE_SYS_PARAM_H || defined _LIBC +# include +#endif +#ifdef _LIBC +# include +#else +# include +#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 + +#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 + +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 + +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 + * significant 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 + +#include +#include + +__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 + +#include "sefs_internal.hh" + +#include +#include +#include + +#include +#include + +/******************** 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 +#include +#include + +/** + * 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 + +#include + +#ifdef LIBSELINUX +#include +#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 +#include +#include +#include +#include +#include +#include +%} + +%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 +%include +%include +%include +%include +%include + +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(v); + } +%} + +%extend sefs_fcfile { + const apol_string_vector_t *fileListStrs() const + { + const apol_vector_t *v = self->fileList(); + return reinterpret_cast(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 + +#define FUSE_USE_VERSION 25 + +#define XATTR_NAME_SELINUX "security.selinux" + +#include +#include + +#include +#include +#include +#include + +/*************** 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 + +#include +#include +#include + +#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 + +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 <> +/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 -- <> +/antietam/middle_bridge -b <> +/antietam/lower_bridge -b <> +/antietam/snavely -p <> 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 + +#include +#include + +#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; +} -- cgit