[network-manager-openconnect/NM_0_8] Add gnome-keyring support to NM_0_8 branch



commit 500d207180829d537089778e618f5bbc4727ae02
Author: Murilo Opsfelder Araujo <muriloo linux vnet ibm com>
Date:   Tue Jun 4 16:20:52 2013 -0300

    Add gnome-keyring support to NM_0_8 branch
    
    Backported the following commits:
    
    72f391a24487ab442290c47e2e670f52c41b0608 Use gnome-keyring for password fields
    db47228f8e73b41549f89d941d52ae8e3acafb62 gnome-keyring: use the vpn_uuid instead of the host
    d9f4b023fd4211affdd5a1b69305a4de78ac60f3 Only store passwords in keyring upon success
    31a0e8b8cf78206cda34ab593ba90c3d28ebbc29 Handle string == NULL in gnome-keyring callback
    a37b1f725c460b5237ed6ab36a961c2e3f1c8145 Remove passwords from gnome-keyring when user disables 'save 
passwords'
    96a0741b154dbb3a629038b34c89469b9bd11adb Fix nm_process_auth_form() to clear 'form_grabbed' and cancel 
keyring ops
    eee2f4fd0f1aa3e2439c9ca337b00f5a382d9b8e When entry with focus gets password from gnome-keyring, select 
the full region

 auth-dialog/Makefile.am |    3 +-
 auth-dialog/main.c      |  220 +++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 207 insertions(+), 16 deletions(-)
---
diff --git a/auth-dialog/Makefile.am b/auth-dialog/Makefile.am
index 3f36f26..25db792 100644
--- a/auth-dialog/Makefile.am
+++ b/auth-dialog/Makefile.am
@@ -5,6 +5,7 @@ libexec_PROGRAMS = nm-openconnect-auth-dialog
 nm_openconnect_auth_dialog_CPPFLAGS = \
        $(NETWORKMANAGER_CFLAGS) \
        $(LIBXML_CFLAGS) \
+       $(GNOMEKEYRING_CFLAGS) \
        $(GTHREAD_CFLAGS) \
        $(GTK_CFLAGS) \
        $(GCONF_CFLAGS) \
@@ -26,9 +27,9 @@ nm_openconnect_auth_dialog_LDADD = \
        $(GTK_LIBS) \
        $(NETWORKMANAGER_LIBS) \
        $(LIBXML_LIBS) \
+       $(GNOMEKEYRING_LIBS) \
        $(GTHREAD_LIBS) \
        $(GCONF_LIBS) \
        $(OPENCONNECT_LIBS)
 
 CLEANFILES = *~
-
diff --git a/auth-dialog/main.c b/auth-dialog/main.c
index 5e08b7a..54e357d 100644
--- a/auth-dialog/main.c
+++ b/auth-dialog/main.c
@@ -85,6 +85,8 @@ g_unix_set_fd_nonblocking (gint     fd,
 }
 #endif /* GLIB_CHECK_VERSION(2,30,0) */
 
+#include <gnome-keyring.h>
+
 #include "auth-dlg-settings.h"
 
 #include "openconnect.h"
@@ -112,6 +114,20 @@ static char *_config_path;
 #include <openssl/ui.h>
 #endif
 
