From 5aa65d374fe9f086ee68a189ef0dda1ae1d35574 Mon Sep 17 00:00:00 2001 From: Rafal Krypa Date: Fri, 29 Apr 2016 20:06:00 +0200 Subject: [PATCH] Change logic of security_manager_set_process_groups_from_appid The API function sets groups in application candidate process. The following changes are applied: - groups are based on privileges assigned to appId, not pkgId - don't consider privileges granted to other apps in the package - if the process was previously added to any group that is mapped to a privilege and app doesn't have access to that privilege, the group will be removed from the process - no group will be added to the process more than once Change-Id: Ifbb5fe48f2ad0bcc69ca00c13e6d7f2a20b148a2 Signed-off-by: Rafal Krypa --- src/client/client-security-manager.cpp | 198 ++++++++++++++++++++++----------- src/common/cynara.cpp | 15 +++ src/common/include/cynara.h | 11 ++ src/common/include/service_impl.h | 7 +- src/common/service_impl.cpp | 60 +++++----- src/server/service/service.cpp | 12 +- 6 files changed, 192 insertions(+), 111 deletions(-) diff --git a/src/client/client-security-manager.cpp b/src/client/client-security-manager.cpp index 28403c6..dd55443 100755 --- a/src/client/client-security-manager.cpp +++ b/src/client/client-security-manager.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -405,87 +406,154 @@ int security_manager_set_process_label_from_appid(const char *app_name) return SECURITY_MANAGER_SUCCESS; } -SECURITY_MANAGER_API -int security_manager_set_process_groups_from_appid(const char *app_name) +static int getProcessGroups(std::vector &groups) { - using namespace SecurityManager; - MessageBuffer send, recv; - int ret; + int ret = getgroups(0, nullptr); + if (ret == -1) { + LogError("Unable to get number of current supplementary groups: " << + GetErrnoString(errno)); + return SECURITY_MANAGER_ERROR_UNKNOWN; + } + int cnt = ret; - LogDebug("security_manager_set_process_groups_from_appid() called"); + std::unique_ptr groupsPtr(new gid_t[cnt]); + if (!groupsPtr) { + LogError("Memory allocation failed."); + return SECURITY_MANAGER_ERROR_MEMORY; + } - return try_catch([&]() -> int { - //checking parameters + ret = getgroups(cnt, groupsPtr.get()); + if (ret == -1) { + LogError("Unable to get list of current supplementary groups: " << + GetErrnoString(errno)); + return SECURITY_MANAGER_ERROR_UNKNOWN; + } - if (app_name == nullptr) { - LogError("app_name is NULL"); - return SECURITY_MANAGER_ERROR_INPUT_PARAM; - } + groups.assign(groupsPtr.get(), groupsPtr.get() + cnt); + return SECURITY_MANAGER_SUCCESS; +} - //put data into buffer - Serialization::Serialize(send, static_cast(SecurityModuleCall::APP_GET_GROUPS), - std::string(app_name)); +static int setProcessGroups(const std::vector &groups) +{ + int ret = setgroups(groups.size(), groups.data()); + if (ret == -1) { + LogError("Unable to set list of current supplementary groups: " << + GetErrnoString(errno)); + return SECURITY_MANAGER_ERROR_UNKNOWN; + } - //send buffer to server - int retval = sendToServer(SERVICE_SOCKET, send.Pop(), recv); - if (retval != SECURITY_MANAGER_SUCCESS) { - LogDebug("Error in sendToServer. Error code: " << retval); - return retval; - } + return SECURITY_MANAGER_SUCCESS; +} - //receive response from server - Deserialization::Deserialize(recv, retval); - if (retval != SECURITY_MANAGER_SUCCESS) { - LogError("Failed to get list of groups from security-manager service. Error code: " << retval); - return retval; +static int groupNamesToGids(const std::vector &groupNames, + std::vector &groups) +{ + groups.reserve(groupNames.size()); + for (const auto &groupName : groupNames) { + struct group *grp = getgrnam(groupName.c_str()); + if (grp == nullptr) { + LogError("No such group: " << groupName); + return SECURITY_MANAGER_ERROR_UNKNOWN; } + groups.push_back(grp->gr_gid); + } - //How many new groups? - int newGroupsCnt; - Deserialization::Deserialize(recv, newGroupsCnt); + return SECURITY_MANAGER_SUCCESS; +} - //And how many groups do we belong to already? - int oldGroupsCnt; - ret = getgroups(0, nullptr); - if (ret == -1) { - LogError("Unable to get list of current supplementary groups: " << - GetErrnoString(errno)); - return SECURITY_MANAGER_ERROR_UNKNOWN; - } - oldGroupsCnt = ret; +static int getPrivilegedGroups(std::vector &groups) +{ + MessageBuffer send, recv; + int ret; - //Allocate an array for both old and new groups gids - std::unique_ptr groups(new gid_t[oldGroupsCnt + newGroupsCnt]); - if (!groups.get()) { - LogError("Memory allocation failed."); - return SECURITY_MANAGER_ERROR_MEMORY; - } + Serialization::Serialize(send, static_cast(SecurityModuleCall::GROUPS_GET)); + ret = sendToServer(SERVICE_SOCKET, send.Pop(), recv); + if (ret != SECURITY_MANAGER_SUCCESS) { + LogDebug("Error in sendToServer. Error code: " << ret); + return ret; + } - //Get the old groups from process - ret = getgroups(oldGroupsCnt, groups.get()); - if (ret == -1) { - LogError("Unable to get list of current supplementary groups: " << - GetErrnoString(errno)); - return SECURITY_MANAGER_ERROR_UNKNOWN; - } + Deserialization::Deserialize(recv, ret); + if (ret != SECURITY_MANAGER_SUCCESS) { + LogError("Failed to get list of groups from security-manager service. " << + "Error code: " << ret); + return ret; + } - //Get the new groups from server response - for (int i = 0; i < newGroupsCnt; ++i) { - gid_t gid; - Deserialization::Deserialize(recv, gid); - groups.get()[oldGroupsCnt + i] = gid; - LogDebug("Adding process to group " << gid); - } + std::vector groupNames; + Deserialization::Deserialize(recv, groupNames); + return groupNamesToGids(groupNames, groups); +} - //Apply the modified groups list - ret = setgroups(oldGroupsCnt + newGroupsCnt, groups.get()); - if (ret == -1) { - LogError("Unable to get list of current supplementary groups: " << - GetErrnoString(errno)); - return SECURITY_MANAGER_ERROR_UNKNOWN; +static int getAppGroups(const std::string appName, std::vector &groups) +{ + MessageBuffer send, recv; + int ret; + + Serialization::Serialize(send, static_cast(SecurityModuleCall::APP_GET_GROUPS), appName); + ret = sendToServer(SERVICE_SOCKET, send.Pop(), recv); + if (ret != SECURITY_MANAGER_SUCCESS) { + LogDebug("Error in sendToServer. Error code: " << ret); + return ret; + } + + Deserialization::Deserialize(recv, ret); + if (ret != SECURITY_MANAGER_SUCCESS) { + LogError("Failed to get list of groups from security-manager service. " << + "Error code: " << ret); + return ret; + } + + std::vector groupNames; + Deserialization::Deserialize(recv, groupNames); + return groupNamesToGids(groupNames, groups); +} + +SECURITY_MANAGER_API +int security_manager_set_process_groups_from_appid(const char *app_name) +{ + using namespace SecurityManager; + int ret; + + LogDebug("security_manager_set_process_groups_from_appid() called"); + + return try_catch([&]() -> int { + //checking parameters + + if (app_name == nullptr) { + LogError("app_name is NULL"); + return SECURITY_MANAGER_ERROR_INPUT_PARAM; } - return SECURITY_MANAGER_SUCCESS; + std::vector currentGroups; + ret = getProcessGroups(currentGroups); + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; + LogDebug("Current supplementary groups count: " << currentGroups.size()); + + std::vector privilegedGroups; + ret = getPrivilegedGroups(privilegedGroups); + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; + LogDebug("All privileged supplementary groups count: " << privilegedGroups.size()); + + std::vector allowedGroups; + ret = getAppGroups(app_name, allowedGroups); + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; + LogDebug("Allowed privileged supplementary groups count: " << allowedGroups.size()); + + std::unordered_set groupsSet(currentGroups.begin(), currentGroups.end()); + // Remove all groups that are mapped to privileges, so if app is not granted + // the privilege, the group will be dropped from current process + for (gid_t group : privilegedGroups) + groupsSet.erase(group); + + // Re-add those privileged groups that an app is entitled to + groupsSet.insert(allowedGroups.begin(), allowedGroups.end()); + LogDebug("Final supplementary groups count: " << groupsSet.size()); + + return setProcessGroups(std::vector(groupsSet.begin(), groupsSet.end())); }); } diff --git a/src/common/cynara.cpp b/src/common/cynara.cpp index bd336a2..d08f7d4 100755 --- a/src/common/cynara.cpp +++ b/src/common/cynara.cpp @@ -324,6 +324,21 @@ void CynaraAdmin::UpdateAppPolicy( SetPolicies(policies); } +void CynaraAdmin::GetAppPolicy(const std::string &label, const std::string &user, + std::vector &privileges) +{ + std::vector policies; + CynaraAdmin::getInstance().ListPolicies( + CynaraAdmin::Buckets.at(Bucket::MANIFESTS), + label, user, CYNARA_ADMIN_ANY, policies); + + for (auto &policy : policies) { + std::string privilege = policy.privilege; + if (privilege.compare(CYNARA_ADMIN_WILDCARD)) + privileges.push_back(std::move(privilege)); + } +} + void CynaraAdmin::UserInit(uid_t uid, security_manager_user_type userType) { Bucket bucket; diff --git a/src/common/include/cynara.h b/src/common/include/cynara.h index 62763d0..219667d 100644 --- a/src/common/include/cynara.h +++ b/src/common/include/cynara.h @@ -132,6 +132,17 @@ public: const std::vector &privileges); /** + * Fetch Cynara policies for the application and the user. + * Caller must have permission to access Cynara administrative socket. + * + * @param[in] label application Smack label + * @param[in] user user identifier + * @param[out] privileges currently enabled privileges + */ + void GetAppPolicy(const std::string &label, const std::string &user, + std::vector &privileges); + + /** * Depending on user type, create link between MAIN bucket and appropriate * USER_TYPE_* bucket for newly added user uid to apply permissions for that * user type. diff --git a/src/common/include/service_impl.h b/src/common/include/service_impl.h index 313ae0e..e96f02c 100644 --- a/src/common/include/service_impl.h +++ b/src/common/include/service_impl.h @@ -27,7 +27,7 @@ #include #include -#include +#include #include "credentials.h" #include "security-manager.h" @@ -102,11 +102,12 @@ public: * * @param[in] creds credentials of the requesting process * @param[in] appName application identifier - * @param[out] gids returned set of allowed group ids + * @param[out] groups returned vector of allowed groups * * @return API return code, as defined in protocols.h */ - int getAppGroups(const Credentials &creds, const std::string &appName, std::unordered_set &gids); + int getAppGroups(const Credentials &creds, const std::string &appName, + std::vector &groups); /** * Process user adding request. diff --git a/src/common/service_impl.cpp b/src/common/service_impl.cpp index 60c0317..89b4c57 100755 --- a/src/common/service_impl.cpp +++ b/src/common/service_impl.cpp @@ -560,57 +560,47 @@ int ServiceImpl::getPkgName(const std::string &appName, std::string &pkgName) return SECURITY_MANAGER_SUCCESS; } +template +static void vectorRemoveDuplicates(std::vector &vec) +{ + std::sort(vec.begin(), vec.end()); + vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); +} + int ServiceImpl::getAppGroups(const Credentials &creds, const std::string &appName, - std::unordered_set &gids) + std::vector &groups) { try { - std::string pkgName; - std::string smackLabel; - std::string uidStr = std::to_string(creds.uid); - std::string pidStr = std::to_string(creds.pid); - LogDebug("appName: " << appName); - - PrivilegeDb::getInstance().GetAppPkgName(appName, pkgName); - if (pkgName.empty()) { - LogWarning("Application " << appName << " not found in database"); - return SECURITY_MANAGER_ERROR_NO_SUCH_OBJECT; - } - LogDebug("pkgName: " << pkgName); - - smackLabel = SmackLabels::generateAppLabel(appName); + std::string smackLabel = SmackLabels::generateAppLabel(appName); LogDebug("smack label: " << smackLabel); std::vector privileges; - PrivilegeDb::getInstance().GetPkgPrivileges(pkgName, creds.uid, privileges); - /*there is also a need of checking, if privilege is granted to all users*/ - size_t tmp = privileges.size(); - PrivilegeDb::getInstance().GetPkgPrivileges(pkgName, getGlobalUserId(), privileges); - /*privileges needs to be sorted and with no duplications - for cynara sake*/ - std::inplace_merge(privileges.begin(), privileges.begin() + tmp, privileges.end()); - privileges.erase(unique(privileges.begin(), privileges.end()), privileges.end()); + std::string uidStr = std::to_string(creds.uid); + CynaraAdmin::getInstance().GetAppPolicy(smackLabel, uidStr, privileges); + CynaraAdmin::getInstance().GetAppPolicy(smackLabel, CYNARA_ADMIN_WILDCARD, privileges); + + vectorRemoveDuplicates(privileges); + + std::string pidStr = std::to_string(creds.pid); for (const auto &privilege : privileges) { - std::vector gidsTmp; - PrivilegeDb::getInstance().GetPrivilegeGroups(privilege, gidsTmp); - if (!gidsTmp.empty()) { + std::vector privGroups; + PrivilegeDb::getInstance().GetPrivilegeGroups(privilege, privGroups); + if (!privGroups.empty()) { LogDebug("Considering privilege " << privilege << " with " << - gidsTmp.size() << " groups assigned"); - // TODO: create method in Cynara class for fetching all privileges of an application + privGroups.size() << " groups assigned"); + if (Cynara::getInstance().check(smackLabel, privilege, uidStr, pidStr)) { - for_each(gidsTmp.begin(), gidsTmp.end(), [&] (std::string group) { - struct group *grp = getgrnam(group.c_str()); - if (grp == NULL) { - LogError("No such group: " << group.c_str()); - return; - } - gids.insert(grp->gr_gid); - }); + groups.insert(groups.end(), + std::make_move_iterator(privGroups.begin()), + std::make_move_iterator(privGroups.end())); LogDebug("Cynara allowed, adding groups"); } else LogDebug("Cynara denied, not adding groups"); } } + vectorRemoveDuplicates(groups); } catch (const PrivilegeDb::Exception::Base &e) { LogError("Database error: " << e.DumpToString()); return SECURITY_MANAGER_ERROR_SERVER_ERROR; diff --git a/src/server/service/service.cpp b/src/server/service/service.cpp index 7d4dae9..a0607a0 100644 --- a/src/server/service/service.cpp +++ b/src/server/service/service.cpp @@ -196,18 +196,14 @@ void Service::processGetPkgName(MessageBuffer &buffer, MessageBuffer &send) void Service::processGetAppGroups(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds) { std::string appName; - std::unordered_set gids; + std::vector groups; int ret; Deserialization::Deserialize(buffer, appName); - ret = serviceImpl.getAppGroups(creds, appName, gids); + ret = serviceImpl.getAppGroups(creds, appName, groups); Serialization::Serialize(send, ret); - if (ret == SECURITY_MANAGER_SUCCESS) { - Serialization::Serialize(send, static_cast(gids.size())); - for (const auto &gid : gids) { - Serialization::Serialize(send, gid); - } - } + if (ret == SECURITY_MANAGER_SUCCESS) + Serialization::Serialize(send, groups); } void Service::processUserAdd(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds) -- 2.7.4