From f9f321fc00f9016569a592140d9e5a24f9c4db01 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Fri, 13 Sep 2024 14:49:12 +0200 Subject: [PATCH 1/6] shared/nm-glib: import newer g_steal_pointer() The version that's there doesn't work with current glib, still considering g_steal_pointer() deprecated. We should probably do a full import, but it's became such a mess in NetworkManager.git that it's not feasible at this point. [lkundrak@v3.sk: Backported from 1.24.0] --- shared/nm-utils/nm-glib.h | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/shared/nm-utils/nm-glib.h b/shared/nm-utils/nm-glib.h index 770cf0f..1b6487c 100644 --- a/shared/nm-utils/nm-glib.h +++ b/shared/nm-utils/nm-glib.h @@ -412,24 +412,26 @@ _nm_g_hash_table_get_keys_as_array (GHashTable *hash_table, /*****************************************************************************/ -#if !GLIB_CHECK_VERSION(2, 44, 0) -static inline gpointer -g_steal_pointer (gpointer pp) -{ - gpointer *ptr = (gpointer *) pp; - gpointer ref; +#define _NM_ENSURE_POINTER(value) \ + do { \ + _nm_unused const void *const _unused_for_type_check = 0 ? (value) : NULL; \ + } while (0) - ref = *ptr; - *ptr = NULL; - - return ref; -} - -/* type safety */ -#define g_steal_pointer(pp) \ - (0 ? (*(pp)) : (g_steal_pointer) (pp)) +#ifdef g_steal_pointer +#undef g_steal_pointer #endif +#define g_steal_pointer(pp) \ + ({ \ + typeof(*(pp)) *const _pp = (pp); \ + typeof(*_pp) _p = *_pp; \ + \ + _NM_ENSURE_POINTER(_p); \ + \ + *_pp = NULL; \ + _p; \ + }) + /*****************************************************************************/ static inline gboolean -- 2.46.0 From 72816f82b029063e4d8aaff6703f175da5232293 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 17 Sep 2024 13:28:58 +0200 Subject: [PATCH 2/6] build: get rid of {properties,src}/libutils.la Useless build of an extra libraries, just making the build slower and more complicated. Get rid of then, and just roll src/libutils.la. [lkundrak@v3.sk: Backported from 1.24.0] --- Makefile.am | 69 +++++++++++++++++++++-------------------------------- 1 file changed, 27 insertions(+), 42 deletions(-) diff --git a/Makefile.am b/Makefile.am index 29084a9..d46cfcd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -33,6 +33,26 @@ nmvpnservice_DATA = nm-libreswan-service.name ############################################################################### +noinst_LTLIBRARIES += shared/libutils.la + +shared_libutils_la_SOURCES = \ + shared/nm-utils/nm-shared-utils.c \ + shared/nm-utils/nm-shared-utils.h \ + shared/utils.c \ + shared/utils.h \ + shared/nm-service-defines.h + +shared_libutils_la_CFLAGS = \ + -DPREFIX=\""$(prefix)"\" \ + $(common_CFLAGS) \ + $(LIBNM_CFLAGS) + +shared_libutils_la_LIBADD = \ + $(GLIB_LIBS) \ + $(LIBNM_LIBS) + +############################################################################### + properties/resources.h: properties/gresource.xml $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) $< --target=$@ --sourcedir=$(srcdir)/properties --generate-header --internal @@ -53,10 +73,6 @@ gtk4/%.ui: properties/%.ui EXTRA_DIST += \ gtk4/nm-libreswan-dialog.ui -plugin_sources = \ - properties/nm-libreswan-editor-plugin.c \ - properties/nm-libreswan-editor-plugin.h - editor_sources = \ properties/nm-libreswan-editor.c \ properties/nm-libreswan-editor.h @@ -68,23 +84,6 @@ common_CFLAGS = \ ############################################################################### -noinst_LTLIBRARIES += properties/libutils.la - -properties_libutils_la_SOURCES = \ - shared/utils.c \ - shared/utils.h \ - shared/nm-utils/nm-vpn-plugin-utils.c \ - shared/nm-utils/nm-vpn-plugin-utils.h \ - shared/nm-utils/nm-shared-utils.c \ - shared/nm-utils/nm-shared-utils.h \ - shared/nm-service-defines.h - -properties_libutils_la_CPPFLAGS = \ - -DPREFIX=\""$(prefix)"\" \ - -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_LIB_BASE \ - $(common_CFLAGS) \ - $(LIBNM_CFLAGS) - plugin_LTLIBRARIES += properties/libnm-vpn-plugin-libreswan.la properties_libnm_vpn_plugin_libreswan_la_CFLAGS = \ @@ -93,10 +92,13 @@ properties_libnm_vpn_plugin_libreswan_la_CFLAGS = \ $(LIBNM_CFLAGS) properties_libnm_vpn_plugin_libreswan_la_SOURCES = \ - $(plugin_sources) + shared/nm-utils/nm-vpn-plugin-utils.c \ + shared/nm-utils/nm-vpn-plugin-utils.h \ + properties/nm-libreswan-editor-plugin.c \ + properties/nm-libreswan-editor-plugin.h properties_libnm_vpn_plugin_libreswan_la_LIBADD = \ - properties/libutils.la \ + shared/libutils.la \ $(LIBNM_LIBS) \ $(DL_LIBS) @@ -198,7 +200,6 @@ auth_dialog_nm_libreswan_auth_dialog_LDADD = \ src_cppflags = \ -DBINDIR=\"$(bindir)\" \ - -DPREFIX=\""$(prefix)"\" \ -DLIBDIR=\""$(libdir)"\" \ -DLIBEXECDIR=\""$(libexecdir)"\" \ -DLOCALSTATEDIR=\""$(localstatedir)"\" \ @@ -230,22 +231,6 @@ src/nm-libreswan-helper-service-dbus.h: src/nm-libreswan-helper-service.xml src/nm-libreswan-helper-service-dbus.c: src/nm-libreswan-helper-service-dbus.h @true -noinst_LTLIBRARIES += src/libutils.la - -src_libutils_la_SOURCES = \ - shared/nm-utils/nm-shared-utils.c \ - shared/nm-utils/nm-shared-utils.h \ - shared/utils.c \ - shared/utils.h \ - shared/nm-service-defines.h - -src_libutils_la_CPPFLAGS = \ - $(src_cppflags) - -src_libutils_la_LIBADD = \ - $(GLIB_LIBS) \ - $(LIBNM_LIBS) - ############################################################################### libexec_PROGRAMS += src/nm-libreswan-service @@ -255,7 +240,7 @@ src_nm_libreswan_service_CPPFLAGS = \ src_nm_libreswan_service_LDADD = \ src/libnm-libreswan-helper-service-dbus.la \ - src/libutils.la \ + shared/libutils.la \ $(GLIB_LIBS) \ $(LIBNM_LIBS) \ $(LIBNL_LIBS) \ @@ -272,7 +257,7 @@ src_nm_libreswan_service_helper_CPPFLAGS = \ src_nm_libreswan_service_helper_LDADD = \ src/libnm-libreswan-helper-service-dbus.la \ - src/libutils.la \ + shared/libutils.la \ $(GLIB_LIBS) \ $(LIBNM_LIBS) -- 2.46.0 From cf9777bd065ddc40c627e1d994432e95b1e70a82 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Mon, 23 Sep 2024 11:39:22 +0200 Subject: [PATCH 3/6] shared/test-utils: cover config write with unit tests Test that we generate good configuration for typical IKEv1 and IKEv2 cases. [lkundrak@v3.sk: Backported from 1.24.0] --- Makefile.am | 16 ++++++ shared/test-utils.c | 127 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 shared/test-utils.c diff --git a/Makefile.am b/Makefile.am index d46cfcd..3f4e85c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,6 +21,8 @@ libexec_PROGRAMS = noinst_PROGRAMS = +TESTS = + SUBDIRS = po man ############################################################################### @@ -51,6 +53,20 @@ shared_libutils_la_LIBADD = \ $(GLIB_LIBS) \ $(LIBNM_LIBS) +noinst_PROGRAMS += shared/test-utils + +TESTS += shared/test-utils + +shared_test_utils_SOURCES = shared/test-utils.c + +shared_test_utils_CFLAGS = \ + $(common_CFLAGS) \ + $(LIBNM_CFLAGS) + +shared_test_utils_LDADD = \ + shared/libutils.la \ + $(LIBNM_LIBS) + ############################################################################### properties/resources.h: properties/gresource.xml diff --git a/shared/test-utils.c b/shared/test-utils.c new file mode 100644 index 0000000..82ee933 --- /dev/null +++ b/shared/test-utils.c @@ -0,0 +1,127 @@ +#include "nm-default.h" + +#include "utils.h" + +#include + +static char * +_setting_into_ipsec_conf (NMSetting *s_vpn, const char *name, GError **error) +{ + gs_unref_object NMConnection *connection = NULL; + gs_unref_object GFile *tmp = NULL; + GFileIOStream *tmpstream; + char buf[4096]; + gboolean res; + gsize count; + gint fd; + + connection = nm_simple_connection_new (); + nm_connection_add_setting (connection, s_vpn); + + tmp = g_file_new_tmp (NULL, &tmpstream, error); + if (tmp == NULL) + return NULL; + + res = g_file_delete (tmp, NULL, error); + if (res == FALSE) + return NULL; + + fd = g_file_descriptor_based_get_fd(G_FILE_DESCRIPTOR_BASED( + g_io_stream_get_output_stream(G_IO_STREAM (tmpstream)))); + + res = nm_libreswan_config_write (fd, 4, connection, + name, NULL, + FALSE, TRUE, NULL, + error); + if (res == FALSE) + return NULL; + + res = g_seekable_seek (G_SEEKABLE(tmpstream), + 0, G_SEEK_SET, NULL, error); + if (res == FALSE) + return NULL; + + res = g_input_stream_read_all ( + g_io_stream_get_input_stream(G_IO_STREAM (tmpstream)), + buf, + sizeof(buf)-1, + &count, + NULL, + error); + if (res == FALSE) + return NULL; + + buf[count] = '\0'; + return g_strdup(buf); +} + +static void +test_config_write (void) +{ + GError *error = NULL; + NMSetting *s_vpn; + char *str; + + s_vpn = nm_setting_vpn_new (); + nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "right", "11.12.13.14"); + str = _setting_into_ipsec_conf (s_vpn, "con_name", &error); + g_assert_no_error (error); + g_assert_cmpstr (str, ==, + "conn con_name\n" + " authby=secret\n" + " left=%defaultroute\n" + " leftmodecfgclient=yes\n" + " right=11.12.13.14\n" + " rightmodecfgserver=yes\n" + " modecfgpull=yes\n" + " rightsubnet=0.0.0.0/0\n" + " leftxauthclient=yes\n" + " remote-peer-type=cisco\n" + " rightxauthserver=yes\n" + " ikelifetime=24h\n" + " salifetime=24h\n" + " rekey=yes\n" + " keyingtries=1\n" + " ikev2=never\n" + " nm-configured=yes\n" + " auto=add\n"); + g_free (str); + + s_vpn = nm_setting_vpn_new (); + nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "ikev2", "insist"); + nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "leftcert", "LibreswanClient"); + nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "leftid", "%fromcert"); + nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "right", "11.12.13.14"); + str = _setting_into_ipsec_conf (s_vpn, + "f0008435-07af-4836-a53d-b43e8730e68f", + &error); + g_assert_no_error (error); + g_assert_cmpstr (str, ==, + "conn f0008435-07af-4836-a53d-b43e8730e68f\n" + " leftid=%fromcert\n" + " leftcert=LibreswanClient\n" + " leftrsasigkey=%cert\n" + " rightrsasigkey=%cert\n" + " left=%defaultroute\n" + " leftmodecfgclient=yes\n" + " right=11.12.13.14\n" + " rightmodecfgserver=yes\n" + " modecfgpull=yes\n" + " rightsubnet=0.0.0.0/0\n" + " rekey=yes\n" + " keyingtries=1\n" + " ikev2=insist\n" + " nm-configured=yes\n" + " auto=add\n"); + g_free (str); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/utils/config/write", test_config_write); + + return g_test_run (); +} -- 2.46.0 From 2b07bfeec5e67cbdce9b23b7c0648cb0ee55416d Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Sun, 22 Sep 2024 14:20:22 +0200 Subject: [PATCH 4/6] all: rework formatting of ipsec.conf Simplify the interface and validate each item carefully. Note this fixes a security issue (CVE-2024-9050), with an "Important" impact severity, where insufficient validation could lead to injection of potentialy malicious values into the ipsec.conf snippet we send over to pluto. https://issues.redhat.com/browse/RHEL-59565 [lkundrak@v3.sk: Backported from 1.24.0] --- properties/nm-libreswan-editor-plugin.c | 30 +- shared/test-utils.c | 97 +---- shared/utils.c | 491 ++++++++++++++---------- shared/utils.h | 28 +- src/nm-libreswan-service.c | 183 +++++---- 5 files changed, 423 insertions(+), 406 deletions(-) diff --git a/properties/nm-libreswan-editor-plugin.c b/properties/nm-libreswan-editor-plugin.c index fe473d1..9393212 100644 --- a/properties/nm-libreswan-editor-plugin.c +++ b/properties/nm-libreswan-editor-plugin.c @@ -286,19 +286,11 @@ export_to_file (NMVpnEditorPlugin *self, { NMSettingVpn *s_vpn; gboolean openswan = FALSE; - int fd, errsv; gs_free_error GError *local = NULL; + gs_free char *ipsec_conf = NULL; gboolean is_openswan; int version; - fd = g_open (path, O_WRONLY | O_CREAT, 0666); - if (fd == -1) { - errsv = errno; - g_set_error (error, NMV_EDITOR_PLUGIN_ERROR, NMV_EDITOR_PLUGIN_ERROR_FAILED, - _("Can’t open file “%s”: %s"), path, g_strerror (errsv)); - return FALSE; - } - s_vpn = nm_connection_get_setting_vpn (connection); if (s_vpn) openswan = nm_streq (nm_setting_vpn_get_service_type (s_vpn), NM_VPN_SERVICE_TYPE_OPENSWAN); @@ -306,24 +298,18 @@ export_to_file (NMVpnEditorPlugin *self, nm_libreswan_detect_version (nm_libreswan_find_helper_bin ("ipsec", NULL), &is_openswan, &version, NULL); - if (!nm_libreswan_config_write (fd, - version, - connection, - nm_connection_get_id (connection), - NULL, - openswan, - TRUE, - NULL, - &local)) { - g_close (fd, NULL); + ipsec_conf = nm_libreswan_get_ipsec_conf (version, s_vpn, + nm_connection_get_id (connection), + NULL, openswan, TRUE, error); + if (ipsec_conf == NULL) + return FALSE; + + if (!g_file_set_contents (path, ipsec_conf, -1, &local)) { g_set_error (error, NMV_EDITOR_PLUGIN_ERROR, NMV_EDITOR_PLUGIN_ERROR_FAILED, _("Error writing to file “%s”: %s"), path, local->message); return FALSE; } - if (!g_close (fd, error)) - return FALSE; - return TRUE; } diff --git a/shared/test-utils.c b/shared/test-utils.c index 82ee933..49aa32a 100644 --- a/shared/test-utils.c +++ b/shared/test-utils.c @@ -2,117 +2,60 @@ #include "utils.h" -#include - -static char * -_setting_into_ipsec_conf (NMSetting *s_vpn, const char *name, GError **error) -{ - gs_unref_object NMConnection *connection = NULL; - gs_unref_object GFile *tmp = NULL; - GFileIOStream *tmpstream; - char buf[4096]; - gboolean res; - gsize count; - gint fd; - - connection = nm_simple_connection_new (); - nm_connection_add_setting (connection, s_vpn); - - tmp = g_file_new_tmp (NULL, &tmpstream, error); - if (tmp == NULL) - return NULL; - - res = g_file_delete (tmp, NULL, error); - if (res == FALSE) - return NULL; - - fd = g_file_descriptor_based_get_fd(G_FILE_DESCRIPTOR_BASED( - g_io_stream_get_output_stream(G_IO_STREAM (tmpstream)))); - - res = nm_libreswan_config_write (fd, 4, connection, - name, NULL, - FALSE, TRUE, NULL, - error); - if (res == FALSE) - return NULL; - - res = g_seekable_seek (G_SEEKABLE(tmpstream), - 0, G_SEEK_SET, NULL, error); - if (res == FALSE) - return NULL; - - res = g_input_stream_read_all ( - g_io_stream_get_input_stream(G_IO_STREAM (tmpstream)), - buf, - sizeof(buf)-1, - &count, - NULL, - error); - if (res == FALSE) - return NULL; - - buf[count] = '\0'; - return g_strdup(buf); -} - static void test_config_write (void) { GError *error = NULL; - NMSetting *s_vpn; + NMSettingVpn *s_vpn; char *str; - s_vpn = nm_setting_vpn_new (); + s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ()); nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "right", "11.12.13.14"); - str = _setting_into_ipsec_conf (s_vpn, "con_name", &error); + str = nm_libreswan_get_ipsec_conf (4, s_vpn, "con_name", NULL, FALSE, TRUE, &error); g_assert_no_error (error); g_assert_cmpstr (str, ==, "conn con_name\n" + " ikev2=never\n" + " right=11.12.13.14\n" " authby=secret\n" " left=%defaultroute\n" " leftmodecfgclient=yes\n" - " right=11.12.13.14\n" - " rightmodecfgserver=yes\n" - " modecfgpull=yes\n" " rightsubnet=0.0.0.0/0\n" " leftxauthclient=yes\n" " remote-peer-type=cisco\n" " rightxauthserver=yes\n" " ikelifetime=24h\n" " salifetime=24h\n" - " rekey=yes\n" " keyingtries=1\n" - " ikev2=never\n" - " nm-configured=yes\n" - " auto=add\n"); + " rekey=yes\n" + " rightmodecfgserver=yes\n" + " modecfgpull=yes\n"); g_free (str); - s_vpn = nm_setting_vpn_new (); + s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ()); nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "ikev2", "insist"); nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "leftcert", "LibreswanClient"); nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "leftid", "%fromcert"); nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "right", "11.12.13.14"); - str = _setting_into_ipsec_conf (s_vpn, - "f0008435-07af-4836-a53d-b43e8730e68f", - &error); + str = nm_libreswan_get_ipsec_conf (4, s_vpn, + "f0008435-07af-4836-a53d-b43e8730e68f", + NULL, FALSE, TRUE, &error); g_assert_no_error (error); g_assert_cmpstr (str, ==, "conn f0008435-07af-4836-a53d-b43e8730e68f\n" + " ikev2=insist\n" + " right=11.12.13.14\n" " leftid=%fromcert\n" - " leftcert=LibreswanClient\n" - " leftrsasigkey=%cert\n" - " rightrsasigkey=%cert\n" + " leftcert=\"LibreswanClient\"\n" + " leftrsasigkey=\"%cert\"\n" + " rightrsasigkey=\"%cert\"\n" " left=%defaultroute\n" " leftmodecfgclient=yes\n" - " right=11.12.13.14\n" - " rightmodecfgserver=yes\n" - " modecfgpull=yes\n" " rightsubnet=0.0.0.0/0\n" - " rekey=yes\n" " keyingtries=1\n" - " ikev2=insist\n" - " nm-configured=yes\n" - " auto=add\n"); + " rekey=yes\n" + " rightmodecfgserver=yes\n" + " modecfgpull=yes\n"); g_free (str); } diff --git a/shared/utils.c b/shared/utils.c index 65bc603..2482311 100644 --- a/shared/utils.c +++ b/shared/utils.c @@ -30,82 +30,109 @@ #include #include -gboolean -write_config_option_newline (int fd, - gboolean new_line, - NMDebugWriteFcn debug_write_fcn, - GError **error, - const char *format, ...) +static gboolean +printable_val (GString *str, const char *val, GError **error) { - gs_free char *string = NULL; const char *p; - va_list args; - gsize l; - int errsv; - gssize w; - va_start (args, format); - string = g_strdup_vprintf (format, args); - va_end (args); + g_return_val_if_fail (val, FALSE); - if (debug_write_fcn) - debug_write_fcn (string); - - l = strlen (string); - if (new_line) { - gs_free char *s = string; - - string = g_new (char, l + 1 + 1); - memcpy (string, s, l); - string[l] = '\n'; - string[l + 1] = '\0'; - l++; + for (p = val; *p != '\0'; p++) { + /* Printable characters except " and space allowed. */ + if (*p != '"' && !g_ascii_isspace (*p) && g_ascii_isprint (*p)) + continue; + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("Invalid character in '%s'"), val); + return FALSE; } - p = string; - while (true) { - w = write (fd, p, l); - if (w == l) - return TRUE; - if (w > 0) { - g_assert (w < l); - p += w; - l -= w; - continue; - } - if (w == 0) { - errsv = EIO; - break; - } - errsv = errno; - if (errsv == EINTR) - continue; - break; - } - g_set_error (error, NMV_EDITOR_PLUGIN_ERROR, NMV_EDITOR_PLUGIN_ERROR, - _("Error writing config: %s"), g_strerror (errsv)); - return FALSE; + g_string_append (str, val); + g_string_append_c (str, '\n'); + return TRUE; } -gboolean -nm_libreswan_config_write (gint fd, - int ipsec_version, - NMConnection *connection, - const char *con_name, - const char *leftupdown_script, - gboolean openswan, - gboolean trailing_newline, - NMDebugWriteFcn debug_write_fcn, - GError **error) +static gboolean +string_val (GString *str, const char *val, GError **error) { - NMSettingVpn *s_vpn; - const char *props_username; - const char *default_username; + const char *p; + + g_return_val_if_fail (val, FALSE); + + for (p = val; *p != '\0'; p++) { + /* Printable characters except " allowed. */ + if (*p != '"' && g_ascii_isprint (*p)) + continue; + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("Invalid character in '%s'"), val); + return FALSE; + } + + g_string_append_printf (str, "\"%s\"\n", val); + return TRUE; +} + +static inline gboolean +optional_string_val (GString *str, const char *key, const char *val, GError **error) +{ + if (val == NULL || val[0] == '\0') + return TRUE; + g_string_append_c (str, ' '); + g_string_append (str, key); + g_string_append_c (str, '='); + + if (!string_val (str, val, error)) { + g_prefix_error (error, _("Invalid value for '%s': "), key); + return FALSE; + } + + return TRUE; +} + +static inline gboolean +optional_printable_val (GString *str, const char *key, const char *val, GError **error) +{ + if (val == NULL || val[0] == '\0') + return TRUE; + + g_string_append_c (str, ' '); + g_string_append (str, key); + g_string_append_c (str, '='); + + if (!printable_val (str, val, error)) { + g_prefix_error (error, _("Invalid value for '%s': "), key); + return FALSE; + } + + return TRUE; +} + + +static inline gboolean +optional_printable (GString *str, NMSettingVpn *s_vpn, const char *key, GError **error) +{ + return optional_printable_val (str, + key, + nm_setting_vpn_get_data_item (s_vpn, key), + error); +} + +char * +nm_libreswan_get_ipsec_conf (int ipsec_version, + NMSettingVpn *s_vpn, + const char *con_name, + const char *leftupdown_script, + gboolean openswan, + gboolean trailing_newline, + GError **error) +{ + nm_auto_free_gstring GString *ipsec_conf = NULL; + const char *username; const char *phase1_alg_str; const char *phase2_alg_str; const char *phase1_lifetime_str; const char *phase2_lifetime_str; const char *left; + const char *right; const char *leftid; const char *leftcert; const char *rightcert; @@ -116,129 +143,176 @@ nm_libreswan_config_write (gint fd, const char *remote_network; const char *ikev2 = NULL; const char *rightid; - const char *narrowing; const char *rekey; - const char *fragmentation; - const char *mobike; const char *pfs; const char *client_family; const char *item; gboolean is_ikev2 = FALSE; - g_return_val_if_fail (fd > 0, FALSE); - g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + g_return_val_if_fail (NM_IS_SETTING_VPN (s_vpn), FALSE); g_return_val_if_fail (!error || !*error, FALSE); g_return_val_if_fail (con_name && *con_name, FALSE); - s_vpn = nm_connection_get_setting_vpn (connection); - g_return_val_if_fail (NM_IS_SETTING_VPN (s_vpn), FALSE); + ipsec_conf = g_string_sized_new (1024); + g_string_append (ipsec_conf, "conn "); + if (!printable_val (ipsec_conf, con_name, error)) { + g_prefix_error (error, _("Bad connection name: ")); + return FALSE; + } - is_ikev2 = nm_libreswan_utils_setting_is_ikev2 (s_vpn, &ikev2); + if (leftupdown_script) { + g_string_append (ipsec_conf, " auto=add\n"); + g_string_append (ipsec_conf, " nm-configured=yes\n"); + g_string_append (ipsec_conf, " leftupdown="); + if (!string_val (ipsec_conf, leftupdown_script, error)) + g_return_val_if_reached (FALSE); + } /* When using IKEv1 (default in our plugin), we should ensure that we make * it explicit to Libreswan (which now defaults to IKEv2): when crypto algorithms * are not specified ("esp" & "ike") Libreswan will use system-wide crypto * policies based on the IKE version in place. */ + is_ikev2 = nm_libreswan_utils_setting_is_ikev2 (s_vpn, &ikev2); if (!ikev2) ikev2 = NM_LIBRESWAN_IKEV2_NEVER; + g_string_append (ipsec_conf, " ikev2="); + if (!printable_val (ipsec_conf, ikev2, error)) { + g_prefix_error (error, _("Invalid value for '%s': "), "ikev2"); + return FALSE; + } + + right = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_RIGHT); + if (right && right[0] != '\0') { + g_string_append (ipsec_conf, " right="); + if (!printable_val (ipsec_conf, right, error)) { + g_prefix_error (error, _("Invalid value for '%s': "), + NM_LIBRESWAN_KEY_RIGHT); + return FALSE; + } + } else { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("'%s' key needs to be present."), NM_LIBRESWAN_KEY_RIGHT); + return FALSE; + } leftid = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTID); - -#define WRITE_CHECK_NEWLINE(fd, new_line, debug_write_fcn, error, ...) \ - G_STMT_START { \ - if (!write_config_option_newline ((fd), (new_line), debug_write_fcn, (error), __VA_ARGS__)) \ - return FALSE; \ - } G_STMT_END -#define WRITE_CHECK(fd, debug_write_fcn, error, ...) WRITE_CHECK_NEWLINE (fd, TRUE, debug_write_fcn, error, __VA_ARGS__) - - WRITE_CHECK (fd, debug_write_fcn, error, "conn %s", con_name); - if (leftid && strlen (leftid)) { + if (leftid && leftid[0] != '\0') { if (!is_ikev2) - WRITE_CHECK (fd, debug_write_fcn, error, " aggrmode=yes"); + g_string_append (ipsec_conf, " aggrmode=yes\n"); if ( leftid[0] == '%' || leftid[0] == '@' || nm_utils_parse_inaddr_bin (AF_UNSPEC, leftid, NULL)) { - WRITE_CHECK (fd, debug_write_fcn, error, " leftid=%s", leftid); + g_string_append (ipsec_conf, " leftid="); } else - WRITE_CHECK (fd, debug_write_fcn, error, " leftid=@%s", leftid); + g_string_append (ipsec_conf, " leftid=@"); + if (!printable_val (ipsec_conf, leftid, error)) { + g_prefix_error (error, _("Invalid value for '%s': "), + NM_LIBRESWAN_KEY_LEFTID); + return FALSE; + } } - item = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_HOSTADDRFAMILY); - if (item && strlen (item)) - WRITE_CHECK (fd, debug_write_fcn, error, " hostaddrfamily=%s", item); + if (!optional_printable (ipsec_conf, s_vpn, NM_LIBRESWAN_KEY_HOSTADDRFAMILY, error)) + return FALSE; client_family = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_CLIENTADDRFAMILY); - if (client_family && strlen (client_family)) - WRITE_CHECK (fd, debug_write_fcn, error, " clientaddrfamily=%s", client_family); + if (client_family && client_family[0] != '\0') { + g_string_append (ipsec_conf, " clientaddrfamily="); + if (!printable_val (ipsec_conf, client_family, error)) { + g_prefix_error (error, _("Invalid value for '%s': "), + NM_LIBRESWAN_KEY_CLIENTADDRFAMILY); + return FALSE; + } + } leftrsasigkey = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTRSASIGKEY); rightrsasigkey = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_RIGHTRSASIGKEY); leftcert = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTCERT); rightcert = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_RIGHTCERT); authby = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_AUTHBY); - if (rightcert && strlen (rightcert)) { - WRITE_CHECK (fd, debug_write_fcn, error, " rightcert=%s", rightcert); + if (rightcert && rightcert[0] != '\0') { + g_string_append (ipsec_conf, " rightcert="); + if (!string_val (ipsec_conf, rightcert, error)) { + g_prefix_error (error, _("Invalid value for '%s': "), + NM_LIBRESWAN_KEY_RIGHTCERT); + return FALSE; + } if (!rightrsasigkey) rightrsasigkey = "%cert"; } - if (leftcert && strlen (leftcert)) { - WRITE_CHECK (fd, debug_write_fcn, error, " leftcert=%s", leftcert); + if (leftcert && leftcert[0] != '\0') { + g_string_append (ipsec_conf, " leftcert="); + if (!string_val (ipsec_conf, leftcert, error)) { + g_prefix_error (error, _("Invalid value for '%s': "), + NM_LIBRESWAN_KEY_LEFTCERT); + return FALSE; + } if (!leftrsasigkey) leftrsasigkey = "%cert"; if (!rightrsasigkey) rightrsasigkey = "%cert"; } - if (leftrsasigkey && strlen (leftrsasigkey)) - WRITE_CHECK (fd, debug_write_fcn, error, " leftrsasigkey=%s", leftrsasigkey); - if (rightrsasigkey && strlen (rightrsasigkey)) - WRITE_CHECK (fd, debug_write_fcn, error, " rightrsasigkey=%s", rightrsasigkey); + if (!optional_string_val (ipsec_conf, NM_LIBRESWAN_KEY_LEFTRSASIGKEY, leftrsasigkey, error)) + return FALSE; + if (!optional_string_val (ipsec_conf, NM_LIBRESWAN_KEY_RIGHTRSASIGKEY, rightrsasigkey, error)) + return FALSE; - if (authby && strlen (authby)) { - WRITE_CHECK (fd, debug_write_fcn, error, " authby=%s", authby); - } else if ( !(leftrsasigkey && strlen (leftrsasigkey)) - && !(rightrsasigkey && strlen (rightrsasigkey))) { - WRITE_CHECK (fd, debug_write_fcn, error, " authby=secret"); + if (authby == NULL || authby[0] == '\0') { + if ( !(leftrsasigkey && leftrsasigkey[0] != '\0') + && !(rightrsasigkey && rightrsasigkey[0] != '\0')) { + authby = "secret"; + } } + if (!optional_printable_val (ipsec_conf, NM_LIBRESWAN_KEY_AUTHBY, authby, error)) + return FALSE; left = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFT); - if (left && strlen (left)) - WRITE_CHECK (fd, debug_write_fcn, error, " left=%s", left); - else - WRITE_CHECK (fd, debug_write_fcn, error, " left=%%defaultroute"); + if (left == NULL || left[0] == '\0') + left = "%defaultroute"; + g_string_append (ipsec_conf, " left="); + if (!printable_val (ipsec_conf, left, error)) { + g_prefix_error (error, _("Invalid value for '%s': "), + NM_LIBRESWAN_KEY_LEFT); + return FALSE; + } item = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTMODECFGCLIENT); if (nm_streq0 (item, "no")) { - WRITE_CHECK (fd, debug_write_fcn, error, " leftmodecfgclient=no"); + g_string_append (ipsec_conf, " leftmodecfgclient=no\n"); } else { - WRITE_CHECK (fd, debug_write_fcn, error, " leftmodecfgclient=yes"); + g_string_append (ipsec_conf, " leftmodecfgclient=yes\n"); } - if (leftupdown_script) - WRITE_CHECK (fd, debug_write_fcn, error, " leftupdown=%s", leftupdown_script); - - WRITE_CHECK (fd, debug_write_fcn, error, " right=%s", nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_RIGHT)); rightid = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_RIGHTID); - if (rightid && strlen (rightid)) { + if (rightid && rightid[0] != '\0') { if ( rightid[0] == '@' || rightid[0] == '%' - || nm_utils_parse_inaddr_bin (AF_UNSPEC, rightid, NULL)) { - WRITE_CHECK (fd, debug_write_fcn, error, " rightid=%s", rightid); - } else - WRITE_CHECK (fd, debug_write_fcn, error, " rightid=@%s", rightid); + || nm_utils_parse_inaddr_bin (AF_UNSPEC, rightid, NULL)) { + g_string_append (ipsec_conf, " rightid="); + } else { + g_string_append (ipsec_conf, " rightid=@"); + } + if (!printable_val (ipsec_conf, rightid, error)) { + g_prefix_error (error, _("Invalid value for '%s': "), + NM_LIBRESWAN_KEY_RIGHTID); + return FALSE; + } } - WRITE_CHECK (fd, debug_write_fcn, error, " rightmodecfgserver=yes"); - WRITE_CHECK (fd, debug_write_fcn, error, " modecfgpull=yes"); - local_network = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LOCALNETWORK); if (local_network) { - WRITE_CHECK (fd, debug_write_fcn, error, " leftsubnet=%s", local_network); + g_string_append (ipsec_conf, " leftsubnet="); + if (!printable_val (ipsec_conf, local_network, error)) { + g_prefix_error (error, _("Invalid value for '%s': "), + NM_LIBRESWAN_KEY_LOCALNETWORK); + return FALSE; + } } remote_network = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_REMOTENETWORK); - if (!remote_network || !strlen (remote_network)) { + if (!remote_network || remote_network[0] == '\0') { int addr_family = AF_UNSPEC; /* Detect the address family of the remote subnet. We use in order: @@ -259,43 +333,50 @@ nm_libreswan_config_write (gint fd, } if (addr_family == AF_INET6) { - WRITE_CHECK (fd, debug_write_fcn, error, " rightsubnet=::/0"); + remote_network = "::/0"; } else { /* For backwards compatibility, if we can't determine the family * assume it's IPv4. Anyway, in the future we need to stop adding * the option automatically. */ - WRITE_CHECK (fd, debug_write_fcn, error, " rightsubnet=0.0.0.0/0"); + remote_network = "0.0.0.0/0"; } - } else { - WRITE_CHECK (fd, debug_write_fcn, error, " rightsubnet=%s", remote_network); + } + g_string_append (ipsec_conf, " rightsubnet="); + if (!printable_val (ipsec_conf, remote_network, error)) { + g_prefix_error (error, _("Invalid value for '%s': "), + NM_LIBRESWAN_KEY_REMOTENETWORK); + return FALSE; } if (!is_ikev2) { /* When IKEv1 is in place, we enforce XAUTH: so, use IKE version * also to check if XAUTH conf options should be passed to Libreswan. */ - WRITE_CHECK (fd, debug_write_fcn, error, " leftxauthclient=yes"); + g_string_append (ipsec_conf, " leftxauthclient=yes\n"); - default_username = nm_setting_vpn_get_user_name (s_vpn); - props_username = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTXAUTHUSER); - if (!props_username) - props_username = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTUSERNAME); - if (props_username && strlen (props_username)) - WRITE_CHECK (fd, debug_write_fcn, error, - ipsec_version >= 4 ? " leftusername=%s" : " leftxauthusername=%s", - props_username); - else if (default_username && strlen (default_username)) - WRITE_CHECK (fd, debug_write_fcn, error, - ipsec_version >= 4 ? " leftusername=%s" : " leftxauthusername=%s", - default_username); + username = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTXAUTHUSER); + if (username == NULL || username[0] == '\0') + username = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTUSERNAME); + if (username == NULL || username[0] == '\0') + username = nm_setting_vpn_get_user_name (s_vpn); + if (username != NULL && username[0] != '\0') { + g_string_append (ipsec_conf, + ipsec_version >= 4 ? + " leftusername=" : + " leftxauthusername="); + if (!string_val (ipsec_conf, username, error)) { + g_prefix_error (error, _("Invalid username: ")); + return FALSE; + } + } - WRITE_CHECK (fd, debug_write_fcn, error, - ipsec_version >= 4 ? " remote-peer-type=cisco" : " remote_peer_type=cisco"); - WRITE_CHECK (fd, debug_write_fcn, error, " rightxauthserver=yes"); + g_string_append (ipsec_conf, + ipsec_version >= 4 ? + " remote-peer-type=cisco\n" : + " remote_peer_type=cisco\n"); + g_string_append (ipsec_conf, " rightxauthserver=yes\n"); } - - phase1_alg_str = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_IKE); /* When the crypto is unspecified, let Libreswan use many sets of crypto * proposals (just leave the property unset). An exception should be made * for IKEv1 connections in aggressive mode: there the DH group in the crypto @@ -304,84 +385,80 @@ nm_libreswan_config_write (gint fd, * force the best proposal that should be accepted by all obsolete VPN SW/HW * acting as a remote access VPN server. */ - if (phase1_alg_str && strlen (phase1_alg_str)) - WRITE_CHECK (fd, debug_write_fcn, error, " ike=%s", phase1_alg_str); - else if (!is_ikev2 && leftid) - WRITE_CHECK (fd, debug_write_fcn, error, " ike=%s", NM_LIBRESWAN_AGGRMODE_DEFAULT_IKE); + phase1_alg_str = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_IKE); + if (phase1_alg_str == NULL || phase1_alg_str[0] == '\0') { + if (!is_ikev2 && leftid) + phase1_alg_str = NM_LIBRESWAN_AGGRMODE_DEFAULT_IKE; + } + if (!optional_string_val (ipsec_conf, NM_LIBRESWAN_KEY_IKE, phase1_alg_str, error)) + return FALSE; phase2_alg_str = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_ESP); - if (phase2_alg_str && strlen (phase2_alg_str)) - WRITE_CHECK (fd, debug_write_fcn, error, " phase2alg=%s", phase2_alg_str); - else if (!is_ikev2 && leftid) - WRITE_CHECK (fd, debug_write_fcn, error, " phase2alg=%s", NM_LIBRESWAN_AGGRMODE_DEFAULT_ESP); + if (phase2_alg_str == NULL || phase2_alg_str[0] == '\0') { + if (!is_ikev2 && leftid) + phase2_alg_str = NM_LIBRESWAN_AGGRMODE_DEFAULT_ESP; + } + if (!optional_string_val (ipsec_conf, "phase2alg", phase2_alg_str, error)) + return FALSE; pfs = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_PFS); if (pfs && !strcmp (pfs, "no")) - WRITE_CHECK (fd, debug_write_fcn, error, " pfs=no"); + g_string_append (ipsec_conf, " pfs=no\n"); - phase1_lifetime_str = nm_setting_vpn_get_data_item (s_vpn, - NM_LIBRESWAN_KEY_IKELIFETIME); - if (phase1_lifetime_str && strlen (phase1_lifetime_str)) - WRITE_CHECK (fd, debug_write_fcn, error, " ikelifetime=%s", phase1_lifetime_str); - else if (!is_ikev2) - WRITE_CHECK (fd, debug_write_fcn, error, " ikelifetime=%s", NM_LIBRESWAN_IKEV1_DEFAULT_LIFETIME); + phase1_lifetime_str = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_IKELIFETIME); + if (phase1_lifetime_str == NULL || phase1_lifetime_str[0] == '\0') { + if (!is_ikev2) + phase1_lifetime_str = NM_LIBRESWAN_IKEV1_DEFAULT_LIFETIME; + } + if (!optional_printable_val (ipsec_conf, NM_LIBRESWAN_KEY_IKELIFETIME, phase1_lifetime_str, error)) + return FALSE; - phase2_lifetime_str = nm_setting_vpn_get_data_item (s_vpn, - NM_LIBRESWAN_KEY_SALIFETIME); - if (phase2_lifetime_str && strlen (phase2_lifetime_str)) - WRITE_CHECK (fd, debug_write_fcn, error, " salifetime=%s", phase2_lifetime_str); - else if (!is_ikev2) - WRITE_CHECK (fd, debug_write_fcn, error, " salifetime=%s", NM_LIBRESWAN_IKEV1_DEFAULT_LIFETIME); + phase2_lifetime_str = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_SALIFETIME); + if (phase2_lifetime_str == NULL || phase2_lifetime_str[0] == '\0') { + if (!is_ikev2) + phase2_lifetime_str = NM_LIBRESWAN_IKEV1_DEFAULT_LIFETIME; + } + if (!optional_printable_val (ipsec_conf, NM_LIBRESWAN_KEY_SALIFETIME, phase2_lifetime_str, error)) + return FALSE; rekey = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_REKEY); - if (!rekey || !strlen (rekey)) { - WRITE_CHECK (fd, debug_write_fcn, error, " rekey=yes"); - WRITE_CHECK (fd, debug_write_fcn, error, " keyingtries=1"); - } else - WRITE_CHECK (fd, debug_write_fcn, error, " rekey=%s", rekey); + if (!rekey || rekey[0] == '\0') { + g_string_append (ipsec_conf, " keyingtries=1\n"); + rekey = "yes"; + } + g_string_append (ipsec_conf, " rekey="); + if (!printable_val (ipsec_conf, rekey, error)) { + g_prefix_error (error, _("Invalid value for '%s': "), + NM_LIBRESWAN_KEY_REKEY); + return FALSE; + } if (!openswan && g_strcmp0 (nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_VENDOR), "Cisco") == 0) - WRITE_CHECK (fd, debug_write_fcn, error, " cisco-unity=yes"); + g_string_append (ipsec_conf, " cisco-unity=yes\n"); - WRITE_CHECK (fd, debug_write_fcn, error, " ikev2=%s", ikev2); + if (!optional_printable (ipsec_conf, s_vpn, NM_LIBRESWAN_KEY_NARROWING, error)) + return FALSE; + if (!optional_printable (ipsec_conf, s_vpn, NM_LIBRESWAN_KEY_FRAGMENTATION, error)) + return FALSE; + if (!optional_printable (ipsec_conf, s_vpn, NM_LIBRESWAN_KEY_MOBIKE, error)) + return FALSE; + if (!optional_printable (ipsec_conf, s_vpn, NM_LIBRESWAN_KEY_DPDDELAY, error)) + return FALSE; + if (!optional_printable (ipsec_conf, s_vpn, NM_LIBRESWAN_KEY_DPDTIMEOUT, error)) + return FALSE; + if (!optional_printable (ipsec_conf, s_vpn, NM_LIBRESWAN_KEY_DPDACTION, error)) + return FALSE; + if (!optional_printable (ipsec_conf, s_vpn, NM_LIBRESWAN_KEY_IPSEC_INTERFACE, error)) + return FALSE; + if (!optional_printable (ipsec_conf, s_vpn, NM_LIBRESWAN_KEY_TYPE, error)) + return FALSE; - narrowing = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_NARROWING); - if (narrowing && strlen (narrowing)) - WRITE_CHECK (fd, debug_write_fcn, error, " narrowing=%s", narrowing); + g_string_append (ipsec_conf, " rightmodecfgserver=yes\n"); + g_string_append (ipsec_conf, " modecfgpull=yes"); + if (trailing_newline) + g_string_append_c (ipsec_conf, '\n'); - fragmentation = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_FRAGMENTATION); - if (fragmentation && strlen (fragmentation)) - WRITE_CHECK (fd, debug_write_fcn, error, " fragmentation=%s", fragmentation); - - mobike = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_MOBIKE); - if (mobike && strlen (mobike)) - WRITE_CHECK (fd, debug_write_fcn, error, " mobike=%s", mobike); - - item = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_DPDDELAY); - if (item && strlen (item)) - WRITE_CHECK (fd, debug_write_fcn, error, " dpddelay=%s", item); - - item = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_DPDTIMEOUT); - if (item && strlen (item)) - WRITE_CHECK (fd, debug_write_fcn, error, " dpdtimeout=%s", item); - - item = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_DPDACTION); - if (item && strlen (item)) - WRITE_CHECK (fd, debug_write_fcn, error, " dpdaction=%s", item); - - item = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_IPSEC_INTERFACE); - if (item && strlen (item)) - WRITE_CHECK (fd, debug_write_fcn, error, " ipsec-interface=%s", item); - - item = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_TYPE); - if (item && strlen (item)) - WRITE_CHECK (fd, debug_write_fcn, error, " type=%s", item); - - WRITE_CHECK (fd, debug_write_fcn, error, " nm-configured=yes"); - - WRITE_CHECK_NEWLINE (fd, trailing_newline, debug_write_fcn, error, " auto=add"); - - return TRUE; + return g_string_free (g_steal_pointer (&ipsec_conf), FALSE); } static const char * diff --git a/shared/utils.h b/shared/utils.h index 7e89841..2e2450c 100644 --- a/shared/utils.h +++ b/shared/utils.h @@ -24,27 +24,13 @@ #ifndef __UTILS_H__ #define __UTILS_H__ -typedef void (*NMDebugWriteFcn) (const char *setting); - -__attribute__((__format__ (__printf__, 5, 6))) -gboolean write_config_option_newline (int fd, - gboolean new_line, - NMDebugWriteFcn debug_write_fcn, - GError **error, - const char *format, ...); - -#define write_config_option(fd, debug_write_fcn, error, ...) write_config_option_newline((fd), TRUE, debug_write_fcn, error, __VA_ARGS__) - -gboolean -nm_libreswan_config_write (gint fd, - int ipsec_version, - NMConnection *connection, - const char *con_name, - const char *leftupdown_script, - gboolean openswan, - gboolean trailing_newline, - NMDebugWriteFcn debug_write_fcn, - GError **error); +char *nm_libreswan_get_ipsec_conf (int ipsec_version, + NMSettingVpn *s_vpn, + const char *con_name, + const char *leftupdown_script, + gboolean openswan, + gboolean trailing_newline, + GError **error); static inline gboolean nm_libreswan_utils_setting_is_ikev2 (NMSettingVpn *s_vpn, const char **out_ikev2) diff --git a/src/nm-libreswan-service.c b/src/nm-libreswan-service.c index e5956af..35f602c 100644 --- a/src/nm-libreswan-service.c +++ b/src/nm-libreswan-service.c @@ -101,12 +101,13 @@ typedef struct { const char *whack_path; char *secrets_path; + char *ipsec_conf; + gboolean openswan; gboolean interactive; gboolean pending_auth; gboolean managed; gboolean xauth_enabled; - int version; GPid pid; guint watch_id; @@ -152,12 +153,6 @@ _LOGD_enabled (void) #define _LOGW(...) _NMLOG(LOG_WARNING, __VA_ARGS__) #define _LOGE(...) _NMLOG(LOG_EMERG, __VA_ARGS__) -static void -_debug_write_option (const char *setting) -{ - _LOGD ("Config %s", setting); -} - /****************************************************************/ static gboolean pr_cb (GIOChannel *source, GIOCondition condition, gpointer user_data); @@ -666,9 +661,9 @@ nm_libreswan_config_psk_write (NMSettingVpn *s_vpn, GError **error) { const char *pw_type, *psk, *leftid, *right; - int fd; - int errsv; - gboolean success; + gs_free const char *secrets = NULL; + mode_t old_mask; + gboolean res; /* Check for ignored group password */ pw_type = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_PSK_INPUT_MODES); @@ -679,47 +674,32 @@ nm_libreswan_config_psk_write (NMSettingVpn *s_vpn, if (!psk) return TRUE; - /* Write the PSK */ - errno = 0; - fd = open (secrets_path, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); - if (fd < 0) { - errsv = errno; - - if (errsv == ENOENT) { - gs_free char *dirname = g_path_get_dirname (secrets_path); - - if (!g_file_test (dirname, G_FILE_TEST_IS_DIR)) { - g_set_error (error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED, - "Failed to open secrets file: no directory %s", - dirname); - return FALSE; - } - } - - g_set_error (error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED, - "Failed to open secrets file: (%d) %s.", - errsv, g_strerror (errsv)); - return FALSE; - } - leftid = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTID); if (leftid) { - success = write_config_option (fd, NULL, error, "@%s: PSK \"%s\"", leftid, psk); + if (strchr (leftid, '"') || strchr (leftid, '\n')) { + g_set_error_literal (error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_INVALID_CONNECTION, + _("Invalid character in password.")); + return FALSE; + } + secrets = g_strdup_printf ("@%s: PSK \"%s\"", leftid, psk); } else { right = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_RIGHT); - g_assert (right); - success = write_config_option (fd, NULL, error, "%s %%any: PSK \"%s\"", right, psk); + + /* nm_libreswan_get_ipsec_conf() in _connect_common should've check these. */ + g_return_val_if_fail (right != NULL, FALSE); + g_return_val_if_fail (strchr (right, '"') == NULL, FALSE); + g_return_val_if_fail (strchr (right, '\n') == NULL, FALSE); + + secrets = g_strdup_printf ("%s %%any: PSK \"%s\"", right, psk); } - if (!success) { - g_close (fd, NULL); - return FALSE; - } - return g_close (fd, error); + + old_mask = umask (S_IRWXG | S_IRWXO); + res = g_file_set_contents (secrets_path, secrets, -1, error); + umask (old_mask); + return res; } /****************************************************************/ @@ -1766,6 +1746,44 @@ done: return success ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE; } +static gboolean +write_config (int fd, + const char *string, + GError **error) +{ + const char *p; + gsize l; + int errsv; + gssize w; + + _LOGD ("Config %s", string); + + l = strlen (string); + p = string; + while (true) { + w = write (fd, p, l); + if (w == l) + return TRUE; + if (w > 0) { + g_assert (w < l); + p += w; + l -= w; + continue; + } + if (w == 0) { + errsv = EIO; + break; + } + errsv = errno; + if (errsv == EINTR) + continue; + break; + } + g_set_error (error, NMV_EDITOR_PLUGIN_ERROR, NMV_EDITOR_PLUGIN_ERROR, + _("Error writing config: %s"), g_strerror (errsv)); + return FALSE; +} + static gboolean connect_step (NMLibreswanPlugin *self, GError **error) { @@ -1848,37 +1866,12 @@ connect_step (NMLibreswanPlugin *self, GError **error) return TRUE; case CONNECT_STEP_CONFIG_ADD: { - gboolean trailing_newline; - gs_free char *bus_name = NULL; - gs_free char *ifupdown_script = NULL; if (!do_spawn (self, &priv->pid, &fd, NULL, error, priv->ipsec_path, "auto", "--replace", "--config", "-", uuid, NULL)) return FALSE; priv->watch_id = g_child_watch_add (priv->pid, child_watch_cb, self); - g_object_get (self, NM_VPN_SERVICE_PLUGIN_DBUS_SERVICE_NAME, &bus_name, NULL); - - /* openswan requires a terminating \n (otherwise it segfaults) while - * libreswan fails parsing the configuration if you include the \n. - * WTF? - */ - trailing_newline = priv->openswan; - - ifupdown_script = g_strdup_printf ("\"%s %d %ld %s\"", - NM_LIBRESWAN_HELPER_PATH, - LOG_DEBUG, - (long) getpid (), - bus_name); - - if (!nm_libreswan_config_write (fd, - priv->version, - priv->connection, - uuid, - ifupdown_script, - priv->openswan, - trailing_newline, - _debug_write_option, - error)) { + if (!write_config (fd, priv->ipsec_conf, error)) { g_close (fd, NULL); return FALSE; } @@ -1928,19 +1921,31 @@ _connect_common (NMVpnServicePlugin *plugin, NMSettingVpn *s_vpn; const char *con_name = nm_connection_get_uuid (connection); gs_free char *ipsec_banner = NULL; + gs_free char *ifupdown_script = NULL; + gs_free char *bus_name = NULL; + gboolean trailing_newline; + int version; if (_LOGD_enabled ()) { _LOGD ("connection:"); nm_connection_dump (connection); } + if (priv->connect_step != CONNECT_STEP_FIRST) { + g_set_error_literal (error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED, + "Already connecting!"); + return FALSE; + } + priv->ipsec_path = nm_libreswan_find_helper_bin ("ipsec", error); if (!priv->ipsec_path) return FALSE; - nm_libreswan_detect_version (priv->ipsec_path, &priv->openswan, &priv->version, &ipsec_banner); + nm_libreswan_detect_version (priv->ipsec_path, &priv->openswan, &version, &ipsec_banner); _LOGD ("ipsec: version banner: %s", ipsec_banner); - _LOGD ("ipsec: detected version %d (%s)", priv->version, priv->openswan ? "Openswan" : "Libreswan"); + _LOGD ("ipsec: detected version %d (%s)", version, priv->openswan ? "Openswan" : "Libreswan"); if (!priv->openswan) { priv->pluto_path = nm_libreswan_find_helper_libexec ("pluto", error); @@ -1960,13 +1965,31 @@ _connect_common (NMVpnServicePlugin *plugin, if (!nm_libreswan_secrets_validate (s_vpn, error)) return FALSE; - if (priv->connect_step != CONNECT_STEP_FIRST) { - g_set_error_literal (error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED, - "Already connecting!"); + g_object_get (self, NM_VPN_SERVICE_PLUGIN_DBUS_SERVICE_NAME, &bus_name, NULL); + + ifupdown_script = g_strdup_printf ("%s %d %ld %s", + NM_LIBRESWAN_HELPER_PATH, + LOG_DEBUG, + (long) getpid (), + bus_name); + + /* openswan requires a terminating \n (otherwise it segfaults) while + * libreswan fails parsing the configuration if you include the \n. + * WTF? + */ + trailing_newline = priv->openswan; + + /* Compose the ipsec.conf early, to catch configuration errors before + * we initiate the conneciton. */ + priv->ipsec_conf = nm_libreswan_get_ipsec_conf (version, + s_vpn, + con_name, + ifupdown_script, + priv->openswan, + trailing_newline, + error); + if (priv->ipsec_conf == NULL) return FALSE; - } /* XAUTH is not part of the IKEv2 standard and we always enforce it in IKEv1 */ priv->xauth_enabled = !nm_libreswan_utils_setting_is_ikev2 (s_vpn, NULL); @@ -2141,6 +2164,7 @@ real_disconnect (NMVpnServicePlugin *plugin, GError **error) priv->watch_id = g_child_watch_add (priv->pid, child_watch_cb, plugin); g_clear_object (&priv->connection); + g_clear_pointer (&priv->ipsec_conf, g_free); return ret; } @@ -2173,6 +2197,7 @@ finalize (GObject *object) { NMLibreswanPluginPrivate *priv = NM_LIBRESWAN_PLUGIN_GET_PRIVATE (object); + g_clear_pointer (&priv->ipsec_conf, g_free); delete_secrets_file (NM_LIBRESWAN_PLUGIN (object)); connect_cleanup (NM_LIBRESWAN_PLUGIN (object)); g_clear_object (&priv->connection); -- 2.46.0 From 8cbc188222d6a3dcff7ed937d44415f75e34b503 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 24 Sep 2024 10:55:02 +0200 Subject: [PATCH 6/6] shared/test-utils: add more test cases Test ipsec.conf formatting more thoroughly, include negative cases. [lkundrak@v3.sk: Backported from 1.24.0] --- shared/test-utils.c | 82 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/shared/test-utils.c b/shared/test-utils.c index 49aa32a..0a92d2b 100644 --- a/shared/test-utils.c +++ b/shared/test-utils.c @@ -2,6 +2,8 @@ #include "utils.h" +#include "nm-utils/nm-shared-utils.h" + static void test_config_write (void) { @@ -57,6 +59,86 @@ test_config_write (void) " rightmodecfgserver=yes\n" " modecfgpull=yes\n"); g_free (str); + + s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ()); + nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "ikev2", "insist"); + nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "leftrsasigkey", "hello"); + nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "rightrsasigkey", "world"); + nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "right", "11.12.13.14"); + str = nm_libreswan_get_ipsec_conf (4, s_vpn, "conn", NULL, FALSE, TRUE, &error); + g_assert_no_error (error); + g_assert_cmpstr (str, ==, + "conn conn\n" + " ikev2=insist\n" + " right=11.12.13.14\n" + " leftrsasigkey=\"hello\"\n" + " rightrsasigkey=\"world\"\n" + " left=%defaultroute\n" + " leftmodecfgclient=yes\n" + " rightsubnet=0.0.0.0/0\n" + " keyingtries=1\n" + " rekey=yes\n" + " rightmodecfgserver=yes\n" + " modecfgpull=yes\n"); + g_free (str); + + + s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ()); + nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "right", "11.12.13.14"); + str = nm_libreswan_get_ipsec_conf (3, s_vpn, + "my_con", + "/foo/bar/ifupdown hello 123 456", + TRUE, FALSE, &error); + g_assert_no_error (error); + g_assert_cmpstr (str, ==, + "conn my_con\n" + " auto=add\n" + " nm-configured=yes\n" + " leftupdown=\"/foo/bar/ifupdown hello 123 456\"\n" + " ikev2=never\n" + " right=11.12.13.14\n" + " authby=secret\n" + " left=%defaultroute\n" + " leftmodecfgclient=yes\n" + " rightsubnet=0.0.0.0/0\n" + " leftxauthclient=yes\n" + " remote_peer_type=cisco\n" + " rightxauthserver=yes\n" + " ikelifetime=24h\n" + " salifetime=24h\n" + " keyingtries=1\n" + " rekey=yes\n" + " rightmodecfgserver=yes\n" + " modecfgpull=yes"); + g_free (str); + + s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ()); + str = nm_libreswan_get_ipsec_conf (4, s_vpn, "conn", NULL, FALSE, TRUE, &error); + g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT); + g_assert_null (str); + g_clear_error (&error); + + s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ()); + nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "right", "11.12.13.14"); + nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "ikev2", "hello world"); + str = nm_libreswan_get_ipsec_conf (4, s_vpn, "conn", NULL, FALSE, TRUE, &error); + g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT); + g_assert_null (str); + g_clear_error (&error); + + s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ()); + nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "right", "11.12\n13.14"); + str = nm_libreswan_get_ipsec_conf (4, s_vpn, "conn", NULL, FALSE, TRUE, &error); + g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT); + g_assert_null (str); + g_clear_error (&error); + + s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ()); + nm_setting_vpn_add_data_item (NM_SETTING_VPN(s_vpn), "rightcert", "\"cert\""); + str = nm_libreswan_get_ipsec_conf (4, s_vpn, "conn", NULL, FALSE, TRUE, &error); + g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT); + g_assert_null (str); + g_clear_error (&error); } int -- 2.46.0