Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 21571047 authored by Jonathan Klee's avatar Jonathan Klee
Browse files

Merge remote-tracking branch 'upstream/master'

parents 53c463e5 cb836474
Loading
Loading
Loading
Loading
+83 −55
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.accounts.AccountAuthenticatorActivity;
import android.accounts.AccountManager;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
@@ -58,17 +59,73 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity {
    public static final String EXTRA_CONSENT_DATA = "consent_data";

    private static final String TAG = "GmsAuthAskPermission";
    private AuthManager authManager;
    private IntentData data;

    private static class IntentData {
        private String accountName;
        private String accountType;
        private Account account;

        private String packageName;
        private String service;
    private AuthManager authManager;

        private int callerUid;
        private int callerPid;

        private ConsentData consentData;
        private boolean fromAccountManager = false;

        private CharSequence appLabel;
        private Drawable appIcon;

        private IntentData(Intent intent) {
            if (intent != null) {
                accountName = intent.getStringExtra(KEY_ACCOUNT_NAME);
                accountType = intent.getStringExtra(KEY_ACCOUNT_TYPE);
                packageName = intent.getStringExtra(KEY_ANDROID_PACKAGE_NAME);
                service = intent.getStringExtra(KEY_AUTHTOKEN);
                callerUid = intent.getIntExtra(KEY_CALLER_UID, 0);
                callerPid = intent.getIntExtra(KEY_CALLER_PID, 0);
                fromAccountManager = intent.hasExtra(EXTRA_FROM_ACCOUNT_MANAGER);
                if (intent.hasExtra(EXTRA_CONSENT_DATA)) {
                    try {
                        consentData = ConsentData.ADAPTER.decode(intent.getByteArrayExtra(EXTRA_CONSENT_DATA));
                    } catch (Exception e) {
                        // Ignore
                    }
                }
            }
            if (accountName != null && accountType != null) {
                account = new Account(accountName, accountType);
            }
        }

        private void verify(Context context) throws Exception {
            if (accountName == null || accountType == null || account == null) throw new IllegalArgumentException("Required account information missing");
            if (packageName == null || service == null) throw new IllegalArgumentException("Required request information missing");
            if (callerUid == 0) throw new IllegalArgumentException("Required caller information missing");
            PackageUtils.getAndCheckPackage(context, packageName, callerUid, callerPid);

            PackageManager packageManager = context.getPackageManager();
            ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0);
            appLabel = packageManager.getApplicationLabel(applicationInfo);
            appIcon = packageManager.getApplicationIcon(applicationInfo);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.ask_permission);
        data = new IntentData(getIntent());
        try {
            data.verify(this);
        } catch (Exception e) {
            Log.w(TAG, "Verification failed", e);
            finish();
            return;
        }

        // This makes the dialog take up the full width
        WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
@@ -77,57 +134,28 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity {
        lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
        getWindow().setAttributes(lp);

        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 = ConsentData.ADAPTER.decode(getIntent().getByteArrayExtra(EXTRA_CONSENT_DATA));
            } catch (Exception e) {
                Log.w(TAG, e);
            }
        } else {
            Log.d(TAG, "No Consent details attached");
        }

        NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        nm.cancel(packageName.hashCode());
        nm.cancel(data.packageName.hashCode());

        if (getIntent().hasExtra(EXTRA_FROM_ACCOUNT_MANAGER)) fromAccountManager = true;
        int callerUid = getIntent().getIntExtra(KEY_CALLER_UID, 0);
        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);
        authManager = new AuthManager(this, data.accountName, data.packageName, data.service);

        // receive package info
        PackageManager packageManager = getPackageManager();
        ApplicationInfo applicationInfo;
        try {
            applicationInfo = packageManager.getApplicationInfo(packageName, 0);
        } catch (PackageManager.NameNotFoundException e) {
            Log.w(TAG, "Failed to find package " + packageName, e);
            finish();
            return;
        }
        CharSequence appLabel = packageManager.getApplicationLabel(applicationInfo);
        Drawable appIcon = packageManager.getApplicationIcon(applicationInfo);
        Bitmap profileIcon = PeopleManager.getOwnerAvatarBitmap(this, account.name, false);
        Bitmap profileIcon = PeopleManager.getOwnerAvatarBitmap(this, data.accountName, false);

        // receive profile icon
        if (profileIcon != null) {
            ((ImageView) findViewById(R.id.account_photo)).setImageBitmap(profileIcon);
        } else {
            new Thread(() -> {
                final Bitmap profileIcon1 = PeopleManager.getOwnerAvatarBitmap(AskPermissionActivity.this, account.name, true);
                final Bitmap profileIcon1 = PeopleManager.getOwnerAvatarBitmap(AskPermissionActivity.this, data.accountName, true);
                runOnUiThread(() -> ((ImageView) findViewById(R.id.account_photo)).setImageBitmap(profileIcon1));
            }).start();
        }

        ((ImageView) findViewById(R.id.app_icon)).setImageDrawable(appIcon);
        ((ImageView) findViewById(R.id.app_icon)).setImageDrawable(data.appIcon);
        if (isOAuth()) {
            ((TextView) findViewById(R.id.title)).setText(getString(R.string.ask_scope_permission_title, appLabel));
            ((TextView) findViewById(R.id.title)).setText(getString(R.string.ask_scope_permission_title, data.appLabel));
        } else {
            ((TextView) findViewById(R.id.title)).setText(getString(R.string.ask_service_permission_title, appLabel));
            ((TextView) findViewById(R.id.title)).setText(getString(R.string.ask_service_permission_title, data.appLabel));
        }
        findViewById(android.R.id.button1).setOnClickListener(v -> onAllow());
        findViewById(android.R.id.button2).setOnClickListener(v -> onDeny());
