[Backport release-26.05] NetworkManager VPN plug-ins: Fix critical vulnerabilities (#565371)

This commit is contained in:
Martin Weinelt
2026-09-20 23:24:11 +00:00
committed by GitHub
8 changed files with 351 additions and 2 deletions

View File

@@ -238,14 +238,13 @@ in
default = [ ];
example = literalExpression ''
with pkgs; [
networkmanager-fortisslvpn
networkmanager-iodine
networkmanager-libreswan
networkmanager-l2tp
networkmanager-openconnect
networkmanager-openvpn
networkmanager-sstp
networkmanager-strongswan
networkmanager-vpnc
]
'';
description = ''

View File

@@ -90,6 +90,9 @@ stdenv.mkDerivation rec {
meta = {
description = "NetworkManager’s FortiSSL plugin";
inherit (networkmanager.meta) maintainers teams platforms;
knownVulnerabilities = [
"CVE-2026-91839"
];
license = lib.licenses.gpl2Plus;
};
}

View File

@@ -0,0 +1,203 @@
diff --git a/src/nm-iodine-service.c b/src/nm-iodine-service.c
index 75a1610e4281588336985636a2dd4eda03575d3a..2cc74f6859304a895be0080dd50f72ddd29a8219 100644
--- a/src/nm-iodine-service.c
+++ b/src/nm-iodine-service.c
@@ -468,14 +468,58 @@ send_password(gint fd, NMSettingVpn *s_vpn)
}
-static gint
+static gboolean
+label_is_valid (const gchar *label, gsize len)
+{
+ if (len == 0 || len > 63)
+ return FALSE;
+ if (label[0] == '-' || label[len - 1] == '-')
+ return FALSE;
+ for (gsize i = 0; i < len; i++)
+ if (!g_ascii_isalnum (label[i]) && label[i] != '-')
+ return FALSE;
+ return TRUE;
+}
+
+
+static gboolean
+domain_is_valid (const gchar *domain)
+{
+ g_autofree char *ascii = NULL;
+ g_auto (GStrv) labels = NULL;
+
+ if (domain == NULL || *domain == '\0')
+ return FALSE;
+
+ /* Normalizes IDN labels to punycode; NULL on malformed UTF-8/encoding */
+ ascii = g_hostname_to_ascii (domain);
+ if (ascii == NULL)
+ return FALSE;
+
+ if (strlen (ascii) > 253)
+ return FALSE;
+
+ labels = g_strsplit (ascii, ".", -1);
+ if (labels == NULL || labels[0] == NULL || labels[1] == NULL)
+ return FALSE;
+
+ for (int i = 0; labels[i] != NULL; i++) {
+ if (!label_is_valid (labels[i], -1))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static gboolean
nm_iodine_start_iodine_binary (NMIodinePlugin *plugin,
NMSettingVpn *s_vpn,
GError **error)
{
GPid pid;
const char **iodine_binary = NULL;
- GPtrArray *iodine_argv;
+ g_autoptr (GPtrArray) iodine_argv = g_ptr_array_new_full (12, NULL);
GSource *iodine_watch;
GIOChannel *stderr_channel;
gint stdin_fd, stderr_fd;
@@ -491,27 +535,20 @@ nm_iodine_start_iodine_binary (NMIodinePlugin *plugin,
}
if (!*iodine_binary) {
- g_set_error (error,
- NM_VPN_PLUGIN_ERROR,
- NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED,
- "%s",
- _("Could not find iodine binary."));
- return -1;
+ g_set_error_literal (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED,
+ _("Could not find iodine binary."));
+ return FALSE;
}
props_fragsize = nm_setting_vpn_get_data_item (s_vpn, NM_IODINE_KEY_FRAGSIZE);
props_nameserver = nm_setting_vpn_get_data_item (s_vpn, NM_IODINE_KEY_NAMESERVER);
props_topdomain = nm_setting_vpn_get_data_item (s_vpn, NM_IODINE_KEY_TOPDOMAIN);
- iodine_argv = g_ptr_array_new ();
g_ptr_array_add (iodine_argv, (gpointer) (*iodine_binary));
/* Run in foreground */
g_ptr_array_add (iodine_argv, (gpointer) "-f");
- if (props_fragsize && strlen(props_fragsize)) {
- g_ptr_array_add (iodine_argv, (gpointer) "-m");
- g_ptr_array_add (iodine_argv, (gpointer) props_fragsize);
- }
-
if (has_user(NM_IODINE_USER)) {
g_ptr_array_add (iodine_argv, (gpointer) "-u");
g_ptr_array_add (iodine_argv, (gpointer) NM_IODINE_USER);
@@ -524,11 +561,51 @@ nm_iodine_start_iodine_binary (NMIodinePlugin *plugin,
} else
g_warning("Not running chrooted");
- if (props_nameserver && strlen(props_nameserver))
- g_ptr_array_add (iodine_argv, (gpointer) props_nameserver);
+ if (props_fragsize && strlen(props_fragsize)) {
+ gchar *end = NULL;
- if (props_topdomain && strlen(props_topdomain))
- g_ptr_array_add (iodine_argv, (gpointer) props_topdomain);
+ (void)g_ascii_strtoll(props_fragsize, &end, 10);
+ if (end != props_fragsize || *end == '\0') {
+ g_ptr_array_add (iodine_argv, (gpointer) "-m");
+ g_ptr_array_add (iodine_argv, (gpointer) props_fragsize);
+ } else {
+ g_warning("Failed to parse fragment size");
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED,
+ _("Invalid fragment size '%s'"),
+ props_fragsize);
+ return FALSE;
+ }
+ }
+
+ if (props_nameserver && strlen(props_nameserver)) {
+ g_autoptr (GInetAddress) addr = g_inet_address_new_from_string (props_nameserver);
+
+ if (addr) {
+ g_ptr_array_add (iodine_argv, (gpointer) props_nameserver);
+ } else {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED,
+ _("Invalid nameserver '%s'"),
+ props_nameserver);
+ return FALSE;
+ }
+ }
+
+ if (props_topdomain && strlen(props_topdomain)) {
+ if (domain_is_valid (props_topdomain))
+ g_ptr_array_add (iodine_argv, (gpointer) props_topdomain);
+ else {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED,
+ _("Invalid domain '%s'"),
+ props_topdomain);
+ return FALSE;
+ }
+ }
g_ptr_array_add (iodine_argv, NULL);
@@ -536,11 +613,9 @@ nm_iodine_start_iodine_binary (NMIodinePlugin *plugin,
G_SPAWN_DO_NOT_REAP_CHILD,
NULL, NULL,
&pid, &stdin_fd, NULL, &stderr_fd, error)) {
- g_ptr_array_free (iodine_argv, TRUE);
g_warning ("iodine failed to start. error: '%s'", (*error)->message);
- return -1;
+ return FALSE;
}
- g_ptr_array_free (iodine_argv, TRUE);
g_message ("iodine started with pid %d", pid);
@@ -556,13 +631,13 @@ nm_iodine_start_iodine_binary (NMIodinePlugin *plugin,
priv->pid = pid;
iodine_watch = g_child_watch_source_new (pid);
g_source_set_callback (iodine_watch,
- (GSourceFunc) iodine_watch_cb,
+ G_SOURCE_FUNC (iodine_watch_cb),
plugin,
NULL);
g_source_attach (iodine_watch, NULL);
g_source_unref (iodine_watch);
- return 0;
+ return TRUE;
}
static gboolean
@@ -572,7 +647,7 @@ real_connect (NMVpnServicePlugin *plugin,
{
NMSettingVpn *s_vpn;
NMIodinePluginPrivate *priv = nm_iodine_plugin_get_instance_private (NM_IODINE_PLUGIN(plugin));
- gint ret = -1;
+ gboolean success;
g_variant_builder_init(&priv->ip4config, G_VARIANT_TYPE_VARDICT);
@@ -584,8 +659,8 @@ real_connect (NMVpnServicePlugin *plugin,
if (!nm_iodine_secrets_validate (s_vpn, error))
goto out;
- ret = nm_iodine_start_iodine_binary (NM_IODINE_PLUGIN (plugin), s_vpn, error);
- if (!ret)
+ success = nm_iodine_start_iodine_binary (NM_IODINE_PLUGIN (plugin), s_vpn, error);
+ if (success)
return TRUE;
out:

View File

@@ -32,6 +32,10 @@ stdenv.mkDerivation {
(replaceVars ./fix-paths.patch {
inherit iodine;
})
# https://gitlab.gnome.org/GNOME/network-manager-iodine/-/issues/4
# https://gitlab.gnome.org/GNOME/network-manager-iodine/-/merge_requests/6
./CVE-2026-91837.patch
];
nativeBuildInputs = [

View File

@@ -0,0 +1,48 @@
diff --git a/properties/nm-libreswan-editor-plugin.c b/properties/nm-libreswan-editor-plugin.c
index 6a178af..787e183 100644
--- a/properties/nm-libreswan-editor-plugin.c
+++ b/properties/nm-libreswan-editor-plugin.c
@@ -96,7 +96,7 @@ export_to_file(NMVpnEditorPlugin *self, const char *path, NMConnection *connecti
openswan = nm_streq(nm_setting_vpn_get_service_type(s_vpn), NM_VPN_SERVICE_TYPE_OPENSWAN);
- nm_libreswan_detect_version(nm_libreswan_find_helper_bin("ipsec", NULL),
+ nm_libreswan_detect_version("@ipsec@",
&is_openswan,
&version,
NULL);
diff --git a/src/nm-libreswan-service.c b/src/nm-libreswan-service.c
index 0458d4e..e0add02 100644
--- a/src/nm-libreswan-service.c
+++ b/src/nm-libreswan-service.c
@@ -1820,7 +1820,7 @@ connect_step(NMLibreswanPlugin *self, GError **error)
if (!priv->openswan) {
const char *stackman_path;
- stackman_path = nm_libreswan_find_helper_libexec("_stackmanager", error);
+ stackman_path = "@libreswan@/libexec/_stackmanager";
if (!stackman_path)
return FALSE;
@@ -1977,7 +1977,7 @@ _connect_common(NMVpnServicePlugin *plugin,
return FALSE;
}
- priv->ipsec_path = nm_libreswan_find_helper_bin("ipsec", error);
+ priv->ipsec_path = "@ipsec@";
if (!priv->ipsec_path)
return FALSE;
@@ -1988,10 +1988,10 @@ _connect_common(NMVpnServicePlugin *plugin,
priv->openswan ? "Openswan" : "Libreswan");
if (!priv->openswan) {
- priv->pluto_path = nm_libreswan_find_helper_libexec("pluto", error);
+ priv->pluto_path = "@libreswan@/libexec/pluto";
if (!priv->pluto_path)
return FALSE;
- priv->whack_path = nm_libreswan_find_helper_libexec("whack", error);
+ priv->whack_path = "@libreswan@/libexec/whack";
if (!priv->whack_path)
return FALSE;
}

View File

@@ -0,0 +1,85 @@
{
dieHook,
fetchpatch,
fetchurl,
glib,
gnome,
gtk3,
gtk4,
intltool,
lib,
libnl,
libnma,
libnma-gtk4,
libreswan,
libsecret,
networkmanager,
pkg-config,
replaceVars,
stdenv,
withGnome ? true,
}:
stdenv.mkDerivation (finalAttrs: {
pname = "NetworkManager-libreswan";
version = "1.2.31";
src = fetchurl {
url = "mirror://gnome/sources/NetworkManager-libreswan/${lib.versions.majorMinor finalAttrs.version}/NetworkManager-libreswan-${finalAttrs.version}.tar.xz";
hash = "sha256-5xq3zWruZoOqlDQusm5hQzv/3y2ml4LsptHCmdzDp28=";
};
patches = [
(replaceVars ./fix-paths.patch {
ipsec = lib.getExe' libreswan "ipsec";
inherit libreswan;
})
];
nativeBuildInputs = [
dieHook
glib
intltool
pkg-config
];
buildInputs = [
libnl
networkmanager
]
++ lib.optionals withGnome [
gtk3
gtk4
libnma
libnma-gtk4
libsecret
];
configureFlags = [
"--with-gnome=${lib.boolToYesNo withGnome}"
"--with-gtk4=${lib.boolToYesNo withGnome}"
"--enable-absolute-paths"
];
strictDeps = true;
__structuredAttrs = true;
postPatch = ''
echo "Ensuring that all helper paths lookups were replaced"
! grep -lr nm_libreswan_find_helper --exclude 'utils.[ch]' || die "^ Found non-replaced helper lookup"
'';
passthru = {
updateScript = gnome.updateScript {
packageName = "NetworkManager-libreswan";
attrPath = "networkmanager-libreswan";
};
networkManagerPlugin = "VPN/nm-libreswan-service.name";
};
meta = {
description = "NetworkManager's libreswan plugin";
inherit (networkmanager.meta) maintainers teams platforms;
license = lib.licenses.gpl2Plus;
};
})

View File

@@ -78,6 +78,9 @@ stdenv.mkDerivation rec {
meta = {
description = "NetworkManager's sstp plugin";
inherit (networkmanager.meta) maintainers teams platforms;
knownVulnerabilities = [
"CVE-2026-91838"
];
license = lib.licenses.gpl2Plus;
};
}

View File

@@ -70,6 +70,10 @@ stdenv.mkDerivation rec {
meta = {
description = "NetworkManager's VPNC plugin";
knownVulnerabilities = [
"CVE-2026-91840"
"CVE-2026-91841"
];
inherit (networkmanager.meta) maintainers teams platforms;
license = lib.licenses.gpl2Plus;
};