*/
#include <cstring>
+#include <unordered_set>
#include "cynara.h"
#include <dpl/log/log.h>
CynaraAdminPolicy::CynaraAdminPolicy(const std::string &client, const std::string &user,
- const std::string &privilege, Operation operation,
+ const std::string &privilege, int operation,
const std::string &bucket)
{
this->client = strdup(client.c_str());
std::string("Error in CynaraAdminPolicy allocation."));
}
- this->result = static_cast<int>(operation);
+ this->result = operation;
this->result_extra = nullptr;
}
that.result_extra = nullptr;
}
+CynaraAdminPolicy& CynaraAdminPolicy::operator=(CynaraAdminPolicy &&that)
+{
+ if (this != &that) {
+ bucket = that.bucket;
+ client = that.client;
+ user = that.user;
+ privilege = that.privilege;
+ result_extra = that.result_extra;
+ result = that.result;
+
+ that.bucket = nullptr;
+ that.client = nullptr;
+ that.user = nullptr;
+ that.privilege = nullptr;
+ that.result_extra = nullptr;
+ };
+
+ return *this;
+}
+
CynaraAdminPolicy::~CynaraAdminPolicy()
{
free(this->bucket);
return true;
case CYNARA_API_ACCESS_DENIED:
return false;
+ case CYNARA_API_MAX_PENDING_REQUESTS:
+ ThrowMsg(CynaraException::MaxPendingRequests, msg);
case CYNARA_API_OUT_OF_MEMORY:
ThrowMsg(CynaraException::OutOfMemory, msg);
case CYNARA_API_INVALID_PARAM:
ThrowMsg(CynaraException::InvalidParam, msg);
case CYNARA_API_SERVICE_NOT_AVAILABLE:
ThrowMsg(CynaraException::ServiceNotAvailable, msg);
+ case CYNARA_API_METHOD_NOT_SUPPORTED:
+ ThrowMsg(CynaraException::MethodNotSupported, msg);
+ case CYNARA_API_OPERATION_NOT_ALLOWED:
+ ThrowMsg(CynaraException::OperationNotAllowed, msg);
+ case CYNARA_API_OPERATION_FAILED:
+ ThrowMsg(CynaraException::OperationFailed, msg);
+ case CYNARA_API_BUCKET_NOT_FOUND:
+ ThrowMsg(CynaraException::BucketNotFound, msg);
default:
ThrowMsg(CynaraException::UnknownError, msg);
}
}
+CynaraAdmin::TypeToDescriptionMap CynaraAdmin::TypeToDescription;
+CynaraAdmin::DescriptionToTypeMap CynaraAdmin::DescriptionToType;
+
CynaraAdmin::CynaraAdmin()
+ : m_policyDescriptionsInitialized(false)
{
checkCynaraError(
cynara_admin_initialize(&m_CynaraAdmin),
void CynaraAdmin::SetPolicies(const std::vector<CynaraAdminPolicy> &policies)
{
+ if (policies.empty()) {
+ LogDebug("no policies to set in Cynara.");
+ return;
+ }
+
std::vector<const struct cynara_admin_policy *> pp_policies(policies.size() + 1);
LogDebug("Sending " << policies.size() << " policies to Cynara");
void CynaraAdmin::UpdateAppPolicy(
const std::string &label,
const std::string &user,
- const std::vector<std::string> &oldPrivileges,
- const std::vector<std::string> &newPrivileges)
+ const std::vector<std::string> &privileges)
{
- std::vector<CynaraAdminPolicy> policies;
+ std::unordered_set<std::string> privilegesSet(privileges.begin(), privileges.end());
- // Perform sort-merge join on oldPrivileges and newPrivileges.
- // Assume that they are already sorted and without duplicates.
- auto oldIter = oldPrivileges.begin();
- auto newIter = newPrivileges.begin();
-
- while (oldIter != oldPrivileges.end() && newIter != newPrivileges.end()) {
- int compare = oldIter->compare(*newIter);
- if (compare == 0) {
- LogDebug("(user = " << user << " label = " << label << ") " <<
- "keeping privilege " << *newIter);
- ++oldIter;
- ++newIter;
- continue;
- } else if (compare < 0) {
+ std::vector<CynaraAdminPolicy> policies;
+ CynaraAdmin::getInstance().ListPolicies(
+ CynaraAdmin::Buckets.at(Bucket::MANIFESTS),
+ label, user, CYNARA_ADMIN_ANY, policies);
+
+ // Compare previous policies with set of new requested privileges
+ for (auto &policy : policies) {
+ if (privilegesSet.erase(policy.privilege)) {
+ // privilege was found and removed from the set, keeping policy
LogDebug("(user = " << user << " label = " << label << ") " <<
- "removing privilege " << *oldIter);
- policies.push_back(CynaraAdminPolicy(label, user, *oldIter,
- CynaraAdminPolicy::Operation::Delete,
- Buckets.at(Bucket::MANIFESTS)));
- ++oldIter;
+ "keeping privilege " << policy.privilege);
} else {
+ // privilege was not found in the set, deleting policy
+ policy.result = static_cast<int>(CynaraAdminPolicy::Operation::Delete);
LogDebug("(user = " << user << " label = " << label << ") " <<
- "adding privilege " << *newIter);
- policies.push_back(CynaraAdminPolicy(label, user, *newIter,
- CynaraAdminPolicy::Operation::Allow,
- Buckets.at(Bucket::MANIFESTS)));
- ++newIter;
+ "removing privilege " << policy.privilege);
}
}
- for (; oldIter != oldPrivileges.end(); ++oldIter) {
- LogDebug("(user = " << user << " label = " << label << ") " <<
- "removing privilege " << *oldIter);
- policies.push_back(CynaraAdminPolicy(label, user, *oldIter,
- CynaraAdminPolicy::Operation::Delete,
- Buckets.at(Bucket::MANIFESTS)));
- }
-
- for (; newIter != newPrivileges.end(); ++newIter) {
+ // Add policies for privileges that weren't previously enabled
+ // Those that were previously enabled are now removed from privilegesSet
+ for (const auto &privilege : privilegesSet) {
LogDebug("(user = " << user << " label = " << label << ") " <<
- "adding privilege " << *newIter);
- policies.push_back(CynaraAdminPolicy(label, user, *newIter,
- CynaraAdminPolicy::Operation::Allow,
+ "adding privilege " << privilege);
+ policies.push_back(CynaraAdminPolicy(label, user, privilege,
+ static_cast<int>(CynaraAdminPolicy::Operation::Allow),
Buckets.at(Bucket::MANIFESTS)));
}
CynaraAdmin::getInstance().SetPolicies(policies);
}
+void CynaraAdmin::ListUsers(std::vector<uid_t> &listOfUsers)
+{
+ std::vector<CynaraAdminPolicy> tmpListOfUsers;
+ CynaraAdmin::getInstance().ListPolicies(
+ CynaraAdmin::Buckets.at(Bucket::MAIN),
+ CYNARA_ADMIN_WILDCARD,
+ CYNARA_ADMIN_ANY,
+ CYNARA_ADMIN_WILDCARD,
+ tmpListOfUsers);
+
+ for (const auto &tmpUser : tmpListOfUsers) {
+ std::string user = tmpUser.user;
+ if (!user.compare(CYNARA_ADMIN_WILDCARD))
+ continue;
+ try {
+ listOfUsers.push_back(std::stoul(user));
+ } catch (std::invalid_argument &e) {
+ LogError("Invalid UID: " << e.what());
+ continue;
+ };
+ };
+ LogDebug("Found users: " << listOfUsers.size());
+};
+
+void CynaraAdmin::UserRemove(uid_t uid)
+{
+ std::vector<CynaraAdminPolicy> policies;
+ std::string user = std::to_string(static_cast<unsigned int>(uid));
+
+ EmptyBucket(Buckets.at(Bucket::PRIVACY_MANAGER),true,
+ CYNARA_ADMIN_ANY, user, CYNARA_ADMIN_ANY);
+}
+
+void CynaraAdmin::ListPolicies(
+ const std::string &bucketName,
+ const std::string &appId,
+ const std::string &user,
+ const std::string &privilege,
+ std::vector<CynaraAdminPolicy> &policies)
+{
+ struct cynara_admin_policy ** pp_policies = nullptr;
+
+ checkCynaraError(
+ cynara_admin_list_policies(m_CynaraAdmin, bucketName.c_str(), appId.c_str(),
+ user.c_str(), privilege.c_str(), &pp_policies),
+ "Error while getting list of policies for bucket: " + bucketName);
+
+ for (std::size_t i = 0; pp_policies[i] != nullptr; i++) {
+ policies.push_back(std::move(*static_cast<CynaraAdminPolicy*>(pp_policies[i])));
+
+ free(pp_policies[i]);
+ }
+
+ free(pp_policies);
+
+}
+
+void CynaraAdmin::EmptyBucket(const std::string &bucketName, bool recursive, const std::string &client,
+ const std::string &user, const std::string &privilege)
+{
+ checkCynaraError(
+ cynara_admin_erase(m_CynaraAdmin, bucketName.c_str(), static_cast<int>(recursive),
+ client.c_str(), user.c_str(), privilege.c_str()),
+ "Error while emptying bucket: " + bucketName + ", filter (C, U, P): " +
+ client + ", " + user + ", " + privilege);
+}
+
+void CynaraAdmin::FetchCynaraPolicyDescriptions(bool forceRefresh)
+{
+ struct cynara_admin_policy_descr **descriptions = nullptr;
+
+ if (!forceRefresh && m_policyDescriptionsInitialized)
+ return;
+
+ // fetch
+ checkCynaraError(
+ cynara_admin_list_policies_descriptions(m_CynaraAdmin, &descriptions),
+ "Error while getting list of policies descriptions from Cynara.");
+
+ if (descriptions[0] == nullptr) {
+ LogError("Fetching policies levels descriptions from Cynara returned empty list. "
+ "There should be at least 2 entries - Allow and Deny");
+ return;
+ }
+
+ // reset the state
+ m_policyDescriptionsInitialized = false;
+ DescriptionToType.clear();
+ TypeToDescription.clear();
+
+ // extract strings
+ for (int i = 0; descriptions[i] != nullptr; i++) {
+ std::string descriptionName(descriptions[i]->name);
+
+ DescriptionToType[descriptionName] = descriptions[i]->result;
+ TypeToDescription[descriptions[i]->result] = std::move(descriptionName);
+
+ free(descriptions[i]->name);
+ free(descriptions[i]);
+ }
+
+ free(descriptions);
+
+ m_policyDescriptionsInitialized = true;
+}
+
+void CynaraAdmin::ListPoliciesDescriptions(std::vector<std::string> &policiesDescriptions)
+{
+ FetchCynaraPolicyDescriptions(false);
+
+ for (const auto &it : TypeToDescription)
+ policiesDescriptions.push_back(it.second);
+}
+
+std::string CynaraAdmin::convertToPolicyDescription(const int policyType, bool forceRefresh)
+{
+ FetchCynaraPolicyDescriptions(forceRefresh);
+
+ return TypeToDescription.at(policyType);
+}
+
+int CynaraAdmin::convertToPolicyType(const std::string &policy, bool forceRefresh)
+{
+ FetchCynaraPolicyDescriptions(forceRefresh);
+
+ return DescriptionToType.at(policy);
+}
+void CynaraAdmin::Check(const std::string &label, const std::string &user, const std::string &privilege,
+ const std::string &bucket, int &result, std::string &resultExtra, const bool recursive)
+{
+ char *resultExtraCstr = nullptr;
+
+ checkCynaraError(
+ cynara_admin_check(m_CynaraAdmin, bucket.c_str(), recursive, label.c_str(),
+ user.c_str(), privilege.c_str(), &result, &resultExtraCstr),
+ "Error while asking cynara admin API for permission for app label: " + label + ", user: "
+ + user + " privilege: " + privilege + " bucket: " + bucket);
+
+ if (resultExtraCstr == nullptr)
+ resultExtra = "";
+ else {
+ resultExtra = std::string(resultExtraCstr);
+ free(resultExtraCstr);
+ }
+}
+
+int CynaraAdmin::GetPrivilegeManagerCurrLevel(const std::string &label, const std::string &user,
+ const std::string &privilege)
+{
+ int result;
+ std::string resultExtra;
+
+ Check(label, user, privilege, Buckets.at(Bucket::PRIVACY_MANAGER), result, resultExtra, true);
+
+ return result;
+}
+
+int CynaraAdmin::GetPrivilegeManagerMaxLevel(const std::string &label, const std::string &user,
+ const std::string &privilege)
+{
+ int result;
+ std::string resultExtra;
+
+ Check(label, user, privilege, Buckets.at(Bucket::MAIN), result, resultExtra, true);
+
+ return result;
+}
+
Cynara::Cynara()
{
+ int ret;
+
+ ret = eventfd(0, 0);
+ if (ret == -1) {
+ LogError("Error while creating eventfd: " << strerror(errno));
+ ThrowMsg(CynaraException::UnknownError, "Error while creating eventfd");
+ }
+
+ // Poll the eventfd for reading
+ pollFds[0].fd = ret;
+ pollFds[0].events = POLLIN;
+
+ // Temporary, will be replaced by cynara fd when available
+ pollFds[1].fd = pollFds[0].fd;
+ pollFds[1].events = 0;
+
checkCynaraError(
- cynara_initialize(&m_Cynara, nullptr),
+ cynara_async_initialize(&cynara, nullptr, &Cynara::statusCallback, &(pollFds[1])),
"Cannot connect to Cynara policy interface.");
+
+ thread = std::thread(&Cynara::run, this);
}
Cynara::~Cynara()
{
- cynara_finish(m_Cynara);
+ LogDebug("Sending terminate event to Cynara thread");
+ terminate.store(true);
+ threadNotifyPut();
+ thread.join();
+
+ // Critical section
+ std::lock_guard<std::mutex> guard(mutex);
+ cynara_async_finish(cynara);
}
Cynara &Cynara::getInstance()
return cynara;
}
+void Cynara::threadNotifyPut()
+{
+ int ret = eventfd_write(pollFds[0].fd, 1);
+ if (ret == -1)
+ LogError("Unexpected error while writing to eventfd: " << strerror(errno));
+}
+
+void Cynara::threadNotifyGet()
+{
+ eventfd_t value;
+ int ret = eventfd_read(pollFds[0].fd, &value);
+ if (ret == -1)
+ LogError("Unexpected error while reading from eventfd: " << strerror(errno));
+}
+
+void Cynara::statusCallback(int oldFd, int newFd, cynara_async_status status,
+ void *ptr)
+{
+ auto cynaraFd = static_cast<struct pollfd *>(ptr);
+
+ LogDebug("Cynara status callback. " <<
+ "Status = " << status << ", oldFd = " << oldFd << ", newFd = " << newFd);
+
+ if (newFd == -1) {
+ cynaraFd->events = 0;
+ } else {
+ cynaraFd->fd = newFd;
+
+ switch (status) {
+ case CYNARA_STATUS_FOR_READ:
+ cynaraFd->events = POLLIN;
+ break;
+
+ case CYNARA_STATUS_FOR_RW:
+ cynaraFd->events = POLLIN | POLLOUT;
+ break;
+ }
+ }
+}
+
+void Cynara::responseCallback(cynara_check_id checkId,
+ cynara_async_call_cause cause, int response, void *ptr)
+{
+ LogDebug("Response for received for Cynara check id: " << checkId);
+
+ auto promise = static_cast<std::promise<bool>*>(ptr);
+
+ switch (cause) {
+ case CYNARA_CALL_CAUSE_ANSWER:
+ LogDebug("Cynara cause: ANSWER: " << response);
+ promise->set_value(response);
+ break;
+
+ case CYNARA_CALL_CAUSE_CANCEL:
+ LogDebug("Cynara cause: CANCEL");
+ promise->set_value(CYNARA_API_ACCESS_DENIED);
+ break;
+
+ case CYNARA_CALL_CAUSE_FINISH:
+ LogDebug("Cynara cause: FINISH");
+ promise->set_value(CYNARA_API_ACCESS_DENIED);
+ break;
+
+ case CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE:
+ LogError("Cynara cause: SERVICE_NOT_AVAILABLE");
+
+ try {
+ ThrowMsg(CynaraException::ServiceNotAvailable,
+ "Cynara service not available");
+ } catch (...) {
+ promise->set_exception(std::current_exception());
+ }
+ break;
+ }
+}
+
+void Cynara::run()
+{
+ LogInfo("Cynara thread started");
+ while (true) {
+ int ret = poll(pollFds, 2, -1);
+ if (ret == -1) {
+ if (errno != EINTR)
+ LogError("Unexpected error returned by poll: " << strerror(errno));
+ continue;
+ }
+
+ // Check eventfd for termination signal
+ if (pollFds[0].revents) {
+ threadNotifyGet();
+ if (terminate.load()) {
+ LogInfo("Cynara thread terminated");
+ return;
+ }
+ }
+
+ // Check if Cynara fd is ready for processing
+ try {
+ if (pollFds[1].revents) {
+ // Critical section
+ std::lock_guard<std::mutex> guard(mutex);
+
+ checkCynaraError(cynara_async_process(cynara),
+ "Unexpected error returned by cynara_async_process");
+ }
+ } catch (const CynaraException::Base &e) {
+ LogError("Error while processing Cynara events: " << e.DumpToString());
+ }
+ }
+}
+
bool Cynara::check(const std::string &label, const std::string &privilege,
const std::string &user, const std::string &session)
{
- return checkCynaraError(
- cynara_check(m_Cynara,
- label.c_str(), session.c_str(), user.c_str(), privilege.c_str()),
- "Cannot check permission with Cynara.");
+ LogDebug("check: client = " << label << ", user = " << user <<
+ ", privilege = " << privilege << ", session = " << session);
+
+ std::promise<bool> promise;
+ auto future = promise.get_future();
+
+ // Critical section
+ {
+ std::lock_guard<std::mutex> guard(mutex);
+
+ int ret = cynara_async_check_cache(cynara,
+ label.c_str(), session.c_str(), user.c_str(), privilege.c_str());
+
+ if (ret != CYNARA_API_CACHE_MISS)
+ return checkCynaraError(ret, "Error while checking Cynara cache");
+
+ LogDebug("Cynara cache miss");
+
+ cynara_check_id check_id;
+ checkCynaraError(
+ cynara_async_create_request(cynara,
+ label.c_str(), session.c_str(), user.c_str(), privilege.c_str(),
+ &check_id, &Cynara::responseCallback, &promise),
+ "Cannot check permission with Cynara.");
+
+ threadNotifyPut();
+ LogDebug("Waiting for response to Cynara query id " << check_id);
+ }
+
+ return future.get();
}
} // namespace SecurityManager