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 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 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 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 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 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 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 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 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