Preparing 2.0.1
[gnupg.git] / scd / apdu.c
index 33b0802..ac2f3c4 100644 (file)
  *
  * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  *
  * $Id$
  */
 
+/* NOTE: This module is also used by other software, thus the use of
+   the macro USE_GNU_PTH is mandatory.  For GnuPG this macro is
+   guaranteed to be defined true. */
+
 #include <config.h>
 #include <errno.h>
 #include <stdio.h>
 #include <assert.h>
 #include <signal.h>
 #ifdef USE_GNU_PTH
-# include <pth.h>
 # include <unistd.h>
 # include <fcntl.h>
-#endif
-#ifdef HAVE_OPENSC
-# include <opensc/opensc.h>
-# ifdef USE_GNU_PTH
-# undef USE_GNU_PTH
-# endif
+# include <pth.h>
 #endif
 
+
 /* If requested include the definitions for the remote APDU protocol
    code. */
 #ifdef USE_G10CODE_RAPDU
 #include "ccid-driver.h"
 
 
-/* To to conflicting use of threading libraries we usually can't link
+/* Due to conflicting use of threading libraries we usually can't link
    against libpcsclite.   Instead we use a wrapper program.  */
 #ifdef USE_GNU_PTH
-#ifndef HAVE_W32_SYSTEM
+#if !defined(HAVE_W32_SYSTEM) && !defined(__CYGWIN__)
 #define NEED_PCSC_WRAPPER 1
 #endif
 #endif
@@ -78,7 +78,7 @@
 #define MAX_READER 4 /* Number of readers we support concurrently. */
 
 
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__CYGWIN__)
 #define DLSTDCALL __stdcall
 #else
 #define DLSTDCALL
 #define MAX_OPEN_FDS 20
 #endif
 
+/* Helper to pass patrameters related to keypad based operations. */
+struct pininfo_s
+{
+  int mode;
+  int minlen;
+  int maxlen;
+  int padlen;
+};
 
 /* A structure to collect information pertaining to one reader
    slot. */
