From 30ed2720a0ecd3067fe760e801ecbdbe1b5ff5be Mon Sep 17 00:00:00 2001 From: Marvin W Date: Tue, 28 May 2019 18:45:54 +0200 Subject: [PATCH] Allow to bypass Android O+ account restrictions for Google Apps --- .../gms/auth/AccountContentProvider.java | 5 +- .../gms/auth/AskPermissionActivity.java | 4 +- .../java/org/microg/gms/auth/AuthManager.java | 14 ++++ .../gms/auth/AuthManagerServiceImpl.java | 15 +++-- .../microg/gms/auth/login/LoginActivity.java | 7 +- .../loginservice/AccountAuthenticator.java | 3 +- .../org/microg/gms/common/PackageUtils.java | 64 +++++++++++++++---- .../gms/ui/AccountSettingsActivity.java | 49 +++++++++++++- .../src/main/res/values/strings.xml | 2 + .../src/main/res/xml/preferences_account.xml | 5 ++ 10 files changed, 147 insertions(+), 21 deletions(-) diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AccountContentProvider.java b/play-services-core/src/main/java/org/microg/gms/auth/AccountContentProvider.java index 5d41d97f..2c09c829 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/AccountContentProvider.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/AccountContentProvider.java @@ -25,6 +25,7 @@ import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.support.annotation.Nullable; import android.util.Log; @@ -51,6 +52,7 @@ public class AccountContentProvider extends ContentProvider { @Nullable @Override public Bundle call(String method, String arg, Bundle extras) { + String packageName = PackageUtils.packageFromProcessId(getContext(), Binder.getCallingPid()); if (!PackageUtils.callerHasExtendedAccess(getContext())) { String[] packagesForUid = getContext().getPackageManager().getPackagesForUid(Binder.getCallingUid()); if (packagesForUid != null && packagesForUid.length != 0) @@ -61,7 +63,8 @@ public class AccountContentProvider extends ContentProvider { } if (PROVIDER_METHOD_GET_ACCOUNTS.equals(method) && AuthConstants.DEFAULT_ACCOUNT_TYPE.equals(arg)) { Bundle result = new Bundle(); - result.putParcelableArray(PROVIDER_EXTRA_ACCOUNTS, AccountManager.get(getContext()).getAccountsByType(arg)); + AccountManager am = AccountManager.get(getContext()); + result.putParcelableArray(PROVIDER_EXTRA_ACCOUNTS, Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 ? am.getAccountsByTypeForPackage(arg, packageName) : am.getAccountsByType(arg)); return result; } else if (PROVIDER_METHOD_CLEAR_PASSWORD.equals(method)) { Account a = extras.getParcelable(PROVIDER_EXTRA_CLEAR_PASSWORD); diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AskPermissionActivity.java b/play-services-core/src/main/java/org/microg/gms/auth/AskPermissionActivity.java index a8df5f16..63c3d343 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/AskPermissionActivity.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/AskPermissionActivity.java @@ -25,6 +25,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Bundle; import android.text.Html; import android.util.Log; @@ -49,6 +50,7 @@ import static android.accounts.AccountManager.KEY_ACCOUNT_NAME; import static android.accounts.AccountManager.KEY_ACCOUNT_TYPE; import static android.accounts.AccountManager.KEY_ANDROID_PACKAGE_NAME; import static android.accounts.AccountManager.KEY_AUTHTOKEN; +import static android.accounts.AccountManager.KEY_CALLER_PID; import static android.accounts.AccountManager.KEY_CALLER_UID; import static android.view.View.GONE; import static android.view.View.VISIBLE; @@ -96,7 +98,7 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity { if (getIntent().hasExtra(EXTRA_FROM_ACCOUNT_MANAGER)) fromAccountManager = true; int callerUid = getIntent().getIntExtra(KEY_CALLER_UID, 0); - PackageUtils.checkPackageUid(this, packageName, callerUid); + packageName = PackageUtils.getAndCheckPackage(this, packageName, getIntent().getIntExtra(KEY_CALLER_UID, 0), getIntent().getIntExtra(KEY_CALLER_PID, 0)); authManager = new AuthManager(this, account.name, packageName, service); // receive package info diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AuthManager.java b/play-services-core/src/main/java/org/microg/gms/auth/AuthManager.java index 6fcd0400..821f09fe 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/AuthManager.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/AuthManager.java @@ -20,6 +20,7 @@ import android.accounts.Account; import android.accounts.AccountManager; import android.content.Context; import android.content.pm.PackageManager; +import android.os.Build; import android.preference.PreferenceManager; import android.util.Log; @@ -35,6 +36,7 @@ public class AuthManager { private static final String TAG = "GmsAuthManager"; public static final String PERMISSION_TREE_BASE = "com.google.android.googleapps.permission.GOOGLE_AUTH."; private static final String PREF_AUTH_TRUST_GOOGLE = "auth_manager_trust_google"; + public static final String PREF_AUTH_VISIBLE = "auth_manager_visible"; public static final int ONE_HOUR_IN_SECONDS = 60 * 60; private final Context context; @@ -91,6 +93,10 @@ public class AuthManager { public void setPermitted(boolean value) { setUserData(buildPermKey(), value ? "1" : "0"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && value && packageName != null) { + // Make account persistently visible as we already granted access + accountManager.setAccountVisibility(getAccount(), packageName, AccountManager.VISIBILITY_VISIBLE); + } } public boolean isPermitted() { @@ -148,6 +154,10 @@ public class AuthManager { public void setAuthToken(String service, String auth) { getAccountManager().setAuthToken(getAccount(), buildTokenKey(service), auth); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && packageName != null && auth != null) { + // Make account persistently visible as we already granted access + accountManager.setAccountVisibility(getAccount(), packageName, AccountManager.VISIBILITY_VISIBLE); + } } public void storeResponse(AuthResponse response) { @@ -172,6 +182,10 @@ public class AuthManager { return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(PREF_AUTH_TRUST_GOOGLE, true); } + public static boolean isAuthVisible(Context context) { + return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(PREF_AUTH_VISIBLE, false); + } + private boolean isSystemApp() { try { int flags = context.getPackageManager().getApplicationInfo(packageName, 0).flags; diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java b/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java index 24d58d19..1f5c26a7 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; +import android.os.Parcel; import android.os.RemoteException; import android.support.v4.app.NotificationCompat; import android.util.Base64; @@ -44,6 +45,7 @@ import java.util.List; import static android.accounts.AccountManager.KEY_ACCOUNT_NAME; import static android.accounts.AccountManager.KEY_ACCOUNT_TYPE; import static android.accounts.AccountManager.KEY_AUTHTOKEN; +import static android.accounts.AccountManager.KEY_CALLER_PID; import static org.microg.gms.auth.AskPermissionActivity.EXTRA_CONSENT_DATA; public class AuthManagerServiceImpl extends IAuthManagerService.Stub { @@ -74,8 +76,7 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { String packageName = extras.getString(KEY_ANDROID_PACKAGE_NAME); if (packageName == null || packageName.isEmpty()) packageName = extras.getString(KEY_CLIENT_PACKAGE_NAME); - int callerUid = extras.getInt(KEY_CALLER_UID, 0); - PackageUtils.checkPackageUid(context, packageName, callerUid, getCallingUid()); + packageName = PackageUtils.getAndCheckCallingPackage(context, packageName, extras.getInt(KEY_CALLER_UID, 0), extras.getInt(KEY_CALLER_PID, 0)); boolean notify = extras.getBoolean(KEY_HANDLE_NOTIFICATION, false); Log.d(TAG, "getToken: account:" + accountName + " scope:" + scope + " extras:" + extras + ", notify: " + notify); @@ -163,10 +164,16 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { public Bundle clearToken(String token, Bundle extras) throws RemoteException { String packageName = extras.getString(KEY_ANDROID_PACKAGE_NAME); if (packageName == null) packageName = extras.getString(KEY_CLIENT_PACKAGE_NAME); - int callerUid = extras.getInt(KEY_CALLER_UID, 0); - PackageUtils.checkPackageUid(context, packageName, callerUid, getCallingUid()); + packageName = PackageUtils.getAndCheckCallingPackage(context, packageName, extras.getInt(KEY_CALLER_UID, 0), extras.getInt(KEY_CALLER_PID, 0)); Log.d(TAG, "clearToken: token:" + token + " extras:" + extras); return null; } + + @Override + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { + if (super.onTransact(code, data, reply, flags)) return true; + Log.d(TAG, "onTransact [unknown]: " + code + ", " + data + ", " + flags); + return false; + } } diff --git a/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java b/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java index 82284471..d37a99c6 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java @@ -60,6 +60,8 @@ import org.microg.gms.people.PeopleManager; import java.io.IOException; import java.util.Locale; +import static android.accounts.AccountManager.PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE; +import static android.accounts.AccountManager.VISIBILITY_USER_MANAGED_VISIBLE; import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION_CODES.GINGERBREAD_MR1; import static android.os.Build.VERSION_CODES.HONEYCOMB; @@ -130,9 +132,12 @@ public class LoginActivity extends AssistantActivity { }); if (getIntent().hasExtra(EXTRA_TOKEN)) { if (getIntent().hasExtra(EXTRA_EMAIL)) { - AccountManager accountManager = AccountManager.get(LoginActivity.this); + AccountManager accountManager = AccountManager.get(this); Account account = new Account(getIntent().getStringExtra(EXTRA_EMAIL), accountType); accountManager.addAccountExplicitly(account, getIntent().getStringExtra(EXTRA_TOKEN), null); + if (AuthManager.isAuthVisible(this) && SDK_INT >= Build.VERSION_CODES.O) { + accountManager.setAccountVisibility(account, PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE, VISIBILITY_USER_MANAGED_VISIBLE); + } retrieveGmsToken(account); } else { retrieveRtToken(getIntent().getStringExtra(EXTRA_TOKEN)); diff --git a/play-services-core/src/main/java/org/microg/gms/auth/loginservice/AccountAuthenticator.java b/play-services-core/src/main/java/org/microg/gms/auth/loginservice/AccountAuthenticator.java index a7f34291..c52565ce 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/loginservice/AccountAuthenticator.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/loginservice/AccountAuthenticator.java @@ -45,6 +45,7 @@ import static android.accounts.AccountManager.KEY_ACCOUNT_TYPE; import static android.accounts.AccountManager.KEY_ANDROID_PACKAGE_NAME; import static android.accounts.AccountManager.KEY_AUTHTOKEN; import static android.accounts.AccountManager.KEY_BOOLEAN_RESULT; +import static android.accounts.AccountManager.KEY_CALLER_PID; import static android.accounts.AccountManager.KEY_CALLER_UID; import static android.accounts.AccountManager.KEY_INTENT; @@ -90,7 +91,7 @@ class AccountAuthenticator extends AbstractAccountAuthenticator { options.keySet(); Log.d(TAG, "getAuthToken: " + account + ", " + authTokenType + ", " + options); String app = options.getString(KEY_ANDROID_PACKAGE_NAME); - PackageUtils.checkPackageUid(context, app, options.getInt(KEY_CALLER_UID), options.getInt(KEY_CALLER_UID)); + app = PackageUtils.getAndCheckPackage(context, app, options.getInt(KEY_CALLER_UID), options.getInt(KEY_CALLER_PID)); AuthManager authManager = new AuthManager(context, account.name, app, authTokenType); try { AuthResponse res = authManager.requestAuth(true); diff --git a/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java b/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java index 7d3030f1..2be84d65 100644 --- a/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java +++ b/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java @@ -87,17 +87,7 @@ public class PackageUtils { } public static void checkPackageUid(Context context, String packageName, int callingUid) { - String[] packagesForUid = context.getPackageManager().getPackagesForUid(callingUid); - if (packagesForUid != null && !Arrays.asList(packagesForUid).contains(packageName)) { - throw new SecurityException("callingUid [" + callingUid + "] is not related to packageName [" + packageName + "]"); - } - } - - public static void checkPackageUid(Context context, String packageName, int callerUid, int callingUid) { - if (callerUid != 0 && callerUid != callingUid) { - throw new SecurityException("callerUid [" + callerUid + "] and real calling uid [" + callingUid + "] mismatch!"); - } - checkPackageUid(context, packageName, callingUid); + getAndCheckPackage(context, packageName, callingUid, 0); } @Nullable @@ -120,10 +110,62 @@ public class PackageUtils { return null; } + @Nullable + public static String getAndCheckCallingPackage(Context context, String suggestedPackageName) { + return getAndCheckCallingPackage(context, suggestedPackageName, 0); + } + + @Nullable + public static String getAndCheckCallingPackage(Context context, int suggestedCallerUid) { + return getAndCheckCallingPackage(context, null, suggestedCallerUid); + } + + @Nullable + public static String getAndCheckCallingPackage(Context context, String suggestedPackageName, int suggestedCallerUid) { + return getAndCheckCallingPackage(context, suggestedPackageName, suggestedCallerUid, 0); + } + + @Nullable + public static String getAndCheckCallingPackage(Context context, String suggestedPackageName, int suggestedCallerUid, int suggestedCallerPid) { + int callingUid = Binder.getCallingUid(), callingPid = Binder.getCallingPid(); + if (suggestedCallerUid > 0 && suggestedCallerUid != callingUid) { + throw new SecurityException("suggested UID [" + suggestedCallerUid + "] and real calling UID [" + callingUid + "] mismatch!"); + } + if (suggestedCallerPid > 0 && suggestedCallerPid != callingPid) { + throw new SecurityException("suggested PID [" + suggestedCallerPid + "] and real calling PID [" + callingPid + "] mismatch!"); + } + return getAndCheckPackage(context, suggestedPackageName, callingUid, Binder.getCallingPid()); + } + + @Nullable + public static String getAndCheckPackage(Context context, String suggestedPackageName, int callingUid) { + return getAndCheckPackage(context, suggestedPackageName, callingUid, 0); + } + + @Nullable + public static String getAndCheckPackage(Context context, String suggestedPackageName, int callingUid, int callingPid) { + String packageName = packageFromProcessId(context, callingPid); + if (packageName == null) { + String[] packagesForUid = context.getPackageManager().getPackagesForUid(callingUid); + if (packagesForUid != null && packagesForUid.length != 0) { + if (packagesForUid.length == 1) { + packageName = packagesForUid[0]; + } else if (Arrays.asList(packagesForUid).contains(suggestedPackageName)) { + packageName = suggestedPackageName; + } + } + } + if (packageName != null && !packageName.equals(suggestedPackageName)) { + throw new SecurityException("UID [" + callingUid + "] is not related to packageName [" + packageName + "]"); + } + return packageName; + } + @Nullable public static String packageFromProcessId(Context context, int pid) { ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); if (manager == null) return null; + if (pid <= 0) return null; for (ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) { if (processInfo.pid == pid) return processInfo.processName; } diff --git a/play-services-core/src/main/java/org/microg/gms/ui/AccountSettingsActivity.java b/play-services-core/src/main/java/org/microg/gms/ui/AccountSettingsActivity.java index a0c66f9a..abb91045 100644 --- a/play-services-core/src/main/java/org/microg/gms/ui/AccountSettingsActivity.java +++ b/play-services-core/src/main/java/org/microg/gms/ui/AccountSettingsActivity.java @@ -16,12 +16,57 @@ package org.microg.gms.ui; +import android.accounts.Account; +import android.accounts.AccountManager; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v7.preference.Preference; + import com.google.android.gms.R; +import org.microg.gms.auth.AuthConstants; +import org.microg.gms.auth.AuthManager; import org.microg.tools.ui.AbstractSettingsActivity; +import org.microg.tools.ui.ResourceSettingsFragment; + +import static android.accounts.AccountManager.PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE; +import static android.accounts.AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE; +import static android.accounts.AccountManager.VISIBILITY_USER_MANAGED_VISIBLE; +import static org.microg.gms.auth.AuthManager.PREF_AUTH_VISIBLE; public class AccountSettingsActivity extends AbstractSettingsActivity { - public AccountSettingsActivity() { - preferencesResource = R.xml.preferences_account; + + @Override + protected Fragment getFragment() { + return new AccountSettingsFragment(); + } + + public static class AccountSettingsFragment extends ResourceSettingsFragment { + public AccountSettingsFragment() { + preferencesResource = R.xml.preferences_account; + } + + @Override + public void onCreatePreferencesFix(@Nullable Bundle savedInstanceState, String rootKey) { + super.onCreatePreferencesFix(savedInstanceState, rootKey); + Preference pref = findPreference(PREF_AUTH_VISIBLE); + if (pref != null) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + pref.setVisible(false); + } else { + pref.setOnPreferenceChangeListener((preference, newValue) -> { + if (newValue instanceof Boolean) { + AccountManager am = AccountManager.get(getContext()); + for (Account account : am.getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE)) { + am.setAccountVisibility(account, PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE, (Boolean) newValue ? VISIBILITY_USER_MANAGED_VISIBLE : VISIBILITY_USER_MANAGED_NOT_VISIBLE); + } + } + return true; + }); + } + } + } } } diff --git a/play-services-core/src/main/res/values/strings.xml b/play-services-core/src/main/res/values/strings.xml index 8d4b1739..66b626ce 100644 --- a/play-services-core/src/main/res/values/strings.xml +++ b/play-services-core/src/main/res/values/strings.xml @@ -128,6 +128,8 @@ This can take a couple of minutes." Trust Google for app permissions When disabled, the user is asked before an apps authorization request is sent to Google. Some applications will fail to use the Google account if this is disabled. + Allow apps to find accounts + When enabled, all applications on this device will be able to see email address of your Google Accounts without prior authorization. Registers your device to Google services and creates a unique device identifier. microG strips identifying bits other than your Google account name from registration data. diff --git a/play-services-core/src/main/res/xml/preferences_account.xml b/play-services-core/src/main/res/xml/preferences_account.xml index 74c27379..32aa3ae9 100644 --- a/play-services-core/src/main/res/xml/preferences_account.xml +++ b/play-services-core/src/main/res/xml/preferences_account.xml @@ -23,6 +23,11 @@ android:key="auth_manager_trust_google" android:summary="@string/pref_auth_trust_google_summary" android:title="@string/pref_auth_trust_google_title"/> +