1// SPDX-License-Identifier: MIT
2/*
3 * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4 */
5
6#include <arpa/inet.h>
7#include <errno.h>
8#include <limits.h>
9#include <net/if.h>
10#include <netdb.h>
11#include <netinet/in.h>
12#include <stddef.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <sys/socket.h>
17#include "containers.h"
18#include "curve25519.h"
19#include "encoding.h"
20#include "ctype.h"
21
22#ifdef _WIN32
23#include "ipc-uapi-windows.h"
24#else
25#include "ipc-uapi-unix.h"
26#endif
27
28static int userspace_set_device(struct wgdevice *dev)
29{
30	char hex[WG_KEY_LEN_HEX], ip[INET6_ADDRSTRLEN], host[4096 + 1], service[512 + 1];
31	struct wgpeer *peer;
32	struct wgallowedip *allowedip;
33	FILE *f;
34	int ret, set_errno = -EPROTO;
35	socklen_t addr_len;
36	size_t line_buffer_len = 0, line_len;
37	char *key = NULL, *value;
38
39	f = userspace_interface_file(dev->name);
40	if (!f)
41		return -errno;
42	fprintf(f, "set=1\n");
43
44	if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) {
45		key_to_hex(hex, dev->private_key);
46		fprintf(f, "private_key=%s\n", hex);
47	}
48	if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
49		fprintf(f, "listen_port=%u\n", dev->listen_port);
50	if (dev->flags & WGDEVICE_HAS_FWMARK)
51		fprintf(f, "fwmark=%u\n", dev->fwmark);
52	if (dev->flags & WGDEVICE_REPLACE_PEERS)
53		fprintf(f, "replace_peers=true\n");
54
55	for_each_wgpeer(dev, peer) {
56		key_to_hex(hex, peer->public_key);
57		fprintf(f, "public_key=%s\n", hex);
58		if (peer->flags & WGPEER_REMOVE_ME) {
59			fprintf(f, "remove=true\n");
60			continue;
61		}
62		if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
63			key_to_hex(hex, peer->preshared_key);
64			fprintf(f, "preshared_key=%s\n", hex);
65		}
66		if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) {
67			addr_len = 0;
68			if (peer->endpoint.addr.sa_family == AF_INET)
69				addr_len = sizeof(struct sockaddr_in);
70			else if (peer->endpoint.addr.sa_family == AF_INET6)
71				addr_len = sizeof(struct sockaddr_in6);
72			if (!getnameinfo(&peer->endpoint.addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) {
73				if (peer->endpoint.addr.sa_family == AF_INET6 && strchr(host, ':'))
74					fprintf(f, "endpoint=[%s]:%s\n", host, service);
75				else
76					fprintf(f, "endpoint=%s:%s\n", host, service);
77			}
78		}
79		if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL)
80			fprintf(f, "persistent_keepalive_interval=%u\n", peer->persistent_keepalive_interval);
81		if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
82			fprintf(f, "replace_allowed_ips=true\n");
83		for_each_wgallowedip(peer, allowedip) {
84			if (allowedip->family == AF_INET) {
85				if (!inet_ntop(AF_INET, &allowedip->ip4, ip, INET6_ADDRSTRLEN))
86					continue;
87			} else if (allowedip->family == AF_INET6) {
88				if (!inet_ntop(AF_INET6, &allowedip->ip6, ip, INET6_ADDRSTRLEN))
89					continue;
90			} else
91				continue;
92			fprintf(f, "allowed_ip=%s/%d\n", ip, allowedip->cidr);
93		}
94	}
95	fprintf(f, "\n");
96	fflush(f);
97
98	while (getline(&key, &line_buffer_len, f) > 0) {
99		line_len = strlen(key);
100		ret = set_errno;
101		if (line_len == 1 && key[0] == '\n')
102			goto out;
103		value = strchr(key, '=');
104		if (!value || line_len == 0 || key[line_len - 1] != '\n')
105			break;
106		*value++ = key[--line_len] = '\0';
107
108		if (!strcmp(key, "errno")) {
109			long long num;
110			char *end;
111			if (value[0] != '-' && !char_is_digit(value[0]))
112				break;
113			num = strtoll(value, &end, 10);
114			if (*end || num > INT_MAX || num < INT_MIN)
115				break;
116			set_errno = num;
117		}
118	}
119	ret = errno ? -errno : -EPROTO;
120out:
121	free(key);
122	fclose(f);
123	errno = -ret;
124	return ret;
125}
126
127#define NUM(max) ({ \
128	unsigned long long num; \
129	char *end; \
130	if (!char_is_digit(value[0])) \
131		break; \
132	num = strtoull(value, &end, 10); \
133	if (*end || num > max) \
134		break; \
135	num; \
136})
137
138static int userspace_get_device(struct wgdevice **out, const char *iface)
139{
140	struct wgdevice *dev;
141	struct wgpeer *peer = NULL;
142	struct wgallowedip *allowedip = NULL;
143	size_t line_buffer_len = 0, line_len;
144	char *key = NULL, *value;
145	FILE *f;
146	int ret = -EPROTO;
147
148	*out = dev = calloc(1, sizeof(*dev));
149	if (!dev)
150		return -errno;
151
152	f = userspace_interface_file(iface);
153	if (!f) {
154		ret = -errno;
155		free(dev);
156		*out = NULL;
157		return ret;
158	}
159
160	fprintf(f, "get=1\n\n");
161	fflush(f);
162
163	strncpy(dev->name, iface, IFNAMSIZ - 1);
164	dev->name[IFNAMSIZ - 1] = '\0';
165
166	while (getline(&key, &line_buffer_len, f) > 0) {
167		line_len = strlen(key);
168		if (line_len == 1 && key[0] == '\n')
169			goto err;
170		value = strchr(key, '=');
171		if (!value || line_len == 0 || key[line_len - 1] != '\n')
172			break;
173		*value++ = key[--line_len] = '\0';
174
175		if (!peer && !strcmp(key, "private_key")) {
176			if (!key_from_hex(dev->private_key, value))
177				break;
178			curve25519_generate_public(dev->public_key, dev->private_key);
179			dev->flags |= WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_PUBLIC_KEY;
180		} else if (!peer && !strcmp(key, "listen_port")) {
181			dev->listen_port = NUM(0xffffU);
182			dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
183		} else if (!peer && !strcmp(key, "fwmark")) {
184			dev->fwmark = NUM(0xffffffffU);
185			dev->flags |= WGDEVICE_HAS_FWMARK;
186		} else if (!strcmp(key, "public_key")) {
187			struct wgpeer *new_peer = calloc(1, sizeof(*new_peer));
188
189			if (!new_peer) {
190				ret = -ENOMEM;
191				goto err;
192			}
193			allowedip = NULL;
194			if (peer)
195				peer->next_peer = new_peer;
196			else
197				dev->first_peer = new_peer;
198			peer = new_peer;
199			if (!key_from_hex(peer->public_key, value))
200				break;
201			peer->flags |= WGPEER_HAS_PUBLIC_KEY;
202		} else if (peer && !strcmp(key, "preshared_key")) {
203			if (!key_from_hex(peer->preshared_key, value))
204				break;
205			if (!key_is_zero(peer->preshared_key))
206				peer->flags |= WGPEER_HAS_PRESHARED_KEY;
207		} else if (peer && !strcmp(key, "endpoint")) {
208			char *begin, *end;
209			struct addrinfo *resolved;
210			struct addrinfo hints = {
211				.ai_family = AF_UNSPEC,
212				.ai_socktype = SOCK_DGRAM,
213				.ai_protocol = IPPROTO_UDP
214			};
215			if (!strlen(value))
216				break;
217			if (value[0] == '[') {
218				begin = &value[1];
219				end = strchr(value, ']');
220				if (!end)
221					break;
222				*end++ = '\0';
223				if (*end++ != ':' || !*end)
224					break;
225			} else {
226				begin = value;
227				end = strrchr(value, ':');
228				if (!end || !*(end + 1))
229					break;
230				*end++ = '\0';
231			}
232			if (getaddrinfo(begin, end, &hints, &resolved) != 0) {
233				ret = ENETUNREACH;
234				goto err;
235			}
236			if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) ||
237			    (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)))
238				memcpy(&peer->endpoint.addr, resolved->ai_addr, resolved->ai_addrlen);
239			else  {
240				freeaddrinfo(resolved);
241				break;
242			}
243			freeaddrinfo(resolved);
244		} else if (peer && !strcmp(key, "persistent_keepalive_interval")) {
245			peer->persistent_keepalive_interval = NUM(0xffffU);
246			peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
247		} else if (peer && !strcmp(key, "allowed_ip")) {
248			struct wgallowedip *new_allowedip;
249			char *end, *mask = value, *ip = strsep(&mask, "/");
250
251			if (!mask || !char_is_digit(mask[0]))
252				break;
253			new_allowedip = calloc(1, sizeof(*new_allowedip));
254			if (!new_allowedip) {
255				ret = -ENOMEM;
256				goto err;
257			}
258			if (allowedip)
259				allowedip->next_allowedip = new_allowedip;
260			else
261				peer->first_allowedip = new_allowedip;
262			allowedip = new_allowedip;
263			allowedip->family = AF_UNSPEC;
264			if (strchr(ip, ':')) {
265				if (inet_pton(AF_INET6, ip, &allowedip->ip6) == 1)
266					allowedip->family = AF_INET6;
267			} else {
268				if (inet_pton(AF_INET, ip, &allowedip->ip4) == 1)
269					allowedip->family = AF_INET;
270			}
271			allowedip->cidr = strtoul(mask, &end, 10);
272			if (*end || allowedip->family == AF_UNSPEC || (allowedip->family == AF_INET6 && allowedip->cidr > 128) || (allowedip->family == AF_INET && allowedip->cidr > 32))
273				break;
274		} else if (peer && !strcmp(key, "last_handshake_time_sec"))
275			peer->last_handshake_time.tv_sec = NUM(0x7fffffffffffffffULL);
276		else if (peer && !strcmp(key, "last_handshake_time_nsec"))
277			peer->last_handshake_time.tv_nsec = NUM(0x7fffffffffffffffULL);
278		else if (peer && !strcmp(key, "rx_bytes"))
279			peer->rx_bytes = NUM(0xffffffffffffffffULL);
280		else if (peer && !strcmp(key, "tx_bytes"))
281			peer->tx_bytes = NUM(0xffffffffffffffffULL);
282		else if (!strcmp(key, "errno"))
283			ret = -NUM(0x7fffffffU);
284	}
285	ret = -EPROTO;
286err:
287	free(key);
288	if (ret) {
289		free_wgdevice(dev);
290		*out = NULL;
291	}
292	fclose(f);
293	errno = -ret;
294	return ret;
295
296}
297#undef NUM
298