From c7fa280e81c26073134c1e2d637d976f808b43ad Mon Sep 17 00:00:00 2001 From: Alex Buynytskyy Date: Thu, 2 Nov 2023 15:15:48 -0700 Subject: Validate package names passed to the installer. Bug: 308989388 Bug: 307532206 Test: atest android.content.pm.cts.PackageManagerTest Change-Id: I840c9c9af5752b3901d4719a13e7908faa43ab04 Merged-In: I840c9c9af5752b3901d4719a13e7908faa43ab04 (cherry picked from commit 1f445474cd1b902b2e7292a0d24e58f020fd51e7) --- .../android/server/pm/PackageInstallerService.java | 77 ++++++++++++++++++++-- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 896b17ec7b12..d834dfe7ad56 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -50,6 +50,7 @@ import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Environment; +import android.os.FileUtils; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -567,17 +568,22 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements // App package name and label length is restricted so that really long strings aren't // written to disk. - if (params.appPackageName != null - && params.appPackageName.length() > SessionParams.MAX_PACKAGE_NAME_LENGTH) { + if (params.appPackageName != null && !isValidPackageName(params.appPackageName)) { params.appPackageName = null; } params.appLabel = TextUtils.trimToSize(params.appLabel, PackageItemInfo.MAX_SAFE_LABEL_LENGTH); - String requestedInstallerPackageName = (params.installerPackageName != null - && params.installerPackageName.length() < SessionParams.MAX_PACKAGE_NAME_LENGTH) - ? params.installerPackageName : installerPackageName; + // Validate installer package name. + if (params.installerPackageName != null && !isValidPackageName( + params.installerPackageName)) { + params.installerPackageName = null; + } + + String requestedInstallerPackageName = + params.installerPackageName != null ? params.installerPackageName + : installerPackageName; if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) { params.installFlags |= PackageManager.INSTALL_FROM_ADB; @@ -872,6 +878,67 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements throw new IllegalStateException("Failed to allocate session ID"); } + /** + * For those names would be used as a part of the file name. Limits size to 223 and reserves 32 + * for the OS. + */ + private static final int MAX_FILE_NAME_SIZE = 223; + + /** + * Check if the given name is valid. + * + * @param name The name to check. + * @param requireSeparator {@code true} if the name requires containing a separator at least. + * @param requireFilename {@code true} to apply file name validation to the given name. It also + * limits length of the name to the {@link #MAX_FILE_NAME_SIZE}. + * @return Success if it's valid. + */ + private static String validateName(String name, boolean requireSeparator, + boolean requireFilename) { + final int N = name.length(); + boolean hasSep = false; + boolean front = true; + for (int i = 0; i < N; i++) { + final char c = name.charAt(i); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + front = false; + continue; + } + if (!front) { + if ((c >= '0' && c <= '9') || c == '_') { + continue; + } + } + if (c == '.') { + hasSep = true; + front = true; + continue; + } + return "bad character '" + c + "'"; + } + if (requireFilename) { + if (!FileUtils.isValidExtFilename(name)) { + return "Invalid filename"; + } else if (N > MAX_FILE_NAME_SIZE) { + return "the length of the name is greater than " + MAX_FILE_NAME_SIZE; + } + } + return hasSep || !requireSeparator ? null : "must have at least one '.' separator"; + } + + private static boolean isValidPackageName(String packageName) { + if (packageName.length() > SessionParams.MAX_PACKAGE_NAME_LENGTH) { + return false; + } + // "android" is a valid package name + String errorMessage = validateName( + packageName, /* requireSeparator= */ false, /* requireFilename */ true); + if (errorMessage != null) { + return false; + } + return true; + } + private File getTmpSessionDir(String volumeUuid) { return Environment.getDataAppDirectory(volumeUuid); } -- cgit v1.2.3-59-g8ed1b