aboutsummaryrefslogtreecommitdiffstats
path: root/g10/apdu.c
diff options
context:
space:
mode:
Diffstat (limited to 'g10/apdu.c')
-rw-r--r--g10/apdu.c1568
1 files changed, 1042 insertions, 526 deletions
diff --git a/g10/apdu.c b/g10/apdu.c
index d29676b41..d114b6eca 100644
--- a/g10/apdu.c
+++ b/g10/apdu.c
@@ -1,5 +1,5 @@
/* apdu.c - ISO 7816 APDU functions and low level I/O
- * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004, 2008, 2009 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -31,9 +31,9 @@
#include <assert.h>
#include <signal.h>
#ifdef USE_GNU_PTH
-# include <pth.h>
# include <unistd.h>
# include <fcntl.h>
+# include <pth.h>
#endif
@@ -54,13 +54,14 @@
#include "memory.h"
#include "util.h"
#include "i18n.h"
+#include "dynload.h"
#include "cardglue.h"
#else /* GNUPG_MAJOR_VERSION != 1 */
#include "scdaemon.h"
+#include "exechelp.h"
#endif /* GNUPG_MAJOR_VERSION != 1 */
#include "apdu.h"
-#include "dynload.h"
#include "ccid-driver.h"
@@ -82,13 +83,8 @@
#define DLSTDCALL
#endif
-#ifdef _POSIX_OPEN_MAX
-#define MAX_OPEN_FDS _POSIX_OPEN_MAX
-#else
-#define MAX_OPEN_FDS 20
-#endif
-/* Helper to pass patrameters related to keypad based operations. */
+/* Helper to pass parameters related to keypad based operations. */
struct pininfo_s
{
int mode;
@@ -104,6 +100,8 @@ struct reader_table_s {
unsigned short port; /* Port number: 0 = unused, 1 - dev/tty */
/* Function pointers intialized to the various backends. */
+ int (*connect_card)(int);
+ int (*disconnect_card)(int);
int (*close_reader)(int);
int (*shutdown_reader)(int);
int (*reset_reader)(int);
@@ -112,6 +110,7 @@ struct reader_table_s {
unsigned char *, size_t *, struct pininfo_s *);
int (*check_keypad)(int, int, int, int, int, int);
void (*dump_status_reader)(int);
+ int (*set_progress_cb)(int, gcry_handler_progress_t, void*);
struct {
ccid_driver_t handle;
@@ -132,6 +131,7 @@ struct reader_table_s {
} rapdu;
#endif /*USE_G10CODE_RAPDU*/
char *rdrname; /* Name of the connected reader or NULL if unknown. */
+ int any_status; /* True if we have seen any status. */
int last_status;
int status;
int is_t0; /* True if we know that we are running T=0. */
@@ -220,6 +220,11 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn);
#define PCSC_E_READER_UNAVAILABLE 0x80100017
#define PCSC_W_REMOVED_CARD 0x80100069
+/* The PC/SC error is defined as a long as per specs. Due to left
+ shifts bit 31 will get sign extended. We use this mask to fix
+ it. */
+#define PCSC_ERR_MASK(a) ((a) & 0xffffffff)
+
struct pcsc_io_request_s
{
@@ -272,7 +277,8 @@ long (* DLSTDCALL pcsc_status) (unsigned long card,
unsigned long *r_protocol,
unsigned char *atr, unsigned long *atrlen);
long (* DLSTDCALL pcsc_begin_transaction) (unsigned long card);
-long (* DLSTDCALL pcsc_end_transaction) (unsigned long card);
+long (* DLSTDCALL pcsc_end_transaction) (unsigned long card,
+ unsigned long disposition);
long (* DLSTDCALL pcsc_transmit) (unsigned long card,
const pcsc_io_request_t send_pci,
const unsigned char *send_buffer,
@@ -286,6 +292,10 @@ long (* DLSTDCALL pcsc_set_timeout) (unsigned long context,
/* Prototypes. */
static int pcsc_get_status (int slot, unsigned int *status);
+static int reset_pcsc_reader (int slot);
+static int apdu_get_status_internal (int slot, int hang, int no_atr_reset,
+ unsigned int *status,
+ unsigned int *changed);
@@ -322,6 +332,8 @@ new_reader_slot (void)
reader_table[reader].lock_initialized = 1;
}
#endif /*USE_GNU_PTH*/
+ reader_table[reader].connect_card = NULL;
+ reader_table[reader].disconnect_card = NULL;
reader_table[reader].close_reader = NULL;
reader_table[reader].shutdown_reader = NULL;
reader_table[reader].reset_reader = NULL;
@@ -329,8 +341,10 @@ new_reader_slot (void)
reader_table[reader].send_apdu_reader = NULL;
reader_table[reader].check_keypad = NULL;
reader_table[reader].dump_status_reader = NULL;
+ reader_table[reader].set_progress_cb = NULL;
- reader_table[reader].used = 1;
+ reader_table[reader].used = 1;
+ reader_table[reader].any_status = 0;
reader_table[reader].last_status = 0;
reader_table[reader].is_t0 = 1;
#ifdef NEED_PCSC_WRAPPER
@@ -381,6 +395,7 @@ host_sw_string (long err)
case SW_HOST_NO_READER: return "no reader";
case SW_HOST_ABORTED: return "aborted";
case SW_HOST_NO_KEYPAD: return "no keypad";
+ case SW_HOST_ALREADY_CONNECTED: return "already connected";
default: return "unknown host status error";
}
}
@@ -402,6 +417,7 @@ apdu_strerror (int rc)
case SW_FILE_NOT_FOUND : return "file not found";
case SW_RECORD_NOT_FOUND:return "record not found";
case SW_REF_NOT_FOUND : return "reference not found";
+ case SW_BAD_LC : return "bad Lc";
case SW_BAD_P0_P1 : return "bad P0 or P1";
case SW_INS_NOT_SUP : return "instruction not supported";
case SW_CLA_NOT_SUP : return "class not supported";
@@ -531,10 +547,11 @@ reset_ct_reader (int slot)
static int
ct_get_status (int slot, unsigned int *status)
{
- *status = 1|2|4; /* FIXME */
+ (void)slot;
+ /* The status we returned is wrong but we don't care becuase ctAPI
+ is not anymore required. */
+ *status = APDU_CARD_USABLE|APDU_CARD_PRESENT|APDU_CARD_ACTIVE;
return 0;
-
- return SW_HOST_NOT_SUPPORTED;
}
/* Actually send the APDU of length APDULEN to SLOT and return a
@@ -548,6 +565,8 @@ ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
unsigned char dad[1], sad[1];
unsigned short ctbuflen;
+ (void)pininfo;
+
/* If we don't have an ATR, we need to reset the reader first. */
if (!reader_table[slot].atrlen
&& (rc = reset_ct_reader (slot)))
@@ -655,6 +674,9 @@ readn (int fd, void *buf, size_t buflen, size_t *nread)
while (nleft > 0)
{
#ifdef USE_GNU_PTH
+# ifdef HAVE_W32_SYSTEM
+# error Cannot use pth_read here because it expects a system HANDLE.
+# endif
n = pth_read (fd, buf, nleft);
#else
n = read (fd, buf, nleft);
@@ -736,7 +758,7 @@ pcsc_error_to_sw (long ec)
{
int rc;
- switch (ec)
+ switch ( PCSC_ERR_MASK (ec) )
{
case 0: rc = 0; break;
@@ -762,177 +784,87 @@ pcsc_error_to_sw (long ec)
static void
dump_pcsc_reader_status (int slot)
{
- log_info ("reader slot %d: active protocol:", slot);
- if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T0))
- log_printf (" T0");
- else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
- log_printf (" T1");
- else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_RAW))
- log_printf (" raw");
- log_printf ("\n");
+ if (reader_table[slot].pcsc.card)
+ {
+ log_info ("reader slot %d: active protocol:", slot);
+ if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T0))
+ log_printf (" T0");
+ else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
+ log_printf (" T1");
+ else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_RAW))
+ log_printf (" raw");
+ log_printf ("\n");
+ }
+ else
+ log_info ("reader slot %d: not connected\n", slot);
}
-/* Send an PC/SC reset command and return a status word on error or 0
- on success. */
+#ifndef NEED_PCSC_WRAPPER
static int
-reset_pcsc_reader (int slot)
+pcsc_get_status_direct (int slot, unsigned int *status)
{
-#ifdef NEED_PCSC_WRAPPER
long err;
- reader_table_t slotp;
- size_t len;
- int i, n;
- unsigned char msgbuf[9];
- unsigned int dummy_status;
- int sw = SW_HOST_CARD_IO_ERROR;
-
- slotp = reader_table + slot;
-
- if (slotp->pcsc.req_fd == -1
- || slotp->pcsc.rsp_fd == -1
- || slotp->pcsc.pid == (pid_t)(-1) )
- {
- log_error ("pcsc_get_status: pcsc-wrapper not running\n");
- return sw;
- }
-
- msgbuf[0] = 0x05; /* RESET command. */
- len = 0;
- msgbuf[1] = (len >> 24);
- msgbuf[2] = (len >> 16);
- msgbuf[3] = (len >> 8);
- msgbuf[4] = (len );
- if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
- {
- log_error ("error sending PC/SC RESET request: %s\n",
- strerror (errno));
- goto command_failed;
- }
+ struct pcsc_readerstate_s rdrstates[1];
- /* Read the response. */
- if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
- {
- log_error ("error receiving PC/SC RESET response: %s\n",
- i? strerror (errno) : "premature EOF");
- goto command_failed;
- }
- len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
- if (msgbuf[0] != 0x81 || len < 4)
- {
- log_error ("invalid response header from PC/SC received\n");
- goto command_failed;
- }
- len -= 4; /* Already read the error code. */
- if (len > DIM (slotp->atr))
- {
- log_error ("PC/SC returned a too large ATR (len=%lx)\n",
- (unsigned long)len);
- sw = SW_HOST_GENERAL_ERROR;
- goto command_failed;
- }
- err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+ memset (rdrstates, 0, sizeof *rdrstates);
+ rdrstates[0].reader = reader_table[slot].rdrname;
+ rdrstates[0].current_state = PCSC_STATE_UNAWARE;
+ err = pcsc_get_status_change (reader_table[slot].pcsc.context,
+ 0,
+ rdrstates, 1);
+ if (err == PCSC_E_TIMEOUT)
+ err = 0; /* Timeout is no error error here. */
if (err)
{
- log_error ("PC/SC RESET failed: %s (0x%lx)\n",
+ log_error ("pcsc_get_status_change failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
- /* If the error code is no smart card, we should not considere
- this a major error and close the wrapper. */
- sw = pcsc_error_to_sw (err);
- if (err == PCSC_E_NO_SMARTCARD)
- return sw;
- goto command_failed;
- }
-
- /* The open function may return a zero for the ATR length to
- indicate that no card is present. */
- n = len;
- if (n)
- {
- if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
- {
- log_error ("error receiving PC/SC RESET response: %s\n",
- i? strerror (errno) : "premature EOF");
- goto command_failed;
- }
- }
- slotp->atrlen = len;
-
- /* Read the status so that IS_T0 will be set. */
- pcsc_get_status (slot, &dummy_status);
-
- return 0;
-
- command_failed:
- close (slotp->pcsc.req_fd);
- close (slotp->pcsc.rsp_fd);
- slotp->pcsc.req_fd = -1;
- slotp->pcsc.rsp_fd = -1;
- kill (slotp->pcsc.pid, SIGTERM);
- slotp->pcsc.pid = (pid_t)(-1);
- slotp->used = 0;
- return sw;
-
-#else /* !NEED_PCSC_WRAPPER */
- long err;
- char reader[250];
- unsigned long nreader, atrlen;
- unsigned long card_state, card_protocol;
-
- if (reader_table[slot].pcsc.card)
- {
- err = pcsc_disconnect (reader_table[slot].pcsc.card, PCSC_LEAVE_CARD);
- if (err)
- {
- log_error ("pcsc_disconnect failed: %s (0x%lx)\n",
- pcsc_error_string (err), err);
- return SW_HOST_CARD_IO_ERROR;
- }
- reader_table[slot].pcsc.card = 0;
- }
-
- err = pcsc_connect (reader_table[slot].pcsc.context,
- reader_table[slot].rdrname,
- PCSC_SHARE_EXCLUSIVE,
- PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
- &reader_table[slot].pcsc.card,
- &reader_table[slot].pcsc.protocol);
- if (err)
- {
- log_error ("pcsc_connect failed: %s (0x%lx)\n",
- pcsc_error_string (err), err);
- reader_table[slot].pcsc.card = 0;
return pcsc_error_to_sw (err);
}
+ /* log_debug */
+ /* ("pcsc_get_status_change: %s%s%s%s%s%s%s%s%s%s\n", */
+ /* (rdrstates[0].event_state & PCSC_STATE_IGNORE)? " ignore":"", */
+ /* (rdrstates[0].event_state & PCSC_STATE_CHANGED)? " changed":"", */
+ /* (rdrstates[0].event_state & PCSC_STATE_UNKNOWN)? " unknown":"", */
+ /* (rdrstates[0].event_state & PCSC_STATE_UNAVAILABLE)?" unavail":"", */
+ /* (rdrstates[0].event_state & PCSC_STATE_EMPTY)? " empty":"", */
+ /* (rdrstates[0].event_state & PCSC_STATE_PRESENT)? " present":"", */
+ /* (rdrstates[0].event_state & PCSC_STATE_ATRMATCH)? " atr":"", */
+ /* (rdrstates[0].event_state & PCSC_STATE_EXCLUSIVE)? " excl":"", */
+ /* (rdrstates[0].event_state & PCSC_STATE_INUSE)? " unuse":"", */
+ /* (rdrstates[0].event_state & PCSC_STATE_MUTE)? " mute":"" ); */
- atrlen = 33;
- nreader = sizeof reader - 1;
- err = pcsc_status (reader_table[slot].pcsc.card,
- reader, &nreader,
- &card_state, &card_protocol,
- reader_table[slot].atr, &atrlen);
- if (err)
- {
- log_error ("pcsc_status failed: %s (0x%lx)\n",
- pcsc_error_string (err), err);
- reader_table[slot].atrlen = 0;
- return pcsc_error_to_sw (err);
- }
- if (atrlen >= DIM (reader_table[0].atr))
- log_bug ("ATR returned by pcsc_status is too large\n");
- reader_table[slot].atrlen = atrlen;
- reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
+ *status = 0;
+ if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
+ *status |= APDU_CARD_PRESENT;
+ if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
+ *status |= APDU_CARD_ACTIVE;
+#ifndef HAVE_W32_SYSTEM
+ /* We indicate a useful card if it is not in use by another
+ application. This is because we only use exclusive access
+ mode. */
+ if ( (*status & (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
+ == (APDU_CARD_PRESENT|APDU_CARD_ACTIVE)
+ && !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
+ *status |= APDU_CARD_USABLE;
+#else
+ /* Some winscard drivers may set EXCLUSIVE and INUSE at the same
+ time when we are the only user (SCM SCR335) under Windows. */
+ if ((*status & (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
+ == (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
+ *status |= APDU_CARD_USABLE;
+#endif
return 0;
-#endif /* !NEED_PCSC_WRAPPER */
}
+#endif /*!NEED_PCSC_WRAPPER*/
+#ifdef NEED_PCSC_WRAPPER
static int
-pcsc_get_status (int slot, unsigned int *status)
+pcsc_get_status_wrapped (int slot, unsigned int *status)
{
-#ifdef NEED_PCSC_WRAPPER
long err;
reader_table_t slotp;
size_t len, full_len;
@@ -978,7 +910,8 @@ pcsc_get_status (int slot, unsigned int *status)
goto command_failed;
}
len -= 4; /* Already read the error code. */
- err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+ err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
+ | (msgbuf[7] << 8 ) | msgbuf[8]);
if (err)
{
log_error ("pcsc_status failed: %s (0x%lx)\n",
@@ -1023,7 +956,6 @@ pcsc_get_status (int slot, unsigned int *status)
/* We are lucky: The wrapper already returns the data in the
required format. */
*status = buffer[3];
-
return 0;
command_failed:
@@ -1035,67 +967,63 @@ pcsc_get_status (int slot, unsigned int *status)
slotp->pcsc.pid = (pid_t)(-1);
slotp->used = 0;
return sw;
+}
+#endif /*NEED_PCSC_WRAPPER*/
-#else /*!NEED_PCSC_WRAPPER*/
- long err;
- struct pcsc_readerstate_s rdrstates[1];
+static int
+pcsc_get_status (int slot, unsigned int *status)
+{
+#ifdef NEED_PCSC_WRAPPER
+ return pcsc_get_status_wrapped (slot, status);
+#else
+ return pcsc_get_status_direct (slot, status);
+#endif
+}
- memset (rdrstates, 0, sizeof *rdrstates);
- rdrstates[0].reader = reader_table[slot].rdrname;
- rdrstates[0].current_state = PCSC_STATE_UNAWARE;
- err = pcsc_get_status_change (reader_table[slot].pcsc.context,
- 0,
- rdrstates, 1);
- if (err == PCSC_E_TIMEOUT)
- err = 0; /* Timeout is no error error here. */
- if (err)
- {
- log_error ("pcsc_get_status_change failed: %s (0x%lx)\n",
- pcsc_error_string (err), err);
- return pcsc_error_to_sw (err);
- }
+#ifndef NEED_PCSC_WRAPPER
+static int
+pcsc_send_apdu_direct (int slot, unsigned char *apdu, size_t apdulen,
+ unsigned char *buffer, size_t *buflen,
+ struct pininfo_s *pininfo)
+{
+ long err;
+ struct pcsc_io_request_s send_pci;
+ unsigned long recv_len;
- /* log_debug */
- /* ("pcsc_get_status_change: %s%s%s%s%s%s%s%s%s%s\n", */
- /* (rdrstates[0].event_state & PCSC_STATE_IGNORE)? " ignore":"", */
- /* (rdrstates[0].event_state & PCSC_STATE_CHANGED)? " changed":"", */
- /* (rdrstates[0].event_state & PCSC_STATE_UNKNOWN)? " unknown":"", */
- /* (rdrstates[0].event_state & PCSC_STATE_UNAVAILABLE)?" unavail":"", */
- /* (rdrstates[0].event_state & PCSC_STATE_EMPTY)? " empty":"", */
- /* (rdrstates[0].event_state & PCSC_STATE_PRESENT)? " present":"", */
- /* (rdrstates[0].event_state & PCSC_STATE_ATRMATCH)? " atr":"", */
- /* (rdrstates[0].event_state & PCSC_STATE_EXCLUSIVE)? " excl":"", */
- /* (rdrstates[0].event_state & PCSC_STATE_INUSE)? " unuse":"", */
- /* (rdrstates[0].event_state & PCSC_STATE_MUTE)? " mute":"" ); */
+ if (!reader_table[slot].atrlen
+ && (err = reset_pcsc_reader (slot)))
+ return err;
- *status = 0;
- if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
- *status |= 2;
- if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
- *status |= 4;
- /* We indicate a useful card if it is not in use by another
- application. This is because we only use exclusive access
- mode. */
- if ( (*status & 6) == 6
- && !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
- *status |= 1;
+ if (DBG_CARD_IO)
+ log_printhex (" PCSC_data:", apdu, apdulen);
- return 0;
-#endif /*!NEED_PCSC_WRAPPER*/
+ if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
+ send_pci.protocol = PCSC_PROTOCOL_T1;
+ else
+ send_pci.protocol = PCSC_PROTOCOL_T0;
+ send_pci.pci_len = sizeof send_pci;
+ recv_len = *buflen;
+ err = pcsc_transmit (reader_table[slot].pcsc.card,
+ &send_pci, apdu, apdulen,
+ NULL, buffer, &recv_len);
+ *buflen = recv_len;
+ if (err)
+ log_error ("pcsc_transmit failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+
+ return pcsc_error_to_sw (err);
}
+#endif /*!NEED_PCSC_WRAPPER*/
-/* Actually send the APDU of length APDULEN to SLOT and return a
- maximum of *BUFLEN data in BUFFER, the actual returned size will be
- set to BUFLEN. Returns: CT API error code. */
+#ifdef NEED_PCSC_WRAPPER
static int
-pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
- unsigned char *buffer, size_t *buflen,
- struct pininfo_s *pininfo)
+pcsc_send_apdu_wrapped (int slot, unsigned char *apdu, size_t apdulen,
+ unsigned char *buffer, size_t *buflen,
+ struct pininfo_s *pininfo)
{
-#ifdef NEED_PCSC_WRAPPER
long err;
reader_table_t slotp;
size_t len, full_len;
@@ -1103,6 +1031,8 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
unsigned char msgbuf[9];
int sw = SW_HOST_CARD_IO_ERROR;
+ (void)pininfo;
+
if (!reader_table[slot].atrlen
&& (err = reset_pcsc_reader (slot)))
return err;
@@ -1148,7 +1078,8 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
goto command_failed;
}
len -= 4; /* Already read the error code. */
- err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+ err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
+ | (msgbuf[7] << 8 ) | msgbuf[8]);
if (err)
{
log_error ("pcsc_transmit failed: %s (0x%lx)\n",
@@ -1174,7 +1105,7 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
err = SW_HOST_INV_VALUE;
}
/* We need to read any rest of the response, to keep the
- protocol runnng. */
+ protocol running. */
while (full_len)
{
unsigned char dummybuf[128];
@@ -1200,43 +1131,43 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
slotp->pcsc.pid = (pid_t)(-1);
slotp->used = 0;
return sw;
+}
+#endif /*NEED_PCSC_WRAPPER*/
-#else /*!NEED_PCSC_WRAPPER*/
-
- long err;
- struct pcsc_io_request_s send_pci;
- unsigned long recv_len;
-
- if (!reader_table[slot].atrlen
- && (err = reset_pcsc_reader (slot)))
- return err;
- if (DBG_CARD_IO)
- log_printhex (" PCSC_data:", apdu, apdulen);
+/* Send the APDU of length APDULEN to SLOT and return a maximum of
+ *BUFLEN data in BUFFER, the actual returned size will be stored at
+ BUFLEN. Returns: A status word. */
+static int
+pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
+ unsigned char *buffer, size_t *buflen,
+ struct pininfo_s *pininfo)
+{
+#ifdef NEED_PCSC_WRAPPER
+ return pcsc_send_apdu_wrapped (slot, apdu, apdulen, buffer, buflen, pininfo);
+#else
+ return pcsc_send_apdu_direct (slot, apdu, apdulen, buffer, buflen, pininfo);
+#endif
+}
- if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
- send_pci.protocol = PCSC_PROTOCOL_T1;
- else
- send_pci.protocol = PCSC_PROTOCOL_T0;
- send_pci.pci_len = sizeof send_pci;
- recv_len = *buflen;
- err = pcsc_transmit (reader_table[slot].pcsc.card,
- &send_pci, apdu, apdulen,
- NULL, buffer, &recv_len);
- *buflen = recv_len;
- if (err)
- log_error ("pcsc_transmit failed: %s (0x%lx)\n",
- pcsc_error_string (err), err);
- return pcsc_error_to_sw (err);
-#endif /*!NEED_PCSC_WRAPPER*/
+#ifndef NEED_PCSC_WRAPPER
+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;
+ reader_table[slot].used = 0;
+ return 0;
}
+#endif /*!NEED_PCSC_WRAPPER*/
+#ifdef NEED_PCSC_WRAPPER
static int
-close_pcsc_reader (int slot)
+close_pcsc_reader_wrapped (int slot)
{
-#ifdef NEED_PCSC_WRAPPER
long err;
reader_table_t slotp;
size_t len;
@@ -1280,7 +1211,8 @@ close_pcsc_reader (int slot)
goto command_failed;
}
len -= 4; /* Already read the error code. */
- err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+ err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
+ | (msgbuf[7] << 8 ) | msgbuf[8]);
if (err)
log_error ("pcsc_close failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
@@ -1297,25 +1229,348 @@ close_pcsc_reader (int slot)
slotp->pcsc.pid = (pid_t)(-1);
slotp->used = 0;
return 0;
+}
+#endif /*NEED_PCSC_WRAPPER*/
-#else /*!NEED_PCSC_WRAPPER*/
- pcsc_release_context (reader_table[slot].pcsc.context);
- xfree (reader_table[slot].rdrname);
- reader_table[slot].rdrname = NULL;
- reader_table[slot].used = 0;
+static int
+close_pcsc_reader (int slot)
+{
+#ifdef NEED_PCSC_WRAPPER
+ return close_pcsc_reader_wrapped (slot);
+#else
+ return close_pcsc_reader_direct (slot);
+#endif
+}
+
+
+/* Connect a PC/SC card. */
+#ifndef NEED_PCSC_WRAPPER
+static int
+connect_pcsc_card (int slot)
+{
+ long err;
+
+ assert (slot >= 0 && slot < MAX_READER);
+
+ if (reader_table[slot].pcsc.card)
+ return SW_HOST_ALREADY_CONNECTED;
+
+ reader_table[slot].atrlen = 0;
+ reader_table[slot].last_status = 0;
+ reader_table[slot].is_t0 = 0;
+
+ err = pcsc_connect (reader_table[slot].pcsc.context,
+ reader_table[slot].rdrname,
+ PCSC_SHARE_EXCLUSIVE,
+ PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
+ &reader_table[slot].pcsc.card,
+ &reader_table[slot].pcsc.protocol);
+ if (err)
+ {
+ reader_table[slot].pcsc.card = 0;
+ if (err != PCSC_E_NO_SMARTCARD)
+ log_error ("pcsc_connect failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ }
+ else
+ {
+ char reader[250];
+ unsigned long readerlen, atrlen;
+ unsigned long card_state, card_protocol;
+
+ atrlen = DIM (reader_table[0].atr);
+ readerlen = sizeof reader -1 ;
+ err = pcsc_status (reader_table[slot].pcsc.card,
+ reader, &readerlen,
+ &card_state, &card_protocol,
+ reader_table[slot].atr, &atrlen);
+ if (err)
+ log_error ("pcsc_status failed: %s (0x%lx) %lu\n",
+ pcsc_error_string (err), err, readerlen);
+ else
+ {
+ if (atrlen > DIM (reader_table[0].atr))
+ log_bug ("ATR returned by pcsc_status is too large\n");
+ reader_table[slot].atrlen = atrlen;
+ /* If we got to here we know that a card is present
+ and usable. Remember this. */
+ reader_table[slot].last_status = ( APDU_CARD_USABLE
+ | APDU_CARD_PRESENT
+ | APDU_CARD_ACTIVE);
+ reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
+ }
+ }
+
+ dump_reader_status (slot);
+ return pcsc_error_to_sw (err);
+}
+#endif /*!NEED_PCSC_WRAPPER*/
+
+
+/* Disconnect a PC/SC card. Note that this succeeds even if the card
+ is not connected. */
+#ifndef NEED_PCSC_WRAPPER
+static int
+disconnect_pcsc_card (int slot)
+{
+ long err;
+
+ assert (slot >= 0 && slot < MAX_READER);
+
+ if (!reader_table[slot].pcsc.card)
+ return 0;
+
+ err = pcsc_disconnect (reader_table[slot].pcsc.card, PCSC_LEAVE_CARD);
+ if (err)
+ {
+ log_error ("pcsc_disconnect failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ return SW_HOST_CARD_IO_ERROR;
+ }
+ reader_table[slot].pcsc.card = 0;
return 0;
+}
#endif /*!NEED_PCSC_WRAPPER*/
+
+
+#ifndef NEED_PCSC_WRAPPER
+static int
+reset_pcsc_reader_direct (int slot)
+{
+ int sw;
+
+ sw = disconnect_pcsc_card (slot);
+ if (!sw)
+ sw = connect_pcsc_card (slot);
+
+ return sw;
}
+#endif /*NEED_PCSC_WRAPPER*/
-/* Note: It is a pitty that we can't return proper error codes. */
+
+#ifdef NEED_PCSC_WRAPPER
static int
-open_pcsc_reader (const char *portstr)
+reset_pcsc_reader_wrapped (int slot)
+{
+ long err;
+ reader_table_t slotp;
+ size_t len;
+ int i, n;
+ unsigned char msgbuf[9];
+ unsigned int dummy_status;
+ int sw = SW_HOST_CARD_IO_ERROR;
+
+ slotp = reader_table + slot;
+
+ if (slotp->pcsc.req_fd == -1
+ || slotp->pcsc.rsp_fd == -1
+ || slotp->pcsc.pid == (pid_t)(-1) )
+ {
+ log_error ("pcsc_get_status: pcsc-wrapper not running\n");
+ return sw;
+ }
+
+ msgbuf[0] = 0x05; /* RESET command. */
+ len = 0;
+ msgbuf[1] = (len >> 24);
+ msgbuf[2] = (len >> 16);
+ msgbuf[3] = (len >> 8);
+ msgbuf[4] = (len );
+ if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
+ {
+ log_error ("error sending PC/SC RESET request: %s\n",
+ strerror (errno));
+ goto command_failed;
+ }
+
+ /* Read the response. */
+ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
+ {
+ log_error ("error receiving PC/SC RESET response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
+ if (msgbuf[0] != 0x81 || len < 4)
+ {
+ log_error ("invalid response header from PC/SC received\n");
+ goto command_failed;
+ }
+ len -= 4; /* Already read the error code. */
+ if (len > DIM (slotp->atr))
+ {
+ log_error ("PC/SC returned a too large ATR (len=%lx)\n",
+ (unsigned long)len);
+ sw = SW_HOST_GENERAL_ERROR;
+ goto command_failed;
+ }
+ err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
+ | (msgbuf[7] << 8 ) | msgbuf[8]);
+ if (err)
+ {
+ log_error ("PC/SC RESET failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ /* If the error code is no smart card, we should not considere
+ this a major error and close the wrapper. */
+ sw = pcsc_error_to_sw (err);
+ if (err == PCSC_E_NO_SMARTCARD)
+ return sw;
+ goto command_failed;
+ }
+
+ /* The open function may return a zero for the ATR length to
+ indicate that no card is present. */
+ n = len;
+ if (n)
+ {
+ if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
+ {
+ log_error ("error receiving PC/SC RESET response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ }
+ slotp->atrlen = len;
+
+ /* Read the status so that IS_T0 will be set. */
+ pcsc_get_status (slot, &dummy_status);
+
+ return 0;
+
+ command_failed:
+ close (slotp->pcsc.req_fd);
+ close (slotp->pcsc.rsp_fd);
+ slotp->pcsc.req_fd = -1;
+ slotp->pcsc.rsp_fd = -1;
+ kill (slotp->pcsc.pid, SIGTERM);
+ slotp->pcsc.pid = (pid_t)(-1);
+ slotp->used = 0;
+ return sw;
+}
+#endif /* !NEED_PCSC_WRAPPER */
+
+
+/* Send an PC/SC reset command and return a status word on error or 0
+ on success. */
+static int
+reset_pcsc_reader (int slot)
{
#ifdef NEED_PCSC_WRAPPER
+ return reset_pcsc_reader_wrapped (slot);
+#else
+ return reset_pcsc_reader_direct (slot);
+#endif
+}
+
+
+/* Open the PC/SC reader without using the wrapper. Returns -1 on
+ error or a slot number for the reader. */
+#ifndef NEED_PCSC_WRAPPER
+static int
+open_pcsc_reader_direct (const char *portstr)
+{
+ long err;
+ int slot;
+ char *list = NULL;
+ unsigned long nreader, listlen;
+ 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)
+ {
+ log_error ("pcsc_establish_context failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ reader_table[slot].used = 0;
+ return -1;
+ }
+
+ 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)
+ {
+ log_error ("error allocating memory for reader list\n");
+ pcsc_release_context (reader_table[slot].pcsc.context);
+ reader_table[slot].used = 0;
+ return -1 /*SW_HOST_OUT_OF_CORE*/;
+ }
+ err = pcsc_list_readers (reader_table[slot].pcsc.context,
+ NULL, list, &nreader);
+ }
+ if (err)
+ {
+ log_error ("pcsc_list_readers 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);
+ return -1;
+ }
+
+ listlen = nreader;
+ p = list;
+ while (nreader)
+ {
+ if (!*p && !p[1])
+ break;
+ if (*p)
+ log_info ("detected reader `%s'\n", p);
+ if (nreader < (strlen (p)+1))
+ {
+ log_error ("invalid response from pcsc_list_readers\n");
+ break;
+ }
+ nreader -= strlen (p)+1;
+ p += strlen (p) + 1;
+ }
+
+ reader_table[slot].rdrname = xtrymalloc (strlen (portstr? portstr : list)+1);
+ if (!reader_table[slot].rdrname)
+ {
+ log_error ("error allocating memory for reader name\n");
+ pcsc_release_context (reader_table[slot].pcsc.context);
+ reader_table[slot].used = 0;
+ return -1;
+ }
+ strcpy (reader_table[slot].rdrname, portstr? portstr : list);
+ xfree (list);
+ list = NULL;
+
+ reader_table[slot].pcsc.card = 0;
+ reader_table[slot].atrlen = 0;
+ reader_table[slot].last_status = 0;
+
+ reader_table[slot].connect_card = connect_pcsc_card;
+ reader_table[slot].disconnect_card = disconnect_pcsc_card;
+ reader_table[slot].close_reader = close_pcsc_reader;
+ reader_table[slot].reset_reader = reset_pcsc_reader;
+ reader_table[slot].get_status_reader = pcsc_get_status;
+ reader_table[slot].send_apdu_reader = pcsc_send_apdu;
+ reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
+
+ dump_reader_status (slot);
+ return slot;
+}
+#endif /*!NEED_PCSC_WRAPPER */
+
+
/* Open the PC/SC reader using the pcsc_wrapper program. This is
needed to cope with different thread models and other peculiarities
of libpcsclite. */
+#ifdef NEED_PCSC_WRAPPER
+static int
+open_pcsc_reader_wrapped (const char *portstr)
+{
int slot;
reader_table_t slotp;
int fd, rp[2], wp[2];
@@ -1326,14 +1581,24 @@ open_pcsc_reader (const char *portstr)
int err;
unsigned int dummy_status;
int sw = SW_HOST_CARD_IO_ERROR;
+ /* Note that we use the constant and not the fucntion because this
+ code won't be be used under Windows. */
+ const char *wrapperpgm = GNUPG_LIBEXECDIR "/gnupg-pcsc-wrapper";
+
+ if (access (wrapperpgm, X_OK))
+ {
+ log_error ("can't run PC/SC access module `%s': %s\n",
+ wrapperpgm, strerror (errno));
+ return -1;
+ }
slot = new_reader_slot ();
if (slot == -1)
return -1;
slotp = reader_table + slot;
- /* Fire up the pcsc wrapper. We don't use any fork/exec code from
- the common directy but implement it direclty so that this file
+ /* Fire up the PC/SCc wrapper. We don't use any fork/exec code from
+ the common directy but implement it directly so that this file
may still be source copied. */
if (pipe (rp) == -1)
@@ -1391,14 +1656,9 @@ open_pcsc_reader (const char *portstr)
log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
/* Close all other files. */
- n = sysconf (_SC_OPEN_MAX);
- if (n < 0)
- n = MAX_OPEN_FDS;
- for (i=3; i < n; i++)
- close(i);
- errno = 0;
+ close_all_fds (3, NULL);
- execl (GNUPG_LIBDIR "/pcsc-wrapper",
+ execl (wrapperpgm,
"pcsc-wrapper",
"--",
"1", /* API version */
@@ -1423,7 +1683,7 @@ open_pcsc_reader (const char *portstr)
#endif
while ( (i=WAIT (pid, NULL, 0)) == -1 && errno == EINTR)
;
-#undef X
+#undef WAIT
/* Now send the open request. */
msgbuf[0] = 0x01; /* OPEN command. */
@@ -1459,7 +1719,8 @@ open_pcsc_reader (const char *portstr)
(unsigned long)len);
goto command_failed;
}
- err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+ err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
+ | (msgbuf[7] << 8 ) | msgbuf[8]);
if (err)
{
log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err));
@@ -1482,7 +1743,9 @@ open_pcsc_reader (const char *portstr)
}
/* If we got to here we know that a card is present
and usable. Thus remember this. */
- slotp->last_status = (1|2|4| 0x8000);
+ slotp->last_status = ( APDU_CARD_USABLE
+ | APDU_CARD_PRESENT
+ | APDU_CARD_ACTIVE);
}
slotp->atrlen = len;
@@ -1490,7 +1753,6 @@ open_pcsc_reader (const char *portstr)
reader_table[slot].reset_reader = reset_pcsc_reader;
reader_table[slot].get_status_reader = pcsc_get_status;
reader_table[slot].send_apdu_reader = pcsc_send_apdu;
- reader_table[slot].check_keypad = NULL;
reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
/* Read the status so that IS_T0 will be set. */
@@ -1510,146 +1772,21 @@ open_pcsc_reader (const char *portstr)
/* There is no way to return SW. */
return -1;
-#else /*!NEED_PCSC_WRAPPER */
- long err;
- int slot;
- char *list = NULL;
- unsigned long nreader, listlen, atrlen;
- char *p;
- unsigned long card_state, card_protocol;
-
- slot = new_reader_slot ();
- if (slot == -1)
- return -1;
-
- err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL,
- &reader_table[slot].pcsc.context);
- if (err)
- {
- log_error ("pcsc_establish_context failed: %s (0x%lx)\n",
- pcsc_error_string (err), err);
- reader_table[slot].used = 0;
- return -1;
- }
-
- 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)
- {
- log_error ("error allocating memory for reader list\n");
- pcsc_release_context (reader_table[slot].pcsc.context);
- reader_table[slot].used = 0;
- return -1 /*SW_HOST_OUT_OF_CORE*/;
- }
- err = pcsc_list_readers (reader_table[slot].pcsc.context,
- NULL, list, &nreader);
- }
- if (err)
- {
- log_error ("pcsc_list_readers 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);
- return -1 /*pcsc_error_to_sw (err)*/;
- }
-
- listlen = nreader;
- p = list;
- while (nreader)
- {
- if (!*p && !p[1])
- break;
- if (*p)
- log_info ("detected reader `%s'\n", p);
- if (nreader < (strlen (p)+1))
- {
- log_error ("invalid response from pcsc_list_readers\n");
- break;
- }
- nreader -= strlen (p)+1;
- p += strlen (p) + 1;
- }
-
- reader_table[slot].rdrname = xtrymalloc (strlen (portstr? portstr : list)+1);
- if (!reader_table[slot].rdrname)
- {
- log_error ("error allocating memory for reader name\n");
- pcsc_release_context (reader_table[slot].pcsc.context);
- reader_table[slot].used = 0;
- return -1 /*SW_HOST_OUT_OF_CORE*/;
- }
- strcpy (reader_table[slot].rdrname, portstr? portstr : list);
- xfree (list);
- list = NULL;
-
- err = pcsc_connect (reader_table[slot].pcsc.context,
- reader_table[slot].rdrname,
- PCSC_SHARE_EXCLUSIVE,
- PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
- &reader_table[slot].pcsc.card,
- &reader_table[slot].pcsc.protocol);
- if (err == PCSC_E_NO_SMARTCARD)
- reader_table[slot].pcsc.card = 0;
- else if (err)
- {
- log_error ("pcsc_connect failed: %s (0x%lx)\n",
- pcsc_error_string (err), err);
- pcsc_release_context (reader_table[slot].pcsc.context);
- xfree (reader_table[slot].rdrname);
- reader_table[slot].rdrname = NULL;
- reader_table[slot].used = 0;
- return -1 /*pcsc_error_to_sw (err)*/;
- }
-
- reader_table[slot].atrlen = 0;
- reader_table[slot].last_status = 0;
- if (!err)
- {
- char reader[250];
- unsigned long readerlen;
-
- atrlen = 32;
- readerlen = sizeof reader -1 ;
- err = pcsc_status (reader_table[slot].pcsc.card,
- reader, &readerlen,
- &card_state, &card_protocol,
- reader_table[slot].atr, &atrlen);
- if (err)
- log_error ("pcsc_status failed: %s (0x%lx) %lu\n",
- pcsc_error_string (err), err, readerlen);
- else
- {
- if (atrlen >= DIM (reader_table[0].atr))
- log_bug ("ATR returned by pcsc_status is too large\n");
- reader_table[slot].atrlen = atrlen;
- /* If we got to here we know that a card is present
- and usable. Thus remember this. */
- reader_table[slot].last_status = (1|2|4| 0x8000);
- reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
- }
- }
-
- reader_table[slot].close_reader = close_pcsc_reader;
- reader_table[slot].reset_reader = reset_pcsc_reader;
- reader_table[slot].get_status_reader = pcsc_get_status;
- reader_table[slot].send_apdu_reader = pcsc_send_apdu;
- reader_table[slot].check_keypad = NULL;
- reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
+}
+#endif /*NEED_PCSC_WRAPPER*/
-/* log_debug ("state from pcsc_status: 0x%lx\n", card_state); */
-/* log_debug ("protocol from pcsc_status: 0x%lx\n", card_protocol); */
- dump_reader_status (slot);
- return slot;
-#endif /*!NEED_PCSC_WRAPPER */
+static int
+open_pcsc_reader (const char *portstr)
+{
+#ifdef NEED_PCSC_WRAPPER
+ return open_pcsc_reader_wrapped (portstr);
+#else
+ return open_pcsc_reader_direct (portstr);
+#endif
}
-
#ifdef HAVE_LIBUSB
/*
@@ -1701,6 +1838,15 @@ reset_ccid_reader (int slot)
static int
+set_progress_cb_ccid_reader (int slot, gcry_handler_progress_t cb, void *cb_arg)
+{
+ reader_table_t slotp = reader_table + slot;
+
+ return ccid_set_progress_cb (slotp->ccid.handle, cb, cb_arg);
+}
+
+
+static int
get_status_ccid (int slot, unsigned int *status)
{
int rc;
@@ -1708,12 +1854,12 @@ get_status_ccid (int slot, unsigned int *status)
rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits);
if (rc)
- return -1;
+ return rc;
if (bits == 0)
- *status = 1|2|4;
+ *status = (APDU_CARD_USABLE|APDU_CARD_PRESENT|APDU_CARD_ACTIVE);
else if (bits == 1)
- *status = 2;
+ *status = APDU_CARD_PRESENT;
else
*status = 0;
@@ -1738,7 +1884,7 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
return err;
if (DBG_CARD_IO)
- log_printhex (" APDU_data:", apdu, apdulen);
+ log_printhex (" raw apdu:", apdu, apdulen);
maxbuflen = *buflen;
if (pininfo)
@@ -1809,7 +1955,9 @@ open_ccid_reader (const char *portstr)
{
/* If we got to here we know that a card is present
and usable. Thus remember this. */
- reader_table[slot].last_status = (1|2|4| 0x8000);
+ reader_table[slot].last_status = (APDU_CARD_USABLE
+ | APDU_CARD_PRESENT
+ | APDU_CARD_ACTIVE);
}
reader_table[slot].close_reader = close_ccid_reader;
@@ -1819,6 +1967,10 @@ open_ccid_reader (const char *portstr)
reader_table[slot].send_apdu_reader = send_apdu_ccid;
reader_table[slot].check_keypad = check_ccid_keypad;
reader_table[slot].dump_status_reader = dump_ccid_reader_status;
+ reader_table[slot].set_progress_cb = set_progress_cb_ccid_reader;
+ /* Our CCID reader code does not support T=0 at all, thus reset the
+ flag. */
+ reader_table[slot].is_t0 = 0;
dump_reader_status (slot);
return slot;
@@ -1913,7 +2065,7 @@ reset_rapdu_reader (int slot)
rapdu_msg_release (msg);
return sw;
}
- if (msg->datalen >= DIM (slotp->atr))
+ if (msg->datalen > DIM (slotp->atr))
{
log_error ("ATR returned by the RAPDU layer is too large\n");
rapdu_msg_release (msg);
@@ -2094,7 +2246,7 @@ open_rapdu_reader (int portno,
rapdu_strerror (msg->cmd));
goto failure;
}
- if (msg->datalen >= DIM (slotp->atr))
+ if (msg->datalen > DIM (slotp->atr))
{
log_error ("ATR returned by the RAPDU layer is too large\n");
goto failure;
@@ -2338,6 +2490,15 @@ apdu_open_remote_reader (const char *portstr,
writefnc, writefnc_value,
closefnc, closefnc_value);
#else
+ (void)portstr;
+ (void)cookie;
+ (void)length;
+ (void)readfnc;
+ (void)readfnc_value;
+ (void)writefnc;
+ (void)writefnc_value;
+ (void)closefnc;
+ (void)closefnc_value;
#ifdef _WIN32
errno = ENOENT;
#else
@@ -2351,21 +2512,58 @@ apdu_open_remote_reader (const char *portstr,
int
apdu_close_reader (int slot)
{
+ int sw;
+
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
+ sw = apdu_disconnect (slot);
+ if (sw)
+ return sw;
if (reader_table[slot].close_reader)
return reader_table[slot].close_reader (slot);
return SW_HOST_NOT_SUPPORTED;
}
+
+/* Function suitable for a cleanup function to close all reader. It
+ should not be used if the reader will be opened again. The reason
+ for implementing this to properly close USB devices so that they
+ will startup the next time without error. */
+void
+apdu_prepare_exit (void)
+{
+ static int sentinel;
+ int slot;
+
+ if (!sentinel)
+ {
+ sentinel = 1;
+ for (slot = 0; slot < MAX_READER; slot++)
+ if (reader_table[slot].used)
+ {
+ apdu_disconnect (slot);
+ if (reader_table[slot].close_reader)
+ reader_table[slot].close_reader (slot);
+ reader_table[slot].used = 0;
+ }
+ sentinel = 0;
+ }
+}
+
+
/* Shutdown a reader; that is basically the same as a close but keeps
- the handle ready for later use. A apdu_reset_header should be used
- to get it active again. */
+ the handle ready for later use. A apdu_reset_reader or apdu_connect
+ should be used to get it active again. */
int
apdu_shutdown_reader (int slot)
{
+ int sw;
+
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
+ sw = apdu_disconnect (slot);
+ if (sw)
+ return sw;
if (reader_table[slot].shutdown_reader)
return reader_table[slot].shutdown_reader (slot);
return SW_HOST_NOT_SUPPORTED;
@@ -2383,6 +2581,91 @@ apdu_enum_reader (int slot, int *used)
return 0;
}
+
+/* Connect a card. This is used to power up the card and make sure
+ that an ATR is available. */
+int
+apdu_connect (int slot)
+{
+ int sw;
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ /* Only if the access method provides a connect function we use it.
+ If not, we expect that the card has been implicitly connected by
+ apdu_open_reader. */
+ if (reader_table[slot].connect_card)
+ {
+ sw = lock_slot (slot);
+ if (!sw)
+ {
+ sw = reader_table[slot].connect_card (slot);
+ unlock_slot (slot);
+ }
+ }
+ else
+ sw = 0;
+
+ /* We need to call apdu_get_status_internal, so that the last-status
+ machinery gets setup properly even if a card is inserted while
+ scdaemon is fired up and apdu_get_status has not yet been called.
+ Without that we would force a reset of the card with the next
+ call to apdu_get_status. */
+ apdu_get_status_internal (slot, 1, 1, NULL, NULL);
+
+ return sw;
+}
+
+
+int
+apdu_disconnect (int slot)
+{
+ int sw;
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if (reader_table[slot].disconnect_card)
+ {
+ sw = lock_slot (slot);
+ if (!sw)
+ {
+ sw = reader_table[slot].disconnect_card (slot);
+ unlock_slot (slot);
+ }
+ }
+ else
+ sw = 0;
+ return sw;
+}
+
+
+/* Set the progress callback of SLOT to CB and its args to CB_ARG. If
+ CB is NULL the progress callback is removed. */
+int
+apdu_set_progress_cb (int slot, gcry_handler_progress_t cb, void *cb_arg)
+{
+ int sw;
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if (reader_table[slot].set_progress_cb)
+ {
+ sw = lock_slot (slot);
+ if (!sw)
+ {
+ sw = reader_table[slot].set_progress_cb (slot, cb, cb_arg);
+ unlock_slot (slot);
+ }
+ }
+ else
+ sw = 0;
+ return sw;
+}
+
+
/* Do a reset for the card in reader at SLOT. */
int
apdu_reset (int slot)
@@ -2403,7 +2686,9 @@ apdu_reset (int slot)
{
/* If we got to here we know that a card is present
and usable. Thus remember this. */
- reader_table[slot].last_status = (1|2|4| 0x8000);
+ reader_table[slot].last_status = (APDU_CARD_USABLE
+ | APDU_CARD_PRESENT
+ | APDU_CARD_ACTIVE);
}
unlock_slot (slot);
@@ -2447,7 +2732,9 @@ apdu_activate (int slot)
{
/* If we got to here we know that a card is present
and usable. Thus remember this. */
- reader_table[slot].last_status = (1|2|4| 0x8000);
+ reader_table[slot].last_status = (APDU_CARD_USABLE
+ | APDU_CARD_PRESENT
+ | APDU_CARD_ACTIVE);
}
}
}
@@ -2458,7 +2745,6 @@ apdu_activate (int slot)
}
-
unsigned char *
apdu_get_atr (int slot, size_t *atrlen)
{
@@ -2466,7 +2752,8 @@ apdu_get_atr (int slot, size_t *atrlen)
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return NULL;
-
+ if (!reader_table[slot].atrlen)
+ return NULL;
buf = xtrymalloc (reader_table[slot].atrlen);
if (!buf)
return NULL;
@@ -2481,20 +2768,20 @@ apdu_get_atr (int slot, size_t *atrlen)
card to become available if HANG is set to true. On success the
bits in STATUS will be set to
- bit 0 = card present and usable
- bit 1 = card present
- bit 2 = card active
- bit 3 = card access locked [not yet implemented]
+ APDU_CARD_USABLE (bit 0) = card present and usable
+ APDU_CARD_PRESENT (bit 1) = card present
+ APDU_CARD_ACTIVE (bit 2) = card active
+ (bit 3) = card access locked [not yet implemented]
- For must application, testing bit 0 is sufficient.
+ For must applications, testing bit 0 is sufficient.
CHANGED will receive the value of the counter tracking the number
of card insertions. This value may be used to detect a card
change.
*/
-int
-apdu_get_status (int slot, int hang,
- unsigned int *status, unsigned int *changed)
+static int
+apdu_get_status_internal (int slot, int hang, int no_atr_reset,
+ unsigned int *status, unsigned int *changed)
{
int sw;
unsigned int s;
@@ -2516,17 +2803,18 @@ apdu_get_status (int slot, int hang,
return sw;
}
- /* Keep track of changes. We use one extra bit to test whether we
- have checked the status at least once. */
- if ( s != (reader_table[slot].last_status & 0x07ff)
- || !reader_table[slot].last_status )
+ /* Keep track of changes. */
+ if (s != reader_table[slot].last_status
+ || !reader_table[slot].any_status )
{
reader_table[slot].change_counter++;
- /* Make sure that the ATR is invalid so that a reset will be by
- activate. */
- reader_table[slot].atrlen = 0;
+ /* Make sure that the ATR is invalid so that a reset will be
+ triggered by apdu_activate. */
+ if (!no_atr_reset)
+ reader_table[slot].atrlen = 0;
}
- reader_table[slot].last_status = (s | 0x8000);
+ reader_table[slot].any_status = 1;
+ reader_table[slot].last_status = s;
if (status)
*status = s;
@@ -2536,6 +2824,15 @@ apdu_get_status (int slot, int hang,
}
+/* See above for a description. */
+int
+apdu_get_status (int slot, int hang,
+ unsigned int *status, unsigned int *changed)
+{
+ return apdu_get_status_internal (slot, hang, 0, status, changed);
+}
+
+
/* Check whether the reader supports the ISO command code COMMAND on
the keypad. Return 0 on success. For a description of the pin
parameters, see ccid-driver.c */
@@ -2567,88 +2864,245 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen,
if (reader_table[slot].send_apdu_reader)
return reader_table[slot].send_apdu_reader (slot,
apdu, apdulen,
- buffer, buflen, pininfo);
+ buffer, buflen,
+ pininfo);
else
return SW_HOST_NOT_SUPPORTED;
}
-/* Core APDU trabceiver function. Parameters are described at
+/* Core APDU tranceiver function. Parameters are described at
apdu_send_le with the exception of PININFO which indicates keypad
- related operations if not NULL. */
+ related operations if not NULL. If EXTENDED_MODE is not 0
+ command chaining or extended length will be used according to these
+ values:
+ n < 0 := Use command chaining with the data part limited to -n
+ in each chunk. If -1 is used a default value is used.
+ n == 0 := No extended mode or command chaining.
+ n == 1 := Use extended length for input and output without a
+ length limit.
+ n > 1 := Use extended length with up to N bytes.
+
+*/
static int
send_le (int slot, int class, int ins, int p0, int p1,
int lc, const char *data, int le,
unsigned char **retbuf, size_t *retbuflen,
- struct pininfo_s *pininfo)
+ struct pininfo_s *pininfo, int extended_mode)
{
-#define RESULTLEN 256
- unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
- the driver. */
+#define SHORT_RESULT_BUFFER_SIZE 258
+ /* We allocate 8 extra bytes as a safety margin towards a driver bug. */
+ unsigned char short_result_buffer[SHORT_RESULT_BUFFER_SIZE+10];
+ unsigned char *result_buffer = NULL;
+ size_t result_buffer_size;
+ unsigned char *result;
size_t resultlen;
- unsigned char apdu[5+256+1];
+ unsigned char short_apdu_buffer[5+256+1];
+ unsigned char *apdu_buffer = NULL;
+ size_t apdu_buffer_size;
+ unsigned char *apdu;
size_t apdulen;
int sw;
- long rc; /* we need a long here due to PC/SC. */
+ long rc; /* We need a long here due to PC/SC. */
+ int did_exact_length_hack = 0;
+ int use_chaining = 0;
+ int use_extended_length = 0;
+ int lc_chunk;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if (DBG_CARD_IO)
- log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d\n",
- class, ins, p0, p1, lc, le);
+ log_debug ("send apdu: c=%02X i=%02X p1=%02X p2=%02X lc=%d le=%d em=%d\n",
+ class, ins, p0, p1, lc, le, extended_mode);
if (lc != -1 && (lc > 255 || lc < 0))
- return SW_WRONG_LENGTH;
- if (le != -1 && (le > 256 || le < 0))
- return SW_WRONG_LENGTH;
+ {
+ /* Data does not fit into an APDU. What we do now depends on
+ the EXTENDED_MODE parameter. */
+ if (!extended_mode)
+ return SW_WRONG_LENGTH; /* No way to send such an APDU. */
+ else if (extended_mode > 0)
+ use_extended_length = 1;
+ else if (extended_mode < 0)
+ {
+ /* Send APDU using chaining mode. */
+ if (lc > 16384)
+ return SW_WRONG_LENGTH; /* Sanity check. */
+ if ((class&0xf0) != 0)
+ return SW_HOST_INV_VALUE; /* Upper 4 bits need to be 0. */
+ use_chaining = extended_mode == -1? 255 : -extended_mode;
+ use_chaining &= 0xff;
+ }
+ else
+ return SW_HOST_INV_VALUE;
+ }
+ else if (lc == -1 && extended_mode > 0)
+ use_extended_length = 1;
+
+ if (le != -1 && (le > (extended_mode > 0? 255:256) || le < 0))
+ {
+ /* Expected Data does not fit into an APDU. What we do now
+ depends on the EXTENDED_MODE parameter. Note that a check
+ for command chaining does not make sense because we are
+ looking at Le. */
+ if (!extended_mode)
+ return SW_WRONG_LENGTH; /* No way to send such an APDU. */
+ else if (use_extended_length)
+ ; /* We are already using extended length. */
+ else if (extended_mode > 0)
+ use_extended_length = 1;
+ else
+ return SW_HOST_INV_VALUE;
+ }
+
if ((!data && lc != -1) || (data && lc == -1))
return SW_HOST_INV_VALUE;
+ if (use_extended_length)
+ {
+ if (reader_table[slot].is_t0)
+ return SW_HOST_NOT_SUPPORTED;
+
+ /* Space for: cls/ins/p1/p2+Z+2_byte_Lc+Lc+2_byte_Le. */
+ apdu_buffer_size = 4 + 1 + (lc >= 0? (2+lc):0) + 2;
+ apdu_buffer = xtrymalloc (apdu_buffer_size + 10);
+ if (!apdu_buffer)
+ return SW_HOST_OUT_OF_CORE;
+ apdu = apdu_buffer;
+ }
+ else
+ {
+ apdu_buffer_size = sizeof short_apdu_buffer;
+ apdu = short_apdu_buffer;
+ }
+
+ if (use_extended_length && (le > 256 || le < 0))
+ {
+ result_buffer_size = le < 0? 4096 : le;
+ result_buffer = xtrymalloc (result_buffer_size + 10);
+ if (!result_buffer)
+ {
+ xfree (apdu_buffer);
+ return SW_HOST_OUT_OF_CORE;
+ }
+ result = result_buffer;
+ }
+ else
+ {
+ result_buffer_size = SHORT_RESULT_BUFFER_SIZE;
+ result = short_result_buffer;
+ }
+#undef SHORT_RESULT_BUFFER_SIZE
+
if ((sw = lock_slot (slot)))
- return sw;
+ {
+ xfree (apdu_buffer);
+ xfree (result_buffer);
+ return sw;
+ }
- apdulen = 0;
- apdu[apdulen++] = class;
- apdu[apdulen++] = ins;
- apdu[apdulen++] = p0;
- apdu[apdulen++] = p1;
- if (lc != -1)
- {
- apdu[apdulen++] = lc;
- memcpy (apdu+apdulen, data, lc);
- apdulen += lc;
- /* T=0 does not allow the use of Lc together with Le; thus
- disable Le in this case. */
- if (reader_table[slot].is_t0)
- le = -1;
- }
- if (le != -1)
- apdu[apdulen++] = le; /* Truncation is okay because 0 means 256. */
- assert (sizeof (apdu) >= apdulen);
- /* As safeguard don't pass any garbage from the stack to the driver. */
- memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
- resultlen = RESULTLEN;
- rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo);
- if (rc || resultlen < 2)
+ do
{
- /* We use log_info here so that in case of a transient error, and
- if this module is used by gpg standalone, the error counter
- isn't incremented. */
- log_info ("apdu_send_simple(%d) failed: %s\n",
- slot, apdu_strerror (rc));
- unlock_slot (slot);
- return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ if (use_extended_length)
+ {
+ use_chaining = 0;
+ apdulen = 0;
+ apdu[apdulen++] = class;
+ apdu[apdulen++] = ins;
+ apdu[apdulen++] = p0;
+ apdu[apdulen++] = p1;
+ apdu[apdulen++] = 0; /* Z byte: Extended length marker. */
+ if (lc >= 0)
+ {
+ apdu[apdulen++] = ((lc >> 8) & 0xff);
+ apdu[apdulen++] = (lc & 0xff);
+ memcpy (apdu+apdulen, data, lc);
+ data += lc;
+ apdulen += lc;
+ }
+ if (le != -1)
+ {
+ apdu[apdulen++] = ((le >> 8) & 0xff);
+ apdu[apdulen++] = (le & 0xff);
+ }
+ }
+ else
+ {
+ apdulen = 0;
+ apdu[apdulen] = class;
+ if (use_chaining && lc > 255)
+ {
+ apdu[apdulen] |= 0x10;
+ assert (use_chaining < 256);
+ lc_chunk = use_chaining;
+ lc -= use_chaining;
+ }
+ else
+ {
+ use_chaining = 0;
+ lc_chunk = lc;
+ }
+ apdulen++;
+ apdu[apdulen++] = ins;
+ apdu[apdulen++] = p0;
+ apdu[apdulen++] = p1;
+ if (lc_chunk != -1)
+ {
+ apdu[apdulen++] = lc_chunk;
+ memcpy (apdu+apdulen, data, lc_chunk);
+ data += lc_chunk;
+ apdulen += lc_chunk;
+ /* T=0 does not allow the use of Lc together with Le;
+ thus disable Le in this case. */
+ if (reader_table[slot].is_t0)
+ le = -1;
+ }
+ if (le != -1 && !use_chaining)
+ apdu[apdulen++] = le; /* Truncation is okay (0 means 256). */
+ }
+
+ exact_length_hack:
+ /* As a safeguard don't pass any garbage to the driver. */
+ assert (apdulen <= apdu_buffer_size);
+ memset (apdu+apdulen, 0, apdu_buffer_size - apdulen);
+ resultlen = result_buffer_size;
+ rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo);
+ if (rc || resultlen < 2)
+ {
+ log_info ("apdu_send_simple(%d) failed: %s\n",
+ slot, apdu_strerror (rc));
+ unlock_slot (slot);
+ xfree (apdu_buffer);
+ xfree (result_buffer);
+ return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ }
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ if (!use_extended_length
+ && !did_exact_length_hack && SW_EXACT_LENGTH_P (sw))
+ {
+ apdu[apdulen-1] = (sw & 0x00ff);
+ did_exact_length_hack = 1;
+ goto exact_length_hack;
+ }
}
- sw = (result[resultlen-2] << 8) | result[resultlen-1];
- /* store away the returned data but strip the statusword. */
+ while (use_chaining && sw == SW_SUCCESS);
+
+ if (apdu_buffer)
+ {
+ xfree (apdu_buffer);
+ apdu_buffer = NULL;
+ apdu_buffer_size = 0;
+ }
+
+ /* Store away the returned data but strip the statusword. */
resultlen -= 2;
if (DBG_CARD_IO)
{
log_debug (" response: sw=%04X datalen=%d\n",
sw, (unsigned int)resultlen);
if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA))
- log_printhex (" dump: ", result, resultlen);
+ log_printhex (" dump: ", result, resultlen);
}
if (sw == SW_SUCCESS || sw == SW_EOF_REACHED)
@@ -2659,6 +3113,7 @@ send_le (int slot, int class, int ins, int p0, int p1,
if (!*retbuf)
{
unlock_slot (slot);
+ xfree (result_buffer);
return SW_HOST_OUT_OF_CORE;
}
*retbuflen = resultlen;
@@ -2678,6 +3133,7 @@ send_le (int slot, int class, int ins, int p0, int p1,
if (!*retbuf)
{
unlock_slot (slot);
+ xfree (result_buffer);
return SW_HOST_OUT_OF_CORE;
}
assert (resultlen < bufsize);
@@ -2692,20 +3148,24 @@ send_le (int slot, int class, int ins, int p0, int p1,
if (DBG_CARD_IO)
log_debug ("apdu_send_simple(%d): %d more bytes available\n",
slot, len);
+ apdu_buffer_size = sizeof short_apdu_buffer;
+ apdu = short_apdu_buffer;
apdulen = 0;
apdu[apdulen++] = class;
apdu[apdulen++] = 0xC0;
apdu[apdulen++] = 0;
apdu[apdulen++] = 0;
apdu[apdulen++] = len;
- memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
- resultlen = RESULTLEN;
+ assert (apdulen <= apdu_buffer_size);
+ memset (apdu+apdulen, 0, apdu_buffer_size - apdulen);
+ resultlen = result_buffer_size;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
if (rc || resultlen < 2)
{
log_error ("apdu_send_simple(%d) for get response failed: %s\n",
slot, apdu_strerror (rc));
unlock_slot (slot);
+ xfree (result_buffer);
return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
@@ -2731,6 +3191,7 @@ send_le (int slot, int class, int ins, int p0, int p1,
if (!tmp)
{
unlock_slot (slot);
+ xfree (result_buffer);
return SW_HOST_OUT_OF_CORE;
}
p = tmp + (p - *retbuf);
@@ -2757,63 +3218,70 @@ send_le (int slot, int class, int ins, int p0, int p1,
}
unlock_slot (slot);
+ xfree (result_buffer);
if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS)
log_printhex (" dump: ", *retbuf, *retbuflen);
return sw;
-#undef RESULTLEN
}
/* Send an APDU to the card in SLOT. The APDU is created from all
given parameters: CLASS, INS, P0, P1, LC, DATA, LE. A value of -1
for LC won't sent this field and the data field; in this case DATA
- must also be passed as NULL. The return value is the status word
- or -1 for an invalid SLOT or other non card related error. If
- RETBUF is not NULL, it will receive an allocated buffer with the
- returned data. The length of that data will be put into
- *RETBUFLEN. The caller is reponsible for releasing the buffer even
- in case of errors. */
+ must also be passed as NULL. If EXTENDED_MODE is not 0 command
+ chaining or extended length will be used; see send_le for details.
+ The return value is the status word or -1 for an invalid SLOT or
+ other non card related error. If RETBUF is not NULL, it will
+ receive an allocated buffer with the returned data. The length of
+ that data will be put into *RETBUFLEN. The caller is reponsible
+ for releasing the buffer even in case of errors. */
int
-apdu_send_le(int slot, int class, int ins, int p0, int p1,
+apdu_send_le(int slot, int extended_mode,
+ int class, int ins, int p0, int p1,
int lc, const char *data, int le,
unsigned char **retbuf, size_t *retbuflen)
{
return send_le (slot, class, ins, p0, p1,
lc, data, le,
retbuf, retbuflen,
- NULL);
+ NULL, extended_mode);
}
/* Send an APDU to the card in SLOT. The APDU is created from all
given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for
LC won't sent this field and the data field; in this case DATA must
- also be passed as NULL. The return value is the status word or -1
- for an invalid SLOT or other non card related error. If RETBUF is
- not NULL, it will receive an allocated buffer with the returned
- data. The length of that data will be put into *RETBUFLEN. The
- caller is reponsible for releasing the buffer even in case of
- errors. */
+ also be passed as NULL. If EXTENDED_MODE is not 0 command chaining
+ or extended length will be used; see send_le for details. The
+ return value is the status word or -1 for an invalid SLOT or other
+ non card related error. If RETBUF is not NULL, it will receive an
+ allocated buffer with the returned data. The length of that data
+ will be put into *RETBUFLEN. The caller is reponsible for
+ releasing the buffer even in case of errors. */
int
-apdu_send (int slot, int class, int ins, int p0, int p1,
+apdu_send (int slot, int extended_mode,
+ int class, int ins, int p0, int p1,
int lc, const char *data, unsigned char **retbuf, size_t *retbuflen)
{
return send_le (slot, class, ins, p0, p1, lc, data, 256,
- retbuf, retbuflen, NULL);
+ retbuf, retbuflen, NULL, extended_mode);
}
/* Send an APDU to the card in SLOT. The APDU is created from all
given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for
LC won't sent this field and the data field; in this case DATA must
- also be passed as NULL. The return value is the status word or -1
- for an invalid SLOT or other non card related error. No data will be
- returned. */
+ also be passed as NULL. If EXTENDED_MODE is not 0 command chaining
+ or extended length will be used; see send_le for details. The
+ return value is the status word or -1 for an invalid SLOT or other
+ non card related error. No data will be returned. */
int
-apdu_send_simple (int slot, int class, int ins, int p0, int p1,
+apdu_send_simple (int slot, int extended_mode,
+ int class, int ins, int p0, int p1,
int lc, const char *data)
{
- return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL);
+ return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL,
+ extended_mode);
}
@@ -2831,29 +3299,36 @@ apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
pininfo.maxlen = pinlen_max;
pininfo.padlen = pin_padlen;
return send_le (slot, class, ins, p0, p1, lc, data, -1,
- NULL, NULL, &pininfo);
+ NULL, NULL, &pininfo, 0);
}
/* This is a more generic version of the apdu sending routine. It
takes an already formatted APDU in APDUDATA or length APDUDATALEN
- and returns the with the APDU including the status word. With
+ and returns with an APDU including the status word. With
HANDLE_MORE set to true this function will handle the MORE DATA
status and return all APDUs concatenated with one status word at
- the end. The function does not return a regular status word but 0
- on success. If the slot is locked, the fucntion returns
- immediately.*/
+ the end. If EXTENDED_LENGTH is != 0 extended lengths are allowed
+ with a max. result data length of EXTENDED_LENGTH bytes. The
+ function does not return a regular status word but 0 on success.
+ If the slot is locked, the function returns immediately with an
+ error. */
int
-apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
+apdu_send_direct (int slot, size_t extended_length,
+ const unsigned char *apdudata, size_t apdudatalen,
int handle_more,
unsigned char **retbuf, size_t *retbuflen)
{
-#define RESULTLEN 256
- unsigned char apdu[5+256+1];
- size_t apdulen;
- unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
- the driver. */
+#define SHORT_RESULT_BUFFER_SIZE 258
+ unsigned char short_result_buffer[SHORT_RESULT_BUFFER_SIZE+10];
+ unsigned char *result_buffer = NULL;
+ size_t result_buffer_size;
+ unsigned char *result;
size_t resultlen;
+ unsigned char short_apdu_buffer[5+256+10];
+ unsigned char *apdu_buffer = NULL;
+ unsigned char *apdu;
+ size_t apdulen;
int sw;
long rc; /* we need a long here due to PC/SC. */
int class;
@@ -2861,23 +3336,59 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
- if ((sw = trylock_slot (slot)))
- return sw;
+ if (apdudatalen > 65535)
+ return SW_HOST_INV_VALUE;
- /* We simply trunctate a too long APDU. */
- if (apdudatalen > sizeof apdu)
- apdudatalen = sizeof apdu;
+ if (apdudatalen > sizeof short_apdu_buffer - 5)
+ {
+ apdu_buffer = xtrymalloc (apdudatalen + 5);
+ if (!apdu_buffer)
+ return SW_HOST_OUT_OF_CORE;
+ apdu = apdu_buffer;
+ }
+ else
+ {
+ apdu = short_apdu_buffer;
+ }
apdulen = apdudatalen;
memcpy (apdu, apdudata, apdudatalen);
class = apdulen? *apdu : 0;
- resultlen = RESULTLEN;
+ if (extended_length >= 256 && extended_length <= 65536)
+ {
+ result_buffer_size = extended_length;
+ result_buffer = xtrymalloc (result_buffer_size + 10);
+ if (!result_buffer)
+ {
+ xfree (apdu_buffer);
+ return SW_HOST_OUT_OF_CORE;
+ }
+ result = result_buffer;
+ }
+ else
+ {
+ result_buffer_size = SHORT_RESULT_BUFFER_SIZE;
+ result = short_result_buffer;
+ }
+#undef SHORT_RESULT_BUFFER_SIZE
+
+ if ((sw = trylock_slot (slot)))
+ {
+ xfree (apdu_buffer);
+ xfree (result_buffer);
+ return sw;
+ }
+
+ resultlen = result_buffer_size;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
+ xfree (apdu_buffer);
+ apdu_buffer = NULL;
if (rc || resultlen < 2)
{
log_error ("apdu_send_direct(%d) failed: %s\n",
slot, apdu_strerror (rc));
unlock_slot (slot);
+ xfree (result_buffer);
return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
@@ -2904,6 +3415,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
if (!*retbuf)
{
unlock_slot (slot);
+ xfree (result_buffer);
return SW_HOST_OUT_OF_CORE;
}
assert (resultlen < bufsize);
@@ -2918,20 +3430,22 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
if (DBG_CARD_IO)
log_debug ("apdu_send_direct(%d): %d more bytes available\n",
slot, len);
+ apdu = short_apdu_buffer;
apdulen = 0;
apdu[apdulen++] = class;
apdu[apdulen++] = 0xC0;
apdu[apdulen++] = 0;
apdu[apdulen++] = 0;
apdu[apdulen++] = len;
- memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
- resultlen = RESULTLEN;
+ memset (apdu+apdulen, 0, sizeof (short_apdu_buffer) - apdulen);
+ resultlen = result_buffer_size;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
if (rc || resultlen < 2)
{
log_error ("apdu_send_direct(%d) for get response failed: %s\n",
slot, apdu_strerror (rc));
unlock_slot (slot);
+ xfree (result_buffer);
return rc ? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
@@ -2957,6 +3471,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
if (!tmp)
{
unlock_slot (slot);
+ xfree (result_buffer);
return SW_HOST_OUT_OF_CORE;
}
p = tmp + (p - *retbuf);
@@ -2967,7 +3482,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
}
}
else
- log_info ("apdu_send_sdirect(%d) "
+ log_info ("apdu_send_direct(%d) "
"got unexpected status %04X from get response\n",
slot, sw);
}
@@ -2989,6 +3504,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
if (!*retbuf)
{
unlock_slot (slot);
+ xfree (result_buffer);
return SW_HOST_OUT_OF_CORE;
}
*retbuflen = resultlen;
@@ -2997,9 +3513,10 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
}
unlock_slot (slot);
+ xfree (result_buffer);
- /* Append the status word - we reseved the two extra bytes while
- allocating the buffer. */
+ /* Append the status word. Note that we reserved the two extra
+ bytes while allocating the buffer. */
if (retbuf)
{
(*retbuf)[(*retbuflen)++] = (sw >> 8);
@@ -3010,5 +3527,4 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
log_printhex (" dump: ", *retbuf, *retbuflen);
return 0;
-#undef RESULTLEN
}