aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/tunnel/firewall/blocker.go
diff options
context:
space:
mode:
Diffstat (limited to 'tunnel/firewall/blocker.go')
-rw-r--r--tunnel/firewall/blocker.go201
1 files changed, 201 insertions, 0 deletions
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
+ }
+}