wireguard: merge branch 'th/wireguard-pt3'

https://github.com/NetworkManager/NetworkManager/pull/295
This commit is contained in:
Thomas Haller 2019-02-22 13:54:48 +01:00
commit 4aaa0ed482
30 changed files with 5274 additions and 31 deletions

View file

@ -670,6 +670,7 @@ libnm_core_lib_h_pub_real = \
libnm-core/nm-setting-wifi-p2p.h \
libnm-core/nm-setting-wimax.h \
libnm-core/nm-setting-wired.h \
libnm-core/nm-setting-wireguard.h \
libnm-core/nm-setting-wireless-security.h \
libnm-core/nm-setting-wireless.h \
libnm-core/nm-setting-wpan.h \
@ -739,6 +740,7 @@ libnm_core_lib_c_settings_real = \
libnm-core/nm-setting-wifi-p2p.c \
libnm-core/nm-setting-wimax.c \
libnm-core/nm-setting-wired.c \
libnm-core/nm-setting-wireguard.c \
libnm-core/nm-setting-wireless-security.c \
libnm-core/nm-setting-wireless.c \
libnm-core/nm-setting-wpan.c

View file

@ -177,6 +177,7 @@ EXTRA_DIST += \
examples/python/gi/get_ips.py \
examples/python/gi/list-connections.py \
examples/python/gi/nm-connection-update-stable-id.py \
examples/python/gi/nm-wg-set \
examples/python/gi/setting-user-data.py \
examples/python/gi/show-wifi-networks.py \
examples/python/gi/update-ip4-method.py \

3
NEWS
View file

@ -14,6 +14,9 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
* Use a new type of secret-keys that combines the secret value with /etc/machine-id.
This way when cloning a VM it suffices to change machine-id to generate different
addresses.
* Add support for WireGuard VPN tunnels to NetworkManager. D-Bus API and libnm
support all options. nmcli supports creating and managing WireGuard profiles,
with the exception of configuring and showing peers.
The following changes were backported to 1.14.x releases between 1.14.0
and 1.14.2 are also present in NetworkManager-1.14:

View file

