DO NOT MERGE - Full (local) restore security changes
(1) Prevent full restore from creating files/directories that are
accessible by other applications
(2) Don't restore filesets from "system" packages; i.e. any that runs
as a special uid, unless they define their own agent for handling
the restore process.
Bug 7168284
This is a cherry-pick from the originating tree.
Change-Id: I9f39ada3c4c3b7ee63330b015e62745e84ccb58f
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index d7f1c9f..f859599 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -64,7 +64,9 @@
/**
* Copy data from a socket to the given File location on permanent storage. The
- * modification time and access mode of the resulting file will be set if desired.
+ * modification time and access mode of the resulting file will be set if desired,
+ * although group/all rwx modes will be stripped: the restored file will not be
+ * accessible from outside the target application even if the original file was.
* If the {@code type} parameter indicates that the result should be a directory,
* the socket parameter may be {@code null}; even if it is valid, no data will be
* read from it in this case.
@@ -79,8 +81,9 @@
* @param type Must be either {@link BackupAgent#TYPE_FILE} for ordinary file data
* or {@link BackupAgent#TYPE_DIRECTORY} for a directory.
* @param mode Unix-style file mode (as used by the chmod(2) syscall) to be set on
- * the output file or directory. If this parameter is negative then neither
- * the mode nor the mtime parameters will be used.
+ * the output file or directory. group/all rwx modes are stripped even if set
+ * in this parameter. If this parameter is negative then neither
+ * the mode nor the mtime values will be applied to the restored file.
* @param mtime A timestamp in the standard Unix epoch that will be imposed as the
* last modification time of the output file. if the {@code mode} parameter is
* negative then this parameter will be ignored.
@@ -105,8 +108,6 @@
if (!parent.exists()) {
// in practice this will only be for the default semantic directories,
// and using the default mode for those is appropriate.
- // TODO: support the edge case of apps that have adjusted the
- // permissions on these core directories
parent.mkdirs();
}
out = new FileOutputStream(outFile);
@@ -146,6 +147,8 @@
// Now twiddle the state to match the backup, assuming all went well
if (mode >= 0 && outFile != null) {
try {
+ // explicitly prevent emplacement of files accessible by outside apps
+ mode &= 0700;
Libcore.os.chmod(outFile.getPath(), (int)mode);
} catch (ErrnoException e) {
e.rethrowAsIOException();
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 2167c49..1f3f172b 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -2450,6 +2450,21 @@
}
}
+ // Cull any packages that run as system-domain uids but do not define their
+ // own backup agents
+ for (int i = 0; i < packagesToBackup.size(); ) {
+ PackageInfo pkg = packagesToBackup.get(i);
+ if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
+ && (pkg.applicationInfo.backupAgentName == null)) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "... ignoring non-agent system package " + pkg.packageName);
+ }
+ packagesToBackup.remove(i);
+ } else {
+ i++;
+ }
+ }
+
FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor());
OutputStream out = null;
@@ -3664,29 +3679,37 @@
// 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;
+ // Restore system-uid-space packages only if they have
+ // defined a custom backup agent
+ if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
+ || (pkgInfo.applicationInfo.backupAgentName != null)) {
+ // 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 {
- Slog.w(TAG, "Restore manifest signatures do not match "
- + "installed application for " + info.packageName);
+ Slog.w(TAG, "Package " + info.packageName
+ + " is system level with no agent");
}
} else {
if (DEBUG) Slog.i(TAG, "Restore manifest from "