scd: Fix an action after card removal.
[gnupg.git] / scd / app.c
index d0b8328..55b8edd 100644 (file)
--- a/scd/app.c
+++ b/scd/app.c
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <pth.h>
+#include <npth.h>
 
 #include "scdaemon.h"
 #include "app-common.h"
-#include "apdu.h"
 #include "iso7816.h"
+#include "apdu.h"
 #include "tlv.h"
 
 /* This table is used to keep track of locks on a per reader base.
@@ -37,9 +37,8 @@
 static struct
 {
   int initialized;
-  pth_mutex_t lock;
+  npth_mutex_t lock;
   app_t app;        /* Application context in use or NULL. */
-  app_t last_app;   /* Last application object used as this slot or NULL. */
 } lock_table[10];
 
 
@@ -48,6 +47,20 @@ static void deallocate_app (app_t app);
 
 
 \f
+static void
+print_progress_line (void *opaque, const char *what, int pc, int cur, int tot)
+{
+  ctrl_t ctrl = opaque;
+  char line[100];
+
+  if (ctrl)
+    {
+      snprintf (line, sizeof line, "%s %c %d %d", what, pc, cur, tot);
+      send_status_direct (ctrl, "PROGRESS", line);
+    }
+}
+
+
 /* Lock the reader SLOT.  This function shall be used right before
    calling any of the actual application functions to serialize access
    to the reader.  We do this always even if the reader is not
@@ -55,35 +68,36 @@ static void deallocate_app (app_t app);
    never shares a reader (while performing one command).  Returns 0 on
    success; only then the unlock_reader function must be called after
    returning from the handler. */
-static gpg_error_t 
-lock_reader (int slot)
+static gpg_error_t
+lock_reader (int slot, ctrl_t ctrl)
 {
-  gpg_error_t err;
+  int res;
 
   if (slot < 0 || slot >= DIM (lock_table))
     return gpg_error (slot<0? GPG_ERR_INV_VALUE : GPG_ERR_RESOURCE_LIMIT);
 
   if (!lock_table[slot].initialized)
     {
-      if (!pth_mutex_init (&lock_table[slot].lock))
+      res = npth_mutex_init (&lock_table[slot].lock, NULL);
+      if (res)
         {
-          err = gpg_error_from_syserror ();
-          log_error ("error initializing mutex: %s\n", strerror (errno));
-          return err;
+          log_error ("error initializing mutex: %s\n", strerror (res));
+          return gpg_error_from_errno (res);
         }
       lock_table[slot].initialized = 1;
       lock_table[slot].app = NULL;
-      lock_table[slot].last_app = NULL;
     }
-  
-  if (!pth_mutex_acquire (&lock_table[slot].lock, 0, NULL))
+
+  res = npth_mutex_lock (&lock_table[slot].lock);
+  if (res)
     {
-      err = gpg_error_from_syserror ();
       log_error ("failed to acquire APP lock for slot %d: %s\n",
-                 slot, strerror (errno));
-      return err;
+                 slot, strerror (res));
+      return gpg_error_from_errno (res);
     }
 
+  apdu_set_progress_cb (slot, print_progress_line, ctrl);
+
   return 0;
 }
 
@@ -91,31 +105,18 @@ lock_reader (int slot)
 static void
 unlock_reader (int slot)
 {
+  int res;
+
   if (slot < 0 || slot >= DIM (lock_table)
       || !lock_table[slot].initialized)
     log_bug ("unlock_reader called for invalid slot %d\n", slot);
 
-  if (!pth_mutex_release (&lock_table[slot].lock))
-    log_error ("failed to release APP lock for slot %d: %s\n",
-               slot, strerror (errno));
-
-}
-
+  apdu_set_progress_cb (slot, NULL, NULL);
 
