cmd/tailscaled: pre-load wintun.dll using a fully-qualified path

In corp PR #14970 I updated the installer to set a security mitigation that
always forces system32 to the front of the Windows dynamic linker's search
path.

Unfortunately there are other products out there that, partying like it's
1995, drop their own, older version of wintun.dll into system32. Since we
look there first, we end up loading that old version.

We can fix this by preloading wintun using a fully-qualified path. When
wintun-go then loads wintun, the dynamic linker will hand it the module
that was previously loaded by us.

Fixes #10023, #10025, #10052

Signed-off-by: Aaron Klotz <aaron@tailscale.com>
This commit is contained in:
Aaron Klotz 2023-11-02 09:31:27 -06:00
parent af49bcaa52
commit 47019ce1f1
1 changed files with 23 additions and 1 deletions

View File

@ -30,6 +30,7 @@ import (
"os"
"os/exec"
"os/signal"
"path/filepath"
"sync"
"syscall"
"time"
@ -299,6 +300,14 @@ func beWindowsSubprocess() bool {
}
}()
// Pre-load wintun.dll using a fully-qualified path so that wintun-go
// loads our copy and not some (possibly outdated) copy dropped in system32.
// (OSS Issue #10023)
fqWintunPath := fullyQualifiedWintunPath(log.Printf)
if _, err := windows.LoadDLL(fqWintunPath); err != nil {
log.Printf("Error pre-loading \"%s\": %v", fqWintunPath, err)
}
sys := new(tsd.System)
netMon, err := netmon.New(log.Printf)
if err != nil {
@ -507,7 +516,7 @@ func babysitProc(ctx context.Context, args []string, logf logger.Logf) {
}
func uninstallWinTun(logf logger.Logf) {
dll := windows.NewLazyDLL("wintun.dll")
dll := windows.NewLazyDLL(fullyQualifiedWintunPath(logf))
if err := dll.Load(); err != nil {
logf("Cannot load wintun.dll for uninstall: %v", err)
return
@ -517,3 +526,16 @@ func uninstallWinTun(logf logger.Logf) {
err := wintun.Uninstall()
logf("Uninstall: %v", err)
}
func fullyQualifiedWintunPath(logf logger.Logf) string {
var dir string
var buf [windows.MAX_PATH]uint16
length := uint32(len(buf))
if err := windows.QueryFullProcessImageName(windows.CurrentProcess(), 0, &buf[0], &length); err != nil {
logf("QueryFullProcessImageName failed: %v", err)
} else {
dir = filepath.Dir(windows.UTF16ToString(buf[:length]))
}
return filepath.Join(dir, "wintun.dll")
}