diff options
| author | 2022-09-13 12:56:50 +0000 | |
|---|---|---|
| committer | 2022-09-13 12:56:50 +0000 | |
| commit | 19a10c830f062d59e8d26c3352eb2bb77e52a130 (patch) | |
| tree | 8b88a144d8297b5c987c25daa406db8e1bb91877 | |
| parent | 268f9715758b3d85bbd241d58adf8cacede3ac75 (diff) | |
| parent | 70e4145b8c85f648456ab403b8ca796dab924481 (diff) | |
Merge "Isolate sdk sandbox data"
| -rw-r--r-- | core/jni/com_android_internal_os_Zygote.cpp | 309 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ProcessList.java | 21 |
2 files changed, 263 insertions, 67 deletions
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index cbc34629e8c4..550259fa7083 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -1277,68 +1277,80 @@ static void isolateAppData(JNIEnv* env, const std::vector<std::string>& merged_d } closedir(dir); - // 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); + // 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); - for (int i = 0; i < size; i += 3) { - std::string const & packageName = merged_data_info_list[i]; - std::string const & volUuid = merged_data_info_list[i + 1]; - std::string const & inode = merged_data_info_list[i + 2]; - - std::string::size_type sz; - long long ceDataInode = std::stoll(inode, &sz); - - std::string actualCePath, actualDePath; - if (volUuid.compare("null") != 0) { - // Volume that is stored in /mnt/expand - char volPath[PATH_MAX]; - char volCePath[PATH_MAX]; - char volDePath[PATH_MAX]; - char volCeUserPath[PATH_MAX]; - char volDeUserPath[PATH_MAX]; - - snprintf(volPath, PATH_MAX, "/mnt/expand/%s", volUuid.c_str()); - snprintf(volCePath, PATH_MAX, "%s/user", volPath); - snprintf(volDePath, PATH_MAX, "%s/user_de", volPath); - snprintf(volCeUserPath, PATH_MAX, "%s/%d", volCePath, userId); - snprintf(volDeUserPath, PATH_MAX, "%s/%d", volDePath, userId); - - PrepareDirIfNotPresent(volPath, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT, fail_fn); - PrepareDirIfNotPresent(volCePath, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT, fail_fn); - PrepareDirIfNotPresent(volDePath, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT, fail_fn); - PrepareDirIfNotPresent(volCeUserPath, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT, - fail_fn); - PrepareDirIfNotPresent(volDeUserPath, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT, - fail_fn); - - actualCePath = volCeUserPath; - actualDePath = volDeUserPath; - } else { - // Internal volume that stored in /data - char internalCeUserPath[PATH_MAX]; - char internalDeUserPath[PATH_MAX]; - snprintf(internalCeUserPath, PATH_MAX, "/data/user/%d", userId); - snprintf(internalDeUserPath, PATH_MAX, "/data/user_de/%d", userId); - // If it's not user 0, create /data/user/$USER. - if (userId == 0) { - actualCePath = internalLegacyCePath; - } else { - PrepareDirIfNotPresent(internalCeUserPath, DEFAULT_DATA_DIR_PERMISSION, - AID_ROOT, AID_ROOT, fail_fn); - actualCePath = internalCeUserPath; + for (int i = 0; i < size; i += 3) { + std::string const& packageName = merged_data_info_list[i]; + std::string const& volUuid = merged_data_info_list[i + 1]; + std::string const& inode = merged_data_info_list[i + 2]; + + std::string::size_type sz; + long long ceDataInode = std::stoll(inode, &sz); + + std::string actualCePath, actualDePath; + if (volUuid.compare("null") != 0) { + // Volume that is stored in /mnt/expand + char volPath[PATH_MAX]; + char volCePath[PATH_MAX]; + char volDePath[PATH_MAX]; + char volCeUserPath[PATH_MAX]; + char volDeUserPath[PATH_MAX]; + + snprintf(volPath, PATH_MAX, "/mnt/expand/%s", volUuid.c_str()); + snprintf(volCePath, PATH_MAX, "%s/user", volPath); + snprintf(volDePath, PATH_MAX, "%s/user_de", volPath); + snprintf(volCeUserPath, PATH_MAX, "%s/%d", volCePath, userId); + snprintf(volDeUserPath, PATH_MAX, "%s/%d", volDePath, userId); + + PrepareDirIfNotPresent(volPath, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT, + fail_fn); + PrepareDirIfNotPresent(volCePath, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT, + fail_fn); + PrepareDirIfNotPresent(volDePath, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT, + fail_fn); + PrepareDirIfNotPresent(volCeUserPath, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT, + fail_fn); + PrepareDirIfNotPresent(volDeUserPath, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT, + fail_fn); + + actualCePath = volCeUserPath; + actualDePath = volDeUserPath; + } else { + // Internal volume that stored in /data + char internalCeUserPath[PATH_MAX]; + char internalDeUserPath[PATH_MAX]; + snprintf(internalCeUserPath, PATH_MAX, "/data/user/%d", userId); + snprintf(internalDeUserPath, PATH_MAX, "/data/user_de/%d", userId); + // If it's not user 0, create /data/user/$USER. + if (userId == 0) { + actualCePath = internalLegacyCePath; + } else { + 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); + actualDePath = internalDeUserPath; + } + isolateAppDataPerPackage(userId, packageName, volUuid, ceDataInode, actualCePath, + actualDePath, 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); } + // 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. @@ -1379,6 +1391,167 @@ 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, @@ -1444,6 +1617,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); @@ -1596,9 +1776,16 @@ 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); diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index affb084708eb..2bd40afe3cc7 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2200,16 +2200,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(); - final String[] sharedPackages = pmInt.getSharedUserPackagesForPackage( - app.info.packageName, app.userId); - final String[] targetPackagesList = sharedPackages.length == 0 - ? new String[]{app.info.packageName} : sharedPackages; + + // 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); + targetPackagesList = sharedPackages.length == 0 + ? new String[]{app.info.packageName} : sharedPackages; + } final boolean hasAppStorage = hasAppStorage(pmInt, app.info.packageName); @@ -2235,7 +2244,7 @@ public final class ProcessList { bindMountAppsData = false; } - if (!hasAppStorage) { + if (!hasAppStorage && !app.isSdkSandbox) { bindMountAppsData = false; pkgDataInfoMap = null; allowlistedAppDataInfoMap = null; |