Compare commits

..

1 Commits

Author SHA1 Message Date
Jason A. Donenfeld f3caeab254 wg-quick: linux: check for CAP_NET_ADMIN and config file access before auto_su
This way people can use wg-quick in situations where they only have
CAP_NET_ADMIN but not other capabilities, and are operating on writable
files.

Suggested-by: Jonny Fillmore <jonathon.fillmore@netprotect.com>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-08-28 11:04:21 +02:00
51 changed files with 346 additions and 1380 deletions

3
.gitignore vendored
View File

@ -1,9 +1,6 @@
cscope.out cscope.out
*.o *.o
*.d *.d
*.lib
*.dll
*.gch
*.dwo *.dwo
src/wg src/wg
src/wg.exe src/wg.exe

View File

@ -81,6 +81,7 @@ enum wgallowedip_attribute {
/* libmnl mini library: */ /* libmnl mini library: */
#define MNL_SOCKET_AUTOPID 0 #define MNL_SOCKET_AUTOPID 0
#define MNL_SOCKET_BUFFER_SIZE (sysconf(_SC_PAGESIZE) < 8192L ? sysconf(_SC_PAGESIZE) : 8192L)
#define MNL_ALIGNTO 4 #define MNL_ALIGNTO 4
#define MNL_ALIGN(len) (((len)+MNL_ALIGNTO-1) & ~(MNL_ALIGNTO-1)) #define MNL_ALIGN(len) (((len)+MNL_ALIGNTO-1) & ~(MNL_ALIGNTO-1))
#define MNL_NLMSG_HDRLEN MNL_ALIGN(sizeof(struct nlmsghdr)) #define MNL_NLMSG_HDRLEN MNL_ALIGN(sizeof(struct nlmsghdr))
@ -128,18 +129,6 @@ typedef int (*mnl_cb_t)(const struct nlmsghdr *nlh, void *data);
#define MNL_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) #define MNL_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
#endif #endif
static size_t mnl_ideal_socket_buffer_size(void)
{
static size_t size = 0;
if (size)
return size;
size = (size_t)sysconf(_SC_PAGESIZE);
if (size > 8192)
size = 8192;
return size;
}
static size_t mnl_nlmsg_size(size_t len) static size_t mnl_nlmsg_size(size_t len)
{ {
return len + MNL_NLMSG_HDRLEN; return len + MNL_NLMSG_HDRLEN;
@ -174,6 +163,7 @@ static void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh, size_t off
return (void *)nlh + MNL_NLMSG_HDRLEN + MNL_ALIGN(offset); return (void *)nlh + MNL_NLMSG_HDRLEN + MNL_ALIGN(offset);
} }
static bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len) static bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len)
{ {
return len >= (int)sizeof(struct nlmsghdr) && return len >= (int)sizeof(struct nlmsghdr) &&
@ -351,6 +341,7 @@ static uint32_t mnl_attr_get_u32(const struct nlattr *attr)
return *((uint32_t *)mnl_attr_get_payload(attr)); return *((uint32_t *)mnl_attr_get_payload(attr));
} }
static uint64_t mnl_attr_get_u64(const struct nlattr *attr) static uint64_t mnl_attr_get_u64(const struct nlattr *attr)
{ {
uint64_t tmp; uint64_t tmp;
@ -418,12 +409,14 @@ static bool mnl_attr_put_u8_check(struct nlmsghdr *nlh, size_t buflen,
return mnl_attr_put_check(nlh, buflen, type, sizeof(uint8_t), &data); return mnl_attr_put_check(nlh, buflen, type, sizeof(uint8_t), &data);
} }
static bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen, static bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen,
uint16_t type, uint16_t data) uint16_t type, uint16_t data)
{ {
return mnl_attr_put_check(nlh, buflen, type, sizeof(uint16_t), &data); return mnl_attr_put_check(nlh, buflen, type, sizeof(uint16_t), &data);
} }
static bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen, static bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen,
uint16_t type, uint32_t data) uint16_t type, uint32_t data)
{ {
@ -448,12 +441,12 @@ static void mnl_attr_nest_cancel(struct nlmsghdr *nlh, struct nlattr *start)
nlh->nlmsg_len -= mnl_nlmsg_get_payload_tail(nlh) - (void *)start; nlh->nlmsg_len -= mnl_nlmsg_get_payload_tail(nlh) - (void *)start;
} }
static int mnl_cb_noop(__attribute__((unused)) const struct nlmsghdr *nlh, __attribute__((unused)) void *data) static int mnl_cb_noop(const struct nlmsghdr *nlh, void *data)
{ {
return MNL_CB_OK; return MNL_CB_OK;
} }
static int mnl_cb_error(const struct nlmsghdr *nlh, __attribute__((unused)) void *data) static int mnl_cb_error(const struct nlmsghdr *nlh, void *data)
{ {
const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh); const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
@ -470,7 +463,7 @@ static int mnl_cb_error(const struct nlmsghdr *nlh, __attribute__((unused)) void
return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR; return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
} }
static int mnl_cb_stop(__attribute__((unused)) const struct nlmsghdr *nlh, __attribute__((unused)) void *data) static int mnl_cb_stop(const struct nlmsghdr *nlh, void *data)
{ {
return MNL_CB_STOP; return MNL_CB_STOP;
} }
@ -503,11 +496,13 @@ static int __mnl_cb_run(const void *buf, size_t numbytes,
return -1; return -1;
} }
if (nlh->nlmsg_flags & NLM_F_DUMP_INTR) { if (nlh->nlmsg_flags & NLM_F_DUMP_INTR) {
errno = EINTR; errno = EINTR;
return -1; return -1;
} }
if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) { if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
if (cb_data){ if (cb_data){
ret = cb_data(nlh, data); ret = cb_data(nlh, data);
@ -577,6 +572,7 @@ static struct mnl_socket *mnl_socket_open(int bus)
return __mnl_socket_open(bus, 0); return __mnl_socket_open(bus, 0);
} }
static int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid) static int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid)
{ {
int ret; int ret;
@ -606,6 +602,7 @@ static int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid
return 0; return 0;
} }
static ssize_t mnl_socket_sendto(const struct mnl_socket *nl, const void *buf, static ssize_t mnl_socket_sendto(const struct mnl_socket *nl, const void *buf,
size_t len) size_t len)
{ {
@ -616,6 +613,7 @@ static ssize_t mnl_socket_sendto(const struct mnl_socket *nl, const void *buf,
(struct sockaddr *) &snl, sizeof(snl)); (struct sockaddr *) &snl, sizeof(snl));
} }
static ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf, static ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf,
size_t bufsiz) size_t bufsiz)
{ {
@ -752,7 +750,7 @@ static int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void
do { do {
err = mnl_socket_recvfrom(nlg->nl, nlg->buf, err = mnl_socket_recvfrom(nlg->nl, nlg->buf,
mnl_ideal_socket_buffer_size()); MNL_SOCKET_BUFFER_SIZE);
if (err <= 0) if (err <= 0)
break; break;
err = mnl_cb_run2(nlg->buf, err, nlg->seq, nlg->portid, err = mnl_cb_run2(nlg->buf, err, nlg->seq, nlg->portid,
@ -798,10 +796,9 @@ static struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t ver
nlg = malloc(sizeof(*nlg)); nlg = malloc(sizeof(*nlg));
if (!nlg) if (!nlg)
return NULL; return NULL;
nlg->id = 0;
err = -ENOMEM; err = -ENOMEM;
nlg->buf = malloc(mnl_ideal_socket_buffer_size()); nlg->buf = malloc(MNL_SOCKET_BUFFER_SIZE);
if (!nlg->buf) if (!nlg->buf)
goto err_buf_alloc; goto err_buf_alloc;
@ -945,7 +942,7 @@ static int fetch_device_names(struct string_list *list)
struct ifinfomsg *ifm; struct ifinfomsg *ifm;
ret = -ENOMEM; ret = -ENOMEM;
rtnl_buffer = calloc(mnl_ideal_socket_buffer_size(), 1); rtnl_buffer = calloc(MNL_SOCKET_BUFFER_SIZE, 1);
if (!rtnl_buffer) if (!rtnl_buffer)
goto cleanup; goto cleanup;
@ -976,7 +973,7 @@ static int fetch_device_names(struct string_list *list)
} }
another: another:
if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, mnl_ideal_socket_buffer_size())) < 0) { if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, MNL_SOCKET_BUFFER_SIZE)) < 0) {
ret = -errno; ret = -errno;
goto cleanup; goto cleanup;
} }
@ -1012,7 +1009,7 @@ static int add_del_iface(const char *ifname, bool add)
struct ifinfomsg *ifm; struct ifinfomsg *ifm;
struct nlattr *nest; struct nlattr *nest;
rtnl_buffer = calloc(mnl_ideal_socket_buffer_size(), 1); rtnl_buffer = calloc(MNL_SOCKET_BUFFER_SIZE, 1);
if (!rtnl_buffer) { if (!rtnl_buffer) {
ret = -ENOMEM; ret = -ENOMEM;
goto cleanup; goto cleanup;
@ -1044,7 +1041,7 @@ static int add_del_iface(const char *ifname, bool add)
ret = -errno; ret = -errno;
goto cleanup; goto cleanup;
} }
if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, mnl_ideal_socket_buffer_size())) < 0) { if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, MNL_SOCKET_BUFFER_SIZE)) < 0) {
ret = -errno; ret = -errno;
goto cleanup; goto cleanup;
} }
@ -1099,10 +1096,10 @@ again:
for (peer = peer ? peer : dev->first_peer; peer; peer = peer->next_peer) { for (peer = peer ? peer : dev->first_peer; peer; peer = peer->next_peer) {
uint32_t flags = 0; uint32_t flags = 0;
peer_nest = mnl_attr_nest_start_check(nlh, mnl_ideal_socket_buffer_size(), 0); peer_nest = mnl_attr_nest_start_check(nlh, MNL_SOCKET_BUFFER_SIZE, 0);
if (!peer_nest) if (!peer_nest)
goto toobig_peers; goto toobig_peers;
if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_PUBLIC_KEY, sizeof(peer->public_key), peer->public_key)) if (!mnl_attr_put_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_PUBLIC_KEY, sizeof(peer->public_key), peer->public_key))
goto toobig_peers; goto toobig_peers;
if (peer->flags & WGPEER_REMOVE_ME) if (peer->flags & WGPEER_REMOVE_ME)
flags |= WGPEER_F_REMOVE_ME; flags |= WGPEER_F_REMOVE_ME;
@ -1110,45 +1107,45 @@ again:
if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS) if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
flags |= WGPEER_F_REPLACE_ALLOWEDIPS; flags |= WGPEER_F_REPLACE_ALLOWEDIPS;
if (peer->flags & WGPEER_HAS_PRESHARED_KEY) { if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_PRESHARED_KEY, sizeof(peer->preshared_key), peer->preshared_key)) if (!mnl_attr_put_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_PRESHARED_KEY, sizeof(peer->preshared_key), peer->preshared_key))
goto toobig_peers; goto toobig_peers;
} }
if (peer->endpoint.addr.sa_family == AF_INET) { if (peer->endpoint.addr.sa_family == AF_INET) {
if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr4), &peer->endpoint.addr4)) if (!mnl_attr_put_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr4), &peer->endpoint.addr4))
goto toobig_peers; goto toobig_peers;
} else if (peer->endpoint.addr.sa_family == AF_INET6) { } else if (peer->endpoint.addr.sa_family == AF_INET6) {
if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr6), &peer->endpoint.addr6)) if (!mnl_attr_put_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr6), &peer->endpoint.addr6))
goto toobig_peers; goto toobig_peers;
} }
if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) { if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {
if (!mnl_attr_put_u16_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval)) if (!mnl_attr_put_u16_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval))
goto toobig_peers; goto toobig_peers;
} }
} }
if (flags) { if (flags) {
if (!mnl_attr_put_u32_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_FLAGS, flags)) if (!mnl_attr_put_u32_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_FLAGS, flags))
goto toobig_peers; goto toobig_peers;
} }
if (peer->first_allowedip) { if (peer->first_allowedip) {
if (!allowedip) if (!allowedip)
allowedip = peer->first_allowedip; allowedip = peer->first_allowedip;
allowedips_nest = mnl_attr_nest_start_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_ALLOWEDIPS); allowedips_nest = mnl_attr_nest_start_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_ALLOWEDIPS);
if (!allowedips_nest) if (!allowedips_nest)
goto toobig_allowedips; goto toobig_allowedips;
for (; allowedip; allowedip = allowedip->next_allowedip) { for (; allowedip; allowedip = allowedip->next_allowedip) {
allowedip_nest = mnl_attr_nest_start_check(nlh, mnl_ideal_socket_buffer_size(), 0); allowedip_nest = mnl_attr_nest_start_check(nlh, MNL_SOCKET_BUFFER_SIZE, 0);
if (!allowedip_nest) if (!allowedip_nest)
goto toobig_allowedips; goto toobig_allowedips;
if (!mnl_attr_put_u16_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_FAMILY, allowedip->family)) if (!mnl_attr_put_u16_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_FAMILY, allowedip->family))
goto toobig_allowedips; goto toobig_allowedips;
if (allowedip->family == AF_INET) { if (allowedip->family == AF_INET) {
if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip4), &allowedip->ip4)) if (!mnl_attr_put_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip4), &allowedip->ip4))
goto toobig_allowedips; goto toobig_allowedips;
} else if (allowedip->family == AF_INET6) { } else if (allowedip->family == AF_INET6) {
if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip6), &allowedip->ip6)) if (!mnl_attr_put_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip6), &allowedip->ip6))
goto toobig_allowedips; goto toobig_allowedips;
} }
if (!mnl_attr_put_u8_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_CIDR_MASK, allowedip->cidr)) if (!mnl_attr_put_u8_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_CIDR_MASK, allowedip->cidr))
goto toobig_allowedips; goto toobig_allowedips;
mnl_attr_nest_end(nlh, allowedip_nest); mnl_attr_nest_end(nlh, allowedip_nest);
allowedip_nest = NULL; allowedip_nest = NULL;

