From 950ca2ba8c026be809ced2438f89ec9146734cf7 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Fri, 30 Aug 2019 10:31:27 -0600 Subject: wintun: put mutex into private namespace --- tun/wintun/namespace_windows.go | 99 +++++++++++++++++++++++++++++++++++++++++ tun/wintun/ring.go | 97 ---------------------------------------- tun/wintun/ring_windows.go | 97 ++++++++++++++++++++++++++++++++++++++++ tun/wintun/wintun_windows.go | 26 ----------- 4 files changed, 196 insertions(+), 123 deletions(-) create mode 100644 tun/wintun/namespace_windows.go delete mode 100644 tun/wintun/ring.go create mode 100644 tun/wintun/ring_windows.go diff --git a/tun/wintun/namespace_windows.go b/tun/wintun/namespace_windows.go new file mode 100644 index 0000000..21791ef --- /dev/null +++ b/tun/wintun/namespace_windows.go @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package wintun + +import ( + "encoding/hex" + "errors" + "fmt" + "sync" + "unsafe" + + "golang.org/x/crypto/blake2s" + "golang.org/x/sys/windows" + "golang.org/x/text/unicode/norm" + + "golang.zx2c4.com/wireguard/ipc/winpipe" + "golang.zx2c4.com/wireguard/tun/wintun/namespaceapi" +) + +var ( + wintunObjectSecurityAttributes *windows.SecurityAttributes + hasInitializedNamespace bool + initializingNamespace sync.Mutex +) + +func initializeNamespace() error { + initializingNamespace.Lock() + defer initializingNamespace.Unlock() + if hasInitializedNamespace { + return nil + } + sd, err := winpipe.SddlToSecurityDescriptor("O:SYD:P(A;;GA;;;SY)") + if err != nil { + return fmt.Errorf("SddlToSecurityDescriptor failed: %v", err) + } + wintunObjectSecurityAttributes = &windows.SecurityAttributes{ + Length: uint32(len(sd)), + SecurityDescriptor: uintptr(unsafe.Pointer(&sd[0])), + } + sid, err := windows.CreateWellKnownSid(windows.WinLocalSystemSid) + if err != nil { + return fmt.Errorf("CreateWellKnownSid(LOCAL_SYSTEM) failed: %v", err) + } + + boundary, err := namespaceapi.CreateBoundaryDescriptor("Wintun") + if err != nil { + return fmt.Errorf("CreateBoundaryDescriptor failed: %v", err) + } + err = boundary.AddSid(sid) + if err != nil { + return fmt.Errorf("AddSIDToBoundaryDescriptor failed: %v", err) + } + for { + _, err = namespaceapi.CreatePrivateNamespace(wintunObjectSecurityAttributes, boundary, "Wintun") + if err == windows.ERROR_ALREADY_EXISTS { + _, err = namespaceapi.OpenPrivateNamespace(boundary, "Wintun") + if err == windows.ERROR_PATH_NOT_FOUND { + continue + } + } + if err != nil { + return fmt.Errorf("Create/OpenPrivateNamespace failed: %v", err) + } + break + } + hasInitializedNamespace = true + return nil +} + +func (pool Pool) takeNameMutex() (windows.Handle, error) { + err := initializeNamespace() + if err != nil { + return 0, err + } + + const mutexLabel = "WireGuard Adapter Name Mutex Stable Suffix v1 jason@zx2c4.com" + b2, _ := blake2s.New256(nil) + b2.Write([]byte(mutexLabel)) + b2.Write(norm.NFC.Bytes([]byte(string(pool)))) + mutexName := `Wintun\Wintun-Name-Mutex-` + hex.EncodeToString(b2.Sum(nil)) + mutex, err := windows.CreateMutex(wintunObjectSecurityAttributes, false, windows.StringToUTF16Ptr(mutexName)) + if err != nil { + err = fmt.Errorf("Error creating name mutex: %v", err) + return 0, err + } + event, err := windows.WaitForSingleObject(mutex, windows.INFINITE) + if err != nil { + windows.CloseHandle(mutex) + return 0, fmt.Errorf("Error waiting on name mutex: %v", err) + } + if event != windows.WAIT_OBJECT_0 { + windows.CloseHandle(mutex) + return 0, errors.New("Error with event trigger of name mutex") + } + return mutex, nil +} diff --git a/tun/wintun/ring.go b/tun/wintun/ring.go deleted file mode 100644 index 8f46bc9..0000000 --- a/tun/wintun/ring.go +++ /dev/null @@ -1,97 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package wintun - -import ( - "unsafe" - - "golang.org/x/sys/windows" -) - -const ( - PacketAlignment = 4 // Number of bytes packets are aligned to in rings - PacketSizeMax = 0xffff // Maximum packet size - PacketCapacity = 0x800000 // Ring capacity, 8MiB - PacketTrailingSize = uint32(unsafe.Sizeof(PacketHeader{})) + ((PacketSizeMax + (PacketAlignment - 1)) &^ (PacketAlignment - 1)) - PacketAlignment - ioctlRegisterRings = (51820 << 16) | (0x970 << 2) | 0 /*METHOD_BUFFERED*/ | (0x3 /*FILE_READ_DATA | FILE_WRITE_DATA*/ << 14) -) - -type PacketHeader struct { - Size uint32 -} - -type Packet struct { - PacketHeader - Data [PacketSizeMax]byte -} - -type Ring struct { - Head uint32 - Tail uint32 - Alertable int32 - Data [PacketCapacity + PacketTrailingSize]byte -} - -type RingDescriptor struct { - Send, Receive struct { - Size uint32 - Ring *Ring - TailMoved windows.Handle - } -} - -// Wrap returns value modulo ring capacity -func (rb *Ring) Wrap(value uint32) uint32 { - return value & (PacketCapacity - 1) -} - -// Aligns a packet size to PacketAlignment -func PacketAlign(size uint32) uint32 { - return (size + (PacketAlignment - 1)) &^ (PacketAlignment - 1) -} - -func (descriptor *RingDescriptor) Init() (err error) { - descriptor.Send.Size = uint32(unsafe.Sizeof(Ring{})) - descriptor.Send.Ring = &Ring{} - descriptor.Send.TailMoved, err = windows.CreateEvent(nil, 0, 0, nil) - if err != nil { - return - } - - descriptor.Receive.Size = uint32(unsafe.Sizeof(Ring{})) - descriptor.Receive.Ring = &Ring{} - descriptor.Receive.TailMoved, err = windows.CreateEvent(nil, 0, 0, nil) - if err != nil { - windows.CloseHandle(descriptor.Send.TailMoved) - return - } - - return -} - -func (descriptor *RingDescriptor) Close() { - if descriptor.Send.TailMoved != 0 { - windows.CloseHandle(descriptor.Send.TailMoved) - descriptor.Send.TailMoved = 0 - } - if descriptor.Send.TailMoved != 0 { - windows.CloseHandle(descriptor.Receive.TailMoved) - descriptor.Receive.TailMoved = 0 - } -} - -func (wintun *Interface) Register(descriptor *RingDescriptor) (windows.Handle, error) { - handle, err := wintun.handle() - if err != nil { - return 0, err - } - var bytesReturned uint32 - err = windows.DeviceIoControl(handle, ioctlRegisterRings, (*byte)(unsafe.Pointer(descriptor)), uint32(unsafe.Sizeof(*descriptor)), nil, 0, &bytesReturned, nil) - if err != nil { - return 0, err - } - return handle, nil -} diff --git a/tun/wintun/ring_windows.go b/tun/wintun/ring_windows.go new file mode 100644 index 0000000..8f46bc9 --- /dev/null +++ b/tun/wintun/ring_windows.go @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package wintun + +import ( + "unsafe" + + "golang.org/x/sys/windows" +) + +const ( + PacketAlignment = 4 // Number of bytes packets are aligned to in rings + PacketSizeMax = 0xffff // Maximum packet size + PacketCapacity = 0x800000 // Ring capacity, 8MiB + PacketTrailingSize = uint32(unsafe.Sizeof(PacketHeader{})) + ((PacketSizeMax + (PacketAlignment - 1)) &^ (PacketAlignment - 1)) - PacketAlignment + ioctlRegisterRings = (51820 << 16) | (0x970 << 2) | 0 /*METHOD_BUFFERED*/ | (0x3 /*FILE_READ_DATA | FILE_WRITE_DATA*/ << 14) +) + +type PacketHeader struct { + Size uint32 +} + +type Packet struct { + PacketHeader + Data [PacketSizeMax]byte +} + +type Ring struct { + Head uint32 + Tail uint32 + Alertable int32 + Data [PacketCapacity + PacketTrailingSize]byte +} + +type RingDescriptor struct { + Send, Receive struct { + Size uint32 + Ring *Ring + TailMoved windows.Handle + } +} + +// Wrap returns value modulo ring capacity +func (rb *Ring) Wrap(value uint32) uint32 { + return value & (PacketCapacity - 1) +} + +// Aligns a packet size to PacketAlignment +func PacketAlign(size uint32) uint32 { + return (size + (PacketAlignment - 1)) &^ (PacketAlignment - 1) +} + +func (descriptor *RingDescriptor) Init() (err error) { + descriptor.Send.Size = uint32(unsafe.Sizeof(Ring{})) + descriptor.Send.Ring = &Ring{} + descriptor.Send.TailMoved, err = windows.CreateEvent(nil, 0, 0, nil) + if err != nil { + return + } + + descriptor.Receive.Size = uint32(unsafe.Sizeof(Ring{})) + descriptor.Receive.Ring = &Ring{} + descriptor.Receive.TailMoved, err = windows.CreateEvent(nil, 0, 0, nil) + if err != nil { + windows.CloseHandle(descriptor.Send.TailMoved) + return + } + + return +} + +func (descriptor *RingDescriptor) Close() { + if descriptor.Send.TailMoved != 0 { + windows.CloseHandle(descriptor.Send.TailMoved) + descriptor.Send.TailMoved = 0 + } + if descriptor.Send.TailMoved != 0 { + windows.CloseHandle(descriptor.Receive.TailMoved) + descriptor.Receive.TailMoved = 0 + } +} + +func (wintun *Interface) Register(descriptor *RingDescriptor) (windows.Handle, error) { + handle, err := wintun.handle() + if err != nil { + return 0, err + } + var bytesReturned uint32 + err = windows.DeviceIoControl(handle, ioctlRegisterRings, (*byte)(unsafe.Pointer(descriptor)), uint32(unsafe.Sizeof(*descriptor)), nil, 0, &bytesReturned, nil) + if err != nil { + return 0, err + } + return handle, nil +} diff --git a/tun/wintun/wintun_windows.go b/tun/wintun/wintun_windows.go index d6fa35f..c654b02 100644 --- a/tun/wintun/wintun_windows.go +++ b/tun/wintun/wintun_windows.go @@ -6,17 +6,14 @@ package wintun import ( - "encoding/hex" "errors" "fmt" "strings" "time" "unsafe" - "golang.org/x/crypto/blake2s" "golang.org/x/sys/windows" "golang.org/x/sys/windows/registry" - "golang.org/x/text/unicode/norm" "golang.zx2c4.com/wireguard/tun/wintun/iphlpapi" "golang.zx2c4.com/wireguard/tun/wintun/nci" @@ -758,26 +755,3 @@ func (wintun *Interface) GUID() windows.GUID { func (wintun *Interface) LUID() uint64 { return ((uint64(wintun.luidIndex) & ((1 << 24) - 1)) << 24) | ((uint64(wintun.ifType) & ((1 << 16) - 1)) << 48) } - -func (pool Pool) takeNameMutex() (windows.Handle, error) { - const mutexLabel = "WireGuard Adapter Name Mutex Stable Suffix v1 jason@zx2c4.com" - b2, _ := blake2s.New256(nil) - b2.Write([]byte(mutexLabel)) - b2.Write(norm.NFC.Bytes([]byte(string(pool)))) - mutexName := `Global\Wintun-Name-Mutex-` + hex.EncodeToString(b2.Sum(nil)) - mutex, err := windows.CreateMutex(nil, false, windows.StringToUTF16Ptr(mutexName)) - if err != nil { - err = fmt.Errorf("Error creating name mutex: %v", err) - return 0, err - } - event, err := windows.WaitForSingleObject(mutex, windows.INFINITE) - if err != nil { - windows.CloseHandle(mutex) - return 0, fmt.Errorf("Error waiting on name mutex: %v", err) - } - if event != windows.WAIT_OBJECT_0 { - windows.CloseHandle(mutex) - return 0, errors.New("Error with event trigger of name mutex") - } - return mutex, nil -} \ No newline at end of file -- cgit v1.2.3