Merge "refactoring: move local functions to an anonymous namespace" into tizen_5.5
[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 <sys/mman.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31
32 using namespace FB;
33 using ldp_xml_parser::MatchItemSend;
34
35 namespace ldp_serialized {
36
37 // Set max size of serialized file to prevent mmap with unexpected memory size.
38 // 1MB. Adjustable
39 #define MAX_SFILE_SIZE (1024 * 1024)
40
41 typedef boost::tokenizer<boost::char_separator<char>> tokenizer;
42
43 template <typename T>
44 struct type_helper;
45
46 namespace {
47
48 inline boost::string_ref s(const flatbuffers::String *str) {
49         return boost::string_ref(str->c_str(), str->size());
50 }
51
52 template <typename T, typename I>
53 bool match(const T &match, const I *i) {
54         return match.match(makeMessageType(i->type()),
55                                            s(i->interface()),
56                                            s(i->path()),
57                                            s(i->member()),
58                                            s(i->name()),
59                                            i->is_name_prefix(),
60                                            makeDecision(i->decision()->decision()));
61 }
62
63 template <> bool match(const ldp_xml_parser::MatchItemAccess &match, const FB::ItemAccess *item) {
64         return match.match(makeBusAccessType(item->type()), item->uid(), item->gid());
65 }
66
67 template <typename T, typename P = typename type_helper<T>::policy_type>
68 ldp_xml_parser::DecisionItem getDecisionItem(const T &item, const P *policy) {
69         const auto *v = policy->items();
70         for (auto it = v->rbegin(); it != v->rend(); ++it) {
71                 if (match(item, *it))
72                         return makeDecisionItem((*it)->decision());
73         }
74         return ldp_xml_parser::Decision::ANY;
75 }
76
77 const DecisionItem *getDecisionItemFromTree(const FB::PolicyOwnNode *node,
78                                                                                         tokenizer &tokens,
79                                                                                         tokenizer::iterator &iterator) {
80         if (iterator == tokens.end()) {
81                 if (node->decision_item()->decision() != Decision_ANY)
82                         return node->decision_item();
83                 else
84                         return node->prefix_decision_item();
85         }
86
87         auto child = node->children()->LookupByKey(iterator->c_str());
88         if (nullptr == child)
89                 return node->prefix_decision_item();
90
91         ++iterator;
92         const DecisionItem *child_decision = getDecisionItemFromTree(child, tokens, iterator);
93         if (child_decision->decision() == Decision_ANY)
94                 return node->prefix_decision_item();
95
96         return child_decision;
97 }
98
99 template <> ldp_xml_parser::DecisionItem getDecisionItem(const ldp_xml_parser::MatchItemOwn &item,
100                                                                                                                  const PolicyOwn *policy) {
101         if (item.getName().length() == 0)
102                 return ldp_xml_parser::Decision::DENY;
103
104         boost::char_separator<char> separator(".");
105         tokenizer tokens(item.getName(), separator);
106
107         auto node = policy->tree();
108         if (nullptr == node)
109                 return ldp_xml_parser::Decision::ANY;
110
111         auto iterator = tokens.begin();
112
113         return makeDecisionItem(getDecisionItemFromTree(node, tokens, iterator));
114 }
115
116 template <typename T, typename P = typename type_helper<T>::policy_type>
117 ldp_xml_parser::DecisionItem getDecisionItemMaybeNull(const T &item, const P *policyPair) {
118         if (nullptr == policyPair)
119                 return ldp_xml_parser::Decision::ANY;
120         return getDecisionItem(item, policyPair->policy());
121 }
122
123 } // anonymous namespace
124
125 class StorageBackendSerialized::StorageBackendSerializedImpl {
126         int fd{-1};
127         uint8_t *mem{static_cast<decltype(mem)>(MAP_FAILED)};
128         size_t length{0};
129         const FB::File *file{nullptr};
130
131         std::unique_ptr<ldp_xml_parser::Serializer> serializer;
132
133         void releaseMMap();
134         void releaseFD();
135
136         typedef std::pair<const ItemSend *, unsigned> ItemWithScore;    // a pair: Item, index of Item
137         typedef std::vector<ItemWithScore> ItemsWithScore;                              // items
138         typedef std::pair<unsigned, ItemsWithScore> HighestScoreWithItems;      // value for index map: highest index for items, items
139         typedef std::map<boost::string_ref, HighestScoreWithItems> MapIndex;
140         typedef std::vector<ItemWithScore> SendPrefixIndex;
141
142         MapIndex sendIndex; // context default
143         SendPrefixIndex sendPrefixIndex; // context default prefix rules
144         std::map<uid_t, MapIndex> userSendIndex;
145         std::map<uid_t, SendPrefixIndex> userSendPrefixIndex;
146
147 public:
148         bool init(const char *filename, bool verify);
149         bool init(const FB::File *f);
150         bool initFromXML(const char *config_name);
151         void release();
152
153         void createSendIndex();
154         ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item, const MapIndex &index, const SendPrefixIndex &prefixIndex);
155         ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item);
156         ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item, uid_t uid);
157
158         void printContent(const bool xml_format = false) const;
159
160         template <typename T, typename M = typename type_helper<T>::policy_set_type>
161         const M *getPolicySet();
162 };
163
164 ldp_xml_parser::DecisionItem StorageBackendSerialized::StorageBackendSerializedImpl::getDecisionFromSendIndex(const MatchItemSend &item,
165                 const MapIndex &index, const SendPrefixIndex &prefixIndex) {
166         ldp_xml_parser::DecisionItem decision(ldp_xml_parser::Decision::ANY);
167
168         ItemWithScore currentBest(nullptr, 0);
169
170         auto updateCurrentBest = [&currentBest, &item, &index](boost::string_ref name) {
171                 // check if there are any rules for the name
172                 auto fit = index.find(name);
173                 if (fit == index.end())
174                         return;
175
176                 // check if there's any chance to get better score
177                 // fit->second.first is the highest score from vector fit->second.second
178                 if (fit->second.first <= currentBest.second)
179                         return;
180
181                 // look for better score
182                 for (const auto &it: fit->second.second) {
183                         if (it.second > currentBest.second && match(item, it.first))
184                                 currentBest = it;
185                 }
186         };
187
188         // iterate over names
189         for (const auto &name: item.getNames()) {
190                 // find and check the no-prefix rules
191                 updateCurrentBest(name);
192
193                 // check the prefix rules
194                 for (const auto &prefixItem: prefixIndex) {
195                         if (prefixItem.second > currentBest.second && match(item, prefixItem.first))
196                                 currentBest = prefixItem;
197                 }
198         }
199
200         // check no-name rules
201         updateCurrentBest("");
202
203         if (currentBest.first)
204                 decision = makeDecisionItem(currentBest.first->decision());
205
206         return decision;
207 }
208
209 ldp_xml_parser::DecisionItem StorageBackendSerialized::StorageBackendSerializedImpl::getDecisionFromSendIndex(const MatchItemSend &item) {
210         return getDecisionFromSendIndex(item, sendIndex, sendPrefixIndex);
211 }
212
213 ldp_xml_parser::DecisionItem StorageBackendSerialized::StorageBackendSerializedImpl::getDecisionFromSendIndex(const MatchItemSend &item, uid_t uid) {
214         return getDecisionFromSendIndex(item, userSendIndex[uid], userSendPrefixIndex[uid]);
215 }
216
217 void StorageBackendSerialized::StorageBackendSerializedImpl::createSendIndex() {
218         // helper for adding items to indexes
219         auto add = [](MapIndex &index, SendPrefixIndex &prefixIndex, boost::string_ref key, ItemWithScore value) {
220                 if (value.first->is_name_prefix()) {
221                         prefixIndex.push_back(value);
222                 } else {
223                         index[key].second.push_back(value);
224                         // we insert items in increasing score order, so we just update the highest score on each add
225                         index[key].first = value.second;
226                 }
227         };
228
229         // context default index
230         const auto *policy = file->m_send_set()->context_default();
231         unsigned cnt = 1;
232
233         const auto *v = policy->items();
234         for (auto it = v->begin(); it != v->end(); ++it)
235                 add(sendIndex, sendPrefixIndex,
236                                 boost::string_ref(it->name()->c_str(), it->name()->size()),
237                                 ItemWithScore(*it, cnt++));
238
239         // users index
240         for (const auto &uidPolicy: *file->m_send_set()->user()) {
241                 cnt = 1;
242                 for (const auto it: *uidPolicy->policy()->items()) {
243                         add(userSendIndex[uidPolicy->id()],
244                                         userSendPrefixIndex[uidPolicy->id()],
245                                         boost::string_ref(it->name()->c_str(), it->name()->size()),
246                                         ItemWithScore(it, cnt++));
247                 }
248         }
249 }
250
251 void StorageBackendSerialized::StorageBackendSerializedImpl::releaseMMap() {
252         assert(MAP_FAILED != mem);
253         assert(0 != length);
254
255         if (munmap(mem, length) != 0)
256                 tslog::log_error("munmap(): ", tslog::print_errno(errno), "\n");
257
258         mem = static_cast<decltype(mem)>(MAP_FAILED);
259         length = 0;
260 }
261
262 void StorageBackendSerialized::StorageBackendSerializedImpl::releaseFD() {
263         assert(-1 != fd);
264
265         if (close(fd) != 0)
266                 tslog::log_error("close(): ", tslog::print_errno(errno), "\n");
267
268         fd = -1;
269 }
270
271 void StorageBackendSerialized::StorageBackendSerializedImpl::release() {
272         if (-1 != fd) {         // we need to check it, because we may have initialized the storage directly from File *
273                 releaseMMap();
274                 releaseFD();
275         }
276
277         if (nullptr != serializer.get()) {
278                 serializer.reset(nullptr);
279         }
280
281         file = nullptr;
282 }
283
284 bool StorageBackendSerialized::StorageBackendSerializedImpl::init(const char *filename, bool verify) {
285         assert(nullptr == file);
286
287         auto err = [filename] (const char *what) {
288                 tslog::log_error("Can't ", what, " ", filename, ": ", tslog::print_errno(errno), "\n");
289                 return false;
290         };
291
292         fd = open(filename, O_RDONLY);
293         if (-1 == fd)
294                 return err("open");
295
296         auto openGuard = transaction_guard::makeGuard([&] () { releaseFD(); });
297
298         struct stat buf;
299
300         if (fstat(fd, &buf) == -1)
301                 return err("stat");
302
303         length = buf.st_size;
304         if (length > MAX_SFILE_SIZE) {
305                 tslog::log_error("Serialized file size(", length, ") is too large. (>", MAX_SFILE_SIZE, ") bytes.\n");
306                 return false;
307         }
308
309         mem = reinterpret_cast<uint8_t*>(mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0));
310         if (MAP_FAILED == mem)
311                 return err("mmap");
312
313         auto mmapGuard = transaction_guard::makeGuard([&] () { releaseMMap(); });
314
315         if (verify) {
316                 auto verifier = flatbuffers::Verifier(mem, length);
317                 if (!FB::VerifyFileBuffer(verifier) || !FB::FileBufferHasIdentifier(mem)) {
318                         tslog::log_error("verification of serialized data: failed\n");
319                         return false;
320                 }
321         }
322
323         openGuard.dismiss();
324         mmapGuard.dismiss();
325
326         file = GetFile(mem);
327         bool res = file != nullptr;
328         if (res)
329                 createSendIndex();
330         return res;
331 }
332
333 bool StorageBackendSerialized::StorageBackendSerializedImpl::init(const FB::File *f) {
334         assert(nullptr == file);
335
336         file = f;
337         createSendIndex();
338         return true;
339 }
340
341 bool StorageBackendSerialized::StorageBackendSerializedImpl::initFromXML(const char *config_name) {
342         assert(nullptr == file);
343         assert(nullptr == serializer.get());
344
345         serializer.reset(new ldp_xml_parser::Serializer());
346
347         size_t serialized_size;
348         uint8_t *data = serializer->serialize(config_name, serialized_size);
349         if (nullptr == data)
350                 return false;
351
352         return init(FB::GetFile(data));
353 }
354
355 void StorageBackendSerialized::StorageBackendSerializedImpl::printContent(const bool xml_format) const {
356         print_content::use_xml_format(xml_format);
357         std::cerr << *file;
358 }
359
360 /*************************************************/
361
362 #define TYPE_HELPER(T, t) \
363         template <> \
364         struct type_helper<ldp_xml_parser::MatchItem##T> { \
365                 typedef FB::T##Set policy_set_type; \
366                 typedef FB::Policy##T policy_type; \
367         }; \
368         template <> \
369         auto StorageBackendSerialized::StorageBackendSerializedImpl::getPolicySet<ldp_xml_parser::MatchItem##T>() \
370                 -> const typename type_helper<ldp_xml_parser::MatchItem##T>::policy_set_type * { \
371                 assert(file); \
372                 return file->m_##t##_set(); \
373         }
374
375 TYPE_HELPER(Own, own)
376 TYPE_HELPER(Send, send)
377 TYPE_HELPER(Receive, receive)
378 TYPE_HELPER(Access, access)
379
380 bool StorageBackendSerialized::init(const char *filename, bool verify) {
381         return pimpl->init(filename, verify);
382 }
383
384 bool StorageBackendSerialized::init(const FB::File *f) {
385         return pimpl->init(f);
386 }
387
388 bool StorageBackendSerialized::initFromXML(const char *config_name) {
389         return pimpl->initFromXML(config_name);
390 }
391
392 void StorageBackendSerialized::release() {
393         pimpl->release();
394 }
395
396 void StorageBackendSerialized::printContent(const bool xml_format) const {
397         pimpl->printContent(xml_format);
398 }
399
400 template <typename T>
401 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const T &item) const {
402         return getDecisionItem(item, pimpl->getPolicySet<T>()->context_mandatory());
403 }
404
405 template <>
406 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const MatchItemSend &item) const {
407         return pimpl->getDecisionFromSendIndex(item);
408 }
409
410 template <>
411 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemUser(uid_t uid, const MatchItemSend &item) const {
412         auto *policyPair = pimpl->getPolicySet<MatchItemSend>()->user()->LookupByKey(uid);
413         if (nullptr == policyPair)
414                 return ldp_xml_parser::Decision::ANY;
415         return pimpl->getDecisionFromSendIndex(item, uid);
416 }
417
418 template <typename T>
419 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const T &item) const {
420         return getDecisionItem(item, pimpl->getPolicySet<T>()->context_default());
421 }
422
423 template <typename T>
424 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemUser(uid_t uid, const T &item) const {
425         return getDecisionItemMaybeNull(item, pimpl->getPolicySet<T>()->user()->LookupByKey(uid));
426 }
427
428 template <typename T>
429 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemGroup(gid_t gid, const T &item) const {
430         return getDecisionItemMaybeNull(item, pimpl->getPolicySet<T>()->group()->LookupByKey(gid));
431 }
432
433 template <typename T>
434 bool StorageBackendSerialized::existsPolicyForGroup(gid_t gid) const {
435         return pimpl->getPolicySet<T>()->group()->LookupByKey(gid) != nullptr;
436 }
437
438 #define T_INSTANTIATION(T) \
439         template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const ldp_xml_parser::MatchItem##T &item) const; \
440         template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const ldp_xml_parser::MatchItem##T &item) const;
441
442 T_INSTANTIATION(Own)
443 T_INSTANTIATION(Send)
444 T_INSTANTIATION(Receive)
445 T_INSTANTIATION(Access)
446
447 #undef T_INSTANTIATION
448
449 #define T_INSTANTIATION2(T) \
450         template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemUser(uid_t uid, const ldp_xml_parser::MatchItem##T &item) const; \
451         template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemGroup(gid_t gid, const ldp_xml_parser::MatchItem##T &item) const; \
452         template bool StorageBackendSerialized::existsPolicyForGroup<ldp_xml_parser::MatchItem##T>(gid_t) const;
453
454 T_INSTANTIATION2(Own)
455 T_INSTANTIATION2(Send)
456 T_INSTANTIATION2(Receive)
457
458 #undef T_INSTANTIATION2
459
460 StorageBackendSerialized::StorageBackendSerialized()
461         : pimpl{new StorageBackendSerializedImpl} {
462 }
463
464 StorageBackendSerialized::~StorageBackendSerialized() {
465         pimpl->release();
466 }
467
468 }