aboutsummaryrefslogtreecommitdiffstats
path: root/scd
diff options
context:
space:
mode:
Diffstat (limited to 'scd')
-rw-r--r--scd/apdu.c560
-rw-r--r--scd/apdu.h2
-rw-r--r--scd/app-common.h6
-rw-r--r--scd/app-nks.c37
-rw-r--r--scd/app-openpgp.c11
-rw-r--r--scd/app-p15.c2
-rw-r--r--scd/app-piv.c27
-rw-r--r--scd/app-sc-hsm.c2
-rw-r--r--scd/app.c558
-rw-r--r--scd/ccid-driver.c26
-rw-r--r--scd/ccid-driver.h8
-rw-r--r--scd/command.c102
-rw-r--r--scd/iso7816.c1
-rw-r--r--scd/scdaemon.c1
-rw-r--r--scd/scdaemon.h3
15 files changed, 920 insertions, 426 deletions
diff --git a/scd/apdu.c b/scd/apdu.c
index 2df113c5e..f86a63897 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -55,7 +55,7 @@
#include "ccid-driver.h"
struct dev_list {
- struct ccid_dev_table *ccid_table;
+ void *table;
const char *portstr;
int idx;
int idx_max;
@@ -76,6 +76,19 @@ typedef unsigned int pcsc_dword_t;
typedef unsigned long pcsc_dword_t;
#endif
+#ifdef HAVE_W32_SYSTEM
+#define HANDLE uintptr_t
+#else
+#define HANDLE long
+#endif
+
+/* PC/SC context to access readers. Shared among all readers. */
+static struct pcsc_global_data {
+ HANDLE context;
+ int count;
+ const char *rdrname[MAX_READER];
+} pcsc;
+
/* A structure to collect information pertaining to one reader
slot. */
struct reader_table_s {
@@ -101,8 +114,7 @@ struct reader_table_s {
ccid_driver_t handle;
} ccid;
struct {
- long context;
- long card;
+ HANDLE card;
pcsc_dword_t protocol;
pcsc_dword_t verify_ioctl;
pcsc_dword_t modify_ioctl;
@@ -288,35 +300,35 @@ typedef struct pcsc_readerstate_s *pcsc_readerstate_t;
long (* DLSTDCALL pcsc_establish_context) (pcsc_dword_t scope,
const void *reserved1,
const void *reserved2,
- long *r_context);
-long (* DLSTDCALL pcsc_release_context) (long context);
-long (* DLSTDCALL pcsc_list_readers) (long context,
+ HANDLE *r_context);
+long (* DLSTDCALL pcsc_release_context) (HANDLE context);
+long (* DLSTDCALL pcsc_list_readers) (HANDLE context,
const char *groups,
char *readers, pcsc_dword_t*readerslen);
-long (* DLSTDCALL pcsc_get_status_change) (long context,
+long (* DLSTDCALL pcsc_get_status_change) (HANDLE context,
pcsc_dword_t timeout,
pcsc_readerstate_t readerstates,
pcsc_dword_t nreaderstates);
-long (* DLSTDCALL pcsc_connect) (long context,
+long (* DLSTDCALL pcsc_connect) (HANDLE context,
const char *reader,
pcsc_dword_t share_mode,
pcsc_dword_t preferred_protocols,
- long *r_card,
+ HANDLE *r_card,
pcsc_dword_t *r_active_protocol);
-long (* DLSTDCALL pcsc_reconnect) (long card,
+long (* DLSTDCALL pcsc_reconnect) (HANDLE card,
pcsc_dword_t share_mode,
pcsc_dword_t preferred_protocols,
pcsc_dword_t initialization,
pcsc_dword_t *r_active_protocol);
-long (* DLSTDCALL pcsc_disconnect) (long card,
+long (* DLSTDCALL pcsc_disconnect) (HANDLE card,
pcsc_dword_t disposition);
-long (* DLSTDCALL pcsc_status) (long card,
+long (* DLSTDCALL pcsc_status) (HANDLE card,
char *reader, pcsc_dword_t *readerlen,
pcsc_dword_t *r_state,
pcsc_dword_t *r_protocol,
unsigned char *atr, pcsc_dword_t *atrlen);
long (* DLSTDCALL pcsc_begin_transaction) (long card);
-long (* DLSTDCALL pcsc_end_transaction) (long card,
+long (* DLSTDCALL pcsc_end_transaction) (HANDLE card,
pcsc_dword_t disposition);
long (* DLSTDCALL pcsc_transmit) (long card,
const pcsc_io_request_t send_pci,
@@ -325,9 +337,9 @@ long (* DLSTDCALL pcsc_transmit) (long card,
pcsc_io_request_t recv_pci,
unsigned char *recv_buffer,
pcsc_dword_t *recv_len);
-long (* DLSTDCALL pcsc_set_timeout) (long context,
+long (* DLSTDCALL pcsc_set_timeout) (HANDLE context,
pcsc_dword_t timeout);
-long (* DLSTDCALL pcsc_control) (long card,
+long (* DLSTDCALL pcsc_control) (HANDLE card,
pcsc_dword_t control_code,
const void *send_buffer,
pcsc_dword_t send_len,
@@ -652,9 +664,7 @@ pcsc_get_status (int slot, unsigned int *status, int on_wire)
memset (rdrstates, 0, sizeof *rdrstates);
rdrstates[0].reader = reader_table[slot].rdrname;
rdrstates[0].current_state = reader_table[slot].pcsc.current_state;
- err = pcsc_get_status_change (reader_table[slot].pcsc.context,
- 0,
- rdrstates, 1);
+ err = pcsc_get_status_change (pcsc.context, 0, rdrstates, 1);
if (err == PCSC_E_TIMEOUT)
err = 0; /* Timeout is no error here. */
if (err)
@@ -788,7 +798,16 @@ control_pcsc (int slot, pcsc_dword_t ioctl_code,
static int
close_pcsc_reader (int slot)
{
- pcsc_release_context (reader_table[slot].pcsc.context);
+ (void)slot;
+ if (--pcsc.count == 0)
+ {
+ int i;
+
+ pcsc_release_context (pcsc.context);
+ pcsc.context = 0;
+ for (i = 0; i < MAX_READER; i++)
+ pcsc.rdrname[i] = NULL;
+ }
return 0;
}
@@ -807,7 +826,7 @@ connect_pcsc_card (int slot)
reader_table[slot].atrlen = 0;
reader_table[slot].is_t0 = 0;
- err = pcsc_connect (reader_table[slot].pcsc.context,
+ err = pcsc_connect (pcsc.context,
reader_table[slot].rdrname,
PCSC_SHARE_EXCLUSIVE,
PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
@@ -829,7 +848,7 @@ connect_pcsc_card (int slot)
pcsc_vendor_specific_init (slot);
atrlen = DIM (reader_table[0].atr);
- readerlen = sizeof reader -1 ;
+ readerlen = sizeof reader - 1;
err = pcsc_status (reader_table[slot].pcsc.card,
reader, &readerlen,
&card_state, &card_protocol,
@@ -1042,94 +1061,128 @@ pcsc_vendor_specific_init (int slot)
return 0;
}
-
-/* Open the PC/SC reader without using the wrapper. Returns -1 on
- error or a slot number for the reader. */
static int
-open_pcsc_reader (const char *portstr)
+pcsc_init (void)
{
+ static int pcsc_api_loaded;
long err;
- int slot;
- char *list = NULL;
- char *rdrname = NULL;
- pcsc_dword_t nreader;
- char *p;
-
- slot = new_reader_slot ();
- if (slot == -1)
- return -1;
- /* Fixme: Allocating a context for each slot is not required. One
- global context should be sufficient. */
- err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL,
- &reader_table[slot].pcsc.context);
- if (err)
+ /* Lets try the PC/SC API */
+ if (!pcsc_api_loaded)
{
- log_error ("pcsc_establish_context failed: %s (0x%lx)\n",
- pcsc_error_string (err), err);
- reader_table[slot].used = 0;
- unlock_slot (slot);
- return -1;
- }
+ void *handle;
- err = pcsc_list_readers (reader_table[slot].pcsc.context,
- NULL, NULL, &nreader);
- if (!err)
- {
- list = xtrymalloc (nreader+1); /* Better add 1 for safety reasons. */
- if (!list)
+ handle = dlopen (opt.pcsc_driver, RTLD_LAZY);
+ if (!handle)
{
- log_error ("error allocating memory for reader list\n");
- pcsc_release_context (reader_table[slot].pcsc.context);
- reader_table[slot].used = 0;
- unlock_slot (slot);
- return -1 /*SW_HOST_OUT_OF_CORE*/;
+ log_error ("apdu_open_reader: failed to open driver '%s': %s\n",
+ opt.pcsc_driver, dlerror ());
+ return -1;
+ }
+
+ pcsc_establish_context = dlsym (handle, "SCardEstablishContext");
+ pcsc_release_context = dlsym (handle, "SCardReleaseContext");
+ pcsc_list_readers = dlsym (handle, "SCardListReaders");
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (!pcsc_list_readers)
+ pcsc_list_readers = dlsym (handle, "SCardListReadersA");
+#endif
+ pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange");
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (!pcsc_get_status_change)
+ pcsc_get_status_change = dlsym (handle, "SCardGetStatusChangeA");
+#endif
+ pcsc_connect = dlsym (handle, "SCardConnect");
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (!pcsc_connect)
+ pcsc_connect = dlsym (handle, "SCardConnectA");
+#endif
+ pcsc_reconnect = dlsym (handle, "SCardReconnect");
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (!pcsc_reconnect)
+ pcsc_reconnect = dlsym (handle, "SCardReconnectA");
+#endif
+ pcsc_disconnect = dlsym (handle, "SCardDisconnect");
+ pcsc_status = dlsym (handle, "SCardStatus");
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (!pcsc_status)
+ pcsc_status = dlsym (handle, "SCardStatusA");
+#endif
+ pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction");
+ pcsc_end_transaction = dlsym (handle, "SCardEndTransaction");
+ pcsc_transmit = dlsym (handle, "SCardTransmit");
+ pcsc_set_timeout = dlsym (handle, "SCardSetTimeout");
+ pcsc_control = dlsym (handle, "SCardControl");
+
+ if (!pcsc_establish_context
+ || !pcsc_release_context
+ || !pcsc_list_readers
+ || !pcsc_get_status_change
+ || !pcsc_connect
+ || !pcsc_reconnect
+ || !pcsc_disconnect
+ || !pcsc_status
+ || !pcsc_begin_transaction
+ || !pcsc_end_transaction
+ || !pcsc_transmit
+ || !pcsc_control
+ /* || !pcsc_set_timeout */)
+ {
+ /* Note that set_timeout is currently not used and also not
+ available under Windows. */
+ log_error ("apdu_open_reader: invalid PC/SC driver "
+ "(%d%d%d%d%d%d%d%d%d%d%d%d%d)\n",
+ !!pcsc_establish_context,
+ !!pcsc_release_context,
+ !!pcsc_list_readers,
+ !!pcsc_get_status_change,
+ !!pcsc_connect,
+ !!pcsc_reconnect,
+ !!pcsc_disconnect,
+ !!pcsc_status,
+ !!pcsc_begin_transaction,
+ !!pcsc_end_transaction,
+ !!pcsc_transmit,
+ !!pcsc_set_timeout,
+ !!pcsc_control );
+ dlclose (handle);
+ return -1;
}
- err = pcsc_list_readers (reader_table[slot].pcsc.context,
- NULL, list, &nreader);
+ pcsc_api_loaded = 1;
}
+
+ err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL,
+ &pcsc.context);
if (err)
{
- log_error ("pcsc_list_readers failed: %s (0x%lx)\n",
+ log_error ("pcsc_establish_context failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
- pcsc_release_context (reader_table[slot].pcsc.context);
- reader_table[slot].used = 0;
- xfree (list);
- unlock_slot (slot);
return -1;
}
- p = list;
- while (nreader)
- {
- if (!*p && !p[1])
- break;
- log_info ("detected reader '%s'\n", p);
- if (nreader < (strlen (p)+1))
- {
- log_error ("invalid response from pcsc_list_readers\n");
- break;
- }
- if (!rdrname && portstr && !strncmp (p, portstr, strlen (portstr)))
- rdrname = p;
- nreader -= strlen (p)+1;
- p += strlen (p) + 1;
- }
+ return 0;
+}
- if (!rdrname)
- rdrname = list;
+/* Open the PC/SC reader. Returns -1 on error or a slot number for
+ the reader. */
+static int
+open_pcsc_reader (const char *rdrname)
+{
+ int slot;
+
+ slot = new_reader_slot ();
+ if (slot == -1)
+ return -1;
reader_table[slot].rdrname = xtrystrdup (rdrname);
if (!reader_table[slot].rdrname)
{
log_error ("error allocating memory for reader name\n");
- pcsc_release_context (reader_table[slot].pcsc.context);
+ close_pcsc_reader (0);
reader_table[slot].used = 0;
unlock_slot (slot);
return -1;
}
- xfree (list);
- list = NULL;
reader_table[slot].pcsc.card = 0;
reader_table[slot].atrlen = 0;
@@ -1142,6 +1195,7 @@ open_pcsc_reader (const char *portstr)
reader_table[slot].send_apdu_reader = pcsc_send_apdu;
reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
+ pcsc.count++;
dump_reader_status (slot);
unlock_slot (slot);
return slot;
@@ -1520,7 +1574,7 @@ open_ccid_reader (struct dev_list *dl)
return -1;
slotp = reader_table + slot;
- err = ccid_open_reader (dl->portstr, dl->idx, dl->ccid_table,
+ err = ccid_open_reader (dl->portstr, dl->idx, dl->table,
&slotp->ccid.handle, &slotp->rdrname);
if (!err)
{
@@ -1866,51 +1920,111 @@ gpg_error_t
apdu_dev_list_start (const char *portstr, struct dev_list **l_p)
{
struct dev_list *dl = xtrymalloc (sizeof (struct dev_list));
+ gpg_error_t err;
*l_p = NULL;
if (!dl)
return gpg_error_from_syserror ();
+ dl->table = NULL;
dl->portstr = portstr;
dl->idx = 0;
+ dl->idx_max = 0;
npth_mutex_lock (&reader_table_lock);
#ifdef HAVE_LIBUSB
- if (opt.disable_ccid)
+ if (!opt.disable_ccid)
{
- dl->ccid_table = NULL;
- dl->idx_max = 1;
+ err = ccid_dev_scan (&dl->idx_max, &dl->table);
+ if (err)
+ {
+ npth_mutex_unlock (&reader_table_lock);
+ return err;
+ }
+
+ if (dl->idx_max == 0)
+ {
+ 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
- {
- gpg_error_t err;
+#endif
+ { /* PC/SC readers. */
+ long r;
+ pcsc_dword_t nreader;
+ char *p = NULL;
- err = ccid_dev_scan (&dl->idx_max, &dl->ccid_table);
- if (err)
- return err;
+ if (!pcsc.context)
+ if (pcsc_init () < 0)
+ {
+ npth_mutex_unlock (&reader_table_lock);
+ return gpg_error (GPG_ERR_NO_SERVICE);
+ }
- if (dl->idx_max == 0)
+ r = pcsc_list_readers (pcsc.context, NULL, NULL, &nreader);
+ if (!r)
{
- /* 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] == ':')
+ p = xtrymalloc (nreader);
+ if (!p)
{
- if (DBG_READER)
- log_debug ("leave: apdu_open_reader => slot=-1 (no ccid)\n");
+ err = gpg_error_from_syserror ();
- xfree (dl);
+ log_error ("error allocating memory for reader list\n");
+ close_pcsc_reader (0);
npth_mutex_unlock (&reader_table_lock);
- return gpg_error (GPG_ERR_ENODEV);
+ return err;
+ }
+ r = pcsc_list_readers (pcsc.context, NULL, p, &nreader);
+ }
+ if (r)
+ {
+ log_error ("pcsc_list_readers failed: %s (0x%lx)\n",
+ pcsc_error_string (r), r);
+ xfree (p);
+ close_pcsc_reader (0);
+ npth_mutex_unlock (&reader_table_lock);
+ return gpg_error (GPG_ERR_NO_SERVICE);
+ }
+
+ dl->table = p;
+ dl->idx_max = 0;
+
+ while (nreader > 0)
+ {
+ size_t n;
+
+ if (!*p)
+ break;
+
+ for (n = 0; n < nreader; n++)
+ if (!p[n])
+ break;
+
+ if (n >= nreader)
+ {
+ log_error ("invalid response from pcsc_list_readers\n");
+ break;
+ }
+
+ log_info ("detected reader '%s'\n", p);
+ pcsc.rdrname[dl->idx_max] = p;
+ nreader -= n + 1;
+ p += n + 1;
+ dl->idx_max++;
+ if (dl->idx_max > MAX_READER)
+ {
+ log_error ("too many readers from pcsc_list_readers\n");
+ dl->idx_max--;
+ break;
}
- else
- dl->idx_max = 1;
}
}
-#else
- dl->ccid_table = NULL;
- dl->idx_max = 1;
-#endif /* HAVE_LIBUSB */
*l_p = dl;
return 0;
@@ -1920,161 +2034,65 @@ void
apdu_dev_list_finish (struct dev_list *dl)
{
#ifdef HAVE_LIBUSB
- if (dl->ccid_table)
- ccid_dev_scan_finish (dl->ccid_table, dl->idx_max);
+ if (!opt.disable_ccid)
+ {
+ if (dl->table)
+ ccid_dev_scan_finish (dl->table, dl->idx_max);
+ }
+ else
#endif
+ { /* PC/SC readers. */
+ int i;
+
+ xfree (dl->table);
+ for (i = 0; i < MAX_READER; i++)
+ pcsc.rdrname[i] = NULL;
+ }
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)
+int
+apdu_open_reader (struct dev_list *dl)
{
- static int pcsc_api_loaded;
int slot;
+ int readerno;
- if (DBG_READER)
- log_debug ("enter: apdu_open_reader: portstr=%s\n", portstr);
+ if (!dl->table)
+ return -1;
- /* Lets try the PC/SC API */
- if (!pcsc_api_loaded)
+ /* 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)
{
- void *handle;
-
- handle = dlopen (opt.pcsc_driver, RTLD_LAZY);
- if (!handle)
- {
- log_error ("apdu_open_reader: failed to open driver '%s': %s\n",
- opt.pcsc_driver, dlerror ());
- return -1;
- }
+ readerno = atoi (dl->portstr);
+ if (readerno < 0 || readerno >= dl->idx_max)
+ return -1;
- pcsc_establish_context = dlsym (handle, "SCardEstablishContext");
- pcsc_release_context = dlsym (handle, "SCardReleaseContext");
- pcsc_list_readers = dlsym (handle, "SCardListReaders");
-#if defined(_WIN32) || defined(__CYGWIN__)
- if (!pcsc_list_readers)
- pcsc_list_readers = dlsym (handle, "SCardListReadersA");
-#endif
- pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange");
-#if defined(_WIN32) || defined(__CYGWIN__)
- if (!pcsc_get_status_change)
- pcsc_get_status_change = dlsym (handle, "SCardGetStatusChangeA");
-#endif
- pcsc_connect = dlsym (handle, "SCardConnect");
-#if defined(_WIN32) || defined(__CYGWIN__)
- if (!pcsc_connect)
- pcsc_connect = dlsym (handle, "SCardConnectA");
-#endif
- pcsc_reconnect = dlsym (handle, "SCardReconnect");
-#if defined(_WIN32) || defined(__CYGWIN__)
- if (!pcsc_reconnect)
- pcsc_reconnect = dlsym (handle, "SCardReconnectA");
-#endif
- pcsc_disconnect = dlsym (handle, "SCardDisconnect");
- pcsc_status = dlsym (handle, "SCardStatus");
-#if defined(_WIN32) || defined(__CYGWIN__)
- if (!pcsc_status)
- pcsc_status = dlsym (handle, "SCardStatusA");
-#endif
- pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction");
- pcsc_end_transaction = dlsym (handle, "SCardEndTransaction");
- pcsc_transmit = dlsym (handle, "SCardTransmit");
- pcsc_set_timeout = dlsym (handle, "SCardSetTimeout");
- pcsc_control = dlsym (handle, "SCardControl");
-
- if (!pcsc_establish_context
- || !pcsc_release_context
- || !pcsc_list_readers
- || !pcsc_get_status_change
- || !pcsc_connect
- || !pcsc_reconnect
- || !pcsc_disconnect
- || !pcsc_status
- || !pcsc_begin_transaction
- || !pcsc_end_transaction
- || !pcsc_transmit
- || !pcsc_control
- /* || !pcsc_set_timeout */)
- {
- /* Note that set_timeout is currently not used and also not
- available under Windows. */
- log_error ("apdu_open_reader: invalid PC/SC driver "
- "(%d%d%d%d%d%d%d%d%d%d%d%d%d)\n",
- !!pcsc_establish_context,
- !!pcsc_release_context,
- !!pcsc_list_readers,
- !!pcsc_get_status_change,
- !!pcsc_connect,
- !!pcsc_reconnect,
- !!pcsc_disconnect,
- !!pcsc_status,
- !!pcsc_begin_transaction,
- !!pcsc_end_transaction,
- !!pcsc_transmit,
- !!pcsc_set_timeout,
- !!pcsc_control );
- dlclose (handle);
- return -1;
- }
- pcsc_api_loaded = 1;
+ dl->idx = readerno;
+ dl->portstr = NULL;
}
-
- slot = open_pcsc_reader (portstr);
-
- if (DBG_READER)
- log_debug ("leave: apdu_open_reader => slot=%d [pc/sc]\n", slot);
- return slot;
-}
-
-int
-apdu_open_reader (struct dev_list *dl, int app_empty)
-{
- int slot;
+ else
+ readerno = 0; /* Default. */
#ifdef HAVE_LIBUSB
- if (dl->ccid_table)
+ if (!opt.disable_ccid)
{ /* 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);
+ /* And stick the reader and no scan. */
dl->idx = dl->idx_max;
- if (slot >= 0)
- return slot;
- else
- return -1;
+ return slot;
}
while (dl->idx < dl->idx_max)
{
- unsigned int bai = ccid_get_BAI (dl->idx, dl->ccid_table);
+ unsigned int bai = ccid_get_BAI (dl->idx, dl->table);
if (DBG_READER)
log_debug ("apdu_open_reader: BAI=%x\n", bai);
@@ -2107,25 +2125,60 @@ apdu_open_reader (struct dev_list *dl, int app_empty)
dl->idx++;
}
- /* Not found. Try one for PC/SC, only when it's the initial scan. */
- if (app_empty && dl->idx == dl->idx_max)
- {
- dl->idx++;
- slot = apdu_open_one_reader (dl->portstr);
- }
- else
- slot = -1;
+ /* Not found. */
+ slot = -1;
}
else
#endif
{ /* PC/SC readers. */
- if (app_empty && dl->idx == 0)
+ if (readerno > 0)
+ { /* Use single, the specific reader. */
+ slot = open_pcsc_reader (pcsc.rdrname[readerno]);
+ /* And stick the reader and no scan. */
+ dl->idx = dl->idx_max;
+ return slot;
+ }
+
+ while (dl->idx < dl->idx_max)
{
- dl->idx++;
- slot = apdu_open_one_reader (dl->portstr);
+ const char *rdrname = pcsc.rdrname[dl->idx];
+
+ if (DBG_READER)
+ log_debug ("apdu_open_reader: %s\n", rdrname);
+
+ /* Check the identity of reader against already opened one. */
+ for (slot = 0; slot < MAX_READER; slot++)
+ if (reader_table[slot].used
+ && !strcmp (reader_table[slot].rdrname, rdrname))
+ break;
+
+ if (slot == MAX_READER)
+ { /* Found a new device. */
+ if (DBG_READER)
+ log_debug ("apdu_open_reader: new device=%s\n", rdrname);
+
+ /* When reader string is specified, check if it is the one. */
+ if (readerno < 0 && strcmp (rdrname, dl->portstr) != 0)
+ continue;
+
+ slot = open_pcsc_reader (rdrname);
+
+ dl->idx++;
+ if (slot >= 0)
+ return slot;
+ else
+ {
+ /* Skip this reader. */
+ log_error ("pcsc open error: skip\n");
+ continue;
+ }
+ }
+ else
+ dl->idx++;
}
- else
- slot = -1;
+
+ /* Not found. */
+ slot = -1;
}
return slot;
@@ -3305,6 +3358,11 @@ apdu_init (void)
gpg_error_t err;
int i;
+ pcsc.count = 0;
+ pcsc.context = 0;
+ for (i = 0; i < MAX_READER; i++)
+ pcsc.rdrname[i] = NULL;
+
if (npth_mutex_init (&reader_table_lock, NULL))
goto leave;
diff --git a/scd/apdu.h b/scd/apdu.h
index 89df45cb8..cdf94ccca 100644
--- a/scd/apdu.h
+++ b/scd/apdu.h
@@ -93,7 +93,7 @@ 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 (struct dev_list *l, int app_empty);
+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,
diff --git a/scd/app-common.h b/scd/app-common.h
index 5866c9b32..87f63bb7e 100644
--- a/scd/app-common.h
+++ b/scd/app-common.h
@@ -39,9 +39,13 @@
/* Flags used with app_readkey. */
#define APP_READKEY_FLAG_INFO 1 /* Send also a KEYPAIRINFO line. */
-/* Bit flags set by the decipher function into R_INFO. */
+/* Flags set by the decipher function into R_INFO. */
#define APP_DECIPHER_INFO_NOPAD 1 /* Padding has been removed. */
+/* Flags used by the app_write_learn_status. */
+#define APP_LEARN_FLAG_KEYPAIRINFO 1 /* Return only keypair infos. */
+#define APP_LEARN_FLAG_MULTI 2 /* Return info for all apps. */
+
/* List of supported card types. Generic is the usual ISO7817-4
* compliant card. More specific card or token versions can be given
diff --git a/scd/app-nks.c b/scd/app-nks.c
index d12720cf6..bb5329bfe 100644
--- a/scd/app-nks.c
+++ b/scd/app-nks.c
@@ -309,8 +309,10 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
int special;
} table[] = {
{ "$AUTHKEYID", 1 },
- { "NKS-VERSION", 2 },
- { "CHV-STATUS", 3 },
+ { "$ENCRKEYID", 2 },
+ { "$SIGNKEYID", 3 },
+ { "NKS-VERSION", 4 },
+ { "CHV-STATUS", 5 },
{ NULL, 0 }
};
gpg_error_t err = 0;
@@ -340,13 +342,27 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
}
break;
- case 2: /* NKS-VERSION */
+ case 2: /* $ENCRKEYID */
+ {
+ char const tmp[] = "NKS-NKS3.45B1";
+ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+ }
+ break;
+
+ case 3: /* $SIGNKEYID */
+ {
+ char const tmp[] = "NKS-NKS3.4531";
+ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+ }
+ break;
+
+ case 4: /* NKS-VERSION */
snprintf (buffer, sizeof buffer, "%d", app->app_local->nks_version);
send_status_info (ctrl, table[idx].name,
buffer, strlen (buffer), NULL, 0);
break;
- case 3: /* CHV-STATUS */
+ case 5: /* CHV-STATUS */
{
/* Returns: PW1.CH PW2.CH PW1.CH.SIG PW2.CH.SIG That are the
two global passwords followed by the two SigG passwords.
@@ -386,6 +402,7 @@ do_learn_status_core (app_t app, ctrl_t ctrl, unsigned int flags, int is_sigg)
char ct_buf[100], id_buf[100];
int i;
const char *tag;
+ const char *usage;
if (is_sigg)
tag = "SIGG";
@@ -403,7 +420,7 @@ do_learn_status_core (app_t app, ctrl_t ctrl, unsigned int flags, int is_sigg)
if (!!filelist[i].is_sigg != !!is_sigg)
continue;
- if (filelist[i].certtype && !(flags &1))
+ if (filelist[i].certtype && !(flags & APP_LEARN_FLAG_KEYPAIRINFO))
{
size_t len;
@@ -435,9 +452,19 @@ do_learn_status_core (app_t app, ctrl_t ctrl, unsigned int flags, int is_sigg)
{
snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X",
tag, filelist[i].fid);
+ if (filelist[i].issignkey && filelist[i].isenckey)
+ usage = "sae";
+ else if (filelist[i].issignkey)
+ usage = "sa";
+ else if (filelist[i].isenckey)
+ usage = "e";
+ else
+ usage = "";
+
send_status_info (ctrl, "KEYPAIRINFO",
gripstr, 40,
id_buf, strlen (id_buf),
+ usage, strlen (usage),
NULL, (size_t)0);
}
}
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index 767f29d26..4f76caac3 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -4296,7 +4296,8 @@ check_against_given_fingerprint (app_t app, const char *fpr, int key)
KEYIDSTR is either:
(1) Serial number
(2) Serial number "/" fingerprint
- (3) keygrip
+ (3) Serial number "[CHV3]"
+ (4) keygrip
When KEYNO is 0 and KEYIDSTR is for a keygrip, the keygrip should
be to be compared is the first one (keygrip for signing).
@@ -4335,8 +4336,6 @@ check_keyidstr (app_t app, const char *keyidstr, int keyno)
; /* no fingerprint given: we allow this for now. */
else if (*s == '/')
fpr = s + 1;
- else
- return gpg_error (GPG_ERR_INV_ID);
for (s=keyidstr, n=0; n < 16; s += 2, n++)
tmp_sn[n] = xtoi_2 (s);
@@ -5229,6 +5228,12 @@ do_reselect (app_t app, ctrl_t ctrl)
* a special flag value. */
err = iso7816_select_application (app_get_slot (app),
openpgp_aid, sizeof openpgp_aid, 0x0001);
+ if (!err)
+ {
+ app->did_chv1 = 0;
+ app->did_chv2 = 0;
+ app->did_chv3 = 0;
+ }
return err;
}
diff --git a/scd/app-p15.c b/scd/app-p15.c
index ce82b34a9..348242f4f 100644
--- a/scd/app-p15.c
+++ b/scd/app-p15.c
@@ -2500,7 +2500,7 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
{
gpg_error_t err;
- if ((flags & 1))
+ if ((flags & APP_LEARN_FLAG_KEYPAIRINFO))
err = 0;
else
{
diff --git a/scd/app-piv.c b/scd/app-piv.c
index 3b94a28e4..3cc7754df 100644
--- a/scd/app-piv.c
+++ b/scd/app-piv.c
@@ -1176,7 +1176,8 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
for (i=0; data_objects[i].tag; i++)
if (data_objects[i].keypair)
- send_keypair_and_cert_info (app, ctrl, data_objects + i, !!(flags & 1));
+ send_keypair_and_cert_info (app, ctrl, data_objects + i,
+ !!(flags & APP_LEARN_FLAG_KEYPAIRINFO));
return 0;
@@ -1365,7 +1366,7 @@ find_dobj_by_keyref (app_t app, const char *keyref)
(void)app;
- if (!ascii_strncasecmp (keyref, "PIV.", 4))
+ if (!ascii_strncasecmp (keyref, "PIV.", 4)) /* Standard keyref */
{
keyref += 4;
for (i=0; data_objects[i].tag; i++)
@@ -1375,7 +1376,7 @@ find_dobj_by_keyref (app_t app, const char *keyref)
return data_objects + i;
}
}
- else if (!strncmp (keyref, "2.16.840.1.101.3.7.", 19))
+ else if (!strncmp (keyref, "2.16.840.1.101.3.7.", 19)) /* OID */
{
keyref += 19;
for (i=0; data_objects[i].tag; i++)
@@ -1385,6 +1386,26 @@ find_dobj_by_keyref (app_t app, const char *keyref)
return data_objects + i;
}
}
+ else if (strlen (keyref) == 40) /* A keygrip */
+ {
+ char *keygripstr = NULL;
+ int tag, dummy_got_cert;
+
+ for (i=0; (tag=data_objects[i].tag); i++)
+ {
+ if (!data_objects[i].keypair)
+ continue;
+ xfree (keygripstr);
+ if (get_keygrip_by_tag (app, tag, &keygripstr, &dummy_got_cert))
+ continue;
+ if (!strcmp (keygripstr, keyref))
+ {
+ xfree (keygripstr);
+ return data_objects + i;
+ }
+ }
+ xfree (keygripstr);
+ }
return NULL;
}
diff --git a/scd/app-sc-hsm.c b/scd/app-sc-hsm.c
index 16d25b581..2f1ab2074 100644
--- a/scd/app-sc-hsm.c
+++ b/scd/app-sc-hsm.c
@@ -1407,7 +1407,7 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
{
gpg_error_t err;
- if ((flags & 1))
+ if ((flags & APP_LEARN_FLAG_KEYPAIRINFO))
err = 0;
else
{
diff --git a/scd/app.c b/scd/app.c
index 57c4b7743..fff2b307f 100644
--- a/scd/app.c
+++ b/scd/app.c
@@ -30,6 +30,11 @@
#include "apdu.h"
#include "../common/tlv.h"
+
+/* Forward declaration of internal function. */
+static gpg_error_t
+select_additional_application_internal (card_t card, apptype_t req_apptype);
+
/* Lock to protect the list of cards and its associated
* applications. */
static npth_mutex_t card_list_lock;
@@ -91,6 +96,13 @@ strapptype (apptype_t t)
}
+const char *
+xstrapptype (app_t app)
+{
+ return app? strapptype (app->apptype) : "[no_app]";
+}
+
+
/* Return the apptype for NAME. */
static apptype_t
apptype_from_name (const char *name)
@@ -109,6 +121,31 @@ apptype_from_name (const char *name)
}
+/* Return the apptype for KEYREF. This is the first part of the
+ * KEYREF up to the dot. */
+static apptype_t
+apptype_from_keyref (const char *keyref)
+{
+ int i;
+ unsigned int n;
+ const char *s;
+
+ if (!keyref)
+ return APPTYPE_NONE;
+ s = strchr (keyref, '.');
+ if (!s || s == keyref || !s[1])
+ return APPTYPE_NONE; /* Not a valid keyref. */
+ n = s - keyref;
+
+ for (i=0; app_priority_list[i].apptype; i++)
+ if (strlen (app_priority_list[i].name) == n
+ && !ascii_strncasecmp (app_priority_list[i].name, keyref, n))
+ return app_priority_list[i].apptype;
+
+ return APPTYPE_NONE;
+}
+
+
/* Initialization function to change the default app_priority_list.
* LIST is a list of comma or space separated strings with application
* names. Unknown names will only result in warning message.
@@ -231,6 +268,7 @@ app_dump_state (void)
{
log_info ("app_dump_state: card=%p slot=%d type=%s\n",
c, c->slot, strcardtype (c->cardtype));
+ /* FIXME The use of log_info risks a race! */
for (a=c->app; a; a = a->next)
log_info ("app_dump_state: app=%p type='%s'\n",
a, strapptype (a->apptype));
@@ -270,7 +308,7 @@ check_application_conflict (card_t card, const char *name,
const unsigned char *serialno_bin,
size_t serialno_bin_len)
{
- app_t app;
+ apptype_t apptype;
if (!card || !name)
return 0;
@@ -284,11 +322,9 @@ check_application_conflict (card_t card, const char *name,
return 0; /* The card does not match the requested S/N. */
}
- /* Check whether the requested NAME matches any already selected
- * application. */
- for (app = card->app; app; app = app->next)
- if (!ascii_strcasecmp (strapptype (app->apptype), name))
- return 0;
+ apptype = apptype_from_name (name);
+ if (card->app->apptype == apptype)
+ return 0;
if (card->app->apptype == APPTYPE_UNDEFINED)
return 0;
@@ -339,6 +375,7 @@ card_reset (card_t card, ctrl_t ctrl, int send_reset)
else
{
ctrl->card_ctx = NULL;
+ ctrl->current_apptype = APPTYPE_NONE;
card_unref (card);
}
@@ -608,7 +645,7 @@ select_application (ctrl_t ctrl, const char *name, card_t *r_card,
int slot;
int periodical_check_needed_this;
- slot = apdu_open_reader (l, !card_top);
+ slot = apdu_open_reader (l);
if (slot < 0)
break;
@@ -652,6 +689,22 @@ select_application (ctrl_t ctrl, const char *name, card_t *r_card,
{
err = check_application_conflict (card, name, NULL, 0);
if (!err)
+ ctrl->current_apptype = card->app ? card->app->apptype : APPTYPE_NONE;
+ else if (gpg_err_code (err) == GPG_ERR_FALSE)
+ {
+ apptype_t req_apptype = apptype_from_name (name);
+
+ if (!req_apptype)
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ else
+ {
+ err = select_additional_application_internal (card, req_apptype);
+ if (!err)
+ ctrl->current_apptype = req_apptype;
+ }
+ }
+
+ if (!err)
{
/* Note: We do not use card_ref as we are already locked. */
card->ref_count++;
@@ -662,8 +715,6 @@ select_application (ctrl_t ctrl, const char *name, card_t *r_card,
card->next = card_top;
card_top = card;
}
-
- ctrl->current_apptype = card->app ? card->app->apptype : 0;
}
unlock_card (card);
}
@@ -676,32 +727,13 @@ select_application (ctrl_t ctrl, const char *name, card_t *r_card,
}
-/* This function needs to be called with the NAME of the new
- * application to be selected on CARD. On success the application is
- * added to the list of the card's active applications as currently
- * active application. On error no new application is allocated.
- * Selecting an already selected application has no effect. */
-gpg_error_t
-select_additional_application (ctrl_t ctrl, const char *name)
+static gpg_error_t
+select_additional_application_internal (card_t card, apptype_t req_apptype)
{
gpg_error_t err = 0;
- apptype_t req_apptype;
- card_t card;
- app_t app = NULL;
+ app_t app;
int i;
- req_apptype = apptype_from_name (name);
- if (!req_apptype)
- err = gpg_error (GPG_ERR_NOT_FOUND);
-
- card = ctrl->card_ctx;
- if (!card)
- return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
-
- err = lock_card (card, ctrl);
- if (err)
- return err;
-
/* Check that the requested app has not yet been put onto the list. */
for (app = card->app; app; app = app->next)
if (app->apptype == req_apptype)
@@ -713,7 +745,6 @@ select_additional_application (ctrl_t ctrl, const char *name)
app = NULL;
goto leave;
}
- app = NULL;
/* Allocate a new app object. */
app = xtrycalloc (1, sizeof *app);
@@ -723,13 +754,17 @@ select_additional_application (ctrl_t ctrl, const char *name)
log_info ("error allocating app context: %s\n", gpg_strerror (err));
goto leave;
}
+ app->card = card;
/* Find the app and run the select. */
for (i=0; app_priority_list[i].apptype; i++)
{
if (app_priority_list[i].apptype == req_apptype
&& is_app_allowed (app_priority_list[i].name))
- err = app_priority_list[i].select_func (app);
+ {
+ err = app_priority_list[i].select_func (app);
+ break;
+ }
}
if (!app_priority_list[i].apptype
|| (err && gpg_err_code (err) != GPG_ERR_OBJ_TERM_STATE))
@@ -742,12 +777,131 @@ select_additional_application (ctrl_t ctrl, const char *name)
* reselect by maybe_switch_app after the select we just did. */
app->next = card->app;
card->app = app;
- ctrl->current_apptype = app->apptype;
- log_error ("added app '%s' to the card context\n", strapptype(app->apptype));
+ log_info ("added app '%s' to the card context and switched\n",
+ strapptype (app->apptype));
leave:
+ if (err)
+ xfree (app);
+ return err;
+}
+
+
+/* Add all possible additional applications to the card context but do
+ * not change the current one. This current works only for Yubikeys. */
+static gpg_error_t
+select_all_additional_applications_internal (card_t card)
+{
+ gpg_error_t err = 0;
+ apptype_t candidates[3];
+ int i, j;
+
+ if (card->cardtype == CARDTYPE_YUBIKEY)
+ {
+ candidates[0] = APPTYPE_OPENPGP;
+ candidates[1] = APPTYPE_PIV;
+ candidates[2] = APPTYPE_NONE;
+ }
+ else
+ {
+ candidates[0] = APPTYPE_NONE;
+ }
+
+ /* Find the app and run the select. */
+ for (i=0; app_priority_list[i].apptype; i++)
+ {
+ app_t app, app_r, app_prev;
+
+ for (j=0; candidates[j]; j++)
+ if (candidates[j] == app_priority_list[i].apptype
+ && is_app_allowed (app_priority_list[i].name))
+ break;
+ if (!candidates[j])
+ continue;
+
+ for (app = card->app; app; app = app->next)
+ if (app->apptype == candidates[j])
+ break;
+ if (app)
+ continue; /* Already on the list of apps. */
+
+ app = xtrycalloc (1, sizeof *app);
+ if (!app)
+ {
+ err = gpg_error_from_syserror ();
+ log_info ("error allocating app context: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ app->card = card;
+ err = app_priority_list[i].select_func (app);
+ if (err)
+ {
+ log_error ("error selecting additional app '%s': %s - skipped\n",
+ strapptype (candidates[j]), gpg_strerror (err));
+ err = 0;
+ xfree (app);
+ }
+ else
+ {
+ /* Append to the list of apps. */
+ app_prev = card->app;
+ for (app_r=app_prev->next; app_r; app_prev=app_r, app_r=app_r->next)
+ ;
+ app_prev->next = app;
+ log_info ("added app '%s' to the card context\n",
+ strapptype (app->apptype));
+ }
+ }
+
+ leave:
+ return err;
+}
+
+
+/* This function needs to be called with the NAME of the new
+ * application to be selected on CARD. On success the application is
+ * added to the list of the card's active applications as currently
+ * active application. On error no new application is allocated.
+ * Selecting an already selected application has no effect. */
+gpg_error_t
+select_additional_application (ctrl_t ctrl, const char *name)
+{
+ gpg_error_t err = 0;
+ apptype_t req_apptype;
+ card_t card;
+
+ if (!name)
+ req_apptype = 0;
+ else
+ {
+ req_apptype = apptype_from_name (name);
+ if (!req_apptype)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+ }
+
+ card = ctrl->card_ctx;
+ if (!card)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+
+ err = lock_card (card, ctrl);
+ if (err)
+ return err;
+
+ if (req_apptype)
+ {
+ err = select_additional_application_internal (card, req_apptype);
+ if (!err)
+ {
+ ctrl->current_apptype = req_apptype;
+ log_debug ("current_apptype is set to %s\n", name);
+ }
+ }
+ else
+ {
+ err = select_all_additional_applications_internal (card);
+ }
+
unlock_card (card);
- xfree (app);
return err;
}
@@ -809,8 +963,6 @@ deallocate_card (card_t card)
xfree (a);
}
- scd_clear_current_app (card);
-
xfree (card->serialno);
unlock_card (card);
xfree (card);
@@ -944,10 +1096,12 @@ app_get_serialno (app_t app)
* that the new active app will be moved to the head of the list at
* CARD->app. Thus function must be called with the card lock held. */
static gpg_error_t
-maybe_switch_app (ctrl_t ctrl, card_t card)
+maybe_switch_app (ctrl_t ctrl, card_t card, const char *keyref)
{
gpg_error_t err;
- app_t app, app_prev;
+ app_t app;
+ app_t app_prev = NULL;
+ apptype_t apptype;
if (!card->ref_count || !card->app)
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
@@ -958,51 +1112,117 @@ maybe_switch_app (ctrl_t ctrl, card_t card)
ctrl->current_apptype = card->app->apptype;
return 0;
}
- log_debug ("card=%p want=%s card->app=%s\n",
- card, strapptype (ctrl->current_apptype),
- strapptype (card->app->apptype));
- app_dump_state ();
- if (ctrl->current_apptype == card->app->apptype)
- return 0; /* No need to switch. */
- app_prev = card->app;
- for (app = app_prev->next; app; app_prev = app, app = app->next)
- if (app->apptype == ctrl->current_apptype)
- break;
+ if (DBG_APP)
+ log_debug ("slot %d: have=%s want=%s keyref=%s\n",
+ card->slot, strapptype (card->app->apptype),
+ strapptype (ctrl->current_apptype),
+ keyref? keyref:"[none]");
+
+ app = NULL;
+ if (keyref)
+ {
+ /* Switch based on the requested KEYREF. */
+ apptype = apptype_from_keyref (keyref);
+ if (apptype)
+ {
+ for (app = card->app; app; app_prev = app, app = app->next)
+ if (app->apptype == apptype)
+ break;
+ if (!app_prev && ctrl->current_apptype == card->app->apptype)
+ return 0; /* Already the first app - no need to switch. */
+ }
+ else if (strlen (keyref) == 40)
+ {
+ /* This looks like a keygrip. Iterate over all apps to find
+ * the corresponding app. */
+ for (app = card->app; app; app_prev = app, app = app->next)
+ if (app->fnc.with_keygrip
+ && !app->fnc.with_keygrip (app, ctrl,
+ KEYGRIP_ACTION_LOOKUP, keyref))
+ break;
+ if (!app_prev && ctrl->current_apptype == card->app->apptype)
+ return 0; /* Already the first app - no need to switch. */
+ }
+ }
+
+ if (!app)
+ {
+ /* Switch based on the current application of this connection or
+ * if a keyref based switch didn't worked. */
+ if (ctrl->current_apptype == card->app->apptype)
+ return 0; /* No need to switch. */
+ app_prev = card->app;
+ for (app = app_prev->next; app; app_prev = app, app = app->next)
+ if (app->apptype == ctrl->current_apptype)
+ break;
+ }
if (!app)
return gpg_error (GPG_ERR_WRONG_CARD);
if (!app->fnc.reselect)
{
log_error ("oops: reselect function missing for '%s'\n",
- strapptype(app->apptype));
+ strapptype (app->apptype));
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
}
err = app->fnc.reselect (app, ctrl);
if (err)
{
- log_error ("error re-selecting '%s': %s\n",
- strapptype(app->apptype), gpg_strerror (err));
+ log_error ("card %d: error re-selecting '%s': %s\n",
+ card->slot, xstrapptype (app), gpg_strerror (err));
return err;
}
- /* Swap APP with the head of the app list. Note that APP is not the
- * head of the list. */
- app_prev->next = app->next;
- app->next = card->app;
- card->app = app;
+
+ /* Swap APP with the head of the app list if needed. Note that APP
+ * is not the head of the list. */
+ if (app_prev)
+ {
+ app_prev->next = app->next;
+ app->next = card->app;
+ card->app = app;
+ }
+
+ if (opt.verbose)
+ log_info ("card %d: %s '%s'\n",
+ card->slot, app_prev? "switched to":"re-selected",
+ xstrapptype (app));
+
ctrl->current_apptype = app->apptype;
- log_error ("switched to '%s'\n", strapptype(app->apptype));
return 0;
}
+/* Helper for app_write_learn_status. */
+static gpg_error_t
+write_learn_status_core (card_t card, app_t app, ctrl_t ctrl,
+ unsigned int flags)
+{
+ /* We do not send CARD and APPTYPE if only keypairinfo is requested. */
+ if (!(flags & APP_LEARN_FLAG_KEYPAIRINFO))
+ {
+ if (card && card->cardtype)
+ send_status_direct (ctrl, "CARDTYPE", strcardtype (card->cardtype));
+ if (card && card->cardversion)
+ send_status_printf (ctrl, "CARDVERSION", "%X", card->cardversion);
+ if (app->apptype)
+ send_status_direct (ctrl, "APPTYPE", strapptype (app->apptype));
+ if (app->appversion)
+ send_status_printf (ctrl, "APPVERSION", "%X", app->appversion);
+ }
+
+ return app->fnc.learn_status (app, ctrl, flags);
+}
+
+
/* Write out the application specific status lines for the LEARN
command. */
gpg_error_t
app_write_learn_status (card_t card, ctrl_t ctrl, unsigned int flags)
{
- gpg_error_t err;
+ gpg_error_t err, err2;
app_t app;
+ int any_reselect = 0;
if (!card)
return gpg_error (GPG_ERR_INV_VALUE);
@@ -1011,29 +1231,43 @@ app_write_learn_status (card_t card, ctrl_t ctrl, unsigned int flags)
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ /* Always make sure that the current app for this connection has
+ * been selected and is at the top of the list. */
+ if ((err = maybe_switch_app (ctrl, card, NULL)))
;
else if (!card->app->fnc.learn_status)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
else
{
- app = card->app;
-
- /* We do not send CARD and APPTYPE if only keypairinfo is requested. */
- if (!(flags &1))
+ err = write_learn_status_core (card, card->app, ctrl, flags);
+ if (!err && card->app->fnc.reselect && (flags & APP_LEARN_FLAG_MULTI))
{
- if (card->cardtype)
- send_status_direct (ctrl, "CARDTYPE", strcardtype (card->cardtype));
- if (card->cardversion)
- send_status_printf (ctrl, "CARDVERSION", "%X", card->cardversion);
- if (app->apptype)
- send_status_direct (ctrl, "APPTYPE", strapptype (app->apptype));
- if (app->appversion)
- send_status_printf (ctrl, "APPVERSION", "%X", app->appversion);
- /* FIXME: Send info for the other active apps of the card? */
+ /* The current app has the reselect feature so that we can
+ * loop over all other apps which are capable of a reselect
+ * and finally reselect the first app again. Note that we
+ * did the learn for the currently selected card above. */
+ app = card->app;
+ for (app = app->next; app && !err; app = app->next)
+ if (app->fnc.reselect)
+ {
+ any_reselect = 1;
+ err = app->fnc.reselect (app, ctrl);
+ if (!err)
+ err = write_learn_status_core (NULL, app, ctrl, flags);
+ }
+ app = card->app;
+ if (any_reselect)
+ {
+ err2 = app->fnc.reselect (app, ctrl);
+ if (err2)
+ {
+ log_error ("error re-selecting '%s': %s\n",
+ strapptype(app->apptype), gpg_strerror (err2));
+ if (!err)
+ err = err2;
+ }
+ }
}
-
- err = app->fnc.learn_status (app, ctrl, flags);
}
unlock_card (card);
@@ -1057,12 +1291,17 @@ app_readcert (card_t card, ctrl_t ctrl, const char *certid,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, certid)))
;
else if (!card->app->fnc.readcert)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
else
- err = card->app->fnc.readcert (card->app, certid, cert, certlen);
+ {
+ if (DBG_APP)
+ log_debug ("slot %d app %s: calling readcert(%s)\n",
+ card->slot, xstrapptype (card->app), certid);
+ err = card->app->fnc.readcert (card->app, certid, cert, certlen);
+ }
unlock_card (card);
return err;
@@ -1094,12 +1333,17 @@ app_readkey (card_t card, ctrl_t ctrl, const char *keyid, unsigned int flags,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, keyid)))
;
else if (!card->app->fnc.readkey)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
else
- err = card->app->fnc.readkey (card->app, ctrl, keyid, flags, pk, pklen);
+ {
+ if (DBG_APP)
+ log_debug ("slot %d app %s: calling readkey(%s)\n",
+ card->slot, xstrapptype (card->app), keyid);
+ err = card->app->fnc.readkey (card->app, ctrl, keyid, flags, pk, pklen);
+ }
unlock_card (card);
return err;
@@ -1118,7 +1362,7 @@ app_getattr (card_t card, ctrl_t ctrl, const char *name)
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, NULL)))
;
else if (name && !strcmp (name, "CARDTYPE"))
{
@@ -1144,7 +1388,12 @@ app_getattr (card_t card, ctrl_t ctrl, const char *name)
else if (!card->app->fnc.getattr)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
else
- err = card->app->fnc.getattr (card->app, ctrl, name);
+ {
+ if (DBG_APP)
+ log_debug ("slot %d app %s: calling getattr(%s)\n",
+ card->slot, xstrapptype (card->app), name);
+ err = card->app->fnc.getattr (card->app, ctrl, name);
+ }
unlock_card (card);
return err;
@@ -1166,13 +1415,18 @@ app_setattr (card_t card, ctrl_t ctrl, const char *name,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, NULL)))
;
else if (!card->app->fnc.setattr)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
else
- err = card->app->fnc.setattr (card->app, name, pincb, pincb_arg,
- value, valuelen);
+ {
+ if (DBG_APP)
+ log_debug ("slot %d app %s: calling setattr(%s)\n",
+ card->slot, xstrapptype (card->app), name);
+ err = card->app->fnc.setattr (card->app, name, pincb, pincb_arg,
+ value, valuelen);
+ }
unlock_card (card);
return err;
@@ -1197,15 +1451,20 @@ app_sign (card_t card, ctrl_t ctrl, const char *keyidstr, int hashalgo,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, keyidstr)))
;
else if (!card->app->fnc.sign)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
else
- err = card->app->fnc.sign (card->app, keyidstr, hashalgo,
- pincb, pincb_arg,
- indata, indatalen,
- outdata, outdatalen);
+ {
+ if (DBG_APP)
+ log_debug ("slot %d app %s: calling sign(%s)\n",
+ card->slot, xstrapptype (card->app), keyidstr);
+ err = card->app->fnc.sign (card->app, keyidstr, hashalgo,
+ pincb, pincb_arg,
+ indata, indatalen,
+ outdata, outdatalen);
+ }
unlock_card (card);
if (opt.verbose)
@@ -1233,15 +1492,20 @@ app_auth (card_t card, ctrl_t ctrl, const char *keyidstr,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, keyidstr)))
;
else if (!card->app->fnc.auth)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
else
- err = card->app->fnc.auth (card->app, keyidstr,
- pincb, pincb_arg,
- indata, indatalen,
- outdata, outdatalen);
+ {
+ if (DBG_APP)
+ log_debug ("slot %d app %s: calling auth(%s)\n",
+ card->slot, xstrapptype (card->app), keyidstr);
+ err = card->app->fnc.auth (card->app, keyidstr,
+ pincb, pincb_arg,
+ indata, indatalen,
+ outdata, outdatalen);
+ }
unlock_card (card);
if (opt.verbose)
@@ -1271,16 +1535,21 @@ app_decipher (card_t card, ctrl_t ctrl, const char *keyidstr,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, keyidstr)))
;
else if (!card->app->fnc.decipher)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
else
- err = card->app->fnc.decipher (card->app, keyidstr,
- pincb, pincb_arg,
- indata, indatalen,
- outdata, outdatalen,
- r_info);
+ {
+ if (DBG_APP)
+ log_debug ("slot %d app %s: calling decipher(%s)\n",
+ card->slot, xstrapptype (card->app), keyidstr);
+ err = card->app->fnc.decipher (card->app, keyidstr,
+ pincb, pincb_arg,
+ indata, indatalen,
+ outdata, outdatalen,
+ r_info);
+ }
unlock_card (card);
if (opt.verbose)
@@ -1305,13 +1574,18 @@ app_writecert (card_t card, ctrl_t ctrl,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, certidstr)))
;
else if (!card->app->fnc.writecert)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
else
- err = card->app->fnc.writecert (card->app, ctrl, certidstr,
- pincb, pincb_arg, data, datalen);
+ {
+ if (DBG_APP)
+ log_debug ("slot %d app %s: calling writecert(%s)\n",
+ card->slot, xstrapptype (card->app), certidstr);
+ err = card->app->fnc.writecert (card->app, ctrl, certidstr,
+ pincb, pincb_arg, data, datalen);
+ }
unlock_card (card);
if (opt.verbose)
@@ -1336,13 +1610,18 @@ app_writekey (card_t card, ctrl_t ctrl,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, keyidstr)))
;
else if (!card->app->fnc.writekey)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
else
- err = card->app->fnc.writekey (card->app, ctrl, keyidstr, flags,
- pincb, pincb_arg, keydata, keydatalen);
+ {
+ if (DBG_APP)
+ log_debug ("slot %d app %s: calling writekey(%s)\n",
+ card->slot, xstrapptype (card->app), keyidstr);
+ err = card->app->fnc.writekey (card->app, ctrl, keyidstr, flags,
+ pincb, pincb_arg, keydata, keydatalen);
+ }
unlock_card (card);
if (opt.verbose)
@@ -1366,13 +1645,18 @@ app_genkey (card_t card, ctrl_t ctrl, const char *keynostr,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, keynostr)))
;
else if (!card->app->fnc.genkey)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
else
- err = card->app->fnc.genkey (card->app, ctrl, keynostr, keytype, flags,
- createtime, pincb, pincb_arg);
+ {
+ if (DBG_APP)
+ log_debug ("slot %d app %s: calling genkey(%s)\n",
+ card->slot, xstrapptype (card->app), keynostr);
+ err = card->app->fnc.genkey (card->app, ctrl, keynostr, keytype, flags,
+ createtime, pincb, pincb_arg);
+ }
unlock_card (card);
if (opt.verbose)
@@ -1421,13 +1705,18 @@ app_change_pin (card_t card, ctrl_t ctrl, const char *chvnostr,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, NULL)))
;
else if (!card->app->fnc.change_pin)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
else
- err = card->app->fnc.change_pin (card->app, ctrl,
- chvnostr, flags, pincb, pincb_arg);
+ {
+ if (DBG_APP)
+ log_debug ("slot %d app %s: calling change_pin(%s)\n",
+ card->slot, xstrapptype (card->app), chvnostr);
+ err = card->app->fnc.change_pin (card->app, ctrl,
+ chvnostr, flags, pincb, pincb_arg);
+ }
unlock_card (card);
if (opt.verbose)
@@ -1452,12 +1741,17 @@ app_check_pin (card_t card, ctrl_t ctrl, const char *keyidstr,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, NULL)))
;
else if (!card->app->fnc.check_pin)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
else
- err = card->app->fnc.check_pin (card->app, keyidstr, pincb, pincb_arg);
+ {
+ if (DBG_APP)
+ log_debug ("slot %d app %s: calling check_pin(%s)\n",
+ card->slot, xstrapptype (card->app), keyidstr);
+ err = card->app->fnc.check_pin (card->app, keyidstr, pincb, pincb_arg);
+ }
unlock_card (card);
if (opt.verbose)
@@ -1691,24 +1985,33 @@ app_send_card_list (ctrl_t ctrl)
card_t
app_do_with_keygrip (ctrl_t ctrl, int action, const char *keygrip_str)
{
- gpg_error_t err;
+ int locked = 0;
card_t c;
app_t a;
npth_mutex_lock (&card_list_lock);
for (c = card_top; c; c = c->next)
- for (a = c->app; a; a = a->next)
- if (a->fnc.with_keygrip)
+ {
+ if (lock_card (c, ctrl))
{
- if (!lock_card (c, ctrl))
- {
- err = a->fnc.with_keygrip (a, ctrl, action, keygrip_str);
- unlock_card (c);
- if (!err)
- goto leave_the_loop;
- }
+ c = NULL;
+ goto leave_the_loop;
}
+ locked = 1;
+ for (a = c->app; a; a = a->next)
+ if (a->fnc.with_keygrip)
+ {
+ if (DBG_APP)
+ log_debug ("slot %d app %s: calling with_keygrip(action=%d)\n",
+ c->slot, xstrapptype (a), action);
+ if (!a->fnc.with_keygrip (a, ctrl, action, keygrip_str))
+ goto leave_the_loop;
+ }
+ unlock_card (c);
+ locked = 0;
+ }
+
leave_the_loop:
@@ -1721,6 +2024,11 @@ app_do_with_keygrip (ctrl_t ctrl, int action, const char *keygrip_str)
if (c && c->app && c->app->apptype != a->apptype)
ctrl->current_apptype = a->apptype;
+ if (locked && c)
+ {
+ unlock_card (c);
+ locked = 0;
+ }
npth_mutex_unlock (&card_list_lock);
return c;
}
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c
index d762490c8..d3e9ef024 100644
--- a/scd/ccid-driver.c
+++ b/scd/ccid-driver.c
@@ -1276,7 +1276,7 @@ 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)
+ccid_dev_scan (int *idx_max_p, void **t_p)
{
ssize_t n;
libusb_device *dev;
@@ -1403,9 +1403,10 @@ ccid_dev_scan (int *idx_max_p, struct ccid_dev_table **t_p)
}
void
-ccid_dev_scan_finish (struct ccid_dev_table *tbl, int max)
+ccid_dev_scan_finish (void *tbl0, int max)
{
int i;
+ struct ccid_dev_table *tbl = tbl0;
for (i = 0; i < max; i++)
{
@@ -1424,12 +1425,13 @@ ccid_dev_scan_finish (struct ccid_dev_table *tbl, int max)
}
unsigned int
-ccid_get_BAI (int idx, struct ccid_dev_table *tbl)
+ccid_get_BAI (int idx, void *tbl0)
{
int n;
int bus, addr, intf;
unsigned int bai;
libusb_device *dev;
+ struct ccid_dev_table *tbl = tbl0;
n = tbl[idx].n;
dev = ccid_usb_dev_list[n];
@@ -1537,7 +1539,7 @@ ccid_usb_thread (void *arg)
static int
ccid_open_usb_reader (const char *spec_reader_name,
- int idx, struct ccid_dev_table *ccid_table,
+ int idx, void *ccid_table0,
ccid_driver_t *handle, char **rdrname_p)
{
libusb_device *dev;
@@ -1549,6 +1551,7 @@ ccid_open_usb_reader (const char *spec_reader_name,
int n;
int bus, addr;
unsigned int bai;
+ struct ccid_dev_table *ccid_table = ccid_table0;
n = ccid_table[idx].n;
ifc_no = ccid_table[idx].interface_number;
@@ -1678,9 +1681,11 @@ ccid_open_usb_reader (const char *spec_reader_name,
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,
+ void *ccid_table0,
ccid_driver_t *handle, char **rdrname_p)
{
+ struct ccid_dev_table *ccid_table = ccid_table0;
+
*handle = calloc (1, sizeof **handle);
if (!*handle)
{
@@ -1940,6 +1945,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
int rc;
int msglen;
int notified = 0;
+ int bwi = 1;
/* Fixme: The next line for the current Valgrind without support
for USB IOCTLs. */
@@ -1950,7 +1956,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
npth_unprotect ();
#endif
rc = libusb_bulk_transfer (handle->idev, handle->ep_bulk_in,
- buffer, length, &msglen, timeout);
+ buffer, length, &msglen, bwi*timeout);
#ifdef USE_NPTH
npth_protect ();
#endif
@@ -1998,6 +2004,10 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
DEBUGOUT_2 ("time extension requested (%02X,%02X)\n",
buffer[7], buffer[8]);
+ bwi = 1;
+ if (buffer[8] != 0 && buffer[8] != 0xff)
+ bwi = buffer[8];
+
/* Gnuk enhancement to prompt user input by ack button */
if (buffer[8] == 0xff && !notified)
{
@@ -2855,7 +2865,7 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
size_t apdu_part_len;
size_t msglen;
unsigned char seqno;
- int bwi = 4;
+ int bwi = 0;
unsigned char chain = 0;
if (apdu_len == 0 || apdu_len > sizeof (msg) - 10)
@@ -3107,7 +3117,7 @@ ccid_transceive (ccid_driver_t handle,
msg[0] = PC_to_RDR_XfrBlock;
msg[5] = 0; /* slot */
msg[6] = seqno = handle->seqno++;
- msg[7] = (wait_more ? wait_more : 1); /* bBWI */
+ msg[7] = wait_more; /* bBWI */
msg[8] = 0; /* RFU */
msg[9] = 0; /* RFU */
set_msg_len (msg, tpdulen);
diff --git a/scd/ccid-driver.h b/scd/ccid-driver.h
index 1550b3eba..8e7ff4482 100644
--- a/scd/ccid-driver.h
+++ b/scd/ccid-driver.h
@@ -116,12 +116,12 @@ struct ccid_dev_table;
int ccid_set_debug_level (int level);
char *ccid_get_reader_list (void);
-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);
+gpg_error_t ccid_dev_scan (int *idx_max, void **t_p);
+void ccid_dev_scan_finish (void *tbl0, int max);
+unsigned int ccid_get_BAI (int, void *tbl0);
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,
+ int idx, void *ccid_table0,
ccid_driver_t *handle, char **rdrname_p);
int ccid_set_progress_cb (ccid_driver_t handle,
void (*cb)(void *, const char *, int, int, int),
diff --git a/scd/command.c b/scd/command.c
index 0096ca96d..73a524b49 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -105,6 +105,12 @@ static struct server_local_s *session_list;
in this variable. */
static struct server_local_s *locked_session;
+
+
+/* Local prototypes. */
+static int command_has_option (const char *cmd, const char *cmdopt);
+
+
/* Convert the STRING into a newly allocated buffer while translating
the hex numbers. Stops at the first invalid character. Blanks and
@@ -218,10 +224,12 @@ open_card (ctrl_t ctrl)
return select_application (ctrl, NULL, &ctrl->card_ctx, 0, NULL, 0);
}
-/* Explicitly open a card for a specific use of APPTYPE or SERIALNO. */
+/* Explicitly open a card for a specific use of APPTYPE or SERIALNO.
+ * If OPT_ALL ist set also add all possible additional apps. */
static gpg_error_t
open_card_with_request (ctrl_t ctrl,
- const char *apptypestr, const char *serialno)
+ const char *apptypestr, const char *serialno,
+ int opt_all)
{
gpg_error_t err;
unsigned char *serialno_bin = NULL;
@@ -249,10 +257,13 @@ open_card_with_request (ctrl_t ctrl,
/* Re-scan USB devices. Release CARD, before the scan. */
/* FIXME: Is a card_unref sufficient or do we need to deallocate? */
ctrl->card_ctx = NULL;
+ ctrl->current_apptype = APPTYPE_NONE;
card_unref (card);
err = select_application (ctrl, apptypestr, &ctrl->card_ctx, 1,
serialno_bin, serialno_bin_len);
+ if (!err && opt_all)
+ err = select_additional_application (ctrl, APPTYPE_NONE);
leave:
xfree (serialno_bin);
@@ -261,7 +272,7 @@ open_card_with_request (ctrl_t ctrl,
static const char hlp_serialno[] =
- "SERIALNO [--demand=<serialno>] [<apptype>]\n"
+ "SERIALNO [--demand=<serialno>] [--all] [<apptype>]\n"
"\n"
"Return the serial number of the card using a status response. This\n"
"function should be used to check for the presence of a card.\n"
@@ -269,6 +280,9 @@ static const char hlp_serialno[] =
"If --demand is given, an application on the card with SERIALNO is\n"
"selected and an error is returned if no such card available.\n"
"\n"
+ "If --all is given, all possible other applications of the card are\n"
+ "will also be selected for on-the-fly swicthing.\n"
+ "\n"
"If APPTYPE is given, an application of that type is selected and an\n"
"error is returned if the application is not supported or available.\n"
"The default is to auto-select the application using a hardwired\n"
@@ -290,6 +304,7 @@ cmd_serialno (assuan_context_t ctx, char *line)
int rc = 0;
char *serial;
const char *demand;
+ int opt_all = has_option (line, "--all");
if ( IS_LOCKED (ctrl) )
return gpg_error (GPG_ERR_LOCKED);
@@ -307,11 +322,13 @@ cmd_serialno (assuan_context_t ctx, char *line)
else
demand = NULL;
+ line = skip_options (line);
+
/* Clear the remove flag so that the open_card is able to reread it. */
if (ctrl->server_local->card_removed)
ctrl->server_local->card_removed = 0;
- if ((rc = open_card_with_request (ctrl, *line? line:NULL, demand)))
+ if ((rc = open_card_with_request (ctrl, *line? line:NULL, demand, opt_all)))
{
ctrl->server_local->card_removed = 1;
return rc;
@@ -337,7 +354,7 @@ cmd_serialno (assuan_context_t ctx, char *line)
static const char hlp_learn[] =
- "LEARN [--force] [--keypairinfo]\n"
+ "LEARN [--force] [--keypairinfo] [--multi]\n"
"\n"
"Learn all useful information of the currently inserted card. When\n"
"used without the force options, the command might do an INQUIRE\n"
@@ -365,7 +382,8 @@ static const char hlp_learn[] =
" PIV = PIV card\n"
" NKS = NetKey card\n"
"\n"
- "are implemented. These strings are aliases for the AID\n"
+ "are implemented. These strings are aliases for the AID. With option\n"
+ "--multi information for all switchable apps are returned.\n"
"\n"
" S KEYPAIRINFO <hexstring_with_keygrip> <hexstring_with_id> [<usage>]\n"
"\n"
@@ -412,6 +430,7 @@ cmd_learn (assuan_context_t ctx, char *line)
ctrl_t ctrl = assuan_get_pointer (ctx);
int rc = 0;
int only_keypairinfo = has_option (line, "--keypairinfo");
+ int opt_multi = has_option (line, "--multi");
if ((rc = open_card (ctrl)))
return rc;
@@ -474,7 +493,10 @@ cmd_learn (assuan_context_t ctx, char *line)
/* Let the application print out its collection of useful status
information. */
if (!rc)
- rc = app_write_learn_status (ctrl->card_ctx, ctrl, only_keypairinfo);
+ rc = app_write_learn_status
+ (ctrl->card_ctx, ctrl,
+ ( (only_keypairinfo? APP_LEARN_FLAG_KEYPAIRINFO : 0)
+ | (opt_multi? APP_LEARN_FLAG_MULTI : 0)) );
return rc;
}
@@ -1557,7 +1579,9 @@ static const char hlp_getinfo[] =
" application per line, fields delimited by colons,\n"
" first field is the name.\n"
" card_list - Return a list of serial numbers of active cards,\n"
- " using a status response.";
+ " using a status response.\n"
+ " cmd_has_option CMD OPT\n"
+ " - Returns OK if command CMD has option OPT.\n";
static gpg_error_t
cmd_getinfo (assuan_context_t ctx, char *line)
{
@@ -1575,6 +1599,38 @@ cmd_getinfo (assuan_context_t ctx, char *line)
snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
}
+ else if (!strncmp (line, "cmd_has_option", 14)
+ && (line[14] == ' ' || line[14] == '\t' || !line[14]))
+ {
+ char *cmd, *cmdopt;
+ line += 14;
+ while (*line == ' ' || *line == '\t')
+ line++;
+ if (!*line)
+ rc = gpg_error (GPG_ERR_MISSING_VALUE);
+ else
+ {
+ cmd = line;
+ while (*line && (*line != ' ' && *line != '\t'))
+ line++;
+ if (!*line)
+ rc = gpg_error (GPG_ERR_MISSING_VALUE);
+ else
+ {
+ *line++ = 0;
+ while (*line == ' ' || *line == '\t')
+ line++;
+ if (!*line)
+ rc = gpg_error (GPG_ERR_MISSING_VALUE);
+ else
+ {
+ cmdopt = line;
+ if (!command_has_option (cmd, cmdopt))
+ rc = gpg_error (GPG_ERR_FALSE);
+ }
+ }
+ }
+ }
else if (!strcmp (line, "socket_name"))
{
const char *s = scd_get_socket_name ();
@@ -1661,6 +1717,7 @@ cmd_restart (assuan_context_t ctx, char *line)
if (card)
{
ctrl->card_ctx = NULL;
+ ctrl->current_apptype = APPTYPE_NONE;
card_unref (card);
}
if (locked_session && ctrl->server_local == locked_session)
@@ -1917,6 +1974,20 @@ send_keyinfo (ctrl_t ctrl, int data, const char *keygrip_str,
+/* Return true if the command CMD implements the option OPT. */
+static int
+command_has_option (const char *cmd, const char *cmdopt)
+{
+ if (!strcmp (cmd, "SERIALNO"))
+ {
+ if (!strcmp (cmdopt, "all"))
+ return 1;
+ }
+
+ return 0;
+}
+
+
/* Tell the assuan library about our commands */
static int
register_commands (assuan_context_t ctx)
@@ -2079,20 +2150,6 @@ scd_command_handler (ctrl_t ctrl, int fd)
}
-/* Clear the current application info for CARD from all sessions.
- * This is used while deallocating a card. */
-void
-scd_clear_current_app (card_t card)
-{
- struct server_local_s *sl;
-
- for (sl=session_list; sl; sl = sl->next_session)
- {
- if (sl->ctrl_backlink->card_ctx == card)
- sl->ctrl_backlink->current_apptype = APPTYPE_NONE;
- }
-}
-
/* Send a line with status information via assuan and escape all given
buffers. The variable elements are pairs of (char *, size_t),
terminated with a (NULL, 0). */
@@ -2234,6 +2291,7 @@ send_client_notifications (card_t card, int removal)
if (removal)
{
sl->ctrl_backlink->card_ctx = NULL;
+ sl->ctrl_backlink->current_apptype = APPTYPE_NONE;
sl->card_removed = 1;
card_unref_locked (card);
}
diff --git a/scd/iso7816.c b/scd/iso7816.c
index 954aa3d4a..d44046e67 100644
--- a/scd/iso7816.c
+++ b/scd/iso7816.c
@@ -67,6 +67,7 @@ map_sw (int sw)
case SW_FILE_NOT_FOUND: ec = GPG_ERR_ENOENT; break;
case SW_RECORD_NOT_FOUND:ec= GPG_ERR_NOT_FOUND; break;
case SW_REF_NOT_FOUND: ec = GPG_ERR_NO_OBJ; break;
+ case SW_INCORRECT_P0_P1:ec = GPG_ERR_INV_VALUE; break;
case SW_BAD_P0_P1: ec = GPG_ERR_INV_VALUE; break;
case SW_EXACT_LENGTH: ec = GPG_ERR_INV_VALUE; break;
case SW_INS_NOT_SUP: ec = GPG_ERR_CARD; break;
diff --git a/scd/scdaemon.c b/scd/scdaemon.c
index e89569e5d..1796db386 100644
--- a/scd/scdaemon.c
+++ b/scd/scdaemon.c
@@ -177,6 +177,7 @@ static struct debug_flags_s debug_flags [] =
{ DBG_IPC_VALUE , "ipc" },
{ DBG_CARD_IO_VALUE, "cardio" },
{ DBG_READER_VALUE , "reader" },
+ { DBG_APP_VALUE , "app" },
{ 0, NULL }
};
diff --git a/scd/scdaemon.h b/scd/scdaemon.h
index b709b1cbc..3f2e3ed55 100644
--- a/scd/scdaemon.h
+++ b/scd/scdaemon.h
@@ -67,6 +67,7 @@ struct
} opt;
+#define DBG_APP_VALUE 1 /* Debug app speific stuff. */
#define DBG_MPI_VALUE 2 /* debug mpi details */
#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */
#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */
@@ -77,6 +78,7 @@ struct
#define DBG_CARD_IO_VALUE 2048
#define DBG_READER_VALUE 4096 /* Trace reader related functions. */
+#define DBG_APP (opt.debug & DBG_APP_VALUE)
#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE)
#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE)
#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE)
@@ -127,7 +129,6 @@ const char *scd_get_socket_name (void);
/*-- command.c --*/
gpg_error_t initialize_module_command (void);
int scd_command_handler (ctrl_t, int);
-void scd_clear_current_app (card_t card);
void send_status_info (ctrl_t ctrl, const char *keyword, ...)
GPGRT_ATTR_SENTINEL(1);
void send_status_direct (ctrl_t ctrl, const char *keyword, const char *args);