diff --git a/configure.ac b/configure.ac index 0b6ad7efd6e608f54f6cf855c3a98e982a0b4470..c51c1c1102094c4bcf1c7ee102fb1d7782646ccc 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/properties/nm-libreswan-editor-plugin.c b/properties/nm-libreswan-editor-plugin.c index 8690c02f0cdf0e100bf9f7f2a64f2b5859de5e09..b4a1720c979dd25e56d495361ff1f7ebdb077979 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 36acc2c661060875a01e61e373ec68d784a1ce09..6463f2a7e51aad92753b07989b3590bd366dd237 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); @@ -168,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 908d3411a9288f657b134e0a04434acf67f07fa9..beb271968d7a700bca72fcc84f931e3db95ef960 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,92 +30,400 @@ #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; + /* Check redundant since NM 1.24 */ + if (val == NULL || val[0] == '\0') + return; + nm_setting_vpn_add_data_item (s_vpn, key, val); +} - g_return_val_if_fail (val, FALSE); +static void +add_ikev2 (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + /* + * 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); +} - 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; +static void +add_id (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + gs_free char *new = NULL; + + 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 (str, val); - g_string_append_c (str, '\n'); - return TRUE; +static void +add_leftrsasigkey (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) + return; + val = "%cert"; + } + 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_rightrsasigkey (NMSettingVpn *s_vpn, const char *key, const char *val) { - const char *p; + 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); +} - g_return_val_if_fail (val, FALSE); +static void +add_left (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + if (val == NULL || val[0] == '\0') + val = "%defaultroute"; + nm_setting_vpn_add_data_item (s_vpn, key, val); +} - 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; +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); +} + +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"); +} + +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 void +add_rekey (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + 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); +} - g_string_append_printf (str, "\"%s\"\n", val); - return TRUE; +static void +add_keyingtries (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + /* Synthetic only. See above. */ } -static inline gboolean -append_optional_string_val (GString *str, const char *key, const char *val, - GError **error) +static void +add_rightsubnet (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + const char *leftsubnet; + const char *af; + + 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 (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); +} + +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. + */ + 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); +} + +static void +add_phase2alg (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, '='); + 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); +} - if (!append_string_val (str, val, error)) { - g_prefix_error (error, _("Invalid value for '%s': "), key); - 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); +} - return TRUE; +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); } -static inline gboolean -append_optional_printable_val (GString *str, const char *key, const char *val, - GError **error) +static void +add_ikev1_yes (NMSettingVpn *s_vpn, const char *key, const char *val) +{ + add_ikev1 (s_vpn, key, "yes"); +} + +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') - return TRUE; + 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); +} + - g_string_append_c (str, ' '); - g_string_append (str, key); - g_string_append_c (str, '='); +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 } +}; - if (!append_printable_val (str, val, error)) { - g_prefix_error (error, _("Invalid value for '%s': "), key); +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; + } + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("Invalid character in '%s'"), val); return FALSE; } return TRUE; } -static inline gboolean -append_optional_printable (GString *str, NMSettingVpn *s_vpn, const char *key, - GError **error) +static NMSettingVpn * +sanitize_setting_vpn (NMSettingVpn *s_vpn, + GError **error) { - return append_optional_printable_val (str, - key, - nm_setting_vpn_get_data_item (s_vpn, key), - error); + gs_unref_object NMSettingVpn *sanitized = NULL; + int handled_items = 0; + const char *val; + int i; + + g_return_val_if_fail (NM_IS_SETTING_VPN (s_vpn), NULL); + g_return_val_if_fail (!error || !*error, NULL); + + 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; + } + + if (handled_items != nm_setting_vpn_get_num_data_items (s_vpn)) { + unsigned int length; + const char **keys; + + keys = nm_setting_vpn_get_data_keys (s_vpn, &length); + for (i = 0; i < length; i++) { + if (nm_setting_vpn_get_data_item (sanitized, keys[i])) + continue; + + 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_free (keys); + g_return_val_if_reached (NULL); + } + + return g_steal_pointer (&sanitized); } char * @@ -127,353 +435,203 @@ nm_libreswan_get_ipsec_conf (int ipsec_version, gboolean trailing_newline, GError **error) { + gs_unref_object NMSettingVpn *sanitized = NULL; 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; + const char *val; + int i; 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); - 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 (!check_val (con_name, FALSE, error)) + return NULL; - 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); - } + sanitized = sanitize_setting_vpn (s_vpn, error); + if (!sanitized) + return 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. - */ - 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; - } - - 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; - } + ipsec_conf = g_string_sized_new (1024); + g_string_append_printf (ipsec_conf, "conn %s\n", con_name); - 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; - } - } + for (i = 0; params[i].name != NULL; i++) { + val = nm_setting_vpn_get_data_item (sanitized, params[i].name); + if (val == NULL) + continue; - if (!append_optional_printable (ipsec_conf, s_vpn, - NM_LIBRESWAN_KEY_HOSTADDRFAMILY, error)) { - return FALSE; - } + if (ipsec_version >= 4 && (params[i].flags & PARAM_OLD)) + continue; + else if (ipsec_version < 4 && (params[i].flags & PARAM_NEW)) + continue; - 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; - } + 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); } - 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"; - } - } - if (!append_optional_printable_val (ipsec_conf, NM_LIBRESWAN_KEY_AUTHBY, - authby, error)) { - return FALSE; + 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'); } - 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 g_string_free (g_steal_pointer (&ipsec_conf), FALSE); +} - 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"); - } +/* + * 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; - 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 (ipsec_conf, NULL); + g_return_val_if_fail (out_con_name && !*out_con_name, 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; - } - } + line_regex = g_regex_new (line_match, G_REGEX_RAW, 0, NULL); + g_return_val_if_fail (line_regex, 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; + s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ()); - /* 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; - } + 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; } - 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"; + 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_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; - } - - 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_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); } - 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"); - } - - /* 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 (parse_error) + break; } - if (!append_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 == NULL || phase2_alg_str[0] == '\0') { - if (!is_ikev2 && leftid) - phase2_alg_str = NM_LIBRESWAN_AGGRMODE_DEFAULT_ESP; + g_regex_unref (line_regex); + if (parse_error) { + g_propagate_error (error, parse_error); + return NULL; } - if (!append_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")) - 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 == 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; + /* 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"); } - 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; - } + sanitized = sanitize_setting_vpn (s_vpn, error); + if (!sanitized) + return NULL; - 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; - } + g_return_val_if_fail (con_name, NULL); - 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"); + /* + * 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; - 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; + 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; + } + } - 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'); + nm_setting_vpn_remove_data_item (sanitized, params[i].name); + } - return g_string_free (g_steal_pointer (&ipsec_conf), FALSE); + *out_con_name = g_steal_pointer (&con_name); + return g_steal_pointer (&sanitized); } static const char * diff --git a/shared/utils.h b/shared/utils.h index 2e2450c76a9fbde288b3f0122d70d28d8366233f..0dc6d741e3886fc2461943e71efe0ec2e8ce1fde 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__ @@ -32,15 +32,16 @@ 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, 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 8a846fbd43588c0ff1647fdd29b29fd6edf940bc..e58bc9e50a6c8ffdc54225d49aed1aac8293c5ba 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", @@ -1996,7 +1824,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 +1910,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)) {