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