From f3c6f38f3be353c5371c33abc971a451c1d24922 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Thu, 10 Oct 2024 00:04:18 +0200 Subject: [PATCH 1/3] shared: make ipsec.conf formatting declarative At this point it is obvious that being clever about adding properties that the user didn't specify was not the greatest idea. nm_libreswan_get_ipsec_conf() has became a sad and miserable mess. This attempts to untangle it. The idea is to have a declarative description of the properties, along with a routine that generates them if unspecified and flags that specify how is it formatted. This hopefully breaks the logic down into managable pieces while keeping nm_libreswan_get_ipsec_conf() reasonably simple. The data, along with the routine that produces a full (canonical, sanitized) configuration is kept separate, because it could be used on import as well (a follow-up commit), chopping of annother bit that has grown too complicated. Moreover it does validation as well, so separate nm_ls_*_validate() could be dropped off. Yes, ha ha ha, *yes*! https://gitlab.gnome.org/GNOME/NetworkManager-libreswan/-/merge_requests/38 --- configure.ac | 6 +- shared/test-utils.c | 63 ++- shared/utils.c | 767 +++++++++++++++++++------------------ shared/utils.h | 7 +- src/nm-libreswan-service.c | 4 +- 5 files changed, 441 insertions(+), 406 deletions(-) diff --git a/configure.ac b/configure.ac index 0b6ad7e..c51c1c1 100644 --- a/configure.ac +++ b/configure.ac @@ -145,9 +145,9 @@ 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) -LIBNM_CFLAGS="$LIBNM_CFLAGS -DNM_VERSION_MIN_REQUIRED=NM_VERSION_1_2" -LIBNM_CFLAGS="$LIBNM_CFLAGS -DNM_VERSION_MAX_ALLOWED=NM_VERSION_1_2" +PKG_CHECK_MODULES(LIBNM, libnm >= 1.12.0) +LIBNM_CFLAGS="$LIBNM_CFLAGS -DNM_VERSION_MIN_REQUIRED=NM_VERSION_1_12" +LIBNM_CFLAGS="$LIBNM_CFLAGS -DNM_VERSION_MAX_ALLOWED=NM_VERSION_1_12" NM_VPN_SERVICE_DIR=`$PKG_CONFIG --define-variable prefix='\${prefix}' --variable vpnservicedir libnm` AC_SUBST(NM_VPN_SERVICE_DIR) diff --git a/shared/test-utils.c b/shared/test-utils.c index 36acc2c..137b0a2 100644 --- a/shared/test-utils.c +++ b/shared/test-utils.c @@ -40,19 +40,45 @@ test_config_write (void) "conn con_name\n" " ikev2=never\n" " right=11.12.13.14\n" - " authby=secret\n" " left=%defaultroute\n" " leftmodecfgclient=yes\n" + " authby=secret\n" + " ikelifetime=24h\n" + " salifetime=24h\n" " rightsubnet=0.0.0.0/0\n" + " rekey=yes\n" + " keyingtries=1\n" " leftxauthclient=yes\n" - " remote-peer-type=cisco\n" " rightxauthserver=yes\n" + " remote-peer-type=cisco\n" + " rightmodecfgserver=yes\n" + " modecfgpull=yes\n"); + g_free (str); + g_object_unref (s_vpn); + + s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ()); + nm_setting_vpn_add_data_item (s_vpn, "right", "11.12.13.14"); + nm_setting_vpn_add_data_item (s_vpn, "dhgroup", "ignored"); + 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" + " left=%defaultroute\n" + " leftmodecfgclient=yes\n" + " authby=secret\n" " ikelifetime=24h\n" " salifetime=24h\n" - " keyingtries=1\n" + " rightsubnet=0.0.0.0/0\n" " rekey=yes\n" + " keyingtries=1\n" + " leftxauthclient=yes\n" + " rightxauthserver=yes\n" + " remote-peer-type=cisco\n" " rightmodecfgserver=yes\n" " modecfgpull=yes\n"); + g_free (str); g_object_unref (s_vpn); @@ -71,13 +97,14 @@ test_config_write (void) " right=11.12.13.14\n" " leftid=%fromcert\n" " leftcert=\"LibreswanClient\"\n" - " leftrsasigkey=\"%cert\"\n" " rightrsasigkey=\"%cert\"\n" + " leftrsasigkey=\"%cert\"\n" " left=%defaultroute\n" " leftmodecfgclient=yes\n" " rightsubnet=0.0.0.0/0\n" - " keyingtries=1\n" " rekey=yes\n" + " phase2alg=aes256-sha1\n" + " keyingtries=1\n" " rightmodecfgserver=yes\n" " modecfgpull=yes\n"); g_free (str); @@ -94,13 +121,14 @@ test_config_write (void) "conn conn\n" " ikev2=insist\n" " right=11.12.13.14\n" - " leftrsasigkey=\"hello\"\n" " rightrsasigkey=\"world\"\n" + " leftrsasigkey=\"hello\"\n" " left=%defaultroute\n" " leftmodecfgclient=yes\n" " rightsubnet=0.0.0.0/0\n" - " keyingtries=1\n" " rekey=yes\n" + " phase2alg=aes256-sha1\n" + " keyingtries=1\n" " rightmodecfgserver=yes\n" " modecfgpull=yes\n"); g_free (str); @@ -115,25 +143,26 @@ test_config_write (void) 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" + " authby=secret\n" " ikelifetime=24h\n" " salifetime=24h\n" - " keyingtries=1\n" + " rightsubnet=0.0.0.0/0\n" " rekey=yes\n" + " keyingtries=1\n" + " leftxauthclient=yes\n" + " rightxauthserver=yes\n" + " remote_peer_type=cisco\n" " rightmodecfgserver=yes\n" - " modecfgpull=yes"); + " modecfgpull=yes\n" + " leftupdown=\"/foo/bar/ifupdown hello 123 456\"\n" + " auto=add\n" + " nm-configured=yes"); g_free (str); + g_object_unref (s_vpn); s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ()); str = nm_libreswan_get_ipsec_conf (4, s_vpn, "conn", NULL, FALSE, TRUE, &error); diff --git a/shared/utils.c b/shared/utils.c index 908d341..aaeaf98 100644 --- a/shared/utils.c +++ b/shared/utils.c @@ -18,7 +18,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2010 - 2015 Red Hat, Inc. + * Copyright (C) 2010 - 2024 Red Hat, Inc. */ #include "nm-default.h" @@ -30,448 +30,457 @@ #include #include -static gboolean -append_printable_val (GString *str, const char *val, GError **error) +enum LibreswanParamFlags { + PARAM_PRINTABLE = 0x0001, /* No quotes, line breaks or whitespace. */ + PARAM_STRING = 0x0002, /* Same as above, except with spaces. */ + PARAM_SYNTHETIC = 0x0004, /* Not configurable, inferred from other options. */ + PARAM_REQUIRED = 0x0008, /* Mandatory parameter. */ + PARAM_OLD = 0x0010, /* Only include for libreswan < 4. */ + PARAM_NEW = 0x0020, /* Only include for libreswan >= 4. */ + PARAM_IGNORE = 0x0040, /* Not passed to or from Libreswan. */ +}; + +struct LibreswanParam { + const char *name; + void (*add_sanitized) (NMSettingVpn *s_vpn, const char *key, const char *val); + enum LibreswanParamFlags flags; +}; + +static void +add (NMSettingVpn *s_vpn, const char *key, const char *val) { - const char *p; - - g_return_val_if_fail (val, FALSE); - - 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; - } - - g_string_append (str, val); - g_string_append_c (str, '\n'); - return TRUE; + /* Check redundant since NM 1.24 */ + if (val == NULL || val[0] == '\0') + return; + nm_setting_vpn_add_data_item (s_vpn, key, val); } -static gboolean -append_string_val (GString *str, const char *val, GError **error) +static void +add_ikev2 (NMSettingVpn *s_vpn, const char *key, const char *val) { - const char *p; + /* + * 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. + */ + if (val == NULL || val[0] == '\0') + val = NM_LIBRESWAN_IKEV2_NEVER; + nm_setting_vpn_add_data_item (s_vpn, key, val); +} - g_return_val_if_fail (val, FALSE); +static void +add_id (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + gs_free char *new = NULL; - 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; + if (val == NULL || val[0] == '\0') + return; + if ( val[0] == '@' || val[0] == '%' + || nm_utils_parse_inaddr_bin (AF_UNSPEC, val, NULL)) { + nm_setting_vpn_add_data_item (s_vpn, key, val); + } else { + new = g_strdup_printf ("@%s", val); + nm_setting_vpn_add_data_item (s_vpn, key, new); } - - g_string_append_printf (str, "\"%s\"\n", val); - return TRUE; } -static inline gboolean -append_optional_string_val (GString *str, const char *key, const char *val, - GError **error) +static void +add_leftrsasigkey (NMSettingVpn *s_vpn, const char *key, const char *val) { - if (val == NULL || val[0] == '\0') - return TRUE; - g_string_append_c (str, ' '); - g_string_append (str, key); - g_string_append_c (str, '='); - - if (!append_string_val (str, val, error)) { - g_prefix_error (error, _("Invalid value for '%s': "), key); - return FALSE; + if (val == NULL || val[0] == '\0') { + if (nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTCERT) == NULL) + return; + val = "%cert"; } + nm_setting_vpn_add_data_item (s_vpn, key, val); +} - return TRUE; +static void +add_rightrsasigkey (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + if (val == NULL || val[0] == '\0') { + if ( nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTCERT) == NULL + && nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_RIGHTCERT) == NULL) + return; + val = "%cert"; + } + nm_setting_vpn_add_data_item (s_vpn, key, val); } -static inline gboolean -append_optional_printable_val (GString *str, const char *key, const char *val, - GError **error) +static void +add_left (NMSettingVpn *s_vpn, const char *key, const char *val) { if (val == NULL || val[0] == '\0') - return TRUE; + val = "%defaultroute"; + nm_setting_vpn_add_data_item (s_vpn, key, val); +} - g_string_append_c (str, ' '); - g_string_append (str, key); - g_string_append_c (str, '='); +static void +add_leftmodecfgclient (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + if (g_strcmp0 (val, "no") != 0) + val = "yes"; + nm_setting_vpn_add_data_item (s_vpn, key, val); +} - if (!append_printable_val (str, val, error)) { - g_prefix_error (error, _("Invalid value for '%s': "), key); - return FALSE; - } +static void +add_authby (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + if ( nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTRSASIGKEY) != NULL + || nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_RIGHTRSASIGKEY) != NULL) + return; + nm_setting_vpn_add_data_item (s_vpn, key, "secret"); +} - return TRUE; +static void +add_pfs (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + if (g_strcmp0 (val, "no") != 0) + return; + nm_setting_vpn_add_data_item (s_vpn, key, val); } -static inline gboolean -append_optional_printable (GString *str, NMSettingVpn *s_vpn, const char *key, - GError **error) +static void +add_rekey (NMSettingVpn *s_vpn, const char *key, const char *val) { - return append_optional_printable_val (str, - key, - nm_setting_vpn_get_data_item (s_vpn, key), - error); + if (val == NULL || val[0] == '\0') { + val = "yes"; + /* + * keyingtries=1 used to be added when rekey defaulted to "yes", + * but not when it was set explicitly. I have no idea why. + * Keeping the behavior as is, even though it's criminally ugly. + */ + nm_setting_vpn_add_data_item (s_vpn, "keyingtries", "1"); + } + nm_setting_vpn_add_data_item (s_vpn, key, val); } -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 void +add_keyingtries (NMSettingVpn *s_vpn, const char *key, const char *val) { - 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; - const char *leftrsasigkey; - const char *rightrsasigkey; - const char *authby; - const char *local_network; - const char *remote_network; - const char *ikev2 = NULL; - const char *rightid; - const char *rekey; - const char *pfs; - const char *client_family; - const char *item; - gboolean is_ikev2 = FALSE; + /* Synthetic only. See above. */ +} - g_return_val_if_fail (NM_IS_SETTING_VPN (s_vpn), NULL); - g_return_val_if_fail (!error || !*error, NULL); - g_return_val_if_fail (con_name && *con_name, NULL); +static void +add_rightsubnet (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + const char *leftsubnet; + const char *af; - ipsec_conf = g_string_sized_new (1024); - g_string_append (ipsec_conf, "conn "); - if (!append_printable_val (ipsec_conf, con_name, error)) { - g_prefix_error (error, _("Bad connection name: ")); - return FALSE; + if (val == NULL || val[0] == '\0') { + af = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_CLIENTADDRFAMILY); + if (g_strcmp0 (af, "ipv6") == 0) + val = "::/0"; } - - 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 (!append_string_val (ipsec_conf, leftupdown_script, error)) - g_return_val_if_reached (FALSE); + if (val == NULL || val[0] == '\0') { + leftsubnet = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LOCALNETWORK); + if (leftsubnet && nm_utils_parse_inaddr_prefix_bin (AF_INET6, leftsubnet, NULL, NULL)) + val = "::/0"; } + if (val == NULL || val[0] == '\0') { + val = "0.0.0.0/0"; + } + nm_setting_vpn_add_data_item (s_vpn, key, val); +} + +static void +add_yes (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + nm_setting_vpn_add_data_item (s_vpn, key, "yes"); +} + +static void +add_cisco_unity (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + if (g_strcmp0 (nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_VENDOR), "Cisco") != 0) + return; + add_yes (s_vpn, key, NULL); +} - /* 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. +static void +add_ike (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + /* + * 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 phase1 proposal must be just one; + * moreover no more than 4 proposal may be specified. So, when + * IKEv1 aggressive mode ('leftid' specified) is configured force + * the best proposal that should be accepted by all obsolete VPN + * SW/HW acting as a remote access VPN server. */ - 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 (!append_printable_val (ipsec_conf, ikev2, error)) { - g_prefix_error (error, _("Invalid value for '%s': "), "ikev2"); - return FALSE; + if (val == NULL || val[0] == '\0') { + if ( nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTID) + && !nm_libreswan_utils_setting_is_ikev2 (s_vpn)) + val = NM_LIBRESWAN_AGGRMODE_DEFAULT_IKE; } + add (s_vpn, key, val); +} - 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 (!append_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; +static void +add_phase2alg (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + if (val == NULL || val[0] == '\0') + val = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_ESP); + if (val == NULL || val[0] == '\0') { + if (nm_libreswan_utils_setting_is_ikev2 (s_vpn)) + val = NM_LIBRESWAN_AGGRMODE_DEFAULT_ESP; } + nm_setting_vpn_add_data_item (s_vpn, key, val); +} - leftid = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTID); - if (leftid && leftid[0] != '\0') { - if (!is_ikev2) - g_string_append (ipsec_conf, " aggrmode=yes\n"); - - if ( leftid[0] == '%' - || leftid[0] == '@' - || nm_utils_parse_inaddr_bin (AF_UNSPEC, leftid, NULL)) { - g_string_append (ipsec_conf, " leftid="); - } else - g_string_append (ipsec_conf, " leftid=@"); - if (!append_printable_val (ipsec_conf, leftid, error)) { - g_prefix_error (error, _("Invalid value for '%s': "), - NM_LIBRESWAN_KEY_LEFTID); - return FALSE; - } +static void +add_lifetime (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + if (val == NULL || val[0] == '\0') { + if (!nm_libreswan_utils_setting_is_ikev2 (s_vpn)) + val = "24h"; } + add (s_vpn, key, val); +} - if (!append_optional_printable (ipsec_conf, s_vpn, - NM_LIBRESWAN_KEY_HOSTADDRFAMILY, error)) { - return FALSE; - } +static void +add_ikev1 (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + if (nm_libreswan_utils_setting_is_ikev2 (s_vpn)) + return; + add (s_vpn, key, val); +} - client_family = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_CLIENTADDRFAMILY); - if (client_family && client_family[0] != '\0') { - g_string_append (ipsec_conf, " clientaddrfamily="); - if (!append_printable_val (ipsec_conf, client_family, error)) { - g_prefix_error (error, _("Invalid value for '%s': "), - NM_LIBRESWAN_KEY_CLIENTADDRFAMILY); - return FALSE; - } - } +static void +add_ikev1_yes (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + add_ikev1 (s_vpn, key, "yes"); +} - 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 && rightcert[0] != '\0') { - g_string_append (ipsec_conf, " rightcert="); - if (!append_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 && leftcert[0] != '\0') { - g_string_append (ipsec_conf, " leftcert="); - if (!append_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 (!append_optional_string_val (ipsec_conf, NM_LIBRESWAN_KEY_LEFTRSASIGKEY, - leftrsasigkey, error)) { - return FALSE; - } - if (!append_optional_string_val (ipsec_conf, NM_LIBRESWAN_KEY_RIGHTRSASIGKEY, - rightrsasigkey, error)) { - return FALSE; - } - if (authby == NULL || authby[0] == '\0') { - if ( !(leftrsasigkey && leftrsasigkey[0] != '\0') - && !(rightrsasigkey && rightrsasigkey[0] != '\0')) { - authby = "secret"; +static void +add_remote_peer_type (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + add_ikev1 (s_vpn, key, "cisco"); +} + +static void +add_aggrmode (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + if (nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTID) == NULL) + return; + add_ikev1_yes (s_vpn, key, NULL); +} + +static void +add_username (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + if (val == NULL || val[0] == '\0') + val = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTXAUTHUSER); + if (val == NULL || val[0] == '\0') + val = nm_setting_vpn_get_user_name (s_vpn); + add_ikev1 (s_vpn, key, val); +} + + +static const struct LibreswanParam params[] = { + { NM_LIBRESWAN_KEY_IKEV2, add_ikev2, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_RIGHT, add, PARAM_PRINTABLE | PARAM_REQUIRED }, + { NM_LIBRESWAN_KEY_LEFTID, add_id, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_RIGHTID, add_id, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_LEFTCERT, add, PARAM_STRING }, + { NM_LIBRESWAN_KEY_RIGHTCERT, add, PARAM_STRING }, + { NM_LIBRESWAN_KEY_RIGHTRSASIGKEY, add_rightrsasigkey, PARAM_STRING }, + { NM_LIBRESWAN_KEY_LEFTRSASIGKEY, add_leftrsasigkey, PARAM_STRING }, + { NM_LIBRESWAN_KEY_LEFT, add_left, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_LEFTMODECFGCLIENT, add_leftmodecfgclient, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_AUTHBY, add_authby, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_PFS, add_pfs, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_IKE, add_ike, PARAM_PRINTABLE }, + + { NM_LIBRESWAN_KEY_IKELIFETIME, add_lifetime, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_SALIFETIME, add_lifetime, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_HOSTADDRFAMILY, add, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_CLIENTADDRFAMILY, add, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_LOCALNETWORK, add, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_REMOTENETWORK, add_rightsubnet, PARAM_PRINTABLE }, + + { NM_LIBRESWAN_KEY_LEFTXAUTHUSER, add_username, PARAM_STRING | PARAM_OLD }, + { NM_LIBRESWAN_KEY_LEFTUSERNAME, add_username, PARAM_STRING | PARAM_NEW }, + + { NM_LIBRESWAN_KEY_NARROWING, add, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_FRAGMENTATION, add, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_MOBIKE, add, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_DPDDELAY, add, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_DPDTIMEOUT, add, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_DPDACTION, add, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_IPSEC_INTERFACE, add, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_TYPE, add, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_REQUIRE_ID_ON_CERTIFICATE, add, PARAM_PRINTABLE }, + + /* Special. */ + { NM_LIBRESWAN_KEY_REKEY, add_rekey, PARAM_PRINTABLE }, + { NM_LIBRESWAN_KEY_ESP, add }, + { "phase2alg", add_phase2alg, PARAM_PRINTABLE | PARAM_SYNTHETIC }, + { NM_LIBRESWAN_KEY_VENDOR, add }, + { "cisco-unity", add_cisco_unity, PARAM_PRINTABLE | PARAM_SYNTHETIC }, + + /* Synthetic, not stored. */ + { "keyingtries", add_keyingtries, PARAM_PRINTABLE | PARAM_SYNTHETIC }, + { "aggrmode", add_aggrmode, PARAM_PRINTABLE | PARAM_SYNTHETIC }, + { "leftxauthclient", add_ikev1_yes, PARAM_PRINTABLE | PARAM_SYNTHETIC }, + { "rightxauthserver", add_ikev1_yes, PARAM_PRINTABLE | PARAM_SYNTHETIC }, + { "remote-peer-type", add_remote_peer_type, PARAM_PRINTABLE | PARAM_SYNTHETIC | PARAM_NEW }, + { "remote_peer_type", add_remote_peer_type, PARAM_PRINTABLE | PARAM_SYNTHETIC | PARAM_OLD }, + { "rightmodecfgserver", add_yes, PARAM_PRINTABLE | PARAM_SYNTHETIC }, + { "modecfgpull", add_yes, PARAM_PRINTABLE | PARAM_SYNTHETIC }, + + /* Used internally or just ignored altogether. */ + { NM_LIBRESWAN_KEY_DOMAIN, add, PARAM_IGNORE }, + { NM_LIBRESWAN_KEY_DHGROUP, add, PARAM_IGNORE }, + { NM_LIBRESWAN_KEY_PFSGROUP, add, PARAM_IGNORE }, + { NM_LIBRESWAN_KEY_PSK_INPUT_MODES, add, PARAM_IGNORE }, + { NM_LIBRESWAN_KEY_XAUTH_PASSWORD_INPUT_MODES, add, PARAM_IGNORE }, + { NM_LIBRESWAN_KEY_PSK_VALUE "-flags", add, PARAM_IGNORE }, + { NM_LIBRESWAN_KEY_XAUTH_PASSWORD "-flags", add, PARAM_IGNORE }, + + { NULL } +}; + +static gboolean +check_val (const char *val, gboolean allow_spaces, GError **error) +{ + const char *p; + + for (p = val; *p != '\0'; p++) { + if (*p != '"' && g_ascii_isprint (*p)) { + if (allow_spaces || !g_ascii_isspace (*p)) + continue; } - } - if (!append_optional_printable_val (ipsec_conf, NM_LIBRESWAN_KEY_AUTHBY, - authby, error)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("Invalid character in '%s'"), val); return FALSE; } - left = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFT); - if (left == NULL || left[0] == '\0') - left = "%defaultroute"; - g_string_append (ipsec_conf, " left="); - if (!append_printable_val (ipsec_conf, left, error)) { - g_prefix_error (error, _("Invalid value for '%s': "), - NM_LIBRESWAN_KEY_LEFT); - return FALSE; - } + return TRUE; +} - item = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTMODECFGCLIENT); - if (nm_streq0 (item, "no")) { - g_string_append (ipsec_conf, " leftmodecfgclient=no\n"); - } else { - g_string_append (ipsec_conf, " leftmodecfgclient=yes\n"); - } +static NMSettingVpn * +sanitize_setting_vpn (NMSettingVpn *s_vpn, + GError **error) +{ + gs_unref_object NMSettingVpn *sanitized = NULL; + int handled_items = 0; + const char *val; + int i; - rightid = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_RIGHTID); - if (rightid && rightid[0] != '\0') { - if ( rightid[0] == '@' - || rightid[0] == '%' - || nm_utils_parse_inaddr_bin (AF_UNSPEC, rightid, NULL)) { - g_string_append (ipsec_conf, " rightid="); - } else { - g_string_append (ipsec_conf, " rightid=@"); - } - if (!append_printable_val (ipsec_conf, rightid, error)) { - g_prefix_error (error, _("Invalid value for '%s': "), - NM_LIBRESWAN_KEY_RIGHTID); - return FALSE; - } - } + g_return_val_if_fail (NM_IS_SETTING_VPN (s_vpn), NULL); + g_return_val_if_fail (!error || !*error, NULL); - local_network = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_LOCALNETWORK); - if (local_network) { - g_string_append (ipsec_conf, " leftsubnet="); - if (!append_printable_val (ipsec_conf, local_network, error)) { - g_prefix_error (error, _("Invalid value for '%s': "), - NM_LIBRESWAN_KEY_LOCALNETWORK); - return FALSE; + sanitized = NM_SETTING_VPN (nm_setting_vpn_new ()); + g_object_set (sanitized, + NM_SETTING_VPN_SERVICE_TYPE, NM_VPN_SERVICE_TYPE_LIBRESWAN, + NULL); + + for (i = 0; params[i].name != NULL; i++) { + val = nm_setting_vpn_get_data_item (s_vpn, params[i].name); + if (val != NULL) { + handled_items++; + } else if (params[i].flags & PARAM_REQUIRED) { + g_set_error (error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("'%s' key needs to be present"), + params[i].name); + return NULL; } + + params[i].add_sanitized (sanitized, params[i].name, val); + + val = nm_setting_vpn_get_data_item (sanitized, params[i].name); + if (val == NULL) + continue; + if (!check_val (val, params[i].flags & PARAM_STRING, error)) + return NULL; } - remote_network = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_REMOTENETWORK); - if (!remote_network || remote_network[0] == '\0') { - int addr_family = AF_UNSPEC; + if (handled_items != nm_setting_vpn_get_num_data_items (s_vpn)) { + unsigned int length; + const char **keys; - /* Detect the address family of the remote subnet. We use in order: - * 1) the "clientaddrfamily" property 2) the local network. - */ - if (nm_streq0 (client_family, "ipv4")) { - addr_family = AF_INET; - } else if (nm_streq0 (client_family, "ipv6")) { - addr_family = AF_INET6; - } else { - if ( local_network - && nm_utils_parse_inaddr_prefix_bin (AF_INET, local_network, NULL, NULL)) { - addr_family = AF_INET; - } else if (local_network - && nm_utils_parse_inaddr_prefix_bin (AF_INET6, local_network, NULL, NULL)) { - addr_family = AF_INET6; + keys = nm_setting_vpn_get_data_keys (s_vpn, &length); + for (i = 0; i < length; i++) { + if ( (params[i].flags & PARAM_SYNTHETIC) == 0 + && nm_setting_vpn_get_data_item (sanitized, keys[i])) { + continue; } - } - if (addr_family == AF_INET6) { - 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. */ - remote_network = "0.0.0.0/0"; + g_set_error (error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("property '%s' invalid or not supported"), + keys[i]); + g_free (keys); + return NULL; } - } - g_string_append (ipsec_conf, " rightsubnet="); - if (!append_printable_val (ipsec_conf, remote_network, error)) { - g_prefix_error (error, _("Invalid value for '%s': "), - NM_LIBRESWAN_KEY_REMOTENETWORK); - return FALSE; + g_free (keys); + g_return_val_if_reached (NULL); } - 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. - */ - g_string_append (ipsec_conf, " leftxauthclient=yes\n"); - - 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 (!append_string_val (ipsec_conf, username, error)) { - g_prefix_error (error, _("Invalid username: ")); - return FALSE; - } - } - - 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"); - } + return g_steal_pointer (&sanitized); +} - /* 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 - * phase1 proposal must be just one; moreover no more than 4 proposal may be - * specified. So, when IKEv1 aggressive mode ('leftid' specified) is configured - * force the best proposal that should be accepted by all obsolete VPN SW/HW - * acting as a remote access VPN server. - */ - 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 (!append_optional_string_val (ipsec_conf, NM_LIBRESWAN_KEY_IKE, phase1_alg_str, error)) - return FALSE; +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) +{ + gs_unref_object NMSettingVpn *sanitized = NULL; + nm_auto_free_gstring GString *ipsec_conf = NULL; + const char *val; + int i; - phase2_alg_str = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_ESP); - if (phase2_alg_str == NULL || phase2_alg_str[0] == '\0') { - if (!is_ikev2 && leftid) - phase2_alg_str = NM_LIBRESWAN_AGGRMODE_DEFAULT_ESP; - } - if (!append_optional_string_val (ipsec_conf, "phase2alg", phase2_alg_str, error)) - return FALSE; + g_return_val_if_fail (NM_IS_SETTING_VPN (s_vpn), NULL); + g_return_val_if_fail (!error || !*error, NULL); + g_return_val_if_fail (con_name && *con_name, NULL); - pfs = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_PFS); - if (pfs && !strcmp (pfs, "no")) - g_string_append (ipsec_conf, " pfs=no\n"); + if (!check_val (con_name, FALSE, error)) + return NULL; - 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 (!append_optional_printable_val (ipsec_conf, NM_LIBRESWAN_KEY_IKELIFETIME, - phase1_lifetime_str, error)) { - return FALSE; - } + sanitized = sanitize_setting_vpn (s_vpn, error); + if (!sanitized) + return NULL; - 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 (!append_optional_printable_val (ipsec_conf, NM_LIBRESWAN_KEY_SALIFETIME, - phase2_lifetime_str, error)) { - return FALSE; - } + ipsec_conf = g_string_sized_new (1024); + g_string_append_printf (ipsec_conf, "conn %s\n", con_name); - rekey = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_REKEY); - if (!rekey || rekey[0] == '\0') { - g_string_append (ipsec_conf, " keyingtries=1\n"); - rekey = "yes"; - } - g_string_append (ipsec_conf, " rekey="); - if (!append_printable_val (ipsec_conf, rekey, error)) { - g_prefix_error (error, _("Invalid value for '%s': "), - NM_LIBRESWAN_KEY_REKEY); - return FALSE; - } + for (i = 0; params[i].name != NULL; i++) { + val = nm_setting_vpn_get_data_item (sanitized, params[i].name); + if (val == NULL) + continue; - if (!openswan && g_strcmp0 (nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_VENDOR), "Cisco") == 0) - g_string_append (ipsec_conf, " cisco-unity=yes\n"); + if (ipsec_version >= 4 && (params[i].flags & PARAM_OLD)) + continue; + else if (ipsec_version < 4 && (params[i].flags & PARAM_NEW)) + continue; - if (!append_optional_printable (ipsec_conf, s_vpn, NM_LIBRESWAN_KEY_NARROWING, error)) - return FALSE; - if (!append_optional_printable (ipsec_conf, s_vpn, NM_LIBRESWAN_KEY_FRAGMENTATION, error)) - return FALSE; - if (!append_optional_printable (ipsec_conf, s_vpn, NM_LIBRESWAN_KEY_MOBIKE, error)) - return FALSE; - if (!append_optional_printable (ipsec_conf, s_vpn, NM_LIBRESWAN_KEY_DPDDELAY, error)) - return FALSE; - if (!append_optional_printable (ipsec_conf, s_vpn, NM_LIBRESWAN_KEY_DPDTIMEOUT, error)) - return FALSE; - if (!append_optional_printable (ipsec_conf, s_vpn, NM_LIBRESWAN_KEY_DPDACTION, error)) - return FALSE; - if (!append_optional_printable (ipsec_conf, s_vpn, NM_LIBRESWAN_KEY_IPSEC_INTERFACE, error)) - return FALSE; - if (!append_optional_printable (ipsec_conf, s_vpn, NM_LIBRESWAN_KEY_TYPE, error)) - return FALSE; - if (!append_optional_printable (ipsec_conf, s_vpn, NM_LIBRESWAN_KEY_REQUIRE_ID_ON_CERTIFICATE, error)) - return FALSE; + if (params[i].flags & PARAM_STRING) + g_string_append_printf (ipsec_conf, " %s=\"%s\"\n", params[i].name, val); + else if (params[i].flags & PARAM_PRINTABLE) + g_string_append_printf (ipsec_conf, " %s=%s\n", params[i].name, val); + } - 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'); + if (leftupdown_script) { + if (!check_val (leftupdown_script, TRUE, error)) + return NULL; + g_string_append_printf (ipsec_conf, " leftupdown=\"%s\"\n", leftupdown_script); + g_string_append (ipsec_conf, " auto=add\n"); + g_string_append (ipsec_conf, " nm-configured=yes"); + if (trailing_newline) + g_string_append_c (ipsec_conf, '\n'); + } return g_string_free (g_steal_pointer (&ipsec_conf), FALSE); } diff --git a/shared/utils.h b/shared/utils.h index 2e2450c..eda47d6 100644 --- a/shared/utils.h +++ b/shared/utils.h @@ -18,7 +18,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2010 - 2015 Red Hat, Inc. + * Copyright (C) 2010 - 2024 Red Hat, Inc. */ #ifndef __UTILS_H__ @@ -33,14 +33,11 @@ char *nm_libreswan_get_ipsec_conf (int ipsec_version, GError **error); static inline gboolean -nm_libreswan_utils_setting_is_ikev2 (NMSettingVpn *s_vpn, const char **out_ikev2) +nm_libreswan_utils_setting_is_ikev2 (NMSettingVpn *s_vpn) { const char *ikev2; ikev2 = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_IKEV2); - if (ikev2 && strlen (ikev2) && out_ikev2) - *out_ikev2 = ikev2; - return NM_IN_STRSET (ikev2, NM_LIBRESWAN_IKEV2_PROPOSE, NM_LIBRESWAN_IKEV2_YES, diff --git a/src/nm-libreswan-service.c b/src/nm-libreswan-service.c index 8a846fb..f41a714 100644 --- a/src/nm-libreswan-service.c +++ b/src/nm-libreswan-service.c @@ -1996,7 +1996,7 @@ _connect_common (NMVpnServicePlugin *plugin, 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); + priv->xauth_enabled = !nm_libreswan_utils_setting_is_ikev2 (s_vpn); if (priv->xauth_enabled) priv->password = g_strdup (nm_setting_vpn_get_secret (s_vpn, NM_LIBRESWAN_KEY_XAUTH_PASSWORD)); @@ -2082,7 +2082,7 @@ real_need_secrets (NMVpnServicePlugin *plugin, } xauth_check: - if (!nm_libreswan_utils_setting_is_ikev2 (s_vpn, NULL)) { + if (!nm_libreswan_utils_setting_is_ikev2 (s_vpn)) { pw_type = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_XAUTH_PASSWORD_INPUT_MODES); if (!pw_type || strcmp (pw_type, NM_LIBRESWAN_PW_TYPE_UNUSED)) { if (!nm_setting_vpn_get_secret (s_vpn, NM_LIBRESWAN_KEY_XAUTH_PASSWORD)) { -- GitLab From 5498c0f47040b6b3e2217fa39f530f958d40c089 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 24 Sep 2024 10:00:32 +0200 Subject: [PATCH 2/3] service: drop nm_ls_*_validate() Let's not pretend they do any actual validation -- they don't. nm_libreswan_get_ipsec_conf() and nm_libreswan_config_psk_write() now do. https://gitlab.gnome.org/GNOME/NetworkManager-libreswan/-/merge_requests/38 --- src/nm-libreswan-service.c | 172 ------------------------------------- 1 file changed, 172 deletions(-) diff --git a/src/nm-libreswan-service.c b/src/nm-libreswan-service.c index f41a714..e58bc9e 100644 --- a/src/nm-libreswan-service.c +++ b/src/nm-libreswan-service.c @@ -226,172 +226,6 @@ pr_cb (GIOChannel *source, GIOCondition condition, gpointer user_data) /****************************************************************/ -typedef struct { - const char *name; - GType type; - gint int_min; - gint int_max; -} ValidProperty; - -static ValidProperty valid_properties[] = { - { NM_LIBRESWAN_KEY_RIGHT, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_RIGHTID, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_RIGHTRSASIGKEY, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_RIGHTCERT, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_LEFT, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_LEFTID, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_LEFTXAUTHUSER, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_LEFTUSERNAME, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_LEFTRSASIGKEY, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_LEFTCERT, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_LEFTMODECFGCLIENT, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_AUTHBY, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_DOMAIN, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_DHGROUP, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_PFS, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_PFSGROUP, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_DPDACTION, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_DPDDELAY, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_DPDTIMEOUT, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_IKE, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_ESP, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_IKELIFETIME, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_SALIFETIME, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_VENDOR, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_REMOTENETWORK, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_LOCALNETWORK, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_IKEV2, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_NARROWING, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_REKEY, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_FRAGMENTATION, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_MOBIKE, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_IPSEC_INTERFACE, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_TYPE, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_HOSTADDRFAMILY, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_CLIENTADDRFAMILY, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_REQUIRE_ID_ON_CERTIFICATE, G_TYPE_STRING, 0, 0 }, - /* Ignored option for internal use */ - { NM_LIBRESWAN_KEY_PSK_INPUT_MODES, G_TYPE_NONE, 0, 0 }, - { NM_LIBRESWAN_KEY_XAUTH_PASSWORD_INPUT_MODES, G_TYPE_NONE, 0, 0 }, - { NM_LIBRESWAN_KEY_PSK_VALUE "-flags", G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_XAUTH_PASSWORD "-flags", G_TYPE_STRING, 0, 0 }, - { NULL, G_TYPE_NONE, 0, 0 } -}; - -static ValidProperty valid_secrets[] = { - { NM_LIBRESWAN_KEY_PSK_VALUE, G_TYPE_STRING, 0, 0 }, - { NM_LIBRESWAN_KEY_XAUTH_PASSWORD, G_TYPE_STRING, 0, 0 }, - { NULL, G_TYPE_NONE, 0, 0 } -}; - -typedef struct ValidateInfo { - ValidProperty *table; - GError **error; - gboolean have_items; -} ValidateInfo; - -static void -validate_one_property (const char *key, const char *value, gpointer user_data) -{ - ValidateInfo *info = (ValidateInfo *) user_data; - int i; - - if (*(info->error)) - return; - - info->have_items = TRUE; - - /* 'name' is the setting name; always allowed but unused */ - if (!strcmp (key, NM_SETTING_NAME)) - return; - - for (i = 0; info->table[i].name; i++) { - ValidProperty prop = info->table[i]; - long int tmp; - - if (strcmp (prop.name, key)) - continue; - - switch (prop.type) { - case G_TYPE_NONE: - return; /* technically valid, but unused */ - case G_TYPE_STRING: - return; /* valid */ - case G_TYPE_INT: - errno = 0; - tmp = strtol (value, NULL, 10); - if (errno == 0 && tmp >= prop.int_min && tmp <= prop.int_max) - return; /* valid */ - - g_set_error (info->error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, - "invalid integer property '%s' or out of range [%d -> %d]", - key, prop.int_min, prop.int_max); - break; - case G_TYPE_BOOLEAN: - if (!strcmp (value, "yes") || !strcmp (value, "no")) - return; /* valid */ - - g_set_error (info->error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, - "invalid boolean property '%s' (not yes or no)", - key); - break; - default: - g_set_error (info->error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, - "unhandled property '%s' type %s", - key, g_type_name (prop.type)); - break; - } - } - - /* Did not find the property from valid_properties or the type did not match */ - if (!info->table[i].name) { - g_set_error (info->error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, - "property '%s' invalid or not supported", - key); - } -} - -static gboolean -nm_libreswan_properties_validate (NMSettingVpn *s_vpn, GError **error) -{ - ValidateInfo info = { &valid_properties[0], error, FALSE }; - - nm_setting_vpn_foreach_data_item (s_vpn, validate_one_property, &info); - if (!info.have_items) { - g_set_error_literal (error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, - "No VPN configuration options."); - return FALSE; - } - - return *error ? FALSE : TRUE; -} - -static gboolean -nm_libreswan_secrets_validate (NMSettingVpn *s_vpn, GError **error) -{ - GError *validate_error = NULL; - ValidateInfo info = { &valid_secrets[0], &validate_error, FALSE }; - - nm_setting_vpn_foreach_secret (s_vpn, validate_one_property, &info); - if (validate_error) { - g_propagate_error (error, validate_error); - return FALSE; - } - return TRUE; -} - -/****************************************************************/ - static void block_quit (NMLibreswanPlugin *self) { @@ -1963,12 +1797,6 @@ _connect_common (NMVpnServicePlugin *plugin, s_vpn = nm_connection_get_setting_vpn (connection); g_assert (s_vpn); - if (!nm_libreswan_properties_validate (s_vpn, error)) - return FALSE; - - if (!nm_libreswan_secrets_validate (s_vpn, error)) - return FALSE; - g_object_get (self, NM_VPN_SERVICE_PLUGIN_DBUS_SERVICE_NAME, &bus_name, NULL); ifupdown_script = g_strdup_printf ("%s %d %ld %s", -- GitLab From baf90380d7f63c994476a365288d85a23007b915 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Thu, 10 Oct 2024 00:04:18 +0200 Subject: [PATCH 3/3] shared: make ipsec.conf parsing declarative Reuse metadata used for producing ipsec.conf fragments to drive the import of ipsec.conf fragments and ensure their soundness. This makes import much more sensible: it now deals with quoting properly, doesn't ignore errors silently. It also validates the handful of properties we decided to be smart about and generate them, but not allow the user to set them explicitly: it just checks whether what we'd generate is the same as specified in input, and happily proceed if it matches and fail otherwise. Also, cover it with tests, like a real grownup. https://gitlab.gnome.org/GNOME/NetworkManager-libreswan/-/merge_requests/38 --- properties/nm-libreswan-editor-plugin.c | 220 +------------ shared/test-utils.c | 418 ++++++++++++++++++++++++ shared/utils.c | 157 ++++++++- shared/utils.h | 4 + 4 files changed, 587 insertions(+), 212 deletions(-) diff --git a/properties/nm-libreswan-editor-plugin.c b/properties/nm-libreswan-editor-plugin.c index 8690c02..b4a1720 100644 --- a/properties/nm-libreswan-editor-plugin.c +++ b/properties/nm-libreswan-editor-plugin.c @@ -61,221 +61,25 @@ import_from_file (NMVpnEditorPlugin *self, const char *path, GError **error) { - NMConnection *connection; + gs_free char *ipsec_conf = NULL; + gs_free char *con_name = NULL; NMSettingConnection *s_con; + NMConnection *connection; NMSettingVpn *s_vpn; - GIOChannel *chan; - char *str_tmp; - int fd, errsv; - gboolean has_conn = FALSE; - gboolean has_ikev2 = FALSE; - gboolean is_ikev2 = TRUE; - gboolean is_aggrmode = FALSE; - gboolean is_default_aggr_ike = FALSE; - gboolean is_default_aggr_esp = FALSE; - gboolean is_default_ikev1_ikelifetime = FALSE; - gboolean is_default_ikev1_salifetime = FALSE; - /* - * All the booleans here are used to track if we are dealing with an IKEv1 configuration - * in aggressive mode: in IKEv1 we enforce our own defaults to libreswan for the "ikelifetime" - * and "salifetime" parameters; similarly, for IKEv1 connection in aggressive mode we enforce - * our own ike and esp chiper suites. - * Things got complicated because for IKEv2 connections we let Libreswan to pick up the default - * values: so, our defaults for IKEv1 when applied to IKEv2 connections are particular values - * that we have to add to the configuration. - * So, track when we hit default IKEv1 values and decide after having read the whole config if - * we need to track them in the NM config or not. - */ - - fd = g_open (path, O_RDONLY, 0777); - if (fd == -1) { - errsv = errno; - g_set_error (error, NMV_EDITOR_PLUGIN_ERROR, 0, - _("Can’t open file “%s”: %s"), path, g_strerror (errsv)); + + if (!g_file_get_contents (path, &ipsec_conf, NULL, error)) + return NULL; + + s_vpn = nm_libreswan_parse_ipsec_conf (ipsec_conf, &con_name, error); + if (!s_vpn) return NULL; - } - connection = nm_simple_connection_new (); s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); - s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ()); + g_object_set (s_con, NM_SETTING_CONNECTION_ID, con_name, NULL); + + connection = nm_simple_connection_new (); nm_connection_add_setting (connection, NM_SETTING (s_con)); nm_connection_add_setting (connection, NM_SETTING (s_vpn)); - g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, NM_VPN_SERVICE_TYPE_LIBRESWAN, NULL); - - chan = g_io_channel_unix_new (fd); - while (g_io_channel_read_line (chan, &str_tmp, NULL, NULL, NULL) == G_IO_STATUS_NORMAL) { - gs_free char *str = str_tmp; - - g_strstrip (str); - if (g_str_has_prefix (str, "conn ")) { - if (has_conn) { - /* only accept the first connection section */ - break; - } - has_conn = TRUE; - g_object_set (s_con, NM_SETTING_CONNECTION_ID, &str[5], NULL); - } else if (g_str_has_prefix (str, "leftid=")) { - if (str[7] == '@') - is_aggrmode = TRUE; - nm_setting_vpn_add_data_item (s_vpn, - NM_LIBRESWAN_KEY_LEFTID, - is_aggrmode ? &str[8] : &str[7]); - } else if (g_str_has_prefix (str, "rightid=")) { - nm_setting_vpn_add_data_item (s_vpn, - NM_LIBRESWAN_KEY_RIGHTID, - (str[8] == '@') ? &str[9] : &str[8]); - } else if (g_str_has_prefix (str, "ikev2=")) { - const char *ikev2 = &str[6]; - - has_ikev2 = TRUE; - if (NM_IN_STRSET (ikev2, - NM_LIBRESWAN_IKEV2_NO, - NM_LIBRESWAN_IKEV2_NEVER)) { - is_ikev2 = FALSE; - } else - is_ikev2 = TRUE; - if (!nm_streq (ikev2, NM_LIBRESWAN_IKEV2_NEVER)) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_IKEV2, ikev2); - } else if (g_str_has_prefix (str, "ike=")) { - const char *ike = &str[4]; - - if (nm_streq (ike, NM_LIBRESWAN_AGGRMODE_DEFAULT_IKE)) - is_default_aggr_ike = TRUE; - else - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_IKE, ike); - } else if (g_str_has_prefix (str, "esp=")) { - const char *esp = &str[4]; - - if (nm_streq (esp, NM_LIBRESWAN_AGGRMODE_DEFAULT_ESP)) - is_default_aggr_esp = TRUE; - else - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_ESP, esp); - } else if (g_str_has_prefix (str, "phase2alg=")) { - const char *esp = &str[10]; - - if (nm_streq (esp, NM_LIBRESWAN_AGGRMODE_DEFAULT_ESP)) - is_default_aggr_esp = TRUE; - else - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_ESP, esp); - } else if (g_str_has_prefix (str, "ikelifetime=")) { - const char *lifetime = &str[12]; - - if (nm_streq (lifetime, NM_LIBRESWAN_IKEV1_DEFAULT_LIFETIME)) - is_default_ikev1_ikelifetime = TRUE; - else - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_IKELIFETIME, lifetime); - } else if (g_str_has_prefix (str, "salifetime=")) { - const char *lifetime = &str[11]; - - if (nm_streq (lifetime, NM_LIBRESWAN_IKEV1_DEFAULT_LIFETIME)) - is_default_ikev1_salifetime = TRUE; - else - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_SALIFETIME, lifetime); - } else if (g_str_has_prefix (str, "left=")) { - if (!nm_streq (str, "left=%defaultroute")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFT, &str[5]); - } else if (g_str_has_prefix (str, "right=")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_RIGHT, &str[6]); - else if (g_str_has_prefix (str, "leftxauthusername=")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTXAUTHUSER, &str[18]); - else if (g_str_has_prefix (str, "leftusername=")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTUSERNAME, &str[13]); - else if (g_str_has_prefix (str, "leftcert=")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTCERT, &str[9]); - else if (g_str_has_prefix (str, "rightcert=")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_RIGHTCERT, &str[10]); - else if (nm_streq0 (str, "leftmodecfgclient=no")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTMODECFGCLIENT, "no"); - else if (g_str_has_prefix (str, "pfs=no")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_PFS, "no"); - else if (g_str_has_prefix (str, "cisco-unity=yes")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_VENDOR, "Cisco"); - else if (g_str_has_prefix (str, "rekey=no")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_REKEY, "no"); - else if (g_str_has_prefix (str, "narrowing=")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_NARROWING, &str[10]); - else if (g_str_has_prefix (str, "fragmentation=")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_FRAGMENTATION, &str[14]); - else if (g_str_has_prefix (str, "mobike=")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_MOBIKE, &str[7]); - else if (g_str_has_prefix (str, "dpddelay=")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_DPDDELAY, &str[9]); - else if (g_str_has_prefix (str, "dpdtimeout=")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_DPDTIMEOUT, &str[11]); - else if (g_str_has_prefix (str, "dpdaction=")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_DPDACTION, &str[10]); - else if (g_str_has_prefix (str, "ipsec-interface=")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_IPSEC_INTERFACE, &str[16]); - else if (g_str_has_prefix (str, "authby=")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_AUTHBY, &str[7]); - else if (g_str_has_prefix (str, "type=")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_TYPE, str + NM_STRLEN("type=")); - else if (g_str_has_prefix (str, "hostaddrfamily=")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_HOSTADDRFAMILY, str + NM_STRLEN("hostaddrfamily=")); - else if (g_str_has_prefix (str, "clientaddrfamily=")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_CLIENTADDRFAMILY, str + NM_STRLEN("clientaddrfamily=")); - else if (g_str_has_prefix (str, "require-id-on-certificate=")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_REQUIRE_ID_ON_CERTIFICATE, str + NM_STRLEN("require-id-on-certificate=")); - else if (g_str_has_prefix (str, "rightsubnet=")) { - if (!g_str_has_prefix (str, "rightsubnet=0.0.0.0/0")) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_REMOTENETWORK, &str[12]); - } else if (g_str_has_prefix (str, "leftsubnet=")) { - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_LOCALNETWORK, str + NM_STRLEN("leftsubnet=")); - } else if (g_str_has_prefix (str, "leftrsasigkey=")) { - if (str[14] != '%') - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_LEFTRSASIGKEY, &str[14]); - } else if (g_str_has_prefix (str, "rightrsasigkey=")) { - if (str[15] != '%') - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_RIGHTRSASIGKEY, &str[15]); - } else { - /* till we don't get an explicit ikev2 value get hints on IKE version: - * libreswan changed the default from IKEv1 to IKEv2 but older NM version - * assumed IKEv1 as default... we should guess smart if we don't get an - * explicit ikev2 value. */ - if ( !has_ikev2 - && ( nm_streq (str, "aggrmode=yes") - || nm_streq (str, "leftxauthclient=yes") - || nm_streq (str, "rightxauthserver=yes"))) { - is_ikev2 = FALSE; - } - /* unknown tokens are silently ignored. */ - } - } - g_io_channel_unref (chan); - - g_close (fd, NULL); - - if (!has_conn) { - g_set_error (error, NMV_EDITOR_PLUGIN_ERROR, NMV_EDITOR_PLUGIN_ERROR_FILE_NOT_VPN, - _("Missing “conn” section in “%s”"), path); - g_object_unref (connection); - return NULL; - } - - if (!has_ikev2 && is_ikev2) - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_IKEV2, NM_LIBRESWAN_IKEV2_YES); - - if (is_ikev2) { - is_aggrmode = FALSE; - if (is_default_ikev1_ikelifetime) { - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_IKELIFETIME, - NM_LIBRESWAN_IKEV1_DEFAULT_LIFETIME); - } - if (is_default_ikev1_salifetime) { - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_SALIFETIME, - NM_LIBRESWAN_IKEV1_DEFAULT_LIFETIME); - } - } - if (!is_aggrmode) { - if (is_default_aggr_ike) { - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_IKE, - NM_LIBRESWAN_AGGRMODE_DEFAULT_IKE); - } - if (is_default_aggr_esp) { - nm_setting_vpn_add_data_item (s_vpn, NM_LIBRESWAN_KEY_ESP, - NM_LIBRESWAN_AGGRMODE_DEFAULT_ESP); - } - } return connection; } diff --git a/shared/test-utils.c b/shared/test-utils.c index 137b0a2..6463f2a 100644 --- a/shared/test-utils.c +++ b/shared/test-utils.c @@ -197,12 +197,430 @@ test_config_write (void) g_object_unref (s_vpn); } +static void +test_config_read (void) +{ + char *con_name = NULL; + GError *error = NULL; + NMSettingVpn *s_vpn; + + /* Minimal. */ + s_vpn = nm_libreswan_parse_ipsec_conf ( + "conn conn\n" + " right=11.12.13.14\n", + &con_name, + &error); + g_assert_no_error (error); + g_assert_cmpint (nm_setting_vpn_get_num_data_items (s_vpn), ==, 9); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "authby"), ==, "secret"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "ikelifetime"), ==, "24h"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "ikev2"), ==, "never"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "left"), ==, "%defaultroute"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "leftmodecfgclient"), ==, "yes"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rekey"), ==, "yes"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "right"), ==, "11.12.13.14"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rightsubnet"), ==, "0.0.0.0/0"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "salifetime"), ==, "24h"); + g_object_unref (s_vpn); + g_clear_pointer (&con_name, g_free); + + /* Also include all generated properties. */ + s_vpn = nm_libreswan_parse_ipsec_conf ( + "conn conn\n" + " salifetime=24h\n" + " rightsubnet=0.0.0.0/0\n" + " right=11.12.13.14\n" + " rekey=yes\n" + " leftmodecfgclient=yes\n" + " left=%defaultroute\n" + " ikev2=never\n" + " ikelifetime=24h\n" + " authby=secret\n", + &con_name, + &error); + g_assert_no_error (error); + g_assert_cmpint (nm_setting_vpn_get_num_data_items (s_vpn), ==, 9); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "authby"), ==, "secret"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "ikelifetime"), ==, "24h"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "ikev2"), ==, "never"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "left"), ==, "%defaultroute"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "leftmodecfgclient"), ==, "yes"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rekey"), ==, "yes"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "right"), ==, "11.12.13.14"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rightsubnet"), ==, "0.0.0.0/0"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "salifetime"), ==, "24h"); + g_object_unref (s_vpn); + g_clear_pointer (&con_name, g_free); + + /* Include all synthetic properties with appropriate values. */ + s_vpn = nm_libreswan_parse_ipsec_conf ( + "conn xpl\n" + " ikev2=never\n" + " right=172.31.79.2\n" + " leftid=@groupname\n" + " left=%defaultroute\n" + " leftmodecfgclient=yes\n" + " authby=secret\n" + " ike=aes256-sha1;modp1536\n" + " ikelifetime=24h\n" + " salifetime=24h\n" + " rightsubnet=10.0.2.0/24\n" + " leftusername=\"username\"\n" + " rekey=yes\n" + " keyingtries=1\n" + " aggrmode=yes\n" + " leftxauthclient=yes\n" + " rightxauthserver=yes\n" + " remote-peer-type=cisco\n" + " rightmodecfgserver=yes\n" + " modecfgpull=yes\n", + &con_name, + &error); + g_assert_no_error (error); + g_assert_cmpint (nm_setting_vpn_get_num_data_items (s_vpn), ==, 12); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "authby"), ==, "secret"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "ike"), ==, "aes256-sha1;modp1536"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "ikelifetime"), ==, "24h"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "ikev2"), ==, "never"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "left"), ==, "%defaultroute"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "leftid"), ==, "@groupname"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "leftmodecfgclient"), ==, "yes"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "leftusername"), ==, "username"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rekey"), ==, "yes"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "right"), ==, "172.31.79.2"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rightsubnet"), ==, "10.0.2.0/24"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "salifetime"), ==, "24h"); + g_object_unref (s_vpn); + g_clear_pointer (&con_name, g_free); + + s_vpn = nm_libreswan_parse_ipsec_conf ( + "conn con_name\n" + " ikev2=never\n" + " right=11.12.13.14\n" + " left=%defaultroute\n" + " leftmodecfgclient=yes\n" + " authby=secret\n" + " ikelifetime=24h\n" + " salifetime=24h\n" + " rightsubnet=0.0.0.0/0\n" + " rekey=yes\n" + " keyingtries=1\n" + " leftxauthclient=yes\n" + " rightxauthserver=yes\n" + " remote-peer-type=cisco\n" + " rightmodecfgserver=yes\n" + " modecfgpull=yes\n", + &con_name, + &error); + g_assert_no_error (error); + g_assert_cmpstr (con_name, ==, "con_name"); + g_assert_cmpint (nm_setting_vpn_get_num_data_items (s_vpn), ==, 9); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "authby"), ==, "secret"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "ikelifetime"), ==, "24h"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "ikev2"), ==, "never"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "left"), ==, "%defaultroute"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "leftmodecfgclient"), ==, "yes"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rekey"), ==, "yes"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "right"), ==, "11.12.13.14"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rightsubnet"), ==, "0.0.0.0/0"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "salifetime"), ==, "24h"); + g_object_unref (s_vpn); + g_clear_pointer (&con_name, g_free); + + s_vpn = nm_libreswan_parse_ipsec_conf ( + "conn f0008435-07af-4836-a53d-b43e8730e68f\n" + " ikev2=insist\n" + " right=11.12.13.14\n" + " leftcert=\"Libreswan Client\"\n" + " rightrsasigkey=\"%cert\"\n" + " leftrsasigkey=%cert\n" + " left=%defaultroute\n" + " leftmodecfgclient=yes\n" + " rightsubnet=0.0.0.0/0\n" + " rekey=yes\n" + " phase2alg=aes256-sha1\n" + " keyingtries=1\n" + " rightmodecfgserver=yes\n" + " modecfgpull=yes\n", + &con_name, + &error); + g_assert_no_error (error); + g_assert_cmpstr (con_name, ==, "f0008435-07af-4836-a53d-b43e8730e68f"); + g_assert_cmpint (nm_setting_vpn_get_num_data_items (s_vpn), ==, 9); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "ikev2"), ==, "insist"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "left"), ==, "%defaultroute"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "leftcert"), ==, "Libreswan Client"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "leftmodecfgclient"), ==, "yes"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "leftrsasigkey"), ==, "%cert"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rekey"), ==, "yes"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "right"), ==, "11.12.13.14"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rightrsasigkey"), ==, "%cert"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rightsubnet"), ==, "0.0.0.0/0"); + g_object_unref (s_vpn); + g_clear_pointer (&con_name, g_free); + + s_vpn = nm_libreswan_parse_ipsec_conf ( + "conn conn\n" + " ikev2=insist\n" + " right=11.12.13.14\n" + " rightrsasigkey=\"world\"\n" + " leftrsasigkey=\"hello\"\n" + " left=%defaultroute\n" + " leftmodecfgclient=yes\n" + " rightsubnet=0.0.0.0/0\n" + " rekey=yes\n" + " esp=aes256-sha1\n" + " keyingtries=1\n" + " rightmodecfgserver=yes\n" + " modecfgpull=yes\n", + &con_name, + &error); + g_assert_no_error (error); + g_assert_cmpstr (con_name, ==, "conn"); + g_assert_cmpint (nm_setting_vpn_get_num_data_items (s_vpn), ==, 9); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "ikev2"), ==, "insist"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "left"), ==, "%defaultroute"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "leftmodecfgclient"), ==, "yes"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "leftrsasigkey"), ==, "hello"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rekey"), ==, "yes"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "right"), ==, "11.12.13.14"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rightrsasigkey"), ==, "world"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rightsubnet"), ==, "0.0.0.0/0"); + g_object_unref (s_vpn); + g_clear_pointer (&con_name, g_free); + + s_vpn = nm_libreswan_parse_ipsec_conf ( + "conn con_name\n" + " ikev2=never\n" + " right=11.12.13.14\n" + " left=%defaultroute\n" + " leftmodecfgclient=yes\n" + " authby=secret\n" + " ikelifetime=24h\n" + " rekey=yes\n" + " rightsubnet=0.0.0.0/0\n" + " salifetime=24h\n", + &con_name, + &error); + g_assert_no_error (error); + g_assert_cmpstr (con_name, ==, "con_name"); + g_assert_cmpint (nm_setting_vpn_get_num_data_items (s_vpn), ==, 9); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "ikev2"), ==, "never"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "right"), ==, "11.12.13.14"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "left"), ==, "%defaultroute"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "leftmodecfgclient"), ==, "yes"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "authby"), ==, "secret"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "ikelifetime"), ==, "24h"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rekey"), ==, "yes"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rightsubnet"), ==, "0.0.0.0/0"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "salifetime"), ==, "24h"); + g_object_unref (s_vpn); + g_clear_pointer (&con_name, g_free); + + s_vpn = nm_libreswan_parse_ipsec_conf ( + "# This configuration was created unded influence.\r\n" + "# Do not edit!\n\r" + "\n" + " # the # below doesn't introduce a comment \n" + "conn con#name\n" + "# comments are preceded by whitespace\n" + "\t\n" + " ikev2=never\n" + " left=\t\t%defaultroute\n" + "\t# moo" + "\tleftmodecfgclient = \t yes\n" + " authby=\"secret\"\n" + "# boo" + " ikelifetime =24h # what\n" + " \t rekey=yes\n" + "\n" + " rightsubnet=\t0.0.0.0/0\n" + " #wot\r" + " \tright=11.12.13.14\n" + " \t\tsalifetime=24h", + &con_name, + &error); + g_assert_no_error (error); + g_assert_cmpstr (con_name, ==, "con#name"); + g_assert_cmpint (nm_setting_vpn_get_num_data_items (s_vpn), ==, 9); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "ikev2"), ==, "never"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "right"), ==, "11.12.13.14"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "left"), ==, "%defaultroute"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "leftmodecfgclient"), ==, "yes"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "authby"), ==, "secret"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "ikelifetime"), ==, "24h"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rekey"), ==, "yes"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rightsubnet"), ==, "0.0.0.0/0"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "salifetime"), ==, "24h"); + g_object_unref (s_vpn); + g_clear_pointer (&con_name, g_free); + + /* Make sure properties with right synthetic values are allowed. */ + s_vpn = nm_libreswan_parse_ipsec_conf ( + "conn conn\n" + " right=11.12.13.14\n" + " vendor=Cisco\n" + " cisco-unity=yes\n", + &con_name, + &error); + g_assert_no_error (error); + g_assert_cmpint (nm_setting_vpn_get_num_data_items (s_vpn), ==, 10); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "authby"), ==, "secret"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "ikelifetime"), ==, "24h"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "ikev2"), ==, "never"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "left"), ==, "%defaultroute"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "leftmodecfgclient"), ==, "yes"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rekey"), ==, "yes"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "right"), ==, "11.12.13.14"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "rightsubnet"), ==, "0.0.0.0/0"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "salifetime"), ==, "24h"); + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "vendor"), ==, "Cisco"); + g_object_unref (s_vpn); + g_clear_pointer (&con_name, g_free); + + s_vpn = nm_libreswan_parse_ipsec_conf ( + "conn my_con\n", + &con_name, + &error); + g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT); + g_assert_null (s_vpn); + g_assert_null (con_name); + g_clear_error (&error); + + s_vpn = nm_libreswan_parse_ipsec_conf ( + " right=11.12.13.14\n", + &con_name, + &error); + g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT); + g_assert_null (s_vpn); + g_assert_null (con_name); + g_clear_error (&error); + + s_vpn = nm_libreswan_parse_ipsec_conf ( + "conn my_con\n" + "right=11.12.13.14\n", + &con_name, + &error); + g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT); + g_assert_null (s_vpn); + g_assert_null (con_name); + g_clear_error (&error); + + s_vpn = nm_libreswan_parse_ipsec_conf ( + " right=11.12.13.14\n" + "conn my_con\n", + &con_name, + &error); + g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT); + g_assert_null (s_vpn); + g_assert_null (con_name); + g_clear_error (&error); + + s_vpn = nm_libreswan_parse_ipsec_conf ( + "conn my_con\n" + " right=11.12.13.14\n" + "conn my_con\n", + &con_name, + &error); + g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT); + g_assert_null (s_vpn); + g_assert_null (con_name); + g_clear_error (&error); + + s_vpn = nm_libreswan_parse_ipsec_conf ( + "conn my_con\n" + " right=11.12.13.14\n" + " right=11.12.13.14\n", + &con_name, + &error); + g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT); + g_assert_null (s_vpn); + g_assert_null (con_name); + g_clear_error (&error); + + /* Not an actual property */ + s_vpn = nm_libreswan_parse_ipsec_conf ( + "conn my_con\n" + " right=11.12.13.14\n" + " hola=prdel\n", + &con_name, + &error); + g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT); + g_assert_null (s_vpn); + g_assert_null (con_name); + g_clear_error (&error); + + s_vpn = nm_libreswan_parse_ipsec_conf ( + "conn my_con\n" + " right=11.12.13.14\n" + " leftcert=Libreswan Client\n", + &con_name, + &error); + g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT); + g_assert_null (s_vpn); + g_assert_null (con_name); + g_clear_error (&error); + + s_vpn = nm_libreswan_parse_ipsec_conf ( + "conn my_con\n" + " ikev2=never\n" + " right=11.12.13.14\n" + " left=%defaultroute\n" + " leftmodecfgclient=yes\n" + " authby=secret\n" + " ikelifetime=24h\n" + " salifetime=24h\n" + " rightsubnet=0.0.0.0/0\n" + " rekey=yes\n" + " keyingtries=1\n" + " leftxauthclient=yes\n" + " rightxauthserver=yes\n" + " remote_peer_type=cisco\n" + " rightmodecfgserver=yes\n" + " modecfgpull=yes\n" + " leftupdown=\"/foo/bar/ifupdown hello 123 456\"\n" + " auto=add\n" + " nm-configured=yes", + &con_name, + &error); + g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT); + g_assert_null (s_vpn); + g_assert_null (con_name); + g_clear_error (&error); + + /* Make sure synthetic properties can't be overriden. */ + s_vpn = nm_libreswan_parse_ipsec_conf ( + "conn conn\n" + " right=11.12.13.14\n" + " rightmodecfgserver=no\n", + &con_name, + &error); + g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT); + g_assert_null (s_vpn); + g_assert_null (con_name); + g_clear_error (&error); + + /* Make sure properties with right synthetic values not allowed when they + * wouldn't be used. */ + s_vpn = nm_libreswan_parse_ipsec_conf ( + "conn conn\n" + " right=11.12.13.14\n" + " cisco-unity=yes\n", + &con_name, + &error); + g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT); + g_assert_null (s_vpn); + g_assert_null (con_name); + g_clear_error (&error); +} + int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/utils/config/write", test_config_write); + g_test_add_func ("/utils/config/read", test_config_read); return g_test_run (); } diff --git a/shared/utils.c b/shared/utils.c index aaeaf98..beb2719 100644 --- a/shared/utils.c +++ b/shared/utils.c @@ -408,12 +408,10 @@ sanitize_setting_vpn (NMSettingVpn *s_vpn, keys = nm_setting_vpn_get_data_keys (s_vpn, &length); for (i = 0; i < length; i++) { - if ( (params[i].flags & PARAM_SYNTHETIC) == 0 - && nm_setting_vpn_get_data_item (sanitized, keys[i])) { + if (nm_setting_vpn_get_data_item (sanitized, keys[i])) continue; - } - g_set_error (error, + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, _("property '%s' invalid or not supported"), @@ -485,6 +483,157 @@ nm_libreswan_get_ipsec_conf (int ipsec_version, return g_string_free (g_steal_pointer (&ipsec_conf), FALSE); } +/* + * The format as described in ipsec.conf(5) is fairly primitive. + * In values, no line breaks are allowed. If there's other whitespace, + * it needs to be enclosed in quote marks. Quote marks are not allowed + * elsewhere. There's no escaping of the quote marks or newlines or + * anything else. This makes it feasible to parse it with a fairly + * regexp. + */ +static const char line_match[] = + "^(?:" + "(?:conn\\s+|\\s+(\\S+)\\s*=\\s*)" /* <"conn "> or ...=... */ + "(?:\"([^\"]*)\"|(\\S+))" /* or "" */ + ")?" /* (or just blank line) */ + "\\s*(?:#.*)?$"; /* optional comment */ + +NMSettingVpn * +nm_libreswan_parse_ipsec_conf (const char *ipsec_conf, + char **out_con_name, + GError **error) +{ + gs_unref_object NMSettingVpn *sanitized = NULL; + gs_unref_object NMSettingVpn *s_vpn = NULL; + gs_strfreev char **lines = NULL; + gs_free char *con_name = NULL; + GMatchInfo *match_info = NULL; + GError *parse_error = NULL; + GRegex *line_regex; + const char *old, *new; + const char *rekey; + char *key, *val; + int i; + + g_return_val_if_fail (ipsec_conf, NULL); + g_return_val_if_fail (out_con_name && !*out_con_name, NULL); + g_return_val_if_fail (!error || !*error, NULL); + + line_regex = g_regex_new (line_match, G_REGEX_RAW, 0, NULL); + g_return_val_if_fail (line_regex, NULL); + + s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ()); + + lines = g_strsplit_set (ipsec_conf, "\r\n", -1); + for (i = 0; lines[i]; i++) { + if (!g_regex_match (line_regex, lines[i], 0, &match_info)) { + parse_error = g_error_new ( + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("'%s' not understood"), + lines[i]); + g_match_info_unref (match_info); + break; + } + + key = g_match_info_fetch (match_info, 1); /* Key */ + val = g_match_info_fetch (match_info, 2); /* Unquoted value */ + if (val[0] == '\0') { + g_free (val); + /* Quoted value (quotes stripped off) */ + val = g_match_info_fetch (match_info, 3); + } + g_match_info_unref (match_info); + + if (key[0] != '\0') { + /* key=value line */ + if (con_name == NULL) { + parse_error = g_error_new ( + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("Expected a conn line before '%s'"), + key); + } else if (nm_setting_vpn_get_data_item (s_vpn, key)) { + parse_error = g_error_new ( + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("'%s' specified multiple times"), + key); + } else { + nm_setting_vpn_add_data_item (s_vpn, key, val); + } + g_free (key); + g_free (val); + } else if (val[0] != '\0') { + /* If key didn't match, then this must be a "conn" line. */ + g_free (key); + if (con_name != NULL) { + g_free (val); + parse_error = g_error_new ( + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("'%s' specified multiple times"), + "conn"); + } else { + con_name = val; + } + } else { + /* Blank line */ + g_free (key); + g_free (val); + } + + if (parse_error) + break; + } + g_regex_unref (line_regex); + if (parse_error) { + g_propagate_error (error, parse_error); + return NULL; + } + + /* The "keyingtries" kludge. See above. */ + rekey = nm_setting_vpn_get_data_item (s_vpn, NM_LIBRESWAN_KEY_REKEY); + if ( rekey && rekey[0] != '\0' + && g_strcmp0 (nm_setting_vpn_get_data_item (s_vpn, "keyingtries"), "1") == 0) { + nm_setting_vpn_remove_data_item (s_vpn, "keyingtries"); + } + + sanitized = sanitize_setting_vpn (s_vpn, error); + if (!sanitized) + return NULL; + + g_return_val_if_fail (con_name, NULL); + + /* + * Verify that the synthetic properties are either not present in the + * original connection, or have the same value as has been synthesized, + * Then remove them. + */ + for (i = 0; params[i].name != NULL; i++) { + if ((params[i].flags & PARAM_SYNTHETIC) == 0) + continue; + + old = nm_setting_vpn_get_data_item (s_vpn, params[i].name); + if (old != NULL) { + new = nm_setting_vpn_get_data_item (sanitized, params[i].name); + if (g_strcmp0 (old, new) != 0) { + g_set_error (error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("'%s' is not supported for '%s'"), + old, params[i].name); + return NULL; + } + } + + nm_setting_vpn_remove_data_item (sanitized, params[i].name); + } + + *out_con_name = g_steal_pointer (&con_name); + return g_steal_pointer (&sanitized); +} + static const char * _find_helper (const char *progname, const char **paths, GError **error) { diff --git a/shared/utils.h b/shared/utils.h index eda47d6..0dc6d74 100644 --- a/shared/utils.h +++ b/shared/utils.h @@ -32,6 +32,10 @@ char *nm_libreswan_get_ipsec_conf (int ipsec_version, gboolean trailing_newline, GError **error); +NMSettingVpn *nm_libreswan_parse_ipsec_conf (const char *ipsec_conf, + char **con_name, + GError **error); + static inline gboolean nm_libreswan_utils_setting_is_ikev2 (NMSettingVpn *s_vpn) { -- GitLab