+static const GnomeKeyringPasswordSchema OPENCONNECT_SCHEMA_DEF = {
+       GNOME_KEYRING_ITEM_GENERIC_SECRET,
+       {
+               {"vpn_uuid", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING},
+               {"auth_id", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING},
+               {"label", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING},
+               {NULL, 0}
+       }
+};
+
+const GnomeKeyringPasswordSchema *OPENCONNECT_SCHEMA = &OPENCONNECT_SCHEMA_DEF;
+
+static void got_keyring_pw(GnomeKeyringResult result, const char *string, gpointer data);
+
 static char *lasthost;
 
 typedef struct vpnhost {
@@ -135,8 +151,50 @@ struct gconf_key {
        struct gconf_key *next;
 };
 
+/* This struct holds all information we need to add a password to
+ * gnome-keyring. It’s used in success_passwords. */
+struct keyring_password {
+       char *description;
+       char *password;
+       char *vpn_uuid;
+       char *auth_id;
+       char *label;
+};
+
+static void keyring_password_free(gpointer data);
+static void keyring_store_passwords(gpointer key, gpointer value, gpointer user_data);
+
+static void keyring_password_free(gpointer data)
+{
+       struct keyring_password *kp = (struct keyring_password*)data;
+       g_free(kp->description);
+       g_free(kp->password);
+       g_free(kp->vpn_uuid);
+       g_free(kp->auth_id);
+       g_free(kp->label);
+       g_free(kp);
+}
+
+static void keyring_store_passwords(gpointer key, gpointer value, gpointer user_data)
+{
+       struct keyring_password *kp = (struct keyring_password*)value;
+       gnome_keyring_store_password_sync (
+               OPENCONNECT_SCHEMA,
+               GNOME_KEYRING_DEFAULT,
+               kp->description,
+               kp->password,
+               "vpn_uuid", kp->vpn_uuid,
+               "auth_id", kp->auth_id,
+               "label", kp->label,
+               NULL
+               );
+}
+
 typedef struct auth_ui_data {
        char *vpn_name;
+       char *vpn_uuid;
+       GHashTable *secrets;
+       GHashTable *success_passwords;
        struct openconnect_info *vpninfo;
        struct gconf_key *success_keys;
        GtkWidget *dialog;
@@ -148,6 +206,7 @@ typedef struct auth_ui_data {
        GtkWidget *cancel_button;
        GtkWidget *login_button;
        GtkWidget *last_notice_icon;
+       GtkWidget *savepass;
        GtkTextBuffer *log;
 
        int retval;
@@ -251,6 +310,8 @@ static void ssl_box_clear(auth_ui_data *ui_data)
 
 typedef struct ui_fragment_data {
        GtkWidget *widget;
+       GtkWidget *entry;
+       gpointer find_request;
        auth_ui_data *ui_data;
 #ifdef OPENCONNECT_OPENSSL
        UI_STRING *uis;
@@ -355,7 +416,7 @@ static gboolean ui_write_prompt (ui_fragment_data *data)
        if (data->uis) {
                label = UI_get0_output_string(data->uis);
                visible = UI_get_input_flags(data->uis) & UI_INPUT_FLAG_ECHO;
-       } else 
+       } else
 #endif
        {
                label = data->opt->label;
@@ -371,6 +432,7 @@ static gboolean ui_write_prompt (ui_fragment_data *data)
 
        entry = gtk_entry_new();
        gtk_box_pack_end(GTK_BOX(hbox), entry, FALSE, FALSE, 0);
+       data->entry = entry;
        if (!visible)
                gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
        if (data->entry_text)
@@ -404,7 +466,7 @@ static gboolean ui_add_select (ui_fragment_data *data)
        gtk_box_pack_end(GTK_BOX(hbox), combo, FALSE, FALSE, 0);
        for (i = 0; i < sopt->nr_choices; i++) {
                gtk_combo_box_append_text(GTK_COMBO_BOX(combo), sopt->choices[i].label);
-               if (data->entry_text && 
+               if (data->entry_text &&
                    !strcmp(data->entry_text, sopt->choices[i].name)) {
                        gtk_combo_box_set_active(GTK_COMBO_BOX(combo), i);
                        g_free(data->entry_text);
@@ -412,14 +474,14 @@ static gboolean ui_add_select (ui_fragment_data *data)
                }
        }
        if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) {
-               gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); 
+               gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
                data->entry_text = sopt->choices[0].name;
        }
 
        if (g_queue_peek_tail(ui_data->form_entries) == data)
                gtk_widget_grab_focus (combo);
        g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(combo_changed), data);
-       /* Hook up the 'show' signal to ensure that we override prompts on 
+       /* Hook up the 'show' signal to ensure that we override prompts on
           UI elements which may be coming later. */
        g_signal_connect(G_OBJECT(combo), "show", G_CALLBACK(combo_changed), data);
 
@@ -527,6 +589,10 @@ static int ui_flush(UI* ui)
        while (!g_queue_is_empty (ui_data->form_entries)) {
                ui_fragment_data *data;
                data = g_queue_pop_tail (ui_data->form_entries);
+
+               if (data->find_request)
+                       gnome_keyring_cancel_request(data->find_request);
+
                if (data->entry_text) {
                        UI_set_result(ui, data->uis, data->entry_text);
                }
@@ -598,6 +664,31 @@ static char *find_form_answer(struct oc_auth_form *form, struct oc_form_opt *opt
        return result;
 }
 
+/* Callback which is called when we got a reply from gnome-keyring for any
+ * password field. Updates the contents of the password field unless the user
+ * entered anything in the meantime. */
+static void got_keyring_pw(GnomeKeyringResult result, const char *string, gpointer userdata)
+{
+       ui_fragment_data *data = (ui_fragment_data*)userdata;
+       if (string != NULL) {
+               if (data->entry) {
+                       if (!g_ascii_strcasecmp("", gtk_entry_get_text(GTK_ENTRY(data->entry)))) {
+                               gtk_entry_set_text(GTK_ENTRY(data->entry), string);
+                               if (gtk_widget_has_focus(data->entry))
+                                       gtk_editable_select_region(GTK_EDITABLE(data->entry), 0, -1);
+                       }
+               } else
+                       data->entry_text = g_strdup (string);
+
+               /* Mark 'Save passwords' if a password is found in keyring */
+               gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->ui_data->savepass), 1);
+       }
+
+       /* zero the find request so that we don’t attempt to cancel it when
+        * closing the dialog */
+       data->find_request = NULL;
+}
+
 /* This part for processing forms from openconnect directly, rather than
    through the SSL UI abstraction (which doesn't allow 'select' options) */
 
@@ -630,7 +721,7 @@ static gboolean ui_form (struct oc_auth_form *form)
                data = g_slice_new0 (ui_fragment_data);
                data->ui_data = ui_data;
                data->opt = opt;
-               
+
                if (opt->type == OC_FORM_OPT_PASSWORD ||
                    opt->type == OC_FORM_OPT_TEXT) {
                        g_mutex_lock (ui_data->form_mutex);
@@ -638,6 +729,18 @@ static gboolean ui_form (struct oc_auth_form *form)
                        g_mutex_unlock (ui_data->form_mutex);
                        if (opt->type != OC_FORM_OPT_PASSWORD)
                                data->entry_text = find_form_answer(form, opt);
+                       else {
+                               data->find_request = gnome_keyring_find_password(
+                                       OPENCONNECT_SCHEMA,
+                                       got_keyring_pw,
+                                       data,
+                                       NULL,
+                                       "vpn_uuid", ui_data->vpn_uuid,
+                                       "auth_id", form->auth_id,
+                                       "label", data->opt->name,
+                                       NULL
+                                       );
+                       }
 
                        ui_write_prompt(data);
                } else if (opt->type == OC_FORM_OPT_SELECT) {
@@ -650,7 +753,7 @@ static gboolean ui_form (struct oc_auth_form *form)
                } else
                        g_slice_free (ui_fragment_data, data);
        }
