scd: ccid-driver supporting larger APDU.
[gnupg.git] / scd / pcsc-wrapper.c
index 23e8442..73b25f4 100644 (file)
@@ -1,11 +1,11 @@
-/* pcsc-wrapper.c - Wrapper for ccessing the PC/SC service
+/* pcsc-wrapper.c - Wrapper for accessing 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
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -14,9 +14,7 @@
  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 /*
@@ -29,7 +27,7 @@
   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
@@ -58,7 +56,7 @@
 #define MYVERSION_LINE PGM " (GnuPG) " VERSION
 #define BUGREPORT_LINE "\nReport bugs to <bug-gnupg@gnu.org>.\n"
 #else
-#define MYVERSION_LINE PGM 
+#define MYVERSION_LINE PGM
 #define BUGREPORT_LINE ""
 #endif
 
@@ -69,14 +67,14 @@ 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_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_PROTOCOL_T0     1
+#define PCSC_PROTOCOL_T1     2
+#define PCSC_PROTOCOL_RAW    4
 
 #define PCSC_SHARE_EXCLUSIVE 1
 #define PCSC_SHARE_SHARED    2
@@ -87,7 +85,7 @@ static int verbose;
 #define PCSC_UNPOWER_CARD    2
 #define PCSC_EJECT_CARD      3
 
-#define PCSC_UNKNOWN    0x0001  
+#define PCSC_UNKNOWN    0x0001
 #define PCSC_ABSENT     0x0002  /* Card is absent.  */
 #define PCSC_PRESENT    0x0004  /* Card is present.  */
 #define PCSC_SWALLOWED  0x0008  /* Card is present and electrical connected. */
@@ -108,7 +106,7 @@ static int verbose;
 #define PCSC_STATE_MUTE               0x0200  /* Unresponsive card.  */
 
 struct pcsc_io_request_s {
-  unsigned long protocol; 
+  unsigned long protocol;
   unsigned long pci_len;
 };
 
@@ -169,7 +167,8 @@ long (* pcsc_status) (unsigned long card,
                       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_end_transaction) (unsigned long card,
+                               unsigned long disposition);
 long (* pcsc_transmit) (unsigned long card,
                         const pcsc_io_request_t send_pci,
                         const unsigned char *send_buffer,
@@ -179,6 +178,13 @@ long (* pcsc_transmit) (unsigned long card,
                         unsigned long *recv_len);
 long (* pcsc_set_timeout) (unsigned long context,
                            unsigned long timeout);
+long (* pcsc_control) (unsigned long card,
+                       unsigned long control_code,
+                       const void *send_buffer,
+                       unsigned long send_len,
+                       void *recv_buffer,
+                       unsigned long recv_len,
+                       unsigned long *bytes_returned);
 
 
 
@@ -236,7 +242,7 @@ request_succeeded (const void *buffer, size_t buflen)
 
   fflush (stdout);
 }
-  
+
 
 
 static unsigned long
