Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1a8ac146 authored by Sanjana Sunil's avatar Sanjana Sunil Committed by Automerger Merge Worker
Browse files

Merge "Isolate sdk sandbox data" am: ca1b446b

parents 21e000ce ca1b446b
Loading
Loading
Loading
Loading
+241 −60
Original line number Diff line number Diff line
@@ -1278,13 +1278,20 @@ static void isolateAppData(JNIEnv* env, const std::vector<std::string>& merged_d
  }
  closedir(dir);

  // No bind mounting of app data should occur in the case of a sandbox process since SDK sandboxes
  // should not be able to read app data. Tmpfs was mounted however since a sandbox should not have
  // access to app data.
  appid_t appId = multiuser_get_app_id(uid);
  bool isSdkSandboxProcess =
          (appId >= AID_SDK_SANDBOX_PROCESS_START && appId <= AID_SDK_SANDBOX_PROCESS_END);
  if (!isSdkSandboxProcess) {
    // Prepare default dirs for user 0 as user 0 always exists.
    int result = symlink("/data/data", "/data/user/0");
    if (result != 0) {
      fail_fn(CREATE_ERROR("Failed to create symlink /data/user/0 %s", strerror(errno)));
    }
  PrepareDirIfNotPresent("/data/user_de/0", DEFAULT_DATA_DIR_PERMISSION,
      AID_ROOT, AID_ROOT, fail_fn);
    PrepareDirIfNotPresent("/data/user_de/0", DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT,
                           fail_fn);

    for (int i = 0; i < size; i += 3) {
      std::string const& packageName = merged_data_info_list[i];
@@ -1329,17 +1336,19 @@ static void isolateAppData(JNIEnv* env, const std::vector<std::string>& merged_d
        if (userId == 0) {
          actualCePath = internalLegacyCePath;
        } else {
        PrepareDirIfNotPresent(internalCeUserPath, DEFAULT_DATA_DIR_PERMISSION,
            AID_ROOT, AID_ROOT, fail_fn);
          PrepareDirIfNotPresent(internalCeUserPath, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT,
                                 AID_ROOT, fail_fn);
          actualCePath = internalCeUserPath;
        }
      PrepareDirIfNotPresent(internalDeUserPath, DEFAULT_DATA_DIR_PERMISSION,
          AID_ROOT, AID_ROOT, fail_fn);
        PrepareDirIfNotPresent(internalDeUserPath, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT,
                               fail_fn);
        actualDePath = internalDeUserPath;
      }
    isolateAppDataPerPackage(userId, packageName, volUuid, ceDataInode,
        actualCePath, actualDePath, fail_fn);
      isolateAppDataPerPackage(userId, packageName, volUuid, ceDataInode, actualCePath,
                               actualDePath, fail_fn);
    }
  }

  // We set the label AFTER everything is done, as we are applying
  // the file operations on tmpfs. If we set the label when we mount
  // tmpfs, SELinux will not happy as we are changing system_data_files.
@@ -1380,6 +1389,165 @@ static void isolateAppData(JNIEnv* env, const std::vector<std::string>& merged_d
  freecon(dataFileContext);
}

/**
 * Without sdk sandbox data isolation, the sandbox could detect if another app is installed on the
 * system by "touching" other data directories like /data/misc_ce/0/sdksandbox/com.whatsapp, similar
 * to apps without app data isolation (see {@link #isolateAppData()}).
 *
 * To prevent this, tmpfs is mounted onto misc_ce and misc_de directories on all possible volumes in
 * a separate mount namespace. The sandbox directory path is then created containing the name of the
 * client app package associated with the sdk sandbox. The contents for this (sdk level storage and
 * shared sdk storage) are bind mounted from the sandbox data mirror.
 */