-       
+
        return ui_show(ui_data);
 }
 
@@ -692,14 +795,28 @@ static int nm_process_auth_form (void *cbdata, struct oc_auth_form *form)
                                        keyname = g_strdup_printf("form:%s:%s", form->auth_id, 
data->opt->name);
                                        remember_gconf_key(ui_data, keyname, strdup(data->entry_text));
                                }
+
+                               if (data->opt->type == OC_FORM_OPT_PASSWORD) {
+                                       /* store the password in gnome-keyring */
+                                       //int result;
+                                       struct keyring_password *kp = g_new(struct keyring_password, 1);
+                                       kp->description = g_strdup_printf(_("OpenConnect: %s: %s:%s"), 
ui_data->vpn_name, form->auth_id, data->opt->name);
+                                       kp->password = g_strdup(data->entry_text);
+                                       kp->vpn_uuid = g_strdup(ui_data->vpn_uuid);
+                                       kp->auth_id = g_strdup(form->auth_id);
+                                       kp->label = g_strdup(data->opt->name);
+
+                                       g_hash_table_insert (ui_data->success_passwords,
+                                                            g_strdup(kp->description), kp);
+                               }
                        }
                        g_slice_free (ui_fragment_data, data);
                }
        }
 
-
+       ui_data->form_grabbed = 0;
        g_mutex_unlock(ui_data->form_mutex);
