]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/netdev/wireguard.c
fileio: add explicit flag for generating world executable warning when reading file
[thirdparty/systemd.git] / src / network / netdev / wireguard.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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
9 #include "sd-resolve.h"
10
11 #include "alloc-util.h"
12 #include "event-util.h"
13 #include "fd-util.h"
14 #include "fileio.h"
15 #include "hexdecoct.h"
16 #include "memory-util.h"
17 #include "netlink-util.h"
18 #include "networkd-manager.h"
19 #include "networkd-util.h"
20 #include "parse-util.h"
21 #include "path-util.h"
22 #include "resolve-private.h"
23 #include "string-util.h"
24 #include "strv.h"
25 #include "wireguard.h"
26
27 static void resolve_endpoints(NetDev *netdev);
28
29 static void wireguard_peer_free(WireguardPeer *peer) {
30 WireguardIPmask *mask;
31
32 if (!peer)
33 return;
34
35 if (peer->wireguard) {
36 LIST_REMOVE(peers, peer->wireguard->peers, peer);
37
38 set_remove(peer->wireguard->peers_with_unresolved_endpoint, peer);
39 set_remove(peer->wireguard->peers_with_failed_endpoint, peer);
40
41 if (peer->section)
42 hashmap_remove(peer->wireguard->peers_by_section, peer->section);
43 }
44
45 network_config_section_free(peer->section);
46
47 while ((mask = peer->ipmasks)) {
48 LIST_REMOVE(ipmasks, peer->ipmasks, mask);
49 free(mask);
50 }
51
52 free(peer->endpoint_host);
53 free(peer->endpoint_port);
54 free(peer->preshared_key_file);
55 explicit_bzero_safe(peer->preshared_key, WG_KEY_LEN);
56
57 free(peer);
58 }
59
60 DEFINE_NETWORK_SECTION_FUNCTIONS(WireguardPeer, wireguard_peer_free);
61
62 static int wireguard_peer_new_static(Wireguard *w, const char *filename, unsigned section_line, WireguardPeer **ret) {
63 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
64 _cleanup_(wireguard_peer_freep) WireguardPeer *peer = NULL;
65 int r;
66
67 assert(w);
68 assert(ret);
69 assert(filename);
70 assert(section_line > 0);
71
72 r = network_config_section_new(filename, section_line, &n);
73 if (r < 0)
74 return r;
75
76 peer = hashmap_get(w->peers_by_section, n);
77 if (peer) {
78 *ret = TAKE_PTR(peer);
79 return 0;
80 }
81
82 peer = new(WireguardPeer, 1);
83 if (!peer)
84 return -ENOMEM;
85
86 *peer = (WireguardPeer) {
87 .flags = WGPEER_F_REPLACE_ALLOWEDIPS,
88 .wireguard = w,
89 .section = TAKE_PTR(n),
90 };
91
92 LIST_PREPEND(peers, w->peers, peer);
93
94 r = hashmap_ensure_allocated(&w->peers_by_section, &network_config_hash_ops);
95 if (r < 0)
96 return r;
97
98 r = hashmap_put(w->peers_by_section, peer->section, peer);
99 if (r < 0)
100 return r;
101
102 *ret = TAKE_PTR(peer);
103 return 0;
104 }
105
106 static int wireguard_set_ipmask_one(NetDev *netdev, sd_netlink_message *message, const WireguardIPmask *mask, uint16_t index) {
107 int r;
108
109 assert(message);
110 assert(mask);
111 assert(index > 0);
112
113 /* This returns 1 on success, 0 on recoverable error, and negative errno on failure. */
114
115 r = sd_netlink_message_open_array(message, index);
116 if (r < 0)
117 return 0;
118
119 r = sd_netlink_message_append_u16(message, WGALLOWEDIP_A_FAMILY, mask->family);
120 if (r < 0)
121 goto cancel;
122
123 r = netlink_message_append_in_addr_union(message, WGALLOWEDIP_A_IPADDR, mask->family, &mask->ip);
124 if (r < 0)
125 goto cancel;
126
127 r = sd_netlink_message_append_u8(message, WGALLOWEDIP_A_CIDR_MASK, mask->cidr);
128 if (r < 0)
129 goto cancel;
130
131 r = sd_netlink_message_close_container(message);
132 if (r < 0)
133 return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
134
135 return 1;
136
137 cancel:
138 r = sd_netlink_message_cancel_array(message);
139 if (r < 0)
140 return log_netdev_error_errno(netdev, r, "Could not cancel wireguard allowed ip message attribute: %m");
141
142 return 0;
143 }
144
145 static int wireguard_set_peer_one(NetDev *netdev, sd_netlink_message *message, const WireguardPeer *peer, uint16_t index, WireguardIPmask **mask_start) {
146 WireguardIPmask *mask, *start;
147 uint16_t j = 0;
148 int r;
149
150 assert(message);
151 assert(peer);
152 assert(index > 0);
153 assert(mask_start);
154
155 /* This returns 1 on success, 0 on recoverable error, and negative errno on failure. */
156
157 start = *mask_start ?: peer->ipmasks;
158
159 r = sd_netlink_message_open_array(message, index);
160 if (r < 0)
161 return 0;
162
163 r = sd_netlink_message_append_data(message, WGPEER_A_PUBLIC_KEY, &peer->public_key, sizeof(peer->public_key));
164 if (r < 0)
165 goto cancel;
166
167 if (!*mask_start) {
168 r = sd_netlink_message_append_data(message, WGPEER_A_PRESHARED_KEY, &peer->preshared_key, WG_KEY_LEN);
169 if (r < 0)
170 goto cancel;
171
172 r = sd_netlink_message_append_u32(message, WGPEER_A_FLAGS, peer->flags);
173 if (r < 0)
174 goto cancel;
175
176 r = sd_netlink_message_append_u16(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval);
177 if (r < 0)
178 goto cancel;
179
180 if (IN_SET(peer->endpoint.sa.sa_family, AF_INET, AF_INET6)) {
181 r = netlink_message_append_sockaddr_union(message, WGPEER_A_ENDPOINT, &peer->endpoint);
182 if (r < 0)
183 goto cancel;
184 }
185 }
186
187 r = sd_netlink_message_open_container(message, WGPEER_A_ALLOWEDIPS);
188 if (r < 0)
189 goto cancel;
190
191 LIST_FOREACH(ipmasks, mask, start) {
192 r = wireguard_set_ipmask_one(netdev, message, mask, ++j);
193 if (r < 0)
194 return r;
195 if (r == 0)
196 break;
197 }
198
199 r = sd_netlink_message_close_container(message);
200 if (r < 0)
201 return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
202
203 r = sd_netlink_message_close_container(message);
204 if (r < 0)
205 return log_netdev_error_errno(netdev, r, "Could not add wireguard peer: %m");
206
207 *mask_start = mask; /* Start next cycle from this mask. */
208 return !mask;
209
210 cancel:
211 r = sd_netlink_message_cancel_array(message);
212 if (r < 0)
213 return log_netdev_error_errno(netdev, r, "Could not cancel wireguard peers: %m");
214
215 return 0;
216 }
217
218 static int wireguard_set_interface(NetDev *netdev) {
219 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
220 WireguardIPmask *mask_start = NULL;
221 WireguardPeer *peer, *peer_start;
222 bool sent_once = false;
223 uint32_t serial;
224 Wireguard *w;
225 int r;
226
227 assert(netdev);
228 w = WIREGUARD(netdev);
229 assert(w);
230
231 for (peer_start = w->peers; peer_start || !sent_once; ) {
232 uint16_t i = 0;
233
234 message = sd_netlink_message_unref(message);
235
236 r = sd_genl_message_new(netdev->manager->genl, SD_GENL_WIREGUARD, WG_CMD_SET_DEVICE, &message);
237 if (r < 0)
238 return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
239
240 r = sd_netlink_message_append_string(message, WGDEVICE_A_IFNAME, netdev->ifname);
241 if (r < 0)
242 return log_netdev_error_errno(netdev, r, "Could not append wireguard interface name: %m");
243
244 if (peer_start == w->peers) {
245 r = sd_netlink_message_append_data(message, WGDEVICE_A_PRIVATE_KEY, &w->private_key, WG_KEY_LEN);
246 if (r < 0)
247 return log_netdev_error_errno(netdev, r, "Could not append wireguard private key: %m");
248
249 r = sd_netlink_message_append_u16(message, WGDEVICE_A_LISTEN_PORT, w->port);
250 if (r < 0)
251 return log_netdev_error_errno(netdev, r, "Could not append wireguard port: %m");
252
253 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FWMARK, w->fwmark);
254 if (r < 0)
255 return log_netdev_error_errno(netdev, r, "Could not append wireguard fwmark: %m");
256
257 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FLAGS, w->flags);
258 if (r < 0)
259 return log_netdev_error_errno(netdev, r, "Could not append wireguard flags: %m");
260 }
261
262 r = sd_netlink_message_open_container(message, WGDEVICE_A_PEERS);
263 if (r < 0)
264 return log_netdev_error_errno(netdev, r, "Could not append wireguard peer attributes: %m");
265
266 LIST_FOREACH(peers, peer, peer_start) {
267 r = wireguard_set_peer_one(netdev, message, peer, ++i, &mask_start);
268 if (r < 0)
269 return r;
270 if (r == 0)
271 break;
272 }
273 peer_start = peer; /* Start next cycle from this peer. */
274
275 r = sd_netlink_message_close_container(message);
276 if (r < 0)
277 return log_netdev_error_errno(netdev, r, "Could not close wireguard container: %m");
278
279 r = sd_netlink_send(netdev->manager->genl, message, &serial);
280 if (r < 0)
281 return log_netdev_error_errno(netdev, r, "Could not set wireguard device: %m");
282
283 sent_once = true;
284 }
285
286 return 0;
287 }
288
289 static void wireguard_peer_destroy_callback(WireguardPeer *peer) {
290 NetDev *netdev;
291
292 assert(peer);
293 assert(peer->wireguard);
294
295 netdev = NETDEV(peer->wireguard);
296
297 if (section_is_invalid(peer->section))
298 wireguard_peer_free(peer);
299
300 netdev_unref(netdev);
301 }
302
303 static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) {
304 NetDev *netdev = userdata;
305 Wireguard *w;
306
307 assert(netdev);
308 w = WIREGUARD(netdev);
309 assert(w);
310
311 if (!netdev_is_managed(netdev))
312 return 0;
313
314 assert(set_isempty(w->peers_with_unresolved_endpoint));
315
316 SWAP_TWO(w->peers_with_unresolved_endpoint, w->peers_with_failed_endpoint);
317
318 resolve_endpoints(netdev);
319
320 return 0;
321 }
322
323 /*
324 * Given the number of retries this function will return will an exponential
325 * increasing time in milliseconds to wait starting at 200ms and capped at 25 seconds.
326 */
327 static int exponential_backoff_milliseconds(unsigned n_retries) {
328 return (2 << MIN(n_retries, 7U)) * 100 * USEC_PER_MSEC;
329 }
330
331 static int wireguard_resolve_handler(sd_resolve_query *q,
332 int ret,
333 const struct addrinfo *ai,
334 WireguardPeer *peer) {
335 NetDev *netdev;
336 Wireguard *w;
337 int r;
338
339 assert(peer);
340 assert(peer->wireguard);
341
342 w = peer->wireguard;
343 netdev = NETDEV(w);
344
345 if (!netdev_is_managed(netdev))
346 return 0;
347
348 if (ret != 0) {
349 log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", peer->endpoint_host, peer->endpoint_port, gai_strerror(ret));
350
351 r = set_ensure_put(&w->peers_with_failed_endpoint, NULL, peer);
352 if (r < 0) {
353 log_netdev_error(netdev, "Failed to save a peer, dropping the peer: %m");
354 peer->section->invalid = true;
355 goto resolve_next;
356 }
357
358 } else if ((ai->ai_family == AF_INET && ai->ai_addrlen == sizeof(struct sockaddr_in)) ||
359 (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6)))
360 memcpy(&peer->endpoint, ai->ai_addr, ai->ai_addrlen);
361 else
362 log_netdev_error(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint %s:%s, ignoring the address.",
363 peer->endpoint_host, peer->endpoint_port);
364
365 resolve_next:
366 if (!set_isempty(w->peers_with_unresolved_endpoint)) {
367 resolve_endpoints(netdev);
368 return 0;
369 }
370
371 (void) wireguard_set_interface(netdev);
372
373 if (!set_isempty(w->peers_with_failed_endpoint)) {
374 usec_t usec;
375
376 w->n_retries++;
377 usec = usec_add(now(CLOCK_MONOTONIC), exponential_backoff_milliseconds(w->n_retries));
378 r = event_reset_time(netdev->manager->event, &w->resolve_retry_event_source,
379 CLOCK_MONOTONIC, usec, 0, on_resolve_retry, netdev,
380 0, "wireguard-resolve-retry", true);
381 if (r < 0) {
382 log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler: %m");
383 return 0;
384 }
385 }
386
387 return 0;
388 }
389
390 static void resolve_endpoints(NetDev *netdev) {
391 static const struct addrinfo hints = {
392 .ai_family = AF_UNSPEC,
393 .ai_socktype = SOCK_DGRAM,
394 .ai_protocol = IPPROTO_UDP
395 };
396 WireguardPeer *peer;
397 Wireguard *w;
398 Iterator i;
399 int r;
400
401 assert(netdev);
402 w = WIREGUARD(netdev);
403 assert(w);
404
405 SET_FOREACH(peer, w->peers_with_unresolved_endpoint, i) {
406 r = resolve_getaddrinfo(netdev->manager->resolve,
407 NULL,
408 peer->endpoint_host,
409 peer->endpoint_port,
410 &hints,
411 wireguard_resolve_handler,
412 wireguard_peer_destroy_callback,
413 peer);
414 if (r == -ENOBUFS)
415 break;
416 if (r < 0) {
417 log_netdev_error_errno(netdev, r, "Failed to create resolver: %m");
418 continue;
419 }
420
421 /* Avoid freeing netdev. It will be unrefed by the destroy callback. */
422 netdev_ref(netdev);
423
424 (void) set_remove(w->peers_with_unresolved_endpoint, peer);
425 }
426 }
427
428 static int netdev_wireguard_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
429 assert(netdev);
430 assert(WIREGUARD(netdev));
431
432 (void) wireguard_set_interface(netdev);
433 resolve_endpoints(netdev);
434 return 0;
435 }
436
437 int config_parse_wireguard_listen_port(
438 const char *unit,
439 const char *filename,
440 unsigned line,
441 const char *section,
442 unsigned section_line,
443 const char *lvalue,
444 int ltype,
445 const char *rvalue,
446 void *data,
447 void *userdata) {
448
449 uint16_t *s = data;
450 int r;
451
452 assert(rvalue);
453 assert(data);
454
455 if (isempty(rvalue) || streq(rvalue, "auto")) {
456 *s = 0;
457 return 0;
458 }
459
460 r = parse_ip_port(rvalue, s);
461 if (r < 0) {
462 log_syntax(unit, LOG_WARNING, filename, line, r,
463 "Invalid port specification, ignoring assignment: %s", rvalue);
464 return 0;
465 }
466
467 return 0;
468 }
469
470 static int wireguard_decode_key_and_warn(
471 const char *rvalue,
472 uint8_t ret[static WG_KEY_LEN],
473 const char *unit,
474 const char *filename,
475 unsigned line,
476 const char *lvalue) {
477
478 _cleanup_(erase_and_freep) void *key = NULL;
479 size_t len;
480 int r;
481
482 assert(rvalue);
483 assert(ret);
484 assert(filename);
485 assert(lvalue);
486
487 if (isempty(rvalue)) {
488 memzero(ret, WG_KEY_LEN);
489 return 0;
490 }
491
492 if (!streq(lvalue, "PublicKey"))
493 (void) warn_file_is_world_accessible(filename, NULL, unit, line);
494
495 r = unbase64mem_full(rvalue, strlen(rvalue), true, &key, &len);
496 if (r < 0)
497 return log_syntax(unit, LOG_WARNING, filename, line, r,
498 "Failed to decode wireguard key provided by %s=, ignoring assignment: %m", lvalue);
499 if (len != WG_KEY_LEN)
500 return log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
501 "Wireguard key provided by %s= has invalid length (%zu bytes), ignoring assignment.",
502 lvalue, len);
503
504 memcpy(ret, key, WG_KEY_LEN);
505 return 0;
506 }
507
508 int config_parse_wireguard_private_key(
509 const char *unit,
510 const char *filename,
511 unsigned line,
512 const char *section,
513 unsigned section_line,
514 const char *lvalue,
515 int ltype,
516 const char *rvalue,
517 void *data,
518 void *userdata) {
519
520 Wireguard *w;
521
522 assert(data);
523 w = WIREGUARD(data);
524 assert(w);
525
526 (void) wireguard_decode_key_and_warn(rvalue, w->private_key, unit, filename, line, lvalue);
527 return 0;
528 }
529
530 int config_parse_wireguard_private_key_file(
531 const char *unit,
532 const char *filename,
533 unsigned line,
534 const char *section,
535 unsigned section_line,
536 const char *lvalue,
537 int ltype,
538 const char *rvalue,
539 void *data,
540 void *userdata) {
541
542 _cleanup_free_ char *path = NULL;
543 Wireguard *w;
544
545 assert(data);
546 w = WIREGUARD(data);
547 assert(w);
548
549 if (isempty(rvalue)) {
550 w->private_key_file = mfree(w->private_key_file);
551 return 0;
552 }
553
554 path = strdup(rvalue);
555 if (!path)
556 return log_oom();
557
558 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
559 return 0;
560
561 return free_and_replace(w->private_key_file, path);
562 }
563
564 int config_parse_wireguard_preshared_key(
565 const char *unit,
566 const char *filename,
567 unsigned line,
568 const char *section,
569 unsigned section_line,
570 const char *lvalue,
571 int ltype,
572 const char *rvalue,
573 void *data,
574 void *userdata) {
575
576 WireguardPeer *peer;
577 Wireguard *w;
578 int r;
579
580 assert(data);
581 w = WIREGUARD(data);
582 assert(w);
583
584 r = wireguard_peer_new_static(w, filename, section_line, &peer);
585 if (r < 0)
586 return log_oom();
587
588 (void) wireguard_decode_key_and_warn(rvalue, peer->preshared_key, unit, filename, line, lvalue);
589 return 0;
590 }
591
592 int config_parse_wireguard_preshared_key_file(
593 const char *unit,
594 const char *filename,
595 unsigned line,
596 const char *section,
597 unsigned section_line,
598 const char *lvalue,
599 int ltype,
600 const char *rvalue,
601 void *data,
602 void *userdata) {
603
604 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
605 _cleanup_free_ char *path = NULL;
606 Wireguard *w;
607 int r;
608
609 assert(data);
610 w = WIREGUARD(data);
611 assert(w);
612
613 r = wireguard_peer_new_static(w, filename, section_line, &peer);
614 if (r < 0)
615 return log_oom();
616
617 if (isempty(rvalue)) {
618 peer->preshared_key_file = mfree(peer->preshared_key_file);
619 TAKE_PTR(peer);
620 return 0;
621 }
622
623 path = strdup(rvalue);
624 if (!path)
625 return log_oom();
626
627 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
628 return 0;
629
630 free_and_replace(peer->preshared_key_file, path);
631 TAKE_PTR(peer);
632 return 0;
633 }
634
635 int config_parse_wireguard_public_key(
636 const char *unit,
637 const char *filename,
638 unsigned line,
639 const char *section,
640 unsigned section_line,
641 const char *lvalue,
642 int ltype,
643 const char *rvalue,
644 void *data,
645 void *userdata) {
646
647 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
648 Wireguard *w;
649 int r;
650
651 assert(data);
652 w = WIREGUARD(data);
653 assert(w);
654
655 r = wireguard_peer_new_static(w, filename, section_line, &peer);
656 if (r < 0)
657 return log_oom();
658
659 r = wireguard_decode_key_and_warn(rvalue, peer->public_key, unit, filename, line, lvalue);
660 if (r < 0)
661 return 0;
662
663 TAKE_PTR(peer);
664 return 0;
665 }
666
667 int config_parse_wireguard_allowed_ips(
668 const char *unit,
669 const char *filename,
670 unsigned line,
671 const char *section,
672 unsigned section_line,
673 const char *lvalue,
674 int ltype,
675 const char *rvalue,
676 void *data,
677 void *userdata) {
678
679 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
680 union in_addr_union addr;
681 unsigned char prefixlen;
682 int r, family;
683 Wireguard *w;
684 WireguardIPmask *ipmask;
685
686 assert(rvalue);
687 assert(data);
688
689 w = WIREGUARD(data);
690 assert(w);
691
692 r = wireguard_peer_new_static(w, filename, section_line, &peer);
693 if (r < 0)
694 return log_oom();
695
696 for (const char *p = rvalue;;) {
697 _cleanup_free_ char *word = NULL;
698
699 r = extract_first_word(&p, &word, "," WHITESPACE, 0);
700 if (r == 0)
701 break;
702 if (r == -ENOMEM)
703 return log_oom();
704 if (r < 0) {
705 log_syntax(unit, LOG_WARNING, filename, line, r,
706 "Failed to split allowed ips \"%s\" option: %m", rvalue);
707 break;
708 }
709
710 r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen);
711 if (r < 0) {
712 log_syntax(unit, LOG_WARNING, filename, line, r,
713 "Network address is invalid, ignoring assignment: %s", word);
714 continue;
715 }
716
717 ipmask = new(WireguardIPmask, 1);
718 if (!ipmask)
719 return log_oom();
720
721 *ipmask = (WireguardIPmask) {
722 .family = family,
723 .ip.in6 = addr.in6,
724 .cidr = prefixlen,
725 };
726
727 LIST_PREPEND(ipmasks, peer->ipmasks, ipmask);
728 }
729
730 TAKE_PTR(peer);
731 return 0;
732 }
733
734 int config_parse_wireguard_endpoint(
735 const char *unit,
736 const char *filename,
737 unsigned line,
738 const char *section,
739 unsigned section_line,
740 const char *lvalue,
741 int ltype,
742 const char *rvalue,
743 void *data,
744 void *userdata) {
745
746 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
747 const char *begin, *end;
748 Wireguard *w;
749 size_t len;
750 int r;
751
752 assert(data);
753 assert(rvalue);
754
755 w = WIREGUARD(data);
756 assert(w);
757
758 if (rvalue[0] == '[') {
759 begin = &rvalue[1];
760 end = strchr(rvalue, ']');
761 if (!end) {
762 log_syntax(unit, LOG_WARNING, filename, line, 0,
763 "Unable to find matching brace of endpoint, ignoring assignment: %s",
764 rvalue);
765 return 0;
766 }
767 len = end - begin;
768 ++end;
769 if (*end != ':' || !*(end + 1)) {
770 log_syntax(unit, LOG_WARNING, filename, line, 0,
771 "Unable to find port of endpoint, ignoring assignment: %s",
772 rvalue);
773 return 0;
774 }
775 ++end;
776 } else {
777 begin = rvalue;
778 end = strrchr(rvalue, ':');
779 if (!end || !*(end + 1)) {
780 log_syntax(unit, LOG_WARNING, filename, line, 0,
781 "Unable to find port of endpoint, ignoring assignment: %s",
782 rvalue);
783 return 0;
784 }
785 len = end - begin;
786 ++end;
787 }
788
789 r = wireguard_peer_new_static(w, filename, section_line, &peer);
790 if (r < 0)
791 return log_oom();
792
793 r = free_and_strndup(&peer->endpoint_host, begin, len);
794 if (r < 0)
795 return log_oom();
796
797 r = free_and_strdup(&peer->endpoint_port, end);
798 if (r < 0)
799 return log_oom();
800
801 r = set_ensure_put(&w->peers_with_unresolved_endpoint, NULL, peer);
802 if (r < 0)
803 return log_oom();
804 TAKE_PTR(peer); /* The peer may already have been in the hash map, that is fine too. */
805
806 return 0;
807 }
808
809 int config_parse_wireguard_keepalive(
810 const char *unit,
811 const char *filename,
812 unsigned line,
813 const char *section,
814 unsigned section_line,
815 const char *lvalue,
816 int ltype,
817 const char *rvalue,
818 void *data,
819 void *userdata) {
820
821 WireguardPeer *peer;
822 uint16_t keepalive = 0;
823 Wireguard *w;
824 int r;
825
826 assert(rvalue);
827 assert(data);
828
829 w = WIREGUARD(data);
830 assert(w);
831
832 r = wireguard_peer_new_static(w, filename, section_line, &peer);
833 if (r < 0)
834 return log_oom();
835
836 if (streq(rvalue, "off"))
837 keepalive = 0;
838 else {
839 r = safe_atou16(rvalue, &keepalive);
840 if (r < 0) {
841 log_syntax(unit, LOG_WARNING, filename, line, r,
842 "Failed to parse \"%s\" as keepalive interval (range 0–65535), ignoring assignment: %m",
843 rvalue);
844 return 0;
845 }
846 }
847
848 peer->persistent_keepalive_interval = keepalive;
849 return 0;
850 }
851
852 static void wireguard_init(NetDev *netdev) {
853 Wireguard *w;
854
855 assert(netdev);
856 w = WIREGUARD(netdev);
857 assert(w);
858
859 w->flags = WGDEVICE_F_REPLACE_PEERS;
860 }
861
862 static void wireguard_done(NetDev *netdev) {
863 Wireguard *w;
864
865 assert(netdev);
866 w = WIREGUARD(netdev);
867 assert(w);
868
869 sd_event_source_unref(w->resolve_retry_event_source);
870
871 explicit_bzero_safe(w->private_key, WG_KEY_LEN);
872 free(w->private_key_file);
873
874 hashmap_free_with_destructor(w->peers_by_section, wireguard_peer_free);
875 set_free(w->peers_with_unresolved_endpoint);
876 set_free(w->peers_with_failed_endpoint);
877 }
878
879 static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_KEY_LEN]) {
880 _cleanup_(erase_and_freep) char *key = NULL;
881 size_t key_len;
882 int r;
883
884 if (!filename)
885 return 0;
886
887 assert(dest);
888
889 (void) warn_file_is_world_accessible(filename, NULL, NULL, 0);
890
891 r = read_full_file_full(AT_FDCWD, filename, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_WARN_WORLD_READABLE, &key, &key_len);
892 if (r < 0)
893 return r;
894
895 if (key_len != WG_KEY_LEN)
896 return -EINVAL;
897
898 memcpy(dest, key, WG_KEY_LEN);
899 return 0;
900 }
901
902 static int wireguard_peer_verify(WireguardPeer *peer) {
903 NetDev *netdev = NETDEV(peer->wireguard);
904 int r;
905
906 if (section_is_invalid(peer->section))
907 return -EINVAL;
908
909 if (eqzero(peer->public_key))
910 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
911 "%s: WireGuardPeer section without PublicKey= configured. "
912 "Ignoring [WireGuardPeer] section from line %u.",
913 peer->section->filename, peer->section->line);
914
915 r = wireguard_read_key_file(peer->preshared_key_file, peer->preshared_key);
916 if (r < 0)
917 return log_netdev_error_errno(netdev, r,
918 "%s: Failed to read preshared key from '%s'. "
919 "Ignoring [WireGuardPeer] section from line %u.",
920 peer->section->filename, peer->preshared_key_file,
921 peer->section->line);
922
923 return 0;
924 }
925
926 static int wireguard_verify(NetDev *netdev, const char *filename) {
927 WireguardPeer *peer, *peer_next;
928 Wireguard *w;
929 int r;
930
931 assert(netdev);
932 w = WIREGUARD(netdev);
933 assert(w);
934
935 r = wireguard_read_key_file(w->private_key_file, w->private_key);
936 if (r < 0)
937 return log_netdev_error_errno(netdev, r,
938 "Failed to read private key from %s. Dropping network device %s.",
939 w->private_key_file, netdev->ifname);
940
941 if (eqzero(w->private_key))
942 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
943 "%s: Missing PrivateKey= or PrivateKeyFile=, "
944 "Dropping network device %s.",
945 filename, netdev->ifname);
946
947 LIST_FOREACH_SAFE(peers, peer, peer_next, w->peers)
948 if (wireguard_peer_verify(peer) < 0)
949 wireguard_peer_free(peer);
950
951 return 0;
952 }
953
954 const NetDevVTable wireguard_vtable = {
955 .object_size = sizeof(Wireguard),
956 .sections = NETDEV_COMMON_SECTIONS "WireGuard\0WireGuardPeer\0",
957 .post_create = netdev_wireguard_post_create,
958 .init = wireguard_init,
959 .done = wireguard_done,
960 .create_type = NETDEV_CREATE_INDEPENDENT,
961 .config_verify = wireguard_verify,
962 .generate_mac = true,
963 };