2020-10-24 08:28:17 +02:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0
|
|
|
|
*
|
|
|
|
* Copyright (C) 2018-2020 WireGuard LLC. All Rights Reserved.
|
|
|
|
*/
|
|
|
|
|
2020-10-31 11:55:26 +01:00
|
|
|
#include "adapter.h"
|
|
|
|
#include "elevate.h"
|
|
|
|
#include "entry.h"
|
|
|
|
#include "logger.h"
|
|
|
|
#include "wintun.h"
|
|
|
|
#include <Windows.h>
|
2020-10-24 08:28:17 +02:00
|
|
|
|
|
|
|
#pragma warning(disable : 4200) /* nonstandard: zero-sized array in struct/union */
|
|
|
|
|
|
|
|
#define TUN_ALIGNMENT sizeof(ULONG)
|
|
|
|
#define TUN_ALIGN(Size) (((ULONG)(Size) + ((ULONG)TUN_ALIGNMENT - 1)) & ~((ULONG)TUN_ALIGNMENT - 1))
|
|
|
|
#define TUN_IS_ALIGNED(Size) (!((ULONG)(Size) & ((ULONG)TUN_ALIGNMENT - 1)))
|
|
|
|
#define TUN_MAX_PACKET_SIZE TUN_ALIGN(sizeof(TUN_PACKET) + WINTUN_MAX_IP_PACKET_SIZE)
|
|
|
|
#define TUN_RING_CAPACITY(Size) ((Size) - sizeof(TUN_RING) - (TUN_MAX_PACKET_SIZE - TUN_ALIGNMENT))
|
|
|
|
#define TUN_RING_SIZE(Capacity) (sizeof(TUN_RING) + (Capacity) + (TUN_MAX_PACKET_SIZE - TUN_ALIGNMENT))
|
|
|
|
#define TUN_RING_WRAP(Value, Capacity) ((Value) & (Capacity - 1))
|
2020-10-25 10:01:35 +01:00
|
|
|
#define LOCK_SPIN_COUNT 0x10000
|
|
|
|
#define TUN_PACKET_RELEASE ((DWORD)0x80000000)
|
2020-10-24 08:28:17 +02:00
|
|
|
|
|
|
|
typedef struct _TUN_PACKET
|
|
|
|
{
|
|
|
|
ULONG Size;
|
2020-10-25 10:01:35 +01:00
|
|
|
UCHAR Data[];
|
2020-10-24 08:28:17 +02:00
|
|
|
} TUN_PACKET;
|
|
|
|
|
|
|
|
typedef struct _TUN_RING
|
|
|
|
{
|
|
|
|
volatile ULONG Head;
|
|
|
|
volatile ULONG Tail;
|
|
|
|
volatile LONG Alertable;
|
|
|
|
UCHAR Data[];
|
|
|
|
} TUN_RING;
|
|
|
|
|
|
|
|
#define TUN_IOCTL_REGISTER_RINGS CTL_CODE(51820U, 0x970U, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
|
|
|
|
|
|
|
|
typedef struct _TUN_REGISTER_RINGS
|
|
|
|
{
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
ULONG RingSize;
|
|
|
|
TUN_RING *Ring;
|
|
|
|
HANDLE TailMoved;
|
|
|
|
} Send, Receive;
|
|
|
|
} TUN_REGISTER_RINGS;
|
|
|
|
|
|
|
|
typedef struct _TUN_SESSION
|
|
|
|
{
|
|
|
|
ULONG Capacity;
|
2020-10-25 10:01:35 +01:00
|
|
|
struct
|
|
|
|
{
|
|
|
|
ULONG Tail;
|
|
|
|
ULONG TailRelease;
|
|
|
|
ULONG PacketsToRelease;
|
|
|
|
CRITICAL_SECTION Lock;
|
|
|
|
} Receive;
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
ULONG Head;
|
|
|
|
ULONG HeadRelease;
|
|
|
|
ULONG PacketsToRelease;
|
|
|
|
CRITICAL_SECTION Lock;
|
|
|
|
} Send;
|
2020-10-24 08:28:17 +02:00
|
|
|
TUN_REGISTER_RINGS Descriptor;
|
|
|
|
HANDLE Handle;
|
|
|
|
} TUN_SESSION;
|
|
|
|
|
|
|
|
WINTUN_STATUS WINAPI
|
2020-10-31 10:28:25 +01:00
|
|
|
WintunStartSession(
|
|
|
|
_In_ const WINTUN_ADAPTER *Adapter,
|
|
|
|
_In_ DWORD Capacity,
|
|
|
|
_Out_ TUN_SESSION **Session,
|
|
|
|
_Out_ HANDLE *ReadWait)
|
2020-10-24 08:28:17 +02:00
|
|
|
{
|
2020-10-30 13:08:57 +01:00
|
|
|
TUN_SESSION *s = HeapAlloc(ModuleHeap, HEAP_ZERO_MEMORY, sizeof(TUN_SESSION));
|
|
|
|
if (!s)
|
2020-10-24 08:28:17 +02:00
|
|
|
return LOG(WINTUN_LOG_ERR, L"Out of memory"), ERROR_OUTOFMEMORY;
|
|
|
|
const ULONG RingSize = TUN_RING_SIZE(Capacity);
|
|
|
|
DWORD Result;
|
|
|
|
BYTE *AllocatedRegion = VirtualAlloc(0, (size_t)RingSize * 2, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
|
|
if (!AllocatedRegion)
|
|
|
|
{
|
|
|
|
Result = LOG_LAST_ERROR(L"Failed to allocate ring memory");
|
|
|
|
goto cleanupRings;
|
|
|
|
}
|
2020-10-30 12:25:24 +01:00
|
|
|
if (!ElevateToSystem())
|
|
|
|
{
|
|
|
|
LOG(WINTUN_LOG_ERR, L"Failed to impersonate SYSTEM user");
|
|
|
|
Result = ERROR_ACCESS_DENIED;
|
|
|
|
goto cleanupAllocatedRegion;
|
|
|
|
}
|
2020-10-30 13:08:57 +01:00
|
|
|
s->Descriptor.Send.RingSize = RingSize;
|
|
|
|
s->Descriptor.Send.Ring = (TUN_RING *)AllocatedRegion;
|
|
|
|
s->Descriptor.Send.TailMoved = CreateEventW(&SecurityAttributes, FALSE, FALSE, NULL);
|
|
|
|
if (!s->Descriptor.Send.TailMoved)
|
2020-10-24 08:28:17 +02:00
|
|
|
{
|
|
|
|
Result = LOG_LAST_ERROR(L"Failed to create send event");
|
2020-10-30 12:25:24 +01:00
|
|
|
goto cleanupToken;
|
2020-10-24 08:28:17 +02:00
|
|
|
}
|
|
|
|
|
2020-10-30 13:08:57 +01:00
|
|
|
s->Descriptor.Receive.RingSize = RingSize;
|
|
|
|
s->Descriptor.Receive.Ring = (TUN_RING *)(AllocatedRegion + RingSize);
|
|
|
|
s->Descriptor.Receive.TailMoved = CreateEventW(&SecurityAttributes, FALSE, FALSE, NULL);
|
|
|
|
if (!s->Descriptor.Receive.TailMoved)
|
2020-10-24 08:28:17 +02:00
|
|
|
{
|
|
|
|
Result = LOG_LAST_ERROR(L"Failed to create receive event");
|
|
|
|
goto cleanupSendTailMoved;
|
|
|
|
}
|
|
|
|
|
2020-10-30 13:08:57 +01:00
|
|
|
Result = WintunGetAdapterDeviceObject(Adapter, &s->Handle);
|
2020-10-24 08:28:17 +02:00
|
|
|
if (Result != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
LOG(WINTUN_LOG_ERR, L"Failed to open adapter device object");
|
|
|
|
goto cleanupReceiveTailMoved;
|
|
|
|
}
|
|
|
|
DWORD BytesReturned;
|
|
|
|
if (!DeviceIoControl(
|
2020-10-30 13:08:57 +01:00
|
|
|
s->Handle,
|
|
|
|
TUN_IOCTL_REGISTER_RINGS,
|
|
|
|
&s->Descriptor,
|
|
|
|
sizeof(TUN_REGISTER_RINGS),
|
|
|
|
NULL,
|
|
|
|
0,
|
|
|
|
&BytesReturned,
|
2020-10-24 08:28:17 +02:00
|
|
|
NULL))
|
|
|
|
{
|
|
|
|
Result = LOG_LAST_ERROR(L"Failed to perform ioctl");
|
|
|
|
goto cleanupHandle;
|
|
|
|
}
|
2020-10-30 12:25:24 +01:00
|
|
|
RevertToSelf();
|
2020-10-30 13:08:57 +01:00
|
|
|
s->Capacity = Capacity;
|
|
|
|
(void)InitializeCriticalSectionAndSpinCount(&s->Receive.Lock, LOCK_SPIN_COUNT);
|
|
|
|
(void)InitializeCriticalSectionAndSpinCount(&s->Send.Lock, LOCK_SPIN_COUNT);
|
|
|
|
*Session = s;
|
2020-10-31 10:28:25 +01:00
|
|
|
*ReadWait = s->Descriptor.Send.TailMoved;
|
2020-10-24 08:28:17 +02:00
|
|
|
return ERROR_SUCCESS;
|
|
|
|
cleanupHandle:
|
2020-10-30 13:08:57 +01:00
|
|
|
CloseHandle(s->Handle);
|
2020-10-24 08:28:17 +02:00
|
|
|
cleanupReceiveTailMoved:
|
2020-10-30 13:08:57 +01:00
|
|
|
CloseHandle(s->Descriptor.Receive.TailMoved);
|
2020-10-24 08:28:17 +02:00
|
|
|
cleanupSendTailMoved:
|
2020-10-30 13:08:57 +01:00
|
|
|
CloseHandle(s->Descriptor.Send.TailMoved);
|
2020-10-30 12:25:24 +01:00
|
|
|
cleanupToken:
|
|
|
|
RevertToSelf();
|
2020-10-24 08:28:17 +02:00
|
|
|
cleanupAllocatedRegion:
|
|
|
|
VirtualFree(AllocatedRegion, 0, MEM_RELEASE);
|
|
|
|
cleanupRings:
|
2020-10-30 13:08:57 +01:00
|
|
|
HeapFree(ModuleHeap, 0, s);
|
2020-10-24 08:28:17 +02:00
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WINAPI
|
|
|
|
WintunEndSession(_In_ TUN_SESSION *Session)
|
|
|
|
{
|
2020-10-30 12:25:24 +01:00
|
|
|
SetEvent(Session->Descriptor.Send.TailMoved); // Wake the reader if it's sleeping.
|
2020-10-25 10:01:35 +01:00
|
|
|
DeleteCriticalSection(&Session->Send.Lock);
|
|
|
|
DeleteCriticalSection(&Session->Receive.Lock);
|
2020-10-24 08:28:17 +02:00
|
|
|
CloseHandle(Session->Handle);
|
|
|
|
CloseHandle(Session->Descriptor.Send.TailMoved);
|
|
|
|
CloseHandle(Session->Descriptor.Receive.TailMoved);
|
|
|
|
VirtualFree(Session->Descriptor.Send.Ring, 0, MEM_RELEASE);
|
2020-10-24 22:12:47 +02:00
|
|
|
HeapFree(ModuleHeap, 0, Session);
|
2020-10-24 08:28:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
WINTUN_STATUS WINAPI
|
2020-10-25 10:01:35 +01:00
|
|
|
WintunReceivePacket(_In_ TUN_SESSION *Session, _Out_bytecapcount_(*PacketSize) BYTE **Packet, _Out_ DWORD *PacketSize)
|
2020-10-24 08:28:17 +02:00
|
|
|
{
|
2020-10-25 10:01:35 +01:00
|
|
|
DWORD Result;
|
|
|
|
EnterCriticalSection(&Session->Send.Lock);
|
|
|
|
if (Session->Send.Head >= Session->Capacity)
|
2020-10-24 08:28:17 +02:00
|
|
|
{
|
2020-10-25 10:01:35 +01:00
|
|
|
Result = ERROR_HANDLE_EOF;
|
|
|
|
goto cleanup;
|
2020-10-24 08:28:17 +02:00
|
|
|
}
|
2020-10-30 16:58:50 +01:00
|
|
|
const ULONG BuffTail = ReadULongAcquire(&Session->Descriptor.Send.Ring->Tail);
|
2020-10-25 10:01:35 +01:00
|
|
|
if (BuffTail >= Session->Capacity)
|
|
|
|
{
|
|
|
|
Result = ERROR_HANDLE_EOF;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (Session->Send.Head == BuffTail)
|
|
|
|
{
|
|
|
|
Result = ERROR_NO_MORE_ITEMS;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
const ULONG BuffContent = TUN_RING_WRAP(BuffTail - Session->Send.Head, Session->Capacity);
|
|
|
|
if (BuffContent < sizeof(TUN_PACKET))
|
|
|
|
{
|
|
|
|
Result = ERROR_INVALID_DATA;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
TUN_PACKET *BuffPacket = (TUN_PACKET *)&Session->Descriptor.Send.Ring->Data[Session->Send.Head];
|
|
|
|
if (BuffPacket->Size > WINTUN_MAX_IP_PACKET_SIZE)
|
|
|
|
{
|
|
|
|
Result = ERROR_INVALID_DATA;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
const ULONG AlignedPacketSize = TUN_ALIGN(sizeof(TUN_PACKET) + BuffPacket->Size);
|
|
|
|
if (AlignedPacketSize > BuffContent)
|
|
|
|
{
|
|
|
|
Result = ERROR_INVALID_DATA;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
*PacketSize = BuffPacket->Size;
|
|
|
|
*Packet = BuffPacket->Data;
|
|
|
|
Session->Send.Head = TUN_RING_WRAP(Session->Send.Head + AlignedPacketSize, Session->Capacity);
|
|
|
|
Session->Send.PacketsToRelease++;
|
|
|
|
Result = ERROR_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
LeaveCriticalSection(&Session->Send.Lock);
|
|
|
|
return Result;
|
|
|
|
}
|
2020-10-24 08:28:17 +02:00
|
|
|
|
2020-10-25 10:01:35 +01:00
|
|
|
void WINAPI
|
|
|
|
WintunReceiveRelease(_In_ TUN_SESSION *Session, _In_ const BYTE *Packet)
|
|
|
|
{
|
|
|
|
EnterCriticalSection(&Session->Send.Lock);
|
|
|
|
TUN_PACKET *ReleasedBuffPacket = (TUN_PACKET *)(Packet - offsetof(TUN_PACKET, Data));
|
|
|
|
ReleasedBuffPacket->Size |= TUN_PACKET_RELEASE;
|
|
|
|
while (Session->Send.PacketsToRelease)
|
|
|
|
{
|
|
|
|
const TUN_PACKET *BuffPacket = (TUN_PACKET *)&Session->Descriptor.Send.Ring->Data[Session->Send.HeadRelease];
|
|
|
|
if ((BuffPacket->Size & TUN_PACKET_RELEASE) == 0)
|
|
|
|
break;
|
|
|
|
const ULONG AlignedPacketSize = TUN_ALIGN(sizeof(TUN_PACKET) + (BuffPacket->Size & ~TUN_PACKET_RELEASE));
|
|
|
|
Session->Send.HeadRelease = TUN_RING_WRAP(Session->Send.HeadRelease + AlignedPacketSize, Session->Capacity);
|
|
|
|
Session->Send.PacketsToRelease--;
|
|
|
|
}
|
2020-10-30 16:58:50 +01:00
|
|
|
WriteULongRelease(&Session->Descriptor.Send.Ring->Head, Session->Send.HeadRelease);
|
2020-10-25 10:01:35 +01:00
|
|
|
LeaveCriticalSection(&Session->Send.Lock);
|
2020-10-24 08:28:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
WINTUN_STATUS WINAPI
|
2020-10-25 10:01:35 +01:00
|
|
|
WintunAllocateSendPacket(_In_ TUN_SESSION *Session, _In_ DWORD PacketSize, _Out_bytecapcount_(PacketSize) BYTE **Packet)
|
2020-10-24 08:28:17 +02:00
|
|
|
{
|
2020-10-25 10:01:35 +01:00
|
|
|
DWORD Result;
|
|
|
|
EnterCriticalSection(&Session->Receive.Lock);
|
|
|
|
if (Session->Receive.Tail >= Session->Capacity)
|
2020-10-24 08:28:17 +02:00
|
|
|
{
|
2020-10-25 10:01:35 +01:00
|
|
|
Result = ERROR_HANDLE_EOF;
|
|
|
|
goto cleanup;
|
2020-10-24 08:28:17 +02:00
|
|
|
}
|
2020-10-25 10:01:35 +01:00
|
|
|
const ULONG AlignedPacketSize = TUN_ALIGN(sizeof(TUN_PACKET) + PacketSize);
|
2020-10-30 16:58:50 +01:00
|
|
|
const ULONG BuffHead = ReadULongAcquire(&Session->Descriptor.Receive.Ring->Head);
|
2020-10-25 10:01:35 +01:00
|
|
|
if (BuffHead >= Session->Capacity)
|
|
|
|
{
|
|
|
|
Result = ERROR_HANDLE_EOF;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
const ULONG BuffSpace = TUN_RING_WRAP(BuffHead - Session->Receive.Tail - TUN_ALIGNMENT, Session->Capacity);
|
|
|
|
if (AlignedPacketSize > BuffSpace)
|
|
|
|
{
|
|
|
|
Result = ERROR_BUFFER_OVERFLOW;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
TUN_PACKET *BuffPacket = (TUN_PACKET *)&Session->Descriptor.Receive.Ring->Data[Session->Receive.Tail];
|
|
|
|
BuffPacket->Size = PacketSize | TUN_PACKET_RELEASE;
|
|
|
|
*Packet = BuffPacket->Data;
|
|
|
|
Session->Receive.Tail = TUN_RING_WRAP(Session->Receive.Tail + AlignedPacketSize, Session->Capacity);
|
|
|
|
Session->Receive.PacketsToRelease++;
|
|
|
|
Result = ERROR_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
LeaveCriticalSection(&Session->Receive.Lock);
|
|
|
|
return Result;
|
|
|
|
}
|
2020-10-24 08:28:17 +02:00
|
|
|
|
2020-10-25 10:01:35 +01:00
|
|
|
void WINAPI
|
|
|
|
WintunSendPacket(_In_ TUN_SESSION *Session, _In_ const BYTE *Packet)
|
|
|
|
{
|
|
|
|
EnterCriticalSection(&Session->Receive.Lock);
|
|
|
|
TUN_PACKET *ReleasedBuffPacket = (TUN_PACKET *)(Packet - offsetof(TUN_PACKET, Data));
|
|
|
|
ReleasedBuffPacket->Size &= ~TUN_PACKET_RELEASE;
|
|
|
|
while (Session->Receive.PacketsToRelease)
|
|
|
|
{
|
|
|
|
const TUN_PACKET *BuffPacket =
|
|
|
|
(TUN_PACKET *)&Session->Descriptor.Receive.Ring->Data[Session->Receive.TailRelease];
|
|
|
|
if (BuffPacket->Size & TUN_PACKET_RELEASE)
|
|
|
|
break;
|
|
|
|
const ULONG AlignedPacketSize = TUN_ALIGN(sizeof(TUN_PACKET) + BuffPacket->Size);
|
|
|
|
Session->Receive.TailRelease =
|
|
|
|
TUN_RING_WRAP(Session->Receive.TailRelease + AlignedPacketSize, Session->Capacity);
|
|
|
|
Session->Receive.PacketsToRelease--;
|
|
|
|
}
|
2020-10-30 16:58:50 +01:00
|
|
|
WriteULongRelease(&Session->Descriptor.Receive.Ring->Tail, Session->Receive.TailRelease);
|
|
|
|
if (ReadAcquire(&Session->Descriptor.Receive.Ring->Alertable))
|
2020-10-25 10:01:35 +01:00
|
|
|
SetEvent(Session->Descriptor.Receive.TailMoved);
|
|
|
|
LeaveCriticalSection(&Session->Receive.Lock);
|
2020-10-24 08:28:17 +02:00
|
|
|
}
|