[PINENTRY PATCH 8/8] gnome3: Honor timeout.

Daniel Kahn Gillmor dkg at fifthhorseman.net
Fri Nov 4 23:57:52 CET 2016


* gnome3/pinentry-gnome3.c (create_prompt): Use timeout to determine
how long to wait for Gcr to provide a system prompt before giving up.
(_gcr_timeout_done): New.  Record that a timeout has elapsed.
(gnome3_cmd_handler): Set up a timeout before launching the prompt,
and tear it down afterward.
(_gcr_prompt_password_done): Report timeout differently from normal
cancellation.
(_gcr_prompt_confirm_done): Report timeout differently from normal
cancellation.

--

Without this change, pinentry-gnome3 does not respect the timeout
parameter at all, and can hang indefinitely in the event that the
system prompter is locked or the user is ignoring the session.

Signed-off-by: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
---
 gnome3/pinentry-gnome3.c | 75 +++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 65 insertions(+), 10 deletions(-)

diff --git a/gnome3/pinentry-gnome3.c b/gnome3/pinentry-gnome3.c
index 06c2076..148ef65 100644
--- a/gnome3/pinentry-gnome3.c
+++ b/gnome3/pinentry-gnome3.c
@@ -74,6 +74,8 @@ struct _gnome3_run {
   GcrPrompt *prompt;
   GMainLoop *main_loop;
   int ret;
+  guint timeout_id;
+  int timed_out;
 };
 
 static void
@@ -82,6 +84,9 @@ _gcr_prompt_password_done (GObject *source_object, GAsyncResult *res, gpointer u
 static void
 _gcr_prompt_confirm_done (GObject *source_object, GAsyncResult *res, gpointer user_data);
 
+static gboolean
+_gcr_timeout_done (gpointer user_data);
+
 static void
 _propagate_g_error_to_pinentry (pinentry_t pe, GError *error, gpg_err_code_t code, const char *loc)
 {
@@ -102,11 +107,25 @@ create_prompt (pinentry_t pe, int confirm)
   char window_id[32];
 
   /* Create the prompt.  */
-  prompt = GCR_PROMPT (gcr_system_prompt_open (-1, NULL, &error));
+  prompt = GCR_PROMPT (gcr_system_prompt_open (pe->timeout ? pe->timeout : -1, NULL, &error));
   if (! prompt)
     {
-      _propagate_g_error_to_pinentry (pe, error, GPG_ERR_CONFIGURATION,
-                                      "gcr_system_prompt_open");
+      /* this means the timeout elapsed, but no prompt was ever shown. */
+      if (error->code == GCR_SYSTEM_PROMPT_IN_PROGRESS)
+        { 
+          fprintf (stderr, "Timeout: the Gcr system prompter was already in use.\n");
+          pe->specific_err_info = strdup ("Timeout: the Gcr system prompter was already in use.");
+          /* not using GPG_ERR_TIMEOUT here because the user never saw
+             a prompt: */
+          pe->specific_err = gpg_error (GPG_ERR_PIN_ENTRY);
+        }
+      else
+        {
+          fprintf (stderr, "couldn't create prompt for gnupg passphrase: %s\n",
+                   error->message);
+          _propagate_g_error_to_pinentry (pe, error, GPG_ERR_CONFIGURATION,
+                                          "gcr_system_prompt_open");
+        }
       g_error_free (error);
       return NULL;
     }
@@ -207,6 +226,8 @@ gnome3_cmd_handler (pinentry_t pe)
     }      
   state.pinentry = pe;
   state.ret = 0;
+  state.timeout_id = 0;
+  state.timed_out = 0;
   state.prompt = create_prompt (pe, !!(pe->pin));
   if (!state.prompt)
 	{
@@ -218,9 +239,13 @@ gnome3_cmd_handler (pinentry_t pe)
   else
     gcr_prompt_confirm_async (state.prompt, NULL, _gcr_prompt_confirm_done, &state);
 
+  if (pe->timeout)
+    state.timeout_id = g_timeout_add_seconds (pe->timeout, _gcr_timeout_done, &state);
   g_main_loop_run (state.main_loop);
 
   /* clean up state: */
+  if (state.timeout_id && !state.timed_out)
+    g_source_destroy (g_main_context_find_source_by_id (NULL, state.timeout_id));
   g_clear_object (&state.prompt);
   g_main_loop_unref (state.main_loop);
   return state.ret;
@@ -243,17 +268,23 @@ _gcr_prompt_password_done (GObject *source_object, GAsyncResult *res, gpointer u
       /* "The returned password is valid until the next time a method
 	 is called to display another prompt."  */
       password = gcr_prompt_password_finish (prompt, res, &error);
-      if (error)
-	/* Error.  */
+      if ((! password && ! error) ||
+          (error && error->code == G_IO_ERROR_CANCELLED))
+        {
+          /* operation was cancelled or timed out.  */
+          ret = -1;
+          if (state->timed_out)
+            state->pinentry->specific_err = gpg_error (GPG_ERR_TIMEOUT);
+          if (error)
+            g_error_free (error);
+        }
+      else if (error)
 	{
           _propagate_g_error_to_pinentry (state->pinentry, error, GPG_ERR_PIN_ENTRY,
                                           "gcr_system_password_finish");
 	  g_error_free (error);
 	  ret = -1;
 	}
-      else if (! password && ! error)
-	/* User cancelled the operation.  */
-	ret = -1;
       else
 	{
 	  pinentry_setbufferlen (pe, strlen (password) + 1);
@@ -295,8 +326,16 @@ _gcr_prompt_confirm_done (GObject *source_object, GAsyncResult *res, gpointer us
       reply = gcr_prompt_confirm_finish (prompt, res, &error);
       if (error)
 	{
-          _propagate_g_error_to_pinentry (state->pinentry, error, GPG_ERR_PIN_ENTRY,
-                                          "gcr_system_confirm_finish");
+          if (error->code == G_IO_ERROR_CANCELLED)
+            {
+              pe->canceled = 1;
+              if (state->timed_out)
+                state->pinentry->specific_err = gpg_error (GPG_ERR_TIMEOUT);
+            }
+          else
+            _propagate_g_error_to_pinentry (state->pinentry, error, GPG_ERR_PIN_ENTRY,
+                                            "gcr_system_confirm_finish");
+	  g_error_free (error);
 	  ret = 0;
 	}
       else if (reply == GCR_PROMPT_REPLY_CONTINUE
@@ -309,6 +348,8 @@ _gcr_prompt_confirm_done (GObject *source_object, GAsyncResult *res, gpointer us
 	/* GCR_PROMPT_REPLY_CANCEL */
 	{
 	  pe->canceled = 1;
+          if (state->timed_out)
+            state->pinentry->specific_err = gpg_error (GPG_ERR_TIMEOUT);
 	  ret = 0;
 	}
       state->ret = ret;
@@ -318,6 +359,20 @@ _gcr_prompt_confirm_done (GObject *source_object, GAsyncResult *res, gpointer us
     g_main_loop_quit (state->main_loop);
 }
 
+static gboolean
+_gcr_timeout_done (gpointer user_data)
+{
+  struct _gnome3_run *state = (struct _gnome3_run *) user_data;
+
+  if (!state)
+    return FALSE;
+
+  state->timed_out = 1;
+  gcr_prompt_close (state->prompt);
+
+  return FALSE;
+}
+
 pinentry_cmd_handler_t pinentry_cmd_handler = gnome3_cmd_handler;
 
 int
-- 
2.10.1




More information about the Gnupg-devel mailing list