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
This commit is contained in:
Christopher Tate
2011-06-03 12:27:51 -07:00
parent 507fc54924
commit a858cb075d
2 changed files with 119 additions and 22 deletions

View File

@ -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,38 +2528,48 @@ 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 {
// 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
// check for packages installed on the system partition, because
// 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");
policy = RestorePolicy.ACCEPT;
// 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
// check for packages installed on the system partition, because
// such packages are signed with the platform cert instead of
// the app developer's cert, so they're different on every
// device.
if (signaturesMatch(sigs, pkgInfo)) {
if (pkgInfo.versionCode >= version) {
Slog.i(TAG, "Sig + version match; taking data");
policy = RestorePolicy.ACCEPT;
} else {
// The data is from a newer version of the app than
// is presently installed. That means we can only
// use it if the matching apk is also supplied.
Slog.d(TAG, "Data version " + version
+ " is newer than installed version "
+ pkgInfo.versionCode + " - requiring apk");
policy = RestorePolicy.ACCEPT_IF_APK;
}
} else {
// The data is from a newer version of the app than
// is presently installed. That means we can only
// use it if the matching apk is also supplied.
Slog.d(TAG, "Data version " + version
+ " is newer than installed version "
+ pkgInfo.versionCode + " - requiring apk");
policy = RestorePolicy.ACCEPT_IF_APK;
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