]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
charon-nm: Add support for EAP-TLS
authorTobias Brunner <tobias@strongswan.org>
Wed, 5 Feb 2020 15:00:10 +0000 (16:00 +0100)
committerTobias Brunner <tobias@strongswan.org>
Fri, 14 Feb 2020 12:44:39 +0000 (13:44 +0100)
The code is structured similar to that in the Android client, but two-round
authentication (cert+EAP) is not supported as that might require multiple
secrets ("password" is currently the only secret field used for every
method) and other details are currently missing too (like configurable
client identities).

src/charon-nm/nm/nm_service.c

index 706e482a21700e649fc9a85adf6646090595360a..b096ad0ef5f2fbb1110eaa1e05ba1633451c5e5e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2017 Lubomir Rintel
  *
- * Copyright (C) 2013-2019 Tobias Brunner
+ * Copyright (C) 2013-2020 Tobias Brunner
  * Copyright (C) 2008-2009 Martin Willi
  * HSR Hochschule fuer Technik Rapperswil
  *
@@ -372,6 +372,177 @@ static identification_t *find_smartcard_key(NMStrongswanPluginPrivate *priv,
        return id;
 }
 
+/**
+ * Add a client auth config for certificate authentication
+ */
+static bool add_auth_cfg_cert(NMStrongswanPluginPrivate *priv,
+                                                         NMSettingVpn *vpn, peer_cfg_t *peer_cfg,
+                                                         GError **err)
+{
+       identification_t *user = NULL;
+       certificate_t *cert = NULL;
+       auth_cfg_t *auth;
+       const char *str, *method, *cert_source;
+
+       method = nm_setting_vpn_get_data_item(vpn, "method");
+       cert_source = nm_setting_vpn_get_data_item(vpn, "cert-source") ?: method;
+
+       if (streq(cert_source, "smartcard"))
+       {
+               char *pin;
+
+               pin = (char*)nm_setting_vpn_get_secret(vpn, "password");
+               if (pin)
+               {
+                       user = find_smartcard_key(priv, pin);
+               }
+               if (!user)
+               {
+                       g_set_error(err, NM_VPN_PLUGIN_ERROR,
+                                               NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+                                               "no usable smartcard certificate found.");
+                       return FALSE;
+               }
+       }
+       /* ... or certificate/private key authentication */
+       else if ((str = nm_setting_vpn_get_data_item(vpn, "usercert")))
+       {
+               public_key_t *public;
+               private_key_t *private = NULL;
+
+               bool agent = streq(cert_source, "agent");
+
+               cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
+                                                                 BUILD_FROM_FILE, str, BUILD_END);
+               if (!cert)
+               {
+                       g_set_error(err, NM_VPN_PLUGIN_ERROR,
+                                               NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+                                               "Loading peer certificate failed.");
+                       return FALSE;
+               }
+               /* try agent */
+               str = nm_setting_vpn_get_secret(vpn, "agent");
+               if (agent && str)
+               {
+                       public = cert->get_public_key(cert);
+                       if (public)
+                       {
+                               private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
+                                                                                        public->get_type(public),
+                                                                                        BUILD_AGENT_SOCKET, str,
+                                                                                        BUILD_PUBLIC_KEY, public,
+                                                                                        BUILD_END);
+                               public->destroy(public);
+                       }
+                       if (!private)
+                       {
+                               g_set_error(err, NM_VPN_PLUGIN_ERROR,
+                                                       NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+                                                       "Connecting to SSH agent failed.");
+                       }
+               }
+               /* ... or key file */
+               str = nm_setting_vpn_get_data_item(vpn, "userkey");
+               if (!agent && str)
+               {
+                       char *secret;
+
+                       secret = (char*)nm_setting_vpn_get_secret(vpn, "password");
+                       if (secret)
+                       {
+                               priv->creds->set_key_password(priv->creds, secret);
+                       }
+                       private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
+                                                                       KEY_ANY, BUILD_FROM_FILE, str, BUILD_END);
+                       if (!private)
+                       {
+                               g_set_error(err, NM_VPN_PLUGIN_ERROR,
+                                                       NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+                                                       "Loading private key failed.");
+                       }
+               }
+               if (private)
+               {
+                       user = cert->get_subject(cert);
+                       user = user->clone(user);
+                       priv->creds->set_cert_and_key(priv->creds, cert, private);
+               }
+               else
+               {
+                       DESTROY_IF(cert);
+                       return FALSE;
+               }
+       }
+
+       auth = auth_cfg_create();
+       if (streq(method, "eap-tls"))
+       {
+               auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
+               auth->add(auth, AUTH_RULE_EAP_TYPE, EAP_TLS);
+               auth->add(auth, AUTH_RULE_AAA_IDENTITY,
+                                 identification_create_from_string("%any"));
+       }
+       else
+       {
+               auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
+       }
+       if (cert)
+       {
+               auth->add(auth, AUTH_RULE_SUBJECT_CERT, cert->get_ref(cert));
+       }
+       auth->add(auth, AUTH_RULE_IDENTITY, user);
+       peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE);
+       return TRUE;
+}
+
+/**
+ * Add a client auth config for username/password authentication
+ */
+static bool add_auth_cfg_pw(NMStrongswanPluginPrivate *priv,
+                                                       NMSettingVpn *vpn, peer_cfg_t *peer_cfg,
+                                                       GError **err)
+{
+       identification_t *user = NULL;
+       auth_cfg_t *auth;
+       const char *str, *method;
+
+       method = nm_setting_vpn_get_data_item(vpn, "method");
+
+       str = nm_setting_vpn_get_data_item(vpn, "user");
+       if (str)
+       {
+               user = identification_create_from_string((char*)str);
+               str = nm_setting_vpn_get_secret(vpn, "password");
+               if (streq(method, "psk") && strlen(str) < 20)
+               {
+                       g_set_error(err, NM_VPN_PLUGIN_ERROR,
+                                               NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+                                               "Pre-shared key is too short.");
+                       user->destroy(user);
+                       return FALSE;
+               }
+               priv->creds->set_username_password(priv->creds, user, (char*)str);
+       }
+       else
+       {
+               g_set_error(err, NM_VPN_PLUGIN_ERROR,
+                                       NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+                                       "Username is missing.");
+               return FALSE;
+       }
+
+       auth = auth_cfg_create();
+       auth->add(auth, AUTH_RULE_AUTH_CLASS,
+                         streq(method, "psk") ? AUTH_CLASS_PSK : AUTH_CLASS_EAP);
+       /* in case EAP-PEAP or EAP-TTLS is used we currently accept any identity */
+       auth->add(auth, AUTH_RULE_AAA_IDENTITY,
+                         identification_create_from_string("%any"));
+       auth->add(auth, AUTH_RULE_IDENTITY, user);
+       peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE);
+       return TRUE;
+}
+
 /**
  * Connect function called from NM via DBUS
  */
