From 478b80a1125813e71e14ba45453df78bf30d4579 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Sat, 11 May 2019 19:34:19 +0200 Subject: tools: add wincompat layer to wg(8) --- src/tools/Makefile | 6 ++ src/tools/genkey.c | 4 ++ src/tools/ipc.c | 4 ++ src/tools/wincompat/compat.h | 31 ++++++++ src/tools/wincompat/getrandom.c | 12 ++++ src/tools/wincompat/include/arpa/inet.h | 0 src/tools/wincompat/include/net/if.h | 0 src/tools/wincompat/include/netdb.h | 0 src/tools/wincompat/include/netinet/in.h | 0 src/tools/wincompat/include/sys/ioctl.h | 0 src/tools/wincompat/include/sys/socket.h | 0 src/tools/wincompat/include/sys/un.h | 0 src/tools/wincompat/init.c | 39 ++++++++++ src/tools/wincompat/ipc.c | 120 +++++++++++++++++++++++++++++++ src/tools/wincompat/libc.c | 105 +++++++++++++++++++++++++++ 15 files changed, 321 insertions(+) create mode 100644 src/tools/wincompat/compat.h create mode 100644 src/tools/wincompat/getrandom.c create mode 100644 src/tools/wincompat/include/arpa/inet.h create mode 100644 src/tools/wincompat/include/net/if.h create mode 100644 src/tools/wincompat/include/netdb.h create mode 100644 src/tools/wincompat/include/netinet/in.h create mode 100644 src/tools/wincompat/include/sys/ioctl.h create mode 100644 src/tools/wincompat/include/sys/socket.h create mode 100644 src/tools/wincompat/include/sys/un.h create mode 100644 src/tools/wincompat/init.c create mode 100644 src/tools/wincompat/ipc.c create mode 100644 src/tools/wincompat/libc.c diff --git a/src/tools/Makefile b/src/tools/Makefile index 0eecc57..e342779 100644 --- a/src/tools/Makefile +++ b/src/tools/Makefile @@ -54,6 +54,12 @@ endif ifeq ($(PLATFORM),haiku) LDLIBS += -lnetwork -lbsd endif +ifeq ($(PLATFORM),windows) +CC := x86_64-w64-mingw32-gcc +CFLAGS += -Iwincompat/include -include wincompat/compat.h +LDLIBS += -lws2_32 +wg: wincompat/libc.o wincompat/init.o +endif ifneq ($(V),1) BUILT_IN_LINK.o := $(LINK.o) diff --git a/src/tools/genkey.c b/src/tools/genkey.c index 21d2f7a..b9c2a86 100644 --- a/src/tools/genkey.c +++ b/src/tools/genkey.c @@ -28,6 +28,7 @@ #include "encoding.h" #include "subcommands.h" +#ifndef WINCOMPAT static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len) { ssize_t ret = 0; @@ -63,6 +64,9 @@ static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint errno = -ret; return i == len; } +#else +#include "wincompat/getrandom.c" +#endif int genkey_main(int argc, char *argv[]) { diff --git a/src/tools/ipc.c b/src/tools/ipc.c index 7ab3a62..e0be413 100644 --- a/src/tools/ipc.c +++ b/src/tools/ipc.c @@ -95,6 +95,7 @@ static int add_next_to_inflatable_buffer(struct inflatable_buffer *buffer) return 0; } +#ifndef WINCOMPAT static FILE *userspace_interface_file(const char *interface) { struct stat sbuf; @@ -197,6 +198,9 @@ out: closedir(dir); return ret; } +#else +#include "wincompat/ipc.c" +#endif static int userspace_set_device(struct wgdevice *dev) { diff --git a/src/tools/wincompat/compat.h b/src/tools/wincompat/compat.h new file mode 100644 index 0000000..4dada77 --- /dev/null +++ b/src/tools/wincompat/compat.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. + */ + +#define __USE_MINGW_ANSI_STDIO 1 +#include +#include +#include + +#include +#include +#include +#include +#include + +#undef interface +#undef min +#undef max + +#define WINCOMPAT + +#define IFNAMSIZ 64 +#define EAI_SYSTEM -99 + +/* libc.c */ +char *strsep(char **str, const char *sep); +ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp); +ssize_t getline(char **buf, size_t *bufsiz, FILE *fp); +int inet_pton(int af, const char *src, void *dst); +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); diff --git a/src/tools/wincompat/getrandom.c b/src/tools/wincompat/getrandom.c new file mode 100644 index 0000000..4e2c4bd --- /dev/null +++ b/src/tools/wincompat/getrandom.c @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#include + +static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len) +{ + return RtlGenRandom(out, len); +} diff --git a/src/tools/wincompat/include/arpa/inet.h b/src/tools/wincompat/include/arpa/inet.h new file mode 100644 index 0000000..e69de29 diff --git a/src/tools/wincompat/include/net/if.h b/src/tools/wincompat/include/net/if.h new file mode 100644 index 0000000..e69de29 diff --git a/src/tools/wincompat/include/netdb.h b/src/tools/wincompat/include/netdb.h new file mode 100644 index 0000000..e69de29 diff --git a/src/tools/wincompat/include/netinet/in.h b/src/tools/wincompat/include/netinet/in.h new file mode 100644 index 0000000..e69de29 diff --git a/src/tools/wincompat/include/sys/ioctl.h b/src/tools/wincompat/include/sys/ioctl.h new file mode 100644 index 0000000..e69de29 diff --git a/src/tools/wincompat/include/sys/socket.h b/src/tools/wincompat/include/sys/socket.h new file mode 100644 index 0000000..e69de29 diff --git a/src/tools/wincompat/include/sys/un.h b/src/tools/wincompat/include/sys/un.h new file mode 100644 index 0000000..e69de29 diff --git a/src/tools/wincompat/init.c b/src/tools/wincompat/init.c new file mode 100644 index 0000000..8d862ff --- /dev/null +++ b/src/tools/wincompat/init.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#include + +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4 +#endif + +__attribute__((constructor)) static void init(void) +{ + char *colormode; + DWORD console_mode; + HANDLE stdout_handle; + WSADATA wsaData; + WSAStartup(MAKEWORD(2, 2), &wsaData); + + stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); // We don't close this. + if (stdout_handle == INVALID_HANDLE_VALUE) + goto no_color; + if (!GetConsoleMode(stdout_handle, &console_mode)) + goto no_color; + if (!SetConsoleMode(stdout_handle, ENABLE_VIRTUAL_TERMINAL_PROCESSING | console_mode)) + goto no_color; + return; + +no_color: + colormode = getenv("WG_COLOR_MODE"); + if (!colormode) + putenv("WG_COLOR_MODE=never"); +} + +__attribute__((destructor)) static void deinit(void) +{ + WSACleanup(); +} diff --git a/src/tools/wincompat/ipc.c b/src/tools/wincompat/ipc.c new file mode 100644 index 0000000..a7a59f8 --- /dev/null +++ b/src/tools/wincompat/ipc.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#include +#include +#include +#include + +static FILE *userspace_interface_file(const char *interface) +{ + char fname[MAX_PATH], error_message[1024 * 128] = { 0 }; + HANDLE thread_token, process_snapshot, winlogon_process, winlogon_token, duplicated_token, pipe_handle = INVALID_HANDLE_VALUE; + PROCESSENTRY32 entry = { .dwSize = sizeof(PROCESSENTRY32) }; + BOOL ret; + int fd; + DWORD last_error = ERROR_SUCCESS; + TOKEN_PRIVILEGES privileges = { + .PrivilegeCount = 1, + .Privileges = {{ .Attributes = SE_PRIVILEGE_ENABLED }} + }; + + if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &privileges.Privileges[0].Luid)) + goto err; + + process_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (process_snapshot == INVALID_HANDLE_VALUE) + goto err; + for (ret = Process32First(process_snapshot, &entry); ret; last_error = GetLastError(), ret = Process32Next(process_snapshot, &entry)) { + if (strcasecmp(entry.szExeFile, "winlogon.exe")) + continue; + + RevertToSelf(); + if (!ImpersonateSelf(SecurityImpersonation)) + continue; + if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, FALSE, &thread_token)) + continue; + if (!AdjustTokenPrivileges(thread_token, FALSE, &privileges, sizeof(privileges), NULL, NULL)) { + last_error = GetLastError(); + CloseHandle(thread_token); + continue; + } + CloseHandle(thread_token); + + winlogon_process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, entry.th32ProcessID); + if (!winlogon_process) + continue; + if (!OpenProcessToken(winlogon_process, TOKEN_IMPERSONATE | TOKEN_DUPLICATE, &winlogon_token)) + continue; + CloseHandle(winlogon_process); + if (!DuplicateToken(winlogon_token, SecurityImpersonation, &duplicated_token)) { + last_error = GetLastError(); + RevertToSelf(); + continue; + } + CloseHandle(winlogon_token); + if (!SetThreadToken(NULL, duplicated_token)) { + last_error = GetLastError(); + CloseHandle(duplicated_token); + continue; + } + CloseHandle(duplicated_token); + + snprintf(fname, sizeof(fname), "\\\\.\\pipe\\WireGuard\\%s", interface); + pipe_handle = CreateFile(fname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + last_error = GetLastError(); + if (pipe_handle != INVALID_HANDLE_VALUE) { + last_error = ERROR_SUCCESS; + break; + } + } + RevertToSelf(); + CloseHandle(process_snapshot); + + if (last_error != ERROR_SUCCESS || pipe_handle == INVALID_HANDLE_VALUE) + goto err; + fd = _open_osfhandle((intptr_t)pipe_handle, _O_RDWR); + if (fd == -1) { + last_error = GetLastError(); + CloseHandle(pipe_handle); + goto err; + } + return _fdopen(fd, "r+"); + +err: + if (last_error == ERROR_SUCCESS) + last_error = GetLastError(); + if (last_error == ERROR_SUCCESS) + last_error = ERROR_ACCESS_DENIED; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), error_message, sizeof(error_message) - 1, NULL); + fprintf(stderr, "Error: Unable to open IPC handle via SYSTEM impersonation: %ld: %s\n", last_error, error_message); + errno = EACCES; + return NULL; +} + +static int userspace_get_wireguard_interfaces(struct inflatable_buffer *buffer) +{ + WIN32_FIND_DATA find_data; + HANDLE find_handle; + int ret = 0; + + find_handle = FindFirstFile("\\\\.\\pipe\\*", &find_data); + if (find_handle == INVALID_HANDLE_VALUE) + return -GetLastError(); + do { + if (strncmp("WireGuard\\", find_data.cFileName, 10)) + continue; + buffer->next = strdup(find_data.cFileName + 10); + buffer->good = true; + ret = add_next_to_inflatable_buffer(buffer); + if (ret < 0) + goto out; + } while (FindNextFile(find_handle, &find_data)); + +out: + FindClose(find_handle); + return ret; +} diff --git a/src/tools/wincompat/libc.c b/src/tools/wincompat/libc.c new file mode 100644 index 0000000..ad30278 --- /dev/null +++ b/src/tools/wincompat/libc.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include + +char *strsep(char **str, const char *sep) +{ + char *s = *str, *end; + if (!s) + return NULL; + end = s + strcspn(s, sep); + if (*end) + *end++ = 0; + else + end = 0; + *str = end; + return s; +} + +ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp) +{ + char *ptr, *eptr; + + if (!*buf || !*bufsiz) { + *bufsiz = BUFSIZ; + if (!(*buf = malloc(*bufsiz))) + return -1; + } + + for (ptr = *buf, eptr = *buf + *bufsiz;;) { + int c = fgetc(fp); + if (c == -1) { + if (feof(fp)) { + ssize_t diff = (ssize_t)(ptr - *buf); + if (diff != 0) { + *ptr = '\0'; + return diff; + } + } + return -1; + } + *ptr++ = c; + if (c == delimiter) { + *ptr = '\0'; + return ptr - *buf; + } + if (ptr + 2 >= eptr) { + char *nbuf; + size_t nbufsiz = *bufsiz * 2; + ssize_t d = ptr - *buf; + if ((nbuf = realloc(*buf, nbufsiz)) == NULL) + return -1; + *buf = nbuf; + *bufsiz = nbufsiz; + eptr = nbuf + nbufsiz; + ptr = nbuf + d; + } + } +} + +ssize_t getline(char **buf, size_t *bufsiz, FILE *fp) +{ + return getdelim(buf, bufsiz, '\n', fp); +} + +int inet_pton(int af, const char *src, void *dst) +{ + struct sockaddr_storage ss = { 0 }; + int size = sizeof(ss); + char s[INET6_ADDRSTRLEN + 1]; + + strncpy(s, src, INET6_ADDRSTRLEN + 1); + s[INET6_ADDRSTRLEN] = '\0'; + + if (WSAStringToAddress(s, af, NULL, (struct sockaddr *)&ss, &size)) + return 0; + if (af == AF_INET) + *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr; + else if (af == AF_INET6) + *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr; + else + return 0; + return 1; +} + +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) +{ + struct sockaddr_storage ss = { .ss_family = af }; + unsigned long s = size; + + if (af == AF_INET) + ((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src; + else if (af == AF_INET6) + ((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src; + else + return NULL; + return WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) ? NULL : dst; +} -- cgit v1.2.3-59-g8ed1b