ike: Add an additional but separate AEAD proposal to IKE config, if supported
[strongswan.git] / src / charon-nm / nm / nm_service.c
index 461837c..82d212d 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2013 Tobias Brunner
  * Copyright (C) 2008-2009 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
 #include "nm_service.h"
 
 #include <daemon.h>
-#include <utils/host.h>
+#include <networking/host.h>
 #include <utils/identification.h>
 #include <config/peer_cfg.h>
 #include <credentials/certificates/x509.h>
+#include <networking/tun_device.h>
 
 #include <stdio.h>
 
@@ -41,6 +43,8 @@ typedef struct {
        nm_creds_t *creds;
        /* attribute handler for DNS/NBNS server information */
        nm_handler_t *handler;
+       /* dummy TUN device */
+       tun_device_t *tun;
        /* name of the connection */
        char *name;
 } NMStrongswanPluginPrivate;
@@ -80,23 +84,33 @@ static GValue* handler_to_val(nm_handler_t *handler,
 static void signal_ipv4_config(NMVPNPlugin *plugin,
                                                           ike_sa_t *ike_sa, child_sa_t *child_sa)
 {
+       NMStrongswanPluginPrivate *priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin);
        GValue *val;
        GHashTable *config;
+       enumerator_t *enumerator;
        host_t *me;
        nm_handler_t *handler;
 
        config = g_hash_table_new(g_str_hash, g_str_equal);
-       me = ike_sa->get_my_host(ike_sa);
-       handler = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin)->handler;
+       handler = priv->handler;
 
        /* NM requires a tundev, but netkey does not use one. Passing the physical
-        * interface does not work, as NM fiddles around with it. Passing the
-        * loopback seems to work, though... */
+        * interface does not work, as NM fiddles around with it. So we pass a dummy
+        * TUN device along for NM to play with... */
        val = g_slice_new0 (GValue);
        g_value_init (val, G_TYPE_STRING);
-       g_value_set_string (val, "lo");
+       g_value_set_string (val, priv->tun->get_name(priv->tun));
        g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, val);
 
+       /* NM installs this IP address on the interface above, so we use the VIP if
+        * we got one.
+        */
+       enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE);
+       if (!enumerator->enumerate(enumerator, &me))
+       {
+               me = ike_sa->get_my_host(ike_sa);
+       }
+       enumerator->destroy(enumerator);
        val = g_slice_new0(GValue);
        g_value_init(val, G_TYPE_UINT);
        g_value_set_uint(val, *(u_int32_t*)me->get_address(me).ptr);
