Validate restored file paths against their nominal domain

Bug 8460775

(cherry picked from commit 7323765bbf13d9638cf2cc1e06113bffcdac46c4)

Change-Id: I6710503799a6df2de142a827513d5b4c107b6ec9
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 9ad33a5..0e835ed 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -440,21 +440,31 @@
             basePath = getCacheDir().getCanonicalPath();
         } else {
             // Not a supported location
-            Log.i(TAG, "Data restored from non-app domain " + domain + ", ignoring");
+            Log.i(TAG, "Unrecognized domain " + domain);
         }
 
         // Now that we've figured out where the data goes, send it on its way
         if (basePath != null) {
+            // Canonicalize the nominal path and verify that it lies within the stated domain
             File outFile = new File(basePath, path);
-            if (DEBUG) Log.i(TAG, "[" + domain + " : " + path + "] mapped to " + outFile.getPath());
-            onRestoreFile(data, size, outFile, type, mode, mtime);
-        } else {
-            // Not a supported output location?  We need to consume the data
-            // anyway, so just use the default "copy the data out" implementation
-            // with a null destination.
-            if (DEBUG) Log.i(TAG, "[ skipping data from unsupported domain " + domain + "]");
-            FullBackup.restoreFile(data, size, type, mode, mtime, null);
+            String outPath = outFile.getCanonicalPath();
+            if (outPath.startsWith(basePath + File.separatorChar)) {
+                if (DEBUG) Log.i(TAG, "[" + domain + " : " + path + "] mapped to " + outPath);
+                onRestoreFile(data, size, outFile, type, mode, mtime);
+                return;
+            } else {
+                // Attempt to restore to a path outside the file's nominal domain.
+                if (DEBUG) {
+                    Log.e(TAG, "Cross-domain restore attempt: " + outPath);
+                }
+            }
         }
+
+        // Not a supported output location, or bad path:  we need to consume the data
+        // anyway, so just use the default "copy the data out" implementation
+        // with a null destination.
+        if (DEBUG) Log.i(TAG, "[ skipping file " + path + "]");
+        FullBackup.restoreFile(data, size, type, mode, mtime, null);
     }
 
     // ----- Core implementation -----