From 0078c320fc2f8342613b5ea849cc697778e1fa6a Mon Sep 17 00:00:00 2001 From: Josephine Pfeiffer Date: Thu, 23 Jul 2026 22:27:58 +0200 Subject: [PATCH] service: support private connections Since NetworkManager 1.58 the daemon refuses to activate a private VPN connection (one with a user in connection.permissions) unless the plugin declares supports-safe-private-file-access, so per-user openconnect connections broke on upgrade with "The 'openconnect' plugin doesn't support private connections." The service runs as root and passes the CA certificate (--cafile) to the openconnect binary. Read it via nm_utils_copy_cert_as_user() (libnm 1.52.2) so a private connection's file is accessed as the owning user, and declare the capability in the .name file. https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1989 --- configure.ac | 2 +- nm-openconnect-service.name.in | 1 + src/nm-openconnect-service.c | 54 +++++++++++++++++++++++++++++++++- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index b1cc414..c712ec2 100644 --- a/configure.ac +++ b/configure.ac @@ -145,7 +145,7 @@ if test x"$with_gtk4" != xno; then PKG_CHECK_MODULES(LIBNMA_GTK4, libnma-gtk4 >= 1.8.33) fi -PKG_CHECK_MODULES(LIBNM, libnm >= 1.2.0) +PKG_CHECK_MODULES(LIBNM, libnm >= 1.52.2) LIBNM_CFLAGS="$LIBNM_CFLAGS -DNM_VERSION_MIN_REQUIRED=NM_VERSION_1_2" LIBNM_CFLAGS="$LIBNM_CFLAGS -DNM_VERSION_MAX_ALLOWED=NM_VERSION_1_4" diff --git a/nm-openconnect-service.name.in b/nm-openconnect-service.name.in index 78d8535..bc2c7e2 100644 --- a/nm-openconnect-service.name.in +++ b/nm-openconnect-service.name.in @@ -3,6 +3,7 @@ name=openconnect service=org.freedesktop.NetworkManager.openconnect program=@LIBEXECDIR@/nm-openconnect-service supports-multiple-connections=true +supports-safe-private-file-access=true [libnm] plugin=@PLUGINDIR@/libnm-vpn-plugin-openconnect.so diff --git a/src/nm-openconnect-service.c b/src/nm-openconnect-service.c index 0005534..ccf3476 100644 --- a/src/nm-openconnect-service.c +++ b/src/nm-openconnect-service.c @@ -122,6 +122,7 @@ static struct { gboolean debug; int log_level; GMainLoop *loop; + GPtrArray *tmp_file_paths; } gl/*obal*/; /*****************************************************************************/ @@ -424,6 +425,43 @@ openconnect_watch_cb (GPid pid, gint status, gpointer user_data) } } +static const char * +get_connection_permission_user (NMSettingConnection *s_con) +{ + const char *ptype; + const char *pitem; + const char *pdetail; + guint num_perms; + guint i; + + num_perms = nm_setting_connection_get_num_permissions (s_con); + for (i = 0; i < num_perms; i++) { + if (!nm_setting_connection_get_permission (s_con, i, &ptype, &pitem, &pdetail)) + continue; + if (nm_streq0 (ptype, "user")) + return pitem; + } + return NULL; +} + +/* On a private connection the daemon runs us as root, but a file the + * connection references must be read with the owning user's privileges. */ +static char * +access_file (const char *filename, const char *user, GError **error) +{ + char *tmp; + + if (!user) + return g_strdup (filename); + + tmp = nm_utils_copy_cert_as_user (filename, user, error); + if (!tmp) + return NULL; + + g_ptr_array_add (gl.tmp_file_paths, g_strdup (tmp)); + return tmp; +} + static gint nm_openconnect_start_openconnect_binary (NMOpenconnectPlugin *plugin, NMSettingConnection *s_con, @@ -442,6 +480,8 @@ nm_openconnect_start_openconnect_binary (NMOpenconnectPlugin *plugin, const char *props_disable_udp; const char *protocol; const char *props_tun_name; + const char *user; + gs_free char *cafile = NULL; /* Find openconnect */ openconnect_binary = openconnect_binary_paths; @@ -484,6 +524,8 @@ nm_openconnect_start_openconnect_binary (NMOpenconnectPlugin *plugin, props_gwcert = nm_setting_vpn_get_secret (s_vpn, NM_OPENCONNECT_KEY_GWCERT); props_resolve = nm_setting_vpn_get_secret (s_vpn, NM_OPENCONNECT_KEY_RESOLVE); + user = get_connection_permission_user (s_con); + props_cacert = nm_setting_vpn_get_data_item (s_vpn, NM_OPENCONNECT_KEY_CACERT); props_mtu = nm_setting_vpn_get_data_item (s_vpn, NM_OPENCONNECT_KEY_MTU); @@ -507,8 +549,13 @@ nm_openconnect_start_openconnect_binary (NMOpenconnectPlugin *plugin, g_ptr_array_add (openconnect_argv, (gpointer) "--servercert"); g_ptr_array_add (openconnect_argv, (gpointer) props_gwcert); } else if (props_cacert && strlen(props_cacert)) { + cafile = access_file (props_cacert, user, error); + if (!cafile) { + g_ptr_array_free (openconnect_argv, TRUE); + return -1; + } g_ptr_array_add (openconnect_argv, (gpointer) "--cafile"); - g_ptr_array_add (openconnect_argv, (gpointer) props_cacert); + g_ptr_array_add (openconnect_argv, (gpointer) cafile); } if (props_mtu && strlen(props_mtu)) { @@ -839,6 +886,7 @@ int main (int argc, char *argv[]) exit (EXIT_FAILURE); gl.loop = g_main_loop_new (NULL, FALSE); + gl.tmp_file_paths = g_ptr_array_new_with_free_func (g_free); if (!persist) g_signal_connect (plugin, "quit", G_CALLBACK (quit_mainloop), gl.loop); @@ -849,5 +897,9 @@ int main (int argc, char *argv[]) g_clear_pointer (&gl.loop, g_main_loop_unref); g_object_unref (plugin); + for (guint i = 0; i < gl.tmp_file_paths->len; i++) + unlink ((const char *) gl.tmp_file_paths->pdata[i]); + g_ptr_array_unref (gl.tmp_file_paths); + exit (EXIT_SUCCESS); } -- GitLab