static void isolateSdkSandboxData(JNIEnv* env, jobjectArray pkg_data_info_list, uid_t uid,
                                  const char* process_name, jstring managed_nice_name,
                                  fail_fn_t fail_fn) {
  const userid_t userId = multiuser_get_user_id(uid);

  int size = (pkg_data_info_list != nullptr) ? env->GetArrayLength(pkg_data_info_list) : 0;
  // The sandbox should only have information of one associated client app (package, uuid, inode)
  if (size != 3) {
    fail_fn(CREATE_ERROR("Unable to isolate sandbox data, incorrect associated app information"));
  }

  auto extract_fn = [env, process_name, managed_nice_name, pkg_data_info_list](int info_list_idx) {
      jstring jstr = (jstring)(env->GetObjectArrayElement(pkg_data_info_list, info_list_idx));
      return ExtractJString(env, process_name, managed_nice_name, jstr).value();
  };
  std::string packageName = extract_fn(0);
  std::string volUuid = extract_fn(1);

  char internalCePath[PATH_MAX];
  char internalDePath[PATH_MAX];
  char externalPrivateMountPath[PATH_MAX];
  snprintf(internalCePath, PATH_MAX, "/data/misc_ce");
  snprintf(internalDePath, PATH_MAX, "/data/misc_de");
  snprintf(externalPrivateMountPath, PATH_MAX, "/mnt/expand");

  char ceUserPath[PATH_MAX];
  char deUserPath[PATH_MAX];
  if (volUuid != "null") {
    snprintf(ceUserPath, PATH_MAX, "%s/%s/misc_ce/%d", externalPrivateMountPath, volUuid.c_str(),
             userId);
    snprintf(deUserPath, PATH_MAX, "%s/%s/misc_de/%d", externalPrivateMountPath, volUuid.c_str(),
             userId);
  } else {
    snprintf(ceUserPath, PATH_MAX, "%s/%d", internalCePath, userId);
    snprintf(deUserPath, PATH_MAX, "%s/%d", internalDePath, userId);
  }

  char ceSandboxPath[PATH_MAX];
  char deSandboxPath[PATH_MAX];
  snprintf(ceSandboxPath, PATH_MAX, "%s/sdksandbox", ceUserPath);
  snprintf(deSandboxPath, PATH_MAX, "%s/sdksandbox", deUserPath);

  // If the client app using the sandbox has been installed when the device is locked and the
  // sandbox starts up when the device is locked, sandbox storage might not have been created.
  // In that case, mount tmpfs for data isolation, but don't bind mount.
  bool bindMountCeSandboxDataDirs = true;
  bool bindMountDeSandboxDataDirs = true;
  if (access(ceSandboxPath, F_OK) != 0) {
    bindMountCeSandboxDataDirs = false;
  }
  if (access(deSandboxPath, F_OK) != 0) {
    bindMountDeSandboxDataDirs = false;
  }

  char* context = nullptr;
  char* userContext = nullptr;
  char* sandboxContext = nullptr;
  if (getfilecon(internalDePath, &context) < 0) {
    fail_fn(CREATE_ERROR("Unable to getfilecon on %s %s", internalDePath, strerror(errno)));
  }
  if (bindMountDeSandboxDataDirs) {
    if (getfilecon(deUserPath, &userContext) < 0) {
      fail_fn(CREATE_ERROR("Unable to getfilecon on %s %s", deUserPath, strerror(errno)));
    }
    if (getfilecon(deSandboxPath, &sandboxContext) < 0) {
      fail_fn(CREATE_ERROR("Unable to getfilecon on %s %s", deSandboxPath, strerror(errno)));
    }
  }

  MountAppDataTmpFs(internalCePath, fail_fn);
  MountAppDataTmpFs(internalDePath, fail_fn);

  // Mount tmpfs on all external volumes
  DIR* dir = opendir(externalPrivateMountPath);
  if (dir == nullptr) {
    fail_fn(CREATE_ERROR("Failed to opendir %s", externalPrivateMountPath));
  }
  struct dirent* ent;
  while ((ent = readdir(dir))) {
    if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue;
    if (ent->d_type != DT_DIR) {
      fail_fn(CREATE_ERROR("Unexpected type: %d %s", ent->d_type, ent->d_name));
    }
    auto volPath = StringPrintf("%s/%s", externalPrivateMountPath, ent->d_name);
    auto externalCePath = StringPrintf("%s/misc_ce", volPath.c_str());
    auto externalDePath = StringPrintf("%s/misc_de", volPath.c_str());

    WaitUntilDirReady(externalCePath.c_str(), fail_fn);
    MountAppDataTmpFs(externalCePath.c_str(), fail_fn);
    WaitUntilDirReady(externalDePath.c_str(), fail_fn);
    MountAppDataTmpFs(externalDePath.c_str(), fail_fn);
  }
  closedir(dir);

  char mirrorCeSandboxPath[PATH_MAX];
  char mirrorDeSandboxPath[PATH_MAX];
  snprintf(mirrorCeSandboxPath, PATH_MAX, "/data_mirror/misc_ce/%s/%d/sdksandbox", volUuid.c_str(),
           userId);
  snprintf(mirrorDeSandboxPath, PATH_MAX, "/data_mirror/misc_de/%s/%d/sdksandbox", volUuid.c_str(),
           userId);

  if (bindMountCeSandboxDataDirs) {
    PrepareDir(ceUserPath, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT, fail_fn);
    PrepareDir(ceSandboxPath, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT, fail_fn);
    // TODO(b/231322885): Use inode numbers to find the correct app path when the device locked.
    createAndMountAppData(packageName, packageName, mirrorCeSandboxPath, ceSandboxPath, fail_fn,
                          true /*call_fail_fn*/);

    relabelDir(ceSandboxPath, sandboxContext, fail_fn);
    relabelDir(ceUserPath, userContext, fail_fn);
  }
  if (bindMountDeSandboxDataDirs) {
    PrepareDir(deUserPath, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT, fail_fn);
    PrepareDir(deSandboxPath, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT, fail_fn);
    createAndMountAppData(packageName, packageName, mirrorDeSandboxPath, deSandboxPath, fail_fn,
                          true /*call_fail_fn*/);

    relabelDir(deSandboxPath, sandboxContext, fail_fn);
    relabelDir(deUserPath, userContext, fail_fn);
  }

  // We set the label AFTER everything is done, as we are applying
  // the file operations on tmpfs. If we set the label when we mount
  // tmpfs, SELinux will not happy as we are changing system_data_files.
  relabelDir(internalCePath, context, fail_fn);
  relabelDir(internalDePath, context, fail_fn);

  // Relabel CE and DE dirs under /mnt/expand
  dir = opendir(externalPrivateMountPath);
  if (dir == nullptr) {
    fail_fn(CREATE_ERROR("Failed to opendir %s", externalPrivateMountPath));
  }
  while ((ent = readdir(dir))) {
    if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue;
    auto volPath = StringPrintf("%s/%s", externalPrivateMountPath, ent->d_name);
    auto externalCePath = StringPrintf("%s/misc_ce", volPath.c_str());
    auto externalDePath = StringPrintf("%s/misc_de", volPath.c_str());
    relabelDir(externalCePath.c_str(), context, fail_fn);
    relabelDir(externalDePath.c_str(), context, fail_fn);
  }
  closedir(dir);

  if (bindMountDeSandboxDataDirs) {
    freecon(sandboxContext);
    freecon(userContext);
  }
  freecon(context);
}

