30f0777c72e321f33138c806e6237d46b568a760
[platform/core/security/security-manager.git] / src / common / cynara.cpp
1 /*
2  *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Rafal Krypa <r.krypa@samsung.com>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License
17  */
18 /*
19  * @file        cynara.cpp
20  * @author      Rafal Krypa <r.krypa@samsung.com>
21  * @brief       Wrapper class for Cynara interface
22  */
23
24 #include <cstring>
25 #include "cynara.h"
26
27 #include <dpl/log/log.h>
28
29 namespace SecurityManager {
30
31 /**
32  * Rules for apps and users are organized into set of buckets stored in Cynara.
33  * Bucket is set of rules (app, uid, privilege) -> (DENY, ALLOW, BUCKET, ...).
34  *  |------------------------|
35  *  |      <<allow>>         |
36  *  |   PRIVACY_MANAGER      |
37  *  |------------------------|
38  *  |  A    U   P      policy|
39  *  |------------------------|
40  *  | app1 uid1 priv1  DENY  |
41  *  |  *   uid2 priv2  DENY  |
42  *  |  * * *      Bucket:MAIN|
43  *  |------------------------|
44  *
45  * For details about buckets see Cynara documentation.
46  *
47  * Security Manager currently defines 8 buckets:
48  * - PRIVACY_MANAGER - first bucket during search (which is actually default bucket
49  *   with empty string as id). If user specifies his preference then required rule
50  *   is created here.
51  * - MAIN            - holds rules denied by manufacturer, redirects to MANIFESTS
52  *   bucket and holds entries for each user pointing to User Type
53  *   specific buckets
54  * - MANIFESTS       - stores rules needed by installed apps (from package
55  *   manifest)
56  * - USER_TYPE_ADMIN
57  * - USER_TYPE_SYSTEM
58  * - USER_TYPE_NORMAL
59  * - USER_TYPE_GUEST - they store privileges from templates for apropriate
60  *   user type. ALLOW rules only.
61  * - ADMIN           - stores custom rules introduced by device administrator.
62  *   Ignored if no matching rule found.
63  *
64  * Below is basic layout of buckets:
65  *
66  *  |------------------------|
67  *  |      <<allow>>         |
68  *  |   PRIVACY_MANAGER      |
69  *  |                        |
70  *  |  * * *      Bucket:MAIN|                         |------------------|
71  *  |------------------------|                         |      <<deny>>    |
72  *             |                                    |->|     MANIFESTS    |
73  *             -----------------                    |  |                  |
74  *                             |                    |  |------------------|
75  *                             V                    |
76  *                     |------------------------|   |
77  *                     |       <<deny>>         |---|
78  *                     |         MAIN           |
79  * |---------------|   |                        |     |-------------------|
80  * |    <<deny>>   |<--| * * *  Bucket:MANIFESTS|---->|      <<deny>>     |
81  * | USER_TYPE_SYST|   |------------------------|     |  USER_TYPE_NORMAL |
82  * |               |        |              |          |                   |
83  * |---------------|        |              |          |-------------------|
84  *        |                 |              |                    |
85  *        |                 V              V                    |
86  *        |      |---------------|      |---------------|       |
87  *        |      |    <<deny>>   |      |    <<deny>>   |       |
88  *        |      |USER_TYPE_GUEST|      |USER_TYPE_ADMIN|       |
89  *        |      |               |      |               |       |
90  *        |      |---------------|      |---------------|       |
91  *        |              |                      |               |
92  *        |              |----             -----|               |
93  *        |                  |             |                    |
94  *        |                  V             V                    |
95  *        |                |------------------|                 |
96  *        |------------->  |     <<none>>     | <---------------|
97  *                         |       ADMIN      |
98  *                         |                  |
99  *                         |------------------|
100  *
101  */
102 CynaraAdmin::BucketsMap CynaraAdmin::Buckets =
103 {
104     { Bucket::PRIVACY_MANAGER, std::string(CYNARA_ADMIN_DEFAULT_BUCKET)},
105     { Bucket::MAIN, std::string("MAIN")},
106     { Bucket::USER_TYPE_ADMIN, std::string("USER_TYPE_ADMIN")},
107     { Bucket::USER_TYPE_NORMAL, std::string("USER_TYPE_NORMAL")},
108     { Bucket::USER_TYPE_GUEST, std::string("USER_TYPE_GUEST") },
109     { Bucket::USER_TYPE_SYSTEM, std::string("USER_TYPE_SYSTEM")},
110     { Bucket::ADMIN, std::string("ADMIN")},
111     { Bucket::MANIFESTS, std::string("MANIFESTS")},
112 };
113
114
115 CynaraAdminPolicy::CynaraAdminPolicy(const std::string &client, const std::string &user,
116         const std::string &privilege, int operation,
117         const std::string &bucket)
118 {
119     this->client = strdup(client.c_str());
120     this->user = strdup(user.c_str());
121     this->privilege = strdup(privilege.c_str());
122     this->bucket = strdup(bucket.c_str());
123
124     if (this->bucket == nullptr || this->client == nullptr ||
125         this->user == nullptr || this->privilege == nullptr) {
126         free(this->bucket);
127         free(this->client);
128         free(this->user);
129         free(this->privilege);
130         ThrowMsg(CynaraException::OutOfMemory,
131                 std::string("Error in CynaraAdminPolicy allocation."));
132     }
133
134     this->result = operation;
135     this->result_extra = nullptr;
136 }
137
138 CynaraAdminPolicy::CynaraAdminPolicy(const std::string &client, const std::string &user,
139     const std::string &privilege, const std::string &goToBucket,
140     const std::string &bucket)
141 {
142     this->bucket = strdup(bucket.c_str());
143     this->client = strdup(client.c_str());
144     this->user = strdup(user.c_str());
145     this->privilege = strdup(privilege.c_str());
146     this->result_extra = strdup(goToBucket.c_str());
147     this->result = CYNARA_ADMIN_BUCKET;
148
149     if (this->bucket == nullptr || this->client == nullptr ||
150         this->user == nullptr || this->privilege == nullptr ||
151         this->result_extra == nullptr) {
152         free(this->bucket);
153         free(this->client);
154         free(this->user);
155         free(this->privilege);
156         free(this->result_extra);
157         ThrowMsg(CynaraException::OutOfMemory,
158                 std::string("Error in CynaraAdminPolicy allocation."));
159     }
160 }
161
162 CynaraAdminPolicy::CynaraAdminPolicy(CynaraAdminPolicy &&that)
163 {
164     bucket = that.bucket;
165     client = that.client;
166     user = that.user;
167     privilege = that.privilege;
168     result_extra = that.result_extra;
169     result = that.result;
170
171     that.bucket = nullptr;
172     that.client = nullptr;
173     that.user = nullptr;
174     that.privilege = nullptr;
175     that.result_extra = nullptr;
176 }
177
178 CynaraAdminPolicy& CynaraAdminPolicy::operator=(CynaraAdminPolicy &&that)
179 {
180     if (this != &that) {
181         bucket = that.bucket;
182         client = that.client;
183         user = that.user;
184         privilege = that.privilege;
185         result_extra = that.result_extra;
186         result = that.result;
187
188         that.bucket = nullptr;
189         that.client = nullptr;
190         that.user = nullptr;
191         that.privilege = nullptr;
192         that.result_extra = nullptr;
193     };
194
195     return *this;
196 }
197
198 CynaraAdminPolicy::~CynaraAdminPolicy()
199 {
200     free(this->bucket);
201     free(this->client);
202     free(this->user);
203     free(this->privilege);
204     free(this->result_extra);
205 }
206
207 static bool checkCynaraError(int result, const std::string &msg)
208 {
209     switch (result) {
210         case CYNARA_API_SUCCESS:
211         case CYNARA_API_ACCESS_ALLOWED:
212             return true;
213         case CYNARA_API_ACCESS_DENIED:
214             return false;
215         case CYNARA_API_MAX_PENDING_REQUESTS:
216             ThrowMsg(CynaraException::MaxPendingRequests, msg);
217         case CYNARA_API_OUT_OF_MEMORY:
218             ThrowMsg(CynaraException::OutOfMemory, msg);
219         case CYNARA_API_INVALID_PARAM:
220             ThrowMsg(CynaraException::InvalidParam, msg);
221         case CYNARA_API_SERVICE_NOT_AVAILABLE:
222             ThrowMsg(CynaraException::ServiceNotAvailable, msg);
223         case CYNARA_API_METHOD_NOT_SUPPORTED:
224             ThrowMsg(CynaraException::MethodNotSupported, msg);
225         case CYNARA_API_OPERATION_NOT_ALLOWED:
226             ThrowMsg(CynaraException::OperationNotAllowed, msg);
227         case CYNARA_API_OPERATION_FAILED:
228             ThrowMsg(CynaraException::OperationFailed, msg);
229         case CYNARA_API_BUCKET_NOT_FOUND:
230             ThrowMsg(CynaraException::BucketNotFound, msg);
231         default:
232             ThrowMsg(CynaraException::UnknownError, msg);
233     }
234 }
235
236 CynaraAdmin::TypeToDescriptionMap CynaraAdmin::TypeToDescription;
237 CynaraAdmin::DescriptionToTypeMap CynaraAdmin::DescriptionToType;
238
239 CynaraAdmin::CynaraAdmin()
240     : m_policyDescriptionsInitialized(false)
241 {
242     checkCynaraError(
243         cynara_admin_initialize(&m_CynaraAdmin),
244         "Cannot connect to Cynara administrative interface.");
245 }
246
247 CynaraAdmin::~CynaraAdmin()
248 {
249     cynara_admin_finish(m_CynaraAdmin);
250 }
251
252 CynaraAdmin &CynaraAdmin::getInstance()
253 {
254     static CynaraAdmin cynaraAdmin;
255     return cynaraAdmin;
256 }
257
258 void CynaraAdmin::SetPolicies(const std::vector<CynaraAdminPolicy> &policies)
259 {
260     if (policies.empty()) {
261         LogDebug("no policies to set in Cynara.");
262         return;
263     }
264
265     std::vector<const struct cynara_admin_policy *> pp_policies(policies.size() + 1);
266
267     LogDebug("Sending " << policies.size() << " policies to Cynara");
268     for (std::size_t i = 0; i < policies.size(); ++i) {
269         pp_policies[i] = static_cast<const struct cynara_admin_policy *>(&policies[i]);
270         LogDebug("policies[" << i << "] = {" <<
271             ".bucket = " << pp_policies[i]->bucket << ", " <<
272             ".client = " << pp_policies[i]->client << ", " <<
273             ".user = " << pp_policies[i]->user << ", " <<
274             ".privilege = " << pp_policies[i]->privilege << ", " <<
275             ".result = " << pp_policies[i]->result << ", " <<
276             ".result_extra = " << pp_policies[i]->result_extra << "}");
277     }
278
279     pp_policies[policies.size()] = nullptr;
280
281     checkCynaraError(
282         cynara_admin_set_policies(m_CynaraAdmin, pp_policies.data()),
283         "Error while updating Cynara policy.");
284 }
285
286 void CynaraAdmin::UpdateAppPolicy(
287     const std::string &label,
288     const std::string &user,
289     const std::vector<std::string> &oldPrivileges,
290     const std::vector<std::string> &newPrivileges)
291 {
292     std::vector<CynaraAdminPolicy> policies;
293
294     // Perform sort-merge join on oldPrivileges and newPrivileges.
295     // Assume that they are already sorted and without duplicates.
296     auto oldIter = oldPrivileges.begin();
297     auto newIter = newPrivileges.begin();
298
299     while (oldIter != oldPrivileges.end() && newIter != newPrivileges.end()) {
300         int compare = oldIter->compare(*newIter);
301         if (compare == 0) {
302             LogDebug("(user = " << user << " label = " << label << ") " <<
303                 "keeping privilege " << *newIter);
304             ++oldIter;
305             ++newIter;
306             continue;
307         } else if (compare < 0) {
308             LogDebug("(user = " << user << " label = " << label << ") " <<
309                 "removing privilege " << *oldIter);
310             policies.push_back(CynaraAdminPolicy(label, user, *oldIter,
311                     static_cast<int>(CynaraAdminPolicy::Operation::Delete),
312                     Buckets.at(Bucket::MANIFESTS)));
313             ++oldIter;
314         } else {
315             LogDebug("(user = " << user << " label = " << label << ") " <<
316                 "adding privilege " << *newIter);
317             policies.push_back(CynaraAdminPolicy(label, user, *newIter,
318                     static_cast<int>(CynaraAdminPolicy::Operation::Allow),
319                     Buckets.at(Bucket::MANIFESTS)));
320             ++newIter;
321         }
322     }
323
324     for (; oldIter != oldPrivileges.end(); ++oldIter) {
325         LogDebug("(user = " << user << " label = " << label << ") " <<
326             "removing privilege " << *oldIter);
327         policies.push_back(CynaraAdminPolicy(label, user, *oldIter,
328                     static_cast<int>(CynaraAdminPolicy::Operation::Delete),
329                     Buckets.at(Bucket::MANIFESTS)));
330     }
331
332     for (; newIter != newPrivileges.end(); ++newIter) {
333         LogDebug("(user = " << user << " label = " << label << ") " <<
334             "adding privilege " << *newIter);
335         policies.push_back(CynaraAdminPolicy(label, user, *newIter,
336                     static_cast<int>(CynaraAdminPolicy::Operation::Allow),
337                     Buckets.at(Bucket::MANIFESTS)));
338     }
339
340     SetPolicies(policies);
341 }
342
343 void CynaraAdmin::UserInit(uid_t uid, security_manager_user_type userType)
344 {
345     Bucket bucket;
346     std::vector<CynaraAdminPolicy> policies;
347
348     switch (userType) {
349         case SM_USER_TYPE_SYSTEM:
350             bucket = Bucket::USER_TYPE_SYSTEM;
351             break;
352         case SM_USER_TYPE_ADMIN:
353             bucket = Bucket::USER_TYPE_ADMIN;
354             break;
355         case SM_USER_TYPE_GUEST:
356             bucket = Bucket::USER_TYPE_GUEST;
357             break;
358         case SM_USER_TYPE_NORMAL:
359             bucket = Bucket::USER_TYPE_NORMAL;
360             break;
361         case SM_USER_TYPE_ANY:
362         case SM_USER_TYPE_NONE:
363         case SM_USER_TYPE_END:
364         default:
365             ThrowMsg(CynaraException::InvalidParam, "User type incorrect");
366     }
367
368     policies.push_back(CynaraAdminPolicy(CYNARA_ADMIN_WILDCARD,
369                                             std::to_string(static_cast<unsigned int>(uid)),
370                                             CYNARA_ADMIN_WILDCARD,
371                                             Buckets.at(bucket),
372                                             Buckets.at(Bucket::MAIN)));
373
374     CynaraAdmin::getInstance().SetPolicies(policies);
375 }
376
377 void CynaraAdmin::ListUsers(std::vector<uid_t> &listOfUsers)
378 {
379     std::vector<CynaraAdminPolicy> tmpListOfUsers;
380     CynaraAdmin::getInstance().ListPolicies(
381         CynaraAdmin::Buckets.at(Bucket::MAIN),
382         CYNARA_ADMIN_WILDCARD,
383         CYNARA_ADMIN_ANY,
384         CYNARA_ADMIN_WILDCARD,
385         tmpListOfUsers);
386
387     for (const auto &tmpUser : tmpListOfUsers) {
388         std::string user = tmpUser.user;
389         if (!user.compare(CYNARA_ADMIN_WILDCARD))
390             continue;
391         try {
392             listOfUsers.push_back(std::stoul(user));
393         } catch (std::invalid_argument &e) {
394             LogError("Invalid UID: " << e.what());
395             continue;
396         };
397     };
398     LogDebug("Found users: " << listOfUsers.size());
399 };
400
401 void CynaraAdmin::UserRemove(uid_t uid)
402 {
403     std::vector<CynaraAdminPolicy> policies;
404     std::string user = std::to_string(static_cast<unsigned int>(uid));
405
406     EmptyBucket(Buckets.at(Bucket::PRIVACY_MANAGER),true,
407             CYNARA_ADMIN_ANY, user, CYNARA_ADMIN_ANY);
408 }
409
410 void CynaraAdmin::ListPolicies(
411     const std::string &bucketName,
412     const std::string &appId,
413     const std::string &user,
414     const std::string &privilege,
415     std::vector<CynaraAdminPolicy> &policies)
416 {
417     struct cynara_admin_policy ** pp_policies = nullptr;
418
419     checkCynaraError(
420         cynara_admin_list_policies(m_CynaraAdmin, bucketName.c_str(), appId.c_str(),
421             user.c_str(), privilege.c_str(), &pp_policies),
422         "Error while getting list of policies for bucket: " + bucketName);
423
424     for (std::size_t i = 0; pp_policies[i] != nullptr; i++) {
425         policies.push_back(std::move(*static_cast<CynaraAdminPolicy*>(pp_policies[i])));
426
427         free(pp_policies[i]);
428     }
429
430     free(pp_policies);
431
432 }
433
434 void CynaraAdmin::EmptyBucket(const std::string &bucketName, bool recursive, const std::string &client,
435     const std::string &user, const std::string &privilege)
436 {
437     checkCynaraError(
438         cynara_admin_erase(m_CynaraAdmin, bucketName.c_str(), static_cast<int>(recursive),
439             client.c_str(), user.c_str(), privilege.c_str()),
440         "Error while emptying bucket: " + bucketName + ", filter (C, U, P): " +
441             client + ", " + user + ", " + privilege);
442 }
443
444 void CynaraAdmin::FetchCynaraPolicyDescriptions(bool forceRefresh)
445 {
446     struct cynara_admin_policy_descr **descriptions = nullptr;
447
448     if (!forceRefresh && m_policyDescriptionsInitialized)
449         return;
450
451     // fetch
452     checkCynaraError(
453         cynara_admin_list_policies_descriptions(m_CynaraAdmin, &descriptions),
454         "Error while getting list of policies descriptions from Cynara.");
455
456     if (descriptions[0] == nullptr) {
457         LogError("Fetching policies levels descriptions from Cynara returned empty list. "
458                 "There should be at least 2 entries - Allow and Deny");
459         return;
460     }
461
462     // reset the state
463     m_policyDescriptionsInitialized = false;
464     DescriptionToType.clear();
465     TypeToDescription.clear();
466
467     // extract strings
468     for (int i = 0; descriptions[i] != nullptr; i++) {
469         std::string descriptionName(descriptions[i]->name);
470
471         DescriptionToType[descriptionName] = descriptions[i]->result;
472         TypeToDescription[descriptions[i]->result] = std::move(descriptionName);
473
474         free(descriptions[i]->name);
475         free(descriptions[i]);
476     }
477
478     free(descriptions);
479
480     m_policyDescriptionsInitialized = true;
481 }
482
483 void CynaraAdmin::ListPoliciesDescriptions(std::vector<std::string> &policiesDescriptions)
484 {
485     FetchCynaraPolicyDescriptions(false);
486
487     for (const auto &it : TypeToDescription)
488         policiesDescriptions.push_back(it.second);
489 }
490
491 std::string CynaraAdmin::convertToPolicyDescription(const int policyType, bool forceRefresh)
492 {
493     FetchCynaraPolicyDescriptions(forceRefresh);
494
495     return TypeToDescription.at(policyType);
496 }
497
498 int CynaraAdmin::convertToPolicyType(const std::string &policy, bool forceRefresh)
499 {
500     FetchCynaraPolicyDescriptions(forceRefresh);
501
502     return DescriptionToType.at(policy);
503 }
504 void CynaraAdmin::Check(const std::string &label, const std::string &user, const std::string &privilege,
505     const std::string &bucket, int &result, std::string &resultExtra, const bool recursive)
506 {
507     char *resultExtraCstr = nullptr;
508
509     checkCynaraError(
510         cynara_admin_check(m_CynaraAdmin, bucket.c_str(), recursive, label.c_str(),
511             user.c_str(), privilege.c_str(), &result, &resultExtraCstr),
512         "Error while asking cynara admin API for permission for app label: " + label + ", user: "
513             + user + " privilege: " + privilege + " bucket: " + bucket);
514
515     if (resultExtraCstr == nullptr)
516         resultExtra = "";
517     else {
518         resultExtra = std::string(resultExtraCstr);
519         free(resultExtraCstr);
520     }
521 }
522
523 int CynaraAdmin::GetPrivilegeManagerCurrLevel(const std::string &label, const std::string &user,
524         const std::string &privilege)
525 {
526     int result;
527     std::string resultExtra;
528
529     Check(label, user, privilege, Buckets.at(Bucket::PRIVACY_MANAGER), result, resultExtra, true);
530
531     return result;
532 }
533
534 int CynaraAdmin::GetPrivilegeManagerMaxLevel(const std::string &label, const std::string &user,
535         const std::string &privilege)
536 {
537     int result;
538     std::string resultExtra;
539
540     Check(label, user, privilege, Buckets.at(Bucket::MAIN), result, resultExtra, true);
541
542     return result;
543 }
544
545 Cynara::Cynara()
546 {
547     int ret;
548
549     ret = eventfd(0, 0);
550     if (ret == -1) {
551         LogError("Error while creating eventfd: " << strerror(errno));
552         ThrowMsg(CynaraException::UnknownError, "Error while creating eventfd");
553     }
554
555     // Poll the eventfd for reading
556     pollFds[0].fd = ret;
557     pollFds[0].events = POLLIN;
558
559     // Temporary, will be replaced by cynara fd when available
560     pollFds[1].fd = pollFds[0].fd;
561     pollFds[1].events = 0;
562
563     checkCynaraError(
564         cynara_async_initialize(&cynara, nullptr, &Cynara::statusCallback, &(pollFds[1])),
565         "Cannot connect to Cynara policy interface.");
566
567     thread = std::thread(&Cynara::run, this);
568 }
569
570 Cynara::~Cynara()
571 {
572     LogDebug("Sending terminate event to Cynara thread");
573     terminate.store(true);
574     threadNotifyPut();
575     thread.join();
576
577     // Critical section
578     std::lock_guard<std::mutex> guard(mutex);
579     cynara_async_finish(cynara);
580 }
581
582 Cynara &Cynara::getInstance()
583 {
584     static Cynara cynara;
585     return cynara;
586 }
587
588 void Cynara::threadNotifyPut()
589 {
590     int ret = eventfd_write(pollFds[0].fd, 1);
591     if (ret == -1)
592         LogError("Unexpected error while writing to eventfd: " << strerror(errno));
593 }
594
595 void Cynara::threadNotifyGet()
596 {
597     eventfd_t value;
598     int ret = eventfd_read(pollFds[0].fd, &value);
599     if (ret == -1)
600         LogError("Unexpected error while reading from eventfd: " << strerror(errno));
601 }
602
603 void Cynara::statusCallback(int oldFd, int newFd, cynara_async_status status,
604     void *ptr)
605 {
606     auto cynaraFd = static_cast<struct pollfd *>(ptr);
607
608     LogDebug("Cynara status callback. " <<
609         "Status = " << status << ", oldFd = " << oldFd << ", newFd = " << newFd);
610
611     if (newFd == -1) {
612         cynaraFd->events = 0;
613     } else {
614         cynaraFd->fd = newFd;
615
616         switch (status) {
617         case CYNARA_STATUS_FOR_READ:
618             cynaraFd->events = POLLIN;
619             break;
620
621         case CYNARA_STATUS_FOR_RW:
622             cynaraFd->events = POLLIN | POLLOUT;
623             break;
624         }
625     }
626 }
627
628 void Cynara::responseCallback(cynara_check_id checkId,
629     cynara_async_call_cause cause, int response, void *ptr)
630 {
631     LogDebug("Response for received for Cynara check id: " << checkId);
632
633     auto promise = static_cast<std::promise<bool>*>(ptr);
634
635     switch (cause) {
636     case CYNARA_CALL_CAUSE_ANSWER:
637         LogDebug("Cynara cause: ANSWER: " << response);
638         promise->set_value(response);
639         break;
640
641     case CYNARA_CALL_CAUSE_CANCEL:
642         LogDebug("Cynara cause: CANCEL");
643         promise->set_value(CYNARA_API_ACCESS_DENIED);
644         break;
645
646     case CYNARA_CALL_CAUSE_FINISH:
647         LogDebug("Cynara cause: FINISH");
648         promise->set_value(CYNARA_API_ACCESS_DENIED);
649         break;
650
651     case CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE:
652         LogError("Cynara cause: SERVICE_NOT_AVAILABLE");
653
654         try {
655             ThrowMsg(CynaraException::ServiceNotAvailable,
656                 "Cynara service not available");
657         } catch (...) {
658             promise->set_exception(std::current_exception());
659         }
660         break;
661     }
662 }
663
664 void Cynara::run()
665 {
666     LogInfo("Cynara thread started");
667     while (true) {
668         int ret = poll(pollFds, 2, -1);
669         if (ret == -1) {
670             if (errno != EINTR)
671                 LogError("Unexpected error returned by poll: " << strerror(errno));
672             continue;
673         }
674
675         // Check eventfd for termination signal
676         if (pollFds[0].revents) {
677             threadNotifyGet();
678             if (terminate.load()) {
679                 LogInfo("Cynara thread terminated");
680                 return;
681             }
682         }
683
684         // Check if Cynara fd is ready for processing
685         try {
686             if (pollFds[1].revents) {
687                 // Critical section
688                 std::lock_guard<std::mutex> guard(mutex);
689
690                 checkCynaraError(cynara_async_process(cynara),
691                     "Unexpected error returned by cynara_async_process");
692             }
693         } catch (const CynaraException::Base &e) {
694             LogError("Error while processing Cynara events: " << e.DumpToString());
695         }
696     }
697 }
698
699 bool Cynara::check(const std::string &label, const std::string &privilege,
700         const std::string &user, const std::string &session)
701 {
702     LogDebug("check: client = " << label << ", user = " << user <<
703         ", privilege = " << privilege << ", session = " << session);
704
705     std::promise<bool> promise;
706     auto future = promise.get_future();
707
708     // Critical section
709     {
710         std::lock_guard<std::mutex> guard(mutex);
711
712         int ret = cynara_async_check_cache(cynara,
713             label.c_str(), session.c_str(), user.c_str(), privilege.c_str());
714
715         if (ret != CYNARA_API_CACHE_MISS)
716             return checkCynaraError(ret, "Error while checking Cynara cache");
717
718         LogDebug("Cynara cache miss");
719
720         cynara_check_id check_id;
721         checkCynaraError(
722             cynara_async_create_request(cynara,
723                 label.c_str(), session.c_str(), user.c_str(), privilege.c_str(),
724                 &check_id, &Cynara::responseCallback, &promise),
725             "Cannot check permission with Cynara.");
726
727         threadNotifyPut();
728         LogDebug("Waiting for response to Cynara query id " << check_id);
729     }
730
731     return future.get();
732 }
733
734 } // namespace SecurityManager