implemented NetworkManager certificate/private key authentication using ssh-agent
authorMartin Willi <martin@strongswan.org>
Thu, 4 Sep 2008 08:40:37 +0000 (08:40 -0000)
committerMartin Willi <martin@strongswan.org>
Thu, 4 Sep 2008 08:40:37 +0000 (08:40 -0000)
src/charon/plugins/nm/gnome/auth-dialog/Makefile.am
src/charon/plugins/nm/gnome/auth-dialog/main.c
src/charon/plugins/nm/gnome/properties/nm-strongswan-dialog.glade
src/charon/plugins/nm/gnome/properties/nm-strongswan.c
src/charon/plugins/nm/nm_creds.c
src/charon/plugins/nm/nm_creds.h
src/charon/plugins/nm/nm_plugin.c
src/charon/plugins/nm/nm_service.c

index a5e9acf..c951647 100644 (file)
@@ -6,6 +6,7 @@ nm_strongswan_auth_dialog_CPPFLAGS = \
        $(LIBGNOMEUI_CFLAGS) \
        $(GNOMEKEYRING_CFLAGS) \
        $(NETWORK_MANAGER_CFLAGS) \
+       $(NM_UTILS_CFLAGS) \
        -DICONDIR=\""$(datadir)/pixmaps"\" \
        -DGLADEDIR=\""$(gladedir)"\" \
        -DBINDIR=\""$(bindir)"\" \
index c270dff..ee9ba0b 100644 (file)
 #include <gtk/gtk.h>
 #include <gnome-keyring.h>
 #include <libgnomeui/libgnomeui.h>
+#include <gconf/gconf-client.h>
+#include <nm-vpn-plugin.h>
+#include <nm-setting-vpn.h>
+#include <nm-setting-connection.h>
 
-#define NM_DBUS_SERVICE_STRONGSWAN    "org.freedesktop.NetworkManager.strongswan"
+#define NM_DBUS_SERVICE_STRONGSWAN     "org.freedesktop.NetworkManager.strongswan"
 
-static char *lookup(char *name, char *service)
+/**
+ * lookup a password in the keyring
+ */
+static char *lookup_password(char *name, char *service)
 {
        GList *list;
        GList *iter;
@@ -55,6 +62,29 @@ static char *lookup(char *name, char *service)
        return pass;
 }
 
