cmd/tailscaled: add a special command to tailscaled's Windows service for removing WinTun

WinTun is installed lazily by tailscaled while it is running as LocalSystem.
Based upon what we're seeing in bug reports and support requests, removing
WinTun as a lesser user may fail under certain Windows versions, even when that
user is an Administrator.

By adding a user-defined command code to tailscaled, we can ask the service to
do the removal on our behalf while it is still running as LocalSystem.

* The uninstall code is basically the same as it is in corp;
* The command code will be sent as a service control request and is protected by
  the SERVICE_USER_DEFINED_CONTROL access right, which requires Administrator.

I'll be adding follow-up patches in corp to engage this functionality.

Updates https://github.com/tailscale/tailscale/issues/6433

Signed-off-by: Aaron Klotz <aaron@tailscale.com>
pull/6659/head
Aaron Klotz 2 years ago
parent 367228ef82
commit 98f21354c6

@ -126,7 +126,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
github.com/x448/float16 from github.com/fxamacker/cbor/v2 github.com/x448/float16 from github.com/fxamacker/cbor/v2
💣 go4.org/mem from tailscale.com/control/controlbase+ 💣 go4.org/mem from tailscale.com/control/controlbase+
go4.org/netipx from tailscale.com/ipn/ipnlocal+ go4.org/netipx from tailscale.com/ipn/ipnlocal+
W 💣 golang.zx2c4.com/wintun from golang.zx2c4.com/wireguard/tun W 💣 golang.zx2c4.com/wintun from golang.zx2c4.com/wireguard/tun+
💣 golang.zx2c4.com/wireguard/conn from golang.zx2c4.com/wireguard/device+ 💣 golang.zx2c4.com/wireguard/conn from golang.zx2c4.com/wireguard/device+
W 💣 golang.zx2c4.com/wireguard/conn/winrio from golang.zx2c4.com/wireguard/conn W 💣 golang.zx2c4.com/wireguard/conn/winrio from golang.zx2c4.com/wireguard/conn
💣 golang.zx2c4.com/wireguard/device from tailscale.com/net/tstun+ 💣 golang.zx2c4.com/wireguard/device from tailscale.com/net/tstun+

@ -38,6 +38,7 @@ import (
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
"golang.org/x/sys/windows/svc" "golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/eventlog" "golang.org/x/sys/windows/svc/eventlog"
"golang.zx2c4.com/wintun"
"golang.zx2c4.com/wireguard/tun" "golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
"tailscale.com/envknob" "tailscale.com/envknob"
@ -64,6 +65,12 @@ func init() {
const serviceName = "Tailscale" const serviceName = "Tailscale"
// Application-defined command codes between 128 and 255
// See https://web.archive.org/web/20221007222822/https://learn.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-controlservice
const (
cmdUninstallWinTun = svc.Cmd(128 + iota)
)
func init() { func init() {
tstunNew = tstunNewWithWindowsRetries tstunNew = tstunNewWithWindowsRetries
} }
@ -184,6 +191,26 @@ func (service *ipnService) Execute(args []string, r <-chan svc.ChangeRequest, ch
syslogf("Service session change notification") syslogf("Service session change notification")
handleSessionChange(cmd) handleSessionChange(cmd)
changes <- cmd.CurrentStatus changes <- cmd.CurrentStatus
case cmdUninstallWinTun:
syslogf("Stopping tailscaled child process and uninstalling WinTun")
// At this point, doneCh is the channel which will be closed when the
// tailscaled subprocess exits. We save that to childDoneCh.
childDoneCh := doneCh
// We reset doneCh to a new channel that will keep the event loop
// running until the uninstallation is done.
doneCh = make(chan struct{})
// Trigger subprocess shutdown.
cancel()
go func() {
// When this goroutine completes, tell the service to break out of its
// event loop.
defer close(doneCh)
// Wait for the subprocess to shutdown.
<-childDoneCh
// Now uninstall WinTun.
uninstallWinTun(log.Printf)
}()
changes <- svc.Status{State: svc.StopPending}
} }
} }
} }
@ -221,6 +248,8 @@ func cmdName(c svc.Cmd) string {
return "SessionChange" return "SessionChange"
case svc.PreShutdown: case svc.PreShutdown:
return "PreShutdown" return "PreShutdown"
case cmdUninstallWinTun:
return "(Application Defined) Uninstall WinTun"
} }
return fmt.Sprintf("Unknown-Service-Cmd-%d", c) return fmt.Sprintf("Unknown-Service-Cmd-%d", c)
} }
@ -447,3 +476,15 @@ func babysitProc(ctx context.Context, args []string, logf logger.Logf) {
} }
} }
} }
func uninstallWinTun(logf logger.Logf) {
dll := windows.NewLazyDLL("wintun.dll")
if err := dll.Load(); err != nil {
logf("Cannot load wintun.dll for uninstall: %v", err)
return
}
logf("Removing wintun driver...")
err := wintun.Uninstall()
logf("Uninstall: %v", err)
}

@ -72,6 +72,7 @@ require (
golang.org/x/term v0.1.0 golang.org/x/term v0.1.0
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11
golang.org/x/tools v0.2.0 golang.org/x/tools v0.2.0
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224
golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c
golang.zx2c4.com/wireguard/windows v0.5.3 golang.zx2c4.com/wireguard/windows v0.5.3
gvisor.dev/gvisor v0.0.0-20220817001344-846276b3dbc5 gvisor.dev/gvisor v0.0.0-20220817001344-846276b3dbc5
@ -276,7 +277,6 @@ require (
golang.org/x/image v0.0.0-20201208152932-35266b937fa6 // indirect golang.org/x/image v0.0.0-20201208152932-35266b937fa6 // indirect
golang.org/x/mod v0.6.0 // indirect golang.org/x/mod v0.6.0 // indirect
golang.org/x/text v0.4.0 // indirect golang.org/x/text v0.4.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
google.golang.org/protobuf v1.28.0 // indirect google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect

@ -16,6 +16,7 @@ import (
_ "golang.org/x/sys/windows/svc" _ "golang.org/x/sys/windows/svc"
_ "golang.org/x/sys/windows/svc/eventlog" _ "golang.org/x/sys/windows/svc/eventlog"
_ "golang.org/x/sys/windows/svc/mgr" _ "golang.org/x/sys/windows/svc/mgr"
_ "golang.zx2c4.com/wintun"
_ "golang.zx2c4.com/wireguard/tun" _ "golang.zx2c4.com/wireguard/tun"
_ "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" _ "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
_ "tailscale.com/cmd/tailscaled/childproc" _ "tailscale.com/cmd/tailscaled/childproc"

Loading…
Cancel
Save