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

Commit c1ebe670 authored by Rhed Jao's avatar Rhed Jao
Browse files

Enforce package visibility filtering on AccountManager APIs

Apps targeting Android U and above calling methods to update or
query account visibility need to declare package visibility needs
for the target package in the manifest. APIs listed below apply this
rule.
 - #getAccountVisibility
 - #getAccountsAndVisibilityForPackage
 - #setAccountVisibility
 - #addAccountExplicitlyWithVisibility

Bug: 154726397
Bug: 186229635
Test: atest CtsAccountManagerTestCases
Test: atest CtsAccountsHostTestCases
Test: atest CtsAppEnumerationTestCases
Change-Id: Ia40a3fb6025557f35e55fd26947dd1d89226f7f7
parent 991dd46e
Loading
Loading
Loading
Loading
+9 −3
Original line number Diff line number Diff line
@@ -1029,7 +1029,9 @@ public class AccountManager {
     * @param password The password to associate with the account, null for none
     * @param extras String values to use for the account's userdata, null for none
     * @param visibility Map from packageName to visibility values which will be set before account
     *        is added. See {@link #getAccountVisibility} for possible values.
     *        is added. See {@link #getAccountVisibility} for possible values. Declaring
     *        <a href="/training/basics/intents/package-visibility">package visibility</a> needs for
     *        package names in the map is needed, if the caller is targeting API level 34 and above.
     *
     * @return True if the account was successfully added, false if the account already exists, the
     *         account is null, or another error occurs.
@@ -1113,7 +1115,9 @@ public class AccountManager {
     * the specified account.
     *
     * @param account {@link Account} to update visibility
     * @param packageName Package name of the application to modify account visibility
     * @param packageName Package name of the application to modify account visibility. Declaring
     *        <a href="/training/basics/intents/package-visibility">package visibility</a> needs
     *        for it is needed, if the caller is targeting API level 34 and above.
     * @param visibility New visibility value
     *
     * @return True, if visibility value was successfully updated.
@@ -1145,7 +1149,9 @@ public class AccountManager {
     * @param account {@link Account} to get visibility
     * @param packageName Package name of the application to get account visibility
     *
     * @return int Visibility of given account.
     * @return int Visibility of given account. For the caller targeting API level 34 and above,
     * {@link #VISIBILITY_NOT_VISIBLE} is returned if the given package is filtered by the rules of
     * <a href="/training/basics/intents/package-visibility">package visibility</a>.
     */
    public @AccountVisibility int getAccountVisibility(Account account, String packageName) {
        if (account == null)
+50 −22
Original line number Diff line number Diff line
@@ -45,6 +45,9 @@ import android.app.PendingIntent;
import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ComponentName;
@@ -70,6 +73,7 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteFullException;
import android.database.sqlite.SQLiteStatement;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -196,6 +200,14 @@ public class AccountManagerService
    private static final int SIGNATURE_CHECK_MATCH = 1;
    private static final int SIGNATURE_CHECK_UID_MATCH = 2;

    /**
     * Apps targeting Android U and above need to declare the package visibility needs in the
     * manifest to access the AccountManager APIs.
     */
    @ChangeId
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
    private static final long ENFORCE_PACKAGE_VISIBILITY_FILTERING = 154726397;

    static {
        ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
        ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
@@ -527,7 +539,7 @@ public class AccountManagerService
     */
    private Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName,
            List<String> accountTypes, Integer callingUid, UserAccounts accounts) {
        if (!packageExistsForUser(packageName, accounts.userId)) {
        if (!canCallerAccessPackage(packageName, callingUid, accounts.userId)) {
            Log.w(TAG, "getAccountsAndVisibilityForPackage#Package not found " + packageName);
            return new LinkedHashMap<>();
        }
@@ -629,6 +641,9 @@ public class AccountManagerService
                   return AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE;
                }
            }
            if (!canCallerAccessPackage(packageName, callingUid, accounts.userId)) {
                return AccountManager.VISIBILITY_NOT_VISIBLE;
            }
            return resolveAccountVisibility(account, packageName, accounts);
        } finally {
            restoreCallingIdentity(identityToken);
@@ -779,7 +794,7 @@ public class AccountManagerService
        try {
            UserAccounts accounts = getUserAccounts(userId);
            return setAccountVisibility(account, packageName, newVisibility, true /* notify */,
                accounts);
                    accounts, callingUid);
        } finally {
            restoreCallingIdentity(identityToken);
        }
@@ -798,11 +813,12 @@ public class AccountManagerService
     * @param newVisibility New visibility calue
     * @param notify if the flag is set applications will get notification about visibility change
     * @param accounts UserAccount that currently hosts the account and application
     * @param callingUid The caller's uid.
     *
     * @return True if account visibility was changed.
     */
    private boolean setAccountVisibility(Account account, String packageName, int newVisibility,
            boolean notify, UserAccounts accounts) {
            boolean notify, UserAccounts accounts, int callingUid) {
        synchronized (accounts.dbLock) {
            synchronized (accounts.cacheLock) {
                Map<String, Integer> packagesToVisibility;
@@ -813,8 +829,8 @@ public class AccountManagerService
                                getRequestingPackages(account, accounts);
                        accountRemovedReceivers = getAccountRemovedReceivers(account, accounts);
                    } else {
                        if (!packageExistsForUser(packageName, accounts.userId)) {
                            return false; // package is not installed.
                        if (!canCallerAccessPackage(packageName, callingUid, accounts.userId)) {
                            return false; // package is not installed or not visible.
                        }
                        packagesToVisibility = new HashMap<>();
                        packagesToVisibility.put(packageName,
@@ -826,8 +842,8 @@ public class AccountManagerService
                    }
                } else {
                    // Notifications will not be send - only used during add account.
                    if (!isSpecialPackageKey(packageName) &&
                            !packageExistsForUser(packageName, accounts.userId)) {
                    if (!isSpecialPackageKey(packageName)
                            && !canCallerAccessPackage(packageName, callingUid, accounts.userId)) {
                        // package is not installed and not meta value.
                        return false;
                    }
@@ -1045,20 +1061,6 @@ public class AccountManagerService
        return (receivers != null && receivers.size() > 0);
    }

    private boolean packageExistsForUser(String packageName, int userId) {
        try {
            final long identityToken = clearCallingIdentity();
            try {
                mPackageManager.getPackageUidAsUser(packageName, userId);
                return true;
            } finally {
                restoreCallingIdentity(identityToken);
            }
        } catch (NameNotFoundException e) {
            return false;
        }
    }

    /**
     * Returns true if packageName is one of special values.
     */
@@ -1903,7 +1905,7 @@ public class AccountManagerService
                        for (Entry<String, Integer> entry : packageToVisibility.entrySet()) {
                            setAccountVisibility(account, entry.getKey() /* package */,
                                    entry.getValue() /* visibility */, false /* notify */,
                                    accounts);
                                    accounts, callingUid);
                        }
                    }
                    accounts.accountsDb.setTransactionSuccessful();
@@ -5915,6 +5917,32 @@ public class AccountManagerService
        return (dpmi != null) && (dpmi.isActiveProfileOwner(uid) || dpmi.isActiveDeviceOwner(uid));
    }

    /**
     * Filter the access to the target package by rules of the package visibility if the caller
     * targeting API level U and above. Otherwise, returns true if the package is installed on
     * the device.
     *
     * @param targetPkgName The package name to check.
     * @param callingUid The caller that is going to access the package.
     * @param userId The user ID where the target package resides.
     * @return true if the caller is able to access the package.
     */
    private boolean canCallerAccessPackage(@NonNull String targetPkgName, int callingUid,
            int userId) {
        final PackageManagerInternal pmInternal =
                LocalServices.getService(PackageManagerInternal.class);
        if (!CompatChanges.isChangeEnabled(ENFORCE_PACKAGE_VISIBILITY_FILTERING, callingUid)) {
            return pmInternal.getPackageUid(
                    targetPkgName, 0 /* flags */, userId) != Process.INVALID_UID;
        }
        final boolean canAccess = !pmInternal.filterAppAccess(targetPkgName, callingUid, userId);
        if (!canAccess && Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "Package " + targetPkgName + " is not visible to caller " + callingUid
                    + " for user " + userId);
        }
        return canAccess;
    }

    @Override
    public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
            throws RemoteException {