-static void
-dump_mutex_state (pth_mutex_t *m)
-{
-#ifdef _W32_PTH_H
-  (void)m;
-  log_printf ("unknown under W32");
-#else
-  if (!(m->mx_state & PTH_MUTEX_INITIALIZED))
-    log_printf ("not_initialized");
-  else if (!(m->mx_state & PTH_MUTEX_LOCKED))
-    log_printf ("not_locked");
-  else
-    log_printf ("locked tid=0x%lx count=%lu", (long)m->mx_owner, m->mx_count);
-#endif
+  res = npth_mutex_unlock (&lock_table[slot].lock);
+  if (res)
+    log_error ("failed to release APP lock for slot %d: %s\n",
+               slot, strerror (res));
 }
 
 
@@ -129,19 +130,12 @@ app_dump_state (void)
   for (slot=0; slot < DIM (lock_table); slot++)
     if (lock_table[slot].initialized)
       {
-        log_info ("app_dump_state: slot=%d lock=", slot);
-        dump_mutex_state (&lock_table[slot].lock);
+        log_info ("app_dump_state: slot=%d", slot);
         if (lock_table[slot].app)
           {
             log_printf (" app=%p", lock_table[slot].app);
             if (lock_table[slot].app->apptype)
-              log_printf (" type=`%s'", lock_table[slot].app->apptype);
-          }
-        if (lock_table[slot].last_app)
-          {
-            log_printf (" lastapp=%p", lock_table[slot].last_app);
-            if (lock_table[slot].last_app->apptype)
-              log_printf (" type=`%s'", lock_table[slot].last_app->apptype);
+              log_printf (" type='%s'", lock_table[slot].app->apptype);
           }
         log_printf ("\n");
       }
@@ -165,46 +159,71 @@ is_app_allowed (const char *name)
 void
 application_notify_card_reset (int slot)
 {
-  app_t app;
-
   if (slot < 0 || slot >= DIM (lock_table))
     return;
 
   /* FIXME: We are ignoring any error value here.  */
-  lock_reader (slot); 
+  lock_reader (slot, NULL);
 
-  /* Deallocate a saved application for that slot, so that we won't
-     try to reuse it.  If there is no saved application, set a flag so
-     that we won't save the current state. */
-  app = lock_table[slot].last_app;
+  /* Release the APP, as it's not reusable any more.  */
+  if (lock_table[slot].app)
+    {
+      if (lock_table[slot].app->ref_count)
+        log_bug ("trying to release active context\n");
 
-  if (app)
+      deallocate_app (lock_table[slot].app);
+      lock_table[slot].app = NULL;
+      log_debug ("application has been released\n");
+    }
+
+  unlock_reader (slot);
+}
+
+
+/*
+ * This function is called with lock held.
+ */
+static gpg_error_t
+check_conflict (int slot, const char *name)
+{
+  app_t app = lock_table[slot].app;
+
+  if (!app || !name || (app->apptype && !ascii_strcasecmp (app->apptype, name)))
+    return 0;
+
+  if (!app->ref_count)
     {
-      lock_table[slot].last_app = NULL;
+      lock_table[slot].app = NULL;
       deallocate_app (app);
+      return 0;
+    }
+  else
+    {
+      log_info ("application '%s' in use by reader %d - can't switch\n",
+                app->apptype? app->apptype : "<null>", slot);
+      return gpg_error (GPG_ERR_CONFLICT);
     }
-  unlock_reader (slot); 
 }
 
 /* This function is used by the serialno command to check for an
    application conflict which may appear if the serialno command is
    used to request a specific application and the connection has
    already done a select_application. */
 gpg_error_t
