aboutsummaryrefslogtreecommitdiffstats
path: root/scd/apdu.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--scd/apdu.c978
1 files changed, 579 insertions, 399 deletions
diff --git a/scd/apdu.c b/scd/apdu.c
index 38a400bdf..c0c5c5d3e 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -103,6 +103,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);
@@ -291,6 +293,7 @@ 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);
@@ -327,6 +330,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;
@@ -386,6 +391,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";
}
}
@@ -536,10 +542,10 @@ reset_ct_reader (int slot)
static int
ct_get_status (int slot, unsigned int *status)
{
- *status = 1|2|4; /* FIXME */
+ /* 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
@@ -767,178 +773,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 = PCSC_ERR_MASK ((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 = DIM(reader_table[0].atr);
- 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;
@@ -1030,7 +945,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:
@@ -1042,74 +956,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;
+
+ if (!reader_table[slot].atrlen
+ && (err = reset_pcsc_reader (slot)))
+ return 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":"" ); */
+ if (DBG_CARD_IO)
+ log_printhex (" PCSC_data:", apdu, apdulen);
- *status = 0;
- if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
- *status |= 2;
- if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
- *status |= 4;
-#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 & 6) == 6
- && !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
- *status |= 1;
-#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 & 6) == 6)
- *status |= 1;
-#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 0;
-#endif /*!NEED_PCSC_WRAPPER*/
+ 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;
@@ -1215,43 +1118,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;
@@ -1313,25 +1216,349 @@ 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
+ | 0x8000);
+ 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];
@@ -1358,8 +1585,8 @@ open_pcsc_reader (const char *portstr)
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)
@@ -1449,7 +1676,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. */
@@ -1509,7 +1736,10 @@ 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
+ | 0x8000);
}
slotp->atrlen = len;
@@ -1517,7 +1747,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. */
@@ -1537,146 +1766,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 = 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. 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
/*
@@ -1738,9 +1842,9 @@ get_status_ccid (int slot, unsigned int *status)
return -1;
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;
@@ -1836,7 +1940,10 @@ 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
+ | 0x8000);
}
reader_table[slot].close_reader = close_ccid_reader;
@@ -2335,7 +2442,11 @@ apdu_open_reader (const char *portstr)
pcsc_api_loaded = 1;
}
+#ifdef NEED_PCSC_WRAPPER
+ return open_pcsc_reader_wrapped (portstr);
+#else
return open_pcsc_reader (portstr);
+#endif
}
@@ -2378,21 +2489,31 @@ 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;
}
/* Shutdown a reader; that is basically the same as a close but keeps
- the handle ready for later use. A apdu_reset_reader 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;
@@ -2410,6 +2531,58 @@ 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;
+ 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;
+}
+
+
+
/* Do a reset for the card in reader at SLOT. */
int
apdu_reset (int slot)
@@ -2430,7 +2603,10 @@ 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
+ | 0x8000);
}
unlock_slot (slot);
@@ -2474,7 +2650,10 @@ 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
+ | 0x8000);
}
}
}
@@ -2493,7 +2672,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;
@@ -2508,12 +2688,12 @@ 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