cmd/tailscale: warn when debug signed and Google Sign-In fails

Fixes tailscale/tailscale#1036

Signed-off-by: Elias Naur <mail@eliasnaur.com>
pull/5/head
Elias Naur 4 years ago
parent 1c61cc0702
commit 2c9fddab4f

@ -8,11 +8,14 @@ import android.app.Application;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.BroadcastReceiver;
import android.content.pm.PackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.Signature;
import android.provider.Settings;
import android.net.ConnectivityManager;
import android.net.Uri;
@ -189,6 +192,16 @@ public class App extends Application {
});
}
// getPackageSignatureFingerprint returns the first package signing certificate, if any.
byte[] getPackageCertificate() throws Exception {
PackageInfo info;
info = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
for (Signature signature : info.signatures) {
return signature.toByteArray();
}
return null;
}
static native void onVPNPrepared();
private static native void onConnectivityChanged(boolean connected);
}

@ -5,6 +5,8 @@
package main
import (
"crypto/sha1"
"fmt"
"log"
"sort"
"strings"
@ -105,6 +107,10 @@ const serverOAuthID = "744055068597-hv4opg0h7vskq1hv37nq3u26t8c15qk0.apps.google
const enabledKey = "ipn_enabled"
// releaseCertFingerprint is the SHA-1 fingerprint of the Google Play Store signing key.
// It is used to check whether the app is signed for release.
const releaseCertFingerprint = "86:9D:11:8B:63:1E:F8:35:C6:D9:C2:66:53:BC:28:22:2F:B8:C1:AE"
// backendEvents receives events from the UI (Activity, Tile etc.) to the backend.
var backendEvents = make(chan UIEvent)
@ -527,6 +533,12 @@ func (a *App) runUI() error {
TokenType: ipn.GoogleIDTokenType,
},
})
} else {
// Warn about possible debug certificate.
if !a.isReleaseSigned() {
ui.ShowMessage("Google Sign-In failed because the app is not signed for Play Store")
w.Invalidate()
}
}
case <-a.updates:
a.mu.Lock()
@ -560,7 +572,7 @@ func (a *App) runUI() error {
}
}
case <-onVPNRevoked:
ui.NotifyRevoked()
ui.ShowMessage("VPN access denied or another VPN service is always-on")
w.Invalidate()
case e := <-w.Events():
switch e := e.(type) {
@ -596,6 +608,32 @@ func (a *App) runUI() error {
}
}
// isReleaseSigned reports whether the app is signed with a release
// signature.
func (a *App) isReleaseSigned() bool {
var cert []byte
err := jni.Do(a.jvm, func(env jni.Env) error {
cls := jni.GetObjectClass(env, a.appCtx)
m := jni.GetMethodID(env, cls, "getPackageCertificate", "()[B")
str, err := jni.CallObjectMethod(env, a.appCtx, m)
if err != nil {
return err
}
cert = jni.GetByteArrayElements(env, jni.ByteArray(str))
return nil
})
if err != nil {
fatalErr(err)
}
h := sha1.New()
h.Write(cert)
fingerprint := h.Sum(nil)
hex := fmt.Sprintf("%x", fingerprint)
// Strip colons and convert to lower case to ease comparing.
wantFingerprint := strings.ReplaceAll(strings.ToLower(releaseCertFingerprint), ":", "")
return hex == wantFingerprint
}
// attachPeer registers an Android Fragment instance for
// handling onActivityResult callbacks.
func (a *App) attachPeer(act jni.Object) {

@ -306,8 +306,8 @@ func (ui *UI) layout(gtx layout.Context, sysIns system.Insets, state *clientStat
return ui.events
}
func (ui *UI) NotifyRevoked() {
ui.message.text = "VPN access denied or another VPN service is always-on"
func (ui *UI) ShowMessage(msg string) {
ui.message.text = msg
ui.message.t0 = time.Now()
}

Loading…
Cancel
Save