@ -822,6 +822,7 @@ const NmcMetaGenericInfo *const metagen_con_active_vpn[_NMC_GENERIC_INFO_TYPE_CO
NM_SETTING_VXLAN_SETTING_NAME"," \
NM_SETTING_WPAN_SETTING_NAME","\
NM_SETTING_6LOWPAN_SETTING_NAME","\
NM_SETTING_WIREGUARD_SETTING_NAME","\
NM_SETTING_PROXY_SETTING_NAME"," \
NM_SETTING_TC_CONFIG_SETTING_NAME"," \
NM_SETTING_SRIOV_SETTING_NAME"," \

View file

@ -7504,6 +7504,28 @@ static const NMMetaPropertyInfo *const property_infos_WIRED[] = {
NULL
};
#undef _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_WIREGUARD
static const NMMetaPropertyInfo *const property_infos_WIREGUARD[] = {
PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_PRIVATE_KEY,
.is_secret = TRUE,
.property_type = &_pt_gobject_string,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS,
.property_type = &_pt_gobject_secret_flags,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_LISTEN_PORT,
.property_type = &_pt_gobject_int,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_FWMARK,
.property_type = &_pt_gobject_int,
.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int, \
.base = 16,
),
),
NULL
};
#undef _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_WIRELESS
static const NMMetaPropertyInfo *const property_infos_WIRELESS[] = {
@ -8001,6 +8023,7 @@ _setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN)
#define SETTING_PRETTY_NAME_WIFI_P2P N_("Wi-Fi P2P connection")
#define SETTING_PRETTY_NAME_WIMAX N_("WiMAX connection")
#define SETTING_PRETTY_NAME_WIRED N_("Wired Ethernet")
#define SETTING_PRETTY_NAME_WIREGUARD N_("WireGuard VPN settings")
#define SETTING_PRETTY_NAME_WIRELESS N_("Wi-Fi connection")
#define SETTING_PRETTY_NAME_WIRELESS_SECURITY N_("Wi-Fi security settings")
#define SETTING_PRETTY_NAME_WPAN N_("WPAN settings")
@ -8264,6 +8287,12 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = {
NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE),
),
),
SETTING_INFO (WIREGUARD,
.valid_parts = NM_META_SETTING_VALID_PARTS (
NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE),
NM_META_SETTING_VALID_PART_ITEM (WIREGUARD, TRUE),
),
),
SETTING_INFO (WIRELESS,
.alias = "wifi",
.valid_parts = NM_META_SETTING_VALID_PARTS (

View file

@ -214,6 +214,32 @@ _secret_real_new_vpn_secret (const char *pretty_name,
return &real->base;
}
static NMSecretAgentSimpleSecret *
_secret_real_new_wireguard_peer_psk (NMSettingWireGuard *s_wg,
const char *public_key,
const char *preshared_key)
{
SecretReal *real;
nm_assert (NM_IS_SETTING_WIREGUARD (s_wg));
nm_assert (public_key);
real = g_slice_new (SecretReal);
*real = (SecretReal) {
.base.secret_type = NM_SECRET_AGENT_SECRET_TYPE_WIREGUARD_PEER_PSK,
.base.pretty_name = g_strdup_printf (_("Preshared-key for %s"),
public_key),
.base.entry_id = g_strdup_printf (NM_SETTING_WIREGUARD_SETTING_NAME"."NM_SETTING_WIREGUARD_PEERS".%s."NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY,
public_key),
.base.value = g_strdup (preshared_key),
.base.is_secret = TRUE,
.base.no_prompt_entry_id = TRUE,
.setting = NM_SETTING (g_object_ref (s_wg)),
.property = g_strdup (public_key),
};
return &real->base;
}
/*****************************************************************************/
static gboolean
@ -405,8 +431,8 @@ add_vpn_secret_helper (GPtrArray *secrets, NMSettingVpn *s_vpn, const char *name
static gboolean
add_vpn_secrets (RequestData *request,
GPtrArray *secrets,
char **msg)
GPtrArray *secrets,
char **msg)
{
NMSettingVpn *s_vpn = nm_connection_get_setting_vpn (request->connection);
const VpnPasswordName *secret_names, *p;
@ -435,6 +461,74 @@ add_vpn_secrets (RequestData *request,
return TRUE;
}
static gboolean
add_wireguard_secrets (RequestData *request,
GPtrArray *secrets,
char **msg,
GError **error)
{
NMSettingWireGuard *s_wg;
NMSecretAgentSimpleSecret *secret;
guint i;
s_wg = NM_SETTING_WIREGUARD (nm_connection_get_setting (request->connection, NM_TYPE_SETTING_WIREGUARD));
if (!s_wg) {
g_set_error (error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED,
"Cannot service a WireGuard secrets request %s for a connection without WireGuard settings",
request->request_id);
return FALSE;
}
if ( !request->hints
|| !request->hints[0]
|| g_strv_contains (NM_CAST_STRV_CC (request->hints), NM_SETTING_WIREGUARD_PRIVATE_KEY)) {
secret = _secret_real_new_plain (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
_("WireGuard private-key"),
NM_SETTING (s_wg),
NM_SETTING_WIREGUARD_PRIVATE_KEY);
g_ptr_array_add (secrets, secret);
}
if (request->hints) {
for (i = 0; request->hints[i]; i++) {
NMWireGuardPeer *peer;
const char *name = request->hints[i];
gs_free char *public_key = NULL;
if (nm_streq (name, NM_SETTING_WIREGUARD_PRIVATE_KEY))
continue;
if (NM_STR_HAS_PREFIX (name, NM_SETTING_WIREGUARD_PEERS".")) {
const char *tmp;
tmp = &name[NM_STRLEN (NM_SETTING_WIREGUARD_PEERS".")];
if (NM_STR_HAS_SUFFIX (tmp, "."NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY)) {
public_key = g_strndup (tmp,
strlen (tmp) - NM_STRLEN ("."NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY));
}
}
if (!public_key)
continue;
peer = nm_setting_wireguard_get_peer_by_public_key (s_wg, public_key, NULL);
g_ptr_array_add (secrets, _secret_real_new_wireguard_peer_psk (s_wg,
( peer
? nm_wireguard_peer_get_public_key (peer)
: public_key),
( peer
? nm_wireguard_peer_get_preshared_key (peer)
: NULL)));
}
}
*msg = g_strdup_printf (_("Secrets are required to connect WireGuard VPN '%s'"),
nm_connection_get_id (request->connection));
return TRUE;
}
typedef struct {
GPid auth_dialog_pid;
GString *auth_dialog_response;
@ -820,6 +914,10 @@ request_secrets_from_ui (RequestData *request)
if (!add_8021x_secrets (request, secrets))
goto out_fail;
}
} else if (nm_connection_is_type (request->connection, NM_SETTING_WIREGUARD_SETTING_NAME)) {
title = _("WireGuard VPN secret");
if (!add_wireguard_secrets (request, secrets, &msg, &error))
goto out_fail_error;
} else if (nm_connection_is_type (request->connection, NM_SETTING_CDMA_SETTING_NAME)) {
NMSettingCdma *s_cdma = nm_connection_get_setting_cdma (request->connection);
@ -980,10 +1078,13 @@ nm_secret_agent_simple_response (NMSecretAgentSimple *self,
if (secrets) {
GVariantBuilder conn_builder, *setting_builder;
GVariantBuilder vpn_secrets_builder;
GVariantBuilder wg_secrets_builder;
GVariantBuilder wg_peer_builder;
GHashTable *settings;
GHashTableIter iter;
const char *name;
gboolean has_vpn = FALSE;
gboolean has_wg = FALSE;
settings = g_hash_table_new (nm_str_hash, g_str_equal);
for (i = 0; i < secrets->len; i++) {
@ -1011,6 +1112,19 @@ nm_secret_agent_simple_response (NMSecretAgentSimple *self,
g_variant_builder_add (&vpn_secrets_builder, "{ss}",
secret->property, secret->base.value);
break;
case NM_SECRET_AGENT_SECRET_TYPE_WIREGUARD_PEER_PSK:
if (!has_wg) {
g_variant_builder_init (&wg_secrets_builder, G_VARIANT_TYPE ("aa{sv}"));
has_wg = TRUE;
}
g_variant_builder_init (&wg_peer_builder, G_VARIANT_TYPE ("a{sv}"));
g_variant_builder_add (&wg_peer_builder, "{sv}",
NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY, g_variant_new_string (secret->property));
g_variant_builder_add (&wg_peer_builder, "{sv}",
NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, g_variant_new_string (secret->base.value));
g_variant_builder_add (&wg_secrets_builder, "a{sv}",
&wg_peer_builder);
break;
}
}
@ -1020,6 +1134,12 @@ nm_secret_agent_simple_response (NMSecretAgentSimple *self,
g_variant_builder_end (&vpn_secrets_builder));
}
if (has_wg) {
g_variant_builder_add (setting_builder, "{sv}",
NM_SETTING_WIREGUARD_PEERS,
g_variant_builder_end (&wg_secrets_builder));
}
g_variant_builder_init (&conn_builder, NM_VARIANT_TYPE_CONNECTION);
g_hash_table_iter_init (&iter, settings);
while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &setting_builder))

View file

@ -25,6 +25,7 @@ typedef enum {
NM_SECRET_AGENT_SECRET_TYPE_PROPERTY,
NM_SECRET_AGENT_SECRET_TYPE_SECRET,
NM_SECRET_AGENT_SECRET_TYPE_VPN_SECRET,
NM_SECRET_AGENT_SECRET_TYPE_WIREGUARD_PEER_PSK,
} NMSecretAgentSecretType;
typedef struct {

View file

@ -362,6 +362,10 @@
#define DESCRIBE_DOC_NM_SETTING_WIFI_P2P_WPS_METHOD N_("Flags indicating which mode of WPS is to be used. There's little point in changing the default setting as NetworkManager will automatically determine the best method to use.")
#define DESCRIBE_DOC_NM_SETTING_WIMAX_MAC_ADDRESS N_("If specified, this connection will only apply to the WiMAX device whose MAC address matches. This property does not change the MAC address of the device (known as MAC spoofing). Deprecated: 1")
#define DESCRIBE_DOC_NM_SETTING_WIMAX_NETWORK_NAME N_("Network Service Provider (NSP) name of the WiMAX network this connection should use. Deprecated: 1")
#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_FWMARK N_("The use of fwmark is optional and is by default off. Setting it to 0 disables it. Otherwise it is a 32-bit fwmark for outgoing packets.")
#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_LISTEN_PORT N_("The listen-port. If listen-port is not specified, the port will be chosen randomly when the interface comes up.")
#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PRIVATE_KEY N_("The 256 bit private-key in base64 encoding.")
#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS N_("Flags indicating how to handle the \"private-key\" property.")
#define DESCRIBE_DOC_NM_SETTING_WPAN_CHANNEL N_("IEEE 802.15.4 channel. A positive integer or -1, meaning \"do not set, use whatever the device is already set to\".")
#define DESCRIBE_DOC_NM_SETTING_WPAN_MAC_ADDRESS N_("If specified, this connection will only apply to the IEEE 802.15.4 (WPAN) MAC layer device whose permanent MAC address matches.")
#define DESCRIBE_DOC_NM_SETTING_WPAN_PAGE N_("IEEE 802.15.4 channel page. A positive integer or -1, meaning \"do not set, use whatever the device is already set to\".")

View file

@ -234,6 +234,7 @@ print ("NetworkManager version " + client.get_version())]]></programlisting></in
<xi:include href="xml/nm-setting-wifi-p2p.xml"/>
<xi:include href="xml/nm-setting-wimax.xml"/>
<xi:include href="xml/nm-setting-wired.xml"/>
<xi:include href="xml/nm-setting-wireguard.xml"/>
<xi:include href="xml/nm-setting-wireless-security.xml"/>
<xi:include href="xml/nm-setting-wireless.xml"/>
<xi:include href="xml/nm-setting-wpan.xml"/>

423
examples/python/gi/nm-wg-set Executable file
View file

@ -0,0 +1,423 @@
#!/usr/bin/env python
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Copyright 2018 - 2019 Red Hat, Inc.
# nm-wg-set: modify an existing WireGuard connection profile.
#
# $ nm-wg-set [id|uuid|interface] ID [wg-args...]
#
# The arguments to set the parameters are like the set parameters from `man 8 wg`.
# For example:
#
# $ nm-wg-set wg0 peer wN8G5HpphoXOGkiXTgBPyr9BhrRm2z9JEI6BiH6fB0g= preshared-key <(wg genpsk)
#
# extra, script specific arguments:
# - private-key-flags
# - preshared-key-flags
#
# Note that the arguments have some simliarities to `wg set` command. But this
# script only modify the connection profile in NetworkManager. They don't (re)activate
# the profile and thus the changes only result in the configuration of the kernel interface
# after activating the profile. Use `nmcli connection up` for that.
#
# The example script does not support creating or deleting the WireGuard profile itself. It also
# does not support modifying other settings of the connection profile, like the IP address configuation.
# For that also use nmcli. For example:
#
# PROFILE=wg0
#
# # create the WireGuard profile with nmcli
# PRIVKEY_FILE=/tmp/wg.key
# (umask 077; rm -f "$PRIVKEY_FILE"; wg genkey > "$PRIVKEY_FILE")
# IFNAME=wg0
# PUBKEY=$(wg pubkey < "$PRIVKEY_FILE")
# IP4ADDR=192.168.99.5/24
# IP4GW=192.168.99.1
# nmcli connection delete id "$PROFILE"
# nmcli connection add \
# type wireguard \
# con-name "$PROFILE" \
# ifname "$IFNAME" \
# connection.stable-id "$PROFILE-$PUBKEY" \
# ipv4.method manual \
# ipv4.addresses "$IP4ADDR" \
# ipv4.gateway "$IP4GW" \
# ipv4.never-default yes \
# ipv6.method link-local \
# wireguard.listen-port 0 \
# wireguard.fwmark 0 \
# wireguard.private-key '' \
# wireguard.private-key-flags 0
# nmcli connection up \
# id "$PROFILE" \
# passwd-file <(echo "wireguard.private-key:$(cat "$PRIVKEY_FILE")")
#
# # modify the WireGuard profile with the script
# nm-wg-set id "$PROFILE" $WG_ARGS
import sys
import re
import gi
gi.require_version('NM', '1.0')
from gi.repository import NM
class MyError(Exception):
pass
def pr(v):
import pprint
pprint.pprint(v, indent=4, depth=5, width=60)
###############################################################################
def connection_is_wireguard(conn):
s_con = conn.get_setting(NM.SettingConnection)
return s_con \
and s_con.get_connection_type() == NM.SETTING_WIREGUARD_SETTING_NAME \
and conn.get_setting(NM.SettingWireGuard)
def connection_to_str(conn):
if connection_is_wireguard(conn):
iface = conn.get_setting(NM.SettingConnection).get_interface_name()
if iface:
extra = ', interface: "%s"' % (iface)
else:
extra = ''
else:
extra = ', type: %s' % (conn.get_setting(NM.SettingConnection).get_connection_type())
return '"%s" (%s%s)' % (conn.get_id(), conn.get_uuid(), extra)
def connections_find(connections, con_spec, con_id):
connections = list(sorted(connections, key=connection_to_str))
l = []
if con_spec in [None, 'id']:
for c in connections:
if con_id == c.get_id():
if c not in l:
l.append(c)
if con_spec in [None, 'interface']:
for c in connections:
s_con = c.get_setting(NM.SettingConnection)
if s_con \
and con_id == s_con.get_interface_name():
if c not in l:
l.append(c)
if con_spec in [None, 'uuid']:
for c in connections:
if con_id == c.get_uuid():
if c not in l:
l.append(c)
return l
###############################################################################
def argv_get_one(argv, idx, type_ctor=None, topic=None):
if topic is not None:
try:
v = argv_get_one(argv, idx, type_ctor, None)
except MyError as e:
if isinstance(topic, (int, long)):
topic = argv[topic]
raise MyError('error for "%s": %s' % (topic, e.message))
return v
v = None
try:
v = argv[idx]
except:
raise MyError('missing argument')
if type_ctor is not None:
try:
v = type_ctor(v)
except Exception as e:
raise MyError('invalid argument "%s" (%s)' % (v, e.message))
return v
###############################################################################
def arg_parse_secret_flags(arg):
try:
f = arg.strip()
n = {
'none': NM.SettingSecretFlags.NONE,
'not-saved': NM.SettingSecretFlags.NOT_SAVED,
'not-required': NM.SettingSecretFlags.NOT_REQUIRED,
'agent-owned': NM.SettingSecretFlags.AGENT_OWNED,
}.get(f)
if n is not None:
return n
return NM.SettingSecretFlags(int(f))
except Exception as e:
raise MyError('invalid secret flags "%s"' % (arg))
def _arg_parse_int(arg, vmin, vmax, key, base = 0):
try:
v = int(arg, base)
if v >= vmin and vmax <= 0xFFFFFFFF:
return v
except:
raise MyError('invalid %s "%s"' % (key, arg))
raise MyError("%s out of range" % (key))
def arg_parse_listen_port(arg):
return _arg_parse_int(arg, 0, 0xFFFF, "listen-port")
def arg_parse_fwmark(arg):
return _arg_parse_int(arg, 0, 0xFFFFFFFF, "fwmark", base = 0)
def arg_parse_persistent_keep_alive(arg):
return _arg_parse_int(arg, 0, 0xFFFFFFFF, "persistent-keepalive")
def arg_parse_allowed_ips(arg):
l = [s.strip() for s in arg.strip().split(',')]
l = [s for s in l if s != '']
l = list(l)
# use a peer to parse and validate the allowed-ips.
peer = NM.WireGuardPeer()
for aip in l:
if not peer.append_allowed_ip(aip, False):
raise MyError('invalid allowed-ip "%s"' % (aip))
return l
###############################################################################
def secret_flags_to_string(flags):
nick = {
NM.SettingSecretFlags.NONE: 'none',
NM.SettingSecretFlags.NOT_SAVED: 'not-saved',
NM.SettingSecretFlags.NOT_REQUIRED: 'not-required',
NM.SettingSecretFlags.AGENT_OWNED: 'agent-owned',
}.get(flags)
num = str(int(flags))
if nick is None:
return num
return '%s (%s)' % (num, nick)
###############################################################################
def wg_read_private_key(privkey_file):
import base64
try:
with open(privkey_file, "r") as f:
data = f.read()
bdata = base64.decodestring(data)
if len(bdata) != 32:
raise Exception("not 32 bytes base64 encoded")
return base64.encodestring(bdata).strip()
except Exception as e:
raise MyError('failed to read private key "%s": %s' % (privkey_file, e.message))
def wg_peer_is_valid(peer, msg = None):
try:
peer.is_valid(True, True)
except gi.repository.GLib.Error as e:
if msg is None:
raise MyError('%s' % (e.message))
else:
raise MyError('%s' % (msg))
###############################################################################
def do_get(nm_client, connection):
s_con = conn.get_setting(NM.SettingConnection)
s_wg = conn.get_setting(NM.SettingWireGuard)
# Fetching secrets is not implemented. For now show them all as
# <hidden>.
print('interface: %s' % (s_con.get_interface_name()))
print('uuid: %s' % (conn.get_uuid()))
print('id: %s' % (conn.get_id()))
print('private-key: %s' % ('<hidden>'))
print('private-key-flags: %s' % (secret_flags_to_string(s_wg.get_private_key_flags())))
print('listen-port: %s' % (s_wg.get_listen_port()))
print('fwmark: 0x%x' % (s_wg.get_fwmark()))
for i in range(s_wg.get_peers_len()):
peer = s_wg.get_peer(i)
print('peer[%d].public-key: %s' % (i, peer.get_public_key()))
print('peer[%d].preshared-key: %s' % (i, '<hidden>' if peer.get_preshared_key_flags() != NM.SettingSecretFlags.NOT_REQUIRED else ''))
print('peer[%d].preshared-key-flags: %s' % (i, secret_flags_to_string(peer.get_preshared_key_flags())))
print('peer[%d].endpoint: %s' % (i, peer.get_endpoint() if peer.get_endpoint() else ''))
print('peer[%d].persistent-keepalive: %s' % (i, peer.get_persistent_keepalive()))
print('peer[%d].allowed-ips: %s' % (i, ','.join([peer.get_allowed_ip(j) for j in range(peer.get_allowed_ips_len())])))
def do_set(nm_client, conn, argv):
s_wg = conn.get_setting(NM.SettingWireGuard)
peer = None
peer_remove = False
peer_idx = None
peer_secret_flags = None
try:
idx = 0
while True:
if peer \
and ( idx >= len(argv) \
or argv[idx] == 'peer'):
if peer_remove:
pp_peer, pp_idx = s_wg.get_peer_by_public_key(peer.get_public_key())
if pp_peer:
s_wg.remove_peer(pp_idx)
else:
if peer_secret_flags is not None:
peer.set_preshared_key_flags(peer_secret_flags)
wg_peer_is_valid(peer)
if peer_idx is None:
s_wg.append_peer(peer)
else:
s_wg.set_peer(peer, peer_idx)
peer = None
peer_remove = False
peer_idx = None
peer_secret_flags = None
if idx >= len(argv):
break;
if not peer and argv[idx] == 'private-key':
key = argv_get_one(argv, idx + 1, None, idx)
if key == '':
s_wg.set_property(NM.SETTING_WIREGUARD_PRIVATE_KEY, None)
else:
s_wg.set_property(NM.SETTING_WIREGUARD_PRIVATE_KEY, wg_read_private_key(key))
idx += 2
continue
if not peer and argv[idx] == 'private-key-flags':
s_wg.set_property(NM.SETTING_WIREGUARD_PRIVATE_KEY_FLAGS, argv_get_one(argv, idx + 1, arg_parse_secret_flags, idx))
idx += 2
continue
if not peer and argv[idx] == 'listen-port':
s_wg.set_property(NM.SETTING_WIREGUARD_LISTEN_PORT, argv_get_one(argv, idx + 1, arg_parse_listen_port, idx))
idx += 2
continue
if not peer and argv[idx] == 'fwmark':
s_wg.set_property(NM.SETTING_WIREGUARD_FWMARK, argv_get_one(argv, idx + 1, arg_parse_fwmark, idx))
idx += 2
continue
if argv[idx] == 'peer':
public_key = argv_get_one(argv, idx + 1, None, idx)
peer, peer_idx = s_wg.get_peer_by_public_key(public_key)
if peer:
peer = peer.new_clone(True)
else:
peer_idx = None
peer = NM.WireGuardPeer()
peer.set_public_key(public_key)
wg_peer_is_valid(peer, 'public key "%s" is invalid' % (public_key))
peer_remove = False
idx += 2
continue
if peer and argv[idx] == 'remove':
peer_remove = True
idx += 1
continue
if peer and argv[idx] == 'preshared-key':
psk = argv_get_one(argv, idx + 1, None, idx)
if psk == '':
peer.set_preshared_key(None)
if peer_secret_flags is not None:
peer_secret_flags = NM.SettingSecretFlags.NOT_REQUIRED
else:
peer.set_preshared_key(wg_read_private_key(psk))
if peer_secret_flags is not None:
peer_secret_flags = NM.SettingSecretFlags.NONE
idx += 2
continue
if peer and argv[idx] == 'preshared-key-flags':
peer_secret_flags = argv_get_one(argv, idx + 1, arg_parse_secret_flags, idx)
idx += 2
continue
if peer and argv[idx] == 'endpoint':
peer.set_endpoint(argv_get_one(argv, idx + 1, None, idx))
idx += 2
continue
if peer and argv[idx] == 'persistent-keepalive':
peer.set_persistent_keepalive(argv_get_one(argv, idx + 1, arg_parse_persistent_keep_alive, idx))
idx += 2
continue
if peer and argv[idx] == 'allowed-ips':
allowed_ips = list(argv_get_one(argv, idx + 1, arg_parse_allowed_ips, idx))
peer.clear_allowed_ips()
for aip in allowed_ips:
peer.append_allowed_ip(aip, False)
del allowed_ips
idx += 2
continue
raise MyError('invalid argument "%s"' % (argv[idx]))
except MyError as e:
print('Error: %s' % (e.message))
sys.exit(1)
try:
conn.commit_changes(True, None)
except Exception as e:
print('failure to commit connection: %s' % (e))
sys.exit(1)
print('Success')
sys.exit(0)
###############################################################################
if __name__ == '__main__':
argv = sys.argv
del argv[0]
con_spec = None
if len(argv) >= 1:
if argv[0] in [ 'id', 'uuid', 'interface' ]:
con_spec = argv[0]
del argv[0]
if len(argv) < 1:
print('Requires an existing NetworkManager connection profile as first argument')
print('Select it based on the connection ID, UUID, or interface-name (optionally qualify the selection with [id|uuid|interface])')
print('Maybe you want to create one first with')
print(' nmcli connection add type wireguard ifname wg0 $MORE_ARGS')
sys.exit(1)
con_id = argv[0]
del argv[0]
nm_client = NM.Client.new(None)
connections = connections_find(nm_client.get_connections(), con_spec, con_id)
if len(connections) == 0:
print('No matching connection %s\"%s\" found.' % ((con_spec+' ' if con_spec else ''), con_id))
print('Maybe you want to create one first with')
print(' nmcli connection add type wireguard ifname wg0 $MORE_ARGS')
sys.exit(1)
if len(connections) > 1:
print("Connection %s\"%s\" is not unique (%s)" % ((con_spec+' ' if con_spec else ''), con_id, ', '.join(['['+connection_to_str(c)+']' for c in connections])))
if not con_spec:
print('Maybe qualify the name with [id|uuid|interface]?')
sys.exit(1)
conn = connections[0]
if not connection_is_wireguard(conn):
print('Connection %s is not a WireGuard profile' % (connection_to_str(conn)))
print('See available profiles with `nmcli connection show`')
sys.exit(1)
if not argv:
do_get(nm_client, conn)
else:
do_set(nm_client, conn, argv)

View file

@ -48,6 +48,7 @@ libnm_core_headers = files(
'nm-setting-wifi-p2p.h',
'nm-setting-wimax.h',
'nm-setting-wired.h',
'nm-setting-wireguard.h',
'nm-setting-wireless-security.h',
'nm-setting-wireless.h',
'nm-setting-wpan.h',
@ -104,6 +105,7 @@ libnm_core_settings_sources = files(
'nm-setting-wifi-p2p.c',
'nm-setting-wimax.c',
'nm-setting-wired.c',
'nm-setting-wireguard.c',
'nm-setting-wireless-security.c',
'nm-setting-wireless.c',
'nm-setting-wpan.c',

View file

@ -905,25 +905,24 @@ _supports_addr_family (NMConnection *self, int family)
static gboolean
_normalize_ip_config (NMConnection *self, GHashTable *parameters)
{
const char *default_ip4_method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
const char *default_ip6_method = NULL;
NMSettingIPConfig *s_ip4, *s_ip6;
NMSettingProxy *s_proxy;
NMSetting *setting;
gboolean changed = FALSE;
guint num, i;
if (parameters)
default_ip6_method = g_hash_table_lookup (parameters, NM_CONNECTION_NORMALIZE_PARAM_IP6_CONFIG_METHOD);
if (!default_ip6_method)
default_ip6_method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
s_ip4 = nm_connection_get_setting_ip4_config (self);
s_ip6 = nm_connection_get_setting_ip6_config (self);
s_proxy = nm_connection_get_setting_proxy (self);
if (_supports_addr_family (self, AF_INET)) {
if (!s_ip4) {
const char *default_ip4_method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
if (nm_connection_is_type (self, NM_SETTING_WIREGUARD_SETTING_NAME))
default_ip4_method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
/* But if no IP4 setting was specified, assume the caller was just
* being lazy and use the default method.
*/
@ -966,6 +965,17 @@ _normalize_ip_config (NMConnection *self, GHashTable *parameters)
if (_supports_addr_family (self, AF_INET6)) {
if (!s_ip6) {
const char *default_ip6_method = NULL;
if (parameters)
default_ip6_method = g_hash_table_lookup (parameters, NM_CONNECTION_NORMALIZE_PARAM_IP6_CONFIG_METHOD);
if (!default_ip6_method) {
if (nm_connection_is_type (self, NM_SETTING_WIREGUARD_SETTING_NAME))
default_ip6_method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
else
default_ip6_method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
}
/* If no IP6 setting was specified, then assume that means IP6 config is
* allowed to fail.
*/
@ -2419,7 +2429,8 @@ nm_connection_is_virtual (NMConnection *connection)
NM_SETTING_TEAM_SETTING_NAME,
NM_SETTING_TUN_SETTING_NAME,
NM_SETTING_VLAN_SETTING_NAME,
NM_SETTING_VXLAN_SETTING_NAME))
NM_SETTING_VXLAN_SETTING_NAME,
NM_SETTING_WIREGUARD_SETTING_NAME))
return TRUE;
if (nm_streq (type, NM_SETTING_INFINIBAND_SETTING_NAME)) {

View file

@ -46,6 +46,7 @@
#include "nm-setting-wifi-p2p.h"
#include "nm-setting-wimax.h"
#include "nm-setting-wired.h"
#include "nm-setting-wireguard.h"
#include "nm-setting-wireless-security.h"
#include "nm-setting-wireless.h"
#include "nm-setting-wpan.h"

View file

@ -79,6 +79,7 @@
#include "nm-setting-wifi-p2p.h"
#include "nm-setting-wimax.h"
#include "nm-setting-wired.h"
#include "nm-setting-wireguard.h"
#include "nm-setting-wireless-security.h"
#include "nm-setting-wireless.h"
#include "nm-setting-wpan.h"
@ -633,6 +634,15 @@ NM_AUTO_DEFINE_FCN_VOID0 (NMSockAddrEndpoint *, _nm_auto_unref_sockaddrendpoint,
/*****************************************************************************/
NMSockAddrEndpoint *_nm_wireguard_peer_get_endpoint (const NMWireGuardPeer *self);
void _nm_wireguard_peer_set_endpoint (NMWireGuardPeer *self,
NMSockAddrEndpoint *endpoint);
void _nm_wireguard_peer_set_public_key_bin (NMWireGuardPeer *self,
const guint8 public_key[static NM_WIREGUARD_PUBLIC_KEY_LEN]);
/*****************************************************************************/
typedef struct _NMSettInfoSetting NMSettInfoSetting;
typedef struct _NMSettInfoProperty NMSettInfoProperty;
@ -768,4 +778,17 @@ gboolean _nm_connection_find_secret (NMConnection *self,
/*****************************************************************************/
#define nm_auto_unref_wgpeer nm_auto(_nm_auto_unref_wgpeer)
NM_AUTO_DEFINE_FCN_VOID0 (NMWireGuardPeer *, _nm_auto_unref_wgpeer, nm_wireguard_peer_unref)
gboolean _nm_utils_wireguard_decode_key (const char *base64_key,
gsize required_key_len,
guint8 *out_key);
gboolean _nm_utils_wireguard_normalize_key (const char *base64_key,
gsize required_key_len,
char **out_base64_key_norm);
/*****************************************************************************/
#endif

View file

@ -72,6 +72,7 @@ typedef struct _NMSettingVxlan NMSettingVxlan;
typedef struct _NMSettingWifiP2P NMSettingWifiP2P;
typedef struct _NMSettingWimax NMSettingWimax;
typedef struct _NMSettingWired NMSettingWired;
typedef struct _NMSettingWireGuard NMSettingWireGuard;
typedef struct _NMSettingWireless NMSettingWireless;
typedef struct _NMSettingWirelessSecurity NMSettingWirelessSecurity;
typedef struct _NMSettingWpan NMSettingWpan;

View file

@ -25,7 +25,8 @@
#error Cannot use this header.
#endif
#define NM_KEYFILE_GROUP_VPN_SECRETS "vpn-secrets"
#define NM_KEYFILE_GROUP_VPN_SECRETS "vpn-secrets"
#define NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER "wireguard-peer."
const char *nm_keyfile_plugin_get_alias_for_setting_name (const char *setting_name);

View file

@ -32,6 +32,7 @@
#include <linux/pkt_sched.h>
#include "nm-utils/nm-secret-utils.h"
#include "systemd/nm-sd-utils-shared.h"
#include "nm-common-macros.h"
#include "nm-core-internal.h"
#include "nm-keyfile-utils.h"
@ -2901,6 +2902,137 @@ out:
nm_connection_add_setting (info->connection, g_steal_pointer (&setting));
}
static void
_read_setting_wireguard_peer (KeyfileReaderInfo *info)
{
gs_unref_object NMSettingWireGuard *s_wg_new = NULL;
nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL;
gs_free_error GError *error = NULL;
NMSettingWireGuard *s_wg;
gs_free char *str = NULL;
const char *cstr = NULL;
const char *key;
gint64 i64;
gs_strfreev char **sa = NULL;
gsize n_sa;
peer = nm_wireguard_peer_new ();
nm_assert (g_str_has_prefix (info->group, NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER));
cstr = &info->group[NM_STRLEN (NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER)];
if ( !_nm_utils_wireguard_normalize_key (cstr, NM_WIREGUARD_PUBLIC_KEY_LEN, &str)
|| !nm_streq0 (str, cstr)) {
/* the group name must be identical to the normalized(!) key, so that it
* is uniquely identified. */
handle_warn (info, NULL, NM_KEYFILE_WARN_SEVERITY_WARN,
_("invalid peer public key in section '%s'"),
info->group);
return;
}
nm_wireguard_peer_set_public_key (peer, cstr);
nm_clear_g_free (&str);
key = NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY;
str = nm_keyfile_plugin_kf_get_string (info->keyfile, info->group, key, NULL);
if (str) {
if (!_nm_utils_wireguard_decode_key (str, NM_WIREGUARD_SYMMETRIC_KEY_LEN, NULL)) {
if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
_("key '%s.%s' is not not a valid 256 bit key in base64 encoding"),
info->group, key))
return;
} else
nm_wireguard_peer_set_preshared_key (peer, str);
nm_clear_g_free (&str);
}
key = NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY_FLAGS;
i64 = nm_keyfile_plugin_kf_get_int64 (info->keyfile, info->group, key, 0, 0, NM_SETTING_SECRET_FLAG_ALL, -1, NULL);
if (errno != ENODATA) {
if ( i64 == -1
|| !_nm_setting_secret_flags_valid (i64)) {
if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
_("key '%s.%s' is not not a valid secret flag"),
info->group, key))
return;
} else
nm_wireguard_peer_set_preshared_key_flags (peer, i64);
}
key = NM_WIREGUARD_PEER_ATTR_PERSISTENT_KEEPALIVE;
i64 = nm_keyfile_plugin_kf_get_int64 (info->keyfile, info->group, key, 0, 0, G_MAXUINT32, -1, NULL);
if (errno != ENODATA) {
if (i64 == -1) {
if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
_("key '%s.%s' is not not a integer in range 0 to 2^32"),
info->group, key))
return;
} else
nm_wireguard_peer_set_persistent_keepalive (peer, i64);
}
key = NM_WIREGUARD_PEER_ATTR_ENDPOINT;
str = nm_keyfile_plugin_kf_get_string (info->keyfile, info->group, key, NULL);
if (str && str[0]) {
nm_auto_unref_sockaddrendpoint NMSockAddrEndpoint *ep = NULL;
ep = nm_sock_addr_endpoint_new (str);
if (!nm_sock_addr_endpoint_get_host (ep)) {
if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
_("key '%s.%s' is not not a valid endpoint"),
info->group, key))
return;
} else
_nm_wireguard_peer_set_endpoint (peer, ep);
}
nm_clear_g_free (&str);
key = NM_WIREGUARD_PEER_ATTR_ALLOWED_IPS;
sa = nm_keyfile_plugin_kf_get_string_list (info->keyfile, info->group, key, &n_sa, NULL);
if (n_sa > 0) {
gboolean has_error = FALSE;
gsize i;
for (i = 0; i < n_sa; i++) {
if (!nm_utils_parse_inaddr_prefix_bin (AF_UNSPEC, sa[i], NULL, NULL, NULL)) {
has_error = TRUE;
continue;
}
nm_wireguard_peer_append_allowed_ip (peer, sa[i], TRUE);
}
if (has_error) {
if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
_("key '%s.%s' has invalid allowed-ips"),
info->group, key))
return;
}
}
nm_clear_pointer (&sa, g_strfreev);
if (info->error)
return;
if (!nm_wireguard_peer_is_valid (peer, TRUE, TRUE, &error)) {
if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
_("peer '%s' is invalid: %s"),
info->group, error->message))
return;
return;
}
s_wg = NM_SETTING_WIREGUARD (nm_connection_get_setting (info->connection, NM_TYPE_SETTING_WIREGUARD));
if (!s_wg) {
s_wg_new = NM_SETTING_WIREGUARD (nm_setting_wireguard_new ());
s_wg = s_wg_new;
}
nm_setting_wireguard_append_peer (s_wg, peer);
if (s_wg_new) {
nm_connection_add_setting (info->connection,
NM_SETTING (g_steal_pointer (&s_wg_new)));
}
}
static void
_read_setting_vpn_secrets (KeyfileReaderInfo *info)
{
@ -3021,7 +3153,9 @@ nm_keyfile_read (GKeyFile *keyfile,
if (nm_streq (groups[i], NM_KEYFILE_GROUP_VPN_SECRETS)) {
/* Only read out secrets when needed */
vpn_secrets = TRUE;
} else
} else if (NM_STR_HAS_PREFIX (groups[i], NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER))
_read_setting_wireguard_peer (&info);
else
_read_setting (&info);
info.group = NULL;
@ -3198,6 +3332,92 @@ out_unset_value:
g_value_unset (&value);
}
static void
_write_setting_wireguard (NMSetting *setting, KeyfileWriterInfo *info)
{
NMSettingWireGuard *s_wg;
guint i_peer, n_peers;
s_wg = NM_SETTING_WIREGUARD (setting);
n_peers = nm_setting_wireguard_get_peers_len (s_wg);
for (i_peer = 0; i_peer < n_peers; i_peer++) {
NMWireGuardPeer *peer = nm_setting_wireguard_get_peer (s_wg, i_peer);
const char *public_key;
char group[NM_STRLEN (NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER) + 200];
NMSettingSecretFlags secret_flags;
gboolean any_key = FALSE;
guint i_aip, n_aip;
const char *cstr;
guint32 u32;
public_key = nm_wireguard_peer_get_public_key (peer);
if ( !public_key
|| !public_key[0]
|| !NM_STRCHAR_ALL (public_key, ch, nm_sd_utils_unbase64char (ch, TRUE) >= 0)) {
/* invalid peer. Skip it */
continue;
}
if (g_snprintf (group,
sizeof (group),
"%s%s",
NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER,
nm_wireguard_peer_get_public_key (peer)) >= sizeof (group)) {
/* Too long. Not a valid public key. Skip the peer. */
continue;
}
cstr = nm_wireguard_peer_get_endpoint (peer);
if (cstr) {
g_key_file_set_string (info->keyfile, group, NM_WIREGUARD_PEER_ATTR_ENDPOINT, cstr);
any_key = TRUE;
}
secret_flags = nm_wireguard_peer_get_preshared_key_flags (peer);
if (_secret_flags_persist_secret (secret_flags)) {
cstr = nm_wireguard_peer_get_preshared_key (peer);
if (cstr) {
g_key_file_set_string (info->keyfile, group, NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, cstr);
any_key = TRUE;
}
}
/* usually, we don't persist the secret-flags 0 (because they are the default).
* For WireGuard peers, the default secret-flags for preshared-key are 4 (not-required).
* So, in this case behave differently: a missing preshared-key-flag setting means
* "not-required". */
if (secret_flags != NM_SETTING_SECRET_FLAG_NOT_REQUIRED) {
g_key_file_set_int64 (info->keyfile, group, NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY_FLAGS, secret_flags);
any_key = TRUE;
}
u32 = nm_wireguard_peer_get_persistent_keepalive (peer);
if (u32) {
g_key_file_set_uint64 (info->keyfile, group, NM_WIREGUARD_PEER_ATTR_PERSISTENT_KEEPALIVE, u32);
any_key = TRUE;
}
n_aip = nm_wireguard_peer_get_allowed_ips_len (peer);
if (n_aip > 0) {
gs_free const char **strv = NULL;
strv = g_new (const char *, ((gsize) n_aip) + 1);
for (i_aip = 0; i_aip < n_aip; i_aip++)
strv[i_aip] = nm_wireguard_peer_get_allowed_ip (peer, i_aip, NULL);
strv[n_aip] = NULL;
g_key_file_set_string_list (info->keyfile, group, NM_WIREGUARD_PEER_ATTR_ALLOWED_IPS,
strv, n_aip);
any_key = TRUE;
}
if (!any_key) {
/* we cannot omit all keys. At an empty endpoint. */
g_key_file_set_string (info->keyfile, group, NM_WIREGUARD_PEER_ATTR_ENDPOINT, "");
}
}
}
GKeyFile *
nm_keyfile_write (NMConnection *connection,
NMKeyfileWriteHandler handler,
@ -3275,6 +3495,12 @@ nm_keyfile_write (NMConnection *connection,
goto out_with_info_error;
}
if (NM_IS_SETTING_WIREGUARD (setting)) {
_write_setting_wireguard (setting, &info);
if (info.error)
goto out_with_info_error;
}
nm_assert (!info.error);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,201 @@
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
* Copyright 2018 - 2019 Red Hat, Inc.
*/
#ifndef __NM_SETTING_WIREGUARD_H__
#define __NM_SETTING_WIREGUARD_H__
#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION)
#error "Only <NetworkManager.h> can be included directly."
#endif
#include "nm-setting.h"
#include "nm-utils.h"
G_BEGIN_DECLS
/*****************************************************************************/
#define NM_WIREGUARD_PUBLIC_KEY_LEN 32
#define NM_WIREGUARD_SYMMETRIC_KEY_LEN 32
/*****************************************************************************/
typedef struct _NMWireGuardPeer NMWireGuardPeer;
NM_AVAILABLE_IN_1_16
GType nm_wireguard_peer_get_type (void);
NM_AVAILABLE_IN_1_16
NMWireGuardPeer *nm_wireguard_peer_new (void);
NM_AVAILABLE_IN_1_16
NMWireGuardPeer *nm_wireguard_peer_new_clone (const NMWireGuardPeer *self,
gboolean with_secrets);
NM_AVAILABLE_IN_1_16
NMWireGuardPeer *nm_wireguard_peer_ref (NMWireGuardPeer *self);
NM_AVAILABLE_IN_1_16
void nm_wireguard_peer_unref (NMWireGuardPeer *self);
NM_AVAILABLE_IN_1_16
void nm_wireguard_peer_seal (NMWireGuardPeer *self);
NM_AVAILABLE_IN_1_16
gboolean nm_wireguard_peer_is_sealed (const NMWireGuardPeer *self);
NM_AVAILABLE_IN_1_16
const char *nm_wireguard_peer_get_public_key (const NMWireGuardPeer *self);
NM_AVAILABLE_IN_1_16
void nm_wireguard_peer_set_public_key (NMWireGuardPeer *self,
const char *public_key);
NM_AVAILABLE_IN_1_16
const char *nm_wireguard_peer_get_preshared_key (const NMWireGuardPeer *self);
NM_AVAILABLE_IN_1_16
void nm_wireguard_peer_set_preshared_key (NMWireGuardPeer *self,
const char *preshared_key);
NM_AVAILABLE_IN_1_16
NMSettingSecretFlags nm_wireguard_peer_get_preshared_key_flags (const NMWireGuardPeer *self);
NM_AVAILABLE_IN_1_16
void nm_wireguard_peer_set_preshared_key_flags (NMWireGuardPeer *self,
NMSettingSecretFlags preshared_key_flags);
NM_AVAILABLE_IN_1_16
guint16 nm_wireguard_peer_get_persistent_keepalive (const NMWireGuardPeer *self);
NM_AVAILABLE_IN_1_16
void nm_wireguard_peer_set_persistent_keepalive (NMWireGuardPeer *self,
guint16 persistent_keepalive);
NM_AVAILABLE_IN_1_16
const char *nm_wireguard_peer_get_endpoint (const NMWireGuardPeer *self);
NM_AVAILABLE_IN_1_16
void nm_wireguard_peer_set_endpoint (NMWireGuardPeer *self,
const char *endpoint);
NM_AVAILABLE_IN_1_16
guint nm_wireguard_peer_get_allowed_ips_len (const NMWireGuardPeer *self);
NM_AVAILABLE_IN_1_16
const char *nm_wireguard_peer_get_allowed_ip (const NMWireGuardPeer *self,
guint idx,
gboolean *out_is_valid);
NM_AVAILABLE_IN_1_16
void nm_wireguard_peer_clear_allowed_ips (NMWireGuardPeer *self);
NM_AVAILABLE_IN_1_16
gboolean nm_wireguard_peer_append_allowed_ip (NMWireGuardPeer *self,
const char *allowed_ip,
gboolean accept_invalid);
NM_AVAILABLE_IN_1_16
gboolean nm_wireguard_peer_remove_allowed_ip (NMWireGuardPeer *self,
guint idx);
NM_AVAILABLE_IN_1_16
gboolean nm_wireguard_peer_is_valid (const NMWireGuardPeer *self,
gboolean check_non_secrets,
gboolean check_secrets,
GError **error);
NM_AVAILABLE_IN_1_16
int nm_wireguard_peer_cmp (const NMWireGuardPeer *a,
const NMWireGuardPeer *b,
NMSettingCompareFlags compare_flags);
/*****************************************************************************/
#define NM_TYPE_SETTING_WIREGUARD (nm_setting_wireguard_get_type ())
#define NM_SETTING_WIREGUARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_WIREGUARD, NMSettingWireGuard))
#define NM_SETTING_WIREGUARD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_WIREGUARD, NMSettingWireGuardClass))
#define NM_IS_SETTING_WIREGUARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_WIREGUARD))
#define NM_IS_SETTING_WIREGUARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_WIREGUARD))
#define NM_SETTING_WIREGUARD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_WIREGUARD, NMSettingWireGuardClass))
#define NM_SETTING_WIREGUARD_SETTING_NAME "wireguard"
#define NM_SETTING_WIREGUARD_PRIVATE_KEY "private-key"
#define NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS "private-key-flags"
#define NM_SETTING_WIREGUARD_LISTEN_PORT "listen-port"
#define NM_SETTING_WIREGUARD_FWMARK "fwmark"
#define NM_SETTING_WIREGUARD_PEERS "peers"
#define NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY "public-key"
#define NM_WIREGUARD_PEER_ATTR_ENDPOINT "endpoint"
#define NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY "preshared-key"
#define NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY_FLAGS "preshared-key-flags"
#define NM_WIREGUARD_PEER_ATTR_ALLOWED_IPS "allowed-ips"
#define NM_WIREGUARD_PEER_ATTR_PERSISTENT_KEEPALIVE "persistent-keepalive"
/*****************************************************************************/
typedef struct _NMSettingWireGuardClass NMSettingWireGuardClass;
NM_AVAILABLE_IN_1_16
GType nm_setting_wireguard_get_type (void);
NM_AVAILABLE_IN_1_16
NMSetting *nm_setting_wireguard_new (void);
/*****************************************************************************/
NM_AVAILABLE_IN_1_16
const char *nm_setting_wireguard_get_private_key (NMSettingWireGuard *self);
NM_AVAILABLE_IN_1_16
NMSettingSecretFlags nm_setting_wireguard_get_private_key_flags (NMSettingWireGuard *self);
NM_AVAILABLE_IN_1_16
guint16 nm_setting_wireguard_get_listen_port (NMSettingWireGuard *self);
NM_AVAILABLE_IN_1_16
guint32 nm_setting_wireguard_get_fwmark (NMSettingWireGuard *self);
/*****************************************************************************/
NM_AVAILABLE_IN_1_16
guint nm_setting_wireguard_get_peers_len (NMSettingWireGuard *self);
NM_AVAILABLE_IN_1_16
NMWireGuardPeer *nm_setting_wireguard_get_peer (NMSettingWireGuard *self,
guint idx);
NM_AVAILABLE_IN_1_16
NMWireGuardPeer *nm_setting_wireguard_get_peer_by_public_key (NMSettingWireGuard *self,
const char *public_key,
guint *out_idx);
NM_AVAILABLE_IN_1_16
void nm_setting_wireguard_set_peer (NMSettingWireGuard *self,
NMWireGuardPeer *peer,
guint idx);
NM_AVAILABLE_IN_1_16
void nm_setting_wireguard_append_peer (NMSettingWireGuard *self,
NMWireGuardPeer *peer);
NM_AVAILABLE_IN_1_16
gboolean nm_setting_wireguard_remove_peer (NMSettingWireGuard *self,
guint idx);
NM_AVAILABLE_IN_1_16
guint nm_setting_wireguard_clear_peers (NMSettingWireGuard *self);
/*****************************************************************************/
G_END_DECLS
#endif /* __NM_SETTING_WIREGUARD_H__ */

View file

@ -2067,7 +2067,7 @@ _nm_setting_update_secrets (NMSetting *setting, GVariant *secrets, GError **erro
int success;
success = NM_SETTING_GET_CLASS (setting)->update_one_secret (setting, secret_key, secret_value, &tmp_error);
g_assert (!((success == NM_SETTING_UPDATE_SECRET_ERROR) ^ (!!tmp_error)));
nm_assert (!((success == NM_SETTING_UPDATE_SECRET_ERROR) ^ (!!tmp_error)));
g_variant_unref (secret_value);

View file

@ -38,6 +38,8 @@
#endif
#include "nm-utils/nm-enum-utils.h"
#include "nm-utils/nm-secret-utils.h"
#include "systemd/nm-sd-utils-shared.h"
#include "nm-common-macros.h"
#include "nm-utils-private.h"
#include "nm-setting-private.h"
@ -6809,3 +6811,76 @@ nm_utils_version (void)
return NM_VERSION;
}
/*****************************************************************************/
/**
* _nm_utils_wireguard_decode_key:
* @base64_key: the (possibly invalid) base64 encode key.
* @required_key_len: the expected (binary) length of the key after
* decoding. If the length does not match, the validation fails.
* @out_key: (allow-none): an optional output buffer for the binary
* key. If given, it will be filled with exactly @required_key_len
* bytes.
*
* Returns: %TRUE if the input key is a valid base64 encoded key
* with @required_key_len bytes.
*/
gboolean
_nm_utils_wireguard_decode_key (const char *base64_key,
gsize required_key_len,
guint8 *out_key)
{
gs_free guint8 *bin_arr = NULL;
gsize base64_key_len;
gsize bin_len;
int r;
if (!base64_key)
return FALSE;
base64_key_len = strlen (base64_key);
r = nm_sd_utils_unbase64mem (base64_key, base64_key_len, &bin_arr, &bin_len);
if (r < 0)
return FALSE;
if (bin_len != required_key_len) {
nm_explicit_bzero (bin_arr, bin_len);
return FALSE;
}
if (nm_utils_memeqzero (bin_arr, required_key_len)) {
/* an all zero key is not valid either. That is used to represet an unset key */
return FALSE;
}
if (out_key)
memcpy (out_key, bin_arr, required_key_len);
nm_explicit_bzero (bin_arr, bin_len);
return TRUE;
}
gboolean
_nm_utils_wireguard_normalize_key (const char *base64_key,
gsize required_key_len,
char **out_base64_key_norm)
{
gs_free guint8 *buf_free = NULL;
guint8 buf_static[200];
guint8 *buf;
if (required_key_len > sizeof (buf_static)) {
buf_free = g_new (guint8, required_key_len);
buf = buf_free;
} else
buf = buf_static;
if (!_nm_utils_wireguard_decode_key (base64_key, required_key_len, buf)) {
NM_SET_OUT (out_base64_key_norm, NULL);
return FALSE;
}
NM_SET_OUT (out_base64_key_norm, g_base64_encode (buf, required_key_len));
nm_explicit_bzero (buf, required_key_len);
return TRUE;
}

View file

@ -115,6 +115,30 @@ _connection_new_from_dbus_strict (GVariant *dict,
/*****************************************************************************/
static char *
_create_random_ipaddr (int addr_family, gboolean as_service)
{
char delimiter = as_service ? ':' : '/';
int num;
if (addr_family == AF_UNSPEC)
addr_family = nmtst_rand_select (AF_INET, AF_INET6);
g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6));
if (as_service)
num = (nmtst_get_rand_int () % 1000) + 30000;
else
num = addr_family == AF_INET ? 32 : 128;
if (addr_family == AF_INET)
return g_strdup_printf ("192.168.%u.%u%c%d", nmtst_get_rand_int () % 256, nmtst_get_rand_int () % 256, delimiter, num);
else
return g_strdup_printf ("a:b:c::%02x:%02x%c%d", nmtst_get_rand_int () % 256, nmtst_get_rand_int () % 256, delimiter, num);
}
/*****************************************************************************/
static void
compare_blob_data (const char *test,
const char *key_path,
@ -2013,6 +2037,235 @@ test_tc_config_dbus (void)
/*****************************************************************************/
static GPtrArray *
_rndt_wg_peers_create (void)
{
GPtrArray *wg_peers;
guint i, n;
wg_peers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_wireguard_peer_unref);
n = nmtst_get_rand_int () % 10;
for (i = 0; i < n; i++) {
NMWireGuardPeer *peer;
guint8 public_key_buf[NM_WIREGUARD_PUBLIC_KEY_LEN];
guint8 preshared_key_buf[NM_WIREGUARD_SYMMETRIC_KEY_LEN];
gs_free char *public_key = NULL;
gs_free char *preshared_key = NULL;
gs_free char *s_endpoint = NULL;
guint i_aip, n_aip;
/* we don't bother to create a valid curve25519 public key. Of course, libnm cannot
* check whether the public key is bogus or not. Hence, for our purpose a random
* bogus key is good enough. */
public_key = g_base64_encode (nmtst_rand_buf (NULL, public_key_buf, sizeof (public_key_buf)), sizeof (public_key_buf));
preshared_key = g_base64_encode (nmtst_rand_buf (NULL, preshared_key_buf, sizeof (preshared_key_buf)), sizeof (preshared_key_buf));
s_endpoint = _create_random_ipaddr (AF_UNSPEC, TRUE);
peer = nm_wireguard_peer_new ();
nm_wireguard_peer_set_public_key (peer, public_key);
nm_wireguard_peer_set_preshared_key (peer, nmtst_rand_select (NULL, preshared_key));
nm_wireguard_peer_set_preshared_key_flags (peer, nmtst_rand_select (NM_SETTING_SECRET_FLAG_NONE,
NM_SETTING_SECRET_FLAG_NOT_SAVED,
NM_SETTING_SECRET_FLAG_AGENT_OWNED));
nm_wireguard_peer_set_persistent_keepalive (peer,
nmtst_rand_select ((guint32) 0, nmtst_get_rand_int ()));
nm_wireguard_peer_set_endpoint (peer, nmtst_rand_select (s_endpoint, NULL));
n_aip = nmtst_rand_select (0, nmtst_get_rand_int () % 10);
for (i_aip = 0; i_aip < n_aip; i_aip++) {
gs_free char *aip = NULL;
aip = _create_random_ipaddr (AF_UNSPEC, FALSE);
if (!nm_wireguard_peer_append_allowed_ip (peer, aip, FALSE))
g_assert_not_reached ();
}
g_assert (nm_wireguard_peer_is_valid (peer, TRUE, TRUE, NULL));
nm_wireguard_peer_seal (peer);
g_ptr_array_add (wg_peers, peer);
}
return wg_peers;
}
static const char *
_rndt_wg_peers_to_keyfile (GPtrArray *wg_peers,
gboolean strict,
char **out_str)
{
nm_auto_free_gstring GString *gstr = NULL;
nm_auto_free_gstring GString *gstr_aip = NULL;
guint i, j;
g_assert (wg_peers);
g_assert (out_str && !*out_str);
nm_gstring_prepare (&gstr);
for (i = 0; i < wg_peers->len; i++) {
const NMWireGuardPeer *peer = wg_peers->pdata[i];
gs_free char *s_endpoint = NULL;
gs_free char *s_preshared_key = NULL;
gs_free char *s_preshared_key_flags = NULL;
gs_free char *s_persistent_keepalive = NULL;
gs_free char *s_allowed_ips = NULL;
if (nm_wireguard_peer_get_endpoint (peer))
s_endpoint = g_strdup_printf ("endpoint=%s\n", nm_wireguard_peer_get_endpoint (peer));
else if (!strict)
s_endpoint = g_strdup_printf ("endpoint=\n");
if ( nm_wireguard_peer_get_preshared_key (peer)
|| !strict) {
if (nm_wireguard_peer_get_preshared_key_flags (peer) == NM_SETTING_SECRET_FLAG_NONE)
s_preshared_key = g_strdup_printf ("preshared-key=%s\n", nm_wireguard_peer_get_preshared_key (peer) ?: "");
}
if ( nm_wireguard_peer_get_preshared_key_flags (peer) != NM_SETTING_SECRET_FLAG_NOT_REQUIRED
|| !strict)
s_preshared_key_flags = g_strdup_printf ("preshared-key-flags=%d\n", (int) nm_wireguard_peer_get_preshared_key_flags (peer));
if ( nm_wireguard_peer_get_persistent_keepalive (peer) != 0
|| !strict)
s_persistent_keepalive = g_strdup_printf ("persistent-keepalive=%u\n", nm_wireguard_peer_get_persistent_keepalive (peer));
if ( nm_wireguard_peer_get_allowed_ips_len (peer) > 0
|| !strict) {
nm_gstring_prepare (&gstr_aip);
for (j = 0; j < nm_wireguard_peer_get_allowed_ips_len (peer); j++)
g_string_append_printf (gstr_aip, "%s;", nm_wireguard_peer_get_allowed_ip (peer, j, NULL));
s_allowed_ips = g_strdup_printf ("allowed-ips=%s\n", gstr_aip->str);
}
if ( !s_endpoint
&& !s_preshared_key
&& !s_preshared_key_flags
&& !s_persistent_keepalive
&& !s_allowed_ips)
s_endpoint = g_strdup_printf ("endpoint=\n");
g_string_append_printf (gstr,
"\n"
"[wireguard-peer.%s]\n"
"%s" /* endpoint */
"%s" /* preshared-key */
"%s" /* preshared-key-flags */
"%s" /* persistent-keepalive */
"%s" /* allowed-ips */
"",
nm_wireguard_peer_get_public_key (peer),
s_endpoint ?: "",
s_preshared_key ?: "",
s_preshared_key_flags ?: "",
s_persistent_keepalive ?: "",
s_allowed_ips ?: "");
}
return (*out_str = g_string_free (g_steal_pointer (&gstr), FALSE));
}
static void
_rndt_wg_peers_assert_equal (NMSettingWireGuard *s_wg,
GPtrArray *peers,
gboolean consider_persistent_secrets,
gboolean consider_all_secrets,
gboolean expect_no_secrets)
{
guint i;
g_assert (NM_IS_SETTING_WIREGUARD (s_wg));
g_assert (peers);
g_assert_cmpint (peers->len, ==, nm_setting_wireguard_get_peers_len (s_wg));
for (i = 0; i < peers->len; i++) {
const NMWireGuardPeer *a = peers->pdata[i];
const NMWireGuardPeer *b = nm_setting_wireguard_get_peer (s_wg, i);
gboolean consider_secrets;
g_assert (a);
g_assert (b);
g_assert_cmpint (nm_wireguard_peer_cmp (a, b, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS), ==, 0);
if ( consider_all_secrets
|| !nm_wireguard_peer_get_preshared_key (a))
consider_secrets = TRUE;
else if (nm_wireguard_peer_get_preshared_key (b))
consider_secrets = TRUE;
else if ( consider_persistent_secrets
&& nm_wireguard_peer_get_preshared_key_flags (b) == NM_SETTING_SECRET_FLAG_NONE)
consider_secrets = TRUE;
else
consider_secrets = FALSE;
if (consider_secrets) {
g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (a), ==, nm_wireguard_peer_get_preshared_key (b));
g_assert_cmpint (nm_wireguard_peer_cmp (a, b, NM_SETTING_COMPARE_FLAG_EXACT), ==, 0);
}
if (expect_no_secrets)
g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (b), ==, NULL);
}
}
static void
_rndt_wg_peers_fix_secrets (NMSettingWireGuard *s_wg,
GPtrArray *peers)
{
guint i;
g_assert (NM_IS_SETTING_WIREGUARD (s_wg));
g_assert (peers);
g_assert_cmpint (peers->len, ==, nm_setting_wireguard_get_peers_len (s_wg));
for (i = 0; i < peers->len; i++) {
const NMWireGuardPeer *a = peers->pdata[i];
const NMWireGuardPeer *b = nm_setting_wireguard_get_peer (s_wg, i);
nm_auto_unref_wgpeer NMWireGuardPeer *b_clone = NULL;
g_assert (a);
g_assert (b);
g_assert_cmpint (nm_wireguard_peer_get_preshared_key_flags (a), ==, nm_wireguard_peer_get_preshared_key_flags (b));
g_assert_cmpint (nm_wireguard_peer_cmp (a, b, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS), ==, 0);
if (!nm_streq0 (nm_wireguard_peer_get_preshared_key (a),
nm_wireguard_peer_get_preshared_key (b))) {
g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (a), !=, NULL);
g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (b), ==, NULL);
g_assert (NM_IN_SET (nm_wireguard_peer_get_preshared_key_flags (a), NM_SETTING_SECRET_FLAG_AGENT_OWNED,
NM_SETTING_SECRET_FLAG_NOT_SAVED));
b_clone = nm_wireguard_peer_new_clone (b, TRUE);
nm_wireguard_peer_set_preshared_key (b_clone, nm_wireguard_peer_get_preshared_key (a));
nm_setting_wireguard_set_peer (s_wg, b_clone, i);
b = nm_setting_wireguard_get_peer (s_wg, i);
g_assert (b == b_clone);
} else {
if (nm_wireguard_peer_get_preshared_key (a)) {
g_assert (NM_IN_SET (nm_wireguard_peer_get_preshared_key_flags (a), NM_SETTING_SECRET_FLAG_NONE,
NM_SETTING_SECRET_FLAG_NOT_REQUIRED));
} else {
g_assert (NM_IN_SET (nm_wireguard_peer_get_preshared_key_flags (a), NM_SETTING_SECRET_FLAG_AGENT_OWNED,
NM_SETTING_SECRET_FLAG_NONE,
NM_SETTING_SECRET_FLAG_NOT_SAVED,
NM_SETTING_SECRET_FLAG_NOT_REQUIRED));
}
}
g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (a), ==, nm_wireguard_peer_get_preshared_key (b));
g_assert_cmpint (nm_wireguard_peer_cmp (a, b, NM_SETTING_COMPARE_FLAG_EXACT), ==, 0);
}
}
static void
test_roundtrip_conversion (gconstpointer test_data)
{
@ -2022,7 +2275,18 @@ test_roundtrip_conversion (gconstpointer test_data)
const char *INTERFACE_NAME = nm_sprintf_bufa (100, "ifname%d", MODE);
guint32 ETH_MTU = nmtst_rand_select ((guint32) 0u,
nmtst_get_rand_int ());
const char *WG_PRIVATE_KEY = nmtst_get_rand_bool ()
? "yGXGK+5bVnxSJUejH4vbpXbq+ZtaG4NB8IHRK/aVtE0="
: NULL;
const NMSettingSecretFlags WG_PRIVATE_KEY_FLAGS = nmtst_rand_select (NM_SETTING_SECRET_FLAG_NONE,
NM_SETTING_SECRET_FLAG_NOT_SAVED,
NM_SETTING_SECRET_FLAG_AGENT_OWNED);
const guint WG_LISTEN_PORT = nmtst_rand_select (0u,
nmtst_get_rand_int () % 0x10000);
const guint WG_FWMARK = nmtst_rand_select (0u,
nmtst_get_rand_int ());
gs_unref_ptrarray GPtrArray *kf_data_arr = g_ptr_array_new_with_free_func (g_free);
gs_unref_ptrarray GPtrArray *wg_peers = NULL;
const NMConnectionSerializationFlags dbus_serialization_flags[] = {
NM_CONNECTION_SERIALIZE_ALL,
NM_CONNECTION_SERIALIZE_NO_SECRETS,
@ -2031,9 +2295,12 @@ test_roundtrip_conversion (gconstpointer test_data)
guint dbus_serialization_flags_idx;
gs_unref_object NMConnection *con = NULL;
gs_free_error GError *error = NULL;
gs_free char *tmp_str = NULL;
guint kf_data_idx;
NMSettingConnection *s_con = NULL;
NMSettingWired *s_eth = NULL;
NMSettingWireGuard *s_wg = NULL;
guint i;
switch (MODE) {
case 0:
@ -2110,6 +2377,116 @@ test_roundtrip_conversion (gconstpointer test_data)
break;
case 1:
con = nmtst_create_minimal_connection (ID, UUID, "wireguard", &s_con);
g_object_set (s_con,
NM_SETTING_CONNECTION_INTERFACE_NAME,
INTERFACE_NAME,
NULL);
nmtst_connection_normalize (con);
s_wg = NM_SETTING_WIREGUARD (nm_connection_get_setting (con, NM_TYPE_SETTING_WIREGUARD));
g_ptr_array_add (kf_data_arr,
g_strdup_printf ("[connection]\n"
"id=%s\n"
"uuid=%s\n"
"type=wireguard\n"
"interface-name=%s\n"
"permissions=\n"
"\n"
"[ipv4]\n"
"dns-search=\n"
"method=disabled\n"
"\n"
"[ipv6]\n"
"addr-gen-mode=stable-privacy\n"
"dns-search=\n"
"method=ignore\n"
"",
ID,
UUID,
INTERFACE_NAME));
break;
case 2:
con = nmtst_create_minimal_connection (ID, UUID, "wireguard", &s_con);
g_object_set (s_con,
NM_SETTING_CONNECTION_INTERFACE_NAME,
INTERFACE_NAME,
NULL);
nmtst_connection_normalize (con);
s_wg = NM_SETTING_WIREGUARD (nm_connection_get_setting (con, NM_TYPE_SETTING_WIREGUARD));
g_object_set (s_wg,
NM_SETTING_WIREGUARD_PRIVATE_KEY,
WG_PRIVATE_KEY,
NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS,
WG_PRIVATE_KEY_FLAGS,
NM_SETTING_WIREGUARD_LISTEN_PORT,
WG_LISTEN_PORT,
NM_SETTING_WIREGUARD_FWMARK,
WG_FWMARK,
NULL);
wg_peers = _rndt_wg_peers_create ();
for (i = 0; i < wg_peers->len; i++)
nm_setting_wireguard_append_peer (s_wg, wg_peers->pdata[i]);
nm_clear_g_free (&tmp_str);
g_ptr_array_add (kf_data_arr,
g_strdup_printf ("[connection]\n"
"id=%s\n"
"uuid=%s\n"
"type=wireguard\n"
"interface-name=%s\n"
"permissions=\n"
"%s" /* [wireguard] */
"%s" /* fwmark */
"%s" /* listen-port */
"%s" /* private-key-flags */
"%s" /* private-key */
"%s" /* [wireguard-peers*] */
"\n"
"[ipv4]\n"
"dns-search=\n"
"method=disabled\n"
"\n"
"[ipv6]\n"
"addr-gen-mode=stable-privacy\n"
"dns-search=\n"
"method=ignore\n"
"",
ID,
UUID,
INTERFACE_NAME,
( ( (WG_FWMARK != 0)
|| (WG_LISTEN_PORT != 0)
|| (WG_PRIVATE_KEY_FLAGS != NM_SETTING_SECRET_FLAG_NONE)
|| ( WG_PRIVATE_KEY
&& WG_PRIVATE_KEY_FLAGS == NM_SETTING_SECRET_FLAG_NONE))
? "\n[wireguard]\n"
: ""),
( (WG_FWMARK != 0)
? nm_sprintf_bufa (100, "fwmark=%u\n", WG_FWMARK)
: ""),
( (WG_LISTEN_PORT != 0)
? nm_sprintf_bufa (100, "listen-port=%u\n", WG_LISTEN_PORT)
: ""),
( (WG_PRIVATE_KEY_FLAGS != NM_SETTING_SECRET_FLAG_NONE)
? nm_sprintf_bufa (100, "private-key-flags=%u\n", (guint) WG_PRIVATE_KEY_FLAGS)
: ""),
( ( WG_PRIVATE_KEY
&& WG_PRIVATE_KEY_FLAGS == NM_SETTING_SECRET_FLAG_NONE)
? nm_sprintf_bufa (100, "private-key=%s\n", WG_PRIVATE_KEY)
: ""),
_rndt_wg_peers_to_keyfile (wg_peers, TRUE, &tmp_str)));
_rndt_wg_peers_assert_equal (s_wg, wg_peers, TRUE, TRUE, FALSE);
break;
default:
g_assert_not_reached ();
}
@ -2131,6 +2508,7 @@ test_roundtrip_conversion (gconstpointer test_data)
/* check that reading any of kf_data_arr yields the same result that we expect. */
for (kf_data_idx = 0; kf_data_idx < kf_data_arr->len; kf_data_idx++) {
gs_unref_object NMConnection *con2 = NULL;
NMSettingWireGuard *s_wg2 = NULL;
NMSettingWired *s_eth2 = NULL;
con2 = nmtst_create_connection_from_keyfile (kf_data_arr->pdata[kf_data_idx], "/no/where/file.nmconnection");
@ -2159,6 +2537,35 @@ test_roundtrip_conversion (gconstpointer test_data)
g_assert_cmpint (nm_setting_wired_get_mtu (s_eth), ==, ETH_MTU);
g_assert_cmpint (nm_setting_wired_get_mtu (s_eth2), ==, ETH_MTU);
break;
case 1:
s_wg2 = NM_SETTING_WIREGUARD (nm_connection_get_setting (con2, NM_TYPE_SETTING_WIREGUARD));
g_assert (NM_IS_SETTING_WIREGUARD (s_wg2));
g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg), ==, NULL);
g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg2), ==, NULL);
break;
case 2:
s_wg2 = NM_SETTING_WIREGUARD (nm_connection_get_setting (con2, NM_TYPE_SETTING_WIREGUARD));
g_assert (NM_IS_SETTING_WIREGUARD (s_wg2));
/* the private key was lost due to the secret-flags. Patch it. */
if (WG_PRIVATE_KEY_FLAGS != NM_SETTING_SECRET_FLAG_NONE) {
g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg2), ==, NULL);
g_object_set (s_wg2,
NM_SETTING_WIREGUARD_PRIVATE_KEY,
WG_PRIVATE_KEY,
NULL);
}
g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg), ==, WG_PRIVATE_KEY);
g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg2), ==, WG_PRIVATE_KEY);
_rndt_wg_peers_assert_equal (s_wg2, wg_peers, TRUE, FALSE, FALSE);
_rndt_wg_peers_fix_secrets (s_wg2, wg_peers);
_rndt_wg_peers_assert_equal (s_wg2, wg_peers, TRUE, TRUE, FALSE);
break;
}
nmtst_assert_connection_equals (con, nmtst_get_rand_bool (), con2, nmtst_get_rand_bool ());
@ -2168,6 +2575,7 @@ test_roundtrip_conversion (gconstpointer test_data)
NMConnectionSerializationFlags flag = dbus_serialization_flags[dbus_serialization_flags_idx];
gs_unref_variant GVariant *con_var = NULL;
gs_unref_object NMConnection *con2 = NULL;
NMSettingWireGuard *s_wg2 = NULL;
con_var = nm_connection_to_dbus (con, flag);
g_assert (g_variant_is_of_type (con_var, NM_VARIANT_TYPE_CONNECTION));
@ -2186,6 +2594,21 @@ test_roundtrip_conversion (gconstpointer test_data)
nmtst_keyfile_assert_data (kf, kf_data_arr->pdata[0], -1);
}
}
switch (MODE) {
case 2:
if (flag == NM_CONNECTION_SERIALIZE_ALL) {
s_wg2 = NM_SETTING_WIREGUARD (nm_connection_get_setting (con2, NM_TYPE_SETTING_WIREGUARD));
if (flag == NM_CONNECTION_SERIALIZE_ALL)
_rndt_wg_peers_assert_equal (s_wg2, wg_peers, TRUE, TRUE, FALSE);
else if (flag == NM_CONNECTION_SERIALIZE_NO_SECRETS)
_rndt_wg_peers_assert_equal (s_wg2, wg_peers, FALSE, FALSE, TRUE);
else
g_assert_not_reached ();
}
break;
}
}
}
@ -2268,6 +2691,8 @@ main (int argc, char **argv)
#endif
g_test_add_data_func ("/libnm/settings/roundtrip-conversion/general/0", GINT_TO_POINTER (0), test_roundtrip_conversion);
g_test_add_data_func ("/libnm/settings/roundtrip-conversion/wireguard/1", GINT_TO_POINTER (1), test_roundtrip_conversion);
g_test_add_data_func ("/libnm/settings/roundtrip-conversion/wireguard/2", GINT_TO_POINTER (2), test_roundtrip_conversion);
return g_test_run ();
}

