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 <gniibe@fsij.org>
This commit is contained in:
NIIBE Yutaka 2017-01-06 09:14:13 +09:00
parent 6170eb8090
commit 8a41e73c31
6 changed files with 596 additions and 197 deletions

View File

@ -66,6 +66,13 @@
#define CCID_DRIVER_INCLUDE_USB_IDS 1 #define CCID_DRIVER_INCLUDE_USB_IDS 1
#include "ccid-driver.h" #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 /* Due to conflicting use of threading libraries we usually can't link
against libpcsclite if we are using Pth. Instead we use a wrapper against libpcsclite if we are using Pth. Instead we use a wrapper
program. Note that with nPth there is no need for 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; int i, reader = -1;
npth_mutex_lock (&reader_table_lock);
for (i=0; i < MAX_READER; i++) for (i=0; i < MAX_READER; i++)
if (!reader_table[i].used) if (!reader_table[i].used)
{ {
@ -436,7 +442,6 @@ new_reader_slot (void)
reader_table[reader].used = 1; reader_table[reader].used = 1;
break; break;
} }
npth_mutex_unlock (&reader_table_lock);
if (reader == -1) if (reader == -1)
{ {
@ -1428,8 +1433,6 @@ static int
close_pcsc_reader_direct (int slot) close_pcsc_reader_direct (int slot)
{ {
pcsc_release_context (reader_table[slot].pcsc.context); pcsc_release_context (reader_table[slot].pcsc.context);
xfree (reader_table[slot].rdrname);
reader_table[slot].rdrname = NULL;
return 0; return 0;
} }
#endif /*!NEED_PCSC_WRAPPER*/ #endif /*!NEED_PCSC_WRAPPER*/
@ -2432,7 +2435,6 @@ static int
close_ccid_reader (int slot) close_ccid_reader (int slot)
{ {
ccid_close_reader (reader_table[slot].ccid.handle); ccid_close_reader (reader_table[slot].ccid.handle);
reader_table[slot].rdrname = NULL;
return 0; 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. */ /* Open the reader and try to read an ATR. */
static int static int
open_ccid_reader (const char *portstr) open_ccid_reader (struct dev_list *dl)
{ {
int err; int err;
int slot; int slot;
@ -2577,8 +2579,8 @@ open_ccid_reader (const char *portstr)
return -1; return -1;
slotp = reader_table + slot; slotp = reader_table + slot;
err = ccid_open_reader (&slotp->ccid.handle, portstr, err = ccid_open_reader (dl->portstr, dl->idx, dl->ccid_table,
(const char **)&slotp->rdrname); &slotp->ccid.handle, &slotp->rdrname);
if (err) if (err)
{ {
slotp->used = 0; slotp->used = 0;
@ -2611,12 +2613,7 @@ open_ccid_reader (const char *portstr)
unlock_slot (slot); unlock_slot (slot);
return slot; return slot;
} }
#endif /* HAVE_LIBUSB */ #endif /* HAVE_LIBUSB */
#ifdef USE_G10CODE_RAPDU #ifdef USE_G10CODE_RAPDU
/* /*
@ -2919,13 +2916,73 @@ open_rapdu_reader (int portno,
/* /*
Driver Access 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 /* 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: error. If PORTSTR is NULL we default to a suitable port (for ctAPI:
the first USB reader. For PC/SC the first listed reader). */ the first USB reader. For PC/SC the first listed reader). */
int static int
apdu_open_reader (const char *portstr) apdu_open_one_reader (const char *portstr)
{ {
static int pcsc_api_loaded, ct_api_loaded; static int pcsc_api_loaded, ct_api_loaded;
int slot; int slot;
@ -2933,49 +2990,6 @@ apdu_open_reader (const char *portstr)
if (DBG_READER) if (DBG_READER)
log_debug ("enter: apdu_open_reader: portstr=%s\n", portstr); 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) if (opt.ctapi_driver && *opt.ctapi_driver)
{ {
int port = portstr? atoi (portstr) : 32768; int port = portstr? atoi (portstr) : 32768;
@ -3005,7 +3019,6 @@ apdu_open_reader (const char *portstr)
return open_ct_reader (port); return open_ct_reader (port);
} }
/* No ctAPI configured, so lets try the PC/SC API */ /* No ctAPI configured, so lets try the PC/SC API */
if (!pcsc_api_loaded) if (!pcsc_api_loaded)
{ {
@ -3099,6 +3112,96 @@ apdu_open_reader (const char *portstr)
return slot; 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 /* 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 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); log_debug ("leave: apdu_close_reader => 0x%x (close_reader)\n", sw);
return sw; return sw;
} }
xfree (reader_table[slot].rdrname);
reader_table[slot].rdrname = NULL;
reader_table[slot].used = 0; reader_table[slot].used = 0;
if (DBG_READER) if (DBG_READER)
log_debug ("leave: apdu_close_reader => SW_HOST_NOT_SUPPORTED\n"); log_debug ("leave: apdu_close_reader => SW_HOST_NOT_SUPPORTED\n");
@ -3204,6 +3309,8 @@ apdu_prepare_exit (void)
apdu_disconnect (slot); apdu_disconnect (slot);
if (reader_table[slot].close_reader) if (reader_table[slot].close_reader)
reader_table[slot].close_reader (slot); reader_table[slot].close_reader (slot);
xfree (reader_table[slot].rdrname);
reader_table[slot].rdrname = NULL;
reader_table[slot].used = 0; reader_table[slot].used = 0;
} }
npth_mutex_unlock (&reader_table_lock); npth_mutex_unlock (&reader_table_lock);

View File

@ -74,6 +74,7 @@ enum {
SW_HOST_ALREADY_CONNECTED = 0x1000f SW_HOST_ALREADY_CONNECTED = 0x1000f
}; };
struct dev_list;
#define SW_EXACT_LENGTH_P(a) (((a)&~0xff) == SW_EXACT_LENGTH) #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_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. */ /* 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, int apdu_open_remote_reader (const char *portstr,
const unsigned char *cookie, size_t length, const unsigned char *cookie, size_t length,
int (*readfnc) (void *opaque, 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_get_status (int slot, int hang, unsigned int *status);
int apdu_check_pinpad (int slot, int command, pininfo_t *pininfo); 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, 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, 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 apdu_send_simple (int slot, int extended_mode,
int class, int ins, int p0, int p1, int class, int ins, int p0, int p1,
int lc, const char *data); int lc, const char *data);

View File

@ -121,6 +121,9 @@ size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff);
/*-- app.c --*/ /*-- app.c --*/
app_t app_list_start (void);
void app_list_finish (void);
void app_dump_state (void); void app_dump_state (void);
void application_notify_card_reset (int slot); void application_notify_card_reset (int slot);
gpg_error_t check_application_conflict (const char *name, app_t app); gpg_error_t check_application_conflict (const char *name, app_t app);

View File

@ -319,22 +319,30 @@ app_new_register (int slot, ctrl_t ctrl, const char *name)
gpg_error_t gpg_error_t
select_application (ctrl_t ctrl, const char *name, app_t *r_app, int scan) 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; app_t app;
int slot;
*r_app = NULL; *r_app = NULL;
if ((scan && !app_top) if (scan || !app_top)
/* FIXME: Here, we can change code to support multiple readers.
For now, we only open a single reader.
*/
|| !app_top)
{ {
slot = apdu_open_reader (opt.reader_port); struct dev_list *l;
if (slot >= 0)
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) 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; err = 0;
else else
err = gpg_error (GPG_ERR_ENODEV); 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) apdu_dev_list_finish (l);
err = app_new_register (slot, ctrl, name);
else
apdu_close_reader (slot);
} }
else
err = 0;
if (!err)
app = app_top;
else
app = NULL;
app = app_top;
if (app) if (app)
{ {
lock_app (app, ctrl); lock_app (app, ctrl);
@ -552,8 +554,6 @@ app_write_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
if (!app) if (!app)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
if (!app->ref_count)
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
if (!app->fnc.learn_status) if (!app->fnc.learn_status)
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@ -1071,3 +1071,16 @@ initialize_module_command (void)
return apdu_init (); 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);
}

View File

@ -239,12 +239,11 @@ static struct
struct ccid_driver_s struct ccid_driver_s
{ {
libusb_device_handle *idev; libusb_device_handle *idev;
char *rid;
int dev_fd; /* -1 for USB transport or file descriptor of the int dev_fd; /* -1 for USB transport or file descriptor of the
transport device. */ transport device. */
unsigned int bai;
unsigned short id_vendor; unsigned short id_vendor;
unsigned short id_product; unsigned short id_product;
unsigned short bcd_device;
int ifc_no; int ifc_no;
int ep_bulk_out; int ep_bulk_out;
int ep_bulk_in; 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 Note, that this code is based on the one in lsusb.c of the
usb-utils package, I wrote on 2003-09-01. -wk. */ usb-utils package, I wrote on 2003-09-01. -wk. */
static int 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) const unsigned char *buf, size_t buflen)
{ {
unsigned int i; unsigned int i;
unsigned int us; unsigned int us;
int have_t1 = 0, have_tpdu=0; int have_t1 = 0, have_tpdu=0;
handle->nonnull_nad = 0; handle->nonnull_nad = 0;
handle->auto_ifsd = 0; handle->auto_ifsd = 0;
handle->max_ifsd = 32; handle->max_ifsd = 32;
@ -761,7 +759,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
handle->auto_param = 0; handle->auto_param = 0;
handle->auto_pps = 0; handle->auto_pps = 0;
DEBUGOUT_3 ("idVendor: %04X idProduct: %04X bcdDevice: %04X\n", 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) if (buflen < 54 || buf[0] < 54)
{ {
DEBUGOUT ("CCID device descriptor is too short\n"); 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 if (handle->id_vendor == VENDOR_SCM
&& handle->max_ifsd > 48 && handle->max_ifsd > 48
&& ( (handle->id_product == SCM_SCR331 && handle->bcd_device < 0x0516) && ( (handle->id_product == SCM_SCR331 && bcd_device < 0x0516)
||(handle->id_product == SCM_SCR331DI && handle->bcd_device < 0x0620) ||(handle->id_product == SCM_SCR331DI && bcd_device < 0x0620)
||(handle->id_product == SCM_SCR335 && handle->bcd_device < 0x0514) ||(handle->id_product == SCM_SCR335 && bcd_device < 0x0514)
||(handle->id_product == SCM_SPR532 && handle->bcd_device < 0x0504) ||(handle->id_product == SCM_SPR532 && bcd_device < 0x0504)
||(handle->id_product == SCM_SCR3320 && handle->bcd_device < 0x0522) ||(handle->id_product == SCM_SCR3320 && bcd_device < 0x0522)
)) ))
{ {
DEBUGOUT ("enabling workaround for buggy SCM readers\n"); 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 #define MAX_DEVICE 4 /* See MAX_READER in apdu.c. */
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;
*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) if (!initialized_usb)
{ {
@ -1558,122 +1566,379 @@ ccid_open_reader (ccid_driver_t *handle, const char *readerid,
initialized_usb = 1; initialized_usb = 1;
} }
/* See whether we want to use the reader ID string or a reader n = libusb_get_device_list (NULL, &ccid_usb_dev_list);
number. A readerno of -1 indicates that the reader ID string is for (i = 0; i < n; i++)
to be used. */
if (readerid && strchr (readerid, ':'))
readerno = -1; /* We want to use the readerid. */
else if (readerid)
{ {
readerno = atoi (readerid); struct libusb_config_descriptor *config;
if (readerno < 0) 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"); /* Found a device. */
rc = CCID_DRIVER_ERR_NO_READER; DEBUGOUT_1 ("Found CCID reader %d\n", idx);
goto leave;
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, scan_finish:
&ifcdesc_extra_len, &ifc_no, &set_no, &ep_bulk_out,
&ep_bulk_in, &ep_intr, &idev, &dev_fd)) if (err)
{ {
if (readerno == -1) *idx_max_p = 0;
DEBUGOUT_1 ("no CCID reader with ID %s\n", readerid ); *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 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; rc = CCID_DRIVER_ERR_NO_READER;
goto leave; goto leave;
} }
/* Okay, this is a CCID reader. */ (*handle)->id_vendor = desc.idVendor;
*handle = calloc (1, sizeof **handle); (*handle)->id_product = desc.idProduct;
if (!*handle) (*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"); DEBUGOUT ("device not supported\n");
rc = CCID_DRIVER_ERR_OUT_OF_CORE; rc = CCID_DRIVER_ERR_NO_READER;
goto leave; goto leave;
} }
(*handle)->rid = rid;
if (idev) /* Regular USB transport. */ rc = libusb_claim_interface (idev, ifc_no);
if (rc)
{ {
(*handle)->idev = idev; DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
(*handle)->dev_fd = -1; rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
(*handle)->id_vendor = desc.idVendor; goto leave;
(*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_2 ("using CCID reader %d (ID=%s)\n", readerno, rid ); if (set_no != 0)
if (idev)
{ {
if (parse_ccid_descriptor (*handle, ifcdesc_extra, ifcdesc_extra_len)) rc = libusb_set_interface_alt_setting (idev, ifc_no, set_no);
{
DEBUGOUT ("device not supported\n");
rc = CCID_DRIVER_ERR_NO_READER;
goto leave;
}
rc = libusb_claim_interface (idev, ifc_no);
if (rc) 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; rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
goto leave; 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); rc = ccid_vendor_specific_init (*handle);
leave: leave:
free (ifcdesc_extra);
if (rc) if (rc)
{ {
free (rid); free (rid);
if (idev) libusb_close (idev);
libusb_close (idev);
if (dev_fd != -1)
close (dev_fd);
free (*handle); free (*handle);
*handle = NULL; *handle = NULL;
} }
else else
if (rdrname_p) {
*rdrname_p = (*handle)->rid; if (rdrname_p)
*rdrname_p = rid;
else
free (rid);
}
return rc; 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 static void
do_close_reader (ccid_driver_t handle) 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)(void *, const char *, int, int, int),
void *cb_arg) void *cb_arg)
{ {
if (!handle || !handle->rid) if (!handle)
return CCID_DRIVER_ERR_INV_VALUE; return CCID_DRIVER_ERR_INV_VALUE;
handle->progress_cb = cb; handle->progress_cb = cb;
@ -1736,7 +2001,6 @@ ccid_close_reader (ccid_driver_t handle)
return 0; return 0;
do_close_reader (handle); do_close_reader (handle);
free (handle->rid);
free (handle); free (handle);
return 0; return 0;
} }

View File

@ -1,5 +1,5 @@
/* ccid-driver.c - USB ChipCardInterfaceDevices driver /* ccid-driver.h - USB ChipCardInterfaceDevices driver
* Copyright (C) 2003 Free Software Foundation, Inc. * Copyright (C) 2003 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -109,10 +109,18 @@ enum {
struct ccid_driver_s; struct ccid_driver_s;
typedef struct ccid_driver_s *ccid_driver_t; typedef struct ccid_driver_s *ccid_driver_t;
struct ccid_dev_table;
int ccid_set_debug_level (int level); int ccid_set_debug_level (int level);
char *ccid_get_reader_list (void); 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, int ccid_set_progress_cb (ccid_driver_t handle,
void (*cb)(void *, const char *, int, int, int), void (*cb)(void *, const char *, int, int, int),
void *cb_arg); void *cb_arg);
@ -126,7 +134,7 @@ int ccid_transceive (ccid_driver_t handle,
unsigned char *resp, size_t maxresplen, size_t *nresp); unsigned char *resp, size_t maxresplen, size_t *nresp);
int ccid_transceive_secure (ccid_driver_t handle, int ccid_transceive_secure (ccid_driver_t handle,
const unsigned char *apdu, size_t apdulen, const unsigned char *apdu, size_t apdulen,
pininfo_t *pininfo, pininfo_t *pininfo,
unsigned char *resp, size_t maxresplen, size_t *nresp); unsigned char *resp, size_t maxresplen, size_t *nresp);
int ccid_transceive_escape (ccid_driver_t handle, int ccid_transceive_escape (ccid_driver_t handle,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,