]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/wireguard-tools/config.c
Merge llvm-project release/16.x llvmorg-16.0.5-0-g185b81e034ba
[FreeBSD/FreeBSD.git] / contrib / wireguard-tools / config.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }