LCOV - code coverage report
Current view: top level - network/netdev - wireguard.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 0 483 0.0 %
Date: 2019-08-22 15:41:25 Functions: 0 29 0.0 %

          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             : };

Generated by: LCOV version 1.14