View file

@ -105,6 +105,7 @@
#include "nm-setting-vxlan.h"
#include "nm-setting-wimax.h"
#include "nm-setting-wired.h"
#include "nm-setting-wireguard.h"
#include "nm-setting-wireless.h"
#include "nm-setting-wireless-security.h"
#include "nm-setting-wpan.h"

View file

@ -1462,6 +1462,19 @@ global:
nm_setting_wifi_p2p_get_wfd_ies;
nm_setting_wifi_p2p_get_wps_method;
nm_setting_wifi_p2p_new;
nm_setting_wireguard_append_peer;
nm_setting_wireguard_clear_peers;
nm_setting_wireguard_get_fwmark;
nm_setting_wireguard_get_listen_port;
nm_setting_wireguard_get_peer;
nm_setting_wireguard_get_peer_by_public_key;
nm_setting_wireguard_get_peers_len;
nm_setting_wireguard_get_private_key;
nm_setting_wireguard_get_private_key_flags;
nm_setting_wireguard_get_type;
nm_setting_wireguard_new;
nm_setting_wireguard_remove_peer;
nm_setting_wireguard_set_peer;
nm_team_link_watcher_get_vlanid;
nm_team_link_watcher_new_arp_ping2;
nm_wifi_p2p_peer_connection_valid;
@ -1477,4 +1490,28 @@ global:
nm_wifi_p2p_peer_get_strength;
nm_wifi_p2p_peer_get_type;
nm_wifi_p2p_peer_get_wfd_ies;
nm_wireguard_peer_append_allowed_ip;
nm_wireguard_peer_clear_allowed_ips;
nm_wireguard_peer_cmp;
nm_wireguard_peer_get_allowed_ip;
nm_wireguard_peer_get_allowed_ips_len;
nm_wireguard_peer_get_endpoint;
nm_wireguard_peer_get_persistent_keepalive;
nm_wireguard_peer_get_preshared_key;
nm_wireguard_peer_get_preshared_key_flags;
nm_wireguard_peer_get_public_key;
nm_wireguard_peer_get_type;
nm_wireguard_peer_is_sealed;
nm_wireguard_peer_is_valid;
nm_wireguard_peer_new;
nm_wireguard_peer_new_clone;
nm_wireguard_peer_ref;
nm_wireguard_peer_remove_allowed_ip;
nm_wireguard_peer_seal;
nm_wireguard_peer_set_endpoint;
nm_wireguard_peer_set_persistent_keepalive;
nm_wireguard_peer_set_preshared_key;
nm_wireguard_peer_set_preshared_key_flags;
nm_wireguard_peer_set_public_key;
nm_wireguard_peer_unref;
} libnm_1_14_0;

