Merge "Don't allow downgrade of apexes unless INSTALL_ALLOW_DOWNGRADE is set"

This commit is contained in:
TreeHugger Robot
2019-02-27 22:41:03 +00:00
committed by Android (Google) Code Review
3 changed files with 63 additions and 25 deletions

View File

@@ -14346,27 +14346,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (dataOwnerPkg != null) {
// If installed, the package will get access to data left on the device by its
// predecessor. As a security measure, this is permited only if this is not a
// version downgrade or if the predecessor package is marked as debuggable and
// a downgrade is explicitly requested.
//
// On debuggable platform builds, downgrades are permitted even for
// non-debuggable packages to make testing easier. Debuggable platform builds do
// not offer security guarantees and thus it's OK to disable some security
// mechanisms to make debugging/testing easier on those builds. However, even on
// debuggable builds downgrades of packages are permitted only if requested via
// installFlags. This is because we aim to keep the behavior of debuggable
// platform builds as close as possible to the behavior of non-debuggable
// platform builds.
final boolean downgradeRequested =
(installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) != 0;
final boolean packageDebuggable =
(dataOwnerPkg.applicationInfo.flags
& ApplicationInfo.FLAG_DEBUGGABLE) != 0;
final boolean downgradePermitted =
(downgradeRequested) && ((Build.IS_DEBUGGABLE) || (packageDebuggable));
if (!downgradePermitted) {
if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
dataOwnerPkg.applicationInfo.flags)) {
try {
checkDowngrade(dataOwnerPkg, pkgLite);
} catch (PackageManagerException e) {

View File

@@ -32,6 +32,7 @@ import android.annotation.Nullable;
import android.app.AppGlobals;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
@@ -795,6 +796,36 @@ public class PackageManagerServiceUtils {
}
}
/**
* Checks whenever downgrade of an app is permitted.
*
* @param installFlags flags of the current install.
* @param applicationFlags flags of the currently installed version of the app.
* @return {@code true} if downgrade is permitted according to the {@code installFlags} and
* {@code applicationFlags}.
*/
public static boolean isDowngradePermitted(int installFlags, int applicationFlags) {
// If installed, the package will get access to data left on the device by its
// predecessor. As a security measure, this is permited only if this is not a
// version downgrade or if the predecessor package is marked as debuggable and
// a downgrade is explicitly requested.
//
// On debuggable platform builds, downgrades are permitted even for
// non-debuggable packages to make testing easier. Debuggable platform builds do
// not offer security guarantees and thus it's OK to disable some security
// mechanisms to make debugging/testing easier on those builds. However, even on
// debuggable builds downgrades of packages are permitted only if requested via
// installFlags. This is because we aim to keep the behavior of debuggable
// platform builds as close as possible to the behavior of non-debuggable
// platform builds.
final boolean downgradeRequested =
(installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) != 0;
final boolean packageDebuggable =
(applicationFlags
& ApplicationInfo.FLAG_DEBUGGABLE) != 0;
return (downgradeRequested) && ((Build.IS_DEBUGGABLE) || (packageDebuggable));
}
/**
* Copy package to the target location.
*

View File

@@ -144,12 +144,40 @@ public class StagingManager {
private boolean submitSessionToApexService(@NonNull PackageInstallerSession session,
List<PackageInstallerSession> childSessions,
ApexInfoList apexInfoList) {
return mApexManager.submitStagedSession(
boolean submittedToApexd = mApexManager.submitStagedSession(
session.sessionId,
childSessions != null
? childSessions.stream().mapToInt(s -> s.sessionId).toArray() :
new int[]{},
apexInfoList);
if (!submittedToApexd) {
session.setStagedSessionFailed(
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
"APEX staging failed, check logcat messages from apexd for more details.");
return false;
}
for (ApexInfo newPackage : apexInfoList.apexInfos) {
PackageInfo activePackage = mApexManager.getActivePackage(newPackage.packageName);
if (activePackage == null) {
continue;
}
long activeVersion = activePackage.applicationInfo.longVersionCode;
boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
session.params.installFlags, activePackage.applicationInfo.flags);
if (activeVersion > newPackage.versionCode && !allowsDowngrade) {
session.setStagedSessionFailed(
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
"Downgrade of APEX package " + newPackage.packageName
+ " is not allowed. Active version: " + activeVersion
+ " attempted: " + newPackage.versionCode);
if (!mApexManager.abortActiveSession()) {
Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
}
return false;
}
}
return true;
}
private static boolean isApexSession(@NonNull PackageInstallerSession session) {
@@ -184,9 +212,7 @@ public class StagingManager {
}
if (!success) {
session.setStagedSessionFailed(
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
"APEX staging failed, check logcat messages from apexd for more details.");
// submitSessionToApexService will populate error.
return;
}