refactoring: move Serializer to ldp_serializer namespace
[platform/core/system/libdbuspolicy.git] / src / internal / storage_backend_serialized.cpp
1 /*
2  * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15 */
16 #include "include/fb_generated.h"
17 #include "print_content.hpp"
18 #include "serialized_convert.hpp"
19 #include "serializer.hpp"
20 #include "storage_backend_serialized.hpp"
21 #include "transaction_guard.hpp"
22 #include "tslog.hpp"
23 #include <boost/tokenizer.hpp>
24 #include <cerrno>
25 #include <fcntl.h>
26 #include <map>
27 #include <string>
28 #include <sys/mman.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32
33 using namespace FB;
34 using ldp_xml_parser::MatchItemSend;
35
36 namespace ldp_serialized {
37
38 // Set max size of serialized file to prevent mmap with unexpected memory size.
39 // 1MB. Adjustable
40 #define MAX_SFILE_SIZE (1024 * 1024)
41
42 typedef boost::tokenizer<boost::char_separator<char>> tokenizer;
43
44 template <typename T>
45 struct type_helper;
46
47 namespace {
48
49 inline boost::string_ref s(const flatbuffers::String *str) {
50         return boost::string_ref(str->c_str(), str->size());
51 }
52
53 template <typename T, typename I>
54 bool match(const T &match, const I *i) {
55         return match.match(makeMessageType(i->type()),
56                                            s(i->interface()),
57                                            s(i->path()),
58                                            s(i->member()),
59                                            s(i->name()),
60                                            i->is_name_prefix(),
61                                            makeDecision(i->decision()->decision()));
62 }
63
64 template <> bool match(const ldp_xml_parser::MatchItemAccess &match, const FB::ItemAccess *item) {
65         return match.match(makeBusAccessType(item->type()), item->uid(), item->gid());
66 }
67
68 template <typename T, typename P = typename type_helper<T>::policy_type>
69 ldp_xml_parser::DecisionItem getDecisionItem(const T &item, const P *policy) {
70         const auto *v = policy->items();
71         for (auto it = v->rbegin(); it != v->rend(); ++it) {
72                 if (match(item, *it))
73                         return makeDecisionItem((*it)->decision());
74         }
75         return ldp_xml_parser::Decision::ANY;
76 }
77
78 const DecisionItem *getDecisionItemFromTree(const FB::PolicyOwnNode *node,
79                                                                                         tokenizer &tokens,
80                                                                                         tokenizer::iterator &iterator) {
81         if (iterator == tokens.end()) {
82                 if (node->decision_item()->decision() != Decision_ANY)
83                         return node->decision_item();
84                 else
85                         return node->prefix_decision_item();
86         }
87
88         auto child = node->children()->LookupByKey(iterator->c_str());
89         if (nullptr == child)
90                 return node->prefix_decision_item();
91
92         ++iterator;
93         const DecisionItem *child_decision = getDecisionItemFromTree(child, tokens, iterator);
94         if (child_decision->decision() == Decision_ANY)
95                 return node->prefix_decision_item();
96
97         return child_decision;
98 }
99
100 template <> ldp_xml_parser::DecisionItem getDecisionItem(const ldp_xml_parser::MatchItemOwn &item,
101                                                                                                                  const PolicyOwn *policy) {
102         if (item.getName().length() == 0)
103                 return ldp_xml_parser::Decision::DENY;
104
105         boost::char_separator<char> separator(".");
106         tokenizer tokens(item.getName(), separator);
107
108         auto node = policy->tree();
109         if (nullptr == node)
110                 return ldp_xml_parser::Decision::ANY;
111
112         auto iterator = tokens.begin();
113
114         return makeDecisionItem(getDecisionItemFromTree(node, tokens, iterator));
115 }
116
117 template <typename T, typename P = typename type_helper<T>::policy_type>
118 ldp_xml_parser::DecisionItem getDecisionItemMaybeNull(const T &item, const P *policyPair) {
119         if (nullptr == policyPair)
120                 return ldp_xml_parser::Decision::ANY;
121         return getDecisionItem(item, policyPair->policy());
122 }
123
124 } // anonymous namespace
125
126 class StorageBackendSerialized::StorageBackendSerializedImpl {
127         int fd{-1};
128         uint8_t *mem{static_cast<decltype(mem)>(MAP_FAILED)};
129         size_t length{0};
130         const FB::File *file{nullptr};
131
132         std::unique_ptr<ldp_serializer::Serializer> serializer;
133
134         void releaseMMap();
135         void releaseFD();
136
137 public:
138         bool init(const char *filename, bool verify);
139         bool initFromData(const uint8_t *mem, bool verify);
140         bool initFromXML(const char *config_name);
141         void release();
142
143         ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item, const FB::PolicySend *policy);
144
145         void printContent(const bool xml_format = false) const;
146
147         template <typename T, typename M = typename type_helper<T>::policy_set_type>
148         const M *getPolicySet();
149 };
150
151 ldp_xml_parser::DecisionItem StorageBackendSerialized::StorageBackendSerializedImpl::getDecisionFromSendIndex(const MatchItemSend &item,
152                 const FB::PolicySend *policy) {
153
154         const auto *index = policy->index();
155
156         if (!index || index->size() == 0)           // this indicates version earlier than LDP2
157                 return getDecisionItem(item, policy);   // make it old way for old databases
158
159         std::pair<const ItemSend*, uint32_t> currentBest(nullptr, 0);
160
161         auto updateCurrentBest = [&currentBest, &item, &policy](const flatbuffers::Vector<uint32_t> *vec) {
162                 // look from the back, the rule is the same as for the full database
163                 // we now only check among less elements, because the database is indexed to small lists
164                 // item_scores are in increasing order in the index, and they serve also as ids of the policy rules
165                 for (auto item_score_it = vec->rbegin(); item_score_it != vec->rend(); item_score_it++) {
166                         const auto *fb_item = policy->items()->Get(*item_score_it - 1); // rules are indexed/scored from 1
167                         if (*item_score_it > currentBest.second && match(item, fb_item)) {
168                                 currentBest.first = fb_item;
169                                 currentBest.second = *item_score_it;
170                                 return;
171                         } else if (*item_score_it <= currentBest.second) {
172                                 // there is no need to look further as we can't improve the score anymore
173                                 return;
174                         }
175                 }
176         };
177
178         auto searchAndUpdateCurrentBest = [&currentBest, &index, &updateCurrentBest](boost::string_ref name_ref) {
179                 // we need to create C-string for flatbuffers lookups
180                 // boost::string_ref gives us correct start, but possibly NUL-terminated in a wrong place, as it does not modify
181                 // input string and keeps only the length
182                 std::string name(name_ref.data(), name_ref.size());
183
184                 // check if there are any rules for the name
185                 const auto *fit = index->LookupByKey(name.c_str());
186                 if (!fit)
187                         return;
188
189                 // check if there's any chance to get better score
190                 if (fit->best_score() <= currentBest.second)
191                         return;
192
193                 // look for better score
194                 updateCurrentBest(fit->item_refs());
195         };
196
197         const auto *prefixIndex = policy->prefix_index();
198         assert(prefixIndex);
199
200         // iterate over names
201         for (const auto &name: item.getNames()) {
202                 // find and check the no-prefix rules
203                 searchAndUpdateCurrentBest(name);
204
205                 // check the prefix rules
206                 updateCurrentBest(prefixIndex);
207         }
208
209         // check no-name rules
210         searchAndUpdateCurrentBest("");
211
212         // if a matching rule was found, return the decision
213         if (currentBest.first)
214                 return makeDecisionItem(currentBest.first->decision());
215
216         // or if no matching rule was not found, return the default decision
217         return ldp_xml_parser::DecisionItem(ldp_xml_parser::Decision::ANY);
218 }
219
220 void StorageBackendSerialized::StorageBackendSerializedImpl::releaseMMap() {
221         assert(MAP_FAILED != mem);
222         assert(0 != length);
223
224         if (munmap(mem, length) != 0)
225                 tslog::log_error("munmap(): ", tslog::print_errno(errno), "\n");
226
227         mem = static_cast<decltype(mem)>(MAP_FAILED);
228         length = 0;
229 }
230
231 void StorageBackendSerialized::StorageBackendSerializedImpl::releaseFD() {
232         assert(-1 != fd);
233
234         if (close(fd) != 0)
235                 tslog::log_error("close(): ", tslog::print_errno(errno), "\n");
236
237         fd = -1;
238 }
239
240 void StorageBackendSerialized::StorageBackendSerializedImpl::release() {
241         if (-1 != fd) {         // we need to check it, because we may have initialized the storage directly from File *
242                 releaseMMap();
243                 releaseFD();
244         }
245
246         if (nullptr != serializer.get()) {
247                 serializer.reset(nullptr);
248         }
249
250         file = nullptr;
251 }
252
253 bool StorageBackendSerialized::StorageBackendSerializedImpl::init(const char *filename, bool verify) {
254         assert(nullptr == file);
255
256         auto err = [filename] (const char *what) {
257                 tslog::log_error("Can't ", what, " ", filename, ": ", tslog::print_errno(errno), "\n");
258                 return false;
259         };
260
261         fd = open(filename, O_RDONLY);
262         if (-1 == fd)
263                 return err("open");
264
265         auto openGuard = transaction_guard::makeGuard([&] () { releaseFD(); });
266
267         struct stat buf;
268
269         if (fstat(fd, &buf) == -1)
270                 return err("stat");
271
272         length = buf.st_size;
273         if (length > MAX_SFILE_SIZE) {
274                 tslog::log_error("Serialized file size(", length, ") is too large. (>", MAX_SFILE_SIZE, ") bytes.\n");
275                 return false;
276         }
277
278         mem = reinterpret_cast<uint8_t*>(mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0));
279         if (MAP_FAILED == mem)
280                 return err("mmap");
281
282         auto mmapGuard = transaction_guard::makeGuard([&] () { releaseMMap(); });
283
284         if (!initFromData(mem, verify))
285                 return false;
286
287         openGuard.dismiss();
288         mmapGuard.dismiss();
289
290         return true;
291 }
292
293 bool StorageBackendSerialized::StorageBackendSerializedImpl::initFromData(const uint8_t *mem, bool verify) {
294         assert(nullptr == file);
295
296         if (verify) {
297                 auto verifier = flatbuffers::Verifier(mem, length);
298                 if (!FB::VerifyFileBuffer(verifier) || !FB::FileBufferHasIdentifier(mem)) {
299                         char fid[FB::FB_ID_SIZE + 1] = {0, };
300                         strncpy(fid, (const char *)(mem + FB::FB_ID_OFFSET), FB::FB_ID_SIZE);
301
302                         if (strcmp(fid, "LDP1") == 0) {
303                                 tslog::log_error("verification of serialized data: not available\n");
304                                 tslog::log_error("header ID          : ", FB::FileIdentifier(), "\n");
305                                 tslog::log_error("serialized data ID : ", fid, "\n");
306                         } else {
307                                 tslog::log_error("verification of serialized data: failed\n");
308                                 return false;
309                         }
310                 }
311         }
312
313         file = GetFile(mem);
314         return file != nullptr;
315 }
316
317 bool StorageBackendSerialized::StorageBackendSerializedImpl::initFromXML(const char *config_name) {
318         assert(nullptr == file);
319         assert(nullptr == serializer.get());
320
321         serializer.reset(new ldp_serializer::Serializer());
322
323         size_t serialized_size;
324         uint8_t *data = serializer->serialize(config_name, serialized_size);
325         if (nullptr == data)
326                 return false;
327
328         return initFromData(data, false);
329 }
330
331 void StorageBackendSerialized::StorageBackendSerializedImpl::printContent(const bool xml_format) const {
332         print_content::use_xml_format(xml_format);
333         std::cerr << *file;
334 }
335
336 /*************************************************/
337
338 #define TYPE_HELPER(T, t) \
339         template <> \
340         struct type_helper<ldp_xml_parser::MatchItem##T> { \
341                 typedef FB::T##Set policy_set_type; \
342                 typedef FB::Policy##T policy_type; \
343         }; \
344         template <> \
345         auto StorageBackendSerialized::StorageBackendSerializedImpl::getPolicySet<ldp_xml_parser::MatchItem##T>() \
346                 -> const typename type_helper<ldp_xml_parser::MatchItem##T>::policy_set_type * { \
347                 assert(file); \
348                 return file->m_##t##_set(); \
349         }
350
351 TYPE_HELPER(Own, own)
352 TYPE_HELPER(Send, send)
353 TYPE_HELPER(Receive, receive)
354 TYPE_HELPER(Access, access)
355
356 bool StorageBackendSerialized::init(const char *filename, bool verify) {
357         return pimpl->init(filename, verify);
358 }
359
360 bool StorageBackendSerialized::initFromData(const uint8_t *serialized_data, bool verify) {
361         return pimpl->initFromData(serialized_data, verify);
362 }
363
364 bool StorageBackendSerialized::initFromXML(const char *config_name) {
365         return pimpl->initFromXML(config_name);
366 }
367
368 void StorageBackendSerialized::release() {
369         pimpl->release();
370 }
371
372 void StorageBackendSerialized::printContent(const bool xml_format) const {
373         pimpl->printContent(xml_format);
374 }
375
376 template <typename T>
377 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const T &item) const {
378         return getDecisionItem(item, pimpl->getPolicySet<T>()->context_mandatory());
379 }
380
381 template <>
382 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const MatchItemSend &item) const {
383         return pimpl->getDecisionFromSendIndex(item, pimpl->getPolicySet<MatchItemSend>()->context_mandatory());
384 }
385
386 template <>
387 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const MatchItemSend &item) const {
388         return pimpl->getDecisionFromSendIndex(item, pimpl->getPolicySet<MatchItemSend>()->context_default());
389 }
390
391 template <>
392 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemUser(uid_t uid, const MatchItemSend &item) const {
393         auto *policyPair = pimpl->getPolicySet<MatchItemSend>()->user()->LookupByKey(uid);
394         if (nullptr == policyPair)
395                 return ldp_xml_parser::Decision::ANY;
396         return pimpl->getDecisionFromSendIndex(item, policyPair->policy());
397 }
398
399 template <typename T>
400 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const T &item) const {
401         return getDecisionItem(item, pimpl->getPolicySet<T>()->context_default());
402 }
403
404 template <typename T>
405 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemUser(uid_t uid, const T &item) const {
406         return getDecisionItemMaybeNull(item, pimpl->getPolicySet<T>()->user()->LookupByKey(uid));
407 }
408
409 template <typename T>
410 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemGroup(gid_t gid, const T &item) const {
411         return getDecisionItemMaybeNull(item, pimpl->getPolicySet<T>()->group()->LookupByKey(gid));
412 }
413
414 template <typename T>
415 bool StorageBackendSerialized::existsPolicyForGroup(gid_t gid) const {
416         return pimpl->getPolicySet<T>()->group()->LookupByKey(gid) != nullptr;
417 }
418
419 #define T_INSTANTIATION(T) \
420         template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const ldp_xml_parser::MatchItem##T &item) const; \
421         template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const ldp_xml_parser::MatchItem##T &item) const;
422
423 T_INSTANTIATION(Own)
424 T_INSTANTIATION(Send)
425 T_INSTANTIATION(Receive)
426 T_INSTANTIATION(Access)
427
428 #undef T_INSTANTIATION
429
430 #define T_INSTANTIATION2(T) \
431         template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemUser(uid_t uid, const ldp_xml_parser::MatchItem##T &item) const; \
432         template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemGroup(gid_t gid, const ldp_xml_parser::MatchItem##T &item) const; \
433         template bool StorageBackendSerialized::existsPolicyForGroup<ldp_xml_parser::MatchItem##T>(gid_t) const;
434
435 T_INSTANTIATION2(Own)
436 T_INSTANTIATION2(Send)
437 T_INSTANTIATION2(Receive)
438
439 #undef T_INSTANTIATION2
440
441 StorageBackendSerialized::StorageBackendSerialized()
442         : pimpl{new StorageBackendSerializedImpl} {
443 }
444
445 StorageBackendSerialized::~StorageBackendSerialized() {
446         pimpl->release();
447 }
448
449 }