diff --git a/Makefile.am b/Makefile.am index bbbb37de87..005dbace18 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/clients/cli/connections.c b/clients/cli/connections.c index ff7d020303..63cb0bb90c 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -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"," \ diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c index e33070dc06..03be32f645 100644 --- a/clients/common/nm-meta-setting-desc.c +++ b/clients/common/nm-meta-setting-desc.c @@ -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 ( diff --git a/clients/common/nm-secret-agent-simple.c b/clients/common/nm-secret-agent-simple.c index ffcb7c8978..6b72d2aa00 100644 --- a/clients/common/nm-secret-agent-simple.c +++ b/clients/common/nm-secret-agent-simple.c @@ -405,8 +405,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 +435,56 @@ 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++) { + const char *name = request->hints[i]; + gs_free char *peer_name = NULL; + + if (nm_streq (name, NM_SETTING_WIREGUARD_PRIVATE_KEY)) + continue; + + /* TODO: add support for WireGuard peers and their preshared-key. */ + g_set_error (error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED, + _("Cannot service unknown WireGuard hint '%s' for secrets request %s"), + name, + request->request_id); + return FALSE; + } + } + + *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 +870,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); diff --git a/clients/common/settings-docs.h.in b/clients/common/settings-docs.h.in index 6a0586e30e..235a9c7f30 100644 --- a/clients/common/settings-docs.h.in +++ b/clients/common/settings-docs.h.in @@ -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\".") diff --git a/docs/libnm/libnm-docs.xml b/docs/libnm/libnm-docs.xml index 2a7b76bf34..74866acebd 100644 --- a/docs/libnm/libnm-docs.xml +++ b/docs/libnm/libnm-docs.xml @@ -234,6 +234,7 @@ print ("NetworkManager version " + client.get_version())]]> + diff --git a/libnm-core/meson.build b/libnm-core/meson.build index a2610fe46f..d10dd1c551 100644 --- a/libnm-core/meson.build +++ b/libnm-core/meson.build @@ -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', diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c index 5e84cf45b6..e958aff06d 100644 --- a/libnm-core/nm-connection.c +++ b/libnm-core/nm-connection.c @@ -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)) { diff --git a/libnm-core/nm-core-enum-types.c.template b/libnm-core/nm-core-enum-types.c.template index 2cef0307a1..94744827ba 100644 --- a/libnm-core/nm-core-enum-types.c.template +++ b/libnm-core/nm-core-enum-types.c.template @@ -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" diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 19a914956e..54d852a7fd 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -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" @@ -768,4 +769,14 @@ gboolean _nm_connection_find_secret (NMConnection *self, /*****************************************************************************/ +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 diff --git a/libnm-core/nm-core-types.h b/libnm-core/nm-core-types.h index e8aa67a93f..f20ffc09da 100644 --- a/libnm-core/nm-core-types.h +++ b/libnm-core/nm-core-types.h @@ -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; diff --git a/libnm-core/nm-setting-wireguard.c b/libnm-core/nm-setting-wireguard.c new file mode 100644 index 0000000000..724dda55d1 --- /dev/null +++ b/libnm-core/nm-setting-wireguard.c @@ -0,0 +1,404 @@ +/* + * 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. + */ + +#include "nm-default.h" + +#include "nm-setting-wireguard.h" + +#include "nm-setting-private.h" +#include "nm-utils-private.h" +#include "nm-connection-private.h" +#include "nm-utils/nm-secret-utils.h" + +/*****************************************************************************/ + +/** + * SECTION:nm-setting-wireguard + * @short_description: Describes connection properties for wireguard related options + * + * The #NMSettingWireGuard object is a #NMSetting subclass that contains settings + * for configuring WireGuard. + **/ + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE ( + PROP_PRIVATE_KEY, + PROP_PRIVATE_KEY_FLAGS, + PROP_LISTEN_PORT, + PROP_FWMARK, +); + +typedef struct { + char *private_key; + NMSettingSecretFlags private_key_flags; + guint32 fwmark; + guint16 listen_port; + bool private_key_valid:1; +} NMSettingWireGuardPrivate; + +/** + * NMSettingWireGuard: + * + * WireGuard Ethernet Settings + * + * Since: 1.16 + */ +struct _NMSettingWireGuard { + NMSetting parent; + NMSettingWireGuardPrivate _priv; +}; + +struct _NMSettingWireGuardClass { + NMSettingClass parent; +}; + +G_DEFINE_TYPE (NMSettingWireGuard, nm_setting_wireguard, NM_TYPE_SETTING) + +#define NM_SETTING_WIREGUARD_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSettingWireGuard, NM_IS_SETTING_WIREGUARD, NMSetting) + +/*****************************************************************************/ + +/** + * nm_setting_wireguard_get_private_key: + * @self: the #NMSettingWireGuard instance + * + * Returns: (transfer none): the set private-key or %NULL. + * + * Since: 1.16 + */ +const char * +nm_setting_wireguard_get_private_key (NMSettingWireGuard *self) +{ + g_return_val_if_fail (NM_IS_SETTING_WIREGUARD (self), NULL); + + return NM_SETTING_WIREGUARD_GET_PRIVATE (self)->private_key; +} + +/** + * nm_setting_wireguard_get_private_key_flags: + * @self: the #NMSettingWireGuard instance + * + * Returns: the secret-flags for #NMSettingWireGuard:private-key. + * + * Since: 1.16 + */ +NMSettingSecretFlags +nm_setting_wireguard_get_private_key_flags (NMSettingWireGuard *self) +{ + g_return_val_if_fail (NM_IS_SETTING_WIREGUARD (self), 0); + + return NM_SETTING_WIREGUARD_GET_PRIVATE (self)->private_key_flags; +} + +/** + * nm_setting_wireguard_get_fwmark: + * @self: the #NMSettingWireGuard instance + * + * Returns: the set firewall mark. + * + * Since: 1.16 + */ +guint32 +nm_setting_wireguard_get_fwmark (NMSettingWireGuard *self) +{ + g_return_val_if_fail (NM_IS_SETTING_WIREGUARD (self), 0); + + return NM_SETTING_WIREGUARD_GET_PRIVATE (self)->fwmark; +} + +/** + * nm_setting_wireguard_get_listen_port: + * @self: the #NMSettingWireGuard instance + * + * Returns: the set UDP listen port. + * + * Since: 1.16 + */ +guint16 +nm_setting_wireguard_get_listen_port (NMSettingWireGuard *self) +{ + g_return_val_if_fail (NM_IS_SETTING_WIREGUARD (self), 0); + + return NM_SETTING_WIREGUARD_GET_PRIVATE (self)->listen_port; +} + +/*****************************************************************************/ + +static gboolean +verify (NMSetting *setting, NMConnection *connection, GError **error) +{ + NMSettingWireGuard *s_wg = NM_SETTING_WIREGUARD (setting); + + if (!_nm_connection_verify_required_interface_name (connection, error)) + return FALSE; + + if (!_nm_utils_secret_flags_validate (nm_setting_wireguard_get_private_key_flags (s_wg), + NM_SETTING_WIREGUARD_SETTING_NAME, + NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS, + NM_SETTING_SECRET_FLAG_NOT_REQUIRED, + error)) + return FALSE; + + if (connection) { + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + const char *method; + + /* WireGuard is Layer 3 only. For the moment, we only support a restricted set of + * IP methods. We may relax that later, once we fix the implementations so they + * actually work. */ + + if ( (s_ip4 = nm_connection_get_setting_ip4_config (connection)) + && (method = nm_setting_ip_config_get_method (s_ip4)) + && !NM_IN_STRSET (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("method \"%s\" is not supported for WireGuard"), + method); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP_CONFIG_METHOD); + return FALSE; + } + + if ( (s_ip6 = nm_connection_get_setting_ip6_config (connection)) + && (method = nm_setting_ip_config_get_method (s_ip6)) + && !NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL, + NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("method \"%s\" is not supported for WireGuard"), + method); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP_CONFIG_METHOD); + return FALSE; + } + } + + /* private-key is a secret, hence we cannot verify it like a regular property. */ + return TRUE; +} + +static gboolean +verify_secrets (NMSetting *setting, NMConnection *connection, GError **error) +{ + NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE (setting); + + if ( priv->private_key + && !priv->private_key_valid) { + g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("key must be 32 bytes base64 encoded")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIREGUARD_SETTING_NAME, NM_SETTING_WIREGUARD_PRIVATE_KEY); + return FALSE; + } + + return TRUE; +} + +static GPtrArray * +need_secrets (NMSetting *setting) +{ + NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE (setting); + GPtrArray *secrets = NULL; + + if ( !priv->private_key + || !priv->private_key_valid) { + secrets = g_ptr_array_new_full (1, NULL); + g_ptr_array_add (secrets, NM_SETTING_WIREGUARD_PRIVATE_KEY); + } + + return secrets; +} + +/*****************************************************************************/ + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMSettingWireGuard *setting = NM_SETTING_WIREGUARD (object); + NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE (setting); + + switch (prop_id) { + case PROP_PRIVATE_KEY: + g_value_set_string (value, priv->private_key); + break; + case PROP_PRIVATE_KEY_FLAGS: + g_value_set_flags (value, priv->private_key_flags); + break; + case PROP_LISTEN_PORT: + g_value_set_uint (value, priv->listen_port); + break; + case PROP_FWMARK: + g_value_set_uint (value, priv->fwmark); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE (object); + const char *str; + + switch (prop_id) { + case PROP_PRIVATE_KEY: + nm_clear_pointer (&priv->private_key, nm_free_secret); + str = g_value_get_string (value); + if (str) { + if (_nm_utils_wireguard_normalize_key (str, + NM_WIREGUARD_PUBLIC_KEY_LEN, + &priv->private_key)) + priv->private_key_valid = TRUE; + else { + priv->private_key = g_strdup (str); + priv->private_key_valid = FALSE; + } + } + break; + case PROP_PRIVATE_KEY_FLAGS: + priv->private_key_flags = g_value_get_flags (value); + break; + case PROP_LISTEN_PORT: + priv->listen_port = g_value_get_uint (value); + break; + case PROP_FWMARK: + priv->fwmark = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_setting_wireguard_init (NMSettingWireGuard *setting) +{ +} + +/** + * nm_setting_wireguard_new: + * + * Creates a new #NMSettingWireGuard object with default values. + * + * Returns: (transfer full): the new empty #NMSettingWireGuard object + * + * Since: 1.16 + **/ +NMSetting * +nm_setting_wireguard_new (void) +{ + return g_object_new (NM_TYPE_SETTING_WIREGUARD, NULL); +} + +static void +finalize (GObject *object) +{ + NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE (object); + + nm_free_secret (priv->private_key); + + G_OBJECT_CLASS (nm_setting_wireguard_parent_class)->finalize (object); +} + +static void +nm_setting_wireguard_class_init (NMSettingWireGuardClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMSettingClass *setting_class = NM_SETTING_CLASS (klass); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + + setting_class->verify = verify; + setting_class->verify_secrets = verify_secrets; + setting_class->need_secrets = need_secrets; + + /** + * NMSettingWireGuard:private-key: + * + * The 256 bit private-key in base64 encoding. + * + * Since: 1.16 + **/ + obj_properties[PROP_PRIVATE_KEY] = + g_param_spec_string (NM_SETTING_WIREGUARD_PRIVATE_KEY, "", "", + NULL, + G_PARAM_READWRITE + | NM_SETTING_PARAM_SECRET + | G_PARAM_STATIC_STRINGS); + + /** + * NMSettingWireGuard:private-key-flags: + * + * Flags indicating how to handle the #NMSettingWirelessSecurity:private-key + * property. + * + * Since: 1.16 + **/ + obj_properties[PROP_PRIVATE_KEY_FLAGS] = + g_param_spec_flags (NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS, "", "", + NM_TYPE_SETTING_SECRET_FLAGS, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE + | G_PARAM_STATIC_STRINGS); + + /** + * NMSettingWireGuard:fwmark: + * + * 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. + * + * Since: 1.16 + **/ + obj_properties[PROP_FWMARK] = + g_param_spec_uint (NM_SETTING_WIREGUARD_FWMARK, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE + | NM_SETTING_PARAM_INFERRABLE + | G_PARAM_STATIC_STRINGS); + + /** + * NMSettingWireGuard:listen-port: + * + * The listen-port. If listen-port is not specified, the port will be chosen + * randomly when the interface comes up. + * + * Since: 1.16 + **/ + obj_properties[PROP_LISTEN_PORT] = + g_param_spec_uint (NM_SETTING_WIREGUARD_LISTEN_PORT, "", "", + 0, 65535, 0, + G_PARAM_READWRITE + | NM_SETTING_PARAM_INFERRABLE + | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); + + _nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_WIREGUARD); +} diff --git a/libnm-core/nm-setting-wireguard.h b/libnm-core/nm-setting-wireguard.h new file mode 100644 index 0000000000..1127b11382 --- /dev/null +++ b/libnm-core/nm-setting-wireguard.h @@ -0,0 +1,80 @@ +/* + * 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 can be included directly." +#endif + +#include "nm-setting.h" + +G_BEGIN_DECLS + +/*****************************************************************************/ + +#define NM_WIREGUARD_PUBLIC_KEY_LEN 32 +#define NM_WIREGUARD_SYMMETRIC_KEY_LEN 32 + +/*****************************************************************************/ + +#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" + +/*****************************************************************************/ + +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); + +/*****************************************************************************/ + +G_END_DECLS + +#endif /* __NM_SETTING_WIREGUARD_H__ */ diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index cd354d036e..fd9f87f879 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -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; +} diff --git a/libnm/NetworkManager.h b/libnm/NetworkManager.h index 1e59d11758..7c70a226fd 100644 --- a/libnm/NetworkManager.h +++ b/libnm/NetworkManager.h @@ -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" diff --git a/libnm/libnm.ver b/libnm/libnm.ver index cc8b211b8c..7689072eeb 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1462,6 +1462,12 @@ global: nm_setting_wifi_p2p_get_wfd_ies; nm_setting_wifi_p2p_get_wps_method; nm_setting_wifi_p2p_new; + nm_setting_wireguard_get_fwmark; + nm_setting_wireguard_get_listen_port; + nm_setting_wireguard_get_private_key; + nm_setting_wireguard_get_private_key_flags; + nm_setting_wireguard_get_type; + nm_setting_wireguard_new; nm_team_link_watcher_get_vlanid; nm_team_link_watcher_new_arp_ping2; nm_wifi_p2p_peer_connection_valid; diff --git a/libnm/nm-autoptr.h b/libnm/nm-autoptr.h index 8abd792e22..40248dcd3e 100644 --- a/libnm/nm-autoptr.h +++ b/libnm/nm-autoptr.h @@ -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) diff --git a/po/POTFILES.in b/po/POTFILES.in index 393f84e8ca..148fd546f9 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -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 diff --git a/shared/nm-meta-setting.c b/shared/nm-meta-setting.c index e7e73bd88e..e666e0b2a2 100644 --- a/shared/nm-meta-setting.c +++ b/shared/nm-meta-setting.c @@ -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, diff --git a/shared/nm-meta-setting.h b/shared/nm-meta-setting.h index 883d8ca195..18727a1638 100644 --- a/shared/nm-meta-setting.h +++ b/shared/nm-meta-setting.h @@ -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, diff --git a/src/devices/nm-device-wireguard.c b/src/devices/nm-device-wireguard.c index 62ec027494..0ca8cf3a5a 100644 --- a/src/devices/nm-device-wireguard.c +++ b/src/devices/nm-device-wireguard.c @@ -21,6 +21,8 @@ #include "nm-device-wireguard.h" +#include "nm-setting-wireguard.h" + #include "nm-device-private.h" #include "platform/nm-platform.h" #include "nm-device-factory.h" @@ -30,6 +32,11 @@ _LOG_DECLARE_SELF(NMDeviceWireGuard); /*****************************************************************************/ +G_STATIC_ASSERT (NM_WIREGUARD_PUBLIC_KEY_LEN == NMP_WIREGUARD_PUBLIC_KEY_LEN); +G_STATIC_ASSERT (NM_WIREGUARD_SYMMETRIC_KEY_LEN == NMP_WIREGUARD_SYMMETRIC_KEY_LEN); + +/*****************************************************************************/ + NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceWireGuard, PROP_PUBLIC_KEY, PROP_LISTEN_PORT,