+/**
+ * check if this connection needs a password
+ */
+static gboolean need_password(char *id)
+{
+       GConfClient *client;
+       char *key, *str;
+       gboolean need_password = FALSE;
+
+       client = gconf_client_get_default();
+       key = g_strdup_printf("/system/networking/connections/%s/%s/%s",
+                                                 id, NM_SETTING_VPN_SETTING_NAME, "method");
+       str = gconf_client_get_string(client, key, NULL);
+       if (str && !strcmp(str, "eap"))
+       {
+               need_password = TRUE;
+       }
+       g_free(str);
+       g_free(key);
+       g_object_unref(client);
+       return need_password;
+}
+
 int main (int argc, char *argv[])
 {
        static gboolean retry = FALSE;
@@ -62,7 +92,7 @@ int main (int argc, char *argv[])
        GOptionContext *context;
        GnomeProgram *program = NULL;
        int exit_status = 1;
-       char buf;
+       char buf, *agent;
        guint32 itemid;
        GtkWidget *dialog;
        GOptionEntry entries[] = {
@@ -91,8 +121,8 @@ int main (int argc, char *argv[])
                fprintf (stderr, "Have to supply ID, name, and service\n");
                g_object_unref (program);
                return 1;
-       }
-
+       }       
+       
        if (strcmp(service, NM_DBUS_SERVICE_STRONGSWAN) != 0)
        {
                fprintf(stderr, "This dialog only works with the '%s' service\n",
@@ -101,43 +131,66 @@ int main (int argc, char *argv[])
                return 1;
        }
        
-       pass = lookup(name, service);
-       if (!pass || retry)
+       if (need_password(id))
        {
-               dialog = gnome_password_dialog_new(_("VPN password required"),
-                                                       _("Password required to establish VPN connection:"),
-                                                       NULL, NULL, TRUE);
-               gnome_password_dialog_set_show_remember(GNOME_PASSWORD_DIALOG(dialog), TRUE);
-               gnome_password_dialog_set_show_username(GNOME_PASSWORD_DIALOG(dialog), FALSE);
-               if (pass)
+               pass = lookup_password(name, service);
+               if (!pass || retry)
                {
-                       gnome_password_dialog_set_password(GNOME_PASSWORD_DIALOG(dialog), pass);
+                       dialog = gnome_password_dialog_new(_("VPN password required"),
+                                                               _("Password required to establish VPN connection:"),
+                                                               NULL, NULL, TRUE);
+                       gnome_password_dialog_set_show_remember(GNOME_PASSWORD_DIALOG(dialog), TRUE);
+                       gnome_password_dialog_set_show_username(GNOME_PASSWORD_DIALOG(dialog), FALSE);
+                       if (pass)
+                       {
+                               gnome_password_dialog_set_password(GNOME_PASSWORD_DIALOG(dialog), pass);
+                       }
+                       if (!gnome_password_dialog_run_and_block(GNOME_PASSWORD_DIALOG(dialog)))
+                       {
+                               g_object_unref (program);
+                               return 1;
+                       }
+
+                       pass = gnome_password_dialog_get_password(GNOME_PASSWORD_DIALOG(dialog));
+                       switch (gnome_password_dialog_get_remember(GNOME_PASSWORD_DIALOG(dialog)))
+                       {
+                               case GNOME_PASSWORD_DIALOG_REMEMBER_NOTHING:
+                                       break;
+                               case GNOME_PASSWORD_DIALOG_REMEMBER_SESSION:
+                                       keyring = "session";
+                                       /* FALL */
+                               case GNOME_PASSWORD_DIALOG_REMEMBER_FOREVER:
+                                       if (gnome_keyring_set_network_password_sync(keyring,
+                                                       g_get_user_name(), NULL, name, "password", service, NULL, 0,
+                                                       pass, &itemid) != GNOME_KEYRING_RESULT_OK)
+                                       {
+                                               g_warning ("storing password in keyring failed");
+                                       }
+                                       break;
+                       }
                }
-               if (!gnome_password_dialog_run_and_block(GNOME_PASSWORD_DIALOG(dialog)))
+               printf("password\n%s\n", pass);
+       }
+       else
+       {
+               agent = getenv("SSH_AUTH_SOCK");
+               if (agent)
                {
-                       g_object_unref (program);
-                       return 1;
+                       printf("agent\n%s\n", agent);
                }
-       
-               pass = gnome_password_dialog_get_password(GNOME_PASSWORD_DIALOG(dialog));
-               switch (gnome_password_dialog_get_remember(GNOME_PASSWORD_DIALOG(dialog)))
+               else
                {
-                       case GNOME_PASSWORD_DIALOG_REMEMBER_NOTHING:
-                               break;
-                       case GNOME_PASSWORD_DIALOG_REMEMBER_SESSION:
-                               keyring = "session";
-                               /* FALL */
-                       case GNOME_PASSWORD_DIALOG_REMEMBER_FOREVER:
-                               if (gnome_keyring_set_network_password_sync(keyring,
-                                               g_get_user_name(), NULL, name, "password", service, NULL, 0,
-                                               pass, &itemid) != GNOME_KEYRING_RESULT_OK)
-                               {
-                                       g_warning ("storing password in keyring failed");
-                               }
-                               break;
+                       GtkWidget *dialog;
+                       
+                       dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR,
+                                                 GTK_BUTTONS_OK, 
+                                                 _("Configuration uses ssh-agent for authentication, "
+                                                 "but ssh-agent is not running!"));
+                       gtk_dialog_run (GTK_DIALOG (dialog));
+                       gtk_widget_destroy (dialog);
+                       return 1;
                }
        }
-       printf("password\n%s\n", pass);
        printf("\n\n");
        /* flush output, wait for input */
        fflush(stdout);
index 5a1b176..f827ba2 100644 (file)
                     <property name="column_spacing">6</property>
                     <property name="row_spacing">6</property>
                     <child>
-                      <widget class="GtkLabel" id="address-label">
+                      <widget class="GtkFileChooserButton" id="certificate-button">
+                        <property name="visible">True</property>
+                        <property name="tooltip_text">Gateway certificate to use for gateway authentication.</property>
+                      </widget>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="certificate-label">
                         <property name="visible">True</property>
                         <property name="xalign">0</property>
-                        <property name="label" translatable="yes">_Address:</property>
+                        <property name="label" translatable="yes">C_ertificate:</property>
                         <property name="use_underline">True</property>
-                        <property name="mnemonic_widget">address-entry</property>
+                        <property name="mnemonic_widget">certificate-button</property>
                       </widget>
                       <packing>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
                         <property name="x_options">GTK_FILL</property>
                         <property name="y_options"></property>
                       </packing>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkLabel" id="certificate-label">
