1// SPDX-License-Identifier: GPL-2.0 OR 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 <limits.h>
8#include <netdb.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <unistd.h>
13#include <sys/socket.h>
14#include <sys/stat.h>
15#include <errno.h>
16
17#include "config.h"
18#include "containers.h"
19#include "ipc.h"
20#include "encoding.h"
21#include "ctype.h"
22
23#define COMMENT_CHAR '#'
24
25static const char *get_value(const char *line, const char *key)
26{
27	size_t linelen = strlen(line);
28	size_t keylen = strlen(key);
29
30	if (keylen >= linelen)
31		return NULL;
32
33	if (strncasecmp(line, key, keylen))
34		return NULL;
35
36	return line + keylen;
37}
38
39static inline bool parse_port(uint16_t *port, uint32_t *flags, const char *value)
40{
41	int ret;
42	struct addrinfo *resolved;
43	struct addrinfo hints = {
44		.ai_family = AF_UNSPEC,
45		.ai_socktype = SOCK_DGRAM,
46		.ai_protocol = IPPROTO_UDP,
47		.ai_flags = AI_PASSIVE
48	};
49
50	if (!strlen(value)) {
51		fprintf(stderr, "Unable to parse empty port\n");
52		return false;
53	}
54
55	ret = getaddrinfo(NULL, value, &hints, &resolved);
56	if (ret) {
57		fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value);
58		return false;
59	}
60
61	ret = -1;
62	if (resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) {
63		*port = ntohs(((struct sockaddr_in *)resolved->ai_addr)->sin_port);
64		ret = 0;
65	} else if (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)) {
66		*port = ntohs(((struct sockaddr_in6 *)resolved->ai_addr)->sin6_port);
67		ret = 0;
68	} else
69		fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s'\n", value);
70
71	freeaddrinfo(resolved);
72	if (!ret)
73		*flags |= WGDEVICE_HAS_LISTEN_PORT;
74	return ret == 0;
75}
76
77static inline bool parse_fwmark(uint32_t *fwmark, uint32_t *flags, const char *value)
78{
79	unsigned long ret;
80	char *end;
81	int base = 10;
82
83	if (!strcasecmp(value, "off")) {
84		*fwmark = 0;
85		*flags |= WGDEVICE_HAS_FWMARK;
86		return true;
87	}
88
89	if (!char_is_digit(value[0]))
90		goto err;
91
92	if (strlen(value) > 2 && value[0] == '0' && value[1] == 'x')
93		base = 16;
94
95	ret = strtoul(value, &end, base);
96	if (*end || ret > UINT32_MAX)
97		goto err;
98
99	*fwmark = ret;
100	*flags |= WGDEVICE_HAS_FWMARK;
101	return true;
102err:
103	fprintf(stderr, "Fwmark is neither 0/off nor 0-0xffffffff: `%s'\n", value);
104	return false;
105}
106
107static inline bool parse_key(uint8_t key[static WG_KEY_LEN], const char *value)
108{
109	if (!key_from_base64(key, value)) {
110		fprintf(stderr, "Key is not the correct length or format: `%s'\n", value);
111		memset(key, 0, WG_KEY_LEN);
112		return false;
113	}
114	return true;
115}
116
117static bool parse_keyfile(uint8_t key[static WG_KEY_LEN], const char *path)
118{
119	FILE *f;
120	int c;
121	char dst[WG_KEY_LEN_BASE64];
122	bool ret = false;
123
124	f = fopen(path, "r");
125	if (!f) {
126		perror("fopen");
127		return false;
128	}
129
130	if (fread(dst, WG_KEY_LEN_BASE64 - 1, 1, f) != 1) {
131		/* If we're at the end and we didn't read anything, we're /dev/null or an empty file. */
132		if (!ferror(f) && feof(f) && !ftell(f)) {
133			memset(key, 0, WG_KEY_LEN);
134			ret = true;
135			goto out;
136		}
137
138		fprintf(stderr, "Invalid length key in key file\n");
139		goto out;
140	}
141	dst[WG_KEY_LEN_BASE64 - 1] = '\0';
142
143	while ((c = getc(f)) != EOF) {
144		if (!char_is_space(c)) {
145			fprintf(stderr, "Found trailing character in key file: `%c'\n", c);
146			goto out;
147		}
148	}
149	if (ferror(f) && errno) {
150		perror("getc");
151		goto out;
152	}
153	ret = parse_key(key, dst);
154
155out:
156	fclose(f);
157	return ret;
158}
159
160static inline bool parse_ip(struct wgallowedip *allowedip, const char *value)
161{
162	allowedip->family = AF_UNSPEC;
163	if (strchr(value, ':')) {
164		if (inet_pton(AF_INET6, value, &allowedip->ip6) == 1)
165			allowedip->family = AF_INET6;
166	} else {
167		if (inet_pton(AF_INET, value, &allowedip->ip4) == 1)
168			allowedip->family = AF_INET;
169	}
170	if (allowedip->family == AF_UNSPEC) {
171		fprintf(stderr, "Unable to parse IP address: `%s'\n", value);
172		return false;
173	}
174	return true;
175}
176
177static inline int parse_dns_retries(void)
178{
179	unsigned long ret;
180	char *retries = getenv("WG_ENDPOINT_RESOLUTION_RETRIES"), *end;
181
182	if (!retries)
183		return 15;
184	if (!strcmp(retries, "infinity"))
185		return -1;
186
187	ret = strtoul(retries, &end, 10);
188	if (*end || ret > INT_MAX) {
189		fprintf(stderr, "Unable to parse WG_ENDPOINT_RESOLUTION_RETRIES: `%s'\n", retries);
190		exit(1);
191	}
192	return (int)ret;
193}
194
195static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value)
196{
197	char *mutable = strdup(value);
198	char *begin, *end;
199	int ret, retries = parse_dns_retries();
200	struct addrinfo *resolved;
201	struct addrinfo hints = {
202		.ai_family = AF_UNSPEC,
203		.ai_socktype = SOCK_DGRAM,
204		.ai_protocol = IPPROTO_UDP
205	};
206	if (!mutable) {
207		perror("strdup");
208		return false;
209	}
210	if (!strlen(value)) {
211		free(mutable);
212		fprintf(stderr, "Unable to parse empty endpoint\n");
213		return false;
214	}
215	if (mutable[0] == '[') {
216		begin = &mutable[1];
217		end = strchr(mutable, ']');
218		if (!end) {
219			free(mutable);
220			fprintf(stderr, "Unable to find matching brace of endpoint: `%s'\n", value);
221			return false;
222		}
223		*end++ = '\0';
224		if (*end++ != ':' || !*end) {
225			free(mutable);
226			fprintf(stderr, "Unable to find port of endpoint: `%s'\n", value);
227			return false;
228		}
229	} else {
230		begin = mutable;
231		end = strrchr(mutable, ':');
232		if (!end || !*(end + 1)) {
233			free(mutable);
234			fprintf(stderr, "Unable to find port of endpoint: `%s'\n", value);
235			return false;
236		}
237		*end++ = '\0';
238	}
239
240	#define min(a, b) ((a) < (b) ? (a) : (b))
241	for (unsigned int timeout = 1000000;; timeout = min(20000000, timeout * 6 / 5)) {
242		ret = getaddrinfo(begin, end, &hints, &resolved);
243		if (!ret)
244			break;
245		/* The set of return codes that are "permanent failures". All other possibilities are potentially transient.
246		 *
247		 * This is according to https://sourceware.org/glibc/wiki/NameResolver which states:
248		 *	"From the perspective of the application that calls getaddrinfo() it perhaps
249		 *	 doesn't matter that much since EAI_FAIL, EAI_NONAME and EAI_NODATA are all
250		 *	 permanent failure codes and the causes are all permanent failures in the
251		 *	 sense that there is no point in retrying later."
252		 *
253		 * So this is what we do, except FreeBSD removed EAI_NODATA some time ago, so that's conditional.
254		 */
255		if (ret == EAI_NONAME || ret == EAI_FAIL ||
256			#ifdef EAI_NODATA
257				ret == EAI_NODATA ||
258			#endif
259				(retries >= 0 && !retries--)) {
260			free(mutable);
261			fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value);
262			return false;
263		}
264		fprintf(stderr, "%s: `%s'. Trying again in %.2f seconds...\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value, timeout / 1000000.0);
265		usleep(timeout);
266	}
267
268	if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) ||
269	    (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)))
270		memcpy(endpoint, resolved->ai_addr, resolved->ai_addrlen);
271	else {
272		freeaddrinfo(resolved);
273		free(mutable);
274		fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s'\n", value);
275		return false;
276	}
277	freeaddrinfo(resolved);
278	free(mutable);
279	return true;
280}
281
282static inline bool parse_persistent_keepalive(uint16_t *interval, uint32_t *flags, const char *value)
283{
284	unsigned long ret;
285	char *end;
286
287	if (!strcasecmp(value, "off")) {
288		*interval = 0;
289		*flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
290		return true;
291	}
292
293	if (!char_is_digit(value[0]))
294		goto err;
295
296	ret = strtoul(value, &end, 10);
297	if (*end || ret > 65535)
298		goto err;
299
300	*interval = (uint16_t)ret;
301	*flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
302	return true;
303err:
304	fprintf(stderr, "Persistent keepalive interval is neither 0/off nor 1-65535: `%s'\n", value);
305	return false;
306}
307
308static bool validate_netmask(struct wgallowedip *allowedip)
309{
310	uint32_t *ip;
311	int last;
312
313	switch (allowedip->family) {
314		case AF_INET:
315			last = 0;
316			ip = (uint32_t *)&allowedip->ip4;
317			break;
318		case AF_INET6:
319			last = 3;
320			ip = (uint32_t *)&allowedip->ip6;
321			break;
322		default:
323			return true; /* We don't know how to validate it, so say 'okay'. */
324	}
325
326	for (int i = last; i >= 0; --i) {
327		uint32_t mask = ~0;
328
329		if (allowedip->cidr >= 32 * (i + 1))
330			break;
331		if (allowedip->cidr > 32 * i)
332			mask >>= (allowedip->cidr - 32 * i);
333		if (ntohl(ip[i]) & mask)
334			return false;
335	}
336
337	return true;
338}
339
340static inline bool parse_allowedips(struct wgpeer *peer, struct wgallowedip **last_allowedip, const char *value)
341{
342	struct wgallowedip *allowedip = *last_allowedip, *new_allowedip;
343	char *mask, *mutable = strdup(value), *sep, *saved_entry;
344
345	if (!mutable) {
346		perror("strdup");
347		return false;
348	}
349	peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
350	if (!strlen(value)) {
351		free(mutable);
352		return true;
353	}
354	sep = mutable;
355	while ((mask = strsep(&sep, ","))) {
356		unsigned long cidr;
357		char *end, *ip;
358
359		saved_entry = strdup(mask);
360		ip = strsep(&mask, "/");
361
362		new_allowedip = calloc(1, sizeof(*new_allowedip));
363		if (!new_allowedip) {
364			perror("calloc");
365			free(saved_entry);
366			free(mutable);
367			return false;
368		}
369
370		if (!parse_ip(new_allowedip, ip)) {
371			free(new_allowedip);
372			free(saved_entry);
373			free(mutable);
374			return false;
375		}
376
377		if (mask) {
378			if (!char_is_digit(mask[0]))
379				goto err;
380			cidr = strtoul(mask, &end, 10);
381			if (*end || (cidr > 32 && new_allowedip->family == AF_INET) || (cidr > 128 && new_allowedip->family == AF_INET6))
382				goto err;
383		} else if (new_allowedip->family == AF_INET)
384			cidr = 32;
385		else if (new_allowedip->family == AF_INET6)
386			cidr = 128;
387		else
388			goto err;
389		new_allowedip->cidr = cidr;
390
391		if (!validate_netmask(new_allowedip))
392			fprintf(stderr, "Warning: AllowedIP has nonzero host part: %s/%s\n", ip, mask);
393
394		if (allowedip)
395			allowedip->next_allowedip = new_allowedip;
396		else
397			peer->first_allowedip = new_allowedip;
398		allowedip = new_allowedip;
399		free(saved_entry);
400	}
401	free(mutable);
402	*last_allowedip = allowedip;
403	return true;
404
405err:
406	free(new_allowedip);
407	free(mutable);
408	fprintf(stderr, "AllowedIP is not in the correct format: `%s'\n", saved_entry);
409	free(saved_entry);
410	return false;
411}
412
413static bool process_line(struct config_ctx *ctx, const char *line)
414{
415	const char *value;
416	bool ret = true;
417
418	if (!strcasecmp(line, "[Interface]")) {
419		ctx->is_peer_section = false;
420		ctx->is_device_section = true;
421		return true;
422	}
423	if (!strcasecmp(line, "[Peer]")) {
424		struct wgpeer *new_peer = calloc(1, sizeof(struct wgpeer));
425
426		if (!new_peer) {
427			perror("calloc");
428			return false;
429		}
430		ctx->last_allowedip = NULL;
431		if (ctx->last_peer)
432			ctx->last_peer->next_peer = new_peer;
433		else
434			ctx->device->first_peer = new_peer;
435		ctx->last_peer = new_peer;
436		ctx->is_peer_section = true;
437		ctx->is_device_section = false;
438		ctx->last_peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
439		return true;
440	}
441
442#define key_match(key) (value = get_value(line, key "="))
443
444	if (ctx->is_device_section) {
445		if (key_match("ListenPort"))
446			ret = parse_port(&ctx->device->listen_port, &ctx->device->flags, value);
447		else if (key_match("FwMark"))
448			ret = parse_fwmark(&ctx->device->fwmark, &ctx->device->flags, value);
449		else if (key_match("PrivateKey")) {
450			ret = parse_key(ctx->device->private_key, value);
451			if (ret)
452				ctx->device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
453		} else
454			goto error;
455	} else if (ctx->is_peer_section) {
456		if (key_match("Endpoint"))
457			ret = parse_endpoint(&ctx->last_peer->endpoint.addr, value);
458		else if (key_match("PublicKey")) {
459			ret = parse_key(ctx->last_peer->public_key, value);
460			if (ret)
461				ctx->last_peer->flags |= WGPEER_HAS_PUBLIC_KEY;
462		} else if (key_match("AllowedIPs"))
463			ret = parse_allowedips(ctx->last_peer, &ctx->last_allowedip, value);
464		else if (key_match("PersistentKeepalive"))
465			ret = parse_persistent_keepalive(&ctx->last_peer->persistent_keepalive_interval, &ctx->last_peer->flags, value);
466		else if (key_match("PresharedKey")) {
467			ret = parse_key(ctx->last_peer->preshared_key, value);
468			if (ret)
469				ctx->last_peer->flags |= WGPEER_HAS_PRESHARED_KEY;
470		} else
471			goto error;
472	} else
473		goto error;
474	return ret;
475
476#undef key_match
477
478error:
479	fprintf(stderr, "Line unrecognized: `%s'\n", line);
480	return false;
481}
482
483bool config_read_line(struct config_ctx *ctx, const char *input)
484{
485	size_t len, cleaned_len = 0;
486	char *line, *comment;
487	bool ret = true;
488
489	/* This is what strchrnul is for, but that isn't portable. */
490	comment = strchr(input, COMMENT_CHAR);
491	if (comment)
492		len = comment - input;
493	else
494		len = strlen(input);
495
496	line = calloc(len + 1, sizeof(char));
497	if (!line) {
498		perror("calloc");
499		ret = false;
500		goto out;
501	}
502
503	for (size_t i = 0; i < len; ++i) {
504		if (!char_is_space(input[i]))
505			line[cleaned_len++] = input[i];
506	}
507	if (!cleaned_len)
508		goto out;
509	ret = process_line(ctx, line);
510out:
511	free(line);
512	if (!ret)
513		free_wgdevice(ctx->device);
514	return ret;
515}
516
517bool config_read_init(struct config_ctx *ctx, bool append)
518{
519	memset(ctx, 0, sizeof(*ctx));
520	ctx->device = calloc(1, sizeof(*ctx->device));
521	if (!ctx->device) {
522		perror("calloc");
523		return false;
524	}
525	if (!append)
526		ctx->device->flags |= WGDEVICE_REPLACE_PEERS | WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_FWMARK | WGDEVICE_HAS_LISTEN_PORT;
527	return true;
528}
529
530struct wgdevice *config_read_finish(struct config_ctx *ctx)
531{
532	struct wgpeer *peer;
533
534	for_each_wgpeer(ctx->device, peer) {
535		if (!(peer->flags & WGPEER_HAS_PUBLIC_KEY)) {
536			fprintf(stderr, "A peer is missing a public key\n");
537			goto err;
538		}
539	}
540	return ctx->device;
541err:
542	free_wgdevice(ctx->device);
543	return NULL;
544}
545
546static char *strip_spaces(const char *in)
547{
548	char *out;
549	size_t t, l, i;
550
551	t = strlen(in);
552	out = calloc(t + 1, sizeof(char));
553	if (!out) {
554		perror("calloc");
555		return NULL;
556	}
557	for (i = 0, l = 0; i < t; ++i) {
558		if (!char_is_space(in[i]))
559			out[l++] = in[i];
560	}
561	return out;
562}
563
564struct wgdevice *config_read_cmd(const char *argv[], int argc)
565{
566	struct wgdevice *device = calloc(1, sizeof(*device));
567	struct wgpeer *peer = NULL;
568	struct wgallowedip *allowedip = NULL;
569
570	if (!device) {
571		perror("calloc");
572		return false;
573	}
574	while (argc > 0) {
575		if (!strcmp(argv[0], "listen-port") && argc >= 2 && !peer) {
576			if (!parse_port(&device->listen_port, &device->flags, argv[1]))
577				goto error;
578			argv += 2;
579			argc -= 2;
580		} else if (!strcmp(argv[0], "fwmark") && argc >= 2 && !peer) {
581			if (!parse_fwmark(&device->fwmark, &device->flags, argv[1]))
582				goto error;
583			argv += 2;
584			argc -= 2;
585		} else if (!strcmp(argv[0], "private-key") && argc >= 2 && !peer) {
586			if (!parse_keyfile(device->private_key, argv[1]))
587				goto error;
588			device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
589			argv += 2;
590			argc -= 2;
591		} else if (!strcmp(argv[0], "peer") && argc >= 2) {
592			struct wgpeer *new_peer = calloc(1, sizeof(*new_peer));
593
594			allowedip = NULL;
595			if (!new_peer) {
596				perror("calloc");
597				goto error;
598			}
599			if (peer)
600				peer->next_peer = new_peer;
601			else
602				device->first_peer = new_peer;
603			peer = new_peer;
604			if (!parse_key(peer->public_key, argv[1]))
605				goto error;
606			peer->flags |= WGPEER_HAS_PUBLIC_KEY;
607			argv += 2;
608			argc -= 2;
609		} else if (!strcmp(argv[0], "remove") && argc >= 1 && peer) {
610			peer->flags |= WGPEER_REMOVE_ME;
611			argv += 1;
612			argc -= 1;
613		} else if (!strcmp(argv[0], "endpoint") && argc >= 2 && peer) {
614			if (!parse_endpoint(&peer->endpoint.addr, argv[1]))
615				goto error;
616			argv += 2;
617			argc -= 2;
618		} else if (!strcmp(argv[0], "allowed-ips") && argc >= 2 && peer) {
619			char *line = strip_spaces(argv[1]);
620
621			if (!line)
622				goto error;
623			if (!parse_allowedips(peer, &allowedip, line)) {
624				free(line);
625				goto error;
626			}
627			free(line);
628			argv += 2;
629			argc -= 2;
630		} else if (!strcmp(argv[0], "persistent-keepalive") && argc >= 2 && peer) {
631			if (!parse_persistent_keepalive(&peer->persistent_keepalive_interval, &peer->flags, argv[1]))
632				goto error;
633			argv += 2;
634			argc -= 2;
635		} else if (!strcmp(argv[0], "preshared-key") && argc >= 2 && peer) {
636			if (!parse_keyfile(peer->preshared_key, argv[1]))
637				goto error;
638			peer->flags |= WGPEER_HAS_PRESHARED_KEY;
639			argv += 2;
640			argc -= 2;
641		} else {
642			fprintf(stderr, "Invalid argument: %s\n", argv[0]);
643			goto error;
644		}
645	}
646	return device;
647error:
648	free_wgdevice(device);
649	return false;
650}
651