[gnome-keyring] ssh-agent: Add GkdSshAgentInteraction API



commit 5cbfcd6b51b32d5d9ecbed20e5dff46e46fd39cc
Author: Daiki Ueno <dueno src gnome org>
Date:   Fri Feb 23 10:28:38 2018 +0100

    ssh-agent: Add GkdSshAgentInteraction API
    
    This could be used by the new implementation of ssh-agent, through the
    GcrSshAskpass and GkdLoginInteraction.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=775981

 daemon/ssh-agent/Makefile.am                      |    9 +-
 daemon/ssh-agent/gkd-ssh-agent-interaction.c      |  204 +++++++++++++++++++++
 daemon/ssh-agent/gkd-ssh-agent-interaction.h      |   37 ++++
 daemon/ssh-agent/test-gkd-ssh-agent-interaction.c |  196 ++++++++++++++++++++
 po/POTFILES.in                                    |    1 +
 5 files changed, 446 insertions(+), 1 deletions(-)
---
diff --git a/daemon/ssh-agent/Makefile.am b/daemon/ssh-agent/Makefile.am
index 4fb718e..8ed821b 100644
--- a/daemon/ssh-agent/Makefile.am
+++ b/daemon/ssh-agent/Makefile.am
@@ -8,6 +8,8 @@ noinst_LTLIBRARIES += \
 libgkd_ssh_agent_la_SOURCES = \
        daemon/ssh-agent/gkd-ssh-agent.c \
        daemon/ssh-agent/gkd-ssh-agent.h \
+       daemon/ssh-agent/gkd-ssh-agent-interaction.c \
+       daemon/ssh-agent/gkd-ssh-agent-interaction.h \
        daemon/ssh-agent/gkd-ssh-agent-private.h \
        daemon/ssh-agent/gkd-ssh-agent-ops.c \
        daemon/ssh-agent/gkd-ssh-agent-proto.c
@@ -47,7 +49,8 @@ ssh_agent_LIBS = \
 
 ssh_agent_TESTS = \
        test-communication \
-       test-keytypes
+       test-keytypes \
+       test-gkd-ssh-agent-interaction
 
 test_keytypes_SOURCES = daemon/ssh-agent/test-keytypes.c
 test_keytypes_CFLAGS = $(ssh_agent_CFLAGS)
@@ -57,5 +60,9 @@ test_communication_SOURCES = daemon/ssh-agent/test-communication.c
 test_communication_CFLAGS = $(ssh_agent_CFLAGS)
 test_communication_LDADD = $(ssh_agent_LIBS)
 
+test_gkd_ssh_agent_interaction_SOURCES = daemon/ssh-agent/test-gkd-ssh-agent-interaction.c
+test_gkd_ssh_agent_interaction_CFLAGS = $(ssh_agent_CFLAGS)
+test_gkd_ssh_agent_interaction_LDADD = $(ssh_agent_LIBS) libgkd-login.la libgkm-wrap-layer.la
+
 check_PROGRAMS += $(ssh_agent_TESTS)
 TESTS += $(ssh_agent_TESTS)
