1 /*- 2 * SPDX-License-Identifier: ISC 3 * 4 * Copyright (C) 2019-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. 5 * Copyright (C) 2019-2020 Matt Dunwoodie <ncon@noconroy.net> 6 * Copyright (c) 2023-2024 Aaron LI <aly@aaronly.me> 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21 #include <sys/param.h> 22 #include <sys/ioctl.h> 23 #include <sys/socket.h> 24 25 #include <arpa/inet.h> 26 #include <net/wg/if_wg.h> 27 28 #include <err.h> 29 #include <errno.h> 30 #include <inttypes.h> 31 #include <limits.h> 32 #include <netdb.h> /* getaddrinfo(), getnameinfo() */ 33 #include <resolv.h> /* b64_pton(), b64_ntop() */ 34 #include <stdbool.h> 35 #include <stddef.h> /* ptrdiff_t */ 36 #include <stdint.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <time.h> /* timespec_get() */ 41 42 #include "ifconfig.h" 43 44 /* 45 * WG_BASE64_KEY_LEN is the size of a base64 encoded WireGuard key. 46 * For every 4 input (base64) bytes, 3 output bytes wil be produced. 47 * The output will be padded with 0 bits, therefore we need more than 48 * the regular 32 bytes of space. 49 */ 50 #define WG_BASE64_KEY_LEN (4 * ((WG_KEY_SIZE + 2) / 3)) 51 52 static struct wg_data_io wg_data; 53 static struct wg_interface_io *wg_interface; 54 static struct wg_peer_io *wg_peer; 55 static struct wg_aip_io *wg_aip; 56 57 58 static void 59 wg_data_init(void) 60 { 61 if (wg_interface != NULL) 62 return; 63 64 strlcpy(wg_data.wgd_name, IfName, sizeof(wg_data.wgd_name)); 65 wg_data.wgd_size = sizeof(*wg_interface); 66 wg_data.wgd_interface = wg_interface = calloc(1, wg_data.wgd_size); 67 if (wg_interface == NULL) 68 err(1, "calloc"); 69 } 70 71 static void 72 wg_data_grow(size_t by) 73 { 74 ptrdiff_t peer_offset, aip_offset; 75 76 wg_data_init(); 77 78 peer_offset = (char *)wg_peer - (char *)wg_interface; 79 aip_offset = (char *)wg_aip - (char *)wg_interface; 80 81 wg_data.wgd_size += by; 82 wg_data.wgd_interface = realloc(wg_interface, wg_data.wgd_size); 83 if (wg_data.wgd_interface == NULL) 84 err(1, "realloc"); 85 86 wg_interface = wg_data.wgd_interface; 87 memset((char *)wg_interface + wg_data.wgd_size - by, 0, by); 88 89 if (wg_peer != NULL) 90 wg_peer = (void *)((char *)wg_interface + peer_offset); 91 if (wg_aip != NULL) 92 wg_aip = (void *)((char *)wg_interface + aip_offset); 93 } 94 95 96 static void 97 wg_callback(int s, void *arg __unused) 98 { 99 if (ioctl(s, SIOCSWG, &wg_data) == -1) 100 err(1, "%s: SIOCSWG", wg_data.wgd_name); 101 } 102 103 static bool wg_cb_registered; 104 105 #define WG_REGISTER_CALLBACK() \ 106 if (!wg_cb_registered) { \ 107 callback_register(wg_callback, NULL); \ 108 wg_cb_registered = true; \ 109 } 110 111 112 static void 113 wg_setkey(const char *privkey, int arg __unused, int s __unused, 114 const struct afswtch *afp __unused) 115 { 116 wg_data_init(); 117 118 if (b64_pton(privkey, wg_interface->i_private, WG_KEY_SIZE) 119 != WG_KEY_SIZE) 120 errx(1, "wgkey: invalid private key: %s", privkey); 121 wg_interface->i_flags |= WG_INTERFACE_HAS_PRIVATE; 122 123 WG_REGISTER_CALLBACK(); 124 } 125 126 static void 127 wg_setport(const char *port, int arg __unused, int s __unused, 128 const struct afswtch *afp __unused) 129 { 130 const char *errmsg = NULL; 131 132 wg_data_init(); 133 134 wg_interface->i_port = (in_port_t)strtonum(port, 0, 65535, &errmsg); 135 if (errmsg != NULL) 136 errx(1, "wgport: invalid port %s: %s", port, errmsg); 137 wg_interface->i_flags |= WG_INTERFACE_HAS_PORT; 138 139 WG_REGISTER_CALLBACK(); 140 } 141 142 static void 143 wg_setcookie(const char *cookie, int arg __unused, int s __unused, 144 const struct afswtch *afp __unused) 145 { 146 const char *errmsg = NULL; 147 148 wg_data_init(); 149 150 wg_interface->i_cookie = 151 (uint32_t)strtonum(cookie, 0, UINT32_MAX, &errmsg); 152 if (errmsg != NULL) 153 errx(1, "wgcookie: invalid cookie %s: %s", cookie, errmsg); 154 wg_interface->i_flags |= WG_INTERFACE_HAS_COOKIE; 155 156 WG_REGISTER_CALLBACK(); 157 } 158 159 static void 160 wg_unsetcookie(const char *x __unused, int arg __unused, int s __unused, 161 const struct afswtch *afp __unused) 162 { 163 wg_data_init(); 164 165 /* Unset cookie by setting it to value 0. */ 166 wg_interface->i_cookie = 0; 167 wg_interface->i_flags |= WG_INTERFACE_HAS_COOKIE; 168 169 WG_REGISTER_CALLBACK(); 170 } 171 172 173 static void 174 wg_setpeer(const char *peerkey, int arg __unused, int s __unused, 175 const struct afswtch *afp __unused) 176 { 177 wg_data_grow(sizeof(*wg_peer)); 178 179 if (wg_aip == NULL) 180 wg_peer = &wg_interface->i_peers[0]; /* first peer */ 181 else 182 wg_peer = (struct wg_peer_io *)wg_aip; /* end of last peer */ 183 wg_aip = &wg_peer->p_aips[0]; 184 185 if (b64_pton(peerkey, wg_peer->p_public, WG_KEY_SIZE) != WG_KEY_SIZE) 186 errx(1, "wgpeer: invalid peer key: %s", peerkey); 187 wg_peer->p_flags |= WG_PEER_HAS_PUBLIC; 188 wg_interface->i_peers_count++; 189 190 WG_REGISTER_CALLBACK(); 191 } 192 193 static void 194 wg_unsetpeer(const char *peerkey, int arg, int s, const struct afswtch *afp) 195 { 196 wg_setpeer(peerkey, arg, s, afp); 197 wg_peer->p_flags |= WG_PEER_REMOVE; 198 } 199 200 static void 201 wg_unsetpeerall(const char *x __unused, int arg __unused, int s __unused, 202 const struct afswtch *afp __unused) 203 { 204 wg_data_init(); 205 206 wg_interface->i_flags |= WG_INTERFACE_REPLACE_PEERS; 207 208 WG_REGISTER_CALLBACK(); 209 } 210 211 /* 212 * Manually parse the CIDR instead of using inet_net_pton() because: 213 * 1. it uses a legacy IPv6 CIDR format (e.g., 1:2:3:4/64) and fails to parse 214 * some now valid IPv6 CIDRs (e.g., 1:2:3:4::/64); 215 * 2. it's not standard and behaves differently across BSDs and Linux. 216 */ 217 static int 218 wg_aip_parse(const char *aip, struct wg_aip_io *waip) 219 { 220 const char *errmsg = NULL; 221 char *p, buf[INET6_ADDRSTRLEN + sizeof("/128")]; 222 int plen; 223 224 if (snprintf(buf, sizeof(buf), "%s", aip) >= (int)sizeof(buf)) 225 return (-1); 226 227 plen = 128; 228 if ((p = strchr(buf, '/')) != NULL) { 229 *p = '\0'; 230 plen = (int)strtonum(p + 1, 0, 128, &errmsg); 231 if (errmsg != NULL) 232 return (-1); 233 } 234 235 if (inet_pton(AF_INET6, buf, &waip->a_ipv6) == 1) { 236 if (plen < 0 || plen > 128) 237 return (-1); 238 waip->a_cidr = plen; 239 waip->a_af = AF_INET6; 240 return (0); 241 } 242 243 if (inet_pton(AF_INET, buf, &waip->a_ipv4) == 1) { 244 if (plen == 128) 245 plen = 32; 246 if (plen < 0 || plen > 32) 247 return (-1); 248 waip->a_cidr = plen; 249 waip->a_af = AF_INET; 250 return (0); 251 } 252 253 return (-1); 254 } 255 256 static void 257 wg_setpeeraip(const char *aip, int arg __unused, int s __unused, 258 const struct afswtch *afp __unused) 259 { 260 if (wg_peer == NULL) 261 errx(1, "wgaip: wgpeer not set"); 262 263 wg_data_grow(sizeof(*wg_aip)); 264 265 if (wg_aip_parse(aip, wg_aip) == -1) 266 errx(1, "wgaip: bad address: %s", aip); 267 268 wg_peer->p_flags |= WG_PEER_REPLACE_AIPS; 269 wg_peer->p_aips_count++; 270 271 wg_aip++; 272 273 WG_REGISTER_CALLBACK(); 274 } 275 276 static void 277 wg_setpeerpsk(const char *psk, int arg __unused, int s __unused, 278 const struct afswtch *afp __unused) 279 { 280 if (wg_peer == NULL) 281 errx(1, "wgpsk: wgpeer not set"); 282 283 if (b64_pton(psk, wg_peer->p_psk, WG_KEY_SIZE) != WG_KEY_SIZE) 284 errx(1, "wgpsk: invalid key: %s", psk); 285 wg_peer->p_flags |= WG_PEER_HAS_PSK; 286 287 WG_REGISTER_CALLBACK(); 288 } 289 290 static void 291 wg_unsetpeerpsk(const char *x __unused, int arg __unused, int s __unused, 292 const struct afswtch *afp __unused) 293 { 294 if (wg_peer == NULL) 295 errx(1, "-wgpsk: wgpeer not set"); 296 297 /* Unset PSK by setting it to empty. */ 298 memset(wg_peer->p_psk, 0, sizeof(wg_peer->p_psk)); 299 wg_peer->p_flags |= WG_PEER_HAS_PSK; 300 301 WG_REGISTER_CALLBACK(); 302 } 303 304 static void 305 wg_setpeerpka(const char *pka, int arg __unused, int s __unused, 306 const struct afswtch *afp __unused) 307 { 308 const char *errmsg = NULL; 309 310 if (wg_peer == NULL) 311 errx(1, "wgpka: wgpeer not set"); 312 313 /* 43200 seconds == 12h, reasonable for a uint16_t value */ 314 wg_peer->p_pka = (uint16_t)strtonum(pka, 0, 43200, &errmsg); 315 if (errmsg != NULL) 316 errx(1, "wgpka: invalid pka %s: %s", pka, errmsg); 317 wg_peer->p_flags |= WG_PEER_HAS_PKA; 318 319 WG_REGISTER_CALLBACK(); 320 } 321 322 static void 323 wg_unsetpeerpka(const char *x __unused, int arg __unused, int s __unused, 324 const struct afswtch *afp __unused) 325 { 326 if (wg_peer == NULL) 327 errx(1, "-wgpka: wgpeer not set"); 328 329 wg_peer->p_pka = 0; 330 wg_peer->p_flags |= WG_PEER_HAS_PKA; 331 332 WG_REGISTER_CALLBACK(); 333 } 334 335 static void 336 wg_setpeerendpoint(const char *host, const char *service, int s __unused, 337 const struct afswtch *afp __unused) 338 { 339 struct addrinfo *ai; 340 int error; 341 342 if (wg_peer == NULL) 343 errx(1, "wgendpoint: wgpeer not set"); 344 345 if ((error = getaddrinfo(host, service, NULL, &ai)) != 0) 346 errx(1, "%s", gai_strerror(error)); 347 348 memcpy(&wg_peer->p_sa, ai->ai_addr, ai->ai_addrlen); 349 wg_peer->p_flags |= WG_PEER_HAS_ENDPOINT; 350 351 freeaddrinfo(ai); 352 WG_REGISTER_CALLBACK(); 353 } 354 355 static void 356 wg_setpeerdesc(const char *desc, int arg __unused, int s __unused, 357 const struct afswtch *afp __unused) 358 { 359 if (wg_peer == NULL) 360 errx(1, "wgdescr: wgpeer not set"); 361 362 strlcpy(wg_peer->p_description, desc, sizeof(wg_peer->p_description)); 363 wg_peer->p_flags |= WG_PEER_SET_DESCRIPTION; 364 365 WG_REGISTER_CALLBACK(); 366 } 367 368 static void 369 wg_unsetpeerdesc(const char *x __unused, int arg __unused, int s __unused, 370 const struct afswtch *afp __unused) 371 { 372 if (wg_peer == NULL) 373 errx(1, "-wgpsk: wgpeer not set"); 374 375 memset(wg_peer->p_description, 0, sizeof(wg_peer->p_description)); 376 wg_peer->p_flags |= WG_PEER_SET_DESCRIPTION; 377 378 WG_REGISTER_CALLBACK(); 379 } 380 381 382 static void 383 wg_status(int s) 384 { 385 struct timespec now; 386 size_t i, j, last_size; 387 char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; 388 char key[WG_BASE64_KEY_LEN + 1]; 389 390 memset(&wg_data, 0, sizeof(wg_data)); 391 strlcpy(wg_data.wgd_name, IfName, sizeof(wg_data.wgd_name)); 392 393 for (;;) { 394 last_size = wg_data.wgd_size; 395 396 if (ioctl(s, SIOCGWG, &wg_data) == -1) { 397 if (errno == EINVAL || errno == ENOTTY) 398 goto out; 399 err(1, "%s: SIOCGWG", wg_data.wgd_name); 400 } 401 402 if (last_size >= wg_data.wgd_size) 403 break; 404 405 wg_interface = calloc(1, wg_data.wgd_size); 406 if (wg_interface == NULL) 407 err(1, "calloc"); 408 free(wg_data.wgd_interface); 409 wg_data.wgd_interface = wg_interface; 410 } 411 412 wg_interface = wg_data.wgd_interface; 413 414 if (wg_interface->i_flags & WG_INTERFACE_HAS_PORT) 415 printf("\twgport: %hu\n", wg_interface->i_port); 416 if (wg_interface->i_flags & WG_INTERFACE_HAS_COOKIE) 417 printf("\twgcookie: %u\n", wg_interface->i_cookie); 418 if (wg_interface->i_flags & WG_INTERFACE_HAS_PRIVATE && printkeys) { 419 b64_ntop(wg_interface->i_private, WG_KEY_SIZE, 420 key, sizeof(key)); 421 printf("\twgkey: %s\n", key); 422 } 423 if (wg_interface->i_flags & WG_INTERFACE_HAS_PUBLIC) { 424 b64_ntop(wg_interface->i_public, WG_KEY_SIZE, 425 key, sizeof(key)); 426 printf("\twgpubkey: %s\n", key); 427 } 428 429 wg_peer = &wg_interface->i_peers[0]; 430 for (i = 0; i < wg_interface->i_peers_count; i++) { 431 b64_ntop(wg_peer->p_public, WG_KEY_SIZE, 432 key, sizeof(key)); 433 printf("\twgpeer: %s\n", key); 434 435 printf("\t\tid: %" PRIu64 "\n", wg_peer->p_id); 436 if (wg_peer->p_description[0] != '\0') 437 printf("\t\twgdescr: %s\n", wg_peer->p_description); 438 if (wg_peer->p_flags & WG_PEER_HAS_PSK) { 439 if (printkeys) { 440 b64_ntop(wg_peer->p_psk, WG_KEY_SIZE, 441 key, sizeof(key)); 442 printf("\t\twgpsk: %s\n", key); 443 } else { 444 printf("\t\twgpsk: (present)\n"); 445 } 446 } 447 if ((wg_peer->p_flags & WG_PEER_HAS_PKA) && wg_peer->p_pka > 0) 448 printf("\t\twgpka: %u (seconds)\n", wg_peer->p_pka); 449 if (wg_peer->p_flags & WG_PEER_HAS_ENDPOINT) { 450 if (getnameinfo(&wg_peer->p_sa, wg_peer->p_sa.sa_len, 451 hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), 452 NI_NUMERICHOST | NI_NUMERICSERV) == 0) 453 printf("\t\twgendpoint: %s %s\n", hbuf, sbuf); 454 else 455 printf("\t\twgendpoint: (unable to print)\n"); 456 } 457 458 printf("\t\ttx: %" PRIu64 " (bytes), rx: %" PRIu64 " (bytes)\n", 459 wg_peer->p_txbytes, wg_peer->p_rxbytes); 460 461 if (wg_peer->p_last_handshake.tv_sec != 0) { 462 timespec_get(&now, TIME_UTC); 463 printf("\t\tlast handshake: %ld seconds ago\n", 464 now.tv_sec - wg_peer->p_last_handshake.tv_sec); 465 } 466 467 for (j = 0; j < wg_peer->p_aips_count; j++) { 468 wg_aip = &wg_peer->p_aips[j]; 469 inet_ntop(wg_aip->a_af, &wg_aip->a_addr, 470 hbuf, sizeof(hbuf)); 471 printf("\t\twgaip: %s/%d\n", hbuf, wg_aip->a_cidr); 472 } 473 474 wg_aip = &wg_peer->p_aips[wg_peer->p_aips_count]; 475 wg_peer = (struct wg_peer_io *)wg_aip; 476 } 477 478 out: 479 free(wg_data.wgd_interface); 480 } 481 482 483 static struct cmd wg_cmds[] = { 484 DEF_CMD_ARG("wgkey", wg_setkey), 485 DEF_CMD_ARG("wgport", wg_setport), 486 DEF_CMD_ARG("wgcookie", wg_setcookie), 487 DEF_CMD("-wgcookie", 0, wg_unsetcookie), 488 DEF_CMD_ARG("wgpeer", wg_setpeer), 489 DEF_CMD_ARG("-wgpeer", wg_unsetpeer), 490 DEF_CMD("-wgpeerall", 0, wg_unsetpeerall), 491 DEF_CMD_ARG("wgaip", wg_setpeeraip), 492 DEF_CMD_ARG("wgpsk", wg_setpeerpsk), 493 DEF_CMD("-wgpsk", 0, wg_unsetpeerpsk), 494 DEF_CMD_ARG("wgpka", wg_setpeerpka), 495 DEF_CMD("-wgpka", 0, wg_unsetpeerpka), 496 DEF_CMD_ARG2("wgendpoint", wg_setpeerendpoint), 497 DEF_CMD_ARG("wgdescr", wg_setpeerdesc), 498 DEF_CMD_ARG("wgdescription", wg_setpeerdesc), 499 DEF_CMD("-wgdescr", 0, wg_unsetpeerdesc), 500 DEF_CMD("-wgdescription", 0, wg_unsetpeerdesc), 501 }; 502 503 static struct afswtch af_wg = { 504 .af_name = "af_wg", /* dummy */ 505 .af_af = AF_UNSPEC, 506 .af_other_status = wg_status, 507 }; 508 509 __constructor(143) 510 static void 511 wg_ctor(void) 512 { 513 size_t i; 514 515 for (i = 0; i < nitems(wg_cmds); i++) 516 cmd_register(&wg_cmds[i]); 517 518 af_register(&af_wg); 519 } 520