From 8a41e73c31adb86d4a7dca4da695e5ad1347811f Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Fri, 6 Jan 2017 09:14:13 +0900 Subject: [PATCH] scd: Support multiple readers by CCID driver. * scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start. (close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled... (apdu_close_reader): ... by this function now. (apdu_prepare_exit): Likewise. (open_ccid_reader): Open with dev_list. (apdu_dev_list_start, apdu_dev_list_finish): New. (apdu_open_one_reader): New. (apdu_open_reader): Support multiple readers. * scd/app.c (select_application): With SCAN, opening all readers available, and register as new APP. (app_write_learn_status): app->ref_count == 0 is valid for APP which is not yet used. (app_list_start, app_list_finish): New. * scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE. Add BAI. (parse_ccid_descriptor): BCD_DEVICE is now on the arguments. (ccid_dev_scan, ccid_dev_scan_finish): New. (ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New. (ccid_open_reader): Support multiple readers. (ccid_set_progress_cb, ccid_close_reader): No RID any more. -- With this change, multiple readers/tokens are supported by the internal CCID driver of GnuPG. Until the changes of upper layers (scdaemon, gpg-agent, and gpg front end), only a single reader is used, though. Signed-off-by: NIIBE Yutaka --- scd/apdu.c | 225 ++++++++++++++++------ scd/apdu.h | 10 +- scd/app-common.h | 3 + scd/app.c | 63 +++--- scd/ccid-driver.c | 474 ++++++++++++++++++++++++++++++++++++---------- scd/ccid-driver.h | 18 +- 6 files changed, 596 insertions(+), 197 deletions(-) diff --git a/scd/apdu.c b/scd/apdu.c index d0b75c872..50363ce4c 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,13 +2916,73 @@ 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 (); + + dl->portstr = portstr; + dl->idx = 0; + + npth_mutex_lock (&reader_table_lock); + +#ifdef HAVE_LIBUSB + if (opt.disable_ccid) + { + dl->ccid_table = NULL; + dl->idx_max = 1; + } + else + { + err = ccid_dev_scan (&dl->idx_max, &dl->ccid_table); + if (err) + return err; + + if (dl->idx_max == 0) + { + /* 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"); + + 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). */ -int -apdu_open_reader (const char *portstr) +static int +apdu_open_one_reader (const char *portstr) { static int pcsc_api_loaded, ct_api_loaded; int slot; @@ -2933,49 +2990,6 @@ apdu_open_reader (const char *portstr) if (DBG_READER) log_debug ("enter: apdu_open_reader: portstr=%s\n", portstr); -#ifdef HAVE_LIBUSB - 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 */ - } - - /* 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 (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 (DBG_READER) - log_debug ("leave: apdu_open_reader => slot=-1 (no ccid)\n"); - return -1; - } - } - -#endif /* HAVE_LIBUSB */ - 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,96 @@ 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 + { + /* Skip this reader. */ + log_error ("ccid open error: skip\n"); + continue; + } + } + 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 +3280,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 +3309,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 3021cf7a5..473def518 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 781bf465c..2371818e5 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 a49972433..6db9e27ef 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 && !app_top) - /* FIXME: Here, we can change code to support multiple readers. - For now, we only open a single reader. - */ - || !app_top) + if (scan || !app_top) { - 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); @@ -552,8 +554,6 @@ app_write_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) if (!app) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.learn_status) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); @@ -1071,3 +1071,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 6d8112282..5e02628e1 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; @@ -744,14 +743,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 +759,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 +962,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 +1532,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; - 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; +#define MAX_DEVICE 4 /* See MAX_READER in apdu.c. */ - *handle = NULL; +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; +}; + +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 +1566,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++) { - readerno = atoi (readerid); - if (readerno < 0) + 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++) + { + 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 ); + *t_p = NULL; + } + + return err; +} + +void +ccid_dev_scan_finish (struct ccid_dev_table *tbl, int max) +{ + int i; + + for (i = 0; i < max; i++) + { + 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; + } + 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) + { + 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 + { + n = tbl[idx].n; + bai = 0xFFFF0000 | n; + } + + 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) + { + DEBUGOUT_1 ("usb_open failed: %s\n", libusb_error_name (rc)); + free (*handle); + *handle = NULL; + return rc; + } + + 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); + + /* 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"); rc = CCID_DRIVER_ERR_NO_READER; goto leave; } - /* Okay, this is a CCID reader. */ - *handle = calloc (1, sizeof **handle); - if (!*handle) + (*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 ("out of memory\n"); - rc = CCID_DRIVER_ERR_OUT_OF_CORE; + DEBUGOUT ("device not supported\n"); + rc = CCID_DRIVER_ERR_NO_READER; goto leave; } - (*handle)->rid = rid; - if (idev) /* Regular USB transport. */ + + rc = libusb_claim_interface (idev, ifc_no); + if (rc) { - (*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; - } - else if (dev_fd != -1) /* Device transport. */ - { - (*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); - } - else - { - assert (!"no transport"); /* Bug. */ + DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc); + rc = CCID_DRIVER_ERR_CARD_IO_ERROR; + goto leave; } - DEBUGOUT_2 ("using CCID reader %d (ID=%s)\n", readerno, rid ); - - if (idev) + if (set_no != 0) { - if (parse_ccid_descriptor (*handle, ifcdesc_extra, ifcdesc_extra_len)) - { - DEBUGOUT ("device not supported\n"); - rc = CCID_DRIVER_ERR_NO_READER; - goto leave; - } - - rc = libusb_claim_interface (idev, ifc_no); + 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 +1984,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 +2001,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 e3aed9f56..9e71f5eb1 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,