-       
+
        /* -1 = cancel,
         *  0 = failure,
         *  1 = success */
@@ -930,6 +1047,16 @@ static int get_gconf_autoconnect(GConfClient *gcl, char *config_path)
        return ret;
 }
 
+static gboolean get_save_passwords(GHashTable *secrets)
+{
+       char *save = g_hash_table_lookup (secrets, "save_passwords");
+
+       if (save && !strcmp(save, "yes"))
+               return TRUE;
+
+       return FALSE;
+}
+
 static int parse_xmlconfig(char *xmlconfig)
 {
        xmlDocPtr xml_doc;
@@ -1131,12 +1258,52 @@ static int write_new_config(void *cbdata, char *buf, int buflen)
 
 static void autocon_toggled(GtkWidget *widget)
 {
+       auth_ui_data *ui_data = _ui_data; /* FIXME global */
+       gchar *enabled = NULL;
        char *config_path = _config_path; /* FIXME global */
        GConfClient *gcl = _gcl; /* FIXME global */
-       int enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
        char *key = g_strdup_printf("%s/vpn/autoconnect", config_path);
 
        gconf_client_set_string(gcl, key, enabled ? "yes" : "no", NULL);
+       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)))
+               enabled = g_strdup ("yes");
+       else
+               enabled = g_strdup ("no");
+
+       g_hash_table_insert (ui_data->secrets, g_strdup ("autoconnect"), enabled);
+}
+
+/* gnome_keyring_delete_password() only deletes one matching password, so
+ * keep doing it until it doesn't succeed. The ui_data is essentially
+ * permanent anyway so no need to worry about its lifetime. */
+static void delete_next_password(GnomeKeyringResult result, gpointer data)
+{
+       auth_ui_data *ui_data = data;
+
+       if (result == GNOME_KEYRING_RESULT_OK) {
+               gnome_keyring_delete_password(OPENCONNECT_SCHEMA,
+                                             delete_next_password,
+                                             ui_data, NULL,
+                                             "vpn_uuid", ui_data->vpn_uuid,
+                                             NULL);
+       }
+}
+
+static void savepass_toggled(GtkWidget *widget, auth_ui_data *ui_data)
+{
+       gchar *enabled;
+
+       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)))
+               enabled = g_strdup ("yes");
+       else {
+               enabled = g_strdup ("no");
+               gnome_keyring_delete_password(OPENCONNECT_SCHEMA,
+                                             delete_next_password,
+                                             ui_data, NULL,
+                                             "vpn_uuid", ui_data->vpn_uuid,
+                                             NULL);
+       }
+       g_hash_table_insert (ui_data->secrets, g_strdup ("save_passwords"), enabled);
 }
 
 static void scroll_log(GtkTextBuffer *log, GtkTextView *view)
@@ -1216,12 +1383,13 @@ static gboolean cookie_obtained(auth_ui_data *ui_data)
                /* user has chosen a new host, start from beginning */
                while (ui_data->success_keys) {
                        struct gconf_key *k = ui_data->success_keys;
-                       
+
                        ui_data->success_keys = k->next;
                        g_free(k->key);
                        g_free(k->value);
                        g_free(k);
-               }                       
+               }
+               g_hash_table_remove_all (ui_data->success_passwords);
                connect_host(ui_data);
                return FALSE;
        }
