From: Konrad Lipinski Date: Tue, 26 Jul 2022 10:52:49 +0000 (+0200) Subject: Add subsession bind mount isolation X-Git-Tag: submit/tizen/20220803.102654~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=acd9399f569c69a8e8c4d07eef48483ea08a801b;p=platform%2Fcore%2Fsecurity%2Fsecurity-manager.git Add subsession bind mount isolation By introducing prepare_app2(app_id, subsession_id) and implementing prepare_app(app_id) as prepare_app2(app_id, nullptr). Null subsession_id indicates the default subsession. The selected subsession is mounted over the "apps_rw" directory. Other subsessions are hidden by mounting an empty directory over the user's "subsession" directory if it exists. Change-Id: I19c884bdd64c53b82fef3447470378c8a8cfae3e --- diff --git a/src/client/client-security-manager.cpp b/src/client/client-security-manager.cpp index 2ba22b79..ff9f7da1 100644 --- a/src/client/client-security-manager.cpp +++ b/src/client/client-security-manager.cpp @@ -819,6 +819,24 @@ int security_manager_drop_process_privileges(void) return SECURITY_MANAGER_SUCCESS; } +static int setupSubsession(TizenPlatformConfig &tpc, const std::string &userAppsRWDir, + const char *subsession_id) +{ + const auto subsessionsDir = tpc.ctxGetEnv(TZ_USER_HOME).append("/subsession/"); + + if (subsession_id) { + // bind the subsession over the main dir + const auto ret = MountNS::bindMountRW(subsessionsDir + subsession_id + "/apps_rw", userAppsRWDir); + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; + // hide all subsessions + return MountNS::bindMountRO(EMPTY_DIRECTORY, subsessionsDir); + } + + // hide all subsessions if subsessionsDir exists + return MountNS::bindMountROIfExists(EMPTY_DIRECTORY, subsessionsDir); +} + static int setupSharedRO(const std::string &pkg_name, bool enabledSharedRO, const std::string &userAppsRWDir, const std::string &userAppsRWSharedDir) { @@ -910,18 +928,25 @@ int security_manager_prepare_app_candidate(void) static inline int security_manager_setup_namespace_internal(const MountNS::PrivilegePathsMap &privilegePathMap, const std::string &pkg_name, bool enabledSharedRO, const std::vector &privPathsStatusVector, - const std::string &app_label) + const std::string &app_label, const char *subsession_id) { // mount namespace setup was made by other process when userAppsRWSharedDir is read only, we can skip it TizenPlatformConfig tpc(geteuid()); std::string userAppsRWDir = tpc.ctxGetEnv(TZ_USER_APP); + + int ret = setupSubsession(tpc, userAppsRWDir, subsession_id); + if (ret != SECURITY_MANAGER_SUCCESS) { + LogError("Failed to setup subsession: " << security_manager_strerror(static_cast(ret))); + return ret; + } + std::string userAppsRWSharedDir = userAppsRWDir + "/.shared/"; if (FS::directoryStatus(userAppsRWSharedDir) > 0 && access(userAppsRWSharedDir.c_str(), W_OK) == -1) { LogDebug("Mount namespace setup was made by other process of multi-process app zygote"); return SECURITY_MANAGER_SUCCESS; } - int ret = setupSharedRO(pkg_name, enabledSharedRO, userAppsRWDir, userAppsRWSharedDir); + ret = setupSharedRO(pkg_name, enabledSharedRO, userAppsRWDir, userAppsRWSharedDir); if (ret != SECURITY_MANAGER_SUCCESS) { LogError("Failed to setup app SharedRO: " << security_manager_strerror(static_cast(ret))); return ret; @@ -939,14 +964,24 @@ static inline int security_manager_setup_namespace_internal(const MountNS::Privi SECURITY_MANAGER_API int security_manager_prepare_app(const char *app_name) { - LOG_EXECUTION_TIME("security_manager_prepare_app(" + std::string(app_name) + ")", Credentials::getCredentialsFromSelf()); + return security_manager_prepare_app2(app_name, nullptr); +} +SECURITY_MANAGER_API +int security_manager_prepare_app2(const char *app_name, const char *subsession_id) +{ if (app_name == nullptr) { LogError("app_name is NULL"); return SECURITY_MANAGER_ERROR_INPUT_PARAM; } - LogDebug("security_manager_prepare_app() called for app " << app_name); + LOG_EXECUTION_TIME("security_manager_prepare_app2(" + std::string(app_name) + "," + + (subsession_id ?: "(default)") + ")", Credentials::getCredentialsFromSelf()); + + if (subsession_id) + LogDebug("security_manager_prepare_app2() called for app " << app_name << " subsession " << subsession_id); + else + LogDebug("security_manager_prepare_app2() called for app " << app_name); return try_catch([&] { @@ -969,7 +1004,7 @@ int security_manager_prepare_app(const char *app_name) } ret = security_manager_setup_namespace_internal(privilegePathMap, pkgName, - prepareAppFlags & PREPARE_APP_SHARED_RO_FLAG, privPathsStatusVector, appLabel); + prepareAppFlags & PREPARE_APP_SHARED_RO_FLAG, privPathsStatusVector, appLabel, subsession_id); if (ret != SECURITY_MANAGER_SUCCESS) { LogError("Unable to setup namespace for application " << app_name); return ret; diff --git a/src/common/include/config.h b/src/common/include/config.h index 7596c9a9..532915a5 100644 --- a/src/common/include/config.h +++ b/src/common/include/config.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2015-2022 Samsung Electronics Co., Ltd. All rights reserved. * * This file is licensed under the terms of MIT License or the Apache License * Version 2.0 of your choice. See the LICENSE.MIT file for MIT license details. @@ -45,6 +45,8 @@ /* Files used in permitted label managment */ #define APPS_LABELS_FILE "apps-labels" +#define EMPTY_DIRECTORY DATA_INSTALL_DIR "/dummy" + /* Policy files */ #define PRIVILEGE_MOUNT_LIST_FILE POLICY_INSTALL_DIR "/privilege-mount.list" diff --git a/src/common/include/mount-namespace.h b/src/common/include/mount-namespace.h index 17a7eb51..8147d2f4 100644 --- a/src/common/include/mount-namespace.h +++ b/src/common/include/mount-namespace.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2017-2022 Samsung Electronics Co., Ltd. All rights reserved. * * This file is licensed under the terms of MIT License or the Apache License * Version 2.0 of your choice. See the LICENSE.MIT file for MIT license details. @@ -63,6 +63,7 @@ int makeMountSlave(const Path &mountPoint); int makeMountPrivate(const Path &mountPoint); int bindMountRW(const Path &source, const Path &target); int bindMountRO(const Path &source, const Path &target); +int bindMountROIfExists(const Path &source, const Path &target); int uMount(const Path &target); bool isPathBound(const Path &what, const Path &where); diff --git a/src/common/mount-namespace.cpp b/src/common/mount-namespace.cpp index f3c86359..8294a581 100644 --- a/src/common/mount-namespace.cpp +++ b/src/common/mount-namespace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2017-2022 Samsung Electronics Co., Ltd. All rights reserved. * * This file is licensed under the terms of MIT License or the Apache License * Version 2.0 of your choice. See the LICENSE.MIT file for MIT license details. @@ -219,13 +219,34 @@ int bindMountRW(const Path &source, const Path &target) int bindMountRO(const Path &source, const Path &target) { - int ret = mount(source.c_str(), target.c_str(), NULL, MS_BIND|MS_RDONLY, NULL); + int ret = mount(source.c_str(), target.c_str(), NULL, MS_BIND, NULL); + if (ret != 0) { + LogError("Failed to bind directory " << source << " to " << target << " " << GetErrnoString(errno)); + return SECURITY_MANAGER_ERROR_MOUNT_ERROR; + } + + ret = mount(NULL, target.c_str(), NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL); if (ret != 0) { LogError("Failed to RO bind directory " << source << " to " << target << " " << GetErrnoString(errno)); return SECURITY_MANAGER_ERROR_MOUNT_ERROR; } - ret = mount("none", target.c_str(), NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL); + return SECURITY_MANAGER_SUCCESS; +} + +int bindMountROIfExists(const Path &source, const Path &target) +{ + int ret = mount(source.c_str(), target.c_str(), NULL, MS_BIND, NULL); + if (ret != 0) { + if (errno == ENOENT) { + LogDebug("ENOENT when binding " << source << " to " << target << ", skipping"); + return SECURITY_MANAGER_SUCCESS; + } + LogError("Failed to bind directory " << source << " to " << target << " " << GetErrnoString(errno)); + return SECURITY_MANAGER_ERROR_MOUNT_ERROR; + } + + ret = mount(NULL, target.c_str(), NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL); if (ret != 0) { LogError("Failed to RO bind directory " << source << " to " << target << " " << GetErrnoString(errno)); return SECURITY_MANAGER_ERROR_MOUNT_ERROR; diff --git a/src/include/app-runtime.h b/src/include/app-runtime.h index 9d7b5b31..82734031 100644 --- a/src/include/app-runtime.h +++ b/src/include/app-runtime.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2016-2022 Samsung Electronics Co., Ltd. All rights reserved. * * This file is licensed under the terms of MIT License or the Apache License * Version 2.0 of your choice. See the LICENSE.MIT file for MIT license details. @@ -109,6 +109,22 @@ int security_manager_prepare_app_candidate(void); */ int security_manager_prepare_app(const char *app_id); +/** + * A convenience function for launchers for preparing security context for an + * application process. It should be called before running actual application code. + * + * This function has to be called in a process where security_manager_prepare_app_candidate() was + * already called and all existing threads are already in the same namespaces. + * + * This function can be called in multithreaded environment. + * However, threads must not be spawned or terminated while the function's running. + * + * \param[in] app_id Application identifier + * \param[in] subsession_id Subsession identifier, NULL if default session + * \return API return code or error code + */ +int security_manager_prepare_app2(const char *app_id, const char *subsession_id); + /** * This function is intended for launchers for cleaning security context for an * application process. It should be called after application termination.