Compare commits
64 Commits
v1.0.20200
...
master
Author | SHA1 | Date |
---|---|---|
|
13f4ac4cb7 | |
|
729242a114 | |
|
e6888dd74e | |
|
b4f6b4f229 | |
|
139aac59a5 | |
|
dbf49a7d17 | |
|
ca2e89ff21 | |
|
7b2ae7aa2f | |
|
71799a8f6d | |
|
5b9c1d6d74 | |
|
c0b68d2eaf | |
|
1fd9570839 | |
|
b906ecb614 | |
|
1ee37b8e48 | |
|
3ba6527130 | |
|
84ac6add7e | |
|
af260d529e | |
|
b3aafa6103 | |
|
52597c3515 | |
|
fabe24df3a | |
|
c70bea7a31 | |
|
d58df7ed10 | |
|
f65c82456d | |
|
9a7e4364b1 | |
|
197689a3cd | |
|
ecb1ea29d7 | |
|
96e42feb3f | |
|
3124afbea3 | |
|
163cef8b90 | |
|
a43f0b634e | |
|
622408872f | |
|
9c811e0f2d | |
|
4e4867dc95 | |
|
f51349c52b | |
|
576e40056d | |
|
396b85280a | |
|
88bc64366e | |
|
957702af94 | |
|
6d3b876492 | |
|
e8fa0f662f | |
|
f97e81c094 | |
|
457f96b65e | |
|
66ed611bd0 | |
|
7e506135f7 | |
|
5e24780d4c | |
|
843a256697 | |
|
66714e2c47 | |
|
b637db4692 | |
|
c3f26340e6 | |
|
265e81a344 | |
|
7a321ce808 | |
|
91fbeb4a92 | |
|
9a0d65e2af | |
|
fbca033c69 | |
|
26683f6c9a | |
|
13fac76a71 | |
|
cf2bf09524 | |
|
b4a8a18797 | |
|
a66219fa10 | |
|
eb4665ecf0 | |
|
197995d50c | |
|
c45d422a93 | |
|
56cb39fb22 | |
|
0cfde946b1 |
|
@ -1,6 +1,9 @@
|
|||
cscope.out
|
||||
*.o
|
||||
*.d
|
||||
*.lib
|
||||
*.dll
|
||||
*.gch
|
||||
*.dwo
|
||||
src/wg
|
||||
src/wg.exe
|
||||
|
|
|
@ -81,7 +81,6 @@ enum wgallowedip_attribute {
|
|||
/* libmnl mini library: */
|
||||
|
||||
#define MNL_SOCKET_AUTOPID 0
|
||||
#define MNL_SOCKET_BUFFER_SIZE (sysconf(_SC_PAGESIZE) < 8192L ? sysconf(_SC_PAGESIZE) : 8192L)
|
||||
#define MNL_ALIGNTO 4
|
||||
#define MNL_ALIGN(len) (((len)+MNL_ALIGNTO-1) & ~(MNL_ALIGNTO-1))
|
||||
#define MNL_NLMSG_HDRLEN MNL_ALIGN(sizeof(struct nlmsghdr))
|
||||
|
@ -129,6 +128,18 @@ typedef int (*mnl_cb_t)(const struct nlmsghdr *nlh, void *data);
|
|||
#define MNL_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
|
||||
#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)
|
||||
{
|
||||
return len + MNL_NLMSG_HDRLEN;
|
||||
|
@ -163,7 +174,6 @@ static void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh, size_t off
|
|||
return (void *)nlh + MNL_NLMSG_HDRLEN + MNL_ALIGN(offset);
|
||||
}
|
||||
|
||||
|
||||
static bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len)
|
||||
{
|
||||
return len >= (int)sizeof(struct nlmsghdr) &&
|
||||
|
@ -341,7 +351,6 @@ static uint32_t mnl_attr_get_u32(const struct nlattr *attr)
|
|||
return *((uint32_t *)mnl_attr_get_payload(attr));
|
||||
}
|
||||
|
||||
|
||||
static uint64_t mnl_attr_get_u64(const struct nlattr *attr)
|
||||
{
|
||||
uint64_t tmp;
|
||||
|
@ -409,14 +418,12 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
static bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen,
|
||||
uint16_t type, 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,
|
||||
uint16_t type, uint32_t data)
|
||||
{
|
||||
|
@ -441,12 +448,12 @@ static void mnl_attr_nest_cancel(struct nlmsghdr *nlh, struct nlattr *start)
|
|||
nlh->nlmsg_len -= mnl_nlmsg_get_payload_tail(nlh) - (void *)start;
|
||||
}
|
||||
|
||||
static int mnl_cb_noop(const struct nlmsghdr *nlh, void *data)
|
||||
static int mnl_cb_noop(__attribute__((unused)) const struct nlmsghdr *nlh, __attribute__((unused)) void *data)
|
||||
{
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int mnl_cb_error(const struct nlmsghdr *nlh, void *data)
|
||||
static int mnl_cb_error(const struct nlmsghdr *nlh, __attribute__((unused)) void *data)
|
||||
{
|
||||
const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
|
||||
|
||||
|
@ -463,7 +470,7 @@ static int mnl_cb_error(const struct nlmsghdr *nlh, void *data)
|
|||
return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
|
||||
}
|
||||
|
||||
static int mnl_cb_stop(const struct nlmsghdr *nlh, void *data)
|
||||
static int mnl_cb_stop(__attribute__((unused)) const struct nlmsghdr *nlh, __attribute__((unused)) void *data)
|
||||
{
|
||||
return MNL_CB_STOP;
|
||||
}
|
||||
|
@ -496,13 +503,11 @@ static int __mnl_cb_run(const void *buf, size_t numbytes,
|
|||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (nlh->nlmsg_flags & NLM_F_DUMP_INTR) {
|
||||
errno = EINTR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
|
||||
if (cb_data){
|
||||
ret = cb_data(nlh, data);
|
||||
|
@ -572,7 +577,6 @@ static struct mnl_socket *mnl_socket_open(int bus)
|
|||
return __mnl_socket_open(bus, 0);
|
||||
}
|
||||
|
||||
|
||||
static int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid)
|
||||
{
|
||||
int ret;
|
||||
|
@ -602,7 +606,6 @@ static int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t mnl_socket_sendto(const struct mnl_socket *nl, const void *buf,
|
||||
size_t len)
|
||||
{
|
||||
|
@ -613,7 +616,6 @@ static ssize_t mnl_socket_sendto(const struct mnl_socket *nl, const void *buf,
|
|||
(struct sockaddr *) &snl, sizeof(snl));
|
||||
}
|
||||
|
||||
|
||||
static ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf,
|
||||
size_t bufsiz)
|
||||
{
|
||||
|
@ -750,7 +752,7 @@ static int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void
|
|||
|
||||
do {
|
||||
err = mnl_socket_recvfrom(nlg->nl, nlg->buf,
|
||||
MNL_SOCKET_BUFFER_SIZE);
|
||||
mnl_ideal_socket_buffer_size());
|
||||
if (err <= 0)
|
||||
break;
|
||||
err = mnl_cb_run2(nlg->buf, err, nlg->seq, nlg->portid,
|
||||
|
@ -796,9 +798,10 @@ static struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t ver
|
|||
nlg = malloc(sizeof(*nlg));
|
||||
if (!nlg)
|
||||
return NULL;
|
||||
nlg->id = 0;
|
||||
|
||||
err = -ENOMEM;
|
||||
nlg->buf = malloc(MNL_SOCKET_BUFFER_SIZE);
|
||||
nlg->buf = malloc(mnl_ideal_socket_buffer_size());
|
||||
if (!nlg->buf)
|
||||
goto err_buf_alloc;
|
||||
|
||||
|
@ -942,7 +945,7 @@ static int fetch_device_names(struct string_list *list)
|
|||
struct ifinfomsg *ifm;
|
||||
|
||||
ret = -ENOMEM;
|
||||
rtnl_buffer = calloc(MNL_SOCKET_BUFFER_SIZE, 1);
|
||||
rtnl_buffer = calloc(mnl_ideal_socket_buffer_size(), 1);
|
||||
if (!rtnl_buffer)
|
||||
goto cleanup;
|
||||
|
||||
|
@ -973,7 +976,7 @@ static int fetch_device_names(struct string_list *list)
|
|||
}
|
||||
|
||||
another:
|
||||
if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, MNL_SOCKET_BUFFER_SIZE)) < 0) {
|
||||
if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, mnl_ideal_socket_buffer_size())) < 0) {
|
||||
ret = -errno;
|
||||
goto cleanup;
|
||||
}
|
||||
|
@ -1009,7 +1012,7 @@ static int add_del_iface(const char *ifname, bool add)
|
|||
struct ifinfomsg *ifm;
|
||||
struct nlattr *nest;
|
||||
|
||||
rtnl_buffer = calloc(MNL_SOCKET_BUFFER_SIZE, 1);
|
||||
rtnl_buffer = calloc(mnl_ideal_socket_buffer_size(), 1);
|
||||
if (!rtnl_buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto cleanup;
|
||||
|
@ -1041,7 +1044,7 @@ static int add_del_iface(const char *ifname, bool add)
|
|||
ret = -errno;
|
||||
goto cleanup;
|
||||
}
|
||||
if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, MNL_SOCKET_BUFFER_SIZE)) < 0) {
|
||||
if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, mnl_ideal_socket_buffer_size())) < 0) {
|
||||
ret = -errno;
|
||||
goto cleanup;
|
||||
}
|
||||
|
@ -1096,10 +1099,10 @@ again:
|
|||
for (peer = peer ? peer : dev->first_peer; peer; peer = peer->next_peer) {
|
||||
uint32_t flags = 0;
|
||||
|
||||
peer_nest = mnl_attr_nest_start_check(nlh, MNL_SOCKET_BUFFER_SIZE, 0);
|
||||
peer_nest = mnl_attr_nest_start_check(nlh, mnl_ideal_socket_buffer_size(), 0);
|
||||
if (!peer_nest)
|
||||
goto toobig_peers;
|
||||
if (!mnl_attr_put_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_PUBLIC_KEY, sizeof(peer->public_key), peer->public_key))
|
||||
if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_PUBLIC_KEY, sizeof(peer->public_key), peer->public_key))
|
||||
goto toobig_peers;
|
||||
if (peer->flags & WGPEER_REMOVE_ME)
|
||||
flags |= WGPEER_F_REMOVE_ME;
|
||||
|
@ -1107,45 +1110,45 @@ again:
|
|||
if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
|
||||
flags |= WGPEER_F_REPLACE_ALLOWEDIPS;
|
||||
if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
|
||||
if (!mnl_attr_put_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_PRESHARED_KEY, sizeof(peer->preshared_key), peer->preshared_key))
|
||||
if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_PRESHARED_KEY, sizeof(peer->preshared_key), peer->preshared_key))
|
||||
goto toobig_peers;
|
||||
}
|
||||
if (peer->endpoint.addr.sa_family == AF_INET) {
|
||||
if (!mnl_attr_put_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr4), &peer->endpoint.addr4))
|
||||
if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr4), &peer->endpoint.addr4))
|
||||
goto toobig_peers;
|
||||
} else if (peer->endpoint.addr.sa_family == AF_INET6) {
|
||||
if (!mnl_attr_put_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr6), &peer->endpoint.addr6))
|
||||
if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr6), &peer->endpoint.addr6))
|
||||
goto toobig_peers;
|
||||
}
|
||||
if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {
|
||||
if (!mnl_attr_put_u16_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval))
|
||||
if (!mnl_attr_put_u16_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval))
|
||||
goto toobig_peers;
|
||||
}
|
||||
}
|
||||
if (flags) {
|
||||
if (!mnl_attr_put_u32_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_FLAGS, flags))
|
||||
if (!mnl_attr_put_u32_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_FLAGS, flags))
|
||||
goto toobig_peers;
|
||||
}
|
||||
if (peer->first_allowedip) {
|
||||
if (!allowedip)
|
||||
allowedip = peer->first_allowedip;
|
||||
allowedips_nest = mnl_attr_nest_start_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_ALLOWEDIPS);
|
||||
allowedips_nest = mnl_attr_nest_start_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_ALLOWEDIPS);
|
||||
if (!allowedips_nest)
|
||||
goto toobig_allowedips;
|
||||
for (; allowedip; allowedip = allowedip->next_allowedip) {
|
||||
allowedip_nest = mnl_attr_nest_start_check(nlh, MNL_SOCKET_BUFFER_SIZE, 0);
|
||||
allowedip_nest = mnl_attr_nest_start_check(nlh, mnl_ideal_socket_buffer_size(), 0);
|
||||
if (!allowedip_nest)
|
||||
goto toobig_allowedips;
|
||||
if (!mnl_attr_put_u16_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_FAMILY, allowedip->family))
|
||||
if (!mnl_attr_put_u16_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_FAMILY, allowedip->family))
|
||||
goto toobig_allowedips;
|
||||
if (allowedip->family == AF_INET) {
|
||||
if (!mnl_attr_put_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip4), &allowedip->ip4))
|
||||
if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip4), &allowedip->ip4))
|
||||
goto toobig_allowedips;
|
||||
} else if (allowedip->family == AF_INET6) {
|
||||
if (!mnl_attr_put_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip6), &allowedip->ip6))
|
||||
if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip6), &allowedip->ip6))
|
||||
goto toobig_allowedips;
|
||||
}
|
||||
if (!mnl_attr_put_u8_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_CIDR_MASK, allowedip->cidr))
|
||||
if (!mnl_attr_put_u8_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_CIDR_MASK, allowedip->cidr))
|
||||
goto toobig_allowedips;
|
||||
mnl_attr_nest_end(nlh, allowedip_nest);
|
||||
allowedip_nest = NULL;
|
||||
|
|
|
@ -40,17 +40,19 @@ enum wg_peer_flags {
|
|||
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 {
|
||||
enum wg_peer_flags flags;
|
||||
|
||||
wg_key public_key;
|
||||
wg_key preshared_key;
|
||||
|
||||
union {
|
||||
struct sockaddr addr;
|
||||
struct sockaddr_in addr4;
|
||||
struct sockaddr_in6 addr6;
|
||||
} endpoint;
|
||||
wg_endpoint endpoint;
|
||||
|
||||
struct timespec64 last_handshake_time;
|
||||
uint64_t rx_bytes, tx_bytes;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?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">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
|
|
|
@ -16,7 +16,7 @@ INTERFACE="${BASH_REMATCH[1]}"
|
|||
process_peer() {
|
||||
[[ $PEER_SECTION -ne 1 || -z $PUBLIC_KEY || -z $ENDPOINT ]] && return 0
|
||||
[[ $(wg show "$INTERFACE" latest-handshakes) =~ ${PUBLIC_KEY//+/\\+}\ ([0-9]+) ]] || return 0
|
||||
(( ($(date +%s) - ${BASH_REMATCH[1]}) > 135 )) || return 0
|
||||
(( ($EPOCHSECONDS - ${BASH_REMATCH[1]}) > 135 )) || return 0
|
||||
wg set "$INTERFACE" peer "$PUBLIC_KEY" endpoint "$ENDPOINT"
|
||||
reset_peer_section
|
||||
}
|
||||
|
|
|
@ -195,10 +195,6 @@ int magic_create_sock4(uint16_t listen_port)
|
|||
if (fd < 0)
|
||||
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));
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
@ -228,10 +224,6 @@ int magic_create_sock6(uint16_t listen_port)
|
|||
if (fd < 0)
|
||||
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));
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
|
26
src/Makefile
26
src/Makefile
|
@ -48,18 +48,29 @@ CFLAGS += -DRUNSTATEDIR="\"$(RUNSTATEDIR)\""
|
|||
ifeq ($(DEBUG),yes)
|
||||
CFLAGS += -g
|
||||
endif
|
||||
WIREGUARD_TOOLS_VERSION = $(patsubst v%,%,$(shell GIT_CEILING_DIRECTORIES="$(PWD)/../.." git describe --dirty 2>/dev/null))
|
||||
WIREGUARD_TOOLS_VERSION = $(patsubst v%,%,$(shell GIT_DIR="$(PWD)/../.git" git describe --dirty 2>/dev/null))
|
||||
ifneq ($(WIREGUARD_TOOLS_VERSION),)
|
||||
CFLAGS += -D'WIREGUARD_TOOLS_VERSION="$(WIREGUARD_TOOLS_VERSION)"'
|
||||
endif
|
||||
ifeq ($(PLATFORM),freebsd)
|
||||
LDLIBS += -lnv
|
||||
endif
|
||||
ifeq ($(PLATFORM),haiku)
|
||||
LDLIBS += -lnetwork -lbsd
|
||||
endif
|
||||
ifeq ($(PLATFORM),windows)
|
||||
CC := x86_64-w64-mingw32-gcc
|
||||
CFLAGS += -Iwincompat/include -include wincompat/compat.h
|
||||
LDLIBS += -lws2_32
|
||||
wg: wincompat/libc.o wincompat/init.o
|
||||
CC := x86_64-w64-mingw32-clang
|
||||
WINDRES := $(shell $(CC) $(CFLAGS) -print-prog-name=windres 2>/dev/null)
|
||||
CFLAGS += -Iwincompat/include -include wincompat/compat.h -DWINVER=0x0601 -D_WIN32_WINNT=0x0601 -flto
|
||||
LDLIBS += -lws2_32 -lsetupapi -lole32 -ladvapi32 -lntdll -Lwincompat
|
||||
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
|
||||
|
||||
ifneq ($(V),1)
|
||||
|
@ -71,12 +82,13 @@ COMPILE.c = @echo " CC $@";
|
|||
COMPILE.c += $(BUILT_IN_COMPILE.c)
|
||||
BUILT_IN_RM := $(RM)
|
||||
RM := @a() { echo " CLEAN $$@"; $(BUILT_IN_RM) "$$@"; }; a
|
||||
WINDRES := @a() { echo " WINDRES $${@: -1}"; $(WINDRES) "$$@"; }; a
|
||||
endif
|
||||
|
||||
wg: $(sort $(patsubst %.c,%.o,$(wildcard *.c)))
|
||||
|
||||
clean:
|
||||
$(RM) wg *.o *.d
|
||||
$(RM) wg *.o *.d $(wildcard wincompat/*.o wincompat/*.lib wincompat/*.dll)
|
||||
|
||||
install: wg
|
||||
@install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 wg "$(DESTDIR)$(BINDIR)/wg"
|
||||
|
@ -90,7 +102,7 @@ install: wg
|
|||
@[ "$(WITH_WGQUICK)" = "yes" -a "$(WITH_BASHCOMPLETION)" = "yes" ] || exit 0; \
|
||||
install -v -m 0644 completion/wg-quick.bash-completion "$(DESTDIR)$(BASHCOMPDIR)/wg-quick"
|
||||
@[ "$(WITH_WGQUICK)" = "yes" -a "$(WITH_SYSTEMDUNITS)" = "yes" ] || exit 0; \
|
||||
install -v -d "$(DESTDIR)$(SYSTEMDUNITDIR)" && install -v -m 0644 systemd/wg-quick@.service "$(DESTDIR)$(SYSTEMDUNITDIR)/wg-quick@.service"
|
||||
install -v -d "$(DESTDIR)$(SYSTEMDUNITDIR)" && install -v -m 0644 systemd/* "$(DESTDIR)$(SYSTEMDUNITDIR)/"
|
||||
|
||||
check: clean
|
||||
scan-build --html-title=wireguard-tools -maxloop 100 --view --keep-going $(MAKE) wg
|
||||
|
|
|
@ -5,12 +5,12 @@ _wg_completion() {
|
|||
local a
|
||||
|
||||
if [[ $COMP_CWORD -eq 1 ]]; then
|
||||
COMPREPLY+=( $(compgen -W "show showconf set setconf addconf genkey genpsk pubkey" -- "${COMP_WORDS[1]}") )
|
||||
COMPREPLY+=( $(compgen -W "help show showconf set setconf addconf syncconf genkey genpsk pubkey" -- "${COMP_WORDS[1]}") )
|
||||
return
|
||||
fi
|
||||
case "${COMP_WORDS[1]}" in
|
||||
genkey|genpsk|pubkey|help) return; ;;
|
||||
show|showconf|set|setconf|addconf) ;;
|
||||
show|showconf|set|setconf|addconf|syncconf) ;;
|
||||
*) return;
|
||||
esac
|
||||
|
||||
|
@ -26,7 +26,7 @@ _wg_completion() {
|
|||
return
|
||||
fi
|
||||
|
||||
if [[ $COMP_CWORD -eq 3 && ( ${COMP_WORDS[1]} == setconf || ${COMP_WORDS[1]} == addconf ) ]]; then
|
||||
if [[ $COMP_CWORD -eq 3 && ( ${COMP_WORDS[1]} == setconf || ${COMP_WORDS[1]} == addconf || ${COMP_WORDS[1]} == syncconf) ]]; then
|
||||
compopt -o filenames
|
||||
mapfile -t a < <(compgen -f -- "${COMP_WORDS[3]}")
|
||||
COMPREPLY+=( "${a[@]}" )
|
||||
|
|
18
src/config.c
18
src/config.c
|
@ -1,11 +1,10 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -19,6 +18,7 @@
|
|||
#include "containers.h"
|
||||
#include "ipc.h"
|
||||
#include "encoding.h"
|
||||
#include "ctype.h"
|
||||
|
||||
#define COMMENT_CHAR '#'
|
||||
|
||||
|
@ -86,7 +86,7 @@ static inline bool parse_fwmark(uint32_t *fwmark, uint32_t *flags, const char *v
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!isdigit(value[0]))
|
||||
if (!char_is_digit(value[0]))
|
||||
goto err;
|
||||
|
||||
if (strlen(value) > 2 && value[0] == '0' && value[1] == 'x')
|
||||
|
@ -141,7 +141,7 @@ static bool parse_keyfile(uint8_t key[static WG_KEY_LEN], const char *path)
|
|||
dst[WG_KEY_LEN_BASE64 - 1] = '\0';
|
||||
|
||||
while ((c = getc(f)) != EOF) {
|
||||
if (!isspace(c)) {
|
||||
if (!char_is_space(c)) {
|
||||
fprintf(stderr, "Found trailing character in key file: `%c'\n", c);
|
||||
goto out;
|
||||
}
|
||||
|
@ -290,7 +290,7 @@ static inline bool parse_persistent_keepalive(uint16_t *interval, uint32_t *flag
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!isdigit(value[0]))
|
||||
if (!char_is_digit(value[0]))
|
||||
goto err;
|
||||
|
||||
ret = strtoul(value, &end, 10);
|
||||
|
@ -375,7 +375,7 @@ static inline bool parse_allowedips(struct wgpeer *peer, struct wgallowedip **la
|
|||
}
|
||||
|
||||
if (mask) {
|
||||
if (!isdigit(mask[0]))
|
||||
if (!char_is_digit(mask[0]))
|
||||
goto err;
|
||||
cidr = strtoul(mask, &end, 10);
|
||||
if (*end || (cidr > 32 && new_allowedip->family == AF_INET) || (cidr > 128 && new_allowedip->family == AF_INET6))
|
||||
|
@ -501,7 +501,7 @@ bool config_read_line(struct config_ctx *ctx, const char *input)
|
|||
}
|
||||
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
if (!isspace(input[i]))
|
||||
if (!char_is_space(input[i]))
|
||||
line[cleaned_len++] = input[i];
|
||||
}
|
||||
if (!cleaned_len)
|
||||
|
@ -555,13 +555,13 @@ static char *strip_spaces(const char *in)
|
|||
return NULL;
|
||||
}
|
||||
for (i = 0, l = 0; i < t; ++i) {
|
||||
if (!isspace(in[i]))
|
||||
if (!char_is_space(in[i]))
|
||||
out[l++] = in[i];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
struct wgdevice *config_read_cmd(char *argv[], int argc)
|
||||
struct wgdevice *config_read_cmd(const char *argv[], int argc)
|
||||
{
|
||||
struct wgdevice *device = calloc(1, sizeof(*device));
|
||||
struct wgpeer *peer = NULL;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
|
||||
/*
|
||||
* 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;
|
||||
};
|
||||
|
||||
struct wgdevice *config_read_cmd(char *argv[], int argc);
|
||||
struct wgdevice *config_read_cmd(const char *argv[], int argc);
|
||||
bool config_read_init(struct config_ctx *ctx, bool append);
|
||||
bool config_read_line(struct config_ctx *ctx, const char *line);
|
||||
struct wgdevice *config_read_finish(struct config_ctx *ctx);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*
|
||||
* Specialized constant-time ctype.h reimplementations that aren't locale-specific.
|
||||
*/
|
||||
|
||||
#ifndef CTYPE_H
|
||||
#define CTYPE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
static inline bool char_is_space(int c)
|
||||
{
|
||||
unsigned char d = c - 9;
|
||||
return (0x80001FU >> (d & 31)) & (1U >> (d >> 5));
|
||||
}
|
||||
|
||||
static inline bool char_is_digit(int c)
|
||||
{
|
||||
return (unsigned int)(('0' - 1 - c) & (c - ('9' + 1))) >> (sizeof(c) * 8 - 1);
|
||||
}
|
||||
|
||||
static inline bool char_is_alpha(int c)
|
||||
{
|
||||
return (unsigned int)(('a' - 1 - (c | 32)) & ((c | 32) - ('z' + 1))) >> (sizeof(c) * 8 - 1);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
/*
|
||||
* Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
|
12
src/genkey.c
12
src/genkey.c
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
@ -28,7 +28,7 @@
|
|||
#include "encoding.h"
|
||||
#include "subcommands.h"
|
||||
|
||||
#ifndef WINCOMPAT
|
||||
#ifndef _WIN32
|
||||
static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
|
@ -65,10 +65,14 @@ static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint
|
|||
return i == len;
|
||||
}
|
||||
#else
|
||||
#include "wincompat/getrandom.c"
|
||||
#include <ntsecapi.h>
|
||||
static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len)
|
||||
{
|
||||
return RtlGenRandom(out, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
int genkey_main(int argc, char *argv[])
|
||||
int genkey_main(int argc, const char *argv[])
|
||||
{
|
||||
uint8_t key[WG_KEY_LEN];
|
||||
char base64[WG_KEY_LEN_BASE64];
|
||||
|
|
|
@ -0,0 +1,360 @@
|
|||
// 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;
|
||||
}
|
|
@ -0,0 +1,525 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <linux/genetlink.h>
|
||||
#include <linux/if_link.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/wireguard.h>
|
||||
#include <netinet/in.h>
|
||||
#include "containers.h"
|
||||
#include "encoding.h"
|
||||
#include "netlink.h"
|
||||
|
||||
#define IPC_SUPPORTS_KERNEL_INTERFACE
|
||||
|
||||
#define SOCKET_BUFFER_SIZE (mnl_ideal_socket_buffer_size())
|
||||
|
||||
struct interface {
|
||||
const char *name;
|
||||
bool is_wireguard;
|
||||
};
|
||||
|
||||
static int parse_linkinfo(const struct nlattr *attr, void *data)
|
||||
{
|
||||
struct interface *interface = data;
|
||||
|
||||
if (mnl_attr_get_type(attr) == IFLA_INFO_KIND && !strcmp(WG_GENL_NAME, mnl_attr_get_str(attr)))
|
||||
interface->is_wireguard = true;
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int parse_infomsg(const struct nlattr *attr, void *data)
|
||||
{
|
||||
struct interface *interface = data;
|
||||
|
||||
if (mnl_attr_get_type(attr) == IFLA_LINKINFO)
|
||||
return mnl_attr_parse_nested(attr, parse_linkinfo, data);
|
||||
else if (mnl_attr_get_type(attr) == IFLA_IFNAME)
|
||||
interface->name = mnl_attr_get_str(attr);
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int read_devices_cb(const struct nlmsghdr *nlh, void *data)
|
||||
{
|
||||
struct string_list *list = data;
|
||||
struct interface interface = { 0 };
|
||||
int ret;
|
||||
|
||||
ret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, &interface);
|
||||
if (ret != MNL_CB_OK)
|
||||
return ret;
|
||||
if (interface.name && interface.is_wireguard)
|
||||
ret = string_list_add(list, interface.name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (nlh->nlmsg_type != NLMSG_DONE)
|
||||
return MNL_CB_OK + 1;
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int kernel_get_wireguard_interfaces(struct string_list *list)
|
||||
{
|
||||
struct mnl_socket *nl = NULL;
|
||||
char *rtnl_buffer = NULL;
|
||||
size_t message_len;
|
||||
unsigned int portid, seq;
|
||||
ssize_t len;
|
||||
int ret = 0;
|
||||
struct nlmsghdr *nlh;
|
||||
struct ifinfomsg *ifm;
|
||||
|
||||
ret = -ENOMEM;
|
||||
rtnl_buffer = calloc(SOCKET_BUFFER_SIZE, 1);
|
||||
if (!rtnl_buffer)
|
||||
goto cleanup;
|
||||
|
||||
nl = mnl_socket_open(NETLINK_ROUTE);
|
||||
if (!nl) {
|
||||
ret = -errno;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
|
||||
ret = -errno;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
seq = time(NULL);
|
||||
portid = mnl_socket_get_portid(nl);
|
||||
nlh = mnl_nlmsg_put_header(rtnl_buffer);
|
||||
nlh->nlmsg_type = RTM_GETLINK;
|
||||
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
|
||||
nlh->nlmsg_seq = seq;
|
||||
ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
|
||||
ifm->ifi_family = AF_UNSPEC;
|
||||
message_len = nlh->nlmsg_len;
|
||||
|
||||
if (mnl_socket_sendto(nl, rtnl_buffer, message_len) < 0) {
|
||||
ret = -errno;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
another:
|
||||
if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, SOCKET_BUFFER_SIZE)) < 0) {
|
||||
ret = -errno;
|
||||
goto cleanup;
|
||||
}
|
||||
if ((len = mnl_cb_run(rtnl_buffer, len, seq, portid, read_devices_cb, list)) < 0) {
|
||||
/* Netlink returns NLM_F_DUMP_INTR if the set of all tunnels changed
|
||||
* during the dump. That's unfortunate, but is pretty common on busy
|
||||
* systems that are adding and removing tunnels all the time. Rather
|
||||
* than retrying, potentially indefinitely, we just work with the
|
||||
* partial results. */
|
||||
if (errno != EINTR) {
|
||||
ret = -errno;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
if (len == MNL_CB_OK + 1)
|
||||
goto another;
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
free(rtnl_buffer);
|
||||
if (nl)
|
||||
mnl_socket_close(nl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kernel_set_device(struct wgdevice *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct wgpeer *peer = NULL;
|
||||
struct wgallowedip *allowedip = NULL;
|
||||
struct nlattr *peers_nest, *peer_nest, *allowedips_nest, *allowedip_nest;
|
||||
struct nlmsghdr *nlh;
|
||||
struct mnlg_socket *nlg;
|
||||
|
||||
nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);
|
||||
if (!nlg)
|
||||
return -errno;
|
||||
|
||||
again:
|
||||
nlh = mnlg_msg_prepare(nlg, WG_CMD_SET_DEVICE, NLM_F_REQUEST | NLM_F_ACK);
|
||||
mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, dev->name);
|
||||
|
||||
if (!peer) {
|
||||
uint32_t flags = 0;
|
||||
|
||||
if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)
|
||||
mnl_attr_put(nlh, WGDEVICE_A_PRIVATE_KEY, sizeof(dev->private_key), dev->private_key);
|
||||
if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
|
||||
mnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, dev->listen_port);
|
||||
if (dev->flags & WGDEVICE_HAS_FWMARK)
|
||||
mnl_attr_put_u32(nlh, WGDEVICE_A_FWMARK, dev->fwmark);
|
||||
if (dev->flags & WGDEVICE_REPLACE_PEERS)
|
||||
flags |= WGDEVICE_F_REPLACE_PEERS;
|
||||
if (flags)
|
||||
mnl_attr_put_u32(nlh, WGDEVICE_A_FLAGS, flags);
|
||||
}
|
||||
if (!dev->first_peer)
|
||||
goto send;
|
||||
peers_nest = peer_nest = allowedips_nest = allowedip_nest = NULL;
|
||||
peers_nest = mnl_attr_nest_start(nlh, WGDEVICE_A_PEERS);
|
||||
for (peer = peer ? peer : dev->first_peer; peer; peer = peer->next_peer) {
|
||||
uint32_t flags = 0;
|
||||
|
||||
peer_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, 0);
|
||||
if (!peer_nest)
|
||||
goto toobig_peers;
|
||||
if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_PUBLIC_KEY, sizeof(peer->public_key), peer->public_key))
|
||||
goto toobig_peers;
|
||||
if (peer->flags & WGPEER_REMOVE_ME)
|
||||
flags |= WGPEER_F_REMOVE_ME;
|
||||
if (!allowedip) {
|
||||
if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
|
||||
flags |= WGPEER_F_REPLACE_ALLOWEDIPS;
|
||||
if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
|
||||
if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_PRESHARED_KEY, sizeof(peer->preshared_key), peer->preshared_key))
|
||||
goto toobig_peers;
|
||||
}
|
||||
if (peer->endpoint.addr.sa_family == AF_INET) {
|
||||
if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr4), &peer->endpoint.addr4))
|
||||
goto toobig_peers;
|
||||
} else if (peer->endpoint.addr.sa_family == AF_INET6) {
|
||||
if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr6), &peer->endpoint.addr6))
|
||||
goto toobig_peers;
|
||||
}
|
||||
if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {
|
||||
if (!mnl_attr_put_u16_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval))
|
||||
goto toobig_peers;
|
||||
}
|
||||
}
|
||||
if (flags) {
|
||||
if (!mnl_attr_put_u32_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_FLAGS, flags))
|
||||
goto toobig_peers;
|
||||
}
|
||||
if (peer->first_allowedip) {
|
||||
if (!allowedip)
|
||||
allowedip = peer->first_allowedip;
|
||||
allowedips_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ALLOWEDIPS);
|
||||
if (!allowedips_nest)
|
||||
goto toobig_allowedips;
|
||||
for (; allowedip; allowedip = allowedip->next_allowedip) {
|
||||
allowedip_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, 0);
|
||||
if (!allowedip_nest)
|
||||
goto toobig_allowedips;
|
||||
if (!mnl_attr_put_u16_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_FAMILY, allowedip->family))
|
||||
goto toobig_allowedips;
|
||||
if (allowedip->family == AF_INET) {
|
||||
if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip4), &allowedip->ip4))
|
||||
goto toobig_allowedips;
|
||||
} else if (allowedip->family == AF_INET6) {
|
||||
if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip6), &allowedip->ip6))
|
||||
goto toobig_allowedips;
|
||||
}
|
||||
if (!mnl_attr_put_u8_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_CIDR_MASK, allowedip->cidr))
|
||||
goto toobig_allowedips;
|
||||
mnl_attr_nest_end(nlh, allowedip_nest);
|
||||
allowedip_nest = NULL;
|
||||
}
|
||||
mnl_attr_nest_end(nlh, allowedips_nest);
|
||||
allowedips_nest = NULL;
|
||||
}
|
||||
|
||||
mnl_attr_nest_end(nlh, peer_nest);
|
||||
peer_nest = NULL;
|
||||
}
|
||||
mnl_attr_nest_end(nlh, peers_nest);
|
||||
peers_nest = NULL;
|
||||
goto send;
|
||||
toobig_allowedips:
|
||||
if (allowedip_nest)
|
||||
mnl_attr_nest_cancel(nlh, allowedip_nest);
|
||||
if (allowedips_nest)
|
||||
mnl_attr_nest_end(nlh, allowedips_nest);
|
||||
mnl_attr_nest_end(nlh, peer_nest);
|
||||
mnl_attr_nest_end(nlh, peers_nest);
|
||||
goto send;
|
||||
toobig_peers:
|
||||
if (peer_nest)
|
||||
mnl_attr_nest_cancel(nlh, peer_nest);
|
||||
mnl_attr_nest_end(nlh, peers_nest);
|
||||
goto send;
|
||||
send:
|
||||
if (mnlg_socket_send(nlg, nlh) < 0) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
errno = 0;
|
||||
if (mnlg_socket_recv_run(nlg, NULL, NULL) < 0) {
|
||||
ret = errno ? -errno : -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (peer)
|
||||
goto again;
|
||||
|
||||
out:
|
||||
mnlg_socket_close(nlg);
|
||||
errno = -ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int parse_allowedip(const struct nlattr *attr, void *data)
|
||||
{
|
||||
struct wgallowedip *allowedip = data;
|
||||
|
||||
switch (mnl_attr_get_type(attr)) {
|
||||
case WGALLOWEDIP_A_UNSPEC:
|
||||
break;
|
||||
case WGALLOWEDIP_A_FAMILY:
|
||||
if (!mnl_attr_validate(attr, MNL_TYPE_U16))
|
||||
allowedip->family = mnl_attr_get_u16(attr);
|
||||
break;
|
||||
case WGALLOWEDIP_A_IPADDR:
|
||||
if (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip4))
|
||||
memcpy(&allowedip->ip4, mnl_attr_get_payload(attr), sizeof(allowedip->ip4));
|
||||
else if (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip6))
|
||||
memcpy(&allowedip->ip6, mnl_attr_get_payload(attr), sizeof(allowedip->ip6));
|
||||
break;
|
||||
case WGALLOWEDIP_A_CIDR_MASK:
|
||||
if (!mnl_attr_validate(attr, MNL_TYPE_U8))
|
||||
allowedip->cidr = mnl_attr_get_u8(attr);
|
||||
break;
|
||||
}
|
||||
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int parse_allowedips(const struct nlattr *attr, void *data)
|
||||
{
|
||||
struct wgpeer *peer = data;
|
||||
struct wgallowedip *new_allowedip = calloc(1, sizeof(*new_allowedip));
|
||||
int ret;
|
||||
|
||||
if (!new_allowedip) {
|
||||
perror("calloc");
|
||||
return MNL_CB_ERROR;
|
||||
}
|
||||
if (!peer->first_allowedip)
|
||||
peer->first_allowedip = peer->last_allowedip = new_allowedip;
|
||||
else {
|
||||
peer->last_allowedip->next_allowedip = new_allowedip;
|
||||
peer->last_allowedip = new_allowedip;
|
||||
}
|
||||
ret = mnl_attr_parse_nested(attr, parse_allowedip, new_allowedip);
|
||||
if (!ret)
|
||||
return ret;
|
||||
if (!((new_allowedip->family == AF_INET && new_allowedip->cidr <= 32) || (new_allowedip->family == AF_INET6 && new_allowedip->cidr <= 128)))
|
||||
return MNL_CB_ERROR;
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int parse_peer(const struct nlattr *attr, void *data)
|
||||
{
|
||||
struct wgpeer *peer = data;
|
||||
|
||||
switch (mnl_attr_get_type(attr)) {
|
||||
case WGPEER_A_UNSPEC:
|
||||
break;
|
||||
case WGPEER_A_PUBLIC_KEY:
|
||||
if (mnl_attr_get_payload_len(attr) == sizeof(peer->public_key)) {
|
||||
memcpy(peer->public_key, mnl_attr_get_payload(attr), sizeof(peer->public_key));
|
||||
peer->flags |= WGPEER_HAS_PUBLIC_KEY;
|
||||
}
|
||||
break;
|
||||
case WGPEER_A_PRESHARED_KEY:
|
||||
if (mnl_attr_get_payload_len(attr) == sizeof(peer->preshared_key)) {
|
||||
memcpy(peer->preshared_key, mnl_attr_get_payload(attr), sizeof(peer->preshared_key));
|
||||
if (!key_is_zero(peer->preshared_key))
|
||||
peer->flags |= WGPEER_HAS_PRESHARED_KEY;
|
||||
}
|
||||
break;
|
||||
case WGPEER_A_ENDPOINT: {
|
||||
struct sockaddr *addr;
|
||||
|
||||
if (mnl_attr_get_payload_len(attr) < sizeof(*addr))
|
||||
break;
|
||||
addr = mnl_attr_get_payload(attr);
|
||||
if (addr->sa_family == AF_INET && mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr4))
|
||||
memcpy(&peer->endpoint.addr4, addr, sizeof(peer->endpoint.addr4));
|
||||
else if (addr->sa_family == AF_INET6 && mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr6))
|
||||
memcpy(&peer->endpoint.addr6, addr, sizeof(peer->endpoint.addr6));
|
||||
break;
|
||||
}
|
||||
case WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL:
|
||||
if (!mnl_attr_validate(attr, MNL_TYPE_U16))
|
||||
peer->persistent_keepalive_interval = mnl_attr_get_u16(attr);
|
||||
break;
|
||||
case WGPEER_A_LAST_HANDSHAKE_TIME:
|
||||
if (mnl_attr_get_payload_len(attr) == sizeof(peer->last_handshake_time))
|
||||
memcpy(&peer->last_handshake_time, mnl_attr_get_payload(attr), sizeof(peer->last_handshake_time));
|
||||
break;
|
||||
case WGPEER_A_RX_BYTES:
|
||||
if (!mnl_attr_validate(attr, MNL_TYPE_U64))
|
||||
peer->rx_bytes = mnl_attr_get_u64(attr);
|
||||
break;
|
||||
case WGPEER_A_TX_BYTES:
|
||||
if (!mnl_attr_validate(attr, MNL_TYPE_U64))
|
||||
peer->tx_bytes = mnl_attr_get_u64(attr);
|
||||
break;
|
||||
case WGPEER_A_ALLOWEDIPS:
|
||||
return mnl_attr_parse_nested(attr, parse_allowedips, peer);
|
||||
}
|
||||
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int parse_peers(const struct nlattr *attr, void *data)
|
||||
{
|
||||
struct wgdevice *device = data;
|
||||
struct wgpeer *new_peer = calloc(1, sizeof(*new_peer));
|
||||
int ret;
|
||||
|
||||
if (!new_peer) {
|
||||
perror("calloc");
|
||||
return MNL_CB_ERROR;
|
||||
}
|
||||
if (!device->first_peer)
|
||||
device->first_peer = device->last_peer = new_peer;
|
||||
else {
|
||||
device->last_peer->next_peer = new_peer;
|
||||
device->last_peer = new_peer;
|
||||
}
|
||||
ret = mnl_attr_parse_nested(attr, parse_peer, new_peer);
|
||||
if (!ret)
|
||||
return ret;
|
||||
if (!(new_peer->flags & WGPEER_HAS_PUBLIC_KEY))
|
||||
return MNL_CB_ERROR;
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int parse_device(const struct nlattr *attr, void *data)
|
||||
{
|
||||
struct wgdevice *device = data;
|
||||
|
||||
switch (mnl_attr_get_type(attr)) {
|
||||
case WGDEVICE_A_UNSPEC:
|
||||
break;
|
||||
case WGDEVICE_A_IFINDEX:
|
||||
if (!mnl_attr_validate(attr, MNL_TYPE_U32))
|
||||
device->ifindex = mnl_attr_get_u32(attr);
|
||||
break;
|
||||
case WGDEVICE_A_IFNAME:
|
||||
if (!mnl_attr_validate(attr, MNL_TYPE_STRING)) {
|
||||
strncpy(device->name, mnl_attr_get_str(attr), sizeof(device->name) - 1);
|
||||
device->name[sizeof(device->name) - 1] = '\0';
|
||||
}
|
||||
break;
|
||||
case WGDEVICE_A_PRIVATE_KEY:
|
||||
if (mnl_attr_get_payload_len(attr) == sizeof(device->private_key)) {
|
||||
memcpy(device->private_key, mnl_attr_get_payload(attr), sizeof(device->private_key));
|
||||
device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
|
||||
}
|
||||
break;
|
||||
case WGDEVICE_A_PUBLIC_KEY:
|
||||
if (mnl_attr_get_payload_len(attr) == sizeof(device->public_key)) {
|
||||
memcpy(device->public_key, mnl_attr_get_payload(attr), sizeof(device->public_key));
|
||||
device->flags |= WGDEVICE_HAS_PUBLIC_KEY;
|
||||
}
|
||||
break;
|
||||
case WGDEVICE_A_LISTEN_PORT:
|
||||
if (!mnl_attr_validate(attr, MNL_TYPE_U16))
|
||||
device->listen_port = mnl_attr_get_u16(attr);
|
||||
break;
|
||||
case WGDEVICE_A_FWMARK:
|
||||
if (!mnl_attr_validate(attr, MNL_TYPE_U32))
|
||||
device->fwmark = mnl_attr_get_u32(attr);
|
||||
break;
|
||||
case WGDEVICE_A_PEERS:
|
||||
return mnl_attr_parse_nested(attr, parse_peers, device);
|
||||
}
|
||||
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int read_device_cb(const struct nlmsghdr *nlh, void *data)
|
||||
{
|
||||
return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), parse_device, data);
|
||||
}
|
||||
|
||||
static void coalesce_peers(struct wgdevice *device)
|
||||
{
|
||||
struct wgpeer *old_next_peer, *peer = device->first_peer;
|
||||
|
||||
while (peer && peer->next_peer) {
|
||||
if (memcmp(peer->public_key, peer->next_peer->public_key, sizeof(peer->public_key))) {
|
||||
peer = peer->next_peer;
|
||||
continue;
|
||||
}
|
||||
if (!peer->first_allowedip) {
|
||||
peer->first_allowedip = peer->next_peer->first_allowedip;
|
||||
peer->last_allowedip = peer->next_peer->last_allowedip;
|
||||
} else {
|
||||
peer->last_allowedip->next_allowedip = peer->next_peer->first_allowedip;
|
||||
peer->last_allowedip = peer->next_peer->last_allowedip;
|
||||
}
|
||||
old_next_peer = peer->next_peer;
|
||||
peer->next_peer = old_next_peer->next_peer;
|
||||
free(old_next_peer);
|
||||
}
|
||||
}
|
||||
|
||||
static int kernel_get_device(struct wgdevice **device, const char *iface)
|
||||
{
|
||||
int ret;
|
||||
struct nlmsghdr *nlh;
|
||||
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:
|
||||
ret = 0;
|
||||
*device = calloc(1, sizeof(**device));
|
||||
if (!*device)
|
||||
return -errno;
|
||||
|
||||
nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);
|
||||
if (!nlg) {
|
||||
free_wgdevice(*device);
|
||||
*device = NULL;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
nlh = mnlg_msg_prepare(nlg, WG_CMD_GET_DEVICE, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
|
||||
mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, iface);
|
||||
if (mnlg_socket_send(nlg, nlh) < 0) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
errno = 0;
|
||||
if (mnlg_socket_recv_run(nlg, read_device_cb, *device) < 0) {
|
||||
ret = errno ? -errno : -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
coalesce_peers(*device);
|
||||
|
||||
out:
|
||||
if (nlg)
|
||||
mnlg_socket_close(nlg);
|
||||
if (ret) {
|
||||
free_wgdevice(*device);
|
||||
if (ret == -EINTR)
|
||||
goto try_again;
|
||||
*device = NULL;
|
||||
}
|
||||
errno = -ret;
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,281 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/types.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_wg.h>
|
||||
#include <netinet/in.h>
|
||||
#include "containers.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 *iface)
|
||||
{
|
||||
struct wg_data_io wgdata = { .wgd_size = 0 };
|
||||
struct wg_interface_io *wg_iface;
|
||||
struct wg_peer_io *wg_peer;
|
||||
struct wg_aip_io *wg_aip;
|
||||
struct wgdevice *dev;
|
||||
struct wgpeer *peer;
|
||||
struct wgallowedip *aip;
|
||||
int s = get_dgram_socket(), ret;
|
||||
|
||||
if (s < 0)
|
||||
return -errno;
|
||||
|
||||
*device = NULL;
|
||||
strlcpy(wgdata.wgd_name, iface, sizeof(wgdata.wgd_name));
|
||||
for (size_t last_size = wgdata.wgd_size;; last_size = wgdata.wgd_size) {
|
||||
if (ioctl(s, SIOCGWG, (caddr_t)&wgdata) < 0)
|
||||
goto out;
|
||||
if (last_size >= wgdata.wgd_size)
|
||||
break;
|
||||
wgdata.wgd_interface = realloc(wgdata.wgd_interface, wgdata.wgd_size);
|
||||
if (!wgdata.wgd_interface)
|
||||
goto out;
|
||||
}
|
||||
|
||||
wg_iface = wgdata.wgd_interface;
|
||||
dev = calloc(1, sizeof(*dev));
|
||||
if (!dev)
|
||||
goto out;
|
||||
strlcpy(dev->name, iface, sizeof(dev->name));
|
||||
|
||||
if (wg_iface->i_flags & WG_INTERFACE_HAS_RTABLE) {
|
||||
dev->fwmark = wg_iface->i_rtable;
|
||||
dev->flags |= WGDEVICE_HAS_FWMARK;
|
||||
}
|
||||
|
||||
if (wg_iface->i_flags & WG_INTERFACE_HAS_PORT) {
|
||||
dev->listen_port = wg_iface->i_port;
|
||||
dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
|
||||
}
|
||||
|
||||
if (wg_iface->i_flags & WG_INTERFACE_HAS_PUBLIC) {
|
||||
memcpy(dev->public_key, wg_iface->i_public, sizeof(dev->public_key));
|
||||
dev->flags |= WGDEVICE_HAS_PUBLIC_KEY;
|
||||
}
|
||||
|
||||
if (wg_iface->i_flags & WG_INTERFACE_HAS_PRIVATE) {
|
||||
memcpy(dev->private_key, wg_iface->i_private, sizeof(dev->private_key));
|
||||
dev->flags |= WGDEVICE_HAS_PRIVATE_KEY;
|
||||
}
|
||||
|
||||
wg_peer = &wg_iface->i_peers[0];
|
||||
for (size_t i = 0; i < wg_iface->i_peers_count; ++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->p_flags & WG_PEER_HAS_PUBLIC) {
|
||||
memcpy(peer->public_key, wg_peer->p_public, sizeof(peer->public_key));
|
||||
peer->flags |= WGPEER_HAS_PUBLIC_KEY;
|
||||
}
|
||||
|
||||
if (wg_peer->p_flags & WG_PEER_HAS_PSK) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (wg_peer->p_flags & WG_PEER_HAS_PKA) {
|
||||
peer->persistent_keepalive_interval = wg_peer->p_pka;
|
||||
peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
|
||||
}
|
||||
|
||||
if (wg_peer->p_flags & WG_PEER_HAS_ENDPOINT && wg_peer->p_sa.sa_len <= sizeof(peer->endpoint.addr))
|
||||
memcpy(&peer->endpoint.addr, &wg_peer->p_sa, wg_peer->p_sa.sa_len);
|
||||
|
||||
peer->rx_bytes = wg_peer->p_rxbytes;
|
||||
peer->tx_bytes = wg_peer->p_txbytes;
|
||||
|
||||
peer->last_handshake_time.tv_sec = wg_peer->p_last_handshake.tv_sec;
|
||||
peer->last_handshake_time.tv_nsec = wg_peer->p_last_handshake.tv_nsec;
|
||||
|
||||
wg_aip = &wg_peer->p_aips[0];
|
||||
for (size_t j = 0; j < wg_peer->p_aips_count; ++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->a_af;
|
||||
if (wg_aip->a_af == AF_INET) {
|
||||
memcpy(&aip->ip4, &wg_aip->a_ipv4, sizeof(aip->ip4));
|
||||
aip->cidr = wg_aip->a_cidr;
|
||||
} else if (wg_aip->a_af == AF_INET6) {
|
||||
memcpy(&aip->ip6, &wg_aip->a_ipv6, sizeof(aip->ip6));
|
||||
aip->cidr = wg_aip->a_cidr;
|
||||
}
|
||||
++wg_aip;
|
||||
}
|
||||
wg_peer = (struct wg_peer_io *)wg_aip;
|
||||
}
|
||||
*device = dev;
|
||||
errno = 0;
|
||||
out:
|
||||
ret = -errno;
|
||||
free(wgdata.wgd_interface);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kernel_set_device(struct wgdevice *dev)
|
||||
{
|
||||
struct wg_data_io wgdata = { .wgd_size = sizeof(struct wg_interface_io) };
|
||||
struct wg_interface_io *wg_iface;
|
||||
struct wg_peer_io *wg_peer;
|
||||
struct wg_aip_io *wg_aip;
|
||||
struct wgpeer *peer;
|
||||
struct wgallowedip *aip;
|
||||
int s = get_dgram_socket(), ret;
|
||||
size_t peer_count, aip_count;
|
||||
|
||||
if (s < 0)
|
||||
return -errno;
|
||||
|
||||
for_each_wgpeer(dev, peer) {
|
||||
wgdata.wgd_size += sizeof(struct wg_peer_io);
|
||||
for_each_wgallowedip(peer, aip)
|
||||
wgdata.wgd_size += sizeof(struct wg_aip_io);
|
||||
}
|
||||
wg_iface = wgdata.wgd_interface = calloc(1, wgdata.wgd_size);
|
||||
if (!wgdata.wgd_interface)
|
||||
return -errno;
|
||||
strlcpy(wgdata.wgd_name, dev->name, sizeof(wgdata.wgd_name));
|
||||
|
||||
if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) {
|
||||
memcpy(wg_iface->i_private, dev->private_key, sizeof(wg_iface->i_private));
|
||||
wg_iface->i_flags |= WG_INTERFACE_HAS_PRIVATE;
|
||||
}
|
||||
|
||||
if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) {
|
||||
wg_iface->i_port = dev->listen_port;
|
||||
wg_iface->i_flags |= WG_INTERFACE_HAS_PORT;
|
||||
}
|
||||
|
||||
if (dev->flags & WGDEVICE_HAS_FWMARK) {
|
||||
wg_iface->i_rtable = dev->fwmark;
|
||||
wg_iface->i_flags |= WG_INTERFACE_HAS_RTABLE;
|
||||
}
|
||||
|
||||
if (dev->flags & WGDEVICE_REPLACE_PEERS)
|
||||
wg_iface->i_flags |= WG_INTERFACE_REPLACE_PEERS;
|
||||
|
||||
peer_count = 0;
|
||||
wg_peer = &wg_iface->i_peers[0];
|
||||
for_each_wgpeer(dev, peer) {
|
||||
wg_peer->p_flags = WG_PEER_HAS_PUBLIC;
|
||||
memcpy(wg_peer->p_public, peer->public_key, sizeof(wg_peer->p_public));
|
||||
|
||||
if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
|
||||
memcpy(wg_peer->p_psk, peer->preshared_key, sizeof(wg_peer->p_psk));
|
||||
wg_peer->p_flags |= WG_PEER_HAS_PSK;
|
||||
}
|
||||
|
||||
if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {
|
||||
wg_peer->p_pka = peer->persistent_keepalive_interval;
|
||||
wg_peer->p_flags |= WG_PEER_HAS_PKA;
|
||||
}
|
||||
|
||||
if ((peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) &&
|
||||
peer->endpoint.addr.sa_len <= sizeof(wg_peer->p_endpoint)) {
|
||||
memcpy(&wg_peer->p_endpoint, &peer->endpoint.addr, peer->endpoint.addr.sa_len);
|
||||
wg_peer->p_flags |= WG_PEER_HAS_ENDPOINT;
|
||||
}
|
||||
|
||||
if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
|
||||
wg_peer->p_flags |= WG_PEER_REPLACE_AIPS;
|
||||
|
||||
if (peer->flags & WGPEER_REMOVE_ME)
|
||||
wg_peer->p_flags |= WG_PEER_REMOVE;
|
||||
|
||||
aip_count = 0;
|
||||
wg_aip = &wg_peer->p_aips[0];
|
||||
for_each_wgallowedip(peer, aip) {
|
||||
wg_aip->a_af = aip->family;
|
||||
wg_aip->a_cidr = aip->cidr;
|
||||
|
||||
if (aip->family == AF_INET)
|
||||
memcpy(&wg_aip->a_ipv4, &aip->ip4, sizeof(wg_aip->a_ipv4));
|
||||
else if (aip->family == AF_INET6)
|
||||
memcpy(&wg_aip->a_ipv6, &aip->ip6, sizeof(wg_aip->a_ipv6));
|
||||
else
|
||||
continue;
|
||||
++aip_count;
|
||||
++wg_aip;
|
||||
}
|
||||
wg_peer->p_aips_count = aip_count;
|
||||
++peer_count;
|
||||
wg_peer = (struct wg_peer_io *)wg_aip;
|
||||
}
|
||||
wg_iface->i_peers_count = peer_count;
|
||||
|
||||
if (ioctl(s, SIOCSWG, (caddr_t)&wgdata) < 0)
|
||||
goto out;
|
||||
errno = 0;
|
||||
|
||||
out:
|
||||
ret = -errno;
|
||||
free(wgdata.wgd_interface);
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#define SOCK_PATH RUNSTATEDIR "/wireguard/"
|
||||
#define SOCK_SUFFIX ".sock"
|
||||
|
||||
static FILE *userspace_interface_file(const char *iface)
|
||||
{
|
||||
struct stat sbuf;
|
||||
struct sockaddr_un addr = { .sun_family = AF_UNIX };
|
||||
int fd = -1, ret;
|
||||
FILE *f = NULL;
|
||||
|
||||
errno = EINVAL;
|
||||
if (strchr(iface, '/'))
|
||||
goto out;
|
||||
ret = snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, iface);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
ret = stat(addr.sun_path, &sbuf);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
errno = EBADF;
|
||||
if (!S_ISSOCK(sbuf.st_mode))
|
||||
goto out;
|
||||
|
||||
ret = fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
|
||||
if (ret < 0) {
|
||||
if (errno == ECONNREFUSED) /* If the process is gone, we try to clean up the socket. */
|
||||
unlink(addr.sun_path);
|
||||
goto out;
|
||||
}
|
||||
f = fdopen(fd, "r+");
|
||||
if (f)
|
||||
errno = 0;
|
||||
out:
|
||||
ret = -errno;
|
||||
if (ret) {
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
errno = -ret;
|
||||
return NULL;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
static bool userspace_has_wireguard_interface(const char *iface)
|
||||
{
|
||||
struct stat sbuf;
|
||||
struct sockaddr_un addr = { .sun_family = AF_UNIX };
|
||||
int fd, ret;
|
||||
|
||||
if (strchr(iface, '/'))
|
||||
return false;
|
||||
if (snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, iface) < 0)
|
||||
return false;
|
||||
if (stat(addr.sun_path, &sbuf) < 0)
|
||||
return false;
|
||||
if (!S_ISSOCK(sbuf.st_mode))
|
||||
return false;
|
||||
ret = fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
|
||||
if (ret < 0 && errno == ECONNREFUSED) { /* If the process is gone, we try to clean up the socket. */
|
||||
close(fd);
|
||||
unlink(addr.sun_path);
|
||||
return false;
|
||||
}
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int userspace_get_wireguard_interfaces(struct string_list *list)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
size_t len;
|
||||
char *end;
|
||||
int ret = 0;
|
||||
|
||||
dir = opendir(SOCK_PATH);
|
||||
if (!dir)
|
||||
return errno == ENOENT ? 0 : -errno;
|
||||
while ((ent = readdir(dir))) {
|
||||
len = strlen(ent->d_name);
|
||||
if (len <= strlen(SOCK_SUFFIX))
|
||||
continue;
|
||||
end = &ent->d_name[len - strlen(SOCK_SUFFIX)];
|
||||
if (strncmp(end, SOCK_SUFFIX, strlen(SOCK_SUFFIX)))
|
||||
continue;
|
||||
*end = '\0';
|
||||
if (!userspace_has_wireguard_interface(ent->d_name))
|
||||
continue;
|
||||
ret = string_list_add(list, ent->d_name);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
closedir(dir);
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <accctrl.h>
|
||||
#include <aclapi.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <fcntl.h>
|
||||
#include <hashtable.h>
|
||||
|
||||
static FILE *userspace_interface_file(const char *iface)
|
||||
{
|
||||
char fname[MAX_PATH];
|
||||
HANDLE pipe_handle;
|
||||
SID expected_sid;
|
||||
DWORD bytes = sizeof(expected_sid);
|
||||
PSID pipe_sid;
|
||||
PSECURITY_DESCRIPTOR pipe_sd;
|
||||
bool equal;
|
||||
int fd;
|
||||
|
||||
if (!CreateWellKnownSid(WinLocalSystemSid, NULL, &expected_sid, &bytes))
|
||||
goto err;
|
||||
|
||||
snprintf(fname, sizeof(fname), "\\\\.\\pipe\\ProtectedPrefix\\Administrators\\WireGuard\\%s", iface);
|
||||
pipe_handle = CreateFileA(fname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
if (pipe_handle == INVALID_HANDLE_VALUE)
|
||||
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);
|
||||
if (fd == -1) {
|
||||
CloseHandle(pipe_handle);
|
||||
return NULL;
|
||||
}
|
||||
return _fdopen(fd, "r+");
|
||||
err_close:
|
||||
CloseHandle(pipe_handle);
|
||||
err:
|
||||
errno = EACCES;
|
||||
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 const char prefix[] = "ProtectedPrefix\\Administrators\\WireGuard\\";
|
||||
WIN32_FIND_DATA find_data;
|
||||
HANDLE find_handle;
|
||||
char *iface;
|
||||
int ret = 0;
|
||||
|
||||
find_handle = FindFirstFile("\\\\.\\pipe\\*", &find_data);
|
||||
if (find_handle == INVALID_HANDLE_VALUE)
|
||||
return -EIO;
|
||||
do {
|
||||
if (strncmp(prefix, find_data.cFileName, strlen(prefix)))
|
||||
continue;
|
||||
iface = find_data.cFileName + strlen(prefix);
|
||||
ret = string_list_add(list, iface);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (!hashtable_find_or_insert_entry(&cached_interfaces, iface)) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
} while (FindNextFile(find_handle, &find_data));
|
||||
have_cached_interfaces = true;
|
||||
|
||||
out:
|
||||
FindClose(find_handle);
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,297 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <net/if.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include "containers.h"
|
||||
#include "curve25519.h"
|
||||
#include "encoding.h"
|
||||
#include "ctype.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "ipc-uapi-windows.h"
|
||||
#else
|
||||
#include "ipc-uapi-unix.h"
|
||||
#endif
|
||||
|
||||
static int userspace_set_device(struct wgdevice *dev)
|
||||
{
|
||||
char hex[WG_KEY_LEN_HEX], ip[INET6_ADDRSTRLEN], host[4096 + 1], service[512 + 1];
|
||||
struct wgpeer *peer;
|
||||
struct wgallowedip *allowedip;
|
||||
FILE *f;
|
||||
int ret, set_errno = -EPROTO;
|
||||
socklen_t addr_len;
|
||||
size_t line_buffer_len = 0, line_len;
|
||||
char *key = NULL, *value;
|
||||
|
||||
f = userspace_interface_file(dev->name);
|
||||
if (!f)
|
||||
return -errno;
|
||||
fprintf(f, "set=1\n");
|
||||
|
||||
if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) {
|
||||
key_to_hex(hex, dev->private_key);
|
||||
fprintf(f, "private_key=%s\n", hex);
|
||||
}
|
||||
if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
|
||||
fprintf(f, "listen_port=%u\n", dev->listen_port);
|
||||
if (dev->flags & WGDEVICE_HAS_FWMARK)
|
||||
fprintf(f, "fwmark=%u\n", dev->fwmark);
|
||||
if (dev->flags & WGDEVICE_REPLACE_PEERS)
|
||||
fprintf(f, "replace_peers=true\n");
|
||||
|
||||
for_each_wgpeer(dev, peer) {
|
||||
key_to_hex(hex, peer->public_key);
|
||||
fprintf(f, "public_key=%s\n", hex);
|
||||
if (peer->flags & WGPEER_REMOVE_ME) {
|
||||
fprintf(f, "remove=true\n");
|
||||
continue;
|
||||
}
|
||||
if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
|
||||
key_to_hex(hex, peer->preshared_key);
|
||||
fprintf(f, "preshared_key=%s\n", hex);
|
||||
}
|
||||
if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) {
|
||||
addr_len = 0;
|
||||
if (peer->endpoint.addr.sa_family == AF_INET)
|
||||
addr_len = sizeof(struct sockaddr_in);
|
||||
else if (peer->endpoint.addr.sa_family == AF_INET6)
|
||||
addr_len = sizeof(struct sockaddr_in6);
|
||||
if (!getnameinfo(&peer->endpoint.addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) {
|
||||
if (peer->endpoint.addr.sa_family == AF_INET6 && strchr(host, ':'))
|
||||
fprintf(f, "endpoint=[%s]:%s\n", host, service);
|
||||
else
|
||||
fprintf(f, "endpoint=%s:%s\n", host, service);
|
||||
}
|
||||
}
|
||||
if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL)
|
||||
fprintf(f, "persistent_keepalive_interval=%u\n", peer->persistent_keepalive_interval);
|
||||
if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
|
||||
fprintf(f, "replace_allowed_ips=true\n");
|
||||
for_each_wgallowedip(peer, allowedip) {
|
||||
if (allowedip->family == AF_INET) {
|
||||
if (!inet_ntop(AF_INET, &allowedip->ip4, ip, INET6_ADDRSTRLEN))
|
||||
continue;
|
||||
} else if (allowedip->family == AF_INET6) {
|
||||
if (!inet_ntop(AF_INET6, &allowedip->ip6, ip, INET6_ADDRSTRLEN))
|
||||
continue;
|
||||
} else
|
||||
continue;
|
||||
fprintf(f, "allowed_ip=%s/%d\n", ip, allowedip->cidr);
|
||||
}
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
fflush(f);
|
||||
|
||||
while (getline(&key, &line_buffer_len, f) > 0) {
|
||||
line_len = strlen(key);
|
||||
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);
|
||||
errno = -ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define NUM(max) ({ \
|
||||
unsigned long long num; \
|
||||
char *end; \
|
||||
if (!char_is_digit(value[0])) \
|
||||
break; \
|
||||
num = strtoull(value, &end, 10); \
|
||||
if (*end || num > max) \
|
||||
break; \
|
||||
num; \
|
||||
})
|
||||
|
||||
static int userspace_get_device(struct wgdevice **out, const char *iface)
|
||||
{
|
||||
struct wgdevice *dev;
|
||||
struct wgpeer *peer = NULL;
|
||||
struct wgallowedip *allowedip = NULL;
|
||||
size_t line_buffer_len = 0, line_len;
|
||||
char *key = NULL, *value;
|
||||
FILE *f;
|
||||
int ret = -EPROTO;
|
||||
|
||||
*out = dev = calloc(1, sizeof(*dev));
|
||||
if (!dev)
|
||||
return -errno;
|
||||
|
||||
f = userspace_interface_file(iface);
|
||||
if (!f) {
|
||||
ret = -errno;
|
||||
free(dev);
|
||||
*out = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
fprintf(f, "get=1\n\n");
|
||||
fflush(f);
|
||||
|
||||
strncpy(dev->name, iface, IFNAMSIZ - 1);
|
||||
dev->name[IFNAMSIZ - 1] = '\0';
|
||||
|
||||
while (getline(&key, &line_buffer_len, f) > 0) {
|
||||
line_len = strlen(key);
|
||||
if (line_len == 1 && key[0] == '\n')
|
||||
goto err;
|
||||
value = strchr(key, '=');
|
||||
if (!value || line_len == 0 || key[line_len - 1] != '\n')
|
||||
break;
|
||||
*value++ = key[--line_len] = '\0';
|
||||
|
||||
if (!peer && !strcmp(key, "private_key")) {
|
||||
if (!key_from_hex(dev->private_key, value))
|
||||
break;
|
||||
curve25519_generate_public(dev->public_key, dev->private_key);
|
||||
dev->flags |= WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_PUBLIC_KEY;
|
||||
} else if (!peer && !strcmp(key, "listen_port")) {
|
||||
dev->listen_port = NUM(0xffffU);
|
||||
dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
|
||||
} else if (!peer && !strcmp(key, "fwmark")) {
|
||||
dev->fwmark = NUM(0xffffffffU);
|
||||
dev->flags |= WGDEVICE_HAS_FWMARK;
|
||||
} else if (!strcmp(key, "public_key")) {
|
||||
struct wgpeer *new_peer = calloc(1, sizeof(*new_peer));
|
||||
|
||||
if (!new_peer) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
allowedip = NULL;
|
||||
if (peer)
|
||||
peer->next_peer = new_peer;
|
||||
else
|
||||
dev->first_peer = new_peer;
|
||||
peer = new_peer;
|
||||
if (!key_from_hex(peer->public_key, value))
|
||||
break;
|
||||
peer->flags |= WGPEER_HAS_PUBLIC_KEY;
|
||||
} else if (peer && !strcmp(key, "preshared_key")) {
|
||||
if (!key_from_hex(peer->preshared_key, value))
|
||||
break;
|
||||
if (!key_is_zero(peer->preshared_key))
|
||||
peer->flags |= WGPEER_HAS_PRESHARED_KEY;
|
||||
} else if (peer && !strcmp(key, "endpoint")) {
|
||||
char *begin, *end;
|
||||
struct addrinfo *resolved;
|
||||
struct addrinfo hints = {
|
||||
.ai_family = AF_UNSPEC,
|
||||
.ai_socktype = SOCK_DGRAM,
|
||||
.ai_protocol = IPPROTO_UDP
|
||||
};
|
||||
if (!strlen(value))
|
||||
break;
|
||||
if (value[0] == '[') {
|
||||
begin = &value[1];
|
||||
end = strchr(value, ']');
|
||||
if (!end)
|
||||
break;
|
||||
*end++ = '\0';
|
||||
if (*end++ != ':' || !*end)
|
||||
break;
|
||||
} else {
|
||||
begin = value;
|
||||
end = strrchr(value, ':');
|
||||
if (!end || !*(end + 1))
|
||||
break;
|
||||
*end++ = '\0';
|
||||
}
|
||||
if (getaddrinfo(begin, end, &hints, &resolved) != 0) {
|
||||
ret = ENETUNREACH;
|
||||
goto err;
|
||||
}
|
||||
if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) ||
|
||||
(resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)))
|
||||
memcpy(&peer->endpoint.addr, resolved->ai_addr, resolved->ai_addrlen);
|
||||
else {
|
||||
freeaddrinfo(resolved);
|
||||
break;
|
||||
}
|
||||
freeaddrinfo(resolved);
|
||||
} else if (peer && !strcmp(key, "persistent_keepalive_interval")) {
|
||||
peer->persistent_keepalive_interval = NUM(0xffffU);
|
||||
peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
|
||||
} else if (peer && !strcmp(key, "allowed_ip")) {
|
||||
struct wgallowedip *new_allowedip;
|
||||
char *end, *mask = value, *ip = strsep(&mask, "/");
|
||||
|
||||
if (!mask || !char_is_digit(mask[0]))
|
||||
break;
|
||||
new_allowedip = calloc(1, sizeof(*new_allowedip));
|
||||
if (!new_allowedip) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
if (allowedip)
|
||||
allowedip->next_allowedip = new_allowedip;
|
||||
else
|
||||
peer->first_allowedip = new_allowedip;
|
||||
allowedip = new_allowedip;
|
||||
allowedip->family = AF_UNSPEC;
|
||||
if (strchr(ip, ':')) {
|
||||
if (inet_pton(AF_INET6, ip, &allowedip->ip6) == 1)
|
||||
allowedip->family = AF_INET6;
|
||||
} else {
|
||||
if (inet_pton(AF_INET, ip, &allowedip->ip4) == 1)
|
||||
allowedip->family = AF_INET;
|
||||
}
|
||||
allowedip->cidr = strtoul(mask, &end, 10);
|
||||
if (*end || allowedip->family == AF_UNSPEC || (allowedip->family == AF_INET6 && allowedip->cidr > 128) || (allowedip->family == AF_INET && allowedip->cidr > 32))
|
||||
break;
|
||||
} else if (peer && !strcmp(key, "last_handshake_time_sec"))
|
||||
peer->last_handshake_time.tv_sec = NUM(0x7fffffffffffffffULL);
|
||||
else if (peer && !strcmp(key, "last_handshake_time_nsec"))
|
||||
peer->last_handshake_time.tv_nsec = NUM(0x7fffffffffffffffULL);
|
||||
else if (peer && !strcmp(key, "rx_bytes"))
|
||||
peer->rx_bytes = NUM(0xffffffffffffffffULL);
|
||||
else if (peer && !strcmp(key, "tx_bytes"))
|
||||
peer->tx_bytes = NUM(0xffffffffffffffffULL);
|
||||
else if (!strcmp(key, "errno"))
|
||||
ret = -NUM(0x7fffffffU);
|
||||
}
|
||||
ret = -EPROTO;
|
||||
err:
|
||||
free(key);
|
||||
if (ret) {
|
||||
free_wgdevice(dev);
|
||||
*out = NULL;
|
||||
}
|
||||
fclose(f);
|
||||
errno = -ret;
|
||||
return ret;
|
||||
|
||||
}
|
||||
#undef NUM
|
|
@ -0,0 +1,450 @@
|
|||
// 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;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
.BR pass (1):
|
||||
|
||||
\fBPostUp = wg set %i private-key <(pass WireGuard/private-keys/%i)\fP
|
||||
\fBPreUp = wg set %i private-key <(pass WireGuard/private-keys/%i)\fP
|
||||
.br
|
||||
|
||||
For use on a server, the following is a more complicated example involving multiple peers:
|
||||
|
@ -254,9 +254,7 @@ This will load the configuration file `/etc/wireguard/wgnet0.conf'.
|
|||
The \fIstrip\fP command is useful for reloading configuration files without disrupting active
|
||||
sessions:
|
||||
|
||||
\fB # wg addconf wgnet0 <(wg-quick strip wgnet0)\fP
|
||||
|
||||
(Note that the above command will add and update peers but will not remove peers.)
|
||||
\fB # wg syncconf wgnet0 <(wg-quick strip wgnet0)\fP
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR wg (8),
|
||||
|
|
|
@ -219,7 +219,14 @@ by running as root:
|
|||
|
||||
\fB # modprobe wireguard && echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control\fP
|
||||
|
||||
On userspace implementations, it is customary to set the \fILOG_LEVEL\fP environment variable to \fIdebug\fP.
|
||||
On OpenBSD and FreeBSD, debugging information can be written into
|
||||
.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
|
||||
.TP
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "curve25519.h"
|
||||
#include "encoding.h"
|
||||
#include "subcommands.h"
|
||||
#include "ctype.h"
|
||||
|
||||
int pubkey_main(int argc, char *argv[])
|
||||
int pubkey_main(int argc, const char *argv[])
|
||||
{
|
||||
uint8_t key[WG_KEY_LEN] __attribute__((aligned(sizeof(uintptr_t))));
|
||||
char base64[WG_KEY_LEN_BASE64];
|
||||
|
@ -31,7 +31,7 @@ int pubkey_main(int argc, char *argv[])
|
|||
|
||||
for (;;) {
|
||||
trailing_char = getc(stdin);
|
||||
if (!trailing_char || isspace(trailing_char) || isblank(trailing_char))
|
||||
if (!trailing_char || char_is_space(trailing_char))
|
||||
continue;
|
||||
if (trailing_char == EOF)
|
||||
break;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
@ -12,7 +12,7 @@
|
|||
#include "ipc.h"
|
||||
#include "subcommands.h"
|
||||
|
||||
int set_main(int argc, char *argv[])
|
||||
int set_main(int argc, const char *argv[])
|
||||
{
|
||||
struct wgdevice *device = NULL;
|
||||
int ret = 1;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
int setconf_main(int argc, char *argv[])
|
||||
int setconf_main(int argc, const char *argv[])
|
||||
{
|
||||
struct wgdevice *device = NULL;
|
||||
struct config_ctx ctx;
|
||||
|
|
14
src/show.c
14
src/show.c
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
time_t diff;
|
||||
const struct wgpeer *a = *(const void **)first, *b = *(const void **)second;
|
||||
const struct wgpeer *a = *(void *const *)first, *b = *(void *const *)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))
|
||||
return 1;
|
||||
|
@ -75,14 +75,14 @@ static char *key(const uint8_t key[static WG_KEY_LEN])
|
|||
return base64;
|
||||
}
|
||||
|
||||
static char *maybe_key(const uint8_t maybe_key[static WG_KEY_LEN], bool have_it)
|
||||
static const char *maybe_key(const uint8_t maybe_key[static WG_KEY_LEN], bool have_it)
|
||||
{
|
||||
if (!have_it)
|
||||
return "(none)";
|
||||
return key(maybe_key);
|
||||
}
|
||||
|
||||
static char *masked_key(const uint8_t masked_key[static WG_KEY_LEN])
|
||||
static const char *masked_key(const uint8_t masked_key[static WG_KEY_LEN])
|
||||
{
|
||||
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
|
||||
printf("off\n");
|
||||
} else if (!strcmp(param, "endpoints")) {
|
||||
if (with_interface)
|
||||
printf("%s\t", device->name);
|
||||
for_each_wgpeer(device, peer) {
|
||||
if (with_interface)
|
||||
printf("%s\t", device->name);
|
||||
printf("%s\t", key(peer->public_key));
|
||||
if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
|
||||
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;
|
||||
}
|
||||
|
||||
int show_main(int argc, char *argv[])
|
||||
int show_main(int argc, const char *argv[])
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
@ -18,7 +18,7 @@
|
|||
#include "ipc.h"
|
||||
#include "subcommands.h"
|
||||
|
||||
int showconf_main(int argc, char *argv[])
|
||||
int showconf_main(int argc, const char *argv[])
|
||||
{
|
||||
char base64[WG_KEY_LEN_BASE64];
|
||||
char ip[INET6_ADDRSTRLEN];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
@ -7,11 +7,11 @@
|
|||
#define SUBCOMMANDS_H
|
||||
|
||||
extern const char *PROG_NAME;
|
||||
int show_main(int argc, char *argv[]);
|
||||
int showconf_main(int argc, char *argv[]);
|
||||
int set_main(int argc, char *argv[]);
|
||||
int setconf_main(int argc, char *argv[]);
|
||||
int genkey_main(int argc, char *argv[]);
|
||||
int pubkey_main(int argc, char *argv[]);
|
||||
int show_main(int argc, const char *argv[]);
|
||||
int showconf_main(int argc, const char *argv[]);
|
||||
int set_main(int argc, const char *argv[]);
|
||||
int setconf_main(int argc, const char *argv[]);
|
||||
int genkey_main(int argc, const char *argv[]);
|
||||
int pubkey_main(int argc, const char *argv[]);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -15,6 +15,7 @@ Type=oneshot
|
|||
RemainAfterExit=yes
|
||||
ExecStart=/usr/bin/wg-quick up %i
|
||||
ExecStop=/usr/bin/wg-quick down %i
|
||||
ExecReload=/bin/bash -c 'exec /usr/bin/wg syncconf %i <(exec /usr/bin/wg-quick strip %i)'
|
||||
Environment=WG_ENDPOINT_RESOLUTION_RETRIES=infinity
|
||||
|
||||
[Install]
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
@ -11,6 +10,8 @@
|
|||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include "ctype.h"
|
||||
#include "terminal.h"
|
||||
|
||||
static bool color_mode(void)
|
||||
{
|
||||
|
@ -46,7 +47,7 @@ static void filter_ansi(const char *fmt, va_list args)
|
|||
if (str[i] == '\x1b' && str[i + 1] == '[') {
|
||||
str[i] = str[i + 1] = '\0';
|
||||
for (j = i + 2; j < len; ++j) {
|
||||
if (isalpha(str[j]))
|
||||
if (char_is_alpha(str[j]))
|
||||
break;
|
||||
str[j] = '\0';
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
#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
|
|
@ -25,26 +25,16 @@
|
|||
#define SIOCSWG _IOWR('i', 210, struct wg_data_io)
|
||||
#define SIOCGWG _IOWR('i', 211, struct wg_data_io)
|
||||
|
||||
struct wg_data_io {
|
||||
char wgd_name[IFNAMSIZ];
|
||||
size_t wgd_size; /* size of the mem below */
|
||||
void *wgd_mem; /* wg_interface_io{1},(wg_peer_io,wg_aip_io*)* */
|
||||
};
|
||||
#define a_ipv4 a_addr.addr_ipv4
|
||||
#define a_ipv6 a_addr.addr_ipv6
|
||||
|
||||
#define WG_INTERFACE_HAS_PUBLIC (1 << 0)
|
||||
#define WG_INTERFACE_HAS_PRIVATE (1 << 1)
|
||||
#define WG_INTERFACE_HAS_PORT (1 << 2)
|
||||
#define WG_INTERFACE_HAS_RTABLE (1 << 3)
|
||||
#define WG_INTERFACE_REPLACE_PEERS (1 << 4)
|
||||
|
||||
struct wg_interface_io {
|
||||
uint8_t i_flags;
|
||||
struct wg_peer_io *i_peers;
|
||||
|
||||
in_port_t i_port;
|
||||
int i_rtable;
|
||||
uint8_t i_public[WG_KEY_LEN];
|
||||
uint8_t i_private[WG_KEY_LEN];
|
||||
struct wg_aip_io {
|
||||
sa_family_t a_af;
|
||||
int a_cidr;
|
||||
union wg_aip_addr {
|
||||
struct in_addr addr_ipv4;
|
||||
struct in6_addr addr_ipv6;
|
||||
} a_addr;
|
||||
};
|
||||
|
||||
#define WG_PEER_HAS_PUBLIC (1 << 0)
|
||||
|
@ -60,42 +50,43 @@ struct wg_interface_io {
|
|||
#define p_sin6 p_endpoint.sa_sin6
|
||||
|
||||
struct wg_peer_io {
|
||||
int p_flags;
|
||||
struct wg_peer_io *p_next;
|
||||
struct wg_aip_io *p_aips;
|
||||
|
||||
int p_protocol_version;
|
||||
uint8_t p_public[WG_KEY_LEN];
|
||||
uint8_t p_psk[WG_KEY_LEN];
|
||||
uint16_t p_pka;
|
||||
int p_flags;
|
||||
int p_protocol_version;
|
||||
uint8_t p_public[WG_KEY_LEN];
|
||||
uint8_t p_psk[WG_KEY_LEN];
|
||||
uint16_t p_pka;
|
||||
union wg_peer_endpoint {
|
||||
struct sockaddr sa_sa;
|
||||
struct sockaddr_in sa_sin;
|
||||
struct sockaddr_in6 sa_sin6;
|
||||
} p_endpoint;
|
||||
|
||||
uint64_t p_txbytes;
|
||||
uint64_t p_rxbytes;
|
||||
struct timespec p_last_handshake; /* nanotime */
|
||||
} p_endpoint;
|
||||
uint64_t p_txbytes;
|
||||
uint64_t p_rxbytes;
|
||||
struct timespec p_last_handshake; /* nanotime */
|
||||
size_t p_aips_count;
|
||||
struct wg_aip_io p_aips[];
|
||||
};
|
||||
|
||||
#define a_af a_data.d_af
|
||||
#define a_cidr a_data.d_cidr
|
||||
#define a_addr a_data.d_addr
|
||||
#define a_ipv4 a_addr.addr_ipv4
|
||||
#define a_ipv6 a_addr.addr_ipv6
|
||||
#define WG_INTERFACE_HAS_PUBLIC (1 << 0)
|
||||
#define WG_INTERFACE_HAS_PRIVATE (1 << 1)
|
||||
#define WG_INTERFACE_HAS_PORT (1 << 2)
|
||||
#define WG_INTERFACE_HAS_RTABLE (1 << 3)
|
||||
#define WG_INTERFACE_REPLACE_PEERS (1 << 4)
|
||||
|
||||
struct wg_aip_io {
|
||||
struct wg_aip_io *a_next;
|
||||
struct wg_interface_io {
|
||||
uint8_t i_flags;
|
||||
in_port_t i_port;
|
||||
int i_rtable;
|
||||
uint8_t i_public[WG_KEY_LEN];
|
||||
uint8_t i_private[WG_KEY_LEN];
|
||||
size_t i_peers_count;
|
||||
struct wg_peer_io i_peers[];
|
||||
};
|
||||
|
||||
struct wg_aip_data {
|
||||
sa_family_t d_af;
|
||||
int d_cidr;
|
||||
union wg_aip_addr {
|
||||
struct in_addr addr_ipv4;
|
||||
struct in6_addr addr_ipv6;
|
||||
} d_addr;
|
||||
} a_data;
|
||||
struct wg_data_io {
|
||||
char wgd_name[IFNAMSIZ];
|
||||
size_t wgd_size; /* total size of the memory pointed to by wgd_interface */
|
||||
struct wg_interface_io *wgd_interface;
|
||||
};
|
||||
|
||||
#endif /* __IF_WG_H__ */
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/* 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
|
|
@ -1,3 +1,3 @@
|
|||
#ifndef WIREGUARD_TOOLS_VERSION
|
||||
#define WIREGUARD_TOOLS_VERSION "1.0.20200510"
|
||||
#define WIREGUARD_TOOLS_VERSION "1.0.20210914"
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*
|
||||
* 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).
|
||||
|
@ -25,6 +25,7 @@
|
|||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
#ifndef WG_PACKAGE_NAME
|
||||
#define WG_PACKAGE_NAME "com.wireguard.android"
|
||||
|
@ -39,6 +40,7 @@
|
|||
|
||||
static bool is_exiting = false;
|
||||
static bool binder_available = false;
|
||||
static unsigned int sdk_version;
|
||||
|
||||
static void *xmalloc(size_t size)
|
||||
{
|
||||
|
@ -726,8 +728,8 @@ static void up_if(unsigned int *netid, const char *iface, uint16_t listen_port)
|
|||
cmd("iptables -I INPUT 1 -p udp --dport %u -j ACCEPT -m comment --comment \"wireguard rule %s\"", listen_port, 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);
|
||||
}
|
||||
cndc("interface setcfg %s up", iface);
|
||||
cndc("network create %u vpn 1 1", *netid);
|
||||
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 interface add %u %s", *netid, iface);
|
||||
}
|
||||
|
||||
|
@ -785,6 +787,7 @@ 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)
|
||||
{
|
||||
_cleanup_free_ uid_t *uids = NULL;
|
||||
uid_t *uid;
|
||||
unsigned int args_per_command = 0;
|
||||
_cleanup_free_ char *ranges = NULL;
|
||||
char range[22];
|
||||
|
@ -796,14 +799,14 @@ static void set_users(unsigned int netid, const char *excluded_applications, con
|
|||
}
|
||||
|
||||
if (excluded_applications || !included_applications) {
|
||||
uids = get_uid_list(excluded_applications);
|
||||
for (start = 0; *uids; start = *uids + 1, ++uids) {
|
||||
if (start > *uids - 1)
|
||||
uid = uids = get_uid_list(excluded_applications);
|
||||
for (start = 0; *uid; start = *uid + 1, ++uid) {
|
||||
if (start > *uid - 1)
|
||||
continue;
|
||||
else if (start == *uids - 1)
|
||||
else if (start == *uid - 1)
|
||||
snprintf(range, sizeof(range), "%u", start);
|
||||
else
|
||||
snprintf(range, sizeof(range), "%u-%u", start, *uids - 1);
|
||||
snprintf(range, sizeof(range), "%u-%u", start, *uid - 1);
|
||||
ranges = concat_and_free(ranges, " ", range);
|
||||
if (++args_per_command % 18 == 0) {
|
||||
cndc("network users add %u %s", netid, ranges);
|
||||
|
@ -816,8 +819,8 @@ static void set_users(unsigned int netid, const char *excluded_applications, con
|
|||
ranges = concat_and_free(ranges, " ", range);
|
||||
}
|
||||
} else {
|
||||
for (uids = get_uid_list(included_applications); *uids; ++uids) {
|
||||
snprintf(range, sizeof(range), "%u", *uids);
|
||||
for (uid = uids = get_uid_list(included_applications); *uid; ++uid) {
|
||||
snprintf(range, sizeof(range), "%u", *uid);
|
||||
ranges = concat_and_free(ranges, " ", range);
|
||||
if (++args_per_command % 18 == 0) {
|
||||
cndc("network users add %u %s", netid, ranges);
|
||||
|
@ -852,7 +855,7 @@ static void set_dnses(unsigned int netid, const char *dnses)
|
|||
if (!len)
|
||||
return;
|
||||
|
||||
xregcomp(®ex_ipnothost, "^[a-zA-Z0-9_=+.-]{1,15}$", REG_EXTENDED | REG_NOSUB);
|
||||
xregcomp(®ex_ipnothost, "(^[0-9.]+$)|(^.*:.*$)", REG_EXTENDED | REG_NOSUB);
|
||||
for (char *dns = strtok(mutable, ", \t\n"); dns; dns = strtok(NULL, ", \t\n")) {
|
||||
if (strchr(dns, '\'') || strchr(dns, '\\'))
|
||||
continue;
|
||||
|
@ -1277,6 +1280,10 @@ int main(int argc, char *argv[])
|
|||
_cleanup_free_ char *excluded_applications = NULL;
|
||||
_cleanup_free_ char *included_applications = NULL;
|
||||
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")))
|
||||
cmd_usage(argv[0]);
|
||||
|
|
|
@ -194,14 +194,14 @@ collect_gateways() {
|
|||
|
||||
GATEWAY4=""
|
||||
while read -r destination gateway _; do
|
||||
[[ $destination == default ]] || continue
|
||||
[[ $destination == default && $gateway != "link#"* ]] || continue
|
||||
GATEWAY4="$gateway"
|
||||
break
|
||||
done < <(netstat -nr -f inet)
|
||||
|
||||
GATEWAY6=""
|
||||
while read -r destination gateway _; do
|
||||
[[ $destination == default ]] || continue
|
||||
[[ $destination == default && $gateway != "link#"* ]] || continue
|
||||
GATEWAY6="$gateway"
|
||||
break
|
||||
done < <(netstat -nr -f inet6)
|
||||
|
@ -324,22 +324,24 @@ monitor_daemon() {
|
|||
echo "[+] Backgrounding route monitor" >&2
|
||||
(trap 'del_routes; del_dns; exit 0' INT TERM EXIT
|
||||
exec >/dev/null 2>&1
|
||||
local event pid=$BASHPID
|
||||
exec 19< <(exec route -n monitor)
|
||||
local event bpid=$BASHPID mpid=$!
|
||||
[[ ${#DNS[@]} -gt 0 ]] && trap set_dns ALRM
|
||||
# TODO: this should also check to see if the endpoint actually changes
|
||||
# in response to incoming packets, and then call set_endpoint_direct_route
|
||||
# then too. That function should be able to gracefully cleanup if the
|
||||
# endpoints change.
|
||||
while read -r event; do
|
||||
while read -u 19 -r event; do
|
||||
[[ $event == RTM_* ]] || continue
|
||||
ifconfig "$REAL_INTERFACE" >/dev/null 2>&1 || break
|
||||
[[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
|
||||
[[ -z $MTU ]] && set_mtu
|
||||
if [[ ${#DNS[@]} -gt 0 ]]; then
|
||||
set_dns
|
||||
sleep 2 && kill -ALRM $pid 2>/dev/null &
|
||||
sleep 2 && kill -ALRM $bpid 2>/dev/null &
|
||||
fi
|
||||
done < <(route -n monitor)) &
|
||||
done
|
||||
kill $mpid) &
|
||||
[[ -n $LAUNCHED_BY_LAUNCHD ]] || disown
|
||||
}
|
||||
|
||||
|
@ -450,8 +452,8 @@ cmd_up() {
|
|||
local i
|
||||
get_real_interface && die "\`$INTERFACE' already exists as \`$REAL_INTERFACE'"
|
||||
trap 'del_if; del_routes; exit' INT TERM EXIT
|
||||
execute_hooks "${PRE_UP[@]}"
|
||||
add_if
|
||||
execute_hooks "${PRE_UP[@]}"
|
||||
set_config
|
||||
for i in "${ADDRESSES[@]}"; do
|
||||
add_addr "$i"
|
||||
|
|
|
@ -8,6 +8,7 @@ set -e -o pipefail
|
|||
shopt -s extglob
|
||||
export LC_ALL=C
|
||||
|
||||
exec 3>&2
|
||||
SELF="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
export PATH="${SELF%/*}:$PATH"
|
||||
|
||||
|
@ -28,7 +29,7 @@ PROGRAM="${0##*/}"
|
|||
ARGS=( "$@" )
|
||||
|
||||
cmd() {
|
||||
echo "[#] $*" >&2
|
||||
echo "[#] $*" >&3
|
||||
"$@"
|
||||
}
|
||||
|
||||
|
@ -114,6 +115,16 @@ auto_su() {
|
|||
}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
|
@ -141,24 +152,14 @@ del_routes() {
|
|||
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() {
|
||||
[[ $HAVE_SET_DNS -eq 0 ]] || unset_dns
|
||||
cmd rm -f "/var/run/wireguard/$INTERFACE.sock"
|
||||
while if_exists; do
|
||||
if [[ -S /var/run/wireguard/$INTERFACE.sock ]]; then
|
||||
cmd rm -f "/var/run/wireguard/$INTERFACE.sock"
|
||||
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
|
||||
# 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
|
||||
|
@ -175,7 +176,7 @@ add_addr() {
|
|||
if [[ $1 == *:* ]]; then
|
||||
cmd ifconfig "$INTERFACE" inet6 "$1" alias
|
||||
else
|
||||
cmd ifconfig "$INTERFACE" inet "$1" "${1%%/*}" alias
|
||||
cmd ifconfig "$INTERFACE" inet "$1" alias
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -283,18 +284,19 @@ monitor_daemon() {
|
|||
(make_temp
|
||||
trap 'del_routes; clean_temp; exit 0' INT TERM EXIT
|
||||
exec >/dev/null 2>&1
|
||||
local event
|
||||
exec 19< <(exec route -n monitor)
|
||||
local event pid=$!
|
||||
# TODO: this should also check to see if the endpoint actually changes
|
||||
# in response to incoming packets, and then call set_endpoint_direct_route
|
||||
# then too. That function should be able to gracefully cleanup if the
|
||||
# endpoints change.
|
||||
while read -r event; do
|
||||
while read -u 19 -r event; do
|
||||
[[ $event == RTM_* ]] || continue
|
||||
[[ -e /var/run/wireguard/$INTERFACE.sock ]] || break
|
||||
if_exists || break
|
||||
ifconfig "$INTERFACE" >/dev/null 2>&1 || break
|
||||
[[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
|
||||
# TODO: set the mtu as well, but only if up
|
||||
done < <(route -n monitor)) & disown
|
||||
done
|
||||
kill $pid) & disown
|
||||
}
|
||||
|
||||
HAVE_SET_DNS=0
|
||||
|
@ -335,7 +337,7 @@ add_route() {
|
|||
}
|
||||
|
||||
set_config() {
|
||||
cmd wg setconf "$INTERFACE" <(echo "$WG_CONFIG")
|
||||
echo "$WG_CONFIG" | cmd wg setconf "$INTERFACE" /dev/stdin
|
||||
}
|
||||
|
||||
save_config() {
|
||||
|
@ -418,8 +420,8 @@ cmd_up() {
|
|||
local i
|
||||
[[ -z $(ifconfig "$INTERFACE" 2>/dev/null) ]] || die "\`$INTERFACE' already exists"
|
||||
trap 'del_if; del_routes; clean_temp; exit' INT TERM EXIT
|
||||
execute_hooks "${PRE_UP[@]}"
|
||||
add_if
|
||||
execute_hooks "${PRE_UP[@]}"
|
||||
set_config
|
||||
for i in "${ADDRESSES[@]}"; do
|
||||
add_addr "$i"
|
||||
|
|
|
@ -220,9 +220,9 @@ add_default() {
|
|||
fi
|
||||
local proto=-4 iptables=iptables pf=ip
|
||||
[[ $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 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
|
||||
printf -v nftcmd '%sadd table %s %s\n' "$nftcmd" "$pf" "$nftable"
|
||||
|
@ -327,8 +327,8 @@ cmd_up() {
|
|||
local i
|
||||
[[ -z $(ip link show dev "$INTERFACE" 2>/dev/null) ]] || die "\`$INTERFACE' already exists"
|
||||
trap 'del_if; exit' INT TERM EXIT
|
||||
execute_hooks "${PRE_UP[@]}"
|
||||
add_if
|
||||
execute_hooks "${PRE_UP[@]}"
|
||||
set_config
|
||||
for i in "${ADDRESSES[@]}"; do
|
||||
add_addr "$i"
|
||||
|
|
|
@ -88,42 +88,33 @@ auto_su() {
|
|||
|
||||
|
||||
get_real_interface() {
|
||||
local interface diff
|
||||
wg show interfaces >/dev/null
|
||||
[[ -f "/var/run/wireguard/$INTERFACE.name" ]] || return 1
|
||||
interface="$(< "/var/run/wireguard/$INTERFACE.name")"
|
||||
if [[ $interface != wg* ]]; then
|
||||
[[ -n $interface && -S "/var/run/wireguard/$interface.sock" ]] || return 1
|
||||
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) ))
|
||||
[[ $diff -ge 2 || $diff -le -2 ]] && return 1
|
||||
echo "[+] Tun interface for $INTERFACE is $interface" >&2
|
||||
else
|
||||
[[ " $(wg show interfaces) " == *" $interface "* ]] || return 1
|
||||
fi
|
||||
REAL_INTERFACE="$interface"
|
||||
return 0
|
||||
local interface line
|
||||
while IFS= read -r line; do
|
||||
if [[ $line =~ ^([a-z]+[0-9]+):\ .+ ]]; then
|
||||
interface="${BASH_REMATCH[1]}"
|
||||
continue
|
||||
fi
|
||||
if [[ $interface == wg* && $line =~ ^\ description:\ wg-quick:\ (.+) && ${BASH_REMATCH[1]} == "$INTERFACE" ]]; then
|
||||
REAL_INTERFACE="$interface"
|
||||
return 0
|
||||
fi
|
||||
done < <(ifconfig)
|
||||
return 1
|
||||
}
|
||||
|
||||
add_if() {
|
||||
local index=0 ret
|
||||
while true; do
|
||||
if ret="$(cmd ifconfig wg$index create 2>&1)"; then
|
||||
mkdir -p "/var/run/wireguard/"
|
||||
echo wg$index > /var/run/wireguard/$INTERFACE.name
|
||||
get_real_interface
|
||||
local -A existing_ifs="( $(wg show interfaces | sed 's/\([^ ]*\)/[\1]=1/g') )"
|
||||
local index ret
|
||||
for ((index=0; index <= 2147483647; ++index)); do [[ -v existing_ifs[wg$index] ]] || break; done
|
||||
if ret="$(cmd ifconfig wg$index create description "wg-quick: $INTERFACE" 2>&1)"; then
|
||||
REAL_INTERFACE="wg$index"
|
||||
return 0
|
||||
fi
|
||||
if [[ $ret != *"ifconfig: SIOCIFCREATE: File exists"* ]]; then
|
||||
echo "[!] Missing WireGuard kernel support ($ret). Falling back to slow userspace implementation." >&3
|
||||
break
|
||||
fi
|
||||
echo "[+] wg$index in use, trying next"
|
||||
((++index))
|
||||
[[ $ret == *"ifconfig: SIOCIFCREATE: File exists"* ]] && continue
|
||||
echo "$ret" >&3
|
||||
return 1
|
||||
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() {
|
||||
|
@ -153,12 +144,7 @@ del_routes() {
|
|||
|
||||
del_if() {
|
||||
unset_dns
|
||||
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"
|
||||
[[ -n $REAL_INTERFACE ]] && cmd ifconfig $REAL_INTERFACE destroy
|
||||
}
|
||||
|
||||
up_if() {
|
||||
|
@ -280,30 +266,42 @@ monitor_daemon() {
|
|||
echo "[+] Backgrounding route monitor" >&2
|
||||
(trap 'del_routes; exit 0' INT TERM EXIT
|
||||
exec >/dev/null 2>&1
|
||||
local event
|
||||
exec 19< <(exec route -n monitor)
|
||||
local event pid=$!
|
||||
# TODO: this should also check to see if the endpoint actually changes
|
||||
# in response to incoming packets, and then call set_endpoint_direct_route
|
||||
# then too. That function should be able to gracefully cleanup if the
|
||||
# endpoints change.
|
||||
while read -r event; do
|
||||
while read -u 19 -r event; do
|
||||
[[ $event == RTM_* ]] || continue
|
||||
ifconfig "$REAL_INTERFACE" >/dev/null 2>&1 || break
|
||||
[[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
|
||||
# TODO: set the mtu as well, but only if up
|
||||
done < <(route -n monitor)) & disown
|
||||
done
|
||||
kill $pid) & disown
|
||||
}
|
||||
|
||||
set_dns() {
|
||||
[[ ${#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 printf 'nameserver %s\n' "${DNS[@]}"
|
||||
[[ ${#DNS_SEARCH[@]} -eq 0 ]] || cmd printf 'search %s\n' "${DNS_SEARCH[*]}"
|
||||
} > /etc/resolv.conf
|
||||
[[ ${#DNS_SEARCH[@]} -eq 0 ]] || cmd printf 'search %s\n' "${DNS_SEARCH[*]}" > /etc/resolv.conf
|
||||
route nameserver ${REAL_INTERFACE} ${DNS[@]}
|
||||
}
|
||||
|
||||
unset_dns() {
|
||||
[[ -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
|
||||
}
|
||||
|
||||
|
@ -419,8 +417,8 @@ cmd_up() {
|
|||
local i
|
||||
get_real_interface && die "\`$INTERFACE' already exists as \`$REAL_INTERFACE'"
|
||||
trap 'del_if; del_routes; exit' INT TERM EXIT
|
||||
execute_hooks "${PRE_UP[@]}"
|
||||
add_if
|
||||
execute_hooks "${PRE_UP[@]}"
|
||||
set_config
|
||||
for i in "${ADDRESSES[@]}"; do
|
||||
add_addr "$i"
|
||||
|
@ -438,9 +436,7 @@ cmd_up() {
|
|||
}
|
||||
|
||||
cmd_down() {
|
||||
if ! get_real_interface || [[ " $(wg show interfaces) " != *" $REAL_INTERFACE "* ]]; then
|
||||
die "\`$INTERFACE' is not a WireGuard interface"
|
||||
fi
|
||||
get_real_interface || die "\`$INTERFACE' is not a WireGuard interface"
|
||||
execute_hooks "${PRE_DOWN[@]}"
|
||||
[[ $SAVE_CONFIG -eq 0 ]] || save_config
|
||||
del_if
|
||||
|
@ -449,9 +445,7 @@ cmd_down() {
|
|||
}
|
||||
|
||||
cmd_save() {
|
||||
if ! get_real_interface || [[ " $(wg show interfaces) " != *" $REAL_INTERFACE "* ]]; then
|
||||
die "\`$INTERFACE' is not a WireGuard interface"
|
||||
fi
|
||||
get_real_interface || die "\`$INTERFACE' is not a WireGuard interface"
|
||||
save_config
|
||||
}
|
||||
|
||||
|
|
8
src/wg.c
8
src/wg.c
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
@ -14,7 +14,7 @@ const char *PROG_NAME;
|
|||
|
||||
static const struct {
|
||||
const char *subcommand;
|
||||
int (*function)(int, char**);
|
||||
int (*function)(int, const char**);
|
||||
const char *description;
|
||||
} subcommands[] = {
|
||||
{ "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");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
PROG_NAME = argv[0];
|
||||
|
||||
|
@ -51,7 +51,7 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
if (argc == 1) {
|
||||
static char *new_argv[] = { "show", NULL };
|
||||
static const char *new_argv[] = { "show", NULL };
|
||||
return show_main(1, new_argv);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,6 @@
|
|||
#undef min
|
||||
#undef max
|
||||
|
||||
#define WINCOMPAT
|
||||
|
||||
#define IFNAMSIZ 64
|
||||
#define EAI_SYSTEM -99
|
||||
|
||||
|
@ -27,5 +25,3 @@
|
|||
char *strsep(char **str, const char *sep);
|
||||
ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp);
|
||||
ssize_t getline(char **buf, size_t *bufsiz, FILE *fp);
|
||||
int inet_pton(int af, const char *src, void *dst);
|
||||
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <ntsecapi.h>
|
||||
|
||||
static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len)
|
||||
{
|
||||
return RtlGenRandom(out, len);
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/* 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
|
|
@ -10,12 +10,22 @@
|
|||
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
|
||||
#endif
|
||||
|
||||
extern void NTAPI RtlGetNtVersionNumbers(DWORD *major, DWORD *minor, DWORD *build);
|
||||
bool is_win7 = false;
|
||||
|
||||
__attribute__((constructor)) static void init(void)
|
||||
{
|
||||
char *colormode;
|
||||
DWORD console_mode;
|
||||
DWORD console_mode, major, minor;
|
||||
HANDLE stdout_handle;
|
||||
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);
|
||||
|
||||
stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); // We don't close this.
|
||||
|
|
|
@ -1,137 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <accctrl.h>
|
||||
#include <aclapi.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
static FILE *userspace_interface_file(const char *iface)
|
||||
{
|
||||
char fname[MAX_PATH], error_message[1024 * 128] = { 0 };
|
||||
HANDLE thread_token, process_snapshot, winlogon_process, winlogon_token, duplicated_token, pipe_handle = INVALID_HANDLE_VALUE;
|
||||
PROCESSENTRY32 entry = { .dwSize = sizeof(PROCESSENTRY32) };
|
||||
PSECURITY_DESCRIPTOR pipe_sd;
|
||||
PSID pipe_sid;
|
||||
SID expected_sid;
|
||||
BOOL ret;
|
||||
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))
|
||||
goto err;
|
||||
|
||||
process_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (process_snapshot == INVALID_HANDLE_VALUE)
|
||||
goto err;
|
||||
for (ret = Process32First(process_snapshot, &entry); ret; last_error = GetLastError(), ret = Process32Next(process_snapshot, &entry)) {
|
||||
if (strcasecmp(entry.szExeFile, "winlogon.exe"))
|
||||
continue;
|
||||
|
||||
RevertToSelf();
|
||||
if (!ImpersonateSelf(SecurityImpersonation))
|
||||
continue;
|
||||
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, FALSE, &thread_token))
|
||||
continue;
|
||||
if (!AdjustTokenPrivileges(thread_token, FALSE, &privileges, sizeof(privileges), NULL, NULL)) {
|
||||
last_error = GetLastError();
|
||||
CloseHandle(thread_token);
|
||||
continue;
|
||||
}
|
||||
CloseHandle(thread_token);
|
||||
|
||||
winlogon_process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, entry.th32ProcessID);
|
||||
if (!winlogon_process)
|
||||
continue;
|
||||
if (!OpenProcessToken(winlogon_process, TOKEN_IMPERSONATE | TOKEN_DUPLICATE, &winlogon_token))
|
||||
continue;
|
||||
CloseHandle(winlogon_process);
|
||||
if (!DuplicateToken(winlogon_token, SecurityImpersonation, &duplicated_token)) {
|
||||
last_error = GetLastError();
|
||||
RevertToSelf();
|
||||
continue;
|
||||
}
|
||||
CloseHandle(winlogon_token);
|
||||
if (!SetThreadToken(NULL, duplicated_token)) {
|
||||
last_error = GetLastError();
|
||||
CloseHandle(duplicated_token);
|
||||
continue;
|
||||
}
|
||||
CloseHandle(duplicated_token);
|
||||
|
||||
snprintf(fname, sizeof(fname), "\\\\.\\pipe\\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;
|
||||
fd = _open_osfhandle((intptr_t)pipe_handle, _O_RDWR);
|
||||
if (fd == -1) {
|
||||
last_error = GetLastError();
|
||||
CloseHandle(pipe_handle);
|
||||
goto err;
|
||||
}
|
||||
return _fdopen(fd, "r+");
|
||||
|
||||
err:
|
||||
if (last_error == ERROR_SUCCESS)
|
||||
last_error = GetLastError();
|
||||
if (last_error == ERROR_SUCCESS)
|
||||
last_error = ERROR_ACCESS_DENIED;
|
||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), error_message, sizeof(error_message) - 1, NULL);
|
||||
fprintf(stderr, "Error: Unable to open IPC handle via SYSTEM impersonation: %ld: %s\n", last_error, error_message);
|
||||
errno = EACCES;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int userspace_get_wireguard_interfaces(struct string_list *list)
|
||||
{
|
||||
static const char prefix[] = "ProtectedPrefix\\Administrators\\WireGuard\\";
|
||||
WIN32_FIND_DATA find_data;
|
||||
HANDLE find_handle;
|
||||
int ret = 0;
|
||||
|
||||
find_handle = FindFirstFile("\\\\.\\pipe\\*", &find_data);
|
||||
if (find_handle == INVALID_HANDLE_VALUE)
|
||||
return -GetLastError();
|
||||
do {
|
||||
if (strncmp(prefix, find_data.cFileName, strlen(prefix)))
|
||||
continue;
|
||||
ret = string_list_add(list, find_data.cFileName + strlen(prefix));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
} while (FindNextFile(find_handle, &find_data));
|
||||
|
||||
out:
|
||||
FindClose(find_handle);
|
||||
return ret;
|
||||
}
|
|
@ -6,8 +6,6 @@
|
|||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
|
||||
char *strsep(char **str, const char *sep)
|
||||
|
@ -69,37 +67,3 @@ ssize_t getline(char **buf, size_t *bufsiz, FILE *fp)
|
|||
{
|
||||
return getdelim(buf, bufsiz, '\n', fp);
|
||||
}
|
||||
|
||||
int inet_pton(int af, const char *src, void *dst)
|
||||
{
|
||||
struct sockaddr_storage ss = { 0 };
|
||||
int size = sizeof(ss);
|
||||
char s[INET6_ADDRSTRLEN + 1];
|
||||
|
||||
strncpy(s, src, INET6_ADDRSTRLEN + 1);
|
||||
s[INET6_ADDRSTRLEN] = '\0';
|
||||
|
||||
if (WSAStringToAddress(s, af, NULL, (struct sockaddr *)&ss, &size))
|
||||
return 0;
|
||||
if (af == AF_INET)
|
||||
*(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr;
|
||||
else if (af == AF_INET6)
|
||||
*(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr;
|
||||
else
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size)
|
||||
{
|
||||
struct sockaddr_storage ss = { .ss_family = af };
|
||||
unsigned long s = size;
|
||||
|
||||
if (af == AF_INET)
|
||||
((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src;
|
||||
else if (af == AF_INET6)
|
||||
((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src;
|
||||
else
|
||||
return NULL;
|
||||
return WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) ? NULL : dst;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// 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;
|
|
@ -0,0 +1,23 @@
|
|||
<?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>
|
|
@ -0,0 +1,40 @@
|
|||
/* 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
|
Loading…
Reference in New Issue