* pcsc-wrapper.c: New.
authorWerner Koch <wk@gnupg.org>
Tue, 20 Apr 2004 14:17:10 +0000 (14:17 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 20 Apr 2004 14:17:10 +0000 (14:17 +0000)
* 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.

scd/ChangeLog
scd/Makefile.am
scd/apdu.c
scd/apdu.h
scd/ccid-driver.c
scd/ccid-driver.h
scd/command.c
scd/iso7816.c
scd/pcsc-wrapper.c [new file with mode: 0644]
scd/scdaemon.c
scd/scdaemon.h

index 895aae9..9b04d5e 100644 (file)
@@ -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.
index da59a19..c3c603d 100644 (file)
 ## 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
index 7843fd5..1b5ebc3 100644 (file)
@@ -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.
  *
 #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
 #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). */
 #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,
 
 
 \f
+#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);
  
index fd7634f..f74bab7 100644 (file)
@@ -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,
index b398e3c..1c81678 100644 (file)
@@ -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.
 
 /* 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) \
 #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;
 }
 
 
 \f
+
+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;
 
index e33be55..8b86eb1 100644 (file)
@@ -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);
index 4746e11..06ff6d2 100644 (file)
@@ -56,14 +56,11 @@ has_option (const char *line, const char *name)
 }
 
 
-
-\f
-/* 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;
+        }
     }
 }
 
+\f
+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);
+      }
+}
index 24361d1..fd3f048 100644 (file)
@@ -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 (file)
index 0000000..4f47ee9
--- /dev/null
@@ -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:
+*/
index bc9d90b..aabd388 100644 (file)
@@ -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*/
index 0987385..1dd32ae 100644 (file)
@@ -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);