xref: /freebsd/contrib/wireguard-tools/config.c (revision adf37648)
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 
get_value(const char * line,const char * key)25 static 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 
parse_port(uint16_t * port,uint32_t * flags,const char * value)39 static 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 
parse_fwmark(uint32_t * fwmark,uint32_t * flags,const char * value)77 static 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;
102 err:
103 	fprintf(stderr, "Fwmark is neither 0/off nor 0-0xffffffff: `%s'\n", value);
104 	return false;
105 }
106 
parse_key(uint8_t key[static WG_KEY_LEN],const char * value)107 static 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 
parse_keyfile(uint8_t key[static WG_KEY_LEN],const char * path)117 static 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 
155 out:
156 	fclose(f);
157 	return ret;
158 }
159 
parse_ip(struct wgallowedip * allowedip,const char * value)160 static 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 
parse_dns_retries(void)177 static 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 
parse_endpoint(struct sockaddr * endpoint,const char * value)195 static 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 
parse_persistent_keepalive(uint16_t * interval,uint32_t * flags,const char * value)282 static 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;
303 err:
304 	fprintf(stderr, "Persistent keepalive interval is neither 0/off nor 1-65535: `%s'\n", value);
305 	return false;
306 }
307 
validate_netmask(struct wgallowedip * allowedip)308 static 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 
parse_allowedips(struct wgpeer * peer,struct wgallowedip ** last_allowedip,const char * value)340 static 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 
405 err:
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 
process_line(struct config_ctx * ctx,const char * line)413 static 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 
478 error:
479 	fprintf(stderr, "Line unrecognized: `%s'\n", line);
480 	return false;
481 }
482 
config_read_line(struct config_ctx * ctx,const char * input)483 bool 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);
510 out:
511 	free(line);
512 	if (!ret)
513 		free_wgdevice(ctx->device);
514 	return ret;
515 }
516 
config_read_init(struct config_ctx * ctx,bool append)517 bool 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 
config_read_finish(struct config_ctx * ctx)530 struct 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;
541 err:
542 	free_wgdevice(ctx->device);
543 	return NULL;
544 }
545 
strip_spaces(const char * in)546 static 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 
config_read_cmd(const char * argv[],int argc)564 struct 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;
647 error:
648 	free_wgdevice(device);
649 	return false;
650 }
651