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

Commit ca1b446b authored by Sanjana Sunil's avatar Sanjana Sunil Committed by Gerrit Code Review
Browse files

Merge "Isolate sdk sandbox data"

parents ed70e4de 7bd74a4f
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
@@ -2202,16 +2202,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);

@@ -2237,7 +2246,7 @@ public final class ProcessList {
                bindMountAppsData = false;
            }

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