From 75ae46d035ce600be3cac3ee597d5e379636fa42 Mon Sep 17 00:00:00 2001 From: Dmitry Dementyev Date: Tue, 14 Feb 2017 13:15:36 -0800 Subject: [PATCH] Add permission check to Intents used by Authenticator Settings. Setting shares system uid and can be used to bypass different security checks. We add proper handling for intents which resolve toexported=true activities with permission filed. Added nested preferences filtering. Bug: 33123882 Test: manual tests Change-Id: Ib5bab7989fc44b4391f9050c6b18f81c58c09cf6 --- .../accounts/ManageAccountsSettings.java | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/com/android/settings/accounts/ManageAccountsSettings.java b/src/com/android/settings/accounts/ManageAccountsSettings.java index 074176bd716..16a764228f8 100644 --- a/src/com/android/settings/accounts/ManageAccountsSettings.java +++ b/src/com/android/settings/accounts/ManageAccountsSettings.java @@ -37,6 +37,7 @@ import android.os.Bundle; import android.os.UserHandle; import android.preference.Preference; import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.util.Log; import android.view.LayoutInflater; @@ -83,7 +84,7 @@ public class ManageAccountsSettings extends AccountPreferenceBase // If an account type is set, then show only accounts of that type private String mAccountType; - // Temporary hack, to deal with backward compatibility + // Temporary hack, to deal with backward compatibility private Account mFirstAccount; @Override @@ -422,15 +423,18 @@ public class ManageAccountsSettings extends AccountPreferenceBase } /** - * Filters through the preference list provided by GoogleLoginService. + * Recursively filters through the preference list provided by GoogleLoginService. * * This method removes all the invalid intent from the list, adds account name as extra into the * intent, and hack the location settings to start it as a fragment. */ - private void updatePreferenceIntents(PreferenceScreen prefs) { + private void updatePreferenceIntents(PreferenceGroup prefs) { final PackageManager pm = getActivity().getPackageManager(); for (int i = 0; i < prefs.getPreferenceCount();) { Preference pref = prefs.getPreference(i); + if (pref instanceof PreferenceGroup) { + updatePreferenceIntents((PreferenceGroup) pref); + } Intent intent = pref.getIntent(); if (intent != null) { // Hack. Launch "Location" as fragment instead of as activity. @@ -479,8 +483,8 @@ public class ManageAccountsSettings extends AccountPreferenceBase } else { Log.e(TAG, "Refusing to launch authenticator intent because" - + "it exploits Settings permissions: " - + prefIntent); + + " it exploits Settings permissions: " + + prefIntent); } return true; } @@ -500,20 +504,26 @@ public class ManageAccountsSettings extends AccountPreferenceBase private boolean isSafeIntent(PackageManager pm, Intent intent) { AuthenticatorDescription authDesc = mAuthenticatorHelper.getAccountTypeDescription(mAccountType); - ResolveInfo resolveInfo = pm.resolveActivity(intent, 0); + ResolveInfo resolveInfo = + pm.resolveActivityAsUser(intent, 0, mUserHandle.getIdentifier()); if (resolveInfo == null) { return false; } ActivityInfo resolvedActivityInfo = resolveInfo.activityInfo; ApplicationInfo resolvedAppInfo = resolvedActivityInfo.applicationInfo; try { + if (resolvedActivityInfo.exported) { + if (resolvedActivityInfo.permission == null) { + return true; // exported activity without permission. + } else if (pm.checkPermission(resolvedActivityInfo.permission, + authDesc.packageName) == PackageManager.PERMISSION_GRANTED) { + return true; + } + } ApplicationInfo authenticatorAppInf = pm.getApplicationInfo(authDesc.packageName, 0); - return resolvedActivityInfo.exported - || resolvedAppInfo.uid == authenticatorAppInf.uid; + return resolvedAppInfo.uid == authenticatorAppInf.uid; } catch (NameNotFoundException e) { - Log.e(TAG, - "Intent considered unsafe due to exception.", - e); + Log.e(TAG, "Intent considered unsafe due to exception.", e); return false; } }