View File

@ -40,19 +40,17 @@ enum wg_peer_flags {
WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4 WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4
}; };
typedef union wg_endpoint {
struct sockaddr addr;
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
} wg_endpoint;
typedef struct wg_peer { typedef struct wg_peer {
enum wg_peer_flags flags; enum wg_peer_flags flags;
wg_key public_key; wg_key public_key;
wg_key preshared_key; wg_key preshared_key;
wg_endpoint endpoint; union {
struct sockaddr addr;
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
} endpoint;
struct timespec64 last_handshake_time; struct timespec64 last_handshake_time;
uint64_t rx_bytes, tx_bytes; uint64_t rx_bytes, tx_bytes;

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd";>
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>Label</key> <key>Label</key>

View File

@ -16,7 +16,7 @@ INTERFACE="${BASH_REMATCH[1]}"
process_peer() { process_peer() {
[[ $PEER_SECTION -ne 1 || -z $PUBLIC_KEY || -z $ENDPOINT ]] && return 0 [[ $PEER_SECTION -ne 1 || -z $PUBLIC_KEY || -z $ENDPOINT ]] && return 0
[[ $(wg show "$INTERFACE" latest-handshakes) =~ ${PUBLIC_KEY//+/\\+}\ ([0-9]+) ]] || return 0 [[ $(wg show "$INTERFACE" latest-handshakes) =~ ${PUBLIC_KEY//+/\\+}\ ([0-9]+) ]] || return 0
(( ($EPOCHSECONDS - ${BASH_REMATCH[1]}) > 135 )) || return 0 (( ($(date +%s) - ${BASH_REMATCH[1]}) > 135 )) || return 0
wg set "$INTERFACE" peer "$PUBLIC_KEY" endpoint "$ENDPOINT" wg set "$INTERFACE" peer "$PUBLIC_KEY" endpoint "$ENDPOINT"
reset_peer_section reset_peer_section
} }

View File

@ -195,6 +195,10 @@ int magic_create_sock4(uint16_t listen_port)
if (fd < 0) if (fd < 0)
return fd; return fd;
ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (ret < 0)
goto err;
ret = setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); ret = setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
if (ret < 0) if (ret < 0)
goto err; goto err;
@ -224,6 +228,10 @@ int magic_create_sock6(uint16_t listen_port)
if (fd < 0) if (fd < 0)
return fd; return fd;
ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (ret < 0)
goto err;
ret = setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); ret = setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
if (ret < 0) if (ret < 0)
goto err; goto err;

View File

@ -48,29 +48,18 @@ CFLAGS += -DRUNSTATEDIR="\"$(RUNSTATEDIR)\""
ifeq ($(DEBUG),yes) ifeq ($(DEBUG),yes)
CFLAGS += -g CFLAGS += -g
endif endif
WIREGUARD_TOOLS_VERSION = $(patsubst v%,%,$(shell GIT_DIR="$(PWD)/../.git" git describe --dirty 2>/dev/null)) WIREGUARD_TOOLS_VERSION = $(patsubst v%,%,$(shell GIT_CEILING_DIRECTORIES="$(PWD)/../.." git describe --dirty 2>/dev/null))
ifneq ($(WIREGUARD_TOOLS_VERSION),) ifneq ($(WIREGUARD_TOOLS_VERSION),)
CFLAGS += -D'WIREGUARD_TOOLS_VERSION="$(WIREGUARD_TOOLS_VERSION)"' CFLAGS += -D'WIREGUARD_TOOLS_VERSION="$(WIREGUARD_TOOLS_VERSION)"'
endif endif
ifeq ($(PLATFORM),freebsd)
LDLIBS += -lnv
endif
ifeq ($(PLATFORM),haiku) ifeq ($(PLATFORM),haiku)
LDLIBS += -lnetwork -lbsd LDLIBS += -lnetwork -lbsd
endif endif
ifeq ($(PLATFORM),windows) ifeq ($(PLATFORM),windows)
CC := x86_64-w64-mingw32-clang CC := x86_64-w64-mingw32-gcc
WINDRES := $(shell $(CC) $(CFLAGS) -print-prog-name=windres 2>/dev/null) CFLAGS += -Iwincompat/include -include wincompat/compat.h
CFLAGS += -Iwincompat/include -include wincompat/compat.h -DWINVER=0x0601 -D_WIN32_WINNT=0x0601 -flto LDLIBS += -lws2_32
LDLIBS += -lws2_32 -lsetupapi -lole32 -ladvapi32 -lntdll -Lwincompat wg: wincompat/libc.o wincompat/init.o
LDFLAGS += -flto -Wl,--dynamicbase -Wl,--nxcompat -Wl,--tsaware -mconsole
LDFLAGS += -Wl,--major-os-version=6 -Wl,--minor-os-version=1 -Wl,--major-subsystem-version=6 -Wl,--minor-subsystem-version=1
# The use of -Wl,/delayload: here implies we're using llvm-mingw
LDFLAGS += -Wl,/delayload:ws2_32.dll -Wl,/delayload:setupapi.dll -Wl,/delayload:ole32.dll -Wl,/delayload:advapi32.dll
VERSION := $(patsubst "%",%,$(filter "%",$(file < version.h)))
wg: wincompat/libc.o wincompat/init.o wincompat/loader.o wincompat/resources.o
wincompat/resources.o: wincompat/resources.rc wincompat/manifest.xml
$(WINDRES) -DVERSION_STR=$(VERSION) -O coff -c 65001 -i $< -o $@
endif endif
ifneq ($(V),1) ifneq ($(V),1)
@ -82,13 +71,12 @@ COMPILE.c = @echo " CC $@";
COMPILE.c += $(BUILT_IN_COMPILE.c) COMPILE.c += $(BUILT_IN_COMPILE.c)
BUILT_IN_RM := $(RM) BUILT_IN_RM := $(RM)
RM := @a() { echo " CLEAN $$@"; $(BUILT_IN_RM) "$$@"; }; a RM := @a() { echo " CLEAN $$@"; $(BUILT_IN_RM) "$$@"; }; a
WINDRES := @a() { echo " WINDRES $${@: -1}"; $(WINDRES) "$$@"; }; a
endif endif
wg: $(sort $(patsubst %.c,%.o,$(wildcard *.c))) wg: $(sort $(patsubst %.c,%.o,$(wildcard *.c)))
clean: clean:
$(RM) wg *.o *.d $(wildcard wincompat/*.o wincompat/*.lib wincompat/*.dll) $(RM) wg *.o *.d
install: wg install: wg
@install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 wg "$(DESTDIR)$(BINDIR)/wg" @install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 wg "$(DESTDIR)$(BINDIR)/wg"

View File

@ -5,12 +5,12 @@ _wg_completion() {
local a local a
if [[ $COMP_CWORD -eq 1 ]]; then if [[ $COMP_CWORD -eq 1 ]]; then
COMPREPLY+=( $(compgen -W "help show showconf set setconf addconf syncconf genkey genpsk pubkey" -- "${COMP_WORDS[1]}") ) COMPREPLY+=( $(compgen -W "show showconf set setconf addconf genkey genpsk pubkey" -- "${COMP_WORDS[1]}") )
return return
fi fi
case "${COMP_WORDS[1]}" in case "${COMP_WORDS[1]}" in
genkey|genpsk|pubkey|help) return; ;; genkey|genpsk|pubkey|help) return; ;;
show|showconf|set|setconf|addconf|syncconf) ;; show|showconf|set|setconf|addconf) ;;
*) return; *) return;
esac esac
@ -26,7 +26,7 @@ _wg_completion() {
return return
fi fi
if [[ $COMP_CWORD -eq 3 && ( ${COMP_WORDS[1]} == setconf || ${COMP_WORDS[1]} == addconf || ${COMP_WORDS[1]} == syncconf) ]]; then if [[ $COMP_CWORD -eq 3 && ( ${COMP_WORDS[1]} == setconf || ${COMP_WORDS[1]} == addconf ) ]]; then
compopt -o filenames compopt -o filenames
mapfile -t a < <(compgen -f -- "${COMP_WORDS[3]}") mapfile -t a < <(compgen -f -- "${COMP_WORDS[3]}")
COMPREPLY+=( "${a[@]}" ) COMPREPLY+=( "${a[@]}" )

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT // SPDX-License-Identifier: GPL-2.0
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */
@ -561,7 +561,7 @@ static char *strip_spaces(const char *in)
return out; return out;
} }
struct wgdevice *config_read_cmd(const char *argv[], int argc) struct wgdevice *config_read_cmd(char *argv[], int argc)
{ {
struct wgdevice *device = calloc(1, sizeof(*device)); struct wgdevice *device = calloc(1, sizeof(*device));
struct wgpeer *peer = NULL; struct wgpeer *peer = NULL;

View File

@ -1,4 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT */ /* SPDX-License-Identifier: GPL-2.0 */
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */
@ -19,7 +19,7 @@ struct config_ctx {
bool is_peer_section, is_device_section; bool is_peer_section, is_device_section;
}; };
struct wgdevice *config_read_cmd(const char *argv[], int argc); struct wgdevice *config_read_cmd(char *argv[], int argc);
bool config_read_init(struct config_ctx *ctx, bool append); bool config_read_init(struct config_ctx *ctx, bool append);
bool config_read_line(struct config_ctx *ctx, const char *line); bool config_read_line(struct config_ctx *ctx, const char *line);
struct wgdevice *config_read_finish(struct config_ctx *ctx); struct wgdevice *config_read_finish(struct config_ctx *ctx);

View File

@ -1,4 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT */ /* SPDX-License-Identifier: GPL-2.0 */
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */

View File

@ -1,4 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT */ /* SPDX-License-Identifier: GPL-2.0 */
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* *

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT // SPDX-License-Identifier: GPL-2.0
/* /*
* Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */

View File

@ -1,4 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT */ /* SPDX-License-Identifier: GPL-2.0 */
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT // SPDX-License-Identifier: GPL-2.0
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* *

View File

@ -1,4 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT */ /* SPDX-License-Identifier: GPL-2.0 */
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT // SPDX-License-Identifier: GPL-2.0
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */
@ -72,7 +72,7 @@ static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint
} }
#endif #endif
int genkey_main(int argc, const char *argv[]) int genkey_main(int argc, char *argv[])
{ {
uint8_t key[WG_KEY_LEN]; uint8_t key[WG_KEY_LEN];
char base64[WG_KEY_LEN_BASE64]; char base64[WG_KEY_LEN_BASE64];

View File

@ -1,360 +0,0 @@
// SPDX-License-Identifier: MIT
/*
* Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*
*/
#include <assert.h>
#include <sys/nv.h>
#include <sys/sockio.h>
#include <dev/wg/if_wg.h>
#define IPC_SUPPORTS_KERNEL_INTERFACE
static int get_dgram_socket(void)
{
static int sock = -1;
if (sock < 0)
sock = socket(AF_INET, SOCK_DGRAM, 0);
return sock;
}
static int kernel_get_wireguard_interfaces(struct string_list *list)
{
struct ifgroupreq ifgr = { .ifgr_name = "wg" };
struct ifg_req *ifg;
int s = get_dgram_socket(), ret = 0;
if (s < 0)
return -errno;
if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0)
return errno == ENOENT ? 0 : -errno;
ifgr.ifgr_groups = calloc(1, ifgr.ifgr_len);
if (!ifgr.ifgr_groups)
return -errno;
if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0) {
ret = -errno;
goto out;
}
for (ifg = ifgr.ifgr_groups; ifg && ifgr.ifgr_len > 0; ++ifg) {
if ((ret = string_list_add(list, ifg->ifgrq_member)) < 0)
goto out;
ifgr.ifgr_len -= sizeof(struct ifg_req);
}
out:
free(ifgr.ifgr_groups);
return ret;
}
static int kernel_get_device(struct wgdevice **device, const char *ifname)
{
struct wg_data_io wgd = { 0 };
nvlist_t *nvl_device = NULL;
const nvlist_t *const *nvl_peers;
struct wgdevice *dev = NULL;
size_t size, peer_count, i;
uint64_t number;
const void *binary;
int ret = 0, s;
*device = NULL;
s = get_dgram_socket();
if (s < 0)
goto err;
strlcpy(wgd.wgd_name, ifname, sizeof(wgd.wgd_name));
if (ioctl(s, SIOCGWG, &wgd) < 0)
goto err;
wgd.wgd_data = malloc(wgd.wgd_size);
if (!wgd.wgd_data)
goto err;
if (ioctl(s, SIOCGWG, &wgd) < 0)
goto err;
dev = calloc(1, sizeof(*dev));
if (!dev)
goto err;
strlcpy(dev->name, ifname, sizeof(dev->name));
nvl_device = nvlist_unpack(wgd.wgd_data, wgd.wgd_size, 0);
if (!nvl_device)
goto err;
if (nvlist_exists_number(nvl_device, "listen-port")) {
number = nvlist_get_number(nvl_device, "listen-port");
if (number <= UINT16_MAX) {
dev->listen_port = number;
dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
}
}
if (nvlist_exists_number(nvl_device, "user-cookie")) {
number = nvlist_get_number(nvl_device, "user-cookie");
if (number <= UINT32_MAX) {
dev->fwmark = number;
dev->flags |= WGDEVICE_HAS_FWMARK;
}
}
if (nvlist_exists_binary(nvl_device, "public-key")) {
binary = nvlist_get_binary(nvl_device, "public-key", &size);
if (binary && size == sizeof(dev->public_key)) {
memcpy(dev->public_key, binary, sizeof(dev->public_key));
dev->flags |= WGDEVICE_HAS_PUBLIC_KEY;
}
}
if (nvlist_exists_binary(nvl_device, "private-key")) {
binary = nvlist_get_binary(nvl_device, "private-key", &size);
if (binary && size == sizeof(dev->private_key)) {
memcpy(dev->private_key, binary, sizeof(dev->private_key));
dev->flags |= WGDEVICE_HAS_PRIVATE_KEY;
}
}
if (!nvlist_exists_nvlist_array(nvl_device, "peers"))
goto skip_peers;
nvl_peers = nvlist_get_nvlist_array(nvl_device, "peers", &peer_count);
if (!nvl_peers)
goto skip_peers;
for (i = 0; i < peer_count; ++i) {
struct wgpeer *peer;
struct wgallowedip *aip = NULL;
const nvlist_t *const *nvl_aips;
size_t aip_count, j;
peer = calloc(1, sizeof(*peer));
if (!peer)
goto err_peer;
if (nvlist_exists_binary(nvl_peers[i], "public-key")) {
binary = nvlist_get_binary(nvl_peers[i], "public-key", &size);
if (binary && size == sizeof(peer->public_key)) {
memcpy(peer->public_key, binary, sizeof(peer->public_key));
peer->flags |= WGPEER_HAS_PUBLIC_KEY;
}
}
if (nvlist_exists_binary(nvl_peers[i], "preshared-key")) {
binary = nvlist_get_binary(nvl_peers[i], "preshared-key", &size);
if (binary && size == sizeof(peer->preshared_key)) {
memcpy(peer->preshared_key, binary, sizeof(peer->preshared_key));
if (!key_is_zero(peer->preshared_key))
peer->flags |= WGPEER_HAS_PRESHARED_KEY;
}
}
if (nvlist_exists_number(nvl_peers[i], "persistent-keepalive-interval")) {
number = nvlist_get_number(nvl_peers[i], "persistent-keepalive-interval");
if (number <= UINT16_MAX) {
peer->persistent_keepalive_interval = number;
peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
}
}
if (nvlist_exists_binary(nvl_peers[i], "endpoint")) {
const struct sockaddr *endpoint = nvlist_get_binary(nvl_peers[i], "endpoint", &size);
if (endpoint && size <= sizeof(peer->endpoint) && size >= sizeof(peer->endpoint.addr) &&
(endpoint->sa_family == AF_INET || endpoint->sa_family == AF_INET6))
memcpy(&peer->endpoint.addr, endpoint, size);
}
if (nvlist_exists_number(nvl_peers[i], "rx-bytes"))
peer->rx_bytes = nvlist_get_number(nvl_peers[i], "rx-bytes");
if (nvlist_exists_number(nvl_peers[i], "tx-bytes"))
peer->tx_bytes = nvlist_get_number(nvl_peers[i], "tx-bytes");
if (nvlist_exists_binary(nvl_peers[i], "last-handshake-time")) {
binary = nvlist_get_binary(nvl_peers[i], "last-handshake-time", &size);
if (binary && size == sizeof(peer->last_handshake_time))
memcpy(&peer->last_handshake_time, binary, sizeof(peer->last_handshake_time));
}
if (!nvlist_exists_nvlist_array(nvl_peers[i], "allowed-ips"))
goto skip_allowed_ips;
nvl_aips = nvlist_get_nvlist_array(nvl_peers[i], "allowed-ips", &aip_count);
if (!aip_count || !nvl_aips)
goto skip_allowed_ips;
for (j = 0; j < aip_count; ++j) {
if (!nvlist_exists_number(nvl_aips[j], "cidr"))
continue;
if (!nvlist_exists_binary(nvl_aips[j], "ipv4") && !nvlist_exists_binary(nvl_aips[j], "ipv6"))
continue;
aip = calloc(1, sizeof(*aip));
if (!aip)
goto err_allowed_ips;
number = nvlist_get_number(nvl_aips[j], "cidr");
if (nvlist_exists_binary(nvl_aips[j], "ipv4")) {
binary = nvlist_get_binary(nvl_aips[j], "ipv4", &size);
if (!binary || number > 32) {
ret = EINVAL;
goto err_allowed_ips;
}
aip->family = AF_INET;
aip->cidr = number;
memcpy(&aip->ip4, binary, sizeof(aip->ip4));
} else {
assert(nvlist_exists_binary(nvl_aips[j], "ipv6"));
binary = nvlist_get_binary(nvl_aips[j], "ipv6", &size);
if (!binary || number > 128) {
ret = EINVAL;
goto err_allowed_ips;
}
aip->family = AF_INET6;
aip->cidr = number;
memcpy(&aip->ip6, binary, sizeof(aip->ip6));
}
if (!peer->first_allowedip)
peer->first_allowedip = aip;
else
peer->last_allowedip->next_allowedip = aip;
peer->last_allowedip = aip;
aip = NULL;
continue;
err_allowed_ips:
if (!ret)
ret = -errno;
free(aip);
goto err_peer;
}
/* Nothing leaked, hopefully -- ownership transferred or aip freed. */
assert(aip == NULL);
skip_allowed_ips:
if (!dev->first_peer)
dev->first_peer = peer;
else
dev->last_peer->next_peer = peer;
dev->last_peer = peer;
continue;
err_peer:
if (!ret)
ret = -errno;
free(peer);
goto err;
}
skip_peers:
free(wgd.wgd_data);
nvlist_destroy(nvl_device);
*device = dev;
return 0;
err:
if (!ret)
ret = -errno;
free(wgd.wgd_data);
nvlist_destroy(nvl_device);
free(dev);
return ret;
}
static int kernel_set_device(struct wgdevice *dev)
{
struct wg_data_io wgd = { 0 };
nvlist_t *nvl_device = NULL, **nvl_peers = NULL;
size_t peer_count = 0, i = 0;
struct wgpeer *peer;
int ret = 0, s;
strlcpy(wgd.wgd_name, dev->name, sizeof(wgd.wgd_name));
nvl_device = nvlist_create(0);
if (!nvl_device)
goto err;
for_each_wgpeer(dev, peer)
++peer_count;
if (peer_count) {
nvl_peers = calloc(peer_count, sizeof(*nvl_peers));
if (!nvl_peers)
goto err;
}
if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)
nvlist_add_binary(nvl_device, "private-key", dev->private_key, sizeof(dev->private_key));
if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
nvlist_add_number(nvl_device, "listen-port", dev->listen_port);
if (dev->flags & WGDEVICE_HAS_FWMARK)
nvlist_add_number(nvl_device, "user-cookie", dev->fwmark);
if (dev->flags & WGDEVICE_REPLACE_PEERS)
nvlist_add_bool(nvl_device, "replace-peers", true);
for_each_wgpeer(dev, peer) {
size_t aip_count = 0, j = 0;
nvlist_t **nvl_aips = NULL;
struct wgallowedip *aip;
nvl_peers[i] = nvlist_create(0);
if (!nvl_peers[i])
goto err_peer;
for_each_wgallowedip(peer, aip)
++aip_count;
if (aip_count) {
nvl_aips = calloc(aip_count, sizeof(*nvl_aips));
if (!nvl_aips)
goto err_peer;
}
nvlist_add_binary(nvl_peers[i], "public-key", peer->public_key, sizeof(peer->public_key));
if (peer->flags & WGPEER_HAS_PRESHARED_KEY)
nvlist_add_binary(nvl_peers[i], "preshared-key", peer->preshared_key, sizeof(peer->preshared_key));
if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL)
nvlist_add_number(nvl_peers[i], "persistent-keepalive-interval", peer->persistent_keepalive_interval);
if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
nvlist_add_binary(nvl_peers[i], "endpoint", &peer->endpoint.addr, peer->endpoint.addr.sa_len);
if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
nvlist_add_bool(nvl_peers[i], "replace-allowedips", true);
if (peer->flags & WGPEER_REMOVE_ME)
nvlist_add_bool(nvl_peers[i], "remove", true);
for_each_wgallowedip(peer, aip) {
nvl_aips[j] = nvlist_create(0);
if (!nvl_aips[j])
goto err_peer;
nvlist_add_number(nvl_aips[j], "cidr", aip->cidr);
if (aip->family == AF_INET)
nvlist_add_binary(nvl_aips[j], "ipv4", &aip->ip4, sizeof(aip->ip4));
else if (aip->family == AF_INET6)
nvlist_add_binary(nvl_aips[j], "ipv6", &aip->ip6, sizeof(aip->ip6));
++j;
}
if (j) {
nvlist_add_nvlist_array(nvl_peers[i], "allowed-ips", (const nvlist_t *const *)nvl_aips, j);
for (j = 0; j < aip_count; ++j)
nvlist_destroy(nvl_aips[j]);
free(nvl_aips);
}
++i;
continue;
err_peer:
ret = -errno;
for (j = 0; j < aip_count && nvl_aips; ++j)
nvlist_destroy(nvl_aips[j]);
free(nvl_aips);
nvlist_destroy(nvl_peers[i]);
nvl_peers[i] = NULL;
goto err;
}
if (i) {
nvlist_add_nvlist_array(nvl_device, "peers", (const nvlist_t *const *)nvl_peers, i);
for (i = 0; i < peer_count; ++i)
nvlist_destroy(nvl_peers[i]);
free(nvl_peers);
nvl_peers = NULL;
}
wgd.wgd_data = nvlist_pack(nvl_device, &wgd.wgd_size);
nvlist_destroy(nvl_device);
nvl_device = NULL;
if (!wgd.wgd_data)
goto err;
s = get_dgram_socket();
if (s < 0)
return -errno;
return ioctl(s, SIOCSWG, &wgd);
err:
if (!ret)
ret = -errno;
for (i = 0; i < peer_count && nvl_peers; ++i)
nvlist_destroy(nvl_peers[i]);
free(nvl_peers);
nvlist_destroy(nvl_device);
return ret;
}

