2020-11-09 05:23:58 +01:00
|
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
2017-12-18 15:20:34 +01:00
|
|
|
|
/***
|
2018-06-12 19:00:24 +02:00
|
|
|
|
Copyright © 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
2017-12-18 15:20:34 +01:00
|
|
|
|
***/
|
|
|
|
|
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
#include <net/if.h>
|
|
|
|
|
|
2018-11-04 12:42:14 +01:00
|
|
|
|
#include "sd-resolve.h"
|
|
|
|
|
|
2017-12-18 15:20:34 +01:00
|
|
|
|
#include "alloc-util.h"
|
2019-03-01 07:41:47 +01:00
|
|
|
|
#include "event-util.h"
|
2017-12-18 15:20:34 +01:00
|
|
|
|
#include "fd-util.h"
|
2019-03-04 06:19:21 +01:00
|
|
|
|
#include "fileio.h"
|
2017-12-18 15:20:34 +01:00
|
|
|
|
#include "hexdecoct.h"
|
2019-03-13 12:02:21 +01:00
|
|
|
|
#include "memory-util.h"
|
2019-02-03 01:47:25 +01:00
|
|
|
|
#include "netlink-util.h"
|
2017-12-18 15:20:34 +01:00
|
|
|
|
#include "networkd-manager.h"
|
2018-12-15 12:57:20 +01:00
|
|
|
|
#include "networkd-util.h"
|
|
|
|
|
#include "parse-util.h"
|
2019-03-04 06:19:21 +01:00
|
|
|
|
#include "path-util.h"
|
2018-12-15 12:57:50 +01:00
|
|
|
|
#include "resolve-private.h"
|
2018-12-15 12:57:20 +01:00
|
|
|
|
#include "string-util.h"
|
|
|
|
|
#include "strv.h"
|
2019-03-13 12:02:21 +01:00
|
|
|
|
#include "wireguard.h"
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
|
|
|
|
static void resolve_endpoints(NetDev *netdev);
|
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
static void wireguard_peer_free(WireguardPeer *peer) {
|
|
|
|
|
WireguardIPmask *mask;
|
|
|
|
|
|
|
|
|
|
if (!peer)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (peer->wireguard) {
|
|
|
|
|
LIST_REMOVE(peers, peer->wireguard->peers, peer);
|
|
|
|
|
|
|
|
|
|
set_remove(peer->wireguard->peers_with_unresolved_endpoint, peer);
|
|
|
|
|
set_remove(peer->wireguard->peers_with_failed_endpoint, peer);
|
|
|
|
|
|
|
|
|
|
if (peer->section)
|
|
|
|
|
hashmap_remove(peer->wireguard->peers_by_section, peer->section);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
network_config_section_free(peer->section);
|
|
|
|
|
|
|
|
|
|
while ((mask = peer->ipmasks)) {
|
|
|
|
|
LIST_REMOVE(ipmasks, peer->ipmasks, mask);
|
|
|
|
|
free(mask);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(peer->endpoint_host);
|
|
|
|
|
free(peer->endpoint_port);
|
2019-04-05 10:33:09 +02:00
|
|
|
|
free(peer->preshared_key_file);
|
2019-04-07 19:48:02 +02:00
|
|
|
|
explicit_bzero_safe(peer->preshared_key, WG_KEY_LEN);
|
2019-02-28 09:12:02 +01:00
|
|
|
|
|
|
|
|
|
free(peer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DEFINE_NETWORK_SECTION_FUNCTIONS(WireguardPeer, wireguard_peer_free);
|
|
|
|
|
|
|
|
|
|
static int wireguard_peer_new_static(Wireguard *w, const char *filename, unsigned section_line, WireguardPeer **ret) {
|
|
|
|
|
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
|
|
|
|
|
_cleanup_(wireguard_peer_freep) WireguardPeer *peer = NULL;
|
|
|
|
|
int r;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
|
|
|
|
assert(w);
|
2019-02-28 09:12:02 +01:00
|
|
|
|
assert(ret);
|
|
|
|
|
assert(filename);
|
|
|
|
|
assert(section_line > 0);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
r = network_config_section_new(filename, section_line, &n);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
peer = hashmap_get(w->peers_by_section, n);
|
|
|
|
|
if (peer) {
|
|
|
|
|
*ret = TAKE_PTR(peer);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
2018-11-03 13:57:06 +01:00
|
|
|
|
peer = new(WireguardPeer, 1);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
if (!peer)
|
2019-02-28 09:12:02 +01:00
|
|
|
|
return -ENOMEM;
|
2018-11-03 13:57:06 +01:00
|
|
|
|
|
|
|
|
|
*peer = (WireguardPeer) {
|
|
|
|
|
.flags = WGPEER_F_REPLACE_ALLOWEDIPS,
|
2019-02-28 09:12:02 +01:00
|
|
|
|
.wireguard = w,
|
|
|
|
|
.section = TAKE_PTR(n),
|
2018-11-03 13:57:06 +01:00
|
|
|
|
};
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
|
|
|
|
LIST_PREPEND(peers, w->peers, peer);
|
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
r = hashmap_ensure_allocated(&w->peers_by_section, &network_config_hash_ops);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
r = hashmap_put(w->peers_by_section, peer->section, peer);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
*ret = TAKE_PTR(peer);
|
|
|
|
|
return 0;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-14 01:45:20 +01:00
|
|
|
|
static int wireguard_set_ipmask_one(NetDev *netdev, sd_netlink_message *message, const WireguardIPmask *mask, uint16_t index) {
|
2017-12-18 15:20:34 +01:00
|
|
|
|
int r;
|
2019-01-14 01:45:20 +01:00
|
|
|
|
|
|
|
|
|
assert(message);
|
|
|
|
|
assert(mask);
|
|
|
|
|
assert(index > 0);
|
|
|
|
|
|
|
|
|
|
/* This returns 1 on success, 0 on recoverable error, and negative errno on failure. */
|
|
|
|
|
|
|
|
|
|
r = sd_netlink_message_open_array(message, index);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
r = sd_netlink_message_append_u16(message, WGALLOWEDIP_A_FAMILY, mask->family);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
goto cancel;
|
|
|
|
|
|
2019-02-03 01:47:25 +01:00
|
|
|
|
r = netlink_message_append_in_addr_union(message, WGALLOWEDIP_A_IPADDR, mask->family, &mask->ip);
|
2019-01-14 01:45:20 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
goto cancel;
|
|
|
|
|
|
|
|
|
|
r = sd_netlink_message_append_u8(message, WGALLOWEDIP_A_CIDR_MASK, mask->cidr);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
goto cancel;
|
|
|
|
|
|
|
|
|
|
r = sd_netlink_message_close_container(message);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
cancel:
|
|
|
|
|
r = sd_netlink_message_cancel_array(message);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_netdev_error_errno(netdev, r, "Could not cancel wireguard allowed ip message attribute: %m");
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int wireguard_set_peer_one(NetDev *netdev, sd_netlink_message *message, const WireguardPeer *peer, uint16_t index, WireguardIPmask **mask_start) {
|
|
|
|
|
WireguardIPmask *mask, *start;
|
|
|
|
|
uint16_t j = 0;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert(message);
|
|
|
|
|
assert(peer);
|
|
|
|
|
assert(index > 0);
|
|
|
|
|
assert(mask_start);
|
|
|
|
|
|
|
|
|
|
/* This returns 1 on success, 0 on recoverable error, and negative errno on failure. */
|
|
|
|
|
|
|
|
|
|
start = *mask_start ?: peer->ipmasks;
|
|
|
|
|
|
|
|
|
|
r = sd_netlink_message_open_array(message, index);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
r = sd_netlink_message_append_data(message, WGPEER_A_PUBLIC_KEY, &peer->public_key, sizeof(peer->public_key));
|
|
|
|
|
if (r < 0)
|
|
|
|
|
goto cancel;
|
|
|
|
|
|
2019-01-28 09:42:35 +01:00
|
|
|
|
if (!*mask_start) {
|
2019-01-14 01:45:20 +01:00
|
|
|
|
r = sd_netlink_message_append_data(message, WGPEER_A_PRESHARED_KEY, &peer->preshared_key, WG_KEY_LEN);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
goto cancel;
|
|
|
|
|
|
|
|
|
|
r = sd_netlink_message_append_u32(message, WGPEER_A_FLAGS, peer->flags);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
goto cancel;
|
|
|
|
|
|
|
|
|
|
r = sd_netlink_message_append_u16(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
goto cancel;
|
|
|
|
|
|
2019-02-03 01:47:25 +01:00
|
|
|
|
if (IN_SET(peer->endpoint.sa.sa_family, AF_INET, AF_INET6)) {
|
|
|
|
|
r = netlink_message_append_sockaddr_union(message, WGPEER_A_ENDPOINT, &peer->endpoint);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
goto cancel;
|
|
|
|
|
}
|
2019-01-14 01:45:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r = sd_netlink_message_open_container(message, WGPEER_A_ALLOWEDIPS);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
goto cancel;
|
|
|
|
|
|
|
|
|
|
LIST_FOREACH(ipmasks, mask, start) {
|
|
|
|
|
r = wireguard_set_ipmask_one(netdev, message, mask, ++j);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
if (r == 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r = sd_netlink_message_close_container(message);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
|
|
|
|
|
|
|
|
|
|
r = sd_netlink_message_close_container(message);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_netdev_error_errno(netdev, r, "Could not add wireguard peer: %m");
|
|
|
|
|
|
|
|
|
|
*mask_start = mask; /* Start next cycle from this mask. */
|
|
|
|
|
return !mask;
|
|
|
|
|
|
|
|
|
|
cancel:
|
|
|
|
|
r = sd_netlink_message_cancel_array(message);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_netdev_error_errno(netdev, r, "Could not cancel wireguard peers: %m");
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int wireguard_set_interface(NetDev *netdev) {
|
2017-12-18 15:20:34 +01:00
|
|
|
|
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
|
2019-01-14 01:45:20 +01:00
|
|
|
|
WireguardIPmask *mask_start = NULL;
|
|
|
|
|
WireguardPeer *peer, *peer_start;
|
2020-06-01 07:22:06 +02:00
|
|
|
|
bool sent_once = false;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
uint32_t serial;
|
2019-01-14 01:45:20 +01:00
|
|
|
|
Wireguard *w;
|
|
|
|
|
int r;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
|
|
|
|
assert(netdev);
|
|
|
|
|
w = WIREGUARD(netdev);
|
|
|
|
|
assert(w);
|
|
|
|
|
|
2020-06-01 07:22:06 +02:00
|
|
|
|
for (peer_start = w->peers; peer_start || !sent_once; ) {
|
2019-01-14 01:45:20 +01:00
|
|
|
|
uint16_t i = 0;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
|
|
|
|
message = sd_netlink_message_unref(message);
|
|
|
|
|
|
|
|
|
|
r = sd_genl_message_new(netdev->manager->genl, SD_GENL_WIREGUARD, WG_CMD_SET_DEVICE, &message);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
|
|
|
|
|
|
|
|
|
|
r = sd_netlink_message_append_string(message, WGDEVICE_A_IFNAME, netdev->ifname);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_netdev_error_errno(netdev, r, "Could not append wireguard interface name: %m");
|
|
|
|
|
|
|
|
|
|
if (peer_start == w->peers) {
|
|
|
|
|
r = sd_netlink_message_append_data(message, WGDEVICE_A_PRIVATE_KEY, &w->private_key, WG_KEY_LEN);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_netdev_error_errno(netdev, r, "Could not append wireguard private key: %m");
|
|
|
|
|
|
|
|
|
|
r = sd_netlink_message_append_u16(message, WGDEVICE_A_LISTEN_PORT, w->port);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_netdev_error_errno(netdev, r, "Could not append wireguard port: %m");
|
|
|
|
|
|
|
|
|
|
r = sd_netlink_message_append_u32(message, WGDEVICE_A_FWMARK, w->fwmark);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_netdev_error_errno(netdev, r, "Could not append wireguard fwmark: %m");
|
|
|
|
|
|
|
|
|
|
r = sd_netlink_message_append_u32(message, WGDEVICE_A_FLAGS, w->flags);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_netdev_error_errno(netdev, r, "Could not append wireguard flags: %m");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r = sd_netlink_message_open_container(message, WGDEVICE_A_PEERS);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_netdev_error_errno(netdev, r, "Could not append wireguard peer attributes: %m");
|
|
|
|
|
|
|
|
|
|
LIST_FOREACH(peers, peer, peer_start) {
|
2019-01-14 01:45:20 +01:00
|
|
|
|
r = wireguard_set_peer_one(netdev, message, peer, ++i, &mask_start);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
if (r < 0)
|
2019-01-14 01:45:20 +01:00
|
|
|
|
return r;
|
|
|
|
|
if (r == 0)
|
2017-12-18 15:20:34 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
2019-01-14 01:45:20 +01:00
|
|
|
|
peer_start = peer; /* Start next cycle from this peer. */
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
|
|
|
|
r = sd_netlink_message_close_container(message);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_netdev_error_errno(netdev, r, "Could not close wireguard container: %m");
|
|
|
|
|
|
|
|
|
|
r = sd_netlink_send(netdev->manager->genl, message, &serial);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_netdev_error_errno(netdev, r, "Could not set wireguard device: %m");
|
2020-06-01 07:22:06 +02:00
|
|
|
|
|
|
|
|
|
sent_once = true;
|
2019-01-14 01:45:20 +01:00
|
|
|
|
}
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
static void wireguard_peer_destroy_callback(WireguardPeer *peer) {
|
|
|
|
|
NetDev *netdev;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
assert(peer);
|
|
|
|
|
assert(peer->wireguard);
|
2018-11-04 12:42:14 +01:00
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
netdev = NETDEV(peer->wireguard);
|
2018-11-04 12:42:14 +01:00
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
if (section_is_invalid(peer->section))
|
|
|
|
|
wireguard_peer_free(peer);
|
|
|
|
|
|
|
|
|
|
netdev_unref(netdev);
|
|
|
|
|
}
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
|
|
|
|
static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) {
|
|
|
|
|
NetDev *netdev = userdata;
|
|
|
|
|
Wireguard *w;
|
|
|
|
|
|
|
|
|
|
assert(netdev);
|
|
|
|
|
w = WIREGUARD(netdev);
|
|
|
|
|
assert(w);
|
|
|
|
|
|
2019-01-13 16:30:37 +01:00
|
|
|
|
if (!netdev_is_managed(netdev))
|
2018-11-03 17:59:46 +01:00
|
|
|
|
return 0;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
assert(set_isempty(w->peers_with_unresolved_endpoint));
|
|
|
|
|
|
|
|
|
|
SWAP_TWO(w->peers_with_unresolved_endpoint, w->peers_with_failed_endpoint);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
|
|
|
|
resolve_endpoints(netdev);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Given the number of retries this function will return will an exponential
|
|
|
|
|
* increasing time in milliseconds to wait starting at 200ms and capped at 25 seconds.
|
|
|
|
|
*/
|
|
|
|
|
static int exponential_backoff_milliseconds(unsigned n_retries) {
|
2019-03-28 17:20:52 +01:00
|
|
|
|
return (2 << MIN(n_retries, 7U)) * 100 * USEC_PER_MSEC;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int wireguard_resolve_handler(sd_resolve_query *q,
|
|
|
|
|
int ret,
|
|
|
|
|
const struct addrinfo *ai,
|
2019-02-28 09:12:02 +01:00
|
|
|
|
WireguardPeer *peer) {
|
2018-12-15 12:57:50 +01:00
|
|
|
|
NetDev *netdev;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
Wireguard *w;
|
|
|
|
|
int r;
|
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
assert(peer);
|
|
|
|
|
assert(peer->wireguard);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
w = peer->wireguard;
|
|
|
|
|
netdev = NETDEV(w);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
2019-01-13 16:30:37 +01:00
|
|
|
|
if (!netdev_is_managed(netdev))
|
2018-11-04 12:42:14 +01:00
|
|
|
|
return 0;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
|
|
|
|
if (ret != 0) {
|
2019-02-28 09:12:02 +01:00
|
|
|
|
log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", peer->endpoint_host, peer->endpoint_port, gai_strerror(ret));
|
|
|
|
|
|
2020-06-05 15:12:29 +02:00
|
|
|
|
r = set_ensure_put(&w->peers_with_failed_endpoint, NULL, peer);
|
2019-02-28 09:12:02 +01:00
|
|
|
|
if (r < 0) {
|
|
|
|
|
log_netdev_error(netdev, "Failed to save a peer, dropping the peer: %m");
|
|
|
|
|
peer->section->invalid = true;
|
|
|
|
|
goto resolve_next;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-18 15:20:34 +01:00
|
|
|
|
} else if ((ai->ai_family == AF_INET && ai->ai_addrlen == sizeof(struct sockaddr_in)) ||
|
2018-11-04 12:42:14 +01:00
|
|
|
|
(ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6)))
|
2019-02-28 09:12:02 +01:00
|
|
|
|
memcpy(&peer->endpoint, ai->ai_addr, ai->ai_addrlen);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
else
|
2019-02-28 09:12:02 +01:00
|
|
|
|
log_netdev_error(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint %s:%s, ignoring the address.",
|
|
|
|
|
peer->endpoint_host, peer->endpoint_port);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
resolve_next:
|
|
|
|
|
if (!set_isempty(w->peers_with_unresolved_endpoint)) {
|
2017-12-18 15:20:34 +01:00
|
|
|
|
resolve_endpoints(netdev);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-14 01:45:20 +01:00
|
|
|
|
(void) wireguard_set_interface(netdev);
|
2019-02-28 09:12:02 +01:00
|
|
|
|
|
|
|
|
|
if (!set_isempty(w->peers_with_failed_endpoint)) {
|
2019-03-01 07:41:47 +01:00
|
|
|
|
usec_t usec;
|
2018-11-03 17:59:46 +01:00
|
|
|
|
|
2017-12-18 15:20:34 +01:00
|
|
|
|
w->n_retries++;
|
2019-03-01 07:41:47 +01:00
|
|
|
|
usec = usec_add(now(CLOCK_MONOTONIC), exponential_backoff_milliseconds(w->n_retries));
|
|
|
|
|
r = event_reset_time(netdev->manager->event, &w->resolve_retry_event_source,
|
|
|
|
|
CLOCK_MONOTONIC, usec, 0, on_resolve_retry, netdev,
|
|
|
|
|
0, "wireguard-resolve-retry", true);
|
2018-11-03 17:59:46 +01:00
|
|
|
|
if (r < 0) {
|
2017-12-18 15:20:34 +01:00
|
|
|
|
log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler: %m");
|
2018-11-03 17:59:46 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2017-12-18 15:20:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void resolve_endpoints(NetDev *netdev) {
|
|
|
|
|
static const struct addrinfo hints = {
|
|
|
|
|
.ai_family = AF_UNSPEC,
|
|
|
|
|
.ai_socktype = SOCK_DGRAM,
|
|
|
|
|
.ai_protocol = IPPROTO_UDP
|
|
|
|
|
};
|
2019-02-28 09:12:02 +01:00
|
|
|
|
WireguardPeer *peer;
|
2018-11-04 12:42:14 +01:00
|
|
|
|
Wireguard *w;
|
2019-05-10 16:31:20 +02:00
|
|
|
|
int r;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
|
|
|
|
assert(netdev);
|
|
|
|
|
w = WIREGUARD(netdev);
|
|
|
|
|
assert(w);
|
|
|
|
|
|
2020-09-08 11:58:29 +02:00
|
|
|
|
SET_FOREACH(peer, w->peers_with_unresolved_endpoint) {
|
2018-12-15 12:57:50 +01:00
|
|
|
|
r = resolve_getaddrinfo(netdev->manager->resolve,
|
|
|
|
|
NULL,
|
2019-02-28 09:12:02 +01:00
|
|
|
|
peer->endpoint_host,
|
|
|
|
|
peer->endpoint_port,
|
2018-12-15 12:57:50 +01:00
|
|
|
|
&hints,
|
|
|
|
|
wireguard_resolve_handler,
|
2019-02-28 09:12:02 +01:00
|
|
|
|
wireguard_peer_destroy_callback,
|
|
|
|
|
peer);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
if (r == -ENOBUFS)
|
|
|
|
|
break;
|
2018-11-04 12:42:14 +01:00
|
|
|
|
if (r < 0) {
|
|
|
|
|
log_netdev_error_errno(netdev, r, "Failed to create resolver: %m");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
2018-11-04 12:42:14 +01:00
|
|
|
|
/* Avoid freeing netdev. It will be unrefed by the destroy callback. */
|
|
|
|
|
netdev_ref(netdev);
|
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
(void) set_remove(w->peers_with_unresolved_endpoint, peer);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int netdev_wireguard_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
|
|
|
|
|
assert(netdev);
|
2019-03-27 14:28:24 +01:00
|
|
|
|
assert(WIREGUARD(netdev));
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
2019-01-14 01:45:20 +01:00
|
|
|
|
(void) wireguard_set_interface(netdev);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
resolve_endpoints(netdev);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-05 10:24:50 +02:00
|
|
|
|
int 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) {
|
|
|
|
|
|
2017-12-18 15:20:34 +01:00
|
|
|
|
uint16_t *s = data;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert(rvalue);
|
|
|
|
|
assert(data);
|
|
|
|
|
|
2019-04-25 00:39:04 +02:00
|
|
|
|
if (isempty(rvalue) || streq(rvalue, "auto")) {
|
|
|
|
|
*s = 0;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r = parse_ip_port(rvalue, s);
|
|
|
|
|
if (r < 0) {
|
2020-07-16 07:48:08 +02:00
|
|
|
|
log_syntax(unit, LOG_WARNING, filename, line, r,
|
2019-04-25 00:39:04 +02:00
|
|
|
|
"Invalid port specification, ignoring assignment: %s", rvalue);
|
|
|
|
|
return 0;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-05 04:35:43 +01:00
|
|
|
|
static int wireguard_decode_key_and_warn(
|
|
|
|
|
const char *rvalue,
|
2019-04-08 17:11:43 +02:00
|
|
|
|
uint8_t ret[static WG_KEY_LEN],
|
2019-03-05 04:35:43 +01:00
|
|
|
|
const char *unit,
|
|
|
|
|
const char *filename,
|
|
|
|
|
unsigned line,
|
|
|
|
|
const char *lvalue) {
|
2019-04-05 10:24:50 +02:00
|
|
|
|
|
2019-07-05 11:29:10 +02:00
|
|
|
|
_cleanup_(erase_and_freep) void *key = NULL;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
size_t len;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert(rvalue);
|
2019-03-05 04:35:43 +01:00
|
|
|
|
assert(ret);
|
|
|
|
|
assert(filename);
|
|
|
|
|
assert(lvalue);
|
|
|
|
|
|
|
|
|
|
if (isempty(rvalue)) {
|
|
|
|
|
memzero(ret, WG_KEY_LEN);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
2019-04-07 20:48:57 +02:00
|
|
|
|
if (!streq(lvalue, "PublicKey"))
|
|
|
|
|
(void) warn_file_is_world_accessible(filename, NULL, unit, line);
|
|
|
|
|
|
2019-04-07 19:48:02 +02:00
|
|
|
|
r = unbase64mem_full(rvalue, strlen(rvalue), true, &key, &len);
|
2020-09-10 06:38:31 +02:00
|
|
|
|
if (r == -ENOMEM)
|
|
|
|
|
return log_oom();
|
2020-09-09 23:06:40 +02:00
|
|
|
|
if (r < 0) {
|
|
|
|
|
log_syntax(unit, LOG_WARNING, filename, line, r,
|
2019-03-04 06:12:03 +01:00
|
|
|
|
"Failed to decode wireguard key provided by %s=, ignoring assignment: %m", lvalue);
|
2020-09-09 23:06:40 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (len != WG_KEY_LEN) {
|
|
|
|
|
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
2019-03-04 06:12:03 +01:00
|
|
|
|
"Wireguard key provided by %s= has invalid length (%zu bytes), ignoring assignment.",
|
|
|
|
|
lvalue, len);
|
2020-09-09 23:06:40 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
2019-03-05 04:35:43 +01:00
|
|
|
|
memcpy(ret, key, WG_KEY_LEN);
|
2019-04-10 07:26:23 +02:00
|
|
|
|
return 0;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-05 10:24:50 +02:00
|
|
|
|
int 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) {
|
|
|
|
|
|
2017-12-18 15:20:34 +01:00
|
|
|
|
Wireguard *w;
|
|
|
|
|
|
|
|
|
|
assert(data);
|
|
|
|
|
w = WIREGUARD(data);
|
|
|
|
|
assert(w);
|
|
|
|
|
|
2020-09-10 06:38:31 +02:00
|
|
|
|
return wireguard_decode_key_and_warn(rvalue, w->private_key, unit, filename, line, lvalue);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-04 06:19:21 +01:00
|
|
|
|
int 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) {
|
|
|
|
|
|
|
|
|
|
_cleanup_free_ char *path = NULL;
|
|
|
|
|
Wireguard *w;
|
|
|
|
|
|
|
|
|
|
assert(data);
|
|
|
|
|
w = WIREGUARD(data);
|
|
|
|
|
assert(w);
|
|
|
|
|
|
|
|
|
|
if (isempty(rvalue)) {
|
|
|
|
|
w->private_key_file = mfree(w->private_key_file);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
path = strdup(rvalue);
|
|
|
|
|
if (!path)
|
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
|
|
if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return free_and_replace(w->private_key_file, path);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-10 06:37:02 +02:00
|
|
|
|
int config_parse_wireguard_peer_key(
|
2019-04-05 10:24:50 +02:00
|
|
|
|
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) {
|
2019-02-28 09:12:02 +01:00
|
|
|
|
|
2020-09-10 06:37:02 +02:00
|
|
|
|
_cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
Wireguard *w;
|
2019-02-28 09:12:02 +01:00
|
|
|
|
int r;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
|
|
|
|
assert(data);
|
|
|
|
|
w = WIREGUARD(data);
|
|
|
|
|
assert(w);
|
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
r = wireguard_peer_new_static(w, filename, section_line, &peer);
|
|
|
|
|
if (r < 0)
|
2020-07-16 07:48:08 +02:00
|
|
|
|
return log_oom();
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
2020-09-10 06:37:02 +02:00
|
|
|
|
r = wireguard_decode_key_and_warn(rvalue,
|
|
|
|
|
streq(lvalue, "PublicKey") ? peer->public_key : peer->preshared_key,
|
|
|
|
|
unit, filename, line, lvalue);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
TAKE_PTR(peer);
|
2020-07-16 07:48:08 +02:00
|
|
|
|
return 0;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-05 10:33:09 +02:00
|
|
|
|
int 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) {
|
|
|
|
|
|
|
|
|
|
_cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
|
|
|
|
|
_cleanup_free_ char *path = NULL;
|
|
|
|
|
Wireguard *w;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert(data);
|
|
|
|
|
w = WIREGUARD(data);
|
|
|
|
|
assert(w);
|
|
|
|
|
|
|
|
|
|
r = wireguard_peer_new_static(w, filename, section_line, &peer);
|
|
|
|
|
if (r < 0)
|
2020-07-16 07:48:08 +02:00
|
|
|
|
return log_oom();
|
2019-04-05 10:33:09 +02:00
|
|
|
|
|
|
|
|
|
if (isempty(rvalue)) {
|
|
|
|
|
peer->preshared_key_file = mfree(peer->preshared_key_file);
|
|
|
|
|
TAKE_PTR(peer);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
path = strdup(rvalue);
|
|
|
|
|
if (!path)
|
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
|
|
if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
free_and_replace(peer->preshared_key_file, path);
|
|
|
|
|
TAKE_PTR(peer);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-05 10:24:50 +02:00
|
|
|
|
int 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) {
|
2019-02-28 09:12:02 +01:00
|
|
|
|
|
|
|
|
|
_cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
union in_addr_union addr;
|
|
|
|
|
unsigned char prefixlen;
|
|
|
|
|
int r, family;
|
|
|
|
|
Wireguard *w;
|
|
|
|
|
WireguardIPmask *ipmask;
|
|
|
|
|
|
|
|
|
|
assert(rvalue);
|
|
|
|
|
assert(data);
|
|
|
|
|
|
|
|
|
|
w = WIREGUARD(data);
|
2019-02-28 09:12:02 +01:00
|
|
|
|
assert(w);
|
|
|
|
|
|
|
|
|
|
r = wireguard_peer_new_static(w, filename, section_line, &peer);
|
|
|
|
|
if (r < 0)
|
2020-07-16 07:48:08 +02:00
|
|
|
|
return log_oom();
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
2020-07-16 07:48:08 +02:00
|
|
|
|
for (const char *p = rvalue;;) {
|
2017-12-18 15:20:34 +01:00
|
|
|
|
_cleanup_free_ char *word = NULL;
|
|
|
|
|
|
2020-07-16 07:48:08 +02:00
|
|
|
|
r = extract_first_word(&p, &word, "," WHITESPACE, 0);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
if (r == 0)
|
|
|
|
|
break;
|
|
|
|
|
if (r == -ENOMEM)
|
|
|
|
|
return log_oom();
|
|
|
|
|
if (r < 0) {
|
2020-07-16 07:48:08 +02:00
|
|
|
|
log_syntax(unit, LOG_WARNING, filename, line, r,
|
2019-02-28 09:12:02 +01:00
|
|
|
|
"Failed to split allowed ips \"%s\" option: %m", rvalue);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen);
|
|
|
|
|
if (r < 0) {
|
2020-07-16 07:48:08 +02:00
|
|
|
|
log_syntax(unit, LOG_WARNING, filename, line, r,
|
2019-02-28 09:12:02 +01:00
|
|
|
|
"Network address is invalid, ignoring assignment: %s", word);
|
|
|
|
|
continue;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-03 13:57:06 +01:00
|
|
|
|
ipmask = new(WireguardIPmask, 1);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
if (!ipmask)
|
|
|
|
|
return log_oom();
|
2018-11-03 13:57:06 +01:00
|
|
|
|
|
|
|
|
|
*ipmask = (WireguardIPmask) {
|
|
|
|
|
.family = family,
|
|
|
|
|
.ip.in6 = addr.in6,
|
|
|
|
|
.cidr = prefixlen,
|
|
|
|
|
};
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
|
|
|
|
LIST_PREPEND(ipmasks, peer->ipmasks, ipmask);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
TAKE_PTR(peer);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-05 10:24:50 +02:00
|
|
|
|
int 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) {
|
2019-02-28 09:12:02 +01:00
|
|
|
|
|
|
|
|
|
_cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
|
|
|
|
|
const char *begin, *end;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
Wireguard *w;
|
|
|
|
|
size_t len;
|
2019-02-28 09:12:02 +01:00
|
|
|
|
int r;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
|
|
|
|
assert(data);
|
|
|
|
|
assert(rvalue);
|
|
|
|
|
|
|
|
|
|
w = WIREGUARD(data);
|
|
|
|
|
assert(w);
|
|
|
|
|
|
|
|
|
|
if (rvalue[0] == '[') {
|
|
|
|
|
begin = &rvalue[1];
|
|
|
|
|
end = strchr(rvalue, ']');
|
|
|
|
|
if (!end) {
|
2020-07-16 07:48:08 +02:00
|
|
|
|
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
2019-02-28 09:12:02 +01:00
|
|
|
|
"Unable to find matching brace of endpoint, ignoring assignment: %s",
|
|
|
|
|
rvalue);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
len = end - begin;
|
|
|
|
|
++end;
|
|
|
|
|
if (*end != ':' || !*(end + 1)) {
|
2020-07-16 07:48:08 +02:00
|
|
|
|
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
2019-02-28 09:12:02 +01:00
|
|
|
|
"Unable to find port of endpoint, ignoring assignment: %s",
|
|
|
|
|
rvalue);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
++end;
|
|
|
|
|
} else {
|
|
|
|
|
begin = rvalue;
|
|
|
|
|
end = strrchr(rvalue, ':');
|
|
|
|
|
if (!end || !*(end + 1)) {
|
2020-07-16 07:48:08 +02:00
|
|
|
|
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
2019-02-28 09:12:02 +01:00
|
|
|
|
"Unable to find port of endpoint, ignoring assignment: %s",
|
|
|
|
|
rvalue);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
len = end - begin;
|
|
|
|
|
++end;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-22 15:55:04 +02:00
|
|
|
|
r = wireguard_peer_new_static(w, filename, section_line, &peer);
|
|
|
|
|
if (r < 0)
|
2020-07-16 07:48:08 +02:00
|
|
|
|
return log_oom();
|
2020-06-22 15:55:04 +02:00
|
|
|
|
|
2019-03-15 15:44:47 +01:00
|
|
|
|
r = free_and_strndup(&peer->endpoint_host, begin, len);
|
|
|
|
|
if (r < 0)
|
2017-12-18 15:20:34 +01:00
|
|
|
|
return log_oom();
|
|
|
|
|
|
2019-03-15 15:44:47 +01:00
|
|
|
|
r = free_and_strdup(&peer->endpoint_port, end);
|
|
|
|
|
if (r < 0)
|
2017-12-18 15:20:34 +01:00
|
|
|
|
return log_oom();
|
|
|
|
|
|
2020-06-05 15:12:29 +02:00
|
|
|
|
r = set_ensure_put(&w->peers_with_unresolved_endpoint, NULL, peer);
|
2019-02-28 09:12:02 +01:00
|
|
|
|
if (r < 0)
|
2018-11-03 13:57:06 +01:00
|
|
|
|
return log_oom();
|
2020-06-22 15:55:04 +02:00
|
|
|
|
TAKE_PTR(peer); /* The peer may already have been in the hash map, that is fine too. */
|
2020-06-05 15:12:29 +02:00
|
|
|
|
|
2017-12-18 15:20:34 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-05 10:24:50 +02:00
|
|
|
|
int 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) {
|
2019-02-28 09:12:02 +01:00
|
|
|
|
|
2020-09-10 06:39:01 +02:00
|
|
|
|
_cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
uint16_t keepalive = 0;
|
|
|
|
|
Wireguard *w;
|
2019-02-28 09:12:02 +01:00
|
|
|
|
int r;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
|
|
|
|
assert(rvalue);
|
|
|
|
|
assert(data);
|
|
|
|
|
|
|
|
|
|
w = WIREGUARD(data);
|
|
|
|
|
assert(w);
|
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
r = wireguard_peer_new_static(w, filename, section_line, &peer);
|
|
|
|
|
if (r < 0)
|
2020-07-16 07:48:08 +02:00
|
|
|
|
return log_oom();
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
|
|
|
|
if (streq(rvalue, "off"))
|
|
|
|
|
keepalive = 0;
|
|
|
|
|
else {
|
|
|
|
|
r = safe_atou16(rvalue, &keepalive);
|
2019-02-28 09:12:02 +01:00
|
|
|
|
if (r < 0) {
|
2020-07-16 07:48:08 +02:00
|
|
|
|
log_syntax(unit, LOG_WARNING, filename, line, r,
|
2020-06-22 15:55:04 +02:00
|
|
|
|
"Failed to parse \"%s\" as keepalive interval (range 0–65535), ignoring assignment: %m",
|
2019-02-28 09:12:02 +01:00
|
|
|
|
rvalue);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2017-12-18 15:20:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
peer->persistent_keepalive_interval = keepalive;
|
2020-09-10 06:39:01 +02:00
|
|
|
|
|
|
|
|
|
TAKE_PTR(peer);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void wireguard_init(NetDev *netdev) {
|
|
|
|
|
Wireguard *w;
|
|
|
|
|
|
|
|
|
|
assert(netdev);
|
|
|
|
|
w = WIREGUARD(netdev);
|
|
|
|
|
assert(w);
|
|
|
|
|
|
|
|
|
|
w->flags = WGDEVICE_F_REPLACE_PEERS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void wireguard_done(NetDev *netdev) {
|
|
|
|
|
Wireguard *w;
|
|
|
|
|
|
|
|
|
|
assert(netdev);
|
|
|
|
|
w = WIREGUARD(netdev);
|
2018-11-03 08:45:17 +01:00
|
|
|
|
assert(w);
|
2017-12-18 15:20:34 +01:00
|
|
|
|
|
2019-03-01 07:41:47 +01:00
|
|
|
|
sd_event_source_unref(w->resolve_retry_event_source);
|
|
|
|
|
|
2019-04-07 19:48:02 +02:00
|
|
|
|
explicit_bzero_safe(w->private_key, WG_KEY_LEN);
|
2019-03-04 06:19:21 +01:00
|
|
|
|
free(w->private_key_file);
|
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
hashmap_free_with_destructor(w->peers_by_section, wireguard_peer_free);
|
|
|
|
|
set_free(w->peers_with_unresolved_endpoint);
|
|
|
|
|
set_free(w->peers_with_failed_endpoint);
|
|
|
|
|
}
|
2018-11-03 08:45:17 +01:00
|
|
|
|
|
2019-04-05 10:28:46 +02:00
|
|
|
|
static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_KEY_LEN]) {
|
2019-07-05 11:29:10 +02:00
|
|
|
|
_cleanup_(erase_and_freep) char *key = NULL;
|
2019-04-05 10:28:46 +02:00
|
|
|
|
size_t key_len;
|
|
|
|
|
int r;
|
2019-03-04 06:19:21 +01:00
|
|
|
|
|
2019-04-05 10:28:46 +02:00
|
|
|
|
if (!filename)
|
2019-03-04 06:19:21 +01:00
|
|
|
|
return 0;
|
|
|
|
|
|
2019-07-12 16:39:07 +02:00
|
|
|
|
assert(dest);
|
|
|
|
|
|
2019-09-11 11:09:55 +02:00
|
|
|
|
(void) warn_file_is_world_accessible(filename, NULL, NULL, 0);
|
|
|
|
|
|
2020-07-17 12:58:19 +02:00
|
|
|
|
r = read_full_file_full(
|
2020-11-04 20:25:06 +01:00
|
|
|
|
AT_FDCWD, filename, UINT64_MAX, SIZE_MAX,
|
2020-07-17 12:58:19 +02:00
|
|
|
|
READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
|
fileio: beef up READ_FULL_FILE_CONNECT_SOCKET to allow setting sender socket name
This beefs up the READ_FULL_FILE_CONNECT_SOCKET logic of
read_full_file_full() a bit: when used a sender socket name may be
specified. If specified as NULL behaviour is as before: the client
socket name is picked by the kernel. But if specified as non-NULL the
client can pick a socket name to use when connecting. This is useful to
communicate a minimal amount of metainformation from client to server,
outside of the transport payload.
Specifically, these beefs up the service credential logic to pass an
abstract AF_UNIX socket name as client socket name when connecting via
READ_FULL_FILE_CONNECT_SOCKET, that includes the requesting unit name
and the eventual credential name. This allows servers implementing the
trivial credential socket logic to distinguish clients: via a simple
getpeername() it can be determined which unit is requesting a
credential, and which credential specifically.
Example: with this patch in place, in a unit file "waldo.service" a
configuration line like the following:
LoadCredential=foo:/run/quux/creds.sock
will result in a connection to the AF_UNIX socket /run/quux/creds.sock,
originating from an abstract namespace AF_UNIX socket:
@$RANDOM/unit/waldo.service/foo
(The $RANDOM is replaced by some randomized string. This is included in
the socket name order to avoid namespace squatting issues: the abstract
socket namespace is open to unprivileged users after all, and care needs
to be taken not to use guessable names)
The services listening on the /run/quux/creds.sock socket may thus
easily retrieve the name of the unit the credential is requested for
plus the credential name, via a simpler getpeername(), discarding the
random preifx and the /unit/ string.
This logic uses "/" as separator between the fields, since both unit
names and credential names appear in the file system, and thus are
designed to use "/" as outer separators. Given that it's a good safe
choice to use as separators here, too avoid any conflicts.
This is a minimal patch only: the new logic is used only for the unit
file credential logic. For other places where we use
READ_FULL_FILE_CONNECT_SOCKET it is probably a good idea to use this
scheme too, but this should be done carefully in later patches, since
the socket names become API that way, and we should determine the right
amount of info to pass over.
2020-11-02 12:07:51 +01:00
|
|
|
|
NULL, &key, &key_len);
|
2019-03-04 06:19:21 +01:00
|
|
|
|
if (r < 0)
|
2019-04-05 10:28:46 +02:00
|
|
|
|
return r;
|
2019-03-04 06:19:21 +01:00
|
|
|
|
|
2019-07-05 11:29:10 +02:00
|
|
|
|
if (key_len != WG_KEY_LEN)
|
|
|
|
|
return -EINVAL;
|
2019-03-04 06:19:21 +01:00
|
|
|
|
|
2019-04-05 10:28:46 +02:00
|
|
|
|
memcpy(dest, key, WG_KEY_LEN);
|
2019-07-05 11:29:10 +02:00
|
|
|
|
return 0;
|
2019-03-04 06:19:21 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-04 06:31:36 +01:00
|
|
|
|
static int wireguard_peer_verify(WireguardPeer *peer) {
|
|
|
|
|
NetDev *netdev = NETDEV(peer->wireguard);
|
2019-04-05 10:33:09 +02:00
|
|
|
|
int r;
|
2019-03-04 06:31:36 +01:00
|
|
|
|
|
|
|
|
|
if (section_is_invalid(peer->section))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
if (eqzero(peer->public_key))
|
|
|
|
|
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
|
|
|
|
"%s: WireGuardPeer section without PublicKey= configured. "
|
|
|
|
|
"Ignoring [WireGuardPeer] section from line %u.",
|
|
|
|
|
peer->section->filename, peer->section->line);
|
|
|
|
|
|
2019-04-05 10:33:09 +02:00
|
|
|
|
r = wireguard_read_key_file(peer->preshared_key_file, peer->preshared_key);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_netdev_error_errno(netdev, r,
|
|
|
|
|
"%s: Failed to read preshared key from '%s'. "
|
|
|
|
|
"Ignoring [WireGuardPeer] section from line %u.",
|
|
|
|
|
peer->section->filename, peer->preshared_key_file,
|
|
|
|
|
peer->section->line);
|
|
|
|
|
|
2019-03-04 06:31:36 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
static int wireguard_verify(NetDev *netdev, const char *filename) {
|
|
|
|
|
WireguardPeer *peer, *peer_next;
|
|
|
|
|
Wireguard *w;
|
2019-03-04 06:19:21 +01:00
|
|
|
|
int r;
|
2018-11-03 08:45:17 +01:00
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
assert(netdev);
|
|
|
|
|
w = WIREGUARD(netdev);
|
|
|
|
|
assert(w);
|
|
|
|
|
|
2019-04-05 10:28:46 +02:00
|
|
|
|
r = wireguard_read_key_file(w->private_key_file, w->private_key);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_netdev_error_errno(netdev, r,
|
|
|
|
|
"Failed to read private key from %s. Dropping network device %s.",
|
|
|
|
|
w->private_key_file, netdev->ifname);
|
2019-03-04 06:31:36 +01:00
|
|
|
|
|
2019-04-05 10:28:46 +02:00
|
|
|
|
if (eqzero(w->private_key))
|
|
|
|
|
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
|
|
|
|
"%s: Missing PrivateKey= or PrivateKeyFile=, "
|
|
|
|
|
"Dropping network device %s.",
|
|
|
|
|
filename, netdev->ifname);
|
2019-03-04 06:19:21 +01:00
|
|
|
|
|
2019-02-28 09:12:02 +01:00
|
|
|
|
LIST_FOREACH_SAFE(peers, peer, peer_next, w->peers)
|
2019-03-04 06:31:36 +01:00
|
|
|
|
if (wireguard_peer_verify(peer) < 0)
|
2019-02-28 09:12:02 +01:00
|
|
|
|
wireguard_peer_free(peer);
|
|
|
|
|
|
|
|
|
|
return 0;
|
2017-12-18 15:20:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const NetDevVTable wireguard_vtable = {
|
|
|
|
|
.object_size = sizeof(Wireguard),
|
2019-11-22 11:43:26 +01:00
|
|
|
|
.sections = NETDEV_COMMON_SECTIONS "WireGuard\0WireGuardPeer\0",
|
2017-12-18 15:20:34 +01:00
|
|
|
|
.post_create = netdev_wireguard_post_create,
|
|
|
|
|
.init = wireguard_init,
|
|
|
|
|
.done = wireguard_done,
|
|
|
|
|
.create_type = NETDEV_CREATE_INDEPENDENT,
|
2019-02-28 09:12:02 +01:00
|
|
|
|
.config_verify = wireguard_verify,
|
2019-05-23 04:27:12 +02:00
|
|
|
|
.generate_mac = true,
|
2017-12-18 15:20:34 +01:00
|
|
|
|
};
|