-check_application_conflict (ctrl_t ctrl, const char *name)
+check_application_conflict (ctrl_t ctrl, int slot, const char *name)
 {
-  int slot = ctrl->reader_slot;
-  app_t app;
+  gpg_error_t err;
 
   if (slot < 0 || slot >= DIM (lock_table))
     return gpg_error (GPG_ERR_INV_VALUE);
 
-  app = lock_table[slot].initialized ? lock_table[slot].app : NULL;
-  if (app && app->apptype && name)
-    if ( ascii_strcasecmp (app->apptype, name))
-        return gpg_error (GPG_ERR_CONFLICT);
-  return 0;
+  err = lock_reader (slot, ctrl);
+  if (err)
+    return err;
+
+  err = check_conflict (slot, name);
+  unlock_reader (slot);
+  return err;
 }
 
 
@@ -220,51 +239,28 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
   app_t app = NULL;
   unsigned char *result = NULL;
   size_t resultlen;
+  int want_undefined;
 
   (void)ctrl;
 
   *r_app = NULL;
 
-  err = lock_reader (slot);
+  want_undefined = (name && !strcmp (name, "undefined"));
+
+  err = lock_reader (slot, ctrl);
   if (err)
     return err;
 
   /* First check whether we already have an application to share. */
-  app = lock_table[slot].initialized ? lock_table[slot].app : NULL;
-  if (app && name)
-    if (!app->apptype || ascii_strcasecmp (app->apptype, name))
-      {
-        unlock_reader (slot);
-        if (app->apptype)
-          log_info ("application `%s' in use by reader %d - can't switch\n",
-                    app->apptype, slot);
-        return gpg_error (GPG_ERR_CONFLICT);
-      }
-
-  /* If we don't have an app, check whether we have a saved
-     application for that slot.  This is useful so that a card does
-     not get reset even if only one session is using the card - this
-     way the PIN cache and other cached data are preserved.  */
-  if (!app && lock_table[slot].initialized && lock_table[slot].last_app)
+  err = check_conflict (slot, name);
+  if (err)
     {
-      app = lock_table[slot].last_app;
-      if (!name || (app->apptype && !ascii_strcasecmp (app->apptype, name)) )
-        {
-          /* Yes, we can reuse this application - either the caller
-             requested an unspecific one or the requested one matches
-             the saved one. */
-          lock_table[slot].app = app;
-          lock_table[slot].last_app = NULL;
-        }
-      else 
-        {
-          /* No, this saved application can't be used - deallocate it. */
-          lock_table[slot].last_app = NULL;
-          deallocate_app (app);
-          app = NULL;
-        }
+      unlock_reader (slot);
+      return err;
     }
 
+  app = lock_table[slot].app;
+
   /* If we can reuse an application, bump the reference count and
      return it.  */
   if (app)
@@ -294,54 +290,66 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
   /* Fixme: We should now first check whether a card is at all
      present. */
 