View File

@ -479,12 +479,6 @@ static int kernel_get_device(struct wgdevice **device, const char *iface)
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
struct mnlg_socket *nlg; struct mnlg_socket *nlg;
/* libmnl doesn't check the buffer size, so enforce that before using. */
if (strlen(iface) >= IFNAMSIZ) {
errno = ENAMETOOLONG;
return -ENAMETOOLONG;
}
try_again: try_again:
ret = 0; ret = 0;
*device = calloc(1, sizeof(**device)); *device = calloc(1, sizeof(**device));

View File

@ -129,8 +129,7 @@ static int kernel_get_device(struct wgdevice **device, const char *iface)
if (wg_peer->p_flags & WG_PEER_HAS_PSK) { if (wg_peer->p_flags & WG_PEER_HAS_PSK) {
memcpy(peer->preshared_key, wg_peer->p_psk, sizeof(peer->preshared_key)); memcpy(peer->preshared_key, wg_peer->p_psk, sizeof(peer->preshared_key));
if (!key_is_zero(peer->preshared_key)) peer->flags |= WGPEER_HAS_PRESHARED_KEY;
peer->flags |= WGPEER_HAS_PRESHARED_KEY;
} }
if (wg_peer->p_flags & WG_PEER_HAS_PKA) { if (wg_peer->p_flags & WG_PEER_HAS_PKA) {
@ -255,12 +254,13 @@ static int kernel_set_device(struct wgdevice *dev)
wg_aip->a_af = aip->family; wg_aip->a_af = aip->family;
wg_aip->a_cidr = aip->cidr; wg_aip->a_cidr = aip->cidr;
if (aip->family == AF_INET) if (aip->family == AF_INET) {
memcpy(&wg_aip->a_ipv4, &aip->ip4, sizeof(wg_aip->a_ipv4)); memcpy(&wg_aip->a_ipv4, &aip->ip4, sizeof(wg_aip->a_ipv4));
else if (aip->family == AF_INET6) } else if (aip->family == AF_INET6) {
memcpy(&wg_aip->a_ipv6, &aip->ip6, sizeof(wg_aip->a_ipv6)); memcpy(&wg_aip->a_ipv6, &aip->ip6, sizeof(wg_aip->a_ipv6));
else } else {
continue; continue;
}
++aip_count; ++aip_count;
++wg_aip; ++wg_aip;
} }

View File