@@ -1262,6 +1430,14 @@ static gboolean cookie_obtained(auth_ui_data *ui_data)
                openconnect_clear_cookie(ui_data->vpninfo);
                printf("\n\n");
                fflush(stdout);
+
+               if (get_save_passwords (ui_data->secrets)) {
+                       g_hash_table_foreach(
+                               ui_data->success_passwords,
+                               keyring_store_passwords,
+                               NULL);
+               }
+
                ui_data->retval = 0;
 
                gtk_main_quit();
@@ -1278,7 +1454,9 @@ static gboolean cookie_obtained(auth_ui_data *ui_data)
                g_free(k->key);
                g_free(k->value);
                g_free(k);
-       }                       
+       }
+
+       g_hash_table_remove_all (ui_data->success_passwords);
 
        return FALSE;
 }
@@ -1494,6 +1672,13 @@ static void build_main_dialog(auth_ui_data *ui_data)
        gtk_widget_set_sensitive (ui_data->cancel_button, FALSE);
        gtk_widget_show(ui_data->cancel_button);
 
+       ui_data->savepass = gtk_check_button_new_with_label(_("Save passwords"));
+       gtk_box_pack_start(GTK_BOX(hbox), ui_data->savepass, FALSE, FALSE, 0);
+       if (get_save_passwords (ui_data->secrets))
+               gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui_data->savepass), 1);
+       g_signal_connect(ui_data->savepass, "toggled", G_CALLBACK(savepass_toggled), ui_data);
+       gtk_widget_show(ui_data->savepass);
+
        exp = gtk_expander_new(_("Log"));
        gtk_box_pack_end(GTK_BOX(vbox), exp, FALSE, FALSE, 0);
        gtk_widget_show(exp);
@@ -1519,7 +1704,7 @@ static void build_main_dialog(auth_ui_data *ui_data)
        g_signal_connect(ui_data->log, "changed", G_CALLBACK(scroll_log), view);
 }
 
-static auth_ui_data *init_ui_data (char *vpn_name)
+static auth_ui_data *init_ui_data (char *vpn_name, char *vpn_uuid)
 {
        auth_ui_data *ui_data;
 
@@ -1532,6 +1717,11 @@ static auth_ui_data *init_ui_data (char *vpn_name)
        ui_data->form_shown_changed = g_cond_new();
        ui_data->cert_response_changed = g_cond_new();
        ui_data->vpn_name = vpn_name;
+       ui_data->vpn_uuid = vpn_uuid;
+       ui_data->secrets = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                 g_free, g_free);
+       ui_data->success_passwords = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                           g_free, keyring_password_free);
        if (pipe(ui_data->cancel_pipes)) {
                /* This should never happen, and the world is probably about
                   to come crashing down around our ears. But attempt to cope
@@ -1550,7 +1740,7 @@ static auth_ui_data *init_ui_data (char *vpn_name)
 
 #if OPENCONNECT_CHECK_VER(1,4)
        openconnect_set_cancel_fd (ui_data->vpninfo, ui_data->cancel_pipes[0]);
-#endif  
+#endif
 
 #if 0
        ui_data->vpninfo->proxy_factory = px_proxy_factory_new();
@@ -1618,7 +1808,7 @@ int main (int argc, char **argv)
        g_thread_init (NULL);
        gtk_init(0, NULL);
 
-       _ui_data = init_ui_data(vpn_name);
+       _ui_data = init_ui_data(vpn_name, vpn_uuid);
        if (get_config(vpn_uuid, _ui_data->vpninfo)) {
                fprintf(stderr, "Failed to find VPN UUID %s in gconf\n", vpn_uuid);
                return 1;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]