Loading src/org/microg/gms/auth/AskPermissionActivity.java +63 −38 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package org.microg.gms.auth; import android.accounts.Account; import android.accounts.AccountAuthenticatorActivity; import android.accounts.AccountManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.graphics.Bitmap; Loading @@ -36,19 +35,29 @@ import android.widget.ListView; import android.widget.TextView; import com.google.android.gms.R; import com.squareup.wire.Wire; import org.microg.gms.common.PackageUtils; import org.microg.gms.people.PeopleManager; import java.io.IOException; 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_UID; public class AskPermissionActivity extends AccountAuthenticatorActivity { public static final String EXTRA_FROM_ACCOUNT_MANAGER = "from_account_manager"; public static final String EXTRA_CONSENT_DATA = "consent_data"; private static final String TAG = "GmsAuthAskPermission"; private Account account; private String packageName; private String service; private AuthManager authManager; private ConsentData consentData; private boolean fromAccountManager = false; @Override Loading @@ -63,13 +72,21 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity { lp.height = WindowManager.LayoutParams.WRAP_CONTENT; getWindow().setAttributes(lp); account = new Account(getIntent().getStringExtra(AccountManager.KEY_ACCOUNT_NAME), getIntent().getStringExtra(AccountManager.KEY_ACCOUNT_TYPE)); packageName = getIntent().getStringExtra(AccountManager.KEY_ANDROID_PACKAGE_NAME); service = getIntent().getStringExtra(AccountManager.KEY_AUTHTOKEN); account = new Account(getIntent().getStringExtra(KEY_ACCOUNT_NAME), getIntent().getStringExtra(KEY_ACCOUNT_TYPE)); packageName = getIntent().getStringExtra(KEY_ANDROID_PACKAGE_NAME); service = getIntent().getStringExtra(KEY_AUTHTOKEN); if (getIntent().hasExtra(EXTRA_CONSENT_DATA)) { try { consentData = new Wire().parseFrom(getIntent().getByteArrayExtra(EXTRA_CONSENT_DATA), ConsentData.class); Log.d(TAG, "Consent: " + consentData); } catch (Exception ignored) { } } if (getIntent().hasExtra(EXTRA_FROM_ACCOUNT_MANAGER)) fromAccountManager = true; int callerUid = getIntent().getIntExtra(AccountManager.KEY_CALLER_UID, 0); int callerUid = getIntent().getIntExtra(KEY_CALLER_UID, 0); PackageUtils.checkPackageUid(this, packageName, callerUid); authManager = new AuthManager(this, account.name, packageName, service); // receive package info PackageManager packageManager = getPackageManager(); Loading Loading @@ -126,7 +143,7 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity { } public void onAllow() { AuthManager.storePermission(this, account, packageName, service); authManager.setPermitted(true); findViewById(android.R.id.button1).setEnabled(false); findViewById(android.R.id.button2).setEnabled(false); findViewById(R.id.progress_bar).setVisibility(View.VISIBLE); Loading @@ -134,27 +151,13 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity { new Thread(new Runnable() { @Override public void run() { Context context = AskPermissionActivity.this; String sig = PackageUtils.firstSignatureDigest(context, packageName); AuthRequest request = new AuthRequest().fromContext(context) .email(account.name) .token(AccountManager.get(context).getPassword(account)) .service(service) .app(packageName, sig) .hasPermission(); if (fromAccountManager) { request.callerIsGms().calledFromAccountManager(); } else { request.callerIsApp(); } try { AuthResponse response = request.getResponse(); AuthManager.storeResponse(context, account, packageName, sig, service, response); AuthResponse response = authManager.requestAuth(fromAccountManager); Bundle result = new Bundle(); result.putString(AccountManager.KEY_AUTHTOKEN, response.auth); result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); result.putString(AccountManager.KEY_ANDROID_PACKAGE_NAME, packageName); result.putString(KEY_AUTHTOKEN, response.auth); result.putString(KEY_ACCOUNT_NAME, account.name); result.putString(KEY_ACCOUNT_TYPE, account.type); result.putString(KEY_ANDROID_PACKAGE_NAME, packageName); result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); setAccountAuthenticatorResult(result); } catch (IOException e) { Loading @@ -167,6 +170,7 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity { } public void onDeny() { authManager.setPermitted(false); finish(); } Loading @@ -179,6 +183,36 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity { return service.startsWith("oauth2:") || service.startsWith("oauth:"); } private String getScopeLabel(String scope) { if (consentData != null) { for (ConsentData.ScopeDetails scopeDetails : consentData.scopes) { if (scope.equals(scopeDetails.id)) { return scopeDetails.title; } } } String labelResourceId = "permission_scope_"; String escapedScope = scope.replace("/", "_").replace("-", "_"); if (scope.startsWith("https://")) { labelResourceId += escapedScope.substring(8); } else { labelResourceId += escapedScope; } int labelResource = getResources().getIdentifier(labelResourceId, "string", getPackageName()); if (labelResource != 0) { return getString(labelResource); } return "unknown"; } private String getServiceLabel(String service) { int labelResource = getResources().getIdentifier("permission_service_" + service + "_label", "string", getPackageName()); if (labelResource != 0) { return getString(labelResource); } return "unknown"; } private class PermissionAdapter extends BaseAdapter { @Override Loading Loading @@ -206,20 +240,11 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity { @Override public View getView(int position, View convertView, ViewGroup parent) { String item = getItem(position); String label = "unknown"; String labelResourceId; String label; if (isOAuth()) { if (item.startsWith("https://")) { labelResourceId = "permission_scope_" + item.substring(8).replace("/", "_").replace("-", "_"); } else { labelResourceId = "permission_scope_" + item.replace("/", "_").replace("-", "_"); } label = getScopeLabel(item); } else { labelResourceId = "permission_service_" + item + "_label"; } int labelResource = getResources().getIdentifier(labelResourceId, "string", getPackageName()); if (labelResource != 0) { label = getString(labelResource); label = getServiceLabel(item); } View view = convertView; if (view == null) { Loading src/org/microg/gms/auth/AuthManager.java +151 −61 Original line number Diff line number Diff line Loading @@ -20,96 +20,186 @@ import android.accounts.Account; import android.accounts.AccountManager; import android.content.Context; import android.content.pm.PackageManager; import android.preference.PreferenceManager; import android.util.Log; import org.microg.gms.common.Constants; import org.microg.gms.common.PackageUtils; import java.io.IOException; import static android.content.pm.ApplicationInfo.FLAG_SYSTEM; import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; 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_KEY_TRUST_GOOGLE = "auth_manager_trust_google"; public static void storeResponse(Context context, Account account, String packageName, String sig, String service, AuthResponse response) { if (service.startsWith("weblogin:")) return; AccountManager accountManager = AccountManager.get(context); if (response.accountId != null) accountManager.setUserData(account, "GoogleUserId", response.accountId); if (response.Sid != null) accountManager.setAuthToken(account, buildTokenKey(packageName, sig, "SID"), response.Sid); if (response.LSid != null) accountManager.setAuthToken(account, buildTokenKey(packageName, sig, "LSID"), response.LSid); if (response.expiry > 0) accountManager.setUserData(account, buildExpireKey(packageName, sig, service), Long.toString(response.expiry)); if (response.auth != null && response.expiry != 0 && response.storeConsentRemotely) { accountManager.setAuthToken(account, buildTokenKey(packageName, sig, service), response.auth); accountManager.setUserData(account, buildPermKey(packageName, sig, service), "1"); private final Context context; private final String accountName; private final String packageName; private final String service; private AccountManager accountManager; private Account account; private String packageSignature; public AuthManager(Context context, String accountName, String packageName, String service) { this.context = context; this.accountName = accountName; this.packageName = packageName; this.service = service; } public AccountManager getAccountManager() { if (accountManager == null) accountManager = AccountManager.get(context); return accountManager; } public static String getToken(Context context, Account account, String packageName, String sig, String service) { if (service.startsWith("weblogin:")) return null; AccountManager accountManager = AccountManager.get(context); return accountManager.peekAuthToken(account, buildTokenKey(packageName, sig, service)); public Account getAccount() { if (account == null) account = new Account(accountName, "com.google"); return account; } public static boolean isPermitted(Context context, Account account, String packageName, String sig, String service) { if (service.startsWith("audience:server:client_id:")) { // https://developers.google.com/accounts/docs/CrossClientAuth Log.d(TAG, "Always permitting scope: " + service); return true; } else if (service.startsWith("weblogin:")) { if (Constants.GMS_PACKAGE_SIGNATURE_SHA1.equals(sig)) { Log.d(TAG, "Permitting weblogin, is Google singed app!"); return true; public String getPackageSignature() { if (packageSignature == null) packageSignature = PackageUtils.firstSignatureDigest(context, packageName); return packageSignature; } return false; } else if (!service.startsWith("oauth:") && !service.startsWith("oauth2:")) { public String buildTokenKey(String service) { return packageName + ":" + getPackageSignature() + ":" + service; } public String buildTokenKey() { return buildTokenKey(service); } public String buildPermKey() { return "perm." + buildTokenKey(); } public void setPermitted(boolean value) { setUserData(buildPermKey(), value ? "1" : "0"); } public boolean isPermitted() { if (!service.startsWith("oauth")) { if (context.getPackageManager().checkPermission(PERMISSION_TREE_BASE + service, packageName) == PackageManager.PERMISSION_GRANTED) { Log.d(TAG, "Permitting, permission is present"); return true; } } AccountManager accountManager = AccountManager.get(context); String perm = accountManager.getUserData(account, buildPermKey(packageName, sig, service)); String perm = getUserData(buildPermKey()); if (!"1".equals(perm)) { Log.d(TAG, "Not permitting, permission not stored for " + packageName + ": " + service); return false; } String exp = accountManager.getUserData(account, buildExpireKey(packageName, sig, service)); if (exp != null) { long expLong = Long.parseLong(exp); if (expLong < System.currentTimeMillis() / 1000L) { Log.d(TAG, "Permission for " + packageName + " / " + service + " present, but expired"); return false; return true; } public void setExpiry(long expiry) { setUserData(buildExpireKey(), Long.toString(expiry)); } return true; public String getUserData(String key) { return getAccountManager().getUserData(getAccount(), key); } public static void storePermission(Context context, Account account, String packageName, String service) { storePermission(context, account, packageName, PackageUtils.firstSignatureDigest(context, packageName), service); public void setUserData(String key, String value) { getAccountManager().setUserData(getAccount(), key, value); } public static void storePermission(Context context, Account account, String packageName, String sig, String service) { AccountManager accountManager = AccountManager.get(context); accountManager.setUserData(account, buildPermKey(packageName, sig, service), "1"); public String peekAuthToken() { return getAccountManager().peekAuthToken(getAccount(), buildTokenKey()); } public static String buildTokenKey(String packageName, String sig, String service) { public String getAuthToken() { if (service.startsWith("weblogin:")) return null; return packageName + ":" + sig + ":" + service; if (getExpiry() != -1 && getExpiry() < System.currentTimeMillis() / 1000L) { Log.d(TAG, "token present, but expired"); return null; } return peekAuthToken(); } public String buildExpireKey() { return "EXP." + buildTokenKey(); } public long getExpiry() { String exp = getUserData(buildExpireKey()); if (exp == null) return -1; return Long.parseLong(exp); } public void setAuthToken(String auth) { setAuthToken(service, auth); } public void setAuthToken(String service, String auth) { getAccountManager().setAuthToken(getAccount(), buildTokenKey(service), auth); } public void storeResponse(AuthResponse response) { if (service.startsWith("weblogin:")) return; if (response.accountId != null) setUserData("GoogleUserId", response.accountId); if (response.Sid != null) setAuthToken("SID", response.Sid); if (response.LSid != null) setAuthToken("LSID", response.LSid); if (response.expiry > 0) setExpiry(response.expiry); if (response.auth != null && response.expiry != 0 && response.storeConsentRemotely) setAuthToken(response.auth); } public static boolean isTrustGooglePermitted(Context context) { return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(PREF_KEY_TRUST_GOOGLE, true); } private boolean isSystemApp() { try { int flags = context.getPackageManager().getApplicationInfo(packageName, 0).flags; return (flags & FLAG_SYSTEM) > 0 || (flags & FLAG_UPDATED_SYSTEM_APP) > 0; } catch (PackageManager.NameNotFoundException e) { return false; } } private static String buildPermKey(String packageName, String sig, String service) { return "perm." + packageName + ":" + sig + ":" + service; public AuthResponse requestAuth(boolean legacy) throws IOException { if (isPermitted() || isTrustGooglePermitted(context)) { String token = getAuthToken(); if (token != null) { AuthResponse response = new AuthResponse(); response.issueAdvice = "stored"; response.auth = token; return response; } } AuthRequest request = new AuthRequest().fromContext(context) .app(packageName, getPackageSignature()) .email(accountName) .token(getAccountManager().getPassword(account)) .service(service); if (isSystemApp()) request.systemPartition(); if (isPermitted()) request.hasPermission(); if (legacy) { request.callerIsGms().calledFromAccountManager(); } else { request.callerIsApp(); } AuthResponse response = request.getResponse(); if (!isPermitted() && !isTrustGooglePermitted(context)) { response.auth = null; } else { storeResponse(response); } return response; } private static String buildExpireKey(String packageName, String sig, String service) { return "EXP." + packageName + ":" + sig + ":" + service; public String getService() { return service; } } src/org/microg/gms/auth/AuthManagerServiceImpl.java +35 −37 Original line number Diff line number Diff line Loading @@ -16,12 +16,11 @@ package org.microg.gms.auth; import android.accounts.Account; import android.accounts.AccountManager; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.RemoteException; import android.util.Base64; import android.util.Log; import com.google.android.auth.IAuthManagerService; Loading @@ -30,13 +29,18 @@ import com.google.android.gms.auth.AccountChangeEventsResponse; import org.microg.gms.common.PackageUtils; 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 org.microg.gms.auth.AskPermissionActivity.EXTRA_CONSENT_DATA; public class AuthManagerServiceImpl extends IAuthManagerService.Stub { private static final String TAG = "GmsAuthManagerSvc"; public static final String GOOGLE_ACCOUNT_TYPE = "com.google"; public static final String KEY_AUTHORITY = "authority"; public static final String KEY_ANDROID_PACKAGE_NAME = "androidPackageName"; public static final String KEY_CALLBACK_INTENT = "callback_intent"; public static final String KEY_CALLER_UID = "callerUid"; public static final String KEY_CLIENT_PACKAGE_NAME = "clientPackageName"; Loading @@ -50,7 +54,7 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { public static final String KEY_ERROR = "Error"; public static final String KEY_USER_RECOVERY_INTENT = "userRecoveryIntent"; private Context context; private final Context context; public AuthManagerServiceImpl(Context context) { this.context = context; Loading @@ -64,18 +68,26 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { boolean notify = extras.getBoolean(KEY_HANDLE_NOTIFICATION, false); Log.d(TAG, "getToken: account:" + accountName + " scope:" + scope + " extras:" + extras + ", notify: " + notify); Account account = new Account(accountName, GOOGLE_ACCOUNT_TYPE); String sig = PackageUtils.firstSignatureDigest(context, packageName); if (!AuthManager.isPermitted(context, account, packageName, sig, scope)) { AuthManager authManager = new AuthManager(context, accountName, packageName, scope); try { AuthResponse res = authManager.requestAuth(false); if (res.auth != null) { Log.d(TAG, "getToken: " + res.auth); Bundle result = new Bundle(); result.putString(KEY_AUTH_TOKEN, res.auth); result.putString(KEY_ERROR, "OK"); return result; } else { Bundle result = new Bundle(); result.putString(KEY_ERROR, "Unknown"); Intent i = new Intent(context, AskPermissionActivity.class); i.putExtras(extras); i.putExtra(AccountManager.KEY_ANDROID_PACKAGE_NAME, packageName); i.putExtra(AccountManager.KEY_ACCOUNT_TYPE, account.type); i.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name); i.putExtra(AccountManager.KEY_AUTHTOKEN, scope); i.putExtra(KEY_ANDROID_PACKAGE_NAME, packageName); i.putExtra(KEY_ACCOUNT_TYPE, GOOGLE_ACCOUNT_TYPE); i.putExtra(KEY_ACCOUNT_NAME, accountName); i.putExtra(KEY_AUTHTOKEN, scope); if (res.consentDataBase64 != null) i.putExtra(EXTRA_CONSENT_DATA, Base64.decode(res.consentDataBase64, Base64.DEFAULT)); if (notify) { i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(i); Loading @@ -84,22 +96,8 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { } return result; } try { AuthResponse response = new AuthRequest().fromContext(context) .app(packageName, sig) .callerIsApp() .email(accountName) .token(AccountManager.get(context).getPassword(account)) .service(scope) .getResponse(); AuthManager.storeResponse(context, account, packageName, sig, scope, response); Log.d("getToken", response.auth); Bundle result = new Bundle(); result.putString(KEY_AUTH_TOKEN, response.auth); result.putString(KEY_ERROR, "Unknown"); return result; } catch (Exception e) { Log.w("AuthManagerService", e); Log.w(TAG, e); throw new RemoteException(e.getMessage()); } } Loading src/org/microg/gms/auth/AuthResponse.java +6 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,12 @@ public class AuthResponse { public long expiry = -1; @ResponseField("storeConsentRemotely") public boolean storeConsentRemotely = true; @ResponseField("Permission") public String permission; @ResponseField("ScopeConsentDetails") public String scopeConsentDetails; @ResponseField("ConsentDataBase64") public String consentDataBase64; public static AuthResponse parse(String result) { AuthResponse response = new AuthResponse(); Loading src/org/microg/gms/auth/login/LoginActivity.java +7 −5 Original line number Diff line number Diff line Loading @@ -47,6 +47,9 @@ import org.microg.gms.people.PeopleManager; import java.util.Locale; import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; import static org.microg.gms.common.Constants.GMS_PACKAGE_SIGNATURE_SHA1; public class LoginActivity extends AssistantActivity { public static final String TMPL_NEW_ACCOUNT = "new_account"; public static final String EXTRA_TMPL = "tmpl"; Loading Loading @@ -192,10 +195,11 @@ public class LoginActivity extends AssistantActivity { } private void retrieveGmsToken(final Account account) { final String service = "ac2dm"; final AuthManager authManager = new AuthManager(this, account.name, GMS_PACKAGE_NAME, "ac2dm"); authManager.setPermitted(true); new AuthRequest().fromContext(this) .appIsGms() .service(service) .service(authManager.getService()) .email(account.name) .token(AccountManager.get(this).getPassword(account)) .systemPartition() Loading @@ -205,9 +209,7 @@ public class LoginActivity extends AssistantActivity { .getResponseAsync(new HttpFormClient.Callback<AuthResponse>() { @Override public void onResponse(AuthResponse response) { AuthManager.storeResponse(LoginActivity.this, account, Constants.GMS_PACKAGE_NAME, Constants.GMS_PACKAGE_SIGNATURE_SHA1, service, response); authManager.storeResponse(response); PeopleManager.loadUserInfo(LoginActivity.this, account); finish(); } Loading Loading
src/org/microg/gms/auth/AskPermissionActivity.java +63 −38 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package org.microg.gms.auth; import android.accounts.Account; import android.accounts.AccountAuthenticatorActivity; import android.accounts.AccountManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.graphics.Bitmap; Loading @@ -36,19 +35,29 @@ import android.widget.ListView; import android.widget.TextView; import com.google.android.gms.R; import com.squareup.wire.Wire; import org.microg.gms.common.PackageUtils; import org.microg.gms.people.PeopleManager; import java.io.IOException; 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_UID; public class AskPermissionActivity extends AccountAuthenticatorActivity { public static final String EXTRA_FROM_ACCOUNT_MANAGER = "from_account_manager"; public static final String EXTRA_CONSENT_DATA = "consent_data"; private static final String TAG = "GmsAuthAskPermission"; private Account account; private String packageName; private String service; private AuthManager authManager; private ConsentData consentData; private boolean fromAccountManager = false; @Override Loading @@ -63,13 +72,21 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity { lp.height = WindowManager.LayoutParams.WRAP_CONTENT; getWindow().setAttributes(lp); account = new Account(getIntent().getStringExtra(AccountManager.KEY_ACCOUNT_NAME), getIntent().getStringExtra(AccountManager.KEY_ACCOUNT_TYPE)); packageName = getIntent().getStringExtra(AccountManager.KEY_ANDROID_PACKAGE_NAME); service = getIntent().getStringExtra(AccountManager.KEY_AUTHTOKEN); account = new Account(getIntent().getStringExtra(KEY_ACCOUNT_NAME), getIntent().getStringExtra(KEY_ACCOUNT_TYPE)); packageName = getIntent().getStringExtra(KEY_ANDROID_PACKAGE_NAME); service = getIntent().getStringExtra(KEY_AUTHTOKEN); if (getIntent().hasExtra(EXTRA_CONSENT_DATA)) { try { consentData = new Wire().parseFrom(getIntent().getByteArrayExtra(EXTRA_CONSENT_DATA), ConsentData.class); Log.d(TAG, "Consent: " + consentData); } catch (Exception ignored) { } } if (getIntent().hasExtra(EXTRA_FROM_ACCOUNT_MANAGER)) fromAccountManager = true; int callerUid = getIntent().getIntExtra(AccountManager.KEY_CALLER_UID, 0); int callerUid = getIntent().getIntExtra(KEY_CALLER_UID, 0); PackageUtils.checkPackageUid(this, packageName, callerUid); authManager = new AuthManager(this, account.name, packageName, service); // receive package info PackageManager packageManager = getPackageManager(); Loading Loading @@ -126,7 +143,7 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity { } public void onAllow() { AuthManager.storePermission(this, account, packageName, service); authManager.setPermitted(true); findViewById(android.R.id.button1).setEnabled(false); findViewById(android.R.id.button2).setEnabled(false); findViewById(R.id.progress_bar).setVisibility(View.VISIBLE); Loading @@ -134,27 +151,13 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity { new Thread(new Runnable() { @Override public void run() { Context context = AskPermissionActivity.this; String sig = PackageUtils.firstSignatureDigest(context, packageName); AuthRequest request = new AuthRequest().fromContext(context) .email(account.name) .token(AccountManager.get(context).getPassword(account)) .service(service) .app(packageName, sig) .hasPermission(); if (fromAccountManager) { request.callerIsGms().calledFromAccountManager(); } else { request.callerIsApp(); } try { AuthResponse response = request.getResponse(); AuthManager.storeResponse(context, account, packageName, sig, service, response); AuthResponse response = authManager.requestAuth(fromAccountManager); Bundle result = new Bundle(); result.putString(AccountManager.KEY_AUTHTOKEN, response.auth); result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); result.putString(AccountManager.KEY_ANDROID_PACKAGE_NAME, packageName); result.putString(KEY_AUTHTOKEN, response.auth); result.putString(KEY_ACCOUNT_NAME, account.name); result.putString(KEY_ACCOUNT_TYPE, account.type); result.putString(KEY_ANDROID_PACKAGE_NAME, packageName); result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); setAccountAuthenticatorResult(result); } catch (IOException e) { Loading @@ -167,6 +170,7 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity { } public void onDeny() { authManager.setPermitted(false); finish(); } Loading @@ -179,6 +183,36 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity { return service.startsWith("oauth2:") || service.startsWith("oauth:"); } private String getScopeLabel(String scope) { if (consentData != null) { for (ConsentData.ScopeDetails scopeDetails : consentData.scopes) { if (scope.equals(scopeDetails.id)) { return scopeDetails.title; } } } String labelResourceId = "permission_scope_"; String escapedScope = scope.replace("/", "_").replace("-", "_"); if (scope.startsWith("https://")) { labelResourceId += escapedScope.substring(8); } else { labelResourceId += escapedScope; } int labelResource = getResources().getIdentifier(labelResourceId, "string", getPackageName()); if (labelResource != 0) { return getString(labelResource); } return "unknown"; } private String getServiceLabel(String service) { int labelResource = getResources().getIdentifier("permission_service_" + service + "_label", "string", getPackageName()); if (labelResource != 0) { return getString(labelResource); } return "unknown"; } private class PermissionAdapter extends BaseAdapter { @Override Loading Loading @@ -206,20 +240,11 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity { @Override public View getView(int position, View convertView, ViewGroup parent) { String item = getItem(position); String label = "unknown"; String labelResourceId; String label; if (isOAuth()) { if (item.startsWith("https://")) { labelResourceId = "permission_scope_" + item.substring(8).replace("/", "_").replace("-", "_"); } else { labelResourceId = "permission_scope_" + item.replace("/", "_").replace("-", "_"); } label = getScopeLabel(item); } else { labelResourceId = "permission_service_" + item + "_label"; } int labelResource = getResources().getIdentifier(labelResourceId, "string", getPackageName()); if (labelResource != 0) { label = getString(labelResource); label = getServiceLabel(item); } View view = convertView; if (view == null) { Loading
src/org/microg/gms/auth/AuthManager.java +151 −61 Original line number Diff line number Diff line Loading @@ -20,96 +20,186 @@ import android.accounts.Account; import android.accounts.AccountManager; import android.content.Context; import android.content.pm.PackageManager; import android.preference.PreferenceManager; import android.util.Log; import org.microg.gms.common.Constants; import org.microg.gms.common.PackageUtils; import java.io.IOException; import static android.content.pm.ApplicationInfo.FLAG_SYSTEM; import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; 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_KEY_TRUST_GOOGLE = "auth_manager_trust_google"; public static void storeResponse(Context context, Account account, String packageName, String sig, String service, AuthResponse response) { if (service.startsWith("weblogin:")) return; AccountManager accountManager = AccountManager.get(context); if (response.accountId != null) accountManager.setUserData(account, "GoogleUserId", response.accountId); if (response.Sid != null) accountManager.setAuthToken(account, buildTokenKey(packageName, sig, "SID"), response.Sid); if (response.LSid != null) accountManager.setAuthToken(account, buildTokenKey(packageName, sig, "LSID"), response.LSid); if (response.expiry > 0) accountManager.setUserData(account, buildExpireKey(packageName, sig, service), Long.toString(response.expiry)); if (response.auth != null && response.expiry != 0 && response.storeConsentRemotely) { accountManager.setAuthToken(account, buildTokenKey(packageName, sig, service), response.auth); accountManager.setUserData(account, buildPermKey(packageName, sig, service), "1"); private final Context context; private final String accountName; private final String packageName; private final String service; private AccountManager accountManager; private Account account; private String packageSignature; public AuthManager(Context context, String accountName, String packageName, String service) { this.context = context; this.accountName = accountName; this.packageName = packageName; this.service = service; } public AccountManager getAccountManager() { if (accountManager == null) accountManager = AccountManager.get(context); return accountManager; } public static String getToken(Context context, Account account, String packageName, String sig, String service) { if (service.startsWith("weblogin:")) return null; AccountManager accountManager = AccountManager.get(context); return accountManager.peekAuthToken(account, buildTokenKey(packageName, sig, service)); public Account getAccount() { if (account == null) account = new Account(accountName, "com.google"); return account; } public static boolean isPermitted(Context context, Account account, String packageName, String sig, String service) { if (service.startsWith("audience:server:client_id:")) { // https://developers.google.com/accounts/docs/CrossClientAuth Log.d(TAG, "Always permitting scope: " + service); return true; } else if (service.startsWith("weblogin:")) { if (Constants.GMS_PACKAGE_SIGNATURE_SHA1.equals(sig)) { Log.d(TAG, "Permitting weblogin, is Google singed app!"); return true; public String getPackageSignature() { if (packageSignature == null) packageSignature = PackageUtils.firstSignatureDigest(context, packageName); return packageSignature; } return false; } else if (!service.startsWith("oauth:") && !service.startsWith("oauth2:")) { public String buildTokenKey(String service) { return packageName + ":" + getPackageSignature() + ":" + service; } public String buildTokenKey() { return buildTokenKey(service); } public String buildPermKey() { return "perm." + buildTokenKey(); } public void setPermitted(boolean value) { setUserData(buildPermKey(), value ? "1" : "0"); } public boolean isPermitted() { if (!service.startsWith("oauth")) { if (context.getPackageManager().checkPermission(PERMISSION_TREE_BASE + service, packageName) == PackageManager.PERMISSION_GRANTED) { Log.d(TAG, "Permitting, permission is present"); return true; } } AccountManager accountManager = AccountManager.get(context); String perm = accountManager.getUserData(account, buildPermKey(packageName, sig, service)); String perm = getUserData(buildPermKey()); if (!"1".equals(perm)) { Log.d(TAG, "Not permitting, permission not stored for " + packageName + ": " + service); return false; } String exp = accountManager.getUserData(account, buildExpireKey(packageName, sig, service)); if (exp != null) { long expLong = Long.parseLong(exp); if (expLong < System.currentTimeMillis() / 1000L) { Log.d(TAG, "Permission for " + packageName + " / " + service + " present, but expired"); return false; return true; } public void setExpiry(long expiry) { setUserData(buildExpireKey(), Long.toString(expiry)); } return true; public String getUserData(String key) { return getAccountManager().getUserData(getAccount(), key); } public static void storePermission(Context context, Account account, String packageName, String service) { storePermission(context, account, packageName, PackageUtils.firstSignatureDigest(context, packageName), service); public void setUserData(String key, String value) { getAccountManager().setUserData(getAccount(), key, value); } public static void storePermission(Context context, Account account, String packageName, String sig, String service) { AccountManager accountManager = AccountManager.get(context); accountManager.setUserData(account, buildPermKey(packageName, sig, service), "1"); public String peekAuthToken() { return getAccountManager().peekAuthToken(getAccount(), buildTokenKey()); } public static String buildTokenKey(String packageName, String sig, String service) { public String getAuthToken() { if (service.startsWith("weblogin:")) return null; return packageName + ":" + sig + ":" + service; if (getExpiry() != -1 && getExpiry() < System.currentTimeMillis() / 1000L) { Log.d(TAG, "token present, but expired"); return null; } return peekAuthToken(); } public String buildExpireKey() { return "EXP." + buildTokenKey(); } public long getExpiry() { String exp = getUserData(buildExpireKey()); if (exp == null) return -1; return Long.parseLong(exp); } public void setAuthToken(String auth) { setAuthToken(service, auth); } public void setAuthToken(String service, String auth) { getAccountManager().setAuthToken(getAccount(), buildTokenKey(service), auth); } public void storeResponse(AuthResponse response) { if (service.startsWith("weblogin:")) return; if (response.accountId != null) setUserData("GoogleUserId", response.accountId); if (response.Sid != null) setAuthToken("SID", response.Sid); if (response.LSid != null) setAuthToken("LSID", response.LSid); if (response.expiry > 0) setExpiry(response.expiry); if (response.auth != null && response.expiry != 0 && response.storeConsentRemotely) setAuthToken(response.auth); } public static boolean isTrustGooglePermitted(Context context) { return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(PREF_KEY_TRUST_GOOGLE, true); } private boolean isSystemApp() { try { int flags = context.getPackageManager().getApplicationInfo(packageName, 0).flags; return (flags & FLAG_SYSTEM) > 0 || (flags & FLAG_UPDATED_SYSTEM_APP) > 0; } catch (PackageManager.NameNotFoundException e) { return false; } } private static String buildPermKey(String packageName, String sig, String service) { return "perm." + packageName + ":" + sig + ":" + service; public AuthResponse requestAuth(boolean legacy) throws IOException { if (isPermitted() || isTrustGooglePermitted(context)) { String token = getAuthToken(); if (token != null) { AuthResponse response = new AuthResponse(); response.issueAdvice = "stored"; response.auth = token; return response; } } AuthRequest request = new AuthRequest().fromContext(context) .app(packageName, getPackageSignature()) .email(accountName) .token(getAccountManager().getPassword(account)) .service(service); if (isSystemApp()) request.systemPartition(); if (isPermitted()) request.hasPermission(); if (legacy) { request.callerIsGms().calledFromAccountManager(); } else { request.callerIsApp(); } AuthResponse response = request.getResponse(); if (!isPermitted() && !isTrustGooglePermitted(context)) { response.auth = null; } else { storeResponse(response); } return response; } private static String buildExpireKey(String packageName, String sig, String service) { return "EXP." + packageName + ":" + sig + ":" + service; public String getService() { return service; } }
src/org/microg/gms/auth/AuthManagerServiceImpl.java +35 −37 Original line number Diff line number Diff line Loading @@ -16,12 +16,11 @@ package org.microg.gms.auth; import android.accounts.Account; import android.accounts.AccountManager; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.RemoteException; import android.util.Base64; import android.util.Log; import com.google.android.auth.IAuthManagerService; Loading @@ -30,13 +29,18 @@ import com.google.android.gms.auth.AccountChangeEventsResponse; import org.microg.gms.common.PackageUtils; 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 org.microg.gms.auth.AskPermissionActivity.EXTRA_CONSENT_DATA; public class AuthManagerServiceImpl extends IAuthManagerService.Stub { private static final String TAG = "GmsAuthManagerSvc"; public static final String GOOGLE_ACCOUNT_TYPE = "com.google"; public static final String KEY_AUTHORITY = "authority"; public static final String KEY_ANDROID_PACKAGE_NAME = "androidPackageName"; public static final String KEY_CALLBACK_INTENT = "callback_intent"; public static final String KEY_CALLER_UID = "callerUid"; public static final String KEY_CLIENT_PACKAGE_NAME = "clientPackageName"; Loading @@ -50,7 +54,7 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { public static final String KEY_ERROR = "Error"; public static final String KEY_USER_RECOVERY_INTENT = "userRecoveryIntent"; private Context context; private final Context context; public AuthManagerServiceImpl(Context context) { this.context = context; Loading @@ -64,18 +68,26 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { boolean notify = extras.getBoolean(KEY_HANDLE_NOTIFICATION, false); Log.d(TAG, "getToken: account:" + accountName + " scope:" + scope + " extras:" + extras + ", notify: " + notify); Account account = new Account(accountName, GOOGLE_ACCOUNT_TYPE); String sig = PackageUtils.firstSignatureDigest(context, packageName); if (!AuthManager.isPermitted(context, account, packageName, sig, scope)) { AuthManager authManager = new AuthManager(context, accountName, packageName, scope); try { AuthResponse res = authManager.requestAuth(false); if (res.auth != null) { Log.d(TAG, "getToken: " + res.auth); Bundle result = new Bundle(); result.putString(KEY_AUTH_TOKEN, res.auth); result.putString(KEY_ERROR, "OK"); return result; } else { Bundle result = new Bundle(); result.putString(KEY_ERROR, "Unknown"); Intent i = new Intent(context, AskPermissionActivity.class); i.putExtras(extras); i.putExtra(AccountManager.KEY_ANDROID_PACKAGE_NAME, packageName); i.putExtra(AccountManager.KEY_ACCOUNT_TYPE, account.type); i.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name); i.putExtra(AccountManager.KEY_AUTHTOKEN, scope); i.putExtra(KEY_ANDROID_PACKAGE_NAME, packageName); i.putExtra(KEY_ACCOUNT_TYPE, GOOGLE_ACCOUNT_TYPE); i.putExtra(KEY_ACCOUNT_NAME, accountName); i.putExtra(KEY_AUTHTOKEN, scope); if (res.consentDataBase64 != null) i.putExtra(EXTRA_CONSENT_DATA, Base64.decode(res.consentDataBase64, Base64.DEFAULT)); if (notify) { i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(i); Loading @@ -84,22 +96,8 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { } return result; } try { AuthResponse response = new AuthRequest().fromContext(context) .app(packageName, sig) .callerIsApp() .email(accountName) .token(AccountManager.get(context).getPassword(account)) .service(scope) .getResponse(); AuthManager.storeResponse(context, account, packageName, sig, scope, response); Log.d("getToken", response.auth); Bundle result = new Bundle(); result.putString(KEY_AUTH_TOKEN, response.auth); result.putString(KEY_ERROR, "Unknown"); return result; } catch (Exception e) { Log.w("AuthManagerService", e); Log.w(TAG, e); throw new RemoteException(e.getMessage()); } } Loading
src/org/microg/gms/auth/AuthResponse.java +6 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,12 @@ public class AuthResponse { public long expiry = -1; @ResponseField("storeConsentRemotely") public boolean storeConsentRemotely = true; @ResponseField("Permission") public String permission; @ResponseField("ScopeConsentDetails") public String scopeConsentDetails; @ResponseField("ConsentDataBase64") public String consentDataBase64; public static AuthResponse parse(String result) { AuthResponse response = new AuthResponse(); Loading
src/org/microg/gms/auth/login/LoginActivity.java +7 −5 Original line number Diff line number Diff line Loading @@ -47,6 +47,9 @@ import org.microg.gms.people.PeopleManager; import java.util.Locale; import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; import static org.microg.gms.common.Constants.GMS_PACKAGE_SIGNATURE_SHA1; public class LoginActivity extends AssistantActivity { public static final String TMPL_NEW_ACCOUNT = "new_account"; public static final String EXTRA_TMPL = "tmpl"; Loading Loading @@ -192,10 +195,11 @@ public class LoginActivity extends AssistantActivity { } private void retrieveGmsToken(final Account account) { final String service = "ac2dm"; final AuthManager authManager = new AuthManager(this, account.name, GMS_PACKAGE_NAME, "ac2dm"); authManager.setPermitted(true); new AuthRequest().fromContext(this) .appIsGms() .service(service) .service(authManager.getService()) .email(account.name) .token(AccountManager.get(this).getPassword(account)) .systemPartition() Loading @@ -205,9 +209,7 @@ public class LoginActivity extends AssistantActivity { .getResponseAsync(new HttpFormClient.Callback<AuthResponse>() { @Override public void onResponse(AuthResponse response) { AuthManager.storeResponse(LoginActivity.this, account, Constants.GMS_PACKAGE_NAME, Constants.GMS_PACKAGE_SIGNATURE_SHA1, service, response); authManager.storeResponse(response); PeopleManager.loadUserInfo(LoginActivity.this, account); finish(); } Loading