diff options
author | Carlos Valdivia <carlosvaldivia@google.com> | 2015-07-30 22:07:31 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2015-07-30 22:07:31 +0000 |
commit | e195d179490754bc9358afe8ba74ecc38aed2360 (patch) | |
tree | 55b20613521b5f038654b133362f0aca1e4ff401 | |
parent | 3991bea8ae768bdb7014deba2ad9e564cdea9ebd (diff) | |
parent | 26ae600b5fb8c657b4f8dc2700374c701c9e50ec (diff) | |
download | frameworks_base-e195d179490754bc9358afe8ba74ecc38aed2360.zip frameworks_base-e195d179490754bc9358afe8ba74ecc38aed2360.tar.gz frameworks_base-e195d179490754bc9358afe8ba74ecc38aed2360.tar.bz2 |
am 26ae600b: Merge "Permissions: GET_ACCOUNTS permission cleanup" into mnc-dev
* commit '26ae600b5fb8c657b4f8dc2700374c701c9e50ec':
Permissions: GET_ACCOUNTS permission cleanup
3 files changed, 199 insertions, 127 deletions
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java index 3e4a66d..9c401c7 100644 --- a/core/java/android/accounts/AbstractAccountAuthenticator.java +++ b/core/java/android/accounts/AbstractAccountAuthenticator.java @@ -138,7 +138,9 @@ public abstract class AbstractAccountAuthenticator { new AccountAuthenticatorResponse(response), accountType, authTokenType, features, options); if (Log.isLoggable(TAG, Log.VERBOSE)) { - result.keySet(); // force it to be unparcelled + if (result != null) { + result.keySet(); // force it to be unparcelled + } Log.v(TAG, "addAccount: result " + AccountManager.sanitizeResult(result)); } if (result != null) { @@ -160,7 +162,9 @@ public abstract class AbstractAccountAuthenticator { final Bundle result = AbstractAccountAuthenticator.this.confirmCredentials( new AccountAuthenticatorResponse(response), account, options); if (Log.isLoggable(TAG, Log.VERBOSE)) { - result.keySet(); // force it to be unparcelled + if (result != null) { + result.keySet(); // force it to be unparcelled + } Log.v(TAG, "confirmCredentials: result " + AccountManager.sanitizeResult(result)); } @@ -185,7 +189,9 @@ public abstract class AbstractAccountAuthenticator { result.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, AbstractAccountAuthenticator.this.getAuthTokenLabel(authTokenType)); if (Log.isLoggable(TAG, Log.VERBOSE)) { - result.keySet(); // force it to be unparcelled + if (result != null) { + result.keySet(); // force it to be unparcelled + } Log.v(TAG, "getAuthTokenLabel: result " + AccountManager.sanitizeResult(result)); } @@ -209,7 +215,9 @@ public abstract class AbstractAccountAuthenticator { new AccountAuthenticatorResponse(response), account, authTokenType, loginOptions); if (Log.isLoggable(TAG, Log.VERBOSE)) { - result.keySet(); // force it to be unparcelled + if (result != null) { + result.keySet(); // force it to be unparcelled + } Log.v(TAG, "getAuthToken: result " + AccountManager.sanitizeResult(result)); } if (result != null) { @@ -234,7 +242,10 @@ public abstract class AbstractAccountAuthenticator { new AccountAuthenticatorResponse(response), account, authTokenType, loginOptions); if (Log.isLoggable(TAG, Log.VERBOSE)) { - result.keySet(); // force it to be unparcelled + // Result may be null. + if (result != null) { + result.keySet(); // force it to be unparcelled + } Log.v(TAG, "updateCredentials: result " + AccountManager.sanitizeResult(result)); } @@ -490,7 +501,7 @@ public abstract class AbstractAccountAuthenticator { * <ul> * <li> {@link AccountManager#KEY_INTENT}, or * <li> {@link AccountManager#KEY_ACCOUNT_NAME} and {@link AccountManager#KEY_ACCOUNT_TYPE} of - * the account that was added, or + * the account whose credentials were updated, or * <li> {@link AccountManager#KEY_ERROR_CODE} and {@link AccountManager#KEY_ERROR_MESSAGE} to * indicate an error * </ul> diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 9394d2c..8c84b4d 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -333,7 +333,7 @@ public class AccountManager { try { return mService.getPassword(account); } catch (RemoteException e) { - // will never happen + // won't ever happen throw new RuntimeException(e); } } @@ -362,7 +362,7 @@ public class AccountManager { try { return mService.getUserData(account, key); } catch (RemoteException e) { - // will never happen + // won't ever happen throw new RuntimeException(e); } } @@ -415,8 +415,10 @@ public class AccountManager { * * <p>It is safe to call this method from the main thread. * - * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#GET_ACCOUNTS}. + * <p>Clients of this method that have not been granted the + * {@link android.Manifest.permission#GET_ACCOUNTS} permission, + * will only see those accounts managed by AbstractAccountAuthenticators whose + * signature matches the client. * * @return An array of {@link Account}, one for each account. Empty * (never null) if no accounts have been added. @@ -438,8 +440,10 @@ public class AccountManager { * * <p>It is safe to call this method from the main thread. * - * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#GET_ACCOUNTS}. + * <p>Clients of this method that have not been granted the + * {@link android.Manifest.permission#GET_ACCOUNTS} permission, + * will only see those accounts managed by AbstractAccountAuthenticators whose + * signature matches the client. * * @return An array of {@link Account}, one for each account. Empty * (never null) if no accounts have been added. @@ -466,7 +470,7 @@ public class AccountManager { try { return mService.getAccountsForPackage(packageName, uid); } catch (RemoteException re) { - // possible security exception + // won't ever happen throw new RuntimeException(re); } } @@ -483,7 +487,7 @@ public class AccountManager { try { return mService.getAccountsByTypeForPackage(type, packageName); } catch (RemoteException re) { - // possible security exception + // won't ever happen throw new RuntimeException(re); } } @@ -497,9 +501,10 @@ public class AccountManager { * * <p>It is safe to call this method from the main thread. * - * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#GET_ACCOUNTS} or share a uid with the - * authenticator that owns the account type. + * <p>Clients of this method that have not been granted the + * {@link android.Manifest.permission#GET_ACCOUNTS} permission, + * will only see those accounts managed by AbstractAccountAuthenticators whose + * signature matches the client. * * <p><b>NOTE:</b> If targeting your app to work on API level 22 and before, * GET_ACCOUNTS permission is needed for those platforms, irrespective of uid @@ -585,7 +590,8 @@ public class AccountManager { * {@link AccountManagerFuture} must not be used on the main thread. * * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#GET_ACCOUNTS}. + * {@link android.Manifest.permission#GET_ACCOUNTS} or be a signature + * match with the AbstractAccountAuthenticator that manages the account. * * @param account The {@link Account} to test * @param features An array of the account features to check @@ -628,9 +634,10 @@ public class AccountManager { * <p>This method may be called from any thread, but the returned * {@link AccountManagerFuture} must not be used on the main thread. * - * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#GET_ACCOUNTS} or share a uid with the - * authenticator that owns the account type. + * <p>Clients of this method that have not been granted the + * {@link android.Manifest.permission#GET_ACCOUNTS} permission, + * will only see those accounts managed by AbstractAccountAuthenticators whose + * signature matches the client. * * @param type The type of accounts to return, must not be null * @param features An array of the account features to require, @@ -701,7 +708,7 @@ public class AccountManager { try { return mService.addAccountExplicitly(account, password, userdata); } catch (RemoteException e) { - // won't ever happen + // Can happen if there was a SecurityException was thrown. throw new RuntimeException(e); } } @@ -966,7 +973,7 @@ public class AccountManager { try { return mService.removeAccountExplicitly(account); } catch (RemoteException e) { - // won't ever happen + // May happen if the caller doesn't match the signature of the authenticator. throw new RuntimeException(e); } } @@ -1114,7 +1121,7 @@ public class AccountManager { try { mService.setUserData(account, key, value); } catch (RemoteException e) { - // won't ever happen + // Will happen if there is not signature match. throw new RuntimeException(e); } } @@ -1733,7 +1740,7 @@ public class AccountManager { * with these fields if an activity was supplied and the account * credentials were successfully updated: * <ul> - * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created + * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account * </ul> * @@ -2501,10 +2508,12 @@ public class AccountManager { * listeners are added in an Activity or Service's {@link Activity#onCreate} * and removed in {@link Activity#onDestroy}. * - * <p>It is safe to call this method from the main thread. + * <p>The listener will only be informed of accounts that would be returned + * to the caller via {@link #getAccounts()}. Typically this means that to + * get any accounts, the caller will need to be grated the GET_ACCOUNTS + * permission. * - * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#GET_ACCOUNTS}. + * <p>It is safe to call this method from the main thread. * * @param listener The listener to send notifications to * @param handler {@link Handler} identifying the thread to use diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 32fd56a..83e8db0 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -527,14 +527,14 @@ public class AccountManagerService + ", pid " + Binder.getCallingPid()); } if (account == null) throw new IllegalArgumentException("account is null"); - if (!isAccountManagedByCaller(account.type, callingUid)) { + int userId = UserHandle.getCallingUserId(); + if (!isAccountManagedByCaller(account.type, callingUid, userId)) { String msg = String.format( "uid %s cannot get secrets for accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } - int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -627,14 +627,14 @@ public class AccountManagerService } if (account == null) throw new IllegalArgumentException("account is null"); if (key == null) throw new IllegalArgumentException("key is null"); - if (!isAccountManagedByCaller(account.type, callingUid)) { + int userId = UserHandle.getCallingUserId(); + if (!isAccountManagedByCaller(account.type, callingUid, userId)) { String msg = String.format( "uid %s cannot get user data for accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } - int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -664,22 +664,32 @@ public class AccountManagerService final long identityToken = clearCallingIdentity(); try { - Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>> - authenticatorCollection = mAuthenticatorCache.getAllServices(userId); - AuthenticatorDescription[] types = - new AuthenticatorDescription[authenticatorCollection.size()]; - int i = 0; - for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator - : authenticatorCollection) { - types[i] = authenticator.type; - i++; - } - return types; + return getAuthenticatorTypesInternal(userId); + } finally { restoreCallingIdentity(identityToken); } } + /** + * Should only be called inside of a clearCallingIdentity block. + */ + private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) { + Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>> + authenticatorCollection = mAuthenticatorCache.getAllServices(userId); + AuthenticatorDescription[] types = + new AuthenticatorDescription[authenticatorCollection.size()]; + int i = 0; + for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator + : authenticatorCollection) { + types[i] = authenticator.type; + i++; + } + return types; + } + + + private boolean isCrossUser(int callingUid, int userId) { return (userId != UserHandle.getCallingUserId() && callingUid != Process.myUid() @@ -697,7 +707,8 @@ public class AccountManagerService + ", pid " + Binder.getCallingPid()); } if (account == null) throw new IllegalArgumentException("account is null"); - if (!isAccountManagedByCaller(account.type, callingUid)) { + int userId = UserHandle.getCallingUserId(); + if (!isAccountManagedByCaller(account.type, callingUid, userId)) { String msg = String.format( "uid %s cannot explicitly add accounts of type: %s", callingUid, @@ -713,12 +724,10 @@ public class AccountManagerService */ // fails if the account already exists - int uid = getCallingUid(); - int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); - return addAccountInternal(accounts, account, password, extras, false, uid); + return addAccountInternal(accounts, account, password, extras, false, callingUid); } finally { restoreCallingIdentity(identityToken); } @@ -794,25 +803,26 @@ public class AccountManagerService if (account == null) { throw new IllegalArgumentException("account is null"); } - if (!isAccountManagedByCaller(account.type, callingUid)) { + int userId = UserHandle.getCallingUserId(); + if (!isAccountManagedByCaller(account.type, callingUid, userId)) { String msg = String.format( "uid %s cannot notify authentication for accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } - int userId = Binder.getCallingUserHandle().getIdentifier(); + if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) { return false; } - int user = UserHandle.getCallingUserId(); + long identityToken = clearCallingIdentity(); try { - UserAccounts accounts = getUserAccounts(user); + UserAccounts accounts = getUserAccounts(userId); + return updateLastAuthenticatedTime(account); } finally { restoreCallingIdentity(identityToken); } - return updateLastAuthenticatedTime(account); } private boolean updateLastAuthenticatedTime(Account account) { @@ -985,8 +995,9 @@ public class AccountManagerService if (response == null) throw new IllegalArgumentException("response is null"); if (account == null) throw new IllegalArgumentException("account is null"); if (features == null) throw new IllegalArgumentException("features is null"); - checkReadAccountsPermitted(callingUid, account.type); int userId = UserHandle.getCallingUserId(); + checkReadAccountsPermitted(callingUid, account.type, userId); + long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -1062,14 +1073,14 @@ public class AccountManagerService + ", pid " + Binder.getCallingPid()); } if (accountToRename == null) throw new IllegalArgumentException("account is null"); - if (!isAccountManagedByCaller(accountToRename.type, callingUid)) { + int userId = UserHandle.getCallingUserId(); + if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) { String msg = String.format( "uid %s cannot rename accounts of type: %s", callingUid, accountToRename.type); throw new SecurityException(msg); } - int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -1211,14 +1222,15 @@ public class AccountManagerService * authenticator. This will let users remove accounts (via Settings in the system) but not * arbitrary applications (like competing authenticators). */ - if (!isAccountManagedByCaller(account.type, callingUid) && !isSystemUid(callingUid)) { + UserHandle user = new UserHandle(userId); + if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier()) + && !isSystemUid(callingUid)) { String msg = String.format( "uid %s cannot remove accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } - if (!canUserModifyAccounts(userId)) { try { response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, @@ -1235,10 +1247,7 @@ public class AccountManagerService } return; } - - UserHandle user = new UserHandle(userId); long identityToken = clearCallingIdentity(); - UserAccounts accounts = getUserAccounts(userId); cancelNotification(getSigninRequiredNotificationId(accounts, account), user); synchronized(accounts.credentialsPermissionNotificationIds) { @@ -1268,6 +1277,7 @@ public class AccountManagerService + ", caller's uid " + callingUid + ", pid " + Binder.getCallingPid()); } + int userId = Binder.getCallingUserHandle().getIdentifier(); if (account == null) { /* * Null accounts should result in returning false, as per @@ -1275,22 +1285,18 @@ public class AccountManagerService */ Log.e(TAG, "account is null"); return false; - } else if (!isAccountManagedByCaller(account.type, callingUid)) { + } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) { String msg = String.format( "uid %s cannot explicitly add accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } - UserAccounts accounts = getUserAccountsForCaller(); - int userId = Binder.getCallingUserHandle().getIdentifier(); if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) { return false; } - logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS); - long identityToken = clearCallingIdentity(); try { return removeAccountInternal(accounts, account); @@ -1524,14 +1530,14 @@ public class AccountManagerService } if (account == null) throw new IllegalArgumentException("account is null"); if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); - if (!isAccountManagedByCaller(account.type, callingUid)) { + int userId = UserHandle.getCallingUserId(); + if (!isAccountManagedByCaller(account.type, callingUid, userId)) { String msg = String.format( "uid %s cannot peek the authtokens associated with accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } - int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -1552,14 +1558,14 @@ public class AccountManagerService } if (account == null) throw new IllegalArgumentException("account is null"); if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); - if (!isAccountManagedByCaller(account.type, callingUid)) { + int userId = UserHandle.getCallingUserId(); + if (!isAccountManagedByCaller(account.type, callingUid, userId)) { String msg = String.format( "uid %s cannot set auth tokens associated with accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } - int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -1578,14 +1584,14 @@ public class AccountManagerService + ", pid " + Binder.getCallingPid()); } if (account == null) throw new IllegalArgumentException("account is null"); - if (!isAccountManagedByCaller(account.type, callingUid)) { + int userId = UserHandle.getCallingUserId(); + if (!isAccountManagedByCaller(account.type, callingUid, userId)) { String msg = String.format( "uid %s cannot set secrets for accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } - int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -1642,14 +1648,14 @@ public class AccountManagerService + ", pid " + Binder.getCallingPid()); } if (account == null) throw new IllegalArgumentException("account is null"); - if (!isAccountManagedByCaller(account.type, callingUid)) { + int userId = UserHandle.getCallingUserId(); + if (!isAccountManagedByCaller(account.type, callingUid, userId)) { String msg = String.format( "uid %s cannot clear passwords for accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } - int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -1670,14 +1676,14 @@ public class AccountManagerService } if (key == null) throw new IllegalArgumentException("key is null"); if (account == null) throw new IllegalArgumentException("account is null"); - if (!isAccountManagedByCaller(account.type, callingUid)) { + int userId = UserHandle.getCallingUserId(); + if (!isAccountManagedByCaller(account.type, callingUid, userId)) { String msg = String.format( "uid %s cannot set user data for accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } - int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -1840,8 +1846,8 @@ public class AccountManagerService // skip the check if customTokens final int callerUid = Binder.getCallingUid(); - final boolean permissionGranted = customTokens || - permissionIsGranted(account, authTokenType, callerUid); + final boolean permissionGranted = + customTokens || permissionIsGranted(account, authTokenType, callerUid, userId); // Get the calling package. We will use it for the purpose of caching. final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME); @@ -2363,14 +2369,14 @@ public class AccountManagerService } if (response == null) throw new IllegalArgumentException("response is null"); if (accountType == null) throw new IllegalArgumentException("accountType is null"); - if (!isAccountManagedByCaller(accountType, callingUid) && !isSystemUid(callingUid)) { + int userId = UserHandle.getCallingUserId(); + if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) { String msg = String.format( "uid %s cannot edit authenticator properites for account type: %s", callingUid, accountType); throw new SecurityException(msg); } - int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -2493,20 +2499,22 @@ public class AccountManagerService } /** - * Returns the accounts for a specific user + * Returns the accounts visible to the client within the context of a specific user * @hide */ public Account[] getAccounts(int userId) { int callingUid = Binder.getCallingUid(); - if (!isReadAccountsPermitted(callingUid, null)) { + List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId); + if (visibleAccountTypes.isEmpty()) { return new Account[0]; } long identityToken = clearCallingIdentity(); try { - UserAccounts accounts = getUserAccounts(userId); - synchronized (accounts.cacheLock) { - return getAccountsFromCacheLocked(accounts, null, callingUid, null); - } + return getAccountsInternal( + userId, + callingUid, + null, // packageName + visibleAccountTypes); } finally { restoreCallingIdentity(identityToken); } @@ -2588,22 +2596,52 @@ public class AccountManagerService callingUid = packageUid; } - // Authenticators should be able to see their own accounts regardless of permissions. - if (TextUtils.isEmpty(type) && !isReadAccountsPermitted(callingUid, type)) { + List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId); + if (visibleAccountTypes.isEmpty() + || (type != null && !visibleAccountTypes.contains(type))) { return new Account[0]; - } + } else if (visibleAccountTypes.contains(type)) { + // Prune the list down to just the requested type. + visibleAccountTypes = new ArrayList<>(); + visibleAccountTypes.add(type); + } // else aggregate all the visible accounts (it won't matter if the list is empty). long identityToken = clearCallingIdentity(); try { - UserAccounts accounts = getUserAccounts(userId); - synchronized (accounts.cacheLock) { - return getAccountsFromCacheLocked(accounts, type, callingUid, callingPackage); - } + return getAccountsInternal( + userId, + callingUid, + callingPackage, + visibleAccountTypes); } finally { restoreCallingIdentity(identityToken); } } + private Account[] getAccountsInternal( + int userId, + int callingUid, + String callingPackage, + List<String> visibleAccountTypes) { + UserAccounts accounts = getUserAccounts(userId); + synchronized (accounts.cacheLock) { + UserAccounts userAccounts = getUserAccounts(userId); + ArrayList<Account> visibleAccounts = new ArrayList<>(); + for (String visibleType : visibleAccountTypes) { + Account[] accountsForType = getAccountsFromCacheLocked( + userAccounts, visibleType, callingUid, callingPackage); + if (accountsForType != null) { + visibleAccounts.addAll(Arrays.asList(accountsForType)); + } + } + Account[] result = new Account[visibleAccounts.size()]; + for (int i = 0; i < visibleAccounts.size(); i++) { + result[i] = visibleAccounts.get(i); + } + return result; + } + } + @Override public boolean addSharedAccountAsUser(Account account, int userId) { userId = handleIncomingUser(userId); @@ -2739,8 +2777,12 @@ public class AccountManagerService } if (response == null) throw new IllegalArgumentException("response is null"); if (type == null) throw new IllegalArgumentException("accountType is null"); - if (!isReadAccountsPermitted(callingUid, type)) { + int userId = UserHandle.getCallingUserId(); + + List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId); + if (!visibleAccountTypes.contains(type)) { Bundle result = new Bundle(); + // Need to return just the accounts that are from matching signatures. result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]); try { response.onResult(result); @@ -2749,7 +2791,6 @@ public class AccountManagerService } return; } - int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts userAccounts = getUserAccounts(userId); @@ -2763,7 +2804,11 @@ public class AccountManagerService onResult(response, result); return; } - new GetAccountsByTypeAndFeatureSession(userAccounts, response, type, features, + new GetAccountsByTypeAndFeatureSession( + userAccounts, + response, + type, + features, callingUid).bind(); } finally { restoreCallingIdentity(identityToken); @@ -3696,10 +3741,11 @@ public class AccountManagerService return false; } - private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) { + private boolean permissionIsGranted( + Account account, String authTokenType, int callerUid, int userId) { final boolean isPrivileged = isPrivileged(callerUid); final boolean fromAuthenticator = account != null - && isAccountManagedByCaller(account.type, callerUid); + && isAccountManagedByCaller(account.type, callerUid, userId); final boolean hasExplicitGrants = account != null && hasExplicitlyGrantedPermission(account, authTokenType, callerUid); if (Log.isLoggable(TAG, Log.VERBOSE)) { @@ -3711,23 +3757,45 @@ public class AccountManagerService return fromAuthenticator || hasExplicitGrants || isPrivileged; } - private boolean isAccountManagedByCaller(String accountType, int callingUid) { + private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId) { if (accountType == null) { return false; + } else { + return getTypesVisibleToCaller(callingUid, userId).contains(accountType); } - final int callingUserId = UserHandle.getUserId(callingUid); + } + + private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) { + if (accountType == null) { + return false; + } else { + return getTypesManagedByCaller(callingUid, userId).contains(accountType); + } + } + + private List<String> getTypesVisibleToCaller(int callingUid, int userId) { + boolean isPermitted = + isPermitted(callingUid, Manifest.permission.GET_ACCOUNTS, + Manifest.permission.GET_ACCOUNTS_PRIVILEGED); + Log.i(TAG, String.format("getTypesVisibleToCaller: isPermitted? %s", isPermitted)); + return getTypesForCaller(callingUid, userId, isPermitted); + } + + private List<String> getTypesManagedByCaller(int callingUid, int userId) { + return getTypesForCaller(callingUid, userId, false); + } + + private List<String> getTypesForCaller( + int callingUid, int userId, boolean isOtherwisePermitted) { + List<String> managedAccountTypes = new ArrayList<>(); for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo : - mAuthenticatorCache.getAllServices(callingUserId)) { - if (serviceInfo.type.type.equals(accountType)) { - /* - * We can't simply compare uids because uids can be recycled before the - * authenticator cache is updated. - */ - final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid); - return sigChk == PackageManager.SIGNATURE_MATCH; + mAuthenticatorCache.getAllServices(userId)) { + final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid); + if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) { + managedAccountTypes.add(serviceInfo.type.type); } } - return false; + return managedAccountTypes; } private boolean isAccountPresentForCaller(String accountName, String accountType) { @@ -3792,28 +3860,12 @@ public class AccountManagerService return false; } - private boolean isReadAccountsPermitted(int callingUid, String accountType) { - /* - * Settings app (which is in the same uid as AcocuntManagerService), apps with the - * GET_ACCOUNTS permission or authenticators that own the account type should be able to - * access accounts of the specified account. - */ - boolean isPermitted = - isPermitted(callingUid, Manifest.permission.GET_ACCOUNTS, - Manifest.permission.GET_ACCOUNTS_PRIVILEGED); - boolean isAccountManagedByCaller = isAccountManagedByCaller(accountType, callingUid); - Log.w(TAG, String.format( - "isReadAccountPermitted: isPermitted: %s, isAM: %s", - isPermitted, - isAccountManagedByCaller)); - return isPermitted || isAccountManagedByCaller; - } - /** Succeeds if any of the specified permissions are granted. */ private void checkReadAccountsPermitted( int callingUid, - String accountType) { - if (!isReadAccountsPermitted(callingUid, accountType)) { + String accountType, + int userId) { + if (!isAccountVisibleToCaller(accountType, callingUid, userId)) { String msg = String.format( "caller uid %s cannot access %s accounts", callingUid, |