View file

@ -80,6 +80,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (NMSettingVxlan, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (NMSettingWifiP2P, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (NMSettingWimax, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (NMSettingWired, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (NMSettingWireGuard, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (NMSettingWireless, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (NMSettingWirelessSecurity, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (NMSettingWpan, g_object_unref)

View file

@ -97,6 +97,7 @@ libnm-core/nm-setting-vxlan.c
libnm-core/nm-setting-wifi-p2p.c
libnm-core/nm-setting-wimax.c
libnm-core/nm-setting-wired.c
libnm-core/nm-setting-wireguard.c
libnm-core/nm-setting-wireless-security.c
libnm-core/nm-setting-wireless.c
libnm-core/nm-setting-wpan.c

View file

@ -65,6 +65,7 @@
#include "nm-setting-wifi-p2p.h"
#include "nm-setting-wimax.h"
#include "nm-setting-wired.h"
#include "nm-setting-wireguard.h"
#include "nm-setting-wireless-security.h"
#include "nm-setting-wireless.h"
#include "nm-setting-wpan.h"
@ -402,6 +403,12 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
.setting_name = NM_SETTING_WIRED_SETTING_NAME,
.get_setting_gtype = nm_setting_wired_get_type,
},
[NM_META_SETTING_TYPE_WIREGUARD] = {
.meta_type = NM_META_SETTING_TYPE_WIREGUARD,
.setting_priority = NM_SETTING_PRIORITY_HW_BASE,
.setting_name = NM_SETTING_WIREGUARD_SETTING_NAME,
.get_setting_gtype = nm_setting_wireguard_get_type,
},
[NM_META_SETTING_TYPE_WIRELESS] = {
.meta_type = NM_META_SETTING_TYPE_WIRELESS,
.setting_priority = NM_SETTING_PRIORITY_HW_BASE,

View file

@ -147,6 +147,7 @@ typedef enum {
NM_META_SETTING_TYPE_VXLAN,
NM_META_SETTING_TYPE_WIFI_P2P,
NM_META_SETTING_TYPE_WIMAX,
NM_META_SETTING_TYPE_WIREGUARD,
NM_META_SETTING_TYPE_WPAN,
NM_META_SETTING_TYPE_UNKNOWN,

File diff suppressed because it is too large Load diff

View file

@ -4363,8 +4363,7 @@ realize_start_setup (NMDevice *self,
* NetworkManager might down the interface or remove the 127.0.0.1 address. */
nm_device_set_unmanaged_flags (self,
NM_UNMANAGED_BY_TYPE,
is_loopback (self)
|| NM_IS_DEVICE_WIREGUARD (self));
is_loopback (self));
nm_device_set_unmanaged_by_user_udev (self);
nm_device_set_unmanaged_by_user_conf (self);