Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

android: add INIT_WITH_AUTHKEY intent #122

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
<intent-filter>
<action android:name="com.tailscale.ipn.CONNECT_VPN" />
<action android:name="com.tailscale.ipn.DISCONNECT_VPN" />
<action android:name="com.tailscale.ipn.INIT_WITH_AUTHKEY" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm reading this correctly, any app can send a disconnect broadcast, then this and cause the device to join a particular tailnet. We need to make sure this intent is not callable or receivable by other applications.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this be len(b.backend.ListProfiles()) == 0 ? If any authenticated profiles exist, whether actively logged in or not, then we wouldn't accept an authkey?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I didn't finish this part.

</intent-filter>
</receiver>
<service android:name=".IPNService"
Expand Down
4 changes: 4 additions & 0 deletions android/src/main/java/com/tailscale/ipn/IPNReceiver.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import android.content.Intent;
import androidx.work.WorkManager;
import androidx.work.OneTimeWorkRequest;
import android.util.Log;

public class IPNReceiver extends BroadcastReceiver {

Expand All @@ -21,6 +22,9 @@ public void onReceive(Context context, Intent intent) {
workManager.enqueue(new OneTimeWorkRequest.Builder(StartVPNWorker.class).build());
} else if (intent.getAction() == "com.tailscale.ipn.DISCONNECT_VPN") {
workManager.enqueue(new OneTimeWorkRequest.Builder(StopVPNWorker.class).build());
} else if (intent.getAction() == "com.tailscale.ipn.INIT_WITH_AUTHKEY") {
String key = intent.getStringExtra("authkey");
IPNService.setAuthKeyForNextConnect(key);
}
}
}
2 changes: 2 additions & 0 deletions android/src/main/java/com/tailscale/ipn/IPNService.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ public void updateStatusNotification(String title, String message) {
startForeground(App.STATUS_NOTIFICATION_ID, builder.build());
}

public static native void setAuthKeyForNextConnect(String authKey);

private native void connect();
private native void disconnect();

Expand Down
17 changes: 15 additions & 2 deletions cmd/tailscale/callbacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ var (
// onConnectivityChange is notified every time the network
// conditions change.
onConnectivityChange = make(chan bool, 1)
// onSetAuthKeyForNextConnect gets an authkey for use in the next connect
// when IPNReceiver receives one.
onSetAuthKeyForNextConnect = make(chan string, 1)

// onGoogleToken receives google ID tokens.
onGoogleToken = make(chan string)
Expand Down Expand Up @@ -109,12 +112,12 @@ func Java_com_tailscale_ipn_IPNService_disconnect(env *C.JNIEnv, this C.jobject)

//export Java_com_tailscale_ipn_StartVPNWorker_connect
func Java_com_tailscale_ipn_StartVPNWorker_connect(env *C.JNIEnv, this C.jobject) {
requestBackend(ConnectEvent{Enable: true})
requestBackend(ConnectEvent{Enable: true})
}

//export Java_com_tailscale_ipn_StopVPNWorker_disconnect
func Java_com_tailscale_ipn_StopVPNWorker_disconnect(env *C.JNIEnv, this C.jobject) {
requestBackend(ConnectEvent{Enable: false})
requestBackend(ConnectEvent{Enable: false})
}

//export Java_com_tailscale_ipn_App_onConnectivityChanged
Expand Down Expand Up @@ -200,3 +203,13 @@ func Java_com_tailscale_ipn_App_onShareIntent(env *C.JNIEnv, cls C.jclass, nfile
}
onFileShare <- files
}

//export Java_com_tailscale_ipn_IPNService_setAuthKeyForNextConnect
func Java_com_tailscale_ipn_IPNService_setAuthKeyForNextConnect(env *C.JNIEnv, this C.jobject, authKeyJStr C.jobject) {
jenv := (*jni.Env)(unsafe.Pointer(env))
authKey := jni.GoString(jenv, jni.String(authKeyJStr))
select {
case onSetAuthKeyForNextConnect <- authKey:
default:
}
}
27 changes: 27 additions & 0 deletions cmd/tailscale/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ type ConnectEvent struct {
Enable bool
}

type SetAuthKeyEvent struct {
AuthKey string
}
type CopyEvent struct {
Text string
}
Expand Down Expand Up @@ -441,6 +444,27 @@ func (a *App) runBackend() error {
go b.backend.StartLoginInteractive()
signingIn = true
}
case SetAuthKeyEvent:
authKey := e.AuthKey
if b.backend.State() <= ipn.Stopped {
log.Printf("using authkey; state=%v", b.backend.State())
go func() {
prefs := ipn.NewPrefs()
prefs.WantRunning = true
err := b.backend.Start(ipn.Options{
AuthKey: authKey,
UpdatePrefs: prefs,
})
log.Printf("authkey: Start error = %v", err)
if err != nil {
fatalErr(err)
} else {
b.backend.StartLoginInteractive()
}
}()
} else {
log.Printf("ignoring authkey in state=%v", b.backend.State())
}
case SetLoginServerEvent:
state.Prefs.ControlURL = e.URL
b.backend.SetPrefs(state.Prefs)
Expand Down Expand Up @@ -895,6 +919,9 @@ func (a *App) runUI() error {
w.Invalidate()
}
}
case authKey := <-onSetAuthKeyForNextConnect:
ui.ShowMessage("got authkey")
requestBackend(SetAuthKeyEvent{AuthKey: authKey})
case p := <-a.prefs:
ui.enabled.Value = p.WantRunning
ui.runningExit = p.AdvertisesExitNode()
Expand Down