xref: /dflybsd-src/sbin/ifconfig/ifwg.c (revision ab4c55c707dde5384d8a233485cc87b3ea249687)
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