summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2004-04-20 16:17:10 +0200
committerWerner Koch <wk@gnupg.org>2004-04-20 16:17:10 +0200
commite950b01ff56c86d8e04b75a7bac4234afc939199 (patch)
treedf584bfcd6ea6348371a037e2983c393c3f02a0d
parentInclude jnlib/types.h and remove our own (diff)
downloadgnupg2-e950b01ff56c86d8e04b75a7bac4234afc939199.tar.xz
gnupg2-e950b01ff56c86d8e04b75a7bac4234afc939199.zip
* pcsc-wrapper.c: New.
* Makefile.am (pkglib_PROGRAMS): Install it here. * apdu.c (writen, readn): New. (open_pcsc_reader, pcsc_send_apdu, close_pcsc_reader): Use the pcsc-wrapper if we are using Pth.
-rw-r--r--scd/ChangeLog54
-rw-r--r--scd/Makefile.am16
-rw-r--r--scd/apdu.c688
-rw-r--r--scd/apdu.h11
-rw-r--r--scd/ccid-driver.c276
-rw-r--r--scd/ccid-driver.h1
-rw-r--r--scd/command.c69
-rw-r--r--scd/iso7816.c3
-rw-r--r--scd/pcsc-wrapper.c631
-rw-r--r--scd/scdaemon.c154
-rw-r--r--scd/scdaemon.h2
11 files changed, 1827 insertions, 78 deletions
diff --git a/scd/ChangeLog b/scd/ChangeLog
index 895aae966..9b04d5e78 100644
--- a/scd/ChangeLog
+++ b/scd/ChangeLog
@@ -1,3 +1,57 @@
+2004-04-20 Werner Koch <wk@gnupg.org>
+
+ * pcsc-wrapper.c: New.
+ * Makefile.am (pkglib_PROGRAMS): Install it here.
+ * apdu.c (writen, readn): New.
+ (open_pcsc_reader, pcsc_send_apdu, close_pcsc_reader): Use the
+ pcsc-wrapper if we are using Pth.
+
+2004-04-19 Werner Koch <wk@gnupg.org>
+
+ * ccid-driver.c (parse_ccid_descriptor): Store some of the reader
+ features away. New arg HANDLE
+ (read_device_info): New arg HANDLE. Changed caller.
+ (bulk_in): Handle time extension requests.
+ (ccid_get_atr): Setup parameters and the IFSD.
+ (compute_edc): New. Factored out code.
+ (ccid_transceive): Use default NADs when required.
+
+2004-04-14 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.h (server_control_s): Add member READER_SLOT.
+ * scdaemon.c (scd_init_default_ctrl): Initialize READER_SLOT to -1.
+ * command.c (open_card): Reuse an open slot.
+ (reset_notify): Just reset the slot if supported by the reader.
+ (do_reset): Factored code from above out.
+ (scd_command_handler): Use it for cleanup.
+
+ * apdu.h: New pseudo stati SW_HOST_NOT_SUPPORTED,
+ SW_HOST_LOCKING_FAILED and SW_HOST_BUSY.
+ * iso7816.c (map_sw): Map it.
+
+ * ccid-driver.c (ccid_slot_status): Add arg STATUSBITS.
+ * apdu.c (apdu_get_status): New.
+ (ct_get_status, pcsc_get_status, ocsc_get_status): New stubs.
+ (get_status_ccid): New.
+ (apdu_reset): New.
+ (reset_ct_reader, reset_pcsc_reader, reset_osc_reader): New stubs.
+ (reset_ccid_reader): New.
+ (apdu_enum_reader): New.
+
+ * apdu.c (lock_slot, trylock_slot, unlock_slot): New helpers.
+ (new_reader_slot) [USE_GNU_PTH]: Init mutex.
+ (apdu_reset, apdu_get_status, apdu_send_le): Run functions
+ in locked mode.
+
+ * command.c (scd_update_reader_status_file): New.
+ * scdaemon.c (handle_tick): Call it.
+
+2004-04-13 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.c: Convert to a Pth application.
+ (handle_signal, ticker_thread, handle_tick): New.
+ (main): Fire up the ticker thread in server mode.
+
2004-03-23 Werner Koch <wk@gnupg.org>
* scdaemon.c (main) <gpgconf_list>: Fixed output for pcsc_driver.
diff --git a/scd/Makefile.am b/scd/Makefile.am
index da59a1997..c3c603d28 100644
--- a/scd/Makefile.am
+++ b/scd/Makefile.am
@@ -19,13 +19,14 @@
## Process this file with automake to produce Makefile.in
bin_PROGRAMS = scdaemon sc-investigate sc-copykeys
+pkglib_PROGRAMS = pcsc-wrapper
AM_CPPFLAGS = -I$(top_srcdir)/intl -I$(top_srcdir)/common
include $(top_srcdir)/am/cmacros.am
AM_CFLAGS = $(OPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \
- $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS)
+ $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) $(PTH_CFLAGS)
card_apps = app-openpgp.c app-nks.c app-dinsig.c
@@ -43,7 +44,8 @@ scdaemon_SOURCES = \
scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \
- $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \
+ $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(PTH_LIBS) \
+ $(KSBA_LIBS) $(LIBASSUAN_LIBS) \
$(LIBUSB_LIBS) -lgpg-error @INTLLIBS@ -ldl
sc_investigate_SOURCES = \
@@ -57,7 +59,8 @@ sc_investigate_SOURCES = \
sc_investigate_LDADD = \
../jnlib/libjnlib.a ../common/libcommon.a \
- $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBUSB_LIBS) \
+ $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(PTH_LIBS) \
+ $(KSBA_LIBS) $(LIBUSB_LIBS) \
@INTLLIBS@ -lgpg-error -ldl
@@ -73,5 +76,10 @@ sc_copykeys_SOURCES = \
sc_copykeys_LDADD = \
../jnlib/libjnlib.a ../common/libcommon.a \
../common/libsimple-pwquery.a \
- $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBUSB_LIBS) \
+ $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(PTH_LIBS) \
+ $(KSBA_LIBS) $(LIBUSB_LIBS) \
-lgpg-error @INTLLIBS@ -ldl
+
+pcsc_wrapper_SOURCES = pcsc-wrapper.c
+pcsc_wrapper_LDADD = -ldl
+pcsc_wrapper_CFLAGS = \ No newline at end of file
diff --git a/scd/apdu.c b/scd/apdu.c
index 7843fd566..1b5ebc375 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -1,5 +1,5 @@
/* apdu.c - ISO 7816 APDU functions and low level I/O
- * Copyright (C) 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -24,6 +24,11 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
+#ifdef USE_GNU_PTH
+# include <pth.h>
+# include <unistd.h>
+# include <fcntl.h>
+#endif
#ifdef HAVE_OPENSC
# include <opensc/opensc.h>
#endif
@@ -48,6 +53,11 @@
#include "dynload.h"
#include "ccid-driver.h"
+#ifdef USE_GNU_PTH
+#define NEED_PCSC_WRAPPER 1
+#endif
+
+
#define MAX_READER 4 /* Number of readers we support concurrently. */
#define CARD_CONNECT_TIMEOUT 1 /* Number of seconds to wait for
insertion of the card (1 = don't wait). */
@@ -59,6 +69,12 @@
#define DLSTDCALL
#endif
+#ifdef _POSIX_OPEN_MAX
+#define MAX_OPEN_FDS _POSIX_OPEN_MAX
+#else
+#define MAX_OPEN_FDS 20
+#endif
+
/* A structure to collect information pertaining to one reader
slot. */
@@ -74,6 +90,11 @@ struct reader_table_s {
unsigned long context;
unsigned long card;
unsigned long protocol;
+#ifdef NEED_PCSC_WRAPPER
+ int req_fd;
+ int rsp_fd;
+ pid_t pid;
+#endif /*NEED_PCSC_WRAPPER*/
} pcsc;
#ifdef HAVE_OPENSC
int is_osc; /* We are using the OpenSC driver layer. */
@@ -85,6 +106,11 @@ struct reader_table_s {
int status;
unsigned char atr[33];
size_t atrlen;
+ unsigned int change_counter;
+#ifdef USE_GNU_PTH
+ int lock_initialized;
+ pth_mutex_t lock;
+#endif
};
typedef struct reader_table_s *reader_table_t;
@@ -185,12 +211,28 @@ new_reader_slot (void)
log_error ("new_reader_slot: out of slots\n");
return -1;
}
+#ifdef USE_GNU_PTH
+ if (!reader_table[reader].lock_initialized)
+ {
+ if (!pth_mutex_init (&reader_table[reader].lock))
+ {
+ log_error ("error initializing mutex: %s\n", strerror (errno));
+ return -1;
+ }
+ reader_table[reader].lock_initialized = 1;
+ }
+#endif /*USE_GNU_PTH*/
reader_table[reader].used = 1;
reader_table[reader].is_ccid = 0;
reader_table[reader].is_ctapi = 0;
#ifdef HAVE_OPENSC
reader_table[reader].is_osc = 0;
#endif
+#ifdef NEED_PCSC_WRAPPER
+ reader_table[reader].pcsc.req_fd = -1;
+ reader_table[reader].pcsc.rsp_fd = -1;
+ reader_table[reader].pcsc.pid = (pid_t)(-1);
+#endif
return reader;
}
@@ -370,6 +412,18 @@ close_ct_reader (int slot)
return 0;
}
+static int
+reset_ct_reader (int slot)
+{
+ return SW_HOST_NOT_SUPPORTED;
+}
+
+
+static int
+ct_get_status (int slot, unsigned int *status)
+{
+ return SW_HOST_NOT_SUPPORTED;
+}
/* Actually send the APDU of length APDULEN to SLOT and return a
maximum of *BUFLEN data in BUFFER, the actual retruned size will be
@@ -397,6 +451,66 @@ ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
+#ifdef NEED_PCSC_WRAPPER
+static int
+writen (int fd, const void *buf, size_t nbytes)
+{
+ size_t nleft = nbytes;
+ int nwritten;
+
+/* log_printhex (" writen:", buf, nbytes); */
+
+ while (nleft > 0)
+ {
+#ifdef USE_GNU_PTH
+ nwritten = pth_write (fd, buf, nleft);
+#else
+ nwritten = write (fd, buf, nleft);
+#endif
+ if (nwritten < 0 && errno == EINTR)
+ continue;
+ if (nwritten < 0)
+ return -1;
+ nleft -= nwritten;
+ buf = (const char*)buf + nwritten;
+ }
+ return 0;
+}
+
+/* Read up to BUFLEN bytes from FD and return the number of bytes
+ actually read in NREAD. Returns -1 on error or 0 on success. */
+static int
+readn (int fd, void *buf, size_t buflen, size_t *nread)
+{
+ size_t nleft = buflen;
+ int n;
+/* void *orig_buf = buf; */
+
+ while (nleft > 0)
+ {
+#ifdef USE_GNU_PTH
+ n = pth_read (fd, buf, nleft);
+#else
+ n = read (fd, buf, nleft);
+#endif
+ if (n < 0 && errno == EINTR)
+ continue;
+ if (n < 0)
+ return -1; /* read error. */
+ if (!n)
+ break; /* EOF */
+ nleft -= n;
+ buf = (char*)buf + n;
+ }
+ if (nread)
+ *nread = buflen - nleft;
+
+/* log_printhex (" readn:", orig_buf, *nread); */
+
+ return 0;
+}
+#endif /*NEED_PCSC_WRAPPER*/
+
static const char *
pcsc_error_string (long err)
{
@@ -457,6 +571,172 @@ pcsc_error_string (long err)
static int
open_pcsc_reader (const char *portstr)
{
+#ifdef 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. */
+ int slot;
+ reader_table_t slotp;
+ int fd, rp[2], wp[2];
+ int n, i;
+ pid_t pid;
+ size_t len;
+ unsigned char msgbuf[9];
+ int err;
+
+ 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
+ may still be source copied. */
+
+ if (pipe (rp) == -1)
+ {
+ log_error ("error creating a pipe: %s\n", strerror (errno));
+ slotp->used = 0;
+ return -1;
+ }
+ if (pipe (wp) == -1)
+ {
+ log_error ("error creating a pipe: %s\n", strerror (errno));
+ close (rp[0]);
+ close (rp[1]);
+ slotp->used = 0;
+ return -1;
+ }
+
+ pid = fork ();
+ if (pid == -1)
+ {
+ log_error ("error forking process: %s\n", strerror (errno));
+ close (rp[0]);
+ close (rp[1]);
+ close (wp[0]);
+ close (wp[1]);
+ slotp->used = 0;
+ return -1;
+ }
+ slotp->pcsc.pid = pid;
+
+ if (!pid)
+ { /*
+ === Child ===
+ */
+
+ /* Double fork. */
+ pid = fork ();
+ if (pid == -1)
+ _exit (31);
+ if (pid)
+ _exit (0); /* Immediate exit this parent, so that the child
+ gets cleaned up by the init process. */
+
+ /* Connect our pipes. */
+ if (wp[0] != 0 && dup2 (wp[0], 0) == -1)
+ log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
+ if (rp[1] != 1 && dup2 (rp[1], 1) == -1)
+ log_fatal ("dup2 stdout failed: %s\n", strerror (errno));
+
+ /* Send stderr to the bit bucket. */
+ fd = open ("/dev/null", O_WRONLY);
+ if (fd == -1)
+ log_fatal ("can't open `/dev/null': %s", strerror (errno));
+ if (fd != 2 && dup2 (fd, 2) == -1)
+ 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;
+
+ execl (GNUPG_LIBDIR "/pcsc-wrapper",
+ "pcsc-wrapper",
+ "--",
+ "1", /* API version */
+ opt.pcsc_driver, /* Name of the PC/SC library. */
+ NULL);
+ _exit (31);
+ }
+
+ /*
+ === Parent ===
+ */
+ close (wp[0]);
+ close (rp[1]);
+ slotp->pcsc.req_fd = wp[1];
+ slotp->pcsc.rsp_fd = rp[0];
+
+ /* Wait for the intermediate child to terminate. */
+ while ( (i=pth_waitpid (pid, NULL, 0)) == -1 && errno == EINTR)
+ ;
+
+ /* Now send the open request. */
+ msgbuf[0] = 0x01; /* OPEN command. */
+ len = portstr? strlen (portstr):0;
+ msgbuf[1] = (len >> 24);
+ msgbuf[2] = (len >> 16);
+ msgbuf[3] = (len >> 8);
+ msgbuf[4] = (len );
+ if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
+ || (portstr && writen (slotp->pcsc.req_fd, portstr, len)))
+ {
+ log_error ("error sending PC/SC OPEN 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 OPEN 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=%x)\n", len);
+ goto command_failed;
+ }
+ err = (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));
+ goto command_failed;
+ }
+ n = len;
+ if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
+ {
+ log_error ("error receiving PC/SC OPEN response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ slotp->atrlen = len;
+
+ dump_reader_status (slot);
+ return slot;
+
+ 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 -1;
+#else /*!NEED_PCSC_WRAPPER */
long err;
int slot;
char *list = NULL;
@@ -559,9 +839,16 @@ open_pcsc_reader (const char *portstr)
dump_reader_status (slot);
return slot;
+#endif /*!NEED_PCSC_WRAPPER */
}
+static int
+pcsc_get_status (int slot, unsigned int *status)
+{
+ return SW_HOST_NOT_SUPPORTED;
+}
+
/* 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. */
@@ -569,6 +856,108 @@ static int
pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen)
{
+#ifdef NEED_PCSC_WRAPPER
+ long err;
+ reader_table_t slotp;
+ size_t len, full_len;
+ int i, n;
+ unsigned char msgbuf[9];
+
+ if (DBG_CARD_IO)
+ log_printhex (" PCSC_data:", apdu, apdulen);
+
+ slotp = reader_table + slot;
+
+ if (slotp->pcsc.req_fd == -1
+ || slotp->pcsc.rsp_fd == -1
+ || slotp->pcsc.pid == (pid_t)(-1) )
+ {
+ log_error ("pcsc_send_apdu: pcsc-wrapper not running\n");
+ return -1;
+ }
+
+ msgbuf[0] = 0x03; /* TRANSMIT command. */
+ len = apdulen;
+ msgbuf[1] = (len >> 24);
+ msgbuf[2] = (len >> 16);
+ msgbuf[3] = (len >> 8);
+ msgbuf[4] = (len );
+ if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
+ || writen (slotp->pcsc.req_fd, apdu, len))
+ {
+ log_error ("error sending PC/SC TRANSMIT 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 TRANSMIT 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. */
+ err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+ if (err)
+ {
+ log_error ("pcsc_transmit failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ return -1;
+ }
+
+ full_len = len;
+
+ n = *buflen < len ? *buflen : len;
+ if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n)
+ {
+ log_error ("error receiving PC/SC TRANSMIT response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ *buflen = n;
+ full_len -= len;
+ if (full_len)
+ {
+ log_error ("pcsc_send_apdu: provided buffer too short - truncated\n");
+ err = -1;
+ }
+ /* We need to read any rest of the response, to keep the
+ protocol runnng. */
+ while (full_len)
+ {
+ unsigned char dummybuf[128];
+
+ n = full_len < DIM (dummybuf) ? full_len : DIM (dummybuf);
+ if ((i=readn (slotp->pcsc.rsp_fd, dummybuf, n, &len)) || len != n)
+ {
+ log_error ("error receiving PC/SC TRANSMIT response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ full_len -= n;
+ }
+
+ return err;
+
+ 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 -1;
+
+#else /*!NEED_PCSC_WRAPPER*/
+
long err;
struct pcsc_io_request_s send_pci;
unsigned long recv_len;
@@ -591,14 +980,87 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
pcsc_error_string (err), err);
return err? -1:0; /* FIXME: Return appropriate error code. */
+#endif /*!NEED_PCSC_WRAPPER*/
}
+
static int
close_pcsc_reader (int slot)
{
+#ifdef NEED_PCSC_WRAPPER
+ long err;
+ reader_table_t slotp;
+ size_t len;
+ int i;
+ unsigned char msgbuf[9];
+
+ slotp = reader_table + slot;
+
+ if (slotp->pcsc.req_fd == -1
+ || slotp->pcsc.rsp_fd == -1
+ || slotp->pcsc.pid == (pid_t)(-1) )
+ {
+ log_error ("close_pcsc_reader: pcsc-wrapper not running\n");
+ return 0;
+ }
+
+ msgbuf[0] = 0x02; /* CLOSE 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 CLOSE 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 CLOSE 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. */
+ err = (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);
+
+ /* We will the wrapper in any case - errors are merely
+ informational. */
+
+ 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 0;
+
+#else /*!NEED_PCSC_WRAPPER*/
+
pcsc_release_context (reader_table[slot].pcsc.context);
reader_table[slot].used = 0;
return 0;
+#endif /*!NEED_PCSC_WRAPPER*/
+}
+
+static int
+reset_pcsc_reader (int slot)
+{
+ return SW_HOST_NOT_SUPPORTED;
}
@@ -661,6 +1123,46 @@ close_ccid_reader (int slot)
}
+static int
+reset_ccid_reader (int slot)
+{
+ int err;
+ reader_table_t slotp = reader_table + slot;
+ unsigned char atr[33];
+ size_t atrlen;
+
+ err = ccid_get_atr (slotp->ccid.handle, atr, sizeof atr, &atrlen);
+ if (err)
+ return -1;
+ /* If the reset was successful, update the ATR. */
+ assert (sizeof slotp->atr >= sizeof atr);
+ slotp->atrlen = atrlen;
+ memcpy (slotp->atr, atr, atrlen);
+ dump_reader_status (slot);
+ return 0;
+}
+
+
+static int
+get_status_ccid (int slot, unsigned int *status)
+{
+ int rc;
+ int bits;
+
+ rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits);
+ if (rc)
+ return -1;
+
+ if (bits == 0)
+ *status = 1|2|4;
+ else if (bits == 1)
+ *status = 2;
+ else
+ *status = 0;
+
+ return 0;
+}
+
/* Actually send the APDU of length APDULEN to SLOT and return a
maximum of *BUFLEN data in BUFFER, the actual returned size will be
@@ -798,6 +1300,18 @@ close_osc_reader (int slot)
return 0;
}
+static int
+reset_osc_reader (int slot)
+{
+ return SW_HOST_NOT_SUPPORTED;
+}
+
+
+static int
+ocsc_get_status (int slot, unsigned int *status)
+{
+ return SW_HOST_NOT_SUPPORTED;
+}
/* Actually send the APDU of length APDULEN to SLOT and return a
@@ -896,6 +1410,45 @@ osc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
Driver Access
*/
+
+static int
+lock_slot (int slot)
+{
+#ifdef USE_GNU_PTH
+ if (!pth_mutex_acquire (&reader_table[slot].lock, 0, NULL))
+ {
+ log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
+ return SW_HOST_LOCKING_FAILED;
+ }
+#endif /*USE_GNU_PTH*/
+ return 0;
+}
+
+static int
+trylock_slot (int slot)
+{
+#ifdef USE_GNU_PTH
+ if (!pth_mutex_acquire (&reader_table[slot].lock, TRUE, NULL))
+ {
+ if (errno == EBUSY)
+ return SW_HOST_BUSY;
+ log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
+ return SW_HOST_LOCKING_FAILED;
+ }
+#endif /*USE_GNU_PTH*/
+ return 0;
+}
+
+static void
+unlock_slot (int slot)
+{
+#ifdef USE_GNU_PTH
+ if (!pth_mutex_release (&reader_table[slot].lock))
+ log_error ("failed to release apdu lock: %s\n", strerror (errno));
+#endif /*USE_GNU_PTH*/
+}
+
+
/* Open the reader and return an internal slot number or -1 on
error. If PORTSTR is NULL we default to a suitable port (for ctAPI:
the first USB reader. For PC/SC the first listed reader). If
@@ -937,7 +1490,7 @@ apdu_open_reader (const char *portstr)
handle = dlopen (opt.ctapi_driver, RTLD_LAZY);
if (!handle)
{
- log_error ("apdu_open_reader: failed to open driver: %s",
+ log_error ("apdu_open_reader: failed to open driver: %s\n",
dlerror ());
return -1;
}
@@ -959,12 +1512,13 @@ apdu_open_reader (const char *portstr)
/* No ctAPI configured, so lets try the PC/SC API */
if (!pcsc_api_loaded)
{
+#ifndef NEED_PCSC_WRAPPER
void *handle;
handle = dlopen (opt.pcsc_driver, RTLD_LAZY);
if (!handle)
{
- log_error ("apdu_open_reader: failed to open driver `%s': %s",
+ log_error ("apdu_open_reader: failed to open driver `%s': %s\n",
opt.pcsc_driver, dlerror ());
return -1;
}
@@ -1020,9 +1574,10 @@ apdu_open_reader (const char *portstr)
dlclose (handle);
return -1;
}
+#endif /*!NEED_PCSC_WRAPPER*/
pcsc_api_loaded = 1;
}
-
+
return open_pcsc_reader (portstr);
}
@@ -1046,6 +1601,47 @@ apdu_close_reader (int slot)
return close_pcsc_reader (slot);
}
+/* Enumerate all readers and return information on whether this reader
+ is in use. The caller should start with SLOT set to 0 and
+ increment it with each call until an error is returned. */
+int
+apdu_enum_reader (int slot, int *used)
+{
+ if (slot < 0 || slot >= MAX_READER)
+ return SW_HOST_NO_DRIVER;
+ *used = reader_table[slot].used;
+ return 0;
+}
+
+/* Do a reset for the card in reader at SLOT. */
+int
+apdu_reset (int slot)
+{
+ int sw;
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if ((sw = lock_slot (slot)))
+ return sw;
+
+ if (reader_table[slot].is_ctapi)
+ sw = reset_ct_reader (slot);
+#ifdef HAVE_LIBUSB
+ else if (reader_table[slot].is_ccid)
+ sw = reset_ccid_reader (slot);
+#endif
+#ifdef HAVE_OPENSC
+ else if (reader_table[slot].is_osc)
+ sw = reset_osc_reader (slot);
+#endif
+ else
+ sw = reset_pcsc_reader (slot);
+
+ unlock_slot (slot);
+ return sw;
+}
+
unsigned char *
apdu_get_atr (int slot, size_t *atrlen)
@@ -1062,6 +1658,7 @@ apdu_get_atr (int slot, size_t *atrlen)
*atrlen = reader_table[slot].atrlen;
return buf;
}
+
static const char *
@@ -1084,7 +1681,62 @@ error_string (int slot, long rc)
}
-/* Dispatcher for the actual send_apdu fucntion. */
+/* Retrieve the status for SLOT. The function does obnly wait fot the
+ 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]
+
+ For must application, tetsing 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)
+{
+ int sw;
+ unsigned int s;
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if ((sw = hang? lock_slot (slot) : trylock_slot (slot)))
+ return sw;
+
+ if (reader_table[slot].is_ctapi)
+ sw = ct_get_status (slot, &s);
+#ifdef HAVE_LIBUSB
+ else if (reader_table[slot].is_ccid)
+ sw = get_status_ccid (slot, &s);
+#endif
+#ifdef HAVE_OPENSC
+ else if (reader_table[slot].is_osc)
+ sw = osc_get_status (slot, &s);
+#endif
+ else
+ sw = pcsc_get_status (slot, &s);
+
+ unlock_slot (slot);
+
+ if (sw)
+ return sw;
+
+ if (status)
+ *status = s;
+ if (changed)
+ *changed = reader_table[slot].change_counter;
+ return 0;
+}
+
+
+/* Dispatcher for the actual send_apdu function. Note, that this
+ function should be called in locked state. */
static int
send_apdu (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen)
@@ -1126,6 +1778,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
int sw;
long rc; /* we need a long here due to PC/SC. */
+ 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);
@@ -1137,6 +1792,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
if ((!data && lc != -1) || (data && lc == -1))
return SW_HOST_INV_VALUE;
+ if ((sw = lock_slot (slot)))
+ return sw;
+
apdulen = 0;
apdu[apdulen++] = class;
apdu[apdulen++] = ins;
@@ -1158,6 +1816,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
{
log_error ("apdu_send_simple(%d) failed: %s\n",
slot, error_string (slot, rc));
+ unlock_slot (slot);
return SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
@@ -1176,7 +1835,10 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
{
*retbuf = xtrymalloc (resultlen? resultlen : 1);
if (!*retbuf)
- return SW_HOST_OUT_OF_CORE;
+ {
+ unlock_slot (slot);
+ return SW_HOST_OUT_OF_CORE;
+ }
*retbuflen = resultlen;
memcpy (*retbuf, result, resultlen);
}
@@ -1192,7 +1854,10 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
{
*retbuf = p = xtrymalloc (bufsize);
if (!*retbuf)
- return SW_HOST_OUT_OF_CORE;
+ {
+ unlock_slot (slot);
+ return SW_HOST_OUT_OF_CORE;
+ }
assert (resultlen < bufsize);
memcpy (p, result, resultlen);
p += resultlen;
@@ -1216,6 +1881,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
{
log_error ("apdu_send_simple(%d) for get response failed: %s\n",
slot, error_string (slot, rc));
+ unlock_slot (slot);
return SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
@@ -1236,7 +1902,10 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
bufsize += resultlen > 4096? resultlen: 4096;
tmp = xtryrealloc (*retbuf, bufsize);
if (!tmp)
- return SW_HOST_OUT_OF_CORE;
+ {
+ unlock_slot (slot);
+ return SW_HOST_OUT_OF_CORE;
+ }
p = tmp + (p - *retbuf);
*retbuf = tmp;
}
@@ -1259,6 +1928,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
*retbuf = tmp;
}
}
+
+ unlock_slot (slot);
+
if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS)
log_printhex (" dump: ", *retbuf, *retbuflen);
diff --git a/scd/apdu.h b/scd/apdu.h
index fd7634f13..f74bab7fe 100644
--- a/scd/apdu.h
+++ b/scd/apdu.h
@@ -48,9 +48,12 @@ enum {
those values can't be issued by a card. */
SW_HOST_OUT_OF_CORE = 0x10001, /* No way yet to differentiate
between errnos on a failed malloc. */
- SW_HOST_INV_VALUE = 0x10002,
+ SW_HOST_INV_VALUE = 0x10002,
SW_HOST_INCOMPLETE_CARD_RESPONSE = 0x10003,
- SW_HOST_NO_DRIVER = 0x10004
+ SW_HOST_NO_DRIVER = 0x10004,
+ SW_HOST_NOT_SUPPORTED = 0x10005,
+ SW_HOST_LOCKING_FAILED= 0x10006,
+ SW_HOST_BUSY = 0x10007
};
@@ -58,10 +61,14 @@ enum {
/* Note , that apdu_open_reader returns no status word but -1 on error. */
int apdu_open_reader (const char *portstr);
int apdu_close_reader (int slot);
+int apdu_enum_reader (int slot, int *used);
unsigned char *apdu_get_atr (int slot, size_t *atrlen);
/* The apdu send functions do return status words. */
+int apdu_reset (int slot);
+int apdu_get_status (int slot, int hang,
+ unsigned int *status, unsigned int *changed);
int apdu_send_simple (int slot, int class, int ins, int p0, int p1,
int lc, const char *data);
int apdu_send (int slot, int class, int ins, int p0, int p1,
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c
index b398e3ce3..1c8167869 100644
--- a/scd/ccid-driver.c
+++ b/scd/ccid-driver.c
@@ -1,5 +1,5 @@
/* ccid-driver.c - USB ChipCardInterfaceDevices driver
- * Copyright (C) 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
* Written by Werner Koch.
*
* This file is part of GnuPG.
@@ -108,7 +108,10 @@
/* Disable all debugging output for now. */
#undef DBG_CARD_IO
-#define DBG_CARD_IO 0
+#define DBG_CARD_IO 1
+
+/* Define to print information pertaining the T=1 protocol. */
+#undef DEBUG_T1
# define DEBUGOUT(t) do { if (DBG_CARD_IO) \
@@ -145,8 +148,6 @@
#endif /* This source not used by scdaemon. */
-/* Define to print information pertaining the T=1 protocol. */
-#undef DEBUG_T1
@@ -184,9 +185,15 @@ struct ccid_driver_s {
int seqno;
unsigned char t1_ns;
unsigned char t1_nr;
+ int nonnull_nad;
+ int auto_ifsd;
+ int max_ifsd;
+ int ifsd;
};
+static unsigned int compute_edc (const unsigned char *data, size_t datalen,
+ int use_crc);
static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen);
static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
size_t *nread, int expected_type, int seqno);
@@ -220,13 +227,18 @@ set_msg_len (unsigned char *msg, unsigned int length)
Note, that this code is based on the one in lsusb.c of the
usb-utils package, I wrote on 2003-09-01. -wk. */
static int
-parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
+parse_ccid_descriptor (ccid_driver_t handle,
+ const unsigned char *buf, size_t buflen)
{
unsigned int i;
unsigned int us;
int have_t1 = 0, have_tpdu=0, have_auto_conf = 0;
+ handle->nonnull_nad = 0;
+ handle->auto_ifsd = 0;
+ handle->max_ifsd = 32;
+ handle->ifsd = 0;
if (buflen < 54 || buf[0] < 54)
{
DEBUGOUT ("CCID device descriptor is too short\n");
@@ -272,6 +284,7 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
us = convert_le_u32(buf+28);
DEBUGOUT_1 (" dwMaxIFSD %5u\n", us);
+ handle->max_ifsd = us;
us = convert_le_u32(buf+32);
DEBUGOUT_1 (" dwSyncProtocols %08X ", us);
@@ -320,9 +333,15 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
if ((us & 0x0100))
DEBUGOUT (" CCID can set ICC in clock stop mode\n");
if ((us & 0x0200))
- DEBUGOUT (" NAD value other than 0x00 accpeted\n");
+ {
+ DEBUGOUT (" NAD value other than 0x00 accepted\n");
+ handle->nonnull_nad = 1;
+ }
if ((us & 0x0400))
- DEBUGOUT (" Auto IFSD exchange\n");
+ {
+ DEBUGOUT (" Auto IFSD exchange\n");
+ handle->auto_ifsd = 1;
+ }
if ((us & 0x00010000))
{
@@ -389,7 +408,7 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
that the device is usable for us. Returns 0 on success or an error
code. */
static int
-read_device_info (struct usb_device *dev)
+read_device_info (ccid_driver_t handle, struct usb_device *dev)
{
int cfg_no;
@@ -414,8 +433,9 @@ read_device_info (struct usb_device *dev)
{
if (ifcdesc->extra)
{
- if (!parse_ccid_descriptor (ifcdesc->extra,
- ifcdesc->extralen))
+ if (!parse_ccid_descriptor (handle,
+ ifcdesc->extra,
+ ifcdesc->extralen))
return 0; /* okay. we can use it. */
}
}
@@ -458,10 +478,22 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
dev->descriptor->idVendor, dev->descriptor->idProduct);
if (!readerno)
{
- rc = read_device_info (dev);
+ *handle = calloc (1, sizeof **handle);
+ if (!*handle)
+ {
+ DEBUGOUT ("out of memory\n");
+ rc = -1;
+ free (*handle);
+ *handle = NULL;
+ goto leave;
+ }
+
+ rc = read_device_info (*handle, dev);
if (rc)
{
DEBUGOUT ("device not supported\n");
+ free (*handle);
+ *handle = NULL;
goto leave;
}
@@ -469,6 +501,8 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
if (rc)
{
DEBUGOUT_1 ("usb_open failed: %d\n", rc);
+ free (*handle);
+ *handle = NULL;
goto leave;
}
@@ -479,16 +513,11 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
if (rc)
{
DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
+ free (*handle);
+ *handle = NULL;
goto leave;
}
- *handle = calloc (1, sizeof **handle);
- if (!*handle)
- {
- DEBUGOUT ("out of memory\n");
- rc = -1;
- goto leave;
- }
(*handle)->idev = idev;
idev = NULL;
/* FIXME: Do we need to get the endpoint addresses from the
@@ -508,7 +537,7 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
usb_free_match (match);
if (!rc && !*handle)
- rc = -1; /* In case we didn't enter the while lool at all. */
+ rc = -1; /* In case we didn't enter the while loop at all. */
return rc;
}
@@ -592,6 +621,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
int i, rc;
size_t msglen;
+ retry:
rc = usb_bulk_read (handle->idev,
0x82,
buffer, length,
@@ -628,6 +658,14 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
return -1;
}
+ if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80)
+ {
+ /* Card present and active, time extension requested. */
+ DEBUGOUT_2 ("time extension requested (%02X,%02X)\n",
+ buffer[7], buffer[8]);
+ goto retry;
+ }
+
DEBUGOUT_3 ("status: %02X error: %02X octet[9]: %02X\n"
" data:", buffer[7], buffer[8], buffer[9] );
for (i=10; i < msglen; i++)
@@ -695,7 +733,7 @@ ccid_poll (ccid_driver_t handle)
int
-ccid_slot_status (ccid_driver_t handle)
+ccid_slot_status (ccid_driver_t handle, int *statusbits)
{
int rc;
unsigned char msg[100];
@@ -716,6 +754,7 @@ ccid_slot_status (ccid_driver_t handle)
rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno);
if (rc)
return rc;
+ *statusbits = (msg[7] & 3);
return 0;
}
@@ -727,8 +766,12 @@ ccid_get_atr (ccid_driver_t handle,
{
int rc;
unsigned char msg[100];
- size_t msglen;
+ unsigned char *tpdu;
+ size_t msglen, tpdulen;
unsigned char seqno;
+ int use_crc = 0;
+ unsigned int edc;
+ int i;
msg[0] = PC_to_RDR_IccPowerOn;
msg[5] = 0; /* slot */
@@ -756,11 +799,135 @@ ccid_get_atr (ccid_driver_t handle,
*atrlen = n;
}
+ /* Setup parameters to select T=1. */
+ msg[0] = PC_to_RDR_SetParameters;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 1; /* Select T=1. */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+
+ /* FIXME: Get those values from the ATR. */
+ msg[10]= 0x01; /* Fi/Di */
+ msg[11]= 0x10; /* LRC, direct convention. */
+ msg[12]= 0; /* Extra guardtime. */
+ msg[13]= 0x41; /* BWI/CWI */
+ msg[14]= 0; /* No clock stoppping. */
+ msg[15]= 254; /* IFSC */
+ msg[16]= 0; /* Does not support non default NAD values. */
+ set_msg_len (msg, 7);
+ msglen = 10 + 7;
+
+ DEBUGOUT ("sending");
+ for (i=0; i < msglen; i++)
+ DEBUGOUT_CONT_1 (" %02X", msg[i]);
+ DEBUGOUT_LF ();
+
+ rc = bulk_out (handle, msg, msglen);
+ if (rc)
+ return rc;
+ /* Note that we ignore the error code on purpose. */
+ bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters, seqno);
+
+
+ /* Send an S-Block with our maximun IFSD to the CCID. */
+ if (!handle->auto_ifsd)
+ {
+ tpdu = msg+10;
+ /* NAD: DAD=1, SAD=0 */
+ tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+ tpdu[1] = (0xc0 | 0 | 1); /* S-block request: change IFSD */
+ tpdu[2] = 1;
+ tpdu[3] = handle->max_ifsd? handle->max_ifsd : 32;
+ tpdulen = 4;
+ edc = compute_edc (tpdu, tpdulen, use_crc);
+ if (use_crc)
+ tpdu[tpdulen++] = (edc >> 8);
+ tpdu[tpdulen++] = edc;
+
+ msg[0] = PC_to_RDR_XfrBlock;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 0;
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ set_msg_len (msg, tpdulen);
+ msglen = 10 + tpdulen;
+
+ DEBUGOUT ("sending");
+ for (i=0; i < msglen; i++)
+ DEBUGOUT_CONT_1 (" %02X", msg[i]);
+ DEBUGOUT_LF ();
+
+#ifdef DEBUG_T1
+ fprintf (stderr, "T1: put %c-block seq=%d\n",
+ ((msg[11] & 0xc0) == 0x80)? 'R' :
+ (msg[11] & 0x80)? 'S' : 'I',
+ ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)));
+#endif
+
+ rc = bulk_out (handle, msg, msglen);
+ if (rc)
+ return rc;
+
+ /* Fixme: The next line for the current Valgrid without support
+ for USB IOCTLs. */
+ memset (msg, 0, sizeof msg);
+
+ rc = bulk_in (handle, msg, sizeof msg, &msglen,
+ RDR_to_PC_DataBlock, seqno);
+ if (rc)
+ return rc;
+
+ tpdu = msg + 10;
+ tpdulen = msglen - 10;
+
+ if (tpdulen < 4)
+ {
+ DEBUGOUT ("cannot yet handle short blocks!\n");
+ return -1;
+ }
+
+#ifdef DEBUG_T1
+ fprintf (stderr, "T1: got %c-block seq=%d err=%d\n",
+ ((msg[11] & 0xc0) == 0x80)? 'R' :
+ (msg[11] & 0x80)? 'S' : 'I',
+ ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
+ ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0
+ );
+#endif
+ if ((tpdu[1] & 0xe0) != 0xe0 || tpdu[2] != 1)
+ {
+ DEBUGOUT ("invalid response for S-block (Change-IFSD)\n");
+ return -1;
+ }
+ DEBUGOUT_1 ("IFSD has been set to %d\n", tpdu[3]);
+ }
+
return 0;
}
+
+static unsigned int
+compute_edc (const unsigned char *data, size_t datalen, int use_crc)
+{
+ if (use_crc)
+ {
+ return 0x42; /* Not yet implemented. */
+ }
+ else
+ {
+ unsigned char crc = 0;
+
+ for (; datalen; datalen--)
+ crc ^= *data++;
+ return crc;
+ }
+}
+
+
/*
Protocol T=1 overview
@@ -819,17 +986,19 @@ ccid_transceive (ccid_driver_t handle,
unsigned char *resp, size_t maxresplen, size_t *nresp)
{
int rc;
- unsigned char send_buffer[10+258], recv_buffer[10+258];
+ unsigned char send_buffer[10+259], recv_buffer[10+259];
const unsigned char *apdu;
size_t apdulen;
unsigned char *msg, *tpdu, *p;
- size_t msglen, tpdulen, n;
+ size_t msglen, tpdulen, last_tpdulen, n;
unsigned char seqno;
int i;
- unsigned char crc;
+ unsigned int edc;
+ int use_crc = 0;
size_t dummy_nresp;
int next_chunk = 1;
int sending = 1;
+ int retries = 0;
if (!nresp)
nresp = &dummy_nresp;
@@ -852,7 +1021,8 @@ ccid_transceive (ccid_driver_t handle,
return -1; /* Invalid length. */
tpdu = msg+10;
- tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
+ /* NAD: DAD=1, SAD=0 */
+ tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */
if (apdulen > 128 /* fixme: replace by ifsc */)
{
@@ -863,12 +1033,11 @@ ccid_transceive (ccid_driver_t handle,
}
tpdu[2] = apdulen;
memcpy (tpdu+3, apdu, apdulen);
- crc = 0;
- for (i=0,p=tpdu; i < apdulen+3; i++)
- crc ^= *p++;
- tpdu[3+apdulen] = crc;
-
- tpdulen = apdulen + 4;
+ tpdulen = 3 + apdulen;
+ edc = compute_edc (tpdu, tpdulen, use_crc);
+ if (use_crc)
+ tpdu[tpdulen++] = (edc >> 8);
+ tpdu[tpdulen++] = edc;
}
msg[0] = PC_to_RDR_XfrBlock;
@@ -879,6 +1048,7 @@ ccid_transceive (ccid_driver_t handle,
msg[9] = 0; /* RFU */
set_msg_len (msg, tpdulen);
msglen = 10 + tpdulen;
+ last_tpdulen = tpdulen;
DEBUGOUT ("sending");
for (i=0; i < msglen; i++)
@@ -926,7 +1096,7 @@ ccid_transceive (ccid_driver_t handle,
if (!(tpdu[1] & 0x80))
{ /* This is an I-block. */
-
+ retries = 0;
if (sending)
{ /* last block sent was successful. */
handle->t1_ns ^= 1;
@@ -937,13 +1107,15 @@ ccid_transceive (ccid_driver_t handle,
{ /* Reponse does not match our sequence number. */
msg = send_buffer;
tpdu = msg+10;
- tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
+ /* NAD: DAD=1, SAD=0 */
+ tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4 | 2); /* R-block */
tpdu[2] = 0;
tpdulen = 3;
- for (crc=0,i=0,p=tpdu; i < tpdulen; i++)
- crc ^= *p++;
- tpdu[tpdulen++] = crc;
+ edc = compute_edc (tpdu, tpdulen, use_crc);
+ if (use_crc)
+ tpdu[tpdulen++] = (edc >> 8);
+ tpdu[tpdulen++] = edc;
continue;
}
@@ -974,20 +1146,27 @@ ccid_transceive (ccid_driver_t handle,
msg = send_buffer;
tpdu = msg+10;
- tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
+ /* NAD: DAD=1, SAD=0 */
+ tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4); /* R-block */
tpdu[2] = 0;
tpdulen = 3;
- for (crc=0,i=0,p=tpdu; i < tpdulen; i++)
- crc ^= *p++;
- tpdu[tpdulen++] = crc;
-
+ edc = compute_edc (tpdu, tpdulen, use_crc);
+ if (use_crc)
+ tpdu[tpdulen++] = (edc >> 8);
+ tpdu[tpdulen++] = edc;
}
else if ((tpdu[1] & 0xc0) == 0x80)
{ /* This is a R-block. */
if ( (tpdu[1] & 0x0f))
{ /* Error: repeat last block */
+ if (++retries > 3)
+ {
+ DEBUGOUT ("3 failed retries\n");
+ return -1;
+ }
msg = send_buffer;
+ tpdulen = last_tpdulen;
}
else if (sending && !!(tpdu[1] & 0x40) == handle->t1_ns)
{ /* Reponse does not match our sequence number. */
@@ -996,6 +1175,7 @@ ccid_transceive (ccid_driver_t handle,
}
else if (sending)
{ /* Send next chunk. */
+ retries = 0;
msg = send_buffer;
next_chunk = 1;
handle->t1_ns ^= 1;
@@ -1008,6 +1188,7 @@ ccid_transceive (ccid_driver_t handle,
}
else
{ /* This is a S-block. */
+ retries = 0;
DEBUGOUT_2 ("T1 S-block %s received cmd=%d\n",
(tpdu[1] & 0x20)? "response": "request",
(tpdu[1] & 0x1f));
@@ -1016,14 +1197,16 @@ ccid_transceive (ccid_driver_t handle,
unsigned char bwi = tpdu[3];
msg = send_buffer;
tpdu = msg+10;
- tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
+ /* NAD: DAD=1, SAD=0 */
+ tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = (0xc0 | 0x20 | 3); /* S-block response */
tpdu[2] = 1;
tpdu[3] = bwi;
tpdulen = 4;
- for (crc=0,i=0,p=tpdu; i < tpdulen; i++)
- crc ^= *p++;
- tpdu[tpdulen++] = crc;
+ edc = compute_edc (tpdu, tpdulen, use_crc);
+ if (use_crc)
+ tpdu[tpdulen++] = (edc >> 8);
+ tpdu[tpdulen++] = edc;
DEBUGOUT_1 ("T1 waittime extension of bwi=%d\n", bwi);
}
else
@@ -1043,6 +1226,7 @@ main (int argc, char **argv)
{
int rc;
ccid_driver_t ccid;
+ unsigned int slotstat;
rc = ccid_open_reader (&ccid, 0);
if (rc)
@@ -1056,7 +1240,7 @@ main (int argc, char **argv)
ccid_poll (ccid);
fputs ("getting slot status ...\n", stderr);
- rc = ccid_slot_status (ccid);
+ rc = ccid_slot_status (ccid, &slotstat);
if (rc)
return 1;
diff --git a/scd/ccid-driver.h b/scd/ccid-driver.h
index e33be55d9..8b86eb1a5 100644
--- a/scd/ccid-driver.h
+++ b/scd/ccid-driver.h
@@ -63,6 +63,7 @@ int ccid_open_reader (ccid_driver_t *handle, int readerno);
int ccid_close_reader (ccid_driver_t handle);
int ccid_get_atr (ccid_driver_t handle,
unsigned char *atr, size_t maxatrlen, size_t *atrlen);
+int ccid_slot_status (ccid_driver_t handle, int *statusbits);
int ccid_transceive (ccid_driver_t handle,
const unsigned char *apdu, size_t apdulen,
unsigned char *resp, size_t maxresplen, size_t *nresp);
diff --git a/scd/command.c b/scd/command.c
index 4746e11b5..06ff6d2e2 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -56,14 +56,11 @@ has_option (const char *line, const char *name)
}
-
-
-/* Note, that this reset_notify is also used for cleanup purposes. */
+/* Reset the card and free the application context. With DO_CLOSE set
+ to true, close the reader and don't do just a reset. */
static void
-reset_notify (ASSUAN_CONTEXT ctx)
+do_reset (ctrl_t ctrl, int do_close)
{
- CTRL ctrl = assuan_get_pointer (ctx);
-
if (ctrl->card_ctx)
{
card_close (ctrl->card_ctx);
@@ -73,13 +70,28 @@ reset_notify (ASSUAN_CONTEXT ctx)
}
if (ctrl->app_ctx)
{
- int slot = ctrl->app_ctx->slot;
release_application (ctrl->app_ctx);
ctrl->app_ctx = NULL;
- apdu_close_reader (slot);
+ }
+ if (ctrl->reader_slot != -1)
+ {
+ if (do_close || apdu_reset (ctrl->reader_slot))
+ {
+ apdu_close_reader (ctrl->reader_slot);
+ ctrl->reader_slot = -1;
+ }
}
}
+
+static void
+reset_notify (ASSUAN_CONTEXT ctx)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+
+ do_reset (ctrl, 0);
+}
+
static int
option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
@@ -92,7 +104,7 @@ option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
function returns an Assuan error, so don't map the error a second
time */
static AssuanError
-open_card (CTRL ctrl, const char *apptype)
+open_card (ctrl_t ctrl, const char *apptype)
{
int slot;
@@ -101,13 +113,13 @@ open_card (CTRL ctrl, const char *apptype)
if (ctrl->card_ctx)
return 0; /* Already initialized using a card context. */
- slot = apdu_open_reader (opt.reader_port);
+ if (ctrl->reader_slot != -1)
+ slot = ctrl->reader_slot;
+ else
+ slot = apdu_open_reader (opt.reader_port);
+ ctrl->reader_slot = slot;
if (slot != -1)
- {
- ctrl->app_ctx = select_application (ctrl, slot, apptype);
- if (!ctrl->app_ctx)
- apdu_close_reader (slot);
- }
+ ctrl->app_ctx = select_application (ctrl, slot, apptype);
if (!ctrl->app_ctx)
{ /* No application found - fall back to old mode. */
/* Note that we should rework the old code to use the
@@ -1084,6 +1096,12 @@ scd_command_handler (int listen_fd)
if (DBG_ASSUAN)
assuan_set_log_stream (ctx, log_get_stream ());
+ /* We open the reader right at startup so that the ticker is able to
+ update the status file. */
+ if (ctrl.reader_slot == -1)
+ ctrl.reader_slot = apdu_open_reader (opt.reader_port);
+
+ /* Command processing loop. */
for (;;)
{
rc = assuan_accept (ctx);
@@ -1104,7 +1122,7 @@ scd_command_handler (int listen_fd)
continue;
}
}
- reset_notify (ctx); /* used for cleanup */
+ do_reset (&ctrl, 1); /* Cleanup. */
assuan_deinit_server (ctx);
}
@@ -1156,3 +1174,22 @@ send_status_info (CTRL ctrl, const char *keyword, ...)
va_end (arg_ptr);
}
+
+
+void
+scd_update_reader_status_file (void)
+{
+ int slot;
+ int used;
+ unsigned int status, changed;
+
+ /* Note, that we only try to get the status, becuase it does not
+ make sense to wait here for a operation to complete. If we are
+ so busy working with the card, delays in the status file updated
+ are should be acceptable. */
+ for (slot=0; !apdu_enum_reader (slot, &used); slot++)
+ if (used && !apdu_get_status (slot, 0, &status, &changed))
+ {
+ log_info ("status of slot %d is %u\n", slot, status);
+ }
+}
diff --git a/scd/iso7816.c b/scd/iso7816.c
index 24361d148..fd3f0485c 100644
--- a/scd/iso7816.c
+++ b/scd/iso7816.c
@@ -82,6 +82,9 @@ map_sw (int sw)
case SW_HOST_OUT_OF_CORE: ec = GPG_ERR_ENOMEM; break;
case SW_HOST_INV_VALUE: ec = GPG_ERR_INV_VALUE; break;
case SW_HOST_INCOMPLETE_CARD_RESPONSE: ec = GPG_ERR_CARD; break;
+ case SW_HOST_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break;
+ case SW_HOST_LOCKING_FAILED: ec = GPG_ERR_BUG; break;
+ case SW_HOST_BUSY: ec = GPG_ERR_EBUSY; break;
default:
if ((sw & 0x010000))
ec = GPG_ERR_GENERAL; /* Should not happen. */
diff --git a/scd/pcsc-wrapper.c b/scd/pcsc-wrapper.c
new file mode 100644
index 000000000..4f47ee95c
--- /dev/null
+++ b/scd/pcsc-wrapper.c
@@ -0,0 +1,631 @@
+/* pcsc-wrapper.c - Wrapper for ccessing the PC/SC service
+ * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+/*
+ This wrapper is required to handle problems with the libpscslite
+ library. That library assumes that pthreads are used and fails
+ badly if one tries to use it with a procerss using Pth.
+
+ The operation model is pretty simple: It reads requests from stdin
+ and returns the answer on stdout. There is no direct mapping to the
+ pcsc interface but to a higher level one which resembles the code
+ used in scdaemon (apdu.c) when not using Pth or while running under
+ Windows.
+
+ The interface is binary consisting of a command tag and the length
+ of the parameter list. The calling process needs to pass the
+ version number of the interface on the command line to make sure
+ that both agree on the same interface. For each port a separate
+ instance of this process needs to be started.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <dlfcn.h>
+
+
+#define PGM "pcsc-wrapper"
+
+/* Allow for a standalone build. */
+#ifdef VERSION
+#define MYVERSION_LINE PGM " (GnuPG) " VERSION
+#define BUGREPORT_LINE "\nReport bugs to <bug-gnupg@gnu.org>.\n"
+#else
+#define MYVERSION_LINE PGM
+#define BUGREPORT_LINE ""
+#endif
+
+#define DEFAULT_PCSC_DRIVER "libpcsclite.so"
+
+
+static int verbose;
+
+
+/* PC/SC constants and function pointer. */
+#define PCSC_SCOPE_USER 0
+#define PCSC_SCOPE_TERMINAL 1
+#define PCSC_SCOPE_SYSTEM 2
+#define PCSC_SCOPE_GLOBAL 3
+
+#define PCSC_PROTOCOL_T0 1
+#define PCSC_PROTOCOL_T1 2
+#define PCSC_PROTOCOL_RAW 4
+
+#define PCSC_SHARE_EXCLUSIVE 1
+#define PCSC_SHARE_SHARED 2
+#define PCSC_SHARE_DIRECT 3
+
+#define PCSC_LEAVE_CARD 0
+#define PCSC_RESET_CARD 1
+#define PCSC_UNPOWER_CARD 2
+#define PCSC_EJECT_CARD 3
+
+struct pcsc_io_request_s {
+ unsigned long protocol;
+ unsigned long pci_len;
+};
+
+typedef struct pcsc_io_request_s *pcsc_io_request_t;
+
+
+static int driver_is_open; /* True if the PC/SC driver has been
+ initialzied and is ready for
+ operations. The follwoing variables
+ are then valid. */
+static unsigned long pcsc_context; /* The current PC/CS context. */
+static unsigned long pcsc_card;
+static unsigned long pcsc_protocol;
+static unsigned char current_atr[33];
+static size_t current_atrlen;
+
+long (* pcsc_establish_context) (unsigned long scope,
+ const void *reserved1,
+ const void *reserved2,
+ unsigned long *r_context);
+long (* pcsc_release_context) (unsigned long context);
+long (* pcsc_list_readers) (unsigned long context,
+ const char *groups,
+ char *readers, unsigned long*readerslen);
+long (* pcsc_connect) (unsigned long context,
+ const char *reader,
+ unsigned long share_mode,
+ unsigned long preferred_protocols,
+ unsigned long *r_card,
+ unsigned long *r_active_protocol);
+long (* pcsc_disconnect) (unsigned long card,
+ unsigned long disposition);
+long (* pcsc_status) (unsigned long card,
+ char *reader, unsigned long *readerlen,
+ unsigned long *r_state,
+ unsigned long *r_protocol,
+ unsigned char *atr, unsigned long *atrlen);
+long (* pcsc_begin_transaction) (unsigned long card);
+long (* pcsc_end_transaction) (unsigned long card);
+long (* pcsc_transmit) (unsigned long card,
+ const pcsc_io_request_t send_pci,
+ const unsigned char *send_buffer,
+ unsigned long send_len,
+ pcsc_io_request_t recv_pci,
+ unsigned char *recv_buffer,
+ unsigned long *recv_len);
+long (* pcsc_set_timeout) (unsigned long context,
+ unsigned long timeout);
+
+
+
+static void
+bad_request (const char *type)
+{
+ fprintf (stderr, PGM ": bad `%s' request\n", type);
+ exit (1);
+}
+
+static void
+request_failed (int err)
+{
+ if (!err)
+ err = -1;
+
+ putchar (0x81); /* Simple error/success response. */
+
+ putchar (0);
+ putchar (0);
+ putchar (0);
+ putchar (4);
+
+ putchar ((err >> 24) & 0xff);
+ putchar ((err >> 16) & 0xff);
+ putchar ((err >> 8) & 0xff);
+ putchar ((err ) & 0xff);
+
+ fflush (stdout);
+}
+
+
+static void
+request_succeeded (const void *buffer, size_t buflen)
+{
+ size_t len;
+
+ putchar (0x81); /* Simple error/success response. */
+
+ len = 4 + buflen;
+ putchar ((len >> 24) & 0xff);
+ putchar ((len >> 16) & 0xff);
+ putchar ((len >> 8) & 0xff);
+ putchar ((len ) & 0xff);
+
+ /* Error code. */
+ putchar (0);
+ putchar (0);
+ putchar (0);
+ putchar (0);
+
+ /* Optional reponse string. */
+ if (buffer)
+ fwrite (buffer, buflen, 1, stdout);
+
+ fflush (stdout);
+}
+
+
+
+static unsigned long
+read_32 (FILE *fp)
+{
+ int c1, c2, c3, c4;
+
+ c1 = getc (stdin);
+ c2 = getc (stdin);
+ c3 = getc (stdin);
+ c4 = getc (stdin);
+ if (c1 == EOF || c2 == EOF || c3 == EOF || c4 == EOF)
+ {
+ fprintf (stderr, PGM ": premature EOF while parsing request\n");
+ exit (1);
+ }
+ return (c1 << 24) | (c2 << 16) | (c3 << 8) | c4;
+}
+
+
+
+static const char *
+pcsc_error_string (long err)
+{
+ const char *s;
+
+ if (!err)
+ return "okay";
+ if ((err & 0x80100000) != 0x80100000)
+ return "invalid PC/SC error code";
+ err &= 0xffff;
+ switch (err)
+ {
+ case 0x0002: s = "cancelled"; break;
+ case 0x000e: s = "can't dispose"; break;
+ case 0x0008: s = "insufficient buffer"; break;
+ case 0x0015: s = "invalid ATR"; break;
+ case 0x0003: s = "invalid handle"; break;
+ case 0x0004: s = "invalid parameter"; break;
+ case 0x0005: s = "invalid target"; break;
+ case 0x0011: s = "invalid value"; break;
+ case 0x0006: s = "no memory"; break;
+ case 0x0013: s = "comm error"; break;
+ case 0x0001: s = "internal error"; break;
+ case 0x0014: s = "unknown error"; break;
+ case 0x0007: s = "waited too long"; break;
+ case 0x0009: s = "unknown reader"; break;
+ case 0x000a: s = "timeout"; break;
+ case 0x000b: s = "sharing violation"; break;
+ case 0x000c: s = "no smartcard"; break;
+ case 0x000d: s = "unknown card"; break;
+ case 0x000f: s = "proto mismatch"; break;
+ case 0x0010: s = "not ready"; break;
+ case 0x0012: s = "system cancelled"; break;
+ case 0x0016: s = "not transacted"; break;
+ case 0x0017: s = "reader unavailable"; break;
+ case 0x0065: s = "unsupported card"; break;
+ case 0x0066: s = "unresponsive card"; break;
+ case 0x0067: s = "unpowered card"; break;
+ case 0x0068: s = "reset card"; break;
+ case 0x0069: s = "removed card"; break;
+ case 0x006a: s = "inserted card"; break;
+ case 0x001f: s = "unsupported feature"; break;
+ case 0x0019: s = "PCI too small"; break;
+ case 0x001a: s = "reader unsupported"; break;
+ case 0x001b: s = "duplicate reader"; break;
+ case 0x001c: s = "card unsupported"; break;
+ case 0x001d: s = "no service"; break;
+ case 0x001e: s = "service stopped"; break;
+ default: s = "unknown PC/SC error code"; break;
+ }
+ return s;
+}
+
+static void
+load_pcsc_driver (const char *libname)
+{
+ void *handle;
+
+ handle = dlopen (libname, RTLD_LAZY);
+ if (!handle)
+ {
+ fprintf (stderr, PGM ": failed to open driver `%s': %s",
+ libname, dlerror ());
+ exit (1);
+ }
+
+ pcsc_establish_context = dlsym (handle, "SCardEstablishContext");
+ pcsc_release_context = dlsym (handle, "SCardReleaseContext");
+ pcsc_list_readers = dlsym (handle, "SCardListReaders");
+ pcsc_connect = dlsym (handle, "SCardConnect");
+ pcsc_disconnect = dlsym (handle, "SCardDisconnect");
+ pcsc_status = dlsym (handle, "SCardStatus");
+ pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction");
+ pcsc_end_transaction = dlsym (handle, "SCardEndTransaction");
+ pcsc_transmit = dlsym (handle, "SCardTransmit");
+ pcsc_set_timeout = dlsym (handle, "SCardSetTimeout");
+
+ if (!pcsc_establish_context
+ || !pcsc_release_context
+ || !pcsc_list_readers
+ || !pcsc_connect
+ || !pcsc_disconnect
+ || !pcsc_status
+ || !pcsc_begin_transaction
+ || !pcsc_end_transaction
+ || !pcsc_transmit
+ /* || !pcsc_set_timeout */)
+ {
+ /* Note that set_timeout is currently not used and also not
+ available under Windows. */
+ fprintf (stderr,
+ "apdu_open_reader: invalid PC/SC driver "
+ "(%d%d%d%d%d%d%d%d%d%d)\n",
+ !!pcsc_establish_context,
+ !!pcsc_release_context,
+ !!pcsc_list_readers,
+ !!pcsc_connect,
+ !!pcsc_disconnect,
+ !!pcsc_status,
+ !!pcsc_begin_transaction,
+ !!pcsc_end_transaction,
+ !!pcsc_transmit,
+ !!pcsc_set_timeout );
+ dlclose (handle);
+ exit (1);
+ }
+}
+
+
+
+
+/* Handle a open request. The argument is expected to be a string
+ with the port indentification. ARGBUF is always guaranteed to be
+ terminted by a 0 which is not counted in ARGLEN. We may modifiy
+ ARGBUF. */
+static void
+handle_open (unsigned char *argbuf, size_t arglen)
+{
+ long err;
+ const char * portstr;
+ char *list = NULL;
+ unsigned long nreader, listlen, atrlen;
+ char *p;
+ unsigned long card_state, card_protocol;
+ unsigned char atr[33];
+
+ /* Make sure there is only the port string */
+ if (arglen != strlen (argbuf))
+ bad_request ("OPEN");
+ portstr = argbuf;
+
+ if (driver_is_open)
+ {
+ fprintf (stderr, PGM ": PC/SC has already been opened\n");
+ request_failed (-1);
+ }
+
+ err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL, &pcsc_context);
+ if (err)
+ {
+ fprintf (stderr, PGM": pcsc_establish_context failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ request_failed (err);
+ return;
+ }
+
+ err = pcsc_list_readers (pcsc_context, NULL, NULL, &nreader);
+ if (!err)
+ {
+ list = malloc (nreader+1); /* Better add 1 for safety reasons. */
+ if (!list)
+ {
+ fprintf (stderr, PGM": error allocating memory for reader list\n");
+ exit (1);
+ }
+ err = pcsc_list_readers (pcsc_context, NULL, list, &nreader);
+ }
+ if (err)
+ {
+ fprintf (stderr, PGM": pcsc_list_readers failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ pcsc_release_context (pcsc_context);
+ free (list);
+ request_failed (err);
+ return;
+ }
+
+ listlen = nreader;
+ p = list;
+ while (nreader)
+ {
+ if (!*p && !p[1])
+ break;
+ fprintf (stderr, PGM": detected reader `%s'\n", p);
+ if (nreader < (strlen (p)+1))
+ {
+ fprintf (stderr, PGM": invalid response from pcsc_list_readers\n");
+ break;
+ }
+ nreader -= strlen (p)+1;
+ p += strlen (p) + 1;
+ }
+
+ err = pcsc_connect (pcsc_context,
+ portstr && *portstr? portstr : list,
+ PCSC_SHARE_EXCLUSIVE,
+ PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
+ &pcsc_card,
+ &pcsc_protocol);
+ if (err)
+ {
+ fprintf (stderr, PGM": pcsc_connect failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ pcsc_release_context (pcsc_context);
+ free (list);
+ request_failed (err);
+ return;
+ }
+
+ atrlen = 32;
+ /* (We need to pass a dummy buffer. We use LIST because it ought to
+ be large enough.) */
+ err = pcsc_status (pcsc_card,
+ list, &listlen,
+ &card_state, &card_protocol,
+ atr, &atrlen);
+ free (list);
+ if (err)
+ {
+ fprintf (stderr, PGM": pcsc_status failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ pcsc_release_context (pcsc_context);
+ request_failed (err);
+ return;
+ }
+ if (atrlen >= sizeof atr || atrlen >= sizeof current_atr)
+ {
+ fprintf (stderr, PGM": ATR returned by pcsc_status is too large\n");
+ exit (4);
+ }
+ memcpy (current_atr, atr, atrlen);
+ current_atrlen = atrlen;
+ driver_is_open = 1;
+ request_succeeded (current_atr, current_atrlen);
+}
+
+
+
+/* Handle a close request. We expect no arguments. We may modifiy
+ ARGBUF. */
+static void
+handle_close (unsigned char *argbuf, size_t arglen)
+{
+ if (!driver_is_open)
+ {
+ fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
+ request_failed (-1);
+ }
+
+ pcsc_release_context (pcsc_context);
+
+ request_succeeded (NULL, 0);
+}
+
+
+
+/* Handle a transmit request. The argument is expected to be a bufer
+ with the APDU. We may modifiy ARGBUF. */
+static void
+handle_transmit (unsigned char *argbuf, size_t arglen)
+{
+ long err;
+ struct pcsc_io_request_s send_pci;
+ unsigned long recv_len;
+ unsigned char buffer[1024];
+
+ /* The apdu should at least be one byte. */
+ if (!arglen)
+ bad_request ("TRANSMIT");
+
+ if (!driver_is_open)
+ {
+ fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
+ request_failed (-1);
+ }
+
+ if ((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 = sizeof (buffer);
+ err = pcsc_transmit (pcsc_card, &send_pci, argbuf, arglen,
+ NULL, buffer, &recv_len);
+ if (err)
+ {
+ if (verbose)
+ fprintf (stderr, PGM": pcsc_transmit failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ request_failed (err);
+ return;
+ }
+ request_succeeded (buffer, recv_len);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+static void
+print_version (int with_help)
+{
+ fputs (MYVERSION_LINE "\n"
+ "Copyright (C) 2004 Free Software Foundation, Inc.\n"
+ "This program comes with ABSOLUTELY NO WARRANTY.\n"
+ "This is free software, and you are welcome to redistribute it\n"
+ "under certain conditions. See the file COPYING for details.\n",
+ stdout);
+
+ if (with_help)
+ fputs ("\n"
+ "Usage: " PGM " [OPTIONS] API-NUMBER [LIBNAME]\n"
+ "Helper to connect scdaemon to the PC/SC library\n"
+ "\n"
+ " --verbose enable extra informational output\n"
+ " --version print version of the program and exit\n"
+ " --help display this help and exit\n"
+ BUGREPORT_LINE, stdout );
+
+ exit (0);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ int last_argc = -1;
+ int api_number = 0;
+ int c;
+
+ if (argc)
+ {
+ argc--; argv++;
+ }
+ while (argc && last_argc != argc )
+ {
+ last_argc = argc;
+ if (!strcmp (*argv, "--"))
+ {
+ argc--; argv++;
+ break;
+ }
+ else if (!strcmp (*argv, "--version"))
+ print_version (0);
+ else if (!strcmp (*argv, "--help"))
+ print_version (1);
+ else if (!strcmp (*argv, "--verbose"))
+ {
+ verbose = 1;
+ argc--; argv++;
+ }
+ }
+ if (argc != 1 && argc != 2)
+ {
+ fprintf (stderr, "usage: " PGM " API-NUMBER [LIBNAME]\n");
+ exit (1);
+ }
+
+ api_number = atoi (*argv);
+ argv++; argc--;
+ if (api_number != 1)
+ {
+ fprintf (stderr, PGM ": api-number %d is not valid\n", api_number);
+ exit (1);
+ }
+
+ load_pcsc_driver (argc? *argv : DEFAULT_PCSC_DRIVER);
+
+ while ((c = getc (stdin)) != EOF)
+ {
+ size_t arglen;
+ unsigned char argbuffer[2048];
+
+ arglen = read_32 (stdin);
+ if (arglen >= sizeof argbuffer - 1)
+ {
+ fprintf (stderr, PGM ": request too long\n");
+ exit (1);
+ }
+ if (arglen && fread (argbuffer, arglen, 1, stdin) != 1)
+ {
+ fprintf (stderr, PGM ": error reading request: %s\n",
+ strerror (errno));
+ exit (1);
+ }
+ argbuffer[arglen] = 0;
+ switch (c)
+ {
+ case 1:
+ handle_open (argbuffer, arglen);
+ break;
+
+ case 2:
+ handle_close (argbuffer, arglen);
+ exit (0);
+ break;
+
+ case 3:
+ handle_transmit (argbuffer, arglen);
+ break;
+
+ default:
+ fprintf (stderr, PGM ": invalid request 0x%02X\n", c);
+ exit (1);
+ }
+ free (argbuffer);
+ }
+ return 0;
+}
+
+
+
+/*
+Local Variables:
+compile-command: "gcc -Wall -g -o pcsc-wrapper pcsc-wrapper.c -ldl"
+End:
+*/
diff --git a/scd/scdaemon.c b/scd/scdaemon.c
index bc9d90b72..aabd38861 100644
--- a/scd/scdaemon.c
+++ b/scd/scdaemon.c
@@ -33,6 +33,9 @@
#include <sys/un.h>
#include <unistd.h>
#include <signal.h>
+#ifdef USE_GNU_PTH
+# include <pth.h>
+#endif
#define JNLIB_NEED_LOG_LOGV
#include "scdaemon.h"
@@ -131,12 +134,24 @@ static ARGPARSE_OPTS opts[] = {
static volatile int caught_fatal_sig = 0;
+/* Flag to indicate that a shutdown was requested. */
+static int shutdown_pending;
+
/* It is possible that we are currently running under setuid permissions */
static int maybe_setuid = 1;
/* Name of the communication socket */
static char socket_name[128];
+
+#ifdef USE_GNU_PTH
+/* Pth wrapper function definitions. */
+GCRY_THREAD_OPTION_PTH_IMPL;
+
+static void *ticker_thread (void *arg);
+#endif /*USE_GNU_PTH*/
+
+
static const char *
my_strusage (int level)
{
@@ -287,6 +302,7 @@ main (int argc, char **argv )
{
ARGPARSE_ARGS pargs;
int orig_argc;
+ gpg_error_t err;
int may_coredump;
char **orig_argv;
FILE *configfp = NULL;
@@ -318,7 +334,18 @@ main (int argc, char **argv )
i18n_init ();
- /* check that the libraries are suitable. Do it here because
+ /* Libgcrypt requires us to register the threading model first.
+ Note that this will also do the pth_init. */
+#ifdef USE_GNU_PTH
+ err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth);
+ if (err)
+ {
+ log_fatal ("can't register GNU Pth with Libgcrypt: %s\n",
+ gpg_strerror (err));
+ }
+#endif /*USE_GNU_PTH*/
+
+ /* Check that the libraries are suitable. Do it here because
the option parsing may need services of the library */
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
{
@@ -568,7 +595,21 @@ main (int argc, char **argv )
if (pipe_server)
- { /* this is the simple pipe based server */
+ { /* This is the simple pipe based server */
+#ifdef USE_GNU_PTH
+ pth_attr_t tattr;
+
+ tattr = pth_attr_new();
+ pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0);
+ pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 64*1024);
+ pth_attr_set (tattr, PTH_ATTR_NAME, "ticker");
+
+ if (!pth_spawn (tattr, ticker_thread, NULL))
+ {
+ log_error ("error spawning ticker thread: %s\n", strerror (errno));
+ scd_exit (2);
+ }
+#endif /*USE_GNU_PTH*/
scd_command_handler (-1);
}
else if (!is_daemon)
@@ -780,6 +821,115 @@ scd_exit (int rc)
void
scd_init_default_ctrl (CTRL ctrl)
{
+ ctrl->reader_slot = -1;
+}
+
+
+#ifdef USE_GNU_PTH
+
+static void
+handle_signal (int signo)
+{
+ switch (signo)
+ {
+ case SIGHUP:
+ log_info ("SIGHUP received - "
+ "re-reading configuration and resetting cards\n");
+/* reread_configuration (); */
+ break;
+
+ case SIGUSR1:
+ if (opt.verbose < 5)
+ opt.verbose++;
+ log_info ("SIGUSR1 received - verbosity set to %d\n", opt.verbose);
+ break;
+
+ case SIGUSR2:
+ if (opt.verbose)
+ opt.verbose--;
+ log_info ("SIGUSR2 received - verbosity set to %d\n", opt.verbose );
+ break;
+
+ case SIGTERM:
+ if (!shutdown_pending)
+ log_info ("SIGTERM received - shutting down ...\n");
+ else
+ log_info ("SIGTERM received - still %ld running threads\n",
+ pth_ctrl( PTH_CTRL_GETTHREADS ));
+ shutdown_pending++;
+ if (shutdown_pending > 2)
+ {
+ log_info ("shutdown forced\n");
+ log_info ("%s %s stopped\n", strusage(11), strusage(13) );
+ cleanup ();
+ scd_exit (0);
+ }
+ break;
+
+ case SIGINT:
+ log_info ("SIGINT received - immediate shutdown\n");
+ log_info( "%s %s stopped\n", strusage(11), strusage(13));
+ cleanup ();
+ scd_exit (0);
+ break;
+ default:
+ log_info ("signal %d received - no action defined\n", signo);
+ }
}
+static void
+handle_tick (void)
+{
+ scd_update_reader_status_file ();
+}
+
+static void *
+ticker_thread (void *dummy_arg)
+{
+ pth_event_t sigs_ev, time_ev = NULL;
+ sigset_t sigs;
+ int signo;
+
+ sigemptyset (&sigs );
+ sigaddset (&sigs, SIGHUP);
+ sigaddset (&sigs, SIGUSR1);
+ sigaddset (&sigs, SIGUSR2);
+ sigaddset (&sigs, SIGINT);
+ sigaddset (&sigs, SIGTERM);
+ sigs_ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo);
+
+ for (;;)
+ {
+ if (!time_ev)
+ {
+ time_ev = pth_event (PTH_EVENT_TIME, pth_timeout (2, 0));
+ if (time_ev)
+ pth_event_concat (sigs_ev, time_ev, NULL);
+ }
+
+ if (pth_wait (sigs_ev) < 1)
+ continue;
+
+ if (
+#ifdef PTH_STATUS_OCCURRED /* This is Pth 2 */
+ pth_event_status (sigs_ev) == PTH_STATUS_OCCURRED
+#else
+ pth_event_occurred (sigs_ev)
+#endif
+ )
+ handle_signal (signo);
+
+ /* Always run the ticker. */
+ if (!shutdown_pending)
+ {
+ pth_event_isolate (sigs_ev);
+ pth_event_free (time_ev, PTH_FREE_ALL);
+ time_ev = NULL;
+ handle_tick ();
+ }
+ }
+
+ pth_event_free (sigs_ev, PTH_FREE_ALL);
+}
+#endif /*USE_GNU_PTH*/
diff --git a/scd/scdaemon.h b/scd/scdaemon.h
index 098738508..1dd32ae90 100644
--- a/scd/scdaemon.h
+++ b/scd/scdaemon.h
@@ -79,6 +79,7 @@ struct app_ctx_s;
struct server_control_s {
struct server_local_s *server_local;
+ int reader_slot; /* Slot of the open reader or -1 if not open. */
struct card_ctx_s *card_ctx;
struct app_ctx_s *app_ctx;
struct {
@@ -101,6 +102,7 @@ void scd_init_default_ctrl (CTRL ctrl);
/*-- command.c --*/
void scd_command_handler (int);
void send_status_info (CTRL ctrl, const char *keyword, ...);
+void scd_update_reader_status_file (void);
/*-- card.c --*/
int card_open (CARD *rcard);