static void insertPackagesToMergedList(JNIEnv* env,
  std::vector<std::string>& merged_data_info_list,
  jobjectArray data_info_list, const char* process_name,
@@ -1445,6 +1613,13 @@ static void isolateJitProfile(JNIEnv* env, jobjectArray pkg_data_info_list,
  MountAppDataTmpFs(kCurProfileDirPath, fail_fn);
  MountAppDataTmpFs(kRefProfileDirPath, fail_fn);

  // Sandbox processes do not have JIT profile, so no data needs to be bind mounted. However, it
  // should still not have access to JIT profile, so tmpfs is mounted.
  appid_t appId = multiuser_get_app_id(uid);
  if (appId >= AID_SDK_SANDBOX_PROCESS_START && appId <= AID_SDK_SANDBOX_PROCESS_END) {
    return;
  }

  // Create profile directory for this user.
  std::string actualCurUserProfile = StringPrintf("%s/%d", kCurProfileDirPath, user_id);
  PrepareDir(actualCurUserProfile, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT, fail_fn);
@@ -1597,9 +1772,15 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
    // Make sure app is running in its own mount namespace before isolating its data directories.
    ensureInAppMountNamespace(fail_fn);

    // Sandbox data and jit profile directories by overlaying a tmpfs on those dirs and bind
    // mount all related packages separately.
    // Isolate app data, jit profile and sandbox data directories by overlaying a tmpfs on those
    // dirs and bind mount all related packages separately.
    if (mount_data_dirs) {
        // Sdk sandbox data isolation does not need to occur for app processes since sepolicy
        // prevents access to sandbox data anyway.
        appid_t appId = multiuser_get_app_id(uid);
        if (appId >= AID_SDK_SANDBOX_PROCESS_START && appId <= AID_SDK_SANDBOX_PROCESS_END) {
            isolateSdkSandboxData(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
        }
        isolateAppData(env, pkg_data_info_list, allowlisted_data_info_list, uid, process_name,
                       managed_nice_name, fail_fn);
        isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
+15 −6
Original line number Diff line number Diff line
@@ -2209,16 +2209,25 @@ public final class ProcessList {
            Map<String, Pair<String, Long>> allowlistedAppDataInfoMap;
            boolean bindMountAppStorageDirs = false;
            boolean bindMountAppsData = mAppDataIsolationEnabled
                    && (UserHandle.isApp(app.uid) || UserHandle.isIsolated(app.uid))
                    && (UserHandle.isApp(app.uid) || UserHandle.isIsolated(app.uid)
                        || app.isSdkSandbox)
                    && mPlatformCompat.isChangeEnabled(APP_DATA_DIRECTORY_ISOLATION, app.info);

            // Get all packages belongs to the same shared uid. sharedPackages is empty array
            // if it doesn't have shared uid.
            final PackageManagerInternal pmInt = mService.getPackageManagerInternal();

            // In the case of sdk sandbox, the pkgDataInfoMap of only the client app associated with
            // the sandbox is required to handle app visibility restrictions for the sandbox.
            final String[] targetPackagesList;
            if (app.isSdkSandbox) {
                targetPackagesList = new String[]{app.sdkSandboxClientAppPackage};
            } else {
                final String[] sharedPackages = pmInt.getSharedUserPackagesForPackage(
                        app.info.packageName, app.userId);
            final String[] targetPackagesList = sharedPackages.length == 0
                targetPackagesList = sharedPackages.length == 0
                        ? new String[]{app.info.packageName} : sharedPackages;
            }

            final boolean hasAppStorage = hasAppStorage(pmInt, app.info.packageName);

@@ -2244,7 +2253,7 @@ public final class ProcessList {
                bindMountAppsData = false;
            }

            if (!hasAppStorage) {
            if (!hasAppStorage && !app.isSdkSandbox) {
                bindMountAppsData = false;
                pkgDataInfoMap = null;
                allowlistedAppDataInfoMap = null;