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.

Change-Id: If68343d155364654fa7db55ace3fb3c4db508c7e
Test: manual tests
Bug: 14408627
This commit is contained in:
Dmitry Dementyev
2017-02-10 16:51:51 -08:00
parent 33b0a66aee
commit 03e250bb55

View File

@@ -37,6 +37,7 @@ import android.os.UserHandle;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.support.v7.preference.Preference.OnPreferenceClickListener; import android.support.v7.preference.Preference.OnPreferenceClickListener;
import android.support.v7.preference.PreferenceGroup;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Log; import android.util.Log;
@@ -84,7 +85,7 @@ public class ManageAccountsSettings extends AccountPreferenceBase
// If an account type is set, then show only accounts of that type // If an account type is set, then show only accounts of that type
private String mAccountType; private String mAccountType;
// Temporary hack, to deal with backward compatibility // Temporary hack, to deal with backward compatibility
// mFirstAccount is used for the injected preferences // mFirstAccount is used for the injected preferences
private Account mFirstAccount; private Account mFirstAccount;
@@ -448,15 +449,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 * 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. * 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(); final PackageManager pm = getActivity().getPackageManager();
for (int i = 0; i < prefs.getPreferenceCount(); ) { for (int i = 0; i < prefs.getPreferenceCount(); ) {
Preference pref = prefs.getPreference(i); Preference pref = prefs.getPreference(i);
if (pref instanceof PreferenceGroup) {
updatePreferenceIntents((PreferenceGroup) pref);
}
Intent intent = pref.getIntent(); Intent intent = pref.getIntent();
if (intent != null) { if (intent != null) {
// Hack. Launch "Location" as fragment instead of as activity. // Hack. Launch "Location" as fragment instead of as activity.
@@ -526,16 +530,24 @@ public class ManageAccountsSettings extends AccountPreferenceBase
private boolean isSafeIntent(PackageManager pm, Intent intent) { private boolean isSafeIntent(PackageManager pm, Intent intent) {
AuthenticatorDescription authDesc = AuthenticatorDescription authDesc =
mAuthenticatorHelper.getAccountTypeDescription(mAccountType); mAuthenticatorHelper.getAccountTypeDescription(mAccountType);
ResolveInfo resolveInfo = pm.resolveActivity(intent, 0); ResolveInfo resolveInfo =
pm.resolveActivityAsUser(intent, 0, mUserHandle.getIdentifier());
if (resolveInfo == null) { if (resolveInfo == null) {
return false; return false;
} }
ActivityInfo resolvedActivityInfo = resolveInfo.activityInfo; ActivityInfo resolvedActivityInfo = resolveInfo.activityInfo;
ApplicationInfo resolvedAppInfo = resolvedActivityInfo.applicationInfo; ApplicationInfo resolvedAppInfo = resolvedActivityInfo.applicationInfo;
try { 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); ApplicationInfo authenticatorAppInf = pm.getApplicationInfo(authDesc.packageName, 0);
return resolvedActivityInfo.exported return resolvedAppInfo.uid == authenticatorAppInf.uid;
|| resolvedAppInfo.uid == authenticatorAppInf.uid;
} catch (NameNotFoundException e) { } catch (NameNotFoundException e) {
Log.e(TAG, Log.e(TAG,
"Intent considered unsafe due to exception.", "Intent considered unsafe due to exception.",