+                      <widget class="GtkLabel" id="address-label">
                         <property name="visible">True</property>
                         <property name="xalign">0</property>
-                        <property name="label" translatable="yes">_Certificate:</property>
+                        <property name="label" translatable="yes">_Address:</property>
                         <property name="use_underline">True</property>
-                        <property name="mnemonic_widget">certificate-button</property>
+                        <property name="mnemonic_widget">address-entry</property>
                       </widget>
                       <packing>
-                        <property name="top_attach">1</property>
-                        <property name="bottom_attach">2</property>
                         <property name="x_options">GTK_FILL</property>
                         <property name="y_options"></property>
                       </packing>
                     </child>
-                    <child>
-                      <widget class="GtkFileChooserButton" id="certificate-button">
-                        <property name="visible">True</property>
-                        <property name="tooltip_text">Gateway certificate to use for gateway authentication.</property>
-                      </widget>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
-                        <property name="top_attach">1</property>
-                        <property name="bottom_attach">2</property>
-                      </packing>
-                    </child>
                   </widget>
                 </child>
               </widget>
           </packing>
         </child>
         <child>
-          <widget class="GtkVBox" id="authentication-vbox">
+          <widget class="GtkVBox" id="client-vbox">
             <property name="visible">True</property>
             <property name="spacing">6</property>
             <child>
-              <widget class="GtkLabel" id="authentication-label">
+              <widget class="GtkLabel" id="client-label">
                 <property name="visible">True</property>
                 <property name="xalign">0</property>
-                <property name="label" translatable="yes">&lt;b&gt;Authentication&lt;/b&gt;</property>
+                <property name="label" translatable="yes">&lt;b&gt;Client&lt;/b&gt;</property>
                 <property name="use_markup">True</property>
               </widget>
               <packing>
               </packing>
             </child>
             <child>
-              <widget class="GtkAlignment" id="authentication-aligement">
+              <widget class="GtkAlignment" id="client-aligement">
                 <property name="visible">True</property>
                 <property name="left_padding">12</property>
                 <child>
-                  <widget class="GtkTable" id="authentication-table">
+                  <widget class="GtkTable" id="client-table">
                     <property name="visible">True</property>
-                    <property name="n_rows">2</property>
+                    <property name="n_rows">3</property>
                     <property name="n_columns">2</property>
                     <property name="column_spacing">6</property>
                     <property name="row_spacing">6</property>
                     <child>
-                      <widget class="GtkEntry" id="user-entry">
+                      <widget class="GtkFileChooserButton" id="usercert-button">
                         <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="has_tooltip">True</property>
-                        <property name="tooltip_text">The username (identity) to use for authentication against the gateway.</property>
+                        <property name="tooltip_text">Client certificate to use for client authentication.</property>
                       </widget>
                       <packing>
                         <property name="left_attach">1</property>
                         <property name="right_attach">2</property>
-                        <property name="y_options"></property>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkLabel" id="method-label">
+                      <widget class="GtkLabel" id="usercert-label">
                         <property name="visible">True</property>
                         <property name="xalign">0</property>
-                        <property name="label" translatable="yes">_Method:</property>
+                        <property name="label" translatable="yes">Ce_rtificate:</property>
                         <property name="use_underline">True</property>
-                        <property name="mnemonic_widget">method-combo</property>
+                        <property name="mnemonic_widget">usercert-button</property>
                       </widget>
                       <packing>
-                        <property name="top_attach">1</property>
-                        <property name="bottom_attach">2</property>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
                         <property name="x_options">GTK_FILL</property>
                         <property name="y_options"></property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkComboBox" id="method-combo">
+                      <widget class="GtkEntry" id="user-entry">
                         <property name="visible">True</property>
+                        <property name="can_focus">True</property>
                         <property name="has_tooltip">True</property>
-                        <property name="tooltip_text">Authentication Method to use for authentication against the Gateway. </property>
-                        <property name="items"></property>
+                        <property name="tooltip_text">The username (identity) to use for authentication against the gateway.</property>
                       </widget>
                       <packing>
                         <property name="left_attach">1</property>
                         <property name="right_attach">2</property>
                         <property name="top_attach">1</property>
                         <property name="bottom_attach">2</property>
+                        <property name="y_options"></property>
                       </packing>
                     </child>
                     <child>
                         <property name="mnemonic_widget">user-entry</property>
                       </widget>
                       <packing>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                        <property name="x_options">GTK_FILL</property>
