From cdb8c53cdea8d8ac6e6f2112e4a5e844bffd01a4 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 Signed-off-by: Jason A. Donenfeld --- 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 ++++++ 13 files changed, 3505 insertions(+) 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 (limited to 'tunnel') 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 + } + } +} -- cgit v1.2.3-59-g8ed1b