@@ -103,7 +111,8 @@ struct reader_table_s {
   int (*reset_reader)(int);
   int (*get_status_reader)(int, unsigned int *);
   int (*send_apdu_reader)(int,unsigned char *,size_t,
-                          unsigned char *, size_t *);
+                          unsigned char *, size_t *, struct pininfo_s *);
+  int (*check_keypad)(int, int, int, int, int, int);
   void (*dump_status_reader)(int);
 
   struct {
@@ -119,12 +128,6 @@ struct reader_table_s {
     pid_t pid;
 #endif /*NEED_PCSC_WRAPPER*/
   } pcsc;
-#ifdef HAVE_OPENSC
-  struct {
-    struct sc_context *ctx;
-    struct sc_card *scard;
-  } osc;
-#endif /*HAVE_OPENSC*/
 #ifdef USE_G10CODE_RAPDU
   struct {
     rapdu_t handle;
@@ -133,6 +136,7 @@ struct reader_table_s {
   char *rdrname;     /* Name of the connected reader or NULL if unknown. */
   int last_status;
   int status;
+  int is_t0;         /* True if we know that we are running T=0. */
   unsigned char atr[33];
   size_t atrlen;           /* A zero length indicates that the ATR has
                               not yet been read; i.e. the card is not
@@ -218,6 +222,11 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn);
 #define PCSC_E_READER_UNAVAILABLE      0x80100017
 #define PCSC_W_REMOVED_CARD            0x80100069
 
+/* The PC/SC error is defined as a long as per specs.  Due to left
+   shifts bit 31 will get sign extended.  We use this mask to fix
+   it. */
+#define PCSC_ERR_MASK(a)  ((a) & 0xffffffff)
+
 
 struct pcsc_io_request_s
 {
@@ -270,7 +279,8 @@ long (* DLSTDCALL pcsc_status) (unsigned long card,
                                 unsigned long *r_protocol,
                                 unsigned char *atr, unsigned long *atrlen);
 long (* DLSTDCALL pcsc_begin_transaction) (unsigned long card);
-long (* DLSTDCALL pcsc_end_transaction) (unsigned long card);
+long (* DLSTDCALL pcsc_end_transaction) (unsigned long card,
+                                         unsigned long disposition);
 long (* DLSTDCALL pcsc_transmit) (unsigned long card,
                                   const pcsc_io_request_t send_pci,
                                   const unsigned char *send_buffer,
@@ -282,6 +292,9 @@ long (* DLSTDCALL pcsc_set_timeout) (unsigned long context,
                                      unsigned long timeout);
 
 
+/*  Prototypes.  */
+static int pcsc_get_status (int slot, unsigned int *status);
+
 
 \f
 /*
@@ -322,10 +335,12 @@ new_reader_slot (void)
   reader_table[reader].reset_reader = NULL;
   reader_table[reader].get_status_reader = NULL;
   reader_table[reader].send_apdu_reader = NULL;
+  reader_table[reader].check_keypad = NULL;
   reader_table[reader].dump_status_reader = NULL;
 
   reader_table[reader].used = 1;
   reader_table[reader].last_status = 0;
+  reader_table[reader].is_t0 = 1;
 #ifdef NEED_PCSC_WRAPPER
   reader_table[reader].pcsc.req_fd = -1;
   reader_table[reader].pcsc.rsp_fd = -1;
@@ -373,6 +388,7 @@ host_sw_string (long err)
     case SW_HOST_GENERAL_ERROR: return "general error";
     case SW_HOST_NO_READER: return "no reader";
     case SW_HOST_ABORTED: return "aborted";
+    case SW_HOST_NO_KEYPAD: return "no keypad"; 
     default: return "unknown host status error";
     }
 }
@@ -534,7 +550,7 @@ ct_get_status (int slot, unsigned int *status)
    set to BUFLEN.  Returns: CT API error code. */
 static int
 ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
-              unsigned char *buffer, size_t *buflen)
+              unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo)
 {
   int rc;
   unsigned char dad[1], sad[1];
@@ -597,6 +613,7 @@ open_ct_reader (int port)
   reader_table[reader].reset_reader = reset_ct_reader;
   reader_table[reader].get_status_reader = ct_get_status;
   reader_table[reader].send_apdu_reader = ct_send_apdu;
+  reader_table[reader].check_keypad = NULL;
   reader_table[reader].dump_status_reader = ct_dump_reader_status;
 
   dump_reader_status (reader);
@@ -727,7 +744,7 @@ pcsc_error_to_sw (long ec)
 {
   int rc;
 
-  switch (ec)
+  switch ( PCSC_ERR_MASK (ec) )
     {
     case 0:  rc = 0; break;
 
@@ -775,6 +792,7 @@ reset_pcsc_reader (int slot)
   size_t len;
   int i, n;
   unsigned char msgbuf[9];
+  unsigned int dummy_status;
   int sw = SW_HOST_CARD_IO_ERROR;
 
   slotp = reader_table + slot;
@@ -816,11 +834,13 @@ reset_pcsc_reader (int slot)
   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);
+      log_error ("PC/SC returned a too large ATR (len=%lx)\n",
+                 (unsigned long)len);
       sw = SW_HOST_GENERAL_ERROR;
       goto command_failed;
     }
-  err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+  err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
+                       | (msgbuf[7] << 8 ) | msgbuf[8]);
   if (err)
     {
       log_error ("PC/SC RESET failed: %s (0x%lx)\n",
@@ -847,6 +867,9 @@ reset_pcsc_reader (int slot)
     }
   slotp->atrlen = len;
 
+  /* Read the status so that IS_T0 will be set. */
+  pcsc_get_status (slot, &dummy_status);
+
   return 0;
 
  command_failed:
@@ -908,6 +931,7 @@ reset_pcsc_reader (int slot)
   if (atrlen >= DIM (reader_table[0].atr))
     log_bug ("ATR returned by pcsc_status is too large\n");
   reader_table[slot].atrlen = atrlen;
+  reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
 
   return 0;
 #endif /* !NEED_PCSC_WRAPPER */
@@ -923,7 +947,7 @@ pcsc_get_status (int slot, unsigned int *status)
   size_t len, full_len;
   int i, n;
   unsigned char msgbuf[9];
-  unsigned char buffer[12];
+  unsigned char buffer[16];
   int sw = SW_HOST_CARD_IO_ERROR;
 
   slotp = reader_table + slot;
@@ -963,7 +987,8 @@ pcsc_get_status (int slot, unsigned int *status)
       goto command_failed;
     }
   len -= 4; /* Already read the error code. */
-  err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+  err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
+                       | (msgbuf[7] << 8 ) | msgbuf[8]);
   if (err)
     {
       log_error ("pcsc_status failed: %s (0x%lx)\n",
@@ -974,14 +999,20 @@ pcsc_get_status (int slot, unsigned int *status)
 
   full_len = len;
 
-  n = 8 < len ? 8 : len;
-  if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != 8)
+  /* The current version returns 3 words but we allow also for old
+     versions returning only 2 words. */
+  n = 12 < len ? 12 : len;
+  if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len))
+      || (len != 8 && len != 12))
     {
       log_error ("error receiving PC/SC STATUS response: %s\n",
                  i? strerror (errno) : "premature EOF");
       goto command_failed;
     }
 