+                        <property name="y_options"></property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="method-label">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Au_thentication:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">method-combo</property>
+                      </widget>
+                      <packing>
                         <property name="x_options">GTK_FILL</property>
                         <property name="y_options"></property>
                       </packing>
                     </child>
+                    <child>
+                      <widget class="GtkComboBox" id="method-combo">
+                        <property name="visible">True</property>
+                        <property name="has_tooltip">True</property>
+                        <property name="tooltip_text">Authentication Method to use for authentication against the Gateway. </property>
+                        <property name="items"></property>
+                      </widget>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                      </packing>
+                    </child>
                   </widget>
                 </child>
               </widget>
index db8d2fb..8d97a55 100644 (file)
@@ -119,26 +119,38 @@ check_validity (StrongswanPluginUiWidget *self, GError **error)
                             "address");
                return FALSE;
        }
-
-       widget = glade_xml_get_widget (priv->xml, "user-entry");
-       str = (char *) gtk_entry_get_text (GTK_ENTRY (widget));
-       if (!str || !strlen (str)) {
-               g_set_error (error,
-                            STRONGSWAN_PLUGIN_UI_ERROR,
-                            STRONGSWAN_PLUGIN_UI_ERROR_INVALID_PROPERTY,
-                            "user");
-               return FALSE;
-       }
-
        return TRUE;
 }
 
 static void
-stuff_changed_cb (GtkWidget *widget, gpointer user_data)
+settings_changed_cb (GtkWidget *widget, gpointer user_data)
 {
        g_signal_emit_by_name (STRONGSWAN_PLUGIN_UI_WIDGET (user_data), "changed");
 }
 
+static void
+method_changed_cb (GtkWidget *widget, gpointer user_data)
+{
+       StrongswanPluginUiWidget *self = STRONGSWAN_PLUGIN_UI_WIDGET (user_data);
+       StrongswanPluginUiWidgetPrivate *priv = STRONGSWAN_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
+       
+       if (gtk_combo_box_get_active (GTK_COMBO_BOX (widget)) == 0)
+       {
+               gtk_widget_show (glade_xml_get_widget (priv->xml, "usercert-label"));
+               gtk_widget_show (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"));
+       }
+       else
+       {
+               gtk_widget_hide (glade_xml_get_widget (priv->xml, "usercert-label"));
+               gtk_widget_hide (glade_xml_get_widget (priv->xml, "usercert-button"));
+               gtk_widget_show (glade_xml_get_widget (priv->xml, "user-label"));
+               gtk_widget_show (glade_xml_get_widget (priv->xml, "user-entry"));
+       }
+       g_signal_emit_by_name (STRONGSWAN_PLUGIN_UI_WIDGET (user_data), "changed");
+}
+
 static gboolean
 init_plugin_ui (StrongswanPluginUiWidget *self, NMConnection *connection, GError **error)
 {
@@ -149,83 +161,87 @@ init_plugin_ui (StrongswanPluginUiWidget *self, NMConnection *connection, GError
        gboolean active;
        
        settings = NM_SETTING_VPN(nm_connection_get_setting(connection, NM_TYPE_SETTING_VPN));
-       if (!settings)
-               return FALSE;
        widget = glade_xml_get_widget (priv->xml, "address-entry");
-       if (!widget)
-               return FALSE;
        value = g_hash_table_lookup (settings->data, "address");
        if (value)
                gtk_entry_set_text (GTK_ENTRY (widget), value);
-       g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (stuff_changed_cb), self);
+       g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (settings_changed_cb), self);
 
        widget = glade_xml_get_widget (priv->xml, "certificate-button");
-       if (!widget)
-               return FALSE;
        value = g_hash_table_lookup (settings->data, "certificate");
        if (value)
                gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget), value);
-       g_signal_connect (G_OBJECT (widget), "selection-changed", G_CALLBACK (stuff_changed_cb), self);
+       g_signal_connect (G_OBJECT (widget), "selection-changed", G_CALLBACK (settings_changed_cb), self);
 
+       widget = glade_xml_get_widget (priv->xml, "user-label");
+       gtk_widget_set_no_show_all (widget, TRUE);
        widget = glade_xml_get_widget (priv->xml, "user-entry");
-       if (!widget)
-               return FALSE;
+       gtk_widget_set_no_show_all (widget, TRUE);
        value = g_hash_table_lookup (settings->data, "user");
        if (value)
                gtk_entry_set_text (GTK_ENTRY (widget), value);
-       g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (stuff_changed_cb), self);
+       g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (settings_changed_cb), self);
 
        widget = glade_xml_get_widget (priv->xml, "method-combo");
