Implemented Smartcard support in NetworkManager frontend
authorMartin Willi <martin@revosec.ch>
Wed, 11 Aug 2010 14:32:04 +0000 (16:32 +0200)
committerMartin Willi <martin@revosec.ch>
Wed, 11 Aug 2010 14:32:04 +0000 (16:32 +0200)
src/libcharon/plugins/nm/gnome/NEWS
src/libcharon/plugins/nm/gnome/auth-dialog/main.c
src/libcharon/plugins/nm/gnome/configure.ac
src/libcharon/plugins/nm/gnome/po/de.po
src/libcharon/plugins/nm/gnome/properties/nm-strongswan.c
src/libcharon/plugins/nm/nm_creds.c
src/libcharon/plugins/nm/nm_creds.h
src/libcharon/plugins/nm/nm_service.c

index 762e2f5..f84ccc8 100644 (file)
@@ -1,3 +1,8 @@
+NetworkManager-strongswan-1.2.0
+-------------------------------
+
+- Added Smartcard support
+
 NetworkManager-strongswan-1.1.2
 -------------------------------
 
index f2716f2..686ff29 100644 (file)
@@ -162,7 +162,7 @@ int main (int argc, char *argv[])
                g_object_unref (program);
                return 1;
        }
-       if (!strcmp(type, "eap") || !strcmp(type, "key"))
+       if (!strcmp(type, "eap") || !strcmp(type, "key") || !strcmp(type, "smartcard"))
        {
                pass = lookup_password(name, service);
                if (!pass || retry)
@@ -172,14 +172,22 @@ int main (int argc, char *argv[])
                                dialog = gnome_password_dialog_new(_("VPN password required"),
                                                        _("EAP password required to establish VPN connection:"),
                                                        NULL, NULL, TRUE);
+                               gnome_password_dialog_set_show_remember(GNOME_PASSWORD_DIALOG(dialog), TRUE);
                        }
-                       else
+                       else if (!strcmp(type, "key"))
                        {
                                dialog = gnome_password_dialog_new(_("VPN password required"),
                                                        _("Private key decryption password required to establish VPN connection:"),
                                                        NULL, NULL, TRUE);
+                               gnome_password_dialog_set_show_remember(GNOME_PASSWORD_DIALOG(dialog), TRUE);
+                       }
+                       else /* smartcard */
+                       {
+                               dialog = gnome_password_dialog_new(_("VPN password required"),
+                                                       _("Smartcard PIN required to establish VPN connection:"),
+                                                       NULL, NULL, TRUE);
+                               gnome_password_dialog_set_show_remember(GNOME_PASSWORD_DIALOG(dialog), FALSE);
                        }
