From e493f911269a2dabab7b05ec28726cdaeffb660e Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Mon, 20 May 2019 14:18:01 +0200 Subject: service: split into tunnel and manager --- main.go | 21 +- manager/install.go | 205 +++++ manager/ipc_client.go | 281 +++++++ manager/ipc_pipe.go | 77 ++ manager/ipc_server.go | 348 +++++++++ manager/names.go | 26 + manager/service.go | 331 +++++++++ manager/tunneltracker.go | 182 +++++ manager/updatestate.go | 57 ++ service/defaultroutemonitor.go | 150 ---- service/errors.go | 96 --- service/firewall/blocker.go | 201 ----- service/firewall/helpers.go | 105 --- service/firewall/mksyscall.go | 8 - service/firewall/rules.go | 1240 ------------------------------- service/firewall/syscall_windows.go | 42 -- service/firewall/types_windows.go | 417 ----------- service/firewall/types_windows_386.go | 88 --- service/firewall/types_windows_amd64.go | 85 --- service/firewall/types_windows_test.go | 538 -------------- service/firewall/zsyscall_windows.go | 186 ----- service/ifaceconfig.go | 220 ------ service/install.go | 204 ----- service/ipc_client.go | 281 ------- service/ipc_pipe.go | 77 -- service/ipc_server.go | 348 --------- service/names.go | 26 - service/service_manager.go | 329 -------- service/service_tunnel.go | 223 ------ service/tokens.go | 70 -- service/tunneltracker.go | 180 ----- service/updatestate.go | 57 -- services/errors.go | 96 +++ services/tokens.go | 70 ++ tunnel/defaultroutemonitor.go | 150 ++++ tunnel/firewall/blocker.go | 201 +++++ tunnel/firewall/helpers.go | 105 +++ tunnel/firewall/mksyscall.go | 8 + tunnel/firewall/rules.go | 1240 +++++++++++++++++++++++++++++++ tunnel/firewall/syscall_windows.go | 42 ++ tunnel/firewall/types_windows.go | 417 +++++++++++ tunnel/firewall/types_windows_386.go | 88 +++ tunnel/firewall/types_windows_amd64.go | 85 +++ tunnel/firewall/types_windows_test.go | 538 ++++++++++++++ tunnel/firewall/zsyscall_windows.go | 186 +++++ tunnel/ifaceconfig.go | 220 ++++++ tunnel/service.go | 225 ++++++ ui/confview.go | 55 +- ui/editdialog.go | 7 +- ui/iconprovider.go | 29 +- ui/listview.go | 26 +- ui/managewindow.go | 20 +- ui/tray.go | 40 +- ui/tunnelspage.go | 25 +- ui/ui.go | 19 +- ui/updatepage.go | 7 +- 56 files changed, 5306 insertions(+), 5292 deletions(-) create mode 100644 manager/install.go create mode 100644 manager/ipc_client.go create mode 100644 manager/ipc_pipe.go create mode 100644 manager/ipc_server.go create mode 100644 manager/names.go create mode 100644 manager/service.go create mode 100644 manager/tunneltracker.go create mode 100644 manager/updatestate.go delete mode 100644 service/defaultroutemonitor.go delete mode 100644 service/errors.go delete mode 100644 service/firewall/blocker.go delete mode 100644 service/firewall/helpers.go delete mode 100644 service/firewall/mksyscall.go delete mode 100644 service/firewall/rules.go delete mode 100644 service/firewall/syscall_windows.go delete mode 100644 service/firewall/types_windows.go delete mode 100644 service/firewall/types_windows_386.go delete mode 100644 service/firewall/types_windows_amd64.go delete mode 100644 service/firewall/types_windows_test.go delete mode 100644 service/firewall/zsyscall_windows.go delete mode 100644 service/ifaceconfig.go delete mode 100644 service/install.go delete mode 100644 service/ipc_client.go delete mode 100644 service/ipc_pipe.go delete mode 100644 service/ipc_server.go delete mode 100644 service/names.go delete mode 100644 service/service_manager.go delete mode 100644 service/service_tunnel.go delete mode 100644 service/tokens.go delete mode 100644 service/tunneltracker.go delete mode 100644 service/updatestate.go create mode 100644 services/errors.go create mode 100644 services/tokens.go create mode 100644 tunnel/defaultroutemonitor.go create mode 100644 tunnel/firewall/blocker.go create mode 100644 tunnel/firewall/helpers.go create mode 100644 tunnel/firewall/mksyscall.go create mode 100644 tunnel/firewall/rules.go create mode 100644 tunnel/firewall/syscall_windows.go create mode 100644 tunnel/firewall/types_windows.go create mode 100644 tunnel/firewall/types_windows_386.go create mode 100644 tunnel/firewall/types_windows_amd64.go create mode 100644 tunnel/firewall/types_windows_test.go create mode 100644 tunnel/firewall/zsyscall_windows.go create mode 100644 tunnel/ifaceconfig.go create mode 100644 tunnel/service.go diff --git a/main.go b/main.go index fa6d4e53..1cb1fd88 100644 --- a/main.go +++ b/main.go @@ -14,8 +14,9 @@ import ( "golang.org/x/sys/windows" + "golang.zx2c4.com/wireguard/windows/manager" "golang.zx2c4.com/wireguard/windows/ringlogger" - "golang.zx2c4.com/wireguard/windows/service" + "golang.zx2c4.com/wireguard/windows/services" "golang.zx2c4.com/wireguard/windows/ui" ) @@ -68,7 +69,7 @@ func checkForAdminGroup() { fatal("Unable to open current process token: ", err) } defer processToken.Close() - if !service.TokenIsMemberOfBuiltInAdministrator(processToken) { + if !services.TokenIsMemberOfBuiltInAdministrator(processToken) { fatal("WireGuard may only be used by users who are a member of the Builtin Administrators group.") } } @@ -115,7 +116,7 @@ func main() { usage() } go ui.WaitForRaiseUIThenQuit() - err := service.InstallManager() + err := manager.InstallManager() if err != nil { fatal(err) } @@ -126,7 +127,7 @@ func main() { if len(os.Args) != 2 { usage() } - err := service.UninstallManager() + err := manager.UninstallManager() if err != nil { fatal(err) } @@ -135,7 +136,7 @@ func main() { if len(os.Args) != 2 { usage() } - err := service.RunManager() + err := manager.RunManager() if err != nil { fatal(err) } @@ -144,7 +145,7 @@ func main() { if len(os.Args) != 3 { usage() } - err := service.InstallTunnel(os.Args[2]) + err := manager.InstallTunnel(os.Args[2]) if err != nil { fatal(err) } @@ -153,7 +154,7 @@ func main() { if len(os.Args) != 3 { usage() } - err := service.UninstallTunnel(os.Args[2]) + err := manager.UninstallTunnel(os.Args[2]) if err != nil { fatal(err) } @@ -162,7 +163,7 @@ func main() { if len(os.Args) != 3 { usage() } - err := service.RunTunnel(os.Args[2]) + err := manager.RunTunnel(os.Args[2]) if err != nil { fatal(err) } @@ -171,7 +172,7 @@ func main() { if len(os.Args) != 6 { usage() } - err := service.DropAllPrivileges() + err := services.DropAllPrivileges() if err != nil { fatal(err) } @@ -191,7 +192,7 @@ func main() { if err != nil { fatal(err) } - service.InitializeIPCClient(readPipe, writePipe, eventPipe) + manager.InitializeIPCClient(readPipe, writePipe, eventPipe) ui.RunUI() return case "/dumplog": diff --git a/manager/install.go b/manager/install.go new file mode 100644 index 00000000..4a570297 --- /dev/null +++ b/manager/install.go @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package manager + +import ( + "errors" + "os" + "time" + + "golang.org/x/sys/windows" + "golang.org/x/sys/windows/svc" + "golang.org/x/sys/windows/svc/mgr" + + "golang.zx2c4.com/wireguard/windows/conf" + "golang.zx2c4.com/wireguard/windows/tunnel" +) + +var cachedServiceManager *mgr.Mgr + +func serviceManager() (*mgr.Mgr, error) { + if cachedServiceManager != nil { + return cachedServiceManager, nil + } + m, err := mgr.Connect() + if err != nil { + return nil, err + } + cachedServiceManager = m + return cachedServiceManager, nil +} + +func InstallManager() error { + m, err := serviceManager() + if err != nil { + return err + } + path, err := os.Executable() + if err != nil { + return nil + } + + //TODO: Do we want to bail if executable isn't being run from the right location? + + serviceName := "WireGuardManager" + service, err := m.OpenService(serviceName) + if err == nil { + status, err := service.Query() + if err != nil { + service.Close() + return err + } + if status.State != svc.Stopped { + service.Close() + return errors.New("Manager already installed and running") + } + err = service.Delete() + service.Close() + if err != nil { + return err + } + for { + service, err = m.OpenService(serviceName) + if err != nil { + break + } + service.Close() + time.Sleep(time.Second / 3) + } + } + + config := mgr.Config{ + ServiceType: windows.SERVICE_WIN32_OWN_PROCESS, + StartType: mgr.StartAutomatic, + ErrorControl: mgr.ErrorNormal, + DisplayName: "WireGuard Manager", + } + + service, err = m.CreateService(serviceName, path, config, "/managerservice") + if err != nil { + return err + } + service.Start() + return service.Close() +} + +func UninstallManager() error { + m, err := serviceManager() + if err != nil { + return err + } + serviceName := "WireGuardManager" + service, err := m.OpenService(serviceName) + if err != nil { + return err + } + service.Control(svc.Stop) + err = service.Delete() + err2 := service.Close() + if err != nil { + return err + } + return err2 +} + +func RunManager() error { + return svc.Run("WireGuardManager", &managerService{}) +} + +func InstallTunnel(configPath string) error { + m, err := serviceManager() + if err != nil { + return err + } + path, err := os.Executable() + if err != nil { + return nil + } + + name, err := conf.NameFromPath(configPath) + if err != nil { + return err + } + + serviceName, err := ServiceNameOfTunnel(name) + if err != nil { + return err + } + service, err := m.OpenService(serviceName) + if err == nil { + status, err := service.Query() + if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE { + service.Close() + return err + } + if status.State != svc.Stopped && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE { + service.Close() + return errors.New("Tunnel already installed and running") + } + err = service.Delete() + service.Close() + if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE { + return err + } + for { + service, err = m.OpenService(serviceName) + if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE { + break + } + service.Close() + time.Sleep(time.Second / 3) + } + } + + config := mgr.Config{ + ServiceType: windows.SERVICE_WIN32_OWN_PROCESS, + StartType: mgr.StartAutomatic, + ErrorControl: mgr.ErrorNormal, + DisplayName: "WireGuard Tunnel: " + name, + } + + service, err = m.CreateService(serviceName, path, config, "/tunnelservice", configPath) + if err != nil { + return err + } + err = service.Start() + go trackTunnelService(name, service) // Pass off reference to handle. + return err +} + +func UninstallTunnel(name string) error { + m, err := serviceManager() + if err != nil { + return err + } + serviceName, err := ServiceNameOfTunnel(name) + if err != nil { + return err + } + service, err := m.OpenService(serviceName) + if err != nil { + return err + } + service.Control(svc.Stop) + err = service.Delete() + err2 := service.Close() + if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE { + return err + } + return err2 +} + +func RunTunnel(confPath string) error { + name, err := conf.NameFromPath(confPath) + if err != nil { + return err + } + serviceName, err := ServiceNameOfTunnel(name) + if err != nil { + return err + } + return svc.Run(serviceName, &tunnel.Service{confPath}) +} diff --git a/manager/ipc_client.go b/manager/ipc_client.go new file mode 100644 index 00000000..a23493f0 --- /dev/null +++ b/manager/ipc_client.go @@ -0,0 +1,281 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package manager + +import ( + "encoding/gob" + "errors" + "net/rpc" + "os" + + "golang.zx2c4.com/wireguard/windows/conf" + "golang.zx2c4.com/wireguard/windows/updater" +) + +type Tunnel struct { + Name string +} + +type TunnelState int + +const ( + TunnelUnknown TunnelState = iota + TunnelStarted + TunnelStopped + TunnelStarting + TunnelStopping +) + +type NotificationType int + +const ( + TunnelChangeNotificationType NotificationType = iota + TunnelsChangeNotificationType + ManagerStoppingNotificationType + UpdateFoundNotificationType + UpdateProgressNotificationType +) + +var rpcClient *rpc.Client + +type TunnelChangeCallback struct { + cb func(tunnel *Tunnel, state TunnelState, globalState TunnelState, err error) +} + +var tunnelChangeCallbacks = make(map[*TunnelChangeCallback]bool) + +type TunnelsChangeCallback struct { + cb func() +} + +var tunnelsChangeCallbacks = make(map[*TunnelsChangeCallback]bool) + +type ManagerStoppingCallback struct { + cb func() +} + +var managerStoppingCallbacks = make(map[*ManagerStoppingCallback]bool) + +type UpdateFoundCallback struct { + cb func(updateState UpdateState) +} + +var updateFoundCallbacks = make(map[*UpdateFoundCallback]bool) + +type UpdateProgressCallback struct { + cb func(dp updater.DownloadProgress) +} + +var updateProgressCallbacks = make(map[*UpdateProgressCallback]bool) + +func InitializeIPCClient(reader *os.File, writer *os.File, events *os.File) { + rpcClient = rpc.NewClient(&pipeRWC{reader, writer}) + go func() { + decoder := gob.NewDecoder(events) + for { + var notificationType NotificationType + err := decoder.Decode(¬ificationType) + if err != nil { + return + } + switch notificationType { + case TunnelChangeNotificationType: + var tunnel string + err := decoder.Decode(&tunnel) + if err != nil || len(tunnel) == 0 { + continue + } + var state TunnelState + err = decoder.Decode(&state) + if err != nil { + continue + } + var globalState TunnelState + err = decoder.Decode(&globalState) + if err != nil { + continue + } + var errStr string + err = decoder.Decode(&errStr) + if err != nil { + continue + } + var retErr error + if len(errStr) > 0 { + retErr = errors.New(errStr) + } + if state == TunnelUnknown { + continue + } + t := &Tunnel{tunnel} + for cb := range tunnelChangeCallbacks { + cb.cb(t, state, globalState, retErr) + } + case TunnelsChangeNotificationType: + for cb := range tunnelsChangeCallbacks { + cb.cb() + } + case ManagerStoppingNotificationType: + for cb := range managerStoppingCallbacks { + cb.cb() + } + case UpdateFoundNotificationType: + var state UpdateState + err = decoder.Decode(&state) + if err != nil { + continue + } + for cb := range updateFoundCallbacks { + cb.cb(state) + } + case UpdateProgressNotificationType: + var dp updater.DownloadProgress + err = decoder.Decode(&dp.Activity) + if err != nil { + continue + } + err = decoder.Decode(&dp.BytesDownloaded) + if err != nil { + continue + } + err = decoder.Decode(&dp.BytesTotal) + if err != nil { + continue + } + var errStr string + err = decoder.Decode(&errStr) + if err != nil { + continue + } + if len(errStr) > 0 { + dp.Error = errors.New(errStr) + } + err = decoder.Decode(&dp.Complete) + if err != nil { + continue + } + for cb := range updateProgressCallbacks { + cb.cb(dp) + } + } + } + }() +} + +func (t *Tunnel) StoredConfig() (c conf.Config, err error) { + err = rpcClient.Call("ManagerService.StoredConfig", t.Name, &c) + return +} + +func (t *Tunnel) RuntimeConfig() (c conf.Config, err error) { + err = rpcClient.Call("ManagerService.RuntimeConfig", t.Name, &c) + return +} + +func (t *Tunnel) Start() error { + return rpcClient.Call("ManagerService.Start", t.Name, nil) +} + +func (t *Tunnel) Stop() error { + return rpcClient.Call("ManagerService.Stop", t.Name, nil) +} + +func (t *Tunnel) Toggle() (oldState TunnelState, err error) { + oldState, err = t.State() + if err != nil { + oldState = TunnelUnknown + return + } + if oldState == TunnelStarted { + err = t.Stop() + } else if oldState == TunnelStopped { + err = t.Start() + } + return +} + +func (t *Tunnel) WaitForStop() error { + return rpcClient.Call("ManagerService.WaitForStop", t.Name, nil) +} + +func (t *Tunnel) Delete() error { + return rpcClient.Call("ManagerService.Delete", t.Name, nil) +} + +func (t *Tunnel) State() (TunnelState, error) { + var state TunnelState + return state, rpcClient.Call("ManagerService.State", t.Name, &state) +} + +func IPCClientNewTunnel(conf *conf.Config) (Tunnel, error) { + var tunnel Tunnel + return tunnel, rpcClient.Call("ManagerService.Create", *conf, &tunnel) +} + +func IPCClientTunnels() ([]Tunnel, error) { + var tunnels []Tunnel + return tunnels, rpcClient.Call("ManagerService.Tunnels", uintptr(0), &tunnels) +} + +func IPCClientGlobalState() (TunnelState, error) { + var state TunnelState + return state, rpcClient.Call("ManagerService.GlobalState", uintptr(0), &state) +} + +func IPCClientQuit(stopTunnelsOnQuit bool) (bool, error) { + var alreadyQuit bool + return alreadyQuit, rpcClient.Call("ManagerService.Quit", stopTunnelsOnQuit, &alreadyQuit) +} + +func IPCClientUpdateState() (UpdateState, error) { + var state UpdateState + return state, rpcClient.Call("ManagerService.UpdateState", uintptr(0), &state) +} + +func IPCClientUpdate() error { + return rpcClient.Call("ManagerService.Update", uintptr(0), nil) +} + +func IPCClientRegisterTunnelChange(cb func(tunnel *Tunnel, state TunnelState, globalState TunnelState, err error)) *TunnelChangeCallback { + s := &TunnelChangeCallback{cb} + tunnelChangeCallbacks[s] = true + return s +} +func (cb *TunnelChangeCallback) Unregister() { + delete(tunnelChangeCallbacks, cb) +} +func IPCClientRegisterTunnelsChange(cb func()) *TunnelsChangeCallback { + s := &TunnelsChangeCallback{cb} + tunnelsChangeCallbacks[s] = true + return s +} +func (cb *TunnelsChangeCallback) Unregister() { + delete(tunnelsChangeCallbacks, cb) +} +func IPCClientRegisterManagerStopping(cb func()) *ManagerStoppingCallback { + s := &ManagerStoppingCallback{cb} + managerStoppingCallbacks[s] = true + return s +} +func (cb *ManagerStoppingCallback) Unregister() { + delete(managerStoppingCallbacks, cb) +} +func IPCClientRegisterUpdateFound(cb func(updateState UpdateState)) *UpdateFoundCallback { + s := &UpdateFoundCallback{cb} + updateFoundCallbacks[s] = true + return s +} +func (cb *UpdateFoundCallback) Unregister() { + delete(updateFoundCallbacks, cb) +} +func IPCClientRegisterUpdateProgress(cb func(dp updater.DownloadProgress)) *UpdateProgressCallback { + s := &UpdateProgressCallback{cb} + updateProgressCallbacks[s] = true + return s +} +func (cb *UpdateProgressCallback) Unregister() { + delete(updateProgressCallbacks, cb) +} diff --git a/manager/ipc_pipe.go b/manager/ipc_pipe.go new file mode 100644 index 00000000..657a6275 --- /dev/null +++ b/manager/ipc_pipe.go @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package manager + +import ( + "os" + "strconv" + + "golang.org/x/sys/windows" +) + +type pipeRWC struct { + reader *os.File + writer *os.File +} + +func (p *pipeRWC) Read(b []byte) (int, error) { + return p.reader.Read(b) +} + +func (p *pipeRWC) Write(b []byte) (int, error) { + return p.writer.Write(b) +} + +func (p *pipeRWC) Close() error { + err1 := p.writer.Close() + err2 := p.reader.Close() + if err1 != nil { + return err1 + } + return err2 +} + +func makeInheritableAndGetStr(f *os.File) (str string, err error) { + sc, err := f.SyscallConn() + if err != nil { + return + } + err2 := sc.Control(func(fd uintptr) { + err = windows.SetHandleInformation(windows.Handle(fd), windows.HANDLE_FLAG_INHERIT, windows.HANDLE_FLAG_INHERIT) + str = strconv.FormatUint(uint64(fd), 10) + }) + if err2 != nil { + err = err2 + } + return +} + +func inheritableEvents() (ourEvents *os.File, theirEvents *os.File, theirEventStr string, err error) { + theirEvents, ourEvents, err = os.Pipe() + if err != nil { + return + } + theirEventStr, err = makeInheritableAndGetStr(theirEvents) + return +} + +func inheritableSocketpairEmulation() (ourReader *os.File, theirReader *os.File, theirReaderStr string, ourWriter *os.File, theirWriter *os.File, theirWriterStr string, err error) { + ourReader, theirWriter, err = os.Pipe() + if err != nil { + return + } + theirWriterStr, err = makeInheritableAndGetStr(theirWriter) + if err != nil { + return + } + + theirReader, ourWriter, err = os.Pipe() + if err != nil { + return + } + theirReaderStr, err = makeInheritableAndGetStr(theirReader) + return +} diff --git a/manager/ipc_server.go b/manager/ipc_server.go new file mode 100644 index 00000000..0accb4d3 --- /dev/null +++ b/manager/ipc_server.go @@ -0,0 +1,348 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package manager + +import ( + "bytes" + "encoding/gob" + "fmt" + "io/ioutil" + "log" + "net/rpc" + "os" + "sync" + "sync/atomic" + "time" + + "github.com/Microsoft/go-winio" + "golang.org/x/sys/windows" + "golang.org/x/sys/windows/svc" + + "golang.zx2c4.com/wireguard/windows/conf" + "golang.zx2c4.com/wireguard/windows/updater" +) + +var managerServices = make(map[*ManagerService]bool) +var managerServicesLock sync.RWMutex +var haveQuit uint32 +var quitManagersChan = make(chan struct{}, 1) + +type ManagerService struct { + events *os.File + elevatedToken windows.Token +} + +func (s *ManagerService) StoredConfig(tunnelName string, config *conf.Config) error { + c, err := conf.LoadFromName(tunnelName) + if err != nil { + return err + } + *config = *c + return nil +} + +func (s *ManagerService) RuntimeConfig(tunnelName string, config *conf.Config) error { + storedConfig, err := conf.LoadFromName(tunnelName) + if err != nil { + return err + } + pipePath, err := PipePathOfTunnel(storedConfig.Name) + if err != nil { + return err + } + pipe, err := winio.DialPipe(pipePath, nil) + if err != nil { + return err + } + pipe.SetWriteDeadline(time.Now().Add(time.Second * 2)) + _, err = pipe.Write([]byte("get=1\n\n")) + if err != nil { + return err + } + pipe.SetReadDeadline(time.Now().Add(time.Second * 2)) + resp, err := ioutil.ReadAll(pipe) + if err != nil { + return err + } + pipe.Close() + runtimeConfig, err := conf.FromUAPI(string(resp), storedConfig) + if err != nil { + return err + } + *config = *runtimeConfig + return nil +} + +func (s *ManagerService) Start(tunnelName string, unused *uintptr) error { + // For now, enforce only one tunnel at a time. Later we'll remove this silly restriction. + trackedTunnelsLock.Lock() + tt := make([]string, 0, len(trackedTunnels)) + var inTransition string + for t, state := range trackedTunnels { + tt = append(tt, t) + if len(t) > 0 && (state == TunnelStarting || state == TunnelUnknown) { + inTransition = t + break + } + } + trackedTunnelsLock.Unlock() + if len(inTransition) != 0 { + return fmt.Errorf("Please allow the tunnel ā€˜%sā€™ to finish activating", inTransition) + } + go func() { + for _, t := range tt { + s.Stop(t, unused) + } + for _, t := range tt { + var state TunnelState + var unused uintptr + if s.State(t, &state) == nil && (state == TunnelStarted || state == TunnelStarting) { + log.Printf("[%s] Trying again to stop zombie tunnel", t) + s.Stop(t, &unused) + time.Sleep(time.Millisecond * 100) + } + } + }() + + // After that process is started -- it's somewhat asynchronous -- we install the new one. + c, err := conf.LoadFromName(tunnelName) + if err != nil { + return err + } + path, err := c.Path() + if err != nil { + return err + } + return InstallTunnel(path) +} + +func (s *ManagerService) Stop(tunnelName string, _ *uintptr) error { + err := UninstallTunnel(tunnelName) + if err == windows.ERROR_SERVICE_DOES_NOT_EXIST { + _, notExistsError := conf.LoadFromName(tunnelName) + if notExistsError == nil { + return nil + } + } + return err +} + +func (s *ManagerService) WaitForStop(tunnelName string, _ *uintptr) error { + serviceName, err := ServiceNameOfTunnel(tunnelName) + if err != nil { + return err + } + m, err := serviceManager() + if err != nil { + return err + } + for { + service, err := m.OpenService(serviceName) + if err == nil || err == windows.ERROR_SERVICE_MARKED_FOR_DELETE { + service.Close() + time.Sleep(time.Second / 3) + } else { + return nil + } + } +} + +func (s *ManagerService) Delete(tunnelName string, _ *uintptr) error { + err := s.Stop(tunnelName, nil) + if err != nil { + return err + } + return conf.DeleteName(tunnelName) +} + +func (s *ManagerService) State(tunnelName string, state *TunnelState) error { + serviceName, err := ServiceNameOfTunnel(tunnelName) + if err != nil { + return err + } + m, err := serviceManager() + if err != nil { + return err + } + service, err := m.OpenService(serviceName) + if err != nil { + *state = TunnelStopped + return nil + } + defer service.Close() + status, err := service.Query() + if err != nil { + *state = TunnelUnknown + return err + } + switch status.State { + case svc.Stopped: + *state = TunnelStopped + case svc.StopPending: + *state = TunnelStopping + case svc.Running: + *state = TunnelStarted + case svc.StartPending: + *state = TunnelStarting + default: + *state = TunnelUnknown + } + return nil +} + +func (s *ManagerService) GlobalState(_ uintptr, state *TunnelState) error { + *state = trackedTunnelsGlobalState() + return nil +} + +func (s *ManagerService) Create(tunnelConfig conf.Config, tunnel *Tunnel) error { + err := tunnelConfig.Save() + if err != nil { + return err + } + *tunnel = Tunnel{tunnelConfig.Name} + return nil + //TODO: handle already existing situation + //TODO: handle already running and existing situation +} + +func (s *ManagerService) Tunnels(_ uintptr, tunnels *[]Tunnel) error { + names, err := conf.ListConfigNames() + if err != nil { + return err + } + *tunnels = make([]Tunnel, len(names)) + for i := 0; i < len(*tunnels); i++ { + (*tunnels)[i].Name = names[i] + } + return nil + //TODO: account for running ones that aren't in the configuration store somehow +} + +func (s *ManagerService) Quit(stopTunnelsOnQuit bool, alreadyQuit *bool) error { + if !atomic.CompareAndSwapUint32(&haveQuit, 0, 1) { + *alreadyQuit = true + return nil + } + *alreadyQuit = false + + // Work around potential race condition of delivering messages to the wrong process by removing from notifications. + managerServicesLock.Lock() + delete(managerServices, s) + managerServicesLock.Unlock() + + if stopTunnelsOnQuit { + names, err := conf.ListConfigNames() + if err != nil { + return err + } + for _, name := range names { + UninstallTunnel(name) + } + } + + quitManagersChan <- struct{}{} + return nil +} + +func (s *ManagerService) UpdateState(_ uintptr, state *UpdateState) error { + *state = updateState + return nil +} + +func (s *ManagerService) Update(_ uintptr, _ *uintptr) error { + progress := updater.DownloadVerifyAndExecute(uintptr(s.elevatedToken)) + go func() { + for { + dp := <-progress + IPCServerNotifyUpdateProgress(dp) + if dp.Complete || dp.Error != nil { + return + } + } + }() + return nil +} + +func IPCServerListen(reader *os.File, writer *os.File, events *os.File, elevatedToken windows.Token) error { + service := &ManagerService{ + events: events, + elevatedToken: elevatedToken, + } + + server := rpc.NewServer() + err := server.Register(service) + if err != nil { + return err + } + + go func() { + managerServicesLock.Lock() + managerServices[service] = true + managerServicesLock.Unlock() + server.ServeConn(&pipeRWC{reader, writer}) + managerServicesLock.Lock() + delete(managerServices, service) + managerServicesLock.Unlock() + + }() + return nil +} + +func notifyAll(notificationType NotificationType, ifaces ...interface{}) { + if len(managerServices) == 0 { + return + } + + var buf bytes.Buffer + encoder := gob.NewEncoder(&buf) + err := encoder.Encode(notificationType) + if err != nil { + return + } + for _, iface := range ifaces { + err = encoder.Encode(iface) + if err != nil { + return + } + } + + managerServicesLock.RLock() + for m := range managerServices { + m.events.SetWriteDeadline(time.Now().Add(time.Second)) + m.events.Write(buf.Bytes()) + } + managerServicesLock.RUnlock() +} + +func IPCServerNotifyTunnelChange(name string, state TunnelState, err error) { + if err == nil { + notifyAll(TunnelChangeNotificationType, name, state, trackedTunnelsGlobalState(), "") + } else { + notifyAll(TunnelChangeNotificationType, name, state, trackedTunnelsGlobalState(), err.Error()) + } +} + +func IPCServerNotifyTunnelsChange() { + notifyAll(TunnelsChangeNotificationType) +} + +func IPCServerNotifyUpdateFound(state UpdateState) { + notifyAll(UpdateFoundNotificationType, state) +} + +func IPCServerNotifyUpdateProgress(dp updater.DownloadProgress) { + if dp.Error == nil { + notifyAll(UpdateProgressNotificationType, dp.Activity, dp.BytesDownloaded, dp.BytesTotal, "", dp.Complete) + } else { + notifyAll(UpdateProgressNotificationType, dp.Activity, dp.BytesDownloaded, dp.BytesTotal, dp.Error.Error(), dp.Complete) + } +} + +func IPCServerNotifyManagerStopping() { + notifyAll(ManagerStoppingNotificationType) + time.Sleep(time.Millisecond * 200) +} diff --git a/manager/names.go b/manager/names.go new file mode 100644 index 00000000..bebf0cae --- /dev/null +++ b/manager/names.go @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package manager + +import ( + "errors" + + "golang.zx2c4.com/wireguard/windows/conf" +) + +func ServiceNameOfTunnel(tunnelName string) (string, error) { + if !conf.TunnelNameIsValid(tunnelName) { + return "", errors.New("Tunnel name is not valid") + } + return "WireGuardTunnel$" + tunnelName, nil +} + +func PipePathOfTunnel(tunnelName string) (string, error) { + if !conf.TunnelNameIsValid(tunnelName) { + return "", errors.New("Tunnel name is not valid") + } + return "\\\\.\\pipe\\WireGuard\\" + tunnelName, nil +} diff --git a/manager/service.go b/manager/service.go new file mode 100644 index 00000000..ba7208d8 --- /dev/null +++ b/manager/service.go @@ -0,0 +1,331 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package manager + +import ( + "errors" + "fmt" + "log" + "os" + "runtime" + "runtime/debug" + "strings" + "sync" + "syscall" + "time" + "unsafe" + + "golang.org/x/sys/windows" + "golang.org/x/sys/windows/svc" + + "golang.zx2c4.com/wireguard/windows/conf" + "golang.zx2c4.com/wireguard/windows/ringlogger" + "golang.zx2c4.com/wireguard/windows/services" + "golang.zx2c4.com/wireguard/windows/version" +) + +type managerService struct{} + +func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) { + changes <- svc.Status{State: svc.StartPending} + + var err error + serviceError := services.ErrorSuccess + + defer func() { + svcSpecificEC, exitCode = services.DetermineErrorCode(err, serviceError) + logErr := services.CombineErrors(err, serviceError) + if logErr != nil { + log.Print(logErr) + } + changes <- svc.Status{State: svc.StopPending} + }() + + err = ringlogger.InitGlobalLogger("MGR") + if err != nil { + serviceError = services.ErrorRingloggerOpen + return + } + defer func() { + if x := recover(); x != nil { + for _, line := range append([]string{fmt.Sprint(x)}, strings.Split(string(debug.Stack()), "\n")...) { + if len(strings.TrimSpace(line)) > 0 { + log.Println(line) + } + } + panic(x) + } + }() + + log.Println("Starting", version.UserAgent()) + + path, err := os.Executable() + if err != nil { + serviceError = services.ErrorDetermineExecutablePath + return + } + + devNull, err := os.OpenFile(os.DevNull, os.O_RDWR, 0) + if err != nil { + serviceError = services.ErrorOpenNULFile + return + } + + err = trackExistingTunnels() + if err != nil { + serviceError = services.ErrorTrackTunnels + return + } + + conf.RegisterStoreChangeCallback(func() { conf.MigrateUnencryptedConfigs() }) // Ignore return value for now, but could be useful later. + conf.RegisterStoreChangeCallback(IPCServerNotifyTunnelsChange) + + procs := make(map[uint32]*os.Process) + aliveSessions := make(map[uint32]bool) + procsLock := sync.Mutex{} + var startProcess func(session uint32) + stoppingManager := false + + startProcess = func(session uint32) { + defer func() { + runtime.UnlockOSThread() + procsLock.Lock() + delete(aliveSessions, session) + procsLock.Unlock() + }() + + var userToken windows.Token + err := windows.WTSQueryUserToken(session, &userToken) + if err != nil { + return + } + if !services.TokenIsMemberOfBuiltInAdministrator(userToken) { + userToken.Close() + return + } + user, err := userToken.GetTokenUser() + if err != nil { + log.Printf("Unable to lookup user from token: %v", err) + userToken.Close() + return + } + username, domain, accType, err := user.User.Sid.LookupAccount("") + if err != nil { + log.Printf("Unable to lookup username from sid: %v", err) + userToken.Close() + return + } + if accType != windows.SidTypeUser { + userToken.Close() + return + } + var elevatedToken windows.Token + if userToken.IsElevated() { + elevatedToken = userToken + } else { + elevatedToken, err = userToken.GetLinkedToken() + userToken.Close() + if err != nil { + log.Printf("Unable to elevate token: %v", err) + return + } + if !elevatedToken.IsElevated() { + elevatedToken.Close() + log.Println("Linked token is not elevated") + return + } + } + defer elevatedToken.Close() + userToken = 0 + first := true + for { + if stoppingManager { + return + } + + procsLock.Lock() + if alive := aliveSessions[session]; !alive { + procsLock.Unlock() + return + } + procsLock.Unlock() + + if !first { + time.Sleep(time.Second) + } else { + first = false + } + + //TODO: we lock the OS thread so that these inheritable handles don't escape into other processes that + // might be running in parallel Go routines. But the Go runtime is strange and who knows what's really + // happening with these or what is inherited. We need to do some analysis to be certain of what's going on. + runtime.LockOSThread() + ourReader, theirReader, theirReaderStr, ourWriter, theirWriter, theirWriterStr, err := inheritableSocketpairEmulation() + if err != nil { + log.Printf("Unable to create two inheritable pipes: %v", err) + return + } + ourEvents, theirEvents, theirEventStr, err := inheritableEvents() + err = IPCServerListen(ourReader, ourWriter, ourEvents, elevatedToken) + if err != nil { + log.Printf("Unable to listen on IPC pipes: %v", err) + return + } + theirLogMapping, theirLogMappingHandle, err := ringlogger.Global.ExportInheritableMappingHandleStr() + if err != nil { + log.Printf("Unable to export inheritable mapping handle for logging: %v", err) + return + } + + log.Printf("Starting UI process for user '%s@%s' for session %d", username, domain, session) + attr := &os.ProcAttr{ + Sys: &syscall.SysProcAttr{ + Token: syscall.Token(elevatedToken), + }, + Files: []*os.File{devNull, devNull, devNull}, + } + procsLock.Lock() + var proc *os.Process + if alive := aliveSessions[session]; alive { + proc, err = os.StartProcess(path, []string{path, "/ui", theirReaderStr, theirWriterStr, theirEventStr, theirLogMapping}, attr) + } else { + err = errors.New("Session has logged out") + } + procsLock.Unlock() + theirReader.Close() + theirWriter.Close() + theirEvents.Close() + windows.Close(theirLogMappingHandle) + runtime.UnlockOSThread() + if err != nil { + ourReader.Close() + ourWriter.Close() + ourEvents.Close() + log.Printf("Unable to start manager UI process for user '%s@%s' for session %d: %v", username, domain, session, err) + return + } + + procsLock.Lock() + procs[session] = proc + procsLock.Unlock() + + sessionIsDead := false + processStatus, err := proc.Wait() + if err == nil { + exitCode := processStatus.Sys().(syscall.WaitStatus).ExitCode + log.Printf("Exited UI process for user '%s@%s' for session %d with status %x", username, domain, session, exitCode) + const STATUS_DLL_INIT_FAILED_LOGOFF = 0xC000026B + sessionIsDead = exitCode == STATUS_DLL_INIT_FAILED_LOGOFF + } else { + log.Printf("Unable to wait for UI process for user '%s@%s' for session %d: %v", username, domain, session, err) + } + + procsLock.Lock() + delete(procs, session) + procsLock.Unlock() + ourReader.Close() + ourWriter.Close() + ourEvents.Close() + + if sessionIsDead { + return + } + } + } + + go checkForUpdates() + + var sessionsPointer *windows.WTS_SESSION_INFO + var count uint32 + err = windows.WTSEnumerateSessions(0, 0, 1, &sessionsPointer, &count) + if err != nil { + serviceError = services.ErrorEnumerateSessions + return + } + sessions := *(*[]windows.WTS_SESSION_INFO)(unsafe.Pointer(&struct { + addr *windows.WTS_SESSION_INFO + len int + cap int + }{sessionsPointer, int(count), int(count)})) + for _, session := range sessions { + if session.State != windows.WTSActive && session.State != windows.WTSDisconnected { + continue + } + procsLock.Lock() + if alive := aliveSessions[session.SessionID]; !alive { + aliveSessions[session.SessionID] = true + if _, ok := procs[session.SessionID]; !ok { + go startProcess(session.SessionID) + } + } + procsLock.Unlock() + } + windows.WTSFreeMemory(uintptr(unsafe.Pointer(sessionsPointer))) + + changes <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptSessionChange} + + uninstall := false +loop: + for { + select { + case <-quitManagersChan: + uninstall = true + break loop + case c := <-r: + switch c.Cmd { + case svc.Stop: + break loop + case svc.Interrogate: + changes <- c.CurrentStatus + case svc.SessionChange: + if c.EventType != windows.WTS_SESSION_LOGON && c.EventType != windows.WTS_SESSION_LOGOFF { + continue + } + sessionNotification := (*windows.WTSSESSION_NOTIFICATION)(unsafe.Pointer(c.EventData)) + if uintptr(sessionNotification.Size) != unsafe.Sizeof(*sessionNotification) { + log.Printf("Unexpected size of WTSSESSION_NOTIFICATION: %d", sessionNotification.Size) + continue + } + if c.EventType == windows.WTS_SESSION_LOGOFF { + procsLock.Lock() + delete(aliveSessions, sessionNotification.SessionID) + if proc, ok := procs[sessionNotification.SessionID]; ok { + proc.Kill() + } + procsLock.Unlock() + } else if c.EventType == windows.WTS_SESSION_LOGON { + procsLock.Lock() + if alive := aliveSessions[sessionNotification.SessionID]; !alive { + aliveSessions[sessionNotification.SessionID] = true + if _, ok := procs[sessionNotification.SessionID]; !ok { + go startProcess(sessionNotification.SessionID) + } + } + procsLock.Unlock() + } + + default: + log.Printf("Unexpected service control request #%d", c) + } + } + } + + changes <- svc.Status{State: svc.StopPending} + procsLock.Lock() + stoppingManager = true + IPCServerNotifyManagerStopping() + for _, proc := range procs { + proc.Kill() + } + procsLock.Unlock() + if uninstall { + err = UninstallManager() + if err != nil { + log.Printf("Unable to uninstaller manager when quitting: %v", err) + } + } + return +} diff --git a/manager/tunneltracker.go b/manager/tunneltracker.go new file mode 100644 index 00000000..1cde98e2 --- /dev/null +++ b/manager/tunneltracker.go @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package manager + +import ( + "fmt" + "log" + "runtime" + "sync" + "syscall" + "time" + + "golang.org/x/sys/windows" + "golang.org/x/sys/windows/svc" + "golang.org/x/sys/windows/svc/mgr" + + "golang.zx2c4.com/wireguard/windows/conf" + "golang.zx2c4.com/wireguard/windows/services" +) + +func trackExistingTunnels() error { + m, err := serviceManager() + if err != nil { + return err + } + names, err := conf.ListConfigNames() + if err != nil { + return err + } + for _, name := range names { + serviceName, err := ServiceNameOfTunnel(name) + if err != nil { + continue + } + service, err := m.OpenService(serviceName) + if err != nil { + continue + } + go trackTunnelService(name, service) + } + return nil +} + +var serviceTrackerCallbackPtr = windows.NewCallback(func(notifier *windows.SERVICE_NOTIFY) uintptr { + return 0 +}) + +var trackedTunnels = make(map[string]TunnelState) +var trackedTunnelsLock = sync.Mutex{} + +func svcStateToTunState(s svc.State) TunnelState { + switch s { + case svc.StartPending: + return TunnelStarting + case svc.Running: + return TunnelStarted + case svc.StopPending: + return TunnelStopping + case svc.Stopped: + return TunnelStopped + default: + return TunnelUnknown + } +} + +func trackedTunnelsGlobalState() (state TunnelState) { + state = TunnelStopped + trackedTunnelsLock.Lock() + defer trackedTunnelsLock.Unlock() + for _, s := range trackedTunnels { + if s == TunnelStarting { + return TunnelStarting + } else if s == TunnelStopping { + return TunnelStopping + } else if s == TunnelStarted || s == TunnelUnknown { + state = TunnelStarted + } + } + return +} + +func trackTunnelService(tunnelName string, service *mgr.Service) { + defer func() { + service.Close() + log.Printf("[%s] Tunnel managerService tracker finished", tunnelName) + }() + + trackedTunnelsLock.Lock() + if _, found := trackedTunnels[tunnelName]; found { + trackedTunnelsLock.Unlock() + return + } + trackedTunnels[tunnelName] = TunnelUnknown + trackedTunnelsLock.Unlock() + defer func() { + trackedTunnelsLock.Lock() + delete(trackedTunnels, tunnelName) + trackedTunnelsLock.Unlock() + }() + + const serviceNotifications = windows.SERVICE_NOTIFY_RUNNING | windows.SERVICE_NOTIFY_START_PENDING | windows.SERVICE_NOTIFY_STOP_PENDING | windows.SERVICE_NOTIFY_STOPPED | windows.SERVICE_NOTIFY_DELETE_PENDING + notifier := &windows.SERVICE_NOTIFY{ + Version: windows.SERVICE_NOTIFY_STATUS_CHANGE, + NotifyCallback: serviceTrackerCallbackPtr, + } + + checkForDisabled := func() (shouldReturn bool) { + config, err := service.Config() + if err == windows.ERROR_SERVICE_MARKED_FOR_DELETE || config.StartType == windows.SERVICE_DISABLED { + log.Printf("[%s] Found disabled service via timeout, so deleting", tunnelName) + service.Delete() + trackedTunnelsLock.Lock() + trackedTunnels[tunnelName] = TunnelStopped + trackedTunnelsLock.Unlock() + IPCServerNotifyTunnelChange(tunnelName, TunnelStopped, nil) + return true + } + return false + } + if checkForDisabled() { + return + } + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + lastState := TunnelUnknown + for { + err := windows.NotifyServiceStatusChange(service.Handle, serviceNotifications, notifier) + switch err { + case nil: + for { + if windows.SleepEx(uint32(time.Second*3/time.Millisecond), true) == windows.WAIT_IO_COMPLETION { + break + } else if checkForDisabled() { + return + } + } + case windows.ERROR_SERVICE_MARKED_FOR_DELETE: + trackedTunnelsLock.Lock() + trackedTunnels[tunnelName] = TunnelStopped + trackedTunnelsLock.Unlock() + IPCServerNotifyTunnelChange(tunnelName, TunnelStopped, nil) + return + case windows.ERROR_SERVICE_NOTIFY_CLIENT_LAGGING: + continue + default: + trackedTunnelsLock.Lock() + trackedTunnels[tunnelName] = TunnelStopped + trackedTunnelsLock.Unlock() + IPCServerNotifyTunnelChange(tunnelName, TunnelStopped, fmt.Errorf("Unable to continue monitoring managerService, so stopping: %v", err)) + service.Control(svc.Stop) + return + } + + state := svcStateToTunState(svc.State(notifier.ServiceStatus.CurrentState)) + var tunnelError error + if state == TunnelStopped { + if notifier.ServiceStatus.Win32ExitCode == uint32(windows.ERROR_SERVICE_SPECIFIC_ERROR) { + maybeErr := services.Error(notifier.ServiceStatus.ServiceSpecificExitCode) + if maybeErr != services.ErrorSuccess { + tunnelError = maybeErr + } + } else { + switch notifier.ServiceStatus.Win32ExitCode { + case uint32(windows.NO_ERROR), uint32(windows.ERROR_SERVICE_NEVER_STARTED): + default: + tunnelError = syscall.Errno(notifier.ServiceStatus.Win32ExitCode) + } + } + } + if state != lastState { + trackedTunnelsLock.Lock() + trackedTunnels[tunnelName] = state + trackedTunnelsLock.Unlock() + IPCServerNotifyTunnelChange(tunnelName, state, tunnelError) + lastState = state + } + } +} diff --git a/manager/updatestate.go b/manager/updatestate.go new file mode 100644 index 00000000..2e82baf8 --- /dev/null +++ b/manager/updatestate.go @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package manager + +import ( + "log" + "time" + + "golang.zx2c4.com/wireguard/windows/updater" + "golang.zx2c4.com/wireguard/windows/version" +) + +type UpdateState uint32 + +const ( + UpdateStateUnknown UpdateState = iota + UpdateStateFoundUpdate + UpdateStateUpdatesDisabledUnofficialBuild +) + +var updateState = UpdateStateUnknown + +func checkForUpdates() { + if !version.IsRunningOfficialVersion() { + log.Println("Build is not official, so updates are disabled") + updateState = UpdateStateUpdatesDisabledUnofficialBuild + IPCServerNotifyUpdateFound(updateState) + return + } + + time.Sleep(time.Second * 10) + + first := true + for { + update, err := updater.CheckForUpdate() + if err == nil && update != nil { + log.Println("An update is available") + updateState = UpdateStateFoundUpdate + IPCServerNotifyUpdateFound(updateState) + return + } + if err != nil { + log.Printf("Update checker: %v", err) + if first { + time.Sleep(time.Minute * 4) + first = false + } else { + time.Sleep(time.Minute * 25) + } + } else { + time.Sleep(time.Hour) + } + } +} diff --git a/service/defaultroutemonitor.go b/service/defaultroutemonitor.go deleted file mode 100644 index d4105447..00000000 --- a/service/defaultroutemonitor.go +++ /dev/null @@ -1,150 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package service - -import ( - "log" - "time" - - "golang.org/x/sys/windows" - "golang.zx2c4.com/winipcfg" - "golang.zx2c4.com/wireguard/device" - "golang.zx2c4.com/wireguard/tun" -) - -func bindSocketRoute(family winipcfg.AddressFamily, device *device.Device, ourLUID uint64, lastLUID *uint64, lastIndex *uint32) error { - routes, err := winipcfg.GetRoutes(family) - if err != nil { - return err - } - lowestMetric := ^uint32(0) - index := uint32(0) // Zero is "unspecified", which for IP_UNICAST_IF resets the value, which is what we want. - luid := uint64(0) // Hopefully luid zero is unspecified, but hard to find docs saying so. - for _, route := range routes { - if route.DestinationPrefix.PrefixLength != 0 || route.InterfaceLUID == ourLUID { - continue - } - ifrow, err := winipcfg.GetIfRow(route.InterfaceLUID) - if err != nil || ifrow.OperStatus != winipcfg.IfOperStatusUp { - log.Printf("Found default route for interface %d, but not up, so skipping", route.InterfaceIndex) - continue - } - if route.Metric < lowestMetric { - lowestMetric = route.Metric - index = route.InterfaceIndex - luid = route.InterfaceLUID - } - } - if luid == *lastLUID && index == *lastIndex { - return nil - } - *lastLUID = luid - *lastIndex = index - if family == windows.AF_INET { - log.Printf("Binding UDPv4 socket to interface %d", index) - return device.BindSocketToInterface4(index) - } else if family == windows.AF_INET6 { - log.Printf("Binding UDPv6 socket to interface %d", index) - return device.BindSocketToInterface6(index) - } - return nil -} - -func getIPInterfaceRetry(luid uint64, family winipcfg.AddressFamily, retry bool) (ipi *winipcfg.IPInterface, err error) { - const maxRetries = 100 - for i := 0; i < maxRetries; i++ { - ipi, err = winipcfg.GetIPInterface(luid, family) - if retry && i != maxRetries-1 && err == windows.ERROR_NOT_FOUND { - time.Sleep(time.Millisecond * 50) - continue - } - break - } - return -} - -func monitorDefaultRoutes(device *device.Device, autoMTU bool, tun *tun.NativeTun) (*winipcfg.RouteChangeCallback, error) { - ourLUID := tun.LUID() - lastLUID4 := uint64(0) - lastLUID6 := uint64(0) - lastIndex4 := uint32(0) - lastIndex6 := uint32(0) - lastMTU := uint32(0) - doIt := func(retry bool) error { - err := bindSocketRoute(windows.AF_INET, device, ourLUID, &lastLUID4, &lastIndex4) - if err != nil { - return err - } - err = bindSocketRoute(windows.AF_INET6, device, ourLUID, &lastLUID6, &lastIndex6) - if err != nil { - return err - } - if !autoMTU { - return nil - } - mtu := uint32(0) - if lastLUID4 != 0 { - iface, err := winipcfg.InterfaceFromLUID(lastLUID4) - if err != nil { - return err - } - if iface.MTU > 0 { - mtu = iface.MTU - } - } - if lastLUID6 != 0 { - iface, err := winipcfg.InterfaceFromLUID(lastLUID6) - if err != nil { - return err - } - if iface.MTU > 0 && iface.MTU < mtu { - mtu = iface.MTU - } - } - if mtu > 0 && (lastMTU == 0 || lastMTU != mtu) { - iface, err := getIPInterfaceRetry(ourLUID, windows.AF_INET, retry) - if err != nil { - return err - } - iface.NLMTU = mtu - 80 - if iface.NLMTU < 576 { - iface.NLMTU = 576 - } - err = iface.Set() - if err != nil { - return err - } - tun.ForceMTU(int(iface.NLMTU)) //TODO: it sort of breaks the model with v6 mtu and v4 mtu being different. Just set v4 one for now. - iface, err = getIPInterfaceRetry(ourLUID, windows.AF_INET6, retry) - if err != nil { - return err - } - iface.NLMTU = mtu - 80 - if iface.NLMTU < 1280 { - iface.NLMTU = 1280 - } - err = iface.Set() - if err != nil { - return err - } - lastMTU = mtu - } - return nil - } - err := doIt(true) - if err != nil { - return nil, err - } - cb, err := winipcfg.RegisterRouteChangeCallback(func(notificationType winipcfg.MibNotificationType, route *winipcfg.Route) { - if route.DestinationPrefix.PrefixLength == 0 { - _ = doIt(false) - } - }) - if err != nil { - return nil, err - } - return cb, nil -} diff --git a/service/errors.go b/service/errors.go deleted file mode 100644 index a2cb7bc5..00000000 --- a/service/errors.go +++ /dev/null @@ -1,96 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. - */ - -package service - -import ( - "fmt" - "syscall" - - "golang.org/x/sys/windows" -) - -type Error uint32 - -const ( - ErrorSuccess Error = iota - ErrorRingloggerOpen - ErrorLoadConfiguration - ErrorCreateWintun - ErrorDetermineWintunName - ErrorUAPIListen - ErrorDNSLookup - ErrorFirewall - ErrorDeviceSetConfig - ErrorBindSocketsToDefaultRoutes - ErrorSetNetConfig - ErrorDetermineExecutablePath - ErrorOpenNULFile - ErrorTrackTunnels - ErrorEnumerateSessions - ErrorDropPrivileges - ErrorWin32 -) - -func (e Error) Error() string { - switch e { - case ErrorSuccess: - return "No error" - case ErrorRingloggerOpen: - return "Unable to open log file" - case ErrorDetermineExecutablePath: - return "Unable to determine path of running executable" - case ErrorLoadConfiguration: - return "Unable to load configuration from path" - case ErrorCreateWintun: - return "Unable to create Wintun device" - case ErrorDetermineWintunName: - return "Unable to determine Wintun name" - case ErrorUAPIListen: - return "Unable to listen on named pipe" - case ErrorDNSLookup: - return "Unable to resolve one or more DNS hostname endpoints" - case ErrorFirewall: - return "Unable to enable firewall rules" - case ErrorDeviceSetConfig: - return "Unable to set device configuration" - case ErrorBindSocketsToDefaultRoutes: - return "Unable to bind sockets to default route" - case ErrorSetNetConfig: - return "Unable to set interface addresses, routes, dns, and/or adapter settings" - case ErrorOpenNULFile: - return "Unable to open NUL file" - case ErrorTrackTunnels: - return "Unable to track existing tunnels" - case ErrorEnumerateSessions: - return "Unable to enumerate current sessions" - case ErrorDropPrivileges: - return "Unable to drop privileges" - case ErrorWin32: - return "An internal Windows error has occurred" - default: - return "An unknown error has occurred" - } -} - -func determineErrorCode(err error, serviceError Error) (bool, uint32) { - if syserr, ok := err.(syscall.Errno); ok { - return false, uint32(syserr) - } else if serviceError != ErrorSuccess { - return true, uint32(serviceError) - } else { - return false, windows.NO_ERROR - } -} - -func combineErrors(err error, serviceError Error) error { - if serviceError != ErrorSuccess { - if err != nil { - return fmt.Errorf("%v: %v", serviceError, err) - } - return serviceError - } - return err -} diff --git a/service/firewall/blocker.go b/service/firewall/blocker.go deleted file mode 100644 index 8034935d..00000000 --- a/service/firewall/blocker.go +++ /dev/null @@ -1,201 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package firewall - -import ( - "errors" - "net" - "unsafe" - - "golang.org/x/sys/windows" -) - -type wfpObjectInstaller func(uintptr) error - -// -// Fundamental WireGuard specific WFP objects. -// -type baseObjects struct { - provider windows.GUID - filters windows.GUID -} - -var wfpSession uintptr - -func createWfpSession() (uintptr, error) { - sessionDisplayData, err := createWtFwpmDisplayData0("WireGuard", "WireGuard dynamic session") - if err != nil { - return 0, wrapErr(err) - } - - session := wtFwpmSession0{ - displayData: *sessionDisplayData, - flags: cFWPM_SESSION_FLAG_DYNAMIC, - txnWaitTimeoutInMSec: windows.INFINITE, - } - - sessionHandle := uintptr(0) - - err = fwpmEngineOpen0(nil, cRPC_C_AUTHN_WINNT, nil, &session, unsafe.Pointer(&sessionHandle)) - if err != nil { - return 0, wrapErr(err) - } - - return sessionHandle, nil -} - -func registerBaseObjects(session uintptr) (*baseObjects, error) { - // {48E29F38-7492-4436-8F92-29D78A8D29D3} - providerGUID := windows.GUID{ - Data1: 0x48e29f38, - Data2: 0x7492, - Data3: 0x4436, - Data4: [8]byte{0x8f, 0x92, 0x29, 0xd7, 0x8a, 0x8d, 0x29, 0xd3}, - } - // {FE3DB7F8-4658-4DE5-8DA9-CE5086A8266B} - filtersGUID := windows.GUID{ - Data1: 0xfe3db7f8, - Data2: 0x4658, - Data3: 0x4de5, - Data4: [8]byte{0x8d, 0xa9, 0xce, 0x50, 0x86, 0xa8, 0x26, 0x6b}, - } - - // - // Register provider. - // - { - displayData, err := createWtFwpmDisplayData0("WireGuard", "The WireGuard provider") - if err != nil { - return nil, wrapErr(err) - } - provider := wtFwpmProvider0{ - providerKey: providerGUID, - displayData: *displayData, - } - err = fwpmProviderAdd0(session, &provider, 0) - if err != nil { - //TODO: cleanup entire call chain of these if failure? - return nil, wrapErr(err) - } - } - - // - // Register filters sublayer. - // - { - displayData, err := createWtFwpmDisplayData0("WireGuard filters", "Permissive and blocking filters") - if err != nil { - return nil, wrapErr(err) - } - sublayer := wtFwpmSublayer0{ - subLayerKey: filtersGUID, - displayData: *displayData, - providerKey: &providerGUID, - weight: ^uint16(0), - } - err = fwpmSubLayerAdd0(session, &sublayer, 0) - if err != nil { - return nil, wrapErr(err) - } - } - - return &baseObjects{ - providerGUID, - filtersGUID, - }, nil -} - -func EnableFirewall(luid uint64, restrictToDNSServers []net.IP, restrictAll bool) error { - if wfpSession != 0 { - return errors.New("The firewall has already been enabled") - } - - session, err := createWfpSession() - if err != nil { - return wrapErr(err) - } - - objectInstaller := func(session uintptr) error { - baseObjects, err := registerBaseObjects(session) - if err != nil { - return wrapErr(err) - } - - if len(restrictToDNSServers) > 0 { - err = blockDNS(restrictToDNSServers, session, baseObjects, 15, 14) - if err != nil { - return wrapErr(err) - } - } - - if restrictAll { - err = permitLoopback(session, baseObjects, 13) - if err != nil { - return wrapErr(err) - } - } - - err = permitTunInterface(session, baseObjects, 12, luid) - if err != nil { - return wrapErr(err) - } - - err = permitWireGuardService(session, baseObjects, 12) - if err != nil { - return wrapErr(err) - } - - if restrictAll { - err = permitDHCPIPv4(session, baseObjects, 12) - if err != nil { - return wrapErr(err) - } - - err = permitDHCPIPv6(session, baseObjects, 12) - if err != nil { - return wrapErr(err) - } - - err = permitNdp(session, baseObjects, 12) - if err != nil { - return wrapErr(err) - } - - /* TODO: actually evaluate if this does anything and if we need this. It's layer 2; our other rules are layer 3. - * In other words, if somebody complains, try enabling it. For now, keep it off. - err = permitHyperV(session, baseObjects, 12) - if err != nil { - return wrapErr(err) - } - */ - } - - if restrictAll { - err = blockAll(session, baseObjects, 0) - if err != nil { - return wrapErr(err) - } - } - - return nil - } - - err = runTransaction(session, objectInstaller) - if err != nil { - fwpmEngineClose0(session) - return wrapErr(err) - } - - wfpSession = session - return nil -} - -func DisableFirewall() { - if wfpSession != 0 { - fwpmEngineClose0(wfpSession) - wfpSession = 0 - } -} diff --git a/service/firewall/helpers.go b/service/firewall/helpers.go deleted file mode 100644 index e340b802..00000000 --- a/service/firewall/helpers.go +++ /dev/null @@ -1,105 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package firewall - -import ( - "fmt" - "os" - "runtime" - "syscall" - "unsafe" - - "golang.org/x/sys/windows" -) - -func runTransaction(session uintptr, operation wfpObjectInstaller) error { - err := fwpmTransactionBegin0(session, 0) - if err != nil { - return wrapErr(err) - } - - err = operation(session) - if err != nil { - fwpmTransactionAbort0(session) - return wrapErr(err) - } - - err = fwpmTransactionCommit0(session) - if err != nil { - fwpmTransactionAbort0(session) - return wrapErr(err) - } - - return nil -} - -func createWtFwpmDisplayData0(name, description string) (*wtFwpmDisplayData0, error) { - namePtr, err := windows.UTF16PtrFromString(name) - if err != nil { - return nil, wrapErr(err) - } - - descriptionPtr, err := windows.UTF16PtrFromString(description) - if err != nil { - return nil, wrapErr(err) - } - - return &wtFwpmDisplayData0{ - name: namePtr, - description: descriptionPtr, - }, nil -} - -func filterWeight(weight uint8) wtFwpValue0 { - return wtFwpValue0{ - _type: cFWP_UINT8, - value: uintptr(weight), - } -} - -func wrapErr(err error) error { - if _, ok := err.(syscall.Errno); !ok { - return err - } - _, file, line, ok := runtime.Caller(1) - if !ok { - return fmt.Errorf("Firewall error at unknown location: %v", err) - } - return fmt.Errorf("Firewall error at %s:%d: %v", file, line, err) -} - -func getCurrentProcessSecurityDescriptor() (*wtFwpByteBlob, error) { - procHandle, err := windows.GetCurrentProcess() - if err != nil { - panic(err) - } - blob := &wtFwpByteBlob{} - err = getSecurityInfo(procHandle, cSE_KERNEL_OBJECT, cDACL_SECURITY_INFORMATION, nil, nil, nil, nil, (*uintptr)(unsafe.Pointer(&blob.data))) - if err != nil { - return nil, wrapErr(err) - } - blob.size = getSecurityDescriptorLength(uintptr(unsafe.Pointer(blob.data))) - return blob, nil -} - -func getCurrentProcessAppID() (*wtFwpByteBlob, error) { - currentFile, err := os.Executable() - if err != nil { - return nil, wrapErr(err) - } - - curFilePtr, err := windows.UTF16PtrFromString(currentFile) - if err != nil { - return nil, wrapErr(err) - } - - var appID *wtFwpByteBlob - err = fwpmGetAppIdFromFileName0(curFilePtr, unsafe.Pointer(&appID)) - if err != nil { - return nil, wrapErr(err) - } - return appID, nil -} diff --git a/service/firewall/mksyscall.go b/service/firewall/mksyscall.go deleted file mode 100644 index 5e4bcaa0..00000000 --- a/service/firewall/mksyscall.go +++ /dev/null @@ -1,8 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package firewall - -//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go diff --git a/service/firewall/rules.go b/service/firewall/rules.go deleted file mode 100644 index 76e2a85b..00000000 --- a/service/firewall/rules.go +++ /dev/null @@ -1,1240 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package firewall - -import ( - "encoding/binary" - "errors" - "net" - "runtime" - "unsafe" - - "golang.org/x/sys/windows" - "golang.zx2c4.com/wireguard/windows/version" -) - -// -// Known addresses. -// -var ( - linkLocal = wtFwpV6AddrAndMask{[16]uint8{0xfe, 0x80}, 10} - - linkLocalDHCPMulticast = wtFwpByteArray16{[16]uint8{0xFF, 0x02, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x2}} - siteLocalDHCPMulticast = wtFwpByteArray16{[16]uint8{0xFF, 0x05, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x3}} - - linkLocalRouterMulticast = wtFwpByteArray16{[16]uint8{0xFF, 0x02, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}} -) - -func permitTunInterface(session uintptr, baseObjects *baseObjects, weight uint8, ifLUID uint64) error { - ifaceCondition := wtFwpmFilterCondition0{ - fieldKey: cFWPM_CONDITION_IP_LOCAL_INTERFACE, - matchType: cFWP_MATCH_EQUAL, - conditionValue: wtFwpConditionValue0{ - _type: cFWP_UINT64, - value: (uintptr)(unsafe.Pointer(&ifLUID)), - }, - } - - filter := wtFwpmFilter0{ - providerKey: &baseObjects.provider, - subLayerKey: baseObjects.filters, - weight: filterWeight(weight), - numFilterConditions: 1, - filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&ifaceCondition)), - action: wtFwpmAction0{ - _type: cFWP_ACTION_PERMIT, - }, - } - - filterID := uint64(0) - - // - // #1 Permit outbound IPv4 traffic. - // - { - displayData, err := createWtFwpmDisplayData0("Permit outbound IPv4 traffic on TUN", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #2 Permit inbound IPv4 traffic. - // - { - displayData, err := createWtFwpmDisplayData0("Permit inbound IPv4 traffic on TUN", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #3 Permit outbound IPv6 traffic. - // - { - displayData, err := createWtFwpmDisplayData0("Permit outbound IPv6 traffic on TUN", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #4 Permit inbound IPv6 traffic. - // - { - displayData, err := createWtFwpmDisplayData0("Permit inbound IPv6 traffic on TUN", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - return nil -} - -func permitWireGuardService(session uintptr, baseObjects *baseObjects, weight uint8) error { - var conditions [2]wtFwpmFilterCondition0 - - // - // First condition is the exe path of the current process. - // - appID, err := getCurrentProcessAppID() - if err != nil { - return wrapErr(err) - } - defer fwpmFreeMemory0(unsafe.Pointer(&appID)) - - conditions[0] = wtFwpmFilterCondition0{ - fieldKey: cFWPM_CONDITION_ALE_APP_ID, - matchType: cFWP_MATCH_EQUAL, - conditionValue: wtFwpConditionValue0{ - _type: cFWP_BYTE_BLOB_TYPE, - value: uintptr(unsafe.Pointer(appID)), - }, - } - - // - // Second condition is the SECURITY_DESCRIPTOR of the current process. - // This prevents low privileged applications hosted in the same exe from matching this filter. - // - sd, err := getCurrentProcessSecurityDescriptor() - if err != nil { - return wrapErr(err) - } - defer windows.LocalFree(windows.Handle(unsafe.Pointer(sd.data))) - - conditions[1] = wtFwpmFilterCondition0{ - fieldKey: cFWPM_CONDITION_ALE_USER_ID, - matchType: cFWP_MATCH_EQUAL, - conditionValue: wtFwpConditionValue0{ - _type: cFWP_SECURITY_DESCRIPTOR_TYPE, - value: uintptr(unsafe.Pointer(sd)), - }, - } - - // - // Assemble the filter. - // - filter := wtFwpmFilter0{ - providerKey: &baseObjects.provider, - subLayerKey: baseObjects.filters, - weight: filterWeight(weight), - flags: cFWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT, - numFilterConditions: uint32(len(conditions)), - filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&conditions)), - action: wtFwpmAction0{ - _type: cFWP_ACTION_PERMIT, - }, - } - - filterID := uint64(0) - - // - // #1 Permit outbound IPv4 traffic. - // - { - displayData, err := createWtFwpmDisplayData0("Permit unrestricted outbound traffic for WireGuard service (IPv4)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #2 Permit inbound IPv4 traffic. - // - { - displayData, err := createWtFwpmDisplayData0("Permit unrestricted inbound traffic for WireGuard service (IPv4)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #3 Permit outbound IPv6 traffic. - // - { - displayData, err := createWtFwpmDisplayData0("Permit unrestricted outbound traffic for WireGuard service (IPv6)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #4 Permit inbound IPv6 traffic. - // - { - displayData, err := createWtFwpmDisplayData0("Permit unrestricted inbound traffic for WireGuard service (IPv6)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - return nil -} - -func permitLoopback(session uintptr, baseObjects *baseObjects, weight uint8) error { - condition := wtFwpmFilterCondition0{ - fieldKey: cFWPM_CONDITION_INTERFACE_TYPE, - matchType: cFWP_MATCH_EQUAL, - conditionValue: wtFwpConditionValue0{ - _type: cFWP_UINT32, - value: uintptr(cIF_TYPE_SOFTWARE_LOOPBACK), - }, - } - - filter := wtFwpmFilter0{ - providerKey: &baseObjects.provider, - subLayerKey: baseObjects.filters, - weight: filterWeight(weight), - numFilterConditions: 1, - filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&condition)), - action: wtFwpmAction0{ - _type: cFWP_ACTION_PERMIT, - }, - } - - filterID := uint64(0) - - // - // #1 Permit outbound IPv4 on loopback. - // - { - displayData, err := createWtFwpmDisplayData0("Permit outbound on loopback (IPv4)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #2 Permit inbound IPv4 on loopback. - // - { - displayData, err := createWtFwpmDisplayData0("Permit inbound on loopback (IPv4)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #3 Permit outbound IPv6 on loopback. - // - { - displayData, err := createWtFwpmDisplayData0("Permit outbound on loopback (IPv6)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #4 Permit inbound IPv6 on loopback. - // - { - displayData, err := createWtFwpmDisplayData0("Permit inbound on loopback (IPv6)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - return nil -} - -func permitDHCPIPv4(session uintptr, baseObjects *baseObjects, weight uint8) error { - // - // #1 Outbound DHCP request on IPv4. - // - { - var conditions [4]wtFwpmFilterCondition0 - - conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL - conditions[0].matchType = cFWP_MATCH_EQUAL - conditions[0].conditionValue._type = cFWP_UINT8 - conditions[0].conditionValue.value = uintptr(cIPPROTO_UDP) - - conditions[1].fieldKey = cFWPM_CONDITION_IP_LOCAL_PORT - conditions[1].matchType = cFWP_MATCH_EQUAL - conditions[1].conditionValue._type = cFWP_UINT16 - conditions[1].conditionValue.value = uintptr(68) - - conditions[2].fieldKey = cFWPM_CONDITION_IP_REMOTE_PORT - conditions[2].matchType = cFWP_MATCH_EQUAL - conditions[2].conditionValue._type = cFWP_UINT16 - conditions[2].conditionValue.value = uintptr(67) - - conditions[3].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS - conditions[3].matchType = cFWP_MATCH_EQUAL - conditions[3].conditionValue._type = cFWP_UINT32 - conditions[3].conditionValue.value = uintptr(0xffffffff) - - displayData, err := createWtFwpmDisplayData0("Permit outbound DHCP request (IPv4)", "") - if err != nil { - return wrapErr(err) - } - - filter := wtFwpmFilter0{ - displayData: *displayData, - providerKey: &baseObjects.provider, - layerKey: cFWPM_LAYER_ALE_AUTH_CONNECT_V4, - subLayerKey: baseObjects.filters, - weight: filterWeight(weight), - numFilterConditions: uint32(len(conditions)), - filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&conditions)), - action: wtFwpmAction0{ - _type: cFWP_ACTION_PERMIT, - }, - } - - filterID := uint64(0) - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #2 Inbound DHCP response on IPv4. - // - { - var conditions [3]wtFwpmFilterCondition0 - - conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL - conditions[0].matchType = cFWP_MATCH_EQUAL - conditions[0].conditionValue._type = cFWP_UINT8 - conditions[0].conditionValue.value = uintptr(cIPPROTO_UDP) - - conditions[1].fieldKey = cFWPM_CONDITION_IP_LOCAL_PORT - conditions[1].matchType = cFWP_MATCH_EQUAL - conditions[1].conditionValue._type = cFWP_UINT16 - conditions[1].conditionValue.value = uintptr(68) - - conditions[2].fieldKey = cFWPM_CONDITION_IP_REMOTE_PORT - conditions[2].matchType = cFWP_MATCH_EQUAL - conditions[2].conditionValue._type = cFWP_UINT16 - conditions[2].conditionValue.value = uintptr(67) - - displayData, err := createWtFwpmDisplayData0("Permit inbound DHCP response (IPv4)", "") - if err != nil { - return wrapErr(err) - } - - filter := wtFwpmFilter0{ - displayData: *displayData, - providerKey: &baseObjects.provider, - layerKey: cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, - subLayerKey: baseObjects.filters, - weight: filterWeight(weight), - numFilterConditions: uint32(len(conditions)), - filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&conditions)), - action: wtFwpmAction0{ - _type: cFWP_ACTION_PERMIT, - }, - } - - filterID := uint64(0) - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - return nil -} - -func permitDHCPIPv6(session uintptr, baseObjects *baseObjects, weight uint8) error { - // - // #1 Outbound DHCP request on IPv6. - // - { - var conditions [6]wtFwpmFilterCondition0 - - conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL - conditions[0].matchType = cFWP_MATCH_EQUAL - conditions[0].conditionValue._type = cFWP_UINT8 - conditions[0].conditionValue.value = uintptr(cIPPROTO_UDP) - - conditions[1].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS - conditions[1].matchType = cFWP_MATCH_EQUAL - conditions[1].conditionValue._type = cFWP_BYTE_ARRAY16_TYPE - conditions[1].conditionValue.value = uintptr(unsafe.Pointer(&linkLocalDHCPMulticast)) - - // Repeat the condition type for logical OR. - conditions[2].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS - conditions[2].matchType = cFWP_MATCH_EQUAL - conditions[2].conditionValue._type = cFWP_BYTE_ARRAY16_TYPE - conditions[2].conditionValue.value = uintptr(unsafe.Pointer(&siteLocalDHCPMulticast)) - - conditions[3].fieldKey = cFWPM_CONDITION_IP_REMOTE_PORT - conditions[3].matchType = cFWP_MATCH_EQUAL - conditions[3].conditionValue._type = cFWP_UINT16 - conditions[3].conditionValue.value = uintptr(547) - - conditions[4].fieldKey = cFWPM_CONDITION_IP_LOCAL_ADDRESS - conditions[4].matchType = cFWP_MATCH_EQUAL - conditions[4].conditionValue._type = cFWP_V6_ADDR_MASK - conditions[4].conditionValue.value = uintptr(unsafe.Pointer(&linkLocal)) - - conditions[5].fieldKey = cFWPM_CONDITION_IP_LOCAL_PORT - conditions[5].matchType = cFWP_MATCH_EQUAL - conditions[5].conditionValue._type = cFWP_UINT16 - conditions[5].conditionValue.value = uintptr(546) - - displayData, err := createWtFwpmDisplayData0("Permit outbound DHCP request (IPv6)", "") - if err != nil { - return wrapErr(err) - } - - filter := wtFwpmFilter0{ - displayData: *displayData, - providerKey: &baseObjects.provider, - layerKey: cFWPM_LAYER_ALE_AUTH_CONNECT_V6, - subLayerKey: baseObjects.filters, - weight: filterWeight(weight), - numFilterConditions: uint32(len(conditions)), - filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&conditions)), - action: wtFwpmAction0{ - _type: cFWP_ACTION_PERMIT, - }, - } - - filterID := uint64(0) - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #2 Inbound DHCP response on IPv6. - // - { - var conditions [5]wtFwpmFilterCondition0 - - conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL - conditions[0].matchType = cFWP_MATCH_EQUAL - conditions[0].conditionValue._type = cFWP_UINT8 - conditions[0].conditionValue.value = uintptr(cIPPROTO_UDP) - - conditions[1].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS - conditions[1].matchType = cFWP_MATCH_EQUAL - conditions[1].conditionValue._type = cFWP_V6_ADDR_MASK - conditions[1].conditionValue.value = uintptr(unsafe.Pointer(&linkLocal)) - - conditions[2].fieldKey = cFWPM_CONDITION_IP_REMOTE_PORT - conditions[2].matchType = cFWP_MATCH_EQUAL - conditions[2].conditionValue._type = cFWP_UINT16 - conditions[2].conditionValue.value = uintptr(547) - - conditions[3].fieldKey = cFWPM_CONDITION_IP_LOCAL_ADDRESS - conditions[3].matchType = cFWP_MATCH_EQUAL - conditions[3].conditionValue._type = cFWP_V6_ADDR_MASK - conditions[3].conditionValue.value = uintptr(unsafe.Pointer(&linkLocal)) - - conditions[4].fieldKey = cFWPM_CONDITION_IP_LOCAL_PORT - conditions[4].matchType = cFWP_MATCH_EQUAL - conditions[4].conditionValue._type = cFWP_UINT16 - conditions[4].conditionValue.value = uintptr(546) - - displayData, err := createWtFwpmDisplayData0("Permit inbound DHCP response (IPv6)", "") - if err != nil { - return wrapErr(err) - } - - filter := wtFwpmFilter0{ - displayData: *displayData, - providerKey: &baseObjects.provider, - layerKey: cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, - subLayerKey: baseObjects.filters, - weight: filterWeight(weight), - numFilterConditions: uint32(len(conditions)), - filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&conditions)), - action: wtFwpmAction0{ - _type: cFWP_ACTION_PERMIT, - }, - } - - filterID := uint64(0) - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - return nil -} - -func permitNdp(session uintptr, baseObjects *baseObjects, weight uint8) error { - - /* TODO: actually handle the hop limit somehow! The rules should vaguely be: - * - icmpv6 133: must be outgoing, dst must be FF02::2/128, hop limit must be 255 - * - icmpv6 134: must be incoming, src must be FE80::/10, hop limit must be 255 - * - icmpv6 135: either incoming or outgoing, hop limit must be 255 - * - icmpv6 136: either incoming or outgoing, hop limit must be 255 - * - icmpv6 137: must be incoming, src must be FE80::/10, hop limit must be 255 - */ - - type filterDefinition struct { - displayData *wtFwpmDisplayData0 - conditions []wtFwpmFilterCondition0 - layer windows.GUID - } - - var defs []filterDefinition - - // - // Router Solicitation Message - // ICMP type 133, code 0. Outgoing. - // - { - conditions := make([]wtFwpmFilterCondition0, 4) - - conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL - conditions[0].matchType = cFWP_MATCH_EQUAL - conditions[0].conditionValue._type = cFWP_UINT8 - conditions[0].conditionValue.value = uintptr(cIPPROTO_ICMPV6) - - conditions[1].fieldKey = cFWPM_CONDITION_ICMP_TYPE - conditions[1].matchType = cFWP_MATCH_EQUAL - conditions[1].conditionValue._type = cFWP_UINT16 - conditions[1].conditionValue.value = uintptr(133) - - conditions[2].fieldKey = cFWPM_CONDITION_ICMP_CODE - conditions[2].matchType = cFWP_MATCH_EQUAL - conditions[2].conditionValue._type = cFWP_UINT16 - conditions[2].conditionValue.value = uintptr(0) - - conditions[3].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS - conditions[3].matchType = cFWP_MATCH_EQUAL - conditions[3].conditionValue._type = cFWP_BYTE_ARRAY16_TYPE - conditions[3].conditionValue.value = uintptr(unsafe.Pointer(&linkLocalRouterMulticast)) - - displayData, err := createWtFwpmDisplayData0("Permit NDP type 133", "") - if err != nil { - return wrapErr(err) - } - - defs = append(defs, filterDefinition{ - displayData: displayData, - conditions: conditions, - layer: cFWPM_LAYER_ALE_AUTH_CONNECT_V6, - }) - } - - // - // Router Advertisement Message - // ICMP type 134, code 0. Incoming. - // - { - conditions := make([]wtFwpmFilterCondition0, 4) - - conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL - conditions[0].matchType = cFWP_MATCH_EQUAL - conditions[0].conditionValue._type = cFWP_UINT8 - conditions[0].conditionValue.value = uintptr(cIPPROTO_ICMPV6) - - conditions[1].fieldKey = cFWPM_CONDITION_ICMP_TYPE - conditions[1].matchType = cFWP_MATCH_EQUAL - conditions[1].conditionValue._type = cFWP_UINT16 - conditions[1].conditionValue.value = uintptr(134) - - conditions[2].fieldKey = cFWPM_CONDITION_ICMP_CODE - conditions[2].matchType = cFWP_MATCH_EQUAL - conditions[2].conditionValue._type = cFWP_UINT16 - conditions[2].conditionValue.value = uintptr(0) - - conditions[3].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS - conditions[3].matchType = cFWP_MATCH_EQUAL - conditions[3].conditionValue._type = cFWP_V6_ADDR_MASK - conditions[3].conditionValue.value = uintptr(unsafe.Pointer(&linkLocal)) - - displayData, err := createWtFwpmDisplayData0("Permit NDP type 134", "") - if err != nil { - return wrapErr(err) - } - - defs = append(defs, filterDefinition{ - displayData: displayData, - conditions: conditions, - layer: cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, - }) - } - - // - // Neighbor Solicitation Message - // ICMP type 135, code 0. Bi-directional. - // - { - conditions := make([]wtFwpmFilterCondition0, 3) - - conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL - conditions[0].matchType = cFWP_MATCH_EQUAL - conditions[0].conditionValue._type = cFWP_UINT8 - conditions[0].conditionValue.value = uintptr(cIPPROTO_ICMPV6) - - conditions[1].fieldKey = cFWPM_CONDITION_ICMP_TYPE - conditions[1].matchType = cFWP_MATCH_EQUAL - conditions[1].conditionValue._type = cFWP_UINT16 - conditions[1].conditionValue.value = uintptr(135) - - conditions[2].fieldKey = cFWPM_CONDITION_ICMP_CODE - conditions[2].matchType = cFWP_MATCH_EQUAL - conditions[2].conditionValue._type = cFWP_UINT16 - conditions[2].conditionValue.value = uintptr(0) - - displayData, err := createWtFwpmDisplayData0("Permit NDP type 135", "") - if err != nil { - return wrapErr(err) - } - - defs = append(defs, filterDefinition{ - displayData: displayData, - conditions: conditions, - layer: cFWPM_LAYER_ALE_AUTH_CONNECT_V6, - }) - - defs = append(defs, filterDefinition{ - displayData: displayData, - conditions: conditions, - layer: cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, - }) - } - - // - // Neighbor Advertisement Message - // ICMP type 136, code 0. Bi-directional. - // - { - conditions := make([]wtFwpmFilterCondition0, 3) - - conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL - conditions[0].matchType = cFWP_MATCH_EQUAL - conditions[0].conditionValue._type = cFWP_UINT8 - conditions[0].conditionValue.value = uintptr(cIPPROTO_ICMPV6) - - conditions[1].fieldKey = cFWPM_CONDITION_ICMP_TYPE - conditions[1].matchType = cFWP_MATCH_EQUAL - conditions[1].conditionValue._type = cFWP_UINT16 - conditions[1].conditionValue.value = uintptr(136) - - conditions[2].fieldKey = cFWPM_CONDITION_ICMP_CODE - conditions[2].matchType = cFWP_MATCH_EQUAL - conditions[2].conditionValue._type = cFWP_UINT16 - conditions[2].conditionValue.value = uintptr(0) - - displayData, err := createWtFwpmDisplayData0("Permit NDP type 136", "") - if err != nil { - return wrapErr(err) - } - - defs = append(defs, filterDefinition{ - displayData: displayData, - conditions: conditions, - layer: cFWPM_LAYER_ALE_AUTH_CONNECT_V6, - }) - - defs = append(defs, filterDefinition{ - displayData: displayData, - conditions: conditions, - layer: cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, - }) - } - - // - // Redirect Message - // ICMP type 137, code 0. Incoming. - // - { - conditions := make([]wtFwpmFilterCondition0, 4) - - conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL - conditions[0].matchType = cFWP_MATCH_EQUAL - conditions[0].conditionValue._type = cFWP_UINT8 - conditions[0].conditionValue.value = uintptr(cIPPROTO_ICMPV6) - - conditions[1].fieldKey = cFWPM_CONDITION_ICMP_TYPE - conditions[1].matchType = cFWP_MATCH_EQUAL - conditions[1].conditionValue._type = cFWP_UINT16 - conditions[1].conditionValue.value = uintptr(137) - - conditions[2].fieldKey = cFWPM_CONDITION_ICMP_CODE - conditions[2].matchType = cFWP_MATCH_EQUAL - conditions[2].conditionValue._type = cFWP_UINT16 - conditions[2].conditionValue.value = uintptr(0) - - conditions[3].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS - conditions[3].matchType = cFWP_MATCH_EQUAL - conditions[3].conditionValue._type = cFWP_V6_ADDR_MASK - conditions[3].conditionValue.value = uintptr(unsafe.Pointer(&linkLocal)) - - displayData, err := createWtFwpmDisplayData0("Permit NDP type 137", "") - if err != nil { - return wrapErr(err) - } - - defs = append(defs, filterDefinition{ - displayData: displayData, - conditions: conditions, - layer: cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, - }) - } - - filter := wtFwpmFilter0{ - providerKey: &baseObjects.provider, - subLayerKey: baseObjects.filters, - weight: filterWeight(weight), - action: wtFwpmAction0{ - _type: cFWP_ACTION_PERMIT, - }, - } - - filterID := uint64(0) - - for _, definition := range defs { - filter.displayData = *definition.displayData - filter.layerKey = definition.layer - filter.numFilterConditions = uint32(len(definition.conditions)) - filter.filterCondition = (*wtFwpmFilterCondition0)(unsafe.Pointer(&definition.conditions[0])) - - err := fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - return nil -} - -func permitHyperV(session uintptr, baseObjects *baseObjects, weight uint8) error { - // - // Only applicable on Win8+. - // - { - v, err := version.OsVersion() - if err != nil { - panic(err) - } - - win8plus := v.MajorVersion > 6 || (v.MajorVersion == 6 && v.MinorVersion >= 3) - - if !win8plus { - return nil - } - } - - condition := wtFwpmFilterCondition0{ - fieldKey: cFWPM_CONDITION_L2_FLAGS, - matchType: cFWP_MATCH_EQUAL, - conditionValue: wtFwpConditionValue0{ - _type: cFWP_UINT32, - value: uintptr(cFWP_CONDITION_L2_IS_VM2VM), - }, - } - - filter := wtFwpmFilter0{ - providerKey: &baseObjects.provider, - subLayerKey: baseObjects.filters, - weight: filterWeight(weight), - numFilterConditions: 1, - filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&condition)), - action: wtFwpmAction0{ - _type: cFWP_ACTION_PERMIT, - }, - } - - filterID := uint64(0) - - // - // #1 Outbound. - // - { - displayData, err := createWtFwpmDisplayData0("Permit Hyper-V => Hyper-V outbound", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #2 Inbound. - // - { - displayData, err := createWtFwpmDisplayData0("Permit Hyper-V => Hyper-V inbound", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_INBOUND_MAC_FRAME_NATIVE - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - return nil -} - -// Block all traffic except what is explicitly permitted by other rules. -func blockAll(session uintptr, baseObjects *baseObjects, weight uint8) error { - filter := wtFwpmFilter0{ - providerKey: &baseObjects.provider, - subLayerKey: baseObjects.filters, - weight: filterWeight(weight), - action: wtFwpmAction0{ - _type: cFWP_ACTION_BLOCK, - }, - } - - filterID := uint64(0) - - // - // #1 Block outbound traffic on IPv4. - // - { - displayData, err := createWtFwpmDisplayData0("Block all outbound (IPv4)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #2 Block inbound traffic on IPv4. - // - { - displayData, err := createWtFwpmDisplayData0("Block all inbound (IPv4)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #3 Block outbound traffic on IPv6. - // - { - displayData, err := createWtFwpmDisplayData0("Block all outbound (IPv6)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #4 Block inbound traffic on IPv6. - // - { - displayData, err := createWtFwpmDisplayData0("Block all inbound (IPv6)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - return nil -} - -// Block all DNS traffic except towards specified DNS servers. -func blockDNS(except []net.IP, session uintptr, baseObjects *baseObjects, weightAllow uint8, weightDeny uint8) error { - if weightDeny >= weightAllow { - return errors.New("The allow weight must be greater than the deny weight") - } - - denyConditions := []wtFwpmFilterCondition0{ - { - fieldKey: cFWPM_CONDITION_IP_REMOTE_PORT, - matchType: cFWP_MATCH_EQUAL, - conditionValue: wtFwpConditionValue0{ - _type: cFWP_UINT16, - value: uintptr(53), - }, - }, - { - fieldKey: cFWPM_CONDITION_IP_PROTOCOL, - matchType: cFWP_MATCH_EQUAL, - conditionValue: wtFwpConditionValue0{ - _type: cFWP_UINT8, - value: uintptr(cIPPROTO_UDP), - }, - }, - // Repeat the condition type for logical OR. - { - fieldKey: cFWPM_CONDITION_IP_PROTOCOL, - matchType: cFWP_MATCH_EQUAL, - conditionValue: wtFwpConditionValue0{ - _type: cFWP_UINT8, - value: uintptr(cIPPROTO_TCP), - }, - }, - } - - filter := wtFwpmFilter0{ - providerKey: &baseObjects.provider, - subLayerKey: baseObjects.filters, - weight: filterWeight(weightDeny), - numFilterConditions: uint32(len(denyConditions)), - filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&denyConditions[0])), - action: wtFwpmAction0{ - _type: cFWP_ACTION_BLOCK, - }, - } - - filterID := uint64(0) - - // - // #1 Block IPv4 outbound DNS. - // - { - displayData, err := createWtFwpmDisplayData0("Block DNS outbound (IPv4)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #2 Block IPv4 inbound DNS. - // - { - displayData, err := createWtFwpmDisplayData0("Block DNS inbound (IPv4)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #3 Block IPv6 outbound DNS. - // - { - displayData, err := createWtFwpmDisplayData0("Block DNS outbound (IPv6)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #4 Block IPv6 inbound DNS. - // - { - displayData, err := createWtFwpmDisplayData0("Block DNS inbound (IPv6)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - allowConditionsV4 := make([]wtFwpmFilterCondition0, 0, len(denyConditions)+len(except)) - allowConditionsV4 = append(allowConditionsV4, denyConditions...) - for _, ip := range except { - ip4 := ip.To4() - if ip4 == nil { - continue - } - allowConditionsV4 = append(allowConditionsV4, wtFwpmFilterCondition0{ - fieldKey: cFWPM_CONDITION_IP_REMOTE_ADDRESS, - matchType: cFWP_MATCH_EQUAL, - conditionValue: wtFwpConditionValue0{ - _type: cFWP_UINT32, - value: uintptr(binary.BigEndian.Uint32(ip4)), - }, - }) - } - - storedPointers := make([]*wtFwpByteArray16, 0, len(except)) - allowConditionsV6 := make([]wtFwpmFilterCondition0, 0, len(denyConditions)+len(except)) - allowConditionsV6 = append(allowConditionsV6, denyConditions...) - for _, ip := range except { - if ip.To4() != nil { - continue - } - var address wtFwpByteArray16 - copy(address.byteArray16[:], ip) - allowConditionsV6 = append(allowConditionsV6, wtFwpmFilterCondition0{ - fieldKey: cFWPM_CONDITION_IP_REMOTE_ADDRESS, - matchType: cFWP_MATCH_EQUAL, - conditionValue: wtFwpConditionValue0{ - _type: cFWP_BYTE_ARRAY16_TYPE, - value: uintptr(unsafe.Pointer(&address)), - }, - }) - storedPointers = append(storedPointers, &address) - } - - filter = wtFwpmFilter0{ - providerKey: &baseObjects.provider, - subLayerKey: baseObjects.filters, - weight: filterWeight(weightAllow), - numFilterConditions: uint32(len(allowConditionsV4)), - filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&allowConditionsV4[0])), - action: wtFwpmAction0{ - _type: cFWP_ACTION_PERMIT, - }, - } - - filterID = uint64(0) - - // - // #5 Allow IPv4 outbound DNS. - // - if len(allowConditionsV4) > len(denyConditions) { - displayData, err := createWtFwpmDisplayData0("Allow DNS outbound (IPv4)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #6 Allow IPv4 inbound DNS. - // - if len(allowConditionsV4) > len(denyConditions) { - displayData, err := createWtFwpmDisplayData0("Allow DNS inbound (IPv4)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - filter.filterCondition = (*wtFwpmFilterCondition0)(unsafe.Pointer(&allowConditionsV6[0])) - filter.numFilterConditions = uint32(len(allowConditionsV6)) - - // - // #7 Allow IPv6 outbound DNS. - // - if len(allowConditionsV6) > len(denyConditions) { - displayData, err := createWtFwpmDisplayData0("Allow DNS outbound (IPv6)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - // - // #8 Allow IPv6 inbound DNS. - // - if len(allowConditionsV6) > len(denyConditions) { - displayData, err := createWtFwpmDisplayData0("Allow DNS inbound (IPv6)", "") - if err != nil { - return wrapErr(err) - } - - filter.displayData = *displayData - filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 - - err = fwpmFilterAdd0(session, &filter, 0, &filterID) - if err != nil { - return wrapErr(err) - } - } - - runtime.KeepAlive(storedPointers) - - return nil -} diff --git a/service/firewall/syscall_windows.go b/service/firewall/syscall_windows.go deleted file mode 100644 index 5ec41b0b..00000000 --- a/service/firewall/syscall_windows.go +++ /dev/null @@ -1,42 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package firewall - -// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmengineopen0 -//sys fwpmEngineOpen0(serverName *uint16, authnService wtRpcCAuthN, authIdentity *uintptr, session *wtFwpmSession0, engineHandle unsafe.Pointer) (err error) [failretval!=0] = fwpuclnt.FwpmEngineOpen0 - -// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmengineclose0 -//sys fwpmEngineClose0(engineHandle uintptr) (err error) [failretval!=0] = fwpuclnt.FwpmEngineClose0 - -// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmsublayeradd0 -//sys fwpmSubLayerAdd0(engineHandle uintptr, subLayer *wtFwpmSublayer0, sd uintptr) (err error) [failretval!=0] = fwpuclnt.FwpmSubLayerAdd0 - -// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmgetappidfromfilename0 -//sys fwpmGetAppIdFromFileName0(fileName *uint16, appID unsafe.Pointer) (err error) [failretval!=0] = fwpuclnt.FwpmGetAppIdFromFileName0 - -// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmfreememory0 -//sys fwpmFreeMemory0(p unsafe.Pointer) = fwpuclnt.FwpmFreeMemory0 - -// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmfilteradd0 -//sys fwpmFilterAdd0(engineHandle uintptr, filter *wtFwpmFilter0, sd uintptr, id *uint64) (err error) [failretval!=0] = fwpuclnt.FwpmFilterAdd0 - -// https://docs.microsoft.com/en-us/windows/desktop/api/Fwpmu/nf-fwpmu-fwpmtransactionbegin0 -//sys fwpmTransactionBegin0(engineHandle uintptr, flags uint32) (err error) [failretval!=0] = fwpuclnt.FwpmTransactionBegin0 - -// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmtransactioncommit0 -//sys fwpmTransactionCommit0(engineHandle uintptr) (err error) [failretval!=0] = fwpuclnt.FwpmTransactionCommit0 - -// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmtransactionabort0 -//sys fwpmTransactionAbort0(engineHandle uintptr) (err error) [failretval!=0] = fwpuclnt.FwpmTransactionAbort0 - -// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmprovideradd0 -//sys fwpmProviderAdd0(engineHandle uintptr, provider *wtFwpmProvider0, sd uintptr) (err error) [failretval!=0] = fwpuclnt.FwpmProviderAdd0 - -// https://docs.microsoft.com/sv-se/windows/desktop/api/aclapi/nf-aclapi-getsecurityinfo -//sys getSecurityInfo(handle windows.Handle, objectType wtObjectType, si uint32, sidOwner *windows.SID, sidGroup *windows.SID, dacl *uintptr, sacl *uintptr, securityDescriptor *uintptr) (err error) [failretval!=0] = advapi32.GetSecurityInfo - -// https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-getsecuritydescriptorlength -//sys getSecurityDescriptorLength(securityDescriptor uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength diff --git a/service/firewall/types_windows.go b/service/firewall/types_windows.go deleted file mode 100644 index e06f7d2b..00000000 --- a/service/firewall/types_windows.go +++ /dev/null @@ -1,417 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package firewall - -import "golang.org/x/sys/windows" - -const ( - anysizeArray = 1 // ANYSIZE_ARRAY defined in winnt.h - - wtFwpBitmapArray64_Size = 8 - - wtFwpByteArray16_Size = 16 - - wtFwpByteArray6_Size = 6 - - wtFwpmAction0_Size = 20 - wtFwpmAction0_filterType_Offset = 4 - - wtFwpV4AddrAndMask_Size = 8 - wtFwpV4AddrAndMask_mask_Offset = 4 - - wtFwpV6AddrAndMask_Size = 17 - wtFwpV6AddrAndMask_prefixLength_Offset = 16 -) - -type wtFwpActionFlag uint32 - -const ( - cFWP_ACTION_FLAG_TERMINATING wtFwpActionFlag = 0x00001000 - cFWP_ACTION_FLAG_NON_TERMINATING wtFwpActionFlag = 0x00002000 - cFWP_ACTION_FLAG_CALLOUT wtFwpActionFlag = 0x00004000 -) - -// FWP_ACTION_TYPE defined in fwptypes.h -type wtFwpActionType uint32 - -const ( - cFWP_ACTION_BLOCK wtFwpActionType = wtFwpActionType(0x00000001 | cFWP_ACTION_FLAG_TERMINATING) - cFWP_ACTION_PERMIT wtFwpActionType = wtFwpActionType(0x00000002 | cFWP_ACTION_FLAG_TERMINATING) - cFWP_ACTION_CALLOUT_TERMINATING wtFwpActionType = wtFwpActionType(0x00000003 | cFWP_ACTION_FLAG_CALLOUT | cFWP_ACTION_FLAG_TERMINATING) - cFWP_ACTION_CALLOUT_INSPECTION wtFwpActionType = wtFwpActionType(0x00000004 | cFWP_ACTION_FLAG_CALLOUT | cFWP_ACTION_FLAG_NON_TERMINATING) - cFWP_ACTION_CALLOUT_UNKNOWN wtFwpActionType = wtFwpActionType(0x00000005 | cFWP_ACTION_FLAG_CALLOUT) - cFWP_ACTION_CONTINUE wtFwpActionType = wtFwpActionType(0x00000006 | cFWP_ACTION_FLAG_NON_TERMINATING) - cFWP_ACTION_NONE wtFwpActionType = 0x00000007 - cFWP_ACTION_NONE_NO_MATCH wtFwpActionType = 0x00000008 - cFWP_ACTION_BITMAP_INDEX_SET wtFwpActionType = 0x00000009 -) - -// FWP_BYTE_BLOB defined in fwptypes.h -// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_byte_blob_) -type wtFwpByteBlob struct { - size uint32 - data *uint8 -} - -// FWP_MATCH_TYPE defined in fwptypes.h -// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ne-fwptypes-fwp_match_type_) -type wtFwpMatchType uint32 - -const ( - cFWP_MATCH_EQUAL wtFwpMatchType = 0 - cFWP_MATCH_GREATER wtFwpMatchType = cFWP_MATCH_EQUAL + 1 - cFWP_MATCH_LESS wtFwpMatchType = cFWP_MATCH_GREATER + 1 - cFWP_MATCH_GREATER_OR_EQUAL wtFwpMatchType = cFWP_MATCH_LESS + 1 - cFWP_MATCH_LESS_OR_EQUAL wtFwpMatchType = cFWP_MATCH_GREATER_OR_EQUAL + 1 - cFWP_MATCH_RANGE wtFwpMatchType = cFWP_MATCH_LESS_OR_EQUAL + 1 - cFWP_MATCH_FLAGS_ALL_SET wtFwpMatchType = cFWP_MATCH_RANGE + 1 - cFWP_MATCH_FLAGS_ANY_SET wtFwpMatchType = cFWP_MATCH_FLAGS_ALL_SET + 1 - cFWP_MATCH_FLAGS_NONE_SET wtFwpMatchType = cFWP_MATCH_FLAGS_ANY_SET + 1 - cFWP_MATCH_EQUAL_CASE_INSENSITIVE wtFwpMatchType = cFWP_MATCH_FLAGS_NONE_SET + 1 - cFWP_MATCH_NOT_EQUAL wtFwpMatchType = cFWP_MATCH_EQUAL_CASE_INSENSITIVE + 1 - cFWP_MATCH_PREFIX wtFwpMatchType = cFWP_MATCH_NOT_EQUAL + 1 - cFWP_MATCH_NOT_PREFIX wtFwpMatchType = cFWP_MATCH_PREFIX + 1 - cFWP_MATCH_TYPE_MAX wtFwpMatchType = cFWP_MATCH_NOT_PREFIX + 1 -) - -// FWPM_ACTION0 defined in fwpmtypes.h -// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_action0_) -type wtFwpmAction0 struct { - _type wtFwpActionType - filterType windows.GUID // Windows type: GUID -} - -// Defined in fwpmu.h. 4cd62a49-59c3-4969-b7f3-bda5d32890a4 -var cFWPM_CONDITION_IP_LOCAL_INTERFACE = windows.GUID{ - Data1: 0x4cd62a49, - Data2: 0x59c3, - Data3: 0x4969, - Data4: [8]byte{0xb7, 0xf3, 0xbd, 0xa5, 0xd3, 0x28, 0x90, 0xa4}, -} - -// Defined in fwpmu.h. b235ae9a-1d64-49b8-a44c-5ff3d9095045 -var cFWPM_CONDITION_IP_REMOTE_ADDRESS = windows.GUID{ - Data1: 0xb235ae9a, - Data2: 0x1d64, - Data3: 0x49b8, - Data4: [8]byte{0xa4, 0x4c, 0x5f, 0xf3, 0xd9, 0x09, 0x50, 0x45}, -} - -// Defined in fwpmu.h. daf8cd14-e09e-4c93-a5ae-c5c13b73ffca -var cFWPM_CONDITION_INTERFACE_TYPE = windows.GUID{ - Data1: 0xdaf8cd14, - Data2: 0xe09e, - Data3: 0x4c93, - Data4: [8]byte{0xa5, 0xae, 0xc5, 0xc1, 0x3b, 0x73, 0xff, 0xca}, -} - -// Defined in fwpmu.h. 3971ef2b-623e-4f9a-8cb1-6e79b806b9a7 -var cFWPM_CONDITION_IP_PROTOCOL = windows.GUID{ - Data1: 0x3971ef2b, - Data2: 0x623e, - Data3: 0x4f9a, - Data4: [8]byte{0x8c, 0xb1, 0x6e, 0x79, 0xb8, 0x06, 0xb9, 0xa7}, -} - -// Defined in fwpmu.h. 0c1ba1af-5765-453f-af22-a8f791ac775b -var cFWPM_CONDITION_IP_LOCAL_PORT = windows.GUID{ - Data1: 0x0c1ba1af, - Data2: 0x5765, - Data3: 0x453f, - Data4: [8]byte{0xaf, 0x22, 0xa8, 0xf7, 0x91, 0xac, 0x77, 0x5b}, -} - -// Defined in fwpmu.h. c35a604d-d22b-4e1a-91b4-68f674ee674b -var cFWPM_CONDITION_IP_REMOTE_PORT = windows.GUID{ - Data1: 0xc35a604d, - Data2: 0xd22b, - Data3: 0x4e1a, - Data4: [8]byte{0x91, 0xb4, 0x68, 0xf6, 0x74, 0xee, 0x67, 0x4b}, -} - -// Defined in fwpmu.h. d78e1e87-8644-4ea5-9437-d809ecefc971 -var cFWPM_CONDITION_ALE_APP_ID = windows.GUID{ - Data1: 0xd78e1e87, - Data2: 0x8644, - Data3: 0x4ea5, - Data4: [8]byte{0x94, 0x37, 0xd8, 0x09, 0xec, 0xef, 0xc9, 0x71}, -} - -// af043a0a-b34d-4f86-979c-c90371af6e66 -var cFWPM_CONDITION_ALE_USER_ID = windows.GUID{ - Data1: 0xaf043a0a, - Data2: 0xb34d, - Data3: 0x4f86, - Data4: [8]byte{0x97, 0x9c, 0xc9, 0x03, 0x71, 0xaf, 0x6e, 0x66}, -} - -// d9ee00de-c1ef-4617-bfe3-ffd8f5a08957 -var cFWPM_CONDITION_IP_LOCAL_ADDRESS = windows.GUID{ - Data1: 0xd9ee00de, - Data2: 0xc1ef, - Data3: 0x4617, - Data4: [8]byte{0xbf, 0xe3, 0xff, 0xd8, 0xf5, 0xa0, 0x89, 0x57}, -} - -var cFWPM_CONDITION_ICMP_TYPE = cFWPM_CONDITION_IP_LOCAL_PORT -var cFWPM_CONDITION_ICMP_CODE = cFWPM_CONDITION_IP_REMOTE_PORT - -// 7bc43cbf-37ba-45f1-b74a-82ff518eeb10 -var cFWPM_CONDITION_L2_FLAGS = windows.GUID{ - Data1: 0x7bc43cbf, - Data2: 0x37ba, - Data3: 0x45f1, - Data4: [8]byte{0xb7, 0x4a, 0x82, 0xff, 0x51, 0x8e, 0xeb, 0x10}, -} - -type wtFwpmL2Flags uint32 - -const cFWP_CONDITION_L2_IS_VM2VM wtFwpmL2Flags = 0x00000010 - -// Defined in fwpmtypes.h -type wtFwpmFilterFlags uint32 - -const ( - cFWPM_FILTER_FLAG_NONE wtFwpmFilterFlags = 0x00000000 - cFWPM_FILTER_FLAG_PERSISTENT wtFwpmFilterFlags = 0x00000001 - cFWPM_FILTER_FLAG_BOOTTIME wtFwpmFilterFlags = 0x00000002 - cFWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT wtFwpmFilterFlags = 0x00000004 - cFWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT wtFwpmFilterFlags = 0x00000008 - cFWPM_FILTER_FLAG_PERMIT_IF_CALLOUT_UNREGISTERED wtFwpmFilterFlags = 0x00000010 - cFWPM_FILTER_FLAG_DISABLED wtFwpmFilterFlags = 0x00000020 - cFWPM_FILTER_FLAG_INDEXED wtFwpmFilterFlags = 0x00000040 - cFWPM_FILTER_FLAG_HAS_SECURITY_REALM_PROVIDER_CONTEXT wtFwpmFilterFlags = 0x00000080 - cFWPM_FILTER_FLAG_SYSTEMOS_ONLY wtFwpmFilterFlags = 0x00000100 - cFWPM_FILTER_FLAG_GAMEOS_ONLY wtFwpmFilterFlags = 0x00000200 - cFWPM_FILTER_FLAG_SILENT_MODE wtFwpmFilterFlags = 0x00000400 - cFWPM_FILTER_FLAG_IPSEC_NO_ACQUIRE_INITIATE wtFwpmFilterFlags = 0x00000800 -) - -// FWPM_LAYER_ALE_AUTH_CONNECT_V4 (c38d57d1-05a7-4c33-904f-7fbceee60e82) defined in fwpmu.h -var cFWPM_LAYER_ALE_AUTH_CONNECT_V4 = windows.GUID{ - Data1: 0xc38d57d1, - Data2: 0x05a7, - Data3: 0x4c33, - Data4: [8]byte{0x90, 0x4f, 0x7f, 0xbc, 0xee, 0xe6, 0x0e, 0x82}, -} - -// e1cd9fe7-f4b5-4273-96c0-592e487b8650 -var cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 = windows.GUID{ - Data1: 0xe1cd9fe7, - Data2: 0xf4b5, - Data3: 0x4273, - Data4: [8]byte{0x96, 0xc0, 0x59, 0x2e, 0x48, 0x7b, 0x86, 0x50}, -} - -// FWPM_LAYER_ALE_AUTH_CONNECT_V6 (4a72393b-319f-44bc-84c3-ba54dcb3b6b4) defined in fwpmu.h -var cFWPM_LAYER_ALE_AUTH_CONNECT_V6 = windows.GUID{ - Data1: 0x4a72393b, - Data2: 0x319f, - Data3: 0x44bc, - Data4: [8]byte{0x84, 0xc3, 0xba, 0x54, 0xdc, 0xb3, 0xb6, 0xb4}, -} - -// a3b42c97-9f04-4672-b87e-cee9c483257f -var cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 = windows.GUID{ - Data1: 0xa3b42c97, - Data2: 0x9f04, - Data3: 0x4672, - Data4: [8]byte{0xb8, 0x7e, 0xce, 0xe9, 0xc4, 0x83, 0x25, 0x7f}, -} - -// 94c44912-9d6f-4ebf-b995-05ab8a088d1b -var cFWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE = windows.GUID{ - Data1: 0x94c44912, - Data2: 0x9d6f, - Data3: 0x4ebf, - Data4: [8]byte{0xb9, 0x95, 0x05, 0xab, 0x8a, 0x08, 0x8d, 0x1b}, -} - -// d4220bd3-62ce-4f08-ae88-b56e8526df50 -var cFWPM_LAYER_INBOUND_MAC_FRAME_NATIVE = windows.GUID{ - Data1: 0xd4220bd3, - Data2: 0x62ce, - Data3: 0x4f08, - Data4: [8]byte{0xae, 0x88, 0xb5, 0x6e, 0x85, 0x26, 0xdf, 0x50}, -} - -// FWP_BITMAP_ARRAY64 defined in fwtypes.h -type wtFwpBitmapArray64 struct { - bitmapArray64 [8]uint8 // Windows type: [8]UINT8 -} - -// FWP_BYTE_ARRAY6 defined in fwtypes.h -// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_byte_array6_) -type wtFwpByteArray6 struct { - byteArray6 [6]uint8 // Windows type: [6]UINT8 -} - -// FWP_BYTE_ARRAY16 defined in fwptypes.h -// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_byte_array16_) -type wtFwpByteArray16 struct { - byteArray16 [16]uint8 // Windows type [16]UINT8 -} - -// FWP_CONDITION_VALUE0 defined in fwptypes.h -// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_condition_value0_). -type wtFwpConditionValue0 wtFwpValue0 - -// FWP_DATA_TYPE defined in fwptypes.h -// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ne-fwptypes-fwp_data_type_) -type wtFwpDataType uint - -const ( - cFWP_EMPTY wtFwpDataType = 0 - cFWP_UINT8 wtFwpDataType = cFWP_EMPTY + 1 - cFWP_UINT16 wtFwpDataType = cFWP_UINT8 + 1 - cFWP_UINT32 wtFwpDataType = cFWP_UINT16 + 1 - cFWP_UINT64 wtFwpDataType = cFWP_UINT32 + 1 - cFWP_INT8 wtFwpDataType = cFWP_UINT64 + 1 - cFWP_INT16 wtFwpDataType = cFWP_INT8 + 1 - cFWP_INT32 wtFwpDataType = cFWP_INT16 + 1 - cFWP_INT64 wtFwpDataType = cFWP_INT32 + 1 - cFWP_FLOAT wtFwpDataType = cFWP_INT64 + 1 - cFWP_DOUBLE wtFwpDataType = cFWP_FLOAT + 1 - cFWP_BYTE_ARRAY16_TYPE wtFwpDataType = cFWP_DOUBLE + 1 - cFWP_BYTE_BLOB_TYPE wtFwpDataType = cFWP_BYTE_ARRAY16_TYPE + 1 - cFWP_SID wtFwpDataType = cFWP_BYTE_BLOB_TYPE + 1 - cFWP_SECURITY_DESCRIPTOR_TYPE wtFwpDataType = cFWP_SID + 1 - cFWP_TOKEN_INFORMATION_TYPE wtFwpDataType = cFWP_SECURITY_DESCRIPTOR_TYPE + 1 - cFWP_TOKEN_ACCESS_INFORMATION_TYPE wtFwpDataType = cFWP_TOKEN_INFORMATION_TYPE + 1 - cFWP_UNICODE_STRING_TYPE wtFwpDataType = cFWP_TOKEN_ACCESS_INFORMATION_TYPE + 1 - cFWP_BYTE_ARRAY6_TYPE wtFwpDataType = cFWP_UNICODE_STRING_TYPE + 1 - cFWP_BITMAP_INDEX_TYPE wtFwpDataType = cFWP_BYTE_ARRAY6_TYPE + 1 - cFWP_BITMAP_ARRAY64_TYPE wtFwpDataType = cFWP_BITMAP_INDEX_TYPE + 1 - cFWP_SINGLE_DATA_TYPE_MAX wtFwpDataType = 0xff - cFWP_V4_ADDR_MASK wtFwpDataType = cFWP_SINGLE_DATA_TYPE_MAX + 1 - cFWP_V6_ADDR_MASK wtFwpDataType = cFWP_V4_ADDR_MASK + 1 - cFWP_RANGE_TYPE wtFwpDataType = cFWP_V6_ADDR_MASK + 1 - cFWP_DATA_TYPE_MAX wtFwpDataType = cFWP_RANGE_TYPE + 1 -) - -// FWP_V4_ADDR_AND_MASK defined in fwptypes.h -// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_v4_addr_and_mask_). -type wtFwpV4AddrAndMask struct { - addr uint32 - mask uint32 -} - -// FWP_V6_ADDR_AND_MASK defined in fwptypes.h -// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_v6_addr_and_mask_). -type wtFwpV6AddrAndMask struct { - addr [16]uint8 - prefixLength uint8 -} - -// FWP_VALUE0 defined in fwptypes.h -// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_value0_) -type wtFwpValue0 struct { - _type wtFwpDataType - value uintptr -} - -// FWPM_DISPLAY_DATA0 defined in fwptypes.h -// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwpm_display_data0_). -type wtFwpmDisplayData0 struct { - name *uint16 // Windows type: *wchar_t - description *uint16 // Windows type: *wchar_t -} - -// FWPM_FILTER_CONDITION0 defined in fwpmtypes.h -// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_filter_condition0_). -type wtFwpmFilterCondition0 struct { - fieldKey windows.GUID // Windows type: GUID - matchType wtFwpMatchType - conditionValue wtFwpConditionValue0 -} - -// FWPM_PROVIDER0 defined in fwpmtypes.h -// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_provider0_) -type wtFwpProvider0 struct { - providerKey windows.GUID // Windows type: GUID - displayData wtFwpmDisplayData0 - flags uint32 - providerData wtFwpByteBlob - serviceName *uint16 // Windows type: *wchar_t -} - -type wtFwpmSessionFlagsValue uint32 - -const ( - cFWPM_SESSION_FLAG_DYNAMIC wtFwpmSessionFlagsValue = 0x00000001 // FWPM_SESSION_FLAG_DYNAMIC defined in fwpmtypes.h -) - -// FWPM_SESSION0 defined in fwpmtypes.h -// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_session0_). -type wtFwpmSession0 struct { - sessionKey windows.GUID // Windows type: GUID - displayData wtFwpmDisplayData0 - flags wtFwpmSessionFlagsValue // Windows type UINT32 - txnWaitTimeoutInMSec uint32 - processId uint32 // Windows type: DWORD - sid *windows.SID - username *uint16 // Windows type: *wchar_t - kernelMode uint8 // Windows type: BOOL -} - -type wtFwpmSublayerFlags uint32 - -const ( - cFWPM_SUBLAYER_FLAG_PERSISTENT wtFwpmSublayerFlags = 0x00000001 // FWPM_SUBLAYER_FLAG_PERSISTENT defined in fwpmtypes.h -) - -// FWPM_SUBLAYER0 defined in fwpmtypes.h -// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_sublayer0_) -type wtFwpmSublayer0 struct { - subLayerKey windows.GUID // Windows type: GUID - displayData wtFwpmDisplayData0 - flags wtFwpmSublayerFlags - providerKey *windows.GUID // Windows type: *GUID - providerData wtFwpByteBlob - weight uint16 -} - -// Defined in rpcdce.h -type wtRpcCAuthN uint32 - -const ( - cRPC_C_AUTHN_NONE wtRpcCAuthN = 0 - cRPC_C_AUTHN_WINNT wtRpcCAuthN = 10 - cRPC_C_AUTHN_DEFAULT wtRpcCAuthN = 0xFFFFFFFF -) - -// FWPM_PROVIDER0 defined in fwpmtypes.h -// (https://docs.microsoft.com/sv-se/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_provider0_). -type wtFwpmProvider0 struct { - providerKey windows.GUID - displayData wtFwpmDisplayData0 - flags uint32 - providerData wtFwpByteBlob - serviceName *uint16 -} - -type wtObjectType uint32 - -const ( - cSE_KERNEL_OBJECT wtObjectType = 6 - - cDACL_SECURITY_INFORMATION = 4 -) - -type wtIfType uint32 - -const ( - cIF_TYPE_SOFTWARE_LOOPBACK wtIfType = 24 -) - -type wtIPProto uint32 - -const ( - cIPPROTO_ICMP wtIPProto = 1 - cIPPROTO_ICMPV6 wtIPProto = 58 - cIPPROTO_TCP wtIPProto = 6 - cIPPROTO_UDP wtIPProto = 17 -) diff --git a/service/firewall/types_windows_386.go b/service/firewall/types_windows_386.go deleted file mode 100644 index 00d7ba8f..00000000 --- a/service/firewall/types_windows_386.go +++ /dev/null @@ -1,88 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package firewall - -import "golang.org/x/sys/windows" - -const ( - wtFwpByteBlob_Size = 8 - wtFwpByteBlob_data_Offset = 4 - - wtFwpConditionValue0_Size = 8 - wtFwpConditionValue0_uint8_Offset = 4 - - wtFwpmDisplayData0_Size = 8 - wtFwpmDisplayData0_description_Offset = 4 - - wtFwpmFilter0_Size = 152 - wtFwpmFilter0_displayData_Offset = 16 - wtFwpmFilter0_flags_Offset = 24 - wtFwpmFilter0_providerKey_Offset = 28 - wtFwpmFilter0_providerData_Offset = 32 - wtFwpmFilter0_layerKey_Offset = 40 - wtFwpmFilter0_subLayerKey_Offset = 56 - wtFwpmFilter0_weight_Offset = 72 - wtFwpmFilter0_numFilterConditions_Offset = 80 - wtFwpmFilter0_filterCondition_Offset = 84 - wtFwpmFilter0_action_Offset = 88 - wtFwpmFilter0_providerContextKey_Offset = 112 - wtFwpmFilter0_reserved_Offset = 128 - wtFwpmFilter0_filterID_Offset = 136 - wtFwpmFilter0_effectiveWeight_Offset = 144 - - wtFwpmFilterCondition0_Size = 28 - wtFwpmFilterCondition0_matchType_Offset = 16 - wtFwpmFilterCondition0_conditionValue_Offset = 20 - - wtFwpmSession0_Size = 48 - wtFwpmSession0_displayData_Offset = 16 - wtFwpmSession0_flags_Offset = 24 - wtFwpmSession0_txnWaitTimeoutInMSec_Offset = 28 - wtFwpmSession0_processId_Offset = 32 - wtFwpmSession0_sid_Offset = 36 - wtFwpmSession0_username_Offset = 40 - wtFwpmSession0_kernelMode_Offset = 44 - - wtFwpmSublayer0_Size = 44 - wtFwpmSublayer0_displayData_Offset = 16 - wtFwpmSublayer0_flags_Offset = 24 - wtFwpmSublayer0_providerKey_Offset = 28 - wtFwpmSublayer0_providerData_Offset = 32 - wtFwpmSublayer0_weight_Offset = 40 - - wtFwpProvider0_Size = 40 - wtFwpProvider0_displayData_Offset = 16 - wtFwpProvider0_flags_Offset = 24 - wtFwpProvider0_providerData_Offset = 28 - wtFwpProvider0_serviceName_Offset = 36 - - wtFwpTokenInformation_Size = 16 - - wtFwpValue0_Size = 8 - wtFwpValue0_value_Offset = 4 -) - -// FWPM_FILTER0 defined in fwpmtypes.h -// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_filter0_). -type wtFwpmFilter0 struct { - filterKey windows.GUID // Windows type: GUID - displayData wtFwpmDisplayData0 - flags wtFwpmFilterFlags - providerKey *windows.GUID // Windows type: *GUID - providerData wtFwpByteBlob - layerKey windows.GUID // Windows type: GUID - subLayerKey windows.GUID // Windows type: GUID - weight wtFwpValue0 - numFilterConditions uint32 - filterCondition *wtFwpmFilterCondition0 - action wtFwpmAction0 - offset1 [4]byte // Layout correction field - providerContextKey windows.GUID // Windows type: GUID - reserved *windows.GUID // Windows type: *GUID - offset2 [4]byte // Layout correction field - filterID uint64 - effectiveWeight wtFwpValue0 -} diff --git a/service/firewall/types_windows_amd64.go b/service/firewall/types_windows_amd64.go deleted file mode 100644 index f0aa557c..00000000 --- a/service/firewall/types_windows_amd64.go +++ /dev/null @@ -1,85 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package firewall - -import "golang.org/x/sys/windows" - -const ( - wtFwpByteBlob_Size = 16 - wtFwpByteBlob_data_Offset = 8 - - wtFwpConditionValue0_Size = 16 - wtFwpConditionValue0_uint8_Offset = 8 - - wtFwpmDisplayData0_Size = 16 - wtFwpmDisplayData0_description_Offset = 8 - - wtFwpmFilter0_Size = 200 - wtFwpmFilter0_displayData_Offset = 16 - wtFwpmFilter0_flags_Offset = 32 - wtFwpmFilter0_providerKey_Offset = 40 - wtFwpmFilter0_providerData_Offset = 48 - wtFwpmFilter0_layerKey_Offset = 64 - wtFwpmFilter0_subLayerKey_Offset = 80 - wtFwpmFilter0_weight_Offset = 96 - wtFwpmFilter0_numFilterConditions_Offset = 112 - wtFwpmFilter0_filterCondition_Offset = 120 - wtFwpmFilter0_action_Offset = 128 - wtFwpmFilter0_providerContextKey_Offset = 152 - wtFwpmFilter0_reserved_Offset = 168 - wtFwpmFilter0_filterID_Offset = 176 - wtFwpmFilter0_effectiveWeight_Offset = 184 - - wtFwpmFilterCondition0_Size = 40 - wtFwpmFilterCondition0_matchType_Offset = 16 - wtFwpmFilterCondition0_conditionValue_Offset = 24 - - wtFwpmSession0_Size = 72 - wtFwpmSession0_displayData_Offset = 16 - wtFwpmSession0_flags_Offset = 32 - wtFwpmSession0_txnWaitTimeoutInMSec_Offset = 36 - wtFwpmSession0_processId_Offset = 40 - wtFwpmSession0_sid_Offset = 48 - wtFwpmSession0_username_Offset = 56 - wtFwpmSession0_kernelMode_Offset = 64 - - wtFwpmSublayer0_Size = 72 - wtFwpmSublayer0_displayData_Offset = 16 - wtFwpmSublayer0_flags_Offset = 32 - wtFwpmSublayer0_providerKey_Offset = 40 - wtFwpmSublayer0_providerData_Offset = 48 - wtFwpmSublayer0_weight_Offset = 64 - - wtFwpProvider0_Size = 64 - wtFwpProvider0_displayData_Offset = 16 - wtFwpProvider0_flags_Offset = 32 - wtFwpProvider0_providerData_Offset = 40 - wtFwpProvider0_serviceName_Offset = 56 - - wtFwpValue0_Size = 16 - wtFwpValue0_value_Offset = 8 -) - -// FWPM_FILTER0 defined in fwpmtypes.h -// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_filter0_). -type wtFwpmFilter0 struct { - filterKey windows.GUID // Windows type: GUID - displayData wtFwpmDisplayData0 - flags wtFwpmFilterFlags // Windows type: UINT32 - providerKey *windows.GUID // Windows type: *GUID - providerData wtFwpByteBlob - layerKey windows.GUID // Windows type: GUID - subLayerKey windows.GUID // Windows type: GUID - weight wtFwpValue0 - numFilterConditions uint32 - filterCondition *wtFwpmFilterCondition0 - action wtFwpmAction0 - offset1 [4]byte // Layout correction field - providerContextKey windows.GUID // Windows type: GUID - reserved *windows.GUID // Windows type: *GUID - filterID uint64 - effectiveWeight wtFwpValue0 -} diff --git a/service/firewall/types_windows_test.go b/service/firewall/types_windows_test.go deleted file mode 100644 index 97cb032c..00000000 --- a/service/firewall/types_windows_test.go +++ /dev/null @@ -1,538 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package firewall - -import ( - "testing" - "unsafe" -) - -func TestWtFwpByteBlobSize(t *testing.T) { - - const actualWtFwpByteBlobSize = unsafe.Sizeof(wtFwpByteBlob{}) - - if actualWtFwpByteBlobSize != wtFwpByteBlob_Size { - t.Errorf("Size of FwpByteBlob is %d, although %d is expected.", actualWtFwpByteBlobSize, - wtFwpByteBlob_Size) - } -} - -func TestWtFwpByteBlobOffsets(t *testing.T) { - - s := wtFwpByteBlob{} - sp := uintptr(unsafe.Pointer(&s)) - - offset := uintptr(unsafe.Pointer(&s.data)) - sp - - if offset != wtFwpByteBlob_data_Offset { - t.Errorf("FwpByteBlob.data offset is %d although %d is expected", offset, wtFwpByteBlob_data_Offset) - return - } -} - -func TestWtFwpmAction0Size(t *testing.T) { - - const actualWtFwpmAction0Size = unsafe.Sizeof(wtFwpmAction0{}) - - if actualWtFwpmAction0Size != wtFwpmAction0_Size { - t.Errorf("Size of wtFwpmAction0 is %d, although %d is expected.", actualWtFwpmAction0Size, - wtFwpmAction0_Size) - } -} - -func TestWtFwpmAction0Offsets(t *testing.T) { - - s := wtFwpmAction0{} - sp := uintptr(unsafe.Pointer(&s)) - - offset := uintptr(unsafe.Pointer(&s.filterType)) - sp - - if offset != wtFwpmAction0_filterType_Offset { - t.Errorf("wtFwpmAction0.filterType offset is %d although %d is expected", offset, - wtFwpmAction0_filterType_Offset) - return - } -} - -func TestWtFwpBitmapArray64Size(t *testing.T) { - - const actualWtFwpBitmapArray64Size = unsafe.Sizeof(wtFwpBitmapArray64{}) - - if actualWtFwpBitmapArray64Size != wtFwpBitmapArray64_Size { - t.Errorf("Size of wtFwpBitmapArray64 is %d, although %d is expected.", actualWtFwpBitmapArray64Size, - wtFwpBitmapArray64_Size) - } -} - -func TestWtFwpByteArray6Size(t *testing.T) { - - const actualWtFwpByteArray6Size = unsafe.Sizeof(wtFwpByteArray6{}) - - if actualWtFwpByteArray6Size != wtFwpByteArray6_Size { - t.Errorf("Size of wtFwpByteArray6 is %d, although %d is expected.", actualWtFwpByteArray6Size, - wtFwpByteArray6_Size) - } -} - -func TestWtFwpByteArray16Size(t *testing.T) { - - const actualWtFwpByteArray16Size = unsafe.Sizeof(wtFwpByteArray16{}) - - if actualWtFwpByteArray16Size != wtFwpByteArray16_Size { - t.Errorf("Size of wtFwpByteArray16 is %d, although %d is expected.", actualWtFwpByteArray16Size, - wtFwpByteArray16_Size) - } -} - -func TestWtFwpConditionValue0Size(t *testing.T) { - - const actualWtFwpConditionValue0Size = unsafe.Sizeof(wtFwpConditionValue0{}) - - if actualWtFwpConditionValue0Size != wtFwpConditionValue0_Size { - t.Errorf("Size of wtFwpConditionValue0 is %d, although %d is expected.", actualWtFwpConditionValue0Size, - wtFwpConditionValue0_Size) - } -} - -func TestWtFwpConditionValue0Offsets(t *testing.T) { - - s := wtFwpConditionValue0{} - sp := uintptr(unsafe.Pointer(&s)) - - offset := uintptr(unsafe.Pointer(&s.value)) - sp - - if offset != wtFwpConditionValue0_uint8_Offset { - t.Errorf("wtFwpConditionValue0.value offset is %d although %d is expected", offset, wtFwpConditionValue0_uint8_Offset) - return - } -} - -func TestWtFwpV4AddrAndMaskSize(t *testing.T) { - - const actualWtFwpV4AddrAndMaskSize = unsafe.Sizeof(wtFwpV4AddrAndMask{}) - - if actualWtFwpV4AddrAndMaskSize != wtFwpV4AddrAndMask_Size { - t.Errorf("Size of wtFwpV4AddrAndMask is %d, although %d is expected.", actualWtFwpV4AddrAndMaskSize, - wtFwpV4AddrAndMask_Size) - } -} - -func TestWtFwpV4AddrAndMaskOffsets(t *testing.T) { - - s := wtFwpV4AddrAndMask{} - sp := uintptr(unsafe.Pointer(&s)) - - offset := uintptr(unsafe.Pointer(&s.mask)) - sp - - if offset != wtFwpV4AddrAndMask_mask_Offset { - t.Errorf("wtFwpV4AddrAndMask.mask offset is %d although %d is expected", offset, - wtFwpV4AddrAndMask_mask_Offset) - return - } -} - -func TestWtFwpV6AddrAndMaskSize(t *testing.T) { - - const actualWtFwpV6AddrAndMaskSize = unsafe.Sizeof(wtFwpV6AddrAndMask{}) - - if actualWtFwpV6AddrAndMaskSize != wtFwpV6AddrAndMask_Size { - t.Errorf("Size of wtFwpV6AddrAndMask is %d, although %d is expected.", actualWtFwpV6AddrAndMaskSize, - wtFwpV6AddrAndMask_Size) - } -} - -func TestWtFwpV6AddrAndMaskOffsets(t *testing.T) { - - s := wtFwpV6AddrAndMask{} - sp := uintptr(unsafe.Pointer(&s)) - - offset := uintptr(unsafe.Pointer(&s.prefixLength)) - sp - - if offset != wtFwpV6AddrAndMask_prefixLength_Offset { - t.Errorf("wtFwpV6AddrAndMask.prefixLength offset is %d although %d is expected", offset, - wtFwpV6AddrAndMask_prefixLength_Offset) - return - } -} - -func TestWtFwpValue0Size(t *testing.T) { - - const actualWtFwpValue0Size = unsafe.Sizeof(wtFwpValue0{}) - - if actualWtFwpValue0Size != wtFwpValue0_Size { - t.Errorf("Size of wtFwpValue0 is %d, although %d is expected.", actualWtFwpValue0Size, wtFwpValue0_Size) - } -} - -func TestWtFwpValue0Offsets(t *testing.T) { - - s := wtFwpValue0{} - sp := uintptr(unsafe.Pointer(&s)) - - offset := uintptr(unsafe.Pointer(&s.value)) - sp - - if offset != wtFwpValue0_value_Offset { - t.Errorf("wtFwpValue0.value offset is %d although %d is expected", offset, wtFwpValue0_value_Offset) - return - } -} - -func TestWtFwpmDisplayData0Size(t *testing.T) { - - const actualWtFwpmDisplayData0Size = unsafe.Sizeof(wtFwpmDisplayData0{}) - - if actualWtFwpmDisplayData0Size != wtFwpmDisplayData0_Size { - t.Errorf("Size of wtFwpmDisplayData0 is %d, although %d is expected.", actualWtFwpmDisplayData0Size, - wtFwpmDisplayData0_Size) - } -} - -func TestWtFwpmDisplayData0Offsets(t *testing.T) { - - s := wtFwpmDisplayData0{} - sp := uintptr(unsafe.Pointer(&s)) - - offset := uintptr(unsafe.Pointer(&s.description)) - sp - - if offset != wtFwpmDisplayData0_description_Offset { - t.Errorf("wtFwpmDisplayData0.description offset is %d although %d is expected", offset, - wtFwpmDisplayData0_description_Offset) - return - } -} - -func TestWtFwpmFilterCondition0Size(t *testing.T) { - - const actualWtFwpmFilterCondition0Size = unsafe.Sizeof(wtFwpmFilterCondition0{}) - - if actualWtFwpmFilterCondition0Size != wtFwpmFilterCondition0_Size { - t.Errorf("Size of wtFwpmFilterCondition0 is %d, although %d is expected.", - actualWtFwpmFilterCondition0Size, wtFwpmFilterCondition0_Size) - } -} - -func TestWtFwpmFilterCondition0Offsets(t *testing.T) { - - s := wtFwpmFilterCondition0{} - sp := uintptr(unsafe.Pointer(&s)) - - offset := uintptr(unsafe.Pointer(&s.matchType)) - sp - - if offset != wtFwpmFilterCondition0_matchType_Offset { - t.Errorf("wtFwpmFilterCondition0.matchType offset is %d although %d is expected", offset, - wtFwpmFilterCondition0_matchType_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.conditionValue)) - sp - - if offset != wtFwpmFilterCondition0_conditionValue_Offset { - t.Errorf("wtFwpmFilterCondition0.conditionValue offset is %d although %d is expected", offset, - wtFwpmFilterCondition0_conditionValue_Offset) - return - } -} - -func TestWtFwpmFilter0Size(t *testing.T) { - - const actualWtFwpmFilter0Size = unsafe.Sizeof(wtFwpmFilter0{}) - - if actualWtFwpmFilter0Size != wtFwpmFilter0_Size { - t.Errorf("Size of wtFwpmFilter0 is %d, although %d is expected.", actualWtFwpmFilter0Size, - wtFwpmFilter0_Size) - } -} - -func TestWtFwpmFilter0Offsets(t *testing.T) { - - s := wtFwpmFilter0{} - sp := uintptr(unsafe.Pointer(&s)) - - offset := uintptr(unsafe.Pointer(&s.displayData)) - sp - - if offset != wtFwpmFilter0_displayData_Offset { - t.Errorf("wtFwpmFilter0.displayData offset is %d although %d is expected", offset, - wtFwpmFilter0_displayData_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.flags)) - sp - - if offset != wtFwpmFilter0_flags_Offset { - t.Errorf("wtFwpmFilter0.flags offset is %d although %d is expected", offset, wtFwpmFilter0_flags_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.providerKey)) - sp - - if offset != wtFwpmFilter0_providerKey_Offset { - t.Errorf("wtFwpmFilter0.providerKey offset is %d although %d is expected", offset, - wtFwpmFilter0_providerKey_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.providerData)) - sp - - if offset != wtFwpmFilter0_providerData_Offset { - t.Errorf("wtFwpmFilter0.providerData offset is %d although %d is expected", offset, - wtFwpmFilter0_providerData_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.layerKey)) - sp - - if offset != wtFwpmFilter0_layerKey_Offset { - t.Errorf("wtFwpmFilter0.layerKey offset is %d although %d is expected", offset, - wtFwpmFilter0_layerKey_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.subLayerKey)) - sp - - if offset != wtFwpmFilter0_subLayerKey_Offset { - t.Errorf("wtFwpmFilter0.subLayerKey offset is %d although %d is expected", offset, - wtFwpmFilter0_subLayerKey_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.weight)) - sp - - if offset != wtFwpmFilter0_weight_Offset { - t.Errorf("wtFwpmFilter0.weight offset is %d although %d is expected", offset, - wtFwpmFilter0_weight_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.numFilterConditions)) - sp - - if offset != wtFwpmFilter0_numFilterConditions_Offset { - t.Errorf("wtFwpmFilter0.numFilterConditions offset is %d although %d is expected", offset, - wtFwpmFilter0_numFilterConditions_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.filterCondition)) - sp - - if offset != wtFwpmFilter0_filterCondition_Offset { - t.Errorf("wtFwpmFilter0.filterCondition offset is %d although %d is expected", offset, - wtFwpmFilter0_filterCondition_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.action)) - sp - - if offset != wtFwpmFilter0_action_Offset { - t.Errorf("wtFwpmFilter0.action offset is %d although %d is expected", offset, - wtFwpmFilter0_action_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.providerContextKey)) - sp - - if offset != wtFwpmFilter0_providerContextKey_Offset { - t.Errorf("wtFwpmFilter0.providerContextKey offset is %d although %d is expected", offset, - wtFwpmFilter0_providerContextKey_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.reserved)) - sp - - if offset != wtFwpmFilter0_reserved_Offset { - t.Errorf("wtFwpmFilter0.reserved offset is %d although %d is expected", offset, - wtFwpmFilter0_reserved_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.filterID)) - sp - - if offset != wtFwpmFilter0_filterID_Offset { - t.Errorf("wtFwpmFilter0.filterID offset is %d although %d is expected", offset, - wtFwpmFilter0_filterID_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.effectiveWeight)) - sp - - if offset != wtFwpmFilter0_effectiveWeight_Offset { - t.Errorf("wtFwpmFilter0.effectiveWeight offset is %d although %d is expected", offset, - wtFwpmFilter0_effectiveWeight_Offset) - return - } -} - -func TestWtFwpProvider0Size(t *testing.T) { - - const actualWtFwpProvider0Size = unsafe.Sizeof(wtFwpProvider0{}) - - if actualWtFwpProvider0Size != wtFwpProvider0_Size { - t.Errorf("Size of wtFwpProvider0 is %d, although %d is expected.", actualWtFwpProvider0Size, - wtFwpProvider0_Size) - } -} - -func TestWtFwpProvider0Offsets(t *testing.T) { - - s := wtFwpProvider0{} - sp := uintptr(unsafe.Pointer(&s)) - - offset := uintptr(unsafe.Pointer(&s.displayData)) - sp - - if offset != wtFwpProvider0_displayData_Offset { - t.Errorf("wtFwpProvider0.displayData offset is %d although %d is expected", offset, - wtFwpProvider0_displayData_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.flags)) - sp - - if offset != wtFwpProvider0_flags_Offset { - t.Errorf("wtFwpProvider0.flags offset is %d although %d is expected", offset, - wtFwpProvider0_flags_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.providerData)) - sp - - if offset != wtFwpProvider0_providerData_Offset { - t.Errorf("wtFwpProvider0.providerData offset is %d although %d is expected", offset, - wtFwpProvider0_providerData_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.serviceName)) - sp - - if offset != wtFwpProvider0_serviceName_Offset { - t.Errorf("wtFwpProvider0.serviceName offset is %d although %d is expected", offset, - wtFwpProvider0_serviceName_Offset) - return - } -} - -func TestWtFwpmSession0Size(t *testing.T) { - - const actualWtFwpmSession0Size = unsafe.Sizeof(wtFwpmSession0{}) - - if actualWtFwpmSession0Size != wtFwpmSession0_Size { - t.Errorf("Size of wtFwpmSession0 is %d, although %d is expected.", actualWtFwpmSession0Size, - wtFwpmSession0_Size) - } -} - -func TestWtFwpmSession0Offsets(t *testing.T) { - - s := wtFwpmSession0{} - sp := uintptr(unsafe.Pointer(&s)) - - offset := uintptr(unsafe.Pointer(&s.displayData)) - sp - - if offset != wtFwpmSession0_displayData_Offset { - t.Errorf("wtFwpmSession0.displayData offset is %d although %d is expected", offset, - wtFwpmSession0_displayData_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.flags)) - sp - - if offset != wtFwpmSession0_flags_Offset { - t.Errorf("wtFwpmSession0.flags offset is %d although %d is expected", offset, wtFwpmSession0_flags_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.txnWaitTimeoutInMSec)) - sp - - if offset != wtFwpmSession0_txnWaitTimeoutInMSec_Offset { - t.Errorf("wtFwpmSession0.txnWaitTimeoutInMSec offset is %d although %d is expected", offset, - wtFwpmSession0_txnWaitTimeoutInMSec_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.processId)) - sp - - if offset != wtFwpmSession0_processId_Offset { - t.Errorf("wtFwpmSession0.processId offset is %d although %d is expected", offset, - wtFwpmSession0_processId_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.sid)) - sp - - if offset != wtFwpmSession0_sid_Offset { - t.Errorf("wtFwpmSession0.sid offset is %d although %d is expected", offset, wtFwpmSession0_sid_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.username)) - sp - - if offset != wtFwpmSession0_username_Offset { - t.Errorf("wtFwpmSession0.username offset is %d although %d is expected", offset, - wtFwpmSession0_username_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.kernelMode)) - sp - - if offset != wtFwpmSession0_kernelMode_Offset { - t.Errorf("wtFwpmSession0.kernelMode offset is %d although %d is expected", offset, - wtFwpmSession0_kernelMode_Offset) - return - } -} - -func TestWtFwpmSublayer0Size(t *testing.T) { - - const actualWtFwpmSublayer0Size = unsafe.Sizeof(wtFwpmSublayer0{}) - - if actualWtFwpmSublayer0Size != wtFwpmSublayer0_Size { - t.Errorf("Size of wtFwpmSublayer0 is %d, although %d is expected.", actualWtFwpmSublayer0Size, - wtFwpmSublayer0_Size) - } -} - -func TestWtFwpmSublayer0Offsets(t *testing.T) { - - s := wtFwpmSublayer0{} - sp := uintptr(unsafe.Pointer(&s)) - - offset := uintptr(unsafe.Pointer(&s.displayData)) - sp - - if offset != wtFwpmSublayer0_displayData_Offset { - t.Errorf("wtFwpmSublayer0.displayData offset is %d although %d is expected", offset, - wtFwpmSublayer0_displayData_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.flags)) - sp - - if offset != wtFwpmSublayer0_flags_Offset { - t.Errorf("wtFwpmSublayer0.flags offset is %d although %d is expected", offset, - wtFwpmSublayer0_flags_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.providerKey)) - sp - - if offset != wtFwpmSublayer0_providerKey_Offset { - t.Errorf("wtFwpmSublayer0.providerKey offset is %d although %d is expected", offset, - wtFwpmSublayer0_providerKey_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.providerData)) - sp - - if offset != wtFwpmSublayer0_providerData_Offset { - t.Errorf("wtFwpmSublayer0.providerData offset is %d although %d is expected", offset, - wtFwpmSublayer0_providerData_Offset) - return - } - - offset = uintptr(unsafe.Pointer(&s.weight)) - sp - - if offset != wtFwpmSublayer0_weight_Offset { - t.Errorf("wtFwpmSublayer0.weight offset is %d although %d is expected", offset, - wtFwpmSublayer0_weight_Offset) - return - } -} diff --git a/service/firewall/zsyscall_windows.go b/service/firewall/zsyscall_windows.go deleted file mode 100644 index 15e72703..00000000 --- a/service/firewall/zsyscall_windows.go +++ /dev/null @@ -1,186 +0,0 @@ -// Code generated by 'go generate'; DO NOT EDIT. - -package firewall - -import ( - "syscall" - "unsafe" - - "golang.org/x/sys/windows" -) - -var _ unsafe.Pointer - -// Do the interface allocations only once for common -// Errno values. -const ( - errnoERROR_IO_PENDING = 997 -) - -var ( - errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) -) - -// errnoErr returns common boxed Errno values, to prevent -// allocations at runtime. -func errnoErr(e syscall.Errno) error { - switch e { - case 0: - return nil - case errnoERROR_IO_PENDING: - return errERROR_IO_PENDING - } - // TODO: add more here, after collecting data on the common - // error values see on Windows. (perhaps when running - // all.bat?) - return e -} - -var ( - modfwpuclnt = windows.NewLazySystemDLL("fwpuclnt.dll") - modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") - - procFwpmEngineOpen0 = modfwpuclnt.NewProc("FwpmEngineOpen0") - procFwpmEngineClose0 = modfwpuclnt.NewProc("FwpmEngineClose0") - procFwpmSubLayerAdd0 = modfwpuclnt.NewProc("FwpmSubLayerAdd0") - procFwpmGetAppIdFromFileName0 = modfwpuclnt.NewProc("FwpmGetAppIdFromFileName0") - procFwpmFreeMemory0 = modfwpuclnt.NewProc("FwpmFreeMemory0") - procFwpmFilterAdd0 = modfwpuclnt.NewProc("FwpmFilterAdd0") - procFwpmTransactionBegin0 = modfwpuclnt.NewProc("FwpmTransactionBegin0") - procFwpmTransactionCommit0 = modfwpuclnt.NewProc("FwpmTransactionCommit0") - procFwpmTransactionAbort0 = modfwpuclnt.NewProc("FwpmTransactionAbort0") - procFwpmProviderAdd0 = modfwpuclnt.NewProc("FwpmProviderAdd0") - procGetSecurityInfo = modadvapi32.NewProc("GetSecurityInfo") - procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength") -) - -func fwpmEngineOpen0(serverName *uint16, authnService wtRpcCAuthN, authIdentity *uintptr, session *wtFwpmSession0, engineHandle unsafe.Pointer) (err error) { - r1, _, e1 := syscall.Syscall6(procFwpmEngineOpen0.Addr(), 5, uintptr(unsafe.Pointer(serverName)), uintptr(authnService), uintptr(unsafe.Pointer(authIdentity)), uintptr(unsafe.Pointer(session)), uintptr(engineHandle), 0) - if r1 != 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func fwpmEngineClose0(engineHandle uintptr) (err error) { - r1, _, e1 := syscall.Syscall(procFwpmEngineClose0.Addr(), 1, uintptr(engineHandle), 0, 0) - if r1 != 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func fwpmSubLayerAdd0(engineHandle uintptr, subLayer *wtFwpmSublayer0, sd uintptr) (err error) { - r1, _, e1 := syscall.Syscall(procFwpmSubLayerAdd0.Addr(), 3, uintptr(engineHandle), uintptr(unsafe.Pointer(subLayer)), uintptr(sd)) - if r1 != 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func fwpmGetAppIdFromFileName0(fileName *uint16, appID unsafe.Pointer) (err error) { - r1, _, e1 := syscall.Syscall(procFwpmGetAppIdFromFileName0.Addr(), 2, uintptr(unsafe.Pointer(fileName)), uintptr(appID), 0) - if r1 != 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func fwpmFreeMemory0(p unsafe.Pointer) { - syscall.Syscall(procFwpmFreeMemory0.Addr(), 1, uintptr(p), 0, 0) - return -} - -func fwpmFilterAdd0(engineHandle uintptr, filter *wtFwpmFilter0, sd uintptr, id *uint64) (err error) { - r1, _, e1 := syscall.Syscall6(procFwpmFilterAdd0.Addr(), 4, uintptr(engineHandle), uintptr(unsafe.Pointer(filter)), uintptr(sd), uintptr(unsafe.Pointer(id)), 0, 0) - if r1 != 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func fwpmTransactionBegin0(engineHandle uintptr, flags uint32) (err error) { - r1, _, e1 := syscall.Syscall(procFwpmTransactionBegin0.Addr(), 2, uintptr(engineHandle), uintptr(flags), 0) - if r1 != 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func fwpmTransactionCommit0(engineHandle uintptr) (err error) { - r1, _, e1 := syscall.Syscall(procFwpmTransactionCommit0.Addr(), 1, uintptr(engineHandle), 0, 0) - if r1 != 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func fwpmTransactionAbort0(engineHandle uintptr) (err error) { - r1, _, e1 := syscall.Syscall(procFwpmTransactionAbort0.Addr(), 1, uintptr(engineHandle), 0, 0) - if r1 != 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func fwpmProviderAdd0(engineHandle uintptr, provider *wtFwpmProvider0, sd uintptr) (err error) { - r1, _, e1 := syscall.Syscall(procFwpmProviderAdd0.Addr(), 3, uintptr(engineHandle), uintptr(unsafe.Pointer(provider)), uintptr(sd)) - if r1 != 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func getSecurityInfo(handle windows.Handle, objectType wtObjectType, si uint32, sidOwner *windows.SID, sidGroup *windows.SID, dacl *uintptr, sacl *uintptr, securityDescriptor *uintptr) (err error) { - r1, _, e1 := syscall.Syscall9(procGetSecurityInfo.Addr(), 8, uintptr(handle), uintptr(objectType), uintptr(si), uintptr(unsafe.Pointer(sidOwner)), uintptr(unsafe.Pointer(sidGroup)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), uintptr(unsafe.Pointer(securityDescriptor)), 0) - if r1 != 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func getSecurityDescriptorLength(securityDescriptor uintptr) (len uint32) { - r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(securityDescriptor), 0, 0) - len = uint32(r0) - return -} diff --git a/service/ifaceconfig.go b/service/ifaceconfig.go deleted file mode 100644 index 7d8b2f76..00000000 --- a/service/ifaceconfig.go +++ /dev/null @@ -1,220 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package service - -import ( - "bytes" - "log" - "net" - "sort" - - "golang.org/x/sys/windows" - "golang.zx2c4.com/winipcfg" - "golang.zx2c4.com/wireguard/tun" - - "golang.zx2c4.com/wireguard/windows/conf" - "golang.zx2c4.com/wireguard/windows/service/firewall" -) - -func cleanupAddressesOnDisconnectedInterfaces(addresses []*net.IPNet) { - if len(addresses) == 0 { - return - } - includedInAddresses := func(a *net.IPNet) bool { - //TODO: this makes the whole algorithm O(n^2). But we can't stick net.IPNet in a Go hashmap. Bummer! - for _, addr := range addresses { - ip := addr.IP - if ip4 := ip.To4(); ip4 != nil { - ip = ip4 - } - mA, _ := addr.Mask.Size() - mB, _ := a.Mask.Size() - if bytes.Equal(ip, a.IP) && mA == mB { - return true - } - } - return false - } - interfaces, err := winipcfg.GetInterfaces() - if err != nil { - return - } - for _, iface := range interfaces { - if iface.OperStatus == winipcfg.IfOperStatusUp { - continue - } - addressesToKeep := make([]*net.IPNet, 0, len(iface.UnicastAddresses)) - for _, address := range iface.UnicastAddresses { - ip := address.Address.Address - if ip4 := ip.To4(); ip4 != nil { - ip = ip4 - } - ipnet := &net.IPNet{ip, net.CIDRMask(int(address.OnLinkPrefixLength), 8*len(ip))} - if !includedInAddresses(ipnet) { - addressesToKeep = append(addressesToKeep, ipnet) - } - } - if len(addressesToKeep) < len(iface.UnicastAddresses) { - log.Printf("Cleaning up stale addresses from interface '%s'", iface.FriendlyName) - iface.SetAddresses(addressesToKeep) - } - } -} - -func configureInterface(conf *conf.Config, tun *tun.NativeTun) error { - iface, err := winipcfg.InterfaceFromLUID(tun.LUID()) - if err != nil { - return err - } - - estimatedRouteCount := len(conf.Interface.Addresses) - for _, peer := range conf.Peers { - estimatedRouteCount += len(peer.AllowedIPs) - } - routes := make([]winipcfg.RouteData, 0, estimatedRouteCount) - var firstGateway4 *net.IP - var firstGateway6 *net.IP - addresses := make([]*net.IPNet, len(conf.Interface.Addresses)) - for i, addr := range conf.Interface.Addresses { - ipnet := addr.IPNet() - addresses[i] = &ipnet - gateway := ipnet.IP.Mask(ipnet.Mask) - if addr.Bits() == 32 && firstGateway4 == nil { - firstGateway4 = &gateway - } else if addr.Bits() == 128 && firstGateway6 == nil { - firstGateway6 = &gateway - } - routes = append(routes, winipcfg.RouteData{ - Destination: net.IPNet{ - IP: gateway, - Mask: ipnet.Mask, - }, - NextHop: gateway, - Metric: 0, - }) - } - - foundDefault4 := false - foundDefault6 := false - for _, peer := range conf.Peers { - for _, allowedip := range peer.AllowedIPs { - if (allowedip.Bits() == 32 && firstGateway4 == nil) || (allowedip.Bits() == 128 && firstGateway6 == nil) { - continue - } - route := winipcfg.RouteData{ - Destination: allowedip.IPNet(), - Metric: 0, - } - if allowedip.Bits() == 32 { - if allowedip.Cidr == 0 { - foundDefault4 = true - } - route.NextHop = *firstGateway4 - } else if allowedip.Bits() == 128 { - if allowedip.Cidr == 0 { - foundDefault6 = true - } - route.NextHop = *firstGateway6 - } - routes = append(routes, route) - } - } - - err = iface.SetAddresses(addresses) - if err == windows.ERROR_OBJECT_ALREADY_EXISTS { - cleanupAddressesOnDisconnectedInterfaces(addresses) - err = iface.SetAddresses(addresses) - } - if err != nil { - return err - } - - deduplicatedRoutes := make([]*winipcfg.RouteData, 0, len(routes)) - sort.Slice(routes, func(i, j int) bool { - return routes[i].Metric < routes[j].Metric || - bytes.Compare(routes[i].NextHop, routes[j].NextHop) == -1 || - bytes.Compare(routes[i].Destination.IP, routes[j].Destination.IP) == -1 || - bytes.Compare(routes[i].Destination.Mask, routes[j].Destination.Mask) == -1 - }) - for i := 0; i < len(routes); i++ { - if i > 0 && routes[i].Metric == routes[i-1].Metric && - bytes.Equal(routes[i].NextHop, routes[i-1].NextHop) && - bytes.Equal(routes[i].Destination.IP, routes[i-1].Destination.IP) && - bytes.Equal(routes[i].Destination.Mask, routes[i-1].Destination.Mask) { - continue - } - deduplicatedRoutes = append(deduplicatedRoutes, &routes[i]) - } - - err = iface.SetRoutes(deduplicatedRoutes) - if err != nil { - return nil - } - - err = iface.SetDNS(conf.Interface.DNS) - if err != nil { - return err - } - - ipif, err := iface.GetIPInterface(windows.AF_INET) - if err != nil { - return err - } - if foundDefault4 { - ipif.UseAutomaticMetric = false - ipif.Metric = 0 - } - if conf.Interface.MTU > 0 { - ipif.NLMTU = uint32(conf.Interface.MTU) - tun.ForceMTU(int(ipif.NLMTU)) - } - err = ipif.Set() - if err != nil { - return err - } - - ipif, err = iface.GetIPInterface(windows.AF_INET6) - if err != nil { - return err - } - if foundDefault6 { - ipif.UseAutomaticMetric = false - ipif.Metric = 0 - } - if conf.Interface.MTU > 0 { - ipif.NLMTU = uint32(conf.Interface.MTU) - } - ipif.DadTransmits = 0 - ipif.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled - err = ipif.Set() - if err != nil { - return err - } - - return nil -} - -func enableFirewall(conf *conf.Config, tun *tun.NativeTun) error { - restrictAll := false - if len(conf.Peers) == 1 { - nextallowedip: - for _, allowedip := range conf.Peers[0].AllowedIPs { - if allowedip.Cidr == 0 { - for _, b := range allowedip.IP { - if b != 0 { - continue nextallowedip - } - } - restrictAll = true - break - } - } - } - if restrictAll && len(conf.Interface.DNS) == 0 { - log.Println("Warning: no DNS server specified, despite having an allowed IPs of 0.0.0.0/0 or ::/0. There may be connectivity issues.") - } - return firewall.EnableFirewall(tun.LUID(), conf.Interface.DNS, restrictAll) -} diff --git a/service/install.go b/service/install.go deleted file mode 100644 index 1d977262..00000000 --- a/service/install.go +++ /dev/null @@ -1,204 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package service - -import ( - "errors" - "os" - "time" - - "golang.org/x/sys/windows" - "golang.org/x/sys/windows/svc" - "golang.org/x/sys/windows/svc/mgr" - - "golang.zx2c4.com/wireguard/windows/conf" -) - -var cachedServiceManager *mgr.Mgr - -func serviceManager() (*mgr.Mgr, error) { - if cachedServiceManager != nil { - return cachedServiceManager, nil - } - m, err := mgr.Connect() - if err != nil { - return nil, err - } - cachedServiceManager = m - return cachedServiceManager, nil -} - -func InstallManager() error { - m, err := serviceManager() - if err != nil { - return err - } - path, err := os.Executable() - if err != nil { - return nil - } - - //TODO: Do we want to bail if executable isn't being run from the right location? - - serviceName := "WireGuardManager" - service, err := m.OpenService(serviceName) - if err == nil { - status, err := service.Query() - if err != nil { - service.Close() - return err - } - if status.State != svc.Stopped { - service.Close() - return errors.New("Manager already installed and running") - } - err = service.Delete() - service.Close() - if err != nil { - return err - } - for { - service, err = m.OpenService(serviceName) - if err != nil { - break - } - service.Close() - time.Sleep(time.Second / 3) - } - } - - config := mgr.Config{ - ServiceType: windows.SERVICE_WIN32_OWN_PROCESS, - StartType: mgr.StartAutomatic, - ErrorControl: mgr.ErrorNormal, - DisplayName: "WireGuard Manager", - } - - service, err = m.CreateService(serviceName, path, config, "/managerservice") - if err != nil { - return err - } - service.Start() - return service.Close() -} - -func UninstallManager() error { - m, err := serviceManager() - if err != nil { - return err - } - serviceName := "WireGuardManager" - service, err := m.OpenService(serviceName) - if err != nil { - return err - } - service.Control(svc.Stop) - err = service.Delete() - err2 := service.Close() - if err != nil { - return err - } - return err2 -} - -func RunManager() error { - return svc.Run("WireGuardManager", &managerService{}) -} - -func InstallTunnel(configPath string) error { - m, err := serviceManager() - if err != nil { - return err - } - path, err := os.Executable() - if err != nil { - return nil - } - - name, err := conf.NameFromPath(configPath) - if err != nil { - return err - } - - serviceName, err := ServiceNameOfTunnel(name) - if err != nil { - return err - } - service, err := m.OpenService(serviceName) - if err == nil { - status, err := service.Query() - if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE { - service.Close() - return err - } - if status.State != svc.Stopped && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE { - service.Close() - return errors.New("Tunnel already installed and running") - } - err = service.Delete() - service.Close() - if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE { - return err - } - for { - service, err = m.OpenService(serviceName) - if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE { - break - } - service.Close() - time.Sleep(time.Second / 3) - } - } - - config := mgr.Config{ - ServiceType: windows.SERVICE_WIN32_OWN_PROCESS, - StartType: mgr.StartAutomatic, - ErrorControl: mgr.ErrorNormal, - DisplayName: "WireGuard Tunnel: " + name, - } - - service, err = m.CreateService(serviceName, path, config, "/tunnelservice", configPath) - if err != nil { - return err - } - err = service.Start() - go trackTunnelService(name, service) // Pass off reference to handle. - return err -} - -func UninstallTunnel(name string) error { - m, err := serviceManager() - if err != nil { - return err - } - serviceName, err := ServiceNameOfTunnel(name) - if err != nil { - return err - } - service, err := m.OpenService(serviceName) - if err != nil { - return err - } - service.Control(svc.Stop) - err = service.Delete() - err2 := service.Close() - if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE { - return err - } - return err2 -} - -func RunTunnel(confPath string) error { - name, err := conf.NameFromPath(confPath) - if err != nil { - return err - } - serviceName, err := ServiceNameOfTunnel(name) - if err != nil { - return err - } - return svc.Run(serviceName, &tunnelService{confPath}) -} diff --git a/service/ipc_client.go b/service/ipc_client.go deleted file mode 100644 index 268f18e9..00000000 --- a/service/ipc_client.go +++ /dev/null @@ -1,281 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package service - -import ( - "encoding/gob" - "errors" - "net/rpc" - "os" - - "golang.zx2c4.com/wireguard/windows/conf" - "golang.zx2c4.com/wireguard/windows/updater" -) - -type Tunnel struct { - Name string -} - -type TunnelState int - -const ( - TunnelUnknown TunnelState = iota - TunnelStarted - TunnelStopped - TunnelStarting - TunnelStopping -) - -type NotificationType int - -const ( - TunnelChangeNotificationType NotificationType = iota - TunnelsChangeNotificationType - ManagerStoppingNotificationType - UpdateFoundNotificationType - UpdateProgressNotificationType -) - -var rpcClient *rpc.Client - -type TunnelChangeCallback struct { - cb func(tunnel *Tunnel, state TunnelState, globalState TunnelState, err error) -} - -var tunnelChangeCallbacks = make(map[*TunnelChangeCallback]bool) - -type TunnelsChangeCallback struct { - cb func() -} - -var tunnelsChangeCallbacks = make(map[*TunnelsChangeCallback]bool) - -type ManagerStoppingCallback struct { - cb func() -} - -var managerStoppingCallbacks = make(map[*ManagerStoppingCallback]bool) - -type UpdateFoundCallback struct { - cb func(updateState UpdateState) -} - -var updateFoundCallbacks = make(map[*UpdateFoundCallback]bool) - -type UpdateProgressCallback struct { - cb func(dp updater.DownloadProgress) -} - -var updateProgressCallbacks = make(map[*UpdateProgressCallback]bool) - -func InitializeIPCClient(reader *os.File, writer *os.File, events *os.File) { - rpcClient = rpc.NewClient(&pipeRWC{reader, writer}) - go func() { - decoder := gob.NewDecoder(events) - for { - var notificationType NotificationType - err := decoder.Decode(¬ificationType) - if err != nil { - return - } - switch notificationType { - case TunnelChangeNotificationType: - var tunnel string - err := decoder.Decode(&tunnel) - if err != nil || len(tunnel) == 0 { - continue - } - var state TunnelState - err = decoder.Decode(&state) - if err != nil { - continue - } - var globalState TunnelState - err = decoder.Decode(&globalState) - if err != nil { - continue - } - var errStr string - err = decoder.Decode(&errStr) - if err != nil { - continue - } - var retErr error - if len(errStr) > 0 { - retErr = errors.New(errStr) - } - if state == TunnelUnknown { - continue - } - t := &Tunnel{tunnel} - for cb := range tunnelChangeCallbacks { - cb.cb(t, state, globalState, retErr) - } - case TunnelsChangeNotificationType: - for cb := range tunnelsChangeCallbacks { - cb.cb() - } - case ManagerStoppingNotificationType: - for cb := range managerStoppingCallbacks { - cb.cb() - } - case UpdateFoundNotificationType: - var state UpdateState - err = decoder.Decode(&state) - if err != nil { - continue - } - for cb := range updateFoundCallbacks { - cb.cb(state) - } - case UpdateProgressNotificationType: - var dp updater.DownloadProgress - err = decoder.Decode(&dp.Activity) - if err != nil { - continue - } - err = decoder.Decode(&dp.BytesDownloaded) - if err != nil { - continue - } - err = decoder.Decode(&dp.BytesTotal) - if err != nil { - continue - } - var errStr string - err = decoder.Decode(&errStr) - if err != nil { - continue - } - if len(errStr) > 0 { - dp.Error = errors.New(errStr) - } - err = decoder.Decode(&dp.Complete) - if err != nil { - continue - } - for cb := range updateProgressCallbacks { - cb.cb(dp) - } - } - } - }() -} - -func (t *Tunnel) StoredConfig() (c conf.Config, err error) { - err = rpcClient.Call("ManagerService.StoredConfig", t.Name, &c) - return -} - -func (t *Tunnel) RuntimeConfig() (c conf.Config, err error) { - err = rpcClient.Call("ManagerService.RuntimeConfig", t.Name, &c) - return -} - -func (t *Tunnel) Start() error { - return rpcClient.Call("ManagerService.Start", t.Name, nil) -} - -func (t *Tunnel) Stop() error { - return rpcClient.Call("ManagerService.Stop", t.Name, nil) -} - -func (t *Tunnel) Toggle() (oldState TunnelState, err error) { - oldState, err = t.State() - if err != nil { - oldState = TunnelUnknown - return - } - if oldState == TunnelStarted { - err = t.Stop() - } else if oldState == TunnelStopped { - err = t.Start() - } - return -} - -func (t *Tunnel) WaitForStop() error { - return rpcClient.Call("ManagerService.WaitForStop", t.Name, nil) -} - -func (t *Tunnel) Delete() error { - return rpcClient.Call("ManagerService.Delete", t.Name, nil) -} - -func (t *Tunnel) State() (TunnelState, error) { - var state TunnelState - return state, rpcClient.Call("ManagerService.State", t.Name, &state) -} - -func IPCClientNewTunnel(conf *conf.Config) (Tunnel, error) { - var tunnel Tunnel - return tunnel, rpcClient.Call("ManagerService.Create", *conf, &tunnel) -} - -func IPCClientTunnels() ([]Tunnel, error) { - var tunnels []Tunnel - return tunnels, rpcClient.Call("ManagerService.Tunnels", uintptr(0), &tunnels) -} - -func IPCClientGlobalState() (TunnelState, error) { - var state TunnelState - return state, rpcClient.Call("ManagerService.GlobalState", uintptr(0), &state) -} - -func IPCClientQuit(stopTunnelsOnQuit bool) (bool, error) { - var alreadyQuit bool - return alreadyQuit, rpcClient.Call("ManagerService.Quit", stopTunnelsOnQuit, &alreadyQuit) -} - -func IPCClientUpdateState() (UpdateState, error) { - var state UpdateState - return state, rpcClient.Call("ManagerService.UpdateState", uintptr(0), &state) -} - -func IPCClientUpdate() error { - return rpcClient.Call("ManagerService.Update", uintptr(0), nil) -} - -func IPCClientRegisterTunnelChange(cb func(tunnel *Tunnel, state TunnelState, globalState TunnelState, err error)) *TunnelChangeCallback { - s := &TunnelChangeCallback{cb} - tunnelChangeCallbacks[s] = true - return s -} -func (cb *TunnelChangeCallback) Unregister() { - delete(tunnelChangeCallbacks, cb) -} -func IPCClientRegisterTunnelsChange(cb func()) *TunnelsChangeCallback { - s := &TunnelsChangeCallback{cb} - tunnelsChangeCallbacks[s] = true - return s -} -func (cb *TunnelsChangeCallback) Unregister() { - delete(tunnelsChangeCallbacks, cb) -} -func IPCClientRegisterManagerStopping(cb func()) *ManagerStoppingCallback { - s := &ManagerStoppingCallback{cb} - managerStoppingCallbacks[s] = true - return s -} -func (cb *ManagerStoppingCallback) Unregister() { - delete(managerStoppingCallbacks, cb) -} -func IPCClientRegisterUpdateFound(cb func(updateState UpdateState)) *UpdateFoundCallback { - s := &UpdateFoundCallback{cb} - updateFoundCallbacks[s] = true - return s -} -func (cb *UpdateFoundCallback) Unregister() { - delete(updateFoundCallbacks, cb) -} -func IPCClientRegisterUpdateProgress(cb func(dp updater.DownloadProgress)) *UpdateProgressCallback { - s := &UpdateProgressCallback{cb} - updateProgressCallbacks[s] = true - return s -} -func (cb *UpdateProgressCallback) Unregister() { - delete(updateProgressCallbacks, cb) -} diff --git a/service/ipc_pipe.go b/service/ipc_pipe.go deleted file mode 100644 index ecf24716..00000000 --- a/service/ipc_pipe.go +++ /dev/null @@ -1,77 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package service - -import ( - "os" - "strconv" - - "golang.org/x/sys/windows" -) - -type pipeRWC struct { - reader *os.File - writer *os.File -} - -func (p *pipeRWC) Read(b []byte) (int, error) { - return p.reader.Read(b) -} - -func (p *pipeRWC) Write(b []byte) (int, error) { - return p.writer.Write(b) -} - -func (p *pipeRWC) Close() error { - err1 := p.writer.Close() - err2 := p.reader.Close() - if err1 != nil { - return err1 - } - return err2 -} - -func makeInheritableAndGetStr(f *os.File) (str string, err error) { - sc, err := f.SyscallConn() - if err != nil { - return - } - err2 := sc.Control(func(fd uintptr) { - err = windows.SetHandleInformation(windows.Handle(fd), windows.HANDLE_FLAG_INHERIT, windows.HANDLE_FLAG_INHERIT) - str = strconv.FormatUint(uint64(fd), 10) - }) - if err2 != nil { - err = err2 - } - return -} - -func inheritableEvents() (ourEvents *os.File, theirEvents *os.File, theirEventStr string, err error) { - theirEvents, ourEvents, err = os.Pipe() - if err != nil { - return - } - theirEventStr, err = makeInheritableAndGetStr(theirEvents) - return -} - -func inheritableSocketpairEmulation() (ourReader *os.File, theirReader *os.File, theirReaderStr string, ourWriter *os.File, theirWriter *os.File, theirWriterStr string, err error) { - ourReader, theirWriter, err = os.Pipe() - if err != nil { - return - } - theirWriterStr, err = makeInheritableAndGetStr(theirWriter) - if err != nil { - return - } - - theirReader, ourWriter, err = os.Pipe() - if err != nil { - return - } - theirReaderStr, err = makeInheritableAndGetStr(theirReader) - return -} diff --git a/service/ipc_server.go b/service/ipc_server.go deleted file mode 100644 index 766f4178..00000000 --- a/service/ipc_server.go +++ /dev/null @@ -1,348 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package service - -import ( - "bytes" - "encoding/gob" - "fmt" - "io/ioutil" - "log" - "net/rpc" - "os" - "sync" - "sync/atomic" - "time" - - "github.com/Microsoft/go-winio" - "golang.org/x/sys/windows" - "golang.org/x/sys/windows/svc" - - "golang.zx2c4.com/wireguard/windows/conf" - "golang.zx2c4.com/wireguard/windows/updater" -) - -var managerServices = make(map[*ManagerService]bool) -var managerServicesLock sync.RWMutex -var haveQuit uint32 -var quitManagersChan = make(chan struct{}, 1) - -type ManagerService struct { - events *os.File - elevatedToken windows.Token -} - -func (s *ManagerService) StoredConfig(tunnelName string, config *conf.Config) error { - c, err := conf.LoadFromName(tunnelName) - if err != nil { - return err - } - *config = *c - return nil -} - -func (s *ManagerService) RuntimeConfig(tunnelName string, config *conf.Config) error { - storedConfig, err := conf.LoadFromName(tunnelName) - if err != nil { - return err - } - pipePath, err := PipePathOfTunnel(storedConfig.Name) - if err != nil { - return err - } - pipe, err := winio.DialPipe(pipePath, nil) - if err != nil { - return err - } - pipe.SetWriteDeadline(time.Now().Add(time.Second * 2)) - _, err = pipe.Write([]byte("get=1\n\n")) - if err != nil { - return err - } - pipe.SetReadDeadline(time.Now().Add(time.Second * 2)) - resp, err := ioutil.ReadAll(pipe) - if err != nil { - return err - } - pipe.Close() - runtimeConfig, err := conf.FromUAPI(string(resp), storedConfig) - if err != nil { - return err - } - *config = *runtimeConfig - return nil -} - -func (s *ManagerService) Start(tunnelName string, unused *uintptr) error { - // For now, enforce only one tunnel at a time. Later we'll remove this silly restriction. - trackedTunnelsLock.Lock() - tt := make([]string, 0, len(trackedTunnels)) - var inTransition string - for t, state := range trackedTunnels { - tt = append(tt, t) - if len(t) > 0 && (state == TunnelStarting || state == TunnelUnknown) { - inTransition = t - break - } - } - trackedTunnelsLock.Unlock() - if len(inTransition) != 0 { - return fmt.Errorf("Please allow the tunnel ā€˜%sā€™ to finish activating", inTransition) - } - go func() { - for _, t := range tt { - s.Stop(t, unused) - } - for _, t := range tt { - var state TunnelState - var unused uintptr - if s.State(t, &state) == nil && (state == TunnelStarted || state == TunnelStarting) { - log.Printf("[%s] Trying again to stop zombie tunnel", t) - s.Stop(t, &unused) - time.Sleep(time.Millisecond * 100) - } - } - }() - - // After that process is started -- it's somewhat asynchronous -- we install the new one. - c, err := conf.LoadFromName(tunnelName) - if err != nil { - return err - } - path, err := c.Path() - if err != nil { - return err - } - return InstallTunnel(path) -} - -func (s *ManagerService) Stop(tunnelName string, _ *uintptr) error { - err := UninstallTunnel(tunnelName) - if err == windows.ERROR_SERVICE_DOES_NOT_EXIST { - _, notExistsError := conf.LoadFromName(tunnelName) - if notExistsError == nil { - return nil - } - } - return err -} - -func (s *ManagerService) WaitForStop(tunnelName string, _ *uintptr) error { - serviceName, err := ServiceNameOfTunnel(tunnelName) - if err != nil { - return err - } - m, err := serviceManager() - if err != nil { - return err - } - for { - service, err := m.OpenService(serviceName) - if err == nil || err == windows.ERROR_SERVICE_MARKED_FOR_DELETE { - service.Close() - time.Sleep(time.Second / 3) - } else { - return nil - } - } -} - -func (s *ManagerService) Delete(tunnelName string, _ *uintptr) error { - err := s.Stop(tunnelName, nil) - if err != nil { - return err - } - return conf.DeleteName(tunnelName) -} - -func (s *ManagerService) State(tunnelName string, state *TunnelState) error { - serviceName, err := ServiceNameOfTunnel(tunnelName) - if err != nil { - return err - } - m, err := serviceManager() - if err != nil { - return err - } - service, err := m.OpenService(serviceName) - if err != nil { - *state = TunnelStopped - return nil - } - defer service.Close() - status, err := service.Query() - if err != nil { - *state = TunnelUnknown - return err - } - switch status.State { - case svc.Stopped: - *state = TunnelStopped - case svc.StopPending: - *state = TunnelStopping - case svc.Running: - *state = TunnelStarted - case svc.StartPending: - *state = TunnelStarting - default: - *state = TunnelUnknown - } - return nil -} - -func (s *ManagerService) GlobalState(_ uintptr, state *TunnelState) error { - *state = trackedTunnelsGlobalState() - return nil -} - -func (s *ManagerService) Create(tunnelConfig conf.Config, tunnel *Tunnel) error { - err := tunnelConfig.Save() - if err != nil { - return err - } - *tunnel = Tunnel{tunnelConfig.Name} - return nil - //TODO: handle already existing situation - //TODO: handle already running and existing situation -} - -func (s *ManagerService) Tunnels(_ uintptr, tunnels *[]Tunnel) error { - names, err := conf.ListConfigNames() - if err != nil { - return err - } - *tunnels = make([]Tunnel, len(names)) - for i := 0; i < len(*tunnels); i++ { - (*tunnels)[i].Name = names[i] - } - return nil - //TODO: account for running ones that aren't in the configuration store somehow -} - -func (s *ManagerService) Quit(stopTunnelsOnQuit bool, alreadyQuit *bool) error { - if !atomic.CompareAndSwapUint32(&haveQuit, 0, 1) { - *alreadyQuit = true - return nil - } - *alreadyQuit = false - - // Work around potential race condition of delivering messages to the wrong process by removing from notifications. - managerServicesLock.Lock() - delete(managerServices, s) - managerServicesLock.Unlock() - - if stopTunnelsOnQuit { - names, err := conf.ListConfigNames() - if err != nil { - return err - } - for _, name := range names { - UninstallTunnel(name) - } - } - - quitManagersChan <- struct{}{} - return nil -} - -func (s *ManagerService) UpdateState(_ uintptr, state *UpdateState) error { - *state = updateState - return nil -} - -func (s *ManagerService) Update(_ uintptr, _ *uintptr) error { - progress := updater.DownloadVerifyAndExecute(uintptr(s.elevatedToken)) - go func() { - for { - dp := <-progress - IPCServerNotifyUpdateProgress(dp) - if dp.Complete || dp.Error != nil { - return - } - } - }() - return nil -} - -func IPCServerListen(reader *os.File, writer *os.File, events *os.File, elevatedToken windows.Token) error { - service := &ManagerService{ - events: events, - elevatedToken: elevatedToken, - } - - server := rpc.NewServer() - err := server.Register(service) - if err != nil { - return err - } - - go func() { - managerServicesLock.Lock() - managerServices[service] = true - managerServicesLock.Unlock() - server.ServeConn(&pipeRWC{reader, writer}) - managerServicesLock.Lock() - delete(managerServices, service) - managerServicesLock.Unlock() - - }() - return nil -} - -func notifyAll(notificationType NotificationType, ifaces ...interface{}) { - if len(managerServices) == 0 { - return - } - - var buf bytes.Buffer - encoder := gob.NewEncoder(&buf) - err := encoder.Encode(notificationType) - if err != nil { - return - } - for _, iface := range ifaces { - err = encoder.Encode(iface) - if err != nil { - return - } - } - - managerServicesLock.RLock() - for m := range managerServices { - m.events.SetWriteDeadline(time.Now().Add(time.Second)) - m.events.Write(buf.Bytes()) - } - managerServicesLock.RUnlock() -} - -func IPCServerNotifyTunnelChange(name string, state TunnelState, err error) { - if err == nil { - notifyAll(TunnelChangeNotificationType, name, state, trackedTunnelsGlobalState(), "") - } else { - notifyAll(TunnelChangeNotificationType, name, state, trackedTunnelsGlobalState(), err.Error()) - } -} - -func IPCServerNotifyTunnelsChange() { - notifyAll(TunnelsChangeNotificationType) -} - -func IPCServerNotifyUpdateFound(state UpdateState) { - notifyAll(UpdateFoundNotificationType, state) -} - -func IPCServerNotifyUpdateProgress(dp updater.DownloadProgress) { - if dp.Error == nil { - notifyAll(UpdateProgressNotificationType, dp.Activity, dp.BytesDownloaded, dp.BytesTotal, "", dp.Complete) - } else { - notifyAll(UpdateProgressNotificationType, dp.Activity, dp.BytesDownloaded, dp.BytesTotal, dp.Error.Error(), dp.Complete) - } -} - -func IPCServerNotifyManagerStopping() { - notifyAll(ManagerStoppingNotificationType) - time.Sleep(time.Millisecond * 200) -} diff --git a/service/names.go b/service/names.go deleted file mode 100644 index e93ed66f..00000000 --- a/service/names.go +++ /dev/null @@ -1,26 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package service - -import ( - "errors" - - "golang.zx2c4.com/wireguard/windows/conf" -) - -func ServiceNameOfTunnel(tunnelName string) (string, error) { - if !conf.TunnelNameIsValid(tunnelName) { - return "", errors.New("Tunnel name is not valid") - } - return "WireGuardTunnel$" + tunnelName, nil -} - -func PipePathOfTunnel(tunnelName string) (string, error) { - if !conf.TunnelNameIsValid(tunnelName) { - return "", errors.New("Tunnel name is not valid") - } - return "\\\\.\\pipe\\WireGuard\\" + tunnelName, nil -} diff --git a/service/service_manager.go b/service/service_manager.go deleted file mode 100644 index 5bd94e0e..00000000 --- a/service/service_manager.go +++ /dev/null @@ -1,329 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package service - -import ( - "errors" - "fmt" - "log" - "os" - "runtime" - "runtime/debug" - "strings" - "sync" - "syscall" - "time" - "unsafe" - - "golang.org/x/sys/windows" - "golang.org/x/sys/windows/svc" - "golang.zx2c4.com/wireguard/windows/conf" - "golang.zx2c4.com/wireguard/windows/ringlogger" - "golang.zx2c4.com/wireguard/windows/version" -) - -type managerService struct{} - -func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) { - changes <- svc.Status{State: svc.StartPending} - - var err error - serviceError := ErrorSuccess - - defer func() { - svcSpecificEC, exitCode = determineErrorCode(err, serviceError) - logErr := combineErrors(err, serviceError) - if logErr != nil { - log.Print(logErr) - } - changes <- svc.Status{State: svc.StopPending} - }() - - err = ringlogger.InitGlobalLogger("MGR") - if err != nil { - serviceError = ErrorRingloggerOpen - return - } - defer func() { - if x := recover(); x != nil { - for _, line := range append([]string{fmt.Sprint(x)}, strings.Split(string(debug.Stack()), "\n")...) { - if len(strings.TrimSpace(line)) > 0 { - log.Println(line) - } - } - panic(x) - } - }() - - log.Println("Starting", version.UserAgent()) - - path, err := os.Executable() - if err != nil { - serviceError = ErrorDetermineExecutablePath - return - } - - devNull, err := os.OpenFile(os.DevNull, os.O_RDWR, 0) - if err != nil { - serviceError = ErrorOpenNULFile - return - } - - err = trackExistingTunnels() - if err != nil { - serviceError = ErrorTrackTunnels - return - } - - conf.RegisterStoreChangeCallback(func() { conf.MigrateUnencryptedConfigs() }) // Ignore return value for now, but could be useful later. - conf.RegisterStoreChangeCallback(IPCServerNotifyTunnelsChange) - - procs := make(map[uint32]*os.Process) - aliveSessions := make(map[uint32]bool) - procsLock := sync.Mutex{} - var startProcess func(session uint32) - stoppingManager := false - - startProcess = func(session uint32) { - defer func() { - runtime.UnlockOSThread() - procsLock.Lock() - delete(aliveSessions, session) - procsLock.Unlock() - }() - - var userToken windows.Token - err := windows.WTSQueryUserToken(session, &userToken) - if err != nil { - return - } - if !TokenIsMemberOfBuiltInAdministrator(userToken) { - userToken.Close() - return - } - user, err := userToken.GetTokenUser() - if err != nil { - log.Printf("Unable to lookup user from token: %v", err) - userToken.Close() - return - } - username, domain, accType, err := user.User.Sid.LookupAccount("") - if err != nil { - log.Printf("Unable to lookup username from sid: %v", err) - userToken.Close() - return - } - if accType != windows.SidTypeUser { - userToken.Close() - return - } - var elevatedToken windows.Token - if userToken.IsElevated() { - elevatedToken = userToken - } else { - elevatedToken, err = userToken.GetLinkedToken() - userToken.Close() - if err != nil { - log.Printf("Unable to elevate token: %v", err) - return - } - if !elevatedToken.IsElevated() { - elevatedToken.Close() - log.Println("Linked token is not elevated") - return - } - } - defer elevatedToken.Close() - userToken = 0 - first := true - for { - if stoppingManager { - return - } - - procsLock.Lock() - if alive := aliveSessions[session]; !alive { - procsLock.Unlock() - return - } - procsLock.Unlock() - - if !first { - time.Sleep(time.Second) - } else { - first = false - } - - //TODO: we lock the OS thread so that these inheritable handles don't escape into other processes that - // might be running in parallel Go routines. But the Go runtime is strange and who knows what's really - // happening with these or what is inherited. We need to do some analysis to be certain of what's going on. - runtime.LockOSThread() - ourReader, theirReader, theirReaderStr, ourWriter, theirWriter, theirWriterStr, err := inheritableSocketpairEmulation() - if err != nil { - log.Printf("Unable to create two inheritable pipes: %v", err) - return - } - ourEvents, theirEvents, theirEventStr, err := inheritableEvents() - err = IPCServerListen(ourReader, ourWriter, ourEvents, elevatedToken) - if err != nil { - log.Printf("Unable to listen on IPC pipes: %v", err) - return - } - theirLogMapping, theirLogMappingHandle, err := ringlogger.Global.ExportInheritableMappingHandleStr() - if err != nil { - log.Printf("Unable to export inheritable mapping handle for logging: %v", err) - return - } - - log.Printf("Starting UI process for user '%s@%s' for session %d", username, domain, session) - attr := &os.ProcAttr{ - Sys: &syscall.SysProcAttr{ - Token: syscall.Token(elevatedToken), - }, - Files: []*os.File{devNull, devNull, devNull}, - } - procsLock.Lock() - var proc *os.Process - if alive := aliveSessions[session]; alive { - proc, err = os.StartProcess(path, []string{path, "/ui", theirReaderStr, theirWriterStr, theirEventStr, theirLogMapping}, attr) - } else { - err = errors.New("Session has logged out") - } - procsLock.Unlock() - theirReader.Close() - theirWriter.Close() - theirEvents.Close() - windows.Close(theirLogMappingHandle) - runtime.UnlockOSThread() - if err != nil { - ourReader.Close() - ourWriter.Close() - ourEvents.Close() - log.Printf("Unable to start manager UI process for user '%s@%s' for session %d: %v", username, domain, session, err) - return - } - - procsLock.Lock() - procs[session] = proc - procsLock.Unlock() - - sessionIsDead := false - processStatus, err := proc.Wait() - if err == nil { - exitCode := processStatus.Sys().(syscall.WaitStatus).ExitCode - log.Printf("Exited UI process for user '%s@%s' for session %d with status %x", username, domain, session, exitCode) - const STATUS_DLL_INIT_FAILED_LOGOFF = 0xC000026B - sessionIsDead = exitCode == STATUS_DLL_INIT_FAILED_LOGOFF - } else { - log.Printf("Unable to wait for UI process for user '%s@%s' for session %d: %v", username, domain, session, err) - } - - procsLock.Lock() - delete(procs, session) - procsLock.Unlock() - ourReader.Close() - ourWriter.Close() - ourEvents.Close() - - if sessionIsDead { - return - } - } - } - - go checkForUpdates() - - var sessionsPointer *windows.WTS_SESSION_INFO - var count uint32 - err = windows.WTSEnumerateSessions(0, 0, 1, &sessionsPointer, &count) - if err != nil { - serviceError = ErrorEnumerateSessions - return - } - sessions := *(*[]windows.WTS_SESSION_INFO)(unsafe.Pointer(&struct { - addr *windows.WTS_SESSION_INFO - len int - cap int - }{sessionsPointer, int(count), int(count)})) - for _, session := range sessions { - if session.State != windows.WTSActive && session.State != windows.WTSDisconnected { - continue - } - procsLock.Lock() - if alive := aliveSessions[session.SessionID]; !alive { - aliveSessions[session.SessionID] = true - if _, ok := procs[session.SessionID]; !ok { - go startProcess(session.SessionID) - } - } - procsLock.Unlock() - } - windows.WTSFreeMemory(uintptr(unsafe.Pointer(sessionsPointer))) - - changes <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptSessionChange} - - uninstall := false -loop: - for { - select { - case <-quitManagersChan: - uninstall = true - break loop - case c := <-r: - switch c.Cmd { - case svc.Stop: - break loop - case svc.Interrogate: - changes <- c.CurrentStatus - case svc.SessionChange: - if c.EventType != windows.WTS_SESSION_LOGON && c.EventType != windows.WTS_SESSION_LOGOFF { - continue - } - sessionNotification := (*windows.WTSSESSION_NOTIFICATION)(unsafe.Pointer(c.EventData)) - if uintptr(sessionNotification.Size) != unsafe.Sizeof(*sessionNotification) { - log.Printf("Unexpected size of WTSSESSION_NOTIFICATION: %d", sessionNotification.Size) - continue - } - if c.EventType == windows.WTS_SESSION_LOGOFF { - procsLock.Lock() - delete(aliveSessions, sessionNotification.SessionID) - if proc, ok := procs[sessionNotification.SessionID]; ok { - proc.Kill() - } - procsLock.Unlock() - } else if c.EventType == windows.WTS_SESSION_LOGON { - procsLock.Lock() - if alive := aliveSessions[sessionNotification.SessionID]; !alive { - aliveSessions[sessionNotification.SessionID] = true - if _, ok := procs[sessionNotification.SessionID]; !ok { - go startProcess(sessionNotification.SessionID) - } - } - procsLock.Unlock() - } - - default: - log.Printf("Unexpected service control request #%d", c) - } - } - } - - changes <- svc.Status{State: svc.StopPending} - procsLock.Lock() - stoppingManager = true - IPCServerNotifyManagerStopping() - for _, proc := range procs { - proc.Kill() - } - procsLock.Unlock() - if uninstall { - err = UninstallManager() - if err != nil { - log.Printf("Unable to uninstaller manager when quitting: %v", err) - } - } - return -} diff --git a/service/service_tunnel.go b/service/service_tunnel.go deleted file mode 100644 index 2a82a2f2..00000000 --- a/service/service_tunnel.go +++ /dev/null @@ -1,223 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. - */ - -package service - -import ( - "bufio" - "bytes" - "fmt" - "log" - "net" - "os" - "runtime" - "runtime/debug" - "strings" - "time" - - "golang.org/x/sys/windows/svc" - "golang.zx2c4.com/winipcfg" - "golang.zx2c4.com/wireguard/device" - "golang.zx2c4.com/wireguard/ipc" - "golang.zx2c4.com/wireguard/tun" - "golang.zx2c4.com/wireguard/windows/conf" - "golang.zx2c4.com/wireguard/windows/ringlogger" - "golang.zx2c4.com/wireguard/windows/version" -) - -type tunnelService struct { - path string -} - -func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) { - changes <- svc.Status{State: svc.StartPending} - - var dev *device.Device - var uapi net.Listener - var routeChangeCallback *winipcfg.RouteChangeCallback - var err error - serviceError := ErrorSuccess - - defer func() { - svcSpecificEC, exitCode = determineErrorCode(err, serviceError) - logErr := combineErrors(err, serviceError) - if logErr != nil { - log.Println(logErr) - } - changes <- svc.Status{State: svc.StopPending} - - stopIt := make(chan bool, 1) - go func() { - t := time.NewTicker(time.Second * 30) - for { - select { - case <-t.C: - t.Stop() - buf := make([]byte, 1024) - for { - n := runtime.Stack(buf, true) - if n < len(buf) { - buf = buf[:n] - break - } - buf = make([]byte, 2*len(buf)) - } - lines := bytes.Split(buf, []byte{'\n'}) - log.Println("Failed to shutdown after 30 seconds. Probably dead locked. Printing stack and killing.") - for _, line := range lines { - if len(bytes.TrimSpace(line)) > 0 { - log.Println(string(line)) - } - } - os.Exit(777) - return - case <-stopIt: - t.Stop() - return - } - } - }() - - if routeChangeCallback != nil { - routeChangeCallback.Unregister() - } - if uapi != nil { - uapi.Close() - } - if dev != nil { - dev.Close() - } - stopIt <- true - log.Println("Shutting down") - }() - - err = ringlogger.InitGlobalLogger("TUN") - if err != nil { - serviceError = ErrorRingloggerOpen - return - } - defer func() { - if x := recover(); x != nil { - for _, line := range append([]string{fmt.Sprint(x)}, strings.Split(string(debug.Stack()), "\n")...) { - if len(strings.TrimSpace(line)) > 0 { - log.Println(line) - } - } - panic(x) - } - }() - - conf, err := conf.LoadFromPath(service.path) - if err != nil { - serviceError = ErrorLoadConfiguration - return - } - - logPrefix := fmt.Sprintf("[%s] ", conf.Name) - log.SetPrefix(logPrefix) - - log.Println("Starting", version.UserAgent()) - - log.Println("Resolving DNS names") - uapiConf, err := conf.ToUAPI() - if err != nil { - serviceError = ErrorDNSLookup - return - } - - log.Println("Creating Wintun device") - wintun, err := tun.CreateTUN(conf.Name) - if err != nil { - serviceError = ErrorCreateWintun - return - } - log.Println("Determining Wintun device name") - realInterfaceName, err := wintun.Name() - if err != nil { - serviceError = ErrorDetermineWintunName - return - } - conf.Name = realInterfaceName - nativeTun := wintun.(*tun.NativeTun) - - log.Println("Enabling firewall rules") - err = enableFirewall(conf, nativeTun) - if err != nil { - serviceError = ErrorFirewall - return - } - - log.Println("Dropping all privileges") - err = DropAllPrivileges() - if err != nil { - serviceError = ErrorDropPrivileges - return - } - - log.Println("Creating interface instance") - logOutput := log.New(ringlogger.Global, logPrefix, 0) - logger := &device.Logger{logOutput, logOutput, logOutput} - dev = device.NewDevice(wintun, logger) - - log.Println("Setting interface configuration") - uapi, err = ipc.UAPIListen(conf.Name) - if err != nil { - serviceError = ErrorUAPIListen - return - } - ipcErr := dev.IpcSetOperation(bufio.NewReader(strings.NewReader(uapiConf))) - if ipcErr != nil { - err = ipcErr - serviceError = ErrorDeviceSetConfig - return - } - - log.Println("Bringing peers up") - dev.Up() - - log.Println("Monitoring default routes") - routeChangeCallback, err = monitorDefaultRoutes(dev, conf.Interface.MTU == 0, nativeTun) - if err != nil { - serviceError = ErrorBindSocketsToDefaultRoutes - return - } - - log.Println("Setting device address") - err = configureInterface(conf, nativeTun) - if err != nil { - serviceError = ErrorSetNetConfig - return - } - - log.Println("Listening for UAPI requests") - go func() { - for { - conn, err := uapi.Accept() - if err != nil { - continue - } - go dev.IpcHandle(conn) - } - }() - - changes <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop} - log.Println("Startup complete") - - for { - select { - case c := <-r: - switch c.Cmd { - case svc.Stop: - return - case svc.Interrogate: - changes <- c.CurrentStatus - default: - log.Printf("Unexpected service control request #%d\n", c) - } - case <-dev.Wait(): - return - } - } -} diff --git a/service/tokens.go b/service/tokens.go deleted file mode 100644 index aade8734..00000000 --- a/service/tokens.go +++ /dev/null @@ -1,70 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package service - -import ( - "errors" - "runtime" - "unsafe" - - "golang.org/x/sys/windows" -) - -func TokenIsMemberOfBuiltInAdministrator(token windows.Token) bool { - adminSid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid) - if err != nil { - return false - } - gs, err := token.GetTokenGroups() - if err != nil { - return false - } - groups := (*[(1 << 28) - 1]windows.SIDAndAttributes)(unsafe.Pointer(&gs.Groups[0]))[:gs.GroupCount] - isAdmin := false - for _, g := range groups { - if (g.Attributes&windows.SE_GROUP_USE_FOR_DENY_ONLY != 0 || g.Attributes&windows.SE_GROUP_ENABLED != 0) && windows.EqualSid(g.Sid, adminSid) { - isAdmin = true - break - } - } - runtime.KeepAlive(gs) - return isAdmin -} - -func DropAllPrivileges() error { - processHandle, err := windows.GetCurrentProcess() - if err != nil { - return err - } - var processToken windows.Token - err = windows.OpenProcessToken(processHandle, windows.TOKEN_READ|windows.TOKEN_WRITE, &processToken) - if err != nil { - return err - } - defer processToken.Close() - - var bufferSizeRequired uint32 - windows.GetTokenInformation(processToken, windows.TokenPrivileges, nil, 0, &bufferSizeRequired) - if bufferSizeRequired == 0 || bufferSizeRequired < uint32(unsafe.Sizeof(windows.Tokenprivileges{}.PrivilegeCount)) { - return errors.New("GetTokenInformation failed to provide a buffer size") - } - buffer := make([]byte, bufferSizeRequired) - var bytesWritten uint32 - err = windows.GetTokenInformation(processToken, windows.TokenPrivileges, &buffer[0], uint32(len(buffer)), &bytesWritten) - if err != nil { - return err - } - if bytesWritten != bufferSizeRequired { - return errors.New("GetTokenInformation returned incomplete data") - } - tokenPrivileges := (*windows.Tokenprivileges)(unsafe.Pointer(&buffer[0])) - for i := uint32(0); i < tokenPrivileges.PrivilegeCount; i++ { - (*windows.LUIDAndAttributes)(unsafe.Pointer(uintptr(unsafe.Pointer(&tokenPrivileges.Privileges[0])) + unsafe.Sizeof(tokenPrivileges.Privileges[0])*uintptr(i))).Attributes = windows.SE_PRIVILEGE_REMOVED - } - err = windows.AdjustTokenPrivileges(processToken, false, tokenPrivileges, 0, nil, nil) - runtime.KeepAlive(buffer) - return err -} diff --git a/service/tunneltracker.go b/service/tunneltracker.go deleted file mode 100644 index 4c61925a..00000000 --- a/service/tunneltracker.go +++ /dev/null @@ -1,180 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package service - -import ( - "fmt" - "log" - "runtime" - "sync" - "syscall" - "time" - - "golang.org/x/sys/windows" - "golang.org/x/sys/windows/svc" - "golang.org/x/sys/windows/svc/mgr" - "golang.zx2c4.com/wireguard/windows/conf" -) - -func trackExistingTunnels() error { - m, err := serviceManager() - if err != nil { - return err - } - names, err := conf.ListConfigNames() - if err != nil { - return err - } - for _, name := range names { - serviceName, err := ServiceNameOfTunnel(name) - if err != nil { - continue - } - service, err := m.OpenService(serviceName) - if err != nil { - continue - } - go trackTunnelService(name, service) - } - return nil -} - -var serviceTrackerCallbackPtr = windows.NewCallback(func(notifier *windows.SERVICE_NOTIFY) uintptr { - return 0 -}) - -var trackedTunnels = make(map[string]TunnelState) -var trackedTunnelsLock = sync.Mutex{} - -func svcStateToTunState(s svc.State) TunnelState { - switch s { - case svc.StartPending: - return TunnelStarting - case svc.Running: - return TunnelStarted - case svc.StopPending: - return TunnelStopping - case svc.Stopped: - return TunnelStopped - default: - return TunnelUnknown - } -} - -func trackedTunnelsGlobalState() (state TunnelState) { - state = TunnelStopped - trackedTunnelsLock.Lock() - defer trackedTunnelsLock.Unlock() - for _, s := range trackedTunnels { - if s == TunnelStarting { - return TunnelStarting - } else if s == TunnelStopping { - return TunnelStopping - } else if s == TunnelStarted || s == TunnelUnknown { - state = TunnelStarted - } - } - return -} - -func trackTunnelService(tunnelName string, service *mgr.Service) { - defer func() { - service.Close() - log.Printf("[%s] Tunnel service tracker finished", tunnelName) - }() - - trackedTunnelsLock.Lock() - if _, found := trackedTunnels[tunnelName]; found { - trackedTunnelsLock.Unlock() - return - } - trackedTunnels[tunnelName] = TunnelUnknown - trackedTunnelsLock.Unlock() - defer func() { - trackedTunnelsLock.Lock() - delete(trackedTunnels, tunnelName) - trackedTunnelsLock.Unlock() - }() - - const serviceNotifications = windows.SERVICE_NOTIFY_RUNNING | windows.SERVICE_NOTIFY_START_PENDING | windows.SERVICE_NOTIFY_STOP_PENDING | windows.SERVICE_NOTIFY_STOPPED | windows.SERVICE_NOTIFY_DELETE_PENDING - notifier := &windows.SERVICE_NOTIFY{ - Version: windows.SERVICE_NOTIFY_STATUS_CHANGE, - NotifyCallback: serviceTrackerCallbackPtr, - } - - checkForDisabled := func() (shouldReturn bool) { - config, err := service.Config() - if err == windows.ERROR_SERVICE_MARKED_FOR_DELETE || config.StartType == windows.SERVICE_DISABLED { - log.Printf("[%s] Found disabled service via timeout, so deleting", tunnelName) - service.Delete() - trackedTunnelsLock.Lock() - trackedTunnels[tunnelName] = TunnelStopped - trackedTunnelsLock.Unlock() - IPCServerNotifyTunnelChange(tunnelName, TunnelStopped, nil) - return true - } - return false - } - if checkForDisabled() { - return - } - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - lastState := TunnelUnknown - for { - err := windows.NotifyServiceStatusChange(service.Handle, serviceNotifications, notifier) - switch err { - case nil: - for { - if windows.SleepEx(uint32(time.Second*3/time.Millisecond), true) == windows.WAIT_IO_COMPLETION { - break - } else if checkForDisabled() { - return - } - } - case windows.ERROR_SERVICE_MARKED_FOR_DELETE: - trackedTunnelsLock.Lock() - trackedTunnels[tunnelName] = TunnelStopped - trackedTunnelsLock.Unlock() - IPCServerNotifyTunnelChange(tunnelName, TunnelStopped, nil) - return - case windows.ERROR_SERVICE_NOTIFY_CLIENT_LAGGING: - continue - default: - trackedTunnelsLock.Lock() - trackedTunnels[tunnelName] = TunnelStopped - trackedTunnelsLock.Unlock() - IPCServerNotifyTunnelChange(tunnelName, TunnelStopped, fmt.Errorf("Unable to continue monitoring service, so stopping: %v", err)) - service.Control(svc.Stop) - return - } - - state := svcStateToTunState(svc.State(notifier.ServiceStatus.CurrentState)) - var tunnelError error - if state == TunnelStopped { - if notifier.ServiceStatus.Win32ExitCode == uint32(windows.ERROR_SERVICE_SPECIFIC_ERROR) { - maybeErr := Error(notifier.ServiceStatus.ServiceSpecificExitCode) - if maybeErr != ErrorSuccess { - tunnelError = maybeErr - } - } else { - switch notifier.ServiceStatus.Win32ExitCode { - case uint32(windows.NO_ERROR), uint32(windows.ERROR_SERVICE_NEVER_STARTED): - default: - tunnelError = syscall.Errno(notifier.ServiceStatus.Win32ExitCode) - } - } - } - if state != lastState { - trackedTunnelsLock.Lock() - trackedTunnels[tunnelName] = state - trackedTunnelsLock.Unlock() - IPCServerNotifyTunnelChange(tunnelName, state, tunnelError) - lastState = state - } - } -} diff --git a/service/updatestate.go b/service/updatestate.go deleted file mode 100644 index 4d9330ff..00000000 --- a/service/updatestate.go +++ /dev/null @@ -1,57 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package service - -import ( - "log" - "time" - - "golang.zx2c4.com/wireguard/windows/updater" - "golang.zx2c4.com/wireguard/windows/version" -) - -type UpdateState uint32 - -const ( - UpdateStateUnknown UpdateState = iota - UpdateStateFoundUpdate - UpdateStateUpdatesDisabledUnofficialBuild -) - -var updateState = UpdateStateUnknown - -func checkForUpdates() { - if !version.IsRunningOfficialVersion() { - log.Println("Build is not official, so updates are disabled") - updateState = UpdateStateUpdatesDisabledUnofficialBuild - IPCServerNotifyUpdateFound(updateState) - return - } - - time.Sleep(time.Second * 10) - - first := true - for { - update, err := updater.CheckForUpdate() - if err == nil && update != nil { - log.Println("An update is available") - updateState = UpdateStateFoundUpdate - IPCServerNotifyUpdateFound(updateState) - return - } - if err != nil { - log.Printf("Update checker: %v", err) - if first { - time.Sleep(time.Minute * 4) - first = false - } else { - time.Sleep(time.Minute * 25) - } - } else { - time.Sleep(time.Hour) - } - } -} diff --git a/services/errors.go b/services/errors.go new file mode 100644 index 00000000..c9b2c049 --- /dev/null +++ b/services/errors.go @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. + */ + +package services + +import ( + "fmt" + "syscall" + + "golang.org/x/sys/windows" +) + +type Error uint32 + +const ( + ErrorSuccess Error = iota + ErrorRingloggerOpen + ErrorLoadConfiguration + ErrorCreateWintun + ErrorDetermineWintunName + ErrorUAPIListen + ErrorDNSLookup + ErrorFirewall + ErrorDeviceSetConfig + ErrorBindSocketsToDefaultRoutes + ErrorSetNetConfig + ErrorDetermineExecutablePath + ErrorOpenNULFile + ErrorTrackTunnels + ErrorEnumerateSessions + ErrorDropPrivileges + ErrorWin32 +) + +func (e Error) Error() string { + switch e { + case ErrorSuccess: + return "No error" + case ErrorRingloggerOpen: + return "Unable to open log file" + case ErrorDetermineExecutablePath: + return "Unable to determine path of running executable" + case ErrorLoadConfiguration: + return "Unable to load configuration from path" + case ErrorCreateWintun: + return "Unable to create Wintun device" + case ErrorDetermineWintunName: + return "Unable to determine Wintun name" + case ErrorUAPIListen: + return "Unable to listen on named pipe" + case ErrorDNSLookup: + return "Unable to resolve one or more DNS hostname endpoints" + case ErrorFirewall: + return "Unable to enable firewall rules" + case ErrorDeviceSetConfig: + return "Unable to set device configuration" + case ErrorBindSocketsToDefaultRoutes: + return "Unable to bind sockets to default route" + case ErrorSetNetConfig: + return "Unable to set interface addresses, routes, dns, and/or adapter settings" + case ErrorOpenNULFile: + return "Unable to open NUL file" + case ErrorTrackTunnels: + return "Unable to track existing tunnels" + case ErrorEnumerateSessions: + return "Unable to enumerate current sessions" + case ErrorDropPrivileges: + return "Unable to drop privileges" + case ErrorWin32: + return "An internal Windows error has occurred" + default: + return "An unknown error has occurred" + } +} + +func DetermineErrorCode(err error, serviceError Error) (bool, uint32) { + if syserr, ok := err.(syscall.Errno); ok { + return false, uint32(syserr) + } else if serviceError != ErrorSuccess { + return true, uint32(serviceError) + } else { + return false, windows.NO_ERROR + } +} + +func CombineErrors(err error, serviceError Error) error { + if serviceError != ErrorSuccess { + if err != nil { + return fmt.Errorf("%v: %v", serviceError, err) + } + return serviceError + } + return err +} diff --git a/services/tokens.go b/services/tokens.go new file mode 100644 index 00000000..aa6f2c4a --- /dev/null +++ b/services/tokens.go @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package services + +import ( + "errors" + "runtime" + "unsafe" + + "golang.org/x/sys/windows" +) + +func TokenIsMemberOfBuiltInAdministrator(token windows.Token) bool { + adminSid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid) + if err != nil { + return false + } + gs, err := token.GetTokenGroups() + if err != nil { + return false + } + groups := (*[(1 << 28) - 1]windows.SIDAndAttributes)(unsafe.Pointer(&gs.Groups[0]))[:gs.GroupCount] + isAdmin := false + for _, g := range groups { + if (g.Attributes&windows.SE_GROUP_USE_FOR_DENY_ONLY != 0 || g.Attributes&windows.SE_GROUP_ENABLED != 0) && windows.EqualSid(g.Sid, adminSid) { + isAdmin = true + break + } + } + runtime.KeepAlive(gs) + return isAdmin +} + +func DropAllPrivileges() error { + processHandle, err := windows.GetCurrentProcess() + if err != nil { + return err + } + var processToken windows.Token + err = windows.OpenProcessToken(processHandle, windows.TOKEN_READ|windows.TOKEN_WRITE, &processToken) + if err != nil { + return err + } + defer processToken.Close() + + var bufferSizeRequired uint32 + windows.GetTokenInformation(processToken, windows.TokenPrivileges, nil, 0, &bufferSizeRequired) + if bufferSizeRequired == 0 || bufferSizeRequired < uint32(unsafe.Sizeof(windows.Tokenprivileges{}.PrivilegeCount)) { + return errors.New("GetTokenInformation failed to provide a buffer size") + } + buffer := make([]byte, bufferSizeRequired) + var bytesWritten uint32 + err = windows.GetTokenInformation(processToken, windows.TokenPrivileges, &buffer[0], uint32(len(buffer)), &bytesWritten) + if err != nil { + return err + } + if bytesWritten != bufferSizeRequired { + return errors.New("GetTokenInformation returned incomplete data") + } + tokenPrivileges := (*windows.Tokenprivileges)(unsafe.Pointer(&buffer[0])) + for i := uint32(0); i < tokenPrivileges.PrivilegeCount; i++ { + (*windows.LUIDAndAttributes)(unsafe.Pointer(uintptr(unsafe.Pointer(&tokenPrivileges.Privileges[0])) + unsafe.Sizeof(tokenPrivileges.Privileges[0])*uintptr(i))).Attributes = windows.SE_PRIVILEGE_REMOVED + } + err = windows.AdjustTokenPrivileges(processToken, false, tokenPrivileges, 0, nil, nil) + runtime.KeepAlive(buffer) + return err +} diff --git a/tunnel/defaultroutemonitor.go b/tunnel/defaultroutemonitor.go new file mode 100644 index 00000000..1ffce5fa --- /dev/null +++ b/tunnel/defaultroutemonitor.go @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package tunnel + +import ( + "log" + "time" + + "golang.org/x/sys/windows" + "golang.zx2c4.com/winipcfg" + "golang.zx2c4.com/wireguard/device" + "golang.zx2c4.com/wireguard/tun" +) + +func bindSocketRoute(family winipcfg.AddressFamily, device *device.Device, ourLUID uint64, lastLUID *uint64, lastIndex *uint32) error { + routes, err := winipcfg.GetRoutes(family) + if err != nil { + return err + } + lowestMetric := ^uint32(0) + index := uint32(0) // Zero is "unspecified", which for IP_UNICAST_IF resets the value, which is what we want. + luid := uint64(0) // Hopefully luid zero is unspecified, but hard to find docs saying so. + for _, route := range routes { + if route.DestinationPrefix.PrefixLength != 0 || route.InterfaceLUID == ourLUID { + continue + } + ifrow, err := winipcfg.GetIfRow(route.InterfaceLUID) + if err != nil || ifrow.OperStatus != winipcfg.IfOperStatusUp { + log.Printf("Found default route for interface %d, but not up, so skipping", route.InterfaceIndex) + continue + } + if route.Metric < lowestMetric { + lowestMetric = route.Metric + index = route.InterfaceIndex + luid = route.InterfaceLUID + } + } + if luid == *lastLUID && index == *lastIndex { + return nil + } + *lastLUID = luid + *lastIndex = index + if family == windows.AF_INET { + log.Printf("Binding UDPv4 socket to interface %d", index) + return device.BindSocketToInterface4(index) + } else if family == windows.AF_INET6 { + log.Printf("Binding UDPv6 socket to interface %d", index) + return device.BindSocketToInterface6(index) + } + return nil +} + +func getIPInterfaceRetry(luid uint64, family winipcfg.AddressFamily, retry bool) (ipi *winipcfg.IPInterface, err error) { + const maxRetries = 100 + for i := 0; i < maxRetries; i++ { + ipi, err = winipcfg.GetIPInterface(luid, family) + if retry && i != maxRetries-1 && err == windows.ERROR_NOT_FOUND { + time.Sleep(time.Millisecond * 50) + continue + } + break + } + return +} + +func monitorDefaultRoutes(device *device.Device, autoMTU bool, tun *tun.NativeTun) (*winipcfg.RouteChangeCallback, error) { + ourLUID := tun.LUID() + lastLUID4 := uint64(0) + lastLUID6 := uint64(0) + lastIndex4 := uint32(0) + lastIndex6 := uint32(0) + lastMTU := uint32(0) + doIt := func(retry bool) error { + err := bindSocketRoute(windows.AF_INET, device, ourLUID, &lastLUID4, &lastIndex4) + if err != nil { + return err + } + err = bindSocketRoute(windows.AF_INET6, device, ourLUID, &lastLUID6, &lastIndex6) + if err != nil { + return err + } + if !autoMTU { + return nil + } + mtu := uint32(0) + if lastLUID4 != 0 { + iface, err := winipcfg.InterfaceFromLUID(lastLUID4) + if err != nil { + return err + } + if iface.MTU > 0 { + mtu = iface.MTU + } + } + if lastLUID6 != 0 { + iface, err := winipcfg.InterfaceFromLUID(lastLUID6) + if err != nil { + return err + } + if iface.MTU > 0 && iface.MTU < mtu { + mtu = iface.MTU + } + } + if mtu > 0 && (lastMTU == 0 || lastMTU != mtu) { + iface, err := getIPInterfaceRetry(ourLUID, windows.AF_INET, retry) + if err != nil { + return err + } + iface.NLMTU = mtu - 80 + if iface.NLMTU < 576 { + iface.NLMTU = 576 + } + err = iface.Set() + if err != nil { + return err + } + tun.ForceMTU(int(iface.NLMTU)) //TODO: it sort of breaks the model with v6 mtu and v4 mtu being different. Just set v4 one for now. + iface, err = getIPInterfaceRetry(ourLUID, windows.AF_INET6, retry) + if err != nil { + return err + } + iface.NLMTU = mtu - 80 + if iface.NLMTU < 1280 { + iface.NLMTU = 1280 + } + err = iface.Set() + if err != nil { + return err + } + lastMTU = mtu + } + return nil + } + err := doIt(true) + if err != nil { + return nil, err + } + cb, err := winipcfg.RegisterRouteChangeCallback(func(notificationType winipcfg.MibNotificationType, route *winipcfg.Route) { + if route.DestinationPrefix.PrefixLength == 0 { + _ = doIt(false) + } + }) + if err != nil { + return nil, err + } + return cb, nil +} diff --git a/tunnel/firewall/blocker.go b/tunnel/firewall/blocker.go new file mode 100644 index 00000000..8034935d --- /dev/null +++ b/tunnel/firewall/blocker.go @@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package firewall + +import ( + "errors" + "net" + "unsafe" + + "golang.org/x/sys/windows" +) + +type wfpObjectInstaller func(uintptr) error + +// +// Fundamental WireGuard specific WFP objects. +// +type baseObjects struct { + provider windows.GUID + filters windows.GUID +} + +var wfpSession uintptr + +func createWfpSession() (uintptr, error) { + sessionDisplayData, err := createWtFwpmDisplayData0("WireGuard", "WireGuard dynamic session") + if err != nil { + return 0, wrapErr(err) + } + + session := wtFwpmSession0{ + displayData: *sessionDisplayData, + flags: cFWPM_SESSION_FLAG_DYNAMIC, + txnWaitTimeoutInMSec: windows.INFINITE, + } + + sessionHandle := uintptr(0) + + err = fwpmEngineOpen0(nil, cRPC_C_AUTHN_WINNT, nil, &session, unsafe.Pointer(&sessionHandle)) + if err != nil { + return 0, wrapErr(err) + } + + return sessionHandle, nil +} + +func registerBaseObjects(session uintptr) (*baseObjects, error) { + // {48E29F38-7492-4436-8F92-29D78A8D29D3} + providerGUID := windows.GUID{ + Data1: 0x48e29f38, + Data2: 0x7492, + Data3: 0x4436, + Data4: [8]byte{0x8f, 0x92, 0x29, 0xd7, 0x8a, 0x8d, 0x29, 0xd3}, + } + // {FE3DB7F8-4658-4DE5-8DA9-CE5086A8266B} + filtersGUID := windows.GUID{ + Data1: 0xfe3db7f8, + Data2: 0x4658, + Data3: 0x4de5, + Data4: [8]byte{0x8d, 0xa9, 0xce, 0x50, 0x86, 0xa8, 0x26, 0x6b}, + } + + // + // Register provider. + // + { + displayData, err := createWtFwpmDisplayData0("WireGuard", "The WireGuard provider") + if err != nil { + return nil, wrapErr(err) + } + provider := wtFwpmProvider0{ + providerKey: providerGUID, + displayData: *displayData, + } + err = fwpmProviderAdd0(session, &provider, 0) + if err != nil { + //TODO: cleanup entire call chain of these if failure? + return nil, wrapErr(err) + } + } + + // + // Register filters sublayer. + // + { + displayData, err := createWtFwpmDisplayData0("WireGuard filters", "Permissive and blocking filters") + if err != nil { + return nil, wrapErr(err) + } + sublayer := wtFwpmSublayer0{ + subLayerKey: filtersGUID, + displayData: *displayData, + providerKey: &providerGUID, + weight: ^uint16(0), + } + err = fwpmSubLayerAdd0(session, &sublayer, 0) + if err != nil { + return nil, wrapErr(err) + } + } + + return &baseObjects{ + providerGUID, + filtersGUID, + }, nil +} + +func EnableFirewall(luid uint64, restrictToDNSServers []net.IP, restrictAll bool) error { + if wfpSession != 0 { + return errors.New("The firewall has already been enabled") + } + + session, err := createWfpSession() + if err != nil { + return wrapErr(err) + } + + objectInstaller := func(session uintptr) error { + baseObjects, err := registerBaseObjects(session) + if err != nil { + return wrapErr(err) + } + + if len(restrictToDNSServers) > 0 { + err = blockDNS(restrictToDNSServers, session, baseObjects, 15, 14) + if err != nil { + return wrapErr(err) + } + } + + if restrictAll { + err = permitLoopback(session, baseObjects, 13) + if err != nil { + return wrapErr(err) + } + } + + err = permitTunInterface(session, baseObjects, 12, luid) + if err != nil { + return wrapErr(err) + } + + err = permitWireGuardService(session, baseObjects, 12) + if err != nil { + return wrapErr(err) + } + + if restrictAll { + err = permitDHCPIPv4(session, baseObjects, 12) + if err != nil { + return wrapErr(err) + } + + err = permitDHCPIPv6(session, baseObjects, 12) + if err != nil { + return wrapErr(err) + } + + err = permitNdp(session, baseObjects, 12) + if err != nil { + return wrapErr(err) + } + + /* TODO: actually evaluate if this does anything and if we need this. It's layer 2; our other rules are layer 3. + * In other words, if somebody complains, try enabling it. For now, keep it off. + err = permitHyperV(session, baseObjects, 12) + if err != nil { + return wrapErr(err) + } + */ + } + + if restrictAll { + err = blockAll(session, baseObjects, 0) + if err != nil { + return wrapErr(err) + } + } + + return nil + } + + err = runTransaction(session, objectInstaller) + if err != nil { + fwpmEngineClose0(session) + return wrapErr(err) + } + + wfpSession = session + return nil +} + +func DisableFirewall() { + if wfpSession != 0 { + fwpmEngineClose0(wfpSession) + wfpSession = 0 + } +} diff --git a/tunnel/firewall/helpers.go b/tunnel/firewall/helpers.go new file mode 100644 index 00000000..e340b802 --- /dev/null +++ b/tunnel/firewall/helpers.go @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package firewall + +import ( + "fmt" + "os" + "runtime" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +func runTransaction(session uintptr, operation wfpObjectInstaller) error { + err := fwpmTransactionBegin0(session, 0) + if err != nil { + return wrapErr(err) + } + + err = operation(session) + if err != nil { + fwpmTransactionAbort0(session) + return wrapErr(err) + } + + err = fwpmTransactionCommit0(session) + if err != nil { + fwpmTransactionAbort0(session) + return wrapErr(err) + } + + return nil +} + +func createWtFwpmDisplayData0(name, description string) (*wtFwpmDisplayData0, error) { + namePtr, err := windows.UTF16PtrFromString(name) + if err != nil { + return nil, wrapErr(err) + } + + descriptionPtr, err := windows.UTF16PtrFromString(description) + if err != nil { + return nil, wrapErr(err) + } + + return &wtFwpmDisplayData0{ + name: namePtr, + description: descriptionPtr, + }, nil +} + +func filterWeight(weight uint8) wtFwpValue0 { + return wtFwpValue0{ + _type: cFWP_UINT8, + value: uintptr(weight), + } +} + +func wrapErr(err error) error { + if _, ok := err.(syscall.Errno); !ok { + return err + } + _, file, line, ok := runtime.Caller(1) + if !ok { + return fmt.Errorf("Firewall error at unknown location: %v", err) + } + return fmt.Errorf("Firewall error at %s:%d: %v", file, line, err) +} + +func getCurrentProcessSecurityDescriptor() (*wtFwpByteBlob, error) { + procHandle, err := windows.GetCurrentProcess() + if err != nil { + panic(err) + } + blob := &wtFwpByteBlob{} + err = getSecurityInfo(procHandle, cSE_KERNEL_OBJECT, cDACL_SECURITY_INFORMATION, nil, nil, nil, nil, (*uintptr)(unsafe.Pointer(&blob.data))) + if err != nil { + return nil, wrapErr(err) + } + blob.size = getSecurityDescriptorLength(uintptr(unsafe.Pointer(blob.data))) + return blob, nil +} + +func getCurrentProcessAppID() (*wtFwpByteBlob, error) { + currentFile, err := os.Executable() + if err != nil { + return nil, wrapErr(err) + } + + curFilePtr, err := windows.UTF16PtrFromString(currentFile) + if err != nil { + return nil, wrapErr(err) + } + + var appID *wtFwpByteBlob + err = fwpmGetAppIdFromFileName0(curFilePtr, unsafe.Pointer(&appID)) + if err != nil { + return nil, wrapErr(err) + } + return appID, nil +} diff --git a/tunnel/firewall/mksyscall.go b/tunnel/firewall/mksyscall.go new file mode 100644 index 00000000..5e4bcaa0 --- /dev/null +++ b/tunnel/firewall/mksyscall.go @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package firewall + +//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go diff --git a/tunnel/firewall/rules.go b/tunnel/firewall/rules.go new file mode 100644 index 00000000..76e2a85b --- /dev/null +++ b/tunnel/firewall/rules.go @@ -0,0 +1,1240 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package firewall + +import ( + "encoding/binary" + "errors" + "net" + "runtime" + "unsafe" + + "golang.org/x/sys/windows" + "golang.zx2c4.com/wireguard/windows/version" +) + +// +// Known addresses. +// +var ( + linkLocal = wtFwpV6AddrAndMask{[16]uint8{0xfe, 0x80}, 10} + + linkLocalDHCPMulticast = wtFwpByteArray16{[16]uint8{0xFF, 0x02, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x2}} + siteLocalDHCPMulticast = wtFwpByteArray16{[16]uint8{0xFF, 0x05, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x3}} + + linkLocalRouterMulticast = wtFwpByteArray16{[16]uint8{0xFF, 0x02, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}} +) + +func permitTunInterface(session uintptr, baseObjects *baseObjects, weight uint8, ifLUID uint64) error { + ifaceCondition := wtFwpmFilterCondition0{ + fieldKey: cFWPM_CONDITION_IP_LOCAL_INTERFACE, + matchType: cFWP_MATCH_EQUAL, + conditionValue: wtFwpConditionValue0{ + _type: cFWP_UINT64, + value: (uintptr)(unsafe.Pointer(&ifLUID)), + }, + } + + filter := wtFwpmFilter0{ + providerKey: &baseObjects.provider, + subLayerKey: baseObjects.filters, + weight: filterWeight(weight), + numFilterConditions: 1, + filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&ifaceCondition)), + action: wtFwpmAction0{ + _type: cFWP_ACTION_PERMIT, + }, + } + + filterID := uint64(0) + + // + // #1 Permit outbound IPv4 traffic. + // + { + displayData, err := createWtFwpmDisplayData0("Permit outbound IPv4 traffic on TUN", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #2 Permit inbound IPv4 traffic. + // + { + displayData, err := createWtFwpmDisplayData0("Permit inbound IPv4 traffic on TUN", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #3 Permit outbound IPv6 traffic. + // + { + displayData, err := createWtFwpmDisplayData0("Permit outbound IPv6 traffic on TUN", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #4 Permit inbound IPv6 traffic. + // + { + displayData, err := createWtFwpmDisplayData0("Permit inbound IPv6 traffic on TUN", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + return nil +} + +func permitWireGuardService(session uintptr, baseObjects *baseObjects, weight uint8) error { + var conditions [2]wtFwpmFilterCondition0 + + // + // First condition is the exe path of the current process. + // + appID, err := getCurrentProcessAppID() + if err != nil { + return wrapErr(err) + } + defer fwpmFreeMemory0(unsafe.Pointer(&appID)) + + conditions[0] = wtFwpmFilterCondition0{ + fieldKey: cFWPM_CONDITION_ALE_APP_ID, + matchType: cFWP_MATCH_EQUAL, + conditionValue: wtFwpConditionValue0{ + _type: cFWP_BYTE_BLOB_TYPE, + value: uintptr(unsafe.Pointer(appID)), + }, + } + + // + // Second condition is the SECURITY_DESCRIPTOR of the current process. + // This prevents low privileged applications hosted in the same exe from matching this filter. + // + sd, err := getCurrentProcessSecurityDescriptor() + if err != nil { + return wrapErr(err) + } + defer windows.LocalFree(windows.Handle(unsafe.Pointer(sd.data))) + + conditions[1] = wtFwpmFilterCondition0{ + fieldKey: cFWPM_CONDITION_ALE_USER_ID, + matchType: cFWP_MATCH_EQUAL, + conditionValue: wtFwpConditionValue0{ + _type: cFWP_SECURITY_DESCRIPTOR_TYPE, + value: uintptr(unsafe.Pointer(sd)), + }, + } + + // + // Assemble the filter. + // + filter := wtFwpmFilter0{ + providerKey: &baseObjects.provider, + subLayerKey: baseObjects.filters, + weight: filterWeight(weight), + flags: cFWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT, + numFilterConditions: uint32(len(conditions)), + filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&conditions)), + action: wtFwpmAction0{ + _type: cFWP_ACTION_PERMIT, + }, + } + + filterID := uint64(0) + + // + // #1 Permit outbound IPv4 traffic. + // + { + displayData, err := createWtFwpmDisplayData0("Permit unrestricted outbound traffic for WireGuard service (IPv4)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #2 Permit inbound IPv4 traffic. + // + { + displayData, err := createWtFwpmDisplayData0("Permit unrestricted inbound traffic for WireGuard service (IPv4)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #3 Permit outbound IPv6 traffic. + // + { + displayData, err := createWtFwpmDisplayData0("Permit unrestricted outbound traffic for WireGuard service (IPv6)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #4 Permit inbound IPv6 traffic. + // + { + displayData, err := createWtFwpmDisplayData0("Permit unrestricted inbound traffic for WireGuard service (IPv6)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + return nil +} + +func permitLoopback(session uintptr, baseObjects *baseObjects, weight uint8) error { + condition := wtFwpmFilterCondition0{ + fieldKey: cFWPM_CONDITION_INTERFACE_TYPE, + matchType: cFWP_MATCH_EQUAL, + conditionValue: wtFwpConditionValue0{ + _type: cFWP_UINT32, + value: uintptr(cIF_TYPE_SOFTWARE_LOOPBACK), + }, + } + + filter := wtFwpmFilter0{ + providerKey: &baseObjects.provider, + subLayerKey: baseObjects.filters, + weight: filterWeight(weight), + numFilterConditions: 1, + filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&condition)), + action: wtFwpmAction0{ + _type: cFWP_ACTION_PERMIT, + }, + } + + filterID := uint64(0) + + // + // #1 Permit outbound IPv4 on loopback. + // + { + displayData, err := createWtFwpmDisplayData0("Permit outbound on loopback (IPv4)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #2 Permit inbound IPv4 on loopback. + // + { + displayData, err := createWtFwpmDisplayData0("Permit inbound on loopback (IPv4)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #3 Permit outbound IPv6 on loopback. + // + { + displayData, err := createWtFwpmDisplayData0("Permit outbound on loopback (IPv6)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #4 Permit inbound IPv6 on loopback. + // + { + displayData, err := createWtFwpmDisplayData0("Permit inbound on loopback (IPv6)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + return nil +} + +func permitDHCPIPv4(session uintptr, baseObjects *baseObjects, weight uint8) error { + // + // #1 Outbound DHCP request on IPv4. + // + { + var conditions [4]wtFwpmFilterCondition0 + + conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL + conditions[0].matchType = cFWP_MATCH_EQUAL + conditions[0].conditionValue._type = cFWP_UINT8 + conditions[0].conditionValue.value = uintptr(cIPPROTO_UDP) + + conditions[1].fieldKey = cFWPM_CONDITION_IP_LOCAL_PORT + conditions[1].matchType = cFWP_MATCH_EQUAL + conditions[1].conditionValue._type = cFWP_UINT16 + conditions[1].conditionValue.value = uintptr(68) + + conditions[2].fieldKey = cFWPM_CONDITION_IP_REMOTE_PORT + conditions[2].matchType = cFWP_MATCH_EQUAL + conditions[2].conditionValue._type = cFWP_UINT16 + conditions[2].conditionValue.value = uintptr(67) + + conditions[3].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS + conditions[3].matchType = cFWP_MATCH_EQUAL + conditions[3].conditionValue._type = cFWP_UINT32 + conditions[3].conditionValue.value = uintptr(0xffffffff) + + displayData, err := createWtFwpmDisplayData0("Permit outbound DHCP request (IPv4)", "") + if err != nil { + return wrapErr(err) + } + + filter := wtFwpmFilter0{ + displayData: *displayData, + providerKey: &baseObjects.provider, + layerKey: cFWPM_LAYER_ALE_AUTH_CONNECT_V4, + subLayerKey: baseObjects.filters, + weight: filterWeight(weight), + numFilterConditions: uint32(len(conditions)), + filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&conditions)), + action: wtFwpmAction0{ + _type: cFWP_ACTION_PERMIT, + }, + } + + filterID := uint64(0) + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #2 Inbound DHCP response on IPv4. + // + { + var conditions [3]wtFwpmFilterCondition0 + + conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL + conditions[0].matchType = cFWP_MATCH_EQUAL + conditions[0].conditionValue._type = cFWP_UINT8 + conditions[0].conditionValue.value = uintptr(cIPPROTO_UDP) + + conditions[1].fieldKey = cFWPM_CONDITION_IP_LOCAL_PORT + conditions[1].matchType = cFWP_MATCH_EQUAL + conditions[1].conditionValue._type = cFWP_UINT16 + conditions[1].conditionValue.value = uintptr(68) + + conditions[2].fieldKey = cFWPM_CONDITION_IP_REMOTE_PORT + conditions[2].matchType = cFWP_MATCH_EQUAL + conditions[2].conditionValue._type = cFWP_UINT16 + conditions[2].conditionValue.value = uintptr(67) + + displayData, err := createWtFwpmDisplayData0("Permit inbound DHCP response (IPv4)", "") + if err != nil { + return wrapErr(err) + } + + filter := wtFwpmFilter0{ + displayData: *displayData, + providerKey: &baseObjects.provider, + layerKey: cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, + subLayerKey: baseObjects.filters, + weight: filterWeight(weight), + numFilterConditions: uint32(len(conditions)), + filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&conditions)), + action: wtFwpmAction0{ + _type: cFWP_ACTION_PERMIT, + }, + } + + filterID := uint64(0) + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + return nil +} + +func permitDHCPIPv6(session uintptr, baseObjects *baseObjects, weight uint8) error { + // + // #1 Outbound DHCP request on IPv6. + // + { + var conditions [6]wtFwpmFilterCondition0 + + conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL + conditions[0].matchType = cFWP_MATCH_EQUAL + conditions[0].conditionValue._type = cFWP_UINT8 + conditions[0].conditionValue.value = uintptr(cIPPROTO_UDP) + + conditions[1].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS + conditions[1].matchType = cFWP_MATCH_EQUAL + conditions[1].conditionValue._type = cFWP_BYTE_ARRAY16_TYPE + conditions[1].conditionValue.value = uintptr(unsafe.Pointer(&linkLocalDHCPMulticast)) + + // Repeat the condition type for logical OR. + conditions[2].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS + conditions[2].matchType = cFWP_MATCH_EQUAL + conditions[2].conditionValue._type = cFWP_BYTE_ARRAY16_TYPE + conditions[2].conditionValue.value = uintptr(unsafe.Pointer(&siteLocalDHCPMulticast)) + + conditions[3].fieldKey = cFWPM_CONDITION_IP_REMOTE_PORT + conditions[3].matchType = cFWP_MATCH_EQUAL + conditions[3].conditionValue._type = cFWP_UINT16 + conditions[3].conditionValue.value = uintptr(547) + + conditions[4].fieldKey = cFWPM_CONDITION_IP_LOCAL_ADDRESS + conditions[4].matchType = cFWP_MATCH_EQUAL + conditions[4].conditionValue._type = cFWP_V6_ADDR_MASK + conditions[4].conditionValue.value = uintptr(unsafe.Pointer(&linkLocal)) + + conditions[5].fieldKey = cFWPM_CONDITION_IP_LOCAL_PORT + conditions[5].matchType = cFWP_MATCH_EQUAL + conditions[5].conditionValue._type = cFWP_UINT16 + conditions[5].conditionValue.value = uintptr(546) + + displayData, err := createWtFwpmDisplayData0("Permit outbound DHCP request (IPv6)", "") + if err != nil { + return wrapErr(err) + } + + filter := wtFwpmFilter0{ + displayData: *displayData, + providerKey: &baseObjects.provider, + layerKey: cFWPM_LAYER_ALE_AUTH_CONNECT_V6, + subLayerKey: baseObjects.filters, + weight: filterWeight(weight), + numFilterConditions: uint32(len(conditions)), + filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&conditions)), + action: wtFwpmAction0{ + _type: cFWP_ACTION_PERMIT, + }, + } + + filterID := uint64(0) + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #2 Inbound DHCP response on IPv6. + // + { + var conditions [5]wtFwpmFilterCondition0 + + conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL + conditions[0].matchType = cFWP_MATCH_EQUAL + conditions[0].conditionValue._type = cFWP_UINT8 + conditions[0].conditionValue.value = uintptr(cIPPROTO_UDP) + + conditions[1].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS + conditions[1].matchType = cFWP_MATCH_EQUAL + conditions[1].conditionValue._type = cFWP_V6_ADDR_MASK + conditions[1].conditionValue.value = uintptr(unsafe.Pointer(&linkLocal)) + + conditions[2].fieldKey = cFWPM_CONDITION_IP_REMOTE_PORT + conditions[2].matchType = cFWP_MATCH_EQUAL + conditions[2].conditionValue._type = cFWP_UINT16 + conditions[2].conditionValue.value = uintptr(547) + + conditions[3].fieldKey = cFWPM_CONDITION_IP_LOCAL_ADDRESS + conditions[3].matchType = cFWP_MATCH_EQUAL + conditions[3].conditionValue._type = cFWP_V6_ADDR_MASK + conditions[3].conditionValue.value = uintptr(unsafe.Pointer(&linkLocal)) + + conditions[4].fieldKey = cFWPM_CONDITION_IP_LOCAL_PORT + conditions[4].matchType = cFWP_MATCH_EQUAL + conditions[4].conditionValue._type = cFWP_UINT16 + conditions[4].conditionValue.value = uintptr(546) + + displayData, err := createWtFwpmDisplayData0("Permit inbound DHCP response (IPv6)", "") + if err != nil { + return wrapErr(err) + } + + filter := wtFwpmFilter0{ + displayData: *displayData, + providerKey: &baseObjects.provider, + layerKey: cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, + subLayerKey: baseObjects.filters, + weight: filterWeight(weight), + numFilterConditions: uint32(len(conditions)), + filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&conditions)), + action: wtFwpmAction0{ + _type: cFWP_ACTION_PERMIT, + }, + } + + filterID := uint64(0) + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + return nil +} + +func permitNdp(session uintptr, baseObjects *baseObjects, weight uint8) error { + + /* TODO: actually handle the hop limit somehow! The rules should vaguely be: + * - icmpv6 133: must be outgoing, dst must be FF02::2/128, hop limit must be 255 + * - icmpv6 134: must be incoming, src must be FE80::/10, hop limit must be 255 + * - icmpv6 135: either incoming or outgoing, hop limit must be 255 + * - icmpv6 136: either incoming or outgoing, hop limit must be 255 + * - icmpv6 137: must be incoming, src must be FE80::/10, hop limit must be 255 + */ + + type filterDefinition struct { + displayData *wtFwpmDisplayData0 + conditions []wtFwpmFilterCondition0 + layer windows.GUID + } + + var defs []filterDefinition + + // + // Router Solicitation Message + // ICMP type 133, code 0. Outgoing. + // + { + conditions := make([]wtFwpmFilterCondition0, 4) + + conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL + conditions[0].matchType = cFWP_MATCH_EQUAL + conditions[0].conditionValue._type = cFWP_UINT8 + conditions[0].conditionValue.value = uintptr(cIPPROTO_ICMPV6) + + conditions[1].fieldKey = cFWPM_CONDITION_ICMP_TYPE + conditions[1].matchType = cFWP_MATCH_EQUAL + conditions[1].conditionValue._type = cFWP_UINT16 + conditions[1].conditionValue.value = uintptr(133) + + conditions[2].fieldKey = cFWPM_CONDITION_ICMP_CODE + conditions[2].matchType = cFWP_MATCH_EQUAL + conditions[2].conditionValue._type = cFWP_UINT16 + conditions[2].conditionValue.value = uintptr(0) + + conditions[3].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS + conditions[3].matchType = cFWP_MATCH_EQUAL + conditions[3].conditionValue._type = cFWP_BYTE_ARRAY16_TYPE + conditions[3].conditionValue.value = uintptr(unsafe.Pointer(&linkLocalRouterMulticast)) + + displayData, err := createWtFwpmDisplayData0("Permit NDP type 133", "") + if err != nil { + return wrapErr(err) + } + + defs = append(defs, filterDefinition{ + displayData: displayData, + conditions: conditions, + layer: cFWPM_LAYER_ALE_AUTH_CONNECT_V6, + }) + } + + // + // Router Advertisement Message + // ICMP type 134, code 0. Incoming. + // + { + conditions := make([]wtFwpmFilterCondition0, 4) + + conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL + conditions[0].matchType = cFWP_MATCH_EQUAL + conditions[0].conditionValue._type = cFWP_UINT8 + conditions[0].conditionValue.value = uintptr(cIPPROTO_ICMPV6) + + conditions[1].fieldKey = cFWPM_CONDITION_ICMP_TYPE + conditions[1].matchType = cFWP_MATCH_EQUAL + conditions[1].conditionValue._type = cFWP_UINT16 + conditions[1].conditionValue.value = uintptr(134) + + conditions[2].fieldKey = cFWPM_CONDITION_ICMP_CODE + conditions[2].matchType = cFWP_MATCH_EQUAL + conditions[2].conditionValue._type = cFWP_UINT16 + conditions[2].conditionValue.value = uintptr(0) + + conditions[3].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS + conditions[3].matchType = cFWP_MATCH_EQUAL + conditions[3].conditionValue._type = cFWP_V6_ADDR_MASK + conditions[3].conditionValue.value = uintptr(unsafe.Pointer(&linkLocal)) + + displayData, err := createWtFwpmDisplayData0("Permit NDP type 134", "") + if err != nil { + return wrapErr(err) + } + + defs = append(defs, filterDefinition{ + displayData: displayData, + conditions: conditions, + layer: cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, + }) + } + + // + // Neighbor Solicitation Message + // ICMP type 135, code 0. Bi-directional. + // + { + conditions := make([]wtFwpmFilterCondition0, 3) + + conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL + conditions[0].matchType = cFWP_MATCH_EQUAL + conditions[0].conditionValue._type = cFWP_UINT8 + conditions[0].conditionValue.value = uintptr(cIPPROTO_ICMPV6) + + conditions[1].fieldKey = cFWPM_CONDITION_ICMP_TYPE + conditions[1].matchType = cFWP_MATCH_EQUAL + conditions[1].conditionValue._type = cFWP_UINT16 + conditions[1].conditionValue.value = uintptr(135) + + conditions[2].fieldKey = cFWPM_CONDITION_ICMP_CODE + conditions[2].matchType = cFWP_MATCH_EQUAL + conditions[2].conditionValue._type = cFWP_UINT16 + conditions[2].conditionValue.value = uintptr(0) + + displayData, err := createWtFwpmDisplayData0("Permit NDP type 135", "") + if err != nil { + return wrapErr(err) + } + + defs = append(defs, filterDefinition{ + displayData: displayData, + conditions: conditions, + layer: cFWPM_LAYER_ALE_AUTH_CONNECT_V6, + }) + + defs = append(defs, filterDefinition{ + displayData: displayData, + conditions: conditions, + layer: cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, + }) + } + + // + // Neighbor Advertisement Message + // ICMP type 136, code 0. Bi-directional. + // + { + conditions := make([]wtFwpmFilterCondition0, 3) + + conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL + conditions[0].matchType = cFWP_MATCH_EQUAL + conditions[0].conditionValue._type = cFWP_UINT8 + conditions[0].conditionValue.value = uintptr(cIPPROTO_ICMPV6) + + conditions[1].fieldKey = cFWPM_CONDITION_ICMP_TYPE + conditions[1].matchType = cFWP_MATCH_EQUAL + conditions[1].conditionValue._type = cFWP_UINT16 + conditions[1].conditionValue.value = uintptr(136) + + conditions[2].fieldKey = cFWPM_CONDITION_ICMP_CODE + conditions[2].matchType = cFWP_MATCH_EQUAL + conditions[2].conditionValue._type = cFWP_UINT16 + conditions[2].conditionValue.value = uintptr(0) + + displayData, err := createWtFwpmDisplayData0("Permit NDP type 136", "") + if err != nil { + return wrapErr(err) + } + + defs = append(defs, filterDefinition{ + displayData: displayData, + conditions: conditions, + layer: cFWPM_LAYER_ALE_AUTH_CONNECT_V6, + }) + + defs = append(defs, filterDefinition{ + displayData: displayData, + conditions: conditions, + layer: cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, + }) + } + + // + // Redirect Message + // ICMP type 137, code 0. Incoming. + // + { + conditions := make([]wtFwpmFilterCondition0, 4) + + conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL + conditions[0].matchType = cFWP_MATCH_EQUAL + conditions[0].conditionValue._type = cFWP_UINT8 + conditions[0].conditionValue.value = uintptr(cIPPROTO_ICMPV6) + + conditions[1].fieldKey = cFWPM_CONDITION_ICMP_TYPE + conditions[1].matchType = cFWP_MATCH_EQUAL + conditions[1].conditionValue._type = cFWP_UINT16 + conditions[1].conditionValue.value = uintptr(137) + + conditions[2].fieldKey = cFWPM_CONDITION_ICMP_CODE + conditions[2].matchType = cFWP_MATCH_EQUAL + conditions[2].conditionValue._type = cFWP_UINT16 + conditions[2].conditionValue.value = uintptr(0) + + conditions[3].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS + conditions[3].matchType = cFWP_MATCH_EQUAL + conditions[3].conditionValue._type = cFWP_V6_ADDR_MASK + conditions[3].conditionValue.value = uintptr(unsafe.Pointer(&linkLocal)) + + displayData, err := createWtFwpmDisplayData0("Permit NDP type 137", "") + if err != nil { + return wrapErr(err) + } + + defs = append(defs, filterDefinition{ + displayData: displayData, + conditions: conditions, + layer: cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, + }) + } + + filter := wtFwpmFilter0{ + providerKey: &baseObjects.provider, + subLayerKey: baseObjects.filters, + weight: filterWeight(weight), + action: wtFwpmAction0{ + _type: cFWP_ACTION_PERMIT, + }, + } + + filterID := uint64(0) + + for _, definition := range defs { + filter.displayData = *definition.displayData + filter.layerKey = definition.layer + filter.numFilterConditions = uint32(len(definition.conditions)) + filter.filterCondition = (*wtFwpmFilterCondition0)(unsafe.Pointer(&definition.conditions[0])) + + err := fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + return nil +} + +func permitHyperV(session uintptr, baseObjects *baseObjects, weight uint8) error { + // + // Only applicable on Win8+. + // + { + v, err := version.OsVersion() + if err != nil { + panic(err) + } + + win8plus := v.MajorVersion > 6 || (v.MajorVersion == 6 && v.MinorVersion >= 3) + + if !win8plus { + return nil + } + } + + condition := wtFwpmFilterCondition0{ + fieldKey: cFWPM_CONDITION_L2_FLAGS, + matchType: cFWP_MATCH_EQUAL, + conditionValue: wtFwpConditionValue0{ + _type: cFWP_UINT32, + value: uintptr(cFWP_CONDITION_L2_IS_VM2VM), + }, + } + + filter := wtFwpmFilter0{ + providerKey: &baseObjects.provider, + subLayerKey: baseObjects.filters, + weight: filterWeight(weight), + numFilterConditions: 1, + filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&condition)), + action: wtFwpmAction0{ + _type: cFWP_ACTION_PERMIT, + }, + } + + filterID := uint64(0) + + // + // #1 Outbound. + // + { + displayData, err := createWtFwpmDisplayData0("Permit Hyper-V => Hyper-V outbound", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #2 Inbound. + // + { + displayData, err := createWtFwpmDisplayData0("Permit Hyper-V => Hyper-V inbound", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_INBOUND_MAC_FRAME_NATIVE + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + return nil +} + +// Block all traffic except what is explicitly permitted by other rules. +func blockAll(session uintptr, baseObjects *baseObjects, weight uint8) error { + filter := wtFwpmFilter0{ + providerKey: &baseObjects.provider, + subLayerKey: baseObjects.filters, + weight: filterWeight(weight), + action: wtFwpmAction0{ + _type: cFWP_ACTION_BLOCK, + }, + } + + filterID := uint64(0) + + // + // #1 Block outbound traffic on IPv4. + // + { + displayData, err := createWtFwpmDisplayData0("Block all outbound (IPv4)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #2 Block inbound traffic on IPv4. + // + { + displayData, err := createWtFwpmDisplayData0("Block all inbound (IPv4)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #3 Block outbound traffic on IPv6. + // + { + displayData, err := createWtFwpmDisplayData0("Block all outbound (IPv6)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #4 Block inbound traffic on IPv6. + // + { + displayData, err := createWtFwpmDisplayData0("Block all inbound (IPv6)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + return nil +} + +// Block all DNS traffic except towards specified DNS servers. +func blockDNS(except []net.IP, session uintptr, baseObjects *baseObjects, weightAllow uint8, weightDeny uint8) error { + if weightDeny >= weightAllow { + return errors.New("The allow weight must be greater than the deny weight") + } + + denyConditions := []wtFwpmFilterCondition0{ + { + fieldKey: cFWPM_CONDITION_IP_REMOTE_PORT, + matchType: cFWP_MATCH_EQUAL, + conditionValue: wtFwpConditionValue0{ + _type: cFWP_UINT16, + value: uintptr(53), + }, + }, + { + fieldKey: cFWPM_CONDITION_IP_PROTOCOL, + matchType: cFWP_MATCH_EQUAL, + conditionValue: wtFwpConditionValue0{ + _type: cFWP_UINT8, + value: uintptr(cIPPROTO_UDP), + }, + }, + // Repeat the condition type for logical OR. + { + fieldKey: cFWPM_CONDITION_IP_PROTOCOL, + matchType: cFWP_MATCH_EQUAL, + conditionValue: wtFwpConditionValue0{ + _type: cFWP_UINT8, + value: uintptr(cIPPROTO_TCP), + }, + }, + } + + filter := wtFwpmFilter0{ + providerKey: &baseObjects.provider, + subLayerKey: baseObjects.filters, + weight: filterWeight(weightDeny), + numFilterConditions: uint32(len(denyConditions)), + filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&denyConditions[0])), + action: wtFwpmAction0{ + _type: cFWP_ACTION_BLOCK, + }, + } + + filterID := uint64(0) + + // + // #1 Block IPv4 outbound DNS. + // + { + displayData, err := createWtFwpmDisplayData0("Block DNS outbound (IPv4)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #2 Block IPv4 inbound DNS. + // + { + displayData, err := createWtFwpmDisplayData0("Block DNS inbound (IPv4)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #3 Block IPv6 outbound DNS. + // + { + displayData, err := createWtFwpmDisplayData0("Block DNS outbound (IPv6)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #4 Block IPv6 inbound DNS. + // + { + displayData, err := createWtFwpmDisplayData0("Block DNS inbound (IPv6)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + allowConditionsV4 := make([]wtFwpmFilterCondition0, 0, len(denyConditions)+len(except)) + allowConditionsV4 = append(allowConditionsV4, denyConditions...) + for _, ip := range except { + ip4 := ip.To4() + if ip4 == nil { + continue + } + allowConditionsV4 = append(allowConditionsV4, wtFwpmFilterCondition0{ + fieldKey: cFWPM_CONDITION_IP_REMOTE_ADDRESS, + matchType: cFWP_MATCH_EQUAL, + conditionValue: wtFwpConditionValue0{ + _type: cFWP_UINT32, + value: uintptr(binary.BigEndian.Uint32(ip4)), + }, + }) + } + + storedPointers := make([]*wtFwpByteArray16, 0, len(except)) + allowConditionsV6 := make([]wtFwpmFilterCondition0, 0, len(denyConditions)+len(except)) + allowConditionsV6 = append(allowConditionsV6, denyConditions...) + for _, ip := range except { + if ip.To4() != nil { + continue + } + var address wtFwpByteArray16 + copy(address.byteArray16[:], ip) + allowConditionsV6 = append(allowConditionsV6, wtFwpmFilterCondition0{ + fieldKey: cFWPM_CONDITION_IP_REMOTE_ADDRESS, + matchType: cFWP_MATCH_EQUAL, + conditionValue: wtFwpConditionValue0{ + _type: cFWP_BYTE_ARRAY16_TYPE, + value: uintptr(unsafe.Pointer(&address)), + }, + }) + storedPointers = append(storedPointers, &address) + } + + filter = wtFwpmFilter0{ + providerKey: &baseObjects.provider, + subLayerKey: baseObjects.filters, + weight: filterWeight(weightAllow), + numFilterConditions: uint32(len(allowConditionsV4)), + filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&allowConditionsV4[0])), + action: wtFwpmAction0{ + _type: cFWP_ACTION_PERMIT, + }, + } + + filterID = uint64(0) + + // + // #5 Allow IPv4 outbound DNS. + // + if len(allowConditionsV4) > len(denyConditions) { + displayData, err := createWtFwpmDisplayData0("Allow DNS outbound (IPv4)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #6 Allow IPv4 inbound DNS. + // + if len(allowConditionsV4) > len(denyConditions) { + displayData, err := createWtFwpmDisplayData0("Allow DNS inbound (IPv4)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + filter.filterCondition = (*wtFwpmFilterCondition0)(unsafe.Pointer(&allowConditionsV6[0])) + filter.numFilterConditions = uint32(len(allowConditionsV6)) + + // + // #7 Allow IPv6 outbound DNS. + // + if len(allowConditionsV6) > len(denyConditions) { + displayData, err := createWtFwpmDisplayData0("Allow DNS outbound (IPv6)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + // + // #8 Allow IPv6 inbound DNS. + // + if len(allowConditionsV6) > len(denyConditions) { + displayData, err := createWtFwpmDisplayData0("Allow DNS inbound (IPv6)", "") + if err != nil { + return wrapErr(err) + } + + filter.displayData = *displayData + filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 + + err = fwpmFilterAdd0(session, &filter, 0, &filterID) + if err != nil { + return wrapErr(err) + } + } + + runtime.KeepAlive(storedPointers) + + return nil +} diff --git a/tunnel/firewall/syscall_windows.go b/tunnel/firewall/syscall_windows.go new file mode 100644 index 00000000..5ec41b0b --- /dev/null +++ b/tunnel/firewall/syscall_windows.go @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package firewall + +// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmengineopen0 +//sys fwpmEngineOpen0(serverName *uint16, authnService wtRpcCAuthN, authIdentity *uintptr, session *wtFwpmSession0, engineHandle unsafe.Pointer) (err error) [failretval!=0] = fwpuclnt.FwpmEngineOpen0 + +// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmengineclose0 +//sys fwpmEngineClose0(engineHandle uintptr) (err error) [failretval!=0] = fwpuclnt.FwpmEngineClose0 + +// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmsublayeradd0 +//sys fwpmSubLayerAdd0(engineHandle uintptr, subLayer *wtFwpmSublayer0, sd uintptr) (err error) [failretval!=0] = fwpuclnt.FwpmSubLayerAdd0 + +// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmgetappidfromfilename0 +//sys fwpmGetAppIdFromFileName0(fileName *uint16, appID unsafe.Pointer) (err error) [failretval!=0] = fwpuclnt.FwpmGetAppIdFromFileName0 + +// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmfreememory0 +//sys fwpmFreeMemory0(p unsafe.Pointer) = fwpuclnt.FwpmFreeMemory0 + +// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmfilteradd0 +//sys fwpmFilterAdd0(engineHandle uintptr, filter *wtFwpmFilter0, sd uintptr, id *uint64) (err error) [failretval!=0] = fwpuclnt.FwpmFilterAdd0 + +// https://docs.microsoft.com/en-us/windows/desktop/api/Fwpmu/nf-fwpmu-fwpmtransactionbegin0 +//sys fwpmTransactionBegin0(engineHandle uintptr, flags uint32) (err error) [failretval!=0] = fwpuclnt.FwpmTransactionBegin0 + +// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmtransactioncommit0 +//sys fwpmTransactionCommit0(engineHandle uintptr) (err error) [failretval!=0] = fwpuclnt.FwpmTransactionCommit0 + +// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmtransactionabort0 +//sys fwpmTransactionAbort0(engineHandle uintptr) (err error) [failretval!=0] = fwpuclnt.FwpmTransactionAbort0 + +// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmprovideradd0 +//sys fwpmProviderAdd0(engineHandle uintptr, provider *wtFwpmProvider0, sd uintptr) (err error) [failretval!=0] = fwpuclnt.FwpmProviderAdd0 + +// https://docs.microsoft.com/sv-se/windows/desktop/api/aclapi/nf-aclapi-getsecurityinfo +//sys getSecurityInfo(handle windows.Handle, objectType wtObjectType, si uint32, sidOwner *windows.SID, sidGroup *windows.SID, dacl *uintptr, sacl *uintptr, securityDescriptor *uintptr) (err error) [failretval!=0] = advapi32.GetSecurityInfo + +// https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-getsecuritydescriptorlength +//sys getSecurityDescriptorLength(securityDescriptor uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength diff --git a/tunnel/firewall/types_windows.go b/tunnel/firewall/types_windows.go new file mode 100644 index 00000000..e06f7d2b --- /dev/null +++ b/tunnel/firewall/types_windows.go @@ -0,0 +1,417 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package firewall + +import "golang.org/x/sys/windows" + +const ( + anysizeArray = 1 // ANYSIZE_ARRAY defined in winnt.h + + wtFwpBitmapArray64_Size = 8 + + wtFwpByteArray16_Size = 16 + + wtFwpByteArray6_Size = 6 + + wtFwpmAction0_Size = 20 + wtFwpmAction0_filterType_Offset = 4 + + wtFwpV4AddrAndMask_Size = 8 + wtFwpV4AddrAndMask_mask_Offset = 4 + + wtFwpV6AddrAndMask_Size = 17 + wtFwpV6AddrAndMask_prefixLength_Offset = 16 +) + +type wtFwpActionFlag uint32 + +const ( + cFWP_ACTION_FLAG_TERMINATING wtFwpActionFlag = 0x00001000 + cFWP_ACTION_FLAG_NON_TERMINATING wtFwpActionFlag = 0x00002000 + cFWP_ACTION_FLAG_CALLOUT wtFwpActionFlag = 0x00004000 +) + +// FWP_ACTION_TYPE defined in fwptypes.h +type wtFwpActionType uint32 + +const ( + cFWP_ACTION_BLOCK wtFwpActionType = wtFwpActionType(0x00000001 | cFWP_ACTION_FLAG_TERMINATING) + cFWP_ACTION_PERMIT wtFwpActionType = wtFwpActionType(0x00000002 | cFWP_ACTION_FLAG_TERMINATING) + cFWP_ACTION_CALLOUT_TERMINATING wtFwpActionType = wtFwpActionType(0x00000003 | cFWP_ACTION_FLAG_CALLOUT | cFWP_ACTION_FLAG_TERMINATING) + cFWP_ACTION_CALLOUT_INSPECTION wtFwpActionType = wtFwpActionType(0x00000004 | cFWP_ACTION_FLAG_CALLOUT | cFWP_ACTION_FLAG_NON_TERMINATING) + cFWP_ACTION_CALLOUT_UNKNOWN wtFwpActionType = wtFwpActionType(0x00000005 | cFWP_ACTION_FLAG_CALLOUT) + cFWP_ACTION_CONTINUE wtFwpActionType = wtFwpActionType(0x00000006 | cFWP_ACTION_FLAG_NON_TERMINATING) + cFWP_ACTION_NONE wtFwpActionType = 0x00000007 + cFWP_ACTION_NONE_NO_MATCH wtFwpActionType = 0x00000008 + cFWP_ACTION_BITMAP_INDEX_SET wtFwpActionType = 0x00000009 +) + +// FWP_BYTE_BLOB defined in fwptypes.h +// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_byte_blob_) +type wtFwpByteBlob struct { + size uint32 + data *uint8 +} + +// FWP_MATCH_TYPE defined in fwptypes.h +// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ne-fwptypes-fwp_match_type_) +type wtFwpMatchType uint32 + +const ( + cFWP_MATCH_EQUAL wtFwpMatchType = 0 + cFWP_MATCH_GREATER wtFwpMatchType = cFWP_MATCH_EQUAL + 1 + cFWP_MATCH_LESS wtFwpMatchType = cFWP_MATCH_GREATER + 1 + cFWP_MATCH_GREATER_OR_EQUAL wtFwpMatchType = cFWP_MATCH_LESS + 1 + cFWP_MATCH_LESS_OR_EQUAL wtFwpMatchType = cFWP_MATCH_GREATER_OR_EQUAL + 1 + cFWP_MATCH_RANGE wtFwpMatchType = cFWP_MATCH_LESS_OR_EQUAL + 1 + cFWP_MATCH_FLAGS_ALL_SET wtFwpMatchType = cFWP_MATCH_RANGE + 1 + cFWP_MATCH_FLAGS_ANY_SET wtFwpMatchType = cFWP_MATCH_FLAGS_ALL_SET + 1 + cFWP_MATCH_FLAGS_NONE_SET wtFwpMatchType = cFWP_MATCH_FLAGS_ANY_SET + 1 + cFWP_MATCH_EQUAL_CASE_INSENSITIVE wtFwpMatchType = cFWP_MATCH_FLAGS_NONE_SET + 1 + cFWP_MATCH_NOT_EQUAL wtFwpMatchType = cFWP_MATCH_EQUAL_CASE_INSENSITIVE + 1 + cFWP_MATCH_PREFIX wtFwpMatchType = cFWP_MATCH_NOT_EQUAL + 1 + cFWP_MATCH_NOT_PREFIX wtFwpMatchType = cFWP_MATCH_PREFIX + 1 + cFWP_MATCH_TYPE_MAX wtFwpMatchType = cFWP_MATCH_NOT_PREFIX + 1 +) + +// FWPM_ACTION0 defined in fwpmtypes.h +// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_action0_) +type wtFwpmAction0 struct { + _type wtFwpActionType + filterType windows.GUID // Windows type: GUID +} + +// Defined in fwpmu.h. 4cd62a49-59c3-4969-b7f3-bda5d32890a4 +var cFWPM_CONDITION_IP_LOCAL_INTERFACE = windows.GUID{ + Data1: 0x4cd62a49, + Data2: 0x59c3, + Data3: 0x4969, + Data4: [8]byte{0xb7, 0xf3, 0xbd, 0xa5, 0xd3, 0x28, 0x90, 0xa4}, +} + +// Defined in fwpmu.h. b235ae9a-1d64-49b8-a44c-5ff3d9095045 +var cFWPM_CONDITION_IP_REMOTE_ADDRESS = windows.GUID{ + Data1: 0xb235ae9a, + Data2: 0x1d64, + Data3: 0x49b8, + Data4: [8]byte{0xa4, 0x4c, 0x5f, 0xf3, 0xd9, 0x09, 0x50, 0x45}, +} + +// Defined in fwpmu.h. daf8cd14-e09e-4c93-a5ae-c5c13b73ffca +var cFWPM_CONDITION_INTERFACE_TYPE = windows.GUID{ + Data1: 0xdaf8cd14, + Data2: 0xe09e, + Data3: 0x4c93, + Data4: [8]byte{0xa5, 0xae, 0xc5, 0xc1, 0x3b, 0x73, 0xff, 0xca}, +} + +// Defined in fwpmu.h. 3971ef2b-623e-4f9a-8cb1-6e79b806b9a7 +var cFWPM_CONDITION_IP_PROTOCOL = windows.GUID{ + Data1: 0x3971ef2b, + Data2: 0x623e, + Data3: 0x4f9a, + Data4: [8]byte{0x8c, 0xb1, 0x6e, 0x79, 0xb8, 0x06, 0xb9, 0xa7}, +} + +// Defined in fwpmu.h. 0c1ba1af-5765-453f-af22-a8f791ac775b +var cFWPM_CONDITION_IP_LOCAL_PORT = windows.GUID{ + Data1: 0x0c1ba1af, + Data2: 0x5765, + Data3: 0x453f, + Data4: [8]byte{0xaf, 0x22, 0xa8, 0xf7, 0x91, 0xac, 0x77, 0x5b}, +} + +// Defined in fwpmu.h. c35a604d-d22b-4e1a-91b4-68f674ee674b +var cFWPM_CONDITION_IP_REMOTE_PORT = windows.GUID{ + Data1: 0xc35a604d, + Data2: 0xd22b, + Data3: 0x4e1a, + Data4: [8]byte{0x91, 0xb4, 0x68, 0xf6, 0x74, 0xee, 0x67, 0x4b}, +} + +// Defined in fwpmu.h. d78e1e87-8644-4ea5-9437-d809ecefc971 +var cFWPM_CONDITION_ALE_APP_ID = windows.GUID{ + Data1: 0xd78e1e87, + Data2: 0x8644, + Data3: 0x4ea5, + Data4: [8]byte{0x94, 0x37, 0xd8, 0x09, 0xec, 0xef, 0xc9, 0x71}, +} + +// af043a0a-b34d-4f86-979c-c90371af6e66 +var cFWPM_CONDITION_ALE_USER_ID = windows.GUID{ + Data1: 0xaf043a0a, + Data2: 0xb34d, + Data3: 0x4f86, + Data4: [8]byte{0x97, 0x9c, 0xc9, 0x03, 0x71, 0xaf, 0x6e, 0x66}, +} + +// d9ee00de-c1ef-4617-bfe3-ffd8f5a08957 +var cFWPM_CONDITION_IP_LOCAL_ADDRESS = windows.GUID{ + Data1: 0xd9ee00de, + Data2: 0xc1ef, + Data3: 0x4617, + Data4: [8]byte{0xbf, 0xe3, 0xff, 0xd8, 0xf5, 0xa0, 0x89, 0x57}, +} + +var cFWPM_CONDITION_ICMP_TYPE = cFWPM_CONDITION_IP_LOCAL_PORT +var cFWPM_CONDITION_ICMP_CODE = cFWPM_CONDITION_IP_REMOTE_PORT + +// 7bc43cbf-37ba-45f1-b74a-82ff518eeb10 +var cFWPM_CONDITION_L2_FLAGS = windows.GUID{ + Data1: 0x7bc43cbf, + Data2: 0x37ba, + Data3: 0x45f1, + Data4: [8]byte{0xb7, 0x4a, 0x82, 0xff, 0x51, 0x8e, 0xeb, 0x10}, +} + +type wtFwpmL2Flags uint32 + +const cFWP_CONDITION_L2_IS_VM2VM wtFwpmL2Flags = 0x00000010 + +// Defined in fwpmtypes.h +type wtFwpmFilterFlags uint32 + +const ( + cFWPM_FILTER_FLAG_NONE wtFwpmFilterFlags = 0x00000000 + cFWPM_FILTER_FLAG_PERSISTENT wtFwpmFilterFlags = 0x00000001 + cFWPM_FILTER_FLAG_BOOTTIME wtFwpmFilterFlags = 0x00000002 + cFWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT wtFwpmFilterFlags = 0x00000004 + cFWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT wtFwpmFilterFlags = 0x00000008 + cFWPM_FILTER_FLAG_PERMIT_IF_CALLOUT_UNREGISTERED wtFwpmFilterFlags = 0x00000010 + cFWPM_FILTER_FLAG_DISABLED wtFwpmFilterFlags = 0x00000020 + cFWPM_FILTER_FLAG_INDEXED wtFwpmFilterFlags = 0x00000040 + cFWPM_FILTER_FLAG_HAS_SECURITY_REALM_PROVIDER_CONTEXT wtFwpmFilterFlags = 0x00000080 + cFWPM_FILTER_FLAG_SYSTEMOS_ONLY wtFwpmFilterFlags = 0x00000100 + cFWPM_FILTER_FLAG_GAMEOS_ONLY wtFwpmFilterFlags = 0x00000200 + cFWPM_FILTER_FLAG_SILENT_MODE wtFwpmFilterFlags = 0x00000400 + cFWPM_FILTER_FLAG_IPSEC_NO_ACQUIRE_INITIATE wtFwpmFilterFlags = 0x00000800 +) + +// FWPM_LAYER_ALE_AUTH_CONNECT_V4 (c38d57d1-05a7-4c33-904f-7fbceee60e82) defined in fwpmu.h +var cFWPM_LAYER_ALE_AUTH_CONNECT_V4 = windows.GUID{ + Data1: 0xc38d57d1, + Data2: 0x05a7, + Data3: 0x4c33, + Data4: [8]byte{0x90, 0x4f, 0x7f, 0xbc, 0xee, 0xe6, 0x0e, 0x82}, +} + +// e1cd9fe7-f4b5-4273-96c0-592e487b8650 +var cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 = windows.GUID{ + Data1: 0xe1cd9fe7, + Data2: 0xf4b5, + Data3: 0x4273, + Data4: [8]byte{0x96, 0xc0, 0x59, 0x2e, 0x48, 0x7b, 0x86, 0x50}, +} + +// FWPM_LAYER_ALE_AUTH_CONNECT_V6 (4a72393b-319f-44bc-84c3-ba54dcb3b6b4) defined in fwpmu.h +var cFWPM_LAYER_ALE_AUTH_CONNECT_V6 = windows.GUID{ + Data1: 0x4a72393b, + Data2: 0x319f, + Data3: 0x44bc, + Data4: [8]byte{0x84, 0xc3, 0xba, 0x54, 0xdc, 0xb3, 0xb6, 0xb4}, +} + +// a3b42c97-9f04-4672-b87e-cee9c483257f +var cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 = windows.GUID{ + Data1: 0xa3b42c97, + Data2: 0x9f04, + Data3: 0x4672, + Data4: [8]byte{0xb8, 0x7e, 0xce, 0xe9, 0xc4, 0x83, 0x25, 0x7f}, +} + +// 94c44912-9d6f-4ebf-b995-05ab8a088d1b +var cFWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE = windows.GUID{ + Data1: 0x94c44912, + Data2: 0x9d6f, + Data3: 0x4ebf, + Data4: [8]byte{0xb9, 0x95, 0x05, 0xab, 0x8a, 0x08, 0x8d, 0x1b}, +} + +// d4220bd3-62ce-4f08-ae88-b56e8526df50 +var cFWPM_LAYER_INBOUND_MAC_FRAME_NATIVE = windows.GUID{ + Data1: 0xd4220bd3, + Data2: 0x62ce, + Data3: 0x4f08, + Data4: [8]byte{0xae, 0x88, 0xb5, 0x6e, 0x85, 0x26, 0xdf, 0x50}, +} + +// FWP_BITMAP_ARRAY64 defined in fwtypes.h +type wtFwpBitmapArray64 struct { + bitmapArray64 [8]uint8 // Windows type: [8]UINT8 +} + +// FWP_BYTE_ARRAY6 defined in fwtypes.h +// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_byte_array6_) +type wtFwpByteArray6 struct { + byteArray6 [6]uint8 // Windows type: [6]UINT8 +} + +// FWP_BYTE_ARRAY16 defined in fwptypes.h +// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_byte_array16_) +type wtFwpByteArray16 struct { + byteArray16 [16]uint8 // Windows type [16]UINT8 +} + +// FWP_CONDITION_VALUE0 defined in fwptypes.h +// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_condition_value0_). +type wtFwpConditionValue0 wtFwpValue0 + +// FWP_DATA_TYPE defined in fwptypes.h +// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ne-fwptypes-fwp_data_type_) +type wtFwpDataType uint + +const ( + cFWP_EMPTY wtFwpDataType = 0 + cFWP_UINT8 wtFwpDataType = cFWP_EMPTY + 1 + cFWP_UINT16 wtFwpDataType = cFWP_UINT8 + 1 + cFWP_UINT32 wtFwpDataType = cFWP_UINT16 + 1 + cFWP_UINT64 wtFwpDataType = cFWP_UINT32 + 1 + cFWP_INT8 wtFwpDataType = cFWP_UINT64 + 1 + cFWP_INT16 wtFwpDataType = cFWP_INT8 + 1 + cFWP_INT32 wtFwpDataType = cFWP_INT16 + 1 + cFWP_INT64 wtFwpDataType = cFWP_INT32 + 1 + cFWP_FLOAT wtFwpDataType = cFWP_INT64 + 1 + cFWP_DOUBLE wtFwpDataType = cFWP_FLOAT + 1 + cFWP_BYTE_ARRAY16_TYPE wtFwpDataType = cFWP_DOUBLE + 1 + cFWP_BYTE_BLOB_TYPE wtFwpDataType = cFWP_BYTE_ARRAY16_TYPE + 1 + cFWP_SID wtFwpDataType = cFWP_BYTE_BLOB_TYPE + 1 + cFWP_SECURITY_DESCRIPTOR_TYPE wtFwpDataType = cFWP_SID + 1 + cFWP_TOKEN_INFORMATION_TYPE wtFwpDataType = cFWP_SECURITY_DESCRIPTOR_TYPE + 1 + cFWP_TOKEN_ACCESS_INFORMATION_TYPE wtFwpDataType = cFWP_TOKEN_INFORMATION_TYPE + 1 + cFWP_UNICODE_STRING_TYPE wtFwpDataType = cFWP_TOKEN_ACCESS_INFORMATION_TYPE + 1 + cFWP_BYTE_ARRAY6_TYPE wtFwpDataType = cFWP_UNICODE_STRING_TYPE + 1 + cFWP_BITMAP_INDEX_TYPE wtFwpDataType = cFWP_BYTE_ARRAY6_TYPE + 1 + cFWP_BITMAP_ARRAY64_TYPE wtFwpDataType = cFWP_BITMAP_INDEX_TYPE + 1 + cFWP_SINGLE_DATA_TYPE_MAX wtFwpDataType = 0xff + cFWP_V4_ADDR_MASK wtFwpDataType = cFWP_SINGLE_DATA_TYPE_MAX + 1 + cFWP_V6_ADDR_MASK wtFwpDataType = cFWP_V4_ADDR_MASK + 1 + cFWP_RANGE_TYPE wtFwpDataType = cFWP_V6_ADDR_MASK + 1 + cFWP_DATA_TYPE_MAX wtFwpDataType = cFWP_RANGE_TYPE + 1 +) + +// FWP_V4_ADDR_AND_MASK defined in fwptypes.h +// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_v4_addr_and_mask_). +type wtFwpV4AddrAndMask struct { + addr uint32 + mask uint32 +} + +// FWP_V6_ADDR_AND_MASK defined in fwptypes.h +// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_v6_addr_and_mask_). +type wtFwpV6AddrAndMask struct { + addr [16]uint8 + prefixLength uint8 +} + +// FWP_VALUE0 defined in fwptypes.h +// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_value0_) +type wtFwpValue0 struct { + _type wtFwpDataType + value uintptr +} + +// FWPM_DISPLAY_DATA0 defined in fwptypes.h +// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwpm_display_data0_). +type wtFwpmDisplayData0 struct { + name *uint16 // Windows type: *wchar_t + description *uint16 // Windows type: *wchar_t +} + +// FWPM_FILTER_CONDITION0 defined in fwpmtypes.h +// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_filter_condition0_). +type wtFwpmFilterCondition0 struct { + fieldKey windows.GUID // Windows type: GUID + matchType wtFwpMatchType + conditionValue wtFwpConditionValue0 +} + +// FWPM_PROVIDER0 defined in fwpmtypes.h +// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_provider0_) +type wtFwpProvider0 struct { + providerKey windows.GUID // Windows type: GUID + displayData wtFwpmDisplayData0 + flags uint32 + providerData wtFwpByteBlob + serviceName *uint16 // Windows type: *wchar_t +} + +type wtFwpmSessionFlagsValue uint32 + +const ( + cFWPM_SESSION_FLAG_DYNAMIC wtFwpmSessionFlagsValue = 0x00000001 // FWPM_SESSION_FLAG_DYNAMIC defined in fwpmtypes.h +) + +// FWPM_SESSION0 defined in fwpmtypes.h +// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_session0_). +type wtFwpmSession0 struct { + sessionKey windows.GUID // Windows type: GUID + displayData wtFwpmDisplayData0 + flags wtFwpmSessionFlagsValue // Windows type UINT32 + txnWaitTimeoutInMSec uint32 + processId uint32 // Windows type: DWORD + sid *windows.SID + username *uint16 // Windows type: *wchar_t + kernelMode uint8 // Windows type: BOOL +} + +type wtFwpmSublayerFlags uint32 + +const ( + cFWPM_SUBLAYER_FLAG_PERSISTENT wtFwpmSublayerFlags = 0x00000001 // FWPM_SUBLAYER_FLAG_PERSISTENT defined in fwpmtypes.h +) + +// FWPM_SUBLAYER0 defined in fwpmtypes.h +// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_sublayer0_) +type wtFwpmSublayer0 struct { + subLayerKey windows.GUID // Windows type: GUID + displayData wtFwpmDisplayData0 + flags wtFwpmSublayerFlags + providerKey *windows.GUID // Windows type: *GUID + providerData wtFwpByteBlob + weight uint16 +} + +// Defined in rpcdce.h +type wtRpcCAuthN uint32 + +const ( + cRPC_C_AUTHN_NONE wtRpcCAuthN = 0 + cRPC_C_AUTHN_WINNT wtRpcCAuthN = 10 + cRPC_C_AUTHN_DEFAULT wtRpcCAuthN = 0xFFFFFFFF +) + +// FWPM_PROVIDER0 defined in fwpmtypes.h +// (https://docs.microsoft.com/sv-se/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_provider0_). +type wtFwpmProvider0 struct { + providerKey windows.GUID + displayData wtFwpmDisplayData0 + flags uint32 + providerData wtFwpByteBlob + serviceName *uint16 +} + +type wtObjectType uint32 + +const ( + cSE_KERNEL_OBJECT wtObjectType = 6 + + cDACL_SECURITY_INFORMATION = 4 +) + +type wtIfType uint32 + +const ( + cIF_TYPE_SOFTWARE_LOOPBACK wtIfType = 24 +) + +type wtIPProto uint32 + +const ( + cIPPROTO_ICMP wtIPProto = 1 + cIPPROTO_ICMPV6 wtIPProto = 58 + cIPPROTO_TCP wtIPProto = 6 + cIPPROTO_UDP wtIPProto = 17 +) diff --git a/tunnel/firewall/types_windows_386.go b/tunnel/firewall/types_windows_386.go new file mode 100644 index 00000000..00d7ba8f --- /dev/null +++ b/tunnel/firewall/types_windows_386.go @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package firewall + +import "golang.org/x/sys/windows" + +const ( + wtFwpByteBlob_Size = 8 + wtFwpByteBlob_data_Offset = 4 + + wtFwpConditionValue0_Size = 8 + wtFwpConditionValue0_uint8_Offset = 4 + + wtFwpmDisplayData0_Size = 8 + wtFwpmDisplayData0_description_Offset = 4 + + wtFwpmFilter0_Size = 152 + wtFwpmFilter0_displayData_Offset = 16 + wtFwpmFilter0_flags_Offset = 24 + wtFwpmFilter0_providerKey_Offset = 28 + wtFwpmFilter0_providerData_Offset = 32 + wtFwpmFilter0_layerKey_Offset = 40 + wtFwpmFilter0_subLayerKey_Offset = 56 + wtFwpmFilter0_weight_Offset = 72 + wtFwpmFilter0_numFilterConditions_Offset = 80 + wtFwpmFilter0_filterCondition_Offset = 84 + wtFwpmFilter0_action_Offset = 88 + wtFwpmFilter0_providerContextKey_Offset = 112 + wtFwpmFilter0_reserved_Offset = 128 + wtFwpmFilter0_filterID_Offset = 136 + wtFwpmFilter0_effectiveWeight_Offset = 144 + + wtFwpmFilterCondition0_Size = 28 + wtFwpmFilterCondition0_matchType_Offset = 16 + wtFwpmFilterCondition0_conditionValue_Offset = 20 + + wtFwpmSession0_Size = 48 + wtFwpmSession0_displayData_Offset = 16 + wtFwpmSession0_flags_Offset = 24 + wtFwpmSession0_txnWaitTimeoutInMSec_Offset = 28 + wtFwpmSession0_processId_Offset = 32 + wtFwpmSession0_sid_Offset = 36 + wtFwpmSession0_username_Offset = 40 + wtFwpmSession0_kernelMode_Offset = 44 + + wtFwpmSublayer0_Size = 44 + wtFwpmSublayer0_displayData_Offset = 16 + wtFwpmSublayer0_flags_Offset = 24 + wtFwpmSublayer0_providerKey_Offset = 28 + wtFwpmSublayer0_providerData_Offset = 32 + wtFwpmSublayer0_weight_Offset = 40 + + wtFwpProvider0_Size = 40 + wtFwpProvider0_displayData_Offset = 16 + wtFwpProvider0_flags_Offset = 24 + wtFwpProvider0_providerData_Offset = 28 + wtFwpProvider0_serviceName_Offset = 36 + + wtFwpTokenInformation_Size = 16 + + wtFwpValue0_Size = 8 + wtFwpValue0_value_Offset = 4 +) + +// FWPM_FILTER0 defined in fwpmtypes.h +// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_filter0_). +type wtFwpmFilter0 struct { + filterKey windows.GUID // Windows type: GUID + displayData wtFwpmDisplayData0 + flags wtFwpmFilterFlags + providerKey *windows.GUID // Windows type: *GUID + providerData wtFwpByteBlob + layerKey windows.GUID // Windows type: GUID + subLayerKey windows.GUID // Windows type: GUID + weight wtFwpValue0 + numFilterConditions uint32 + filterCondition *wtFwpmFilterCondition0 + action wtFwpmAction0 + offset1 [4]byte // Layout correction field + providerContextKey windows.GUID // Windows type: GUID + reserved *windows.GUID // Windows type: *GUID + offset2 [4]byte // Layout correction field + filterID uint64 + effectiveWeight wtFwpValue0 +} diff --git a/tunnel/firewall/types_windows_amd64.go b/tunnel/firewall/types_windows_amd64.go new file mode 100644 index 00000000..f0aa557c --- /dev/null +++ b/tunnel/firewall/types_windows_amd64.go @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package firewall + +import "golang.org/x/sys/windows" + +const ( + wtFwpByteBlob_Size = 16 + wtFwpByteBlob_data_Offset = 8 + + wtFwpConditionValue0_Size = 16 + wtFwpConditionValue0_uint8_Offset = 8 + + wtFwpmDisplayData0_Size = 16 + wtFwpmDisplayData0_description_Offset = 8 + + wtFwpmFilter0_Size = 200 + wtFwpmFilter0_displayData_Offset = 16 + wtFwpmFilter0_flags_Offset = 32 + wtFwpmFilter0_providerKey_Offset = 40 + wtFwpmFilter0_providerData_Offset = 48 + wtFwpmFilter0_layerKey_Offset = 64 + wtFwpmFilter0_subLayerKey_Offset = 80 + wtFwpmFilter0_weight_Offset = 96 + wtFwpmFilter0_numFilterConditions_Offset = 112 + wtFwpmFilter0_filterCondition_Offset = 120 + wtFwpmFilter0_action_Offset = 128 + wtFwpmFilter0_providerContextKey_Offset = 152 + wtFwpmFilter0_reserved_Offset = 168 + wtFwpmFilter0_filterID_Offset = 176 + wtFwpmFilter0_effectiveWeight_Offset = 184 + + wtFwpmFilterCondition0_Size = 40 + wtFwpmFilterCondition0_matchType_Offset = 16 + wtFwpmFilterCondition0_conditionValue_Offset = 24 + + wtFwpmSession0_Size = 72 + wtFwpmSession0_displayData_Offset = 16 + wtFwpmSession0_flags_Offset = 32 + wtFwpmSession0_txnWaitTimeoutInMSec_Offset = 36 + wtFwpmSession0_processId_Offset = 40 + wtFwpmSession0_sid_Offset = 48 + wtFwpmSession0_username_Offset = 56 + wtFwpmSession0_kernelMode_Offset = 64 + + wtFwpmSublayer0_Size = 72 + wtFwpmSublayer0_displayData_Offset = 16 + wtFwpmSublayer0_flags_Offset = 32 + wtFwpmSublayer0_providerKey_Offset = 40 + wtFwpmSublayer0_providerData_Offset = 48 + wtFwpmSublayer0_weight_Offset = 64 + + wtFwpProvider0_Size = 64 + wtFwpProvider0_displayData_Offset = 16 + wtFwpProvider0_flags_Offset = 32 + wtFwpProvider0_providerData_Offset = 40 + wtFwpProvider0_serviceName_Offset = 56 + + wtFwpValue0_Size = 16 + wtFwpValue0_value_Offset = 8 +) + +// FWPM_FILTER0 defined in fwpmtypes.h +// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_filter0_). +type wtFwpmFilter0 struct { + filterKey windows.GUID // Windows type: GUID + displayData wtFwpmDisplayData0 + flags wtFwpmFilterFlags // Windows type: UINT32 + providerKey *windows.GUID // Windows type: *GUID + providerData wtFwpByteBlob + layerKey windows.GUID // Windows type: GUID + subLayerKey windows.GUID // Windows type: GUID + weight wtFwpValue0 + numFilterConditions uint32 + filterCondition *wtFwpmFilterCondition0 + action wtFwpmAction0 + offset1 [4]byte // Layout correction field + providerContextKey windows.GUID // Windows type: GUID + reserved *windows.GUID // Windows type: *GUID + filterID uint64 + effectiveWeight wtFwpValue0 +} diff --git a/tunnel/firewall/types_windows_test.go b/tunnel/firewall/types_windows_test.go new file mode 100644 index 00000000..97cb032c --- /dev/null +++ b/tunnel/firewall/types_windows_test.go @@ -0,0 +1,538 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package firewall + +import ( + "testing" + "unsafe" +) + +func TestWtFwpByteBlobSize(t *testing.T) { + + const actualWtFwpByteBlobSize = unsafe.Sizeof(wtFwpByteBlob{}) + + if actualWtFwpByteBlobSize != wtFwpByteBlob_Size { + t.Errorf("Size of FwpByteBlob is %d, although %d is expected.", actualWtFwpByteBlobSize, + wtFwpByteBlob_Size) + } +} + +func TestWtFwpByteBlobOffsets(t *testing.T) { + + s := wtFwpByteBlob{} + sp := uintptr(unsafe.Pointer(&s)) + + offset := uintptr(unsafe.Pointer(&s.data)) - sp + + if offset != wtFwpByteBlob_data_Offset { + t.Errorf("FwpByteBlob.data offset is %d although %d is expected", offset, wtFwpByteBlob_data_Offset) + return + } +} + +func TestWtFwpmAction0Size(t *testing.T) { + + const actualWtFwpmAction0Size = unsafe.Sizeof(wtFwpmAction0{}) + + if actualWtFwpmAction0Size != wtFwpmAction0_Size { + t.Errorf("Size of wtFwpmAction0 is %d, although %d is expected.", actualWtFwpmAction0Size, + wtFwpmAction0_Size) + } +} + +func TestWtFwpmAction0Offsets(t *testing.T) { + + s := wtFwpmAction0{} + sp := uintptr(unsafe.Pointer(&s)) + + offset := uintptr(unsafe.Pointer(&s.filterType)) - sp + + if offset != wtFwpmAction0_filterType_Offset { + t.Errorf("wtFwpmAction0.filterType offset is %d although %d is expected", offset, + wtFwpmAction0_filterType_Offset) + return + } +} + +func TestWtFwpBitmapArray64Size(t *testing.T) { + + const actualWtFwpBitmapArray64Size = unsafe.Sizeof(wtFwpBitmapArray64{}) + + if actualWtFwpBitmapArray64Size != wtFwpBitmapArray64_Size { + t.Errorf("Size of wtFwpBitmapArray64 is %d, although %d is expected.", actualWtFwpBitmapArray64Size, + wtFwpBitmapArray64_Size) + } +} + +func TestWtFwpByteArray6Size(t *testing.T) { + + const actualWtFwpByteArray6Size = unsafe.Sizeof(wtFwpByteArray6{}) + + if actualWtFwpByteArray6Size != wtFwpByteArray6_Size { + t.Errorf("Size of wtFwpByteArray6 is %d, although %d is expected.", actualWtFwpByteArray6Size, + wtFwpByteArray6_Size) + } +} + +func TestWtFwpByteArray16Size(t *testing.T) { + + const actualWtFwpByteArray16Size = unsafe.Sizeof(wtFwpByteArray16{}) + + if actualWtFwpByteArray16Size != wtFwpByteArray16_Size { + t.Errorf("Size of wtFwpByteArray16 is %d, although %d is expected.", actualWtFwpByteArray16Size, + wtFwpByteArray16_Size) + } +} + +func TestWtFwpConditionValue0Size(t *testing.T) { + + const actualWtFwpConditionValue0Size = unsafe.Sizeof(wtFwpConditionValue0{}) + + if actualWtFwpConditionValue0Size != wtFwpConditionValue0_Size { + t.Errorf("Size of wtFwpConditionValue0 is %d, although %d is expected.", actualWtFwpConditionValue0Size, + wtFwpConditionValue0_Size) + } +} + +func TestWtFwpConditionValue0Offsets(t *testing.T) { + + s := wtFwpConditionValue0{} + sp := uintptr(unsafe.Pointer(&s)) + + offset := uintptr(unsafe.Pointer(&s.value)) - sp + + if offset != wtFwpConditionValue0_uint8_Offset { + t.Errorf("wtFwpConditionValue0.value offset is %d although %d is expected", offset, wtFwpConditionValue0_uint8_Offset) + return + } +} + +func TestWtFwpV4AddrAndMaskSize(t *testing.T) { + + const actualWtFwpV4AddrAndMaskSize = unsafe.Sizeof(wtFwpV4AddrAndMask{}) + + if actualWtFwpV4AddrAndMaskSize != wtFwpV4AddrAndMask_Size { + t.Errorf("Size of wtFwpV4AddrAndMask is %d, although %d is expected.", actualWtFwpV4AddrAndMaskSize, + wtFwpV4AddrAndMask_Size) + } +} + +func TestWtFwpV4AddrAndMaskOffsets(t *testing.T) { + + s := wtFwpV4AddrAndMask{} + sp := uintptr(unsafe.Pointer(&s)) + + offset := uintptr(unsafe.Pointer(&s.mask)) - sp + + if offset != wtFwpV4AddrAndMask_mask_Offset { + t.Errorf("wtFwpV4AddrAndMask.mask offset is %d although %d is expected", offset, + wtFwpV4AddrAndMask_mask_Offset) + return + } +} + +func TestWtFwpV6AddrAndMaskSize(t *testing.T) { + + const actualWtFwpV6AddrAndMaskSize = unsafe.Sizeof(wtFwpV6AddrAndMask{}) + + if actualWtFwpV6AddrAndMaskSize != wtFwpV6AddrAndMask_Size { + t.Errorf("Size of wtFwpV6AddrAndMask is %d, although %d is expected.", actualWtFwpV6AddrAndMaskSize, + wtFwpV6AddrAndMask_Size) + } +} + +func TestWtFwpV6AddrAndMaskOffsets(t *testing.T) { + + s := wtFwpV6AddrAndMask{} + sp := uintptr(unsafe.Pointer(&s)) + + offset := uintptr(unsafe.Pointer(&s.prefixLength)) - sp + + if offset != wtFwpV6AddrAndMask_prefixLength_Offset { + t.Errorf("wtFwpV6AddrAndMask.prefixLength offset is %d although %d is expected", offset, + wtFwpV6AddrAndMask_prefixLength_Offset) + return + } +} + +func TestWtFwpValue0Size(t *testing.T) { + + const actualWtFwpValue0Size = unsafe.Sizeof(wtFwpValue0{}) + + if actualWtFwpValue0Size != wtFwpValue0_Size { + t.Errorf("Size of wtFwpValue0 is %d, although %d is expected.", actualWtFwpValue0Size, wtFwpValue0_Size) + } +} + +func TestWtFwpValue0Offsets(t *testing.T) { + + s := wtFwpValue0{} + sp := uintptr(unsafe.Pointer(&s)) + + offset := uintptr(unsafe.Pointer(&s.value)) - sp + + if offset != wtFwpValue0_value_Offset { + t.Errorf("wtFwpValue0.value offset is %d although %d is expected", offset, wtFwpValue0_value_Offset) + return + } +} + +func TestWtFwpmDisplayData0Size(t *testing.T) { + + const actualWtFwpmDisplayData0Size = unsafe.Sizeof(wtFwpmDisplayData0{}) + + if actualWtFwpmDisplayData0Size != wtFwpmDisplayData0_Size { + t.Errorf("Size of wtFwpmDisplayData0 is %d, although %d is expected.", actualWtFwpmDisplayData0Size, + wtFwpmDisplayData0_Size) + } +} + +func TestWtFwpmDisplayData0Offsets(t *testing.T) { + + s := wtFwpmDisplayData0{} + sp := uintptr(unsafe.Pointer(&s)) + + offset := uintptr(unsafe.Pointer(&s.description)) - sp + + if offset != wtFwpmDisplayData0_description_Offset { + t.Errorf("wtFwpmDisplayData0.description offset is %d although %d is expected", offset, + wtFwpmDisplayData0_description_Offset) + return + } +} + +func TestWtFwpmFilterCondition0Size(t *testing.T) { + + const actualWtFwpmFilterCondition0Size = unsafe.Sizeof(wtFwpmFilterCondition0{}) + + if actualWtFwpmFilterCondition0Size != wtFwpmFilterCondition0_Size { + t.Errorf("Size of wtFwpmFilterCondition0 is %d, although %d is expected.", + actualWtFwpmFilterCondition0Size, wtFwpmFilterCondition0_Size) + } +} + +func TestWtFwpmFilterCondition0Offsets(t *testing.T) { + + s := wtFwpmFilterCondition0{} + sp := uintptr(unsafe.Pointer(&s)) + + offset := uintptr(unsafe.Pointer(&s.matchType)) - sp + + if offset != wtFwpmFilterCondition0_matchType_Offset { + t.Errorf("wtFwpmFilterCondition0.matchType offset is %d although %d is expected", offset, + wtFwpmFilterCondition0_matchType_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.conditionValue)) - sp + + if offset != wtFwpmFilterCondition0_conditionValue_Offset { + t.Errorf("wtFwpmFilterCondition0.conditionValue offset is %d although %d is expected", offset, + wtFwpmFilterCondition0_conditionValue_Offset) + return + } +} + +func TestWtFwpmFilter0Size(t *testing.T) { + + const actualWtFwpmFilter0Size = unsafe.Sizeof(wtFwpmFilter0{}) + + if actualWtFwpmFilter0Size != wtFwpmFilter0_Size { + t.Errorf("Size of wtFwpmFilter0 is %d, although %d is expected.", actualWtFwpmFilter0Size, + wtFwpmFilter0_Size) + } +} + +func TestWtFwpmFilter0Offsets(t *testing.T) { + + s := wtFwpmFilter0{} + sp := uintptr(unsafe.Pointer(&s)) + + offset := uintptr(unsafe.Pointer(&s.displayData)) - sp + + if offset != wtFwpmFilter0_displayData_Offset { + t.Errorf("wtFwpmFilter0.displayData offset is %d although %d is expected", offset, + wtFwpmFilter0_displayData_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.flags)) - sp + + if offset != wtFwpmFilter0_flags_Offset { + t.Errorf("wtFwpmFilter0.flags offset is %d although %d is expected", offset, wtFwpmFilter0_flags_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.providerKey)) - sp + + if offset != wtFwpmFilter0_providerKey_Offset { + t.Errorf("wtFwpmFilter0.providerKey offset is %d although %d is expected", offset, + wtFwpmFilter0_providerKey_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.providerData)) - sp + + if offset != wtFwpmFilter0_providerData_Offset { + t.Errorf("wtFwpmFilter0.providerData offset is %d although %d is expected", offset, + wtFwpmFilter0_providerData_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.layerKey)) - sp + + if offset != wtFwpmFilter0_layerKey_Offset { + t.Errorf("wtFwpmFilter0.layerKey offset is %d although %d is expected", offset, + wtFwpmFilter0_layerKey_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.subLayerKey)) - sp + + if offset != wtFwpmFilter0_subLayerKey_Offset { + t.Errorf("wtFwpmFilter0.subLayerKey offset is %d although %d is expected", offset, + wtFwpmFilter0_subLayerKey_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.weight)) - sp + + if offset != wtFwpmFilter0_weight_Offset { + t.Errorf("wtFwpmFilter0.weight offset is %d although %d is expected", offset, + wtFwpmFilter0_weight_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.numFilterConditions)) - sp + + if offset != wtFwpmFilter0_numFilterConditions_Offset { + t.Errorf("wtFwpmFilter0.numFilterConditions offset is %d although %d is expected", offset, + wtFwpmFilter0_numFilterConditions_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.filterCondition)) - sp + + if offset != wtFwpmFilter0_filterCondition_Offset { + t.Errorf("wtFwpmFilter0.filterCondition offset is %d although %d is expected", offset, + wtFwpmFilter0_filterCondition_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.action)) - sp + + if offset != wtFwpmFilter0_action_Offset { + t.Errorf("wtFwpmFilter0.action offset is %d although %d is expected", offset, + wtFwpmFilter0_action_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.providerContextKey)) - sp + + if offset != wtFwpmFilter0_providerContextKey_Offset { + t.Errorf("wtFwpmFilter0.providerContextKey offset is %d although %d is expected", offset, + wtFwpmFilter0_providerContextKey_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.reserved)) - sp + + if offset != wtFwpmFilter0_reserved_Offset { + t.Errorf("wtFwpmFilter0.reserved offset is %d although %d is expected", offset, + wtFwpmFilter0_reserved_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.filterID)) - sp + + if offset != wtFwpmFilter0_filterID_Offset { + t.Errorf("wtFwpmFilter0.filterID offset is %d although %d is expected", offset, + wtFwpmFilter0_filterID_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.effectiveWeight)) - sp + + if offset != wtFwpmFilter0_effectiveWeight_Offset { + t.Errorf("wtFwpmFilter0.effectiveWeight offset is %d although %d is expected", offset, + wtFwpmFilter0_effectiveWeight_Offset) + return + } +} + +func TestWtFwpProvider0Size(t *testing.T) { + + const actualWtFwpProvider0Size = unsafe.Sizeof(wtFwpProvider0{}) + + if actualWtFwpProvider0Size != wtFwpProvider0_Size { + t.Errorf("Size of wtFwpProvider0 is %d, although %d is expected.", actualWtFwpProvider0Size, + wtFwpProvider0_Size) + } +} + +func TestWtFwpProvider0Offsets(t *testing.T) { + + s := wtFwpProvider0{} + sp := uintptr(unsafe.Pointer(&s)) + + offset := uintptr(unsafe.Pointer(&s.displayData)) - sp + + if offset != wtFwpProvider0_displayData_Offset { + t.Errorf("wtFwpProvider0.displayData offset is %d although %d is expected", offset, + wtFwpProvider0_displayData_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.flags)) - sp + + if offset != wtFwpProvider0_flags_Offset { + t.Errorf("wtFwpProvider0.flags offset is %d although %d is expected", offset, + wtFwpProvider0_flags_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.providerData)) - sp + + if offset != wtFwpProvider0_providerData_Offset { + t.Errorf("wtFwpProvider0.providerData offset is %d although %d is expected", offset, + wtFwpProvider0_providerData_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.serviceName)) - sp + + if offset != wtFwpProvider0_serviceName_Offset { + t.Errorf("wtFwpProvider0.serviceName offset is %d although %d is expected", offset, + wtFwpProvider0_serviceName_Offset) + return + } +} + +func TestWtFwpmSession0Size(t *testing.T) { + + const actualWtFwpmSession0Size = unsafe.Sizeof(wtFwpmSession0{}) + + if actualWtFwpmSession0Size != wtFwpmSession0_Size { + t.Errorf("Size of wtFwpmSession0 is %d, although %d is expected.", actualWtFwpmSession0Size, + wtFwpmSession0_Size) + } +} + +func TestWtFwpmSession0Offsets(t *testing.T) { + + s := wtFwpmSession0{} + sp := uintptr(unsafe.Pointer(&s)) + + offset := uintptr(unsafe.Pointer(&s.displayData)) - sp + + if offset != wtFwpmSession0_displayData_Offset { + t.Errorf("wtFwpmSession0.displayData offset is %d although %d is expected", offset, + wtFwpmSession0_displayData_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.flags)) - sp + + if offset != wtFwpmSession0_flags_Offset { + t.Errorf("wtFwpmSession0.flags offset is %d although %d is expected", offset, wtFwpmSession0_flags_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.txnWaitTimeoutInMSec)) - sp + + if offset != wtFwpmSession0_txnWaitTimeoutInMSec_Offset { + t.Errorf("wtFwpmSession0.txnWaitTimeoutInMSec offset is %d although %d is expected", offset, + wtFwpmSession0_txnWaitTimeoutInMSec_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.processId)) - sp + + if offset != wtFwpmSession0_processId_Offset { + t.Errorf("wtFwpmSession0.processId offset is %d although %d is expected", offset, + wtFwpmSession0_processId_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.sid)) - sp + + if offset != wtFwpmSession0_sid_Offset { + t.Errorf("wtFwpmSession0.sid offset is %d although %d is expected", offset, wtFwpmSession0_sid_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.username)) - sp + + if offset != wtFwpmSession0_username_Offset { + t.Errorf("wtFwpmSession0.username offset is %d although %d is expected", offset, + wtFwpmSession0_username_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.kernelMode)) - sp + + if offset != wtFwpmSession0_kernelMode_Offset { + t.Errorf("wtFwpmSession0.kernelMode offset is %d although %d is expected", offset, + wtFwpmSession0_kernelMode_Offset) + return + } +} + +func TestWtFwpmSublayer0Size(t *testing.T) { + + const actualWtFwpmSublayer0Size = unsafe.Sizeof(wtFwpmSublayer0{}) + + if actualWtFwpmSublayer0Size != wtFwpmSublayer0_Size { + t.Errorf("Size of wtFwpmSublayer0 is %d, although %d is expected.", actualWtFwpmSublayer0Size, + wtFwpmSublayer0_Size) + } +} + +func TestWtFwpmSublayer0Offsets(t *testing.T) { + + s := wtFwpmSublayer0{} + sp := uintptr(unsafe.Pointer(&s)) + + offset := uintptr(unsafe.Pointer(&s.displayData)) - sp + + if offset != wtFwpmSublayer0_displayData_Offset { + t.Errorf("wtFwpmSublayer0.displayData offset is %d although %d is expected", offset, + wtFwpmSublayer0_displayData_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.flags)) - sp + + if offset != wtFwpmSublayer0_flags_Offset { + t.Errorf("wtFwpmSublayer0.flags offset is %d although %d is expected", offset, + wtFwpmSublayer0_flags_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.providerKey)) - sp + + if offset != wtFwpmSublayer0_providerKey_Offset { + t.Errorf("wtFwpmSublayer0.providerKey offset is %d although %d is expected", offset, + wtFwpmSublayer0_providerKey_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.providerData)) - sp + + if offset != wtFwpmSublayer0_providerData_Offset { + t.Errorf("wtFwpmSublayer0.providerData offset is %d although %d is expected", offset, + wtFwpmSublayer0_providerData_Offset) + return + } + + offset = uintptr(unsafe.Pointer(&s.weight)) - sp + + if offset != wtFwpmSublayer0_weight_Offset { + t.Errorf("wtFwpmSublayer0.weight offset is %d although %d is expected", offset, + wtFwpmSublayer0_weight_Offset) + return + } +} diff --git a/tunnel/firewall/zsyscall_windows.go b/tunnel/firewall/zsyscall_windows.go new file mode 100644 index 00000000..15e72703 --- /dev/null +++ b/tunnel/firewall/zsyscall_windows.go @@ -0,0 +1,186 @@ +// Code generated by 'go generate'; DO NOT EDIT. + +package firewall + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modfwpuclnt = windows.NewLazySystemDLL("fwpuclnt.dll") + modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") + + procFwpmEngineOpen0 = modfwpuclnt.NewProc("FwpmEngineOpen0") + procFwpmEngineClose0 = modfwpuclnt.NewProc("FwpmEngineClose0") + procFwpmSubLayerAdd0 = modfwpuclnt.NewProc("FwpmSubLayerAdd0") + procFwpmGetAppIdFromFileName0 = modfwpuclnt.NewProc("FwpmGetAppIdFromFileName0") + procFwpmFreeMemory0 = modfwpuclnt.NewProc("FwpmFreeMemory0") + procFwpmFilterAdd0 = modfwpuclnt.NewProc("FwpmFilterAdd0") + procFwpmTransactionBegin0 = modfwpuclnt.NewProc("FwpmTransactionBegin0") + procFwpmTransactionCommit0 = modfwpuclnt.NewProc("FwpmTransactionCommit0") + procFwpmTransactionAbort0 = modfwpuclnt.NewProc("FwpmTransactionAbort0") + procFwpmProviderAdd0 = modfwpuclnt.NewProc("FwpmProviderAdd0") + procGetSecurityInfo = modadvapi32.NewProc("GetSecurityInfo") + procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength") +) + +func fwpmEngineOpen0(serverName *uint16, authnService wtRpcCAuthN, authIdentity *uintptr, session *wtFwpmSession0, engineHandle unsafe.Pointer) (err error) { + r1, _, e1 := syscall.Syscall6(procFwpmEngineOpen0.Addr(), 5, uintptr(unsafe.Pointer(serverName)), uintptr(authnService), uintptr(unsafe.Pointer(authIdentity)), uintptr(unsafe.Pointer(session)), uintptr(engineHandle), 0) + if r1 != 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func fwpmEngineClose0(engineHandle uintptr) (err error) { + r1, _, e1 := syscall.Syscall(procFwpmEngineClose0.Addr(), 1, uintptr(engineHandle), 0, 0) + if r1 != 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func fwpmSubLayerAdd0(engineHandle uintptr, subLayer *wtFwpmSublayer0, sd uintptr) (err error) { + r1, _, e1 := syscall.Syscall(procFwpmSubLayerAdd0.Addr(), 3, uintptr(engineHandle), uintptr(unsafe.Pointer(subLayer)), uintptr(sd)) + if r1 != 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func fwpmGetAppIdFromFileName0(fileName *uint16, appID unsafe.Pointer) (err error) { + r1, _, e1 := syscall.Syscall(procFwpmGetAppIdFromFileName0.Addr(), 2, uintptr(unsafe.Pointer(fileName)), uintptr(appID), 0) + if r1 != 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func fwpmFreeMemory0(p unsafe.Pointer) { + syscall.Syscall(procFwpmFreeMemory0.Addr(), 1, uintptr(p), 0, 0) + return +} + +func fwpmFilterAdd0(engineHandle uintptr, filter *wtFwpmFilter0, sd uintptr, id *uint64) (err error) { + r1, _, e1 := syscall.Syscall6(procFwpmFilterAdd0.Addr(), 4, uintptr(engineHandle), uintptr(unsafe.Pointer(filter)), uintptr(sd), uintptr(unsafe.Pointer(id)), 0, 0) + if r1 != 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func fwpmTransactionBegin0(engineHandle uintptr, flags uint32) (err error) { + r1, _, e1 := syscall.Syscall(procFwpmTransactionBegin0.Addr(), 2, uintptr(engineHandle), uintptr(flags), 0) + if r1 != 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func fwpmTransactionCommit0(engineHandle uintptr) (err error) { + r1, _, e1 := syscall.Syscall(procFwpmTransactionCommit0.Addr(), 1, uintptr(engineHandle), 0, 0) + if r1 != 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func fwpmTransactionAbort0(engineHandle uintptr) (err error) { + r1, _, e1 := syscall.Syscall(procFwpmTransactionAbort0.Addr(), 1, uintptr(engineHandle), 0, 0) + if r1 != 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func fwpmProviderAdd0(engineHandle uintptr, provider *wtFwpmProvider0, sd uintptr) (err error) { + r1, _, e1 := syscall.Syscall(procFwpmProviderAdd0.Addr(), 3, uintptr(engineHandle), uintptr(unsafe.Pointer(provider)), uintptr(sd)) + if r1 != 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getSecurityInfo(handle windows.Handle, objectType wtObjectType, si uint32, sidOwner *windows.SID, sidGroup *windows.SID, dacl *uintptr, sacl *uintptr, securityDescriptor *uintptr) (err error) { + r1, _, e1 := syscall.Syscall9(procGetSecurityInfo.Addr(), 8, uintptr(handle), uintptr(objectType), uintptr(si), uintptr(unsafe.Pointer(sidOwner)), uintptr(unsafe.Pointer(sidGroup)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), uintptr(unsafe.Pointer(securityDescriptor)), 0) + if r1 != 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getSecurityDescriptorLength(securityDescriptor uintptr) (len uint32) { + r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(securityDescriptor), 0, 0) + len = uint32(r0) + return +} diff --git a/tunnel/ifaceconfig.go b/tunnel/ifaceconfig.go new file mode 100644 index 00000000..453d4ca5 --- /dev/null +++ b/tunnel/ifaceconfig.go @@ -0,0 +1,220 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package tunnel + +import ( + "bytes" + "log" + "net" + "sort" + + "golang.org/x/sys/windows" + "golang.zx2c4.com/winipcfg" + "golang.zx2c4.com/wireguard/tun" + + "golang.zx2c4.com/wireguard/windows/conf" + "golang.zx2c4.com/wireguard/windows/tunnel/firewall" +) + +func cleanupAddressesOnDisconnectedInterfaces(addresses []*net.IPNet) { + if len(addresses) == 0 { + return + } + includedInAddresses := func(a *net.IPNet) bool { + //TODO: this makes the whole algorithm O(n^2). But we can't stick net.IPNet in a Go hashmap. Bummer! + for _, addr := range addresses { + ip := addr.IP + if ip4 := ip.To4(); ip4 != nil { + ip = ip4 + } + mA, _ := addr.Mask.Size() + mB, _ := a.Mask.Size() + if bytes.Equal(ip, a.IP) && mA == mB { + return true + } + } + return false + } + interfaces, err := winipcfg.GetInterfaces() + if err != nil { + return + } + for _, iface := range interfaces { + if iface.OperStatus == winipcfg.IfOperStatusUp { + continue + } + addressesToKeep := make([]*net.IPNet, 0, len(iface.UnicastAddresses)) + for _, address := range iface.UnicastAddresses { + ip := address.Address.Address + if ip4 := ip.To4(); ip4 != nil { + ip = ip4 + } + ipnet := &net.IPNet{ip, net.CIDRMask(int(address.OnLinkPrefixLength), 8*len(ip))} + if !includedInAddresses(ipnet) { + addressesToKeep = append(addressesToKeep, ipnet) + } + } + if len(addressesToKeep) < len(iface.UnicastAddresses) { + log.Printf("Cleaning up stale addresses from interface '%s'", iface.FriendlyName) + iface.SetAddresses(addressesToKeep) + } + } +} + +func configureInterface(conf *conf.Config, tun *tun.NativeTun) error { + iface, err := winipcfg.InterfaceFromLUID(tun.LUID()) + if err != nil { + return err + } + + estimatedRouteCount := len(conf.Interface.Addresses) + for _, peer := range conf.Peers { + estimatedRouteCount += len(peer.AllowedIPs) + } + routes := make([]winipcfg.RouteData, 0, estimatedRouteCount) + var firstGateway4 *net.IP + var firstGateway6 *net.IP + addresses := make([]*net.IPNet, len(conf.Interface.Addresses)) + for i, addr := range conf.Interface.Addresses { + ipnet := addr.IPNet() + addresses[i] = &ipnet + gateway := ipnet.IP.Mask(ipnet.Mask) + if addr.Bits() == 32 && firstGateway4 == nil { + firstGateway4 = &gateway + } else if addr.Bits() == 128 && firstGateway6 == nil { + firstGateway6 = &gateway + } + routes = append(routes, winipcfg.RouteData{ + Destination: net.IPNet{ + IP: gateway, + Mask: ipnet.Mask, + }, + NextHop: gateway, + Metric: 0, + }) + } + + foundDefault4 := false + foundDefault6 := false + for _, peer := range conf.Peers { + for _, allowedip := range peer.AllowedIPs { + if (allowedip.Bits() == 32 && firstGateway4 == nil) || (allowedip.Bits() == 128 && firstGateway6 == nil) { + continue + } + route := winipcfg.RouteData{ + Destination: allowedip.IPNet(), + Metric: 0, + } + if allowedip.Bits() == 32 { + if allowedip.Cidr == 0 { + foundDefault4 = true + } + route.NextHop = *firstGateway4 + } else if allowedip.Bits() == 128 { + if allowedip.Cidr == 0 { + foundDefault6 = true + } + route.NextHop = *firstGateway6 + } + routes = append(routes, route) + } + } + + err = iface.SetAddresses(addresses) + if err == windows.ERROR_OBJECT_ALREADY_EXISTS { + cleanupAddressesOnDisconnectedInterfaces(addresses) + err = iface.SetAddresses(addresses) + } + if err != nil { + return err + } + + deduplicatedRoutes := make([]*winipcfg.RouteData, 0, len(routes)) + sort.Slice(routes, func(i, j int) bool { + return routes[i].Metric < routes[j].Metric || + bytes.Compare(routes[i].NextHop, routes[j].NextHop) == -1 || + bytes.Compare(routes[i].Destination.IP, routes[j].Destination.IP) == -1 || + bytes.Compare(routes[i].Destination.Mask, routes[j].Destination.Mask) == -1 + }) + for i := 0; i < len(routes); i++ { + if i > 0 && routes[i].Metric == routes[i-1].Metric && + bytes.Equal(routes[i].NextHop, routes[i-1].NextHop) && + bytes.Equal(routes[i].Destination.IP, routes[i-1].Destination.IP) && + bytes.Equal(routes[i].Destination.Mask, routes[i-1].Destination.Mask) { + continue + } + deduplicatedRoutes = append(deduplicatedRoutes, &routes[i]) + } + + err = iface.SetRoutes(deduplicatedRoutes) + if err != nil { + return nil + } + + err = iface.SetDNS(conf.Interface.DNS) + if err != nil { + return err + } + + ipif, err := iface.GetIPInterface(windows.AF_INET) + if err != nil { + return err + } + if foundDefault4 { + ipif.UseAutomaticMetric = false + ipif.Metric = 0 + } + if conf.Interface.MTU > 0 { + ipif.NLMTU = uint32(conf.Interface.MTU) + tun.ForceMTU(int(ipif.NLMTU)) + } + err = ipif.Set() + if err != nil { + return err + } + + ipif, err = iface.GetIPInterface(windows.AF_INET6) + if err != nil { + return err + } + if foundDefault6 { + ipif.UseAutomaticMetric = false + ipif.Metric = 0 + } + if conf.Interface.MTU > 0 { + ipif.NLMTU = uint32(conf.Interface.MTU) + } + ipif.DadTransmits = 0 + ipif.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled + err = ipif.Set() + if err != nil { + return err + } + + return nil +} + +func enableFirewall(conf *conf.Config, tun *tun.NativeTun) error { + restrictAll := false + if len(conf.Peers) == 1 { + nextallowedip: + for _, allowedip := range conf.Peers[0].AllowedIPs { + if allowedip.Cidr == 0 { + for _, b := range allowedip.IP { + if b != 0 { + continue nextallowedip + } + } + restrictAll = true + break + } + } + } + if restrictAll && len(conf.Interface.DNS) == 0 { + log.Println("Warning: no DNS server specified, despite having an allowed IPs of 0.0.0.0/0 or ::/0. There may be connectivity issues.") + } + return firewall.EnableFirewall(tun.LUID(), conf.Interface.DNS, restrictAll) +} diff --git a/tunnel/service.go b/tunnel/service.go new file mode 100644 index 00000000..e93a2c40 --- /dev/null +++ b/tunnel/service.go @@ -0,0 +1,225 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. + */ + +package tunnel + +import ( + "bufio" + "bytes" + "fmt" + "log" + "net" + "os" + "runtime" + "runtime/debug" + "strings" + "time" + + "golang.org/x/sys/windows/svc" + "golang.zx2c4.com/winipcfg" + "golang.zx2c4.com/wireguard/device" + "golang.zx2c4.com/wireguard/ipc" + "golang.zx2c4.com/wireguard/tun" + + "golang.zx2c4.com/wireguard/windows/conf" + "golang.zx2c4.com/wireguard/windows/ringlogger" + "golang.zx2c4.com/wireguard/windows/services" + "golang.zx2c4.com/wireguard/windows/version" +) + +type Service struct { + Path string +} + +func (service *Service) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) { + changes <- svc.Status{State: svc.StartPending} + + var dev *device.Device + var uapi net.Listener + var routeChangeCallback *winipcfg.RouteChangeCallback + var err error + serviceError := services.ErrorSuccess + + defer func() { + svcSpecificEC, exitCode = services.DetermineErrorCode(err, serviceError) + logErr := services.CombineErrors(err, serviceError) + if logErr != nil { + log.Println(logErr) + } + changes <- svc.Status{State: svc.StopPending} + + stopIt := make(chan bool, 1) + go func() { + t := time.NewTicker(time.Second * 30) + for { + select { + case <-t.C: + t.Stop() + buf := make([]byte, 1024) + for { + n := runtime.Stack(buf, true) + if n < len(buf) { + buf = buf[:n] + break + } + buf = make([]byte, 2*len(buf)) + } + lines := bytes.Split(buf, []byte{'\n'}) + log.Println("Failed to shutdown after 30 seconds. Probably dead locked. Printing stack and killing.") + for _, line := range lines { + if len(bytes.TrimSpace(line)) > 0 { + log.Println(string(line)) + } + } + os.Exit(777) + return + case <-stopIt: + t.Stop() + return + } + } + }() + + if routeChangeCallback != nil { + routeChangeCallback.Unregister() + } + if uapi != nil { + uapi.Close() + } + if dev != nil { + dev.Close() + } + stopIt <- true + log.Println("Shutting down") + }() + + err = ringlogger.InitGlobalLogger("TUN") + if err != nil { + serviceError = services.ErrorRingloggerOpen + return + } + defer func() { + if x := recover(); x != nil { + for _, line := range append([]string{fmt.Sprint(x)}, strings.Split(string(debug.Stack()), "\n")...) { + if len(strings.TrimSpace(line)) > 0 { + log.Println(line) + } + } + panic(x) + } + }() + + conf, err := conf.LoadFromPath(service.Path) + if err != nil { + serviceError = services.ErrorLoadConfiguration + return + } + + logPrefix := fmt.Sprintf("[%s] ", conf.Name) + log.SetPrefix(logPrefix) + + log.Println("Starting", version.UserAgent()) + + log.Println("Resolving DNS names") + uapiConf, err := conf.ToUAPI() + if err != nil { + serviceError = services.ErrorDNSLookup + return + } + + log.Println("Creating Wintun device") + wintun, err := tun.CreateTUN(conf.Name) + if err != nil { + serviceError = services.ErrorCreateWintun + return + } + log.Println("Determining Wintun device name") + realInterfaceName, err := wintun.Name() + if err != nil { + serviceError = services.ErrorDetermineWintunName + return + } + conf.Name = realInterfaceName + nativeTun := wintun.(*tun.NativeTun) + + log.Println("Enabling firewall rules") + err = enableFirewall(conf, nativeTun) + if err != nil { + serviceError = services.ErrorFirewall + return + } + + log.Println("Dropping all privileges") + err = services.DropAllPrivileges() + if err != nil { + serviceError = services.ErrorDropPrivileges + return + } + + log.Println("Creating interface instance") + logOutput := log.New(ringlogger.Global, logPrefix, 0) + logger := &device.Logger{logOutput, logOutput, logOutput} + dev = device.NewDevice(wintun, logger) + + log.Println("Setting interface configuration") + uapi, err = ipc.UAPIListen(conf.Name) + if err != nil { + serviceError = services.ErrorUAPIListen + return + } + ipcErr := dev.IpcSetOperation(bufio.NewReader(strings.NewReader(uapiConf))) + if ipcErr != nil { + err = ipcErr + serviceError = services.ErrorDeviceSetConfig + return + } + + log.Println("Bringing peers up") + dev.Up() + + log.Println("Monitoring default routes") + routeChangeCallback, err = monitorDefaultRoutes(dev, conf.Interface.MTU == 0, nativeTun) + if err != nil { + serviceError = services.ErrorBindSocketsToDefaultRoutes + return + } + + log.Println("Setting device address") + err = configureInterface(conf, nativeTun) + if err != nil { + serviceError = services.ErrorSetNetConfig + return + } + + log.Println("Listening for UAPI requests") + go func() { + for { + conn, err := uapi.Accept() + if err != nil { + continue + } + go dev.IpcHandle(conn) + } + }() + + changes <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop} + log.Println("Startup complete") + + for { + select { + case c := <-r: + switch c.Cmd { + case svc.Stop: + return + case svc.Interrogate: + changes <- c.CurrentStatus + default: + log.Printf("Unexpected service control request #%d\n", c) + } + case <-dev.Wait(): + return + } + } +} diff --git a/ui/confview.go b/ui/confview.go index 8e28e83e..665841da 100644 --- a/ui/confview.go +++ b/ui/confview.go @@ -13,8 +13,9 @@ import ( "github.com/lxn/walk" "github.com/lxn/win" + "golang.zx2c4.com/wireguard/windows/conf" - "golang.zx2c4.com/wireguard/windows/service" + "golang.zx2c4.com/wireguard/windows/manager" ) type widgetsLine interface { @@ -28,7 +29,7 @@ type widgetsLinesView interface { type rectAndSizeAndState struct { rect walk.Rectangle size walk.Size - state service.TunnelState + state manager.TunnelState } type labelStatusLine struct { @@ -75,8 +76,8 @@ type ConfView struct { name *walk.GroupBox interfaze *interfaceView peers map[conf.Key]*peerView - tunnelChangedCB *service.TunnelChangeCallback - tunnel *service.Tunnel + tunnelChangedCB *manager.TunnelChangeCallback + tunnel *manager.Tunnel updateTicker *time.Ticker } @@ -84,7 +85,7 @@ func (lsl *labelStatusLine) widgets() (walk.Widget, walk.Widget) { return lsl.label, lsl.statusComposite } -func (lsl *labelStatusLine) update(state service.TunnelState) { +func (lsl *labelStatusLine) update(state manager.TunnelState) { icon, err := iconForState(state, 14) if err == nil { lsl.statusImage.SetImage(icon) @@ -122,7 +123,7 @@ func newLabelStatusLine(parent walk.Container) *labelStatusLine { lsl.statusLabel.FocusedChanged().Attach(func() { lsl.statusLabel.SetTextSelection(0, 0) }) - lsl.update(service.TunnelUnknown) + lsl.update(manager.TunnelUnknown) return lsl } @@ -167,26 +168,26 @@ func (tal *toggleActiveLine) widgets() (walk.Widget, walk.Widget) { return nil, tal.composite } -func (tal *toggleActiveLine) updateGlobal(globalState service.TunnelState) { - tal.button.SetEnabled(globalState == service.TunnelStarted || globalState == service.TunnelStopped) +func (tal *toggleActiveLine) updateGlobal(globalState manager.TunnelState) { + tal.button.SetEnabled(globalState == manager.TunnelStarted || globalState == manager.TunnelStopped) } -func (tal *toggleActiveLine) update(state service.TunnelState) { +func (tal *toggleActiveLine) update(state manager.TunnelState) { var text string switch state { - case service.TunnelStarted: + case manager.TunnelStarted: text = "Deactivate" - case service.TunnelStopped: + case manager.TunnelStopped: text = "Activate" - case service.TunnelStarting, service.TunnelStopping: + case manager.TunnelStarting, manager.TunnelStopping: text = textForState(state, true) default: text = "" } tal.button.SetText(text) - tal.button.SetVisible(state != service.TunnelUnknown) + tal.button.SetVisible(state != manager.TunnelUnknown) } func newToggleActiveLine(parent walk.Container) *toggleActiveLine { @@ -199,7 +200,7 @@ func newToggleActiveLine(parent walk.Container) *toggleActiveLine { tal.button, _ = walk.NewPushButton(tal.composite) walk.NewHSpacer(tal.composite) - tal.update(service.TunnelStopped) + tal.update(manager.TunnelStopped) return tal } @@ -388,9 +389,9 @@ func NewConfView(parent walk.Container) (*ConfView, error) { cv.interfaze = newInterfaceView(cv.name) cv.interfaze.toggleActive.button.Clicked().Attach(cv.onToggleActiveClicked) cv.peers = make(map[conf.Key]*peerView) - cv.tunnelChangedCB = service.IPCClientRegisterTunnelChange(cv.onTunnelChanged) + cv.tunnelChangedCB = manager.IPCClientRegisterTunnelChange(cv.onTunnelChanged) cv.SetTunnel(nil) - globalState, _ := service.IPCClientGlobalState() + globalState, _ := manager.IPCClientGlobalState() cv.interfaze.toggleActive.updateGlobal(globalState) if err := walk.InitWrapperWindow(cv); err != nil { @@ -405,9 +406,9 @@ func NewConfView(parent walk.Container) (*ConfView, error) { } if cv.tunnel != nil { tunnel := cv.tunnel - var state service.TunnelState + var state manager.TunnelState var config conf.Config - if state, _ = tunnel.State(); state == service.TunnelStarted { + if state, _ = tunnel.State(); state == manager.TunnelStarted { config, _ = tunnel.RuntimeConfig() } if config.Name == "" { @@ -440,11 +441,11 @@ func (cv *ConfView) onToggleActiveClicked() { oldState, err := cv.tunnel.Toggle() if err != nil { cv.Synchronize(func() { - if oldState == service.TunnelUnknown { + if oldState == manager.TunnelUnknown { walk.MsgBox(cv.Form(), "Failed to determine tunnel state", err.Error(), walk.MsgBoxIconError) - } else if oldState == service.TunnelStopped { + } else if oldState == manager.TunnelStopped { walk.MsgBox(cv.Form(), "Failed to activate tunnel", err.Error(), walk.MsgBoxIconError) - } else if oldState == service.TunnelStarted { + } else if oldState == manager.TunnelStarted { walk.MsgBox(cv.Form(), "Failed to deactivate tunnel", err.Error(), walk.MsgBoxIconError) } }) @@ -452,7 +453,7 @@ func (cv *ConfView) onToggleActiveClicked() { }() } -func (cv *ConfView) onTunnelChanged(tunnel *service.Tunnel, state service.TunnelState, globalState service.TunnelState, err error) { +func (cv *ConfView) onTunnelChanged(tunnel *manager.Tunnel, state manager.TunnelState, globalState manager.TunnelState, err error) { cv.Synchronize(func() { cv.interfaze.toggleActive.updateGlobal(globalState) if cv.tunnel != nil && cv.tunnel.Name == tunnel.Name { @@ -462,7 +463,7 @@ func (cv *ConfView) onTunnelChanged(tunnel *service.Tunnel, state service.Tunnel }) if cv.tunnel != nil && cv.tunnel.Name == tunnel.Name { var config conf.Config - if state == service.TunnelStarted { + if state == manager.TunnelStarted { config, _ = tunnel.RuntimeConfig() } if config.Name == "" { @@ -474,14 +475,14 @@ func (cv *ConfView) onTunnelChanged(tunnel *service.Tunnel, state service.Tunnel } } -func (cv *ConfView) SetTunnel(tunnel *service.Tunnel) { +func (cv *ConfView) SetTunnel(tunnel *manager.Tunnel) { cv.tunnel = tunnel //XXX: This races with the read in the updateTicker, but it's pointer-sized! var config conf.Config - var state service.TunnelState + var state manager.TunnelState if tunnel != nil { go func() { - if state, _ = tunnel.State(); state == service.TunnelStarted { + if state, _ = tunnel.State(); state == manager.TunnelStarted { config, _ = tunnel.RuntimeConfig() } if config.Name == "" { @@ -496,7 +497,7 @@ func (cv *ConfView) SetTunnel(tunnel *service.Tunnel) { } } -func (cv *ConfView) setTunnel(tunnel *service.Tunnel, config *conf.Config, state service.TunnelState) { +func (cv *ConfView) setTunnel(tunnel *manager.Tunnel, config *conf.Config, state manager.TunnelState) { if !(cv.tunnel == nil || tunnel == nil || tunnel.Name == cv.tunnel.Name) { return } diff --git a/ui/editdialog.go b/ui/editdialog.go index f46e91e2..27c22eb3 100644 --- a/ui/editdialog.go +++ b/ui/editdialog.go @@ -10,8 +10,9 @@ import ( "strings" "github.com/lxn/walk" + "golang.zx2c4.com/wireguard/windows/conf" - "golang.zx2c4.com/wireguard/windows/service" + "golang.zx2c4.com/wireguard/windows/manager" "golang.zx2c4.com/wireguard/windows/ui/syntax" ) @@ -27,7 +28,7 @@ type EditDialog struct { blockUntunneledTraficCheckGuard bool } -func runTunnelEditDialog(owner walk.Form, tunnel *service.Tunnel, clone bool) *conf.Config { +func runTunnelEditDialog(owner walk.Form, tunnel *manager.Tunnel, clone bool) *conf.Config { dlg := &EditDialog{} var title string @@ -268,7 +269,7 @@ func (dlg *EditDialog) onSaveButtonClicked() { newNameLower := strings.ToLower(newName) if newNameLower != strings.ToLower(dlg.config.Name) { - existingTunnelList, err := service.IPCClientTunnels() + existingTunnelList, err := manager.IPCClientTunnels() if err != nil { walk.MsgBox(dlg, "Unable to list existing tunnels", err.Error(), walk.MsgBoxIconError) return diff --git a/ui/iconprovider.go b/ui/iconprovider.go index d7d6fbcf..9a9d7aeb 100644 --- a/ui/iconprovider.go +++ b/ui/iconprovider.go @@ -7,12 +7,13 @@ package ui import ( "github.com/lxn/walk" - "golang.zx2c4.com/wireguard/windows/service" + + "golang.zx2c4.com/wireguard/windows/manager" ) type widthAndState struct { width int - state service.TunnelState + state manager.TunnelState } type widthAndDllIdx struct { @@ -23,7 +24,7 @@ type widthAndDllIdx struct { var cachedOverlayIconsForWidthAndState = make(map[widthAndState]walk.Image) -func iconWithOverlayForState(state service.TunnelState, size int) (icon walk.Image, err error) { +func iconWithOverlayForState(state manager.TunnelState, size int) (icon walk.Image, err error) { icon = cachedOverlayIconsForWidthAndState[widthAndState{size, state}] if icon != nil { return @@ -34,7 +35,7 @@ func iconWithOverlayForState(state service.TunnelState, size int) (icon walk.Ima return } - if state == service.TunnelStopped { + if state == manager.TunnelStopped { return wireguardIcon, err //TODO: if we find something prettier than the gray dot, then remove this clause } @@ -64,15 +65,15 @@ func iconWithOverlayForState(state service.TunnelState, size int) (icon walk.Ima var cachedIconsForWidthAndState = make(map[widthAndState]*walk.Icon) -func iconForState(state service.TunnelState, size int) (icon *walk.Icon, err error) { +func iconForState(state manager.TunnelState, size int) (icon *walk.Icon, err error) { icon = cachedIconsForWidthAndState[widthAndState{size, state}] if icon != nil { return } switch state { - case service.TunnelStarted: + case manager.TunnelStarted: icon, err = loadSystemIcon("imageres", 101, size) - case service.TunnelStopped: + case manager.TunnelStopped: icon, err = walk.NewIconFromResourceWithSize("dot-gray.ico", walk.Size{size, size}) //TODO: replace with real icon default: icon, err = loadSystemIcon("shell32", 238, size) //TODO: this doesn't look that great overlayed on the app icon @@ -83,22 +84,22 @@ func iconForState(state service.TunnelState, size int) (icon *walk.Icon, err err return } -func textForState(state service.TunnelState, withEllipsis bool) (text string) { +func textForState(state manager.TunnelState, withEllipsis bool) (text string) { switch state { - case service.TunnelStarted: + case manager.TunnelStarted: text = "Active" - case service.TunnelStarting: + case manager.TunnelStarting: text = "Activating" - case service.TunnelStopped: + case manager.TunnelStopped: text = "Inactive" - case service.TunnelStopping: + case manager.TunnelStopping: text = "Deactivating" - case service.TunnelUnknown: + case manager.TunnelUnknown: text = "Unknown state" } if withEllipsis { switch state { - case service.TunnelStarting, service.TunnelStopping: + case manager.TunnelStarting, manager.TunnelStopping: text += "..." } } diff --git a/ui/listview.go b/ui/listview.go index a8c5de89..b99f2d99 100644 --- a/ui/listview.go +++ b/ui/listview.go @@ -10,9 +10,9 @@ import ( "sync/atomic" "golang.zx2c4.com/wireguard/windows/conf" + "golang.zx2c4.com/wireguard/windows/manager" "github.com/lxn/walk" - "golang.zx2c4.com/wireguard/windows/service" ) // ListModel is a struct to store the currently known tunnels to the GUI, suitable as a model for a walk.TableView. @@ -20,7 +20,7 @@ type ListModel struct { walk.TableModelBase walk.SorterBase - tunnels []service.Tunnel + tunnels []manager.Tunnel } func (t *ListModel) RowCount() int { @@ -55,8 +55,8 @@ type ListView struct { model *ListModel - tunnelChangedCB *service.TunnelChangeCallback - tunnelsChangedCB *service.TunnelsChangeCallback + tunnelChangedCB *manager.TunnelChangeCallback + tunnelsChangedCB *manager.TunnelsChangeCallback tunnelsUpdateSuspended int32 } @@ -88,8 +88,8 @@ func NewListView(parent walk.Container) (*ListView, error) { disposables.Spare() - tunnelsView.tunnelChangedCB = service.IPCClientRegisterTunnelChange(tunnelsView.onTunnelChange) - tunnelsView.tunnelsChangedCB = service.IPCClientRegisterTunnelsChange(tunnelsView.onTunnelsChange) + tunnelsView.tunnelChangedCB = manager.IPCClientRegisterTunnelChange(tunnelsView.onTunnelChange) + tunnelsView.tunnelsChangedCB = manager.IPCClientRegisterTunnelsChange(tunnelsView.onTunnelsChange) return tunnelsView, nil } @@ -141,7 +141,7 @@ func (tv *ListView) StyleCell(style *walk.CellStyle) { canvas.DrawImageStretched(icon, b) } -func (tv *ListView) CurrentTunnel() *service.Tunnel { +func (tv *ListView) CurrentTunnel() *manager.Tunnel { idx := tv.CurrentIndex() if idx == -1 { return nil @@ -150,7 +150,7 @@ func (tv *ListView) CurrentTunnel() *service.Tunnel { return &tv.model.tunnels[idx] } -func (tv *ListView) onTunnelChange(tunnel *service.Tunnel, state service.TunnelState, globalState service.TunnelState, err error) { +func (tv *ListView) onTunnelChange(tunnel *manager.Tunnel, state manager.TunnelState, globalState manager.TunnelState, err error) { tv.Synchronize(func() { idx := -1 for i := range tv.model.tunnels { @@ -183,13 +183,13 @@ func (tv *ListView) SetSuspendTunnelsUpdate(suspend bool) { } func (tv *ListView) Load(asyncUI bool) { - tunnels, err := service.IPCClientTunnels() + tunnels, err := manager.IPCClientTunnels() if err != nil { return } doUI := func() { - newTunnels := make(map[service.Tunnel]bool, len(tunnels)) - oldTunnels := make(map[service.Tunnel]bool, len(tv.model.tunnels)) + newTunnels := make(map[manager.Tunnel]bool, len(tunnels)) + oldTunnels := make(map[manager.Tunnel]bool, len(tv.model.tunnels)) for _, tunnel := range tunnels { newTunnels[tunnel] = true } @@ -245,7 +245,7 @@ func (tv *ListView) selectTunnel(tunnelName string) { } func (tv *ListView) SelectFirstActiveTunnel() { - tunnels := make([]service.Tunnel, len(tv.model.tunnels)) + tunnels := make([]manager.Tunnel, len(tv.model.tunnels)) copy(tunnels, tv.model.tunnels) go func() { for _, tunnel := range tunnels { @@ -253,7 +253,7 @@ func (tv *ListView) SelectFirstActiveTunnel() { if err != nil { continue } - if state == service.TunnelStarting || state == service.TunnelStarted { + if state == manager.TunnelStarting || state == manager.TunnelStarted { tv.Synchronize(func() { tv.selectTunnel(tunnel.Name) }) diff --git a/ui/managewindow.go b/ui/managewindow.go index b1a22ac3..7c643aba 100644 --- a/ui/managewindow.go +++ b/ui/managewindow.go @@ -12,7 +12,7 @@ import ( "github.com/lxn/win" "golang.org/x/sys/windows" - "golang.zx2c4.com/wireguard/windows/service" + "golang.zx2c4.com/wireguard/windows/manager" ) type ManageTunnelsWindow struct { @@ -23,7 +23,7 @@ type ManageTunnelsWindow struct { logPage *LogPage updatePage *UpdatePage - tunnelChangedCB *service.TunnelChangeCallback + tunnelChangedCB *manager.TunnelChangeCallback } const ( @@ -98,9 +98,9 @@ func NewManageTunnelsWindow() (*ManageTunnelsWindow, error) { } mtw.tabs.Pages().Add(mtw.logPage.TabPage) - mtw.tunnelChangedCB = service.IPCClientRegisterTunnelChange(mtw.onTunnelChange) - globalState, _ := service.IPCClientGlobalState() - mtw.onTunnelChange(nil, service.TunnelUnknown, globalState, nil) + mtw.tunnelChangedCB = manager.IPCClientRegisterTunnelChange(mtw.onTunnelChange) + globalState, _ := manager.IPCClientGlobalState() + mtw.onTunnelChange(nil, manager.TunnelUnknown, globalState, nil) systemMenu := win.GetSystemMenu(mtw.Handle(), false) if systemMenu != 0 { @@ -129,26 +129,26 @@ func (mtw *ManageTunnelsWindow) Dispose() { mtw.FormBase.Dispose() } -func (mtw *ManageTunnelsWindow) updateProgressIndicator(globalState service.TunnelState) { +func (mtw *ManageTunnelsWindow) updateProgressIndicator(globalState manager.TunnelState) { pi := mtw.ProgressIndicator() if pi == nil { return } switch globalState { - case service.TunnelStopping, service.TunnelStarting: + case manager.TunnelStopping, manager.TunnelStarting: pi.SetState(walk.PIIndeterminate) default: pi.SetState(walk.PINoProgress) } if icon, err := iconForState(globalState, 16); err == nil { - if globalState == service.TunnelStopped { + if globalState == manager.TunnelStopped { icon = nil } pi.SetOverlayIcon(icon, textForState(globalState, false)) } } -func (mtw *ManageTunnelsWindow) onTunnelChange(tunnel *service.Tunnel, state service.TunnelState, globalState service.TunnelState, err error) { +func (mtw *ManageTunnelsWindow) onTunnelChange(tunnel *manager.Tunnel, state manager.TunnelState, globalState manager.TunnelState, err error) { mtw.Synchronize(func() { mtw.updateProgressIndicator(globalState) @@ -204,7 +204,7 @@ func (mtw *ManageTunnelsWindow) WndProc(hwnd win.HWND, msg uint32, wParam, lPara case taskbarButtonCreatedMsg: ret := mtw.FormBase.WndProc(hwnd, msg, wParam, lParam) go func() { - globalState, err := service.IPCClientGlobalState() + globalState, err := manager.IPCClientGlobalState() if err == nil { mtw.Synchronize(func() { mtw.updateProgressIndicator(globalState) diff --git a/ui/tray.go b/ui/tray.go index d376e749..673439ce 100644 --- a/ui/tray.go +++ b/ui/tray.go @@ -12,9 +12,9 @@ import ( "time" "golang.zx2c4.com/wireguard/windows/conf" + "golang.zx2c4.com/wireguard/windows/manager" "github.com/lxn/walk" - "golang.zx2c4.com/wireguard/windows/service" ) // Status + active CIDRs + separator @@ -28,8 +28,8 @@ type Tray struct { mtw *ManageTunnelsWindow - tunnelChangedCB *service.TunnelChangeCallback - tunnelsChangedCB *service.TunnelsChangeCallback + tunnelChangedCB *manager.TunnelChangeCallback + tunnelsChangedCB *manager.TunnelsChangeCallback clicked func() } @@ -102,10 +102,10 @@ func (tray *Tray) setup() error { tray.ContextMenu().Actions().Add(action) } - tray.tunnelChangedCB = service.IPCClientRegisterTunnelChange(tray.onTunnelChange) - tray.tunnelsChangedCB = service.IPCClientRegisterTunnelsChange(tray.onTunnelsChange) + tray.tunnelChangedCB = manager.IPCClientRegisterTunnelChange(tray.onTunnelChange) + tray.tunnelsChangedCB = manager.IPCClientRegisterTunnelsChange(tray.onTunnelsChange) tray.onTunnelsChange() - globalState, _ := service.IPCClientGlobalState() + globalState, _ := manager.IPCClientGlobalState() tray.updateGlobalState(globalState) return nil @@ -124,7 +124,7 @@ func (tray *Tray) Dispose() error { } func (tray *Tray) onTunnelsChange() { - tunnels, err := service.IPCClientTunnels() + tunnels, err := manager.IPCClientTunnels() if err != nil { return } @@ -144,7 +144,7 @@ func (tray *Tray) onTunnelsChange() { }) } -func (tray *Tray) addTunnelAction(tunnel *service.Tunnel) { +func (tray *Tray) addTunnelAction(tunnel *manager.Tunnel) { tunnelAction := walk.NewAction() tunnelAction.SetText(tunnel.Name) tunnelAction.SetEnabled(true) @@ -159,11 +159,11 @@ func (tray *Tray) addTunnelAction(tunnel *service.Tunnel) { raise(tray.mtw.Handle()) tray.mtw.tunnelsPage.listView.selectTunnel(tclosure.Name) tray.mtw.tabs.SetCurrentIndex(0) - if oldState == service.TunnelUnknown { + if oldState == manager.TunnelUnknown { walk.MsgBox(tray.mtw, "Failed to determine tunnel state", err.Error(), walk.MsgBoxIconError) - } else if oldState == service.TunnelStopped { + } else if oldState == manager.TunnelStopped { walk.MsgBox(tray.mtw, "Failed to activate tunnel", err.Error(), walk.MsgBoxIconError) - } else if oldState == service.TunnelStarted { + } else if oldState == manager.TunnelStarted { walk.MsgBox(tray.mtw, "Failed to deactivate tunnel", err.Error(), walk.MsgBoxIconError) } }) @@ -208,7 +208,7 @@ func (tray *Tray) removeTunnelAction(tunnelName string) { delete(tray.tunnels, tunnelName) } -func (tray *Tray) onTunnelChange(tunnel *service.Tunnel, state service.TunnelState, globalState service.TunnelState, err error) { +func (tray *Tray) onTunnelChange(tunnel *manager.Tunnel, state manager.TunnelState, globalState manager.TunnelState, err error) { tray.mtw.Synchronize(func() { tray.updateGlobalState(globalState) tray.SetTunnelState(tunnel, state, err == nil) @@ -218,7 +218,7 @@ func (tray *Tray) onTunnelChange(tunnel *service.Tunnel, state service.TunnelSta }) } -func (tray *Tray) updateGlobalState(globalState service.TunnelState) { +func (tray *Tray) updateGlobalState(globalState manager.TunnelState) { if icon, err := iconWithOverlayForState(globalState, 16); err == nil { tray.SetIcon(icon) } @@ -238,23 +238,23 @@ func (tray *Tray) updateGlobalState(globalState service.TunnelState) { statusAction.SetText(fmt.Sprintf("Status: %s", textForState(globalState, false))) switch globalState { - case service.TunnelStarting: + case manager.TunnelStarting: setTunnelActionsEnabled(false) - case service.TunnelStarted: + case manager.TunnelStarted: activeCIDRsAction.SetVisible(true) setTunnelActionsEnabled(true) - case service.TunnelStopping: + case manager.TunnelStopping: setTunnelActionsEnabled(false) - case service.TunnelStopped: + case manager.TunnelStopped: activeCIDRsAction.SetVisible(false) setTunnelActionsEnabled(true) } } -func (tray *Tray) SetTunnelState(tunnel *service.Tunnel, state service.TunnelState, showNotifications bool) { +func (tray *Tray) SetTunnelState(tunnel *manager.Tunnel, state manager.TunnelState, showNotifications bool) { tunnelAction := tray.tunnels[tunnel.Name] if tunnelAction == nil { return @@ -266,7 +266,7 @@ func (tray *Tray) SetTunnelState(tunnel *service.Tunnel, state service.TunnelSta wasChecked := tunnelAction.Checked() switch state { - case service.TunnelStarted: + case manager.TunnelStarted: activeCIDRsAction.SetText("") go func() { config, err := tunnel.RuntimeConfig() @@ -291,7 +291,7 @@ func (tray *Tray) SetTunnelState(tunnel *service.Tunnel, state service.TunnelSta tray.ShowCustom("WireGuard Activated", fmt.Sprintf("The %s tunnel has been activated.", tunnel.Name), icon) } - case service.TunnelStopped: + case manager.TunnelStopped: tunnelAction.SetChecked(false) if wasChecked && showNotifications { icon, _ := loadSystemIcon("imageres", 26, 128) //TODO: this icon isn't very good... diff --git a/ui/tunnelspage.go b/ui/tunnelspage.go index d6deffff..847036fe 100644 --- a/ui/tunnelspage.go +++ b/ui/tunnelspage.go @@ -16,8 +16,9 @@ import ( "github.com/lxn/walk" "github.com/lxn/win" + "golang.zx2c4.com/wireguard/windows/conf" - "golang.zx2c4.com/wireguard/windows/service" + "golang.zx2c4.com/wireguard/windows/manager" ) type TunnelsPage struct { @@ -314,7 +315,7 @@ func (tp *TunnelsPage) importFiles(paths []string) { return conf.TunnelNameIsLess(unparsedConfigs[j].Name, unparsedConfigs[i].Name) }) - existingTunnelList, err := service.IPCClientTunnels() + existingTunnelList, err := manager.IPCClientTunnels() if err != nil { syncedMsgBox("Error", fmt.Sprintf("Could not enumerate existing tunnels: %v", lastErr), walk.MsgBoxIconWarning) return @@ -336,7 +337,7 @@ func (tp *TunnelsPage) importFiles(paths []string) { lastErr = err continue } - _, err = service.IPCClientNewTunnel(config) + _, err = manager.IPCClientNewTunnel(config) if err != nil { lastErr = err continue @@ -384,7 +385,7 @@ func (tp *TunnelsPage) exportTunnels(filePath string) { } func (tp *TunnelsPage) addTunnel(config *conf.Config) { - _, err := service.IPCClientNewTunnel(config) + _, err := manager.IPCClientNewTunnel(config) if err != nil { walk.MsgBox(tp.Form(), "Unable to create tunnel", err.Error(), walk.MsgBoxIconError) } @@ -395,18 +396,18 @@ func (tp *TunnelsPage) addTunnel(config *conf.Config) { func (tp *TunnelsPage) onTunnelsViewItemActivated() { go func() { - globalState, err := service.IPCClientGlobalState() - if err != nil || (globalState != service.TunnelStarted && globalState != service.TunnelStopped) { + globalState, err := manager.IPCClientGlobalState() + if err != nil || (globalState != manager.TunnelStarted && globalState != manager.TunnelStopped) { return } oldState, err := tp.listView.CurrentTunnel().Toggle() if err != nil { tp.Synchronize(func() { - if oldState == service.TunnelUnknown { + if oldState == manager.TunnelUnknown { walk.MsgBox(tp.Form(), "Failed to determine tunnel state", err.Error(), walk.MsgBoxIconError) - } else if oldState == service.TunnelStopped { + } else if oldState == manager.TunnelStopped { walk.MsgBox(tp.Form(), "Failed to activate tunnel", err.Error(), walk.MsgBoxIconError) - } else if oldState == service.TunnelStarted { + } else if oldState == manager.TunnelStarted { walk.MsgBox(tp.Form(), "Failed to deactivate tunnel", err.Error(), walk.MsgBoxIconError) } }) @@ -426,8 +427,8 @@ func (tp *TunnelsPage) onEditTunnel() { priorState, err := tunnel.State() tunnel.Delete() tunnel.WaitForStop() - tunnel, err2 := service.IPCClientNewTunnel(config) - if err == nil && err2 == nil && (priorState == service.TunnelStarting || priorState == service.TunnelStarted) { + tunnel, err2 := manager.IPCClientNewTunnel(config) + if err == nil && err2 == nil && (priorState == manager.TunnelStarting || priorState == manager.TunnelStarted) { tunnel.Start() } }() @@ -490,7 +491,7 @@ func (tp *TunnelsPage) onDelete() { tp.listView.selectTunnel(selectTunnelAfter) } - tunnelsToDelete := make([]service.Tunnel, len(indices)) + tunnelsToDelete := make([]manager.Tunnel, len(indices)) for i, j := range indices { tunnelsToDelete[i] = tp.listView.model.tunnels[j] diff --git a/ui/ui.go b/ui/ui.go index 6805754c..0f91b61e 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -13,7 +13,8 @@ import ( "github.com/lxn/walk" "github.com/lxn/win" - "golang.zx2c4.com/wireguard/windows/service" + + "golang.zx2c4.com/wireguard/windows/manager" "golang.zx2c4.com/wireguard/windows/version" ) @@ -55,29 +56,29 @@ func RunUI() { } } - service.IPCClientRegisterManagerStopping(func() { + manager.IPCClientRegisterManagerStopping(func() { mtw.Synchronize(func() { walk.App().Exit(0) }) }) - onUpdateNotification := func(updateState service.UpdateState) { - if updateState == service.UpdateStateUnknown { + onUpdateNotification := func(updateState manager.UpdateState) { + if updateState == manager.UpdateStateUnknown { return } mtw.Synchronize(func() { switch updateState { - case service.UpdateStateFoundUpdate: + case manager.UpdateStateFoundUpdate: mtw.UpdateFound() tray.UpdateFound() - case service.UpdateStateUpdatesDisabledUnofficialBuild: + case manager.UpdateStateUpdatesDisabledUnofficialBuild: mtw.SetTitle(mtw.Title() + " (unsigned build, no updates)") } }) } - service.IPCClientRegisterUpdateFound(onUpdateNotification) + manager.IPCClientRegisterUpdateFound(onUpdateNotification) go func() { - updateState, err := service.IPCClientUpdateState() + updateState, err := manager.IPCClientUpdateState() if err == nil { onUpdateNotification(updateState) } @@ -92,7 +93,7 @@ func RunUI() { mtw.Dispose() if shouldQuitManagerWhenExiting { - _, err := service.IPCClientQuit(true) + _, err := manager.IPCClientQuit(true) if err != nil { walk.MsgBox(nil, "Error Exiting WireGuard", fmt.Sprintf("Unable to exit service due to: %s. You may want to stop WireGuard from the service manager.", err), walk.MsgBoxIconError) } diff --git a/ui/updatepage.go b/ui/updatepage.go index 1cab0d26..95657798 100644 --- a/ui/updatepage.go +++ b/ui/updatepage.go @@ -9,7 +9,8 @@ import ( "fmt" "github.com/lxn/walk" - "golang.zx2c4.com/wireguard/windows/service" + + "golang.zx2c4.com/wireguard/windows/manager" "golang.zx2c4.com/wireguard/windows/updater" ) @@ -77,14 +78,14 @@ func NewUpdatePage() (*UpdatePage, error) { button.Clicked().Attach(func() { switchToUpdatingState() - err := service.IPCClientUpdate() + err := manager.IPCClientUpdate() if err != nil { switchToReadyState() status.SetText(fmt.Sprintf("Error: %v. Please try again.", err)) } }) - service.IPCClientRegisterUpdateProgress(func(dp updater.DownloadProgress) { + manager.IPCClientRegisterUpdateProgress(func(dp updater.DownloadProgress) { up.Synchronize(func() { switchToUpdatingState() if dp.Error != nil { -- cgit v1.2.3-59-g8ed1b