scdaemon monitoring usb device removal

NIIBE Yutaka gniibe at fsij.org
Thu Dec 22 05:55:09 CET 2016


NIIBE Yutaka <gniibe at fsij.org> writes:
> I'll post the change after the release of GnuPG 2.1.17.  Please
> note that this is experimental change.

Here is the expelimenta change.  It assumes that the card reader is
always token.

Writing this patch, I now consider that re-organizing the scdaemon would
be better.

Currently, gpg-agent assumes a single card and asks operations to
scdaemon by serial number of the card (+ identifier/keyno like
OPENPGP.1, OPENPGP.2, and OPENPGP.3).  My current idea is that
it's much simpler if we can use KEYGRIP here.  Then, supporting
multiple cards will be easier.


diff --git a/scd/apdu.c b/scd/apdu.c
index 225ceee..dc473ab 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -2597,7 +2597,7 @@ ccid_pinpad_operation (int slot, int class, int ins, int p0, int p1,
 
 /* Open the reader and try to read an ATR.  */
 static int
-open_ccid_reader (const char *portstr)
+open_ccid_reader (const char *portstr, void *arg)
 {
   int err;
   int slot;
@@ -2608,7 +2608,7 @@ open_ccid_reader (const char *portstr)
     return -1;
   slotp = reader_table + slot;
 
-  err = ccid_open_reader (&slotp->ccid.handle, portstr,
+  err = ccid_open_reader (&slotp->ccid.handle, portstr, arg,
                           (const char **)&slotp->rdrname);
   if (err)
     {
@@ -2964,7 +2964,7 @@ open_rapdu_reader (int portno,
    error. If PORTSTR is NULL we default to a suitable port (for ctAPI:
    the first USB reader.  For PC/SC the first listed reader). */
 int
-apdu_open_reader (const char *portstr)
+apdu_open_reader (const char *portstr, void *arg)
 {
   static int pcsc_api_loaded, ct_api_loaded;
   int slot;
@@ -2979,7 +2979,7 @@ apdu_open_reader (const char *portstr)
       int i;
       const char *s;
 
-      slot = open_ccid_reader (portstr);
+      slot = open_ccid_reader (portstr, arg);
       if (slot != -1)
         {
           once_available = 1;
diff --git a/scd/apdu.h b/scd/apdu.h
index c79b3fd..8970b32 100644
--- a/scd/apdu.h
+++ b/scd/apdu.h
@@ -85,7 +85,7 @@ enum {
 
 
 /* Note, that apdu_open_reader returns no status word but -1 on error. */
-int apdu_open_reader (const char *portstr);
+int apdu_open_reader (const char *portstr, void *arg);
 int apdu_open_remote_reader (const char *portstr,
                              const unsigned char *cookie, size_t length,
                              int (*readfnc) (void *opaque,
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c
index 0917105..e22827e 100644
--- a/scd/ccid-driver.c
+++ b/scd/ccid-driver.c
@@ -89,6 +89,7 @@
 #endif /*HAVE_NPTH*/
 
 #include <libusb.h>
+#include <poll.h>
 
 #include "scdaemon.h"
 #include "iso7816.h"
@@ -273,6 +274,7 @@ struct ccid_driver_s
      ccid_set_progress_cb.  */
   void (*progress_cb)(void *, const char *, int, int, int);
   void *progress_cb_arg;
+  void *upper_layer;
 };
 
 
@@ -1547,10 +1549,49 @@ ccid_vendor_specific_init (ccid_driver_t handle)
 }
 
 
+static void
+removal_cb (void *arg)
+{
+  ccid_driver_t handle = (ccid_driver_t)arg;
+
+  scd_handle_reader_removal (handle->upper_layer);
+}
+
+static void
+manage_removal_of_device (ccid_driver_t handle, int is_token)
+{
+  const struct libusb_pollfd **pfd_array;
+
+  DEBUGOUT_1 ("manage removal of device (%p)\n", handle);
+
+  /* FIXME: it's good if there is an API of libusb to get
+     corresponding FD for the specific device.
+     This works for now with a single USB device.
+     For multiple devices support, fix is needed.
+   */
+  pfd_array = libusb_get_pollfds (NULL);
+  if (pfd_array)
+    {
+      const struct libusb_pollfd **p = pfd_array;
+      do
+        {
+          DEBUGOUT_2 ("polling fd (%d): %08x\n", (*p)->fd, (*p)->events);
+          if (((*p)->events & POLLOUT))
+            {
+              /* This is fd for the USB device.  */
+              scd_manage_device_removal_check ((*p)->fd, is_token, removal_cb, handle);
+            }
+        }
+      while (*++p);
+
+      libusb_free_pollfds (pfd_array);
+    }
+}
+
 /* Open the reader with the internal number READERNO and return a
    pointer to be used as handle in HANDLE.  Returns 0 on success. */
 int
-ccid_open_reader (ccid_driver_t *handle, const char *readerid,
+ccid_open_reader (ccid_driver_t *handle, const char *readerid, void *arg,
                   const char **rdrname_p)
 {
   int rc = 0;
@@ -1622,6 +1663,8 @@ ccid_open_reader (ccid_driver_t *handle, const char *readerid,
       (*handle)->ep_bulk_out = ep_bulk_out;
       (*handle)->ep_bulk_in = ep_bulk_in;
       (*handle)->ep_intr = ep_intr;
+      (*handle)->upper_layer = arg;
+      manage_removal_of_device (*handle, 1);
     }
   else if (dev_fd != -1) /* Device transport. */
     {
@@ -3650,7 +3693,7 @@ main (int argc, char **argv)
         break;
     }
 
-  rc = ccid_open_reader (&ccid, argc? *argv:NULL, NULL);
+  rc = ccid_open_reader (&ccid, argc? *argv:NULL, NULL, NULL);
   if (rc)
     return 1;
 
diff --git a/scd/ccid-driver.h b/scd/ccid-driver.h
index e3aed9f..f7c46d0 100644
--- a/scd/ccid-driver.h
+++ b/scd/ccid-driver.h
@@ -111,7 +111,7 @@ typedef struct ccid_driver_s *ccid_driver_t;
 
 int ccid_set_debug_level (int level);
 char *ccid_get_reader_list (void);
-int ccid_open_reader (ccid_driver_t *handle, const char *readerid,
+int ccid_open_reader (ccid_driver_t *handle, const char *readerid, void *arg,
                       const char **rdrname_p);
 int ccid_set_progress_cb (ccid_driver_t handle,
                           void (*cb)(void *, const char *, int, int, int),
diff --git a/scd/command.c b/scd/command.c
index f6dfa39..d5598d4 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -395,7 +395,7 @@ get_current_reader (void)
   /* Try to open the reader. */
   if (vr->slot == -1)
     {
-      vr->slot = apdu_open_reader (opt.reader_port);
+      vr->slot = apdu_open_reader (opt.reader_port, vr);
 
       /* If we still don't have a slot, we have no readers.
 	 Invalidate for now until a reader is attached. */
@@ -2338,3 +2338,16 @@ scd_update_reader_status_file (void)
     log_error ("failed to release status_file_update lock: %s\n",
 	       strerror (err));
 }
+
+
+void
+scd_handle_reader_removal (void *arg)
+{
+  struct vreader_s *vr = arg;
+  int idx = vr - vreader_table;
+
+  if (!vr->valid || vr->slot == -1)
+    return;
+
+  update_card_removed (idx, 1);
+}
diff --git a/scd/scdaemon.c b/scd/scdaemon.c
index 38e3c40..8deeb13 100644
--- a/scd/scdaemon.c
+++ b/scd/scdaemon.c
@@ -1175,6 +1175,87 @@ start_connection_thread (void *arg)
   return NULL;
 }
 
+static fd_set fdset;
+static int nfd;
+
+struct reader_list {
+  int fd;
+  int is_token;
+  callback cb;
+  void *arg;
+  struct reader_list *next;
+};
+struct reader_list *reader_list;
+
+
+/* Register/Unregister FD to monitor removal of token.  */
+void
+scd_manage_device_removal_check (int fd, int is_token, callback cb, void *arg)
+{
+  struct reader_list *l;
+
+  l = xtrycalloc (1, sizeof *l);
+  if (!l)
+    {
+      log_error ("error allocating device monitor check: %s\n",
+                 strerror (errno) );
+      scd_exit (2);
+    }
+
+  l->fd = fd;
+  l->is_token = is_token;
+  l->cb = cb;
+  l->arg = arg;
+  l->next = reader_list;
+  reader_list = l;
+
+  FD_SET (fd, &fdset);
+  if (fd > nfd)
+    nfd = fd;
+}
+
+static int
+usb_token_registered (void)
+{
+  struct reader_list *l;
+
+  if (reader_list == NULL)
+    return 0;
+
+  for (l = reader_list; l; l = l->next)
+    if (!l->is_token)
+      return 0;
+
+  return 1;
+}
+
+static void
+handle_device_removal (int fd)
+{
+  struct reader_list *l, *l_prev, *l_next;
+
+  l_prev = NULL;
+  for (l = reader_list; l; l = l_next)
+    {
+      l_next = l->next;
+
+      if (l->fd == fd)
+        {
+          l->cb (l->arg);
+          FD_CLR (fd, &fdset);
+
+          if (l_prev)
+            l_prev->next = l_next;
+          else
+            reader_list = l_next;
+          xfree (l);
+          break;
+        }
+      else
+        l_prev = l;
+    }
+}
+
 
 /* Connection handler loop.  Wait for connection requests and spawn a
    thread after accepting a connection.  LISTEN_FD is allowed to be -1
@@ -1186,13 +1267,13 @@ handle_connections (int listen_fd)
   npth_attr_t tattr;
   struct sockaddr_un paddr;
   socklen_t plen;
-  fd_set fdset, read_fdset;
+  fd_set read_fdset;
   int ret;
   int fd;
-  int nfd;
   struct timespec abstime;
   struct timespec curtime;
   struct timespec timeout;
+  struct timespec *t;
   int saved_errno;
 #ifndef HAVE_W32_SYSTEM
   int signo;
@@ -1241,29 +1322,35 @@ handle_connections (int listen_fd)
           listen_fd = -1;
 	}
 
-      npth_clock_gettime (&curtime);
-      if (!(npth_timercmp (&curtime, &abstime, <)))
-	{
-	  /* Timeout.  */
-	  handle_tick ();
-	  timeout.tv_sec = TIMERTICK_INTERVAL_SEC;
-	  timeout.tv_nsec = TIMERTICK_INTERVAL_USEC * 1000;
-	  npth_timeradd (&curtime, &timeout, &abstime);
-	}
-      npth_timersub (&abstime, &curtime, &timeout);
+     if (usb_token_registered ())
+       t = NULL;
+     else
+       {
+         npth_clock_gettime (&curtime);
+         if (!(npth_timercmp (&curtime, &abstime, <)))
+           {
+             /* Timeout.  */
+             handle_tick ();
+             timeout.tv_sec = TIMERTICK_INTERVAL_SEC;
+             timeout.tv_nsec = TIMERTICK_INTERVAL_USEC * 1000;
+             npth_timeradd (&curtime, &timeout, &abstime);
+           }
+         npth_timersub (&abstime, &curtime, &timeout);
+         t = &timeout;
+       }
 
       /* POSIX says that fd_set should be implemented as a structure,
          thus a simple assignment is fine to copy the entire set.  */
       read_fdset = fdset;
 
 #ifndef HAVE_W32_SYSTEM
-      ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout, npth_sigev_sigmask());
+      ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, t, npth_sigev_sigmask());
       saved_errno = errno;
 
       while (npth_sigev_get_pending(&signo))
 	handle_signal (signo);
 #else
-      ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, &timeout, NULL, NULL);
+      ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, t, NULL, NULL);
       saved_errno = errno;
 #endif
 
@@ -1314,7 +1401,18 @@ handle_connections (int listen_fd)
 		npth_setname_np (thread, threadname);
             }
           fd = -1;
-	}
+
+          ret--;
+        }
+
+      if (ret)
+        {
+          int i;
+
+          for (i = 0; i < FD_SETSIZE; i++)
+            if (FD_ISSET (i, &read_fdset))
+              handle_device_removal (i);
+        }
     }
 
   cleanup ();
diff --git a/scd/scdaemon.h b/scd/scdaemon.h
index 38aa490..fd0b69f 100644
--- a/scd/scdaemon.h
+++ b/scd/scdaemon.h
@@ -112,6 +112,7 @@ struct server_control_s
 };
 
 typedef struct app_ctx_s *app_t;
+typedef void (*callback) (void *);
 
 /*-- scdaemon.c --*/
 void scd_exit (int rc);
@@ -124,6 +125,8 @@ void send_status_info (ctrl_t ctrl, const char *keyword, ...)
      GPGRT_ATTR_SENTINEL(1);
 void send_status_direct (ctrl_t ctrl, const char *keyword, const char *args);
 void scd_update_reader_status_file (void);
+void scd_manage_device_removal_check (int fd, int is_token, callback cb, void *arg);
+void scd_handle_reader_removal (void *arg);
 
 
 #endif /*SCDAEMON_H*/
--



More information about the Gnupg-devel mailing list