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

Commit ee21507c authored by Dipankar Bhardwaj's avatar Dipankar Bhardwaj Committed by Android Build Coastguard Worker
Browse files

Restrict access to directories

Restricted access to Android/data, Android/obb and Android/sandbox
directories and its sub-directories. Replacing path's pattern match
check with file equality check.

Test: atest DocumentsClientTest
Bug: 341680936
Flag: EXEMPT bug fix
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:9ed668bfb50e62697059d8802cfe02f1f06ff74f)
Merged-In: I8879900e57e1702d11797b81e86d0cc3f55bac22
Change-Id: I8879900e57e1702d11797b81e86d0cc3f55bac22
parent a885c9b9
Loading
Loading
Loading
Loading
+68 −11
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.externalstorage;

import static java.util.regex.Pattern.CASE_INSENSITIVE;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.usage.StorageStatsManager;
@@ -61,12 +59,15 @@ import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.UUID;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * Presents content of the shared (a.k.a. "external") storage.
@@ -89,12 +90,9 @@ public class ExternalStorageProvider extends FileSystemProvider {
    private static final Uri BASE_URI =
            new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build();

    /**
     * Regex for detecting {@code /Android/data/}, {@code /Android/obb/} and
     * {@code /Android/sandbox/} along with all their subdirectories and content.
     */
    private static final Pattern PATTERN_RESTRICTED_ANDROID_SUBTREES =
            Pattern.compile("^Android/(?:data|obb|sandbox)(?:/.+)?", CASE_INSENSITIVE);
    private static final String PRIMARY_EMULATED_STORAGE_PATH = "/storage/emulated/";

    private static final String STORAGE_PATH = "/storage/";

    private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
            Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
@@ -308,10 +306,69 @@ public class ExternalStorageProvider extends FileSystemProvider {
            return false;
        }

        final String path = getPathFromDocId(documentId);
        return PATTERN_RESTRICTED_ANDROID_SUBTREES.matcher(path).matches();
        try {
            final RootInfo root = getRootFromDocId(documentId);
            final String canonicalPath = getPathFromDocId(documentId);
            return isRestrictedPath(root.rootId, canonicalPath);
        } catch (Exception e) {
            return true;
        }
    }

    /**
     * Based on the given root id and path, we restrict path access if file is Android/data or
     * Android/obb or Android/sandbox or one of their subdirectories.
     *
     * @param canonicalPath of the file
     * @return true if path is restricted
     */
    private boolean isRestrictedPath(String rootId, String canonicalPath) {
        if (rootId == null || canonicalPath == null) {
            return true;
        }

        final String rootPath;
        if (rootId.equalsIgnoreCase(ROOT_ID_PRIMARY_EMULATED)) {
            // Creates "/storage/emulated/<user-id>"
            rootPath = PRIMARY_EMULATED_STORAGE_PATH + UserHandle.myUserId();
        } else {
            // Creates "/storage/<volume-uuid>"
            rootPath = STORAGE_PATH + rootId;
        }
        List<java.nio.file.Path> restrictedPathList = Arrays.asList(
                Paths.get(rootPath, "Android", "data"),
                Paths.get(rootPath, "Android", "obb"),
                Paths.get(rootPath, "Android", "sandbox"));
        // We need to identify restricted parent paths which actually exist on the device
        List<java.nio.file.Path> validRestrictedPathsToCheck = restrictedPathList.stream().filter(
                Files::exists).collect(Collectors.toList());

        boolean isRestricted = false;
        java.nio.file.Path filePathToCheck = Paths.get(rootPath, canonicalPath);
        try {
            while (filePathToCheck != null) {
                for (java.nio.file.Path restrictedPath : validRestrictedPathsToCheck) {
                    if (Files.isSameFile(restrictedPath, filePathToCheck)) {
                        isRestricted = true;
                        Log.v(TAG, "Restricting access for path: " + filePathToCheck);
                        break;
                    }
                }
                if (isRestricted) {
                    break;
                }

                filePathToCheck = filePathToCheck.getParent();
            }
        } catch (Exception e) {
            Log.w(TAG, "Error in checking file equality check.", e);
            isRestricted = true;
        }

        return isRestricted;
    }


    /**
     * Check that the directory is the root of storage or blocked file from tree.
     * <p>