-  /* Try to read the GDO file first to get a default serial number. */
-  err = iso7816_select_file (slot, 0x3F00, 1, NULL, NULL);
-  if (!err)
-    err = iso7816_select_file (slot, 0x2F02, 0, NULL, NULL);
-  if (!err)
-     err = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
-  if (!err)
+  /* Try to read the GDO file first to get a default serial number.
+     We skip this if the undefined application has been requested. */
+  if (!want_undefined)
     {
-      size_t n;
-      const unsigned char *p;
-
-      p = find_tlv_unchecked (result, resultlen, 0x5A, &n);
-      if (p)
-        resultlen -= (p-result);
-      if (p && n > resultlen && n == 0x0d && resultlen+1 == n)
-        {
-          /* The object it does not fit into the buffer.  This is an
-             invalid encoding (or the buffer is too short.  However, I
-             have some test cards with such an invalid encoding and
-             therefore I use this ugly workaround to return something
-             I can further experiment with. */
-          log_info ("enabling BMI testcard workaround\n");
-          n--;
-        }
-
-      if (p && n <= resultlen)
+      err = iso7816_select_file (slot, 0x3F00, 1, NULL, NULL);
+      if (!err)
+        err = iso7816_select_file (slot, 0x2F02, 0, NULL, NULL);
+      if (!err)
+        err = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
+      if (!err)
         {
-          /* The GDO file is pretty short, thus we simply reuse it for
-             storing the serial number. */
-          memmove (result, p, n);
-          app->serialno = result;
-          app->serialnolen = n;
-          err = app_munge_serialno (app);
-          if (err)
-            goto leave;
+          size_t n;
+          const unsigned char *p;
+
+          p = find_tlv_unchecked (result, resultlen, 0x5A, &n);
+          if (p)
+            resultlen -= (p-result);
+          if (p && n > resultlen && n == 0x0d && resultlen+1 == n)
+            {
+              /* The object it does not fit into the buffer.  This is an
+                 invalid encoding (or the buffer is too short.  However, I
+                 have some test cards with such an invalid encoding and
+                 therefore I use this ugly workaround to return something
+                 I can further experiment with. */
+              log_info ("enabling BMI testcard workaround\n");
+              n--;
+            }
+
+          if (p && n <= resultlen)
+            {
+              /* The GDO file is pretty short, thus we simply reuse it for
+                 storing the serial number. */
+              memmove (result, p, n);
+              app->serialno = result;
+              app->serialnolen = n;
+              err = app_munge_serialno (app);
+              if (err)
+                goto leave;
+            }
+          else
+            xfree (result);
+          result = NULL;
         }
-      else
-        xfree (result);
-      result = NULL;
     }
 
   /* For certain error codes, there is no need to try more.  */
-  if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT)
+  if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT
+      || gpg_err_code (err) == GPG_ERR_ENODEV)
     goto leave;
-  
 
   /* Figure out the application to use.  */
-  err = gpg_error (GPG_ERR_NOT_FOUND);
+  if (want_undefined)
+    {
+      /* We switch to the "undefined" application only if explicitly
+         requested.  */
+      app->apptype = "UNDEFINED";
+      err = 0;
+    }
+  else
+    err = gpg_error (GPG_ERR_NOT_FOUND);
 
   if (err && is_app_allowed ("openpgp")
           && (!name || !strcmp (name, "openpgp")))
@@ -350,19 +358,21 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
     err = app_select_nks (app);
   if (err && is_app_allowed ("p15") && (!name || !strcmp (name, "p15")))
     err = app_select_p15 (app);
