ifconfig(8): Show wg(4) private key and PSK if '-k' specified
[dragonfly.git] / sbin / ifconfig / ifwg.c
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 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];
181         else
182                 wg_peer = (struct wg_peer_io *)wg_aip;
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 static void
212 wg_setpeeraip(const char *aip, int arg __unused, int s __unused,
213               const struct afswtch *afp __unused)
214 {
215         int res;
216
217         if (wg_peer == NULL)
218                 errx(1, "wgaip: wgpeer not set");
219
220         wg_data_grow(sizeof(*wg_aip));
221
222         if ((res = inet_net_pton(AF_INET, aip, &wg_aip->a_ipv4,
223                                  sizeof(wg_aip->a_ipv4))) != -1) {
224                 wg_aip->a_af = AF_INET;
225         } else if ((res = inet_net_pton(AF_INET6, aip, &wg_aip->a_ipv6,
226                                         sizeof(wg_aip->a_ipv6))) != -1) {
227                 wg_aip->a_af = AF_INET6;
228         } else {
229                 errx(1, "wgaip: bad address: %s", aip);
230         }
231         wg_aip->a_cidr = res;
232
233         wg_peer->p_flags |= WG_PEER_REPLACE_AIPS;
234         wg_peer->p_aips_count++;
235
236         wg_aip++;
237
238         WG_REGISTER_CALLBACK();
239 }
240
241 static void
242 wg_setpeerpsk(const char *psk, int arg __unused, int s __unused,
243               const struct afswtch *afp __unused)
244 {
245         if (wg_peer == NULL)
246                 errx(1, "wgpsk: wgpeer not set");
247
248         if (b64_pton(psk, wg_peer->p_psk, WG_KEY_SIZE) != WG_KEY_SIZE)
249                 errx(1, "wgpsk: invalid key: %s", psk);
250         wg_peer->p_flags |= WG_PEER_HAS_PSK;
251
252         WG_REGISTER_CALLBACK();
253 }
254
255 static void
256 wg_unsetpeerpsk(const char *x __unused, int arg __unused, int s __unused,
257                 const struct afswtch *afp __unused)
258 {
259         if (wg_peer == NULL)
260                 errx(1, "-wgpsk: wgpeer not set");
261
262         /* Unset PSK by setting it to empty. */
263         memset(wg_peer->p_psk, 0, sizeof(wg_peer->p_psk));
264         wg_peer->p_flags |= WG_PEER_HAS_PSK;
265
266         WG_REGISTER_CALLBACK();
267 }
268
269 static void
270 wg_setpeerpka(const char *pka, int arg __unused, int s __unused,
271               const struct afswtch *afp __unused)
272 {
273         const char *errmsg = NULL;
274
275         if (wg_peer == NULL)
276                 errx(1, "wgpka: wgpeer not set");
277
278         /* 43200 seconds == 12h, reasonable for a uint16_t value */
279         wg_peer->p_pka = (uint16_t)strtonum(pka, 0, 43200, &errmsg);
280         if (errmsg != NULL)
281                 errx(1, "wgpka: invalid pka %s: %s", pka, errmsg);
282         wg_peer->p_flags |= WG_PEER_HAS_PKA;
283
284         WG_REGISTER_CALLBACK();
285 }
286
287 static void
288 wg_unsetpeerpka(const char *x __unused, int arg __unused, int s __unused,
289                 const struct afswtch *afp __unused)
290 {
291         if (wg_peer == NULL)
292                 errx(1, "wgpka: wgpeer not set");
293
294         wg_peer->p_pka = 0;
295         wg_peer->p_flags |= WG_PEER_HAS_PKA;
296
297         WG_REGISTER_CALLBACK();
298 }
299
300 static void
301 wg_setpeerep(const char *host, const char *service, int s __unused,
302              const struct afswtch *afp __unused)
303 {
304         struct addrinfo *ai;
305         int error;
306
307         if (wg_peer == NULL)
308                 errx(1, "wgendpoint: wgpeer not set");
309
310         if ((error = getaddrinfo(host, service, NULL, &ai)) != 0)
311                 errx(1, "%s", gai_strerror(error));
312
313         memcpy(&wg_peer->p_sa, ai->ai_addr, ai->ai_addrlen);
314         wg_peer->p_flags |= WG_PEER_HAS_ENDPOINT;
315
316         freeaddrinfo(ai);
317         WG_REGISTER_CALLBACK();
318 }
319
320 static void
321 wg_setpeerdesc(const char *desc, int arg __unused, int s __unused,
322                const struct afswtch *afp __unused)
323 {
324         if (wg_peer == NULL)
325                 errx(1, "wgdescr: wgpeer not set");
326
327         strlcpy(wg_peer->p_description, desc, sizeof(wg_peer->p_description));
328         wg_peer->p_flags |= WG_PEER_SET_DESCRIPTION;
329
330         WG_REGISTER_CALLBACK();
331 }
332
333 static void
334 wg_unsetpeerdesc(const char *x __unused, int arg __unused, int s __unused,
335                  const struct afswtch *afp __unused)
336 {
337         if (wg_peer == NULL)
338                 errx(1, "-wgpsk: wgpeer not set");
339
340         memset(wg_peer->p_description, 0, sizeof(wg_peer->p_description));
341         wg_peer->p_flags |= WG_PEER_SET_DESCRIPTION;
342
343         WG_REGISTER_CALLBACK();
344 }
345
346
347 static void
348 wg_status(int s)
349 {
350         struct timespec now;
351         size_t i, j, last_size;
352         char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
353         char key[WG_BASE64_KEY_LEN + 1];
354
355         memset(&wg_data, 0, sizeof(wg_data));
356         strlcpy(wg_data.wgd_name, IfName, sizeof(wg_data.wgd_name));
357
358         for (;;) {
359                 last_size = wg_data.wgd_size;
360
361                 if (ioctl(s, SIOCGWG, &wg_data) == -1) {
362                         if (errno == EINVAL || errno == ENOTTY)
363                                 goto out;
364                         err(1, "%s: SIOCGWG", wg_data.wgd_name);
365                 }
366
367                 if (last_size >= wg_data.wgd_size)
368                         break;
369
370                 wg_interface = calloc(1, wg_data.wgd_size);
371                 if (wg_interface == NULL)
372                         err(1, "calloc");
373                 free(wg_data.wgd_interface);
374                 wg_data.wgd_interface = wg_interface;
375         }
376
377         wg_interface = wg_data.wgd_interface;
378
379         if (wg_interface->i_flags & WG_INTERFACE_HAS_PORT)
380                 printf("\twgport: %hu\n", wg_interface->i_port);
381         if (wg_interface->i_flags & WG_INTERFACE_HAS_COOKIE)
382                 printf("\twgcookie: %u\n", wg_interface->i_cookie);
383         if (wg_interface->i_flags & WG_INTERFACE_HAS_PRIVATE && printkeys) {
384                 b64_ntop(wg_interface->i_private, WG_KEY_SIZE,
385                          key, sizeof(key));
386                 printf("\twgkey: %s\n", key);
387         }
388         if (wg_interface->i_flags & WG_INTERFACE_HAS_PUBLIC) {
389                 b64_ntop(wg_interface->i_public, WG_KEY_SIZE,
390                          key, sizeof(key));
391                 printf("\twgpubkey: %s\n", key);
392         }
393
394         wg_peer = &wg_interface->i_peers[0];
395         for (i = 0; i < wg_interface->i_peers_count; i++) {
396                 b64_ntop(wg_peer->p_public, WG_KEY_SIZE,
397                          key, sizeof(key));
398                 printf("\twgpeer: %s\n", key);
399
400                 if (wg_peer->p_description[0] != '\0')
401                         printf("\t\twgdescr: %s\n", wg_peer->p_description);
402                 if (wg_peer->p_flags & WG_PEER_HAS_PSK) {
403                         if (printkeys) {
404                                 b64_ntop(wg_peer->p_psk, WG_KEY_SIZE,
405                                          key, sizeof(key));
406                                 printf("\t\twgpsk: %s\n", key);
407                         } else {
408                                 printf("\t\twgpsk: (present)\n");
409                         }
410                 }
411                 if ((wg_peer->p_flags & WG_PEER_HAS_PKA) && wg_peer->p_pka > 0)
412                         printf("\t\twgpka: %u (seconds)\n", wg_peer->p_pka);
413                 if (wg_peer->p_flags & WG_PEER_HAS_ENDPOINT) {
414                         if (getnameinfo(&wg_peer->p_sa, wg_peer->p_sa.sa_len,
415                                         hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
416                                         NI_NUMERICHOST | NI_NUMERICSERV) == 0)
417                                 printf("\t\twgendpoint: %s %s\n", hbuf, sbuf);
418                         else
419                                 printf("\t\twgendpoint: (unable to print)\n");
420                 }
421
422                 printf("\t\ttx: %" PRIu64 " (bytes), rx: %" PRIu64 " (bytes)\n",
423                        wg_peer->p_txbytes, wg_peer->p_rxbytes);
424
425                 if (wg_peer->p_last_handshake.tv_sec != 0) {
426                         timespec_get(&now, TIME_UTC);
427                         printf("\t\tlast handshake: %ld seconds ago\n",
428                                now.tv_sec - wg_peer->p_last_handshake.tv_sec);
429                 }
430
431                 for (j = 0; j < wg_peer->p_aips_count; j++) {
432                         wg_aip = &wg_peer->p_aips[j];
433                         inet_ntop(wg_aip->a_af, &wg_aip->a_addr,
434                                   hbuf, sizeof(hbuf));
435                         printf("\t\twgaip: %s/%d\n", hbuf, wg_aip->a_cidr);
436                 }
437
438                 wg_aip = &wg_peer->p_aips[wg_peer->p_aips_count];
439                 wg_peer = (struct wg_peer_io *)wg_aip;
440         }
441
442 out:
443         free(wg_data.wgd_interface);
444 }
445
446
447 static struct cmd wg_cmds[] = {
448         DEF_CMD_ARG("wgkey",                    wg_setkey),
449         DEF_CMD_ARG("wgport",                   wg_setport),
450         DEF_CMD_ARG("wgcookie",                 wg_setcookie),
451         DEF_CMD("-wgcookie",            0,      wg_unsetcookie),
452         DEF_CMD_ARG("wgpeer",                   wg_setpeer),
453         DEF_CMD_ARG("-wgpeer",                  wg_unsetpeer),
454         DEF_CMD("-wgpeerall",           0,      wg_unsetpeerall),
455         DEF_CMD_ARG("wgaip",                    wg_setpeeraip),
456         DEF_CMD_ARG("wgpsk",                    wg_setpeerpsk),
457         DEF_CMD("-wgpsk",               0,      wg_unsetpeerpsk),
458         DEF_CMD_ARG("wgpka",                    wg_setpeerpka),
459         DEF_CMD("-wgpka",               0,      wg_unsetpeerpka),
460         DEF_CMD_ARG2("wgendpoint",              wg_setpeerep),
461         DEF_CMD_ARG("wgdescr",                  wg_setpeerdesc),
462         DEF_CMD_ARG("wgdescription",            wg_setpeerdesc),
463         DEF_CMD("-wgdescr",             0,      wg_unsetpeerdesc),
464         DEF_CMD("-wgdescription",       0,      wg_unsetpeerdesc),
465 };
466
467 static struct afswtch af_wg = {
468         .af_name                = "af_wg", /* dummy */
469         .af_af                  = AF_UNSPEC,
470         .af_other_status        = wg_status,
471 };
472
473 __constructor(143)
474 static void
475 wg_ctor(void)
476 {
477         size_t i;
478
479         for (i = 0; i < nitems(wg_cmds); i++)
480                 cmd_register(&wg_cmds[i]);
481
482         af_register(&af_wg);
483 }