Loading services/core/java/com/android/server/accounts/AccountManagerService.java +84 −4 Original line number Diff line number Diff line Loading @@ -1773,7 +1773,8 @@ public class AccountManagerService // Create a Session for the target user and pass in the bundle completeCloningAccount(response, result, account, toAccounts, userFrom); } else { super.onResult(result); // Bundle format is not defined. super.onResultSkipSanitization(result); } } }.bind(); Loading Loading @@ -1860,7 +1861,8 @@ public class AccountManagerService // account to avoid retries? // TODO: what we do with the visibility? super.onResult(result); // Bundle format is not defined. super.onResultSkipSanitization(result); } @Override Loading Loading @@ -2106,6 +2108,7 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); result = sanitizeBundle(result); IAccountManagerResponse response = getResponseAndClose(); if (response != null) { try { Loading Loading @@ -2456,6 +2459,7 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); result = sanitizeBundle(result); if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT) && !result.containsKey(AccountManager.KEY_INTENT)) { final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT); Loading Loading @@ -2970,6 +2974,7 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); result = sanitizeBundle(result); if (result != null) { String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL); Bundle bundle = new Bundle(); Loading Loading @@ -3147,6 +3152,7 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); result = sanitizeBundle(result); if (result != null) { if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) { Intent intent = newGrantCredentialsPermissionIntent( Loading Loading @@ -3618,6 +3624,12 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); Bundle sessionBundle = null; if (result != null) { // Session bundle will be removed from result. sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); } result = sanitizeBundle(result); mNumResults++; Intent intent = null; if (result != null) { Loading Loading @@ -3679,7 +3691,6 @@ public class AccountManagerService // bundle contains data necessary for finishing the session // later. The session bundle will be encrypted here and // decrypted later when trying to finish the session. Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); if (sessionBundle != null) { String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE); if (TextUtils.isEmpty(accountType) Loading Loading @@ -4067,6 +4078,7 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); result = sanitizeBundle(result); IAccountManagerResponse response = getResponseAndClose(); if (response == null) { return; Loading Loading @@ -4380,6 +4392,7 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); result = sanitizeBundle(result); mNumResults++; if (result == null) { onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle"); Loading Loading @@ -4936,6 +4949,68 @@ public class AccountManagerService callback, resultReceiver); } // All keys for Strings passed from AbstractAccountAuthenticator using Bundle. private static final String[] sStringBundleKeys = new String[] { AccountManager.KEY_ACCOUNT_NAME, AccountManager.KEY_ACCOUNT_TYPE, AccountManager.KEY_AUTHTOKEN, AccountManager.KEY_AUTH_TOKEN_LABEL, AccountManager.KEY_ERROR_MESSAGE, AccountManager.KEY_PASSWORD, AccountManager.KEY_ACCOUNT_STATUS_TOKEN}; /** * Keep only documented fields in a Bundle received from AbstractAccountAuthenticator. */ protected static Bundle sanitizeBundle(Bundle bundle) { if (bundle == null) { return null; } Bundle sanitizedBundle = new Bundle(); Bundle.setDefusable(sanitizedBundle, true); int updatedKeysCount = 0; for (String stringKey : sStringBundleKeys) { if (bundle.containsKey(stringKey)) { String value = bundle.getString(stringKey); sanitizedBundle.putString(stringKey, value); updatedKeysCount++; } } String key = AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY; if (bundle.containsKey(key)) { long expiryMillis = bundle.getLong(key, 0L); sanitizedBundle.putLong(key, expiryMillis); updatedKeysCount++; } key = AccountManager.KEY_BOOLEAN_RESULT; if (bundle.containsKey(key)) { boolean booleanResult = bundle.getBoolean(key, false); sanitizedBundle.putBoolean(key, booleanResult); updatedKeysCount++; } key = AccountManager.KEY_ERROR_CODE; if (bundle.containsKey(key)) { int errorCode = bundle.getInt(key, 0); sanitizedBundle.putInt(key, errorCode); updatedKeysCount++; } key = AccountManager.KEY_INTENT; if (bundle.containsKey(key)) { Intent intent = bundle.getParcelable(key, Intent.class); sanitizedBundle.putParcelable(key, intent); updatedKeysCount++; } if (bundle.containsKey(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE)) { // The field is not copied in sanitized bundle. updatedKeysCount++; } if (updatedKeysCount != bundle.size()) { Log.w(TAG, "Size mismatch after sanitizeBundle call."); } return sanitizedBundle; } private abstract class Session extends IAccountAuthenticatorResponse.Stub implements IBinder.DeathRecipient, ServiceConnection { private final Object mSessionLock = new Object(); Loading Loading @@ -5226,9 +5301,14 @@ public class AccountManagerService } } } @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); result = sanitizeBundle(result); onResultSkipSanitization(result); } public void onResultSkipSanitization(Bundle result) { Bundle.setDefusable(result, true); mNumResults++; Intent intent = null; Loading services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +46 −16 Original line number Diff line number Diff line Loading @@ -1163,16 +1163,6 @@ public class AccountManagerServiceTest extends AndroidTestCase { verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture()); Bundle result = mBundleCaptor.getValue(); Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); assertNotNull(sessionBundle); // Assert that session bundle is decrypted and hence data is visible. assertEquals(AccountManagerServiceTestFixtures.SESSION_DATA_VALUE_1, sessionBundle.getString(AccountManagerServiceTestFixtures.SESSION_DATA_NAME_1)); // Assert finishSessionAsUser added calling uid and pid into the sessionBundle assertTrue(sessionBundle.containsKey(AccountManager.KEY_CALLER_UID)); assertTrue(sessionBundle.containsKey(AccountManager.KEY_CALLER_PID)); assertEquals(sessionBundle.getString( AccountManager.KEY_ANDROID_PACKAGE_NAME), "APCT.package"); // Verify response data assertNull(result.getString(AccountManager.KEY_AUTHTOKEN, null)); Loading Loading @@ -2121,12 +2111,6 @@ public class AccountManagerServiceTest extends AndroidTestCase { result.getString(AccountManager.KEY_ACCOUNT_NAME)); assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, result.getString(AccountManager.KEY_ACCOUNT_TYPE)); Bundle optionBundle = result.getParcelable( AccountManagerServiceTestFixtures.KEY_OPTIONS_BUNDLE); // Assert addAccountAsUser added calling uid and pid into the option bundle assertTrue(optionBundle.containsKey(AccountManager.KEY_CALLER_UID)); assertTrue(optionBundle.containsKey(AccountManager.KEY_CALLER_PID)); } @SmallTest Loading Loading @@ -3457,6 +3441,52 @@ public class AccountManagerServiceTest extends AndroidTestCase { + (readTotalTime.doubleValue() / readerCount / loopSize)); } @SmallTest public void testSanitizeBundle_expectedFields() throws Exception { Bundle bundle = new Bundle(); bundle.putString(AccountManager.KEY_ACCOUNT_NAME, "name"); bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, "type"); bundle.putString(AccountManager.KEY_AUTHTOKEN, "token"); bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, "label"); bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "error message"); bundle.putString(AccountManager.KEY_PASSWORD, "password"); bundle.putString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN, "status"); bundle.putLong(AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 123L); bundle.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); bundle.putInt(AccountManager.KEY_ERROR_CODE, 456); Bundle sanitizedBundle = AccountManagerService.sanitizeBundle(bundle); assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_NAME), "name"); assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE), "type"); assertEquals(sanitizedBundle.getString(AccountManager.KEY_AUTHTOKEN), "token"); assertEquals(sanitizedBundle.getString(AccountManager.KEY_AUTH_TOKEN_LABEL), "label"); assertEquals(sanitizedBundle.getString(AccountManager.KEY_ERROR_MESSAGE), "error message"); assertEquals(sanitizedBundle.getString(AccountManager.KEY_PASSWORD), "password"); assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN), "status"); assertEquals(sanitizedBundle.getLong( AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0), 123L); assertEquals(sanitizedBundle.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false), true); assertEquals(sanitizedBundle.getInt(AccountManager.KEY_ERROR_CODE, 0), 456); } @SmallTest public void testSanitizeBundle_filtersUnexpectedFields() throws Exception { Bundle bundle = new Bundle(); bundle.putString(AccountManager.KEY_ACCOUNT_NAME, "name"); bundle.putString("unknown_key", "value"); Bundle sessionBundle = new Bundle(); bundle.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, sessionBundle); Bundle sanitizedBundle = AccountManagerService.sanitizeBundle(bundle); assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_NAME), "name"); assertFalse(sanitizedBundle.containsKey("unknown_key")); // It is a valid response from Authenticator which will be accessed using original Bundle assertFalse(sanitizedBundle.containsKey(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE)); } private void waitForCyclicBarrier(CyclicBarrier cyclicBarrier) { try { cyclicBarrier.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); Loading Loading
services/core/java/com/android/server/accounts/AccountManagerService.java +84 −4 Original line number Diff line number Diff line Loading @@ -1773,7 +1773,8 @@ public class AccountManagerService // Create a Session for the target user and pass in the bundle completeCloningAccount(response, result, account, toAccounts, userFrom); } else { super.onResult(result); // Bundle format is not defined. super.onResultSkipSanitization(result); } } }.bind(); Loading Loading @@ -1860,7 +1861,8 @@ public class AccountManagerService // account to avoid retries? // TODO: what we do with the visibility? super.onResult(result); // Bundle format is not defined. super.onResultSkipSanitization(result); } @Override Loading Loading @@ -2106,6 +2108,7 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); result = sanitizeBundle(result); IAccountManagerResponse response = getResponseAndClose(); if (response != null) { try { Loading Loading @@ -2456,6 +2459,7 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); result = sanitizeBundle(result); if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT) && !result.containsKey(AccountManager.KEY_INTENT)) { final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT); Loading Loading @@ -2970,6 +2974,7 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); result = sanitizeBundle(result); if (result != null) { String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL); Bundle bundle = new Bundle(); Loading Loading @@ -3147,6 +3152,7 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); result = sanitizeBundle(result); if (result != null) { if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) { Intent intent = newGrantCredentialsPermissionIntent( Loading Loading @@ -3618,6 +3624,12 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); Bundle sessionBundle = null; if (result != null) { // Session bundle will be removed from result. sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); } result = sanitizeBundle(result); mNumResults++; Intent intent = null; if (result != null) { Loading Loading @@ -3679,7 +3691,6 @@ public class AccountManagerService // bundle contains data necessary for finishing the session // later. The session bundle will be encrypted here and // decrypted later when trying to finish the session. Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); if (sessionBundle != null) { String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE); if (TextUtils.isEmpty(accountType) Loading Loading @@ -4067,6 +4078,7 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); result = sanitizeBundle(result); IAccountManagerResponse response = getResponseAndClose(); if (response == null) { return; Loading Loading @@ -4380,6 +4392,7 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); result = sanitizeBundle(result); mNumResults++; if (result == null) { onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle"); Loading Loading @@ -4936,6 +4949,68 @@ public class AccountManagerService callback, resultReceiver); } // All keys for Strings passed from AbstractAccountAuthenticator using Bundle. private static final String[] sStringBundleKeys = new String[] { AccountManager.KEY_ACCOUNT_NAME, AccountManager.KEY_ACCOUNT_TYPE, AccountManager.KEY_AUTHTOKEN, AccountManager.KEY_AUTH_TOKEN_LABEL, AccountManager.KEY_ERROR_MESSAGE, AccountManager.KEY_PASSWORD, AccountManager.KEY_ACCOUNT_STATUS_TOKEN}; /** * Keep only documented fields in a Bundle received from AbstractAccountAuthenticator. */ protected static Bundle sanitizeBundle(Bundle bundle) { if (bundle == null) { return null; } Bundle sanitizedBundle = new Bundle(); Bundle.setDefusable(sanitizedBundle, true); int updatedKeysCount = 0; for (String stringKey : sStringBundleKeys) { if (bundle.containsKey(stringKey)) { String value = bundle.getString(stringKey); sanitizedBundle.putString(stringKey, value); updatedKeysCount++; } } String key = AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY; if (bundle.containsKey(key)) { long expiryMillis = bundle.getLong(key, 0L); sanitizedBundle.putLong(key, expiryMillis); updatedKeysCount++; } key = AccountManager.KEY_BOOLEAN_RESULT; if (bundle.containsKey(key)) { boolean booleanResult = bundle.getBoolean(key, false); sanitizedBundle.putBoolean(key, booleanResult); updatedKeysCount++; } key = AccountManager.KEY_ERROR_CODE; if (bundle.containsKey(key)) { int errorCode = bundle.getInt(key, 0); sanitizedBundle.putInt(key, errorCode); updatedKeysCount++; } key = AccountManager.KEY_INTENT; if (bundle.containsKey(key)) { Intent intent = bundle.getParcelable(key, Intent.class); sanitizedBundle.putParcelable(key, intent); updatedKeysCount++; } if (bundle.containsKey(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE)) { // The field is not copied in sanitized bundle. updatedKeysCount++; } if (updatedKeysCount != bundle.size()) { Log.w(TAG, "Size mismatch after sanitizeBundle call."); } return sanitizedBundle; } private abstract class Session extends IAccountAuthenticatorResponse.Stub implements IBinder.DeathRecipient, ServiceConnection { private final Object mSessionLock = new Object(); Loading Loading @@ -5226,9 +5301,14 @@ public class AccountManagerService } } } @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); result = sanitizeBundle(result); onResultSkipSanitization(result); } public void onResultSkipSanitization(Bundle result) { Bundle.setDefusable(result, true); mNumResults++; Intent intent = null; Loading
services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +46 −16 Original line number Diff line number Diff line Loading @@ -1163,16 +1163,6 @@ public class AccountManagerServiceTest extends AndroidTestCase { verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture()); Bundle result = mBundleCaptor.getValue(); Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); assertNotNull(sessionBundle); // Assert that session bundle is decrypted and hence data is visible. assertEquals(AccountManagerServiceTestFixtures.SESSION_DATA_VALUE_1, sessionBundle.getString(AccountManagerServiceTestFixtures.SESSION_DATA_NAME_1)); // Assert finishSessionAsUser added calling uid and pid into the sessionBundle assertTrue(sessionBundle.containsKey(AccountManager.KEY_CALLER_UID)); assertTrue(sessionBundle.containsKey(AccountManager.KEY_CALLER_PID)); assertEquals(sessionBundle.getString( AccountManager.KEY_ANDROID_PACKAGE_NAME), "APCT.package"); // Verify response data assertNull(result.getString(AccountManager.KEY_AUTHTOKEN, null)); Loading Loading @@ -2121,12 +2111,6 @@ public class AccountManagerServiceTest extends AndroidTestCase { result.getString(AccountManager.KEY_ACCOUNT_NAME)); assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, result.getString(AccountManager.KEY_ACCOUNT_TYPE)); Bundle optionBundle = result.getParcelable( AccountManagerServiceTestFixtures.KEY_OPTIONS_BUNDLE); // Assert addAccountAsUser added calling uid and pid into the option bundle assertTrue(optionBundle.containsKey(AccountManager.KEY_CALLER_UID)); assertTrue(optionBundle.containsKey(AccountManager.KEY_CALLER_PID)); } @SmallTest Loading Loading @@ -3457,6 +3441,52 @@ public class AccountManagerServiceTest extends AndroidTestCase { + (readTotalTime.doubleValue() / readerCount / loopSize)); } @SmallTest public void testSanitizeBundle_expectedFields() throws Exception { Bundle bundle = new Bundle(); bundle.putString(AccountManager.KEY_ACCOUNT_NAME, "name"); bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, "type"); bundle.putString(AccountManager.KEY_AUTHTOKEN, "token"); bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, "label"); bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "error message"); bundle.putString(AccountManager.KEY_PASSWORD, "password"); bundle.putString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN, "status"); bundle.putLong(AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 123L); bundle.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); bundle.putInt(AccountManager.KEY_ERROR_CODE, 456); Bundle sanitizedBundle = AccountManagerService.sanitizeBundle(bundle); assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_NAME), "name"); assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE), "type"); assertEquals(sanitizedBundle.getString(AccountManager.KEY_AUTHTOKEN), "token"); assertEquals(sanitizedBundle.getString(AccountManager.KEY_AUTH_TOKEN_LABEL), "label"); assertEquals(sanitizedBundle.getString(AccountManager.KEY_ERROR_MESSAGE), "error message"); assertEquals(sanitizedBundle.getString(AccountManager.KEY_PASSWORD), "password"); assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN), "status"); assertEquals(sanitizedBundle.getLong( AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0), 123L); assertEquals(sanitizedBundle.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false), true); assertEquals(sanitizedBundle.getInt(AccountManager.KEY_ERROR_CODE, 0), 456); } @SmallTest public void testSanitizeBundle_filtersUnexpectedFields() throws Exception { Bundle bundle = new Bundle(); bundle.putString(AccountManager.KEY_ACCOUNT_NAME, "name"); bundle.putString("unknown_key", "value"); Bundle sessionBundle = new Bundle(); bundle.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, sessionBundle); Bundle sanitizedBundle = AccountManagerService.sanitizeBundle(bundle); assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_NAME), "name"); assertFalse(sanitizedBundle.containsKey("unknown_key")); // It is a valid response from Authenticator which will be accessed using original Bundle assertFalse(sanitizedBundle.containsKey(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE)); } private void waitForCyclicBarrier(CyclicBarrier cyclicBarrier) { try { cyclicBarrier.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); Loading