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

Commit 8bbb815a authored by Nikita Ioffe's avatar Nikita Ioffe
Browse files

Don't allow downgrade of apexes unless INSTALL_ALLOW_DOWNGRADE is set

Extracted check on whenever downgrades are allowed to
PackageManagerServiceUtils# isDowngradePermitteisDowngradePermitted
method to share it between PackageManagerService and StagingManager.

E2E test will happen in a follow-up CL. Need to refactor
ApexE2EBaseHostTest a bit before.

Bug: 124859257
Test: manually tried to downgrade using adb install with and without -d
Change-Id: Iaf0110965a4e7a82ae07f708368807aaac8cc4ec
parent 750c23d5
Loading
Loading
Loading
Loading
+2 −21
Original line number Diff line number Diff line
@@ -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) {
+31 −0
Original line number Diff line number Diff line
@@ -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.
     *
+30 −4
Original line number Diff line number Diff line
@@ -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;
        }