@@ -383,8 +554,8 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
        NMSettingConnection *conn;
        NMSettingVpn *vpn;
        enumerator_t *enumerator;
-       identification_t *user = NULL, *gateway = NULL;
-       const char *str;
+       identification_t *gateway = NULL;
+       const char *str, *method;
        bool virtual, proposal;
        proposal_t *prop;
        ike_cfg_t *ike_cfg;
@@ -396,7 +567,7 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
        auth_class_t auth_class = AUTH_CLASS_EAP;
        certificate_t *cert = NULL;
        x509_t *x509;
-       bool agent = FALSE, smartcard = FALSE, loose_gateway_id = FALSE;
+       bool loose_gateway_id = FALSE;
        ike_cfg_create_t ike = {
                .version = IKEV2,
                .local = "%any",
@@ -458,25 +629,6 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
        ike.force_encap = streq(str, "yes");
        str = nm_setting_vpn_get_data_item(vpn, "ipcomp");
        child.options |= streq(str, "yes") ? OPT_IPCOMP : 0;
-       str = nm_setting_vpn_get_data_item(vpn, "method");
-       if (streq(str, "psk"))
-       {
-               auth_class = AUTH_CLASS_PSK;
-       }
-       else if (streq(str, "agent"))
-       {
-               auth_class = AUTH_CLASS_PUBKEY;
-               agent = TRUE;
-       }
-       else if (streq(str, "key"))
-       {
-               auth_class = AUTH_CLASS_PUBKEY;
-       }
-       else if (streq(str, "smartcard"))
-       {
-               auth_class = AUTH_CLASS_PUBKEY;
-               smartcard = TRUE;
-       }
 
        /**
         * Register credentials
@@ -528,129 +680,6 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
        }
        DBG1(DBG_CFG, "using gateway identity '%Y'", gateway);
 
-       if (auth_class == AUTH_CLASS_EAP ||
-               auth_class == AUTH_CLASS_PSK)
-       {
-               /* username/password or PSK authentication ... */
-               str = nm_setting_vpn_get_data_item(vpn, "user");
-               if (str)
-               {
-                       user = identification_create_from_string((char*)str);
-                       str = nm_setting_vpn_get_secret(vpn, "password");
-                       if (auth_class == AUTH_CLASS_PSK &&
-                               strlen(str) < 20)
-                       {
-                               g_set_error(err, NM_VPN_PLUGIN_ERROR,
-                                                       NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
-                                                       "pre-shared key is too short.");
-                               gateway->destroy(gateway);
-                               user->destroy(user);
-                               return FALSE;
-                       }
-                       priv->creds->set_username_password(priv->creds, user, (char*)str);
-               }
-       }
-
-       if (auth_class == AUTH_CLASS_PUBKEY)
-       {
-               if (smartcard)
-               {
-                       char *pin;
-
-                       pin = (char*)nm_setting_vpn_get_secret(vpn, "password");
-                       if (pin)
-                       {
-                               user = find_smartcard_key(priv, pin);
-                       }
-                       if (!user)
-                       {
-                               g_set_error(err, NM_VPN_PLUGIN_ERROR,
-                                                       NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
-                                                       "no usable smartcard certificate found.");
-                               gateway->destroy(gateway);
-                               return FALSE;
-                       }
-               }
-               /* ... or certificate/private key authentication */
-               else if ((str = nm_setting_vpn_get_data_item(vpn, "usercert")))
-               {
-                       public_key_t *public;
-                       private_key_t *private = NULL;
-
-                       cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
-                                                                         BUILD_FROM_FILE, str, BUILD_END);
-                       if (!cert)
-                       {
-                               g_set_error(err, NM_VPN_PLUGIN_ERROR,
-                                                       NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
-                                                       "Loading peer certificate failed.");
-                               gateway->destroy(gateway);
-                               return FALSE;
-                       }
-                       /* try agent */
-                       str = nm_setting_vpn_get_secret(vpn, "agent");
-                       if (agent && str)
-                       {
-                               public = cert->get_public_key(cert);
-                               if (public)
-                               {
-                                       private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
-                                                                                                public->get_type(public),
-                                                                                                BUILD_AGENT_SOCKET, str,
-                                                                                                BUILD_PUBLIC_KEY, public,
-                                                                                                BUILD_END);
-                                       public->destroy(public);
-                               }
-                               if (!private)
-                               {
-                                       g_set_error(err, NM_VPN_PLUGIN_ERROR,
-                                                               NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
-                                                               "Connecting to SSH agent failed.");
-                               }
-                       }
-                       /* ... or key file */
-                       str = nm_setting_vpn_get_data_item(vpn, "userkey");
-                       if (!agent && str)
-                       {
-                               char *secret;
-
-                               secret = (char*)nm_setting_vpn_get_secret(vpn, "password");
-                               if (secret)
-                               {
-                                       priv->creds->set_key_password(priv->creds, secret);
-                               }
-                               private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
-                                                               KEY_ANY, BUILD_FROM_FILE, str, BUILD_END);
-                               if (!private)
-                               {
-                                       g_set_error(err, NM_VPN_PLUGIN_ERROR,
-                                                               NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
-                                                               "Loading private key failed.");
-                               }
-                       }
-                       if (private)
-                       {
-                               user = cert->get_subject(cert);
-                               user = user->clone(user);
-                               priv->creds->set_cert_and_key(priv->creds, cert, private);
-                       }
-                       else
-                       {
-                               DESTROY_IF(cert);
-                               gateway->destroy(gateway);
-                               return FALSE;
-                       }
-               }
-       }
-
-       if (!user)
-       {
-               g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
-                                       "Configuration parameters missing.");
-               gateway->destroy(gateway);
-               return FALSE;
-       }
-
        /**
         * Set up configurations
         */
