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

Commit a858cb07 authored by Christopher Tate's avatar Christopher Tate
Browse files

Respect android:allowBackup="false" during full backup & restore

Packages with this manifest attribute set 'false' will not be backed
up even through the "full device backup" infrastructure.  If someone
produces an apparent restore file with data for such an application,
it will not actually be restored onto the device.

When an apk is installed during the course of a restore operation,
it is validated against the manifest contents and deleted if there
is a mismatch.  Also, if the newly-installed app is found to
disallow backups, no file content will be processed for that app.

Bug 4532159

Change-Id: I59630054584b1394e567de939192e22e597044ee
parent 507fc549
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -21,7 +21,7 @@
    <uses-permission android:name="android.permission.BACKUP" />

    <application android:allowClearUserData="false"
                 android:killAfterRestore="false"
                 android:allowBackup="false"
                 android:permission="android.permission.CONFIRM_FULL_BACKUP" >

        <activity android:name=".BackupRestoreConfirmation" 
+118 −21
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageInstallObserver;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
@@ -1709,6 +1710,16 @@ class BackupManagerService extends IBackupManager.Stub {
                }
            }

            // Cull any packages that have indicated that backups are not permitted.
            for (int i = 0; i < packagesToBackup.size(); ) {
                PackageInfo info = packagesToBackup.get(i);
                if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
                    packagesToBackup.remove(i);
                } else {
                    i++;
                }
            }

            // Now back up the app data via the agent mechanism
            PackageInfo pkg = null;
            try {
@@ -1937,7 +1948,6 @@ class BackupManagerService extends IBackupManager.Stub {
            // Which packages we've already wiped data on.  We prepopulate this
            // with a whitelist of packages known to be unclearable.
            mClearedPackages.add("android");
            mClearedPackages.add("com.android.backupconfirm");
            mClearedPackages.add("com.android.providers.settings");
        }

@@ -2314,6 +2324,7 @@ class BackupManagerService extends IBackupManager.Stub {

        class RestoreInstallObserver extends IPackageInstallObserver.Stub {
            final AtomicBoolean mDone = new AtomicBoolean();
            String mPackageName;
            int mResult;

            public void reset() {
@@ -2341,12 +2352,45 @@ class BackupManagerService extends IBackupManager.Stub {
                    throws RemoteException {
                synchronized (mDone) {
                    mResult = returnCode;
                    mPackageName = packageName;
                    mDone.set(true);
                    mDone.notifyAll();
                }
            }
        }

        class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
            final AtomicBoolean mDone = new AtomicBoolean();
            int mResult;

            public void reset() {
                synchronized (mDone) {
                    mDone.set(false);
                }
            }

            public void waitForCompletion() {
                synchronized (mDone) {
                    while (mDone.get() == false) {
                        try {
                            mDone.wait();
                        } catch (InterruptedException e) { }
                    }
                }
            }

            @Override
            public void packageDeleted(String packageName, int returnCode) throws RemoteException {
                synchronized (mDone) {
                    mResult = returnCode;
                    mDone.set(true);
                    mDone.notifyAll();
                }
            }
        }

        final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
        final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();

        boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
            boolean okay = true;
@@ -2385,6 +2429,49 @@ class BackupManagerService extends IBackupManager.Stub {
                    if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
                        okay = false;
                    }
                } else {
                    // Okay, the install succeeded.  Make sure it was the right app.
                    boolean uninstall = false;
                    if (!mInstallObserver.mPackageName.equals(info.packageName)) {
                        Slog.w(TAG, "Restore stream claimed to include apk for "
                                + info.packageName + " but apk was really "
                                + mInstallObserver.mPackageName);
                        // delete the package we just put in place; it might be fraudulent
                        okay = false;
                        uninstall = true;
                    } else {
                        try {
                            PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
                                    PackageManager.GET_SIGNATURES);
                            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
                                Slog.w(TAG, "Restore stream contains apk of package "
                                        + info.packageName + " but it disallows backup/restore");
                                okay = false;
                            } else {
                                // So far so good -- do the signatures match the manifest?
                                Signature[] sigs = mManifestSignatures.get(info.packageName);
                                if (!signaturesMatch(sigs, pkg)) {
                                    Slog.w(TAG, "Installed app " + info.packageName
                                            + " signatures do not match restore manifest");
                                    okay = false;
                                    uninstall = true;
                                }
                            }
                        } catch (NameNotFoundException e) {
                            Slog.w(TAG, "Install of package " + info.packageName
                                    + " succeeded but now not found");
                            okay = false;
                        }
                    }

                    // If we're not okay at this point, we need to delete the package
                    // that we just installed.
                    if (uninstall) {
                        mDeleteObserver.reset();
                        mPackageManager.deletePackage(mInstallObserver.mPackageName,
                                mDeleteObserver, 0);
                        mDeleteObserver.waitForCompletion();
                    }
                }
            } catch (IOException e) {
                Slog.e(TAG, "Unable to transcribe restored apk for install");
@@ -2441,16 +2528,21 @@ class BackupManagerService extends IBackupManager.Stub {
                        boolean hasApk = str[0].equals("1");
                        offset = extractLine(buffer, offset, str);
                        int numSigs = Integer.parseInt(str[0]);
                        Signature[] sigs = null;
                        if (numSigs > 0) {
                            sigs = new Signature[numSigs];
                            Signature[] sigs = new Signature[numSigs];
                            for (int i = 0; i < numSigs; i++) {
                                offset = extractLine(buffer, offset, str);
                                sigs[i] = new Signature(str[0]);
                            }
                            mManifestSignatures.put(info.packageName, sigs);

                            // Okay, got the manifest info we need...
                            try {
                                PackageInfo pkgInfo = mPackageManager.getPackageInfo(
                                        info.packageName, PackageManager.GET_SIGNATURES);
                                // Fall through to IGNORE if the app explicitly disallows backup
                                final int flags = pkgInfo.applicationInfo.flags;
                                if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
                                    // Verify signatures against any installed version; if they
                                    // don't match, then we fall though and ignore the data.  The
                                    // signatureMatch() method explicitly ignores the signature
@@ -2458,8 +2550,6 @@ class BackupManagerService extends IBackupManager.Stub {
                                    // such packages are signed with the platform cert instead of
                                    // the app developer's cert, so they're different on every
                                    // device.
                                PackageInfo pkgInfo = mPackageManager.getPackageInfo(
                                        info.packageName, PackageManager.GET_SIGNATURES);
                                    if (signaturesMatch(sigs, pkgInfo)) {
                                        if (pkgInfo.versionCode >= version) {
                                            Slog.i(TAG, "Sig + version match; taking data");
@@ -2473,6 +2563,13 @@ class BackupManagerService extends IBackupManager.Stub {
                                                    + pkgInfo.versionCode + " - requiring apk");
                                            policy = RestorePolicy.ACCEPT_IF_APK;
                                        }
                                    } else {
                                        Slog.w(TAG, "Restore manifest signatures do not match "
                                                + "installed application for " + info.packageName);
                                    }
                                } else {
                                    if (DEBUG) Slog.i(TAG, "Restore manifest from "
                                            + info.packageName + " but allowBackup=false");
                                }
                            } catch (NameNotFoundException e) {
                                // Okay, the target app isn't installed.  We can process