aboutsummaryrefslogtreecommitdiffstats
path: root/scd/apdu.c
diff options
context:
space:
mode:
authorNIIBE Yutaka <[email protected]>2017-01-06 00:14:13 +0000
committerNIIBE Yutaka <[email protected]>2017-01-06 00:47:31 +0000
commit8a41e73c31adb86d4a7dca4da695e5ad1347811f (patch)
tree2524a19140709697330b279806b63b4234db24f8 /scd/apdu.c
parentSilence two -Wlogical-op warnings. (diff)
downloadgnupg-8a41e73c31adb86d4a7dca4da695e5ad1347811f.tar.gz
gnupg-8a41e73c31adb86d4a7dca4da695e5ad1347811f.zip
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 <[email protected]>
Diffstat (limited to 'scd/apdu.c')
-rw-r--r--scd/apdu.c219
1 files changed, 163 insertions, 56 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,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,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);