From 1bf8c3adf21b50a6c24f7c0246884cf389941c93 Mon Sep 17 00:00:00 2001 From: Rafal Krypa Date: Fri, 31 Mar 2017 13:38:34 +0200 Subject: [PATCH] Implement security_manager_prepare_app_privacy Change-Id: I9467a359672f5a1e3147a92ae2eb282a1e643b26 Signed-off-by: Rafal Krypa --- packaging/security-manager.spec | 1 + src/client/CMakeLists.txt | 1 + src/client/client-security-manager.cpp | 200 +++++++++++++++++++++++++++++++-- src/common/cynara.cpp | 18 +++ src/common/include/cynara.h | 12 ++ src/common/include/privilege-info.h | 2 + src/common/include/protocols.h | 1 + src/common/include/service_impl.h | 18 +++ src/common/privilege-info.cpp | 5 + src/common/service_impl.cpp | 23 ++++ src/server/service/include/service.h | 9 ++ src/server/service/service.cpp | 18 +++ 12 files changed, 299 insertions(+), 9 deletions(-) diff --git a/packaging/security-manager.spec b/packaging/security-manager.spec index 9e388ad..025e90b 100644 --- a/packaging/security-manager.spec +++ b/packaging/security-manager.spec @@ -33,6 +33,7 @@ BuildRequires: pkgconfig(db-util) BuildRequires: pkgconfig(cynara-admin) BuildRequires: pkgconfig(cynara-client-async) BuildRequires: pkgconfig(security-privilege-manager) +BuildRequires: pkgconfig(askuser-notification-ipc) BuildRequires: boost-devel %{?systemd_requires} diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index 9d56f05..ddbb352 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -4,6 +4,7 @@ PKG_CHECK_MODULES(CLIENT_DEP libsmack libcap libprocps + askuser-notification-ipc ) SET(CLIENT_VERSION_MAJOR 2) diff --git a/src/client/client-security-manager.cpp b/src/client/client-security-manager.cpp index 3a62fef..dff5c46 100644 --- a/src/client/client-security-manager.cpp +++ b/src/client/client-security-manager.cpp @@ -57,10 +57,13 @@ #include #include #include +#include +#include #include #include #include +#include #include "filesystem.h" @@ -280,6 +283,16 @@ int security_manager_app_uninstall(const app_inst_req *p_req) }); } +static int getAppPkgName(std::string &pkgName, const std::string &appName) +{ + ClientRequest request(SecurityModuleCall::APP_GET_PKG_NAME); + if (request.send(appName).failed()) + return request.getStatus(); + + request.recv(pkgName); + return SECURITY_MANAGER_SUCCESS; +} + SECURITY_MANAGER_API int security_manager_get_app_pkgid(char **pkg_name, const char *app_name) { @@ -300,18 +313,17 @@ int security_manager_get_app_pkgid(char **pkg_name, const char *app_name) return SECURITY_MANAGER_ERROR_INPUT_PARAM; } - ClientRequest request(SecurityModuleCall::APP_GET_PKG_NAME); - if (request.send(std::string(app_name)).failed()) - return request.getStatus(); + std::string pkgName; + int ret = getAppPkgName(pkgName, std::string(app_name)); + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; - std::string pkgNameString; - request.recv(pkgNameString); - if (pkgNameString.empty()) { + if (pkgName.empty()) { LogError("Unexpected empty pkgName"); return SECURITY_MANAGER_ERROR_UNKNOWN; } - *pkg_name = strdup(pkgNameString.c_str()); + *pkg_name = strdup(pkgName.c_str()); if (*pkg_name == NULL) { LogError("Failed to allocate memory for pkgName"); return SECURITY_MANAGER_ERROR_MEMORY; @@ -1573,17 +1585,187 @@ int security_manager_shm_open(const char *name, int oflag, mode_t mode, const ch }); } +static int getAppPrivacy(const std::string &appName, + std::vector &privacyAsk, std::vector &privacyDeny) +{ + using namespace SecurityManager; + + ClientRequest request(SecurityModuleCall::APP_GET_PRIVACY); + if (request.send(appName).failed()) + return request.getStatus(); + + request.recv(privacyAsk); + request.recv(privacyDeny); + return SECURITY_MANAGER_SUCCESS; +} + +static int updateAppPrivacy(const std::string &appName, + const std::vector &privacy, const std::string &policy) +{ + using namespace SecurityManager; + + class privacyUpdateRequest { + public: + int init() + { + return security_manager_policy_update_req_new(&m_req); + }; + + int add(const std::string &appName, const std::string &privilege, + const std::string &policy) + { + int ret; + policy_entry *entry = nullptr; + + ret = security_manager_policy_entry_new(&entry); + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; + m_entries.push_back(entry); + + ret = security_manager_policy_entry_set_application(entry, appName.c_str()); + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; + + ret = security_manager_policy_entry_set_privilege(entry, privilege.c_str()); + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; + + ret = security_manager_policy_entry_set_level(entry, policy.c_str()); + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; + + ret = security_manager_policy_update_req_add_entry(m_req, entry); + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; + + return SECURITY_MANAGER_SUCCESS; + }; + + int send() + { + return security_manager_policy_update_send(m_req); + } + + ~privacyUpdateRequest() + { + for (policy_entry *entry : m_entries) + security_manager_policy_entry_free(entry); + security_manager_policy_update_req_free(m_req); + }; + + private: + policy_update_req *m_req = nullptr; + std::vector m_entries; + }; + + int ret; + privacyUpdateRequest pur; + ret = pur.init(); + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; + + for (const std::string &privilege : privacy) { + ret = pur.add(appName, privilege, policy); + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; + } + + return pur.send(); +} + SECURITY_MANAGER_API int security_manager_prepare_app_privacy(const char *app_name) { using namespace SecurityManager; + return try_catch([&]() -> int { if (app_name == nullptr) { LogError("app_name is NULL"); return SECURITY_MANAGER_ERROR_INPUT_PARAM; } - // TODO: stub implementation - return SECURITY_MANAGER_ERROR_UNKNOWN; + int ret; + std::string pkgName; + ret = getAppPkgName(pkgName, std::string(app_name)); + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; + + std::vector privacyAsk; + std::vector privacyDeny; + ret = getAppPrivacy(app_name, privacyAsk, privacyDeny); + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; + + if (privacyAsk.empty()) { + if (privacyDeny.empty()) + return SECURITY_MANAGER_SUCCESS; + + if (PrivilegeInfo::isAppWhiteListed(pkgName)) + return SECURITY_MANAGER_SUCCESS; + + AskUser::Protocol::toast_fail_launch(pkgName, app_name, getuid()); + return SECURITY_MANAGER_ERROR_APP_LAUNCH_PROHIBITED; + } + + std::sort(privacyAsk.begin(), privacyAsk.end()); + std::sort(privacyDeny.begin(), privacyDeny.end()); + + std::vector privacy; + std::merge( + privacyAsk.begin(), privacyAsk.end(), + privacyDeny.begin(), privacyDeny.end(), + std::back_inserter(privacy)); + + int askResult; + if (AskUser::Protocol::popup_launch(pkgName, app_name, getuid(), privacy, askResult) < 0) { + LogError("Launch pop-up failed"); + return SECURITY_MANAGER_ERROR_UNKNOWN; + } + + bool launchAllowed; + switch (askResult) { + case ASKUSER_DENY_ONCE: + LogDebug("Launch pop-up response: deny once"); + launchAllowed = false; + ret = SECURITY_MANAGER_SUCCESS; + break; + + case ASKUSER_DENY_FOREVER: + LogDebug("Launch pop-up response: deny forever"); + launchAllowed = false; + ret = updateAppPrivacy(app_name, privacy, Config::PRIVACY_POLICY_DENY); + break; + + case ASKUSER_ALLOW_ONCE: + LogDebug("Launch pop-up response: allow once"); + launchAllowed = true; + ret = SECURITY_MANAGER_SUCCESS; + break; + + case ASKUSER_ALLOW_FOREVER: + LogDebug("Launch pop-up response: allow forever"); + launchAllowed = true; + ret = updateAppPrivacy(app_name, privacy, Config::PRIVACY_POLICY_ALLOW); + break; + + default: + LogError("Launch pop-up response: UNKNOWN"); + return SECURITY_MANAGER_ERROR_UNKNOWN; + } + + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; + + if (launchAllowed) + return SECURITY_MANAGER_SUCCESS; + + if (PrivilegeInfo::isAppWhiteListed(pkgName)) { + LogInfo("Launch pop-up denied privileges, whitelisted app - launching"); + return SECURITY_MANAGER_SUCCESS; + } + + LogInfo("Launch pop-up denied privileges, whitelisted app - not launching"); + AskUser::Protocol::toast_fail_launch(pkgName, app_name, getuid()); + return SECURITY_MANAGER_ERROR_APP_LAUNCH_PROHIBITED; }); } diff --git a/src/common/cynara.cpp b/src/common/cynara.cpp index 42742b5..2b3215b 100644 --- a/src/common/cynara.cpp +++ b/src/common/cynara.cpp @@ -390,6 +390,24 @@ void CynaraAdmin::getAppPolicy(const std::string &label, const std::string &user } } +void CynaraAdmin::getAppPrivacy(const std::string &label, const std::string &user, + std::vector &privacyAsk, std::vector &privacyDeny) +{ + privacyAsk.clear(); + privacyDeny.clear(); + + std::vector policies; + listPolicies(CynaraAdmin::Buckets.at(Bucket::PRIVACY_MANAGER), + label, user, CYNARA_ADMIN_ANY, policies); + + for (const auto &policy : policies) { + if (policy.result == convertToPolicyType(Config::PRIVACY_POLICY_ASK)) + privacyAsk.push_back(std::string(policy.privilege)); + else if (policy.result == convertToPolicyType(Config::PRIVACY_POLICY_DENY)) + privacyDeny.push_back(std::string(policy.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 ec2b4eb..638051b 100644 --- a/src/common/include/cynara.h +++ b/src/common/include/cynara.h @@ -148,6 +148,18 @@ public: std::vector &privileges); /** + * Fetch application privileges that are set to ASK_USER_LEGACY + * (launch pop-up needed) or PRIVACY_DENY (disabled) + * + * @param[in] label application Smack label + * @param[in] user user identifier + * @param[out] privacyAsk returned vector of privileges that are set to ASK_USER_LEGACY + * @param[out] privacyDeny returned vector of privileges that are set to PRIVACY_DENY + */ + void getAppPrivacy(const std::string &label, const std::string &user, + std::vector &privacyAsk, std::vector &privacyDeny); + + /** * 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/privilege-info.h b/src/common/include/privilege-info.h index 64a3f59..90a5379 100644 --- a/src/common/include/privilege-info.h +++ b/src/common/include/privilege-info.h @@ -47,6 +47,8 @@ public: bool hasAttribute(PrivilegeAttr attr); + static bool isAppWhiteListed(const std::string &pkgName); + private: uid_t m_uid; std::string m_appId; diff --git a/src/common/include/protocols.h b/src/common/include/protocols.h index fcf1314..e9a6d6b 100644 --- a/src/common/include/protocols.h +++ b/src/common/include/protocols.h @@ -88,6 +88,7 @@ enum class SecurityModuleCall GROUPS_FOR_UID, LABEL_FOR_PROCESS, SHM_APP_NAME, + APP_GET_PRIVACY, NOOP = 0x90, }; diff --git a/src/common/include/service_impl.h b/src/common/include/service_impl.h index bc75e50..87988a5 100644 --- a/src/common/include/service_impl.h +++ b/src/common/include/service_impl.h @@ -234,6 +234,7 @@ public: * @return API return code, as defined in protocols.h */ int labelForProcess(const std::string &appName, std::string &label); + /* * Request for access to shared memory segment for * appName application. @@ -247,6 +248,23 @@ public: int shmAppName(const Credentials &creds, const std::string &shmName, const std::string &appName); + + /* + * Fetch application privileges that are set to ASK_USER_LEGACY + * (launch pop-up needed) or PRIVACY_DENY (disabled) + * + * @param[in] creds credentials of the requesting process + * @param[in] appName application identifier + * @param[out] privacyAsk returned vector of privileges that are set to ASK_USER_LEGACY + * @param[out] privacyDeny returned vector of privileges that are set to PRIVACY_DENY + * + * @return API return code, as defined in protocols.h + */ + int getAppPrivacy(const Credentials &creds, + const std::string &appName, + std::vector &privacyAsk, + std::vector &privacyDeny); + private: bool authenticate(const Credentials &creds, const std::string &privilege); diff --git a/src/common/privilege-info.cpp b/src/common/privilege-info.cpp index 8df2c3d..cb26a73 100644 --- a/src/common/privilege-info.cpp +++ b/src/common/privilege-info.cpp @@ -57,5 +57,10 @@ bool PrivilegeInfo::hasAttribute(PrivilegeAttr attr) } } +bool PrivilegeInfo::isAppWhiteListed(const std::string &pkgName) +{ + return privilege_info_is_privacy_white_list_application(pkgName.c_str()); +} + } // namespace SecurityManager diff --git a/src/common/service_impl.cpp b/src/common/service_impl.cpp index bec0060..f459a89 100644 --- a/src/common/service_impl.cpp +++ b/src/common/service_impl.cpp @@ -1691,4 +1691,27 @@ int ServiceImpl::shmAppName(const Credentials &creds, const std::string &shmName return SECURITY_MANAGER_SUCCESS; } +int ServiceImpl::getAppPrivacy(const Credentials &creds, const std::string &appName, + std::vector &privacyAsk, + std::vector &privacyDeny) +{ + try { + std::string uidStr = std::to_string(creds.uid); + std::string appProcessLabel = getAppProcessLabel(appName); + std::vector privileges; + m_cynaraAdmin.getAppPrivacy(appProcessLabel, uidStr, privacyAsk, privacyDeny); + } catch (const CynaraException::Base &e) { + LogError("Error while reading Cynara policy: " << e.DumpToString()); + return SECURITY_MANAGER_ERROR_SERVER_ERROR; + } catch (const SmackException::Base &e) { + LogError("Error while generating Smack label: " << e.DumpToString()); + return SECURITY_MANAGER_ERROR_SERVER_ERROR; + } catch (const std::bad_alloc &e) { + LogError("Memory allocation failed: " << e.what()); + return SECURITY_MANAGER_ERROR_MEMORY; + } + + return SECURITY_MANAGER_SUCCESS; +} + } /* namespace SecurityManager */ diff --git a/src/server/service/include/service.h b/src/server/service/include/service.h index 90c8ed8..ba76ecb 100644 --- a/src/server/service/include/service.h +++ b/src/server/service/include/service.h @@ -202,6 +202,15 @@ private: * @param creds credentials of the requesting process */ void processShmAppName(MessageBuffer &recv, MessageBuffer &send, const Credentials &creds); + + /** + * Process getting privacy privileges for application + * + * @param recv Raw received data buffer + * @param send Raw data buffer to be sent + * @param creds credentials of the requesting process + */ + void processGetAppPrivacy(MessageBuffer &recv, MessageBuffer &send, const Credentials &creds); }; } // namespace SecurityManager diff --git a/src/server/service/service.cpp b/src/server/service/service.cpp index fe4f865..f556304 100644 --- a/src/server/service/service.cpp +++ b/src/server/service/service.cpp @@ -150,6 +150,9 @@ bool Service::processOne(const ConnectionID &conn, MessageBuffer &buffer, case SecurityModuleCall::SHM_APP_NAME: processShmAppName(buffer, send, creds); break; + case SecurityModuleCall::APP_GET_PRIVACY: + processGetAppPrivacy(buffer, send, creds); + break; default: LogError("Invalid call: " << call_type_int); Throw(ServiceException::InvalidAction); @@ -418,4 +421,19 @@ void Service::processShmAppName(MessageBuffer &recv, MessageBuffer &send, const Serialization::Serialize(send, ret); } +void Service::processGetAppPrivacy(MessageBuffer &recv, MessageBuffer &send, const Credentials &creds) +{ + std::string appName; + Deserialization::Deserialize(recv, appName); + + std::vector privacyAsk, privacyDeny; + int ret = serviceImpl.getAppPrivacy(creds, appName, privacyAsk, privacyDeny); + Serialization::Serialize(send, ret); + if (ret == SECURITY_MANAGER_SUCCESS) { + Serialization::Serialize(send, privacyAsk); + Serialization::Serialize(send, privacyDeny); + } +} + + } // namespace SecurityManager -- 2.7.4