]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/netdev/wireguard.c
networkd/wireguard: support network.wireguard.* credentials
[thirdparty/systemd.git] / src / network / netdev / wireguard.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3 Copyright © 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4 ***/
5
6 #include <sys/ioctl.h>
7 #include <net/if.h>
8 #include <netinet/in.h>
9 #include <linux/if_arp.h>
10 #include <linux/ipv6_route.h>
11
12 #include "sd-resolve.h"
13
14 #include "alloc-util.h"
15 #include "creds-util.h"
16 #include "dns-domain.h"
17 #include "event-util.h"
18 #include "fd-util.h"
19 #include "fileio.h"
20 #include "hexdecoct.h"
21 #include "memory-util.h"
22 #include "netlink-util.h"
23 #include "networkd-manager.h"
24 #include "networkd-route-util.h"
25 #include "networkd-route.h"
26 #include "networkd-util.h"
27 #include "parse-helpers.h"
28 #include "parse-util.h"
29 #include "path-util.h"
30 #include "random-util.h"
31 #include "resolve-private.h"
32 #include "string-util.h"
33 #include "strv.h"
34 #include "wireguard.h"
35
36 static void wireguard_resolve_endpoints(NetDev *netdev);
37 static int peer_resolve_endpoint(WireguardPeer *peer);
38
39 static void wireguard_peer_clear_ipmasks(WireguardPeer *peer) {
40 assert(peer);
41
42 LIST_CLEAR(ipmasks, peer->ipmasks, free);
43 }
44
45 static WireguardPeer* wireguard_peer_free(WireguardPeer *peer) {
46 if (!peer)
47 return NULL;
48
49 if (peer->wireguard) {
50 LIST_REMOVE(peers, peer->wireguard->peers, peer);
51
52 if (peer->section)
53 hashmap_remove(peer->wireguard->peers_by_section, peer->section);
54 }
55
56 config_section_free(peer->section);
57
58 wireguard_peer_clear_ipmasks(peer);
59
60 free(peer->endpoint_host);
61 free(peer->endpoint_port);
62 free(peer->preshared_key_file);
63 explicit_bzero_safe(peer->preshared_key, WG_KEY_LEN);
64
65 sd_event_source_disable_unref(peer->resolve_retry_event_source);
66 sd_resolve_query_unref(peer->resolve_query);
67
68 return mfree(peer);
69 }
70
71 DEFINE_SECTION_CLEANUP_FUNCTIONS(WireguardPeer, wireguard_peer_free);
72
73 static int wireguard_peer_new_static(Wireguard *w, const char *filename, unsigned section_line, WireguardPeer **ret) {
74 _cleanup_(config_section_freep) ConfigSection *n = NULL;
75 _cleanup_(wireguard_peer_freep) WireguardPeer *peer = NULL;
76 int r;
77
78 assert(w);
79 assert(ret);
80 assert(filename);
81 assert(section_line > 0);
82
83 r = config_section_new(filename, section_line, &n);
84 if (r < 0)
85 return r;
86
87 peer = hashmap_get(w->peers_by_section, n);
88 if (peer) {
89 *ret = TAKE_PTR(peer);
90 return 0;
91 }
92
93 peer = new(WireguardPeer, 1);
94 if (!peer)
95 return -ENOMEM;
96
97 *peer = (WireguardPeer) {
98 .flags = WGPEER_F_REPLACE_ALLOWEDIPS,
99 .wireguard = w,
100 .section = TAKE_PTR(n),
101 };
102
103 LIST_PREPEND(peers, w->peers, peer);
104
105 r = hashmap_ensure_put(&w->peers_by_section, &config_section_hash_ops, peer->section, peer);
106 if (r < 0)
107 return r;
108
109 *ret = TAKE_PTR(peer);
110 return 0;
111 }
112
113 static int wireguard_set_ipmask_one(NetDev *netdev, sd_netlink_message *message, const WireguardIPmask *mask, uint16_t index) {
114 int r;
115
116 assert(message);
117 assert(mask);
118 assert(index > 0);
119
120 /* This returns 1 on success, 0 on recoverable error, and negative errno on failure. */
121
122 r = sd_netlink_message_open_array(message, index);
123 if (r < 0)
124 return 0;
125
126 r = sd_netlink_message_append_u16(message, WGALLOWEDIP_A_FAMILY, mask->family);
127 if (r < 0)
128 goto cancel;
129
130 r = netlink_message_append_in_addr_union(message, WGALLOWEDIP_A_IPADDR, mask->family, &mask->ip);
131 if (r < 0)
132 goto cancel;
133
134 r = sd_netlink_message_append_u8(message, WGALLOWEDIP_A_CIDR_MASK, mask->cidr);
135 if (r < 0)
136 goto cancel;
137
138 r = sd_netlink_message_close_container(message);
139 if (r < 0)
140 return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
141
142 return 1;
143
144 cancel:
145 r = sd_netlink_message_cancel_array(message);
146 if (r < 0)
147 return log_netdev_error_errno(netdev, r, "Could not cancel wireguard allowed ip message attribute: %m");
148
149 return 0;
150 }
151
152 static int wireguard_set_peer_one(NetDev *netdev, sd_netlink_message *message, const WireguardPeer *peer, uint16_t index, WireguardIPmask **mask_start) {
153 WireguardIPmask *start, *last = NULL;
154 uint16_t j = 0;
155 int r;
156
157 assert(message);
158 assert(peer);
159 assert(index > 0);
160 assert(mask_start);
161
162 /* This returns 1 on success, 0 on recoverable error, and negative errno on failure. */
163
164 start = *mask_start ?: peer->ipmasks;
165
166 r = sd_netlink_message_open_array(message, index);
167 if (r < 0)
168 return 0;
169
170 r = sd_netlink_message_append_data(message, WGPEER_A_PUBLIC_KEY, &peer->public_key, sizeof(peer->public_key));
171 if (r < 0)
172 goto cancel;
173
174 if (!*mask_start) {
175 r = sd_netlink_message_append_data(message, WGPEER_A_PRESHARED_KEY, &peer->preshared_key, WG_KEY_LEN);
176 if (r < 0)
177 goto cancel;
178
179 r = sd_netlink_message_append_u32(message, WGPEER_A_FLAGS, peer->flags);
180 if (r < 0)
181 goto cancel;
182
183 r = sd_netlink_message_append_u16(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval);
184 if (r < 0)
185 goto cancel;
186
187 if (IN_SET(peer->endpoint.sa.sa_family, AF_INET, AF_INET6)) {
188 r = netlink_message_append_sockaddr_union(message, WGPEER_A_ENDPOINT, &peer->endpoint);
189 if (r < 0)
190 goto cancel;
191 }
192 }
193
194 r = sd_netlink_message_open_container(message, WGPEER_A_ALLOWEDIPS);
195 if (r < 0)
196 goto cancel;
197
198 LIST_FOREACH(ipmasks, mask, start) {
199 r = wireguard_set_ipmask_one(netdev, message, mask, ++j);
200 if (r < 0)
201 return r;
202 if (r == 0) {
203 last = mask;
204 break;
205 }
206 }
207
208 r = sd_netlink_message_close_container(message);
209 if (r < 0)
210 return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
211
212 r = sd_netlink_message_close_container(message);
213 if (r < 0)
214 return log_netdev_error_errno(netdev, r, "Could not add wireguard peer: %m");
215
216 *mask_start = last; /* Start next cycle from this mask. */
217 return !last;
218
219 cancel:
220 r = sd_netlink_message_cancel_array(message);
221 if (r < 0)
222 return log_netdev_error_errno(netdev, r, "Could not cancel wireguard peers: %m");
223
224 return 0;
225 }
226
227 static int wireguard_set_interface(NetDev *netdev) {
228 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
229 WireguardIPmask *mask_start = NULL;
230 bool sent_once = false;
231 uint32_t serial;
232 Wireguard *w = WIREGUARD(netdev);
233 int r;
234
235 for (WireguardPeer *peer_start = w->peers; peer_start || !sent_once; ) {
236 uint16_t i = 0;
237
238 message = sd_netlink_message_unref(message);
239
240 r = sd_genl_message_new(netdev->manager->genl, WG_GENL_NAME, WG_CMD_SET_DEVICE, &message);
241 if (r < 0)
242 return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
243
244 r = sd_netlink_message_append_string(message, WGDEVICE_A_IFNAME, netdev->ifname);
245 if (r < 0)
246 return log_netdev_error_errno(netdev, r, "Could not append wireguard interface name: %m");
247
248 if (peer_start == w->peers) {
249 r = sd_netlink_message_append_data(message, WGDEVICE_A_PRIVATE_KEY, &w->private_key, WG_KEY_LEN);
250 if (r < 0)
251 return log_netdev_error_errno(netdev, r, "Could not append wireguard private key: %m");
252
253 r = sd_netlink_message_append_u16(message, WGDEVICE_A_LISTEN_PORT, w->port);
254 if (r < 0)
255 return log_netdev_error_errno(netdev, r, "Could not append wireguard port: %m");
256
257 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FWMARK, w->fwmark);
258 if (r < 0)
259 return log_netdev_error_errno(netdev, r, "Could not append wireguard fwmark: %m");
260
261 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FLAGS, w->flags);
262 if (r < 0)
263 return log_netdev_error_errno(netdev, r, "Could not append wireguard flags: %m");
264 }
265
266 r = sd_netlink_message_open_container(message, WGDEVICE_A_PEERS);
267 if (r < 0)
268 return log_netdev_error_errno(netdev, r, "Could not append wireguard peer attributes: %m");
269
270 WireguardPeer *peer_last = NULL;
271 LIST_FOREACH(peers, peer, peer_start) {
272 r = wireguard_set_peer_one(netdev, message, peer, ++i, &mask_start);
273 if (r < 0)
274 return r;
275 if (r == 0) {
276 peer_last = peer;
277 break;
278 }
279 }
280 peer_start = peer_last; /* Start next cycle from this peer. */
281
282 r = sd_netlink_message_close_container(message);
283 if (r < 0)
284 return log_netdev_error_errno(netdev, r, "Could not close wireguard container: %m");
285
286 r = sd_netlink_send(netdev->manager->genl, message, &serial);
287 if (r < 0)
288 return log_netdev_error_errno(netdev, r, "Could not set wireguard device: %m");
289
290 sent_once = true;
291 }
292
293 return 0;
294 }
295
296 static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) {
297 WireguardPeer *peer = ASSERT_PTR(userdata);
298 NetDev *netdev;
299
300 assert(peer->wireguard);
301
302 netdev = NETDEV(peer->wireguard);
303
304 if (!netdev_is_managed(netdev))
305 return 0;
306
307 peer->resolve_query = sd_resolve_query_unref(peer->resolve_query);
308
309 (void) peer_resolve_endpoint(peer);
310 return 0;
311 }
312
313 static usec_t peer_next_resolve_usec(WireguardPeer *peer) {
314 usec_t usec;
315
316 /* Given the number of retries this function will return an exponential increasing amount of
317 * milliseconds to wait starting at 200ms and capped at 25 seconds. */
318
319 assert(peer);
320
321 usec = (2 << MIN(peer->n_retries, 7U)) * 100 * USEC_PER_MSEC;
322
323 return random_u64_range(usec / 10) + usec * 9 / 10;
324 }
325
326 static int wireguard_peer_resolve_handler(
327 sd_resolve_query *q,
328 int ret,
329 const struct addrinfo *ai,
330 void *userdata) {
331
332 WireguardPeer *peer = ASSERT_PTR(userdata);
333 NetDev *netdev;
334 int r;
335
336 assert(peer->wireguard);
337
338 netdev = NETDEV(peer->wireguard);
339
340 if (!netdev_is_managed(netdev))
341 return 0;
342
343 if (ret != 0) {
344 log_netdev_warning(netdev, "Failed to resolve host '%s:%s', ignoring: %s",
345 peer->endpoint_host, peer->endpoint_port, gai_strerror(ret));
346 peer->n_retries++;
347
348 } else {
349 bool found = false;
350 for (; ai; ai = ai->ai_next) {
351 if (!IN_SET(ai->ai_family, AF_INET, AF_INET6))
352 continue;
353
354 if (ai->ai_addrlen != (ai->ai_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)))
355 continue;
356
357 memcpy(&peer->endpoint, ai->ai_addr, ai->ai_addrlen);
358 (void) wireguard_set_interface(netdev);
359 peer->n_retries = 0;
360 found = true;
361 break;
362 }
363
364 if (!found) {
365 log_netdev_warning(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint %s:%s, ignoring the endpoint.",
366 peer->endpoint_host, peer->endpoint_port);
367 peer->n_retries++;
368 }
369 }
370
371 if (peer->n_retries > 0) {
372 r = event_reset_time_relative(netdev->manager->event,
373 &peer->resolve_retry_event_source,
374 CLOCK_BOOTTIME,
375 peer_next_resolve_usec(peer), 0,
376 on_resolve_retry, peer, 0, "wireguard-resolve-retry", true);
377 if (r < 0)
378 log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler for endpoint %s:%s, ignoring: %m",
379 peer->endpoint_host, peer->endpoint_port);
380 }
381
382 wireguard_resolve_endpoints(netdev);
383 return 0;
384 }
385
386 static int peer_resolve_endpoint(WireguardPeer *peer) {
387 static const struct addrinfo hints = {
388 .ai_family = AF_UNSPEC,
389 .ai_socktype = SOCK_DGRAM,
390 .ai_protocol = IPPROTO_UDP
391 };
392 NetDev *netdev;
393 int r;
394
395 assert(peer);
396 assert(peer->wireguard);
397
398 netdev = NETDEV(peer->wireguard);
399
400 if (!peer->endpoint_host || !peer->endpoint_port)
401 /* Not necessary to resolve the endpoint. */
402 return 0;
403
404 if (sd_event_source_get_enabled(peer->resolve_retry_event_source, NULL) > 0)
405 /* Timer event source is enabled. The endpoint will be resolved later. */
406 return 0;
407
408 if (peer->resolve_query)
409 /* Being resolved, or already resolved. */
410 return 0;
411
412 r = sd_resolve_getaddrinfo(netdev->manager->resolve,
413 &peer->resolve_query,
414 peer->endpoint_host,
415 peer->endpoint_port,
416 &hints,
417 wireguard_peer_resolve_handler,
418 peer);
419 if (r < 0)
420 return log_netdev_full_errno(netdev, r == -ENOBUFS ? LOG_DEBUG : LOG_WARNING, r,
421 "Failed to create endpoint resolver for %s:%s, ignoring: %m",
422 peer->endpoint_host, peer->endpoint_port);
423
424 return 0;
425 }
426
427 static void wireguard_resolve_endpoints(NetDev *netdev) {
428 Wireguard *w = WIREGUARD(netdev);
429
430 LIST_FOREACH(peers, peer, w->peers)
431 if (peer_resolve_endpoint(peer) == -ENOBUFS)
432 /* Too many requests. Let's resolve remaining endpoints later. */
433 break;
434 }
435
436 static int netdev_wireguard_post_create(NetDev *netdev, Link *link) {
437 assert(WIREGUARD(netdev));
438
439 (void) wireguard_set_interface(netdev);
440 wireguard_resolve_endpoints(netdev);
441 return 0;
442 }
443
444 int config_parse_wireguard_listen_port(
445 const char *unit,
446 const char *filename,
447 unsigned line,
448 const char *section,
449 unsigned section_line,
450 const char *lvalue,
451 int ltype,
452 const char *rvalue,
453 void *data,
454 void *userdata) {
455
456 uint16_t *s = ASSERT_PTR(data);
457 int r;
458
459 assert(rvalue);
460
461 if (isempty(rvalue) || streq(rvalue, "auto")) {
462 *s = 0;
463 return 0;
464 }
465
466 r = parse_ip_port(rvalue, s);
467 if (r < 0) {
468 log_syntax(unit, LOG_WARNING, filename, line, r,
469 "Invalid port specification, ignoring assignment: %s", rvalue);
470 return 0;
471 }
472
473 return 0;
474 }
475
476 static int wireguard_decode_key_and_warn(
477 const char *rvalue,
478 uint8_t ret[static WG_KEY_LEN],
479 const char *unit,
480 const char *filename,
481 unsigned line,
482 const char *lvalue) {
483
484 _cleanup_(erase_and_freep) void *key = NULL;
485 _cleanup_(erase_and_freep) char *cred = NULL;
486 const char *cred_name;
487 size_t len;
488 int r;
489
490 assert(rvalue);
491 assert(ret);
492 assert(filename);
493 assert(lvalue);
494
495 if (isempty(rvalue)) {
496 memzero(ret, WG_KEY_LEN);
497 return 0;
498 }
499
500 cred_name = startswith(rvalue, "@");
501 if (cred_name) {
502 r = read_credential(cred_name, (void**) &cred, /* ret_size = */ NULL);
503 if (r == -ENOMEM)
504 return log_oom();
505 if (r < 0) {
506 log_syntax(unit, LOG_WARNING, filename, line, r,
507 "Failed to read credential for wireguard key (%s=), ignoring assignment: %m",
508 lvalue);
509 return 0;
510 }
511
512 } else if (!streq(lvalue, "PublicKey"))
513 (void) warn_file_is_world_accessible(filename, NULL, unit, line);
514
515 r = unbase64mem_full(cred ?: rvalue, SIZE_MAX, /* secure = */ true, &key, &len);
516 if (r == -ENOMEM)
517 return log_oom();
518 if (r < 0) {
519 log_syntax(unit, LOG_WARNING, filename, line, r,
520 "Failed to decode wireguard key provided by %s=, ignoring assignment: %m", lvalue);
521 return 0;
522 }
523 if (len != WG_KEY_LEN) {
524 log_syntax(unit, LOG_WARNING, filename, line, 0,
525 "Wireguard key provided by %s= has invalid length (%zu bytes), ignoring assignment.",
526 lvalue, len);
527 return 0;
528 }
529
530 memcpy(ret, key, WG_KEY_LEN);
531 return 0;
532 }
533
534 int config_parse_wireguard_private_key(
535 const char *unit,
536 const char *filename,
537 unsigned line,
538 const char *section,
539 unsigned section_line,
540 const char *lvalue,
541 int ltype,
542 const char *rvalue,
543 void *data,
544 void *userdata) {
545
546 Wireguard *w = WIREGUARD(data);
547
548 return wireguard_decode_key_and_warn(rvalue, w->private_key, unit, filename, line, lvalue);
549 }
550
551 int config_parse_wireguard_private_key_file(
552 const char *unit,
553 const char *filename,
554 unsigned line,
555 const char *section,
556 unsigned section_line,
557 const char *lvalue,
558 int ltype,
559 const char *rvalue,
560 void *data,
561 void *userdata) {
562
563 Wireguard *w = WIREGUARD(data);
564 _cleanup_free_ char *path = NULL;
565
566 if (isempty(rvalue)) {
567 w->private_key_file = mfree(w->private_key_file);
568 return 0;
569 }
570
571 path = strdup(rvalue);
572 if (!path)
573 return log_oom();
574
575 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
576 return 0;
577
578 return free_and_replace(w->private_key_file, path);
579 }
580
581 int config_parse_wireguard_peer_key(
582 const char *unit,
583 const char *filename,
584 unsigned line,
585 const char *section,
586 unsigned section_line,
587 const char *lvalue,
588 int ltype,
589 const char *rvalue,
590 void *data,
591 void *userdata) {
592
593 Wireguard *w = WIREGUARD(data);
594 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
595 int r;
596
597 r = wireguard_peer_new_static(w, filename, section_line, &peer);
598 if (r < 0)
599 return log_oom();
600
601 r = wireguard_decode_key_and_warn(rvalue,
602 streq(lvalue, "PublicKey") ? peer->public_key : peer->preshared_key,
603 unit, filename, line, lvalue);
604 if (r < 0)
605 return r;
606
607 TAKE_PTR(peer);
608 return 0;
609 }
610
611 int config_parse_wireguard_preshared_key_file(
612 const char *unit,
613 const char *filename,
614 unsigned line,
615 const char *section,
616 unsigned section_line,
617 const char *lvalue,
618 int ltype,
619 const char *rvalue,
620 void *data,
621 void *userdata) {
622
623 Wireguard *w = WIREGUARD(data);
624 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
625 _cleanup_free_ char *path = NULL;
626 int r;
627
628 r = wireguard_peer_new_static(w, filename, section_line, &peer);
629 if (r < 0)
630 return log_oom();
631
632 if (isempty(rvalue)) {
633 peer->preshared_key_file = mfree(peer->preshared_key_file);
634 TAKE_PTR(peer);
635 return 0;
636 }
637
638 path = strdup(rvalue);
639 if (!path)
640 return log_oom();
641
642 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
643 return 0;
644
645 free_and_replace(peer->preshared_key_file, path);
646 TAKE_PTR(peer);
647 return 0;
648 }
649
650 int config_parse_wireguard_allowed_ips(
651 const char *unit,
652 const char *filename,
653 unsigned line,
654 const char *section,
655 unsigned section_line,
656 const char *lvalue,
657 int ltype,
658 const char *rvalue,
659 void *data,
660 void *userdata) {
661
662 assert(rvalue);
663
664 Wireguard *w = WIREGUARD(data);
665 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
666 union in_addr_union addr;
667 unsigned char prefixlen;
668 int r, family;
669 WireguardIPmask *ipmask;
670
671 r = wireguard_peer_new_static(w, filename, section_line, &peer);
672 if (r < 0)
673 return log_oom();
674
675 if (isempty(rvalue)) {
676 wireguard_peer_clear_ipmasks(peer);
677 TAKE_PTR(peer);
678 return 0;
679 }
680
681 for (const char *p = rvalue;;) {
682 _cleanup_free_ char *word = NULL;
683 union in_addr_union masked;
684
685 r = extract_first_word(&p, &word, "," WHITESPACE, 0);
686 if (r == 0)
687 break;
688 if (r == -ENOMEM)
689 return log_oom();
690 if (r < 0) {
691 log_syntax(unit, LOG_WARNING, filename, line, r,
692 "Failed to split allowed ips \"%s\" option: %m", rvalue);
693 break;
694 }
695
696 r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen);
697 if (r < 0) {
698 log_syntax(unit, LOG_WARNING, filename, line, r,
699 "Network address is invalid, ignoring assignment: %s", word);
700 continue;
701 }
702
703 masked = addr;
704 assert_se(in_addr_mask(family, &masked, prefixlen) >= 0);
705 if (!in_addr_equal(family, &masked, &addr))
706 log_syntax(unit, LOG_WARNING, filename, line, 0,
707 "Specified address '%s' is not properly masked, assuming '%s'.",
708 word,
709 IN_ADDR_PREFIX_TO_STRING(family, &masked, prefixlen));
710
711 ipmask = new(WireguardIPmask, 1);
712 if (!ipmask)
713 return log_oom();
714
715 *ipmask = (WireguardIPmask) {
716 .family = family,
717 .ip = masked,
718 .cidr = prefixlen,
719 };
720
721 LIST_PREPEND(ipmasks, peer->ipmasks, ipmask);
722 }
723
724 TAKE_PTR(peer);
725 return 0;
726 }
727
728 int config_parse_wireguard_endpoint(
729 const char *unit,
730 const char *filename,
731 unsigned line,
732 const char *section,
733 unsigned section_line,
734 const char *lvalue,
735 int ltype,
736 const char *rvalue,
737 void *data,
738 void *userdata) {
739
740 Wireguard *w = WIREGUARD(userdata);
741 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
742 _cleanup_free_ char *cred = NULL;
743 const char *cred_name, *endpoint;
744 uint16_t port;
745 int r;
746
747 assert(filename);
748 assert(rvalue);
749
750 r = wireguard_peer_new_static(w, filename, section_line, &peer);
751 if (r < 0)
752 return log_oom();
753
754 cred_name = startswith(rvalue, "@");
755 if (cred_name) {
756 r = read_credential(cred_name, (void**) &cred, /* ret_size = */ NULL);
757 if (r == -ENOMEM)
758 return log_oom();
759 if (r < 0) {
760 log_syntax(unit, LOG_WARNING, filename, line, r,
761 "Failed to read credential for wireguard endpoint, ignoring assignment: %m");
762 return 0;
763 }
764
765 endpoint = strstrip(cred);
766 } else
767 endpoint = rvalue;
768
769 union in_addr_union addr;
770 int family;
771
772 r = in_addr_port_ifindex_name_from_string_auto(endpoint, &family, &addr, &port, NULL, NULL);
773 if (r >= 0) {
774 if (family == AF_INET)
775 peer->endpoint.in = (struct sockaddr_in) {
776 .sin_family = AF_INET,
777 .sin_addr = addr.in,
778 .sin_port = htobe16(port),
779 };
780 else if (family == AF_INET6)
781 peer->endpoint.in6 = (struct sockaddr_in6) {
782 .sin6_family = AF_INET6,
783 .sin6_addr = addr.in6,
784 .sin6_port = htobe16(port),
785 };
786 else
787 assert_not_reached();
788
789 peer->endpoint_host = mfree(peer->endpoint_host);
790 peer->endpoint_port = mfree(peer->endpoint_port);
791
792 TAKE_PTR(peer);
793 return 0;
794 }
795
796 _cleanup_free_ char *host = NULL;
797 const char *p;
798
799 p = strrchr(endpoint, ':');
800 if (!p) {
801 log_syntax(unit, LOG_WARNING, filename, line, 0,
802 "Unable to find port of endpoint, ignoring assignment: %s",
803 rvalue); /* We log the original assignment instead of resolved credential here,
804 as the latter might be previously encrypted and we'd expose them in
805 unprotected logs otherwise. */
806 return 0;
807 }
808
809 host = strndup(endpoint, p - endpoint);
810 if (!host)
811 return log_oom();
812 p++;
813
814 if (!dns_name_is_valid(host)) {
815 log_syntax(unit, LOG_WARNING, filename, line, 0,
816 "Invalid domain name of endpoint, ignoring assignment: %s",
817 rvalue);
818 return 0;
819 }
820
821 r = parse_ip_port(p, &port);
822 if (r < 0) {
823 log_syntax(unit, LOG_WARNING, filename, line, r,
824 "Invalid port of endpoint, ignoring assignment: %s",
825 rvalue);
826 return 0;
827 }
828
829 peer->endpoint = (union sockaddr_union) {};
830
831 free_and_replace(peer->endpoint_host, host);
832
833 r = free_and_strdup(&peer->endpoint_port, p);
834 if (r < 0)
835 return log_oom();
836
837 TAKE_PTR(peer); /* The peer may already have been in the hash map, that is fine too. */
838 return 0;
839 }
840
841 int config_parse_wireguard_keepalive(
842 const char *unit,
843 const char *filename,
844 unsigned line,
845 const char *section,
846 unsigned section_line,
847 const char *lvalue,
848 int ltype,
849 const char *rvalue,
850 void *data,
851 void *userdata) {
852
853 assert(rvalue);
854
855 Wireguard *w = WIREGUARD(data);
856 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
857 uint16_t keepalive = 0;
858 int r;
859
860 r = wireguard_peer_new_static(w, filename, section_line, &peer);
861 if (r < 0)
862 return log_oom();
863
864 if (streq(rvalue, "off"))
865 keepalive = 0;
866 else {
867 r = safe_atou16(rvalue, &keepalive);
868 if (r < 0) {
869 log_syntax(unit, LOG_WARNING, filename, line, r,
870 "Failed to parse \"%s\" as keepalive interval (range 0–65535), ignoring assignment: %m",
871 rvalue);
872 return 0;
873 }
874 }
875
876 peer->persistent_keepalive_interval = keepalive;
877
878 TAKE_PTR(peer);
879 return 0;
880 }
881
882 int config_parse_wireguard_route_table(
883 const char *unit,
884 const char *filename,
885 unsigned line,
886 const char *section,
887 unsigned section_line,
888 const char *lvalue,
889 int ltype,
890 const char *rvalue,
891 void *data,
892 void *userdata) {
893
894 NetDev *netdev = ASSERT_PTR(userdata);
895 uint32_t *table = ASSERT_PTR(data);
896 int r;
897
898 assert(filename);
899 assert(lvalue);
900 assert(rvalue);
901
902 if (isempty(rvalue) || parse_boolean(rvalue) == 0) {
903 *table = 0; /* Disabled. */
904 return 0;
905 }
906
907 r = manager_get_route_table_from_string(netdev->manager, rvalue, table);
908 if (r < 0) {
909 log_syntax(unit, LOG_WARNING, filename, line, r,
910 "Failed to parse %s=, ignoring assignment: %s",
911 lvalue, rvalue);
912 return 0;
913 }
914
915 return 0;
916 }
917
918 int config_parse_wireguard_peer_route_table(
919 const char *unit,
920 const char *filename,
921 unsigned line,
922 const char *section,
923 unsigned section_line,
924 const char *lvalue,
925 int ltype,
926 const char *rvalue,
927 void *data,
928 void *userdata) {
929
930 Wireguard *w = WIREGUARD(userdata);
931 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
932 int r;
933
934 assert(filename);
935 assert(lvalue);
936 assert(rvalue);
937 assert(NETDEV(w)->manager);
938
939 r = wireguard_peer_new_static(w, filename, section_line, &peer);
940 if (r < 0)
941 return log_oom();
942
943 if (isempty(rvalue)) {
944 peer->route_table_set = false; /* Use the table specified in [WireGuard] section. */
945 TAKE_PTR(peer);
946 return 0;
947 }
948
949 if (parse_boolean(rvalue) == 0) {
950 peer->route_table = 0; /* Disabled. */
951 peer->route_table_set = true;
952 TAKE_PTR(peer);
953 return 0;
954 }
955
956 r = manager_get_route_table_from_string(NETDEV(w)->manager, rvalue, &peer->route_table);
957 if (r < 0) {
958 log_syntax(unit, LOG_WARNING, filename, line, r,
959 "Failed to parse %s=, ignoring assignment: %s",
960 lvalue, rvalue);
961 return 0;
962 }
963
964 peer->route_table_set = true;
965 TAKE_PTR(peer);
966 return 0;
967 }
968
969 int config_parse_wireguard_route_priority(
970 const char *unit,
971 const char *filename,
972 unsigned line,
973 const char *section,
974 unsigned section_line,
975 const char *lvalue,
976 int ltype,
977 const char *rvalue,
978 void *data,
979 void *userdata) {
980
981 uint32_t *priority = ASSERT_PTR(data);
982 int r;
983
984 assert(filename);
985 assert(lvalue);
986 assert(rvalue);
987
988 if (isempty(rvalue)) {
989 *priority = 0;
990 return 0;
991 }
992
993 r = safe_atou32(rvalue, priority);
994 if (r < 0) {
995 log_syntax(unit, LOG_WARNING, filename, line, r,
996 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
997 return 0;
998 }
999
1000 return 0;
1001 }
1002
1003 int config_parse_wireguard_peer_route_priority(
1004 const char *unit,
1005 const char *filename,
1006 unsigned line,
1007 const char *section,
1008 unsigned section_line,
1009 const char *lvalue,
1010 int ltype,
1011 const char *rvalue,
1012 void *data,
1013 void *userdata) {
1014
1015 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
1016 Wireguard *w;
1017 int r;
1018
1019 assert(filename);
1020 assert(lvalue);
1021 assert(rvalue);
1022 assert(userdata);
1023
1024 w = WIREGUARD(userdata);
1025 assert(w);
1026
1027 r = wireguard_peer_new_static(w, filename, section_line, &peer);
1028 if (r < 0)
1029 return log_oom();
1030
1031 if (isempty(rvalue)) {
1032 peer->route_priority_set = false; /* Use the priority specified in [WireGuard] section. */
1033 TAKE_PTR(peer);
1034 return 0;
1035 }
1036
1037 r = safe_atou32(rvalue, &peer->route_priority);
1038 if (r < 0) {
1039 log_syntax(unit, LOG_WARNING, filename, line, r,
1040 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
1041 return 0;
1042 }
1043
1044 peer->route_priority_set = true;
1045 TAKE_PTR(peer);
1046 return 0;
1047 }
1048
1049 static void wireguard_init(NetDev *netdev) {
1050 Wireguard *w = WIREGUARD(netdev);
1051
1052 w->flags = WGDEVICE_F_REPLACE_PEERS;
1053 }
1054
1055 static void wireguard_done(NetDev *netdev) {
1056 Wireguard *w = WIREGUARD(netdev);
1057
1058 explicit_bzero_safe(w->private_key, WG_KEY_LEN);
1059 free(w->private_key_file);
1060
1061 hashmap_free_with_destructor(w->peers_by_section, wireguard_peer_free);
1062
1063 set_free(w->routes);
1064 }
1065
1066 static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_KEY_LEN]) {
1067 _cleanup_(erase_and_freep) char *key = NULL;
1068 size_t key_len;
1069 int r;
1070
1071 if (!filename)
1072 return 0;
1073
1074 assert(dest);
1075
1076 r = read_full_file_full(
1077 AT_FDCWD, filename, UINT64_MAX, WG_KEY_LEN,
1078 READ_FULL_FILE_SECURE |
1079 READ_FULL_FILE_UNBASE64 |
1080 READ_FULL_FILE_WARN_WORLD_READABLE |
1081 READ_FULL_FILE_CONNECT_SOCKET |
1082 READ_FULL_FILE_FAIL_WHEN_LARGER,
1083 NULL, &key, &key_len);
1084 if (r < 0)
1085 return r;
1086
1087 if (key_len != WG_KEY_LEN)
1088 return -EINVAL;
1089
1090 memcpy(dest, key, WG_KEY_LEN);
1091 return 0;
1092 }
1093
1094 static int wireguard_peer_verify(WireguardPeer *peer) {
1095 NetDev *netdev = NETDEV(peer->wireguard);
1096 int r;
1097
1098 if (section_is_invalid(peer->section))
1099 return -EINVAL;
1100
1101 if (eqzero(peer->public_key))
1102 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1103 "%s: WireGuardPeer section without PublicKey= configured. "
1104 "Ignoring [WireGuardPeer] section from line %u.",
1105 peer->section->filename, peer->section->line);
1106
1107 r = wireguard_read_key_file(peer->preshared_key_file, peer->preshared_key);
1108 if (r < 0)
1109 return log_netdev_error_errno(netdev, r,
1110 "%s: Failed to read preshared key from '%s'. "
1111 "Ignoring [WireGuardPeer] section from line %u.",
1112 peer->section->filename, peer->preshared_key_file,
1113 peer->section->line);
1114
1115 return 0;
1116 }
1117
1118 static int wireguard_read_default_key_cred(NetDev *netdev, const char *filename) {
1119 Wireguard *w = WIREGUARD(netdev);
1120 _cleanup_free_ char *config_name = NULL;
1121 int r;
1122
1123 assert(filename);
1124
1125 r = path_extract_filename(filename, &config_name);
1126 if (r < 0)
1127 return log_netdev_error_errno(netdev, r,
1128 "%s: Failed to extract config name, ignoring network device: %m",
1129 filename);
1130
1131 char *p = endswith(config_name, ".netdev");
1132 if (!p)
1133 /* Fuzzer run? Then we just ignore this device. */
1134 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1135 "%s: Invalid netdev config name, refusing default key lookup.",
1136 filename);
1137 *p = '\0';
1138
1139 _cleanup_(erase_and_freep) char *cred = NULL;
1140
1141 r = read_credential(strjoina("network.wireguard.private.", config_name), (void**) &cred, /* ret_size = */ NULL);
1142 if (r < 0)
1143 return log_netdev_error_errno(netdev, r,
1144 "%s: No private key specified and default key isn't available, "
1145 "ignoring network device: %m",
1146 filename);
1147
1148 _cleanup_(erase_and_freep) void *key = NULL;
1149 size_t len;
1150
1151 r = unbase64mem_full(cred, SIZE_MAX, /* secure = */ true, &key, &len);
1152 if (r < 0)
1153 return log_netdev_error_errno(netdev, r,
1154 "%s: No private key specified and default key cannot be parsed, "
1155 "ignoring network device: %m",
1156 filename);
1157 if (len != WG_KEY_LEN)
1158 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1159 "%s: No private key specified and default key is invalid. "
1160 "Ignoring network device.",
1161 filename);
1162
1163 memcpy(w->private_key, key, WG_KEY_LEN);
1164 return 0;
1165 }
1166
1167 static int wireguard_verify(NetDev *netdev, const char *filename) {
1168 Wireguard *w = WIREGUARD(netdev);
1169 int r;
1170
1171 r = wireguard_read_key_file(w->private_key_file, w->private_key);
1172 if (r < 0)
1173 return log_netdev_error_errno(netdev, r,
1174 "Failed to read private key from %s. Ignoring network device.",
1175 w->private_key_file);
1176
1177 if (eqzero(w->private_key)) {
1178 r = wireguard_read_default_key_cred(netdev, filename);
1179 if (r < 0)
1180 return r;
1181 }
1182
1183 LIST_FOREACH(peers, peer, w->peers) {
1184 if (wireguard_peer_verify(peer) < 0) {
1185 wireguard_peer_free(peer);
1186 continue;
1187 }
1188
1189 if ((peer->route_table_set ? peer->route_table : w->route_table) == 0)
1190 continue;
1191
1192 LIST_FOREACH(ipmasks, ipmask, peer->ipmasks) {
1193 _cleanup_(route_freep) Route *route = NULL;
1194
1195 r = route_new(&route);
1196 if (r < 0)
1197 return log_oom();
1198
1199 route->family = ipmask->family;
1200 route->dst = ipmask->ip;
1201 route->dst_prefixlen = ipmask->cidr;
1202 route->scope = RT_SCOPE_UNIVERSE;
1203 route->protocol = RTPROT_STATIC;
1204 route->table = peer->route_table_set ? peer->route_table : w->route_table;
1205 route->priority = peer->route_priority_set ? peer->route_priority : w->route_priority;
1206 if (route->priority == 0 && route->family == AF_INET6)
1207 route->priority = IP6_RT_PRIO_USER;
1208 route->source = NETWORK_CONFIG_SOURCE_STATIC;
1209
1210 r = set_ensure_consume(&w->routes, &route_hash_ops, TAKE_PTR(route));
1211 if (r < 0)
1212 return log_oom();
1213 }
1214 }
1215
1216 return 0;
1217 }
1218
1219 const NetDevVTable wireguard_vtable = {
1220 .object_size = sizeof(Wireguard),
1221 .sections = NETDEV_COMMON_SECTIONS "WireGuard\0WireGuardPeer\0",
1222 .post_create = netdev_wireguard_post_create,
1223 .init = wireguard_init,
1224 .done = wireguard_done,
1225 .create_type = NETDEV_CREATE_INDEPENDENT,
1226 .config_verify = wireguard_verify,
1227 .iftype = ARPHRD_NONE,
1228 };