Merge "printer: print FileIdentifier" into tizen_5.5 accepted/tizen/5.5/unified/20191104.111320 submit/tizen_5.5/20191101.081616
authorsanghyeok oh <sanghyeok.oh@samsung.com>
Fri, 1 Nov 2019 08:08:20 +0000 (08:08 +0000)
committerGerrit Code Review <gerrit@review.ap-northeast-2.compute.internal>
Fri, 1 Nov 2019 08:08:20 +0000 (08:08 +0000)
doc/documentation.rst [new file with mode: 0644]
src/internal/include/fb.fbs
src/internal/include/fb_generated.h
src/internal/include/flatbuffers/flatbuffers.h
src/internal/serializer.cpp
src/internal/storage_backend_serialized.cpp
src/internal/transaction_guard.hpp
src/libdbuspolicy1.cpp

diff --git a/doc/documentation.rst b/doc/documentation.rst
new file mode 100644 (file)
index 0000000..00b4ade
--- /dev/null
@@ -0,0 +1,114 @@
+Libdbuspolicy documentation
+---------------------------
+
+This document describes purpose, assumptions,  and implementation of libdbuspolicy.
+
+Doxygen docs
+------------
+
+API and many internal functions and methods are described in doxygen markups through the code.
+This document does not describe API or any particular function or method.
+
+Purpose
+-------
+
+Tizen on some platforms supports kdbus, an in-kernel D-Bus implementation. Kdbus systems
+work without central D-Bus daemon (dbus-daemon) running. Messages are passed between
+processes by the kernel driver. The driver supports some kind of access control,
+but it is not sufficient for Tizen. For example, it does not support Cynara privileges.
+
+That's why this library has been created. It controls accesses and sits between
+D-Bus libraries and the kdbus driver. D-Bus libraries, which support libdbuspolicy
+are libdbus and glib. systemd's sd-bus does not support libdbuspolicy. Libdbuspolicy
+uses standard XML policy files as defined for dbus-daemon.
+
+Security considerations
+-----------------------
+
+If a rogue program is executed, it can bypass libdbus, glib and libdbuspolicy
+and connect directly to kdbus to send or receive messages. Only file access rules apply.
+However, if other clients use standard libdbus or glib libraries, the messages
+will be checked against both sending and receiving policy. The only real
+downside is owning policy. A rogue program can own any name, and even hijack names
+from other owners.
+
+Implementation (a bit of history)
+-------------------------------------
+
+Implementation of libdbuspolicy went through several phases. In the first phase
+a database of rules was created. The database was in fact two databases: system and session
+database. Each of them was divided into default, mandatory, user and group sub-databases.
+
+This basic structure was maintained at least until 2019.
+
+In 2018 it appeared that memory consumption of the database was too big. Heavy kilobytes
+multiplied by over a hundred of processes were taking up heavy megabytes. Additionally,
+initialization of the database slowed down each program which was using D-Bus. This led
+to code refactoring and introducing serialization.
+
+Google's FlatBuffers (https://google.github.io/flatbuffers/) were selected to implement
+serialization. In order to do that, the code was heavily refactored.
+
+Layers
+------
+
+There are the following layers in libdbuspolicy:
+
+#. API and programs: library code for connecting database searches with acquiring data from kdbus,
+   and program code for the utilities, i.e. printer, serializer, finder;
+#. checking: library code for handling access check requests, with full information; it performs
+   several database queries per one check;
+#. database: data keeping, searching and converting;
+#. FlatBuffers: serialization and deserialization.
+
+There is also some helping code for printing diagnostics, etc.
+
+Design
+------
+
+The almost current overview of the design of the checking, database, and FlatBuffers layers:
+
+.. image:: dia/Classes-use-only-serialized.png
+
+Diagram description:
+
+#. MatchItem, MatchItemWithUserAndGroup - queries constructed by PolicyChecker;
+#. PolicyChecker - entry point for checking policy; creates MatchItems to pass them
+   to StorageBackendSerialized;
+#. 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; serves as an intermediate stage
+   for StorageBackendSerialized;
+#. 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; used for creating serialized data from
+   StorageBackendXML;
+#. Printer - an entry point for printer tool;
+#. FlatBuffers - a library used for management of serialized data.
+
+The implementation differs from the design mostly in these ways:
+
+#. StorageBackendXML no longer is able to get decisions (getDecisionItem*), it is used
+   only for conversion to serialized form (StorageBackendSerialized)
+#. Additional use case, Finder, was added; it's a bit like 'grep' for policies: prints
+   matching rules for a given query
+
+Improvements
+------------
+
+Instead of a direct linear search a simple mapping index for send rules was introduced
+in StorageBackendSerialized. Basically, it creates a map for send_destination names.
+Send_destination is a parameter by which the rules differ the most. Thus, "splitting"
+linear search over just matching send_destination names gives significant performance
+boost. As of Oct 2019 it has been implemented without the serialization. It needs
+about 15KB per process. Therefore, there is a plan to add the serialization to the index.
+
+Additionally, there is a concept of creating a direct acycling graph containing the rules.
+It would contains "levels", for keeping track of various parts of the query/key
+(name, interface, method, object, message type, etc.). Each level is dedicated for
+one part. A node in a level in which key is a text can be e.g. a trie. A query would
+traverse the dag through levels, until reaching a leaf with ALLOW, DENY or CHECK value.
+Creating and serializing such graph would make the querying process very fast and low-memory
+consuming.
index 8fce719..efbf6b9 100644 (file)
@@ -53,6 +53,8 @@ table SendSet {
 
 table PolicySend {
     items:[ItemSend];
+    index:[NameScoresPair];            // version "LDP2" extension
+    prefix_index:[uint];               // version "LDP2" extension
 }
 
 table ItemSend {
@@ -107,6 +109,13 @@ table DecisionItem {
     privilege:string;
 }
 
+/* version LDP2 extension starts here - sendIndex added */
+table NameScoresPair {
+    name: string (key);
+    best_score: uint;
+    item_refs:[uint];
+}
+
 root_type File;
 
-file_identifier "LDP1";
+file_identifier "LDP2";
index ce371e4..335b3d9 100644 (file)
@@ -42,6 +42,8 @@ struct ItemAccess;
 
 struct DecisionItem;
 
+struct NameScoresPair;
+
 enum Decision {
   Decision_ANY = 0,
   Decision_ALLOW = 1,
@@ -723,16 +725,29 @@ inline flatbuffers::Offset<SendSet> CreateSendSetDirect(
 
 struct PolicySend FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
   enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
-    VT_ITEMS = 4
+    VT_ITEMS = 4,
+    VT_INDEX = 6,
+    VT_PREFIX_INDEX = 8
   };
   const flatbuffers::Vector<flatbuffers::Offset<ItemSend>> *items() const {
     return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<ItemSend>> *>(VT_ITEMS);
   }
+  const flatbuffers::Vector<flatbuffers::Offset<NameScoresPair>> *index() const {
+    return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<NameScoresPair>> *>(VT_INDEX);
+  }
+  const flatbuffers::Vector<uint32_t> *prefix_index() const {
+    return GetPointer<const flatbuffers::Vector<uint32_t> *>(VT_PREFIX_INDEX);
+  }
   bool Verify(flatbuffers::Verifier &verifier) const {
     return VerifyTableStart(verifier) &&
            VerifyOffset(verifier, VT_ITEMS) &&
            verifier.VerifyVector(items()) &&
            verifier.VerifyVectorOfTables(items()) &&
+           VerifyOffset(verifier, VT_INDEX) &&
+           verifier.VerifyVector(index()) &&
+           verifier.VerifyVectorOfTables(index()) &&
+           VerifyOffset(verifier, VT_PREFIX_INDEX) &&
+           verifier.VerifyVector(prefix_index()) &&
            verifier.EndTable();
   }
 };
@@ -743,6 +758,12 @@ struct PolicySendBuilder {
   void add_items(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ItemSend>>> items) {
     fbb_.AddOffset(PolicySend::VT_ITEMS, items);
   }
+  void add_index(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<NameScoresPair>>> index) {
+    fbb_.AddOffset(PolicySend::VT_INDEX, index);
+  }
+  void add_prefix_index(flatbuffers::Offset<flatbuffers::Vector<uint32_t>> prefix_index) {
+    fbb_.AddOffset(PolicySend::VT_PREFIX_INDEX, prefix_index);
+  }
   explicit PolicySendBuilder(flatbuffers::FlatBufferBuilder &_fbb)
         : fbb_(_fbb) {
     start_ = fbb_.StartTable();
@@ -757,19 +778,29 @@ struct PolicySendBuilder {
 
 inline flatbuffers::Offset<PolicySend> CreatePolicySend(
     flatbuffers::FlatBufferBuilder &_fbb,
-    flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ItemSend>>> items = 0) {
+    flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ItemSend>>> items = 0,
+    flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<NameScoresPair>>> index = 0,
+    flatbuffers::Offset<flatbuffers::Vector<uint32_t>> prefix_index = 0) {
   PolicySendBuilder builder_(_fbb);
+  builder_.add_prefix_index(prefix_index);
+  builder_.add_index(index);
   builder_.add_items(items);
   return builder_.Finish();
 }
 
 inline flatbuffers::Offset<PolicySend> CreatePolicySendDirect(
     flatbuffers::FlatBufferBuilder &_fbb,
-    const std::vector<flatbuffers::Offset<ItemSend>> *items = nullptr) {
+    const std::vector<flatbuffers::Offset<ItemSend>> *items = nullptr,
+    const std::vector<flatbuffers::Offset<NameScoresPair>> *index = nullptr,
+    const std::vector<uint32_t> *prefix_index = nullptr) {
   auto items__ = items ? _fbb.CreateVector<flatbuffers::Offset<ItemSend>>(*items) : 0;
+  auto index__ = index ? _fbb.CreateVector<flatbuffers::Offset<NameScoresPair>>(*index) : 0;
+  auto prefix_index__ = prefix_index ? _fbb.CreateVector<uint32_t>(*prefix_index) : 0;
   return FB::CreatePolicySend(
       _fbb,
-      items__);
+      items__,
+      index__,
+      prefix_index__);
 }
 
 struct ItemSend FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
@@ -1409,6 +1440,89 @@ inline flatbuffers::Offset<DecisionItem> CreateDecisionItemDirect(
       privilege__);
 }
 
+struct NameScoresPair FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_NAME = 4,
+    VT_BEST_SCORE = 6,
+    VT_ITEM_REFS = 8
+  };
+  const flatbuffers::String *name() const {
+    return GetPointer<const flatbuffers::String *>(VT_NAME);
+  }
+  bool KeyCompareLessThan(const NameScoresPair *o) const {
+    return *name() < *o->name();
+  }
+  int KeyCompareWithValue(const char *val) const {
+    return strcmp(name()->c_str(), val);
+  }
+  uint32_t best_score() const {
+    return GetField<uint32_t>(VT_BEST_SCORE, 0);
+  }
+  const flatbuffers::Vector<uint32_t> *item_refs() const {
+    return GetPointer<const flatbuffers::Vector<uint32_t> *>(VT_ITEM_REFS);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyOffsetRequired(verifier, VT_NAME) &&
+           verifier.VerifyString(name()) &&
+           VerifyField<uint32_t>(verifier, VT_BEST_SCORE) &&
+           VerifyOffset(verifier, VT_ITEM_REFS) &&
+           verifier.VerifyVector(item_refs()) &&
+           verifier.EndTable();
+  }
+};
+
+struct NameScoresPairBuilder {
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_name(flatbuffers::Offset<flatbuffers::String> name) {
+    fbb_.AddOffset(NameScoresPair::VT_NAME, name);
+  }
+  void add_best_score(uint32_t best_score) {
+    fbb_.AddElement<uint32_t>(NameScoresPair::VT_BEST_SCORE, best_score, 0);
+  }
+  void add_item_refs(flatbuffers::Offset<flatbuffers::Vector<uint32_t>> item_refs) {
+    fbb_.AddOffset(NameScoresPair::VT_ITEM_REFS, item_refs);
+  }
+  explicit NameScoresPairBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  NameScoresPairBuilder &operator=(const NameScoresPairBuilder &);
+  flatbuffers::Offset<NameScoresPair> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<NameScoresPair>(end);
+    fbb_.Required(o, NameScoresPair::VT_NAME);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<NameScoresPair> CreateNameScoresPair(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    flatbuffers::Offset<flatbuffers::String> name = 0,
+    uint32_t best_score = 0,
+    flatbuffers::Offset<flatbuffers::Vector<uint32_t>> item_refs = 0) {
+  NameScoresPairBuilder builder_(_fbb);
+  builder_.add_item_refs(item_refs);
+  builder_.add_best_score(best_score);
+  builder_.add_name(name);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<NameScoresPair> CreateNameScoresPairDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    const char *name = nullptr,
+    uint32_t best_score = 0,
+    const std::vector<uint32_t> *item_refs = nullptr) {
+  auto name__ = name ? _fbb.CreateString(name) : 0;
+  auto item_refs__ = item_refs ? _fbb.CreateVector<uint32_t>(*item_refs) : 0;
+  return FB::CreateNameScoresPair(
+      _fbb,
+      name__,
+      best_score,
+      item_refs__);
+}
+
 inline const FB::File *GetFile(const void *buf) {
   return flatbuffers::GetRoot<FB::File>(buf);
 }
@@ -1418,7 +1532,7 @@ inline const FB::File *GetSizePrefixedFile(const void *buf) {
 }
 
 inline const char *FileIdentifier() {
-  return "LDP1";
+  return "LDP2";
 }
 
 inline bool FileBufferHasIdentifier(const void *buf) {
index 8c87dff..8f5ae32 100644 (file)
@@ -551,6 +551,9 @@ class DetachedBuffer {
   #if !defined(FLATBUFFERS_CPP98_STL)
   // clang-format on
   DetachedBuffer &operator=(DetachedBuffer &&other) {
+    if (this == &other)
+      return *this;
+
     destroy();
 
     allocator_ = other.allocator_;
index eb185e8..f5cee57 100644 (file)
@@ -222,6 +222,48 @@ auto Serializer::serialize_policy(const PolicyOwn &policy)
        return serialize_tree(policy.getTree());
 }
 
+template <>
+auto Serializer::serialize_policy(const PolicySend &policy)
+                                   -> FbOff<FB::PolicySend> {
+       std::vector<FbOff<FB::ItemSend>> items;
+
+       // this maps a name to a pair (highest score + list of scores/ids for this name)
+       std::map<boost::string_ref, std::pair<uint32_t, std::vector<uint32_t>>> policyIndex;
+       // prefix index is just a list of ids
+       std::vector<uint32_t> prefixIndex;
+       uint32_t cnt = 1;
+
+       for (const auto &item : policy.getItems()) {
+               items.push_back(serialize_item<PolicySend>(item));
+
+               // create indexes
+               if (!item.isNamePrefix()) {
+                       auto &elem = policyIndex[item.getName()];   // create or get an entry
+                       elem.second.push_back(cnt);                 // add score/id to the list
+                       // we insert items in increasing score/id order, so we just update the highest score on each add
+                       elem.first = cnt;
+               } else {
+                       // just collect the prefix rules
+                       prefixIndex.push_back(cnt);
+               }
+               ++cnt;
+       }
+
+       // serialize main index
+       std::vector<FbOff<FB::NameScoresPair>> index;
+
+       for (auto &it: policyIndex)
+               index.push_back(FB::CreateNameScoresPairDirect(m_builder,
+                                                                                                               it.first.data(),     // name
+                                                                                                               it.second.first,     // best_score
+                                                                                                               &it.second.second)); // vector of scores/ids
+
+       return FB::CreatePolicySend(m_builder,
+                       m_builder.CreateVector(items),
+                       m_builder.CreateVector(index),
+                       m_builder.CreateVector(prefixIndex));
+}
+
 template <typename T>
 auto Serializer::serialize_policy(const T &policy) -> FbOff<typename type_helper<T>::policy> {
        std::vector<FbOff<typename type_helper<T>::item>> items;
index 929f4d5..fa8fe4e 100644 (file)
@@ -24,6 +24,7 @@
 #include <cerrno>
 #include <fcntl.h>
 #include <map>
+#include <string>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -43,6 +44,85 @@ typedef boost::tokenizer<boost::char_separator<char>> tokenizer;
 template <typename T>
 struct type_helper;
 
+namespace {
+
+inline boost::string_ref s(const flatbuffers::String *str) {
+       return boost::string_ref(str->c_str(), str->size());
+}
+
+template <typename T, typename I>
+bool match(const T &match, const I *i) {
+       return match.match(makeMessageType(i->type()),
+                                          s(i->interface()),
+                                          s(i->path()),
+                                          s(i->member()),
+                                          s(i->name()),
+                                          i->is_name_prefix(),
+                                          makeDecision(i->decision()->decision()));
+}
+
+template <> bool match(const ldp_xml_parser::MatchItemAccess &match, const FB::ItemAccess *item) {
+       return match.match(makeBusAccessType(item->type()), item->uid(), item->gid());
+}
+
+template <typename T, typename P = typename type_helper<T>::policy_type>
+ldp_xml_parser::DecisionItem getDecisionItem(const T &item, const P *policy) {
+       const auto *v = policy->items();
+       for (auto it = v->rbegin(); it != v->rend(); ++it) {
+               if (match(item, *it))
+                       return makeDecisionItem((*it)->decision());
+       }
+       return ldp_xml_parser::Decision::ANY;
+}
+
+const DecisionItem *getDecisionItemFromTree(const FB::PolicyOwnNode *node,
+                                                                                       tokenizer &tokens,
+                                                                                       tokenizer::iterator &iterator) {
+       if (iterator == tokens.end()) {
+               if (node->decision_item()->decision() != Decision_ANY)
+                       return node->decision_item();
+               else
+                       return node->prefix_decision_item();
+       }
+
+       auto child = node->children()->LookupByKey(iterator->c_str());
+       if (nullptr == child)
+               return node->prefix_decision_item();
+
+       ++iterator;
+       const DecisionItem *child_decision = getDecisionItemFromTree(child, tokens, iterator);
+       if (child_decision->decision() == Decision_ANY)
+               return node->prefix_decision_item();
+
+       return child_decision;
+}
+
+template <> ldp_xml_parser::DecisionItem getDecisionItem(const ldp_xml_parser::MatchItemOwn &item,
+                                                                                                                const PolicyOwn *policy) {
+       if (item.getName().length() == 0)
+               return ldp_xml_parser::Decision::DENY;
+
+       boost::char_separator<char> separator(".");
+       tokenizer tokens(item.getName(), separator);
+
+       auto node = policy->tree();
+       if (nullptr == node)
+               return ldp_xml_parser::Decision::ANY;
+
+       auto iterator = tokens.begin();
+
+       return makeDecisionItem(getDecisionItemFromTree(node, tokens, iterator));
+}
+
+template <typename T, typename P = typename type_helper<T>::policy_type>
+ldp_xml_parser::DecisionItem getDecisionItemMaybeNull(const T &item, const P *policyPair) {
+       if (nullptr == policyPair)
+               return ldp_xml_parser::Decision::ANY;
+       return getDecisionItem(item, policyPair->policy());
+}
+
+} // anonymous namespace
+
 class StorageBackendSerialized::StorageBackendSerializedImpl {
        int fd{-1};
        uint8_t *mem{static_cast<decltype(mem)>(MAP_FAILED)};
@@ -54,27 +134,13 @@ class StorageBackendSerialized::StorageBackendSerializedImpl {
        void releaseMMap();
        void releaseFD();
 
-       typedef std::pair<const ItemSend *, unsigned> ItemWithScore;    // a pair: Item, index of Item
-       typedef std::vector<ItemWithScore> ItemsWithScore;                              // items
-       typedef std::pair<unsigned, ItemsWithScore> HighestScoreWithItems;      // value for index map: highest index for items, items
-       typedef std::map<boost::string_ref, HighestScoreWithItems> MapIndex;
-       typedef std::vector<ItemWithScore> SendPrefixIndex;
-
-       MapIndex sendIndex; // context default
-       SendPrefixIndex sendPrefixIndex; // context default prefix rules
-       std::map<uid_t, MapIndex> userSendIndex;
-       std::map<uid_t, SendPrefixIndex> userSendPrefixIndex;
-
 public:
        bool init(const char *filename, bool verify);
        bool init(const FB::File *f);
        bool initFromXML(const char *config_name);
        void release();
 
-       void createSendIndex();
-       ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item, const MapIndex &index, const SendPrefixIndex &prefixIndex);
-       ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item);
-       ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item, uid_t uid);
+       ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item, const FB::PolicySend *policy);
 
        void printContent(const bool xml_format = false) const;
 
@@ -82,106 +148,73 @@ public:
        const M *getPolicySet();
 };
 
-inline boost::string_ref s(const flatbuffers::String *str) {
-       return boost::string_ref(str->c_str(), str->size());
-}
-
-template <typename T, typename I>
-bool match(const T &match, const I *i) {
-       return match.match(makeMessageType(i->type()),
-                                          s(i->interface()),
-                                          s(i->path()),
-                                          s(i->member()),
-                                          s(i->name()),
-                                          i->is_name_prefix(),
-                                          makeDecision(i->decision()->decision()));
-}
-
 ldp_xml_parser::DecisionItem StorageBackendSerialized::StorageBackendSerializedImpl::getDecisionFromSendIndex(const MatchItemSend &item,
-               const MapIndex &index, const SendPrefixIndex &prefixIndex) {
-       ldp_xml_parser::DecisionItem decision(ldp_xml_parser::Decision::ANY);
+               const FB::PolicySend *policy) {
+
+       const auto *index = policy->index();
+
+       if (!index || index->size() == 0)           // this indicates version earlier than LDP2
+               return getDecisionItem(item, policy);   // make it old way for old databases
+
+       std::pair<const ItemSend*, uint32_t> currentBest(nullptr, 0);
+
+       auto updateCurrentBest = [&currentBest, &item, &policy](const flatbuffers::Vector<uint32_t> *vec) {
+               // look from the back, the rule is the same as for the full database
+               // we now only check among less elements, because the database is indexed to small lists
+               // item_scores are in increasing order in the index, and they serve also as ids of the policy rules
+               for (auto item_score_it = vec->rbegin(); item_score_it != vec->rend(); item_score_it++) {
+                       const auto *fb_item = policy->items()->Get(*item_score_it - 1); // rules are indexed/scored from 1
+                       if (*item_score_it > currentBest.second && match(item, fb_item)) {
+                               currentBest.first = fb_item;
+                               currentBest.second = *item_score_it;
+                               return;
+                       } else if (*item_score_it <= currentBest.second) {
+                               // there is no need to look further as we can't improve the score anymore
+                               return;
+                       }
+               }
+       };
 
-       ItemWithScore currentBest(nullptr, 0);
+       auto searchAndUpdateCurrentBest = [&currentBest, &index, &updateCurrentBest](boost::string_ref name_ref) {
+               // we need to create C-string for flatbuffers lookups
+               // boost::string_ref gives us correct start, but possibly NUL-terminated in a wrong place, as it does not modify
+               // input string and keeps only the length
+               std::string name(name_ref.data(), name_ref.size());
 
-       auto updateCurrentBest = [&currentBest, &item, &index](boost::string_ref name) {
                // check if there are any rules for the name
-               auto fit = index.find(name);
-               if (fit == index.end())
+               const auto *fit = index->LookupByKey(name.c_str());
+               if (!fit)
                        return;
 
                // check if there's any chance to get better score
-               // fit->second.first is the highest score from vector fit->second.second
-               if (fit->second.first <= currentBest.second)
+               if (fit->best_score() <= currentBest.second)
                        return;
 
                // look for better score
-               for (const auto &it: fit->second.second) {
-                       if (it.second > currentBest.second && match(item, it.first))
-                               currentBest = it;
-               }
+               updateCurrentBest(fit->item_refs());
        };
 
+       const auto *prefixIndex = policy->prefix_index();
+       assert(prefixIndex);
+
        // iterate over names
        for (const auto &name: item.getNames()) {
                // find and check the no-prefix rules
-               updateCurrentBest(name);
+               searchAndUpdateCurrentBest(name);
 
                // check the prefix rules
-               for (const auto &prefixItem: prefixIndex) {
-                       if (prefixItem.second > currentBest.second && match(item, prefixItem.first))
-                               currentBest = prefixItem;
-               }
+               updateCurrentBest(prefixIndex);
        }
 
        // check no-name rules
-       updateCurrentBest("");
+       searchAndUpdateCurrentBest("");
 
+       // if a matching rule was found, return the decision
        if (currentBest.first)
-               decision = makeDecisionItem(currentBest.first->decision());
-
-       return decision;
-}
-
-ldp_xml_parser::DecisionItem StorageBackendSerialized::StorageBackendSerializedImpl::getDecisionFromSendIndex(const MatchItemSend &item) {
-       return getDecisionFromSendIndex(item, sendIndex, sendPrefixIndex);
-}
-
-ldp_xml_parser::DecisionItem StorageBackendSerialized::StorageBackendSerializedImpl::getDecisionFromSendIndex(const MatchItemSend &item, uid_t uid) {
-       return getDecisionFromSendIndex(item, userSendIndex[uid], userSendPrefixIndex[uid]);
-}
-
-void StorageBackendSerialized::StorageBackendSerializedImpl::createSendIndex() {
-       // helper for adding items to indexes
-       auto add = [](MapIndex &index, SendPrefixIndex &prefixIndex, boost::string_ref key, ItemWithScore value) {
-               if (value.first->is_name_prefix()) {
-                       prefixIndex.push_back(value);
-               } else {
-                       index[key].second.push_back(value);
-                       // we insert items in increasing score order, so we just update the highest score on each add
-                       index[key].first = value.second;
-               }
-       };
-
-       // context default index
-       const auto *policy = file->m_send_set()->context_default();
-       unsigned cnt = 1;
+               return makeDecisionItem(currentBest.first->decision());
 
-       const auto *v = policy->items();
-       for (auto it = v->begin(); it != v->end(); ++it)
-               add(sendIndex, sendPrefixIndex,
-                               boost::string_ref(it->name()->c_str(), it->name()->size()),
-                               ItemWithScore(*it, cnt++));
-
-       // users index
-       for (const auto &uidPolicy: *file->m_send_set()->user()) {
-               cnt = 1;
-               for (const auto it: *uidPolicy->policy()->items()) {
-                       add(userSendIndex[uidPolicy->id()],
-                                       userSendPrefixIndex[uidPolicy->id()],
-                                       boost::string_ref(it->name()->c_str(), it->name()->size()),
-                                       ItemWithScore(it, cnt++));
-               }
-       }
+       // or if no matching rule was not found, return the default decision
+       return ldp_xml_parser::DecisionItem(ldp_xml_parser::Decision::ANY);
 }
 
 void StorageBackendSerialized::StorageBackendSerializedImpl::releaseMMap() {
@@ -268,17 +301,13 @@ bool StorageBackendSerialized::StorageBackendSerializedImpl::init(const char *fi
        mmapGuard.dismiss();
 
        file = GetFile(mem);
-       bool res = file != nullptr;
-       if (res)
-               createSendIndex();
-       return res;
+       return file != nullptr;
 }
 
 bool StorageBackendSerialized::StorageBackendSerializedImpl::init(const FB::File *f) {
        assert(nullptr == file);
 
        file = f;
-       createSendIndex();
        return true;
 }
 
@@ -341,74 +370,19 @@ void StorageBackendSerialized::printContent(const bool xml_format) const {
        pimpl->printContent(xml_format);
 }
 
-template <> bool match(const ldp_xml_parser::MatchItemAccess &match, const FB::ItemAccess *item) {
-       return match.match(makeBusAccessType(item->type()), item->uid(), item->gid());
-}
-
-template <typename T, typename P = typename type_helper<T>::policy_type>
-ldp_xml_parser::DecisionItem getDecisionItem(const T &item, const P *policy) {
-       const auto *v = policy->items();
-       for (auto it = v->rbegin(); it != v->rend(); ++it) {
-               if (match(item, *it))
-                       return makeDecisionItem((*it)->decision());
-       }
-       return ldp_xml_parser::Decision::ANY;
-}
-
-const DecisionItem *getDecisionItemFromTree(const FB::PolicyOwnNode *node,
-                                                                                       tokenizer &tokens,
-                                                                                       tokenizer::iterator &iterator) {
-       if (iterator == tokens.end()) {
-               if (node->decision_item()->decision() != Decision_ANY)
-                       return node->decision_item();
-               else
-                       return node->prefix_decision_item();
-       }
-
-       auto child = node->children()->LookupByKey(iterator->c_str());
-       if (nullptr == child)
-               return node->prefix_decision_item();
-
-       ++iterator;
-       const DecisionItem *child_decision = getDecisionItemFromTree(child, tokens, iterator);
-       if (child_decision->decision() == Decision_ANY)
-               return node->prefix_decision_item();
-
-       return child_decision;
-}
-
-template <> ldp_xml_parser::DecisionItem getDecisionItem(const ldp_xml_parser::MatchItemOwn &item,
-                                                                                                                const PolicyOwn *policy) {
-       if (item.getName().length() == 0)
-               return ldp_xml_parser::Decision::DENY;
-
-       boost::char_separator<char> separator(".");
-       tokenizer tokens(item.getName(), separator);
-
-       auto node = policy->tree();
-       if (nullptr == node)
-               return ldp_xml_parser::Decision::ANY;
-
-       auto iterator = tokens.begin();
-
-       return makeDecisionItem(getDecisionItemFromTree(node, tokens, iterator));
-}
-
-template <typename T, typename P = typename type_helper<T>::policy_type>
-ldp_xml_parser::DecisionItem getDecisionItemMaybeNull(const T &item, const P *policyPair) {
-       if (nullptr == policyPair)
-               return ldp_xml_parser::Decision::ANY;
-       return getDecisionItem(item, policyPair->policy());
-}
-
 template <typename T>
 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const T &item) const {
        return getDecisionItem(item, pimpl->getPolicySet<T>()->context_mandatory());
 }
 
 template <>
+ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const MatchItemSend &item) const {
+       return pimpl->getDecisionFromSendIndex(item, pimpl->getPolicySet<MatchItemSend>()->context_mandatory());
+}
+
+template <>
 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const MatchItemSend &item) const {
-       return pimpl->getDecisionFromSendIndex(item);
+       return pimpl->getDecisionFromSendIndex(item, pimpl->getPolicySet<MatchItemSend>()->context_default());
 }
 
 template <>
@@ -416,7 +390,7 @@ ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemUser(uid_t
        auto *policyPair = pimpl->getPolicySet<MatchItemSend>()->user()->LookupByKey(uid);
        if (nullptr == policyPair)
                return ldp_xml_parser::Decision::ANY;
-       return pimpl->getDecisionFromSendIndex(item, uid);
+       return pimpl->getDecisionFromSendIndex(item, policyPair->policy());
 }
 
 template <typename T>
index dec307f..9df2600 100644 (file)
@@ -42,7 +42,7 @@ public:
        }
 
        Guard(Guard &&g) : fun(std::move(g.fun)), active(g.active) {
-               dismiss();
+               g.dismiss();
        }
 };
 
index 404dad7..1c8f2c3 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/limits.h>
 #include <memory>
 #include <mutex>
+#include <new>
 #include <pwd.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -191,7 +192,7 @@ public:
 
        static inline kconn *get_shared(BusType bus_type, int fd)
        {
-               kconn *result = new kconn(Checker::get(bus_type).checker(), fd);
+               kconn *result = new (std::nothrow) kconn(Checker::get(bus_type).checker(), fd);
                if (nullptr == result)
                        LOGE("Error: failed to allocate memory for policy configuration");
                return result;