[DO NOT MERGE] Enforce user separation on external storage With WRITE_MEDIA_STORAGE permission, MediaProvider can access external storage across users. Access should be rejected for content in other user's external storage if the caller has no WRITE_MEDIA_STORAGE/INTERACT_ACROSS_USERS Test: manual + android.provider.cts.MediaStore_FilesTest Bug: 34114752 Change-Id: I577cc33574185657592246c1103df4174ff3acf6 (cherry picked from commit cc1cecc9be51f9655d9acdf1729345d16f078c21)
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java index e181e59..1ff9cd3 100644 --- a/src/com/android/providers/media/MediaProvider.java +++ b/src/com/android/providers/media/MediaProvider.java
@@ -17,6 +17,7 @@ package com.android.providers.media; import static android.Manifest.permission.ACCESS_CACHE_FILESYSTEM; +import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; import static android.Manifest.permission.WRITE_MEDIA_STORAGE; @@ -42,6 +43,7 @@ import android.content.UriMatcher; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.UserInfo; import android.content.res.Resources; import android.database.Cursor; import android.database.DatabaseUtils; @@ -62,6 +64,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Environment; +import android.os.FileUtils; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; @@ -69,6 +72,8 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; +import android.os.UserHandle; +import android.os.UserManager; import android.os.storage.StorageManager; import android.os.storage.StorageVolume; import android.os.storage.VolumeInfo; @@ -135,7 +140,6 @@ private void updateStoragePaths() { mExternalStoragePaths = mStorageManager.getVolumePaths(); - try { mExternalPath = Environment.getExternalStorageDirectory().getCanonicalPath() + File.separator; @@ -4875,10 +4879,47 @@ // don't write to non-cache, non-sdcard files. throw new FileNotFoundException("Can't access " + file); } else { + boolean hasWriteMediaStorage = c.checkCallingOrSelfPermission(WRITE_MEDIA_STORAGE) + == PackageManager.PERMISSION_GRANTED; + boolean hasInteractAcrossUsers = c.checkCallingOrSelfPermission(INTERACT_ACROSS_USERS) + == PackageManager.PERMISSION_GRANTED; + if (!hasWriteMediaStorage && !hasInteractAcrossUsers && isOtherUserExternalDir(path)) { + throw new FileNotFoundException("Can't access across users " + file); + } checkWorldReadAccess(path); } } + private boolean isOtherUserExternalDir(String path) { + List<VolumeInfo> volumes = mStorageManager.getVolumes(); + for (VolumeInfo volume : volumes) { + if (contains(volume.path, path)) { + // If any of mExternalStoragePaths belongs to this volume and doesn't contain + // the path, then we consider the path to be from another user + for (String externalStoragePath : mExternalStoragePaths) { + if (contains(volume.path, externalStoragePath) + && !contains(externalStoragePath, path)) { + return true; + } + } + } + } + return false; + } + + private static boolean contains(String dirPath, String filePath) { + if (dirPath== null || filePath == null) return false; + + if (dirPath.equals(filePath)) { + return true; + } + + if (!dirPath.endsWith("/")) { + dirPath += "/"; + } + return filePath.startsWith(dirPath); + } + private boolean isSecondaryExternalPath(String path) { for (int i = 1; i < mExternalStoragePaths.length; i++) { if (path.startsWith(mExternalStoragePaths[i])) {