From f32994b0bf07d62bf596cc8bb6ec3c3a5f133ac4 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 8 Sep 2021 15:50:28 +0200 Subject: [PATCH] scd: Support PC/SC for "getinfo reader_list". * scd/apdu.c: Include membuf.h. (pcsc): Add reader_list field. (open_pcsc_reader): Fill that field. (apdu_get_reader_list): New. * scd/command.c: Remove header ccid-driver.h. (pretty_assuan_send_data): New. (cmd_getinfo): Print all reader names. -- Note that depending on the card backend (ccid or PC/SC) it might be necessary to first send a reset followed by SERIALNO to get an updated list of reader. Or well send KILLSCD. The pretty printing of Assuan data lines does only work if you connect direct to scdaemon because the wrapper in gpg-agent does not know about this and combines the Assuan lines again. Signed-off-by: Werner Koch --- scd/apdu.c | 45 ++++++++++++++++++++++++++++++++++++++++++ scd/apdu.h | 1 + scd/command.c | 54 +++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 88 insertions(+), 12 deletions(-) diff --git a/scd/apdu.c b/scd/apdu.c index f00b35915..709ce3086 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -60,6 +60,7 @@ #include "../common/exechelp.h" #endif /* GNUPG_MAJOR_VERSION != 1 */ #include "../common/host2net.h" +#include "../common/membuf.h" #include "iso7816.h" #include "apdu.h" @@ -92,6 +93,7 @@ typedef unsigned long pcsc_dword_t; static struct pcsc { int count; /* Reference count - valid if .context != -1 */ long context; + char *reader_list; /* List of detected readers. */ } pcsc; /* A structure to collect information pertaining to one reader @@ -1204,6 +1206,10 @@ open_pcsc_reader (const char *portstr) pcsc_dword_t nreader; char *p; size_t n; + membuf_t reader_mb; + + xfree (pcsc.reader_list); + pcsc.reader_list = NULL; if (pcsc.context == -1) if (pcsc_init () < 0) @@ -1212,6 +1218,7 @@ open_pcsc_reader (const char *portstr) if (DBG_READER) log_debug ("open_pcsc_reader(portstr=%s)\n", portstr); + slot = new_reader_slot (); if (slot == -1) return -1; /* No need to cleanup here. */ @@ -1243,6 +1250,8 @@ open_pcsc_reader (const char *portstr) goto leave; } + init_membuf (&reader_mb, 256); + p = list; while (nreader > 0) { @@ -1260,11 +1269,17 @@ open_pcsc_reader (const char *portstr) } log_info ("detected reader '%s'\n", p); + put_membuf_str (&reader_mb, p); + put_membuf (&reader_mb, "\n", 1); if (!rdrname && portstr && !strncmp (p, portstr, strlen (portstr))) rdrname = p; nreader -= n + 1; p += n + 1; } + put_membuf (&reader_mb, "", 1); + pcsc.reader_list = get_membuf (&reader_mb, NULL); + if (!pcsc.reader_list) + log_error ("error allocating memory for reader list\n"); if (!rdrname) rdrname = list; @@ -3353,6 +3368,35 @@ apdu_get_reader_name (int slot) return reader_table[slot].rdrname; } + +/* Return the list of currently known readers. Caller must free the + * returned value. Might return NULL. */ +char * +apdu_get_reader_list (void) +{ + membuf_t mb; + char *ccidlist = NULL; + + init_membuf (&mb, 256); +#ifdef HAVE_LIBUSB + ccidlist = ccid_get_reader_list (); +#endif + + if (ccidlist && *ccidlist) + put_membuf_str (&mb, ccidlist); + if (pcsc.reader_list && *pcsc.reader_list) + { + if (ccidlist && *ccidlist) + put_membuf (&mb, "\n", 1); + put_membuf_str (&mb, pcsc.reader_list); + } + xfree (ccidlist); + put_membuf (&mb, "", 1); + + return get_membuf (&mb, NULL); +} + + gpg_error_t apdu_init (void) { @@ -3362,6 +3406,7 @@ apdu_init (void) pcsc.count = 0; pcsc.context = -1; + pcsc.reader_list = NULL; if (npth_mutex_init (&reader_table_lock, NULL)) goto leave; diff --git a/scd/apdu.h b/scd/apdu.h index a7f2b145f..32b8e9eff 100644 --- a/scd/apdu.h +++ b/scd/apdu.h @@ -151,5 +151,6 @@ int apdu_send_direct (int slot, size_t extended_length, int handle_more, unsigned int *r_sw, unsigned char **retbuf, size_t *retbuflen); const char *apdu_get_reader_name (int slot); +char *apdu_get_reader_list (void); #endif /*APDU_H*/ diff --git a/scd/command.c b/scd/command.c index e2debf5b8..fab65860b 100644 --- a/scd/command.c +++ b/scd/command.c @@ -36,9 +36,6 @@ #include "iso7816.h" #include "apdu.h" /* Required for apdu_*_reader (). */ #include "atr.h" -#ifdef HAVE_LIBUSB -#include "ccid-driver.h" -#endif #include "../common/asshelp.h" #include "../common/server-help.h" @@ -1401,6 +1398,45 @@ cmd_unlock (assuan_context_t ctx, char *line) } +/* Ease reading of Assuan data ;ines by sending a physical line after + * each LF. */ +static gpg_error_t +pretty_assuan_send_data (assuan_context_t ctx, + const void *buffer_arg, size_t size) +{ + const char *buffer = buffer_arg; + const char *p; + size_t n, nbytes; + gpg_error_t err; + + nbytes = size; + do + { + p = memchr (buffer, '\n', nbytes); + n = p ? (p - buffer) + 1 : nbytes; + err = assuan_send_data (ctx, buffer, n); + if (err) + { + /* We also set ERRNO in case this function is used by a + * custom estream I/O handler. */ + gpg_err_set_errno (EIO); + goto leave; + } + buffer += n; + nbytes -= n; + if (nbytes && (err=assuan_send_data (ctx, NULL, 0))) /* Flush line. */ + { + gpg_err_set_errno (EIO); + goto leave; + } + } + while (nbytes); + + leave: + return err; +} + + static const char hlp_getinfo[] = "GETINFO \n" "\n" @@ -1418,8 +1454,7 @@ static const char hlp_getinfo[] = " 'u' Usable card present.\n" " 'r' Card removed. A reset is necessary.\n" " These flags are exclusive.\n" - " reader_list - Return a list of detected card readers. Does\n" - " currently only work with the internal CCID driver.\n" + " reader_list - Return a list of detected card readers.\n" " deny_admin - Returns OK if admin commands are not allowed or\n" " GPG_ERR_GENERAL if admin commands are allowed.\n" " app_list - Return a list of supported applications. One\n" @@ -1474,14 +1509,9 @@ cmd_getinfo (assuan_context_t ctx, char *line) } else if (!strcmp (line, "reader_list")) { -#ifdef HAVE_LIBUSB - char *s = ccid_get_reader_list (); -#else - char *s = NULL; -#endif - + char *s = apdu_get_reader_list (); if (s) - rc = assuan_send_data (ctx, s, strlen (s)); + rc = pretty_assuan_send_data (ctx, s, strlen (s)); else rc = gpg_error (GPG_ERR_NO_DATA); xfree (s);