+  slotp->is_t0 = (len == 12 && !!(buffer[11] & PCSC_PROTOCOL_T0));
+
+
   full_len -= len;
   /* Newer versions of the wrapper might send more status bytes.
      Read them. */
@@ -1071,7 +1102,8 @@ pcsc_get_status (int slot, unsigned int *status)
    set to BUFLEN.  Returns: CT API error code. */
 static int
 pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
-                unsigned char *buffer, size_t *buflen)
+                unsigned char *buffer, size_t *buflen, 
+                struct pininfo_s *pininfo)
 {
 #ifdef NEED_PCSC_WRAPPER
   long err;
@@ -1126,7 +1158,8 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
       goto command_failed;
     }
   len -= 4; /* Already read the error code. */
-  err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+  err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
+                       | (msgbuf[7] << 8 ) | msgbuf[8]);
   if (err)
     {
       log_error ("pcsc_transmit failed: %s (0x%lx)\n",
@@ -1258,7 +1291,8 @@ close_pcsc_reader (int slot)
       goto command_failed;
     }
   len -= 4; /* Already read the error code. */
-  err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+  err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
+                       | (msgbuf[7] << 8 ) | msgbuf[8]);
   if (err)
     log_error ("pcsc_close failed: %s (0x%lx)\n",
                pcsc_error_string (err), err);
@@ -1302,7 +1336,16 @@ open_pcsc_reader (const char *portstr)
   size_t len;
   unsigned char msgbuf[9];
   int err;
+  unsigned int dummy_status;
   int sw = SW_HOST_CARD_IO_ERROR;
+  const char *wrapperpgm = GNUPG_LIBEXECDIR "/gnupg-pcsc-wrapper";
+
+  if (access (wrapperpgm, X_OK))
+    {
+      log_error ("can't run PC/SC access module `%s': %s\n",
+                 wrapperpgm, strerror (errno));
+      return -1;
+    }
 
   slot = new_reader_slot ();
   if (slot == -1)
@@ -1375,7 +1418,7 @@ open_pcsc_reader (const char *portstr)
         close(i);
       errno = 0;
 
-      execl (GNUPG_LIBDIR "/pcsc-wrapper",
+      execl (wrapperpgm,
              "pcsc-wrapper",
              "--",
              "1", /* API version */
@@ -1432,10 +1475,12 @@ open_pcsc_reader (const char *portstr)
   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);
+      log_error ("PC/SC returned a too large ATR (len=%lx)\n",
+                 (unsigned long)len);
       goto command_failed;
     }
-  err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+  err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
+                       | (msgbuf[7] << 8 ) | msgbuf[8]);
   if (err)
     {
       log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err));
@@ -1445,7 +1490,7 @@ open_pcsc_reader (const char *portstr)
 
   slotp->last_status = 0;
 
-  /* The open fucntion may return a zero for the ATR length to
+  /* The open request may return a zero for the ATR length to
      indicate that no card is present.  */
   n = len;
   if (n)
@@ -1466,8 +1511,12 @@ open_pcsc_reader (const char *portstr)
   reader_table[slot].reset_reader = reset_pcsc_reader;
   reader_table[slot].get_status_reader = pcsc_get_status;
   reader_table[slot].send_apdu_reader = pcsc_send_apdu;
+  reader_table[slot].check_keypad = NULL;
   reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
 
+  /* Read the status so that IS_T0 will be set. */
+  pcsc_get_status (slot, &dummy_status);
+
   dump_reader_status (slot);
   return slot;
 
@@ -1556,6 +1605,7 @@ open_pcsc_reader (const char *portstr)
     }
   strcpy (reader_table[slot].rdrname, portstr? portstr : list);
   xfree (list);
+  list = NULL;
 
   err = pcsc_connect (reader_table[slot].pcsc.context,
                       reader_table[slot].rdrname,
@@ -1573,7 +1623,6 @@ open_pcsc_reader (const char *portstr)
       xfree (reader_table[slot].rdrname);
       reader_table[slot].rdrname = NULL;
       reader_table[slot].used = 0;
-      xfree (list);
       return -1 /*pcsc_error_to_sw (err)*/;
     }
 
