From ef347bb169dec1c3f5baf2295d3ca84ae4a84351 Mon Sep 17 00:00:00 2001 From: Adrian Szyndela Date: Fri, 1 Mar 2019 13:32:30 +0100 Subject: [PATCH 01/16] refactoring: simplify strings management in XmlParser File name is passed from external API by const char *. This (at least partially) eliminates construction of std::string where it is not needed. Change-Id: I05375820fe2996cb4f95af5be8bbbab5ecd87fc4 --- src/internal/xml_parser.cpp | 28 ++++++++++++---------------- src/internal/xml_parser.hpp | 8 ++++---- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/internal/xml_parser.cpp b/src/internal/xml_parser.cpp index d5815e5..2433e03 100644 --- a/src/internal/xml_parser.cpp +++ b/src/internal/xml_parser.cpp @@ -26,13 +26,9 @@ std::string expandPath(const std::string& parent_dir, const std::string& path) { return parent_dir + "/" + path; } -std::string getDir(std::string path) { - // dirname may modify path, so we must pass a modifiable char* to it - char* modifiable_path = new char[path.size() + 1]; - strncpy(modifiable_path, path.c_str(), path.size() + 1); - std::string ret = dirname(modifiable_path); - delete[] modifiable_path; - return ret; +std::string getDir(const char *path) { + std::string p(path); + return dirname(&p.front()); } class XmlParser::IncludeItem { @@ -83,12 +79,12 @@ void text_handler(void *data, const char *text, int len) { } } -bool XmlParser::isMainConfFile(const std::string& filename) { +bool XmlParser::isMainConfFile(const char *filename) { switch (curr_bus) { case SYSTEM_BUS: - return (filename == SYSTEM_BUS_CONF_FILE_PRIMARY || filename == SYSTEM_BUS_CONF_FILE_SECONDARY); + return strcmp(filename, SYSTEM_BUS_CONF_FILE_PRIMARY) == 0 || strcmp(filename, SYSTEM_BUS_CONF_FILE_SECONDARY) == 0; case SESSION_BUS: - return (filename == SESSION_BUS_CONF_FILE_PRIMARY || filename == SESSION_BUS_CONF_FILE_SECONDARY); + return strcmp(filename, SESSION_BUS_CONF_FILE_PRIMARY) == 0 || strcmp(filename, SESSION_BUS_CONF_FILE_SECONDARY) == 0; } return false; } @@ -180,14 +176,14 @@ void XmlParser::elementEnd(const char *el) { } } -int XmlParser::parsePolicyConfigFile(BusType bus, const std::string& fname) { +int XmlParser::parsePolicyConfigFile(BusType bus, const char *fname) { tslog::log("XmlParser::parsePolicyConfigFile called with filename: ", fname, "\n"); curr_bus = bus; parsePolicyConfigFileInternal(fname); return ret_code; } -void XmlParser::parsePolicyConfigFileInternal(const std::string& filename) { +void XmlParser::parsePolicyConfigFileInternal(const char *filename) { ret_code = 0; try { parseXmlFile(filename); @@ -208,13 +204,13 @@ void XmlParser::parsePolicyConfigFileInternal(const std::string& filename) { } std::vector curr_included_files = included_files; // deep copy for (const auto& included_file : curr_included_files) { - parsePolicyConfigFileInternal(included_file); + parsePolicyConfigFileInternal(included_file.c_str()); } } -std::unique_ptr file2str(const std::string& filename) { +std::unique_ptr file2str(const char *filename) { - FILE *fp = fopen(filename.c_str(), "rb"); + FILE *fp = fopen(filename, "rb"); if (fp == nullptr) { throw std::runtime_error(std::string("Failed to open file: ").append(filename).c_str()); } @@ -240,7 +236,7 @@ std::unique_ptr file2str(const std::string& filename) { return str; } -void XmlParser::parseXmlFile(const std::string& filename) { +void XmlParser::parseXmlFile(const char *filename) { included_files.clear(); tslog::log("Processing: ", filename, " ...\n"); diff --git a/src/internal/xml_parser.hpp b/src/internal/xml_parser.hpp index 8ede445..a47b963 100644 --- a/src/internal/xml_parser.hpp +++ b/src/internal/xml_parser.hpp @@ -33,7 +33,7 @@ namespace ldp_xml_parser { public: /** Parses given config file for declared bus type */ - int parsePolicyConfigFile(BusType bus_type, const std::string& fname); + int parsePolicyConfigFile(BusType bus_type, const char *fname); void elementStart(const char *el, const char **attr); @@ -47,13 +47,13 @@ namespace ldp_xml_parser class IncludeItem; /** Decides whether a filename describes one of main system.conf/session.conf files */ - bool isMainConfFile(const std::string& filename); + bool isMainConfFile(const char *filename); /** Parses config file and all files included in it (recursively) */ - void parsePolicyConfigFileInternal(const std::string& filename); + void parsePolicyConfigFileInternal(const char *filename); /** Parses config file and returns all files included in it */ - void parseXmlFile(const std::string& filename); + void parseXmlFile(const char *filename); /** Parses element */ IncludeItem parseIncludeItem(); -- 2.7.4 From eadbaa988fc258ccc63564569aa2425e32d89066 Mon Sep 17 00:00:00 2001 From: Adrian Szyndela Date: Fri, 1 Mar 2019 13:46:40 +0100 Subject: [PATCH 02/16] refactoring: hide XmlParser This disconnects XmlParser from __internal_init() and hides it behind StorageBackendXML. This way XmlParser is local, non static and non global anymore, a bit more tightly connected to StorageBackendXML, but more loosely connected to other components. Change-Id: I712129dfc9f8da357e7a7d66411aaea3590172e7 --- src/internal/internal.cpp | 5 ++--- src/internal/naive_policy_checker.cpp | 4 ++-- src/internal/naive_policy_checker.hpp | 2 +- src/internal/naive_policy_db.cpp | 13 ++----------- src/internal/naive_policy_db.hpp | 2 +- src/internal/policy.cpp | 15 +++++++-------- src/internal/policy.hpp | 12 ++++++++---- src/internal/storage_backend_xml.cpp | 6 +++++- src/internal/storage_backend_xml.hpp | 2 +- src/internal/xml_parser.cpp | 31 ++++++++++++------------------- src/internal/xml_parser.hpp | 15 ++++++++++----- 11 files changed, 51 insertions(+), 56 deletions(-) diff --git a/src/internal/internal.cpp b/src/internal/internal.cpp index 82757ec..d5dcd22 100644 --- a/src/internal/internal.cpp +++ b/src/internal/internal.cpp @@ -36,11 +36,10 @@ static const char* get_str(const char* const szstr) { int __internal_init(BusType bus_type, const char* const config_name) { - policy_checker().clearDb(bus_type); - auto err = static_parser().parsePolicyConfigFile(bus_type, get_str(config_name)); + auto ok = policy_checker().initDb(bus_type, get_str(config_name)); if (tslog::enabled()) memory_dump(bus_type); - return err; + return ok ? 0 : -1; } pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; diff --git a/src/internal/naive_policy_checker.cpp b/src/internal/naive_policy_checker.cpp index f35bd11..ab7f78f 100644 --- a/src/internal/naive_policy_checker.cpp +++ b/src/internal/naive_policy_checker.cpp @@ -120,9 +120,9 @@ void NaivePolicyChecker::updateGroupDb(BusType bus_type, uid_t uid, gid_t gid) getPolicyDb(bus_type).initializeGroups(uid, gid); } -void NaivePolicyChecker::clearDb(BusType bus_type) +bool NaivePolicyChecker::initDb(BusType bus_type, const char *config_name) { - getPolicyDb(bus_type).clear(); + return getPolicyDb(bus_type).init(config_name); } void NaivePolicyChecker::printContent(BusType bus_type) diff --git a/src/internal/naive_policy_checker.hpp b/src/internal/naive_policy_checker.hpp index b9da290..e1a6e5a 100644 --- a/src/internal/naive_policy_checker.hpp +++ b/src/internal/naive_policy_checker.hpp @@ -88,7 +88,7 @@ namespace ldp_xml_parser /** Clears all db data, useful for reloading configuration * during testing. */ - void clearDb(BusType bus_type); + bool initDb(BusType bus_type, const char *filename); void updateGroupDb(BusType bus_type, uid_t uid, gid_t gid); diff --git a/src/internal/naive_policy_db.cpp b/src/internal/naive_policy_db.cpp index cf53c10..08e95bc 100644 --- a/src/internal/naive_policy_db.cpp +++ b/src/internal/naive_policy_db.cpp @@ -57,14 +57,6 @@ void NaivePolicyDb::updateSupplementaryGroupsOwn(const VGid &groups, uid_t uid, } /********************* NaivePolicyDb **********************/ -template -void NaivePolicyDb::addItem(const PolicyType policy_type, - const PolicyTypeValue policy_type_value, - T &item) { - tslog::log("Add item: ", item, ", decision: ", item.getDecision(), "\n"); - backend.addItem(policy_type, policy_type_value, item); -} - void NaivePolicyDb::printContent() const { backend.printContent(); @@ -136,8 +128,8 @@ VGid &NaivePolicyDb::getMapGroup(uid_t uid) const return mapGroups[type_list::IDX::idx][uid]; } -void NaivePolicyDb::clear() { - backend.clear(); +bool NaivePolicyDb::init(const char *filename) { + return backend.init(filename); } template @@ -164,7 +156,6 @@ const std::map &NaivePolicyDb::getPoliciesGroup() const { */ #define T_INSTANTIATION(T) \ template const VGid *NaivePolicyDb::getGroups(uid_t, gid_t) const; \ - template void NaivePolicyDb::addItem(const PolicyType policy_type, const PolicyTypeValue policy_type_value, Item##T &item); \ template const Policy##T &NaivePolicyDb::getPolicyContextMandatory() const; \ template const Policy##T &NaivePolicyDb::getPolicyContextDefault() const; diff --git a/src/internal/naive_policy_db.hpp b/src/internal/naive_policy_db.hpp index 9137dfc..1bbea87 100644 --- a/src/internal/naive_policy_db.hpp +++ b/src/internal/naive_policy_db.hpp @@ -86,7 +86,7 @@ namespace ldp_xml_parser const PolicyTypeValue policy_type_value, T &item); - void clear(); + bool init(const char *filename); // The getPolicy*() methods are intended for Serializers to get // access to policy structures. diff --git a/src/internal/policy.cpp b/src/internal/policy.cpp index 15ef3c3..6863b3e 100644 --- a/src/internal/policy.cpp +++ b/src/internal/policy.cpp @@ -316,8 +316,8 @@ ItemAccess *ItemBuilder::getAccessItem() { return &__current_access; } -ItemBuilder::ItemBuilder() - : __current_item_type(ItemType::GENERIC) +ItemBuilder::ItemBuilder(ldp_xml::StorageBackendXML &storage) + : __storage(storage), __current_item_type(ItemType::GENERIC) { reset(); } @@ -331,24 +331,23 @@ void ItemBuilder::reset() { __current_item_type = ItemType::GENERIC; } -void ItemBuilder::generateItem(BusType bus_type, PolicyType& policy_type, PolicyTypeValue& policy_type_value) { - NaivePolicyDb &db = policy_checker().getPolicyDb(bus_type); +void ItemBuilder::generateItem(PolicyType& policy_type, PolicyTypeValue& policy_type_value) { switch (__current_item_type) { case ItemType::OWN: __current_own.__decision = __decision; - db.addItem(policy_type, policy_type_value, __current_own); + __storage.addItem(policy_type, policy_type_value, __current_own); break; case ItemType::SEND: __current_send.__decision = __decision; - db.addItem(policy_type, policy_type_value, __current_send); + __storage.addItem(policy_type, policy_type_value, __current_send); break; case ItemType::RECEIVE: __current_receive.__decision = __decision; - db.addItem(policy_type, policy_type_value, __current_receive); + __storage.addItem(policy_type, policy_type_value, __current_receive); break; case ItemType::ACCESS: __current_access.__decision = __decision; - db.addItem(policy_type, policy_type_value, __current_access); + __storage.addItem(policy_type, policy_type_value, __current_access); break; case ItemType::GENERIC: // do nothing diff --git a/src/internal/policy.hpp b/src/internal/policy.hpp index c0d1d9b..1ef7ed5 100644 --- a/src/internal/policy.hpp +++ b/src/internal/policy.hpp @@ -34,6 +34,10 @@ /** Maximum tree node children. It is connected with proper characters which can be used in name.*/ #define MAX_CHILDREN 65 +namespace ldp_xml { + class StorageBackendXML; +} + namespace ldp_xml_parser { /** Type of sent message*/ @@ -246,11 +250,11 @@ namespace ldp_xml_parser using MatchItemTypes = type_list::type_list; - class NaivePolicyDb; - /** Allows to create items from given name, decision etc. */ class ItemBuilder { private: + ldp_xml::StorageBackendXML &__storage; + DecisionItem __decision; ItemType __current_item_type; @@ -263,9 +267,9 @@ namespace ldp_xml_parser ItemSendReceive* getSendReceiveItem(); ItemAccess* getAccessItem(); public: - ItemBuilder(); + explicit ItemBuilder(ldp_xml::StorageBackendXML &storage); ~ItemBuilder(); - void generateItem(BusType bus_type, PolicyType& policy_type, PolicyTypeValue& policy_type_value); + void generateItem(PolicyType& policy_type, PolicyTypeValue& policy_type_value); void reset(); void setOwnItem(); void setSendItem(); diff --git a/src/internal/storage_backend_xml.cpp b/src/internal/storage_backend_xml.cpp index 5cfb9cb..4247108 100644 --- a/src/internal/storage_backend_xml.cpp +++ b/src/internal/storage_backend_xml.cpp @@ -1,5 +1,6 @@ #include "storage_backend_xml.hpp" #include "policy_containers.hpp" +#include "xml_parser.hpp" using namespace ldp_xml_parser; @@ -264,11 +265,14 @@ template void StorageBackendXML::addItem(const ldp_xml_parser::PolicyType policy_type, const ldp_xml_parser::PolicyTypeValue policy_type_value, T &item) { + tslog::log("Add item: ", item, ", decision: ", item.getDecision(), "\n"); pimpl->getPolicySet().addItem(policy_type, policy_type_value, item); } -void StorageBackendXML::clear() { +bool StorageBackendXML::init(const char *filename) { pimpl.reset(new StorageBackendXMLImpl); + XmlParser parser(*this, filename); + return parser.parsePolicyConfigFile() == 0; } void StorageBackendXML::printContent() const { diff --git a/src/internal/storage_backend_xml.hpp b/src/internal/storage_backend_xml.hpp index dd4cffc..84b2acb 100644 --- a/src/internal/storage_backend_xml.hpp +++ b/src/internal/storage_backend_xml.hpp @@ -20,7 +20,7 @@ public: const ldp_xml_parser::PolicyTypeValue policy_type_value, T &item); - void clear(); + bool init(const char *filename); void printContent() const; diff --git a/src/internal/xml_parser.cpp b/src/internal/xml_parser.cpp index 2433e03..0835c37 100644 --- a/src/internal/xml_parser.cpp +++ b/src/internal/xml_parser.cpp @@ -47,8 +47,7 @@ public: } void start_element_handler(void *data, const char *el, const char **attr) { - (void)data; - XmlParser& parser = static_parser(); + XmlParser& parser = *reinterpret_cast(data); try { parser.elementStart(el, attr); } catch (...) { @@ -58,8 +57,7 @@ void start_element_handler(void *data, const char *el, const char **attr) { } void end_element_handler(void *data, const char *el) { - (void)data; - XmlParser& parser = static_parser(); + XmlParser& parser = *reinterpret_cast(data); try { parser.elementEnd(el); } catch (...) { @@ -69,8 +67,7 @@ void end_element_handler(void *data, const char *el) { } void text_handler(void *data, const char *text, int len) { - (void)data; - XmlParser& parser = static_parser(); + XmlParser& parser = *reinterpret_cast(data); try { parser.text(text, len); } catch (...) { @@ -80,13 +77,7 @@ void text_handler(void *data, const char *text, int len) { } bool XmlParser::isMainConfFile(const char *filename) { - switch (curr_bus) { - case SYSTEM_BUS: - return strcmp(filename, SYSTEM_BUS_CONF_FILE_PRIMARY) == 0 || strcmp(filename, SYSTEM_BUS_CONF_FILE_SECONDARY) == 0; - case SESSION_BUS: - return strcmp(filename, SESSION_BUS_CONF_FILE_PRIMARY) == 0 || strcmp(filename, SESSION_BUS_CONF_FILE_SECONDARY) == 0; - } - return false; + return strcmp(main_filename, filename) == 0; } void XmlParser::setRetCode(int ret) { @@ -176,10 +167,9 @@ void XmlParser::elementEnd(const char *el) { } } -int XmlParser::parsePolicyConfigFile(BusType bus, const char *fname) { - tslog::log("XmlParser::parsePolicyConfigFile called with filename: ", fname, "\n"); - curr_bus = bus; - parsePolicyConfigFileInternal(fname); +int XmlParser::parsePolicyConfigFile() { + tslog::log("XmlParser::parsePolicyConfigFile called with filename: ", main_filename, "\n"); + parsePolicyConfigFileInternal(main_filename); return ret_code; } @@ -246,6 +236,7 @@ void XmlParser::parseXmlFile(const char *filename) { auto parser = std::unique_ptr>(XML_ParserCreate(nullptr), [](XML_Parser p) { XML_ParserFree(p); }); + XML_SetUserData(parser.get(), this); XML_SetElementHandler(parser.get(), start_element_handler, end_element_handler); XML_SetCharacterDataHandler(parser.get(), text_handler); @@ -371,7 +362,7 @@ void XmlParser::parseRule(Decision decision, const char **attr) parseRuleAttribute(attr[i], attr[i + 1]); } - __builder.generateItem(curr_bus, policy_type, policy_type_value); + __builder.generateItem(policy_type, policy_type_value); } namespace { @@ -443,4 +434,6 @@ void XmlParser::parseRuleAttribute(const char *name, const char *value) __builder.addMessageType(__str_to_message_type(value)); } -DEF_NODESTRUCT_GLOBAL(ldp_xml_parser::XmlParser, static_parser); +XmlParser::XmlParser(ldp_xml::StorageBackendXML &storage, const char *fname) + : __builder{storage}, main_filename{fname} +{} diff --git a/src/internal/xml_parser.hpp b/src/internal/xml_parser.hpp index a47b963..12afd0c 100644 --- a/src/internal/xml_parser.hpp +++ b/src/internal/xml_parser.hpp @@ -25,6 +25,11 @@ #include #include "policy.hpp" #include "global_nodestruct.hpp" +#include + +namespace ldp_xml { + class StorageBackendXML; +} namespace ldp_xml_parser { @@ -33,7 +38,7 @@ namespace ldp_xml_parser { public: /** Parses given config file for declared bus type */ - int parsePolicyConfigFile(BusType bus_type, const char *fname); + int parsePolicyConfigFile(); void elementStart(const char *el, const char **attr); @@ -43,6 +48,8 @@ namespace ldp_xml_parser void setRetCode(int ret); + explicit XmlParser(ldp_xml::StorageBackendXML &storage, const char *fname); + private: class IncludeItem; @@ -73,20 +80,18 @@ namespace ldp_xml_parser typedef enum { IDLE, BUSCONFIG, INCLUDE, INCLUDEDIR, POLICY, RULE } ParseState; - ParseState state; + ParseState state{IDLE}; bool ignore_always; bool ignore_missing; - BusType curr_bus; std::string current_text; std::string curr_dir; int ret_code; + const char *main_filename; std::vector included_files; }; } //namespace -DCL_NODESTRUCT_GLOBAL(ldp_xml_parser::XmlParser, static_parser) - #endif -- 2.7.4 From 9e5b302e8fe280c6b4068ac09842c52a6b107079 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Mon, 4 Mar 2019 10:35:44 +0100 Subject: [PATCH 03/16] Fix flatbuffers reverse_iterator Change-Id: I77233a416d76198175859c19adccdb41b5ab2ea8 --- src/internal/include/flatbuffers/flatbuffers.h | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/internal/include/flatbuffers/flatbuffers.h b/src/internal/include/flatbuffers/flatbuffers.h index 062c7f5..8c87dff 100644 --- a/src/internal/include/flatbuffers/flatbuffers.h +++ b/src/internal/include/flatbuffers/flatbuffers.h @@ -201,14 +201,11 @@ template struct VectorIterator { template struct VectorReverseIterator : public std::reverse_iterator { - explicit VectorReverseIterator(Iterator iter) : iter_(iter) {} + explicit VectorReverseIterator(Iterator iter) : std::reverse_iterator(iter) {} - typename Iterator::value_type operator*() const { return *(iter_ - 1); } + typename Iterator::value_type operator*() const { return *(std::reverse_iterator::current - 1); } - typename Iterator::value_type operator->() const { return *(iter_ - 1); } - - private: - Iterator iter_; + typename Iterator::value_type operator->() const { return *(std::reverse_iterator::current - 1); } }; struct String; @@ -272,8 +269,8 @@ template class Vector { reverse_iterator rbegin() { return reverse_iterator(end()); } const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } - reverse_iterator rend() { return reverse_iterator(end()); } - const_reverse_iterator rend() const { return const_reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } const_iterator cbegin() const { return begin(); } -- 2.7.4 From bb854760b8ca7b2b06d99a6fc5aa69cf8d0557f6 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Thu, 28 Feb 2019 14:48:49 +0100 Subject: [PATCH 04/16] serialization: add getDecisionItem tests Change-Id: Ia97e38b09a410ed8fdf94465ae4af556b646d31b --- Makefile.am | 18 ++ src/test-libdbuspolicy1-access-deny-gdi.cpp | 194 ++++++++++++++++++ src/test-libdbuspolicy1-method-gdi.cpp | 157 +++++++++++++++ src/test-libdbuspolicy1-ownership-deny-gdi.cpp | 209 ++++++++++++++++++++ src/test-libdbuspolicy1-ownership-gdi.cpp | 135 +++++++++++++ ...buspolicy1-send_destination_prefix-deny-gdi.cpp | 219 +++++++++++++++++++++ src/test-libdbuspolicy1-signal-gdi.cpp | 113 +++++++++++ 7 files changed, 1045 insertions(+) create mode 100644 src/test-libdbuspolicy1-access-deny-gdi.cpp create mode 100644 src/test-libdbuspolicy1-method-gdi.cpp create mode 100644 src/test-libdbuspolicy1-ownership-deny-gdi.cpp create mode 100644 src/test-libdbuspolicy1-ownership-gdi.cpp create mode 100644 src/test-libdbuspolicy1-send_destination_prefix-deny-gdi.cpp create mode 100644 src/test-libdbuspolicy1-signal-gdi.cpp diff --git a/Makefile.am b/Makefile.am index 28d274e..4ea3c14 100644 --- a/Makefile.am +++ b/Makefile.am @@ -122,21 +122,33 @@ EXTRA_DIST += src/libdbuspolicy1.pc.in CLEANFILES += src/libdbuspolicy1.pc TESTS = src/test-libdbuspolicy1-ownership \ + src/test-libdbuspolicy1-ownership-gdi \ src/test-libdbuspolicy1-ownership-deny \ + src/test-libdbuspolicy1-ownership-deny-gdi \ src/test-libdbuspolicy1-signal \ + src/test-libdbuspolicy1-signal-gdi \ src/test-libdbuspolicy1-method \ + src/test-libdbuspolicy1-method-gdi \ src/test-libdbuspolicy1-access-deny \ + src/test-libdbuspolicy1-access-deny-gdi \ src/test-libdbuspolicy1-send_destination_prefix-deny \ + src/test-libdbuspolicy1-send_destination_prefix-deny-gdi \ src/test-serializer check_PROGRAMS = $(TESTS) src_test_libdbuspolicy1_ownership_SOURCES = src/test-libdbuspolicy1-ownership.cpp +src_test_libdbuspolicy1_ownership_gdi_SOURCES = src/test-libdbuspolicy1-ownership-gdi.cpp src_test_libdbuspolicy1_ownership_deny_SOURCES = src/test-libdbuspolicy1-ownership-deny.cpp +src_test_libdbuspolicy1_ownership_deny_gdi_SOURCES = src/test-libdbuspolicy1-ownership-deny-gdi.cpp src_test_libdbuspolicy1_signal_SOURCES = src/test-libdbuspolicy1-signal.cpp +src_test_libdbuspolicy1_signal_gdi_SOURCES = src/test-libdbuspolicy1-signal-gdi.cpp src_test_libdbuspolicy1_method_SOURCES = src/test-libdbuspolicy1-method.cpp +src_test_libdbuspolicy1_method_gdi_SOURCES = src/test-libdbuspolicy1-method-gdi.cpp src_test_libdbuspolicy1_access_deny_SOURCES = src/test-libdbuspolicy1-access-deny.cpp +src_test_libdbuspolicy1_access_deny_gdi_SOURCES = src/test-libdbuspolicy1-access-deny-gdi.cpp src_test_libdbuspolicy1_send_destination_prefix_deny_SOURCES = src/test-libdbuspolicy1-send_destination_prefix-deny.cpp +src_test_libdbuspolicy1_send_destination_prefix_deny_gdi_SOURCES = src/test-libdbuspolicy1-send_destination_prefix-deny-gdi.cpp src_test_serializer_SOURCES = src/test-serializer.cpp noinst_LTLIBRARIES = src/libinternal.a @@ -152,11 +164,17 @@ TESTS_LDADD = src/libinternal.a \ -lexpat src_test_libdbuspolicy1_ownership_LDADD = $(TESTS_LDADD) +src_test_libdbuspolicy1_ownership_gdi_LDADD = $(TESTS_LDADD) src_test_libdbuspolicy1_ownership_deny_LDADD = $(TESTS_LDADD) +src_test_libdbuspolicy1_ownership_deny_gdi_LDADD = $(TESTS_LDADD) src_test_libdbuspolicy1_signal_LDADD = $(TESTS_LDADD) +src_test_libdbuspolicy1_signal_gdi_LDADD = $(TESTS_LDADD) src_test_libdbuspolicy1_method_LDADD = $(TESTS_LDADD) +src_test_libdbuspolicy1_method_gdi_LDADD = $(TESTS_LDADD) src_test_libdbuspolicy1_access_deny_LDADD = $(TESTS_LDADD) +src_test_libdbuspolicy1_access_deny_gdi_LDADD = $(TESTS_LDADD) src_test_libdbuspolicy1_send_destination_prefix_deny_LDADD = $(TESTS_LDADD) +src_test_libdbuspolicy1_send_destination_prefix_deny_gdi_LDADD = $(TESTS_LDADD) src_test_serializer_LDADD = $(TESTS_LDADD) if ENABLE_STANDALONE_TESTS diff --git a/src/test-libdbuspolicy1-access-deny-gdi.cpp b/src/test-libdbuspolicy1-access-deny-gdi.cpp new file mode 100644 index 0000000..f42a5f7 --- /dev/null +++ b/src/test-libdbuspolicy1-access-deny-gdi.cpp @@ -0,0 +1,194 @@ +#include +#include +#include +#include "internal/internal.h" +#include "internal/policy.hpp" +#include "internal/naive_policy_checker.hpp" +#include "internal/naive_policy_db.hpp" +#include "internal/serializer.hpp" +#include "internal/include/fb_generated.h" +#include "internal/storage_backend_serialized.hpp" +#include "internal/storage_backend_xml.hpp" +#include + +using namespace ldp_xml_parser; +using namespace ldp_serialized; + +struct AccessTest { + Decision expected_result; + uid_t user; + gid_t group; + const char* label; +}; + +const char* DEFAULT_LABEL = "User::Shell"; +const char* PRIVILEGED_LABEL = "System::Privileged"; + +std::map DECISIONS { + {Decision::ANY, "ANY"}, + {Decision::ALLOW, "ALLOW"}, + {Decision::DENY, "DENY"}, + {Decision::CHECK, "CHECK"}}; + + +const std::vector default_allow_tests = { + {Decision::ALLOW, 0, 0, PRIVILEGED_LABEL}, + {Decision::ALLOW, 2, 20, DEFAULT_LABEL}, //deny user, allow group + {Decision::ALLOW, 3, 30, DEFAULT_LABEL}, //deny group, allow user + {Decision::DENY, 4, 30, DEFAULT_LABEL}, //deny group, no user rule + {Decision::ALLOW, 5, 5, DEFAULT_LABEL}, //no rules + {Decision::DENY, 6, 6, DEFAULT_LABEL}, //deny user (mandatory) + {Decision::DENY, 7, 7, DEFAULT_LABEL}, //allow user (def), deny user (mandatory) + {Decision::ALLOW, 8, 8, DEFAULT_LABEL}, //deny user (def), allow user (mandatory) + {Decision::CHECK, 9991, 9991, DEFAULT_LABEL}, //check-user=allow (def) + {Decision::CHECK, 9992, 9992, DEFAULT_LABEL}, //check-user=allow (def), check-group=deny (mandatory) + {Decision::CHECK, 9993, 9993, DEFAULT_LABEL}, //check-user=allow (def, mandatory) + {Decision::CHECK, 888, 888, PRIVILEGED_LABEL} //check-user=allow (def) (label) +}; + +const std::vector default_deny_tests = { + {Decision::DENY, 0, 0, PRIVILEGED_LABEL}, + {Decision::ALLOW, 2, 20, DEFAULT_LABEL}, + {Decision::ALLOW, 3, 30, DEFAULT_LABEL}, + {Decision::DENY, 4, 30, DEFAULT_LABEL}, + {Decision::DENY, 5, 5, DEFAULT_LABEL}, // no rules, so denied by default + {Decision::DENY, 6, 6, DEFAULT_LABEL}, + {Decision::DENY, 7, 7, DEFAULT_LABEL}, + {Decision::ALLOW, 8, 8, DEFAULT_LABEL}, + {Decision::CHECK, 9991, 9991, DEFAULT_LABEL}, + {Decision::CHECK, 9992, 9992, DEFAULT_LABEL}, + {Decision::CHECK, 9993, 9993, DEFAULT_LABEL}, + {Decision::CHECK, 888, 888, PRIVILEGED_LABEL} +}; + +const uid_t bus_owner = 500; + +const std::vector session_tests = { + {Decision::ALLOW, 500, 500, DEFAULT_LABEL}, // allowed implicitly as the bus owner + {Decision::ANY, 400, 500, DEFAULT_LABEL}, + {Decision::ANY, 0, 0, PRIVILEGED_LABEL}, + {Decision::ANY, 900, 900, DEFAULT_LABEL}, + {Decision::ALLOW, 600, 600, DEFAULT_LABEL}, // allowed explicitly in policy (user) + {Decision::ALLOW, 700, 700, DEFAULT_LABEL}, // allowed explicitly in policy (group) + {Decision::DENY, 800, 700, DEFAULT_LABEL} // allow group, deny user +}; + +typedef std::pair> TestBusSetup; + +const std::vector> access_tests{ + {{"tests/default_deny/system.conf", default_allow_tests}, {"tests/default_deny/session.conf", session_tests}}, + {{"tests/default_deny/system-au.conf", default_allow_tests}, {}}, + {{"tests/default_deny/system-ag.conf", default_allow_tests}, {}}, + {{"tests/default_deny/system-du.conf", default_deny_tests}, {}}, + {{"tests/default_deny/system-dg.conf", default_deny_tests}, {}} +}; + +void print_test(const struct AccessTest* t, bool result) { + printf("uid = %lu, gid = %lu, label = %s, expected = %d, result = %d", + (unsigned long)t->user, (unsigned long)t->group, t->label, (int)t->expected_result, (int)result); +} + +template +bool run_tests_for_bus(const DB &db, const BusType bus_type, const std::vector& test_setup, int& i, bool& passed) { + + for (const auto& test : test_setup) { + __internal_init_sup_group(bus_type, test.user, test.group); + + auto *policydb = &policy_checker().getPolicyDb(bus_type); + + const auto &gids = *policydb->getGroups(test.user, test.group); + auto m_item = MatchItemAccess(test.user, gids); + auto decision = db.getDecisionItemContextMandatory(m_item).getDecision(); + if (decision == Decision::ANY) + decision = db.getDecisionItemContextDefault(m_item).getDecision(); + if (decision == Decision::ANY) { + if (bus_owner == test.user) + decision = Decision::ALLOW; + + } + bool res = decision == test.expected_result; + if (!res) { + printf("[ERROR][%d] access test failed: %s %s ", i, DECISIONS[test.expected_result], + DECISIONS[decision]); + print_test(&test, res); + printf("\n"); + passed = false; + } + i++; + } + return passed; +} + +bool run_policy_db(const std::pair access_test) { + bool passed = true; + int i = 0; + const auto& system_bus_setup = access_test.first; + const auto& session_bus_setup = access_test.second; + + __internal_init(SYSTEM_BUS, system_bus_setup.first.c_str()); + if (session_bus_setup.first != "") { + __internal_init(SESSION_BUS, session_bus_setup.first.c_str()); + } + + auto *sys_db = &policy_checker().getPolicyDb(SYSTEM_BUS); + auto *ses_db = &policy_checker().getPolicyDb(SESSION_BUS); + + printf("POLICY_DB:\n"); + return run_tests_for_bus(*sys_db, SYSTEM_BUS, system_bus_setup.second, i, passed) && + run_tests_for_bus(*ses_db, SESSION_BUS, session_bus_setup.second, i, passed); +} + +bool run_fb(const std::pair access_test) { + bool passed = true; + int i = 0; + Serializer serializer; + + const auto& system_bus_setup = access_test.first; + const auto& session_bus_setup = access_test.second; + + size_t size; + + uint8_t *buff_sys = nullptr, *buff_ses = nullptr; + buff_sys = serializer.serialize(system_bus_setup.first.c_str(), size); + if (session_bus_setup.first == "") + buff_ses = serializer.serialize(session_bus_setup.first.c_str(), size); + + StorageBackendSerialized storage_sys, storage_ses; + + printf("FLATBUFFERS:\n"); + + const FB::File *file_sys = FB::GetFile(buff_sys); + storage_sys.init(file_sys); + bool res = run_tests_for_bus(storage_sys, SYSTEM_BUS, system_bus_setup.second, i, passed); + + if (buff_ses) { + const FB::File *file_ses = FB::GetFile(buff_ses); + storage_ses.init(file_ses); + res &= run_tests_for_bus(storage_ses, SESSION_BUS, session_bus_setup.second, i, passed); + } + return res; +} + +bool run_xml(const std::pair access_test) { + (void)access_test; + return true; +} + +bool run_tests() { + bool passed = true; + for (const auto& access_test : access_tests) { + printf("f: %s s: %s\n", access_test.first.first.c_str(), access_test.second.first.c_str()); + passed &= run_policy_db(access_test); + passed &= run_fb(access_test); + passed &= run_xml(access_test); + } + + return passed; +} + +int main() { + __internal_init_once(); + if (!run_tests()) + return -1; + return 0; +} diff --git a/src/test-libdbuspolicy1-method-gdi.cpp b/src/test-libdbuspolicy1-method-gdi.cpp new file mode 100644 index 0000000..16c6c45 --- /dev/null +++ b/src/test-libdbuspolicy1-method-gdi.cpp @@ -0,0 +1,157 @@ + +#include +#include +#include +#include +#include +#include "internal/internal.h" +#include "internal/policy.hpp" +#include "internal/naive_policy_checker.hpp" +#include "internal/serializer.hpp" +#include "internal/include/fb_generated.h" +#include "internal/storage_backend_serialized.hpp" +#include "internal/storage_backend_xml.hpp" + +using namespace ldp_xml_parser; +using namespace ldp_serialized; + +std::map DECISIONS { + {Decision::ANY, "ANY" }, + {Decision::ALLOW, "ALLOW" }, + {Decision::DENY, "DENY" }, + {Decision::CHECK, "CHECK" } +}; + +enum MessageDirection { + RECEIVE, + SEND +}; + +struct MethodTest { + Decision expected_result; + uid_t user; + gid_t group; + const char* label; + const char* name; + const char* path; + const char* interface; + const char* member; + MessageType type; + MessageDirection recv_send; +}; + +/** + * This test set tests ability to parse xml db + * and check method call allowance in many use cases + */ + +struct MethodTest method_tests[]={ + (struct MethodTest){Decision::ALLOW, 0, 0, "test", "org.test.test2", NULL, "org.test.Itest1", "DoIt", MessageType::METHOD_CALL, MessageDirection::SEND }, + (struct MethodTest){Decision::ALLOW, 0, 0, "test", "org.test.test3", NULL, "org.test.Itest1", "DoIt", MessageType::METHOD_CALL, MessageDirection::RECEIVE }, + + (struct MethodTest){Decision::ALLOW, 5001, 100, "test", "org.test.test3", NULL, "org.test.Itest1", "DoIt", MessageType::METHOD_CALL, MessageDirection::RECEIVE }, + (struct MethodTest){Decision::ALLOW, 0, 0, "test", "org.test.test2", NULL, "org.test.Itest1", "DoIt", MessageType::METHOD_CALL, MessageDirection::SEND }, + + (struct MethodTest){Decision::DENY, 0, 0, "test", "org.test.test2", NULL, "org.test.Itest1", "DontDoIt", MessageType::METHOD_CALL, MessageDirection::SEND }, + (struct MethodTest){Decision::ALLOW, 0, 0, "test", "org.test.test3", NULL, "org.test.Itest1", "DontDoIt", MessageType::METHOD_CALL, MessageDirection::RECEIVE }, + + (struct MethodTest){Decision::DENY, 0, 0, "test", "org.test.test2", NULL, "org.test.Itest1", "DontDoIt", MessageType::METHOD_CALL, MessageDirection::SEND }, + (struct MethodTest){Decision::DENY, 5001, 100, "test", "org.test.test3", NULL, "org.test.Itest1", "DontDoIt", MessageType::METHOD_CALL, MessageDirection::RECEIVE }, + + (struct MethodTest){Decision::ALLOW, 0, 0, "test", "test.te34.fg4 a.b.c.d.e org.test.test2", NULL, "org.test.Itest1", "NotKnown", MessageType::METHOD_CALL, MessageDirection::SEND }, + (struct MethodTest){Decision::DENY, 0, 0, "test", "test.te34.fg4 a.b.c.d.e", NULL, "org.test.Itest1", "NotKnown", MessageType::METHOD_CALL, MessageDirection::SEND }, + (struct MethodTest){Decision::ALLOW, 0, 0, "test", "org.test.test3", NULL, "org.test.Itest1", "NotKnown", MessageType::METHOD_CALL, MessageDirection::RECEIVE }, + + (struct MethodTest){Decision::ALLOW, 0, 0, "test", "org.test.test2", NULL, "org.test.Itest1", "NotKnown", MessageType::METHOD_CALL, MessageDirection::SEND }, + (struct MethodTest){Decision::DENY, 5001, 100, "test", "org.test.test3", NULL, "org.test.Itest1", "NotKnown", MessageType::METHOD_CALL, MessageDirection::RECEIVE }, + + (struct MethodTest){Decision::DENY, 0, 0, "test", "org.test.test2", NULL, "org.test.Itest2", "NotKnown", MessageType::METHOD_CALL, MessageDirection::SEND }, + (struct MethodTest){Decision::ALLOW, 5001, 100, "test", "org.test.test3", NULL, "org.test.Itest2", "NotKnown", MessageType::METHOD_CALL, MessageDirection::RECEIVE }, +}; + +void methodTest_print(const struct MethodTest* t, Decision result) { + printf("uid = %lu, gid = %lu, label = %s, name = %s, path = %s, interface = %s, member = %s, expected = %s, result = %s (type=%d)", + (unsigned long)t->user, (unsigned long)t->group, t->label, t->name, t->path, t->interface, t->member, DECISIONS[t->expected_result], DECISIONS[result], (int)t->recv_send); +} + +template +Decision get_decision(DB &db, const MethodTest &test) { + DecisionItem ret; + + T m_item = T(test.interface, test.member, test.path, test.type); + m_item.addNames(test.name); + + ret = db.getDecisionItemContextMandatory(m_item); + if (ret.getDecision() == Decision::ANY) { + ret = db.getDecisionItemUser(test.user, m_item); + } + if (ret.getDecision() == Decision::ANY) { + ret = db.getDecisionItemGroup(test.group, m_item); + } + if (ret.getDecision() == Decision::ANY) { + ret = db.getDecisionItemContextDefault(m_item); + } + return ret.getDecision(); +} + +template +bool method_test(DB &db) { + unsigned int i = 0; + bool flag = true; + Decision decision; + + for (const auto& test : method_tests) { + if (test.recv_send == MessageDirection::SEND) { + decision = get_decision(db, test); + } else { + decision = get_decision(db, test); + } + + bool res = decision == test.expected_result; + if (!res) { + printf("[ERROR][%d] method test failed: %s %s ", i++, DECISIONS[test.expected_result], + DECISIONS[decision]); + methodTest_print(&test, decision); + printf("\n"); + flag = false; + } + } + return flag; +} + +bool run_policy_db() { + __internal_init(SYSTEM_BUS, "tests/default_allow/system.conf"); + auto &db = policy_checker().getPolicyDb(SYSTEM_BUS); + printf("POLICY_DB:\n"); + return method_test(db); +} + +bool run_fb() { + Serializer serializer; + + size_t size; + uint8_t *buff = serializer.serialize("tests/default_allow/system.conf", size); + const FB::File *file = FB::GetFile(buff); + + StorageBackendSerialized storage; + storage.init(file); + printf("FLATBUFFERS:\n"); + bool ret = method_test(storage); + return ret; +} + +bool run_xml() { + // This will be filled in the future + return true; +} + +bool run_tests() { + return run_policy_db() && run_fb() && run_xml(); +} + +int main() { + __internal_init_once(); + if (!run_tests()) + return -1; +return 0; +} diff --git a/src/test-libdbuspolicy1-ownership-deny-gdi.cpp b/src/test-libdbuspolicy1-ownership-deny-gdi.cpp new file mode 100644 index 0000000..3bee8f6 --- /dev/null +++ b/src/test-libdbuspolicy1-ownership-deny-gdi.cpp @@ -0,0 +1,209 @@ +#include +#include +#include +#include "internal/naive_policy_checker.hpp" +#include "internal/policy.hpp" +#include "internal/internal.h" +#include "internal/serializer.hpp" +#include "internal/include/fb_generated.h" +#include "internal/storage_backend_serialized.hpp" +#include "internal/storage_backend_xml.hpp" + +using namespace ldp_xml_parser; +using namespace ldp_serialized; + +struct OwnershipTest { + Decision expected_result; + uid_t user; + gid_t group; + const char* label; + const char* service; +}; + +std::map DECISIONS { + { Decision::ANY, "ANY" }, + { Decision::ALLOW, "ALLOW" }, + { Decision::DENY, "DENY" }, + { Decision::CHECK, "CHECK" } +}; + +const int ROOT = 0; +const int GUEST = 9999; +const int GUEST1 = 9991; +const int GUEST2 = 9992; +const int GUEST12 = 9993; + +/** + * This test set tests ability to parse xml db + * and check ownership privilege in many use cases + * including prefix feature + */ +struct OwnershipTest ownership_tests[]={ + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.ldpo.a" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.ldpoga" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.ldpogd" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.ldposa" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.ldpo.any_suffix" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.ldpnotexistent" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.ldponotexistent" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.z" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.zz" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.aa" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.aaa" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a1" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a1.b" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a1.b1" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a1.c" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.b" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.b.c" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.b.z" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.c" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.c.c" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.c.z" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.z" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.zz" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.bsth" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.bsthelse" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c.z.z" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c.d" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c.d.z" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c.d.e" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c.d.esth" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c.d.ee" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c.d.e.z" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c.d.e.z.z" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c.d.e.f" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c1" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c1.z.z" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c1.d" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c1.d.z" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c1.d.e" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c1.d.esth" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c1.d.ee" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c1.d.e.z" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c1.d.e.z.z" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c1.d.e.f" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c1.d.e.f.g" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c2" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c2.d" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c2.d.z" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c2.dd" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c2.d.e" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c2.d.e.f" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c2.d.e.f.z" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c2.d.e.fsth" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c3" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c3.z.z" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c3.d" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c3.d.z" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c3.d.e" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c3.d.esth" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c3.d.ee" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c3.d.e.z" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c3.d.e.z.z" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c3.d.e.f" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.b.c3.d.e.f.g" }, + {Decision::DENY, ROOT, ROOT, "User::Shell", "org.tizen.a.c" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.c.z" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.d" }, + {Decision::ALLOW, ROOT, ROOT, "User::Shell", "org.tizen.a.d.z" }, + {Decision::CHECK, ROOT, ROOT, "User::Shell", "org.tizen.pok" }, + {Decision::CHECK, ROOT, ROOT, "User::Shell", "org.tizen.pnope" }, + {Decision::CHECK, GUEST, GUEST, "User::Shell", "org.tizen.pok" }, + {Decision::CHECK, GUEST, GUEST, "User::Shell", "org.tizen.pnope" }, + {Decision::ALLOW, GUEST, GUEST, "User::Shell", "org.tizen.pok1" }, + {Decision::ALLOW, GUEST, GUEST, "User::Shell", "org.tizen.pok1.z" }, + {Decision::DENY, GUEST, GUEST, "User::Shell", "org.tizen.pok1.a" }, + {Decision::CHECK, GUEST, GUEST, "User::Shell", "org.tizen.pok1.a.b1" }, + {Decision::CHECK, GUEST, GUEST, "User::Shell", "org.tizen.pok1.a.b1.z" }, + {Decision::CHECK, GUEST, GUEST, "User::Shell", "org.tizen.pok1.a.b2" }, + {Decision::DENY, GUEST, GUEST, "User::Shell", "org.tizen.pok1.a.b2.z" }, + {Decision::CHECK, GUEST, GUEST, "User::Shell", "org.tizen.pok1.a.b3" }, + {Decision::CHECK, GUEST, GUEST, "User::Shell", "org.tizen.pok1.a.b3.z" }, + {Decision::CHECK, GUEST, GUEST, "User::Shell", "org.tizen.pok1.a.b4" }, + {Decision::DENY, GUEST, GUEST, "User::Shell", "org.tizen.pok1.a.b4.z" }, + {Decision::CHECK, GUEST, GUEST, "User::Shell", "org.tizen.pok1.a1" }, + {Decision::CHECK, GUEST, GUEST, "User::Shell", "org.tizen.pok1.a1.z" }, + {Decision::CHECK, GUEST1, GUEST1, "User::Shell", "org.tizen.pok2" }, + {Decision::CHECK, GUEST2, GUEST2, "User::Shell", "org.tizen.pok2" }, + {Decision::CHECK, GUEST12, GUEST12, "User::Shell", "org.tizen.pok2" }, + {Decision::DENY, GUEST1, GUEST1, "User::Shell", "org.tizen.pok2.a" }, + {Decision::DENY, GUEST2, GUEST2, "User::Shell", "org.tizen.pok2.a" }, + {Decision::DENY, GUEST12, GUEST12, "User::Shell", "org.tizen.pok2.a" }, + {Decision::CHECK, GUEST1, GUEST1, "User::Shell", "org.tizen.pok2.a.b" }, + {Decision::CHECK, GUEST2, GUEST2, "User::Shell", "org.tizen.pok2.a.b" }, + {Decision::CHECK, GUEST12, GUEST12, "User::Shell", "org.tizen.pok2.a.b" } +}; + +void ownershipTest_print(const struct OwnershipTest* t, Decision result) { + printf("uid = %lu, gid = %lu, label = %s, service = %s, expected = %s, result = %s", + (unsigned long)t->user, (unsigned long)t->group, t->label, t->service, DECISIONS[t->expected_result], DECISIONS[result]); +} + +template +bool ownership_test(const DB &db) { + unsigned i = 0; + bool flag = true; + + for (auto const &test : ownership_tests) { + auto m_item = MatchItemOwn(test.service); + + auto ret = db.getDecisionItemContextMandatory(m_item); + + if (ret.getDecision() == Decision::ANY) + ret = db.getDecisionItemUser(test.user, m_item); + + if (ret.getDecision() == Decision::ANY) + ret = db.getDecisionItemGroup(test.group, m_item); + + if (ret.getDecision() == Decision::ANY) + ret = db.getDecisionItemContextDefault(m_item); + + auto decision = ret.getDecision(); + if (decision != test.expected_result) { + printf("[ERROR][%d] ownership test failed: %s %s ", i, DECISIONS[test.expected_result], DECISIONS[decision]); + ownershipTest_print(&test, decision); + printf("\n"); + flag = false; + } + } + return flag; +} + +bool run_policy_db() { + __internal_init(SYSTEM_BUS, "tests/default_deny/system.conf"); + auto &db = policy_checker().getPolicyDb(SYSTEM_BUS); + printf("POLICY_DB:\n"); + return ownership_test(db); +} + +bool run_fb() { + Serializer serializer; + size_t size; + uint8_t *buff = serializer.serialize("tests/default_deny/system.conf", size); + + const FB::File *file = FB::GetFile(buff); + StorageBackendSerialized storage; + storage.init(file); + + printf("FRAMEBUFFERS:\n"); + return ownership_test(storage); +} + +bool run_xml() { + return true; +} + +bool run_tests() { + return run_policy_db() && run_fb() && run_xml(); +} + +int main() { + __internal_init_once(); + if (!run_tests()) + return -1; +return 0; +} diff --git a/src/test-libdbuspolicy1-ownership-gdi.cpp b/src/test-libdbuspolicy1-ownership-gdi.cpp new file mode 100644 index 0000000..9a3e00a --- /dev/null +++ b/src/test-libdbuspolicy1-ownership-gdi.cpp @@ -0,0 +1,135 @@ +#include +#include +#include +#include +#include +#include "internal/internal.h" +#include "internal/policy.hpp" +#include "internal/naive_policy_checker.hpp" +#include "internal/serializer.hpp" +#include "internal/include/fb_generated.h" +#include "internal/storage_backend_serialized.hpp" +#include "internal/storage_backend_serialized.hpp" + +using namespace ldp_xml_parser; +using namespace ldp_serialized; + +struct OwnershipTest { + Decision expected_result; + uid_t user; + gid_t group; + const char* label; + const char* service; +}; + +std::map DECISIONS { + { Decision::ANY, "ANY" }, + { Decision::ALLOW, "ALLOW" }, + { Decision::DENY, "DENY" }, + { Decision::CHECK, "CHECK" } +}; + +/** + * This test set tests ability to parse xml db + * and check ownership privilege in many use cases + * including prefix feature + */ +struct OwnershipTest ownership_tests[]={ + (struct OwnershipTest){Decision::ALLOW, 0, 0, "test", "org.test.test1" }, + (struct OwnershipTest){Decision::ALLOW, 5009, 0, "test", "org.test.test1" }, + + (struct OwnershipTest){Decision::DENY, 0, 0, "test", "org.test.test2" }, + (struct OwnershipTest){Decision::DENY, 5009, 0, "test", "org.test.test2" }, + + (struct OwnershipTest){Decision::DENY, 0, 0, "test", "org.test.test3" }, + (struct OwnershipTest){Decision::DENY, 5009, 0, "test", "org.test.test3" }, + + (struct OwnershipTest){Decision::DENY, 0, 0, "test", "org.test.test4" }, + (struct OwnershipTest){Decision::ALLOW, 5009, 0, "test", "org.test.test4" }, + + (struct OwnershipTest){Decision::DENY, 0, 0, "test", "org.test.test5" }, + + (struct OwnershipTest){Decision::ALLOW, 0, 0, "test", "org.test.test6" }, + + (struct OwnershipTest){Decision::ALLOW, 0, 0, "test", "org.test.test7" }, + + (struct OwnershipTest){Decision::ALLOW, 0, 0, "test", "a.b.c" }, + (struct OwnershipTest){Decision::ALLOW, 0, 0, "test", "a.b" }, + (struct OwnershipTest){Decision::DENY, 0, 0, "test", "c" }, + (struct OwnershipTest){Decision::DENY, 0, 0, "test", "a.c" }, + (struct OwnershipTest){Decision::DENY, 0, 0, "test", "b.c" }, + + (struct OwnershipTest){Decision::ALLOW, 1000, 0, "test", "org.test.testX" }, + (struct OwnershipTest){Decision::ALLOW, 1000, 0, "test", "org.test.testY" } +}; + +void ownershipTest_print(const struct OwnershipTest* t, const Decision result) { + printf("uid = %lu, gid = %lu, label = %s, service = %s, expected = %s, result = %s", + (unsigned long)t->user, (unsigned long)t->group, t->label, t->service, DECISIONS[t->expected_result], DECISIONS[result]); +} + +template +bool ownership_test(const DB &db) { + unsigned i = 0; + bool flag = true; + for (const auto& test : ownership_tests) { + auto m_item = MatchItemOwn(test.service); + + auto ret = db.getDecisionItemContextMandatory(m_item); + + if (ret.getDecision() == Decision::ANY) + ret = db.getDecisionItemUser(test.user, m_item); + + if (ret.getDecision() == Decision::ANY) + ret = db.getDecisionItemGroup(test.group, m_item); + + if (ret.getDecision() == Decision::ANY) + ret = db.getDecisionItemContextDefault(m_item); + + auto decision = ret.getDecision(); + + if (decision != test.expected_result) { + printf("[ERROR][%d] ownership test failed: %s %s ", i, DECISIONS[test.expected_result], + DECISIONS[decision]); + ownershipTest_print(&test, decision); + printf("\n"); + flag = false; + } + } + return flag; +} + +bool run_policy_db() { + __internal_init(SYSTEM_BUS, "tests/default_allow/system.conf"); + auto &db = policy_checker().getPolicyDb(SYSTEM_BUS); + printf("POLICY DB:\n"); + return ownership_test(db); +} + +bool run_fb() { + Serializer serializer; + size_t size; + uint8_t *buff = serializer.serialize("tests/default_allow/system.conf", size); + const FB::File *file = FB::GetFile(buff); + + StorageBackendSerialized storage; + storage.init(file); + printf("FLATBUFFERS:\n"); + return ownership_test(storage); +} + +bool run_xml() { + // This will be filled in the future + return true; +} + +bool run_tests() { + return run_policy_db() && run_fb() && run_xml(); +} + +int main() { + __internal_init_once(); + if (!run_tests()) + return -1; +return 0; +} diff --git a/src/test-libdbuspolicy1-send_destination_prefix-deny-gdi.cpp b/src/test-libdbuspolicy1-send_destination_prefix-deny-gdi.cpp new file mode 100644 index 0000000..45938c5 --- /dev/null +++ b/src/test-libdbuspolicy1-send_destination_prefix-deny-gdi.cpp @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include +#include "internal/internal.h" +#include "internal/policy.hpp" +#include "internal/naive_policy_checker.hpp" +#include "internal/serializer.hpp" +#include "internal/include/fb_generated.h" +#include "internal/storage_backend_serialized.hpp" +#include "internal/storage_backend_serialized.hpp" + +using namespace ldp_xml_parser; +using namespace ldp_serialized; + +std::map DECISIONS { + { Decision::ANY, "ANY" }, + { Decision::ALLOW, "ALLOW" }, + { Decision::DENY, "DENY" }, + { Decision::CHECK, "CHECK" } +}; + +struct Test { + Decision expected_result; + uid_t user; + gid_t group; + const char *label; + const char *destination; + const char *path; + const char *interface; + const char *member; + ldp_xml_parser::MessageType type; +}; + +const int ROOT = 0; + +#define TC(expected_result, names) \ + {(expected_result), ROOT, ROOT, "User::Shell", (names), "/", "a.b", "d", ldp_xml_parser::MessageType::METHOD_CALL} + +/** + * This test set tests ability to parse xml db + * and check sending privilege in use cases + * checking send_destination_prefix + */ +struct Test tests[]={ + /* straight-forward tests - base allow */ + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.f.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.apf"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.apf.f.f.f.f"), + /* multiple names owned */ + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ao org.tizen.test.dest_prefix.ap.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.f org.tizen.test.dest_prefix.ao"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.do org.tizen.test.dest_prefix.ap.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.f org.tizen.test.dest_prefix.do"), + /* target holes in default allow */ + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.1.d"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.1.dp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.1.dp.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.1.dp.f.f.f.f org.tizen.test.dest_prefix.ao"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.1.dp.f.f.f.f org.tizen.test.dest_prefix.ap"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ao org.tizen.test.dest_prefix.ap.1.dp.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap org.tizen.test.dest_prefix.ap.1.dp.f.f.f.f"), + /* target holes in holes in default allow */ + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.1.d.ap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.1.d.ap.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.1.dp.ap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.1.dp.ap.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.1.dp.a"), + /* check redefinitions in default allow */ + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.2.apxdp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.2.apxdp.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.2.apxdp.dp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.2.apxdp.dp.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.2.apxdp.dp.ap"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.2.apxdp.dp.ap.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.2.apxdp.dp.ap.d"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.2.apxdp.dp.a"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.2.apxdp.dp.ap.f.a"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.2.apxdp.f.f.f.ap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.2.apxdp.f.f.f.ap.f.f.f"), + /* totally cancelling previous definitions in default allow */ + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.3.dpxap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.3.dpxap.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.3.dpxap.ap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.3.dpxap.ap.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.3.dpxap.ap.dp"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.3.dpxap.ap.dp.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.3.dpxap.ap.dp.ap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.3.dpxap.ap.dp.ap.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.3.dpxap.ap.dp.a"), + /* straight-forward tests - base deny */ + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.f.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dpf"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dpf.f.f.f.f"), + /* multiple names owned */ + TC(Decision::DENY, "org.tizen.test.dest_prefix.do org.tizen.test.dest_prefix.dp.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.f org.tizen.test.dest_prefix.do"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ao org.tizen.test.dest_prefix.dp.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.f org.tizen.test.dest_prefix.ao"), + /* target holes in default deny */ + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.1.a"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.1.ap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.1.ap.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.1.ap.f.f.f.f org.tizen.test.dest_prefix.do"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.1.ap.f.f.f.f org.tizen.test.dest_prefix.dp"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.do org.tizen.test.dest_prefix.dp.1.ap.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp org.tizen.test.dest_prefix.dp.1.ap.f.f.f.f"), + /* target holes in holes in default demy */ + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.1.a.dp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.1.a.dp.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.1.ap.dp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.1.ap.dp.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.1.ap.d"), + /* check redefinitions in default deny */ + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.2.dpxap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.2.dpxap.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.2.dpxap.ap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.2.dpxap.ap.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.2.dpxap.ap.dp"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.2.dpxap.ap.dp.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.2.dpxap.ap.dp.a"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.2.dpxap.ap.d"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.2.dpxap.ap.dp.f.d"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.2.dpxap.f.f.f.dp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.2.dpxap.f.f.f.dp.f.f.f"), + /* totally cancelling previous definitions in default deny */ + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.3.apxdp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.3.apxdp.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.3.apxdp.dp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.3.apxdp.dp.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.3.apxdp.dp.ap"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.3.apxdp.dp.ap.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.3.apxdp.dp.ap.dp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.3.apxdp.dp.ap.dp.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.3.apxdp.dp.ap.d"), + /* checking order in multiple names case */ + TC(Decision::DENY, "org.tizen.test.dest_prefix.ao org.tizen.test.dest_prefix.do"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ao.ao org.tizen.test.dest_prefix.do"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.do org.tizen.test.dest_prefix.ao"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.do org.tizen.test.dest_prefix.ao.ao"), +}; + +void test_print(const struct Test* t, Decision result) { + printf("uid = %lu, gid = %lu, label = %s, destination = %s, expected = %s, result = %s", + (unsigned long)t->user, (unsigned long)t->group, t->label, t->destination, DECISIONS[t->expected_result], DECISIONS[result]); +} + +template +bool send_prefix_test(const DB &db) +{ + unsigned i = 0; + bool flag = true; + + for (const auto &test : tests) { + MatchItemSend m_item(test.interface, test.member, test.path, test.type); + m_item.addNames(test.destination); + + auto ret = db.getDecisionItemContextMandatory(m_item); + + if (ret.getDecision() == Decision::ANY) + ret = db.getDecisionItemUser(test.user, m_item); + + if (ret.getDecision() == Decision::ANY) + ret = db.getDecisionItemGroup(test.group, m_item); + + if (ret.getDecision() == Decision::ANY) + ret = db.getDecisionItemContextDefault(m_item); + + auto decision = ret.getDecision(); + + if (test.expected_result != decision) { + printf("[ERROR][%d] test failed: %s %s ", i, DECISIONS[test.expected_result], DECISIONS[decision]); + test_print(&test, decision); + printf("\n"); + flag = false; + } + } + return flag; +} + +bool run_policy_db() { + __internal_init(SYSTEM_BUS, "tests/default_deny/system.conf"); + auto &db = policy_checker().getPolicyDb(SYSTEM_BUS); + printf("POLICY_DB:\n"); + return send_prefix_test(db); +} + +bool run_fb() { + Serializer serializer; + size_t size; + uint8_t *buff = serializer.serialize("tests/default_deny/system.conf", size); + + const FB::File *file = FB::GetFile(buff); + + StorageBackendSerialized storage; + storage.init(file); + + printf("FLATBUFFERS:\n"); + return send_prefix_test(storage); +} + +bool run_xml() { + return true; +} + +bool run_tests() { + return run_policy_db() && run_fb() && run_xml(); +} + +int main() +{ + __internal_init_once(); + if (!run_tests()) + return -1; + return 0; +} diff --git a/src/test-libdbuspolicy1-signal-gdi.cpp b/src/test-libdbuspolicy1-signal-gdi.cpp new file mode 100644 index 0000000..5f7f04d --- /dev/null +++ b/src/test-libdbuspolicy1-signal-gdi.cpp @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include "internal/internal.h" +#include "internal/policy.hpp" +#include "internal/naive_policy_checker.hpp" +#include "internal/serializer.hpp" +#include "internal/include/fb_generated.h" +#include "internal/storage_backend_serialized.hpp" +#include "internal/storage_backend_serialized.hpp" + +using namespace ldp_xml_parser; +using namespace ldp_serialized; + +struct SignalTest { + Decision expected_result; + uid_t user; + gid_t group; + const char* label; + const char* dest; + const char* interface; +}; + +std::map DECISIONS { + { Decision::ANY, "ANY" }, + { Decision::ALLOW, "ALLOW" }, + { Decision::DENY, "DENY" }, + { Decision::CHECK, "CHECK" } +}; + +/** + * This test set tests ability to parse xml db + * and check signal call allowance in many use cases + */ +struct SignalTest signal_tests[]={ + (struct SignalTest){Decision::ALLOW, 0, 0, "test", "bli.bla.blubb test.test1 test.tes3", "/an/object/path"}, + (struct SignalTest){Decision::DENY, 5010, 0, "test", "bli.bla.blubb", "/an/object/path"}, +}; + +void signalTest_print(const struct SignalTest* t, Decision result) { + printf("uid = %lu, gid = %lu, label = %s, dest = %s, interface = %s, expected = %s, result = %s", + (unsigned long)t->user, (unsigned long)t->group, t->label, t->dest, t->interface, DECISIONS[t->expected_result], DECISIONS[result]); +} + +template +bool signal_test(const DB &db) { + unsigned i = 0; + bool flag = true; + for (const auto &test : signal_tests) { + MatchItemSend m_item(test.interface, NULL, NULL, ldp_xml_parser::MessageType::SIGNAL); + m_item.addNames(test.dest); + + auto ret = db.getDecisionItemContextMandatory(m_item); + + if (ret.getDecision() == Decision::ANY) + ret = db.getDecisionItemUser(test.user, m_item); + + if (ret.getDecision() == Decision::ANY) + ret = db.getDecisionItemGroup(test.group, m_item); + + if (ret.getDecision() == Decision::ANY) + ret = db.getDecisionItemContextDefault(m_item); + + auto decision = ret.getDecision(); + + if (test.expected_result != decision) { + printf("[ERROR][%d] signal test failed: %s %s ", i, DECISIONS[test.expected_result], DECISIONS[decision]); + signalTest_print(&test, decision); + printf("\n"); + flag = false; + } + } + return flag; +} + +bool run_policy_db() { + __internal_init(SYSTEM_BUS, "tests/default_allow/system.conf"); + auto &db = policy_checker().getPolicyDb(SYSTEM_BUS); + + printf("POLICY_DB:\n"); + return signal_test(db); +} + +bool run_fb() { + Serializer serializer; + size_t size; + uint8_t *buff = serializer.serialize("tests/default_allow/system.conf", size); + + const FB::File *file = FB::GetFile(buff); + + StorageBackendSerialized storage; + storage.init(file); + + printf("FLATBUFFERS:\n"); + return signal_test(storage); +} + +bool run_xml() { + return true; +} + +bool run_tests() { + return run_policy_db() && run_fb() && run_xml(); +} + +int main() { + __internal_init_once(); + if (!run_tests()) + return -1; +return 0; +} -- 2.7.4 From 5df9d6d4dd5a6b90b6aee408bbd5d09bbd358960 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Mon, 4 Mar 2019 15:03:15 +0100 Subject: [PATCH 05/16] Init the logger to read settings from environment variables Change-Id: I985325bc3d2ee2d589b8c467f6d8d8e92ceb31b5 --- src/internal/serializer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/internal/serializer.cpp b/src/internal/serializer.cpp index c4beffb..af69580 100644 --- a/src/internal/serializer.cpp +++ b/src/internal/serializer.cpp @@ -106,6 +106,7 @@ uint8_t* Serializer::serialize(const BusType bus_type, size_t &size) { uint8_t* Serializer::serialize(const std::string config_path, size_t &size) { // SYSTEM_BUS here because something had to be choosen + __internal_init_once(); if (__internal_init(BusType::SYSTEM_BUS, config_path.c_str()) != 0) cout << "internal_init error" << endl; -- 2.7.4 From b54beab648c474b11df783fc11ce4c9e391062b9 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Tue, 5 Mar 2019 12:14:19 +0100 Subject: [PATCH 06/16] Add flatbuffers vs xml performance test Change-Id: I0dfc715e8f011cec6e38040914290a79d63d01f3 --- Makefile.am | 4 +- src/stest_performance.cpp | 275 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 src/stest_performance.cpp diff --git a/Makefile.am b/Makefile.am index 4ea3c14..0ac5c8b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -190,7 +190,7 @@ libdbuspolicy_tests_SOURCES = src/test_runner.c runnerdir = ${libdir}/dbus-tests/runner/ alonetestdir = ${libdir}/dbus-tests/test-suites/libdbuspolicy-tests/ -alonetest_PROGRAMS = dbus_daemon stest_ownership stest_method_call stest_signal stest_cynara stest_memory +alonetest_PROGRAMS = dbus_daemon stest_ownership stest_method_call stest_signal stest_cynara stest_memory stest_performance dbus_daemon_SOURCES = src/dbus_daemon.c stest_ownership_SOURCES = src/stest_ownership.c src/stest_common.c @@ -198,12 +198,14 @@ stest_method_call_SOURCES = src/stest_method_call.c src/stest_common.c stest_signal_SOURCES = src/stest_signal.c src/stest_common.c stest_cynara_SOURCES = src/stest_cynara.c src/stest_common.c stest_memory_SOURCES = src/stest_memory.c +stest_performance_SOURCES = src/stest_performance.cpp stest_ownership_LDADD = src/libinternalfortests.a -lexpat -lstdc++ $(CYNARA_LIBS) $(DLOG_LIBS) stest_method_call_LDADD = src/libinternalfortests.a -lexpat -lstdc++ $(CYNARA_LIBS) $(DLOG_LIBS) stest_signal_LDADD = src/libinternalfortests.a -lexpat -lstdc++ $(CYNARA_LIBS) $(DLOG_LIBS) stest_cynara_LDADD = src/libinternalfortests.a -lexpat -lstdc++ $(CYNARA_LIBS) $(DLOG_LIBS) stest_memory_LDADD = src/libinternalfortests.a -lexpat -lstdc++ $(CYNARA_LIBS) $(DLOG_LIBS) +stest_performance_LDADD = src/libinternal.a -lexpat -lstdc++ $(CYNARA_LIBS) $(DLOG_LIBS) all-tests:: $(alonetest_PROGRAMS) $(runner_PROGRAMS) endif diff --git a/src/stest_performance.cpp b/src/stest_performance.cpp new file mode 100644 index 0000000..48e503b --- /dev/null +++ b/src/stest_performance.cpp @@ -0,0 +1,275 @@ +#include +#include + +#include +#include +#include +#include +#include +#include "internal/internal.h" +#include "internal/policy.hpp" +#include "internal/naive_policy_checker.hpp" +#include "internal/serializer.hpp" +#include "internal/include/fb_generated.h" +#include "internal/storage_backend_serialized.hpp" +#include "internal/storage_backend_serialized.hpp" +#include "libdbuspolicy1-private.h" + +using namespace ldp_xml_parser; +using namespace ldp_serialized; + +enum class Choice { + ALL, + XML, + FB, +}; + +std::map DECISIONS { + { Decision::ANY, "ANY" }, + { Decision::ALLOW, "ALLOW" }, + { Decision::DENY, "DENY" }, + { Decision::CHECK, "CHECK" } +}; + +struct Test { + Decision expected_result; + uid_t user; + gid_t group; + const char *label; + const char *destination; + const char *path; + const char *interface; + const char *member; + ldp_xml_parser::MessageType type; +}; + +const int ROOT = 0; + +#define TC(expected_result, names) \ + {(expected_result), ROOT, ROOT, "User::Shell", (names), "/", "a.b", "d", ldp_xml_parser::MessageType::METHOD_CALL} + +/** + * This test set tests ability to parse xml db + * and check sending privilege in use cases + * checking send_destination_prefix + */ +struct Test tests[]={ + /* straight-forward tests - base allow */ + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.f.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.apf"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.apf.f.f.f.f"), + /* multiple names owned */ + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ao org.tizen.test.dest_prefix.ap.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.f org.tizen.test.dest_prefix.ao"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.do org.tizen.test.dest_prefix.ap.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.f org.tizen.test.dest_prefix.do"), + /* target holes in default allow */ + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.1.d"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.1.dp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.1.dp.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.1.dp.f.f.f.f org.tizen.test.dest_prefix.ao"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.1.dp.f.f.f.f org.tizen.test.dest_prefix.ap"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ao org.tizen.test.dest_prefix.ap.1.dp.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap org.tizen.test.dest_prefix.ap.1.dp.f.f.f.f"), + /* target holes in holes in default allow */ + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.1.d.ap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.1.d.ap.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.1.dp.ap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.1.dp.ap.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.1.dp.a"), + /* check redefinitions in default allow */ + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.2.apxdp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.2.apxdp.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.2.apxdp.dp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.2.apxdp.dp.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.2.apxdp.dp.ap"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.2.apxdp.dp.ap.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ap.2.apxdp.dp.ap.d"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.2.apxdp.dp.a"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.2.apxdp.dp.ap.f.a"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.2.apxdp.f.f.f.ap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.2.apxdp.f.f.f.ap.f.f.f"), + /* totally cancelling previous definitions in default allow */ + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.3.dpxap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.3.dpxap.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.3.dpxap.ap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.3.dpxap.ap.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.3.dpxap.ap.dp"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.3.dpxap.ap.dp.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.3.dpxap.ap.dp.ap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.3.dpxap.ap.dp.ap.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ap.3.dpxap.ap.dp.a"), + /* straight-forward tests - base deny */ + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.f.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dpf"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dpf.f.f.f.f"), + /* multiple names owned */ + TC(Decision::DENY, "org.tizen.test.dest_prefix.do org.tizen.test.dest_prefix.dp.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.f org.tizen.test.dest_prefix.do"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.ao org.tizen.test.dest_prefix.dp.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.f org.tizen.test.dest_prefix.ao"), + /* target holes in default deny */ + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.1.a"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.1.ap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.1.ap.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.1.ap.f.f.f.f org.tizen.test.dest_prefix.do"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.1.ap.f.f.f.f org.tizen.test.dest_prefix.dp"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.do org.tizen.test.dest_prefix.dp.1.ap.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp org.tizen.test.dest_prefix.dp.1.ap.f.f.f.f"), + /* target holes in holes in default demy */ + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.1.a.dp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.1.a.dp.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.1.ap.dp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.1.ap.dp.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.1.ap.d"), + /* check redefinitions in default deny */ + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.2.dpxap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.2.dpxap.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.2.dpxap.ap"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.2.dpxap.ap.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.2.dpxap.ap.dp"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.2.dpxap.ap.dp.f.f.f.f"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.dp.2.dpxap.ap.dp.a"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.2.dpxap.ap.d"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.2.dpxap.ap.dp.f.d"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.2.dpxap.f.f.f.dp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.2.dpxap.f.f.f.dp.f.f.f"), + /* totally cancelling previous definitions in default deny */ + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.3.apxdp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.3.apxdp.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.3.apxdp.dp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.3.apxdp.dp.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.3.apxdp.dp.ap"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.3.apxdp.dp.ap.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.3.apxdp.dp.ap.dp"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.3.apxdp.dp.ap.dp.f.f.f.f"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.dp.3.apxdp.dp.ap.d"), + /* checking order in multiple names case */ + TC(Decision::DENY, "org.tizen.test.dest_prefix.ao org.tizen.test.dest_prefix.do"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.ao.ao org.tizen.test.dest_prefix.do"), + TC(Decision::DENY, "org.tizen.test.dest_prefix.do org.tizen.test.dest_prefix.ao"), + TC(Decision::ALLOW, "org.tizen.test.dest_prefix.do org.tizen.test.dest_prefix.ao.ao"), +}; + +void test_print(const struct Test* t, Decision result) { + printf("uid = %lu, gid = %lu, label = %s, destination = %s, expected = %s, result = %s", + (unsigned long)t->user, (unsigned long)t->group, t->label, t->destination, DECISIONS[t->expected_result], DECISIONS[result]); +} + +template +void send_prefix_test(const DB &db) +{ + for (const auto &test : tests) { + MatchItemSend m_item(test.interface, test.member, test.path, test.type); + m_item.addNames(test.destination); + + auto ret = db.getDecisionItemContextMandatory(m_item); + + if (ret.getDecision() == Decision::ANY) + ret = db.getDecisionItemUser(test.user, m_item); + + if (ret.getDecision() == Decision::ANY) + ret = db.getDecisionItemGroup(test.group, m_item); + + if (ret.getDecision() == Decision::ANY) + ret = db.getDecisionItemContextDefault(m_item); + } +} + +void run_x_times(std::function func, size_t times) { + clock_t begin = clock(); + for (size_t i = 0; i < times; i++) + func(); + clock_t end = clock(); + + std::cout << "run: " << static_cast(end - begin)/CLOCKS_PER_SEC << std::endl; +} + +void run_policy_db(const char *conf_file, size_t count) { + __internal_init(SYSTEM_BUS, conf_file); + auto &db = policy_checker().getPolicyDb(SYSTEM_BUS); + + printf("XML:\n"); + run_x_times([&db](){ send_prefix_test(db); }, count); +} + +void run_fb(const char *conf_file, size_t count) { + Serializer serializer; + size_t size; + uint8_t *buff = serializer.serialize(conf_file, size); + + const FB::File *file = FB::GetFile(buff); + + StorageBackendSerialized storage; + storage.init(file); + + printf("FLATBUFFERS:\n"); + run_x_times([&storage](){ send_prefix_test(storage); }, count); +} + +void run_tests(const char *conf_file, size_t c, Choice ch) { + if (ch == Choice::ALL || ch == Choice::XML) + run_policy_db(conf_file, c); + + if (ch == Choice::ALL || ch == Choice::FB) + run_fb(conf_file, c); +} + +void print_help(const char *name) { + cout << endl; + cout << "usage: " << name << " {-f|-x|-a} {--system|--session|-c } " << endl; + cout << endl; + cout << " -f - Flatbuffers" << endl; + cout << " -x - XML" << endl; + cout << " -a - Flatbuffers and XML" << endl; + cout << endl; +} + +static const struct option options[] { + {"system", no_argument, 0, 0}, + {"session", no_argument, 0, 0} +}; + +int main(int argc, char *argv[]) +{ + int c; + std::string input_filename = system_bus_conf_file_primary(); + size_t count = 100; + Choice choice = Choice::ALL; + + while (1) { + int option_index; + c = getopt_long(argc, argv, "fxac:", options, &option_index); + if (c == -1) + break; + switch(c) { + case 0: + if (option_index == 1) + input_filename = session_bus_conf_file_primary(); + break; + case 'f': + choice = Choice::FB; + break; + case 'x': + choice = Choice::XML; + break; + case 'c': + input_filename = optarg; + break; + } + } + + if (optind < argc) { + count = stoi(argv[optind]); + } else { + print_help(argv[0]); + return 1; + } + + __internal_init_once(); + run_tests(input_filename.c_str(), count, choice); + + return 0; +} -- 2.7.4 From 4dc83f0cc809a4c049e1a32d78fdc0d8d99e8a3c Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Tue, 5 Mar 2019 15:48:45 +0100 Subject: [PATCH 07/16] Improve getDecisionItem() and match() performance Change-Id: I117a1d496cb709fc63958e0d826725fb01597daa --- src/internal/policy_containers.hpp | 2 +- src/internal/storage_backend_serialized.cpp | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/internal/policy_containers.hpp b/src/internal/policy_containers.hpp index 5b0ddca..47f70f0 100644 --- a/src/internal/policy_containers.hpp +++ b/src/internal/policy_containers.hpp @@ -45,7 +45,7 @@ template class PolicySRBase : public PolicyBase { public: DecisionItem getDecisionItem(const typename TI::match_type &item) const { - for (auto i : *this) { + for (auto &i : *this) { tslog::log_verbose("-read: ", i.getDecision(), " ", i, "\n"); if (item.match(i.getType(), i.getInterface(), i.getPath(), diff --git a/src/internal/storage_backend_serialized.cpp b/src/internal/storage_backend_serialized.cpp index fb5ccda..ae2b350 100644 --- a/src/internal/storage_backend_serialized.cpp +++ b/src/internal/storage_backend_serialized.cpp @@ -162,10 +162,18 @@ void StorageBackendSerialized::printContent() const { pimpl->printContent(); } +inline boost::string_ref s(const flatbuffers::String *str) { + return boost::string_ref(str->c_str(), str->size()); +} + template bool match(const T &match, const I *i) { - return match.match(makeMessageType(i->type()), i->interface()->c_str(), i->path()->c_str(), - i->member()->c_str(), i->name()->c_str(), i->is_name_prefix()); + return match.match(makeMessageType(i->type()), + s(i->interface()), + s(i->path()), + s(i->member()), + s(i->name()), + i->is_name_prefix()); } template <> bool match(const ldp_xml_parser::MatchItemAccess &match, const FB::ItemAccess *item) { -- 2.7.4 From c33f68b92aa785fdc754c74197c57a1f81864a90 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Wed, 6 Mar 2019 11:10:23 +0100 Subject: [PATCH 08/16] Add test to check performance of loading config files Change-Id: I6354bc17befa330f1b63efc792ab951a9c0ebe39 --- Makefile.am | 5 +- src/stest_load_perf.cpp | 157 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 src/stest_load_perf.cpp diff --git a/Makefile.am b/Makefile.am index 0ac5c8b..be78f32 100644 --- a/Makefile.am +++ b/Makefile.am @@ -190,7 +190,7 @@ libdbuspolicy_tests_SOURCES = src/test_runner.c runnerdir = ${libdir}/dbus-tests/runner/ alonetestdir = ${libdir}/dbus-tests/test-suites/libdbuspolicy-tests/ -alonetest_PROGRAMS = dbus_daemon stest_ownership stest_method_call stest_signal stest_cynara stest_memory stest_performance +alonetest_PROGRAMS = dbus_daemon stest_ownership stest_method_call stest_signal stest_cynara stest_memory stest_performance stest_load_perf dbus_daemon_SOURCES = src/dbus_daemon.c stest_ownership_SOURCES = src/stest_ownership.c src/stest_common.c @@ -199,6 +199,7 @@ stest_signal_SOURCES = src/stest_signal.c src/stest_common.c stest_cynara_SOURCES = src/stest_cynara.c src/stest_common.c stest_memory_SOURCES = src/stest_memory.c stest_performance_SOURCES = src/stest_performance.cpp +stest_load_perf_SOURCES = src/stest_load_perf.cpp stest_ownership_LDADD = src/libinternalfortests.a -lexpat -lstdc++ $(CYNARA_LIBS) $(DLOG_LIBS) stest_method_call_LDADD = src/libinternalfortests.a -lexpat -lstdc++ $(CYNARA_LIBS) $(DLOG_LIBS) @@ -206,6 +207,8 @@ stest_signal_LDADD = src/libinternalfortests.a -lexpat -lstdc++ $(CYNARA_LIBS) $ stest_cynara_LDADD = src/libinternalfortests.a -lexpat -lstdc++ $(CYNARA_LIBS) $(DLOG_LIBS) stest_memory_LDADD = src/libinternalfortests.a -lexpat -lstdc++ $(CYNARA_LIBS) $(DLOG_LIBS) stest_performance_LDADD = src/libinternal.a -lexpat -lstdc++ $(CYNARA_LIBS) $(DLOG_LIBS) +stest_load_perf_LDADD = src/libinternal.a -lexpat -lstdc++ $(CYNARA_LIBS) $(DLOG_LIBS) +stest_load_perf_CPPFLAGS = -ggdb ${AM_CPPFLAGS} -O0 all-tests:: $(alonetest_PROGRAMS) $(runner_PROGRAMS) endif diff --git a/src/stest_load_perf.cpp b/src/stest_load_perf.cpp new file mode 100644 index 0000000..70208ca --- /dev/null +++ b/src/stest_load_perf.cpp @@ -0,0 +1,157 @@ +#include +#include + +#include +#include +#include +#include +#include "internal/internal.h" +#include "internal/policy.hpp" +#include "internal/naive_policy_checker.hpp" +#include "internal/serializer.hpp" +#include "internal/include/fb_generated.h" +#include "internal/storage_backend_serialized.hpp" +#include "internal/storage_backend_serialized.hpp" +#include "libdbuspolicy1-private.h" + +using namespace ldp_xml_parser; +using namespace ldp_serialized; + +enum class Choice { + NONE, + FB, + XML, + XMLplusFB, + ALL, +}; + +bool measure(std::function func, size_t count, const char *desc) { + bool flag = true; + clock_t begin = clock(); + for (size_t i = 0; i < count; i++) + flag &= func(); + clock_t end = clock(); + + std::cout << desc << ": " << static_cast(end - begin)/CLOCKS_PER_SEC << std::endl; + return flag; +} + +bool run_xml(const char *conf_file) { + return __internal_init(SYSTEM_BUS, conf_file) == 0; +} + +bool run_xml_plus_fb(const char *conf_file) { + Serializer serializer; + size_t size; + uint8_t *buff = serializer.serialize(conf_file, size); + + const FB::File *file = FB::GetFile(buff); + + StorageBackendSerialized storage; + return storage.init(file); +} + +bool run_fb(const char *conf_file) { + StorageBackendSerialized sbs; + return sbs.init(conf_file, true); +} + +void run_tests(const char *conf_file, const char *conf_bin, size_t c, Choice ch) { + if (ch == Choice::ALL || ch == Choice::XML) { + if (!measure([&conf_file, c]() { return run_xml(conf_file); }, c, "XML")) { + cout << "ERROR" << endl; + } + } + if (ch == Choice::ALL || ch == Choice::FB) { + if (!measure([&conf_bin, c]() { return run_fb(conf_bin); }, c, "FB")) { + cout << "ERROR" << endl; + } + } + if (ch == Choice::ALL || ch == Choice::XMLplusFB) + if (!measure([&conf_file, c]() { return run_xml_plus_fb(conf_file); }, c, "FB after XML")) { + cout << "ERROR" << endl; + } +} + +void print_help(const char *name) { + cout << endl; + cout << "usage: " << name << " {-f |-x|-d|-a } {--system|--session|-c } " << endl; + cout << endl; + cout << " -f - Flatbuffers" << endl; + cout << " -x - XML" << endl; + cout << " -d - FB after XML" << endl; + cout << " -a - All tests" << endl; + cout << endl; +} + +static const struct option options[] { + {"system", no_argument, 0, 0}, + {"session", no_argument, 0, 0} +}; + +int main(int argc, char *argv[]) +{ + int c; + std::string input_filename = system_bus_conf_file_primary(); + std::string binary_file = ""; + size_t count = 100; + Choice choice = Choice::NONE; + + while (1) { + int option_index; + c = getopt_long(argc, argv, "f:xda:c:", options, &option_index); + if (c == -1) + break; + switch(c) { + case 0: + if (option_index == 1) + input_filename = session_bus_conf_file_primary(); + break; + case 'a': + if (choice != Choice::NONE) { + print_help(argv[0]); + return -1; + } + choice = Choice::ALL; + binary_file = optarg; + break; + case 'f': + if (choice != Choice::NONE) { + print_help(argv[0]); + return -1; + } + choice = Choice::FB; + binary_file = optarg; + break; + case 'x': + if (choice != Choice::NONE) { + print_help(argv[0]); + return -1; + } + choice = Choice::XML; + break; + case 'd': + if (choice != Choice::NONE) { + print_help(argv[0]); + return -1; + } + choice = Choice::XMLplusFB; + break; + case 'c': + input_filename = optarg; + break; + } + } + + if (optind < argc) { + count = stoi(argv[optind]); + } else { + print_help(argv[0]); + return 1; + } + + __internal_init_once(); + run_tests(input_filename.c_str(), binary_file.c_str(), count, choice); + + return 0; +} -- 2.7.4 From e9f42d681816f46d4b5b94781ec17aa7b54d6b7d Mon Sep 17 00:00:00 2001 From: Adrian Szyndela Date: Tue, 5 Mar 2019 11:29:11 +0100 Subject: [PATCH 09/16] refactoring: make two checkers for two buses This changes NaivePolicyChecker to contain only one database per object. Thus, we need to have two NaivePolicyCheckers. It changes relation between NaivePolicyChecker and NaivePolicyDb to 1:1 relation. It will help with removal of NaivePolicyDb. After this, each bus (session and system) has its own, dedicated checker. Change-Id: I7b07db0803b001e5a591a090d259666de2f7074b --- src/internal/internal.cpp | 18 ++++---- src/internal/naive_policy_checker.cpp | 51 ++++++++++------------ src/internal/naive_policy_checker.hpp | 38 ++++++++-------- src/internal/serializer.cpp | 6 +-- src/internal/serializer.hpp | 2 +- src/stest_performance.cpp | 2 +- src/test-libdbuspolicy1-access-deny-gdi.cpp | 6 +-- src/test-libdbuspolicy1-method-gdi.cpp | 2 +- src/test-libdbuspolicy1-ownership-deny-gdi.cpp | 2 +- src/test-libdbuspolicy1-ownership-gdi.cpp | 2 +- ...buspolicy1-send_destination_prefix-deny-gdi.cpp | 2 +- src/test-libdbuspolicy1-signal-gdi.cpp | 2 +- 12 files changed, 64 insertions(+), 69 deletions(-) diff --git a/src/internal/internal.cpp b/src/internal/internal.cpp index d5dcd22..d1c75fc 100644 --- a/src/internal/internal.cpp +++ b/src/internal/internal.cpp @@ -36,7 +36,7 @@ static const char* get_str(const char* const szstr) { int __internal_init(BusType bus_type, const char* const config_name) { - auto ok = policy_checker().initDb(bus_type, get_str(config_name)); + auto ok = policy_checker(bus_type).initDb(get_str(config_name)); if (tslog::enabled()) memory_dump(bus_type); return ok ? 0 : -1; @@ -46,7 +46,7 @@ pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; void memory_dump(BusType bus_type) { - policy_checker().printContent(bus_type); + policy_checker(bus_type).printContent(); } void __internal_init_once() @@ -65,7 +65,7 @@ void __internal_init_flush_logs() void __internal_init_sup_group(BusType bus_type, uid_t uid, gid_t gid) { - policy_checker().updateGroupDb(bus_type, uid, gid); + policy_checker(bus_type).updateGroupDb(uid, gid); } void __internal_enter() @@ -85,7 +85,7 @@ int __internal_can_open(BusType bus_type, gid_t group, const char* const label) { - return static_cast(policy_checker().check(bus_type, bus_owner, user, group, label)); + return static_cast(policy_checker(bus_type).check(bus_owner, user, group, label)); } int __internal_can_send(BusType bus_type, @@ -103,7 +103,7 @@ int __internal_can_send(BusType bus_type, tslog::log_verbose("Destination too long: ", destination, "\n"); return false; } - return static_cast(policy_checker().check(bus_type, user, group, label, matcher)); + return static_cast(policy_checker(bus_type).check(user, group, label, matcher)); } int __internal_can_send_multi_dest(BusType bus_type, @@ -122,7 +122,7 @@ int __internal_can_send_multi_dest(BusType bus_type, while (destination[i]) { matcher.addName(destination[i++]); } - return static_cast(policy_checker().check(bus_type, user, group, label, matcher)); + return static_cast(policy_checker(bus_type).check(user, group, label, matcher)); } int __internal_can_recv(BusType bus_type, @@ -140,7 +140,7 @@ int __internal_can_recv(BusType bus_type, tslog::log_verbose("Sender too long: ", sender, "\n"); return false; } - return static_cast(policy_checker().check(bus_type, user, group, label, matcher)); + return static_cast(policy_checker(bus_type).check(user, group, label, matcher)); } int __internal_can_recv_multi(BusType bus_type, @@ -159,7 +159,7 @@ int __internal_can_recv_multi(BusType bus_type, while (sender[i]) { matcher.addName(sender[i++]); } - return static_cast(policy_checker().check(bus_type, user, group, label, matcher)); + return static_cast(policy_checker(bus_type).check(user, group, label, matcher)); } @@ -169,5 +169,5 @@ int __internal_can_own(BusType bus_type, const char* const label, const char* const service) { - return static_cast(policy_checker().check(bus_type, user, group, label, MatchItemOwn(service))); + return static_cast(policy_checker(bus_type).check(user, group, label, MatchItemOwn(service))); } diff --git a/src/internal/naive_policy_checker.cpp b/src/internal/naive_policy_checker.cpp index ab7f78f..ea6d111 100644 --- a/src/internal/naive_policy_checker.cpp +++ b/src/internal/naive_policy_checker.cpp @@ -9,11 +9,8 @@ using namespace ldp_xml_parser; -DEF_NODESTRUCT_GLOBAL(ldp_xml_parser::NaivePolicyChecker, policy_checker) - -NaivePolicyDb& NaivePolicyChecker::getPolicyDb(BusType type) { - return m_bus_db[type]; -} +DEF_NODESTRUCT_GLOBAL(ldp_xml_parser::NaivePolicyChecker, policy_checker_system) +DEF_NODESTRUCT_GLOBAL(ldp_xml_parser::NaivePolicyChecker, policy_checker_session) DecisionResult NaivePolicyChecker::parseDecision(const DecisionItem& decision, uid_t uid, @@ -40,9 +37,9 @@ DecisionResult NaivePolicyChecker::parseDecision(const DecisionItem& decision, return DecisionResult::DENY; } -DecisionItem NaivePolicyChecker::checkItemAccess(BusType bus_type, const MatchItemAccess& item) +DecisionItem NaivePolicyChecker::checkItemAccess(const MatchItemAccess& item) { - const NaivePolicyDb& policy_db = getPolicyDb(bus_type); + const NaivePolicyDb& policy_db = getPolicyDb(); DecisionItem ret = policy_db.getDecisionItemContextMandatory(item); // access rules can be defined only in default/mandatory context @@ -54,13 +51,12 @@ DecisionItem NaivePolicyChecker::checkItemAccess(BusType bus_type, const MatchIt return ret; } -DecisionResult NaivePolicyChecker::check(BusType bus_type, - uid_t bus_owner, +DecisionResult NaivePolicyChecker::check(uid_t bus_owner, uid_t uid, gid_t gid, const char* const label) { - const auto &gids = *getPolicyDb(bus_type).getGroups(uid, gid); - auto ret = checkItemAccess(bus_type, MatchItemAccess(uid, gids)); + const auto &gids = *getPolicyDb().getGroups(uid, gid); + auto ret = checkItemAccess(MatchItemAccess(uid, gids)); if (ret.getDecision() == Decision::ANY) { if (bus_owner == uid) { ret = Decision::ALLOW; @@ -70,21 +66,20 @@ DecisionResult NaivePolicyChecker::check(BusType bus_type, } template -DecisionResult NaivePolicyChecker::check(BusType bus_type, - uid_t uid, +DecisionResult NaivePolicyChecker::check(uid_t uid, gid_t gid, const char* const label, const T &matchItem) { - auto ret = checkItem(bus_type, uid, gid, matchItem); + auto ret = checkItem(uid, gid, matchItem); return parseDecision(ret, uid, label); } -template DecisionResult NaivePolicyChecker::check(BusType, uid_t, gid_t, const char *, const MatchItemOwn &); -template DecisionResult NaivePolicyChecker::check(BusType, uid_t, gid_t, const char *, const MatchItemSend &); -template DecisionResult NaivePolicyChecker::check(BusType, uid_t, gid_t, const char *, const MatchItemReceive &); +template DecisionResult NaivePolicyChecker::check(uid_t, gid_t, const char *, const MatchItemOwn &); +template DecisionResult NaivePolicyChecker::check(uid_t, gid_t, const char *, const MatchItemSend &); +template DecisionResult NaivePolicyChecker::check(uid_t, gid_t, const char *, const MatchItemReceive &); template -DecisionItem NaivePolicyChecker::checkItem(BusType bus_type, uid_t uid, gid_t gid, const T& item) { - const NaivePolicyDb& policy_db = getPolicyDb(bus_type); +DecisionItem NaivePolicyChecker::checkItem(uid_t uid, gid_t gid, const T& item) { + const NaivePolicyDb& policy_db = getPolicyDb(); DecisionItem ret = policy_db.getDecisionItemContextMandatory(item); @@ -92,7 +87,7 @@ DecisionItem NaivePolicyChecker::checkItem(BusType bus_type, uid_t uid, gid_t gi ret = policy_db.getDecisionItemUser(uid, item); if (ret.getDecision() == Decision::ANY) - ret = checkGroupPolicies(policy_db, uid, gid, item); + ret = checkGroupPolicies(uid, gid, item); if (ret.getDecision() == Decision::ANY) ret = policy_db.getDecisionItemContextDefault(item); @@ -101,7 +96,9 @@ DecisionItem NaivePolicyChecker::checkItem(BusType bus_type, uid_t uid, gid_t gi } template -DecisionItem NaivePolicyChecker::checkGroupPolicies(const NaivePolicyDb& policy_db, uid_t uid, gid_t gid, const T& item) { +DecisionItem NaivePolicyChecker::checkGroupPolicies(uid_t uid, gid_t gid, const T& item) { + const NaivePolicyDb& policy_db = getPolicyDb(); + const auto *sgroups = policy_db.getGroups(uid, gid); if (sgroups == nullptr) return Decision::ANY; @@ -115,17 +112,17 @@ DecisionItem NaivePolicyChecker::checkGroupPolicies(const NaivePolicyDb& policy_ return Decision::ANY; } -void NaivePolicyChecker::updateGroupDb(BusType bus_type, uid_t uid, gid_t gid) +void NaivePolicyChecker::updateGroupDb(uid_t uid, gid_t gid) { - getPolicyDb(bus_type).initializeGroups(uid, gid); + getPolicyDb().initializeGroups(uid, gid); } -bool NaivePolicyChecker::initDb(BusType bus_type, const char *config_name) +bool NaivePolicyChecker::initDb(const char *config_name) { - return getPolicyDb(bus_type).init(config_name); + return getPolicyDb().init(config_name); } -void NaivePolicyChecker::printContent(BusType bus_type) +void NaivePolicyChecker::printContent() { - getPolicyDb(bus_type).printContent(); + getPolicyDb().printContent(); } diff --git a/src/internal/naive_policy_checker.hpp b/src/internal/naive_policy_checker.hpp index e1a6e5a..b4004aa 100644 --- a/src/internal/naive_policy_checker.hpp +++ b/src/internal/naive_policy_checker.hpp @@ -36,7 +36,7 @@ namespace ldp_xml_parser private: /** Policy databases for system and session bus */ - NaivePolicyDb m_bus_db[2]; + NaivePolicyDb m_bus_db; /** Parses delivered decision. In case of Decision::CHECK calls cynara. * \param[in] decision Decision from checkers @@ -51,7 +51,6 @@ namespace ldp_xml_parser const char* label) const; /** Checks policy for a given own, send or receive item - * \param[in] bus_type Bus type (system/session) * \param[in] uid User id * \param[in] gid User group id * \param[in] item Item to check @@ -59,57 +58,50 @@ namespace ldp_xml_parser * \ingroup Implementation */ template - DecisionItem checkItem(BusType bus_type, - uid_t uid, + DecisionItem checkItem(uid_t uid, gid_t gid, const T& item); /** Checks policy for a given access item - * \param[in] bus_type Bus type (system/session) * \param[in] item Item to check * \return Returns deny=0, allow=1 or cynara error * \ingroup Implementation */ - DecisionItem checkItemAccess(BusType bus_type, const MatchItemAccess &item); + DecisionItem checkItemAccess(const MatchItemAccess &item); template - DecisionItem checkGroupPolicies(const NaivePolicyDb& policy_db, - uid_t uid, + DecisionItem checkGroupPolicies(uid_t uid, gid_t gid, const T& item); public: /** Retrieves policy db - * \param[in] type Type of database (system/session bus) * \return Returns reference to chosen bus policy db */ - NaivePolicyDb& getPolicyDb(BusType type); + NaivePolicyDb& getPolicyDb() { return m_bus_db; } /** Clears all db data, useful for reloading configuration * during testing. */ - bool initDb(BusType bus_type, const char *filename); + bool initDb(const char *filename); - void updateGroupDb(BusType bus_type, uid_t uid, gid_t gid); + void updateGroupDb(uid_t uid, gid_t gid); /** Prints to stderr the structures and the amount of their memory */ - void printContent(BusType bus_type); + void printContent(); /** Checks access/open policy for given item - * \param[in] bus_type Bus type (system/session) * \param[in] uid User id * \param[in] gid User group id * \return Returns deny=0, allow=1 or cynara error * \ingroup Implementation */ - DecisionResult check(BusType bus_type, - uid_t bus_owner, + DecisionResult check(uid_t bus_owner, uid_t uid, gid_t gid, const char* const label); /** Checks send/receive/ownership policy for given item - * \param[in] bus_type Bus type (system/session) * \param[in] uid User id * \param[in] gid User group id * \param[in] label User label @@ -118,14 +110,20 @@ namespace ldp_xml_parser * \ingroup Implementation */ template - DecisionResult check(BusType bus_type, - uid_t uid, + DecisionResult check(uid_t uid, gid_t gid, const char* const label, const T &matchItem); }; } -DCL_NODESTRUCT_GLOBAL(ldp_xml_parser::NaivePolicyChecker, policy_checker) +DCL_NODESTRUCT_GLOBAL(ldp_xml_parser::NaivePolicyChecker, policy_checker_system) +DCL_NODESTRUCT_GLOBAL(ldp_xml_parser::NaivePolicyChecker, policy_checker_session) + +inline ldp_xml_parser::NaivePolicyChecker &policy_checker(BusType bus_type) { + if (SESSION_BUS == bus_type) + return policy_checker_session(); + return policy_checker_system(); +} #endif diff --git a/src/internal/serializer.cpp b/src/internal/serializer.cpp index af69580..1d9970d 100644 --- a/src/internal/serializer.cpp +++ b/src/internal/serializer.cpp @@ -83,8 +83,8 @@ struct Serializer::type_helper { static constexpr auto create_item = &FB::CreateItemAccess; }; -uint8_t* Serializer::serialize(const BusType bus_type, size_t &size) { - m_db = &policy_checker().getPolicyDb(bus_type); +uint8_t* Serializer::serialize(const NaivePolicyDb &db, size_t &size) { + m_db = &db; auto own_set = serialize_set(); auto send_set = serialize_set(); @@ -110,7 +110,7 @@ uint8_t* Serializer::serialize(const std::string config_path, size_t &size) { if (__internal_init(BusType::SYSTEM_BUS, config_path.c_str()) != 0) cout << "internal_init error" << endl; - return serialize(BusType::SYSTEM_BUS, size); + return serialize(policy_checker_system().getPolicyDb(), size); } void Serializer::serialize(const std::string config_path, ostream &output) { diff --git a/src/internal/serializer.hpp b/src/internal/serializer.hpp index 12be5b0..582687a 100644 --- a/src/internal/serializer.hpp +++ b/src/internal/serializer.hpp @@ -69,7 +69,7 @@ namespace ldp_xml_parser FbOff context_mandatory) -> FbOff::set>; public: - uint8_t *serialize(const BusType bus_type, size_t &size); + uint8_t *serialize(const NaivePolicyDb &db, size_t &size); uint8_t *serialize(const std::string config_path, size_t &size); void serialize(const std::string config_path, ostream &output); friend class SerializerTests; diff --git a/src/stest_performance.cpp b/src/stest_performance.cpp index 48e503b..f37b7ad 100644 --- a/src/stest_performance.cpp +++ b/src/stest_performance.cpp @@ -189,7 +189,7 @@ void run_x_times(std::function func, size_t times) { void run_policy_db(const char *conf_file, size_t count) { __internal_init(SYSTEM_BUS, conf_file); - auto &db = policy_checker().getPolicyDb(SYSTEM_BUS); + auto &db = policy_checker_system().getPolicyDb(); printf("XML:\n"); run_x_times([&db](){ send_prefix_test(db); }, count); diff --git a/src/test-libdbuspolicy1-access-deny-gdi.cpp b/src/test-libdbuspolicy1-access-deny-gdi.cpp index f42a5f7..5e6046d 100644 --- a/src/test-libdbuspolicy1-access-deny-gdi.cpp +++ b/src/test-libdbuspolicy1-access-deny-gdi.cpp @@ -94,7 +94,7 @@ bool run_tests_for_bus(const DB &db, const BusType bus_type, const std::vectorgetGroups(test.user, test.group); auto m_item = MatchItemAccess(test.user, gids); @@ -130,8 +130,8 @@ bool run_policy_db(const std::pair access_test) { __internal_init(SESSION_BUS, session_bus_setup.first.c_str()); } - auto *sys_db = &policy_checker().getPolicyDb(SYSTEM_BUS); - auto *ses_db = &policy_checker().getPolicyDb(SESSION_BUS); + auto *sys_db = &policy_checker_system().getPolicyDb(); + auto *ses_db = &policy_checker_session().getPolicyDb(); printf("POLICY_DB:\n"); return run_tests_for_bus(*sys_db, SYSTEM_BUS, system_bus_setup.second, i, passed) && diff --git a/src/test-libdbuspolicy1-method-gdi.cpp b/src/test-libdbuspolicy1-method-gdi.cpp index 16c6c45..64ae2c6 100644 --- a/src/test-libdbuspolicy1-method-gdi.cpp +++ b/src/test-libdbuspolicy1-method-gdi.cpp @@ -121,7 +121,7 @@ bool method_test(DB &db) { bool run_policy_db() { __internal_init(SYSTEM_BUS, "tests/default_allow/system.conf"); - auto &db = policy_checker().getPolicyDb(SYSTEM_BUS); + auto &db = policy_checker_system().getPolicyDb(); printf("POLICY_DB:\n"); return method_test(db); } diff --git a/src/test-libdbuspolicy1-ownership-deny-gdi.cpp b/src/test-libdbuspolicy1-ownership-deny-gdi.cpp index 3bee8f6..af4bc20 100644 --- a/src/test-libdbuspolicy1-ownership-deny-gdi.cpp +++ b/src/test-libdbuspolicy1-ownership-deny-gdi.cpp @@ -175,7 +175,7 @@ bool ownership_test(const DB &db) { bool run_policy_db() { __internal_init(SYSTEM_BUS, "tests/default_deny/system.conf"); - auto &db = policy_checker().getPolicyDb(SYSTEM_BUS); + auto &db = policy_checker_system().getPolicyDb(); printf("POLICY_DB:\n"); return ownership_test(db); } diff --git a/src/test-libdbuspolicy1-ownership-gdi.cpp b/src/test-libdbuspolicy1-ownership-gdi.cpp index 9a3e00a..f517685 100644 --- a/src/test-libdbuspolicy1-ownership-gdi.cpp +++ b/src/test-libdbuspolicy1-ownership-gdi.cpp @@ -101,7 +101,7 @@ bool ownership_test(const DB &db) { bool run_policy_db() { __internal_init(SYSTEM_BUS, "tests/default_allow/system.conf"); - auto &db = policy_checker().getPolicyDb(SYSTEM_BUS); + auto &db = policy_checker_system().getPolicyDb(); printf("POLICY DB:\n"); return ownership_test(db); } diff --git a/src/test-libdbuspolicy1-send_destination_prefix-deny-gdi.cpp b/src/test-libdbuspolicy1-send_destination_prefix-deny-gdi.cpp index 45938c5..a7e96d1 100644 --- a/src/test-libdbuspolicy1-send_destination_prefix-deny-gdi.cpp +++ b/src/test-libdbuspolicy1-send_destination_prefix-deny-gdi.cpp @@ -183,7 +183,7 @@ bool send_prefix_test(const DB &db) bool run_policy_db() { __internal_init(SYSTEM_BUS, "tests/default_deny/system.conf"); - auto &db = policy_checker().getPolicyDb(SYSTEM_BUS); + auto &db = policy_checker_system().getPolicyDb(); printf("POLICY_DB:\n"); return send_prefix_test(db); } diff --git a/src/test-libdbuspolicy1-signal-gdi.cpp b/src/test-libdbuspolicy1-signal-gdi.cpp index 5f7f04d..dcf39aa 100644 --- a/src/test-libdbuspolicy1-signal-gdi.cpp +++ b/src/test-libdbuspolicy1-signal-gdi.cpp @@ -77,7 +77,7 @@ bool signal_test(const DB &db) { bool run_policy_db() { __internal_init(SYSTEM_BUS, "tests/default_allow/system.conf"); - auto &db = policy_checker().getPolicyDb(SYSTEM_BUS); + auto &db = policy_checker_system().getPolicyDb(); printf("POLICY_DB:\n"); return signal_test(db); -- 2.7.4 From c29fde0ac7d09cb1c42b7e76c61798d91acf00f3 Mon Sep 17 00:00:00 2001 From: Adrian Szyndela Date: Tue, 5 Mar 2019 12:37:26 +0100 Subject: [PATCH 10/16] refactoring: merge NaivePolicyDb to NaivePolicyChecker NaivePolicyDb's role was recently limited to management of group maps, and passing requests to a backend. This removes NaivePolicyDb by moving management of group maps to NaivePolicyChecker and using the backend directly. Change-Id: Iabbb790c7e18716bb0b99a178e7a26252dcfc41a --- Makefile.am | 1 - src/internal/internal.cpp | 1 - src/internal/naive_policy_checker.cpp | 120 +++++++++++++++++-- src/internal/naive_policy_checker.hpp | 28 ++++- src/internal/naive_policy_db.cpp | 179 ---------------------------- src/internal/naive_policy_db.hpp | 105 ---------------- src/internal/policy.cpp | 1 - src/internal/serializer.cpp | 3 +- src/internal/serializer.hpp | 6 +- src/internal/storage_backend_xml.hpp | 2 +- src/test-libdbuspolicy1-access-deny-gdi.cpp | 5 +- src/test-serializer.cpp | 1 - 12 files changed, 140 insertions(+), 312 deletions(-) delete mode 100644 src/internal/naive_policy_db.cpp delete mode 100644 src/internal/naive_policy_db.hpp diff --git a/Makefile.am b/Makefile.am index be78f32..5c47c77 100644 --- a/Makefile.am +++ b/Makefile.am @@ -55,7 +55,6 @@ COMMON_SRC =\ src/libdbuspolicy1.c \ src/internal/internal.cpp \ src/internal/naive_policy_checker.cpp \ - src/internal/naive_policy_db.cpp \ src/internal/policy.cpp \ src/internal/policy_containers.cpp \ src/internal/own_tree.cpp \ diff --git a/src/internal/internal.cpp b/src/internal/internal.cpp index d1c75fc..9c1d499 100644 --- a/src/internal/internal.cpp +++ b/src/internal/internal.cpp @@ -23,7 +23,6 @@ #include #include "xml_parser.hpp" #include "policy.hpp" -#include "naive_policy_db.hpp" #include "naive_policy_checker.hpp" #include "internal.h" #include "tslog.hpp" diff --git a/src/internal/naive_policy_checker.cpp b/src/internal/naive_policy_checker.cpp index ea6d111..b5402f6 100644 --- a/src/internal/naive_policy_checker.cpp +++ b/src/internal/naive_policy_checker.cpp @@ -1,17 +1,95 @@ #include "naive_policy_checker.hpp" #include "cynara.hpp" #include "tslog.hpp" +#include "groups_proxy.hpp" /** * \file * \ingroup Implementation */ -using namespace ldp_xml_parser; DEF_NODESTRUCT_GLOBAL(ldp_xml_parser::NaivePolicyChecker, policy_checker_system) DEF_NODESTRUCT_GLOBAL(ldp_xml_parser::NaivePolicyChecker, policy_checker_session) +namespace ldp_xml_parser { + +template +VGid &NaivePolicyChecker::getMapGroup(uid_t uid) +{ + return mapGroups[type_list::IDX::idx][uid]; +} + +void NaivePolicyChecker::updateSupplementaryGroups(const VGid &groups, uid_t uid, gid_t gid) { + auto &vsend = getMapGroup(uid); + auto &vrecv = getMapGroup(uid); + auto &vaccess = getMapGroup(uid); + + if (groups.empty()) { + vsend.push_back(gid); + vrecv.push_back(gid); + vaccess.push_back(gid); + return; + } + + /* insert supplementary group */ + for (const auto group : groups) { + if (getPolicyDb().existsPolicyForGroup(group)) + vsend.push_back(group); + if (getPolicyDb().existsPolicyForGroup(group)) + vrecv.push_back(group); + vaccess.push_back(group); // no filtering, it will be used once + } + + if (vsend.empty()) + vsend.push_back(-1); + if (vrecv.empty()) + vrecv.push_back(-1); +} + +void NaivePolicyChecker::updateSupplementaryGroupsOwn(const VGid &groups, uid_t uid, gid_t gid) { + auto &vown = getMapGroup(uid); + if (groups.empty()) { + vown.push_back(gid); + } else { + for (const auto group : groups) { + if (getPolicyDb().existsPolicyForGroup(group)) + vown.push_back(group); + } + } +} + +template <> +const VGid *NaivePolicyChecker::getGroups(uid_t uid, gid_t) +{ + return &getMapGroup(uid); +} + +template <> +const VGid *NaivePolicyChecker::getGroups(uid_t uid, gid_t) +{ + return &getMapGroup(uid); +} + +template +const VGid *NaivePolicyChecker::getGroups(uid_t uid, gid_t gid) +{ + if (uid == getuid() && gid == getgid()) + return &getMapGroup(uid); + + pthread_mutex_lock(&mutexGroup); + auto &vgid = getMapGroup(uid); + + if (vgid.empty()) + updateSupplementaryGroups(get_groups(uid, gid), uid, gid); + pthread_mutex_unlock(&mutexGroup); + + if (vgid[0] == (gid_t)-1) + return nullptr; + + return &vgid; +} + DecisionResult NaivePolicyChecker::parseDecision(const DecisionItem& decision, uid_t uid, const char* label) const { @@ -39,7 +117,7 @@ DecisionResult NaivePolicyChecker::parseDecision(const DecisionItem& decision, DecisionItem NaivePolicyChecker::checkItemAccess(const MatchItemAccess& item) { - const NaivePolicyDb& policy_db = getPolicyDb(); + const ldp_xml::StorageBackendXML &policy_db = getPolicyDb(); DecisionItem ret = policy_db.getDecisionItemContextMandatory(item); // access rules can be defined only in default/mandatory context @@ -55,7 +133,7 @@ DecisionResult NaivePolicyChecker::check(uid_t bus_owner, uid_t uid, gid_t gid, const char* const label) { - const auto &gids = *getPolicyDb().getGroups(uid, gid); + const auto &gids = *getGroups(uid, gid); auto ret = checkItemAccess(MatchItemAccess(uid, gids)); if (ret.getDecision() == Decision::ANY) { if (bus_owner == uid) { @@ -79,7 +157,7 @@ template DecisionResult NaivePolicyChecker::check(uid_t, gid_t, const char *, co template DecisionItem NaivePolicyChecker::checkItem(uid_t uid, gid_t gid, const T& item) { - const NaivePolicyDb& policy_db = getPolicyDb(); + const ldp_xml::StorageBackendXML &policy_db = getPolicyDb(); DecisionItem ret = policy_db.getDecisionItemContextMandatory(item); @@ -97,14 +175,12 @@ DecisionItem NaivePolicyChecker::checkItem(uid_t uid, gid_t gid, const T& item) template DecisionItem NaivePolicyChecker::checkGroupPolicies(uid_t uid, gid_t gid, const T& item) { - const NaivePolicyDb& policy_db = getPolicyDb(); - - const auto *sgroups = policy_db.getGroups(uid, gid); + const auto *sgroups = getGroups(uid, gid); if (sgroups == nullptr) return Decision::ANY; for (const auto sgid : *sgroups) { - DecisionItem ret = policy_db.getDecisionItemGroup(sgid, item); + DecisionItem ret = getPolicyDb().getDecisionItemGroup(sgid, item); if (ret.getDecision() != Decision::ANY) return ret; @@ -114,7 +190,16 @@ DecisionItem NaivePolicyChecker::checkGroupPolicies(uid_t uid, gid_t gid, const void NaivePolicyChecker::updateGroupDb(uid_t uid, gid_t gid) { - getPolicyDb().initializeGroups(uid, gid); + pthread_mutex_lock(&mutexGroup); + + for (auto &mapGroup : mapGroups) + mapGroup.clear(); + + auto groups = get_groups(uid, gid); + updateSupplementaryGroups(groups, uid, gid); + updateSupplementaryGroupsOwn(groups, uid, gid); + + pthread_mutex_unlock(&mutexGroup); } bool NaivePolicyChecker::initDb(const char *config_name) @@ -125,4 +210,21 @@ bool NaivePolicyChecker::initDb(const char *config_name) void NaivePolicyChecker::printContent() { getPolicyDb().printContent(); + + for (const auto &mapGroup : mapGroups) { + std::cerr << std::endl << "---- mapGroup ----" << std::endl; + int size = sizeof(mapGroup); + size += mapGroup.size() * sizeof(typename std::remove_reference::type::value_type); + + for (const auto& i : mapGroup) { + size += i.second.capacity() * sizeof(gid_t); + std::cerr << "uid: " << i.first << " | gids: "; + for (auto j : i.second) + std::cerr << " " << j << ","; + std::cerr << std::endl; + } + std::cerr << "Memory consumption: " << size << " B" << std::endl; + } } + +} // namespace ldp_xml_parser diff --git a/src/internal/naive_policy_checker.hpp b/src/internal/naive_policy_checker.hpp index b4004aa..d34383e 100644 --- a/src/internal/naive_policy_checker.hpp +++ b/src/internal/naive_policy_checker.hpp @@ -24,25 +24,26 @@ #define _NAIVE_DECISIONER_H #include "policy.hpp" -#include "naive_policy_db.hpp" #include "global_nodestruct.hpp" +#include "storage_backend_xml.hpp" namespace ldp_xml_parser { + typedef std::vector VGid; + /* Class which checks rights in policies retrieved from policy db * \ingroup Implementation */ class NaivePolicyChecker { private: - /** Policy databases for system and session bus */ - NaivePolicyDb m_bus_db; + /** Policy database */ + ldp_xml::StorageBackendXML m_bus_db; /** Parses delivered decision. In case of Decision::CHECK calls cynara. * \param[in] decision Decision from checkers * \param[in] uid User id * \param[in] label User label - * \param[in] privilege Privilege string * \return Returns deny=0, allow=1 or cynara error * \ingroup Implementation */ @@ -74,11 +75,23 @@ namespace ldp_xml_parser gid_t gid, const T& item); + /* group maps management */ + std::map mapGroups[MatchItemTypes::count]; + /* A mutex for mapGroups */ + pthread_mutex_t mutexGroup = PTHREAD_MUTEX_INITIALIZER; + + template + VGid &getMapGroup(uid_t uid); + + void updateSupplementaryGroups(const VGid &groups, uid_t uid, gid_t gid); + void updateSupplementaryGroupsOwn(const VGid &groups, uid_t uid, gid_t gid); + public: /** Retrieves policy db - * \return Returns reference to chosen bus policy db + * \return Returns reference to the policy db */ - NaivePolicyDb& getPolicyDb() { return m_bus_db; } + ldp_xml::StorageBackendXML &getPolicyDb() { return m_bus_db; } + const ldp_xml::StorageBackendXML &getPolicyDb() const { return m_bus_db; } /** Clears all db data, useful for reloading configuration * during testing. @@ -87,6 +100,9 @@ namespace ldp_xml_parser void updateGroupDb(uid_t uid, gid_t gid); + template + const VGid *getGroups(uid_t uid, gid_t gid); + /** Prints to stderr the structures and the amount of their memory */ void printContent(); diff --git a/src/internal/naive_policy_db.cpp b/src/internal/naive_policy_db.cpp deleted file mode 100644 index 08e95bc..0000000 --- a/src/internal/naive_policy_db.cpp +++ /dev/null @@ -1,179 +0,0 @@ -#include "policy_containers.hpp" -#include "naive_policy_db.hpp" -#include "cynara.hpp" -#include "groups_proxy.hpp" -#include "print_content.hpp" -#include "tslog.hpp" -#include "type_list.h" -#include "storage_backend_xml.hpp" - -#include -#include - -/** - * \file - * \ingroup Implementation - */ - -namespace ldp_xml_parser { - -void NaivePolicyDb::updateSupplementaryGroups(const VGid &groups, uid_t uid, gid_t gid) const { - auto &vsend = getMapGroup(uid); - auto &vrecv = getMapGroup(uid); - auto &vaccess = getMapGroup(uid); - - if (groups.empty()) { - vsend.push_back(gid); - vrecv.push_back(gid); - vaccess.push_back(gid); - return; - } - - /* insert supplementary group */ - for (const auto group : groups) { - if (backend.existsPolicyForGroup(group)) - vsend.push_back(group); - if (backend.existsPolicyForGroup(group)) - vrecv.push_back(group); - vaccess.push_back(group); // no filtering, it will be used once - } - - if (vsend.empty()) - vsend.push_back(-1); - if (vrecv.empty()) - vrecv.push_back(-1); -} - -void NaivePolicyDb::updateSupplementaryGroupsOwn(const VGid &groups, uid_t uid, gid_t gid) const { - auto &vown = getMapGroup(uid); - if (groups.empty()) { - vown.push_back(gid); - } else { - for (const auto group : groups) { - if (backend.existsPolicyForGroup(group)) - vown.push_back(group); - } - } -} - -/********************* NaivePolicyDb **********************/ -void NaivePolicyDb::printContent() const -{ - backend.printContent(); - - for (const auto &mapGroup : mapGroups) { - std::cerr << std::endl << "---- mapGroup ----" << std::endl; - int size = sizeof(mapGroup); - size += mapGroup.size() * sizeof(typename std::remove_reference::type::value_type); - - for (const auto& i : mapGroup) { - size += i.second.capacity() * sizeof(gid_t); - std::cerr << "uid: " << i.first << " | gids: "; - for (auto j : i.second) - std::cerr << " " << j << ","; - std::cerr << std::endl; - } - std::cerr << "Memory consumption: " << size << " B" << std::endl; - } -} - -template <> -const VGid *NaivePolicyDb::getGroups(uid_t uid, gid_t) const -{ - return &getMapGroup(uid); -} - -template <> -const VGid *NaivePolicyDb::getGroups(uid_t uid, gid_t) const -{ - return &getMapGroup(uid); -} - -template -const VGid *NaivePolicyDb::getGroups(uid_t uid, gid_t gid) const -{ - if (uid == getuid() && gid == getgid()) - return &getMapGroup(uid); - - pthread_mutex_lock(&mutexGroup); - auto &vgid = getMapGroup(uid); - - if (vgid.empty()) - updateSupplementaryGroups(get_groups(uid, gid), uid, gid); - pthread_mutex_unlock(&mutexGroup); - - if (vgid[0] == (gid_t)-1) - return nullptr; - - return &vgid; -} - -void NaivePolicyDb::initializeGroups(uid_t uid, gid_t gid) -{ - pthread_mutex_lock(&mutexGroup); - - for (auto &mapGroup : mapGroups) - mapGroup.clear(); - - auto groups = get_groups(uid, gid); - updateSupplementaryGroups(groups, uid, gid); - updateSupplementaryGroupsOwn(groups, uid, gid); - - pthread_mutex_unlock(&mutexGroup); -} - -template -VGid &NaivePolicyDb::getMapGroup(uid_t uid) const -{ - return mapGroups[type_list::IDX::idx][uid]; -} - -bool NaivePolicyDb::init(const char *filename) { - return backend.init(filename); -} - -template -const T &NaivePolicyDb::getPolicyContextMandatory() const { - return backend.getPolicyContextMandatory(); -} - -template -const T &NaivePolicyDb::getPolicyContextDefault() const { - return backend.getPolicyContextDefault(); -} - -template -const std::map &NaivePolicyDb::getPoliciesUser() const { - return backend.getPoliciesUser(); -} - -template -const std::map &NaivePolicyDb::getPoliciesGroup() const { - return backend.getPoliciesGroup(); -} - -/* Explicit instantiation is needed for used public template methods defined in this file. - */ -#define T_INSTANTIATION(T) \ - template const VGid *NaivePolicyDb::getGroups(uid_t, gid_t) const; \ - template const Policy##T &NaivePolicyDb::getPolicyContextMandatory() const; \ - template const Policy##T &NaivePolicyDb::getPolicyContextDefault() const; - -T_INSTANTIATION(Own) -T_INSTANTIATION(Send) -T_INSTANTIATION(Receive) -T_INSTANTIATION(Access) - -#undef T_INSTANTIATION - -#define T_INSTANTIATION(T) \ - template const std::map &NaivePolicyDb::getPoliciesGroup() const; \ - template const std::map &NaivePolicyDb::getPoliciesUser() const; - -T_INSTANTIATION(Own) -T_INSTANTIATION(Send) -T_INSTANTIATION(Receive) - -#undef T_INSTANTIATION - -} // namespace ldp_xml_parser diff --git a/src/internal/naive_policy_db.hpp b/src/internal/naive_policy_db.hpp deleted file mode 100644 index 1bbea87..0000000 --- a/src/internal/naive_policy_db.hpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ -#ifndef _NAIVE_DB_H -#define _NAIVE_DB_H - -#include "storage_backend_xml.hpp" -#include -#include -#include -#include -#include "policy.hpp" - -/** - * \file - * \ingroup Implementation - */ - -namespace ldp_xml_parser -{ - typedef std::vector VGid; - - /** Database class, contains policies for ownership and send/receive */ - class NaivePolicyDb { - private: - ldp_xml::StorageBackendXML backend; - - mutable std::map mapGroups[MatchItemTypes::count]; - /* A mutex for mapGroups */ - mutable pthread_mutex_t mutexGroup = PTHREAD_MUTEX_INITIALIZER; - - template - VGid &getMapGroup(uid_t uid) const; - - void updateSupplementaryGroups(const VGid &groups, uid_t uid, gid_t gid) const; - void updateSupplementaryGroupsOwn(const VGid &groups, uid_t uid, gid_t gid) const; - - public: - /***** Common interface for all backends ******/ - void printContent() const; - - template - DecisionItem getDecisionItemContextMandatory(const T &item) const - { return backend.getDecisionItemContextMandatory(item); } - - template - DecisionItem getDecisionItemContextDefault(const T &item) const - { return backend.getDecisionItemContextDefault(item); } - - template - DecisionItem getDecisionItemUser(uid_t uid, const T &item) const - { return backend.getDecisionItemUser(uid, item); } - - template - DecisionItem getDecisionItemGroup(gid_t gid, const T &item) const - { return backend.getDecisionItemGroup(gid, item); } - - /******* Common interface for group maps management **********/ - template - const VGid *getGroups(uid_t uid, gid_t gid) const; - - void initializeGroups(uid_t uid, gid_t gid); - - /******* Interface for read-write backends (e.g. XML-based) */ - /* This will be probably moved from here */ - - /** Adds item to a policy - * \param[in] policy_type Policy type - * \param[in] policy_type_value Policy type value - * \param[in] item Item to add - */ - template - void addItem(const PolicyType policy_type, - const PolicyTypeValue policy_type_value, - T &item); - - bool init(const char *filename); - - // The getPolicy*() methods are intended for Serializers to get - // access to policy structures. - // Supported template parameters are: PolicyOwn, PolicySend, PolicyReceive - // and for PolicyAccess only getPolicyContext*() - template - const T &getPolicyContextMandatory() const; - template - const T &getPolicyContextDefault() const; - template - const std::map &getPoliciesUser() const; - template - const std::map &getPoliciesGroup() const; - }; -} -#endif diff --git a/src/internal/policy.cpp b/src/internal/policy.cpp index 6863b3e..af21c59 100644 --- a/src/internal/policy.cpp +++ b/src/internal/policy.cpp @@ -5,7 +5,6 @@ #include "policy.hpp" #include "naive_policy_checker.hpp" -#include "naive_policy_db.hpp" #include "tslog.hpp" #include diff --git a/src/internal/serializer.cpp b/src/internal/serializer.cpp index 1d9970d..1b8566d 100644 --- a/src/internal/serializer.cpp +++ b/src/internal/serializer.cpp @@ -4,7 +4,6 @@ #include "internal.h" #include "naive_policy_checker.hpp" -#include "naive_policy_db.hpp" #include "serializer.hpp" #include "include/flatbuffers/flatbuffers.h" #include "include/fb_generated.h" @@ -83,7 +82,7 @@ struct Serializer::type_helper { static constexpr auto create_item = &FB::CreateItemAccess; }; -uint8_t* Serializer::serialize(const NaivePolicyDb &db, size_t &size) { +uint8_t* Serializer::serialize(const ldp_xml::StorageBackendXML &db, size_t &size) { m_db = &db; auto own_set = serialize_set(); diff --git a/src/internal/serializer.hpp b/src/internal/serializer.hpp index 582687a..f8a35c6 100644 --- a/src/internal/serializer.hpp +++ b/src/internal/serializer.hpp @@ -8,7 +8,7 @@ #include "include/flatbuffers/flatbuffers.h" #include "include/fb_generated.h" -#include "naive_policy_db.hpp" +#include "storage_backend_xml.hpp" #include "policy_containers.hpp" using namespace std; @@ -30,7 +30,7 @@ namespace ldp_xml_parser template struct type_helper; - const NaivePolicyDb *m_db; + const ldp_xml::StorageBackendXML *m_db; flatbuffers::FlatBufferBuilder m_builder; template @@ -69,7 +69,7 @@ namespace ldp_xml_parser FbOff context_mandatory) -> FbOff::set>; public: - uint8_t *serialize(const NaivePolicyDb &db, size_t &size); + uint8_t *serialize(const ldp_xml::StorageBackendXML &db, size_t &size); uint8_t *serialize(const std::string config_path, size_t &size); void serialize(const std::string config_path, ostream &output); friend class SerializerTests; diff --git a/src/internal/storage_backend_xml.hpp b/src/internal/storage_backend_xml.hpp index 84b2acb..6a0c690 100644 --- a/src/internal/storage_backend_xml.hpp +++ b/src/internal/storage_backend_xml.hpp @@ -38,7 +38,7 @@ public: const std::map &getPoliciesGroup() const; // This works with T set to MatchItemOwn, MatchItemSend or MatchItemReceive - // This is needed for filtering mapGroups. Check NaivePolicyDb. + // This is needed for filtering mapGroups. Check NaivePolicyChecker. template bool existsPolicyForGroup(gid_t gid) const; diff --git a/src/test-libdbuspolicy1-access-deny-gdi.cpp b/src/test-libdbuspolicy1-access-deny-gdi.cpp index 5e6046d..7f44f66 100644 --- a/src/test-libdbuspolicy1-access-deny-gdi.cpp +++ b/src/test-libdbuspolicy1-access-deny-gdi.cpp @@ -4,7 +4,6 @@ #include "internal/internal.h" #include "internal/policy.hpp" #include "internal/naive_policy_checker.hpp" -#include "internal/naive_policy_db.hpp" #include "internal/serializer.hpp" #include "internal/include/fb_generated.h" #include "internal/storage_backend_serialized.hpp" @@ -94,9 +93,9 @@ bool run_tests_for_bus(const DB &db, const BusType bus_type, const std::vectorgetGroups(test.user, test.group); + const auto &gids = *checker.getGroups(test.user, test.group); auto m_item = MatchItemAccess(test.user, gids); auto decision = db.getDecisionItemContextMandatory(m_item).getDecision(); if (decision == Decision::ANY) diff --git a/src/test-serializer.cpp b/src/test-serializer.cpp index 95b33fe..63689d4 100644 --- a/src/test-serializer.cpp +++ b/src/test-serializer.cpp @@ -4,7 +4,6 @@ #include "internal/include/flatbuffers/flatbuffers.h" #include "internal/include/fb_generated.h" -#include "internal/naive_policy_db.hpp" #include "internal/policy_containers.hpp" #include "internal/serializer.hpp" -- 2.7.4 From c7c5ad3265c0492fdb353c62284f22d16ed499f1 Mon Sep 17 00:00:00 2001 From: Adrian Szyndela Date: Wed, 6 Mar 2019 12:37:50 +0100 Subject: [PATCH 11/16] serialization: add missing method to serialized storage Change-Id: Ieb56be3dd3f303dca7c851c02757fe6ab5eb64ae --- src/internal/storage_backend_serialized.cpp | 10 ++++++++-- src/internal/storage_backend_serialized.hpp | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/internal/storage_backend_serialized.cpp b/src/internal/storage_backend_serialized.cpp index ae2b350..0590c4a 100644 --- a/src/internal/storage_backend_serialized.cpp +++ b/src/internal/storage_backend_serialized.cpp @@ -112,7 +112,7 @@ bool StorageBackendSerialized::StorageBackendSerializedImpl::init(const char *fi mmapGuard.dismiss(); file = GetFile(mem); - return true; + return file != nullptr; } bool StorageBackendSerialized::StorageBackendSerializedImpl::init(const FB::File *f) { @@ -258,6 +258,11 @@ ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemGroup(gid_ return getDecisionItemMaybeNull(item, pimpl->getPolicySet()->group()->LookupByKey(gid)); } +template +bool StorageBackendSerialized::existsPolicyForGroup(gid_t gid) const { + return pimpl->getPolicySet()->group()->LookupByKey(gid) != nullptr; +} + #define T_INSTANTIATION(T) \ template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const ldp_xml_parser::MatchItem##T &item) const; \ template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const ldp_xml_parser::MatchItem##T &item) const; @@ -271,7 +276,8 @@ T_INSTANTIATION(Access) #define T_INSTANTIATION2(T) \ template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemUser(uid_t uid, const ldp_xml_parser::MatchItem##T &item) const; \ - template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemGroup(gid_t gid, const ldp_xml_parser::MatchItem##T &item) const; + template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemGroup(gid_t gid, const ldp_xml_parser::MatchItem##T &item) const; \ + template bool StorageBackendSerialized::existsPolicyForGroup(gid_t) const; T_INSTANTIATION2(Own) T_INSTANTIATION2(Send) diff --git a/src/internal/storage_backend_serialized.hpp b/src/internal/storage_backend_serialized.hpp index bf48dd7..2e5422d 100644 --- a/src/internal/storage_backend_serialized.hpp +++ b/src/internal/storage_backend_serialized.hpp @@ -31,6 +31,12 @@ public: ldp_xml_parser::DecisionItem getDecisionItemUser(uid_t uid, const T &item) const; template ldp_xml_parser::DecisionItem getDecisionItemGroup(gid_t gid, const T &item) const; + + // This works with T set to MatchItemOwn, MatchItemSend or MatchItemReceive + // This is needed for filtering mapGroups. Check NaivePolicyChecker. + template + bool existsPolicyForGroup(gid_t gid) const; + }; } -- 2.7.4 From 85add34e1b6b5e1c4d1abb2722ae95e33112df10 Mon Sep 17 00:00:00 2001 From: Adrian Szyndela Date: Wed, 6 Mar 2019 14:30:30 +0100 Subject: [PATCH 12/16] serialization: switch backend to serialized Change-Id: I87bf5d7461c168ceb6c2dda034f153060bfe39ee --- src/internal/internal.cpp | 14 +++++++++-- src/internal/internal.h | 18 ++++++++++++++ src/internal/naive_policy_checker.cpp | 37 ++++++++++++++++------------- src/internal/naive_policy_checker.hpp | 12 ++++++---- src/internal/policy.cpp | 1 + src/internal/serializer.cpp | 8 +++++-- src/internal/storage_backend_serialized.cpp | 26 ++++++++++++++++++++ src/internal/storage_backend_serialized.hpp | 1 + src/libdbuspolicy1.c | 2 +- 9 files changed, 93 insertions(+), 26 deletions(-) diff --git a/src/internal/internal.cpp b/src/internal/internal.cpp index 9c1d499..699b1e6 100644 --- a/src/internal/internal.cpp +++ b/src/internal/internal.cpp @@ -33,14 +33,24 @@ static const char* get_str(const char* const szstr) { return (szstr != NULL) ? szstr : ""; } -int __internal_init(BusType bus_type, const char* const config_name) +int __internal_init_serialized(BusType bus_type, const char *config_name, const char *serialized_filename) { - auto ok = policy_checker(bus_type).initDb(get_str(config_name)); + auto ok = policy_checker(bus_type).initDb(get_str(config_name), serialized_filename); if (tslog::enabled()) memory_dump(bus_type); return ok ? 0 : -1; } +int __internal_init(BusType bus_type, const char* const config_name) +{ + return __internal_init_serialized(bus_type, config_name, nullptr); +} + +int __internal_init_auto_serialized(BusType bus_type, const char *config_name) +{ + return __internal_init_serialized(bus_type, config_name, std::string(config_name).append(".serialized").c_str()); +} + pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; void memory_dump(BusType bus_type) diff --git a/src/internal/internal.h b/src/internal/internal.h index 30ff4fd..a3e6047 100644 --- a/src/internal/internal.h +++ b/src/internal/internal.h @@ -47,6 +47,24 @@ typedef enum { */ int __internal_init(BusType bus_type, const char* const config_name); +/** Initializes policies from given serialized policy configuration file name + * but in case of failure uses provided XML policy configuration file name + * \param[in] bus_type Bus type (system/session) + * \param[in] config_name Configuration file name + * \param[in] serialized_filename Serialized policy file name + */ +int __internal_init_serialized(BusType bus_type, + const char* const config_name, + const char *serialized_filename); + +/** Tries to initialize policy from serialized policy file name made up from + * standard XML policy file name by adding ".serialized" suffix to it. + * In case of failure initializes policy from given XML policy file name + * \param[in] bus_type Bus type (system/session) + * \param[in] config_name XML configuration file name + */ +int __internal_init_auto_serialized(BusType bus_type, const char* const config_name); + /** Inits tslog. */ void __internal_init_once(void); diff --git a/src/internal/naive_policy_checker.cpp b/src/internal/naive_policy_checker.cpp index b5402f6..f925566 100644 --- a/src/internal/naive_policy_checker.cpp +++ b/src/internal/naive_policy_checker.cpp @@ -2,6 +2,7 @@ #include "cynara.hpp" #include "tslog.hpp" #include "groups_proxy.hpp" +#include "serializer.hpp" /** * \file @@ -34,9 +35,9 @@ void NaivePolicyChecker::updateSupplementaryGroups(const VGid &groups, uid_t uid /* insert supplementary group */ for (const auto group : groups) { - if (getPolicyDb().existsPolicyForGroup(group)) + if (m_bus_db.existsPolicyForGroup(group)) vsend.push_back(group); - if (getPolicyDb().existsPolicyForGroup(group)) + if (m_bus_db.existsPolicyForGroup(group)) vrecv.push_back(group); vaccess.push_back(group); // no filtering, it will be used once } @@ -53,7 +54,7 @@ void NaivePolicyChecker::updateSupplementaryGroupsOwn(const VGid &groups, uid_t vown.push_back(gid); } else { for (const auto group : groups) { - if (getPolicyDb().existsPolicyForGroup(group)) + if (m_bus_db.existsPolicyForGroup(group)) vown.push_back(group); } } @@ -117,14 +118,12 @@ DecisionResult NaivePolicyChecker::parseDecision(const DecisionItem& decision, DecisionItem NaivePolicyChecker::checkItemAccess(const MatchItemAccess& item) { - const ldp_xml::StorageBackendXML &policy_db = getPolicyDb(); - - DecisionItem ret = policy_db.getDecisionItemContextMandatory(item); + DecisionItem ret = m_bus_db.getDecisionItemContextMandatory(item); // access rules can be defined only in default/mandatory context // defining them elsewhere is considered as policy syntax error by dbus-daemon // thus, no checking in user or group policies if (ret.getDecision() == Decision::ANY) - ret = policy_db.getDecisionItemContextDefault(item); + ret = m_bus_db.getDecisionItemContextDefault(item); return ret; } @@ -157,18 +156,16 @@ template DecisionResult NaivePolicyChecker::check(uid_t, gid_t, const char *, co template DecisionItem NaivePolicyChecker::checkItem(uid_t uid, gid_t gid, const T& item) { - const ldp_xml::StorageBackendXML &policy_db = getPolicyDb(); - - DecisionItem ret = policy_db.getDecisionItemContextMandatory(item); + DecisionItem ret = m_bus_db.getDecisionItemContextMandatory(item); if (ret.getDecision() == Decision::ANY) - ret = policy_db.getDecisionItemUser(uid, item); + ret = m_bus_db.getDecisionItemUser(uid, item); if (ret.getDecision() == Decision::ANY) ret = checkGroupPolicies(uid, gid, item); if (ret.getDecision() == Decision::ANY) - ret = policy_db.getDecisionItemContextDefault(item); + ret = m_bus_db.getDecisionItemContextDefault(item); return ret; } @@ -180,7 +177,7 @@ DecisionItem NaivePolicyChecker::checkGroupPolicies(uid_t uid, gid_t gid, const return Decision::ANY; for (const auto sgid : *sgroups) { - DecisionItem ret = getPolicyDb().getDecisionItemGroup(sgid, item); + DecisionItem ret = m_bus_db.getDecisionItemGroup(sgid, item); if (ret.getDecision() != Decision::ANY) return ret; @@ -202,14 +199,22 @@ void NaivePolicyChecker::updateGroupDb(uid_t uid, gid_t gid) pthread_mutex_unlock(&mutexGroup); } -bool NaivePolicyChecker::initDb(const char *config_name) +bool NaivePolicyChecker::initDb(const char *config_name, const char *serialized_filename) { - return getPolicyDb().init(config_name); + m_bus_db.release(); + + if (serialized_filename) { + if (m_bus_db.init(serialized_filename)) + return true; + } + + // fallback - we have only XML files + return m_bus_db.initFromXML(config_name); } void NaivePolicyChecker::printContent() { - getPolicyDb().printContent(); + m_bus_db.printContent(); for (const auto &mapGroup : mapGroups) { std::cerr << std::endl << "---- mapGroup ----" << std::endl; diff --git a/src/internal/naive_policy_checker.hpp b/src/internal/naive_policy_checker.hpp index d34383e..cf6b875 100644 --- a/src/internal/naive_policy_checker.hpp +++ b/src/internal/naive_policy_checker.hpp @@ -25,7 +25,10 @@ #include "policy.hpp" #include "global_nodestruct.hpp" -#include "storage_backend_xml.hpp" +#include "storage_backend_serialized.hpp" + +#include +#include namespace ldp_xml_parser { @@ -38,7 +41,7 @@ namespace ldp_xml_parser private: /** Policy database */ - ldp_xml::StorageBackendXML m_bus_db; + ldp_serialized::StorageBackendSerialized m_bus_db; /** Parses delivered decision. In case of Decision::CHECK calls cynara. * \param[in] decision Decision from checkers @@ -90,13 +93,12 @@ namespace ldp_xml_parser /** Retrieves policy db * \return Returns reference to the policy db */ - ldp_xml::StorageBackendXML &getPolicyDb() { return m_bus_db; } - const ldp_xml::StorageBackendXML &getPolicyDb() const { return m_bus_db; } + const decltype(m_bus_db) &getPolicyDb() const { return m_bus_db; } /** Clears all db data, useful for reloading configuration * during testing. */ - bool initDb(const char *filename); + bool initDb(const char *config_name, const char *serialized_filename = nullptr); void updateGroupDb(uid_t uid, gid_t gid); diff --git a/src/internal/policy.cpp b/src/internal/policy.cpp index af21c59..5504d8d 100644 --- a/src/internal/policy.cpp +++ b/src/internal/policy.cpp @@ -6,6 +6,7 @@ #include "policy.hpp" #include "naive_policy_checker.hpp" #include "tslog.hpp" +#include "storage_backend_xml.hpp" #include #include diff --git a/src/internal/serializer.cpp b/src/internal/serializer.cpp index 1b8566d..1815d29 100644 --- a/src/internal/serializer.cpp +++ b/src/internal/serializer.cpp @@ -106,10 +106,14 @@ uint8_t* Serializer::serialize(const ldp_xml::StorageBackendXML &db, size_t &siz uint8_t* Serializer::serialize(const std::string config_path, size_t &size) { // SYSTEM_BUS here because something had to be choosen __internal_init_once(); - if (__internal_init(BusType::SYSTEM_BUS, config_path.c_str()) != 0) + ldp_xml::StorageBackendXML xmlStorage; + + if (!xmlStorage.init(config_path.c_str())) { cout << "internal_init error" << endl; + return nullptr; + } - return serialize(policy_checker_system().getPolicyDb(), size); + return serialize(xmlStorage, size); } void Serializer::serialize(const std::string config_path, ostream &output) { diff --git a/src/internal/storage_backend_serialized.cpp b/src/internal/storage_backend_serialized.cpp index 0590c4a..db2792b 100644 --- a/src/internal/storage_backend_serialized.cpp +++ b/src/internal/storage_backend_serialized.cpp @@ -3,6 +3,7 @@ #include "serialized_convert.hpp" #include "tslog.hpp" #include "transaction_guard.hpp" +#include "serializer.hpp" #include "include/fb_generated.h" @@ -30,12 +31,15 @@ class StorageBackendSerialized::StorageBackendSerializedImpl { size_t length{0}; const FB::File *file{nullptr}; + std::unique_ptr serializer; + void releaseMMap(); void releaseFD(); public: bool init(const char *filename, bool verify); bool init(const FB::File *f); + bool initFromXML(const char *config_name); void release(); void printContent() const; @@ -70,6 +74,10 @@ void StorageBackendSerialized::StorageBackendSerializedImpl::release() { releaseFD(); } + if (nullptr != serializer.get()) { + serializer.reset(nullptr); + } + file = nullptr; } @@ -122,6 +130,20 @@ bool StorageBackendSerialized::StorageBackendSerializedImpl::init(const FB::File return true; } +bool StorageBackendSerialized::StorageBackendSerializedImpl::initFromXML(const char *config_name) { + assert(nullptr == file); + assert(nullptr == serializer.get()); + + serializer.reset(new ldp_xml_parser::Serializer()); + + size_t serialized_size; + uint8_t *data = serializer->serialize(config_name, serialized_size); + if (nullptr == data) + return false; + + return init(FB::GetFile(data)); +} + void StorageBackendSerialized::StorageBackendSerializedImpl::printContent() const { std::cerr << *file; } @@ -154,6 +176,10 @@ bool StorageBackendSerialized::init(const FB::File *f) { return pimpl->init(f); } +bool StorageBackendSerialized::initFromXML(const char *config_name) { + return pimpl->initFromXML(config_name); +} + void StorageBackendSerialized::release() { pimpl->release(); } diff --git a/src/internal/storage_backend_serialized.hpp b/src/internal/storage_backend_serialized.hpp index 2e5422d..0a7e64f 100644 --- a/src/internal/storage_backend_serialized.hpp +++ b/src/internal/storage_backend_serialized.hpp @@ -16,6 +16,7 @@ public: bool init(const char *filename, bool verify = false); bool init(const FB::File *file); + bool initFromXML(const char *config_name); void release(); void printContent() const; diff --git a/src/libdbuspolicy1.c b/src/libdbuspolicy1.c index 94fc189..c67e2c5 100644 --- a/src/libdbuspolicy1.c +++ b/src/libdbuspolicy1.c @@ -240,7 +240,7 @@ DBUSPOLICY1_EXPORT void* dbuspolicy1_init(const char *bus_path) dbuspolicy_init_once(); } if (!init_once[bus_type]) { - rp = __internal_init(bus_type, (bus_type == SYSTEM_BUS) ? system_bus_conf_file_primary() : session_bus_conf_file_primary()); + rp = __internal_init_auto_serialized(bus_type, (bus_type == SYSTEM_BUS) ? system_bus_conf_file_primary() : session_bus_conf_file_primary()); if (rp < 0) rs = __internal_init(bus_type, (bus_type == SYSTEM_BUS) ? SYSTEM_BUS_CONF_FILE_SECONDARY : SESSION_BUS_CONF_FILE_SECONDARY); else -- 2.7.4 From 3a125dacb5c5ec34d0ba3b7b9c9c1e589978d00b Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Thu, 7 Mar 2019 09:17:29 +0100 Subject: [PATCH 13/16] refactoring: remove unused parameters and "using namespace std" Change-Id: I886c7dfd075fce52bfa5a8d1e7bf6d9aeb301ec9 --- src/internal/serializer.cpp | 30 ++++++++---------------------- src/internal/serializer.hpp | 9 +++------ src/stest_load_perf.cpp | 24 ++++++++++++------------ src/stest_performance.cpp | 16 ++++++++-------- 4 files changed, 31 insertions(+), 48 deletions(-) diff --git a/src/internal/serializer.cpp b/src/internal/serializer.cpp index 1815d29..b75ecf3 100644 --- a/src/internal/serializer.cpp +++ b/src/internal/serializer.cpp @@ -8,25 +8,23 @@ #include "include/flatbuffers/flatbuffers.h" #include "include/fb_generated.h" -using namespace std; - namespace ldp_xml_parser { -map decisions_map { +std::map decisions_map { { Decision::ANY, FB::Decision_ANY }, { Decision::DENY, FB::Decision_DENY }, { Decision::ALLOW, FB::Decision_ALLOW }, { Decision::CHECK, FB::Decision_CHECK } }; -map bus_access_map { +std::map bus_access_map { { BusAccessType::USER, FB::BusAccessType_USER }, { BusAccessType::GROUP, FB::BusAccessType_GROUP }, { BusAccessType::ALL_USERS, FB::BusAccessType_ALL_USERS }, { BusAccessType::ALL_GROUPS, FB::BusAccessType_ALL_GROUPS }, }; -map message_type_map { +std::map message_type_map { { MessageType::ANY, FB::MessageType_ANY }, { MessageType::METHOD_CALL, FB::MessageType_METHOD_CALL }, { MessageType::METHOD_RETURN, FB::MessageType_METHOD_RETURN }, @@ -109,14 +107,14 @@ uint8_t* Serializer::serialize(const std::string config_path, size_t &size) { ldp_xml::StorageBackendXML xmlStorage; if (!xmlStorage.init(config_path.c_str())) { - cout << "internal_init error" << endl; + std::cout << "internal_init error" << std::endl; return nullptr; } return serialize(xmlStorage, size); } -void Serializer::serialize(const std::string config_path, ostream &output) { +void Serializer::serialize(const std::string config_path, std::ostream &output) { size_t size; uint8_t *buf = serialize(config_path, size); @@ -150,7 +148,7 @@ FbOff Serializer::serialize_tree(const OwnershipTree &tree) { return policy; } -FbOff Serializer::serialize_tree(const shared_ptr &node) { +FbOff Serializer::serialize_tree(const std::shared_ptr &node) { auto prefix_decision_item = serialize_decision(node->getOwnPrefixDecisionItem()); auto decision_item = serialize_decision(node->getOwnDecisionItem()); @@ -200,25 +198,13 @@ auto Serializer::serialize_item(const P &item) -> FbOff: } template -auto Serializer::serialize_policy(const T &policy, - const std::vector::item>> items) +auto Serializer::serialize_policy(const std::vector::item>> items) -> FbOff::policy> { - (void)policy; auto create_policy = get_create_policy(); return create_policy(m_builder, m_builder.CreateVector(items)); } template <> -auto Serializer::serialize_policy(const PolicyAccess &policy, - const std::vector> items) - -> FbOff { - (void)policy; - auto create_policy = get_create_policy(); - return create_policy(m_builder, - m_builder.CreateVector(items)); -} - -template <> auto Serializer::serialize_policy(const PolicyOwn &policy) -> FbOff { return serialize_tree(policy.getTree()); @@ -232,7 +218,7 @@ auto Serializer::serialize_policy(const T &policy) -> FbOff(item)); } - return serialize_policy(policy, items); + return serialize_policy(items); } template diff --git a/src/internal/serializer.hpp b/src/internal/serializer.hpp index f8a35c6..57f13b7 100644 --- a/src/internal/serializer.hpp +++ b/src/internal/serializer.hpp @@ -11,8 +11,6 @@ #include "storage_backend_xml.hpp" #include "policy_containers.hpp" -using namespace std; - namespace ldp_xml_parser { enum class SetType : uint8_t { @@ -43,7 +41,7 @@ namespace ldp_xml_parser auto get_create_item() -> decltype(type_helper::create_item); FbOff serialize_tree(const OwnershipTree &tree); - FbOff serialize_tree(const shared_ptr &node); + FbOff serialize_tree(const std::shared_ptr &node); FbOff serialize_decision(const DecisionItem &item); template @@ -53,8 +51,7 @@ namespace ldp_xml_parser auto serialize_policy(const T &policy) -> FbOff::policy>; template - auto serialize_policy(const T &policy, - const std::vector::item>> items) + auto serialize_policy(const std::vector::item>> items) -> FbOff::policy>; template @@ -71,7 +68,7 @@ namespace ldp_xml_parser public: uint8_t *serialize(const ldp_xml::StorageBackendXML &db, size_t &size); uint8_t *serialize(const std::string config_path, size_t &size); - void serialize(const std::string config_path, ostream &output); + void serialize(const std::string config_path, std::ostream &output); friend class SerializerTests; }; } diff --git a/src/stest_load_perf.cpp b/src/stest_load_perf.cpp index 70208ca..2794a15 100644 --- a/src/stest_load_perf.cpp +++ b/src/stest_load_perf.cpp @@ -59,29 +59,29 @@ bool run_fb(const char *conf_file) { void run_tests(const char *conf_file, const char *conf_bin, size_t c, Choice ch) { if (ch == Choice::ALL || ch == Choice::XML) { if (!measure([&conf_file, c]() { return run_xml(conf_file); }, c, "XML")) { - cout << "ERROR" << endl; + std::cout << "ERROR" << std::endl; } } if (ch == Choice::ALL || ch == Choice::FB) { if (!measure([&conf_bin, c]() { return run_fb(conf_bin); }, c, "FB")) { - cout << "ERROR" << endl; + std::cout << "ERROR" << std::endl; } } if (ch == Choice::ALL || ch == Choice::XMLplusFB) if (!measure([&conf_file, c]() { return run_xml_plus_fb(conf_file); }, c, "FB after XML")) { - cout << "ERROR" << endl; + std::cout << "ERROR" << std::endl; } } void print_help(const char *name) { - cout << endl; - cout << "usage: " << name << " {-f |-x|-d|-a } {--system|--session|-c } " << endl; - cout << endl; - cout << " -f - Flatbuffers" << endl; - cout << " -x - XML" << endl; - cout << " -d - FB after XML" << endl; - cout << " -a - All tests" << endl; - cout << endl; + std::cout << std::endl; + std::cout << "usage: " << name << " {-f |-x|-d|-a } {--system|--session|-c } " << std::endl; + std::cout << std::endl; + std::cout << " -f - Flatbuffers" << std::endl; + std::cout << " -x - XML" << std::endl; + std::cout << " -d - FB after XML" << std::endl; + std::cout << " -a - All tests" << std::endl; + std::cout << std::endl; } static const struct option options[] { @@ -144,7 +144,7 @@ int main(int argc, char *argv[]) } if (optind < argc) { - count = stoi(argv[optind]); + count = std::stoi(argv[optind]); } else { print_help(argv[0]); return 1; diff --git a/src/stest_performance.cpp b/src/stest_performance.cpp index f37b7ad..a98f139 100644 --- a/src/stest_performance.cpp +++ b/src/stest_performance.cpp @@ -218,13 +218,13 @@ void run_tests(const char *conf_file, size_t c, Choice ch) { } void print_help(const char *name) { - cout << endl; - cout << "usage: " << name << " {-f|-x|-a} {--system|--session|-c } " << endl; - cout << endl; - cout << " -f - Flatbuffers" << endl; - cout << " -x - XML" << endl; - cout << " -a - Flatbuffers and XML" << endl; - cout << endl; + std::cout << std::endl; + std::cout << "usage: " << name << " {-f|-x|-a} {--system|--session|-c } " << std::endl; + std::cout << std::endl; + std::cout << " -f - Flatbuffers" << std::endl; + std::cout << " -x - XML" << std::endl; + std::cout << " -a - Flatbuffers and XML" << std::endl; + std::cout << std::endl; } static const struct option options[] { @@ -262,7 +262,7 @@ int main(int argc, char *argv[]) } if (optind < argc) { - count = stoi(argv[optind]); + count = std::stoi(argv[optind]); } else { print_help(argv[0]); return 1; -- 2.7.4 From 80eb19d4c3d22d45e5e7a21989a0bc48b1cda175 Mon Sep 17 00:00:00 2001 From: Adrian Szyndela Date: Thu, 7 Mar 2019 11:06:55 +0100 Subject: [PATCH 14/16] refactoring: remove unused code This removes code which is unused after switching backend from StorageBackendXML to StorageBackendSerialized. Change-Id: I2326b24f06c7eae050e190e15d8f1c4740a4d29e --- Makefile.am | 1 - src/internal/own_tree.cpp | 67 --------------- src/internal/own_tree.hpp | 6 -- src/internal/policy_containers.cpp | 8 -- src/internal/policy_containers.hpp | 74 +---------------- src/internal/storage_backend_xml.cpp | 157 ++--------------------------------- src/internal/storage_backend_xml.hpp | 17 ---- 7 files changed, 8 insertions(+), 322 deletions(-) delete mode 100644 src/internal/policy_containers.cpp diff --git a/Makefile.am b/Makefile.am index 5c47c77..8df326f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -56,7 +56,6 @@ COMMON_SRC =\ src/internal/internal.cpp \ src/internal/naive_policy_checker.cpp \ src/internal/policy.cpp \ - src/internal/policy_containers.cpp \ src/internal/own_tree.cpp \ src/internal/xml_parser.cpp \ src/internal/tslog.cpp \ diff --git a/src/internal/own_tree.cpp b/src/internal/own_tree.cpp index c81d7a8..ceebd3d 100644 --- a/src/internal/own_tree.cpp +++ b/src/internal/own_tree.cpp @@ -1,5 +1,4 @@ #include "own_tree.hpp" -#include "print_content.hpp" #include #include #include @@ -43,36 +42,6 @@ void OwnershipTree::addItem(const ItemOwn &item) { __root->add(tokens, item.getDecision(), item.isPrefix()); } -DecisionItem OwnershipTree::getDecisionItem(const MatchItemOwn& item) const -{ - if (item.getName().length() == 0) { - return Decision::DENY; - } - - auto tokens = tokenize(item.getName()); - return __root->getDecisionItem(tokens); -} - -void OwnershipTree::printTreeLevel(const TreeNode& node, const std::string& indent) const -{ - std::cerr << indent << "| " << node.__token << " (" << node.__children.size() << ") | " - << node.__own_decision_item << " " << node.__own_prefix_decision_item << std::endl; - - for (const auto& i : node.__children) - printTreeLevel(*i.second, indent + "\t"); -} - -void OwnershipTree::printTree() const -{ - printTreeLevel(*__root, ""); -} - -size_t OwnershipTree::getSize() const -{ - return __root->getSize(); -} - - TreeNode::TreeNode(std::string token) :__token(token), __own_prefix_decision_item(Decision::ANY), __own_decision_item(Decision::ANY) {} @@ -99,17 +68,6 @@ void TreeNode::add(std::deque& tokens, const DecisionItem& decision it->second->add(tokens, decision, is_prefix); } -size_t TreeNode::getSize() const -{ - size_t size = sizeof(*this) + get_string_heap_allocated_memory(__token) + __own_decision_item.getSize() - + __own_prefix_decision_item.getSize(); - - for (const auto& i : __children) - size += sizeof(i) + get_string_heap_allocated_memory(i.first) + i.second->getSize(); - - return size; -} - void TreeNode::setDecisionItem(const DecisionItem& decision, const bool is_prefix) { if (is_prefix) { @@ -136,28 +94,3 @@ const DecisionItem &TreeNode::getOwnDecisionItem() const { const std::unordered_map> &TreeNode::getChildren() const { return __children; } - -DecisionItem TreeNode::getDecisionItem(std::deque& tokens) const -{ - if (tokens.empty()) { - if (__own_decision_item.getDecision() != Decision::ANY) { - return __own_decision_item; - } else { - return __own_prefix_decision_item; - } - } - - const auto& token = tokens.front(); - auto it = __children.find(token); - if (it == __children.end()) { - return __own_prefix_decision_item; - } - assert(it != __children.end()); - tokens.pop_front(); - - DecisionItem child_decision = it->second->getDecisionItem(tokens); - if (child_decision.getDecision() == Decision::ANY) { - return __own_prefix_decision_item; - } - return child_decision; -} diff --git a/src/internal/own_tree.hpp b/src/internal/own_tree.hpp index cd67969..8823706 100644 --- a/src/internal/own_tree.hpp +++ b/src/internal/own_tree.hpp @@ -36,7 +36,6 @@ namespace ldp_xml_parser TreeNode(std::string token); void add(std::deque& tokens, const DecisionItem& decision, const bool is_prefix); void setDecisionItem(const DecisionItem& decision, const bool is_prefix); - DecisionItem getDecisionItem(std::deque& tokens) const; const std::string &getToken() const; const DecisionItem &getOwnPrefixDecisionItem() const; const DecisionItem &getOwnDecisionItem() const; @@ -48,7 +47,6 @@ namespace ldp_xml_parser DecisionItem __own_prefix_decision_item; DecisionItem __own_decision_item; std::unordered_map> __children; - size_t getSize() const; friend class OwnershipTree; }; @@ -57,13 +55,9 @@ namespace ldp_xml_parser public: OwnershipTree(); void addItem(const ItemOwn &item); - DecisionItem getDecisionItem(const MatchItemOwn& item) const; - void printTree() const; - size_t getSize() const; const std::shared_ptr getRoot() const { return __root; }; private: std::shared_ptr __root; - void printTreeLevel(const TreeNode& node, const std::string& indent) const; }; } #endif diff --git a/src/internal/policy_containers.cpp b/src/internal/policy_containers.cpp deleted file mode 100644 index 9683e13..0000000 --- a/src/internal/policy_containers.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "policy_containers.hpp" - -namespace ldp_xml_parser { -constexpr const char *PolicySend::name; -constexpr const char *PolicyReceive::name; -constexpr const char *PolicyOwn::name; -constexpr const char *PolicyAccess::name; -} diff --git a/src/internal/policy_containers.hpp b/src/internal/policy_containers.hpp index 47f70f0..7e8ba56 100644 --- a/src/internal/policy_containers.hpp +++ b/src/internal/policy_containers.hpp @@ -1,9 +1,7 @@ #pragma once -#include "tslog.hpp" #include "policy.hpp" #include "own_tree.hpp" -#include "print_content.hpp" #include namespace ldp_xml_parser { @@ -13,9 +11,6 @@ class PolicyBase { protected: /** Vector with policy items */ std::vector m_items; - typedef typename decltype(m_items)::const_reverse_iterator PolicyConstIterator; - PolicyConstIterator begin() const { return m_items.rbegin(); } - PolicyConstIterator end() const { return m_items.rend(); } public: typedef TI item_type; @@ -24,66 +19,12 @@ public: */ void addItem(TI &item) { m_items.push_back(std::move(item)); } - void printContent() const { - for (const auto& i : m_items) - std::cerr << i << std::endl; - } - - size_t getSize() const { - size_t size = m_items.capacity() * sizeof(typename decltype(m_items)::value_type); - for (const auto& i : m_items) - size += i.getSize(); - return size; - } - const std::vector &getItems() const { return m_items; } }; -/****************** PolicySRBase: a base for send/receive policies ************************/ -/** Class containing policy with send/receive rules */ -template -class PolicySRBase : public PolicyBase { -public: - DecisionItem getDecisionItem(const typename TI::match_type &item) const { - for (auto &i : *this) { - tslog::log_verbose("-read: ", i.getDecision(), " ", i, "\n"); - - if (item.match(i.getType(), i.getInterface(), i.getPath(), - i.getMember(), i.getName(), i.isNamePrefix())) { - tslog::log_verbose("-matched: ", i.getDecision(), " ", i, "\n"); - - return i.getDecision(); - } - } - return Decision::ANY; - } -}; - -/****************** PolicySend ************************/ -class PolicySend : public PolicySRBase { -public: - static constexpr const char *name = "send"; -}; - -/****************** PolicyReceive ************************/ -class PolicyReceive : public PolicySRBase { -public: - static constexpr const char *name = "receive"; -}; - -/****************** PolicyAccess ************************/ -class PolicyAccess : public PolicyBase { -public: - static constexpr const char *name = "access"; - DecisionItem getDecisionItem(const typename item_type::match_type &item) const { - for (auto i : *this) { - if (item.match(i.getType(), i.getUid(), i.getGid())) { - return i.getDecision(); - } - } - return Decision::ANY; - } -}; +using PolicySend = PolicyBase; +using PolicyReceive = PolicyBase; +using PolicyAccess = PolicyBase; /****************** PolicyOwn ************************/ /** Class containing policy with ownership rules */ @@ -98,16 +39,7 @@ public: */ void addItem(const ItemOwn &item) { ownership_tree.addItem(item); } - DecisionItem getDecisionItem(const MatchItemOwn& item) const { - return ownership_tree.getDecisionItem(item); - } - - void printContent() const { ownership_tree.printTree(); } - - size_t getSize() const { return ownership_tree.getSize(); } const OwnershipTree &getTree() const { return ownership_tree; }; - - static constexpr const char *name = "own"; }; } diff --git a/src/internal/storage_backend_xml.cpp b/src/internal/storage_backend_xml.cpp index 4247108..44287f4 100644 --- a/src/internal/storage_backend_xml.cpp +++ b/src/internal/storage_backend_xml.cpp @@ -1,6 +1,10 @@ #include "storage_backend_xml.hpp" #include "policy_containers.hpp" #include "xml_parser.hpp" +#include "tslog.hpp" +#include "print_content.hpp" + +#include using namespace ldp_xml_parser; @@ -24,52 +28,7 @@ protected: template void addItemGroup(gid_t gid, T &item) { group[gid].addItem(item); } - void printContent() const { - for (const auto& i : user) { - std::cerr << "user: " << i.first << std::endl; - i.second.printContent(); - } - - for (const auto& i : group) { - std::cerr << "group: " << i.first << std::endl; - i.second.printContent(); - } - } - - size_t getSize() const { - size_t size = user.size() * sizeof(typename decltype(user)::value_type); - for (const auto& i : user) - size += i.second.getSize(); - - size += group.size() * sizeof(typename decltype(group)::value_type); - for (const auto& i : group) - size += i.second.getSize(); - return size; - } - public: - const P *getPolicyUser(uid_t uid) const { - tslog::log("---policy_type = USER =", uid, "\n"); - - auto it = user.find(uid); - if (it == user.end()) { - tslog::log_verbose("GetPolicy: Out of Range exception\n"); - return nullptr; - } - return &(it->second); - } - - const P *getPolicyGroup(gid_t gid) const { - tslog::log("---policy_type = GROUP = ", gid, "\n"); - - auto it = group.find(gid); - if (it == group.end()) { - tslog::log_verbose("GetPolicy: Out of Range exception\n"); - return nullptr; - } - return &(it->second); - } - const std::map &getPoliciesUser() const { return user; } const std::map &getPoliciesGroup() const { return group; } }; @@ -82,9 +41,6 @@ protected: template void addItemGroup(gid_t, T &) { assert(false); } - - void printContent() const {} - size_t getSize() const { return 0; } }; /****************** PolicySet ************************/ @@ -131,37 +87,6 @@ public: const P &getRefPolicyContextMandatory() const { return contextMandatory; } const P &getRefPolicyContextDefault() const { return contextDefault; } - - const P *getPolicyContextMandatory() const { - tslog::log("---policy_type = CONTEXT = MANDATORY\n"); - return &contextMandatory; - } - - const P *getPolicyContextDefault() const { - tslog::log("---policy_type = CONTEXT = DEFAULT\n"); - return &contextDefault; - } - - void printSet() const { - std::cerr << "context default" << std::endl; - contextDefault.printContent(); - std::cerr << "context mandatory" << std::endl; - contextMandatory.printContent(); - - UG::printContent(); - - std::cerr << "Memory consumption: " << getSetSize() << " B" << std::endl; - } - - size_t getSetSize() const { - size_t size = sizeof(*this); - - size += contextMandatory.getSize(); - size += contextDefault.getSize(); - - size += UG::getSize(); - return size; - } }; /****************** StorageBackendXML ************************/ @@ -183,14 +108,6 @@ public: const typename MatchPolicy::policy_set_type &getPolicySet() const; template typename MatchPolicy::policy_set_type &getPolicySet(); - - template ::policy_type, - typename PS = typename MatchPolicy::policy_set_type, - typename ...Args> - DecisionItem getDecisionItem(const T &item, - const P *(PS::*f)(Args ... args) const, - Args ...args) const; }; /* Tie MatchItems with proper policy sets. @@ -200,25 +117,21 @@ public: * The below DEF_GET_POLICY_SET defines specialization for MatchPolicy template, * and a specialization of StorageBackendXMLImpl::getPolicySet template function * - * Specialization for MatchPolicy provides field 'policy_type' which specifies - * a type of policy used within 'field' parameter, and field 'policy_set_type' + * Specialization for MatchPolicy provides field 'policy_set_type' * which specifies a type of the 'field' parameter. * Specialization of StorageBackendXMLImpl::getPolicySet returns the 'field'. * * For example: DEF_GET_POLICY_SET(MatchItemOwn, m_own_set) defines equivalent to * MatchPolicy { - * typedef PolicyOwn policy_type; * typedef PolicySet policy_set_type; * } * PolicySet &getPolicySet const; * * Thanks to this construction we do not need to manually specify PolicyOwn in functions that * know MatchItemOwn - it is inferred. - * */ #define DEF_GET_POLICY_SET(T, field) \ template <> struct StorageBackendXML::StorageBackendXMLImpl::MatchPolicy { \ - typedef decltype(field)::policy_type policy_type; \ typedef decltype(field) policy_set_type; \ }; \ template <> struct StorageBackendXML::StorageBackendXMLImpl::MatchPolicy { \ @@ -244,23 +157,6 @@ DEF_GET_POLICY_SET(MatchItemAccess, m_access_set) #undef DEF_GET_POLICY_SET -template -DecisionItem StorageBackendXML::StorageBackendXMLImpl::getDecisionItem(const T &item, - const P *(PS::*f)(Args ... args) const, - Args ...args) const -{ - const auto &policySet = getPolicySet(); - typedef typename std::remove_reference::type PolicySetType; - - auto policy = (policySet.*f)(args...); - if (nullptr == policy) - return Decision::ANY; - - tslog::log_verbose("Checking ", PolicySetType::policy_type::name, " policy for: ", item, "\n"); - - return policy->getDecisionItem(item); -} - template void StorageBackendXML::addItem(const ldp_xml_parser::PolicyType policy_type, const ldp_xml_parser::PolicyTypeValue policy_type_value, @@ -275,19 +171,6 @@ bool StorageBackendXML::init(const char *filename) { return parser.parsePolicyConfigFile() == 0; } -void StorageBackendXML::printContent() const { - #define PRINT_SET(x) \ - std::cerr << std::endl << "----" #x "----" << std::endl; \ - (x).printSet(); - - PRINT_SET(pimpl->m_own_set) - PRINT_SET(pimpl->m_send_set) - PRINT_SET(pimpl->m_receive_set) - PRINT_SET(pimpl->m_access_set) - - #undef PRINT_SET -} - template const T &StorageBackendXML::getPolicyContextMandatory() const { return pimpl->getPolicySet().getRefPolicyContextMandatory(); @@ -308,31 +191,6 @@ const std::map &StorageBackendXML::getPoliciesGroup() const { return pimpl->getPolicySet().getPoliciesGroup(); } -template -bool StorageBackendXML::existsPolicyForGroup(gid_t gid) const { - return pimpl->getPolicySet().getPolicyGroup(gid) != nullptr; -} - -template -ldp_xml_parser::DecisionItem StorageBackendXML::getDecisionItemContextMandatory(const T &item) const { - return pimpl->getDecisionItem(item, &StorageBackendXMLImpl::MatchPolicy::policy_set_type::getPolicyContextMandatory); -} - -template -ldp_xml_parser::DecisionItem StorageBackendXML::getDecisionItemContextDefault(const T &item) const { - return pimpl->getDecisionItem(item, &StorageBackendXMLImpl::MatchPolicy::policy_set_type::getPolicyContextDefault); -} - -template -ldp_xml_parser::DecisionItem StorageBackendXML::getDecisionItemUser(uid_t uid, const T &item) const { - return pimpl->getDecisionItem(item, &StorageBackendXMLImpl::MatchPolicy::policy_set_type::getPolicyUser, uid); -} - -template -ldp_xml_parser::DecisionItem StorageBackendXML::getDecisionItemGroup(gid_t gid, const T &item) const { - return pimpl->getDecisionItem(item, &StorageBackendXMLImpl::MatchPolicy::policy_set_type::getPolicyGroup, gid); -} - StorageBackendXML::StorageBackendXML() : pimpl{new StorageBackendXMLImpl} { } @@ -341,8 +199,6 @@ StorageBackendXML::~StorageBackendXML() = default; /* Explicit instantiation is needed for used public template methods defined in this file. */ #define T_INSTANTIATION(T) \ - template ldp_xml_parser::DecisionItem StorageBackendXML::getDecisionItemContextMandatory(const MatchItem##T &item) const; \ - template ldp_xml_parser::DecisionItem StorageBackendXML::getDecisionItemContextDefault(const MatchItem##T &item) const; \ template void StorageBackendXML::addItem(const PolicyType policy_type, const PolicyTypeValue policy_type_value, Item##T &item); \ template const Policy##T &StorageBackendXML::getPolicyContextMandatory() const; \ template const Policy##T &StorageBackendXML::getPolicyContextDefault() const; @@ -355,9 +211,6 @@ T_INSTANTIATION(Access) #undef T_INSTANTIATION #define T_INSTANTIATION(T) \ - template bool StorageBackendXML::existsPolicyForGroup(gid_t gid) const; \ - template ldp_xml_parser::DecisionItem StorageBackendXML::getDecisionItemUser(uid_t uid, const MatchItem##T &item) const; \ - template ldp_xml_parser::DecisionItem StorageBackendXML::getDecisionItemGroup(gid_t gid, const MatchItem##T &item) const; \ template const std::map &StorageBackendXML::getPoliciesGroup() const; \ template const std::map &StorageBackendXML::getPoliciesUser() const; diff --git a/src/internal/storage_backend_xml.hpp b/src/internal/storage_backend_xml.hpp index 6a0c690..4987ba5 100644 --- a/src/internal/storage_backend_xml.hpp +++ b/src/internal/storage_backend_xml.hpp @@ -22,8 +22,6 @@ public: bool init(const char *filename); - void printContent() const; - // The getPolicy*() methods are intended for Serializers to get // access to policy structures. // Supported template parameters are: PolicyOwn, PolicySend, PolicyReceive @@ -36,21 +34,6 @@ public: const std::map &getPoliciesUser() const; template const std::map &getPoliciesGroup() const; - - // This works with T set to MatchItemOwn, MatchItemSend or MatchItemReceive - // This is needed for filtering mapGroups. Check NaivePolicyChecker. - template - bool existsPolicyForGroup(gid_t gid) const; - - // These work with T set to MatchItemOwn, MatchItemSend, MatchItemReceive or MatchItemAccess - template - ldp_xml_parser::DecisionItem getDecisionItemContextMandatory(const T &item) const; - template - ldp_xml_parser::DecisionItem getDecisionItemContextDefault(const T &item) const; - template - ldp_xml_parser::DecisionItem getDecisionItemUser(uid_t uid, const T &item) const; - template - ldp_xml_parser::DecisionItem getDecisionItemGroup(gid_t gid, const T &item) const; }; } -- 2.7.4 From e05b9cf9cb6a44854a90172b7e16ffbe843d8275 Mon Sep 17 00:00:00 2001 From: Adrian Szyndela Date: Thu, 7 Mar 2019 11:34:54 +0100 Subject: [PATCH 15/16] refactoring: change string to ref-to-string in own tree This changes std::string in construction of TreeNode to reference-to-string to avoid additional copy of the string. Change-Id: I49ac9d8e83f8691c022c9113d427bc60c7463902 --- src/internal/own_tree.cpp | 2 +- src/internal/own_tree.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/internal/own_tree.cpp b/src/internal/own_tree.cpp index ceebd3d..1eb58f1 100644 --- a/src/internal/own_tree.cpp +++ b/src/internal/own_tree.cpp @@ -42,7 +42,7 @@ void OwnershipTree::addItem(const ItemOwn &item) { __root->add(tokens, item.getDecision(), item.isPrefix()); } -TreeNode::TreeNode(std::string token) +TreeNode::TreeNode(const std::string &token) :__token(token), __own_prefix_decision_item(Decision::ANY), __own_decision_item(Decision::ANY) {} diff --git a/src/internal/own_tree.hpp b/src/internal/own_tree.hpp index 8823706..c96a861 100644 --- a/src/internal/own_tree.hpp +++ b/src/internal/own_tree.hpp @@ -33,7 +33,7 @@ namespace ldp_xml_parser class TreeNode{ public: - TreeNode(std::string token); + explicit TreeNode(const std::string &token); void add(std::deque& tokens, const DecisionItem& decision, const bool is_prefix); void setDecisionItem(const DecisionItem& decision, const bool is_prefix); const std::string &getToken() const; -- 2.7.4 From add6ca825be06f05f77ec73158931b2e88d41426 Mon Sep 17 00:00:00 2001 From: Adrian Szyndela Date: Thu, 7 Mar 2019 11:44:53 +0100 Subject: [PATCH 16/16] Add design documentation Change-Id: I123ed895c624fe712d4bd3f6da8f72c820e3a87f --- doc/design.txt | 34 ++++++++++++++++++++++++++++ doc/dia/Classes-only-serialized-backend.dia | Bin 0 -> 3751 bytes doc/dia/Classes-only-serialized-backend.png | Bin 0 -> 72358 bytes doc/dia/Classes-use-only-serialized.dia | Bin 0 -> 4325 bytes doc/dia/Classes-use-only-serialized.png | Bin 0 -> 96976 bytes doc/dia/Classes.dia | Bin 0 -> 4495 bytes doc/dia/Classes.png | Bin 0 -> 97548 bytes doc/dia/UseCases.dia | Bin 0 -> 1490 bytes doc/dia/UseCases.png | Bin 0 -> 32874 bytes 9 files changed, 34 insertions(+) create mode 100644 doc/design.txt create mode 100644 doc/dia/Classes-only-serialized-backend.dia create mode 100644 doc/dia/Classes-only-serialized-backend.png create mode 100644 doc/dia/Classes-use-only-serialized.dia create mode 100644 doc/dia/Classes-use-only-serialized.png create mode 100644 doc/dia/Classes.dia create mode 100644 doc/dia/Classes.png create mode 100644 doc/dia/UseCases.dia create mode 100644 doc/dia/UseCases.png diff --git a/doc/design.txt b/doc/design.txt new file mode 100644 index 0000000..5361ca9 --- /dev/null +++ b/doc/design.txt @@ -0,0 +1,34 @@ +dia/ subdirectory contains dia design diagrams for libdbuspolicy along with generated pngs. + +This is a short description of the diagrams: +- UseCases - an overview of use cases; +- Classes - a diagram of classes, basic version: + - MatchItem, MatchItemWithUserAndGroup - queries constructed by PolicyChecker; + - PolicyChecker - entry point for checking policy; creates MatchItems to pass them as + queries to StorageBackends; it chooses StorageBackend depending on the system + availability (e.g. if serialized policy file exists); + - StorageBackend - interface class; defines query interface for PolicyChecker, + and provides entry point for printing content; + - StorageBackendXML - storage specific for XML-based data; supports adding items and + provides accessory functions for serialization; + - XmlParser - parser for XML data; creates items and adds them to StorageBackendXML; + - StorageBackendSerialized - storage specific for serialized data; + - Serializer - a translator between XML-based data and serialized data; used as an + entry point for serializator tool; + - Printer - an entry point for printer tool; + - FlatBuffers - a library used for management of serialized data. + +- Classes-use-only-serialized - a diagram of classes; this is improved Classes; + the differences are: + - PolicyChecker - uses only StorageBackendSerialized; + - Serializer - used for creating serialized data from StorageBackendXML + if serialized policy file is not available; + - StorageBackendXML - serves only as an intermediate stage for StorageBackendSerialized; + +- Classes-only-serialized-backend - a diagram of classes; this is potential improvement + of Classes-use-only-serialized; the differences are: + - There is no StorageBackendXML; + - Serializer uses XmlParser to create FlatBuffers directly (in case of unavailable + serialized policy file); + - we are not sure if this will improve initialization time, as all the objects have to be + fully created before serialization anyway. diff --git a/doc/dia/Classes-only-serialized-backend.dia b/doc/dia/Classes-only-serialized-backend.dia new file mode 100644 index 0000000000000000000000000000000000000000..5ba71389d6b7de35a2f650fba535888233c3f7e0 GIT binary patch literal 3751 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 literal 0 HcmV?d00001 diff --git a/doc/dia/Classes-only-serialized-backend.png b/doc/dia/Classes-only-serialized-backend.png new file mode 100644 index 0000000000000000000000000000000000000000..5ed5c9660aeb6113b9bfa586db6e057bbb9887da GIT binary patch literal 72358 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

*_5LYD`# z>A&m^@f1;;kIu;IZv*N4>+WU7O07*gYeOxVMr+NzGv3>oZd;pnehq!s@>?^onm4~- zen6NpKZ|y*oj<$&VWFjF!<_1)zUlP$DoVxC$t&yNy9)8jJCJ!_2XTqG)%m|{nt%9l8pE*wGXDRH_7 zeP;pA?GditAe?yFv~QBSWch2z3Ep*GsU_^66ccRd50-2#!WK@hrpd-^m8Na^Y!*AF3=Oay`flQwpG?3W=- zOK}4W5j72_IkMnCtap`^N%PDf=&bs31sn4Pk9h)*p@G6bjXSt<_W6jn0oup>VlWVm z!8uwtb5OAN#d#Ec&f86ELpcAh5@_YH*<1O7mLRtdBmC{Zc3BQ)cOttOxehJNEJd8- z%;!ihW-`~Lw~DQiBt@f1V`QmCRvTz;$3W&4iy2F_AP#4`Z(UWC)h|kp`uSHg5}x!` zLS)b|W?N?nh!q%e^MX~(*4MuWs4=N$K_wOPpi#M?%O$2v2=kZLzO|ssv!r}FjAl5c z_1evT)OV5sYntquEd^qPQ879$idH%*QV;w*V@utR#l@-Wmat(3;Ws=* zZE|7PLrmI_G9P%Qr)*wwYw!e?XuWE)q+D;{ zNN5=3(c!nvpz>7kUT&OP({}#%m=Yjh$hLlspni8{fQ0of)(z{aM=ER-La}1}XT0E?a-)e`|?F@pwA{-&QTg zB1OPOF5!8L1fD9$cRnKG z?M#2oztAj-IUWV4eF%p$v*vYV5k!wdqjGn#Pt%NGi@hX{xB5=Sys%E`~@sYXHfvv@w z&_Q0}ueW0TJU-Ntvwt_LWeAO-YT_+%s`<0ONyb4M>Y83)Rk))zV^sD*sYG2g zvE233dLS($=?D4ozjI>@WK#C&F%3S`ArcRpvUN&0+@u!^o=bE<&U@au%-4WL47?B3 zmJN7cV}7(c1)1Kt1U)`*S`*lpMm|;(de`*%64hE%ct&DQLpIV$f*;Rf}t z`4d~Ann)h_`PXAkrbOvp37~c$U8SRoE2m=g6Qd%=EOrE zSVTyDa+SodUp~&U`={?^R?4$uC!-OQxD%k3-+M!!)MXtXPevdn6(&H;oeFIqoGN4a znnF|U*_dUL>_Ooi<3B_VV$El45~ccDthHnhs`l-C=y}EC7Z5D_i(g zOWw=n9_%HZnGjV>C7V(FKWK|jfSU=RGG$U)!w#GVYNP&zLgRtc#F~Fx@phfVAD%EV zHUJFoFYQ2?ny(bOn>0;Vaz#KiKKly_-pur44-Z?}#5d!5r%V_}=>V5O1=u(O z*T@JwEnJ;QZnFxr3@%8*M21$L(@sCWW8M8EM%egpEkS3KRya#_zjSleJn^=|*PTVp zU?Qb=_OHlgpuQ#hGOOz1bSvF(Xs%z_V*95Xi#d*_B2Bf^kz$(%b)l7#az=XdWVHzZ z<*oM{VP$1WWoj~PPrvg3d)6Zxfig5=B^)l4tnEKv3p&2p8N zX_~IUop&^1+vV_G4?a||E}1N?ANKvjU|Nj_a`LOY6g`-ID1FD!n zrAhoE)_;}c69YZ|%+Nw&a(U~We5#jqBBp2u&Tm$;;FU$t0Yo9T6AQCzn>X+;d0#fX ziQULKTS3J`#~gfK`yPX;%36b4V4nx7Xw01Z6CYH~k?p$PZ>}TZESiI4W6T;s{2%}n zubwsruEZDQWdp9)gED;eK)4NP;Fk7QJnG2<*K65VVx^{y4?X}grYq_ihGqMP73$$S zRP@=uVp3m}!J$ge2w#9>!PdS{)A!_>$`bX5S|3qZirQw=B#_s5gE)H&7%Kr|v@|{C zK%;!|Ohn`IV_^+f6_VGX1W<4iALieu@Gtso1JaJY639saDV}k$OFLs%;}uh34`wbf yw2r0txkm7zVx5(kn3kq5-UGhJj0#^8) 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%0A|0EKcEG0kx_NgD4qFCE>z6H{Y>sbN*hjUyaO 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®Ak_3x416uq?`_hVBF0pU{h0=HVDhEUD5f}& z(h$Z766663+Fod_dJ~Vw+j%SNsiq5m^OF%1#SDi_(#mCm#~I|uwQZdt*-Y88n--PF z4=9Otv&uSuBY0n5vux1}X;=ia3*V4H`cz$#X2b_kmINPgiozb@I8wgWGp`|fCW?V* z#NR~N=;cMzJW|$vDqA-Rn#k(UpX<3ajDU=*g~>LUFJcnG1dn2$dVv>#!?Zej5;hgd z@(^R=*SHKZMyGGz?RNsR4Yo1WLY#B+rr^S~2an(2;7RCdK>68(P$(li$=F{t*hU{I zClMaLRmmH^TV@*|_}Ld3NlS$TG&u5MSSMsEuyvc3KzX6M;KAzpV}L7Ilm4cl@A)El zFqTZO-(TUw36f3>VCvrsEu45Qm|v&SiVkD-8IR})Vv%<=hb!K`(neUQ>W_HJ3kFQ* zkHCR(kw+sl#Z5!PTS$ld)rD)={J4)3vfu4&<*af1>gjDqjv@s_gnl6R?%gKt3!KdS z0f5-v?(S&}juq!HDY`N*x_!(6&o_pW!n`52odW{zqz~PD_ukgnV*Eg3{glr$h?XI7 z4(GZ&nHjkonMPg!arMunq{D)I>P>8Ca9ciSngK?lq>FeyKD{GyFk2pyQ!LfqErT(UEq9?_y>M3u|fe@FNc++6w zm@#b*^flx7o!rTZ#R1V7Iv#?&dzRdUu5hm+-``0_TDNXZicctqH6@0Ff4}2kS++!{ z4{vT3=YlZ+(gav!0Iz6;)vJ0MZa6P;G)Udh!Kp=wg8G3ii7CtU_4G~<5l@_mKKZDn zSvy4Go|sV(F%BG1r)36`<6qcCbU8v{#D4m{hqp-5?A_ail}^X*d-;Lkzfh6?7wog+ zCqjJMYWJj#OeYeF;cRiIjVhHw`qS?!sV%s zYxA?_7Y$Vc5~VHH-+r;SY99TqFC_3m+6mkO0EanyEG(Jr!_yaXD6+g=EnNhIw9d#S zV0>tjL1=P1{kN6J4n5t@Y)Fz~MzGPpz%a>yPYT}pVD`Gn{Q2o+W2?*O{?r0INmJ4h z?nPR3Ss6=h(d~EOXxKoH0X&R|SlUnD>NM~YyXMieX9q5H5$fE_N2WwMGQ8zh9m4t< z*yd1$x_zhY!2Gt2wDjO!7GF&ps!nDDLgE4Em5~O^iJUD0-Cxy@hUvK4;vH5B@*Ena-ENU2-C3JIwT=MhGW^&>7 zn<`=H*yE{=GkRFRz`)ws)gMfv6Xvy7ZS-$FUQ<5Se~?p{p6%!qWvh`F+l04ktF<*c zH6bn5IYv)2<3UlP;o*lbt5rH3y|13A@+N8Z$l1q=9zRr0D``2>EToUqqt^3p)?Z8i z=2fd`+`9E2Uu(aw9cjorq%Q(8*3u(st-RN0vZcjRo0Y2`&Vt5x_T00w9 z!VI5f&hYv3`|iH_TNPj|?38pPHc-&~S>%f8GGzC&0@dU_)U*rHM0c#*r?;FAN z_|N2%pFB8sFTMH}i?UyMONx6(;AcL{Tnxak179X=! zMNT@GWaRjllaNtrQxj~l_n{t;Oyr#C}{hizimj*#7rxmPa@wRwsF76)E0QaE`fh&6UY*zI%x>nvcO&L8pNAreY^mqOGqmzp| z1Z2+{u$LNc7?bf_Hj@Ds2JasCrz3G$UQ4&HfKrwur6T_#B3DEya}O_7$XovxAFv#PcIxudfe)k*EYy&PBDw{Lf<=k)B=Yd^8sVRFhe zdQ8zs{4u*r*RE{!_H#_m8pe9j6!h@n@1@U3lol;qSh^HZ%E++UzyH2j9utgf!y3U% ztP<|;-mRj$Yl2&W%>5^i9&M4Id$Q0^PfcW?vkxA{!opU)jYqbD>NeQ)V?*1DE;y$^HS@nQ>ns(-q1Z| zZEr6~d4{X31c^X~={AHP2_BpH=bw=Vb6T1+?f3@Cu8elhVWJXB_L6pUZDquVVqz|p zyM_0gG-^~U@%?AdCLJm{S zFbto%T1n)af4m^;;-xKb&NR)DT<1jHj#I8-yajUJs!KnsgCw@hXKHK^3R;(;ewU`D zIa?Fr-pkw}myJAdAcEzLv=7A`hyYMoq{U&9PMnJ9T4G^Su>*67w{f876&4Ob?hm9u z7c4t$8Y0kW%NN4@FZ`DRWNfBP>8GiwN$~34{R-v;=&CqEJvOQu@wC?#t?L1rx_UZ5hUDevGtTVT_Lk`_xbf)YcqF2d+rAqP$K8A5wrIL9hyRn)LMx@$ z^j3c;sEIB{GXniG$A{6C+}woNSaYpVp0vX`w=sAmP($c+4=Fo?4eGXSk^SdMgA=_Wkr=r&# z0HMvBHmx*PV~3&$8g$BMjf(c-ew)k@%rmMKd!{GA;Z#N29m%(vTG+yFd*zG%lwDr$c2?jd2@NjB9 zdV~q~oV-h4>>A~FLmn^@UR6m+XFs;OXW-`m&PU#FvkKnJDFk6RFX49cx_BL9jDTaK zM6L$>nS$P8z#3y?4`4U~F>_-;dPXTeGGNFPc%$2eCnpMBFaYF)!+@%Nn%N#0nIA7- zQySNC<}VBrrsi(i#L!y$Ab?GgCn>nD!~Rf5IIfxOUi9cu4YI+JG8};nR5aS3L2gy( zM)MRm>e|wb za>7fti2e*`!!Dc5DV!sQdkei@OuEzy8#!g=?3B4}`i|656#JLHc+qpfQQKqqy`DMq z4Jtk0NOyi&s+286Hp{ZZKPhbZP!(|)2e7iT9X^qma4lXODF5l>M}@%AZUKpN36

rS0X+^*Fjt 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 literal 0 HcmV?d00001 diff --git a/doc/dia/Classes.dia b/doc/dia/Classes.dia new file mode 100644 index 0000000000000000000000000000000000000000..8d48c82da4a5840e1d8a451310ac33c9cfc7d8c4 GIT binary patch literal 4495 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 literal 0 HcmV?d00001 diff --git a/doc/dia/UseCases.png b/doc/dia/UseCases.png new file mode 100644 index 0000000000000000000000000000000000000000..25cc46d99c6ede30bef2872b00631342edaaf1a1 GIT binary patch literal 32874 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 A*8l(j literal 0 HcmV?d00001 -- 2.7.4