scd: support of multiple card readers

NIIBE Yutaka gniibe at fsij.org
Fri Dec 30 05:33:17 CET 2016


Hello,

Here is my experimental patch for support of multiple card readers.

It support multiple card readers by internal CCID driver.  PC/SC support
is unchanged.

Please note that this change is only the lower level things.

Currently, only "learn --all --force" (adding new option --all) is
supported by command level.  To use effectively, we need to change the
command sets of scdaemon so that it can work with multiple cards.

Perhaps, specifying KEYGRIP for methods would be good.  Or, adding
KEYINFO command just like gpg-agent would be good.


diff --git a/scd/apdu.c b/scd/apdu.c
index d0b75c8..d9fde61 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -66,6 +66,13 @@
 #define CCID_DRIVER_INCLUDE_USB_IDS 1
 #include "ccid-driver.h"
 
+struct dev_list {
+  struct ccid_dev_table *ccid_table;
+  const char *portstr;
+  int idx;
+  int idx_max;
+};
+
 /* Due to conflicting use of threading libraries we usually can't link
    against libpcsclite if we are using Pth.  Instead we use a wrapper
    program.  Note that with nPth there is no need for a wrapper. */
@@ -428,7 +435,6 @@ new_reader_slot (void)
 {
   int i, reader = -1;
 
-  npth_mutex_lock (&reader_table_lock);
   for (i=0; i < MAX_READER; i++)
     if (!reader_table[i].used)
       {
@@ -436,7 +442,6 @@ new_reader_slot (void)
         reader_table[reader].used = 1;
         break;
       }
-  npth_mutex_unlock (&reader_table_lock);
 
   if (reader == -1)
     {
@@ -1428,8 +1433,6 @@ static int
 close_pcsc_reader_direct (int slot)
 {
   pcsc_release_context (reader_table[slot].pcsc.context);
-  xfree (reader_table[slot].rdrname);
-  reader_table[slot].rdrname = NULL;
   return 0;
 }
 #endif /*!NEED_PCSC_WRAPPER*/
@@ -2432,7 +2435,6 @@ static int
 close_ccid_reader (int slot)
 {
   ccid_close_reader (reader_table[slot].ccid.handle);
-  reader_table[slot].rdrname = NULL;
   return 0;
 }
 
@@ -2566,7 +2568,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 (struct dev_list *dl)
 {
   int err;
   int slot;
@@ -2577,8 +2579,8 @@ open_ccid_reader (const char *portstr)
     return -1;
   slotp = reader_table + slot;
 
-  err = ccid_open_reader (&slotp->ccid.handle, portstr,
-                          (const char **)&slotp->rdrname);
+  err = ccid_open_reader (dl->portstr, dl->idx, dl->ccid_table,
+                          &slotp->ccid.handle, &slotp->rdrname);
   if (err)
     {
       slotp->used = 0;
@@ -2611,12 +2613,7 @@ open_ccid_reader (const char *portstr)
   unlock_slot (slot);
   return slot;
 }
-
-
-
 #endif /* HAVE_LIBUSB */
-
-
 
 #ifdef USE_G10CODE_RAPDU
 /*
@@ -2919,63 +2916,80 @@ open_rapdu_reader (int portno,
 /*
        Driver Access
  */
+gpg_error_t
+apdu_dev_list_start (const char *portstr, struct dev_list **l_p)
+{
+  gpg_error_t err;
+  struct dev_list *dl = xtrymalloc (sizeof (struct dev_list));
 
+  *l_p = NULL;
+  if (!dl)
+    return gpg_error_from_syserror ();
 
-/* Open the reader and return an internal slot number or -1 on
-   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)
-{
-  static int pcsc_api_loaded, ct_api_loaded;
-  int slot;
+  dl->portstr = portstr;
+  dl->idx = 0;
 
-  if (DBG_READER)
-    log_debug ("enter: apdu_open_reader: portstr=%s\n", portstr);
+  npth_mutex_lock (&reader_table_lock);
 
 #ifdef HAVE_LIBUSB
-  if (!opt.disable_ccid)
+  if (opt.disable_ccid)
     {
-      static int once_available;
-      int i;
-      const char *s;
-
-      slot = open_ccid_reader (portstr);
-      if (slot != -1)
-        {
-          once_available = 1;
-          if (DBG_READER)
-            log_debug ("leave: apdu_open_reader => slot=%d [ccid]\n", slot);
-          return slot; /* got one */
-        }
+      dl->ccid_table = NULL;
+      dl->idx_max = 1;
+    }
+  else
+    {
+      err = ccid_dev_scan (&dl->idx_max, &dl->ccid_table);
+      if (err)
+        return err;
 
-      /* If we ever loaded successfully loaded a CCID reader we never
-         want to fallback to another driver.  This solves a problem
-         where ccid was used, the card unplugged and then scdaemon
-         tries to find a new reader and will eventually try PC/SC over
-         and over again.  To reset this flag "gpgconf --kill scdaemon"
-         can be used.  */
-      if (once_available)
+      if (dl->idx_max == 0)
         {
-          if (DBG_READER)
-            log_debug ("leave: apdu_open_reader => slot=-1 (once_avail)\n");
-          return -1;
-        }
-
-      /* If a CCID reader specification has been given, the user does
-         not want a fallback to other drivers. */
-      if (portstr)
-        for (s=portstr, i=0; *s; s++)
-          if (*s == ':' && (++i == 3))
+          /* If a CCID reader specification has been given, the user does
+             not want a fallback to other drivers. */
+          if (portstr && strlen (portstr) > 5 && portstr[4] == ':')
             {
               if (DBG_READER)
                 log_debug ("leave: apdu_open_reader => slot=-1 (no ccid)\n");
-              return -1;
+
+              xfree (dl);
+              npth_mutex_unlock (&reader_table_lock);
+              return gpg_error (GPG_ERR_ENODEV);
             }
+          else
+            dl->idx_max = 1;
+        }
     }
-
+#else
+  dl->ccid_table = NULL;
+  dl->idx_max = 1;
 #endif /* HAVE_LIBUSB */
 
+  *l_p = dl;
+  return 0;
+}
+
+void
+apdu_dev_list_finish (struct dev_list *dl)
+{
+  ccid_dev_scan_finish (dl->ccid_table, dl->idx_max);
+  xfree (dl);
+  npth_mutex_unlock (&reader_table_lock);
+}
+
+
+/* Open the reader and return an internal slot number or -1 on
+   error. If PORTSTR is NULL we default to a suitable port (for ctAPI:
+   the first USB reader.  For PC/SC the first listed reader). */
+static int
+apdu_open_one_reader (const char *portstr)
+{
+  static int pcsc_api_loaded, ct_api_loaded;
+  int slot;
+
+  if (DBG_READER)
+    log_debug ("enter: apdu_open_reader: portstr=%s\n", portstr);
+
   if (opt.ctapi_driver && *opt.ctapi_driver)
     {
       int port = portstr? atoi (portstr) : 32768;
@@ -3005,7 +3019,6 @@ apdu_open_reader (const char *portstr)
       return open_ct_reader (port);
     }
 
-
   /* No ctAPI configured, so lets try the PC/SC API */
   if (!pcsc_api_loaded)
     {
@@ -3099,6 +3112,95 @@ apdu_open_reader (const char *portstr)
   return slot;
 }
 
+int
+apdu_open_reader (struct dev_list *dl)
+{
+  int slot;
+
+  if (dl->ccid_table)
+    { /* CCID readers.  */
+      int readerno;
+
+      /* See whether we want to use the reader ID string or a reader
+         number. A readerno of -1 indicates that the reader ID string is
+         to be used. */
+      if (dl->portstr && strchr (dl->portstr, ':'))
+        readerno = -1; /* We want to use the readerid.  */
+      else if (dl->portstr)
+        {
+          readerno = atoi (dl->portstr);
+          if (readerno < 0)
+            {
+              return -1;
+            }
+        }
+      else
+        readerno = 0;  /* Default. */
+
+      if (readerno > 0)
+        { /* Use single, the specific reader.  */
+          if (readerno >= dl->idx_max)
+            return -1;
+
+          dl->idx = readerno;
+          dl->portstr = NULL;
+          slot = open_ccid_reader (dl);
+          dl->idx = dl->idx_max;
+          if (slot >= 0)
+            return slot;
+          else
+            return -1;
+        }
+
+      while (dl->idx < dl->idx_max)
+        {
+          unsigned int bai = ccid_get_BAI (dl->idx, dl->ccid_table);
+
+          if (DBG_READER)
+            log_debug ("apdu_open_reader: BAI=%x\n", bai);
+
+          /* Check identity by BAI against already opened HANDLEs.  */
+          for (slot = 0; slot < MAX_READER; slot++)
+            if (reader_table[slot].used
+                && ccid_compare_BAI (reader_table[slot].ccid.handle, bai))
+              break;
+
+          if (slot == MAX_READER)
+            { /* Found a new device.  */
+              if (DBG_READER)
+                log_debug ("apdu_open_reader: new device=%x\n", bai);
+
+              slot = open_ccid_reader (dl);
+
+              dl->idx++;
+              if (slot >= 0)
+                return slot;
+              else
+                {
+                  log_error ("ccid open error\n");
+                  return -1;
+                }
+            }
+          else
+            dl->idx++;
+        }
+
+      slot = -1;
+    }
+  else
+    { /* PC/SC readers.  */
+      if (dl->idx++ == 0)
+        slot = apdu_open_one_reader (dl->portstr);
+      else
+        slot = -1;
+    }
+
+  if (DBG_READER)
+    log_debug ("leave: apdu_open_reader => slot=%d [ccid]\n", slot);
+
+  return slot;
+}
+
 
 /* Open an remote reader and return an internal slot number or -1 on
    error. This function is an alternative to apdu_open_reader and used
@@ -3177,6 +3279,8 @@ apdu_close_reader (int slot)
         log_debug ("leave: apdu_close_reader => 0x%x (close_reader)\n", sw);
       return sw;
     }
+  xfree (reader_table[slot].rdrname);
+  reader_table[slot].rdrname = NULL;
   reader_table[slot].used = 0;
   if (DBG_READER)
     log_debug ("leave: apdu_close_reader => SW_HOST_NOT_SUPPORTED\n");
@@ -3204,6 +3308,8 @@ apdu_prepare_exit (void)
             apdu_disconnect (slot);
             if (reader_table[slot].close_reader)
               reader_table[slot].close_reader (slot);
+            xfree (reader_table[slot].rdrname);
+            reader_table[slot].rdrname = NULL;
             reader_table[slot].used = 0;
           }
       npth_mutex_unlock (&reader_table_lock);
diff --git a/scd/apdu.h b/scd/apdu.h
index 3021cf7..473def5 100644
--- a/scd/apdu.h
+++ b/scd/apdu.h
@@ -74,6 +74,7 @@ enum {
   SW_HOST_ALREADY_CONNECTED = 0x1000f
 };
 
+struct dev_list;
 
 #define SW_EXACT_LENGTH_P(a) (((a)&~0xff) == SW_EXACT_LENGTH)
 
@@ -86,8 +87,11 @@ enum {
 
 gpg_error_t apdu_init (void);
 
+gpg_error_t apdu_dev_list_start (const char *portstr, struct dev_list **l_p);
+void apdu_dev_list_finish (struct dev_list *l);
+
 /* Note, that apdu_open_reader returns no status word but -1 on error. */
-int apdu_open_reader (const char *portstr);
+int apdu_open_reader (struct dev_list *l);
 int apdu_open_remote_reader (const char *portstr,
                              const unsigned char *cookie, size_t length,
                              int (*readfnc) (void *opaque,
@@ -117,9 +121,9 @@ int apdu_reset (int slot);
 int apdu_get_status (int slot, int hang, unsigned int *status);
 int apdu_check_pinpad (int slot, int command, pininfo_t *pininfo);
 int apdu_pinpad_verify (int slot, int class, int ins, int p0, int p1,
-			pininfo_t *pininfo);
+                        pininfo_t *pininfo);
 int apdu_pinpad_modify (int slot, int class, int ins, int p0, int p1,
-			pininfo_t *pininfo);
+                        pininfo_t *pininfo);
 int apdu_send_simple (int slot, int extended_mode,
                       int class, int ins, int p0, int p1,
                       int lc, const char *data);
diff --git a/scd/app-common.h b/scd/app-common.h
index 781bf46..2371818 100644
--- a/scd/app-common.h
+++ b/scd/app-common.h
@@ -121,6 +121,9 @@ size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff);
 
 
 /*-- app.c --*/
+app_t app_list_start (void);
+void app_list_finish (void);
+
 void app_dump_state (void);
 void application_notify_card_reset (int slot);
 gpg_error_t check_application_conflict (const char *name, app_t app);
diff --git a/scd/app.c b/scd/app.c
index 39910d2..92d0783 100644
--- a/scd/app.c
+++ b/scd/app.c
@@ -319,22 +319,30 @@ app_new_register (int slot, ctrl_t ctrl, const char *name)
 gpg_error_t
 select_application (ctrl_t ctrl, const char *name, app_t *r_app, int scan)
 {
-  gpg_error_t err;
+  gpg_error_t err = 0;
   app_t app;
-  int slot;
 
   *r_app = NULL;
 
-  if (scan
-      /* FIXME: Here, we can change code to support multiple readers.
-         For now, we only open a single reader.
-      */
-      && !app_top)
+  if (scan)
     {
-      slot = apdu_open_reader (opt.reader_port);
-      if (slot >= 0)
+      struct dev_list *l;
+
+      err = apdu_dev_list_start (opt.reader_port, &l);
+      if (err)
+        return err;
+
+      while (1)
         {
-          int sw = apdu_connect (slot);
+          int slot;
+          int sw;
+
+          slot = apdu_open_reader (l);
+          if (slot < 0)
+            break;
+
+          err = 0;
+          sw = apdu_connect (slot);
 
           if (sw == SW_HOST_CARD_INACTIVE)
             {
@@ -346,23 +354,17 @@ select_application (ctrl_t ctrl, const char *name, app_t *r_app, int scan)
             err = 0;
           else
             err = gpg_error (GPG_ERR_ENODEV);
+
+          if (!err)
+            err = app_new_register (slot, ctrl, name);
+          else
+            apdu_close_reader (slot);
         }
-      else
-        err = gpg_error (GPG_ERR_ENODEV);
 
-      if (!err)
-        err = app_new_register (slot, ctrl, name);
-      else
-        apdu_close_reader (slot);
+      apdu_dev_list_finish (l);
     }
-  else
-    err = 0;
-
-  if (!err)
-    app = app_top;
-  else
-    app = NULL;
 
+  app = app_top;
   if (app)
     {
       lock_app (app, ctrl);
@@ -550,8 +552,10 @@ app_write_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
 
   if (!app)
     return gpg_error (GPG_ERR_INV_VALUE);
+#if 0
   if (!app->ref_count)
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+#endif
   if (!app->fnc.learn_status)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
 
@@ -1069,3 +1073,16 @@ initialize_module_command (void)
 
   return apdu_init ();
 }
+
+app_t
+app_list_start (void)
+{
+  npth_mutex_lock (&app_list_lock);
+  return app_top;
+}
+
+void
+app_list_finish (void)
+{
+  npth_mutex_unlock (&app_list_lock);
+}
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c
index 6d81122..48dd887 100644
--- a/scd/ccid-driver.c
+++ b/scd/ccid-driver.c
@@ -239,12 +239,11 @@ static struct
 struct ccid_driver_s
 {
   libusb_device_handle *idev;
-  char *rid;
   int dev_fd;  /* -1 for USB transport or file descriptor of the
                    transport device. */
+  unsigned int bai;
   unsigned short id_vendor;
   unsigned short id_product;
-  unsigned short bcd_device;
   int ifc_no;
   int ep_bulk_out;
   int ep_bulk_in;
@@ -718,8 +717,6 @@ print_r2p_unknown (const unsigned char *msg, size_t msglen)
 static void
 prepare_special_transport (ccid_driver_t handle)
 {
-  assert (!handle->id_vendor);
-
   handle->nonnull_nad = 0;
   handle->auto_ifsd = 0;
   handle->max_ifsd = 32;
@@ -744,14 +741,13 @@ prepare_special_transport (ccid_driver_t handle)
    Note, that this code is based on the one in lsusb.c of the
    usb-utils package, I wrote on 2003-09-01. -wk. */
 static int
-parse_ccid_descriptor (ccid_driver_t handle,
+parse_ccid_descriptor (ccid_driver_t handle, unsigned short bcd_device,
                        const unsigned char *buf, size_t buflen)
 {
   unsigned int i;
   unsigned int us;
   int have_t1 = 0, have_tpdu=0;
 
-
   handle->nonnull_nad = 0;
   handle->auto_ifsd = 0;
   handle->max_ifsd = 32;
@@ -761,7 +757,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
   handle->auto_param = 0;
   handle->auto_pps = 0;
   DEBUGOUT_3 ("idVendor: %04X  idProduct: %04X  bcdDevice: %04X\n",
-              handle->id_vendor, handle->id_product, handle->bcd_device);
+              handle->id_vendor, handle->id_product, bcd_device);
   if (buflen < 54 || buf[0] < 54)
     {
       DEBUGOUT ("CCID device descriptor is too short\n");
@@ -964,11 +960,11 @@ parse_ccid_descriptor (ccid_driver_t handle,
   */
   if (handle->id_vendor == VENDOR_SCM
       && handle->max_ifsd > 48
-      && (  (handle->id_product == SCM_SCR331   && handle->bcd_device < 0x0516)
-          ||(handle->id_product == SCM_SCR331DI && handle->bcd_device < 0x0620)
-          ||(handle->id_product == SCM_SCR335   && handle->bcd_device < 0x0514)
-          ||(handle->id_product == SCM_SPR532   && handle->bcd_device < 0x0504)
-          ||(handle->id_product == SCM_SCR3320  && handle->bcd_device < 0x0522)
+      && (  (handle->id_product == SCM_SCR331   && bcd_device < 0x0516)
+          ||(handle->id_product == SCM_SCR331DI && bcd_device < 0x0620)
+          ||(handle->id_product == SCM_SCR335   && bcd_device < 0x0514)
+          ||(handle->id_product == SCM_SPR532   && bcd_device < 0x0504)
+          ||(handle->id_product == SCM_SCR3320  && bcd_device < 0x0522)
           ))
     {
       DEBUGOUT ("enabling workaround for buggy SCM readers\n");
@@ -1534,23 +1530,33 @@ ccid_vendor_specific_init (ccid_driver_t handle)
 }
 
 
-/* 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,
-                  const char **rdrname_p)
-{
-  int rc = 0;
-  libusb_device_handle *idev = NULL;
-  int dev_fd = -1;
-  char *rid = NULL;
-  unsigned char *ifcdesc_extra = NULL;
+#define MAX_DEVICE 4 /* See MAX_READER in apdu.c.  */
+
+struct ccid_dev_table {
+  int n;                        /* Index to ccid_usb_dev_list */
+  int transport;
+  int interface_number;
+  int setting_number;
+  unsigned char *ifcdesc_extra;
+  int ep_bulk_out;
+  int ep_bulk_in;
+  int ep_intr;
   size_t ifcdesc_extra_len;
-  int readerno;
-  int ifc_no, set_no, ep_bulk_out, ep_bulk_in, ep_intr;
-  struct libusb_device_descriptor desc;
+};
 
-  *handle = NULL;
+static libusb_device **ccid_usb_dev_list;
+static struct ccid_dev_table ccid_dev_table[MAX_DEVICE];
+
+gpg_error_t
+ccid_dev_scan (int *idx_max_p, struct ccid_dev_table **t_p)
+{
+  ssize_t n;
+  libusb_device *dev;
+  int i;
+  int ifc_no;
+  int set_no;
+  int idx = 0;
+  int err = 0;
 
   if (!initialized_usb)
     {
@@ -1558,122 +1564,379 @@ ccid_open_reader (ccid_driver_t *handle, const char *readerid,
       initialized_usb = 1;
     }
 
-  /* See whether we want to use the reader ID string or a reader
-     number. A readerno of -1 indicates that the reader ID string is
-     to be used. */
-  if (readerid && strchr (readerid, ':'))
-    readerno = -1; /* We want to use the readerid.  */
-  else if (readerid)
+  n = libusb_get_device_list (NULL, &ccid_usb_dev_list);
+  for (i = 0; i < n; i++)
+    {
+      struct libusb_config_descriptor *config;
+      struct libusb_device_descriptor desc;
+
+      dev = ccid_usb_dev_list[i];
+
+      if (libusb_get_device_descriptor (dev, &desc))
+        continue;
+
+      if (libusb_get_active_config_descriptor (dev, &config))
+        continue;
+
+      for (ifc_no=0; ifc_no < config->bNumInterfaces; ifc_no++)
+        for (set_no=0; set_no < config->interface[ifc_no].num_altsetting;
+             set_no++)
+          {
+            const struct libusb_interface_descriptor *ifcdesc;
+
+            ifcdesc = &config->interface[ifc_no].altsetting[set_no];
+            /* The second condition is for older SCM SPR 532 who did
+               not know about the assigned CCID class.  The third
+               condition does the same for a Cherry SmartTerminal
+               ST-2000.  Instead of trying to interpret the strings
+               we simply check the product ID. */
+            if (ifcdesc && ifcdesc->extra
+                && ((ifcdesc->bInterfaceClass == 11
+                     && ifcdesc->bInterfaceSubClass == 0
+                     && ifcdesc->bInterfaceProtocol == 0)
+                    || (ifcdesc->bInterfaceClass == 255
+                        && desc.idVendor == VENDOR_SCM
+                        && desc.idProduct == SCM_SPR532)
+                    || (ifcdesc->bInterfaceClass == 255
+                        && desc.idVendor == VENDOR_CHERRY
+                        && desc.idProduct == CHERRY_ST2000)))
+              {
+                /* Found a reader.  */
+                unsigned char *ifcdesc_extra;
+
+                ifcdesc_extra = malloc (ifcdesc->extra_length);
+                if (!ifcdesc_extra)
+                  {
+                    err = gpg_error_from_syserror ();
+                    libusb_free_config_descriptor (config);
+                    goto scan_finish;
+                  }
+                memcpy (ifcdesc_extra, ifcdesc->extra, ifcdesc->extra_length);
+
+                ccid_dev_table[idx].transport = TRANSPORT_USB;
+                ccid_dev_table[idx].n = i;
+                ccid_dev_table[idx].interface_number = ifc_no;
+                ccid_dev_table[idx].setting_number = set_no;
+                ccid_dev_table[idx].ifcdesc_extra = ifcdesc_extra;
+                ccid_dev_table[idx].ifcdesc_extra_len = ifcdesc->extra_length;
+                ccid_dev_table[idx].ep_bulk_out = find_endpoint (ifcdesc, 0);
+                ccid_dev_table[idx].ep_bulk_in = find_endpoint (ifcdesc, 1);
+                ccid_dev_table[idx].ep_intr = find_endpoint (ifcdesc, 2);
+
+                idx++;
+                if (idx >= MAX_DEVICE)
+                  {
+                    libusb_free_config_descriptor (config);
+                    err = 0;
+                    goto scan_finish;
+                  }
+              }
+          }
+
+      libusb_free_config_descriptor (config);
+    }
+
+  /* Now check whether there are any devices with special transport types. */
+  for (i=0; transports[i].name; i++)
     {
-      readerno = atoi (readerid);
-      if (readerno < 0)
+      if (access (transports[i].name, (R_OK|W_OK)) == 0)
         {
-          DEBUGOUT ("no CCID readers found\n");
-          rc = CCID_DRIVER_ERR_NO_READER;
-          goto leave;
+          /* Found a device. */
+          DEBUGOUT_1 ("Found CCID reader %d\n", idx);
+
+          ccid_dev_table[idx].transport = TRANSPORT_CM4040;
+          ccid_dev_table[idx].n = i;
+          ccid_dev_table[idx].interface_number = 0;
+          ccid_dev_table[idx].setting_number = 0;
+          ccid_dev_table[idx].ifcdesc_extra = NULL;
+          ccid_dev_table[idx].ifcdesc_extra_len = 0;
+          ccid_dev_table[idx].ep_bulk_out = 0;
+          ccid_dev_table[idx].ep_bulk_in = 0;
+          ccid_dev_table[idx].ep_intr = 0;
+
+          idx++;
+          if (idx >= MAX_DEVICE)
+            goto scan_finish;
         }
     }
-  else
-    readerno = 0;  /* Default. */
 
-  if (scan_or_find_devices (readerno, readerid, &rid, &desc, &ifcdesc_extra,
-                            &ifcdesc_extra_len, &ifc_no, &set_no, &ep_bulk_out,
-                            &ep_bulk_in, &ep_intr, &idev, &dev_fd))
+ scan_finish:
+
+  if (err)
     {
-      if (readerno == -1)
-        DEBUGOUT_1 ("no CCID reader with ID %s\n", readerid );
+      *idx_max_p = 0;
+      *t_p = NULL;
+      for (i = 0; i < idx; i++)
+        {
+          free (ccid_dev_table[idx].ifcdesc_extra);
+          ccid_dev_table[idx].transport = 0;
+          ccid_dev_table[idx].n = 0;
+          ccid_dev_table[idx].interface_number = 0;
+          ccid_dev_table[idx].setting_number = 0;
+          ccid_dev_table[idx].ifcdesc_extra = NULL;
+          ccid_dev_table[idx].ifcdesc_extra_len = 0;
+          ccid_dev_table[idx].ep_bulk_out = 0;
+          ccid_dev_table[idx].ep_bulk_in = 0;
+          ccid_dev_table[idx].ep_intr = 0;
+        }
+      libusb_free_device_list (ccid_usb_dev_list, 1);
+      ccid_usb_dev_list = NULL;
+    }
+  else
+    {
+      *idx_max_p = idx;
+      if (idx)
+        *t_p = ccid_dev_table;
       else
-        DEBUGOUT_1 ("no CCID reader with number %d\n", readerno );
-      rc = CCID_DRIVER_ERR_NO_READER;
-      goto leave;
+        *t_p = NULL;
     }
 
-  /* Okay, this is a CCID reader. */
-  *handle = calloc (1, sizeof **handle);
-  if (!*handle)
+  return err;
+}
+
+void
+ccid_dev_scan_finish (struct ccid_dev_table *tbl, int max)
+{
+  int i;
+
+  for (i = 0; i < max; i++)
     {
-      DEBUGOUT ("out of memory\n");
-      rc = CCID_DRIVER_ERR_OUT_OF_CORE;
-      goto leave;
+      free (tbl[i].ifcdesc_extra);
+      tbl[i].transport = 0;
+      tbl[i].n = 0;
+      tbl[i].interface_number = 0;
+      tbl[i].setting_number = 0;
+      tbl[i].ifcdesc_extra = NULL;
+      tbl[i].ifcdesc_extra_len = 0;
+      tbl[i].ep_bulk_out = 0;
+      tbl[i].ep_bulk_in = 0;
+      tbl[i].ep_intr = 0;
     }
-  (*handle)->rid = rid;
-  if (idev) /* Regular USB transport. */
+  libusb_free_device_list (ccid_usb_dev_list, 1);
+  ccid_usb_dev_list = NULL;
+}
+
+unsigned int
+ccid_get_BAI (int idx, struct ccid_dev_table *tbl)
+{
+  int n;
+  int bus, addr, intf;
+  unsigned int bai;
+
+  if (tbl[idx].transport == TRANSPORT_USB)
     {
-      (*handle)->idev = idev;
-      (*handle)->dev_fd = -1;
-      (*handle)->id_vendor = desc.idVendor;
-      (*handle)->id_product = desc.idProduct;
-      (*handle)->bcd_device = desc.bcdDevice;
-      (*handle)->ifc_no = ifc_no;
-      (*handle)->ep_bulk_out = ep_bulk_out;
-      (*handle)->ep_bulk_in = ep_bulk_in;
-      (*handle)->ep_intr = ep_intr;
+      libusb_device *dev;
+
+      n = tbl[idx].n;
+      dev = ccid_usb_dev_list[n];
+
+      bus = libusb_get_bus_number (dev);
+      addr = libusb_get_device_address (dev);
+      intf = tbl[idx].interface_number;
+      bai = (bus << 16) | (addr << 8) | intf;
     }
-  else if (dev_fd != -1) /* Device transport. */
+  else
     {
-      (*handle)->idev = NULL;
-      (*handle)->dev_fd = dev_fd;
-      (*handle)->id_vendor = 0;  /* Magic vendor for special transport. */
-      (*handle)->id_product = ifc_no; /* Transport type */
-      prepare_special_transport (*handle);
+      n = tbl[idx].n;
+      bai = 0xFFFF0000 | n;
     }
-  else
+
+  return bai;
+}
+
+int
+ccid_compare_BAI (ccid_driver_t handle, unsigned int bai)
+{
+  return handle->bai == bai;
+}
+
+static int
+ccid_open_usb_reader (const char *spec_reader_name,
+                      int idx, struct ccid_dev_table *ccid_table,
+                      ccid_driver_t *handle, char **rdrname_p)
+{
+  libusb_device *dev;
+  libusb_device_handle *idev = NULL;
+  char *rid;
+  int rc = 0;
+  int ifc_no, set_no;
+  struct libusb_device_descriptor desc;
+  int n;
+  int bus, addr;
+  unsigned int bai;
+
+  n = ccid_table[idx].n;
+  ifc_no = ccid_table[idx].interface_number;
+  set_no = ccid_table[idx].setting_number;
+
+  dev = ccid_usb_dev_list[n];
+  bus = libusb_get_bus_number (dev);
+  addr = libusb_get_device_address (dev);
+  bai = (bus << 16) | (addr << 8) | ifc_no;
+
+  rc = libusb_open (dev, &idev);
+  if (rc)
     {
-      assert (!"no transport"); /* Bug. */
+      DEBUGOUT_1 ("usb_open failed: %s\n", libusb_error_name (rc));
+      free (*handle);
+      *handle = NULL;
+      return rc;
     }
 
-  DEBUGOUT_2 ("using CCID reader %d (ID=%s)\n",  readerno, rid );
+  rc = libusb_get_device_descriptor (dev, &desc);
+  if (rc)
+    {
+      libusb_close (idev);
+      free (*handle);
+      *handle = NULL;
+      return rc;
+    }
+
+  rid = make_reader_id (idev, desc.idVendor, desc.idProduct,
+                        desc.iSerialNumber);
 
-  if (idev)
+  /* Check to see if reader name matches the spec.  */
+  if (spec_reader_name
+      && strncmp (rid, spec_reader_name, strlen (spec_reader_name)))
     {
-      if (parse_ccid_descriptor (*handle, ifcdesc_extra, ifcdesc_extra_len))
-        {
-          DEBUGOUT ("device not supported\n");
-          rc = CCID_DRIVER_ERR_NO_READER;
-          goto leave;
-        }
+      DEBUGOUT ("device not matched\n");
+      rc = CCID_DRIVER_ERR_NO_READER;
+      goto leave;
+    }
+
+  (*handle)->id_vendor = desc.idVendor;
+  (*handle)->id_product = desc.idProduct;
+  (*handle)->idev = idev;
+  (*handle)->dev_fd = -1;
+  (*handle)->bai = bai;
+  (*handle)->ifc_no = ifc_no;
+  (*handle)->ep_bulk_out = ccid_table[idx].ep_bulk_out;
+  (*handle)->ep_bulk_in = ccid_table[idx].ep_bulk_in;
+  (*handle)->ep_intr = ccid_table[idx].ep_intr;
+
+  DEBUGOUT_2 ("using CCID reader %d (ID=%s)\n", idx, rid);
+
+  if (parse_ccid_descriptor (*handle, desc.bcdDevice,
+                             ccid_table[idx].ifcdesc_extra,
+                             ccid_table[idx].ifcdesc_extra_len))
+    {
+      DEBUGOUT ("device not supported\n");
+      rc = CCID_DRIVER_ERR_NO_READER;
+      goto leave;
+    }
+
+  rc = libusb_claim_interface (idev, ifc_no);
+  if (rc)
+    {
+      DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
+      rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
+      goto leave;
+    }
 
-      rc = libusb_claim_interface (idev, ifc_no);
+  if (set_no != 0)
+    {
+      rc = libusb_set_interface_alt_setting (idev, ifc_no, set_no);
       if (rc)
         {
-          DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
+          DEBUGOUT_1 ("usb_set_interface_alt_setting failed: %d\n", rc);
           rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
           goto leave;
         }
-
-      if (set_no != 0)
-        {
-          rc = libusb_set_interface_alt_setting (idev, ifc_no, set_no);
-          if (rc)
-            {
-              DEBUGOUT_1 ("usb_set_interface_alt_setting failed: %d\n", rc);
-              rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
-              goto leave;
-            }
-        }
     }
 
   rc = ccid_vendor_specific_init (*handle);
 
  leave:
-  free (ifcdesc_extra);
   if (rc)
     {
       free (rid);
-      if (idev)
-        libusb_close (idev);
-      if (dev_fd != -1)
-        close (dev_fd);
+      libusb_close (idev);
       free (*handle);
       *handle = NULL;
     }
   else
-    if (rdrname_p)
-      *rdrname_p = (*handle)->rid;
+    {
+      if (rdrname_p)
+        *rdrname_p = rid;
+      else
+        free (rid);
+    }
 
   return rc;
 }
 
+/* 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 (const char *spec_reader_name,
+                  int idx, struct ccid_dev_table *ccid_table,
+                  ccid_driver_t *handle, char **rdrname_p)
+{
+  int n;
+  int fd;
+  char *rid;
+
+  *handle = calloc (1, sizeof **handle);
+  if (!*handle)
+    {
+      DEBUGOUT ("out of memory\n");
+      return CCID_DRIVER_ERR_OUT_OF_CORE;
+    }
+
+  if (ccid_table[idx].transport == TRANSPORT_USB)
+    return ccid_open_usb_reader (spec_reader_name, idx, ccid_table,
+                                 handle, rdrname_p);
+
+  /* Special transport support.  */
+
+  n = ccid_table[idx].n;
+  fd = open (transports[n].name, O_RDWR);
+  if (fd < 0)
+    {
+      DEBUGOUT_2 ("failed to open '%s': %s\n",
+                  transports[n].name, strerror (errno));
+      free (*handle);
+      *handle = NULL;
+      return -1;
+    }
+
+  rid = malloc (strlen (transports[n].name) + 30 + 10);
+  if (!rid)
+    {
+      close (fd);
+      free (*handle);
+      *handle = NULL;
+      return -1; /* Error. */
+    }
+
+  sprintf (rid, "0000:%04X:%s:0", transports[n].type, transports[n].name);
+
+  /* Check to see if reader name matches the spec.  */
+  if (spec_reader_name
+      && strncmp (rid, spec_reader_name, strlen (spec_reader_name)))
+    {
+      DEBUGOUT ("device not matched\n");
+      free (rid);
+      close (fd);
+      free (*handle);
+      *handle = NULL;
+      return -1;
+    }
+
+  (*handle)->id_vendor = 0;
+  (*handle)->id_product = transports[n].type;
+  (*handle)->idev = NULL;
+  (*handle)->dev_fd = fd;
+  (*handle)->bai = 0xFFFF0000 | n;
+  prepare_special_transport (*handle);
+  if (rdrname_p)
+    *rdrname_p = rid;
+  else
+    free (rid);
+
+  return 0;
+}
+
 
 static void
 do_close_reader (ccid_driver_t handle)
@@ -1719,7 +1982,7 @@ ccid_set_progress_cb (ccid_driver_t handle,
                       void (*cb)(void *, const char *, int, int, int),
                       void *cb_arg)
 {
-  if (!handle || !handle->rid)
+  if (!handle)
     return CCID_DRIVER_ERR_INV_VALUE;
 
   handle->progress_cb = cb;
@@ -1736,7 +1999,6 @@ ccid_close_reader (ccid_driver_t handle)
     return 0;
 
   do_close_reader (handle);
-  free (handle->rid);
   free (handle);
   return 0;
 }
diff --git a/scd/ccid-driver.h b/scd/ccid-driver.h
index e3aed9f..9e71f5e 100644
--- a/scd/ccid-driver.h
+++ b/scd/ccid-driver.h
@@ -1,5 +1,5 @@
-/* ccid-driver.c - USB ChipCardInterfaceDevices driver
- *	Copyright (C) 2003 Free Software Foundation, Inc.
+/* ccid-driver.h - USB ChipCardInterfaceDevices driver
+ * Copyright (C) 2003 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -109,10 +109,18 @@ enum {
 struct ccid_driver_s;
 typedef struct ccid_driver_s *ccid_driver_t;
 
+struct ccid_dev_table;
+
 int ccid_set_debug_level (int level);
 char *ccid_get_reader_list (void);
-int ccid_open_reader (ccid_driver_t *handle, const char *readerid,
-                      const char **rdrname_p);
+
+gpg_error_t ccid_dev_scan (int *idx_max, struct ccid_dev_table **t_p);
+void ccid_dev_scan_finish (struct ccid_dev_table *tbl, int max);
+unsigned int ccid_get_BAI (int, struct ccid_dev_table *tbl);
+int ccid_compare_BAI (ccid_driver_t handle, unsigned int);
+int ccid_open_reader (const char *spec_reader_name,
+                      int idx, struct ccid_dev_table *ccid_table,
+                      ccid_driver_t *handle, char **rdrname_p);
 int ccid_set_progress_cb (ccid_driver_t handle,
                           void (*cb)(void *, const char *, int, int, int),
                           void *cb_arg);
@@ -126,7 +134,7 @@ int ccid_transceive (ccid_driver_t handle,
                      unsigned char *resp, size_t maxresplen, size_t *nresp);
 int ccid_transceive_secure (ccid_driver_t handle,
                      const unsigned char *apdu, size_t apdulen,
-		     pininfo_t *pininfo,
+                     pininfo_t *pininfo,
                      unsigned char *resp, size_t maxresplen, size_t *nresp);
 int ccid_transceive_escape (ccid_driver_t handle,
                             const unsigned char *data, size_t datalen,
diff --git a/scd/command.c b/scd/command.c
index ce2be34..59b16a2 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -281,6 +281,75 @@ cmd_serialno (assuan_context_t ctx, char *line)
 }
 
 
+static gpg_error_t
+do_learn (ctrl_t ctrl, assuan_context_t ctx, app_t app,
+          int force, int only_keypairinfo)
+{
+  int rc = 0;
+
+  /* Unless the force option is used we try a shortcut by identifying
+     the card using a serial number and inquiring the client with
+     that. The client may choose to cancel the operation if he already
+     knows about this card */
+  if (!only_keypairinfo)
+    {
+      const char *reader;
+      char *serial;
+      time_t stamp;
+
+      reader = apdu_get_reader_name (app->slot);
+      if (!reader)
+        return out_of_core ();
+      send_status_direct (ctrl, "READER", reader);
+      /* No need to free the string of READER.  */
+
+      rc = app_get_serial_and_stamp (app, &serial, &stamp);
+      if (rc)
+        return rc;
+
+      rc = print_assuan_status (ctx, "SERIALNO", "%s %lu",
+                                serial, (unsigned long)stamp);
+      if (rc < 0)
+        {
+          xfree (serial);
+          return out_of_core ();
+        }
+
+      if (!force)
+        {
+          char *command;
+
+          rc = gpgrt_asprintf (&command, "KNOWNCARDP %s %lu",
+                               serial, (unsigned long)stamp);
+          if (rc < 0)
+            {
+              xfree (serial);
+              return out_of_core ();
+            }
+          rc = assuan_inquire (ctx, command, NULL, NULL, 0);
+          xfree (command);
+          if (rc)
+            {
+              if (gpg_err_code (rc) != GPG_ERR_ASS_CANCELED)
+                log_error ("inquire KNOWNCARDP failed: %s\n",
+                           gpg_strerror (rc));
+              xfree (serial);
+              return rc;
+            }
+          /* Not canceled, so we have to proceeed.  */
+        }
+      xfree (serial);
+    }
+
+  /* Let the application print out its collection of useful status
+     information. */
+  if (!rc)
+    rc = app_write_learn_status (app, ctrl, only_keypairinfo);
+
+  return rc;
+}
+
+
 static const char hlp_learn[] =
   "LEARN [--force] [--keypairinfo]\n"
   "\n"
@@ -355,75 +424,30 @@ cmd_learn (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
   int rc = 0;
+  int force = has_option (line, "--force");
   int only_keypairinfo = has_option (line, "--keypairinfo");
+  int all = has_option (line, "--all");
+
+  app_t app;
 
   if ((rc = open_card (ctrl, NULL, 0)))
     return rc;
 
-  /* Unless the force option is used we try a shortcut by identifying
-     the card using a serial number and inquiring the client with
-     that. The client may choose to cancel the operation if he already
-     knows about this card */
-  if (!only_keypairinfo)
+  if (!all)
     {
-      const char *reader;
-      char *serial;
-      time_t stamp;
-      app_t app = ctrl->app_ctx;
-
+      app = ctrl->app_ctx;
       if (!app)
         return gpg_error (GPG_ERR_CARD_NOT_PRESENT);
 
-      reader = apdu_get_reader_name (app->slot);
-      if (!reader)
-        return out_of_core ();
-      send_status_direct (ctrl, "READER", reader);
-      /* No need to free the string of READER.  */
-
-      rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp);
-      if (rc)
-        return rc;
-
-      rc = print_assuan_status (ctx, "SERIALNO", "%s %lu",
-                                serial, (unsigned long)stamp);
-      if (rc < 0)
-        {
-          xfree (serial);
-          return out_of_core ();
-        }
-
-      if (!has_option (line, "--force"))
-        {
-          char *command;
-
-          rc = gpgrt_asprintf (&command, "KNOWNCARDP %s %lu",
-                               serial, (unsigned long)stamp);
-          if (rc < 0)
-            {
-              xfree (serial);
-              return out_of_core ();
-            }
-          rc = assuan_inquire (ctx, command, NULL, NULL, 0);
-          xfree (command);
-          if (rc)
-            {
-              if (gpg_err_code (rc) != GPG_ERR_ASS_CANCELED)
-                log_error ("inquire KNOWNCARDP failed: %s\n",
-                           gpg_strerror (rc));
-              xfree (serial);
-              return rc;
-            }
-          /* Not canceled, so we have to proceeed.  */
-        }
-      xfree (serial);
+      return do_learn (ctrl, ctx, app, force, only_keypairinfo);
+    }
+  else
+    {
+      for (app = app_list_start (); app; app = app->next)
+        do_learn (ctrl, ctx, app, force, only_keypairinfo);
+      app_list_finish ();
+      return 0;
     }
-
-  /* Let the application print out its collection of useful status
-     information. */
-  if (!rc)
-    rc = app_write_learn_status (ctrl->app_ctx, ctrl, only_keypairinfo);
-
-  return rc;
 }
 
 
-- 



More information about the Gnupg-devel mailing list