Add safety checks on KEY_INTENT mismatch.

For many years, Parcel mismatch typed exploits has been using the
AccoungManagerService's passing of KEY_INTENT workflow, as a foothold of
launching arbitrary intents. We are adding an extra check on the service
side to simulate the final deserialization of the KEY_INTENT value, to
make sure the client side won't get a mismatched KEY_INTENT value.

Bug: 250588548
Bug: 240138294
Test: atest CtsAccountManagerTestCases
Test: local test, also see b/250588548
Change-Id: I433e34f6e21ce15c89825044a15b1dec46bb25cc
This commit is contained in:
Hao Ke
2022-10-04 19:43:58 +00:00
parent 77ebe8c56c
commit ba27731d04

View File

@@ -89,6 +89,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.stats.devicepolicy.DevicePolicyEnums;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -3100,7 +3101,7 @@ public class AccountManagerService
*/
if (!checkKeyIntent(
Binder.getCallingUid(),
intent)) {
result)) {
onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
"invalid intent in bundle returned");
return;
@@ -3519,7 +3520,7 @@ public class AccountManagerService
&& (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
if (!checkKeyIntent(
Binder.getCallingUid(),
intent)) {
result)) {
onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
"invalid intent in bundle returned");
return;
@@ -4870,7 +4871,13 @@ public class AccountManagerService
* into launching arbitrary intents on the device via by tricking to click authenticator
* supplied entries in the system Settings app.
*/
protected boolean checkKeyIntent(int authUid, Intent intent) {
protected boolean checkKeyIntent(int authUid, Bundle bundle) {
if (!checkKeyIntentParceledCorrectly(bundle)) {
EventLog.writeEvent(0x534e4554, "250588548", authUid, "");
return false;
}
Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT, Intent.class);
// Explicitly set an empty ClipData to ensure that we don't offer to
// promote any Uris contained inside for granting purposes
if (intent.getClipData() == null) {
@@ -4905,6 +4912,25 @@ public class AccountManagerService
}
}
/**
* Simulate the client side's deserialization of KEY_INTENT value, to make sure they don't
* violate our security policy.
*
* In particular we want to make sure the Authenticator doesn't trick users
* into launching arbitrary intents on the device via exploiting any other Parcel read/write
* mismatch problems.
*/
private boolean checkKeyIntentParceledCorrectly(Bundle bundle) {
Parcel p = Parcel.obtain();
p.writeBundle(bundle);
p.setDataPosition(0);
Bundle simulateBundle = p.readBundle();
p.recycle();
Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT, Intent.class);
return (intent.filterEquals(simulateBundle.getParcelable(AccountManager.KEY_INTENT,
Intent.class)));
}
private boolean isExportedSystemActivity(ActivityInfo activityInfo) {
String className = activityInfo.name;
return "android".equals(activityInfo.packageName) &&
@@ -5051,7 +5077,7 @@ public class AccountManagerService
&& (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
if (!checkKeyIntent(
Binder.getCallingUid(),
intent)) {
result)) {
onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
"invalid intent in bundle returned");
return;