-       if (!widget)
-               return FALSE;
+       gtk_combo_box_append_text (GTK_COMBO_BOX (widget), _("Certificate/ssh-agent"));
        gtk_combo_box_append_text (GTK_COMBO_BOX (widget), _("EAP"));
-       /* TODO: PSK is disabled until we have the possibility to enforce strong
-        * secrets. 
-       gtk_combo_box_append_text (GTK_COMBO_BOX (widget), _("Preshared Key")); */
        value = g_hash_table_lookup (settings->data, "method");
        if (value) {
-               if (g_strcasecmp (value, "eap") == 0) {
+               if (g_strcasecmp (value, "agent") == 0) {
                        gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
                }
-               if (g_strcasecmp (value, "psk") == 0) {
+               if (g_strcasecmp (value, "eap") == 0) {
                        gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 1);
                }
        }
-       g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (stuff_changed_cb), self);
+       g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (method_changed_cb), self);
        if (gtk_combo_box_get_active (GTK_COMBO_BOX (widget)) == -1)
-       {       /* default to EAP */    
+       {
                gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
-       }       
+       }
+       if (gtk_combo_box_get_active (GTK_COMBO_BOX (widget)) != 0)
+       {
+               gtk_widget_hide (glade_xml_get_widget (priv->xml, "usercert-label"));
+               gtk_widget_hide (glade_xml_get_widget (priv->xml, "usercert-button"));
+       }
+       else
+       {
+               gtk_widget_hide (glade_xml_get_widget (priv->xml, "user-label"));
+               gtk_widget_hide (glade_xml_get_widget (priv->xml, "user-entry"));
+       }
+       
+       widget = glade_xml_get_widget (priv->xml, "usercert-label");
+       gtk_widget_set_no_show_all (widget, TRUE);
+       widget = glade_xml_get_widget (priv->xml, "usercert-button");
+       gtk_widget_set_no_show_all (widget, TRUE);
+       value = g_hash_table_lookup (settings->data, "usercert");
+       if (value)
+               gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget), value);
+       g_signal_connect (G_OBJECT (widget), "selection-changed", G_CALLBACK (settings_changed_cb), self);
        
        widget = glade_xml_get_widget (priv->xml, "virtual-check");
-       if (!widget)
-               return FALSE;
        value = g_hash_table_lookup (settings->data, "virtual");
        if (value && strcmp(value, "yes") == 0)
        {
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
        }
-       g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (stuff_changed_cb), self);
+       g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (settings_changed_cb), self);
        
        widget = glade_xml_get_widget (priv->xml, "encap-check");
-       if (!widget)
-               return FALSE;
        value = g_hash_table_lookup (settings->data, "encap");
        if (value && strcmp(value, "yes") == 0)
        {
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
        }
-       g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (stuff_changed_cb), self);
+       g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (settings_changed_cb), self);
        
        widget = glade_xml_get_widget (priv->xml, "ipcomp-check");
-       if (!widget)
-               return FALSE;
        value = g_hash_table_lookup (settings->data, "ipcomp");
        if (value && strcmp(value, "yes") == 0)
        {
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
        }
-       g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (stuff_changed_cb), self);
+       g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (settings_changed_cb), self);
 
        return TRUE;
 }
