summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCarlos Valdivia <carlosvaldivia@google.com>2015-07-30 21:58:16 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2015-07-30 21:58:16 +0000
commit26ae600b5fb8c657b4f8dc2700374c701c9e50ec (patch)
tree164719bcf981c5d61897d7ba1fa8cd704a4b95db
parent7676e403bd65435eb3d2f34e7e0174fd37f6e818 (diff)
parente7ed827a104ba005b93faa2edb3bc77f72b240ec (diff)
downloadframeworks_base-26ae600b5fb8c657b4f8dc2700374c701c9e50ec.zip
frameworks_base-26ae600b5fb8c657b4f8dc2700374c701c9e50ec.tar.gz
frameworks_base-26ae600b5fb8c657b4f8dc2700374c701c9e50ec.tar.bz2
Merge "Permissions: GET_ACCOUNTS permission cleanup" into mnc-dev
-rw-r--r--core/java/android/accounts/AbstractAccountAuthenticator.java23
-rw-r--r--core/java/android/accounts/AccountManager.java53
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java250
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,