#include "sd-resolve.h"
#include "alloc-util.h"
+#include "creds-util.h"
#include "dns-domain.h"
#include "event-util.h"
#include "fd-util.h"
#include "networkd-route-util.h"
#include "networkd-route.h"
#include "networkd-util.h"
+#include "parse-helpers.h"
#include "parse-util.h"
#include "path-util.h"
#include "random-util.h"
static void wireguard_resolve_endpoints(NetDev *netdev);
static int peer_resolve_endpoint(WireguardPeer *peer);
-static WireguardPeer* wireguard_peer_free(WireguardPeer *peer) {
- WireguardIPmask *mask;
+static void wireguard_peer_clear_ipmasks(WireguardPeer *peer) {
+ assert(peer);
+ LIST_CLEAR(ipmasks, peer->ipmasks, free);
+}
+
+static WireguardPeer* wireguard_peer_free(WireguardPeer *peer) {
if (!peer)
return NULL;
config_section_free(peer->section);
- while ((mask = peer->ipmasks)) {
- LIST_REMOVE(ipmasks, peer->ipmasks, mask);
- free(mask);
- }
+ wireguard_peer_clear_ipmasks(peer);
free(peer->endpoint_host);
free(peer->endpoint_port);
static int wireguard_set_interface(NetDev *netdev) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
WireguardIPmask *mask_start = NULL;
- WireguardPeer *peer_start;
bool sent_once = false;
uint32_t serial;
- Wireguard *w;
+ Wireguard *w = WIREGUARD(netdev);
int r;
- assert(netdev);
- w = WIREGUARD(netdev);
- assert(w);
-
- for (peer_start = w->peers; peer_start || !sent_once; ) {
+ for (WireguardPeer *peer_start = w->peers; peer_start || !sent_once; ) {
uint16_t i = 0;
message = sd_netlink_message_unref(message);
}
static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) {
- WireguardPeer *peer = userdata;
+ WireguardPeer *peer = ASSERT_PTR(userdata);
NetDev *netdev;
- assert(peer);
assert(peer->wireguard);
netdev = NETDEV(peer->wireguard);
const struct addrinfo *ai,
void *userdata) {
- WireguardPeer *peer = userdata;
+ WireguardPeer *peer = ASSERT_PTR(userdata);
NetDev *netdev;
int r;
- assert(peer);
assert(peer->wireguard);
netdev = NETDEV(peer->wireguard);
if (peer->n_retries > 0) {
r = event_reset_time_relative(netdev->manager->event,
&peer->resolve_retry_event_source,
- clock_boottime_or_monotonic(),
+ CLOCK_BOOTTIME,
peer_next_resolve_usec(peer), 0,
on_resolve_retry, peer, 0, "wireguard-resolve-retry", true);
if (r < 0)
/* Not necessary to resolve the endpoint. */
return 0;
- if (event_source_is_enabled(peer->resolve_retry_event_source) > 0)
+ if (sd_event_source_get_enabled(peer->resolve_retry_event_source, NULL) > 0)
/* Timer event source is enabled. The endpoint will be resolved later. */
return 0;
}
static void wireguard_resolve_endpoints(NetDev *netdev) {
- Wireguard *w;
-
- assert(netdev);
- w = WIREGUARD(netdev);
- assert(w);
+ Wireguard *w = WIREGUARD(netdev);
LIST_FOREACH(peers, peer, w->peers)
if (peer_resolve_endpoint(peer) == -ENOBUFS)
}
static int netdev_wireguard_post_create(NetDev *netdev, Link *link) {
- assert(netdev);
assert(WIREGUARD(netdev));
(void) wireguard_set_interface(netdev);
void *data,
void *userdata) {
- uint16_t *s = data;
+ uint16_t *s = ASSERT_PTR(data);
int r;
assert(rvalue);
- assert(data);
if (isempty(rvalue) || streq(rvalue, "auto")) {
*s = 0;
const char *lvalue) {
_cleanup_(erase_and_freep) void *key = NULL;
+ _cleanup_(erase_and_freep) char *cred = NULL;
+ const char *cred_name;
size_t len;
int r;
return 0;
}
- if (!streq(lvalue, "PublicKey"))
+ cred_name = startswith(rvalue, "@");
+ if (cred_name) {
+ r = read_credential(cred_name, (void**) &cred, /* ret_size = */ NULL);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to read credential for wireguard key (%s=), ignoring assignment: %m",
+ lvalue);
+ return 0;
+ }
+
+ } else if (!streq(lvalue, "PublicKey"))
(void) warn_file_is_world_accessible(filename, NULL, unit, line);
- r = unbase64mem_full(rvalue, strlen(rvalue), true, &key, &len);
+ r = unbase64mem_full(cred ?: rvalue, SIZE_MAX, /* secure = */ true, &key, &len);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
void *data,
void *userdata) {
- Wireguard *w;
-
- assert(data);
- w = WIREGUARD(data);
- assert(w);
+ Wireguard *w = WIREGUARD(data);
return wireguard_decode_key_and_warn(rvalue, w->private_key, unit, filename, line, lvalue);
}
void *data,
void *userdata) {
+ Wireguard *w = WIREGUARD(data);
_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);
void *data,
void *userdata) {
+ Wireguard *w = WIREGUARD(data);
_cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = 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)
return log_oom();
void *data,
void *userdata) {
+ Wireguard *w = WIREGUARD(data);
_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)
return log_oom();
void *data,
void *userdata) {
+ assert(rvalue);
+
+ Wireguard *w = WIREGUARD(data);
_cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
union in_addr_union addr;
unsigned char prefixlen;
int r, family;
- Wireguard *w;
WireguardIPmask *ipmask;
- assert(rvalue);
- assert(data);
-
- w = WIREGUARD(data);
- assert(w);
-
r = wireguard_peer_new_static(w, filename, section_line, &peer);
if (r < 0)
return log_oom();
+ if (isempty(rvalue)) {
+ wireguard_peer_clear_ipmasks(peer);
+ TAKE_PTR(peer);
+ return 0;
+ }
+
for (const char *p = rvalue;;) {
_cleanup_free_ char *word = NULL;
union in_addr_union masked;
masked = addr;
assert_se(in_addr_mask(family, &masked, prefixlen) >= 0);
- if (!in_addr_equal(family, &masked, &addr)) {
- _cleanup_free_ char *buf = NULL;
-
- (void) in_addr_prefix_to_string(family, &masked, prefixlen, &buf);
+ if (!in_addr_equal(family, &masked, &addr))
log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Specified address '%s' is not properly masked, assuming '%s'.", word, strna(buf));
- }
+ "Specified address '%s' is not properly masked, assuming '%s'.",
+ word,
+ IN_ADDR_PREFIX_TO_STRING(family, &masked, prefixlen));
ipmask = new(WireguardIPmask, 1);
if (!ipmask)
void *data,
void *userdata) {
+ Wireguard *w = WIREGUARD(userdata);
_cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
- _cleanup_free_ char *host = NULL;
- union in_addr_union addr;
- const char *p;
+ _cleanup_free_ char *cred = NULL;
+ const char *cred_name, *endpoint;
uint16_t port;
- Wireguard *w;
- int family, r;
+ int r;
assert(filename);
assert(rvalue);
- assert(userdata);
-
- w = WIREGUARD(userdata);
- assert(w);
r = wireguard_peer_new_static(w, filename, section_line, &peer);
if (r < 0)
return log_oom();
- r = in_addr_port_ifindex_name_from_string_auto(rvalue, &family, &addr, &port, NULL, NULL);
+ cred_name = startswith(rvalue, "@");
+ if (cred_name) {
+ r = read_credential(cred_name, (void**) &cred, /* ret_size = */ NULL);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to read credential for wireguard endpoint, ignoring assignment: %m");
+ return 0;
+ }
+
+ endpoint = strstrip(cred);
+ } else
+ endpoint = rvalue;
+
+ union in_addr_union addr;
+ int family;
+
+ r = in_addr_port_ifindex_name_from_string_auto(endpoint, &family, &addr, &port, NULL, NULL);
if (r >= 0) {
if (family == AF_INET)
peer->endpoint.in = (struct sockaddr_in) {
return 0;
}
- p = strrchr(rvalue, ':');
+ _cleanup_free_ char *host = NULL;
+ const char *p;
+
+ p = strrchr(endpoint, ':');
if (!p) {
log_syntax(unit, LOG_WARNING, filename, line, 0,
"Unable to find port of endpoint, ignoring assignment: %s",
- rvalue);
+ rvalue); /* We log the original assignment instead of resolved credential here,
+ as the latter might be previously encrypted and we'd expose them in
+ unprotected logs otherwise. */
return 0;
}
- host = strndup(rvalue, p - rvalue);
+ host = strndup(endpoint, p - endpoint);
if (!host)
return log_oom();
+ p++;
if (!dns_name_is_valid(host)) {
log_syntax(unit, LOG_WARNING, filename, line, 0,
return 0;
}
- p++;
r = parse_ip_port(p, &port);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
void *data,
void *userdata) {
+ assert(rvalue);
+
+ Wireguard *w = WIREGUARD(data);
_cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
uint16_t keepalive = 0;
- Wireguard *w;
int r;
- assert(rvalue);
- assert(data);
-
- w = WIREGUARD(data);
- assert(w);
-
r = wireguard_peer_new_static(w, filename, section_line, &peer);
if (r < 0)
return log_oom();
void *data,
void *userdata) {
- NetDev *netdev = userdata;
- uint32_t *table = data;
+ NetDev *netdev = ASSERT_PTR(userdata);
+ uint32_t *table = ASSERT_PTR(data);
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
- assert(data);
- assert(userdata);
if (isempty(rvalue) || parse_boolean(rvalue) == 0) {
*table = 0; /* Disabled. */
void *data,
void *userdata) {
+ Wireguard *w = WIREGUARD(userdata);
_cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
- NetDev *netdev = userdata;
- Wireguard *w;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
- assert(netdev);
- assert(netdev->manager);
-
- w = WIREGUARD(netdev);
- assert(w);
+ assert(NETDEV(w)->manager);
r = wireguard_peer_new_static(w, filename, section_line, &peer);
if (r < 0)
return 0;
}
- r = manager_get_route_table_from_string(netdev->manager, rvalue, &peer->route_table);
+ r = manager_get_route_table_from_string(NETDEV(w)->manager, rvalue, &peer->route_table);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to parse %s=, ignoring assignment: %s",
void *data,
void *userdata) {
- uint32_t *priority = data;
+ uint32_t *priority = ASSERT_PTR(data);
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
- assert(data);
if (isempty(rvalue)) {
*priority = 0;
}
static void wireguard_init(NetDev *netdev) {
- Wireguard *w;
-
- assert(netdev);
- w = WIREGUARD(netdev);
- assert(w);
+ Wireguard *w = WIREGUARD(netdev);
w->flags = WGDEVICE_F_REPLACE_PEERS;
}
static void wireguard_done(NetDev *netdev) {
- Wireguard *w;
-
- assert(netdev);
- w = WIREGUARD(netdev);
- assert(w);
+ Wireguard *w = WIREGUARD(netdev);
explicit_bzero_safe(w->private_key, WG_KEY_LEN);
free(w->private_key_file);
assert(dest);
- (void) warn_file_is_world_accessible(filename, NULL, NULL, 0);
-
r = read_full_file_full(
- AT_FDCWD, filename, UINT64_MAX, SIZE_MAX,
- READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
+ AT_FDCWD, filename, UINT64_MAX, WG_KEY_LEN,
+ READ_FULL_FILE_SECURE |
+ READ_FULL_FILE_UNBASE64 |
+ READ_FULL_FILE_WARN_WORLD_READABLE |
+ READ_FULL_FILE_CONNECT_SOCKET |
+ READ_FULL_FILE_FAIL_WHEN_LARGER,
NULL, &key, &key_len);
if (r < 0)
return r;
return 0;
}
-static int wireguard_verify(NetDev *netdev, const char *filename) {
- Wireguard *w;
+static int wireguard_read_default_key_cred(NetDev *netdev, const char *filename) {
+ Wireguard *w = WIREGUARD(netdev);
+ _cleanup_free_ char *config_name = NULL;
int r;
- assert(netdev);
- w = WIREGUARD(netdev);
- assert(w);
+ assert(filename);
+
+ r = path_extract_filename(filename, &config_name);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r,
+ "%s: Failed to extract config name, ignoring network device: %m",
+ filename);
+
+ char *p = endswith(config_name, ".netdev");
+ if (!p)
+ /* Fuzzer run? Then we just ignore this device. */
+ return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
+ "%s: Invalid netdev config name, refusing default key lookup.",
+ filename);
+ *p = '\0';
+
+ _cleanup_(erase_and_freep) char *cred = NULL;
+
+ r = read_credential(strjoina("network.wireguard.private.", config_name), (void**) &cred, /* ret_size = */ NULL);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r,
+ "%s: No private key specified and default key isn't available, "
+ "ignoring network device: %m",
+ filename);
+
+ _cleanup_(erase_and_freep) void *key = NULL;
+ size_t len;
+
+ r = unbase64mem_full(cred, SIZE_MAX, /* secure = */ true, &key, &len);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r,
+ "%s: No private key specified and default key cannot be parsed, "
+ "ignoring network device: %m",
+ filename);
+ if (len != WG_KEY_LEN)
+ return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
+ "%s: No private key specified and default key is invalid. "
+ "Ignoring network device.",
+ filename);
+
+ memcpy(w->private_key, key, WG_KEY_LEN);
+ return 0;
+}
+
+static int wireguard_verify(NetDev *netdev, const char *filename) {
+ Wireguard *w = WIREGUARD(netdev);
+ int r;
r = wireguard_read_key_file(w->private_key_file, w->private_key);
if (r < 0)
"Failed to read private key from %s. Ignoring network device.",
w->private_key_file);
- if (eqzero(w->private_key))
- return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
- "%s: Missing PrivateKey= or PrivateKeyFile=, "
- "Ignoring network device.", filename);
+ if (eqzero(w->private_key)) {
+ r = wireguard_read_default_key_cred(netdev, filename);
+ if (r < 0)
+ return r;
+ }
LIST_FOREACH(peers, peer, w->peers) {
if (wireguard_peer_verify(peer) < 0) {
continue;
LIST_FOREACH(ipmasks, ipmask, peer->ipmasks) {
- _cleanup_(route_freep) Route *route = NULL;
+ _cleanup_(route_unrefp) Route *route = NULL;
r = route_new(&route);
if (r < 0)
return log_oom();
+ /* For route_section_verify() below. */
+ r = config_section_new(peer->section->filename, peer->section->line, &route->section);
+ if (r < 0)
+ return log_oom();
+
+ route->source = NETWORK_CONFIG_SOURCE_STATIC;
route->family = ipmask->family;
route->dst = ipmask->ip;
route->dst_prefixlen = ipmask->cidr;
- route->scope = RT_SCOPE_UNIVERSE;
route->protocol = RTPROT_STATIC;
+ route->protocol_set = true;
route->table = peer->route_table_set ? peer->route_table : w->route_table;
+ route->table_set = true;
route->priority = peer->route_priority_set ? peer->route_priority : w->route_priority;
- if (route->priority == 0 && route->family == AF_INET6)
- route->priority = IP6_RT_PRIO_USER;
- route->source = NETWORK_CONFIG_SOURCE_STATIC;
+ route->priority_set = true;
- r = set_ensure_consume(&w->routes, &route_hash_ops, TAKE_PTR(route));
+ if (route_section_verify(route) < 0)
+ continue;
+
+ r = set_ensure_put(&w->routes, &route_hash_ops, route);
if (r < 0)
return log_oom();
+ if (r == 0)
+ continue;
+
+ route->wireguard = w;
+ TAKE_PTR(route);
}
}