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