diff --git a/daemon/ssh-agent/gkd-ssh-agent-interaction.c b/daemon/ssh-agent/gkd-ssh-agent-interaction.c
new file mode 100644
index 0000000..b3b7660
--- /dev/null
+++ b/daemon/ssh-agent/gkd-ssh-agent-interaction.c
@@ -0,0 +1,204 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daiki Ueno
+ */
+
+#include "config.h"
+
+#include "gkd-ssh-agent-interaction.h"
+#include "gkd-ssh-agent-private.h"
+#include "daemon/login/gkd-login-password.h"
+
+#include <gcr/gcr-base.h>
+#include <glib/gi18n-lib.h>
+
+enum {
+       PROP_0,
+       PROP_PROMPTER_NAME
+};
+
+struct _GkdSshAgentInteraction {
+       GTlsInteraction interaction;
+       gchar *prompter_name;
+};
+
+G_DEFINE_TYPE (GkdSshAgentInteraction, gkd_ssh_agent_interaction, G_TYPE_TLS_INTERACTION);
+
+static void
+gkd_ssh_agent_interaction_init (GkdSshAgentInteraction *self)
+{
+}
+
+static void
+on_prompt_password (GObject *source_object,
+                   GAsyncResult *result,
+                   gpointer user_data)
+{
+       GTask *task = G_TASK (user_data);
+       GTlsPassword *password = g_task_get_task_data (task);
+       GkdLoginPassword *login_password = GKD_LOGIN_PASSWORD (password);
+       GcrPrompt *prompt = GCR_PROMPT (source_object);
+       GError *error = NULL;
+       const gchar *value;
+
+       value = gcr_prompt_password_finish (prompt, result, &error);
+       if (!value) {
+               g_object_unref (prompt);
+               if (error)
+                       g_task_return_error (task, error);
+               else
+                       g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "cancelled");
+               g_object_unref (task);
+               return;
+       }
+       g_tls_password_set_value (password, (const guchar *)value, strlen (value));
+       gkd_login_password_set_store_password (login_password,
+                                              gcr_prompt_get_choice_chosen (prompt));
+       g_object_unref (prompt);
+
+       g_task_return_int (task, G_TLS_INTERACTION_HANDLED);
+       g_object_unref (task);
+}
+
+static void
+on_prompt_open (GObject *source_object,
+                GAsyncResult *result,
+                gpointer user_data)
+{
+       GTask *task = G_TASK (user_data);
+       GTlsPassword *password = g_task_get_task_data (task);
+       GkdLoginPassword *login_password = GKD_LOGIN_PASSWORD (password);
+       GError *error = NULL;
+       GcrPrompt *prompt;
+       const gchar *choice;
+       gchar *text;
+
+       prompt = gcr_system_prompt_open_finish (result, &error);
+       if (!prompt) {
+               g_task_return_error (task, error);
+               g_object_unref (task);
+               return;
+       }
+
+       gcr_prompt_set_title (prompt, _("Unlock private key"));
+       gcr_prompt_set_message (prompt, _("Enter password to unlock the private key"));
+
+       /* TRANSLATORS: The private key is locked */
+       text = g_strdup_printf (_("An application wants access to the private key “%s”, but it is locked"),
+                               g_tls_password_get_description (password));
+       gcr_prompt_set_description (prompt, text);
+       g_free (text);
+
+       choice = NULL;
+       if (gkd_login_password_get_login_available (login_password))
+               choice = _("Automatically unlock this key whenever I’m logged in");
+       gcr_prompt_set_choice_label (prompt, choice);
+       gcr_prompt_set_continue_label (prompt, _("Unlock"));
+
+       if (g_tls_password_get_flags (password) & G_TLS_PASSWORD_RETRY)
+               gcr_prompt_set_warning (prompt, _("The unlock password was incorrect"));
+
+       gcr_prompt_password_async (prompt, g_task_get_cancellable (task), on_prompt_password, task);
+}
+
+static void
+gkd_ssh_agent_interaction_ask_password_async (GTlsInteraction *interaction,
+                                        GTlsPassword *password,
+                                        GCancellable *cancellable,
+                                        GAsyncReadyCallback callback,
+                                        gpointer user_data)
+{
+       GkdSshAgentInteraction *self = GKD_SSH_AGENT_INTERACTION (interaction);
+       GTask *task;
+
+       task = g_task_new (interaction, cancellable, callback, user_data);
+       g_task_set_task_data (task, g_object_ref (password), g_object_unref);
+
+       gcr_system_prompt_open_for_prompter_async (self->prompter_name, 60,
+                                                  cancellable,
+                                                  on_prompt_open,
+                                                  task);
+}
+
+static GTlsInteractionResult
+gkd_ssh_agent_interaction_ask_password_finish (GTlsInteraction *interaction,
+                                         GAsyncResult *res,
+                                         GError **error)
+{
+       GTask *task = G_TASK (res);
+       GTlsInteractionResult result;
+
+       result = g_task_propagate_int (task, error);
+       if (result == -1)
+               return G_TLS_INTERACTION_FAILED;
+       return result;
+}
+
+static void
+gkd_ssh_agent_interaction_set_property (GObject *object,
+                                  guint prop_id,
+                                  const GValue *value,
+                                  GParamSpec *pspec)
+{
+       GkdSshAgentInteraction *self = GKD_SSH_AGENT_INTERACTION (object);
+
+       switch (prop_id) {
+       case PROP_PROMPTER_NAME:
+               self->prompter_name = g_value_dup_string (value);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+gkd_ssh_agent_interaction_finalize (GObject *object)
+{
+       GkdSshAgentInteraction *self = GKD_SSH_AGENT_INTERACTION (object);
+
+       g_free (self->prompter_name);
+
+       G_OBJECT_CLASS (gkd_ssh_agent_interaction_parent_class)->finalize (object);
+}
+
+static void
+gkd_ssh_agent_interaction_class_init (GkdSshAgentInteractionClass *klass)
+{
+       GTlsInteractionClass *interaction_class = G_TLS_INTERACTION_CLASS (klass);
+       GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+       interaction_class->ask_password_async = gkd_ssh_agent_interaction_ask_password_async;
+       interaction_class->ask_password_finish = gkd_ssh_agent_interaction_ask_password_finish;
+
+       gobject_class->set_property = gkd_ssh_agent_interaction_set_property;
+       gobject_class->finalize = gkd_ssh_agent_interaction_finalize;
+
+       g_object_class_install_property (gobject_class, PROP_PROMPTER_NAME,
+                g_param_spec_string ("prompter-name", "Prompter-name", "Prompter-name",
+                                     NULL,
+                                     G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
+}
+
+GTlsInteraction *
+gkd_ssh_agent_interaction_new (const gchar *prompter_name)
+{
+       return g_object_new (GKD_TYPE_SSH_AGENT_INTERACTION, "prompter-name", prompter_name, NULL);
+}
diff --git a/daemon/ssh-agent/gkd-ssh-agent-interaction.h b/daemon/ssh-agent/gkd-ssh-agent-interaction.h
new file mode 100644
index 0000000..20f6647
--- /dev/null
+++ b/daemon/ssh-agent/gkd-ssh-agent-interaction.h
@@ -0,0 +1,37 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daiki Ueno
+ */
+
+#ifndef _GKD_SSH_AGENT_INTERACTION_H__
+#define __GKD_SSH_AGENT_INTERACTION_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GKD_TYPE_SSH_AGENT_INTERACTION gkd_ssh_agent_interaction_get_type ()
+G_DECLARE_FINAL_TYPE (GkdSshAgentInteraction, gkd_ssh_agent_interaction, GKD, SSH_AGENT_INTERACTION, 
GTlsInteraction)
+
+GTlsInteraction *gkd_ssh_agent_interaction_new (const gchar *prompter_name);
+
+G_END_DECLS
+
+#endif /* __GKD_SSH_INTERACTION_H__ */
diff --git a/daemon/ssh-agent/test-gkd-ssh-agent-interaction.c 
b/daemon/ssh-agent/test-gkd-ssh-agent-interaction.c
new file mode 100644
index 0000000..0f53aee
--- /dev/null
+++ b/daemon/ssh-agent/test-gkd-ssh-agent-interaction.c
@@ -0,0 +1,196 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daiki Ueno
+ */
+
+#include "config.h"
+
+#include "gkd-ssh-agent-private.h"
+#include "gkd-ssh-agent-interaction.h"
+#include "daemon/login/gkd-login-password.h"
+#include "egg/egg-testing.h"
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gcr/gcr-base.h>
+
+typedef struct {
+       const gchar *prompter_name;
+       GTlsPassword *password;
+} Test;
+
+static void
+setup (Test *test, gboolean login_available)
+{
+       GTlsPassword *password;
+
+       password = g_tls_password_new (G_TLS_PASSWORD_NONE, "");
+       test->password = g_object_new (GKD_TYPE_LOGIN_PASSWORD,
+                                      "base", password,
+                                      "login-available", login_available,
+                                      "description", "ssh-key",
+                                      NULL);
+       g_object_unref (password);
+
+       test->prompter_name = gcr_mock_prompter_start ();
+}
+
+static void
+setup_no_login (Test *test, gconstpointer unused)
+{
+       setup (test, FALSE);
+}
+
+static void
+setup_login (Test *test, gconstpointer unused)
+{
+       setup (test, TRUE);
+}
+
+static void
+teardown (Test *test, gconstpointer unused)
+{
+       gcr_mock_prompter_stop ();
+
+       g_object_unref (test->password);
+}
+
+static void
+on_async_result (GObject *source_object,
+                GAsyncResult *result,
+                gpointer user_data)
+{
+        GAsyncResult **ret = user_data;
+        *ret = g_object_ref (result);
+       egg_test_wait_stop ();
+}
+
+static void
+test_ask_password_no_login (Test *test, gconstpointer unused)
+{
+       GTlsInteraction *interaction;
+       GAsyncResult *result = NULL;
+       GError *error = NULL;
+       const guchar *value;
+       gsize length;
+       GTlsInteractionResult ret;
+
+       interaction = gkd_ssh_agent_interaction_new (test->prompter_name);
+       gcr_mock_prompter_expect_password_ok ("password", NULL);
+       g_tls_interaction_ask_password_async (interaction,
+                                             test->password,
+                                             NULL,
+                                             on_async_result,
+                                             &result);
+       g_assert (result == NULL);
+
+       egg_test_wait ();
+
+       g_assert (result != NULL);
+
+       ret = g_tls_interaction_ask_password_finish (interaction, result, &error);
+       g_assert_cmpint (ret, ==, G_TLS_INTERACTION_HANDLED);
+       g_assert_no_error (error);
+
+       value = g_tls_password_get_value (test->password, &length);
+       g_assert_cmpmem ("password", 8, value, length);
+       g_assert_false (gkd_login_password_get_store_password (GKD_LOGIN_PASSWORD (test->password)));
+       g_object_unref (interaction);
+       g_object_unref (result);
+}
+
+static void
+test_ask_password_login (Test *test, gconstpointer unused)
+{
+       GTlsInteraction *interaction;
+       GAsyncResult *result = NULL;
+       GError *error = NULL;
+       const guchar *value;
+       gsize length;
+       GTlsInteractionResult ret;
+
+       interaction = gkd_ssh_agent_interaction_new (test->prompter_name);
+       gcr_mock_prompter_expect_password_ok ("password", "choice-chosen", TRUE, NULL);
+       g_tls_interaction_ask_password_async (interaction,
+                                             test->password,
+                                             NULL,
+                                             on_async_result,
+                                             &result);
+       g_assert (result == NULL);
+
+       egg_test_wait ();
+
+       ret = g_tls_interaction_ask_password_finish (interaction, result, &error);
+       g_assert_cmpint (ret, ==, G_TLS_INTERACTION_HANDLED);
+       g_assert_no_error (error);
+
+       value = g_tls_password_get_value (test->password, &length);
+       g_assert_cmpmem ("password", 8, value, length);
+       g_assert_true (gkd_login_password_get_store_password (GKD_LOGIN_PASSWORD (test->password)));
+       g_object_unref (interaction);
+       g_object_unref (result);
+}
+
+static void
+test_ask_password_cancel (Test *test, gconstpointer unused)
+{
+       GTlsInteraction *interaction;
+       GAsyncResult *result = NULL;
+       GError *error = NULL;
+       const guchar *value;
+       gsize length;
+       GTlsInteractionResult ret;
+
+       interaction = gkd_ssh_agent_interaction_new (test->prompter_name);
+       gcr_mock_prompter_expect_password_cancel ();
+       g_tls_interaction_ask_password_async (interaction,
+                                             test->password,
+                                             NULL,
+                                             on_async_result,
+                                             &result);
+       g_assert (result == NULL);
+
+       egg_test_wait ();
+
+       g_assert (result != NULL);
+
+
+       ret = g_tls_interaction_ask_password_finish (interaction, result, &error);
+       g_assert_cmpint (ret, ==, G_TLS_INTERACTION_FAILED);
+       g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+
+       value = g_tls_password_get_value (test->password, &length);
+       g_assert_cmpmem ("", 0, value, length);
+       g_assert_false (gkd_login_password_get_store_password (GKD_LOGIN_PASSWORD (test->password)));
+       g_object_unref (interaction);
+       g_object_unref (result);
+}
+
+int
+main (int argc, char **argv)
+{
+       g_test_init (&argc, &argv, NULL);
+
+       g_test_add ("/ssh-agent/interaction/ask_password_no_login", Test, NULL, setup_no_login, 
test_ask_password_no_login, teardown);
+       g_test_add ("/ssh-agent/interaction/ask_password_login", Test, NULL, setup_login, 
test_ask_password_login, teardown);
+       g_test_add ("/ssh-agent/interaction/ask_password_cancel", Test, NULL, setup_no_login, 
test_ask_password_cancel, teardown);
+
+       return egg_tests_run_with_loop ();
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 3ed5e3d..8f82ce2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -7,6 +7,7 @@ daemon/gnome-keyring-pkcs11.desktop.in.in
 daemon/gnome-keyring-secrets.desktop.in.in
 daemon/gnome-keyring-ssh.desktop.in.in
 daemon/login/gkd-login.c
+daemon/ssh-agent/gkd-ssh-agent-interaction.c
 egg/dotlock.c
 egg/egg-oid.c
 pkcs11/gkm/gkm-certificate.c


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