-  if (err && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig")))
-    err = app_select_dinsig (app);
   if (err && is_app_allowed ("geldkarte")
       && (!name || !strcmp (name, "geldkarte")))
     err = app_select_geldkarte (app);
-  if (err && name)
+  if (err && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig")))
+    err = app_select_dinsig (app);
+  if (err && is_app_allowed ("sc-hsm") && (!name || !strcmp (name, "sc-hsm")))
+    err = app_select_sc_hsm (app);
+  if (err && name && gpg_err_code (err) != GPG_ERR_OBJ_TERM_STATE)
     err = gpg_error (GPG_ERR_NOT_SUPPORTED);
 
  leave:
   if (err)
     {
       if (name)
-        log_info ("can't select application `%s': %s\n",
+        log_info ("can't select application '%s': %s\n",
                   name, gpg_strerror (err));
       else
         log_info ("no supported card application found: %s\n",
@@ -388,17 +398,20 @@ get_supported_applications (void)
     "openpgp",
     "nks",
     "p15",
-    "dinsig",
     "geldkarte",
+    "dinsig",
+    "sc-hsm",
+    /* Note: "undefined" is not listed here because it needs special
+       treatment by the client.  */
     NULL
   };
   int idx;
   size_t nbytes;
   char *buffer, *p;
-  
+
   for (nbytes=1, idx=0; list[idx]; idx++)
     nbytes += strlen (list[idx]) + 1 + 1;
-  
+
   buffer = xtrymalloc (nbytes);
   if (!buffer)
     return NULL;
@@ -447,7 +460,7 @@ release_application (app_t app)
   /* Move the reference to the application in the lock table. */
   slot = app->slot;
   /* FIXME: We are ignoring any error value.  */
-  lock_reader (slot);
+  lock_reader (slot, NULL);
   if (lock_table[slot].app != app)
     {
       unlock_reader (slot);
@@ -456,10 +469,10 @@ release_application (app_t app)
       return;
     }
 
-  if (lock_table[slot].last_app)
-    deallocate_app (lock_table[slot].last_app);
-  lock_table[slot].last_app = lock_table[slot].app;
-  lock_table[slot].app = NULL;
+  /* We don't deallocate app here.  Instead, we keep it.  This is
+     useful so that a card does not get reset even if only one session
+     is using the card - this way the PIN cache and other cached data
+     are preserved.  */
   unlock_reader (slot);
 }
 
@@ -467,22 +480,22 @@ release_application (app_t app)
 
 /* The serial number may need some cosmetics.  Do it here.  This
    function shall only be called once after a new serial number has
-   been put into APP->serialno. 
+   been put into APP->serialno.
 
    Prefixes we use:
-   
+
      FF 00 00 = For serial numbers starting with an FF
      FF 01 00 = Some german p15 cards return an empty serial number so the
                 serial number from the EF(TokenInfo) is used instead.
      FF 7F 00 = No serialno.
-     
+
      All other serial number not starting with FF are used as they are.
 */
 gpg_error_t
 app_munge_serialno (app_t app)
 {
   if (app->serialnolen && app->serialno[0] == 0xff)
-    { 
+    {
       /* The serial number starts with our special prefix.  This
          requires that we put our default prefix "FF0000" in front. */
       unsigned char *p = xtrymalloc (app->serialnolen + 3);
@@ -495,7 +508,7 @@ app_munge_serialno (app_t app)
       app->serialno = p;
     }
   else if (!app->serialnolen)
-    { 
+    {
       unsigned char *p = xtrymalloc (3);
       if (!p)
         return gpg_error_from_syserror ();
@@ -515,7 +528,7 @@ app_munge_serialno (app_t app)
    no update time is available the returned value is 0.  Caller must
    free SERIAL unless the function returns an error.  If STAMP is not
    of interest, NULL may be passed. */
-gpg_error_t 
+gpg_error_t
 app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp)
 {
   char *buf;
@@ -542,7 +555,7 @@ app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp)
 /* Write out the application specifig status lines for the LEARN
    command. */
 gpg_error_t
-app_write_learn_status (app_t app, ctrl_t ctrl)
+app_write_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
 {
   gpg_error_t err;
 
@@ -553,13 +566,14 @@ app_write_learn_status (app_t app, ctrl_t ctrl)
   if (!app->fnc.learn_status)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
 
-  if (app->apptype)
+  /* We do not send APPTYPE if only keypairinfo is requested.  */
+  if (app->apptype && !(flags & 1))
     send_status_info (ctrl, "APPTYPE",
                       app->apptype, strlen (app->apptype), NULL, 0);
-  err = lock_reader (app->slot);
+  err = lock_reader (app->slot, ctrl);
   if (err)
     return err;
-  err = app->fnc.learn_status (app, ctrl);
+  err = app->fnc.learn_status (app, ctrl, flags);
   unlock_reader (app->slot);
   return err;
 }
@@ -581,7 +595,7 @@ app_readcert (app_t app, const char *certid,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.readcert)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot);
+  err = lock_reader (app->slot, NULL/* FIXME*/);
   if (err)
     return err;
   err = app->fnc.readcert (app, certid, cert, certlen);
@@ -613,7 +627,7 @@ app_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.readkey)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot);
+  err = lock_reader (app->slot, NULL /*FIXME*/);
   if (err)
     return err;
   err= app->fnc.readkey (app, keyid, pk, pklen);
@@ -623,7 +637,7 @@ app_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
 
 
 /* Perform a GETATTR operation.  */
-gpg_error_t 
+gpg_error_t
 app_getattr (app_t app, ctrl_t ctrl, const char *name)
 {
   gpg_error_t err;
@@ -644,7 +658,7 @@ app_getattr (app_t app, ctrl_t ctrl, const char *name)
       char *serial;
       time_t stamp;
       int rc;
-      
+
       rc = app_get_serial_and_stamp (app, &serial, &stamp);
       if (rc)
         return rc;
@@ -655,7 +669,7 @@ app_getattr (app_t app, ctrl_t ctrl, const char *name)
 
   if (!app->fnc.getattr)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot);
+  err = lock_reader (app->slot, ctrl);
   if (err)
     return err;
   err =  app->fnc.getattr (app, ctrl, name);
@@ -664,7 +678,7 @@ app_getattr (app_t app, ctrl_t ctrl, const char *name)
 }
 
 /* Perform a SETATTR operation.  */
-gpg_error_t 
+gpg_error_t
 app_setattr (app_t app, const char *name,
              gpg_error_t (*pincb)(void*, const char *, char **),
              void *pincb_arg,
@@ -678,7 +692,7 @@ app_setattr (app_t app, const char *name,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.setattr)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot);
+  err = lock_reader (app->slot, NULL /*FIXME*/);
   if (err)
     return err;
   err = app->fnc.setattr (app, name, pincb, pincb_arg, value, valuelen);
@@ -689,7 +703,7 @@ app_setattr (app_t app, const char *name,
 /* Create the signature and return the allocated result in OUTDATA.
    If a PIN is required the PINCB will be used to ask for the PIN; it
    should return the PIN in an allocated buffer and put it into PIN.  */
-gpg_error_t 
+gpg_error_t
 app_sign (app_t app, const char *keyidstr, int hashalgo,
           gpg_error_t (*pincb)(void*, const char *, char **),
           void *pincb_arg,
@@ -704,7 +718,7 @@ app_sign (app_t app, const char *keyidstr, int hashalgo,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.sign)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot);
+  err = lock_reader (app->slot, NULL /*FIXME*/);
   if (err)
     return err;
   err = app->fnc.sign (app, keyidstr, hashalgo,
@@ -721,7 +735,7 @@ app_sign (app_t app, const char *keyidstr, int hashalgo,
    return the allocated result in OUTDATA.  If a PIN is required the
    PINCB will be used to ask for the PIN; it should return the PIN in
    an allocated buffer and put it into PIN.  */
-gpg_error_t 
+gpg_error_t
 app_auth (app_t app, const char *keyidstr,
           gpg_error_t (*pincb)(void*, const char *, char **),
           void *pincb_arg,
@@ -736,7 +750,7 @@ app_auth (app_t app, const char *keyidstr,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.auth)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot);
+  err = lock_reader (app->slot, NULL /*FIXME*/);
   if (err)
     return err;
   err = app->fnc.auth (app, keyidstr,
@@ -753,28 +767,32 @@ app_auth (app_t app, const char *keyidstr,
 /* Decrypt the data in INDATA and return the allocated result in OUTDATA.
    If a PIN is required the PINCB will be used to ask for the PIN; it
    should return the PIN in an allocated buffer and put it into PIN.  */
-gpg_error_t 
+gpg_error_t
 app_decipher (app_t app, const char *keyidstr,
               gpg_error_t (*pincb)(void*, const char *, char **),
               void *pincb_arg,
               const void *indata, size_t indatalen,
-              unsigned char **outdata, size_t *outdatalen )
+              unsigned char **outdata, size_t *outdatalen,
+              unsigned int *r_info)
 {
   gpg_error_t err;
 
+  *r_info = 0;
+
   if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb)
     return gpg_error (GPG_ERR_INV_VALUE);
   if (!app->ref_count)
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.decipher)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot);
+  err = lock_reader (app->slot, NULL /*FIXME*/);
   if (err)
     return err;
   err = app->fnc.decipher (app, keyidstr,
                            pincb, pincb_arg,
                            indata, indatalen,
-                           outdata, outdatalen);
+                           outdata, outdatalen,
+                           r_info);
   unlock_reader (app->slot);
   if (opt.verbose)
     log_info ("operation decipher result: %s\n", gpg_strerror (err));
@@ -798,7 +816,7 @@ app_writecert (app_t app, ctrl_t ctrl,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.writecert)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot);
+  err = lock_reader (app->slot, ctrl);
   if (err)
     return err;
   err = app->fnc.writecert (app, ctrl, certidstr,
@@ -826,7 +844,7 @@ app_writekey (app_t app, ctrl_t ctrl,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.writekey)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot);
+  err = lock_reader (app->slot, ctrl);
   if (err)
     return err;
   err = app->fnc.writekey (app, ctrl, keyidstr, flags,
@@ -839,7 +857,7 @@ app_writekey (app_t app, ctrl_t ctrl,
 
 
 /* Perform a SETATTR operation.  */
-gpg_error_t 
+gpg_error_t
 app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
             time_t createtime,
             gpg_error_t (*pincb)(void*, const char *, char **),
@@ -853,10 +871,10 @@ app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.genkey)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot);
+  err = lock_reader (app->slot, ctrl);
   if (err)
     return err;
-  err = app->fnc.genkey (app, ctrl, keynostr, flags, 
+  err = app->fnc.genkey (app, ctrl, keynostr, flags,
                          createtime, pincb, pincb_arg);
   unlock_reader (app->slot);
   if (opt.verbose)
@@ -865,7 +883,7 @@ app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
 }
 
 
-/* Perform a GET CHALLENGE operation.  This fucntion is special as it
+/* Perform a GET CHALLENGE operation.  This function is special as it
    directly accesses the card without any application specific
    wrapper. */
 gpg_error_t
@@ -877,7 +895,7 @@ app_get_challenge (app_t app, size_t nbytes, unsigned char *buffer)
     return gpg_error (GPG_ERR_INV_VALUE);
   if (!app->ref_count)
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
-  err = lock_reader (app->slot);
+  err = lock_reader (app->slot, NULL /*FIXME*/);
   if (err)
     return err;
   err = iso7816_get_challenge (app->slot, nbytes, buffer);
@@ -888,7 +906,7 @@ app_get_challenge (app_t app, size_t nbytes, unsigned char *buffer)
 
 
 /* Perform a CHANGE REFERENCE DATA or RESET RETRY COUNTER operation.  */
-gpg_error_t 
+gpg_error_t
 app_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode,
                 gpg_error_t (*pincb)(void*, const char *, char **),
                 void *pincb_arg)
@@ -901,7 +919,7 @@ app_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.change_pin)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot);
+  err = lock_reader (app->slot, ctrl);
   if (err)
     return err;
   err = app->fnc.change_pin (app, ctrl, chvnostr, reset_mode,
@@ -916,7 +934,7 @@ app_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode,
 /* Perform a VERIFY operation without doing anything lese.  This may
    be used to initialze a the PIN cache for long lasting other
    operations.  Its use is highly application dependent. */
-gpg_error_t 
+gpg_error_t
 app_check_pin (app_t app, const char *keyidstr,
                gpg_error_t (*pincb)(void*, const char *, char **),
                void *pincb_arg)
@@ -929,7 +947,7 @@ app_check_pin (app_t app, const char *keyidstr,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.check_pin)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot);
+  err = lock_reader (app->slot, NULL /*FIXME*/);
   if (err)
     return err;
   err = app->fnc.check_pin (app, keyidstr, pincb, pincb_arg);
@@ -938,4 +956,3 @@ app_check_pin (app_t app, const char *keyidstr,
     log_info ("operation check_pin result: %s\n", gpg_strerror (err));
   return err;
 }
-