agent: Terminate pinentry process gracefully, by watching socket. master
authorNIIBE Yutaka <gniibe@fsij.org>
Tue, 19 Feb 2019 05:36:50 +0000 (14:36 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Tue, 19 Feb 2019 05:36:50 +0000 (14:36 +0900)
* agent/call-pinentry.c (watch_sock): New.
(do_getpin): Spawn the watching thread.

--

While we don't have npth_cancel (and it's difficult to implement it
correctly), this is a kind of best compromise allowing a thread's
polling when pinentry is active.

GnuPG-bug-id: 2011
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
agent/call-pinentry.c

index 0c8f7dc..34dde37 100644 (file)
@@ -942,16 +942,88 @@ build_cmd_setdesc (char *line, size_t linelen, const char *desc)
 
 
 \f
-/* Ask pinentry to get a pin by "GETPIN" command.
- * FIXME: Support EOF detection of the socket: ctrl->thread_startup.fd
+/* Watch the socket's EOF condition, while checking finish of
+   foreground thread.  When EOF condition is detected, terminate
+   the pinentry process behind the assuan pipe.
+ */
+static void *
+watch_sock (void *arg)
+{
+  gnupg_fd_t *p = (gnupg_fd_t *)arg;
+  pid_t pid = assuan_get_pid (entry_ctx);
+
+  while (1)
+    {
+      int err;
+      gnupg_fd_t sock = *p;
+      fd_set fdset;
+      struct timeval timeout = { 0, 500000 };
+
+      if (sock == GNUPG_INVALID_FD)
+        return NULL;
+
+      FD_ZERO (&fdset);
+      FD_SET (FD2INT (sock), &fdset);
+      err = npth_select (FD2INT (sock)+1, &fdset, NULL, NULL, &timeout);
+
+      if (err < 0)
+        {
+          if (errno == EINTR)
+            continue;
+          else
+            return NULL;
+        }
+
+      /* Possibly, it's EOF.  */
+      if (err > 0)
+        break;
+    }
+
+  if (pid == (pid_t)(-1))
+    ; /* No pid available can't send a kill. */
+#ifdef HAVE_W32_SYSTEM
+  /* Older versions of assuan set PID to 0 on Windows to indicate an
+     invalid value.  */
+  else if (pid != (pid_t) INVALID_HANDLE_VALUE && pid != 0)
+    TerminateProcess ((HANDLE)pid, 1);
+#else
+  else if (pid > 0)
+    kill (pid, SIGINT);
+#endif
+
+  return NULL;
+}
+
+
+/* Ask pinentry to get a pin by "GETPIN" command, spawning a thread
+   detecting the socket's EOF.
  */
 static gpg_error_t
 do_getpin (ctrl_t ctrl, struct entry_parm_s *parm)
 {
-  int rc;
+  npth_attr_t tattr;
+  gpg_error_t rc;
+  int err;
+  npth_t thread;
   int saveflag = assuan_get_flag (entry_ctx, ASSUAN_CONFIDENTIAL);
+  gnupg_fd_t sock_watched = ctrl->thread_startup.fd;
+
+  err = npth_attr_init (&tattr);
+  if (err)
+    {
+      log_error ("do_getpin: error npth_attr_init: %s\n", strerror (err));
+      return gpg_error_from_errno (err);
+    }
+  npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE);
+
+  err = npth_create (&thread, &tattr, watch_sock, (void *)&sock_watched);
+  npth_attr_destroy (&tattr);
+  if (err)
+    {
+      log_error ("do_getpin: error spawning thread: %s\n", strerror (err));
+      return gpg_error_from_errno (err);
+    }
 
-  (void)ctrl;
   assuan_begin_confidential (entry_ctx);
   rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, parm,
                         inq_quality, entry_ctx,
@@ -968,6 +1040,11 @@ do_getpin (ctrl_t ctrl, struct entry_parm_s *parm)
       && gpg_err_code (rc) == GPG_ERR_CANCELED)
     rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_FULLY_CANCELED);
 
+  sock_watched = GNUPG_INVALID_FD;
+  err = npth_join (thread, NULL);
+  if (err)
+    log_error ("do_getpin: error joining thread: %s\n", strerror (err));
+
   return rc;
 }
 \f