2 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
4 * Contact: Rafal Krypa <r.krypa@samsung.com>
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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
20 * @author Rafal Krypa <r.krypa@samsung.com>
21 * @brief Wrapper class for Cynara interface
27 #include <dpl/log/log.h>
29 namespace SecurityManager {
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 * |------------------------|
37 * |------------------------|
39 * |------------------------|
40 * | app1 uid1 priv1 DENY |
41 * | * uid2 priv2 DENY |
42 * | * * * Bucket:MAIN|
43 * |------------------------|
45 * For details about buckets see Cynara documentation.
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
51 * - MAIN - holds rules denied by manufacturer, redirects to MANIFESTS
52 * bucket and holds entries for each user pointing to User Type
54 * - MANIFESTS - stores rules needed by installed apps (from package
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.
64 * Below is basic layout of buckets:
66 * |------------------------|
70 * | * * * Bucket:MAIN| |------------------|
71 * |------------------------| | <<deny>> |
73 * ----------------- | | |
74 * | | |------------------|
76 * |------------------------| |
79 * |---------------| | | |-------------------|
80 * | <<deny>> |<--| * * * Bucket:MANIFESTS|---->| <<deny>> |
81 * | USER_TYPE_SYST| |------------------------| | USER_TYPE_NORMAL |
83 * |---------------| | | |-------------------|
86 * | |---------------| |---------------| |
87 * | | <<deny>> | | <<deny>> | |
88 * | |USER_TYPE_GUEST| |USER_TYPE_ADMIN| |
90 * | |---------------| |---------------| |
95 * | |------------------| |
96 * |-------------> | <<none>> | <---------------|
99 * |------------------|
102 CynaraAdmin::BucketsMap CynaraAdmin::Buckets =
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")},
115 CynaraAdminPolicy::CynaraAdminPolicy(const std::string &client, const std::string &user,
116 const std::string &privilege, int operation,
117 const std::string &bucket)
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());
124 if (this->bucket == nullptr || this->client == nullptr ||
125 this->user == nullptr || this->privilege == nullptr) {
129 free(this->privilege);
130 ThrowMsg(CynaraException::OutOfMemory,
131 std::string("Error in CynaraAdminPolicy allocation."));
134 this->result = operation;
135 this->result_extra = nullptr;
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)
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;
149 if (this->bucket == nullptr || this->client == nullptr ||
150 this->user == nullptr || this->privilege == nullptr ||
151 this->result_extra == nullptr) {
155 free(this->privilege);
156 free(this->result_extra);
157 ThrowMsg(CynaraException::OutOfMemory,
158 std::string("Error in CynaraAdminPolicy allocation."));
162 CynaraAdminPolicy::CynaraAdminPolicy(CynaraAdminPolicy &&that)
164 bucket = that.bucket;
165 client = that.client;
167 privilege = that.privilege;
168 result_extra = that.result_extra;
169 result = that.result;
171 that.bucket = nullptr;
172 that.client = nullptr;
174 that.privilege = nullptr;
175 that.result_extra = nullptr;
178 CynaraAdminPolicy& CynaraAdminPolicy::operator=(CynaraAdminPolicy &&that)
181 bucket = that.bucket;
182 client = that.client;
184 privilege = that.privilege;
185 result_extra = that.result_extra;
186 result = that.result;
188 that.bucket = nullptr;
189 that.client = nullptr;
191 that.privilege = nullptr;
192 that.result_extra = nullptr;
198 CynaraAdminPolicy::~CynaraAdminPolicy()
203 free(this->privilege);
204 free(this->result_extra);
207 static bool checkCynaraError(int result, const std::string &msg)
210 case CYNARA_API_SUCCESS:
211 case CYNARA_API_ACCESS_ALLOWED:
213 case CYNARA_API_ACCESS_DENIED:
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);
232 ThrowMsg(CynaraException::UnknownError, msg);
236 CynaraAdmin::TypeToDescriptionMap CynaraAdmin::TypeToDescription;
237 CynaraAdmin::DescriptionToTypeMap CynaraAdmin::DescriptionToType;
239 CynaraAdmin::CynaraAdmin()
240 : m_policyDescriptionsInitialized(false)
243 cynara_admin_initialize(&m_CynaraAdmin),
244 "Cannot connect to Cynara administrative interface.");
247 CynaraAdmin::~CynaraAdmin()
249 cynara_admin_finish(m_CynaraAdmin);
252 CynaraAdmin &CynaraAdmin::getInstance()
254 static CynaraAdmin cynaraAdmin;
258 void CynaraAdmin::SetPolicies(const std::vector<CynaraAdminPolicy> &policies)
260 if (policies.empty()) {
261 LogDebug("no policies to set in Cynara.");
265 std::vector<const struct cynara_admin_policy *> pp_policies(policies.size() + 1);
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 << "}");
279 pp_policies[policies.size()] = nullptr;
282 cynara_admin_set_policies(m_CynaraAdmin, pp_policies.data()),
283 "Error while updating Cynara policy.");
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)
292 std::vector<CynaraAdminPolicy> policies;
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();
299 while (oldIter != oldPrivileges.end() && newIter != newPrivileges.end()) {
300 int compare = oldIter->compare(*newIter);
302 LogDebug("(user = " << user << " label = " << label << ") " <<
303 "keeping privilege " << *newIter);
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)));
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)));
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)));
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)));
340 SetPolicies(policies);
343 void CynaraAdmin::UserInit(uid_t uid, security_manager_user_type userType)
346 std::vector<CynaraAdminPolicy> policies;
349 case SM_USER_TYPE_SYSTEM:
350 bucket = Bucket::USER_TYPE_SYSTEM;
352 case SM_USER_TYPE_ADMIN:
353 bucket = Bucket::USER_TYPE_ADMIN;
355 case SM_USER_TYPE_GUEST:
356 bucket = Bucket::USER_TYPE_GUEST;
358 case SM_USER_TYPE_NORMAL:
359 bucket = Bucket::USER_TYPE_NORMAL;
361 case SM_USER_TYPE_ANY:
362 case SM_USER_TYPE_NONE:
363 case SM_USER_TYPE_END:
365 ThrowMsg(CynaraException::InvalidParam, "User type incorrect");
368 policies.push_back(CynaraAdminPolicy(CYNARA_ADMIN_WILDCARD,
369 std::to_string(static_cast<unsigned int>(uid)),
370 CYNARA_ADMIN_WILDCARD,
372 Buckets.at(Bucket::MAIN)));
374 CynaraAdmin::getInstance().SetPolicies(policies);
377 void CynaraAdmin::ListUsers(std::vector<uid_t> &listOfUsers)
379 std::vector<CynaraAdminPolicy> tmpListOfUsers;
380 CynaraAdmin::getInstance().ListPolicies(
381 CynaraAdmin::Buckets.at(Bucket::MAIN),
382 CYNARA_ADMIN_WILDCARD,
384 CYNARA_ADMIN_WILDCARD,
387 for (const auto &tmpUser : tmpListOfUsers) {
388 std::string user = tmpUser.user;
389 if (!user.compare(CYNARA_ADMIN_WILDCARD))
392 listOfUsers.push_back(std::stoul(user));
393 } catch (std::invalid_argument &e) {
394 LogError("Invalid UID: " << e.what());
398 LogDebug("Found users: " << listOfUsers.size());
401 void CynaraAdmin::UserRemove(uid_t uid)
403 std::vector<CynaraAdminPolicy> policies;
404 std::string user = std::to_string(static_cast<unsigned int>(uid));
406 EmptyBucket(Buckets.at(Bucket::PRIVACY_MANAGER),true,
407 CYNARA_ADMIN_ANY, user, CYNARA_ADMIN_ANY);
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)
417 struct cynara_admin_policy ** pp_policies = nullptr;
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);
424 for (std::size_t i = 0; pp_policies[i] != nullptr; i++) {
425 policies.push_back(std::move(*static_cast<CynaraAdminPolicy*>(pp_policies[i])));
427 free(pp_policies[i]);
434 void CynaraAdmin::EmptyBucket(const std::string &bucketName, bool recursive, const std::string &client,
435 const std::string &user, const std::string &privilege)
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);
444 void CynaraAdmin::FetchCynaraPolicyDescriptions(bool forceRefresh)
446 struct cynara_admin_policy_descr **descriptions = nullptr;
448 if (!forceRefresh && m_policyDescriptionsInitialized)
453 cynara_admin_list_policies_descriptions(m_CynaraAdmin, &descriptions),
454 "Error while getting list of policies descriptions from Cynara.");
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");
463 m_policyDescriptionsInitialized = false;
464 DescriptionToType.clear();
465 TypeToDescription.clear();
468 for (int i = 0; descriptions[i] != nullptr; i++) {
469 std::string descriptionName(descriptions[i]->name);
471 DescriptionToType[descriptionName] = descriptions[i]->result;
472 TypeToDescription[descriptions[i]->result] = std::move(descriptionName);
474 free(descriptions[i]->name);
475 free(descriptions[i]);
480 m_policyDescriptionsInitialized = true;
483 void CynaraAdmin::ListPoliciesDescriptions(std::vector<std::string> &policiesDescriptions)
485 FetchCynaraPolicyDescriptions(false);
487 for (const auto &it : TypeToDescription)
488 policiesDescriptions.push_back(it.second);
491 std::string CynaraAdmin::convertToPolicyDescription(const int policyType, bool forceRefresh)
493 FetchCynaraPolicyDescriptions(forceRefresh);
495 return TypeToDescription.at(policyType);
498 int CynaraAdmin::convertToPolicyType(const std::string &policy, bool forceRefresh)
500 FetchCynaraPolicyDescriptions(forceRefresh);
502 return DescriptionToType.at(policy);
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)
507 char *resultExtraCstr = nullptr;
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);
515 if (resultExtraCstr == nullptr)
518 resultExtra = std::string(resultExtraCstr);
519 free(resultExtraCstr);
523 int CynaraAdmin::GetPrivilegeManagerCurrLevel(const std::string &label, const std::string &user,
524 const std::string &privilege)
527 std::string resultExtra;
529 Check(label, user, privilege, Buckets.at(Bucket::PRIVACY_MANAGER), result, resultExtra, true);
534 int CynaraAdmin::GetPrivilegeManagerMaxLevel(const std::string &label, const std::string &user,
535 const std::string &privilege)
538 std::string resultExtra;
540 Check(label, user, privilege, Buckets.at(Bucket::MAIN), result, resultExtra, true);
551 LogError("Error while creating eventfd: " << strerror(errno));
552 ThrowMsg(CynaraException::UnknownError, "Error while creating eventfd");
555 // Poll the eventfd for reading
557 pollFds[0].events = POLLIN;
559 // Temporary, will be replaced by cynara fd when available
560 pollFds[1].fd = pollFds[0].fd;
561 pollFds[1].events = 0;
564 cynara_async_initialize(&cynara, nullptr, &Cynara::statusCallback, &(pollFds[1])),
565 "Cannot connect to Cynara policy interface.");
567 thread = std::thread(&Cynara::run, this);
572 LogDebug("Sending terminate event to Cynara thread");
573 terminate.store(true);
578 std::lock_guard<std::mutex> guard(mutex);
579 cynara_async_finish(cynara);
582 Cynara &Cynara::getInstance()
584 static Cynara cynara;
588 void Cynara::threadNotifyPut()
590 int ret = eventfd_write(pollFds[0].fd, 1);
592 LogError("Unexpected error while writing to eventfd: " << strerror(errno));
595 void Cynara::threadNotifyGet()
598 int ret = eventfd_read(pollFds[0].fd, &value);
600 LogError("Unexpected error while reading from eventfd: " << strerror(errno));
603 void Cynara::statusCallback(int oldFd, int newFd, cynara_async_status status,
606 auto cynaraFd = static_cast<struct pollfd *>(ptr);
608 LogDebug("Cynara status callback. " <<
609 "Status = " << status << ", oldFd = " << oldFd << ", newFd = " << newFd);
612 cynaraFd->events = 0;
614 cynaraFd->fd = newFd;
617 case CYNARA_STATUS_FOR_READ:
618 cynaraFd->events = POLLIN;
621 case CYNARA_STATUS_FOR_RW:
622 cynaraFd->events = POLLIN | POLLOUT;
628 void Cynara::responseCallback(cynara_check_id checkId,
629 cynara_async_call_cause cause, int response, void *ptr)
631 LogDebug("Response for received for Cynara check id: " << checkId);
633 auto promise = static_cast<std::promise<bool>*>(ptr);
636 case CYNARA_CALL_CAUSE_ANSWER:
637 LogDebug("Cynara cause: ANSWER: " << response);
638 promise->set_value(response);
641 case CYNARA_CALL_CAUSE_CANCEL:
642 LogDebug("Cynara cause: CANCEL");
643 promise->set_value(CYNARA_API_ACCESS_DENIED);
646 case CYNARA_CALL_CAUSE_FINISH:
647 LogDebug("Cynara cause: FINISH");
648 promise->set_value(CYNARA_API_ACCESS_DENIED);
651 case CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE:
652 LogError("Cynara cause: SERVICE_NOT_AVAILABLE");
655 ThrowMsg(CynaraException::ServiceNotAvailable,
656 "Cynara service not available");
658 promise->set_exception(std::current_exception());
666 LogInfo("Cynara thread started");
668 int ret = poll(pollFds, 2, -1);
671 LogError("Unexpected error returned by poll: " << strerror(errno));
675 // Check eventfd for termination signal
676 if (pollFds[0].revents) {
678 if (terminate.load()) {
679 LogInfo("Cynara thread terminated");
684 // Check if Cynara fd is ready for processing
686 if (pollFds[1].revents) {
688 std::lock_guard<std::mutex> guard(mutex);
690 checkCynaraError(cynara_async_process(cynara),
691 "Unexpected error returned by cynara_async_process");
693 } catch (const CynaraException::Base &e) {
694 LogError("Error while processing Cynara events: " << e.DumpToString());
699 bool Cynara::check(const std::string &label, const std::string &privilege,
700 const std::string &user, const std::string &session)
702 LogDebug("check: client = " << label << ", user = " << user <<
703 ", privilege = " << privilege << ", session = " << session);
705 std::promise<bool> promise;
706 auto future = promise.get_future();
710 std::lock_guard<std::mutex> guard(mutex);
712 int ret = cynara_async_check_cache(cynara,
713 label.c_str(), session.c_str(), user.c_str(), privilege.c_str());
715 if (ret != CYNARA_API_CACHE_MISS)
716 return checkCynaraError(ret, "Error while checking Cynara cache");
718 LogDebug("Cynara cache miss");
720 cynara_check_id check_id;
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.");
728 LogDebug("Waiting for response to Cynara query id " << check_id);
734 } // namespace SecurityManager