-                       gnome_password_dialog_set_show_remember(GNOME_PASSWORD_DIALOG(dialog), TRUE);
                        gnome_password_dialog_set_show_username(GNOME_PASSWORD_DIALOG(dialog), FALSE);
                        if (pass)
                        {
index b15e6e6..148db3e 100644 (file)
@@ -1,6 +1,6 @@
 AC_PREREQ(2.52)
 
-AC_INIT(NetworkManager-strongswan, 1.1.2, martin@strongswan.org, NetworkManager-strongswan)
+AC_INIT(NetworkManager-strongswan, 1.2.0, martin@strongswan.org, NetworkManager-strongswan)
 AM_INIT_AUTOMAKE([subdir-objects])
 AM_MAINTAINER_MODE
 
index 52fb7ff..e6649e5 100644 (file)
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: NetworkManager-strongswan\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-02-18 09:20+0100\n"
+"POT-Creation-Date: 2010-08-11 16:12+0200\n"
 "PO-Revision-Date: 2010-02-18 09:20+0100\n"
 "Last-Translator: Martin Willi <martin@strongswan.org>\n"
 "Language-Team: de <martin@strongswan.org>\n"
@@ -22,15 +22,19 @@ msgstr "IPsec/IKEv2 (strongswan)"
 msgid "IPsec with the IKEv2 key exchange protocol."
 msgstr "IPsec mit dem IKEv2 Protokoll."
 
-#: ../properties/nm-strongswan.c:200
+#: ../properties/nm-strongswan.c:208
 msgid "Certificate/private key"
 msgstr "Zertifikat/Privater Schlüssel"
 
-#: ../properties/nm-strongswan.c:201
+#: ../properties/nm-strongswan.c:209
 msgid "Certificate/ssh-agent"
 msgstr "Zertifikat/ssh-agent"
 
-#: ../properties/nm-strongswan.c:202
+#: ../properties/nm-strongswan.c:210
+msgid "Smartcard"
+msgstr "Smartcard"
+
+#: ../properties/nm-strongswan.c:211
 msgid "EAP"
 msgstr "EAP"
 
@@ -87,8 +91,8 @@ msgid ""
 "IPComp compresses raw IP packets before they get encrypted. This saves some "
 "bandwith, but uses more processing power."
 msgstr ""
-"IPComp komprimiert IP-Pakete, bevor sie verschlsselt werden. Diese Option "
-"kann Bandbreite sparen, bentigt jedoch zustzliche Rechenleistung."
+"IPComp komprimiert IP-Pakete, bevor sie verschlüsselt werden. Diese Option "
+"kann Bandbreite sparen, benötigt jedoch zusätzliche Rechenleistung."
 
 #: ../properties/nm-strongswan-dialog.glade.h:13
 msgid "Private _key:"
@@ -126,7 +130,7 @@ msgstr ""
 
 #: ../properties/nm-strongswan-dialog.glade.h:18
 msgid "The username (identity) to use for authentication against the gateway."
-msgstr "Benutzername/Identitt für die Authentisierung gegenüber dem Gateway."
+msgstr "Benutzername/Identität für die Authentisierung gegenüber dem Gateway."
 
 #: ../properties/nm-strongswan-dialog.glade.h:19
 msgid "Use IP c_ompression"
@@ -140,7 +144,8 @@ msgstr "_Adresse:"
 msgid "_Username:"
 msgstr "_Benutzername:"
 
-#: ../auth-dialog/main.c:172 ../auth-dialog/main.c:178
+#: ../auth-dialog/main.c:172 ../auth-dialog/main.c:179
+#: ../auth-dialog/main.c:186
 msgid "VPN password required"
 msgstr "VPN Passwort notwendig"
 
@@ -148,13 +153,18 @@ msgstr "VPN Passwort notwendig"
 msgid "EAP password required to establish VPN connection:"
 msgstr "Für die Erstellung des VPN-Tunnels ist ein EAP-Passwort erforderlich:"
 
-#: ../auth-dialog/main.c:179
+#: ../auth-dialog/main.c:180
 msgid "Private key decryption password required to establish VPN connection:"
 msgstr ""
 "Der Private Schlüssel für die Erstellung des VPN-Tunnels ist durch ein "
 "Passwort geschützt:"
 
-#: ../auth-dialog/main.c:225
+#: ../auth-dialog/main.c:187
+#, fuzzy
+msgid "Smartcard PIN required to establish VPN connection:"
+msgstr "Für die Smartcard ist eine PIN erforderlich:"
+
+#: ../auth-dialog/main.c:233
 msgid ""
 "Configuration uses ssh-agent for authentication, but ssh-agent is not "
 "running!"
index 4b69296..45c0df3 100644 (file)
@@ -142,6 +142,14 @@ static void update_layout (GtkWidget *widget, StrongswanPluginUiWidgetPrivate *p
                        gtk_widget_hide (glade_xml_get_widget (priv->xml, "userkey-button"));
                        break;
                case 2:
+                       gtk_widget_hide (glade_xml_get_widget (priv->xml, "usercert-label"));
+                       gtk_widget_hide (glade_xml_get_widget (priv->xml, "usercert-button"));
+                       gtk_widget_hide (glade_xml_get_widget (priv->xml, "user-label"));
+                       gtk_widget_hide (glade_xml_get_widget (priv->xml, "user-entry"));
+                       gtk_widget_hide (glade_xml_get_widget (priv->xml, "userkey-label"));
+                       gtk_widget_hide (glade_xml_get_widget (priv->xml, "userkey-button"));
+                       break;
+               case 3:
                        gtk_widget_show (glade_xml_get_widget (priv->xml, "user-label"));
                        gtk_widget_show (glade_xml_get_widget (priv->xml, "user-entry"));
                        gtk_widget_hide (glade_xml_get_widget (priv->xml, "usercert-label"));
@@ -199,6 +207,7 @@ init_plugin_ui (StrongswanPluginUiWidget *self, NMConnection *connection, GError
        widget = glade_xml_get_widget (priv->xml, "method-combo");
        gtk_combo_box_append_text (GTK_COMBO_BOX (widget), _("Certificate/private key"));
        gtk_combo_box_append_text (GTK_COMBO_BOX (widget), _("Certificate/ssh-agent"));
+       gtk_combo_box_append_text (GTK_COMBO_BOX (widget), _("Smartcard"));
        gtk_combo_box_append_text (GTK_COMBO_BOX (widget), _("EAP"));
        value = nm_setting_vpn_get_data_item (settings, "method");
        if (value) {
@@ -208,9 +217,12 @@ init_plugin_ui (StrongswanPluginUiWidget *self, NMConnection *connection, GError
                if (g_strcmp0 (value, "agent") == 0) {
                        gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 1);
                }
-               if (g_strcmp0 (value, "eap") == 0) {
+               if (g_strcmp0 (value, "smartcard") == 0) {
                        gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 2);
                }
+               if (g_strcmp0 (value, "eap") == 0) {
+                       gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 3);
+               }
        }
        if (gtk_combo_box_get_active (GTK_COMBO_BOX (widget)) == -1)
        {
@@ -330,6 +342,9 @@ update_connection (NMVpnPluginUiWidgetInterface *iface,
                        str = "agent";
                        break;
                case 2:
+                       str = "smartcard";
+                       break;
+               case 3:
                        widget = glade_xml_get_widget (priv->xml, "user-entry");
                        str = (char *) gtk_entry_get_text (GTK_ENTRY (widget));
                        if (str && strlen (str)) {
index 4ee9436..6387870 100644 (file)
@@ -51,11 +51,16 @@ struct private_nm_creds_t {
        char *pass;
 
        /**
-        * Private key decryption password
+        * Private key decryption password / smartcard pin
         */
        char *keypass;
 
        /**
+        * private key ID of smartcard key
+        */
+       chunk_t keyid;
+
+       /**
         * users certificate
         */
        certificate_t *usercert;
@@ -296,6 +301,14 @@ static enumerator_t* create_shared_enumerator(private_nm_creds_t *this,
                        }
                        key = chunk_create(this->keypass, strlen(this->keypass));
                        break;
+               case SHARED_PIN:
+                       if (!this->keypass || !me ||
+                               !chunk_equals(me->get_encoding(me), this->keyid))
+                       {
+                               return NULL;
+                       }
+                       key = chunk_create(this->keypass, strlen(this->keypass));
+                       break;
                default:
                        return NULL;
        }
@@ -403,6 +416,19 @@ static void set_key_password(private_nm_creds_t *this, char *password)
 }
 
 /**
+ * Implementation of nm_creds_t.set_pin
+ */
+static void set_pin(private_nm_creds_t *this, chunk_t keyid, char *pin)
+{
+       this->lock->write_lock(this->lock);
+       free(this->keypass);
+       free(this->keyid.ptr);
+       this->keypass = pin ? strdup(pin) : NULL;
+       this->keyid = chunk_clone(keyid);
+       this->lock->unlock(this->lock);
+}
+
+/**
  * Implementation of nm_creds_t.set_cert_and_key
  */
 static void set_cert_and_key(private_nm_creds_t *this, certificate_t *cert,
@@ -429,12 +455,16 @@ static void clear(private_nm_creds_t *this)
        }
        DESTROY_IF(this->user);
        free(this->pass);
+       free(this->keypass);
+       free(this->keyid.ptr);
        DESTROY_IF(this->usercert);
        DESTROY_IF(this->key);
        this->key = NULL;
        this->usercert = NULL;
        this->pass = NULL;
        this->user = NULL;
+       this->keypass = NULL;
+       this->keyid = chunk_empty;
 }
 
 /**
@@ -464,6 +494,7 @@ nm_creds_t *nm_creds_create()
        this->public.load_ca_dir = (void(*)(nm_creds_t*, char *dir))load_ca_dir;
        this->public.set_username_password = (void(*)(nm_creds_t*, identification_t *id, char *password))set_username_password;
        this->public.set_key_password = (void(*)(nm_creds_t*, char *password))set_key_password;
+       this->public.set_pin = (void(*)(nm_creds_t*, chunk_t keyid, char *pin))set_pin;
        this->public.set_cert_and_key = (void(*)(nm_creds_t*, certificate_t *cert, private_key_t *key))set_cert_and_key;
        this->public.clear = (void(*)(nm_creds_t*))clear;
        this->public.destroy = (void(*)(nm_creds_t*))destroy;
@@ -475,6 +506,8 @@ nm_creds_t *nm_creds_create()
        this->pass = NULL;
        this->usercert = NULL;
        this->key = NULL;
+       this->keypass = NULL;
+       this->keyid = chunk_empty;
 
        return &this->public;
 }
index 44936dd..91f645c 100644 (file)
@@ -67,6 +67,14 @@ struct nm_creds_t {
        void (*set_key_password)(nm_creds_t *this, char *password);
 
        /**
+        * Set the PIN to unlock a smartcard.
+        *
+        * @param keyid         keyid of the smartcard key
+        * @param pin           PIN
+        */
+       void (*set_pin)(nm_creds_t *this, chunk_t keyid, char *pin);
+
+       /**
         * Set the certificate and private key to use for client authentication.
         *
         * @param cert          client certificate
index 910b01c..72c5bbb 100644 (file)
@@ -204,6 +204,59 @@ static bool ike_rekey(listener_t *listener, ike_sa_t *old, ike_sa_t *new)
 }
 
 /**
+ * Find a certificate for which we have a private key on a smartcard
+ */
+static identification_t *find_smartcard_key(NMStrongswanPluginPrivate *priv,
+                                                                                       char *pin)
+{
+       enumerator_t *enumerator, *sans;
+       identification_t *id = NULL;
+       certificate_t *cert;
+       x509_t *x509;
+       private_key_t *key;
+       chunk_t keyid;
+
+       enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
+                                                                                       CERT_X509, KEY_ANY, NULL, FALSE);
+       while (enumerator->enumerate(enumerator, &cert))
+       {
+               x509 = (x509_t*)cert;
+
+               /* there might be a lot of certificates, filter them by usage */
+               if ((x509->get_flags(x509) & X509_CLIENT_AUTH) &&
+                       !(x509->get_flags(x509) & X509_CA))
+               {
+                       keyid = x509->get_subjectKeyIdentifier(x509);
+                       if (keyid.ptr)
+                       {
+                               /* try to find a private key by the certificate keyid */
+                               priv->creds->set_pin(priv->creds, keyid, pin);
+                               key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
+                                                               KEY_ANY, BUILD_PKCS11_KEYID, keyid, BUILD_END);
+                               if (key)
+                               {
+                                       /* prefer a more convenient subjectAltName */
+                                       sans = x509->create_subjectAltName_enumerator(x509);
+                                       if (!sans->enumerate(sans, &id))
+                                       {
+                                               id = cert->get_subject(cert);
+                                       }
+                                       id = id->clone(id);
+                                       sans->destroy(sans);
+
+                                       DBG1(DBG_CFG, "using smartcard certificate '%Y'", id);
+                                       priv->creds->set_cert_and_key(priv->creds,
+                                                                                                 cert->get_ref(cert), key);
+                                       break;
+                               }
+                       }
+               }
+       }
+       enumerator->destroy(enumerator);
+       return id;
+}
+
+/**
  * Connect function called from NM via DBUS
  */
 static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection,
@@ -224,7 +277,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;
+       bool agent = FALSE, smartcard = FALSE;
        lifetime_cfg_t lifetime = {
                .time = {
                        .life = 10800 /* 3h */,
@@ -279,6 +332,11 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection,
                {
                        auth_class = AUTH_CLASS_PUBKEY;
                }
+               else if (streq(str, "smartcard"))
+               {
+                       auth_class = AUTH_CLASS_PUBKEY;
+                       smartcard = TRUE;
+               }
        }
 
        /**
@@ -338,9 +396,26 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection,
 
        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 authenitcation */
-               str = nm_setting_vpn_get_data_item(vpn, "usercert");
-               if (str)
+               else if ((str = nm_setting_vpn_get_data_item(vpn, "usercert")))
                {
                        public_key_t *public;
                        private_key_t *private = NULL;
@@ -534,6 +609,13 @@ static gboolean need_secrets(NMVPNPlugin *plugin, NMConnection *connection,
                                }
                        }
                }
+               else if streq(method, "smartcard")
+               {
+                       if (nm_setting_vpn_get_secret(settings, "password"))
+                       {
+                               return FALSE;
+                       }
+               }
        }
        *setting_name = NM_SETTING_VPN_SETTING_NAME;
        return TRUE;