Allow to bypass Android O+ account restrictions for Google Apps

This commit is contained in:
Marvin W 2019-05-28 18:45:54 +02:00
parent 7a646e3346
commit 30ed2720a0
No known key found for this signature in database
GPG Key ID: 072E9235DB996F2A
10 changed files with 147 additions and 21 deletions

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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));

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
});
}
}
}
}
}

View File

@ -128,6 +128,8 @@ This can take a couple of minutes."</string>
<string name="pref_auth_trust_google_title">Trust Google for app permissions</string>
<string name="pref_auth_trust_google_summary">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.</string>
<string name="pref_auth_visible_title">Allow apps to find accounts</string>
<string name="pref_auth_visible_summary">When enabled, all applications on this device will be able to see email address of your Google Accounts without prior authorization.</string>
<string name="pref_checkin_enable_summary">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.</string>

View File

@ -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"/>
<SwitchPreference
android:defaultValue="false"
android:key="auth_manager_visible"
android:summary="@string/pref_auth_visible_summary"
android:title="@string/pref_auth_visible_title"/>
</PreferenceCategory>
<Preference