Preparing 2.0.1
[gnupg.git] / scd / apdu.c
index d23a4ad..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>
@@ -28,9 +33,9 @@
 #include <assert.h>
 #include <signal.h>
 #ifdef USE_GNU_PTH
-# include <pth.h>
 # include <unistd.h>
 # include <fcntl.h>
+# include <pth.h>
 #endif
 
 
 #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
@@ -73,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. */
@@ -98,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 {
@@ -122,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
@@ -207,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
 {
@@ -259,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,
@@ -271,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
 /*
@@ -311,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;
@@ -362,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";
     }
 }
@@ -523,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];
@@ -586,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);
@@ -716,7 +744,7 @@ pcsc_error_to_sw (long ec)
 {
   int rc;
 
-  switch (ec)
+  switch ( PCSC_ERR_MASK (ec) )
     {
     case 0:  rc = 0; break;
 
@@ -764,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;
@@ -805,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",
@@ -836,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:
@@ -897,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 */
@@ -912,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;
@@ -952,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",
@@ -963,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. */
@@ -1060,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;
@@ -1115,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",
@@ -1247,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);
@@ -1291,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)
@@ -1364,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 */
@@ -1421,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));
@@ -1434,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)
@@ -1455,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;
 
@@ -1545,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,
@@ -1562,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)*/;
     }
 
@@ -1590,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);
         }
     }
 
@@ -1597,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); */
@@ -1685,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;
@@ -1699,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);
@@ -1709,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)
@@ -1748,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);
@@ -1904,7 +1995,8 @@ my_rapdu_get_status (int slot, unsigned int *status)
    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;
@@ -1991,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,
@@ -2034,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);
@@ -2169,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
@@ -2286,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)
@@ -2389,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;
@@ -2463,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;
@@ -2475,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;
@@ -2512,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;
@@ -2530,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",
@@ -2605,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",
@@ -2671,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
@@ -2683,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
@@ -2697,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);
 }
 
 
@@ -2707,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
@@ -2730,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",
@@ -2792,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",