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