@@ -142,12 +170,12 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity {
        findViewById(R.id.no_progress_bar).setVisibility(GONE);
        new Thread(() -> {
            try {
                AuthResponse response = authManager.requestAuth(fromAccountManager);
                AuthResponse response = authManager.requestAuth(data.fromAccountManager);
                Bundle result = new Bundle();
                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.putString(KEY_ACCOUNT_NAME, data.accountName);
                result.putString(KEY_ACCOUNT_TYPE, data.accountType);
                result.putString(KEY_ANDROID_PACKAGE_NAME, data.packageName);
                result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
                setAccountAuthenticatorResult(result);
            } catch (IOException e) {
@@ -164,20 +192,20 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity {

    @Override
    public void finish() {
        if (packageName != null) {
        if (data.packageName != null) {
            NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            nm.cancel(packageName.hashCode());
            nm.cancel(data.packageName.hashCode());
        }
        super.finish();
    }

    private boolean isOAuth() {
        return service.startsWith("oauth2:") || service.startsWith("oauth:");
        return data.service.startsWith("oauth2:") || data.service.startsWith("oauth:");
    }

    private String getScopeLabel(String scope) {
        if (consentData != null) {
            for (ConsentData.ScopeDetails scopeDetails : consentData.scopes) {
        if (data.consentData != null) {
            for (ConsentData.ScopeDetails scopeDetails : data.consentData.scopes) {
                if (scope.equals(scopeDetails.id)) {
                    return scopeDetails.title;
                }
@@ -198,8 +226,8 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity {
    }

    private String getScopeDescription(String scope) {
        if (consentData != null) {
            for (ConsentData.ScopeDetails scopeDetails : consentData.scopes) {
        if (data.consentData != null) {
            for (ConsentData.ScopeDetails scopeDetails : data.consentData.scopes) {
                if (scope.equals(scopeDetails.id)) {
                    return scopeDetails.description;
                }
@@ -221,7 +249,7 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity {
        @Override
        public int getCount() {
            if (isOAuth()) {
                return service.split(" ").length;
                return data.service.split(" ").length;
            }
            return 1;
        }
@@ -229,10 +257,10 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity {
        @Override
        public String getItem(int position) {
            if (isOAuth()) {
                String tokens = service.split(":", 2)[1];
                String tokens = data.service.split(":", 2)[1];
                return tokens.split(" ")[position];
            }
            return service;
            return data.service;
        }

        @Override
+8 −20
Original line number Diff line number Diff line
@@ -8,28 +8,10 @@ package org.microg.gms.ui
import android.annotation.SuppressLint
import android.os.Bundle
import android.text.format.DateUtils
import android.util.Base64
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.core.view.ViewCompat
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentContainerView
import androidx.fragment.app.add
import androidx.fragment.app.commit
import androidx.lifecycle.lifecycleScope
import androidx.preference.*
import com.google.android.gms.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.json.JSONObject
import org.microg.gms.gcm.GcmDatabase
import org.microg.gms.gcm.PushRegisterManager
import org.microg.gms.safetynet.SafetyNetDatabase
import org.microg.gms.safetynet.SafetyNetRequestType
import org.microg.gms.safetynet.SafetyNetRequestType.ATTESTATION
import org.microg.gms.safetynet.SafetyNetRequestType.RECAPTCHA

@@ -58,8 +40,14 @@ class SafetyNetAppPreferencesFragment : PreferenceFragmentCompat() {
        lifecycleScope.launchWhenResumed {
            val context = requireContext()
            val summaries =
                packageName?.let { packageName -> SafetyNetDatabase(context).use { it.getRecentRequests(packageName) } }
                    .orEmpty()
                packageName?.let { packageName ->
                    val db = SafetyNetDatabase(context)
                    try {
                        db.getRecentRequests(packageName)
                    } finally {
                        db.close()
                    }
                }.orEmpty()
            recents.removeAll()
            recents.addPreference(recentsNone)
            recentsNone.isVisible = summaries.isEmpty()
+6 −1
Original line number Diff line number Diff line
@@ -103,7 +103,12 @@ class SafetyNetPreferencesFragment : PreferenceFragmentCompat() {
        lifecycleScope.launchWhenResumed {
            val context = requireContext()
            val (apps, showAll) = withContext(Dispatchers.IO) {
                val apps = SafetyNetDatabase(requireContext()).use { it.recentApps }
                val db = SafetyNetDatabase(context)
                val apps = try {
                    db.recentApps
                } finally {
                    db.close()
                }
                apps.map { app ->
                    app to context.packageManager.getApplicationInfoIfExists(app.first)
                }.mapNotNull { (app, info) ->
+4 −5
Original line number Diff line number Diff line
@@ -167,7 +167,8 @@ suspend fun RequestOptions.checkIsValid(context: Context, facetId: String, packa
        }
        // FIXME: Standard suggests doing additional checks, but this is already sensible enough
    } else if (facetId.startsWith("android:apk-key-hash:") && packageName != null) {
        val sha256FacetId = getAltFacetId(context, packageName, facetId)
        val sha256FacetId = getAltFacetId(context, packageName, facetId) ?:
            throw RequestHandlingException(NOT_ALLOWED_ERR, "Can't resolve $facetId to SHA-256 Facet")
        if (!isAssetLinked(context, rpId, sha256FacetId, packageName)) {
            throw RequestHandlingException(NOT_ALLOWED_ERR, "RP ID $rpId not allowed from facet $sha256FacetId")
        }
@@ -218,7 +219,7 @@ fun getApkKeyHashFacetId(context: Context, packageName: String): String {
    return "android:apk-key-hash:${digest.toBase64(HASH_BASE64_FLAGS)}"
}

fun getAltFacetId(context: Context, packageName: String, facetId: String): String {
fun getAltFacetId(context: Context, packageName: String, facetId: String): String? {
    val firstSignature = context.packageManager.getSignatures(packageName).firstOrNull()
        ?: throw RequestHandlingException(NOT_ALLOWED_ERR, "Unknown package $packageName")
    return when (facetId) {
@@ -228,9 +229,7 @@ fun getAltFacetId(context: Context, packageName: String, facetId: String): Strin
        "android:apk-key-hash-sha256:${firstSignature.digest("SHA-256").toBase64(HASH_BASE64_FLAGS)}" -> {
            "android:apk-key-hash:${firstSignature.digest("SHA1").toBase64(HASH_BASE64_FLAGS)}"
        }
        else -> {
            throw RequestHandlingException(NOT_ALLOWED_ERR, "Package $packageName does not match facet $facetId")
        }
        else -> null
    }
}

+1 −1
Original line number Diff line number Diff line
@@ -153,7 +153,7 @@ abstract class TransportHandler(val transport: Transport, val callback: Transpor
                if (connection.hasCtap1Support &&
                    !connection.canMakeCredentialWithoutUserVerification && connection.hasClientPin &&
                    options.registerOptions.authenticatorSelection.requireUserVerification != REQUIRED &&
                    !options.registerOptions.authenticatorSelection.requireResidentKey
                    options.registerOptions.authenticatorSelection.requireResidentKey != true
                ) {
                    Log.d(TAG, "Using CTAP1/U2F for PIN-less registration")
                    ctap1register(connection, options, clientDataHash)
Loading