2019-09-10 11:19:01 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2013-12-02 16:20:26 -05:00
|
|
|
/*
|
2019-10-01 09:20:35 +02:00
|
|
|
* Copyright (C) 2013 - 2015 Red Hat, Inc.
|
2013-12-02 16:20:26 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
2015-11-23 17:47:00 +01:00
|
|
|
* SECTION:nm-vpn-helpers
|
2013-12-02 16:20:26 -05:00
|
|
|
* @short_description: VPN-related utilities
|
|
|
|
|
*/
|
|
|
|
|
|
2016-02-12 14:44:52 +01:00
|
|
|
#include "nm-default.h"
|
2016-02-19 14:57:48 +01:00
|
|
|
|
2016-02-12 14:44:52 +01:00
|
|
|
#include "nm-vpn-helpers.h"
|
|
|
|
|
|
2019-02-28 14:22:35 +01:00
|
|
|
#include <arpa/inet.h>
|
|
|
|
|
#include <net/if.h>
|
|
|
|
|
|
|
|
|
|
#include "nm-client-utils.h"
|
2015-12-11 21:47:27 +01:00
|
|
|
#include "nm-utils.h"
|
2019-04-15 08:16:00 +02:00
|
|
|
#include "nm-glib-aux/nm-io-utils.h"
|
|
|
|
|
#include "nm-glib-aux/nm-secret-utils.h"
|
2013-12-02 16:20:26 -05:00
|
|
|
|
cli: fix handling of VPN names in nmcli by using libnm functions
At various places, nmcli requires to specify a VPN type by name, for example
$ nmcli connection add type vpn ifname '*' vpn-type $VPN_TYPE
This $VPN_TYPE used to be a hard-coded list of known VPN plugin names.
But actually, it should be a VPN service-type. A service-type used to be
the D-Bus name of the VPN plugin. Now, with multiple VPN support that
is no longer the case, but it still has the form of a D-Bus bus name.
Alternativley, it could be an alias, which is just a way for plugins
to support multiple service-types.
Fix that, to support fully qualified service-types in the form
of D-Bus bus names. Also, support lookup by name, in which case
the present plugin-info instances are searched.
Finally, support a list of hard-code short-names.
All the logic how to translate a short-name to a fully qualified
service-type is now inside libnm, so that various user agree on
those names and don't have to hard-code them each.
2016-06-07 11:06:14 +02:00
|
|
|
/*****************************************************************************/
|
2013-12-02 16:20:26 -05:00
|
|
|
|
2015-05-26 11:10:12 +02:00
|
|
|
NMVpnEditorPlugin *
|
cli: fix handling of VPN names in nmcli by using libnm functions
At various places, nmcli requires to specify a VPN type by name, for example
$ nmcli connection add type vpn ifname '*' vpn-type $VPN_TYPE
This $VPN_TYPE used to be a hard-coded list of known VPN plugin names.
But actually, it should be a VPN service-type. A service-type used to be
the D-Bus name of the VPN plugin. Now, with multiple VPN support that
is no longer the case, but it still has the form of a D-Bus bus name.
Alternativley, it could be an alias, which is just a way for plugins
to support multiple service-types.
Fix that, to support fully qualified service-types in the form
of D-Bus bus names. Also, support lookup by name, in which case
the present plugin-info instances are searched.
Finally, support a list of hard-code short-names.
All the logic how to translate a short-name to a fully qualified
service-type is now inside libnm, so that various user agree on
those names and don't have to hard-code them each.
2016-06-07 11:06:14 +02:00
|
|
|
nm_vpn_get_editor_plugin (const char *service_type, GError **error)
|
2013-12-02 16:20:26 -05:00
|
|
|
{
|
2015-05-26 11:10:12 +02:00
|
|
|
NMVpnEditorPlugin *plugin = NULL;
|
|
|
|
|
NMVpnPluginInfo *plugin_info;
|
2016-05-19 11:17:46 +02:00
|
|
|
gs_free_error GError *local = NULL;
|
2015-05-26 11:10:12 +02:00
|
|
|
|
cli: fix handling of VPN names in nmcli by using libnm functions
At various places, nmcli requires to specify a VPN type by name, for example
$ nmcli connection add type vpn ifname '*' vpn-type $VPN_TYPE
This $VPN_TYPE used to be a hard-coded list of known VPN plugin names.
But actually, it should be a VPN service-type. A service-type used to be
the D-Bus name of the VPN plugin. Now, with multiple VPN support that
is no longer the case, but it still has the form of a D-Bus bus name.
Alternativley, it could be an alias, which is just a way for plugins
to support multiple service-types.
Fix that, to support fully qualified service-types in the form
of D-Bus bus names. Also, support lookup by name, in which case
the present plugin-info instances are searched.
Finally, support a list of hard-code short-names.
All the logic how to translate a short-name to a fully qualified
service-type is now inside libnm, so that various user agree on
those names and don't have to hard-code them each.
2016-06-07 11:06:14 +02:00
|
|
|
g_return_val_if_fail (service_type, NULL);
|
2015-11-30 16:20:10 +01:00
|
|
|
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
2013-12-02 16:20:26 -05:00
|
|
|
|
cli: fix handling of VPN names in nmcli by using libnm functions
At various places, nmcli requires to specify a VPN type by name, for example
$ nmcli connection add type vpn ifname '*' vpn-type $VPN_TYPE
This $VPN_TYPE used to be a hard-coded list of known VPN plugin names.
But actually, it should be a VPN service-type. A service-type used to be
the D-Bus name of the VPN plugin. Now, with multiple VPN support that
is no longer the case, but it still has the form of a D-Bus bus name.
Alternativley, it could be an alias, which is just a way for plugins
to support multiple service-types.
Fix that, to support fully qualified service-types in the form
of D-Bus bus names. Also, support lookup by name, in which case
the present plugin-info instances are searched.
Finally, support a list of hard-code short-names.
All the logic how to translate a short-name to a fully qualified
service-type is now inside libnm, so that various user agree on
those names and don't have to hard-code them each.
2016-06-07 11:06:14 +02:00
|
|
|
plugin_info = nm_vpn_plugin_info_list_find_by_service (nm_vpn_get_plugin_infos (), service_type);
|
2015-11-30 16:39:55 +01:00
|
|
|
|
2016-05-19 11:17:46 +02:00
|
|
|
if (!plugin_info) {
|
2016-05-19 10:06:27 +02:00
|
|
|
g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_FAILED,
|
cli: fix handling of VPN names in nmcli by using libnm functions
At various places, nmcli requires to specify a VPN type by name, for example
$ nmcli connection add type vpn ifname '*' vpn-type $VPN_TYPE
This $VPN_TYPE used to be a hard-coded list of known VPN plugin names.
But actually, it should be a VPN service-type. A service-type used to be
the D-Bus name of the VPN plugin. Now, with multiple VPN support that
is no longer the case, but it still has the form of a D-Bus bus name.
Alternativley, it could be an alias, which is just a way for plugins
to support multiple service-types.
Fix that, to support fully qualified service-types in the form
of D-Bus bus names. Also, support lookup by name, in which case
the present plugin-info instances are searched.
Finally, support a list of hard-code short-names.
All the logic how to translate a short-name to a fully qualified
service-type is now inside libnm, so that various user agree on
those names and don't have to hard-code them each.
2016-06-07 11:06:14 +02:00
|
|
|
_("unknown VPN plugin \"%s\""), service_type);
|
2016-05-19 11:17:46 +02:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
plugin = nm_vpn_plugin_info_get_editor_plugin (plugin_info);
|
|
|
|
|
if (!plugin)
|
|
|
|
|
plugin = nm_vpn_plugin_info_load_editor_plugin (plugin_info, &local);
|
|
|
|
|
|
|
|
|
|
if (!plugin) {
|
|
|
|
|
if ( !nm_vpn_plugin_info_get_plugin (plugin_info)
|
|
|
|
|
&& nm_vpn_plugin_info_lookup_property (plugin_info, NM_VPN_PLUGIN_INFO_KF_GROUP_GNOME, "properties")) {
|
|
|
|
|
g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_FAILED,
|
2016-08-26 19:04:47 +03:00
|
|
|
_("cannot load legacy-only VPN plugin \"%s\" for \"%s\""),
|
2016-05-19 11:17:46 +02:00
|
|
|
nm_vpn_plugin_info_get_name (plugin_info),
|
|
|
|
|
nm_vpn_plugin_info_get_filename (plugin_info));
|
|
|
|
|
} else if (g_error_matches (local, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
|
|
|
|
|
g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_FAILED,
|
|
|
|
|
_("cannot load VPN plugin \"%s\" due to missing \"%s\". Missing client plugin?"),
|
|
|
|
|
nm_vpn_plugin_info_get_name (plugin_info),
|
|
|
|
|
nm_vpn_plugin_info_get_plugin (plugin_info));
|
|
|
|
|
} else {
|
|
|
|
|
g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_FAILED,
|
|
|
|
|
_("failed to load VPN plugin \"%s\": %s"),
|
|
|
|
|
nm_vpn_plugin_info_get_name (plugin_info),
|
|
|
|
|
local->message);
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-26 11:10:12 +02:00
|
|
|
return plugin;
|
2013-12-02 16:20:26 -05:00
|
|
|
}
|
|
|
|
|
|
2015-05-26 11:10:12 +02:00
|
|
|
GSList *
|
2016-06-06 10:21:18 +02:00
|
|
|
nm_vpn_get_plugin_infos (void)
|
2013-12-02 16:20:26 -05:00
|
|
|
{
|
cli: fix handling of VPN names in nmcli by using libnm functions
At various places, nmcli requires to specify a VPN type by name, for example
$ nmcli connection add type vpn ifname '*' vpn-type $VPN_TYPE
This $VPN_TYPE used to be a hard-coded list of known VPN plugin names.
But actually, it should be a VPN service-type. A service-type used to be
the D-Bus name of the VPN plugin. Now, with multiple VPN support that
is no longer the case, but it still has the form of a D-Bus bus name.
Alternativley, it could be an alias, which is just a way for plugins
to support multiple service-types.
Fix that, to support fully qualified service-types in the form
of D-Bus bus names. Also, support lookup by name, in which case
the present plugin-info instances are searched.
Finally, support a list of hard-code short-names.
All the logic how to translate a short-name to a fully qualified
service-type is now inside libnm, so that various user agree on
those names and don't have to hard-code them each.
2016-06-07 11:06:14 +02:00
|
|
|
static bool plugins_loaded;
|
|
|
|
|
static GSList *plugins = NULL;
|
|
|
|
|
|
2015-05-26 11:10:12 +02:00
|
|
|
if (G_LIKELY (plugins_loaded))
|
2013-12-02 16:20:26 -05:00
|
|
|
return plugins;
|
2015-05-26 11:10:12 +02:00
|
|
|
plugins_loaded = TRUE;
|
|
|
|
|
plugins = nm_vpn_plugin_info_list_load ();
|
2013-12-02 16:20:26 -05:00
|
|
|
return plugins;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean
|
2015-11-23 17:47:00 +01:00
|
|
|
nm_vpn_supports_ipv6 (NMConnection *connection)
|
2013-12-02 16:20:26 -05:00
|
|
|
{
|
libnm, core, cli, tui: fix the capitalization of various types
GLib/Gtk have mostly settled on the convention that two-letter
acronyms in type names remain all-caps (eg, "IO"), but longer acronyms
become initial-caps-only (eg, "Tcp").
NM was inconsistent, with most long acronyms using initial caps only
(Adsl, Cdma, Dcb, Gsm, Olpc, Vlan), but others using all caps (DHCP,
PPP, PPPOE, VPN). Fix libnm and src/ to use initial-caps only for all
three-or-more-letter-long acronyms (and update nmcli and nmtui for the
libnm changes).
2014-06-26 13:44:36 -04:00
|
|
|
NMSettingVpn *s_vpn;
|
2013-12-02 16:20:26 -05:00
|
|
|
const char *service_type;
|
2015-05-26 11:10:12 +02:00
|
|
|
NMVpnEditorPlugin *plugin;
|
2013-12-02 16:20:26 -05:00
|
|
|
guint32 capabilities;
|
|
|
|
|
|
|
|
|
|
s_vpn = nm_connection_get_setting_vpn (connection);
|
|
|
|
|
g_return_val_if_fail (s_vpn != NULL, FALSE);
|
|
|
|
|
|
|
|
|
|
service_type = nm_setting_vpn_get_service_type (s_vpn);
|
2016-05-19 10:08:47 +02:00
|
|
|
if (!service_type)
|
|
|
|
|
return FALSE;
|
2013-12-02 16:20:26 -05:00
|
|
|
|
cli: fix handling of VPN names in nmcli by using libnm functions
At various places, nmcli requires to specify a VPN type by name, for example
$ nmcli connection add type vpn ifname '*' vpn-type $VPN_TYPE
This $VPN_TYPE used to be a hard-coded list of known VPN plugin names.
But actually, it should be a VPN service-type. A service-type used to be
the D-Bus name of the VPN plugin. Now, with multiple VPN support that
is no longer the case, but it still has the form of a D-Bus bus name.
Alternativley, it could be an alias, which is just a way for plugins
to support multiple service-types.
Fix that, to support fully qualified service-types in the form
of D-Bus bus names. Also, support lookup by name, in which case
the present plugin-info instances are searched.
Finally, support a list of hard-code short-names.
All the logic how to translate a short-name to a fully qualified
service-type is now inside libnm, so that various user agree on
those names and don't have to hard-code them each.
2016-06-07 11:06:14 +02:00
|
|
|
plugin = nm_vpn_get_editor_plugin (service_type, NULL);
|
2016-05-19 10:08:47 +02:00
|
|
|
if (!plugin)
|
|
|
|
|
return FALSE;
|
2013-12-02 16:20:26 -05:00
|
|
|
|
2015-05-26 11:10:12 +02:00
|
|
|
capabilities = nm_vpn_editor_plugin_get_capabilities (plugin);
|
|
|
|
|
return NM_FLAGS_HAS (capabilities, NM_VPN_EDITOR_PLUGIN_CAPABILITY_IPV6);
|
2013-12-02 16:20:26 -05:00
|
|
|
}
|
2015-11-27 11:09:20 +01:00
|
|
|
|
|
|
|
|
const VpnPasswordName *
|
2016-06-20 19:04:34 +02:00
|
|
|
nm_vpn_get_secret_names (const char *service_type)
|
2015-11-27 11:09:20 +01:00
|
|
|
{
|
2019-02-08 11:47:47 +01:00
|
|
|
static const VpnPasswordName generic_vpn_secrets[] = {
|
2017-04-05 13:40:01 +02:00
|
|
|
{ "password", N_("Password") },
|
|
|
|
|
{ 0 }
|
|
|
|
|
};
|
2019-02-08 11:47:47 +01:00
|
|
|
static const VpnPasswordName openvpn_secrets[] = {
|
2017-04-05 13:40:01 +02:00
|
|
|
{ "password", N_("Password") },
|
|
|
|
|
{ "cert-pass", N_("Certificate password") },
|
|
|
|
|
{ "http-proxy-password", N_("HTTP proxy password") },
|
|
|
|
|
{ 0 }
|
|
|
|
|
};
|
2019-02-08 11:47:47 +01:00
|
|
|
static const VpnPasswordName vpnc_secrets[] = {
|
2017-04-05 13:40:01 +02:00
|
|
|
{ "Xauth password", N_("Password") },
|
|
|
|
|
{ "IPSec secret", N_("Group password") },
|
|
|
|
|
{ 0 }
|
|
|
|
|
};
|
2019-02-08 11:47:47 +01:00
|
|
|
static const VpnPasswordName swan_secrets[] = {
|
2017-04-05 13:40:01 +02:00
|
|
|
{ "xauthpassword", N_("Password") },
|
|
|
|
|
{ "pskvalue", N_("Group password") },
|
|
|
|
|
{ 0 }
|
|
|
|
|
};
|
2019-02-08 11:47:47 +01:00
|
|
|
static const VpnPasswordName openconnect_secrets[] = {
|
2017-04-05 13:40:01 +02:00
|
|
|
{ "gateway", N_("Gateway") },
|
|
|
|
|
{ "cookie", N_("Cookie") },
|
|
|
|
|
{ "gwcert", N_("Gateway certificate hash") },
|
|
|
|
|
{ 0 }
|
|
|
|
|
};
|
2015-11-27 11:09:20 +01:00
|
|
|
const char *type;
|
|
|
|
|
|
2016-06-20 19:04:34 +02:00
|
|
|
if (!service_type)
|
2015-11-27 11:09:20 +01:00
|
|
|
return NULL;
|
|
|
|
|
|
2016-06-20 19:04:34 +02:00
|
|
|
if ( !g_str_has_prefix (service_type, NM_DBUS_INTERFACE)
|
|
|
|
|
|| service_type[NM_STRLEN (NM_DBUS_INTERFACE)] != '.') {
|
2016-06-20 19:02:40 +02:00
|
|
|
/* all our well-known, hard-coded vpn-types start with NM_DBUS_INTERFACE. */
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2015-11-27 11:09:20 +01:00
|
|
|
|
2016-06-20 19:04:34 +02:00
|
|
|
type = service_type + (NM_STRLEN (NM_DBUS_INTERFACE) + 1);
|
2016-05-03 23:39:40 +02:00
|
|
|
if ( !g_strcmp0 (type, "pptp")
|
2015-11-27 11:09:20 +01:00
|
|
|
|| !g_strcmp0 (type, "iodine")
|
|
|
|
|
|| !g_strcmp0 (type, "ssh")
|
|
|
|
|
|| !g_strcmp0 (type, "l2tp")
|
|
|
|
|
|| !g_strcmp0 (type, "fortisslvpn"))
|
|
|
|
|
return generic_vpn_secrets;
|
2016-05-03 23:39:40 +02:00
|
|
|
else if (!g_strcmp0 (type, "openvpn"))
|
|
|
|
|
return openvpn_secrets;
|
2015-11-27 11:09:20 +01:00
|
|
|
else if (!g_strcmp0 (type, "vpnc"))
|
|
|
|
|
return vpnc_secrets;
|
|
|
|
|
else if ( !g_strcmp0 (type, "openswan")
|
|
|
|
|
|| !g_strcmp0 (type, "libreswan")
|
|
|
|
|
|| !g_strcmp0 (type, "strongswan"))
|
|
|
|
|
return swan_secrets;
|
|
|
|
|
else if (!g_strcmp0 (type, "openconnect"))
|
|
|
|
|
return openconnect_secrets;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-11 21:47:27 +01:00
|
|
|
static gboolean
|
|
|
|
|
_extract_variable_value (char *line, const char *tag, char **value)
|
|
|
|
|
{
|
|
|
|
|
char *p1, *p2;
|
|
|
|
|
|
2019-04-08 15:14:49 +02:00
|
|
|
if (!g_str_has_prefix (line, tag))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
p1 = line + strlen (tag);
|
|
|
|
|
p2 = line + strlen (line) - 1;
|
|
|
|
|
if ((*p1 == '\'' || *p1 == '"') && (*p1 == *p2)) {
|
|
|
|
|
p1++;
|
|
|
|
|
*p2 = '\0';
|
2015-12-11 21:47:27 +01:00
|
|
|
}
|
2019-04-08 15:14:49 +02:00
|
|
|
NM_SET_OUT (value, g_strdup (p1));
|
|
|
|
|
return TRUE;
|
2015-12-11 21:47:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
|
nm_vpn_openconnect_authenticate_helper (const char *host,
|
|
|
|
|
char **cookie,
|
|
|
|
|
char **gateway,
|
|
|
|
|
char **gwcert,
|
|
|
|
|
int *status,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
2019-04-04 13:09:09 +02:00
|
|
|
gs_free char *output = NULL;
|
2019-04-08 15:14:49 +02:00
|
|
|
gs_free const char **output_v = NULL;
|
|
|
|
|
const char *const*iter;
|
2015-12-11 21:47:27 +01:00
|
|
|
const char *path;
|
|
|
|
|
const char *const DEFAULT_PATHS[] = {
|
|
|
|
|
"/sbin/",
|
|
|
|
|
"/usr/sbin/",
|
|
|
|
|
"/usr/local/sbin/",
|
|
|
|
|
"/bin/",
|
|
|
|
|
"/usr/bin/",
|
|
|
|
|
"/usr/local/bin/",
|
|
|
|
|
NULL,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
path = nm_utils_file_search_in_paths ("openconnect", "/usr/sbin/openconnect", DEFAULT_PATHS,
|
|
|
|
|
G_FILE_TEST_IS_EXECUTABLE, NULL, NULL, error);
|
|
|
|
|
if (!path)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
2019-04-08 15:14:49 +02:00
|
|
|
if (!g_spawn_sync (NULL,
|
|
|
|
|
(char **) NM_MAKE_STRV (path, "--authenticate", host),
|
|
|
|
|
NULL,
|
|
|
|
|
G_SPAWN_SEARCH_PATH
|
|
|
|
|
| G_SPAWN_CHILD_INHERITS_STDIN,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
&output,
|
|
|
|
|
NULL,
|
|
|
|
|
status,
|
|
|
|
|
error))
|
2015-12-11 21:47:27 +01:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
/* Parse output and set cookie, gateway and gwcert
|
|
|
|
|
* output example:
|
|
|
|
|
* COOKIE='loremipsum'
|
|
|
|
|
* HOST='1.2.3.4'
|
|
|
|
|
* FINGERPRINT='sha1:32bac90cf09a722e10ecc1942c67fe2ac8c21e2e'
|
|
|
|
|
*/
|
2019-04-08 15:14:49 +02:00
|
|
|
output_v = nm_utils_strsplit_set_with_empty (output, "\r\n");
|
|
|
|
|
for (iter = output_v; iter && *iter; iter++) {
|
|
|
|
|
char *s_mutable = (char *) *iter;
|
|
|
|
|
|
|
|
|
|
_extract_variable_value (s_mutable, "COOKIE=", cookie);
|
|
|
|
|
_extract_variable_value (s_mutable, "HOST=", gateway);
|
|
|
|
|
_extract_variable_value (s_mutable, "FINGERPRINT=", gwcert);
|
2015-12-11 21:47:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-28 14:22:35 +01:00
|
|
|
static gboolean
|
|
|
|
|
_wg_complete_peer (GPtrArray **p_peers,
|
|
|
|
|
NMWireGuardPeer *peer_take,
|
|
|
|
|
gsize peer_start_line_nr,
|
|
|
|
|
const char *filename,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
nm_auto_unref_wgpeer NMWireGuardPeer *peer = peer_take;
|
|
|
|
|
gs_free_error GError *local = NULL;
|
|
|
|
|
|
|
|
|
|
if (!peer)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
if (!nm_wireguard_peer_is_valid (peer, TRUE, TRUE, &local)) {
|
|
|
|
|
nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
|
|
|
|
|
_("Invalid peer starting at %s:%zu: %s"),
|
|
|
|
|
filename,
|
|
|
|
|
peer_start_line_nr,
|
|
|
|
|
local->message);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!*p_peers)
|
|
|
|
|
*p_peers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_wireguard_peer_unref);
|
|
|
|
|
g_ptr_array_add (*p_peers, g_steal_pointer (&peer));
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
_line_match (char *line, const char *key, gsize key_len, const char **out_key, char **out_value)
|
|
|
|
|
{
|
|
|
|
|
nm_assert (line);
|
|
|
|
|
nm_assert (key);
|
|
|
|
|
nm_assert (strlen (key) == key_len);
|
|
|
|
|
nm_assert (!strchr (key, '='));
|
|
|
|
|
nm_assert (out_key && !*out_key);
|
|
|
|
|
nm_assert (out_value && !*out_value);
|
|
|
|
|
|
|
|
|
|
/* Note that `wg-quick` (linux.bash) does case-insensitive comparison (shopt -s nocasematch).
|
|
|
|
|
* `wg setconf` does case-insensitive comparison too (with strncasecmp, which is locale dependent).
|
|
|
|
|
*
|
|
|
|
|
* We do a case-insensitive comparison of the key, however in a locale-independent manner. */
|
|
|
|
|
|
|
|
|
|
if (g_ascii_strncasecmp (line, key, key_len) != 0)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (line[key_len] != '=')
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
*out_key = key;
|
|
|
|
|
*out_value = &line[key_len + 1];
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define line_match(line, key, out_key, out_value) \
|
|
|
|
|
_line_match ((line), ""key"", NM_STRLEN (key), (out_key), (out_value))
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
value_split_word (char **line_remainder, char **out_word)
|
|
|
|
|
{
|
|
|
|
|
char *str;
|
|
|
|
|
|
|
|
|
|
if ((*line_remainder)[0] == '\0')
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
*out_word = *line_remainder;
|
|
|
|
|
|
|
|
|
|
str = strchrnul (*line_remainder, ',');
|
|
|
|
|
if (str[0] == ',') {
|
|
|
|
|
str[0] = '\0';
|
|
|
|
|
*line_remainder = &str[1];
|
|
|
|
|
} else
|
|
|
|
|
*line_remainder = str;
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NMConnection *
|
|
|
|
|
nm_vpn_wireguard_import (const char *filename,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
nm_auto_clear_secret_ptr NMSecretPtr file_content = NM_SECRET_PTR_INIT ();
|
|
|
|
|
char ifname[IFNAMSIZ];
|
|
|
|
|
gs_free char *uuid = NULL;
|
|
|
|
|
gboolean ifname_valid = FALSE;
|
|
|
|
|
const char *cstr;
|
|
|
|
|
char *line_remainder;
|
|
|
|
|
gs_unref_object NMConnection *connection = NULL;
|
|
|
|
|
NMSettingConnection *s_con;
|
|
|
|
|
NMSettingIPConfig *s_ip4;
|
|
|
|
|
NMSettingIPConfig *s_ip6;
|
|
|
|
|
NMSettingWireGuard *s_wg;
|
|
|
|
|
gs_free_error GError *local = NULL;
|
|
|
|
|
enum {
|
|
|
|
|
LINE_CONTEXT_INIT,
|
|
|
|
|
LINE_CONTEXT_INTERFACE,
|
|
|
|
|
LINE_CONTEXT_PEER,
|
|
|
|
|
} line_context;
|
|
|
|
|
gsize line_nr;
|
|
|
|
|
gsize current_peer_start_line_nr = 0;
|
|
|
|
|
nm_auto_unref_wgpeer NMWireGuardPeer *current_peer = NULL;
|
|
|
|
|
gs_unref_ptrarray GPtrArray *data_dns_v4 = NULL;
|
|
|
|
|
gs_unref_ptrarray GPtrArray *data_dns_v6 = NULL;
|
|
|
|
|
gs_unref_ptrarray GPtrArray *data_addr_v4 = NULL;
|
|
|
|
|
gs_unref_ptrarray GPtrArray *data_addr_v6 = NULL;
|
|
|
|
|
gs_unref_ptrarray GPtrArray *data_peers = NULL;
|
|
|
|
|
const char *data_private_key = NULL;
|
|
|
|
|
gint64 data_table;
|
|
|
|
|
guint data_listen_port = 0;
|
|
|
|
|
guint data_fwmark = 0;
|
|
|
|
|
guint data_mtu = 0;
|
|
|
|
|
int is_v4;
|
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (filename, NULL);
|
|
|
|
|
g_return_val_if_fail (!error || !*error, NULL);
|
|
|
|
|
|
|
|
|
|
/* contrary to "wg-quick", we never interpret the filename as "/etc/wireguard/$INTERFACE.conf".
|
|
|
|
|
* If the filename has no '/', it is interpreted as relative to the current working directory.
|
|
|
|
|
* However, we do require a suitable filename suffix and that the name corresponds to the interface
|
|
|
|
|
* name. */
|
|
|
|
|
cstr = strrchr (filename, '/');
|
|
|
|
|
cstr = cstr ? &cstr[1] : filename;
|
|
|
|
|
if (NM_STR_HAS_SUFFIX (cstr, ".conf")) {
|
|
|
|
|
gsize len = strlen (cstr) - NM_STRLEN (".conf");
|
|
|
|
|
|
|
|
|
|
if (len > 0 && len < sizeof (ifname)) {
|
|
|
|
|
memcpy (ifname, cstr, len);
|
|
|
|
|
ifname[len] = '\0';
|
|
|
|
|
|
2020-02-17 17:36:55 +01:00
|
|
|
if (nm_utils_ifname_valid (ifname, NMU_IFACE_KERNEL, NULL))
|
2019-02-28 14:22:35 +01:00
|
|
|
ifname_valid = TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!ifname_valid) {
|
|
|
|
|
nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN,
|
2020-02-17 17:36:55 +01:00
|
|
|
_("The name of the WireGuard config must be a valid interface name followed by \".conf\""));
|
2019-02-28 14:22:35 +01:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
if (!nm_utils_file_get_contents (-1,
|
|
|
|
|
filename,
|
|
|
|
|
10*1024*1024,
|
|
|
|
|
NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET,
|
|
|
|
|
&file_content.str,
|
|
|
|
|
&file_content.len,
|
|
|
|
|
NULL,
|
|
|
|
|
error))
|
2019-02-28 14:22:35 +01:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* We interpret the file like `wg-quick up` and `wg setconf` do.
|
|
|
|
|
*
|
|
|
|
|
* Of course the WireGuard scripts do something fundamentlly different. They
|
|
|
|
|
* perform actions to configure the WireGuard link in kernel, add routes and
|
|
|
|
|
* addresses, and call resolvconf. It all happens at the time when the script
|
|
|
|
|
* run.
|
|
|
|
|
*
|
|
|
|
|
* This code here instead generates a NetworkManager connection profile so that
|
|
|
|
|
* NetworkManager will apply a similar configuration when later activating the profile. */
|
|
|
|
|
|
|
|
|
|
#define _TABLE_AUTO ((gint64) -1)
|
|
|
|
|
#define _TABLE_OFF ((gint64) -2)
|
|
|
|
|
|
|
|
|
|
data_table = _TABLE_AUTO;
|
|
|
|
|
|
|
|
|
|
line_remainder = file_content.str;
|
|
|
|
|
line_context = LINE_CONTEXT_INIT;
|
|
|
|
|
line_nr = 0;
|
|
|
|
|
while (line_remainder[0] != '\0') {
|
|
|
|
|
const char *matched_key = NULL;
|
|
|
|
|
char *value = NULL;
|
|
|
|
|
char *line;
|
|
|
|
|
char ch;
|
|
|
|
|
gint64 i64;
|
|
|
|
|
|
|
|
|
|
line_nr++;
|
|
|
|
|
|
|
|
|
|
line = line_remainder;
|
|
|
|
|
line_remainder = strchrnul (line, '\n');
|
|
|
|
|
if (line_remainder[0] != '\0')
|
|
|
|
|
(line_remainder++)[0] = '\0';
|
|
|
|
|
|
|
|
|
|
/* Drop all spaces and truncate at first '#'.
|
|
|
|
|
* See wg's config_read_line().
|
|
|
|
|
*
|
|
|
|
|
* Note that wg-quick doesn't do that.
|
|
|
|
|
*
|
|
|
|
|
* Neither `wg setconf` nor `wg-quick` does a strict parsing.
|
|
|
|
|
* We don't either. Just try to interpret the file (mostly) the same as
|
|
|
|
|
* they would.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
gsize l, n;
|
|
|
|
|
|
|
|
|
|
n = 0;
|
|
|
|
|
for (l = 0; (ch = line[l]); l++) {
|
|
|
|
|
if (g_ascii_isspace (ch)) {
|
|
|
|
|
/* wg-setconf strips all whitespace before parsing the content. That means,
|
|
|
|
|
* *[I nterface]" will be accepted. We do that too. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (ch == '#')
|
|
|
|
|
break;
|
|
|
|
|
line[n++] = line[l];
|
|
|
|
|
}
|
|
|
|
|
if (n == 0)
|
|
|
|
|
continue;
|
|
|
|
|
line[n] = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (g_ascii_strcasecmp (line, "[Interface]") == 0) {
|
|
|
|
|
if (!_wg_complete_peer (&data_peers,
|
|
|
|
|
g_steal_pointer (¤t_peer),
|
|
|
|
|
current_peer_start_line_nr,
|
|
|
|
|
filename,
|
|
|
|
|
error))
|
|
|
|
|
return FALSE;
|
|
|
|
|
line_context = LINE_CONTEXT_INTERFACE;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (g_ascii_strcasecmp (line, "[Peer]") == 0) {
|
|
|
|
|
if (!_wg_complete_peer (&data_peers,
|
|
|
|
|
g_steal_pointer (¤t_peer),
|
|
|
|
|
current_peer_start_line_nr,
|
|
|
|
|
filename,
|
|
|
|
|
error))
|
|
|
|
|
return FALSE;
|
|
|
|
|
current_peer_start_line_nr = line_nr;
|
|
|
|
|
current_peer = nm_wireguard_peer_new ();
|
|
|
|
|
line_context = LINE_CONTEXT_PEER;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (line_context == LINE_CONTEXT_INTERFACE) {
|
|
|
|
|
|
|
|
|
|
if (line_match (line, "Address", &matched_key, &value)) {
|
|
|
|
|
char *value_word;
|
|
|
|
|
|
|
|
|
|
while (value_split_word (&value, &value_word)) {
|
|
|
|
|
GPtrArray **p_data_addr;
|
|
|
|
|
NMIPAddr addr_bin;
|
|
|
|
|
int addr_family;
|
|
|
|
|
int prefix_len;
|
|
|
|
|
|
|
|
|
|
if (!nm_utils_parse_inaddr_prefix_bin (AF_UNSPEC,
|
|
|
|
|
value_word,
|
|
|
|
|
&addr_family,
|
|
|
|
|
&addr_bin,
|
|
|
|
|
&prefix_len))
|
|
|
|
|
goto fail_invalid_value;
|
|
|
|
|
|
|
|
|
|
p_data_addr = (addr_family == AF_INET)
|
|
|
|
|
? &data_addr_v4
|
|
|
|
|
: &data_addr_v6;
|
|
|
|
|
|
|
|
|
|
if (!*p_data_addr)
|
|
|
|
|
*p_data_addr = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_ip_address_unref);
|
|
|
|
|
|
|
|
|
|
g_ptr_array_add (*p_data_addr,
|
|
|
|
|
nm_ip_address_new_binary (addr_family,
|
|
|
|
|
&addr_bin,
|
|
|
|
|
prefix_len == -1
|
|
|
|
|
? ((addr_family == AF_INET) ? 32 : 128)
|
|
|
|
|
: prefix_len,
|
|
|
|
|
NULL));
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (line_match (line, "MTU", &matched_key, &value)) {
|
|
|
|
|
i64 = _nm_utils_ascii_str_to_int64 (value, 0, 0, G_MAXUINT32, -1);
|
|
|
|
|
if (i64 == -1)
|
|
|
|
|
goto fail_invalid_value;
|
|
|
|
|
|
|
|
|
|
/* wg-quick accepts the "MTU" value, but it also fetches routes to
|
|
|
|
|
* autodetect it. NetworkManager won't do that, we can only configure
|
2019-03-11 12:00:32 +01:00
|
|
|
* an explicit MTU or no autodetection will be performed. */
|
2019-02-28 14:22:35 +01:00
|
|
|
data_mtu = i64;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (line_match (line, "DNS", &matched_key, &value)) {
|
|
|
|
|
char *value_word;
|
|
|
|
|
|
|
|
|
|
while (value_split_word (&value, &value_word)) {
|
|
|
|
|
GPtrArray **p_data_dns;
|
|
|
|
|
NMIPAddr addr_bin;
|
|
|
|
|
int addr_family;
|
|
|
|
|
|
|
|
|
|
if (!nm_utils_parse_inaddr_bin (AF_UNSPEC,
|
|
|
|
|
value_word,
|
|
|
|
|
&addr_family,
|
|
|
|
|
&addr_bin))
|
|
|
|
|
goto fail_invalid_value;
|
|
|
|
|
|
|
|
|
|
p_data_dns = (addr_family == AF_INET)
|
|
|
|
|
? &data_dns_v4
|
|
|
|
|
: &data_dns_v6;
|
|
|
|
|
if (!*p_data_dns)
|
|
|
|
|
*p_data_dns = g_ptr_array_new_with_free_func (g_free);
|
|
|
|
|
|
2020-04-08 10:29:03 +02:00
|
|
|
g_ptr_array_add (*p_data_dns,
|
|
|
|
|
nm_utils_inet_ntop_dup (addr_family, &addr_bin));
|
2019-02-28 14:22:35 +01:00
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (line_match (line, "Table", &matched_key, &value)) {
|
|
|
|
|
|
|
|
|
|
if (nm_streq (value, "auto"))
|
|
|
|
|
data_table = _TABLE_AUTO;
|
|
|
|
|
else if (nm_streq (value, "off"))
|
|
|
|
|
data_table = _TABLE_OFF;
|
|
|
|
|
else {
|
|
|
|
|
/* we don't support table names from /etc/iproute2/rt_tables
|
|
|
|
|
* But we accept hex like `ip route add` would. */
|
|
|
|
|
i64 = _nm_utils_ascii_str_to_int64 (value, 0, 0, G_MAXINT32, -1);
|
|
|
|
|
if (i64 == -1)
|
|
|
|
|
goto fail_invalid_value;
|
|
|
|
|
data_table = i64;
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( line_match (line, "PreUp", &matched_key, &value)
|
|
|
|
|
|| line_match (line, "PreDown", &matched_key, &value)
|
|
|
|
|
|| line_match (line, "PostUp", &matched_key, &value)
|
|
|
|
|
|| line_match (line, "PostDown", &matched_key, &value)) {
|
2019-03-11 12:00:32 +01:00
|
|
|
/* we don't run any scripts. Silently ignore these parameters. */
|
2019-02-28 14:22:35 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (line_match (line, "SaveConfig", &matched_key, &value)) {
|
|
|
|
|
/* we ignore the setting, but enforce that it's either true or false (like
|
|
|
|
|
* wg-quick. */
|
|
|
|
|
if (!NM_IN_STRSET (value, "true", "false"))
|
|
|
|
|
goto fail_invalid_value;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (line_match (line, "ListenPort", &matched_key, &value)) {
|
|
|
|
|
/* we don't use getaddrinfo(), unlike `wg setconf`. Just interpret
|
|
|
|
|
* the port as plain decimal number. */
|
|
|
|
|
i64 = _nm_utils_ascii_str_to_int64 (value, 10, 0, 0xFFFF, -1);
|
|
|
|
|
if (i64 == -1)
|
|
|
|
|
goto fail_invalid_value;
|
|
|
|
|
data_listen_port = i64;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (line_match (line, "FwMark", &matched_key, &value)) {
|
|
|
|
|
if (nm_streq (value, "off"))
|
|
|
|
|
data_fwmark = 0;
|
|
|
|
|
else {
|
|
|
|
|
i64 = _nm_utils_ascii_str_to_int64 (value, 0, 0, G_MAXINT32, -1);
|
|
|
|
|
if (i64 == -1)
|
|
|
|
|
goto fail_invalid_value;
|
|
|
|
|
data_fwmark = i64;
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (line_match (line, "PrivateKey", &matched_key, &value)) {
|
|
|
|
|
if (!nm_utils_base64secret_decode (value, NM_WIREGUARD_PUBLIC_KEY_LEN, NULL))
|
|
|
|
|
goto fail_invalid_secret;
|
|
|
|
|
data_private_key = value;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
goto fail_invalid_line;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (line_context == LINE_CONTEXT_PEER) {
|
|
|
|
|
|
|
|
|
|
if (line_match (line, "Endpoint", &matched_key, &value)) {
|
|
|
|
|
if (!nm_wireguard_peer_set_endpoint (current_peer, value, FALSE))
|
|
|
|
|
goto fail_invalid_value;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (line_match (line, "PublicKey", &matched_key, &value)) {
|
|
|
|
|
if (!nm_wireguard_peer_set_public_key (current_peer, value, FALSE))
|
|
|
|
|
goto fail_invalid_value;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (line_match (line, "AllowedIPs", &matched_key, &value)) {
|
|
|
|
|
char *value_word;
|
|
|
|
|
|
|
|
|
|
while (value_split_word (&value, &value_word)) {
|
|
|
|
|
if (!nm_wireguard_peer_append_allowed_ip (current_peer,
|
|
|
|
|
value_word,
|
|
|
|
|
FALSE))
|
|
|
|
|
goto fail_invalid_value;
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (line_match (line, "PersistentKeepalive", &matched_key, &value)) {
|
|
|
|
|
if (nm_streq (value, "off"))
|
|
|
|
|
i64 = 0;
|
|
|
|
|
else {
|
|
|
|
|
i64 = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT16, -1);
|
|
|
|
|
if (i64 == -1)
|
|
|
|
|
goto fail_invalid_value;
|
|
|
|
|
}
|
|
|
|
|
nm_wireguard_peer_set_persistent_keepalive (current_peer, i64);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (line_match (line, "PresharedKey", &matched_key, &value)) {
|
|
|
|
|
if (!nm_wireguard_peer_set_preshared_key (current_peer, value, FALSE))
|
|
|
|
|
goto fail_invalid_secret;
|
|
|
|
|
nm_wireguard_peer_set_preshared_key_flags (current_peer, NM_SETTING_SECRET_FLAG_NONE);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
goto fail_invalid_line;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fail_invalid_line:
|
|
|
|
|
nm_utils_error_set (error, NM_UTILS_ERROR_INVALID_ARGUMENT,
|
|
|
|
|
_("unrecognized line at %s:%zu"),
|
|
|
|
|
filename, line_nr);
|
|
|
|
|
return FALSE;
|
|
|
|
|
fail_invalid_value:
|
|
|
|
|
nm_utils_error_set (error, NM_UTILS_ERROR_INVALID_ARGUMENT,
|
|
|
|
|
_("invalid value for '%s' at %s:%zu"),
|
|
|
|
|
matched_key, filename, line_nr);
|
|
|
|
|
return FALSE;
|
|
|
|
|
fail_invalid_secret:
|
|
|
|
|
nm_utils_error_set (error, NM_UTILS_ERROR_INVALID_ARGUMENT,
|
|
|
|
|
_("invalid secret '%s' at %s:%zu"),
|
|
|
|
|
matched_key, filename, line_nr);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!_wg_complete_peer (&data_peers,
|
|
|
|
|
g_steal_pointer (¤t_peer),
|
|
|
|
|
current_peer_start_line_nr,
|
|
|
|
|
filename,
|
|
|
|
|
error))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
connection = nm_simple_connection_new ();
|
|
|
|
|
s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
|
|
|
|
|
nm_connection_add_setting (connection, NM_SETTING (s_con));
|
|
|
|
|
s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ());
|
|
|
|
|
nm_connection_add_setting (connection, NM_SETTING (s_ip4));
|
|
|
|
|
s_ip6 = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ());
|
|
|
|
|
nm_connection_add_setting (connection, NM_SETTING (s_ip6));
|
|
|
|
|
s_wg = NM_SETTING_WIREGUARD (nm_setting_wireguard_new ());
|
|
|
|
|
nm_connection_add_setting (connection, NM_SETTING (s_wg));
|
|
|
|
|
|
|
|
|
|
uuid = nm_utils_uuid_generate ();
|
|
|
|
|
|
|
|
|
|
g_object_set (s_con,
|
|
|
|
|
NM_SETTING_CONNECTION_ID,
|
|
|
|
|
ifname,
|
|
|
|
|
NM_SETTING_CONNECTION_UUID,
|
|
|
|
|
uuid,
|
|
|
|
|
NM_SETTING_CONNECTION_TYPE,
|
|
|
|
|
NM_SETTING_WIREGUARD_SETTING_NAME,
|
|
|
|
|
NM_SETTING_CONNECTION_INTERFACE_NAME,
|
|
|
|
|
ifname,
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
g_object_set (s_wg,
|
|
|
|
|
NM_SETTING_WIREGUARD_PRIVATE_KEY,
|
|
|
|
|
data_private_key,
|
|
|
|
|
NM_SETTING_WIREGUARD_LISTEN_PORT,
|
|
|
|
|
data_listen_port,
|
|
|
|
|
NM_SETTING_WIREGUARD_FWMARK,
|
|
|
|
|
data_fwmark,
|
|
|
|
|
NM_SETTING_WIREGUARD_MTU,
|
|
|
|
|
data_mtu,
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
if (data_peers) {
|
|
|
|
|
for (i = 0; i < data_peers->len; i++)
|
|
|
|
|
nm_setting_wireguard_append_peer (s_wg, data_peers->pdata[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (is_v4 = 0; is_v4 < 2; is_v4++) {
|
2019-04-26 13:25:21 +02:00
|
|
|
const char *method_disabled = is_v4 ? NM_SETTING_IP4_CONFIG_METHOD_DISABLED : NM_SETTING_IP6_CONFIG_METHOD_DISABLED;
|
2019-02-28 14:22:35 +01:00
|
|
|
const char *method_manual = is_v4 ? NM_SETTING_IP4_CONFIG_METHOD_MANUAL : NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
|
|
|
|
|
NMSettingIPConfig *s_ip = is_v4 ? s_ip4 : s_ip6;
|
|
|
|
|
GPtrArray *data_dns = is_v4 ? data_dns_v4 : data_dns_v6;
|
|
|
|
|
GPtrArray *data_addr = is_v4 ? data_addr_v4 : data_addr_v6;
|
|
|
|
|
|
|
|
|
|
if (data_dns && !data_addr) {
|
|
|
|
|
/* When specifying "DNS", we also require an "Address" for the same address
|
|
|
|
|
* family. That is because a NMSettingIPConfig cannot have @method_disabled
|
|
|
|
|
* and DNS settings at the same time.
|
|
|
|
|
*
|
|
|
|
|
* We don't have addresses. Silently ignore the DNS setting. */
|
|
|
|
|
data_dns = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_object_set (s_ip,
|
|
|
|
|
NM_SETTING_IP_CONFIG_METHOD,
|
|
|
|
|
data_addr ? method_manual : method_disabled,
|
|
|
|
|
NULL);
|
|
|
|
|
|
2020-04-08 13:42:00 +02:00
|
|
|
/* For WireGuard profiles, always set dns-priority to a negative value,
|
|
|
|
|
* so that DNS servers on other profiles get ignored. This is also what
|
|
|
|
|
* wg-quick does, by calling `resolvconf -x`. */
|
|
|
|
|
g_object_set (s_ip,
|
|
|
|
|
NM_SETTING_IP_CONFIG_DNS_PRIORITY,
|
|
|
|
|
(int) -10,
|
|
|
|
|
NULL);
|
|
|
|
|
|
2019-02-28 14:22:35 +01:00
|
|
|
if (data_addr) {
|
|
|
|
|
for (i = 0; i < data_addr->len; i++)
|
|
|
|
|
nm_setting_ip_config_add_address (s_ip, data_addr->pdata[i]);
|
|
|
|
|
}
|
|
|
|
|
if (data_dns) {
|
|
|
|
|
for (i = 0; i < data_dns->len; i++)
|
|
|
|
|
nm_setting_ip_config_add_dns (s_ip, data_dns->pdata[i]);
|
2020-04-08 10:37:51 +02:00
|
|
|
|
|
|
|
|
/* the wg-quick file cannot handle search domains. When configuring a DNS server
|
|
|
|
|
* in the wg-quick file, assume that the user want to use it for all searches. */
|
|
|
|
|
nm_setting_ip_config_add_dns_search (s_ip, "~");
|
2019-02-28 14:22:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data_table == _TABLE_AUTO) {
|
|
|
|
|
/* in the "auto" setting, wg-quick adds peer-routes automatically to the main
|
|
|
|
|
* table. NetworkManager will do that too, but there are differences:
|
|
|
|
|
*
|
|
|
|
|
* - NetworkManager (contrary to wg-quick) does not check whether the peer-route is necessary.
|
|
|
|
|
* It will always add a route for each allowed-ips range, even if there is already another
|
|
|
|
|
* route that would ensure packets to the endpoint are routed via the WireGuard interface.
|
|
|
|
|
* If you don't want that, disable "wireguard.peer-routes", and add the necessary routes
|
|
|
|
|
* yourself to "ipv4.routes" and "ipv6.routes".
|
|
|
|
|
*
|
|
|
|
|
* - With "auto", wg-quick also configures policy routing to handle default-routes (/0) to
|
2020-04-08 10:50:01 +02:00
|
|
|
* avoid routing loops.
|
|
|
|
|
* The imported connection profile will have wireguard.ip4-auto-default-route and
|
|
|
|
|
* wireguard.ip6-auto-default-route set to "default". It will thus configure wg-quick's
|
|
|
|
|
* policy routing if the profile has any AllowedIPs ranges with /0.
|
2019-02-28 14:22:35 +01:00
|
|
|
*/
|
|
|
|
|
} else if (data_table == _TABLE_OFF) {
|
|
|
|
|
if (is_v4) {
|
|
|
|
|
g_object_set (s_wg,
|
|
|
|
|
NM_SETTING_WIREGUARD_PEER_ROUTES,
|
|
|
|
|
FALSE,
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
g_object_set (s_ip,
|
|
|
|
|
NM_SETTING_IP_CONFIG_ROUTE_TABLE,
|
|
|
|
|
(guint) data_table,
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!nm_connection_normalize (connection, NULL, NULL, &local)) {
|
|
|
|
|
nm_utils_error_set (error, NM_UTILS_ERROR_INVALID_ARGUMENT,
|
|
|
|
|
_("Failed to create WireGuard connection: %s"),
|
|
|
|
|
local->message);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return g_steal_pointer (&connection);
|
|
|
|
|
}
|