aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2021-08-13 14:11:47 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2021-09-12 22:58:32 +0200
commit1288280beae653cb77bff7b2452bd28fd7957d50 (patch)
tree039c1db64c20b7cb33899856e7b89554c1a475dd
parentdocs: mention update notification behavior change (diff)
downloadwireguard-windows-1288280beae653cb77bff7b2452bd28fd7957d50.tar.xz
wireguard-windows-1288280beae653cb77bff7b2452bd28fd7957d50.zip
global: prefer WireGuardNT over wireguard-go/Wintun
Also remove Wintun driver on startup. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r--README.md2
-rw-r--r--conf/admin_windows.go46
-rw-r--r--docs/adminregistry.md16
-rw-r--r--docs/attacksurface.md15
-rw-r--r--docs/netquirk.md2
-rw-r--r--embeddable-dll-service/main.go15
-rw-r--r--manager/ipc_server.go2
-rw-r--r--manager/service.go6
-rw-r--r--tunnel/service.go8
-rw-r--r--ui/aboutdialog.go2
-rw-r--r--ui/confview.go56
-rw-r--r--version/protocol.go28
12 files changed, 29 insertions, 169 deletions
diff --git a/README.md b/README.md
index ba1dd41b..334ff9f8 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# [WireGuard](https://www.wireguard.com/) for Windows
-This is a fully-featured WireGuard client for Windows that uses [Wintun](https://www.wintun.net/) or [WireGuardNT](https://git.zx2c4.com/wireguard-nt/about/). It is the only official and recommended way of using WireGuard on Windows.
+This is a fully-featured WireGuard client for Windows that uses [WireGuardNT](https://git.zx2c4.com/wireguard-nt/about/). It is the only official and recommended way of using WireGuard on Windows.
## Download &amp; Install
diff --git a/conf/admin_windows.go b/conf/admin_windows.go
index 3ac1cae0..a135aa6d 100644
--- a/conf/admin_windows.go
+++ b/conf/admin_windows.go
@@ -5,11 +5,7 @@
package conf
-import (
- "strings"
-
- "golang.org/x/sys/windows/registry"
-)
+import "golang.org/x/sys/windows/registry"
const adminRegKey = `Software\WireGuard`
@@ -27,34 +23,7 @@ func openAdminKey() (registry.Key, error) {
return adminKey, nil
}
-func IsInsiderEnrolled() bool {
- if AdminBool("IgnoreInsiderProgram") {
- return false
- }
- key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\WindowsSelfHost\Applicability`, registry.QUERY_VALUE)
- if err != nil {
- return false
- }
- defer key.Close()
- val, _, err := key.GetIntegerValue("IsBuildFlightingEnabled")
- if err != nil || val != 1 {
- return false
- }
- val, _, err = key.GetIntegerValue("EnablePreviewBuilds")
- if err != nil || val != 1 {
- return false
- }
- ring, _, err := key.GetStringValue("Ring")
- if err != nil || !strings.EqualFold(ring, "external") {
- return false
- }
- return true
-}
-
func AdminBool(name string) bool {
- if name == "ExperimentalKernelDriver" && IsInsiderEnrolled() {
- return true
- }
key, err := openAdminKey()
if err != nil {
return false
@@ -65,16 +34,3 @@ func AdminBool(name string) bool {
}
return val != 0
}
-
-func SetAdminBool(name string, val bool) error {
- key, _, err := registry.CreateKey(registry.LOCAL_MACHINE, adminRegKey, registry.SET_VALUE|registry.WOW64_64KEY)
- if err != nil {
- return err
- }
- defer key.Close()
- if val {
- return key.SetDWordValue(name, 1)
- } else {
- return key.DeleteValue(name)
- }
-}
diff --git a/docs/adminregistry.md b/docs/adminregistry.md
index 245c49a9..bd13fee9 100644
--- a/docs/adminregistry.md
+++ b/docs/adminregistry.md
@@ -38,14 +38,16 @@ executing these scripts.
> reg add HKLM\Software\WireGuard /v DangerousScriptExecution /t REG_DWORD /d 1 /f
```
-#### `HKLM\Software\WireGuard\ExperimentalKernelDriver`
+#### `HKLM\Software\WireGuard\UseUserspaceImplementation`
-When this key is set to `DWORD(1)`, an experimental kernel driver from the
-[WireGuardNT](https://git.zx2c4.com/wireguard-nt/about/) project is used instead
-of the much slower wireguard-go/Wintun implementation. There are significant
-performance gains, but do note that this is _currently_ considered experimental,
-and hence is not recommended.
+When this key is set to `DWORD(1)`, the legacy wireguard-go/Wintun implementation
+is used instead of the newer, faster [WireGuardNT](https://git.zx2c4.com/wireguard-nt/about/)
+implementation. This is an intended stop-gap solution in case there are early bugs
+with WireGuardNT, and this option will be removed after a short period. If you use
+this option, please send an email to team@wireguard.com explaining the issues you
+had with WireGuardNT, so that they can be fixed before this option goes away. If
+you are not having issues, do not use this option.
```
-> reg add HKLM\Software\WireGuard /v ExperimentalKernelDriver /t REG_DWORD /d 1 /f
+> reg add HKLM\Software\WireGuard /v UseUserspaceImplementation /t REG_DWORD /d 1 /f
```
diff --git a/docs/attacksurface.md b/docs/attacksurface.md
index 53bcd7c6..a495ba41 100644
--- a/docs/attacksurface.md
+++ b/docs/attacksurface.md
@@ -4,14 +4,6 @@ _This is an evolving document, describing currently known attack surface, a few
WireGuard for Windows consists of four components: a kernel driver, and three separate interacting userspace parts.
-### Wintun
-
-Wintun is a kernel driver. It exposes:
-
- - A miniport driver to the ndis stack, meaning any process on the system that can access the network stack in a reasonable way can send and receive packets, hitting those related ndis handlers.
- - There are also various ndis OID calls, accessible to certain users, which hit further code.
- - IOCTLs are added to the NDIS device file, and those IOCTLs are restricted to `O:SYD:P(A;;FA;;;SY)(A;;FA;;;BA)S:(ML;;NWNRNX;;;HI)`. The IOCTL allows userspace to register a pair of rings and event objects, which Wintun then locks the pages of with a double mapping and takes a reference to the event object. It parses the contents of the ring to send and receive layer 3 packets, each of which it minimally parses to determine IP family.
-
### WireGuardNT
WireGuardNT is a kernel driver. It exposes:
@@ -24,12 +16,9 @@ WireGuardNT is a kernel driver. It exposes:
### Tunnel Service
-The tunnel service is a userspace service running as Local System, responsible for either A) creating UDP sockets, creating Wintun adapters, and speaking the WireGuard protocol between the two, or B) creating WireGuardNT adapters and configuring them. It exposes:
+The tunnel service is a userspace service running as Local System, responsible for creating WireGuardNT adapters and configuring them. It exposes:
- - In case A) a listening pipe in `\\.\pipe\ProtectedPrefix\Administrators\WireGuard\%s`, where `%s` is some basename of an already valid filename. Its DACL is set to `O:SYD:P(A;;GA;;;SY)(A;;GA;;;BA)S:(ML;;NWNRNX;;;HI)`. If the config file used by the tunnel service is not DPAPI-encrypted and it is owned by a SID other than "Local System" then an additional ACE is added giving that file owner SID access to the named pipe. This pipe gives access to private keys and allows for reconfiguration of the interface, as well as rebinding to different ports (below 1024, even). Clients who connect to the pipe run `GetSecurityInfo` to verify that it is owned by "Local System".
- - A global mutex is used for Wintun/WireGuardNT interface creation, with the same DACL as the pipe, but first CreatePrivateNamespace is called with a "Local System" SID.
- - In case A) it handles data from its two UDP sockets, accessible to the public Internet.
- - In case A) it handles data from Wintun, accessible to all users who can do anything with the network stack.
+ - A global mutex is used for WireGuardNT interface creation, with the same DACL as the pipe, but first CreatePrivateNamespace is called with a "Local System" SID.
- After some initial setup, it uses `AdjustTokenPrivileges` to remove all privileges, except for `SeLoadDriverPrivilege`, so that it can remove the interface when shutting down. This latter point is rather unfortunate, as `SeLoadDriverPrivilege` can be used for all sorts of interesting escalation. Future work includes forking an additional process or the like so that we can drop this from the main tunnel process.
### Manager Service
diff --git a/docs/netquirk.md b/docs/netquirk.md
index c0aa7bf3..5c676f6f 100644
--- a/docs/netquirk.md
+++ b/docs/netquirk.md
@@ -4,7 +4,7 @@ As part of setting up a WireGuard tunnel, the tunnel service also sets up variou
### Routing
-The tunnel service takes all the allowed IPs from each peer, deduplicates them, and adds them to the routes for the WireGuard interface. The service then monitors which interface on the system has a default route (a route with a `/0` CIDR) that is not the WireGuard interface itself, and it uses the [`IP_UNICAST_IF` socket option](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options) to bind WireGuard's UDP packets to that default route interface. If no MTU has been specified in the configuration, it also sets the MTU of the WireGuard interface to be 80 less than the MTU of that default route interface. If no default route interface can be found, then those outgoing packets are blackholed. If WireGuard packets would ordinarily be routed over a non-default route interface, the use of `IP_UNICAST_IF` forces the packets to, in fact, go over the default route interface. This is problematic, but at this moment, Microsoft has not provided a viable workaround. If any allowed IPs are a `/0`, then the interface is given a `0` metric and Windows' [automatic metric logic](https://docs.microsoft.com/en-us/troubleshoot/windows-server/networking/automatic-metric-for-ipv4-routes) is disabled.
+The tunnel service takes all the allowed IPs from each peer, deduplicates them, and adds them to the routes for the WireGuard interface. The service then monitors which interface on the system has a default route (a route with a `/0` CIDR) that is not the WireGuard interface itself, and, if no MTU has been specified in the configuration, it sets the MTU of the WireGuard interface to be 80 less than the MTU of that default route interface. WireGuardNT also monitors the routing table and determines the outgoing route that does not loopback to itself, and then sends each packet using `IP_PKTINFO`/`IPV6_PKTINFO`. It keeps track of the incoming interface and source address for received packets, and always replies to the sender in that way.
### Firewall Considerations for `/0` Allowed IPs
diff --git a/embeddable-dll-service/main.go b/embeddable-dll-service/main.go
index b2e02fa4..df63034e 100644
--- a/embeddable-dll-service/main.go
+++ b/embeddable-dll-service/main.go
@@ -20,24 +20,11 @@ import (
"unsafe"
)
-func init() {
- tunnel.ForceImplementation = 1
- tunnel.UseFixedGUIDInsteadOfDeterministic = true
-}
-
-//export WireGuardForceLegacyImplementation
-func WireGuardForceLegacyImplementation(useOldCode bool) {
- if useOldCode {
- tunnel.ForceImplementation = 2
- } else {
- tunnel.ForceImplementation = 1
- }
-}
-
//export WireGuardTunnelService
func WireGuardTunnelService(confFile16 *uint16) bool {
confFile := windows.UTF16PtrToString(confFile16)
conf.PresetRootDirectory(filepath.Dir(confFile))
+ tunnel.UseFixedGUIDInsteadOfDeterministic = true
err := tunnel.Run(confFile)
if err != nil {
log.Printf("Service run error: %v", err)
diff --git a/manager/ipc_server.go b/manager/ipc_server.go
index 337cb8b4..de97ffb1 100644
--- a/manager/ipc_server.go
+++ b/manager/ipc_server.go
@@ -47,7 +47,7 @@ func (s *ManagerService) StoredConfig(tunnelName string) (*conf.Config, error) {
}
func (s *ManagerService) RuntimeConfig(tunnelName string) (*conf.Config, error) {
- if conf.AdminBool("ExperimentalKernelDriver") {
+ if !conf.AdminBool("UseUserspaceImplementation") {
storedConfig, err := conf.LoadFromName(tunnelName)
if err != nil {
return nil, err
diff --git a/manager/service.go b/manager/service.go
index 6f0df505..1deb50a4 100644
--- a/manager/service.go
+++ b/manager/service.go
@@ -17,6 +17,7 @@ import (
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/svc"
+ "golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/windows/conf"
"golang.zx2c4.com/wireguard/windows/elevate"
@@ -261,6 +262,11 @@ func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest
}
time.AfterFunc(time.Second*10, cleanupStaleNetworkInterfaces)
+ time.AfterFunc(time.Second*15, func() {
+ if !conf.AdminBool("UseUserspaceImplementation") {
+ tun.WintunPool.DeleteDriver()
+ }
+ })
go checkForUpdates()
var sessionsPointer *windows.WTS_SESSION_INFO
diff --git a/tunnel/service.go b/tunnel/service.go
index 14d03b5f..9df4fecc 100644
--- a/tunnel/service.go
+++ b/tunnel/service.go
@@ -34,12 +34,6 @@ type tunnelService struct {
Path string
}
-// Escape hatch to let the embeddable-dll-service force an implementation.
-// 0 - use the registry key
-// 1 - use the kernel driver
-// 2 - use wireguard-go
-var ForceImplementation = 0
-
func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
changes <- svc.Status{State: svc.StartPending}
@@ -172,7 +166,7 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest,
}
log.Println("Creating network adapter")
- if ForceImplementation == 1 || (ForceImplementation == 0 && conf.AdminBool("ExperimentalKernelDriver")) {
+ if UseFixedGUIDInsteadOfDeterministic || !conf.AdminBool("UseUserspaceImplementation") {
// Does an adapter with this name already exist?
adapter, err = driver.DefaultPool.OpenAdapter(config.Name)
if err == nil {
diff --git a/ui/aboutdialog.go b/ui/aboutdialog.go
index f9be1f90..eaeed96c 100644
--- a/ui/aboutdialog.go
+++ b/ui/aboutdialog.go
@@ -95,7 +95,7 @@ func runAboutDialog(owner walk.Form) error {
return err
}
detailsLbl.SetTextAlignment(walk.AlignHCenterVNear)
- detailsLbl.SetText(l18n.Sprintf("App version: %s\nGo backend version: %s\nGo version: %s-%s\nOperating system: %s\nArchitecture: %s", version.Number, version.ProtoImplementation(), strings.TrimPrefix(runtime.Version(), "go"), runtime.GOARCH, version.OsName(), version.Arch()))
+ detailsLbl.SetText(l18n.Sprintf("App version: %s\nGo version: %s-%s\nOperating system: %s\nArchitecture: %s", version.Number, strings.TrimPrefix(runtime.Version(), "go"), runtime.GOARCH, version.OsName(), version.Arch()))
copyrightLbl, err := walk.NewTextLabel(showingAboutDialog)
if err != nil {
diff --git a/ui/confview.go b/ui/confview.go
index c4e5b8bb..3d16f38f 100644
--- a/ui/confview.go
+++ b/ui/confview.go
@@ -41,7 +41,6 @@ type labelTextLine struct {
type toggleActiveLine struct {
composite *walk.Composite
button *walk.PushButton
- betaTest *walk.CheckBox
}
type interfaceView struct {
@@ -212,12 +211,6 @@ func (tal *toggleActiveLine) widgets() (walk.Widget, walk.Widget) {
func (tal *toggleActiveLine) updateGlobal(globalState manager.TunnelState) {
tal.button.SetEnabled(globalState == manager.TunnelStarted || globalState == manager.TunnelStopped)
- tal.betaTest.SetVisible(globalState == manager.TunnelStopped && IsAdmin && !conf.IsInsiderEnrolled())
- if globalState == manager.TunnelStopped && IsAdmin && !conf.IsInsiderEnrolled() {
- tal.composite.Layout().SetMargins(walk.Margins{})
- } else {
- tal.composite.Layout().SetMargins(walk.Margins{0, 0, 0, 6})
- }
}
func (tal *toggleActiveLine) update(state manager.TunnelState) {
@@ -253,56 +246,17 @@ func newToggleActiveLine(parent walk.Container) (*toggleActiveLine, error) {
return nil, err
}
disposables.Add(tal.composite)
- vlayout := walk.NewVBoxLayout()
- vlayout.SetMargins(walk.Margins{})
- vlayout.SetSpacing(0)
- tal.composite.SetLayout(vlayout)
+ layout := walk.NewHBoxLayout()
+ layout.SetMargins(walk.Margins{0, 0, 0, 6})
+ tal.composite.SetLayout(layout)
- buttonComposite, err := walk.NewComposite(tal.composite)
- if err != nil {
- return nil, err
- }
- hlayout := walk.NewHBoxLayout()
- hlayout.SetMargins(walk.Margins{})
- buttonComposite.SetLayout(hlayout)
- if tal.button, err = walk.NewPushButton(buttonComposite); err != nil {
+ if tal.button, err = walk.NewPushButton(tal.composite); err != nil {
return nil, err
}
disposables.Add(tal.button)
- walk.NewHSpacer(buttonComposite)
+ walk.NewHSpacer(tal.composite)
tal.update(manager.TunnelStopped)
- betaComposite, err := walk.NewComposite(tal.composite)
- if err != nil {
- return nil, err
- }
- hlayout = walk.NewHBoxLayout()
- hlayout.SetMargins(walk.Margins{})
- betaComposite.SetLayout(hlayout)
- if tal.betaTest, err = walk.NewCheckBox(betaComposite); err != nil {
- return nil, err
- }
- tal.betaTest.SetText(l18n.Sprintf("&Test experimental kernel driver"))
- tal.betaTest.SetVisible(IsAdmin && !conf.IsInsiderEnrolled())
- const experimentalKernelDriver = "ExperimentalKernelDriver"
- tal.betaTest.SetChecked(conf.AdminBool(experimentalKernelDriver))
- tal.betaTest.CheckedChanged().Attach(func() {
- if tal.betaTest.Checked() {
- if walk.MsgBox(parent.Form(), l18n.Sprintf("Enable experimental kernel driver?"),
- l18n.Sprintf("The WireGuard project is currently testing a high performance kernel driver "+
- "called WireGuardNT. It will eventually be enabled by default, but for now the project needs "+
- "testers to try it out. Whether you encounter problems or you find that it works well, please do "+
- "email team@wireguard.com about your experience.\n\nWould you like to enable the experimental "+
- "kernel driver?"), walk.MsgBoxIconQuestion|walk.MsgBoxYesNo) != walk.DlgCmdYes {
- tal.betaTest.SetChecked(false)
- }
- }
- if conf.SetAdminBool(experimentalKernelDriver, tal.betaTest.Checked()) != nil {
- tal.betaTest.SetChecked(conf.AdminBool(experimentalKernelDriver))
- }
- })
- walk.NewHSpacer(betaComposite)
-
disposables.Spare()
return tal, nil
diff --git a/version/protocol.go b/version/protocol.go
deleted file mode 100644
index 72a35e95..00000000
--- a/version/protocol.go
+++ /dev/null
@@ -1,28 +0,0 @@
-/* SPDX-License-Identifier: MIT
- *
- * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
- */
-
-package version
-
-import (
- "runtime/debug"
- "strings"
-)
-
-func ProtoImplementation() string {
- info, ok := debug.ReadBuildInfo()
- if !ok {
- return "unknown"
- }
- for _, dep := range info.Deps {
- if dep.Path == "golang.zx2c4.com/wireguard" {
- parts := strings.Split(dep.Version, "-")
- if len(parts) == 3 && len(parts[2]) == 12 {
- return parts[2][:7]
- }
- return dep.Version
- }
- }
- return "unknown"
-}