@@ -107,6 +121,14 @@ static void signal_ipv4_config(NMVPNPlugin *plugin,
        g_value_set_uint(val, me->get_address(me).len * 8);
        g_hash_table_insert(config, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
 
+       /* prevent NM from changing the default route. we set our own route in our
+        * own routing table
+        */
+       val = g_slice_new0(GValue);
+       g_value_init(val, G_TYPE_BOOLEAN);
+       g_value_set_boolean(val, TRUE);
+       g_hash_table_insert(config, NM_VPN_PLUGIN_IP4_CONFIG_NEVER_DEFAULT, val);
+
        val = handler_to_val(handler, INTERNAL_IP4_DNS);
        g_hash_table_insert(config, NM_VPN_PLUGIN_IP4_CONFIG_DNS, val);
 
@@ -277,7 +299,7 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection,
        auth_class_t auth_class = AUTH_CLASS_EAP;
        certificate_t *cert = NULL;
        x509_t *x509;
-       bool agent = FALSE, smartcard = FALSE;
+       bool agent = FALSE, smartcard = FALSE, loose_gateway_id = FALSE;
        lifetime_cfg_t lifetime = {
                .time = {
                        .life = 10800 /* 3h */,
@@ -303,6 +325,12 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection,
                 priv->name);
        DBG4(DBG_CFG, "%s",
                 nm_setting_to_string(NM_SETTING(vpn)));
+       if (!priv->tun)
+       {
+               g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED,
+                                       "Failed to create dummy TUN device.");
+               return FALSE;
+       }
        address = nm_setting_vpn_get_data_item(vpn, "address");
        if (!address || !*address)
        {
@@ -380,11 +408,13 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection,
                 * included in the gateway certificate. */
                gateway = identification_create_from_string((char*)address);
                DBG1(DBG_CFG, "using CA certificate, gateway identity '%Y'", gateway);
+               loose_gateway_id = TRUE;
        }
 
-       if (auth_class == AUTH_CLASS_EAP)
+       if (auth_class == AUTH_CLASS_EAP ||
+               auth_class == AUTH_CLASS_PSK)
        {
-               /* username/password authentication ... */
+               /* username/password or PSK authentication ... */
                str = nm_setting_vpn_get_data_item(vpn, "user");
                if (str)
                {
@@ -497,15 +527,17 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection,
        /**
         * Set up configurations
         */
-       ike_cfg = ike_cfg_create(TRUE, encap, "0.0.0.0", FALSE,
+       ike_cfg = ike_cfg_create(IKEV2, TRUE, encap, "0.0.0.0",
                                                         charon->socket->get_port(charon->socket, FALSE),
-                                                       (char*)address, FALSE, IKEV2_UDP_PORT);
+                                                       (char*)address, IKEV2_UDP_PORT,
+                                                        FRAGMENTATION_NO, 0);
        ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
-       peer_cfg = peer_cfg_create(priv->name, IKEV2, ike_cfg,
+       ike_cfg->add_proposal(ike_cfg, proposal_create_default_aead(PROTO_IKE));
+       peer_cfg = peer_cfg_create(priv->name, ike_cfg,
                                        CERT_SEND_IF_ASKED, UNIQUE_REPLACE, 1, /* keyingtries */
                                        36000, 0, /* rekey 10h, reauth none */
                                        600, 600, /* jitter, over 10min */
-                                       TRUE, FALSE, /* mobike, aggressive */
+                                       TRUE, FALSE, TRUE, /* mobike, aggressive, pull */
                                        0, 0, /* DPD delay, timeout */
                                        FALSE, NULL, NULL); /* mediation */
        if (virtual)
@@ -517,8 +549,16 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection,
        auth->add(auth, AUTH_RULE_IDENTITY, user);
        peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE);
        auth = auth_cfg_create();
-       auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
+       if (auth_class == AUTH_CLASS_PSK)
+       {
+               auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK);
+       }
+       else
+       {
+               auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
+       }
        auth->add(auth, AUTH_RULE_IDENTITY, gateway);
+       auth->add(auth, AUTH_RULE_IDENTITY_LOOSE, loose_gateway_id);
        peer_cfg->add_auth_cfg(peer_cfg, auth, FALSE);
 
        child_cfg = child_cfg_create(priv->name, &lifetime,
@@ -591,7 +631,7 @@ static gboolean need_secrets(NMVPNPlugin *plugin, NMConnection *connection,
        method = nm_setting_vpn_get_data_item(settings, "method");
        if (method)
        {
-               if (streq(method, "eap"))
+               if (streq(method, "eap") || streq(method, "psk"))
                {
                        if (nm_setting_vpn_get_secret(settings, "password"))
                        {
@@ -620,9 +660,13 @@ static gboolean need_secrets(NMVPNPlugin *plugin, NMConnection *connection,
                                        key->destroy(key);
                                        return FALSE;
                                }
+                               else if (nm_setting_vpn_get_secret(settings, "password"))
+                               {
+                                       return FALSE;
+                               }
                        }
                }
-               else if streq(method, "smartcard")
+               else if (streq(method, "smartcard"))
                {
                        if (nm_setting_vpn_get_secret(settings, "password"))
                        {
@@ -677,6 +721,25 @@ static void nm_strongswan_plugin_init(NMStrongswanPlugin *plugin)
        memset(&priv->listener, 0, sizeof(listener_t));
        priv->listener.child_updown = child_updown;
        priv->listener.ike_rekey = ike_rekey;
+       priv->tun = tun_device_create(NULL);
+       priv->name = NULL;
+}
+
+/**
+ * Destructor
+ */
+static void nm_strongswan_plugin_dispose(GObject *obj)
+{
+       NMStrongswanPlugin *plugin;
+       NMStrongswanPluginPrivate *priv;
+
+       plugin = NM_STRONGSWAN_PLUGIN(obj);
+       priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin);
+       if (priv->tun)
+       {
+               priv->tun->destroy(priv->tun);
+               priv->tun = NULL;
+       }
 }
 
 /**
@@ -692,6 +755,7 @@ static void nm_strongswan_plugin_class_init(
        parent_class->connect = connect_;
        parent_class->need_secrets = need_secrets;
        parent_class->disconnect = disconnect;
+       G_OBJECT_CLASS(strongswan_class)->dispose = nm_strongswan_plugin_dispose;
 }
 
 /**
@@ -708,11 +772,10 @@ NMStrongswanPlugin *nm_strongswan_plugin_new(nm_creds_t *creds,
        {
                NMStrongswanPluginPrivate *priv;
 
+               /* the rest of the initialization happened in _init above */
                priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin);
                priv->creds = creds;
                priv->handler = handler;
-               priv->name = NULL;
        }
        return plugin;
 }
-