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 1 year 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
💣 go4.org/mem from tailscale.com/control/controlbase+
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+
W 💣 golang.zx2c4.com/wireguard/conn/winrio from golang.zx2c4.com/wireguard/conn
💣 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/svc"
"golang.org/x/sys/windows/svc/eventlog"
"golang.zx2c4.com/wintun"
"golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
"tailscale.com/envknob"
@ -64,6 +65,12 @@ func init() {
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() {
tstunNew = tstunNewWithWindowsRetries
}
@ -184,6 +191,26 @@ func (service *ipnService) Execute(args []string, r <-chan svc.ChangeRequest, ch
syslogf("Service session change notification")
handleSessionChange(cmd)
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"
case svc.PreShutdown:
return "PreShutdown"
case cmdUninstallWinTun:
return "(Application Defined) Uninstall WinTun"
}
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/time v0.0.0-20211116232009-f0f3c7e86c11
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/windows v0.5.3
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/mod v0.6.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
gopkg.in/ini.v1 v1.66.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/eventlog"
_ "golang.org/x/sys/windows/svc/mgr"
_ "golang.zx2c4.com/wintun"
_ "golang.zx2c4.com/wireguard/tun"
_ "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
_ "tailscale.com/cmd/tailscaled/childproc"

Loading…
Cancel
Save