@@ -1601,6 +1650,7 @@ open_pcsc_reader (const char *portstr)
           /* If we got to here we know that a card is present
              and usable.  Thus remember this.  */
           reader_table[slot].last_status = (1|2|4| 0x8000);
+          reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
         }
     }
 
@@ -1608,6 +1658,7 @@ open_pcsc_reader (const char *portstr)
   reader_table[slot].reset_reader = reset_pcsc_reader;
   reader_table[slot].get_status_reader = pcsc_get_status;
   reader_table[slot].send_apdu_reader = pcsc_send_apdu;
+  reader_table[slot].check_keypad = NULL;
   reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
 
 /*   log_debug ("state    from pcsc_status: 0x%lx\n", card_state); */
@@ -1696,7 +1747,8 @@ get_status_ccid (int slot, unsigned int *status)
    set to BUFLEN.  Returns: Internal CCID driver error code. */
 static int
 send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
-                unsigned char *buffer, size_t *buflen)
+                unsigned char *buffer, size_t *buflen,
+                struct pininfo_s *pininfo)
 {
   long err;
   size_t maxbuflen;
@@ -1710,9 +1762,18 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
     log_printhex ("  APDU_data:", apdu, apdulen);
 
   maxbuflen = *buflen;
-  err = ccid_transceive (reader_table[slot].ccid.handle,
-                         apdu, apdulen,
-                         buffer, maxbuflen, buflen);
+  if (pininfo)
+    err = ccid_transceive_secure (reader_table[slot].ccid.handle,
+                                  apdu, apdulen,
+                                  pininfo->mode,
+                                  pininfo->minlen,
+                                  pininfo->maxlen,
+                                  pininfo->padlen,
+                                  buffer, maxbuflen, buflen);
+  else
+    err = ccid_transceive (reader_table[slot].ccid.handle,
+                           apdu, apdulen,
+                           buffer, maxbuflen, buflen);
   if (err)
     log_error ("ccid_transceive failed: (0x%lx)\n",
                err);
@@ -1720,6 +1781,24 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
   return err;
 }
 
+
+/* Check whether the CCID reader supports the ISO command code COMMAND
+   on the keypad.  Return 0 on success.  For a description of the pin
+   parameters, see ccid-driver.c */
+static int
+check_ccid_keypad (int slot, int command, int pin_mode,
+                   int pinlen_min, int pinlen_max, int pin_padlen)
+{
+  unsigned char apdu[] = { 0, 0, 0, 0x81 };
+
+  apdu[1] = command;
+  return ccid_transceive_secure (reader_table[slot].ccid.handle,
+                                 apdu, sizeof apdu,
+                                 pin_mode, pinlen_min, pinlen_max, pin_padlen,
+                                 NULL, 0, NULL);
+}
+
+
 /* Open the reader and try to read an ATR.  */
 static int
 open_ccid_reader (const char *portstr)
@@ -1759,6 +1838,7 @@ open_ccid_reader (const char *portstr)
   reader_table[slot].reset_reader = reset_ccid_reader;
   reader_table[slot].get_status_reader = get_status_ccid;
   reader_table[slot].send_apdu_reader = send_apdu_ccid;
+  reader_table[slot].check_keypad = check_ccid_keypad;
   reader_table[slot].dump_status_reader = dump_ccid_reader_status;
 
   dump_reader_status (slot);
@@ -1771,224 +1851,6 @@ open_ccid_reader (const char *portstr)
 
 
 \f