@ -10,96 +10,126 @@
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include <fcntl.h> #include <fcntl.h>
#include <hashtable.h>
static FILE *userspace_interface_file(const char *iface) static FILE *userspace_interface_file(const char *iface)
{ {
char fname[MAX_PATH]; char fname[MAX_PATH], error_message[1024 * 128] = { 0 };
HANDLE pipe_handle; HANDLE thread_token, process_snapshot, winlogon_process, winlogon_token, duplicated_token, pipe_handle = INVALID_HANDLE_VALUE;
SID expected_sid; PROCESSENTRY32 entry = { .dwSize = sizeof(PROCESSENTRY32) };
DWORD bytes = sizeof(expected_sid);
PSID pipe_sid;
PSECURITY_DESCRIPTOR pipe_sd; PSECURITY_DESCRIPTOR pipe_sd;
bool equal; PSID pipe_sid;
SID expected_sid;
BOOL ret;
int fd; int fd;
DWORD last_error = ERROR_SUCCESS, bytes = sizeof(expected_sid);
TOKEN_PRIVILEGES privileges = {
.PrivilegeCount = 1,
.Privileges = {{ .Attributes = SE_PRIVILEGE_ENABLED }}
};
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &privileges.Privileges[0].Luid))
goto err;
if (!CreateWellKnownSid(WinLocalSystemSid, NULL, &expected_sid, &bytes)) if (!CreateWellKnownSid(WinLocalSystemSid, NULL, &expected_sid, &bytes))
goto err; goto err;
snprintf(fname, sizeof(fname), "\\\\.\\pipe\\ProtectedPrefix\\Administrators\\WireGuard\\%s", iface); process_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
pipe_handle = CreateFileA(fname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (process_snapshot == INVALID_HANDLE_VALUE)
if (pipe_handle == 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\\ProtectedPrefix\\Administrators\\WireGuard\\%s", iface);
pipe_handle = CreateFile(fname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
last_error = GetLastError();
if (pipe_handle == INVALID_HANDLE_VALUE)
continue;
last_error = GetSecurityInfo(pipe_handle, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &pipe_sid, NULL, NULL, NULL, &pipe_sd);
if (last_error != ERROR_SUCCESS) {
CloseHandle(pipe_handle);
continue;
}
last_error = EqualSid(&expected_sid, pipe_sid) ? ERROR_SUCCESS : ERROR_ACCESS_DENIED;
LocalFree(pipe_sd);
if (last_error != ERROR_SUCCESS) {
CloseHandle(pipe_handle);
continue;
}
last_error = ERROR_SUCCESS;
break;
}
RevertToSelf();
CloseHandle(process_snapshot);
if (last_error != ERROR_SUCCESS || pipe_handle == INVALID_HANDLE_VALUE)
goto err; goto err;
if (GetSecurityInfo(pipe_handle, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &pipe_sid, NULL, NULL, NULL, &pipe_sd) != ERROR_SUCCESS)
goto err_close;
equal = EqualSid(&expected_sid, pipe_sid);
LocalFree(pipe_sd);
if (!equal)
goto err_close;
fd = _open_osfhandle((intptr_t)pipe_handle, _O_RDWR); fd = _open_osfhandle((intptr_t)pipe_handle, _O_RDWR);
if (fd == -1) { if (fd == -1) {
last_error = GetLastError();
CloseHandle(pipe_handle); CloseHandle(pipe_handle);
return NULL; goto err;
} }
return _fdopen(fd, "r+"); return _fdopen(fd, "r+");
err_close:
CloseHandle(pipe_handle);
err: 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; errno = EACCES;
return NULL; return NULL;
} }
static bool have_cached_interfaces;
static struct hashtable cached_interfaces;
static bool userspace_has_wireguard_interface(const char *iface)
{
char fname[MAX_PATH];
WIN32_FIND_DATA find_data;
HANDLE find_handle;
bool ret = false;
if (have_cached_interfaces)
return hashtable_find_entry(&cached_interfaces, iface) != NULL;
snprintf(fname, sizeof(fname), "ProtectedPrefix\\Administrators\\WireGuard\\%s", iface);
find_handle = FindFirstFile("\\\\.\\pipe\\*", &find_data);
if (find_handle == INVALID_HANDLE_VALUE)
return -EIO;
do {
if (!strcmp(fname, find_data.cFileName)) {
ret = true;
break;
}
} while (FindNextFile(find_handle, &find_data));
FindClose(find_handle);
return ret;
}
static int userspace_get_wireguard_interfaces(struct string_list *list) static int userspace_get_wireguard_interfaces(struct string_list *list)
{ {
static const char prefix[] = "ProtectedPrefix\\Administrators\\WireGuard\\"; static const char prefix[] = "ProtectedPrefix\\Administrators\\WireGuard\\";
WIN32_FIND_DATA find_data; WIN32_FIND_DATA find_data;
HANDLE find_handle; HANDLE find_handle;
char *iface;
int ret = 0; int ret = 0;
find_handle = FindFirstFile("\\\\.\\pipe\\*", &find_data); find_handle = FindFirstFile("\\\\.\\pipe\\*", &find_data);
if (find_handle == INVALID_HANDLE_VALUE) if (find_handle == INVALID_HANDLE_VALUE)
return -EIO; return -GetLastError();
do { do {
if (strncmp(prefix, find_data.cFileName, strlen(prefix))) if (strncmp(prefix, find_data.cFileName, strlen(prefix)))
continue; continue;
iface = find_data.cFileName + strlen(prefix); ret = string_list_add(list, find_data.cFileName + strlen(prefix));
ret = string_list_add(list, iface);
if (ret < 0) if (ret < 0)
goto out; goto out;
if (!hashtable_find_or_insert_entry(&cached_interfaces, iface)) {
ret = -errno;
goto out;
}
} while (FindNextFile(find_handle, &find_data)); } while (FindNextFile(find_handle, &find_data));
have_cached_interfaces = true;
out: out:
FindClose(find_handle); FindClose(find_handle);

View File

@ -5,7 +5,6 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <errno.h> #include <errno.h>
#include <limits.h>
#include <net/if.h> #include <net/if.h>
#include <netdb.h> #include <netdb.h>
#include <netinet/in.h> #include <netinet/in.h>
@ -31,10 +30,8 @@ static int userspace_set_device(struct wgdevice *dev)
struct wgpeer *peer; struct wgpeer *peer;
struct wgallowedip *allowedip; struct wgallowedip *allowedip;
FILE *f; FILE *f;
int ret, set_errno = -EPROTO; int ret;
socklen_t addr_len; socklen_t addr_len;
size_t line_buffer_len = 0, line_len;
char *key = NULL, *value;
f = userspace_interface_file(dev->name); f = userspace_interface_file(dev->name);
if (!f) if (!f)
@ -95,30 +92,8 @@ static int userspace_set_device(struct wgdevice *dev)
fprintf(f, "\n"); fprintf(f, "\n");
fflush(f); fflush(f);
while (getline(&key, &line_buffer_len, f) > 0) { if (fscanf(f, "errno=%d\n\n", &ret) != 1)
line_len = strlen(key); ret = errno ? -errno : -EPROTO;
ret = set_errno;
if (line_len == 1 && key[0] == '\n')
goto out;
value = strchr(key, '=');
if (!value || line_len == 0 || key[line_len - 1] != '\n')
break;
*value++ = key[--line_len] = '\0';
if (!strcmp(key, "errno")) {
long long num;
char *end;
if (value[0] != '-' && !char_is_digit(value[0]))
break;
num = strtoll(value, &end, 10);
if (*end || num > INT_MAX || num < INT_MIN)
break;
set_errno = num;
}
}
ret = errno ? -errno : -EPROTO;
out:
free(key);
fclose(f); fclose(f);
errno = -ret; errno = -ret;
return ret; return ret;

View File

@ -1,450 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
#include "containers.h"
#include <windows.h>
#include <setupapi.h>
#include <cfgmgr32.h>
#include <iphlpapi.h>
#include <initguid.h>
#include <devguid.h>
#include <ddk/ndisguid.h>
#include <wireguard.h>
#include <hashtable.h>
#define IPC_SUPPORTS_KERNEL_INTERFACE
static bool have_cached_kernel_interfaces;
static struct hashtable cached_kernel_interfaces;
static const DEVPROPKEY devpkey_name = DEVPKEY_WG_NAME;
extern bool is_win7;
static int kernel_get_wireguard_interfaces(struct string_list *list)
{
HDEVINFO dev_info = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, is_win7 ? L"ROOT\\WIREGUARD" : L"SWD\\WireGuard", NULL, DIGCF_PRESENT, NULL, NULL, NULL);
bool will_have_cached_kernel_interfaces = true;
if (dev_info == INVALID_HANDLE_VALUE) {
errno = EACCES;
return -errno;
}
for (DWORD i = 0;; ++i) {
DWORD buf_len;
WCHAR adapter_name[MAX_ADAPTER_NAME];
SP_DEVINFO_DATA dev_info_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };
DEVPROPTYPE prop_type;
ULONG status, problem_code;
char *interface_name;
struct hashtable_entry *entry;
if (!SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data)) {
if (GetLastError() == ERROR_NO_MORE_ITEMS)
break;
continue;
}
if (!SetupDiGetDevicePropertyW(dev_info, &dev_info_data, &devpkey_name,
&prop_type, (PBYTE)adapter_name,
sizeof(adapter_name), NULL, 0) ||
prop_type != DEVPROP_TYPE_STRING)
continue;
adapter_name[_countof(adapter_name) - 1] = L'0';
if (!adapter_name[0])
continue;
buf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, NULL, 0, NULL, NULL);
if (!buf_len)
continue;
interface_name = malloc(buf_len);
if (!interface_name)
continue;
buf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, interface_name, buf_len, NULL, NULL);
if (!buf_len) {
free(interface_name);
continue;
}
if (CM_Get_DevNode_Status(&status, &problem_code, dev_info_data.DevInst, 0) == CR_SUCCESS &&
(status & (DN_DRIVER_LOADED | DN_STARTED)) == (DN_DRIVER_LOADED | DN_STARTED))
string_list_add(list, interface_name);
entry = hashtable_find_or_insert_entry(&cached_kernel_interfaces, interface_name);
free(interface_name);
if (!entry)
continue;
if (SetupDiGetDeviceInstanceIdW(dev_info, &dev_info_data, NULL, 0, &buf_len) || GetLastError() != ERROR_INSUFFICIENT_BUFFER)
continue;
entry->value = calloc(sizeof(WCHAR), buf_len);
if (!entry->value)
continue;
if (!SetupDiGetDeviceInstanceIdW(dev_info, &dev_info_data, entry->value, buf_len, &buf_len)) {
free(entry->value);
entry->value = NULL;
continue;
}
will_have_cached_kernel_interfaces = true;
}
SetupDiDestroyDeviceInfoList(dev_info);
have_cached_kernel_interfaces = will_have_cached_kernel_interfaces;
return 0;
}
static HANDLE kernel_interface_handle(const char *iface)
{
HDEVINFO dev_info;
WCHAR *interfaces = NULL;
HANDLE handle;
if (have_cached_kernel_interfaces) {
struct hashtable_entry *entry = hashtable_find_entry(&cached_kernel_interfaces, iface);
if (entry) {
DWORD buf_len;
if (CM_Get_Device_Interface_List_SizeW(
&buf_len, (GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)entry->value,
CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS)
goto err_hash;
interfaces = calloc(buf_len, sizeof(*interfaces));
if (!interfaces)
goto err_hash;
if (CM_Get_Device_Interface_ListW(
(GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)entry->value, interfaces, buf_len,
CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS || !interfaces[0]) {
free(interfaces);
interfaces = NULL;
goto err_hash;
}
handle = CreateFileW(interfaces, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, 0, NULL);
free(interfaces);
if (handle == INVALID_HANDLE_VALUE)
goto err_hash;
return handle;
err_hash:
errno = EACCES;
return NULL;
}
}
dev_info = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, is_win7 ? L"ROOT\\WIREGUARD" : L"SWD\\WireGuard", NULL, DIGCF_PRESENT, NULL, NULL, NULL);
if (dev_info == INVALID_HANDLE_VALUE)
return NULL;
for (DWORD i = 0; !interfaces; ++i) {
bool found;
DWORD buf_len;
WCHAR *buf, adapter_name[MAX_ADAPTER_NAME];
SP_DEVINFO_DATA dev_info_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };
DEVPROPTYPE prop_type;
char *interface_name;
if (!SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data)) {
if (GetLastError() == ERROR_NO_MORE_ITEMS)
break;
continue;
}
if (!SetupDiGetDevicePropertyW(dev_info, &dev_info_data, &devpkey_name,
&prop_type, (PBYTE)adapter_name,
sizeof(adapter_name), NULL, 0) ||
prop_type != DEVPROP_TYPE_STRING)
continue;
adapter_name[_countof(adapter_name) - 1] = L'0';
if (!adapter_name[0])
continue;
buf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, NULL, 0, NULL, NULL);
if (!buf_len)
continue;
interface_name = malloc(buf_len);
if (!interface_name)
continue;
buf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, interface_name, buf_len, NULL, NULL);
if (!buf_len) {
free(interface_name);
continue;
}
found = !strcmp(interface_name, iface);
free(interface_name);
if (!found)
continue;
if (SetupDiGetDeviceInstanceIdW(dev_info, &dev_info_data, NULL, 0, &buf_len) || GetLastError() != ERROR_INSUFFICIENT_BUFFER)
continue;
buf = calloc(sizeof(*buf), buf_len);
if (!buf)
continue;
if (!SetupDiGetDeviceInstanceIdW(dev_info, &dev_info_data, buf, buf_len, &buf_len))
goto cleanup_instance_id;
if (CM_Get_Device_Interface_List_SizeW(
&buf_len, (GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)buf,
CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS)
goto cleanup_instance_id;
interfaces = calloc(buf_len, sizeof(*interfaces));
if (!interfaces)
goto cleanup_instance_id;
if (CM_Get_Device_Interface_ListW(
(GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)buf, interfaces, buf_len,
CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS || !interfaces[0]) {
free(interfaces);
interfaces = NULL;
goto cleanup_instance_id;
}
cleanup_instance_id:
free(buf);
}
SetupDiDestroyDeviceInfoList(dev_info);
if (!interfaces) {
errno = ENOENT;
return NULL;
}
handle = CreateFileW(interfaces, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, 0, NULL);
free(interfaces);
if (handle == INVALID_HANDLE_VALUE) {
errno = EACCES;
return NULL;
}
return handle;
}
static int kernel_get_device(struct wgdevice **device, const char *iface)
{
WG_IOCTL_INTERFACE *wg_iface;
WG_IOCTL_PEER *wg_peer;
WG_IOCTL_ALLOWED_IP *wg_aip;
void *buf = NULL;
DWORD buf_len = 0;
HANDLE handle = kernel_interface_handle(iface);
struct wgdevice *dev;
struct wgpeer *peer;
struct wgallowedip *aip;
int ret;
*device = NULL;
if (!handle)
return -errno;
while (!DeviceIoControl(handle, WG_IOCTL_GET, NULL, 0, buf, buf_len, &buf_len, NULL)) {
free(buf);
if (GetLastError() != ERROR_MORE_DATA) {
errno = EACCES;
return -errno;
}
buf = malloc(buf_len);
if (!buf)
return -errno;
}
wg_iface = (WG_IOCTL_INTERFACE *)buf;
dev = calloc(1, sizeof(*dev));
if (!dev)
goto out;
strncpy(dev->name, iface, sizeof(dev->name));
dev->name[sizeof(dev->name) - 1] = '\0';
if (wg_iface->Flags & WG_IOCTL_INTERFACE_HAS_LISTEN_PORT) {
dev->listen_port = wg_iface->ListenPort;
dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
}
if (wg_iface->Flags & WG_IOCTL_INTERFACE_HAS_PUBLIC_KEY) {
memcpy(dev->public_key, wg_iface->PublicKey, sizeof(dev->public_key));
dev->flags |= WGDEVICE_HAS_PUBLIC_KEY;
}
if (wg_iface->Flags & WG_IOCTL_INTERFACE_HAS_PRIVATE_KEY) {
memcpy(dev->private_key, wg_iface->PrivateKey, sizeof(dev->private_key));
dev->flags |= WGDEVICE_HAS_PRIVATE_KEY;
}
wg_peer = buf + sizeof(WG_IOCTL_INTERFACE);
for (ULONG i = 0; i < wg_iface->PeersCount; ++i) {
peer = calloc(1, sizeof(*peer));
if (!peer)
goto out;
if (dev->first_peer == NULL)
dev->first_peer = peer;
else
dev->last_peer->next_peer = peer;
dev->last_peer = peer;
if (wg_peer->Flags & WG_IOCTL_PEER_HAS_PUBLIC_KEY) {
memcpy(peer->public_key, wg_peer->PublicKey, sizeof(peer->public_key));
peer->flags |= WGPEER_HAS_PUBLIC_KEY;
}
if (wg_peer->Flags & WG_IOCTL_PEER_HAS_PRESHARED_KEY) {
memcpy(peer->preshared_key, wg_peer->PresharedKey, sizeof(peer->preshared_key));
if (!key_is_zero(peer->preshared_key))
peer->flags |= WGPEER_HAS_PRESHARED_KEY;
}
if (wg_peer->Flags & WG_IOCTL_PEER_HAS_PERSISTENT_KEEPALIVE) {
peer->persistent_keepalive_interval = wg_peer->PersistentKeepalive;
peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
}
if (wg_peer->Flags & WG_IOCTL_PEER_HAS_ENDPOINT) {
if (wg_peer->Endpoint.si_family == AF_INET)
peer->endpoint.addr4 = wg_peer->Endpoint.Ipv4;
else if (wg_peer->Endpoint.si_family == AF_INET6)
peer->endpoint.addr6 = wg_peer->Endpoint.Ipv6;
}
peer->rx_bytes = wg_peer->RxBytes;
peer->tx_bytes = wg_peer->TxBytes;
if (wg_peer->LastHandshake) {
peer->last_handshake_time.tv_sec = wg_peer->LastHandshake / 10000000 - 11644473600LL;
peer->last_handshake_time.tv_nsec = wg_peer->LastHandshake % 10000000 * 100;
}
wg_aip = (void *)wg_peer + sizeof(WG_IOCTL_PEER);
for (ULONG j = 0; j < wg_peer->AllowedIPsCount; ++j) {
aip = calloc(1, sizeof(*aip));
if (!aip)
goto out;
if (peer->first_allowedip == NULL)
peer->first_allowedip = aip;
else
peer->last_allowedip->next_allowedip = aip;
peer->last_allowedip = aip;
aip->family = wg_aip->AddressFamily;
if (wg_aip->AddressFamily == AF_INET) {
memcpy(&aip->ip4, &wg_aip->Address.V4, sizeof(aip->ip4));
aip->cidr = wg_aip->Cidr;
} else if (wg_aip->AddressFamily == AF_INET6) {
memcpy(&aip->ip6, &wg_aip->Address.V6, sizeof(aip->ip6));
aip->cidr = wg_aip->Cidr;
}
++wg_aip;
}
wg_peer = (WG_IOCTL_PEER *)wg_aip;
}
*device = dev;
errno = 0;
out:
ret = -errno;
free(buf);
CloseHandle(handle);
return ret;
}
static int kernel_set_device(struct wgdevice *dev)
{
WG_IOCTL_INTERFACE *wg_iface = NULL;
WG_IOCTL_PEER *wg_peer;
WG_IOCTL_ALLOWED_IP *wg_aip;
DWORD buf_len = sizeof(WG_IOCTL_INTERFACE);
HANDLE handle = kernel_interface_handle(dev->name);
struct wgpeer *peer;
struct wgallowedip *aip;
size_t peer_count, aip_count;
int ret = 0;
if (!handle)
return -errno;
for_each_wgpeer(dev, peer) {
if (DWORD_MAX - buf_len < sizeof(WG_IOCTL_PEER)) {
errno = EOVERFLOW;
goto out;
}
buf_len += sizeof(WG_IOCTL_PEER);
for_each_wgallowedip(peer, aip) {
if (DWORD_MAX - buf_len < sizeof(WG_IOCTL_ALLOWED_IP)) {
errno = EOVERFLOW;
goto out;
}
buf_len += sizeof(WG_IOCTL_ALLOWED_IP);
}
}
wg_iface = calloc(1, buf_len);
if (!wg_iface)
goto out;
if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) {
memcpy(wg_iface->PrivateKey, dev->private_key, sizeof(wg_iface->PrivateKey));
wg_iface->Flags |= WG_IOCTL_INTERFACE_HAS_PRIVATE_KEY;
}
if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) {
wg_iface->ListenPort = dev->listen_port;
wg_iface->Flags |= WG_IOCTL_INTERFACE_HAS_LISTEN_PORT;
}
if (dev->flags & WGDEVICE_REPLACE_PEERS)
wg_iface->Flags |= WG_IOCTL_INTERFACE_REPLACE_PEERS;
peer_count = 0;
wg_peer = (void *)wg_iface + sizeof(WG_IOCTL_INTERFACE);
for_each_wgpeer(dev, peer) {
wg_peer->Flags = WG_IOCTL_PEER_HAS_PUBLIC_KEY;
memcpy(wg_peer->PublicKey, peer->public_key, sizeof(wg_peer->PublicKey));
if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
memcpy(wg_peer->PresharedKey, peer->preshared_key, sizeof(wg_peer->PresharedKey));
wg_peer->Flags |= WG_IOCTL_PEER_HAS_PRESHARED_KEY;
}
if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {
wg_peer->PersistentKeepalive = peer->persistent_keepalive_interval;
wg_peer->Flags |= WG_IOCTL_PEER_HAS_PERSISTENT_KEEPALIVE;
}
if (peer->endpoint.addr.sa_family == AF_INET) {
wg_peer->Endpoint.Ipv4 = peer->endpoint.addr4;
wg_peer->Flags |= WG_IOCTL_PEER_HAS_ENDPOINT;
} else if (peer->endpoint.addr.sa_family == AF_INET6) {
wg_peer->Endpoint.Ipv6 = peer->endpoint.addr6;
wg_peer->Flags |= WG_IOCTL_PEER_HAS_ENDPOINT;
}
if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
wg_peer->Flags |= WG_IOCTL_PEER_REPLACE_ALLOWED_IPS;
if (peer->flags & WGPEER_REMOVE_ME)
wg_peer->Flags |= WG_IOCTL_PEER_REMOVE;
aip_count = 0;
wg_aip = (void *)wg_peer + sizeof(WG_IOCTL_PEER);
for_each_wgallowedip(peer, aip) {
wg_aip->AddressFamily = aip->family;
wg_aip->Cidr = aip->cidr;
if (aip->family == AF_INET)
wg_aip->Address.V4 = aip->ip4;
else if (aip->family == AF_INET6)
wg_aip->Address.V6 = aip->ip6;
else
continue;
++aip_count;
++wg_aip;
}
wg_peer->AllowedIPsCount = aip_count;
++peer_count;
wg_peer = (WG_IOCTL_PEER *)wg_aip;
}
wg_iface->PeersCount = peer_count;
if (!DeviceIoControl(handle, WG_IOCTL_SET, NULL, 0, wg_iface, buf_len, &buf_len, NULL)) {
errno = EACCES;
goto out;
}
errno = 0;
out:
ret = -errno;
free(wg_iface);
CloseHandle(handle);
return ret;
}

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT // SPDX-License-Identifier: GPL-2.0
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */
@ -7,7 +7,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
#include "containers.h" #include "containers.h"
#include "ipc.h"
struct string_list { struct string_list {
char *buffer; char *buffer;
@ -45,10 +44,6 @@ static int string_list_add(struct string_list *list, const char *str)
#include "ipc-linux.h" #include "ipc-linux.h"
#elif defined(__OpenBSD__) #elif defined(__OpenBSD__)
#include "ipc-openbsd.h" #include "ipc-openbsd.h"
#elif defined(__FreeBSD__)
#include "ipc-freebsd.h"
#elif defined(_WIN32)
#include "ipc-windows.h"
#endif #endif
/* first\0second\0third\0forth\0last\0\0 */ /* first\0second\0third\0forth\0last\0\0 */

View File

@ -1,4 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT */ /* SPDX-License-Identifier: GPL-2.0 */
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */

View File

@ -168,7 +168,7 @@ sockets, which bypass Netfilter.) When IPv6 is in use, additional similar lines
Or, perhaps it is desirable to store private keys in encrypted form, such as through use of Or, perhaps it is desirable to store private keys in encrypted form, such as through use of
.BR pass (1): .BR pass (1):
\fBPreUp = wg set %i private-key <(pass WireGuard/private-keys/%i)\fP \fBPostUp = wg set %i private-key <(pass WireGuard/private-keys/%i)\fP
.br .br
For use on a server, the following is a more complicated example involving multiple peers: For use on a server, the following is a more complicated example involving multiple peers:

View File

@ -219,14 +219,7 @@ by running as root:
\fB # modprobe wireguard && echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control\fP \fB # modprobe wireguard && echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control\fP
On OpenBSD and FreeBSD, debugging information can be written into On userspace implementations, it is customary to set the \fILOG_LEVEL\fP environment variable to \fIdebug\fP.
.BR dmesg (1)
on a per-interface basis by using
.BR ifconfig (1):
\fB # ifconfig wg0 debug
On userspace implementations, it is customary to set the \fILOG_LEVEL\fP environment variable to \fIverbose\fP.
.SH ENVIRONMENT VARIABLES .SH ENVIRONMENT VARIABLES
.TP .TP

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT // SPDX-License-Identifier: GPL-2.0
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */
@ -11,7 +11,7 @@
#include "subcommands.h" #include "subcommands.h"
#include "ctype.h" #include "ctype.h"
int pubkey_main(int argc, const char *argv[]) int pubkey_main(int argc, char *argv[])
{ {
uint8_t key[WG_KEY_LEN] __attribute__((aligned(sizeof(uintptr_t)))); uint8_t key[WG_KEY_LEN] __attribute__((aligned(sizeof(uintptr_t))));
char base64[WG_KEY_LEN_BASE64]; char base64[WG_KEY_LEN_BASE64];

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT // SPDX-License-Identifier: GPL-2.0
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */
@ -12,7 +12,7 @@
#include "ipc.h" #include "ipc.h"
#include "subcommands.h" #include "subcommands.h"
int set_main(int argc, const char *argv[]) int set_main(int argc, char *argv[])
{ {
struct wgdevice *device = NULL; struct wgdevice *device = NULL;
int ret = 1; int ret = 1;

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT // SPDX-License-Identifier: GPL-2.0
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */
@ -98,7 +98,7 @@ static bool sync_conf(struct wgdevice *file)
return true; return true;
} }
int setconf_main(int argc, const char *argv[]) int setconf_main(int argc, char *argv[])
{ {
struct wgdevice *device = NULL; struct wgdevice *device = NULL;
struct config_ctx ctx; struct config_ctx ctx;

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT // SPDX-License-Identifier: GPL-2.0
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */
@ -27,7 +27,7 @@
static int peer_cmp(const void *first, const void *second) static int peer_cmp(const void *first, const void *second)
{ {
time_t diff; time_t diff;
const struct wgpeer *a = *(void *const *)first, *b = *(void *const *)second; const struct wgpeer *a = *(const void **)first, *b = *(const void **)second;
if (!a->last_handshake_time.tv_sec && !a->last_handshake_time.tv_nsec && (b->last_handshake_time.tv_sec || b->last_handshake_time.tv_nsec)) if (!a->last_handshake_time.tv_sec && !a->last_handshake_time.tv_nsec && (b->last_handshake_time.tv_sec || b->last_handshake_time.tv_nsec))
return 1; return 1;
@ -75,14 +75,14 @@ static char *key(const uint8_t key[static WG_KEY_LEN])
return base64; return base64;
} }
static const char *maybe_key(const uint8_t maybe_key[static WG_KEY_LEN], bool have_it) static char *maybe_key(const uint8_t maybe_key[static WG_KEY_LEN], bool have_it)
{ {
if (!have_it) if (!have_it)
return "(none)"; return "(none)";
return key(maybe_key); return key(maybe_key);
} }
static const char *masked_key(const uint8_t masked_key[static WG_KEY_LEN]) static char *masked_key(const uint8_t masked_key[static WG_KEY_LEN])
{ {
const char *var = getenv("WG_HIDE_KEYS"); const char *var = getenv("WG_HIDE_KEYS");
@ -312,9 +312,9 @@ static bool ugly_print(struct wgdevice *device, const char *param, bool with_int
else else
printf("off\n"); printf("off\n");
} else if (!strcmp(param, "endpoints")) { } else if (!strcmp(param, "endpoints")) {
if (with_interface)
printf("%s\t", device->name);
for_each_wgpeer(device, peer) { for_each_wgpeer(device, peer) {
if (with_interface)
printf("%s\t", device->name);
printf("%s\t", key(peer->public_key)); printf("%s\t", key(peer->public_key));
if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
printf("%s\n", endpoint(&peer->endpoint.addr)); printf("%s\n", endpoint(&peer->endpoint.addr));
@ -376,7 +376,7 @@ static bool ugly_print(struct wgdevice *device, const char *param, bool with_int
return true; return true;
} }
int show_main(int argc, const char *argv[]) int show_main(int argc, char *argv[])
{ {
int ret = 0; int ret = 0;

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT // SPDX-License-Identifier: GPL-2.0
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */
@ -18,7 +18,7 @@
#include "ipc.h" #include "ipc.h"
#include "subcommands.h" #include "subcommands.h"
int showconf_main(int argc, const char *argv[]) int showconf_main(int argc, char *argv[])
{ {
char base64[WG_KEY_LEN_BASE64]; char base64[WG_KEY_LEN_BASE64];
char ip[INET6_ADDRSTRLEN]; char ip[INET6_ADDRSTRLEN];

View File

@ -1,4 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT */ /* SPDX-License-Identifier: GPL-2.0 */
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */
@ -7,11 +7,11 @@
#define SUBCOMMANDS_H #define SUBCOMMANDS_H
extern const char *PROG_NAME; extern const char *PROG_NAME;
int show_main(int argc, const char *argv[]); int show_main(int argc, char *argv[]);
int showconf_main(int argc, const char *argv[]); int showconf_main(int argc, char *argv[]);
int set_main(int argc, const char *argv[]); int set_main(int argc, char *argv[]);
int setconf_main(int argc, const char *argv[]); int setconf_main(int argc, char *argv[]);
int genkey_main(int argc, const char *argv[]); int genkey_main(int argc, char *argv[]);
int pubkey_main(int argc, const char *argv[]); int pubkey_main(int argc, char *argv[]);
#endif #endif

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT // SPDX-License-Identifier: GPL-2.0
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */
@ -11,7 +11,6 @@
#include <stdbool.h> #include <stdbool.h>
#include <unistd.h> #include <unistd.h>
#include "ctype.h" #include "ctype.h"
#include "terminal.h"
static bool color_mode(void) static bool color_mode(void)
{ {

View File

@ -1,4 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT */ /* SPDX-License-Identifier: GPL-2.0 */
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */

View File

@ -1,16 +0,0 @@
#ifndef __IF_WG_H__
#define __IF_WG_H__
#include <net/if.h>
#include <netinet/in.h>
struct wg_data_io {
char wgd_name[IFNAMSIZ];
void *wgd_data;
size_t wgd_size;
};
#define SIOCSWG _IOWR('i', 210, struct wg_data_io)
#define SIOCGWG _IOWR('i', 211, struct wg_data_io)
#endif

View File

@ -1,80 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2021 WireGuard LLC. All Rights Reserved.
*/
#ifndef _WIREGUARD_NT_H
#define _WIREGUARD_NT_H
#include <ntdef.h>
#include <ws2def.h>
#include <ws2ipdef.h>
#include <inaddr.h>
#include <in6addr.h>
#define WG_KEY_LEN 32
typedef struct _WG_IOCTL_ALLOWED_IP
{
union
{
IN_ADDR V4;
IN6_ADDR V6;
} Address;
ADDRESS_FAMILY AddressFamily;
UCHAR Cidr;
} __attribute__((aligned(8))) WG_IOCTL_ALLOWED_IP;
typedef enum
{
WG_IOCTL_PEER_HAS_PUBLIC_KEY = 1 << 0,
WG_IOCTL_PEER_HAS_PRESHARED_KEY = 1 << 1,
WG_IOCTL_PEER_HAS_PERSISTENT_KEEPALIVE = 1 << 2,
WG_IOCTL_PEER_HAS_ENDPOINT = 1 << 3,
WG_IOCTL_PEER_HAS_PROTOCOL_VERSION = 1 << 4,
WG_IOCTL_PEER_REPLACE_ALLOWED_IPS = 1 << 5,
WG_IOCTL_PEER_REMOVE = 1 << 6,
WG_IOCTL_PEER_UPDATE = 1 << 7
} WG_IOCTL_PEER_FLAG;
typedef struct _WG_IOCTL_PEER
{
WG_IOCTL_PEER_FLAG Flags;
ULONG ProtocolVersion; /* 0 = latest protocol, 1 = this protocol. */
UCHAR PublicKey[WG_KEY_LEN];
UCHAR PresharedKey[WG_KEY_LEN];
USHORT PersistentKeepalive;
SOCKADDR_INET Endpoint;
ULONG64 TxBytes;
ULONG64 RxBytes;
ULONG64 LastHandshake;
ULONG AllowedIPsCount;
} __attribute__((aligned(8))) WG_IOCTL_PEER;
typedef enum
{
WG_IOCTL_INTERFACE_HAS_PUBLIC_KEY = 1 << 0,
WG_IOCTL_INTERFACE_HAS_PRIVATE_KEY = 1 << 1,
WG_IOCTL_INTERFACE_HAS_LISTEN_PORT = 1 << 2,
WG_IOCTL_INTERFACE_REPLACE_PEERS = 1 << 3
} WG_IOCTL_INTERFACE_FLAG;
typedef struct _WG_IOCTL_INTERFACE
{
WG_IOCTL_INTERFACE_FLAG Flags;
USHORT ListenPort;
UCHAR PrivateKey[WG_KEY_LEN];
UCHAR PublicKey[WG_KEY_LEN];
ULONG PeersCount;
} __attribute__((aligned(8))) WG_IOCTL_INTERFACE;
#define WG_IOCTL_GET CTL_CODE(45208U, 321, METHOD_OUT_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
#define WG_IOCTL_SET CTL_CODE(45208U, 322, METHOD_IN_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
#define DEVPKEY_WG_NAME (DEVPROPKEY) { \
{ 0x65726957, 0x7547, 0x7261, { 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x4b, 0x65, 0x79 } }, \
DEVPROPID_FIRST_USABLE + 1 \
}
#endif

View File

@ -1,3 +1,3 @@
#ifndef WIREGUARD_TOOLS_VERSION #ifndef WIREGUARD_TOOLS_VERSION
#define WIREGUARD_TOOLS_VERSION "1.0.20210914" #define WIREGUARD_TOOLS_VERSION "1.0.20200827"
#endif #endif

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
/* /*
* Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* *
* This is a shell script written in C. It very intentionally still functions like * This is a shell script written in C. It very intentionally still functions like
* a shell script, calling out to external executables such as ip(8). * a shell script, calling out to external executables such as ip(8).
@ -25,7 +25,6 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/system_properties.h>
#ifndef WG_PACKAGE_NAME #ifndef WG_PACKAGE_NAME
#define WG_PACKAGE_NAME "com.wireguard.android" #define WG_PACKAGE_NAME "com.wireguard.android"
@ -40,7 +39,6 @@
static bool is_exiting = false; static bool is_exiting = false;
static bool binder_available = false; static bool binder_available = false;
static unsigned int sdk_version;
static void *xmalloc(size_t size) static void *xmalloc(size_t size)
{ {
@ -729,7 +727,7 @@ static void up_if(unsigned int *netid, const char *iface, uint16_t listen_port)
cmd("ip6tables -I INPUT 1 -p udp --dport %u -j %s -m comment --comment \"wireguard rule %s\"", listen_port, should_block_ipv6(iface) ? "DROP" : "ACCEPT", iface); cmd("ip6tables -I INPUT 1 -p udp --dport %u -j %s -m comment --comment \"wireguard rule %s\"", listen_port, should_block_ipv6(iface) ? "DROP" : "ACCEPT", iface);
} }
cmd("ip link set up dev %s", iface); cmd("ip link set up dev %s", iface);
cndc(sdk_version < 31 ? "network create %u vpn 1 1" : "network create %u vpn 1", *netid); cndc("network create %u vpn 1 1", *netid);
cndc("network interface add %u %s", *netid, iface); cndc("network interface add %u %s", *netid, iface);
} }
@ -787,7 +785,6 @@ static uid_t *get_uid_list(const char *selected_applications)
static void set_users(unsigned int netid, const char *excluded_applications, const char *included_applications) static void set_users(unsigned int netid, const char *excluded_applications, const char *included_applications)
{ {
_cleanup_free_ uid_t *uids = NULL; _cleanup_free_ uid_t *uids = NULL;
uid_t *uid;
unsigned int args_per_command = 0; unsigned int args_per_command = 0;
_cleanup_free_ char *ranges = NULL; _cleanup_free_ char *ranges = NULL;
char range[22]; char range[22];
@ -799,14 +796,14 @@ static void set_users(unsigned int netid, const char *excluded_applications, con
} }
if (excluded_applications || !included_applications) { if (excluded_applications || !included_applications) {
uid = uids = get_uid_list(excluded_applications); uids = get_uid_list(excluded_applications);
for (start = 0; *uid; start = *uid + 1, ++uid) { for (start = 0; *uids; start = *uids + 1, ++uids) {
if (start > *uid - 1) if (start > *uids - 1)
continue; continue;
else if (start == *uid - 1) else if (start == *uids - 1)
snprintf(range, sizeof(range), "%u", start); snprintf(range, sizeof(range), "%u", start);
else else
snprintf(range, sizeof(range), "%u-%u", start, *uid - 1); snprintf(range, sizeof(range), "%u-%u", start, *uids - 1);
ranges = concat_and_free(ranges, " ", range); ranges = concat_and_free(ranges, " ", range);
if (++args_per_command % 18 == 0) { if (++args_per_command % 18 == 0) {
cndc("network users add %u %s", netid, ranges); cndc("network users add %u %s", netid, ranges);
@ -819,8 +816,8 @@ static void set_users(unsigned int netid, const char *excluded_applications, con
ranges = concat_and_free(ranges, " ", range); ranges = concat_and_free(ranges, " ", range);
} }
} else { } else {
for (uid = uids = get_uid_list(included_applications); *uid; ++uid) { for (uids = get_uid_list(included_applications); *uids; ++uids) {
snprintf(range, sizeof(range), "%u", *uid); snprintf(range, sizeof(range), "%u", *uids);
ranges = concat_and_free(ranges, " ", range); ranges = concat_and_free(ranges, " ", range);
if (++args_per_command % 18 == 0) { if (++args_per_command % 18 == 0) {
cndc("network users add %u %s", netid, ranges); cndc("network users add %u %s", netid, ranges);
@ -855,7 +852,7 @@ static void set_dnses(unsigned int netid, const char *dnses)
if (!len) if (!len)
return; return;
xregcomp(&regex_ipnothost, "(^[0-9.]+$)|(^.*:.*$)", REG_EXTENDED | REG_NOSUB); xregcomp(&regex_ipnothost, "^[a-zA-Z0-9_=+.-]{1,15}$", REG_EXTENDED | REG_NOSUB);
for (char *dns = strtok(mutable, ", \t\n"); dns; dns = strtok(NULL, ", \t\n")) { for (char *dns = strtok(mutable, ", \t\n"); dns; dns = strtok(NULL, ", \t\n")) {
if (strchr(dns, '\'') || strchr(dns, '\\')) if (strchr(dns, '\'') || strchr(dns, '\\'))
continue; continue;
@ -1280,10 +1277,6 @@ int main(int argc, char *argv[])
_cleanup_free_ char *excluded_applications = NULL; _cleanup_free_ char *excluded_applications = NULL;
_cleanup_free_ char *included_applications = NULL; _cleanup_free_ char *included_applications = NULL;
unsigned int mtu; unsigned int mtu;
char prop[PROP_VALUE_MAX + 1];
if (__system_property_get("ro.build.version.sdk", prop))
sdk_version = atoi(prop);
if (argc == 2 && (!strcmp(argv[1], "help") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) if (argc == 2 && (!strcmp(argv[1], "help") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")))
cmd_usage(argv[0]); cmd_usage(argv[0]);

View File

@ -194,14 +194,14 @@ collect_gateways() {
GATEWAY4="" GATEWAY4=""
while read -r destination gateway _; do while read -r destination gateway _; do
[[ $destination == default && $gateway != "link#"* ]] || continue [[ $destination == default ]] || continue
GATEWAY4="$gateway" GATEWAY4="$gateway"
break break
done < <(netstat -nr -f inet) done < <(netstat -nr -f inet)
GATEWAY6="" GATEWAY6=""
while read -r destination gateway _; do while read -r destination gateway _; do
[[ $destination == default && $gateway != "link#"* ]] || continue [[ $destination == default ]] || continue
GATEWAY6="$gateway" GATEWAY6="$gateway"
break break
done < <(netstat -nr -f inet6) done < <(netstat -nr -f inet6)
@ -324,24 +324,22 @@ monitor_daemon() {
echo "[+] Backgrounding route monitor" >&2 echo "[+] Backgrounding route monitor" >&2
(trap 'del_routes; del_dns; exit 0' INT TERM EXIT (trap 'del_routes; del_dns; exit 0' INT TERM EXIT
exec >/dev/null 2>&1 exec >/dev/null 2>&1
exec 19< <(exec route -n monitor) local event pid=$BASHPID
local event bpid=$BASHPID mpid=$!
[[ ${#DNS[@]} -gt 0 ]] && trap set_dns ALRM [[ ${#DNS[@]} -gt 0 ]] && trap set_dns ALRM
# TODO: this should also check to see if the endpoint actually changes # TODO: this should also check to see if the endpoint actually changes
# in response to incoming packets, and then call set_endpoint_direct_route # in response to incoming packets, and then call set_endpoint_direct_route
# then too. That function should be able to gracefully cleanup if the # then too. That function should be able to gracefully cleanup if the
# endpoints change. # endpoints change.
while read -u 19 -r event; do while read -r event; do
[[ $event == RTM_* ]] || continue [[ $event == RTM_* ]] || continue
ifconfig "$REAL_INTERFACE" >/dev/null 2>&1 || break ifconfig "$REAL_INTERFACE" >/dev/null 2>&1 || break
[[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
[[ -z $MTU ]] && set_mtu [[ -z $MTU ]] && set_mtu
if [[ ${#DNS[@]} -gt 0 ]]; then if [[ ${#DNS[@]} -gt 0 ]]; then
set_dns set_dns
sleep 2 && kill -ALRM $bpid 2>/dev/null & sleep 2 && kill -ALRM $pid 2>/dev/null &
fi fi
done done < <(route -n monitor)) &
kill $mpid) &
[[ -n $LAUNCHED_BY_LAUNCHD ]] || disown [[ -n $LAUNCHED_BY_LAUNCHD ]] || disown
} }
@ -452,8 +450,8 @@ cmd_up() {
local i local i
get_real_interface && die "\`$INTERFACE' already exists as \`$REAL_INTERFACE'" get_real_interface && die "\`$INTERFACE' already exists as \`$REAL_INTERFACE'"
trap 'del_if; del_routes; exit' INT TERM EXIT trap 'del_if; del_routes; exit' INT TERM EXIT
add_if
execute_hooks "${PRE_UP[@]}" execute_hooks "${PRE_UP[@]}"
add_if
set_config set_config
for i in "${ADDRESSES[@]}"; do for i in "${ADDRESSES[@]}"; do
add_addr "$i" add_addr "$i"

View File

@ -8,7 +8,6 @@ set -e -o pipefail
shopt -s extglob shopt -s extglob
export LC_ALL=C export LC_ALL=C
exec 3>&2
SELF="$(readlink -f "${BASH_SOURCE[0]}")" SELF="$(readlink -f "${BASH_SOURCE[0]}")"
export PATH="${SELF%/*}:$PATH" export PATH="${SELF%/*}:$PATH"
@ -29,7 +28,7 @@ PROGRAM="${0##*/}"
ARGS=( "$@" ) ARGS=( "$@" )
cmd() { cmd() {
echo "[#] $*" >&3 echo "[#] $*" >&2
"$@" "$@"
} }
@ -115,16 +114,6 @@ auto_su() {
} }
add_if() { add_if() {
local ret rc
if ret="$(cmd ifconfig wg create name "$INTERFACE" 2>&1 >/dev/null)"; then
return 0
fi
rc=$?
if [[ $ret == *"ifconfig: ioctl SIOCSIFNAME (set name): File exists"* ]]; then
echo "$ret" >&3
return $rc
fi
echo "[!] Missing WireGuard kernel support ($ret). Falling back to slow userspace implementation." >&3
cmd "${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}" "$INTERFACE" cmd "${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}" "$INTERFACE"
} }
@ -152,14 +141,24 @@ del_routes() {
done done
} }
if_exists() {
# HACK: The goal is simply to determine whether or not the interface exists. The
# straight-forward way of doing this would be `ifconfig $INTERFACE`, but this
# invokes the SIOCGIFSTATUS ioctl, which races with interface shutdown inside
# the tun driver, resulting in a kernel panic. So we work around it the stupid
# way by using the one utility that appears to call if_nametoindex fairly early
# and fails if it doesn't exist: `arp`.
if arp -i "$INTERFACE" -a -n >/dev/null 2>&1; then
return 0
else
return 1
fi
}
del_if() { del_if() {
[[ $HAVE_SET_DNS -eq 0 ]] || unset_dns [[ $HAVE_SET_DNS -eq 0 ]] || unset_dns
if [[ -S /var/run/wireguard/$INTERFACE.sock ]]; then cmd rm -f "/var/run/wireguard/$INTERFACE.sock"
cmd rm -f "/var/run/wireguard/$INTERFACE.sock" while if_exists; do
else
cmd ifconfig "$INTERFACE" destroy
fi
while ifconfig "$INTERFACE" >/dev/null 2>&1; do
# HACK: it would be nice to `route monitor` here and wait for RTM_IFANNOUNCE # HACK: it would be nice to `route monitor` here and wait for RTM_IFANNOUNCE
# but it turns out that the announcement is made before the interface # but it turns out that the announcement is made before the interface
# disappears so we sometimes get a hang. So, we're instead left with polling # disappears so we sometimes get a hang. So, we're instead left with polling
@ -176,7 +175,7 @@ add_addr() {
if [[ $1 == *:* ]]; then if [[ $1 == *:* ]]; then
cmd ifconfig "$INTERFACE" inet6 "$1" alias cmd ifconfig "$INTERFACE" inet6 "$1" alias
else else
cmd ifconfig "$INTERFACE" inet "$1" alias cmd ifconfig "$INTERFACE" inet "$1" "${1%%/*}" alias
fi fi
} }
@ -284,19 +283,18 @@ monitor_daemon() {
(make_temp (make_temp
trap 'del_routes; clean_temp; exit 0' INT TERM EXIT trap 'del_routes; clean_temp; exit 0' INT TERM EXIT
exec >/dev/null 2>&1 exec >/dev/null 2>&1
exec 19< <(exec route -n monitor) local event
local event pid=$!
# TODO: this should also check to see if the endpoint actually changes # TODO: this should also check to see if the endpoint actually changes
# in response to incoming packets, and then call set_endpoint_direct_route # in response to incoming packets, and then call set_endpoint_direct_route
# then too. That function should be able to gracefully cleanup if the # then too. That function should be able to gracefully cleanup if the
# endpoints change. # endpoints change.
while read -u 19 -r event; do while read -r event; do
[[ $event == RTM_* ]] || continue [[ $event == RTM_* ]] || continue
ifconfig "$INTERFACE" >/dev/null 2>&1 || break [[ -e /var/run/wireguard/$INTERFACE.sock ]] || break
if_exists || break
[[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
# TODO: set the mtu as well, but only if up # TODO: set the mtu as well, but only if up
done done < <(route -n monitor)) & disown
kill $pid) & disown
} }
HAVE_SET_DNS=0 HAVE_SET_DNS=0
@ -337,7 +335,7 @@ add_route() {
} }
set_config() { set_config() {
echo "$WG_CONFIG" | cmd wg setconf "$INTERFACE" /dev/stdin cmd wg setconf "$INTERFACE" <(echo "$WG_CONFIG")
} }
save_config() { save_config() {
@ -420,8 +418,8 @@ cmd_up() {
local i local i
[[ -z $(ifconfig "$INTERFACE" 2>/dev/null) ]] || die "\`$INTERFACE' already exists" [[ -z $(ifconfig "$INTERFACE" 2>/dev/null) ]] || die "\`$INTERFACE' already exists"
trap 'del_if; del_routes; clean_temp; exit' INT TERM EXIT trap 'del_if; del_routes; clean_temp; exit' INT TERM EXIT
add_if
execute_hooks "${PRE_UP[@]}" execute_hooks "${PRE_UP[@]}"
add_if
set_config set_config
for i in "${ADDRESSES[@]}"; do for i in "${ADDRESSES[@]}"; do
add_addr "$i" add_addr "$i"

View File

@ -81,8 +81,27 @@ read_bool() {
esac esac
} }
has_cap_net_admin() {
local line
while read -r line; do
[[ $line =~ ^CapEff:\ [0-9a-f]*([0-9a-f])[0-9a-f]{3}$ ]] || continue
(( 0x${BASH_REMATCH[1]} & 1 != 0 )) && return 0
return 1
done < /proc/self/status
return 1
}
config_file_is_writable() {
local cf="$1"
[[ $cf =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]] && CONFIG_FILE="/etc/wireguard/$cf.conf"
[[ -w $cf ]] && return 0
return 1
}
auto_su() { auto_su() {
[[ $UID == 0 ]] || exec sudo -p "$PROGRAM must be run as root. Please enter the password for %u to continue: " -- "$BASH" -- "$SELF" "${ARGS[@]}" [[ $UID == 0 ]] && return 0
has_cap_net_admin && config_file_is_writable "${ARGS[2]}" && return 0
exec sudo -p "$PROGRAM must be run as root. Please enter the password for %u to continue: " -- "$BASH" -- "$SELF" "${ARGS[@]}"
} }
add_if() { add_if() {
@ -220,9 +239,9 @@ add_default() {
fi fi
local proto=-4 iptables=iptables pf=ip local proto=-4 iptables=iptables pf=ip
[[ $1 == *:* ]] && proto=-6 iptables=ip6tables pf=ip6 [[ $1 == *:* ]] && proto=-6 iptables=ip6tables pf=ip6
cmd ip $proto route add "$1" dev "$INTERFACE" table $table
cmd ip $proto rule add not fwmark $table table $table cmd ip $proto rule add not fwmark $table table $table
cmd ip $proto rule add table main suppress_prefixlength 0 cmd ip $proto rule add table main suppress_prefixlength 0
cmd ip $proto route add "$1" dev "$INTERFACE" table $table
local marker="-m comment --comment \"wg-quick(8) rule for $INTERFACE\"" restore=$'*raw\n' nftable="wg-quick-$INTERFACE" nftcmd local marker="-m comment --comment \"wg-quick(8) rule for $INTERFACE\"" restore=$'*raw\n' nftable="wg-quick-$INTERFACE" nftcmd
printf -v nftcmd '%sadd table %s %s\n' "$nftcmd" "$pf" "$nftable" printf -v nftcmd '%sadd table %s %s\n' "$nftcmd" "$pf" "$nftable"
@ -327,8 +346,8 @@ cmd_up() {
local i local i
[[ -z $(ip link show dev "$INTERFACE" 2>/dev/null) ]] || die "\`$INTERFACE' already exists" [[ -z $(ip link show dev "$INTERFACE" 2>/dev/null) ]] || die "\`$INTERFACE' already exists"
trap 'del_if; exit' INT TERM EXIT trap 'del_if; exit' INT TERM EXIT
add_if
execute_hooks "${PRE_UP[@]}" execute_hooks "${PRE_UP[@]}"
add_if
set_config set_config
for i in "${ADDRESSES[@]}"; do for i in "${ADDRESSES[@]}"; do
add_addr "$i" add_addr "$i"

View File

@ -88,33 +88,42 @@ auto_su() {
get_real_interface() { get_real_interface() {
local interface line local interface diff
while IFS= read -r line; do wg show interfaces >/dev/null
if [[ $line =~ ^([a-z]+[0-9]+):\ .+ ]]; then [[ -f "/var/run/wireguard/$INTERFACE.name" ]] || return 1
interface="${BASH_REMATCH[1]}" interface="$(< "/var/run/wireguard/$INTERFACE.name")"
continue if [[ $interface != wg* ]]; then
fi [[ -n $interface && -S "/var/run/wireguard/$interface.sock" ]] || return 1
if [[ $interface == wg* && $line =~ ^\ description:\ wg-quick:\ (.+) && ${BASH_REMATCH[1]} == "$INTERFACE" ]]; then diff=$(( $(stat -f %m "/var/run/wireguard/$interface.sock" 2>/dev/null || echo 200) - $(stat -f %m "/var/run/wireguard/$INTERFACE.name" 2>/dev/null || echo 100) ))
REAL_INTERFACE="$interface" [[ $diff -ge 2 || $diff -le -2 ]] && return 1
return 0 echo "[+] Tun interface for $INTERFACE is $interface" >&2
fi else
done < <(ifconfig) [[ " $(wg show interfaces) " == *" $interface "* ]] || return 1
return 1 fi
REAL_INTERFACE="$interface"
return 0
} }
add_if() { add_if() {
local index=0 ret
while true; do while true; do
local -A existing_ifs="( $(wg show interfaces | sed 's/\([^ ]*\)/[\1]=1/g') )" if ret="$(cmd ifconfig wg$index create 2>&1)"; then
local index ret mkdir -p "/var/run/wireguard/"
for ((index=0; index <= 2147483647; ++index)); do [[ -v existing_ifs[wg$index] ]] || break; done echo wg$index > /var/run/wireguard/$INTERFACE.name
if ret="$(cmd ifconfig wg$index create description "wg-quick: $INTERFACE" 2>&1)"; then get_real_interface
REAL_INTERFACE="wg$index"
return 0 return 0
fi fi
[[ $ret == *"ifconfig: SIOCIFCREATE: File exists"* ]] && continue if [[ $ret != *"ifconfig: SIOCIFCREATE: File exists"* ]]; then
echo "$ret" >&3 echo "[!] Missing WireGuard kernel support ($ret). Falling back to slow userspace implementation." >&3
return 1 break
fi
echo "[+] wg$index in use, trying next"
((++index))
done done
export WG_TUN_NAME_FILE="/var/run/wireguard/$INTERFACE.name"
mkdir -p "/var/run/wireguard/"
cmd "${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}" tun
get_real_interface
} }
del_routes() { del_routes() {
@ -144,7 +153,12 @@ del_routes() {
del_if() { del_if() {
unset_dns unset_dns
[[ -n $REAL_INTERFACE ]] && cmd ifconfig $REAL_INTERFACE destroy if [[ -n $REAL_INTERFACE && $REAL_INTERFACE != wg* ]]; then
cmd rm -f "/var/run/wireguard/$REAL_INTERFACE.sock"
else
cmd ifconfig $REAL_INTERFACE destroy
fi
cmd rm -f "/var/run/wireguard/$INTERFACE.name"
} }
up_if() { up_if() {
@ -266,42 +280,30 @@ monitor_daemon() {
echo "[+] Backgrounding route monitor" >&2 echo "[+] Backgrounding route monitor" >&2
(trap 'del_routes; exit 0' INT TERM EXIT (trap 'del_routes; exit 0' INT TERM EXIT
exec >/dev/null 2>&1 exec >/dev/null 2>&1
exec 19< <(exec route -n monitor) local event
local event pid=$!
# TODO: this should also check to see if the endpoint actually changes # TODO: this should also check to see if the endpoint actually changes
# in response to incoming packets, and then call set_endpoint_direct_route # in response to incoming packets, and then call set_endpoint_direct_route
# then too. That function should be able to gracefully cleanup if the # then too. That function should be able to gracefully cleanup if the
# endpoints change. # endpoints change.
while read -u 19 -r event; do while read -r event; do
[[ $event == RTM_* ]] || continue [[ $event == RTM_* ]] || continue
ifconfig "$REAL_INTERFACE" >/dev/null 2>&1 || break ifconfig "$REAL_INTERFACE" >/dev/null 2>&1 || break
[[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
# TODO: set the mtu as well, but only if up # TODO: set the mtu as well, but only if up
done done < <(route -n monitor)) & disown
kill $pid) & disown
} }
set_dns() { set_dns() {
[[ ${#DNS[@]} -gt 0 ]] || return 0 [[ ${#DNS[@]} -gt 0 ]] || return 0
# TODO: this is a horrible way of doing it. Has OpenBSD no resolvconf?
# TODO: add exclusive support for nameservers
if pgrep -qx unwind; then
echo "[!] WARNING: unwind will leak DNS queries" >&2
elif pgrep -qx resolvd; then
echo "[!] WARNING: resolvd may leak DNS queries" >&2
else
echo "[+] resolvd is not running, DNS will not be configured" >&2
return 0
fi
cmd cp /etc/resolv.conf "/etc/resolv.conf.wg-quick-backup.$INTERFACE" cmd cp /etc/resolv.conf "/etc/resolv.conf.wg-quick-backup.$INTERFACE"
[[ ${#DNS_SEARCH[@]} -eq 0 ]] || cmd printf 'search %s\n' "${DNS_SEARCH[*]}" > /etc/resolv.conf { cmd printf 'nameserver %s\n' "${DNS[@]}"
route nameserver ${REAL_INTERFACE} ${DNS[@]} [[ ${#DNS_SEARCH[@]} -eq 0 ]] || cmd printf 'search %s\n' "${DNS_SEARCH[*]}"
} > /etc/resolv.conf
} }
unset_dns() { unset_dns() {
[[ -f "/etc/resolv.conf.wg-quick-backup.$INTERFACE" ]] || return 0 [[ -f "/etc/resolv.conf.wg-quick-backup.$INTERFACE" ]] || return 0
route nameserver ${REAL_INTERFACE}
cmd mv "/etc/resolv.conf.wg-quick-backup.$INTERFACE" /etc/resolv.conf cmd mv "/etc/resolv.conf.wg-quick-backup.$INTERFACE" /etc/resolv.conf
} }
@ -417,8 +419,8 @@ cmd_up() {
local i local i
get_real_interface && die "\`$INTERFACE' already exists as \`$REAL_INTERFACE'" get_real_interface && die "\`$INTERFACE' already exists as \`$REAL_INTERFACE'"
trap 'del_if; del_routes; exit' INT TERM EXIT trap 'del_if; del_routes; exit' INT TERM EXIT
add_if
execute_hooks "${PRE_UP[@]}" execute_hooks "${PRE_UP[@]}"
add_if
set_config set_config
for i in "${ADDRESSES[@]}"; do for i in "${ADDRESSES[@]}"; do
add_addr "$i" add_addr "$i"
@ -436,7 +438,9 @@ cmd_up() {
} }
cmd_down() { cmd_down() {
get_real_interface || die "\`$INTERFACE' is not a WireGuard interface" if ! get_real_interface || [[ " $(wg show interfaces) " != *" $REAL_INTERFACE "* ]]; then
die "\`$INTERFACE' is not a WireGuard interface"
fi
execute_hooks "${PRE_DOWN[@]}" execute_hooks "${PRE_DOWN[@]}"
[[ $SAVE_CONFIG -eq 0 ]] || save_config [[ $SAVE_CONFIG -eq 0 ]] || save_config
del_if del_if
@ -445,7 +449,9 @@ cmd_down() {
} }
cmd_save() { cmd_save() {
get_real_interface || die "\`$INTERFACE' is not a WireGuard interface" if ! get_real_interface || [[ " $(wg show interfaces) " != *" $REAL_INTERFACE "* ]]; then
die "\`$INTERFACE' is not a WireGuard interface"
fi
save_config save_config
} }

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT // SPDX-License-Identifier: GPL-2.0
/* /*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */
@ -14,7 +14,7 @@ const char *PROG_NAME;
static const struct { static const struct {
const char *subcommand; const char *subcommand;
int (*function)(int, const char**); int (*function)(int, char**);
const char *description; const char *description;
} subcommands[] = { } subcommands[] = {
{ "show", show_main, "Shows the current configuration and device information" }, { "show", show_main, "Shows the current configuration and device information" },
@ -37,7 +37,7 @@ static void show_usage(FILE *file)
fprintf(file, "You may pass `--help' to any of these subcommands to view usage.\n"); fprintf(file, "You may pass `--help' to any of these subcommands to view usage.\n");
} }
int main(int argc, const char *argv[]) int main(int argc, char *argv[])
{ {
PROG_NAME = argv[0]; PROG_NAME = argv[0];
@ -51,7 +51,7 @@ int main(int argc, const char *argv[])
} }
if (argc == 1) { if (argc == 1) {
static const char *new_argv[] = { "show", NULL }; static char *new_argv[] = { "show", NULL };
return show_main(1, new_argv); return show_main(1, new_argv);
} }

View File

@ -25,3 +25,5 @@
char *strsep(char **str, const char *sep); char *strsep(char **str, const char *sep);
ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp); ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp);
ssize_t getline(char **buf, size_t *bufsiz, 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);

View File

@ -1,61 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
*/
#ifndef _HASHTABLE_H
#define _HASHTABLE_H
#include <string.h>
enum { HASHTABLE_ENTRY_BUCKETS_POW2 = 1 << 10 };
struct hashtable_entry {
char *key;
void *value;
struct hashtable_entry *next;
};
struct hashtable {
struct hashtable_entry *entry_buckets[HASHTABLE_ENTRY_BUCKETS_POW2];
};
static unsigned int hashtable_bucket(const char *str)
{
unsigned long hash = 5381;
char c;
while ((c = *str++))
hash = ((hash << 5) + hash) ^ c;
return hash & (HASHTABLE_ENTRY_BUCKETS_POW2 - 1);
}
static struct hashtable_entry *hashtable_find_entry(struct hashtable *hashtable, const char *key)
{
struct hashtable_entry *entry;
for (entry = hashtable->entry_buckets[hashtable_bucket(key)]; entry; entry = entry->next) {
if (!strcmp(entry->key, key))
return entry;
}
return NULL;
}
static struct hashtable_entry *hashtable_find_or_insert_entry(struct hashtable *hashtable, const char *key)
{
struct hashtable_entry **entry;
for (entry = &hashtable->entry_buckets[hashtable_bucket(key)]; *entry; entry = &(*entry)->next) {
if (!strcmp((*entry)->key, key))
return *entry;
}
*entry = calloc(1, sizeof(**entry));
if (!*entry)
return NULL;
(*entry)->key = strdup(key);
if (!(*entry)->key) {
free(*entry);
*entry = NULL;
return NULL;
}
return *entry;
}
#endif

View File

@ -10,22 +10,12 @@
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
#endif #endif
extern void NTAPI RtlGetNtVersionNumbers(DWORD *major, DWORD *minor, DWORD *build);
bool is_win7 = false;
__attribute__((constructor)) static void init(void) __attribute__((constructor)) static void init(void)
{ {
char *colormode; char *colormode;
DWORD console_mode, major, minor; DWORD console_mode;
HANDLE stdout_handle; HANDLE stdout_handle;
WSADATA wsaData; WSADATA wsaData;
if (!SetDllDirectoryA("") || !SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32))
abort();
RtlGetNtVersionNumbers(&major, &minor, NULL);
is_win7 = (major == 6 && minor <= 1) || major < 6;
WSAStartup(MAKEWORD(2, 2), &wsaData); WSAStartup(MAKEWORD(2, 2), &wsaData);
stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); // We don't close this. stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); // We don't close this.

View File

@ -6,6 +6,8 @@
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h> #include <windows.h>
char *strsep(char **str, const char *sep) char *strsep(char **str, const char *sep)
@ -67,3 +69,37 @@ ssize_t getline(char **buf, size_t *bufsiz, FILE *fp)
{ {
return getdelim(buf, bufsiz, '\n', 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;
}

View File

@ -1,20 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
#include <windows.h>
#include <delayimp.h>
static FARPROC WINAPI delayed_load_library_hook(unsigned dliNotify, PDelayLoadInfo pdli)
{
HMODULE library;
if (dliNotify != dliNotePreLoadLibrary)
return NULL;
library = LoadLibraryExA(pdli->szDll, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (!library)
abort();
return (FARPROC)library;
}
PfnDliHook __pfnDliNotifyHook2 = delayed_load_library_hook;

View File

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="wg" type="win32" />
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
</application>
</compatibility>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" />
</requestedPrivileges>
</security>
</trustInfo>
</assembly>

View File

@ -1,40 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2020 Jason A. Donenfeld. All Rights Reserved.
*/
#include <windows.h>
#pragma code_page(65001) // UTF-8
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST manifest.xml
#define STRINGIZE(x) #x
#define EXPAND(x) STRINGIZE(x)
VS_VERSION_INFO VERSIONINFO
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "WireGuard LLC"
VALUE "FileDescription", "WireGuard wg(8) CLI: Fast, Modern, Secure VPN Tunnel"
VALUE "FileVersion", EXPAND(VERSION_STR)
VALUE "InternalName", "wg"
VALUE "LegalCopyright", "Copyright © 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved."
VALUE "OriginalFilename", "wg.exe"
VALUE "ProductName", "WireGuard"
VALUE "ProductVersion", EXPAND(VERSION_STR)
VALUE "Comments", "https://www.wireguard.com/"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 0x4b0
END
END