Add subsession bind mount isolation
authorKonrad Lipinski <k.lipinski2@samsung.com>
Tue, 26 Jul 2022 10:52:49 +0000 (12:52 +0200)
committerKonrad Lipinski <k.lipinski2@samsung.com>
Thu, 4 Aug 2022 14:05:58 +0000 (16:05 +0200)
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

src/client/client-security-manager.cpp
src/common/include/config.h
src/common/include/mount-namespace.h
src/common/mount-namespace.cpp
src/include/app-runtime.h

index 2ba22b7..ff9f7da 100644 (file)
@@ -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<bool> &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<lib_retcode>(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<lib_retcode>(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;
index 7596c9a..532915a 100644 (file)
@@ -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"
index 17a7eb5..8147d2f 100644 (file)
@@ -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);
 
index f3c8635..8294a58 100644 (file)
@@ -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;
index 9d7b5b3..8273403 100644 (file)
@@ -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.
@@ -110,6 +110,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.
  *