Merge "printer: print FileIdentifier" 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 <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_xml_parser::Serializer> serializer;
133
134         void releaseMMap();
135         void releaseFD();
136
137 public:
138         bool init(const char *filename, bool verify);
139         bool init(const FB::File *f);
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         if (verify) {
284                 auto verifier = flatbuffers::Verifier(mem, length);
285                 if (!FB::VerifyFileBuffer(verifier) || !FB::FileBufferHasIdentifier(mem)) {
286                         char fid[FB::FB_ID_SIZE + 1] = {0, };
287                         strncpy(fid, (const char *)(mem + FB::FB_ID_OFFSET), FB::FB_ID_SIZE);
288
289                         if (strcmp(fid, "LDP1") == 0) {
290                                 tslog::log_error("verification of serialized data: not available\n");
291                                 tslog::log_error("header ID          : ", FB::FileIdentifier(), "\n");
292                                 tslog::log_error("serialized data ID : ", fid, "\n");
293                         } else {
294                                 tslog::log_error("verification of serialized data: failed\n");
295                                 return false;
296                         }
297                 }
298         }
299
300         openGuard.dismiss();
301         mmapGuard.dismiss();
302
303         file = GetFile(mem);
304         return file != nullptr;
305 }
306
307 bool StorageBackendSerialized::StorageBackendSerializedImpl::init(const FB::File *f) {
308         assert(nullptr == file);
309
310         file = f;
311         return true;
312 }
313
314 bool StorageBackendSerialized::StorageBackendSerializedImpl::initFromXML(const char *config_name) {
315         assert(nullptr == file);
316         assert(nullptr == serializer.get());
317
318         serializer.reset(new ldp_xml_parser::Serializer());
319
320         size_t serialized_size;
321         uint8_t *data = serializer->serialize(config_name, serialized_size);
322         if (nullptr == data)
323                 return false;
324
325         return init(FB::GetFile(data));
326 }
327
328 void StorageBackendSerialized::StorageBackendSerializedImpl::printContent(const bool xml_format) const {
329         print_content::use_xml_format(xml_format);
330         std::cerr << *file;
331 }
332
333 /*************************************************/
334
335 #define TYPE_HELPER(T, t) \
336         template <> \
337         struct type_helper<ldp_xml_parser::MatchItem##T> { \
338                 typedef FB::T##Set policy_set_type; \
339                 typedef FB::Policy##T policy_type; \
340         }; \
341         template <> \
342         auto StorageBackendSerialized::StorageBackendSerializedImpl::getPolicySet<ldp_xml_parser::MatchItem##T>() \
343                 -> const typename type_helper<ldp_xml_parser::MatchItem##T>::policy_set_type * { \
344                 assert(file); \
345                 return file->m_##t##_set(); \
346         }
347
348 TYPE_HELPER(Own, own)
349 TYPE_HELPER(Send, send)
350 TYPE_HELPER(Receive, receive)
351 TYPE_HELPER(Access, access)
352
353 bool StorageBackendSerialized::init(const char *filename, bool verify) {
354         return pimpl->init(filename, verify);
355 }
356
357 bool StorageBackendSerialized::init(const FB::File *f) {
358         return pimpl->init(f);
359 }
360
361 bool StorageBackendSerialized::initFromXML(const char *config_name) {
362         return pimpl->initFromXML(config_name);
363 }
364
365 void StorageBackendSerialized::release() {
366         pimpl->release();
367 }
368
369 void StorageBackendSerialized::printContent(const bool xml_format) const {
370         pimpl->printContent(xml_format);
371 }
372
373 template <typename T>
374 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const T &item) const {
375         return getDecisionItem(item, pimpl->getPolicySet<T>()->context_mandatory());
376 }
377
378 template <>
379 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const MatchItemSend &item) const {
380         return pimpl->getDecisionFromSendIndex(item, pimpl->getPolicySet<MatchItemSend>()->context_mandatory());
381 }
382
383 template <>
384 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const MatchItemSend &item) const {
385         return pimpl->getDecisionFromSendIndex(item, pimpl->getPolicySet<MatchItemSend>()->context_default());
386 }
387
388 template <>
389 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemUser(uid_t uid, const MatchItemSend &item) const {
390         auto *policyPair = pimpl->getPolicySet<MatchItemSend>()->user()->LookupByKey(uid);
391         if (nullptr == policyPair)
392                 return ldp_xml_parser::Decision::ANY;
393         return pimpl->getDecisionFromSendIndex(item, policyPair->policy());
394 }
395
396 template <typename T>
397 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const T &item) const {
398         return getDecisionItem(item, pimpl->getPolicySet<T>()->context_default());
399 }
400
401 template <typename T>
402 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemUser(uid_t uid, const T &item) const {
403         return getDecisionItemMaybeNull(item, pimpl->getPolicySet<T>()->user()->LookupByKey(uid));
404 }
405
406 template <typename T>
407 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemGroup(gid_t gid, const T &item) const {
408         return getDecisionItemMaybeNull(item, pimpl->getPolicySet<T>()->group()->LookupByKey(gid));
409 }
410
411 template <typename T>
412 bool StorageBackendSerialized::existsPolicyForGroup(gid_t gid) const {
413         return pimpl->getPolicySet<T>()->group()->LookupByKey(gid) != nullptr;
414 }
415
416 #define T_INSTANTIATION(T) \
417         template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const ldp_xml_parser::MatchItem##T &item) const; \
418         template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const ldp_xml_parser::MatchItem##T &item) const;
419
420 T_INSTANTIATION(Own)
421 T_INSTANTIATION(Send)
422 T_INSTANTIATION(Receive)
423 T_INSTANTIATION(Access)
424
425 #undef T_INSTANTIATION
426
427 #define T_INSTANTIATION2(T) \
428         template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemUser(uid_t uid, const ldp_xml_parser::MatchItem##T &item) const; \
429         template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemGroup(gid_t gid, const ldp_xml_parser::MatchItem##T &item) const; \
430         template bool StorageBackendSerialized::existsPolicyForGroup<ldp_xml_parser::MatchItem##T>(gid_t) const;
431
432 T_INSTANTIATION2(Own)
433 T_INSTANTIATION2(Send)
434 T_INSTANTIATION2(Receive)
435
436 #undef T_INSTANTIATION2
437
438 StorageBackendSerialized::StorageBackendSerialized()
439         : pimpl{new StorageBackendSerializedImpl} {
440 }
441
442 StorageBackendSerialized::~StorageBackendSerialized() {
443         pimpl->release();
444 }
445
446 }