-#ifdef HAVE_OPENSC
-/*
-     OpenSC Interface.
-
-     This uses the OpenSC primitives to send APDUs.  We need this
-     because we can't mix OpenSC and native (i.e. ctAPI or PC/SC)
-     access to a card for resource conflict reasons.
- */
-
-
-static int
-close_osc_reader (int slot)
-{
-  /* FIXME: Implement. */
-  reader_table[slot].used = 0;
-  return 0;
-}
-
-static int
-reset_osc_reader (int slot)
-{
-  return SW_HOST_NOT_SUPPORTED;
-}
-
-
-static int
-osc_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: OpenSC error code. */
-static int
-osc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
-                unsigned char *buffer, size_t *buflen)
-{
-  long err;
-  struct sc_apdu a;
-  unsigned char data[SC_MAX_APDU_BUFFER_SIZE];
-  unsigned char result[SC_MAX_APDU_BUFFER_SIZE];
-
-  if (DBG_CARD_IO)
-    log_printhex ("  APDU_data:", apdu, apdulen);
-
-  if (apdulen < 4)
-    {
-      log_error ("osc_send_apdu: APDU is too short\n");
-      return SW_HOST_INV_VALUE;
-    }
-
-  memset(&a, 0, sizeof a);
-  a.cla = *apdu++;
-  a.ins = *apdu++;
-  a.p1 = *apdu++;
-  a.p2 = *apdu++;
-  apdulen -= 4;
-
-  if (!apdulen)
-    a.cse = SC_APDU_CASE_1;
-  else if (apdulen == 1)
-    {
-      a.le = *apdu? *apdu : 256;
-      apdu++; apdulen--;
-      a.cse = SC_APDU_CASE_2_SHORT;
-    }
-  else
-    {
-      a.lc = *apdu++; apdulen--;
-      if (apdulen < a.lc)
-        {
-          log_error ("osc_send_apdu: APDU shorter than specified in Lc\n");
-          return SW_HOST_INV_VALUE;
-
-        }
-      memcpy(data, apdu, a.lc);
-      apdu += a.lc; apdulen -= a.lc;
-
-      a.data = data;
-      a.datalen = a.lc;
-
-      if (!apdulen)
-        a.cse = SC_APDU_CASE_3_SHORT;
-      else
-        {
-          a.le = *apdu? *apdu : 256;
-          apdu++; apdulen--;
-          if (apdulen)
-            {
-              log_error ("osc_send_apdu: APDU larger than specified\n");
-              return SW_HOST_INV_VALUE;
-            }
-          a.cse = SC_APDU_CASE_4_SHORT;
-        }
-    }
-
-  a.resp = result;
-  a.resplen = DIM(result);
-
-  err = sc_transmit_apdu (reader_table[slot].osc.scard, &a);
-  if (err)
-    {
-      log_error ("sc_apdu_transmit failed: %s\n", sc_strerror (err));
-      return SW_HOST_CARD_IO_ERROR;
-    }
-
-  if (*buflen < 2 || a.resplen > *buflen - 2)
-    {
-      log_error ("osc_send_apdu: provided buffer too short to store result\n");
-      return SW_HOST_INV_VALUE;
-    }
-  memcpy (buffer, a.resp, a.resplen);
-  buffer[a.resplen] = a.sw1;
-  buffer[a.resplen+1] = a.sw2;
-  *buflen = a.resplen + 2;
-  return 0;
-}
-
-static int
-open_osc_reader (int portno)
-{
-  int err;
-  int slot;
-  reader_table_t slotp;
-
-  slot = new_reader_slot ();
-  if (slot == -1)
-    return -1;
-  slotp = reader_table + slot;
-
-  err = sc_establish_context (&slotp->osc.ctx, "scdaemon");
-  if (err)
-    {
-      log_error ("failed to establish SC context: %s\n", sc_strerror (err));
-      slotp->used = 0;
-      return -1;
-    }
-  if (portno < 0 || portno >= slotp->osc.ctx->reader_count)
-    {
-      log_error ("no card reader available\n");
-      sc_release_context (slotp->osc.ctx);
-      slotp->used = 0;
-      return -1;
-    }
-
-  /* Redirect to our logging facility. */
-  slotp->osc.ctx->error_file = log_get_stream ();
-  slotp->osc.ctx->debug = opt.debug_sc;
-  slotp->osc.ctx->debug_file = log_get_stream ();
-
-  if (sc_detect_card_presence (slotp->osc.ctx->reader[portno], 0) != 1)
-    {
-      log_error ("no card present\n");
-      sc_release_context (slotp->osc.ctx);
-      slotp->used = 0;
-      return -1;
-    }
-
-  /* We want the standard ISO driver. */
-  /*FIXME: OpenSC does not like "iso7816", so we use EMV for now. */
-  err = sc_set_card_driver(slotp->osc.ctx, "emv");
-  if (err)
-    {
-      log_error ("failed to select the iso7816 driver: %s\n",
-                 sc_strerror (err));
-      sc_release_context (slotp->osc.ctx);
-      slotp->used = 0;
-      return -1;
-    }
-
-  /* Now connect the card and hope that OpenSC won't try to be too
-     smart. */
-  err = sc_connect_card (slotp->osc.ctx->reader[portno], 0,
-                         &slotp->osc.scard);
-  if (err)
-    {
-      log_error ("failed to connect card in reader %d: %s\n",
-                 portno, sc_strerror (err));
-      sc_release_context (slotp->osc.ctx);
-      slotp->used = 0;
-      return -1;
-    }
-  if (opt.verbose)
-    log_info ("connected to card in opensc reader %d using driver `%s'\n",
-              portno, slotp->osc.scard->driver->name);
-
-  err = sc_lock (slotp->osc.scard);
-  if (err)
-    {
-      log_error ("can't lock card in reader %d: %s\n",
-                 portno, sc_strerror (err));
-      sc_disconnect_card (slotp->osc.scard, 0);
-      sc_release_context (slotp->osc.ctx);
-      slotp->used = 0;
-      return -1;
-    }
-
-  if (slotp->osc.scard->atr_len >= DIM (slotp->atr))
-    log_bug ("ATR returned by opensc is too large\n");
-  slotp->atrlen = slotp->osc.scard->atr_len;
-  memcpy (slotp->atr, slotp->osc.scard->atr, slotp->atrlen);
-
-  reader_table[slot].close_reader = close_osc_reader;
-  reader_table[slot].reset_reader = reset_osc_reader;
-  reader_table[slot].get_status_reader = osc_get_status;
-  reader_table[slot].send_apdu_reader = osc_send_apdu;
-  reader_table[slot].dump_status_reader = NULL;
-
-  dump_reader_status (slot);
-  return slot;
-}
-
-#endif /* HAVE_OPENSC */
-
-
-\f
 #ifdef USE_G10CODE_RAPDU
 /*
      The Remote APDU Interface.
@@ -2130,10 +1992,11 @@ my_rapdu_get_status (int slot, unsigned int *status)
 
 /* 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: OpenSC error code. */
