[NetworkManager-openconnect/el6] Add gnome-keyring support
David Woodhouse
dwmw2 at fedoraproject.org
Mon Jun 24 21:47:23 UTC 2013
commit 465401076e20cfab798d06465090385fad8e6341
Author: David Woodhouse <David.Woodhouse at intel.com>
Date: Mon Jun 24 22:47:07 2013 +0100
Add gnome-keyring support
NetworkManager-openconnect.spec | 7 +-
keyring-support.patch | 470 +++++++++++++++++++++++++++++++++++++++
2 files changed, 476 insertions(+), 1 deletions(-)
---
diff --git a/NetworkManager-openconnect.spec b/NetworkManager-openconnect.spec
index ec7af6a..f95750a 100644
--- a/NetworkManager-openconnect.spec
+++ b/NetworkManager-openconnect.spec
@@ -9,7 +9,7 @@
Summary: NetworkManager VPN integration for openconnect
Name: NetworkManager-openconnect
Version: 0.8.6.0
-Release: 1%{snapshot}%{?dist}
+Release: 2%{snapshot}%{?dist}
License: GPLv2+
Group: System Environment/Base
URL: http://www.gnome.org/projects/NetworkManager/
@@ -18,6 +18,7 @@ Source: %{name}-%{realversion}%{snapshot}.tar.bz2
Patch1: build-against-libopenconnect2.patch
# Extra patches to make it build against NetworkManager 0.8.1:
Patch2: build-against-081.patch
+Patch3: keyring-support.patch
BuildRoot: %{_tmppath}/%{name}-%{version}-root
BuildRequires: gtk2-devel >= %{gtk2_version}
@@ -48,6 +49,7 @@ with NetworkManager and the GNOME desktop
%setup -q -n NetworkManager-openconnect-%{realversion}
%patch1 -p1
%patch2 -p1
+%patch3 -p1
%build
autoreconf
@@ -105,6 +107,9 @@ fi
%{_datadir}/gnome-vpn-properties/openconnect/nm-openconnect-dialog.ui
%changelog
+* Mon Jun 24 2013 Murilo Opsfelder Araujo <muriloo at linux.vnet.ibm.com> - 0.8.6.0-2
+- Add gnome-keyring support
+
* Wed Jun 20 2012 David Woodhouse <David.Woodhouse at intel.com> - 0.8.6.0-1
- Update to 0.8.6.0 for EPEL6
diff --git a/keyring-support.patch b/keyring-support.patch
new file mode 100644
index 0000000..ee05190
--- /dev/null
+++ b/keyring-support.patch
@@ -0,0 +1,470 @@
+From 500d207180829d537089778e618f5bbc4727ae02 Mon Sep 17 00:00:00 2001
+From: Murilo Opsfelder Araujo <muriloo at linux.vnet.ibm.com>
+Date: Tue, 4 Jun 2013 16:20:52 -0300
+Subject: [PATCH 17/17] Add gnome-keyring support to NM_0_8 branch
+Cc: Murilo Opsfelder Araujo <muriloo at linux.vnet.ibm.com>
+
+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
+
+Signed-off-by: Murilo Opsfelder Araujo <muriloo at linux.vnet.ibm.com>
+---
+ 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;
+--
+1.8.3.1.549.g1f3a412
+
More information about the scm-commits
mailing list