@@ -244,10 +250,10 @@ read_32 (FILE *fp)
 {
   int c1, c2, c3, c4;
 
-  c1 = getc (stdin);
-  c2 = getc (stdin);
-  c3 = getc (stdin);
-  c4 = getc (stdin);
+  c1 = getc (fp);
+  c2 = getc (fp);
+  c3 = getc (fp);
+  c4 = getc (fp);
   if (c1 == EOF || c2 == EOF || c3 == EOF || c4 == EOF)
     {
       fprintf (stderr, PGM ": premature EOF while parsing request\n");
@@ -272,40 +278,40 @@ pcsc_error_string (long err)
     {
     case 0x0002: s = "cancelled"; break;
     case 0x000e: s = "can't dispose"; break;
-    case 0x0008: s = "insufficient buffer"; 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 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 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 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 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;      
+    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;
@@ -336,42 +342,45 @@ load_pcsc_driver (const char *libname)
   pcsc_end_transaction   = dlsym (handle, "SCardEndTransaction");
   pcsc_transmit          = dlsym (handle, "SCardTransmit");
   pcsc_set_timeout       = dlsym (handle, "SCardSetTimeout");
+  pcsc_control           = dlsym (handle, "SCardControl");
 
   if (!pcsc_establish_context
-      || !pcsc_release_context  
-      || !pcsc_list_readers     
+      || !pcsc_release_context
+      || !pcsc_list_readers
       || !pcsc_get_status_change
-      || !pcsc_connect          
-      || !pcsc_reconnect          
+      || !pcsc_connect
+      || !pcsc_reconnect
       || !pcsc_disconnect
       || !pcsc_status
       || !pcsc_begin_transaction
       || !pcsc_end_transaction
-      || !pcsc_transmit         
+      || !pcsc_transmit
+      || !pcsc_control
       /* || !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%d%d)\n",
+               "(%d%d%d%d%d%d%d%d%d%d%d%d%d)\n",
                !!pcsc_establish_context,
-               !!pcsc_release_context,  
-               !!pcsc_list_readers,     
-               !!pcsc_get_status_change,     
-               !!pcsc_connect,          
-               !!pcsc_reconnect,          
+               !!pcsc_release_context,
+               !!pcsc_list_readers,
+               !!pcsc_get_status_change,
+               !!pcsc_connect,
+               !!pcsc_reconnect,
                !!pcsc_disconnect,
                !!pcsc_status,
                !!pcsc_begin_transaction,
                !!pcsc_end_transaction,
-               !!pcsc_transmit,         
-               !!pcsc_set_timeout );
+               !!pcsc_transmit,
+               !!pcsc_set_timeout,
+               !!pcsc_control );
       dlclose (handle);
       exit (1);
     }
 }
-  
+
 
 
 
@@ -385,7 +394,7 @@ handle_open (unsigned char *argbuf, size_t arglen)
   long err;
   const char * portstr;
   char *list = NULL;
-  unsigned long nreader, listlen, atrlen;
+  unsigned long nreader, atrlen;
   char *p;
   unsigned long card_state, card_protocol;
   unsigned char atr[33];
@@ -410,7 +419,7 @@ handle_open (unsigned char *argbuf, size_t arglen)
       request_failed (err);
       return;
     }
-  
+
   err = pcsc_list_readers (pcsc_context, NULL, NULL, &nreader);
   if (!err)
     {
@@ -432,7 +441,6 @@ handle_open (unsigned char *argbuf, size_t arglen)
       return;
     }
 
-  listlen = nreader;
   p = list;
   while (nreader)
     {
@@ -474,10 +482,12 @@ handle_open (unsigned char *argbuf, size_t arglen)
       pcsc_release_context (pcsc_context);
       free (current_rdrname);
       current_rdrname = NULL;
+      pcsc_card = 0;
+      pcsc_protocol = 0;
       request_failed (err);
       return;
-    }      
-  
+    }
+
   current_atrlen = 0;
   if (!err)
     {
@@ -517,6 +527,9 @@ handle_open (unsigned char *argbuf, size_t arglen)
 static void
 handle_close (unsigned char *argbuf, size_t arglen)
 {
+  (void)argbuf;
+  (void)arglen;
+
   if (!driver_is_open)
     {
       fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
@@ -527,6 +540,8 @@ handle_close (unsigned char *argbuf, size_t arglen)
   free (current_rdrname);
   current_rdrname = NULL;
   pcsc_release_context (pcsc_context);
+  pcsc_card = 0;
+  pcsc_protocol = 0;
 
   request_succeeded (NULL, 0);
 }
@@ -543,6 +558,9 @@ handle_status (unsigned char *argbuf, size_t arglen)
   int status;
   unsigned char buf[20];
 
+  (void)argbuf;
+  (void)arglen;
+
   if (!driver_is_open)
     {
       fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
@@ -567,17 +585,20 @@ handle_status (unsigned char *argbuf, size_t arglen)
     }
 
   status = 0;
-  if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
-    status |= 2;
-  if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
-    status |= 4;
-  /* We indicate a useful card if it is not in use by another
-     application.  This is because we only use exclusive access
-     mode.  */
-  if ( (status & 6) == 6
-       && !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
-    status |= 1;
-  
+  if ( !(rdrstates[0].event_state & PCSC_STATE_UNKNOWN) )
+    {
+      if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
+        status |= 2;
+      if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
+        status |= 4;
+      /* We indicate a useful card if it is not in use by another
+         application.  This is because we only use exclusive access
+         mode.  */
+      if ( (status & 6) == 6
+           && !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
+        status |= 1;
+    }
+
   /* First word is identical to the one used by apdu.c. */
   buf[0] = 0;
   buf[1] = 0;
@@ -608,6 +629,9 @@ handle_reset (unsigned char *argbuf, size_t arglen)
   unsigned long nreader, atrlen;
   unsigned long card_state, card_protocol;
 
+  (void)argbuf;
+  (void)arglen;
+
   if (!driver_is_open)
     {
       fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
@@ -618,6 +642,8 @@ handle_reset (unsigned char *argbuf, size_t arglen)
   if (pcsc_card)
     {
       err = pcsc_disconnect (pcsc_card, PCSC_LEAVE_CARD);
+      if (err == 0x80100003)  /* Invalid handle.  (already disconnected) */
+        err = 0;
       if (err)
         {
           fprintf (stderr, PGM": pcsc_disconnect failed: %s (0x%lx)\n",
@@ -641,9 +667,9 @@ handle_reset (unsigned char *argbuf, size_t arglen)
       pcsc_card = 0;
       request_failed (err);
       return;
-    }      
+    }
+
 
-  
   atrlen = 33;
   nreader = sizeof reader - 1;
   err = pcsc_status (pcsc_card,
@@ -704,6 +730,38 @@ handle_transmit (unsigned char *argbuf, size_t arglen)
 }
 
 
+/* Handle a control request.  The argument is expected to be a buffer
+   which contains CONTROL_CODE (4-byte) and INPUT_BYTES.
+ */
+static void
+handle_control (unsigned char *argbuf, size_t arglen)
+{
+  long err;
+  unsigned long ioctl_code;
+  unsigned long recv_len = 1024;
+  unsigned char buffer[1024];
+
+  if (arglen < 4)
+    bad_request ("CONTROL");
+
+  ioctl_code = (argbuf[0] << 24) | (argbuf[1] << 16) | (argbuf[2] << 8) | argbuf[3];
+  argbuf += 4;
+  arglen -= 4;
+
+  recv_len = sizeof (buffer);
+  err = pcsc_control (pcsc_card, ioctl_code, argbuf, arglen,
+                      buffer, recv_len, &recv_len);
+  if (err)
+    {
+      if (verbose)
+        fprintf (stderr, PGM": pcsc_control 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)
@@ -714,7 +772,7 @@ print_version (int with_help)
          "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"
@@ -724,7 +782,7 @@ print_version (int with_help)
           "  --version   print version of the program and exit\n"
           "  --help      display this help and exit\n"
           BUGREPORT_LINE, stdout );
-  
+
   exit (0);
 }
 
@@ -735,7 +793,7 @@ main (int argc, char **argv)
   int last_argc = -1;
   int api_number = 0;
   int c;
+
   if (argc)
     {
       argc--; argv++;
@@ -757,7 +815,7 @@ main (int argc, char **argv)
           verbose = 1;
           argc--; argv++;
         }
-    }          
+    }
   if (argc != 1 && argc != 2)
     {
       fprintf (stderr, "usage: " PGM " API-NUMBER [LIBNAME]\n");
@@ -778,7 +836,7 @@ main (int argc, char **argv)
     {
       size_t arglen;
       unsigned char argbuffer[2048];
-      
+
       arglen = read_32 (stdin);
       if (arglen >= sizeof argbuffer - 1)
         {
@@ -815,6 +873,10 @@ main (int argc, char **argv)
           handle_reset (argbuffer, arglen);
           break;
 
+        case 6:
+          handle_control (argbuffer, arglen);
+          break;
+
         default:
           fprintf (stderr, PGM ": invalid request 0x%02X\n", c);
           exit (1);