+   set to BUFLEN.  Returns: APDU error code. */
 static int
 my_rapdu_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
-                    unsigned char *buffer, size_t *buflen)
+                    unsigned char *buffer, size_t *buflen,
+                    struct pininfo_s *pininfo)
 {
   int err;
   reader_table_t slotp;
@@ -2220,6 +2083,7 @@ open_rapdu_reader (int portno,
       return -1;
     }
 
+  rapdu_set_reader (slotp->rapdu.handle, portno);
 
   rapdu_set_iofunc (slotp->rapdu.handle,
                     readfnc, readfnc_value,
@@ -2263,6 +2127,7 @@ open_rapdu_reader (int portno,
   reader_table[slot].reset_reader = reset_rapdu_reader;
   reader_table[slot].get_status_reader = my_rapdu_get_status;
   reader_table[slot].send_apdu_reader = my_rapdu_send_apdu;
+  reader_table[slot].check_keypad = NULL;
   reader_table[slot].dump_status_reader = NULL;
 
   dump_reader_status (slot);
@@ -2325,8 +2190,7 @@ unlock_slot (int slot)
 
 /* 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
-   OpenSC support is compiled in, we first try to use OpenSC. */
+   the first USB reader.  For PC/SC the first listed reader). */
 int
 apdu_open_reader (const char *portstr)
 {
@@ -2352,16 +2216,6 @@ apdu_open_reader (const char *portstr)
 
 #endif /* HAVE_LIBUSB */
 
-#ifdef HAVE_OPENSC
-  if (!opt.disable_opensc)
-    {
-      int port = portstr? atoi (portstr) : 0;
-
-      return open_osc_reader (port);
-    }
-#endif /* HAVE_OPENSC */
-
-
   if (opt.ctapi_driver && *opt.ctapi_driver)
     {
       int port = portstr? atoi (portstr) : 32768;
@@ -2409,28 +2263,28 @@ apdu_open_reader (const char *portstr)
       pcsc_establish_context = dlsym (handle, "SCardEstablishContext");
       pcsc_release_context   = dlsym (handle, "SCardReleaseContext");
       pcsc_list_readers      = dlsym (handle, "SCardListReaders");
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__CYGWIN__)
       if (!pcsc_list_readers)
         pcsc_list_readers    = dlsym (handle, "SCardListReadersA");
 #endif
       pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange");
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__CYGWIN__)
       if (!pcsc_get_status_change)
         pcsc_get_status_change = dlsym (handle, "SCardGetStatusChangeA");
 #endif
       pcsc_connect           = dlsym (handle, "SCardConnect");
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__CYGWIN__)
       if (!pcsc_connect)
         pcsc_connect         = dlsym (handle, "SCardConnectA");
 #endif
       pcsc_reconnect         = dlsym (handle, "SCardReconnect");
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__CYGWIN__)
       if (!pcsc_reconnect)
         pcsc_reconnect       = dlsym (handle, "SCardReconnectA");
 #endif
       pcsc_disconnect        = dlsym (handle, "SCardDisconnect");
       pcsc_status            = dlsym (handle, "SCardStatus");
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__CYGWIN__)
       if (!pcsc_status)
         pcsc_status          = dlsym (handle, "SCardStatusA");
 #endif
