Remove the resolved path of session's APK from user confirmation intent

Intent sent to the user for install confirmation includes the resolved
path of the session's base apk. A malicious app can modify this path, resulting
in the installer activity to show a different app icon and label in the
confirmation dialog.

To fix this, expose a SystemApi for getting the resolved path and
permission protect it with a privileged permission.

Bug: 269728874
Test: atest CtsPackageInstallTestCases:SessionTest
Change-Id: Ibe7fe3ab5e74e3e910d9f7bd5b6f3f2e3c0ca658
This commit is contained in:
Sumedh Sen
2023-02-27 11:10:47 -08:00
parent 8d85aaf0a2
commit 7623ac966e
7 changed files with 38 additions and 23 deletions

View File

@@ -272,6 +272,7 @@ package android {
field public static final String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG";
field public static final String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE";
field public static final String READ_GLOBAL_APP_SEARCH_DATA = "android.permission.READ_GLOBAL_APP_SEARCH_DATA";
field public static final String READ_INSTALLED_SESSION_PATHS = "android.permission.READ_INSTALLED_SESSION_PATHS";
field public static final String READ_INSTALL_SESSIONS = "android.permission.READ_INSTALL_SESSIONS";
field public static final String READ_NETWORK_USAGE_HISTORY = "android.permission.READ_NETWORK_USAGE_HISTORY";
field public static final String READ_OEM_UNLOCK_STATE = "android.permission.READ_OEM_UNLOCK_STATE";
@@ -3802,7 +3803,6 @@ package android.content.pm {
field public static final String EXTRA_CALLBACK = "android.content.pm.extra.CALLBACK";
field public static final String EXTRA_DATA_LOADER_TYPE = "android.content.pm.extra.DATA_LOADER_TYPE";
field public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS";
field public static final String EXTRA_RESOLVED_BASE_PATH = "android.content.pm.extra.RESOLVED_BASE_PATH";
field public static final int LOCATION_DATA_APP = 0; // 0x0
field public static final int LOCATION_MEDIA_DATA = 2; // 0x2
field public static final int LOCATION_MEDIA_OBB = 1; // 0x1
@@ -3839,6 +3839,7 @@ package android.content.pm {
method public boolean getInstallAsVirtualPreload();
method public int getPendingUserActionReason();
method public boolean getRequestDowngrade();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_INSTALLED_SESSION_PATHS) public String getResolvedBaseApkPath();
method public int getRollbackDataPolicy();
method @NonNull public java.util.Set<java.lang.String> getWhitelistedRestrictedPermissions();
}

View File

@@ -333,17 +333,6 @@ public class PackageInstaller {
@SystemApi
public static final String EXTRA_DATA_LOADER_TYPE = "android.content.pm.extra.DATA_LOADER_TYPE";
/**
* Path to the validated base APK for this session, which may point at an
* APK inside the session (when the session defines the base), or it may
* point at the existing base APK (when adding splits to an existing app).
*
* @hide
*/
@SystemApi
public static final String EXTRA_RESOLVED_BASE_PATH =
"android.content.pm.extra.RESOLVED_BASE_PATH";
/**
* Streaming installation pending.
* Caller should make sure DataLoader is able to prepare image and reinitiate the operation.
@@ -3549,6 +3538,19 @@ public class PackageInstaller {
return referrerUri;
}
/**
* @return the path to the validated base APK for this session, which may point at an
* APK inside the session (when the session defines the base), or it may
* point at the existing base APK (when adding splits to an existing app).
*
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.READ_INSTALLED_SESSION_PATHS)
public @Nullable String getResolvedBaseApkPath() {
return resolvedBaseCodePath;
}
/**
* Get the value set in {@link SessionParams#setGrantedRuntimePermissions(String[])}.
*

View File

@@ -5421,6 +5421,15 @@
<permission android:name="android.permission.INSTALL_DPC_PACKAGES"
android:protectionLevel="signature|role" />
<!-- @SystemApi Allows an application to read resolved paths to the APKs (Base and any splits)
of a session based install.
<p>Not for use by third-party applications.
@hide
-->
<permission android:name="android.permission.READ_INSTALLED_SESSION_PATHS"
android:protectionLevel="signature|installer" />
<uses-permission android:name="android.permission.READ_INSTALLED_SESSION_PATHS" />
<!-- Allows an application to use System Data Loaders.
<p>Not for use by third-party applications.
@hide

View File

@@ -9,6 +9,7 @@
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
<uses-permission android:name="android.permission.READ_INSTALL_SESSIONS" />
<uses-permission android:name="android.permission.READ_INSTALLED_SESSION_PATHS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS" />
<uses-permission android:name="android.permission.USE_RESERVED_DISK" />

View File

@@ -374,16 +374,15 @@ public class PackageInstallerActivity extends AlertActivity {
final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID,
-1 /* defaultValue */);
final SessionInfo info = mInstaller.getSessionInfo(sessionId);
final String resolvedBaseCodePath = intent.getStringExtra(
PackageInstaller.EXTRA_RESOLVED_BASE_PATH);
if (info == null || !info.isSealed() || resolvedBaseCodePath == null) {
String resolvedPath = info.getResolvedBaseApkPath();
if (info == null || !info.isSealed() || resolvedPath == null) {
Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
finish();
return;
}
mSessionId = sessionId;
packageSource = Uri.fromFile(new File(resolvedBaseCodePath));
packageSource = Uri.fromFile(new File(resolvedPath));
mOriginatingURI = null;
mReferrerURI = null;
mPendingUserActionReason = info.getPendingUserActionReason();

View File

@@ -830,6 +830,9 @@
<uses-permission android:name="android.permission.LOG_FOREGROUND_RESOURCE_USE"/>
<!-- Permission required for CTS test - CtsPackageInstallTestCases -->
<uses-permission android:name="android.permission.READ_INSTALLED_SESSION_PATHS" />
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"

View File

@@ -1135,8 +1135,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
info.userId = userId;
info.installerPackageName = mInstallSource.mInstallerPackageName;
info.installerAttributionTag = mInstallSource.mInstallerAttributionTag;
info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?
mResolvedBaseFile.getAbsolutePath() : null;
if (mContext.checkCallingOrSelfPermission(
Manifest.permission.READ_INSTALLED_SESSION_PATHS)
== PackageManager.PERMISSION_GRANTED && mResolvedBaseFile != null) {
info.resolvedBaseCodePath = mResolvedBaseFile.getAbsolutePath();
} else {
info.resolvedBaseCodePath = null;
}
info.progress = progress;
info.sealed = mSealed;
info.isCommitted = isCommitted();
@@ -2763,11 +2768,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
: PackageInstaller.ACTION_CONFIRM_INSTALL);
intent.setPackage(mPm.getPackageInstallerPackageName());
intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
synchronized (mLock) {
intent.putExtra(PackageInstaller.EXTRA_RESOLVED_BASE_PATH,
mResolvedBaseFile != null ? mResolvedBaseFile.getAbsolutePath() : null);
}
sendOnUserActionRequired(mContext, target, sessionId, intent);
}