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