@@ -2526,7 +2380,7 @@ apdu_close_reader (int slot)
 }
 
 /* Shutdown a reader; that is basically the same as a close but keeps
-   the handle ready for later use. A apdu_reset_header should be used
+   the handle ready for later use. A apdu_reset_reader should be used
    to get it active again. */
 int
 apdu_shutdown_reader (int slot)
@@ -2629,7 +2483,7 @@ apdu_activate (int slot)
 unsigned char *
 apdu_get_atr (int slot, size_t *atrlen)
 {
-  char *buf;
+  unsigned char *buf;
 
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return NULL;
@@ -2703,11 +2557,30 @@ apdu_get_status (int slot, int hang,
 }
 
 
+/* Check whether the reader supports the ISO command code COMMAND on
+   the keypad.  Return 0 on success.  For a description of the pin
+   parameters, see ccid-driver.c */
+int
+apdu_check_keypad (int slot, int command, int pin_mode,
+                   int pinlen_min, int pinlen_max, int pin_padlen)
+{
+  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+    return SW_HOST_NO_DRIVER;
+
+  if (reader_table[slot].check_keypad)
+    return reader_table[slot].check_keypad (slot, command,
+                                            pin_mode, pinlen_min, pinlen_max,
+                                            pin_padlen);
+  else
+    return SW_HOST_NOT_SUPPORTED;
+}
+
+
 /* 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)
+           unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo)
 {
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
@@ -2715,26 +2588,22 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen,
   if (reader_table[slot].send_apdu_reader)
     return reader_table[slot].send_apdu_reader (slot,
                                                 apdu, apdulen,
-                                                buffer, buflen);
+                                                buffer, buflen, pininfo);
   else
     return SW_HOST_NOT_SUPPORTED;
 }
 
-/* Send an APDU to the card in SLOT.  The APDU is created from all
-   given parameters: CLASS, INS, P0, P1, LC, DATA, LE.  A value of -1
-   for LC won't sent this field and the data field; in this case DATA
-   must also be passed as NULL.  The return value is the status word
-   or -1 for an invalid SLOT or other non card related error.  If
-   RETBUF is not NULL, it will receive an allocated buffer with the
-   returned data.  The length of that data will be put into
-   *RETBUFLEN.  The caller is reponsible for releasing the buffer even
-   in case of errors.  */
-int
-apdu_send_le(int slot, int class, int ins, int p0, int p1,
-             int lc, const char *data, int le,
-             unsigned char **retbuf, size_t *retbuflen)
+
+/* Core APDU trabceiver function. Parameters are described at
+   apdu_send_le with the exception of PININFO which indicates keypad
+   related operations if not NULL. */
+static int
+send_le (int slot, int class, int ins, int p0, int p1,
+         int lc, const char *data, int le,
+         unsigned char **retbuf, size_t *retbuflen,
+         struct pininfo_s *pininfo)
 {
-#define RESULTLEN 256
+#define RESULTLEN 258
   unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
                                          the driver. */
   size_t resultlen;
@@ -2752,7 +2621,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
 
   if (lc != -1 && (lc > 255 || lc < 0))
     return SW_WRONG_LENGTH;
-  if (le != -1 && (le > 256 || le < 1))
+  if (le != -1 && (le > 256 || le < 0))
     return SW_WRONG_LENGTH;
   if ((!data && lc != -1) || (data && lc == -1))
     return SW_HOST_INV_VALUE;
@@ -2770,14 +2639,18 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
       apdu[apdulen++] = lc;
       memcpy (apdu+apdulen, data, lc);
       apdulen += lc;
+      /* T=0 does not allow the use of Lc together with Le; thus
+         disable Le in this case. */
+      if (reader_table[slot].is_t0)
+        le = -1;
     }
   if (le != -1)
-    apdu[apdulen++] = le; /* Truncation is okay becuase 0 means 256. */
+    apdu[apdulen++] = le; /* Truncation is okay because 0 means 256. */
   assert (sizeof (apdu) >= apdulen);
   /* As safeguard don't pass any garbage from the stack to the driver. */
   memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
   resultlen = RESULTLEN;
-  rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
+  rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo);
   if (rc || resultlen < 2)
     {
       log_error ("apdu_send_simple(%d) failed: %s\n",
@@ -2845,7 +2718,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
           apdu[apdulen++] = len;
           memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
           resultlen = RESULTLEN;
-          rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
+          rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
           if (rc || resultlen < 2)
             {
               log_error ("apdu_send_simple(%d) for get response failed: %s\n",
@@ -2911,6 +2784,27 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
 }
 
 /* Send an APDU to the card in SLOT.  The APDU is created from all
+   given parameters: CLASS, INS, P0, P1, LC, DATA, LE.  A value of -1
+   for LC won't sent this field and the data field; in this case DATA
+   must also be passed as NULL.  The return value is the status word
+   or -1 for an invalid SLOT or other non card related error.  If
+   RETBUF is not NULL, it will receive an allocated buffer with the
+   returned data.  The length of that data will be put into
+   *RETBUFLEN.  The caller is reponsible for releasing the buffer even
+   in case of errors.  */
+int
+apdu_send_le(int slot, int class, int ins, int p0, int p1,
+             int lc, const char *data, int le,
+             unsigned char **retbuf, size_t *retbuflen)
+{
+  return send_le (slot, class, ins, p0, p1,
+                  lc, data, le,
+                  retbuf, retbuflen,
+                  NULL);
+}
+
+
+/* Send an APDU to the card in SLOT.  The APDU is created from all
    given parameters: CLASS, INS, P0, P1, LC, DATA.  A value of -1 for
    LC won't sent this field and the data field; in this case DATA must
    also be passed as NULL. The return value is the status word or -1
@@ -2923,8 +2817,8 @@ int
 apdu_send (int slot, int class, int ins, int p0, int p1,
            int lc, const char *data, unsigned char **retbuf, size_t *retbuflen)
 {
-  return apdu_send_le (slot, class, ins, p0, p1, lc, data, 256,
-                       retbuf, retbuflen);
+  return send_le (slot, class, ins, p0, p1, lc, data, 256,
+                  retbuf, retbuflen, NULL);
 }
 
 /* Send an APDU to the card in SLOT.  The APDU is created from all
@@ -2937,7 +2831,25 @@ int
 apdu_send_simple (int slot, int class, int ins, int p0, int p1,
                   int lc, const char *data)
 {
-  return apdu_send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL);
+  return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL);
+}
+
+
+/* Same as apdu_send_simple but uses the keypad of the reader. */
+int
+apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
+                     int lc, const char *data,  
+                     int pin_mode,
+                     int pinlen_min, int pinlen_max, int pin_padlen)
+{
+  struct pininfo_s pininfo;
+
+  pininfo.mode = pin_mode;
+  pininfo.minlen = pinlen_min;
+  pininfo.maxlen = pinlen_max;
+  pininfo.padlen = pin_padlen;
+  return send_le (slot, class, ins, p0, p1, lc, data, -1,
+                  NULL, NULL, &pininfo);
 }
 
 
@@ -2947,14 +2859,14 @@ apdu_send_simple (int slot, int class, int ins, int p0, int p1,
    HANDLE_MORE set to true this function will handle the MORE DATA
    status and return all APDUs concatenated with one status word at
    the end.  The function does not return a regular status word but 0
-   on success.  If the slot is locked, the fucntion returns
-   immediately.*/
+   on success.  If the slot is locked, the function returns
+   immediately with an error.  */
 int
 apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
                   int handle_more,
                   unsigned char **retbuf, size_t *retbuflen)
 {
-#define RESULTLEN 256
+#define RESULTLEN 258
   unsigned char apdu[5+256+1];
   size_t apdulen;
   unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
@@ -2970,15 +2882,15 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
   if ((sw = trylock_slot (slot)))
     return sw;
 
-  /* We simply trucntate a too long APDU.  */
+  /* We simply trunctate a too long APDU.  */
   if (apdudatalen > sizeof apdu)
     apdudatalen = sizeof apdu;
   apdulen = apdudatalen;
   memcpy (apdu, apdudata, apdudatalen);
   class = apdulen? *apdu : 0;
 
-
-  rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
+  resultlen = RESULTLEN;
+  rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
   if (rc || resultlen < 2)
     {
       log_error ("apdu_send_direct(%d) failed: %s\n",
@@ -3032,7 +2944,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
           apdu[apdulen++] = len;
           memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
           resultlen = RESULTLEN;
-          rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
+          rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
           if (rc || resultlen < 2)
             {
               log_error ("apdu_send_direct(%d) for get response failed: %s\n",