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_ip_prefix(struct wgpeer * peer,uint32_t * flags,char ** mask)340 static inline void parse_ip_prefix(struct wgpeer *peer, uint32_t *flags, char **mask)
341 {
342 /* If the IP is prefixed with either '+' or '-' consider this an
343 * incremental change. Disable WGPEER_REPLACE_ALLOWEDIPS. */
344 switch ((*mask)[0]) {
345 case '-':
346 *flags |= WGALLOWEDIP_REMOVE_ME;
347 /* fall through */
348 case '+':
349 peer->flags &= ~WGPEER_REPLACE_ALLOWEDIPS;
350 ++(*mask);
351 }
352 }
353
parse_allowedips(struct wgpeer * peer,struct wgallowedip ** last_allowedip,const char * value)354 static inline bool parse_allowedips(struct wgpeer *peer, struct wgallowedip **last_allowedip, const char *value)
355 {
356 struct wgallowedip *allowedip = *last_allowedip, *new_allowedip;
357 char *mask, *mutable = strdup(value), *sep, *saved_entry;
358
359 if (!mutable) {
360 perror("strdup");
361 return false;
362 }
363 peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
364 if (!strlen(value)) {
365 free(mutable);
366 return true;
367 }
368 sep = mutable;
369 while ((mask = strsep(&sep, ","))) {
370 uint32_t flags = 0;
371 unsigned long cidr;
372 char *end, *ip;
373
374 parse_ip_prefix(peer, &flags, &mask);
375
376 saved_entry = strdup(mask);
377 if (!saved_entry) {
378 perror("strdup");
379 free(mutable);
380 return false;
381 }
382 ip = strsep(&mask, "/");
383
384 new_allowedip = calloc(1, sizeof(*new_allowedip));
385 if (!new_allowedip) {
386 perror("calloc");
387 free(saved_entry);
388 free(mutable);
389 return false;
390 }
391
392 if (!parse_ip(new_allowedip, ip)) {
393 free(new_allowedip);
394 free(saved_entry);
395 free(mutable);
396 return false;
397 }
398
399 if (mask) {
400 if (!char_is_digit(mask[0]))
401 goto err;
402 cidr = strtoul(mask, &end, 10);
403 if (*end || (cidr > 32 && new_allowedip->family == AF_INET) || (cidr > 128 && new_allowedip->family == AF_INET6))
404 goto err;
405 } else if (new_allowedip->family == AF_INET)
406 cidr = 32;
407 else if (new_allowedip->family == AF_INET6)
408 cidr = 128;
409 else
410 goto err;
411 new_allowedip->cidr = cidr;
412 new_allowedip->flags = flags;
413
414 if (!validate_netmask(new_allowedip))
415 fprintf(stderr, "Warning: AllowedIP has nonzero host part: %s/%s\n", ip, mask);
416
417 if (allowedip)
418 allowedip->next_allowedip = new_allowedip;
419 else
420 peer->first_allowedip = new_allowedip;
421 allowedip = new_allowedip;
422 free(saved_entry);
423 }
424 free(mutable);
425 *last_allowedip = allowedip;
426 return true;
427
428 err:
429 free(new_allowedip);
430 free(mutable);
431 fprintf(stderr, "AllowedIP is not in the correct format: `%s'\n", saved_entry);
432 free(saved_entry);
433 return false;
434 }
435
process_line(struct config_ctx * ctx,const char * line)436 static bool process_line(struct config_ctx *ctx, const char *line)
437 {
438 const char *value;
439 bool ret = true;
440
441 if (!strcasecmp(line, "[Interface]")) {
442 ctx->is_peer_section = false;
443 ctx->is_device_section = true;
444 return true;
445 }
446 if (!strcasecmp(line, "[Peer]")) {
447 struct wgpeer *new_peer = calloc(1, sizeof(struct wgpeer));
448
449 if (!new_peer) {
450 perror("calloc");
451 return false;
452 }
453 ctx->last_allowedip = NULL;
454 if (ctx->last_peer)
455 ctx->last_peer->next_peer = new_peer;
456 else
457 ctx->device->first_peer = new_peer;
458 ctx->last_peer = new_peer;
459 ctx->is_peer_section = true;
460 ctx->is_device_section = false;
461 ctx->last_peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
462 return true;
463 }
464
465 #define key_match(key) (value = get_value(line, key "="))
466
467 if (ctx->is_device_section) {
468 if (key_match("ListenPort"))
469 ret = parse_port(&ctx->device->listen_port, &ctx->device->flags, value);
470 else if (key_match("FwMark"))
471 ret = parse_fwmark(&ctx->device->fwmark, &ctx->device->flags, value);
472 else if (key_match("PrivateKey")) {
473 ret = parse_key(ctx->device->private_key, value);
474 if (ret)
475 ctx->device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
476 } else
477 goto error;
478 } else if (ctx->is_peer_section) {
479 if (key_match("Endpoint"))
480 ret = parse_endpoint(&ctx->last_peer->endpoint.addr, value);
481 else if (key_match("PublicKey")) {
482 ret = parse_key(ctx->last_peer->public_key, value);
483 if (ret)
484 ctx->last_peer->flags |= WGPEER_HAS_PUBLIC_KEY;
485 } else if (key_match("AllowedIPs"))
486 ret = parse_allowedips(ctx->last_peer, &ctx->last_allowedip, value);
487 else if (key_match("PersistentKeepalive"))
488 ret = parse_persistent_keepalive(&ctx->last_peer->persistent_keepalive_interval, &ctx->last_peer->flags, value);
489 else if (key_match("PresharedKey")) {
490 ret = parse_key(ctx->last_peer->preshared_key, value);
491 if (ret)
492 ctx->last_peer->flags |= WGPEER_HAS_PRESHARED_KEY;
493 } else
494 goto error;
495 } else
496 goto error;
497 return ret;
498
499 #undef key_match
500
501 error:
502 fprintf(stderr, "Line unrecognized: `%s'\n", line);
503 return false;
504 }
505
config_read_line(struct config_ctx * ctx,const char * input)506 bool config_read_line(struct config_ctx *ctx, const char *input)
507 {
508 size_t len, cleaned_len = 0;
509 char *line, *comment;
510 bool ret = true;
511
512 /* This is what strchrnul is for, but that isn't portable. */
513 comment = strchr(input, COMMENT_CHAR);
514 if (comment)
515 len = comment - input;
516 else
517 len = strlen(input);
518
519 line = calloc(len + 1, sizeof(char));
520 if (!line) {
521 perror("calloc");
522 ret = false;
523 goto out;
524 }
525
526 for (size_t i = 0; i < len; ++i) {
527 if (!char_is_space(input[i]))
528 line[cleaned_len++] = input[i];
529 }
530 if (!cleaned_len)
531 goto out;
532 ret = process_line(ctx, line);
533 out:
534 free(line);
535 if (!ret)
536 free_wgdevice(ctx->device);
537 return ret;
538 }
539
config_read_init(struct config_ctx * ctx,bool append)540 bool config_read_init(struct config_ctx *ctx, bool append)
541 {
542 memset(ctx, 0, sizeof(*ctx));
543 ctx->device = calloc(1, sizeof(*ctx->device));
544 if (!ctx->device) {
545 perror("calloc");
546 return false;
547 }
548 if (!append)
549 ctx->device->flags |= WGDEVICE_REPLACE_PEERS | WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_FWMARK | WGDEVICE_HAS_LISTEN_PORT;
550 return true;
551 }
552
config_read_finish(struct config_ctx * ctx)553 struct wgdevice *config_read_finish(struct config_ctx *ctx)
554 {
555 struct wgpeer *peer;
556
557 for_each_wgpeer(ctx->device, peer) {
558 if (!(peer->flags & WGPEER_HAS_PUBLIC_KEY)) {
559 fprintf(stderr, "A peer is missing a public key\n");
560 goto err;
561 }
562 }
563 return ctx->device;
564 err:
565 free_wgdevice(ctx->device);
566 return NULL;
567 }
568
strip_spaces(const char * in)569 static char *strip_spaces(const char *in)
570 {
571 char *out;
572 size_t t, l, i;
573
574 t = strlen(in);
575 out = calloc(t + 1, sizeof(char));
576 if (!out) {
577 perror("calloc");
578 return NULL;
579 }
580 for (i = 0, l = 0; i < t; ++i) {
581 if (!char_is_space(in[i]))
582 out[l++] = in[i];
583 }
584 return out;
585 }
586
config_read_cmd(const char * argv[],int argc)587 struct wgdevice *config_read_cmd(const char *argv[], int argc)
588 {
589 struct wgdevice *device = calloc(1, sizeof(*device));
590 struct wgpeer *peer = NULL;
591 struct wgallowedip *allowedip = NULL;
592
593 if (!device) {
594 perror("calloc");
595 return false;
596 }
597 while (argc > 0) {
598 if (!strcmp(argv[0], "listen-port") && argc >= 2 && !peer) {
599 if (!parse_port(&device->listen_port, &device->flags, argv[1]))
600 goto error;
601 argv += 2;
602 argc -= 2;
603 } else if (!strcmp(argv[0], "fwmark") && argc >= 2 && !peer) {
604 if (!parse_fwmark(&device->fwmark, &device->flags, argv[1]))
605 goto error;
606 argv += 2;
607 argc -= 2;
608 } else if (!strcmp(argv[0], "private-key") && argc >= 2 && !peer) {
609 if (!parse_keyfile(device->private_key, argv[1]))
610 goto error;
611 device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
612 argv += 2;
613 argc -= 2;
614 } else if (!strcmp(argv[0], "peer") && argc >= 2) {
615 struct wgpeer *new_peer = calloc(1, sizeof(*new_peer));
616
617 allowedip = NULL;
618 if (!new_peer) {
619 perror("calloc");
620 goto error;
621 }
622 if (peer)
623 peer->next_peer = new_peer;
624 else
625 device->first_peer = new_peer;
626 peer = new_peer;
627 if (!parse_key(peer->public_key, argv[1]))
628 goto error;
629 peer->flags |= WGPEER_HAS_PUBLIC_KEY;
630 argv += 2;
631 argc -= 2;
632 } else if (!strcmp(argv[0], "remove") && argc >= 1 && peer) {
633 peer->flags |= WGPEER_REMOVE_ME;
634 argv += 1;
635 argc -= 1;
636 } else if (!strcmp(argv[0], "endpoint") && argc >= 2 && peer) {
637 if (!parse_endpoint(&peer->endpoint.addr, argv[1]))
638 goto error;
639 argv += 2;
640 argc -= 2;
641 } else if (!strcmp(argv[0], "allowed-ips") && argc >= 2 && peer) {
642 char *line = strip_spaces(argv[1]);
643
644 if (!line)
645 goto error;
646 if (!parse_allowedips(peer, &allowedip, line)) {
647 free(line);
648 goto error;
649 }
650 free(line);
651 argv += 2;
652 argc -= 2;
653 } else if (!strcmp(argv[0], "persistent-keepalive") && argc >= 2 && peer) {
654 if (!parse_persistent_keepalive(&peer->persistent_keepalive_interval, &peer->flags, argv[1]))
655 goto error;
656 argv += 2;
657 argc -= 2;
658 } else if (!strcmp(argv[0], "preshared-key") && argc >= 2 && peer) {
659 if (!parse_keyfile(peer->preshared_key, argv[1]))
660 goto error;
661 peer->flags |= WGPEER_HAS_PRESHARED_KEY;
662 argv += 2;
663 argc -= 2;
664 } else {
665 fprintf(stderr, "Invalid argument: %s\n", argv[0]);
666 goto error;
667 }
668 }
669 return device;
670 error:
671 free_wgdevice(device);
672 return false;
673 }
674