@@ -673,7 +702,6 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
                                enumerator->destroy(enumerator);
                                ike_cfg->destroy(ike_cfg);
                                gateway->destroy(gateway);
-                               user->destroy(user);
                                return FALSE;
                        }
                        ike_cfg->add_proposal(ike_cfg, prop);
@@ -692,10 +720,43 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
                peer_cfg->add_virtual_ip(peer_cfg, host_create_any(AF_INET));
                peer_cfg->add_virtual_ip(peer_cfg, host_create_any(AF_INET6));
        }
-       auth = auth_cfg_create();
-       auth->add(auth, AUTH_RULE_AUTH_CLASS, auth_class);
-       auth->add(auth, AUTH_RULE_IDENTITY, user);
-       peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE);
+
+       method = nm_setting_vpn_get_data_item(vpn, "method");
+       if (streq(method, "cert") ||
+               streq(method, "eap-tls") ||
+               streq(method, "key") ||
+               streq(method, "agent") ||
+               streq(method, "smartcard"))
+       {
+               if (!add_auth_cfg_cert (priv, vpn, peer_cfg, err))
+               {
+                       peer_cfg->destroy(peer_cfg);
+                       ike_cfg->destroy(ike_cfg);
+                       gateway->destroy(gateway);
+                       return FALSE;
+               }
+       }
+       else if (streq(method, "eap") ||
+                        streq(method, "psk"))
+       {
+               if (!add_auth_cfg_pw(priv, vpn, peer_cfg, err))
+               {
+                       peer_cfg->destroy(peer_cfg);
+                       ike_cfg->destroy(ike_cfg);
+                       gateway->destroy(gateway);
+                       return FALSE;
+               }
+       }
+       else
+       {
+               g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+                                       "Configuration parameters missing.");
+               peer_cfg->destroy(peer_cfg);
+               ike_cfg->destroy(ike_cfg);
+               gateway->destroy(gateway);
+               return FALSE;
+       }
+
        auth = auth_cfg_create();
        if (auth_class == AUTH_CLASS_PSK)
        {
@@ -794,58 +855,64 @@ static gboolean need_secrets(NMVpnServicePlugin *plugin, NMConnection *connectio
                                                         const char **setting_name, GError **error)
 {
        NMSettingVpn *settings;
-       const char *method, *path;
+       const char *method, *cert_source, *path;
+       bool need_secret = FALSE;
 
        settings = NM_SETTING_VPN(nm_connection_get_setting(connection,
                                                                                                                NM_TYPE_SETTING_VPN));
        method = nm_setting_vpn_get_data_item(settings, "method");
        if (method)
        {
-               if (streq(method, "eap") || streq(method, "psk"))
+               if (streq(method, "cert") ||
+                       streq(method, "eap-tls") ||
+                       streq(method, "key") ||
+                       streq(method, "agent") ||
+                       streq(method, "smartcard"))
                {
-                       if (nm_setting_vpn_get_secret(settings, "password"))
+                       cert_source = nm_setting_vpn_get_data_item(settings, "cert-source");
+                       if (!cert_source)
                        {
-                               return FALSE;
+                               cert_source = method;
                        }
-               }
-               else if (streq(method, "agent"))
-               {
-                       if (nm_setting_vpn_get_secret(settings, "agent"))
+                       if (streq(cert_source, "agent"))
                        {
-                               return FALSE;
+                               need_secret = !nm_setting_vpn_get_secret(settings, "agent");
                        }
-               }
-               else if (streq(method, "key"))
-               {
-                       path = nm_setting_vpn_get_data_item(settings, "userkey");
-                       if (path)
+                       else if (streq(cert_source, "smartcard"))
                        {
-                               private_key_t *key;
-
-                               /* try to load/decrypt the private key */
-                               key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
-                                                               KEY_ANY, BUILD_FROM_FILE, path, BUILD_END);
-                               if (key)
-                               {
-                                       key->destroy(key);
-                                       return FALSE;
-                               }
-                               else if (nm_setting_vpn_get_secret(settings, "password"))
+                               need_secret = !nm_setting_vpn_get_secret(settings, "password");
+                       }
+                       else
+                       {
+                               need_secret = TRUE;
+                               path = nm_setting_vpn_get_data_item(settings, "userkey");
+                               if (path)
                                {
-                                       return FALSE;
+                                       private_key_t *key;
+
+                                       /* try to load/decrypt the private key */
+                                       key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
+                                                                       KEY_ANY, BUILD_FROM_FILE, path, BUILD_END);
+                                       if (key)
+                                       {
+                                               key->destroy(key);
+                                               need_secret = FALSE;
+                                       }
+                                       else if (nm_setting_vpn_get_secret(settings, "password"))
+                                       {
+                                               need_secret = FALSE;
+                                       }
                                }
                        }
                }
-               else if (streq(method, "smartcard"))
+               else if (streq(method, "eap") ||
+                                streq(method, "psk"))
                {
-                       if (nm_setting_vpn_get_secret(settings, "password"))
-                       {
-                               return FALSE;
-                       }
+                       need_secret = !nm_setting_vpn_get_secret(settings, "password");
                }
        }
        *setting_name = NM_SETTING_VPN_SETTING_NAME;
-       return TRUE;
+       return need_secret;
 }
 
 /**