safesocket: support connecting to Mac TCP server from within App Sandbox

pull/572/head
Brad Fitzpatrick 4 years ago
parent f267a7396f
commit f562c35c0d

@ -28,17 +28,16 @@ import (
// (pty, parent PID), etc. // (pty, parent PID), etc.
func ActLikeCLI() bool { func ActLikeCLI() bool {
if len(os.Args) < 2 { if len(os.Args) < 2 {
// TODO: on Windows & Mac, show usage if we're being run with a pty.
return false return false
} }
switch os.Args[1] { switch os.Args[1] {
case "up", "status", "netcheck": case "up", "status", "netcheck", "-h", "--help":
return true return true
} }
return false return false
} }
// Run runs the CLI. The args do npot include the binary name. // Run runs the CLI. The args do not include the binary name.
func Run(args []string) error { func Run(args []string) error {
rootfs := flag.NewFlagSet("tailscale", flag.ExitOnError) rootfs := flag.NewFlagSet("tailscale", flag.ExitOnError)
rootfs.StringVar(&rootArgs.socket, "socket", paths.DefaultTailscaledSocket(), "path to tailscaled's unix socket") rootfs.StringVar(&rootArgs.socket, "socket", paths.DefaultTailscaledSocket(), "path to tailscaled's unix socket")

@ -11,6 +11,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"log" "log"
"net" "net"
"os" "os"
@ -77,6 +78,16 @@ func listen(path string, port uint16) (ln net.Listener, _ uint16, err error) {
// * it also picks a random hex string that acts as an auth token // * it also picks a random hex string that acts as an auth token
// * it then creates a file named "sameuserproof-$PORT-$TOKEN" and leaves // * it then creates a file named "sameuserproof-$PORT-$TOKEN" and leaves
// that file descriptor open forever. // that file descriptor open forever.
//
// Then, we do different things depending on whether the user is
// running cmd/tailscale that they built themselves (running as
// themselves, outside the App Sandbox), or whether the user is
// running the CLI via the GUI binary
// (e.g. /Applications/Tailscale.app/Contents/MacOS/Tailscale <args>),
// in which case we're running within the App Sandbox.
//
// If we're outside the App Sandbox:
//
// * then we come along here, running as the same UID, but outside // * then we come along here, running as the same UID, but outside
// of the sandbox, and look for it. We can run lsof on our own processes, // of the sandbox, and look for it. We can run lsof on our own processes,
// but other users on the system can't. // but other users on the system can't.
@ -86,7 +97,38 @@ func listen(path string, port uint16) (ln net.Listener, _ uint16, err error) {
// * server verifies $TOKEN, sends "#IPN\n" if okay. // * server verifies $TOKEN, sends "#IPN\n" if okay.
// * server is now protocol switched // * server is now protocol switched
// * we return the net.Conn and the caller speaks the normal protocol // * we return the net.Conn and the caller speaks the normal protocol
//
// If we're inside the App Sandbox, then TS_MACOS_CLI_SHARED_DIR has
// been set to our shared directory. We now have to find the most
// recent "sameuserproof" file (there should only be 1, but previous
// versions of the macOS app didn't clean them up).
func connectMacOSAppSandbox() (net.Conn, error) { func connectMacOSAppSandbox() (net.Conn, error) {
// Are we running the Tailscale.app GUI binary as a CLI, running within the App Sandbox?
if d := os.Getenv("TS_MACOS_CLI_SHARED_DIR"); d != "" {
fis, err := ioutil.ReadDir(d)
if err != nil {
return nil, fmt.Errorf("reading TS_MACOS_CLI_SHARED_DIR: %w", err)
}
var best os.FileInfo
for _, fi := range fis {
if !strings.HasPrefix(fi.Name(), "sameuserproof-") || strings.Count(fi.Name(), "-") != 2 {
continue
}
if best == nil || fi.ModTime().After(best.ModTime()) {
best = fi
}
}
if best == nil {
return nil, fmt.Errorf("no sameuserproof token found in TS_MACOS_CLI_SHARED_DIR %q", d)
}
f := strings.SplitN(best.Name(), "-", 3)
portStr, token := f[1], f[2]
return connectMacTCP(portStr, token)
}
// Otherwise, assume we're running the cmd/tailscale binary from outside the
// App Sandbox.
out, err := exec.Command("lsof", out, err := exec.Command("lsof",
"-n", // numeric sockets; don't do DNS lookups, etc "-n", // numeric sockets; don't do DNS lookups, etc
"-a", // logical AND remaining options "-a", // logical AND remaining options
@ -110,6 +152,12 @@ func connectMacOSAppSandbox() (net.Conn, error) {
continue continue
} }
portStr, token := f[0], f[1] portStr, token := f[0], f[1]
return connectMacTCP(portStr, token)
}
return nil, fmt.Errorf("failed to find Tailscale's IPNExtension process")
}
func connectMacTCP(portStr, token string) (net.Conn, error) {
c, err := net.Dial("tcp", "localhost:"+portStr) c, err := net.Dial("tcp", "localhost:"+portStr)
if err != nil { if err != nil {
return nil, fmt.Errorf("error dialing IPNExtension: %w", err) return nil, fmt.Errorf("error dialing IPNExtension: %w", err)
@ -127,5 +175,3 @@ func connectMacOSAppSandbox() (net.Conn, error) {
} }
return c, nil return c, nil
} }
return nil, fmt.Errorf("failed to find Tailscale's IPNExtension process")
}

Loading…
Cancel
Save