@@ -270,24 +286,26 @@ update_connection (NMVpnPluginUiWidgetInterface *iface,
        if (str) {
                g_hash_table_insert (settings->data, g_strdup ("certificate"), g_strdup(str));
        }
-
-       widget = glade_xml_get_widget (priv->xml, "user-entry");
-       str = (char *) gtk_entry_get_text (GTK_ENTRY (widget));
-       if (str && strlen (str)) {
-               g_hash_table_insert (settings->data, g_strdup ("user"), g_strdup(str));
-       }
-
+       
        widget = glade_xml_get_widget (priv->xml, "method-combo");
        switch (gtk_combo_box_get_active (GTK_COMBO_BOX (widget)))
        {
+               case 0:
                default:
-                       str = "eap";
+                       widget = glade_xml_get_widget (priv->xml, "usercert-button");
+                       str = (char *) gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
+                       if (str) {
+                               g_hash_table_insert (settings->data, g_strdup ("usercert"), g_strdup(str));
+                       }
+                       str = "agent";
                        break;
                case 1:
-                       str = "psk";
-                       break;
-               case 2:
-                       str = "pubkey";
+                       widget = glade_xml_get_widget (priv->xml, "user-entry");
+                       str = (char *) gtk_entry_get_text (GTK_ENTRY (widget));
+                       if (str && strlen (str)) {
+                               g_hash_table_insert (settings->data, g_strdup ("user"), g_strdup(str));
+                       }
+                       str = "eap";
                        break;
        }
        g_hash_table_insert (settings->data, g_strdup ("method"), g_strdup(str));
index 0052341..f165653 100644 (file)
@@ -50,18 +50,66 @@ struct private_nm_creds_t {
        char *pass;
        
        /**
+        * users certificate
+        */
+       certificate_t *usercert;
+       
+       /**
+        * users private key
+        */
+       private_key_t *key;
+       
+       /**
         * read/write lock
         */
        pthread_rwlock_t lock;
 };
 
 /**
+ * Enumerator for user certificate
+ */
+static enumerator_t *create_usercert_enumerator(private_nm_creds_t *this,
+                                                       certificate_type_t cert, key_type_t key)
+{
+       public_key_t *public;
+       
+       if (cert != CERT_ANY && cert != this->usercert->get_type(this->usercert))
+       {
+               return NULL;
+       }
+       if (key != KEY_ANY)
+       {       
+               public = this->usercert->get_public_key(this->usercert);
+               if (!public)
+               {
+                       return NULL;
+               }
+               if (public->get_type(public) != key)
+               {
+                       public->destroy(public);
+                       return NULL;
+               }
+               public->destroy(public);
+       }
+       pthread_rwlock_rdlock(&this->lock);
+       return enumerator_create_cleaner(
+                                                               enumerator_create_single(this->usercert, NULL),
+                                                               (void*)pthread_rwlock_unlock, &this->lock);
+}
+
+/**
  * Implements credential_set_t.create_cert_enumerator
  */
 static enumerator_t* create_cert_enumerator(private_nm_creds_t *this,
                                                        certificate_type_t cert, key_type_t key,
                                                        identification_t *id, bool trusted)
 {
+       if (id && this->usercert &&
+               id->equals(id, this->usercert->get_subject(this->usercert)))
+       {
+               return create_usercert_enumerator(this, cert, key);
+       }
+
        if (!this->cert)
        {
                return NULL;
@@ -96,6 +144,35 @@ static enumerator_t* create_cert_enumerator(private_nm_creds_t *this,
 }
 
 /**
+ * Implements credential_set_t.create_cert_enumerator
+ */
+static enumerator_t* create_private_enumerator(private_nm_creds_t *this,
+                                                                               key_type_t type, identification_t *id)
+{
+       if (this->key == NULL)
+       {
+               return NULL;
+       }
+       if (type != KEY_ANY && type != this->key->get_type(this->key))
+       {
+               return NULL;
+       }
+       if (id && id->get_type(id) != ID_ANY)
+       {
+               identification_t *keyid;
+               
+               keyid = this->key->get_id(this->key, id->get_type(id));
+               if (!keyid || !keyid->equals(keyid, id))
+               {
+                       return NULL;
+               }
+       }
+       pthread_rwlock_rdlock(&this->lock);
+       return enumerator_create_cleaner(enumerator_create_single(this->key, NULL),
+                                                                        (void*)pthread_rwlock_unlock, &this->lock);
+}
+
+/**
  * shared key enumerator implementation
  */
 typedef struct {
@@ -179,7 +256,7 @@ static void set_certificate(private_nm_creds_t *this, certificate_t *cert)
 /**
  * Implementation of nm_creds_t.set_password
  */
-static void set_password(private_nm_creds_t *this, identification_t *id,
+static void set_username_password(private_nm_creds_t *this, identification_t *id,
                                                 char *password)
 {
        pthread_rwlock_wrlock(&this->lock);
@@ -188,18 +265,47 @@ static void set_password(private_nm_creds_t *this, identification_t *id,
        this->user = identification_create_from_encoding(ID_EAP,
                                                                                                         id->get_encoding(id));
        free(this->pass);
-       this->pass = strdup(password);
+       this->pass = password ? strdup(password) : NULL;
        pthread_rwlock_unlock(&this->lock);
 }
 
 /**
- * Implementation of nm_creds_t.destroy
+ * Implementation of nm_creds_t.set_cert_and_key
  */
-static void destroy(private_nm_creds_t *this)
+static void set_cert_and_key(private_nm_creds_t *this, certificate_t *cert,    
+                                                        private_key_t *key)
+{
+       pthread_rwlock_wrlock(&this->lock);
+       DESTROY_IF(this->key);
+       DESTROY_IF(this->usercert);
+       this->key = key;
+       this->usercert = cert;
+       pthread_rwlock_unlock(&this->lock);
+}      
+
+/**
+ * Implementation of nm_creds_t.clear
+ */
+static void clear(private_nm_creds_t *this)
 {
        DESTROY_IF(this->cert);
        DESTROY_IF(this->user);
        free(this->pass);
+       DESTROY_IF(this->usercert);
+       DESTROY_IF(this->key);
+       this->key = NULL;
+       this->usercert = NULL;
+       this->pass = NULL;
+       this->cert = NULL;
+       this->user = NULL;
+}
+
+/**
+ * Implementation of nm_creds_t.destroy
+ */
+static void destroy(private_nm_creds_t *this)
+{
+       clear(this);
        pthread_rwlock_destroy(&this->lock);
        free(this);
 }
@@ -211,13 +317,15 @@ nm_creds_t *nm_creds_create()
 {
        private_nm_creds_t *this = malloc_thing(private_nm_creds_t);
        
-       this->public.set.create_private_enumerator = (void*)return_null;
+       this->public.set.create_private_enumerator = (void*)create_private_enumerator;
        this->public.set.create_cert_enumerator = (void*)create_cert_enumerator;
        this->public.set.create_shared_enumerator = (void*)create_shared_enumerator;
        this->public.set.create_cdp_enumerator = (void*)return_null;
        this->public.set.cache_cert = (void*)nop;
        this->public.set_certificate = (void(*)(nm_creds_t*, certificate_t *cert))set_certificate;
-       this->public.set_password = (void(*)(nm_creds_t*, identification_t *id, char *password))set_password;
+       this->public.set_username_password = (void(*)(nm_creds_t*, identification_t *id, char *password))set_username_password;
+       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;
        
        pthread_rwlock_init(&this->lock, NULL);
@@ -225,6 +333,8 @@ nm_creds_t *nm_creds_create()
        this->cert = NULL;
        this->user = NULL;
        this->pass = NULL;
+       this->usercert = NULL;
+       this->key = NULL;
        
        return &this->public;
 }
index 737e80a..4bcc321 100644 (file)
@@ -50,8 +50,21 @@ struct nm_creds_t {
         * @param id            ID of the user
         * @param password      password to use for authentication
         */
-       void (*set_password)(nm_creds_t *this, identification_t *id, char *password);
-       
+       void (*set_username_password)(nm_creds_t *this, identification_t *id,
+                                                                 char *password);
+       /**
+        * Set the certificate and private key to use for client authentication.
+        *
+        * @param cert          client certificate
+        * @param key           associated private key
+        */
+       void (*set_cert_and_key)(nm_creds_t *this, certificate_t *cert,
+                                                        private_key_t *key);
+       /**
+        * Clear the stored credentials.
+        */
+       void (*clear)(nm_creds_t *this);
+
        /**
         * Destroy a nm_creds instance.
         */
index f906dca..1336293 100644 (file)
@@ -22,6 +22,8 @@
 #include <daemon.h>
 #include <processing/jobs/callback_job.h>
 
+#define CAP_DAC_OVERRIDE 1
+
 typedef struct private_nm_plugin_t private_nm_plugin_t;
 
 /**
@@ -106,6 +108,9 @@ plugin_t *plugin_create()
                return NULL;
        }
        
+       /* bypass file permissions to read from users ssh-agent */
+       charon->keep_cap(charon, CAP_DAC_OVERRIDE);
+       
        charon->processor->queue_job(charon->processor, 
                 (job_t*)callback_job_create((callback_job_cb_t)run, this, NULL, NULL));
        
index 37db6e0..beda6ee 100644 (file)
@@ -183,19 +183,6 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection,
        
        DBG1(DBG_CFG, "received NetworkManager connection: %s",
                 nm_setting_to_string(NM_SETTING(settings)));
-       str = g_hash_table_lookup(settings->data, "user");
-       if (!str)
-       {
-               g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
-                                   "Username missing.");
-               return FALSE;
-       }
-       user = identification_create_from_string(str);
-       if (!user)
-       {       /* fallback to ID_KEY_ID for non-qualified usernames */
-               user = identification_create_from_encoding(ID_KEY_ID,
-                                                                                               chunk_create(str, strlen(str)));
-       }
        address = g_hash_table_lookup(settings->data, "address");
        if (!address || !*address)
        {
@@ -216,7 +203,7 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection,
                {
                        auth_class = AUTH_CLASS_PSK;
                }
-               else if (streq(str, "pubkey"))
+               else if (streq(str, "agent"))
                {
                        auth_class = AUTH_CLASS_PUBKEY;
                }
@@ -226,7 +213,9 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection,
         * Register credentials
         */
        creds = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin)->creds;
-        
+       creds->clear(creds);
+       
+       /* gateway cert */
        str = g_hash_table_lookup(settings->data, "certificate");
        if (str)
        {
@@ -237,14 +226,69 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection,
        if (!cert)
        {
                g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
-                                   "Loading certificate failed.");
+                                   "Loading gateway certificate failed.");
                return FALSE;
        }
        gateway = cert->get_subject(cert);
-       str = g_hash_table_lookup(settings->data, "password");
-       if (str)
+       
+       if (auth_class == AUTH_CLASS_EAP)
        {
-               creds->set_password(creds, user, str);
+               /* username/password authentication ... */
+               str = g_hash_table_lookup(settings->data, "user");
+               if (str)
+               {
+                       user = identification_create_from_string(str);
+                       str = g_hash_table_lookup(settings->data, "password");
+                       creds->set_username_password(creds, user, str);
+               }
+       }
+       
+       if (auth_class == AUTH_CLASS_PUBKEY)
+       {
+               /* ... or certificate/private key authenitcation */
+               str = g_hash_table_lookup(settings->data, "usercert");
+               if (str)
+               {
+                       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);       
+                       str = g_hash_table_lookup(settings->data, "agent");
+                       if (str && cert)
+                       {
+                               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)
+                       {
+                               user = cert->get_subject(cert);
+                               user = user->clone(user);
+                               creds->set_cert_and_key(creds, cert, private);
+                       }
+                       else
+                       {
+                               DESTROY_IF(cert);
+                               g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+                                                       "Loading user certificate/private key failed.");
+                               return FALSE;
+                       }
+               }
+       }
+       
+       if (!user)
+       {
+               g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+                                       "Configuration parameters missing.");
+               return FALSE;
        }
        
        /**
@@ -255,7 +299,7 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection,
        peer_cfg = peer_cfg_create(CONFIG_NAME, 2, ike_cfg,
                                        user, gateway->clone(gateway),
                                        CERT_SEND_IF_ASKED, UNIQUE_REPLACE, 1, /* keyingtries */
-                                       18000, 0, /* rekey 5h, reauth none */
+                                       36000, 0, /* rekey 10h, reauth none */
                                        600, 600, /* jitter, over 10min */
                                        TRUE, 0, /* mobike, DPD */
                                        virtual ? host_create_from_string("0.0.0.0", 0) : NULL,
@@ -263,10 +307,10 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection,
        auth = peer_cfg->get_auth(peer_cfg);
        auth->add_item(auth, AUTHN_AUTH_CLASS, &auth_class);
        child_cfg = child_cfg_create(CONFIG_NAME,
-                                                                3600, 3000, /* lifetime 1h, rekey 50min */
+                                                                10800, 10200, /* lifetime 3h, rekey 2h50min */
                                                                 300, /* jitter 5min */
                                                                 NULL, TRUE, MODE_TUNNEL, /* updown, hostaccess */
-                                                                ACTION_NONE, ACTION_NONE, ipcomp);
+                                                                ACTION_NONE, ACTION_RESTART, ipcomp);
        child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
        ts = traffic_selector_create_dynamic(0, 0, 65535);
        child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
@@ -315,15 +359,30 @@ static gboolean need_secrets(NMVPNPlugin *plugin, NMConnection *connection,
                                                         char **setting_name, GError **error)
 {
        NMSettingVPN *settings;
+       char *method;
        
        settings = NM_SETTING_VPN(nm_connection_get_setting(connection,
                                                                                                                NM_TYPE_SETTING_VPN));
-       if (!g_hash_table_lookup(settings->data, "password"))
+       method = g_hash_table_lookup(settings->data, "method");
+       if (method)
        {
-               *setting_name = NM_SETTING_VPN_SETTING_NAME;
-               return TRUE;
+               if (streq(method, "eap"))
+               {
+                       if (g_hash_table_lookup(settings->data, "password"))
+                       {
+                               return FALSE;
+                       }
+               }
+               else if (streq(method, "agent"))
+               {
+                       if (g_hash_table_lookup(settings->data, "agent"))
+                       {
+                               return FALSE;
+                       }
+               }
        }
-       return FALSE;
+       *setting_name = NM_SETTING_VPN_SETTING_NAME;
+       return TRUE;
 }
 
 /**