Also check for GPG_ERR_ASS_CANCELED during an inquire.
[gnupg.git] / agent / call-scd.c
index 78e28fe..f9ededb 100644 (file)
@@ -1,11 +1,12 @@
 /* call-scd.c - fork of the scdaemon to do SC operations
- *     Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2002, 2005, 2007, 2010,
+ *               2011 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,8 +15,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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <ctype.h>
 #include <assert.h>
 #include <unistd.h>
+#ifdef HAVE_SIGNAL_H
+# include <signal.h>
+#endif
 #include <sys/stat.h>
 #include <sys/types.h>
 #ifndef HAVE_W32_SYSTEM
 #include <sys/wait.h>
 #endif
-#include <pth.h>
+#include <npth.h>
 
 #include "agent.h"
 #include <assuan.h>
 /* Definition of module local data of the CTRL structure.  */
 struct scd_local_s
 {
+  /* We keep a list of all allocated context with a an achnor at
+     SCD_LOCAL_LIST (see below). */
+  struct scd_local_s *next_local;
+
+  /* We need to get back to the ctrl object actually referencing this
+     structure.  This is really an awkward way of enumerint the lcoal
+     contects.  A much cleaner way would be to keep a global list of
+     ctrl objects to enumerate them.  */
+  ctrl_t ctrl_backlink;
+
   assuan_context_t ctx; /* NULL or session context for the SCdaemon
                            used with this connection. */
   int locked;           /* This flag is used to assert proper use of
@@ -64,16 +77,23 @@ struct learn_parm_s
   void *sinfo_cb_arg;
 };
 
-struct inq_needpin_s 
+struct inq_needpin_s
 {
   assuan_context_t ctx;
   int (*getpin_cb)(void *, const char *, char*, size_t);
   void *getpin_cb_arg;
+  assuan_context_t passthru;  /* If not NULL, pass unknown inquiries
+                                 up to the caller.  */
+  int any_inq_seen;
 };
 
 
+/* To keep track of all active SCD contexts, we keep a linked list
+   anchored at this variable. */
+static struct scd_local_s *scd_local_list;
+
 /* A Mutex used inside the start_scd function. */
-static pth_mutex_t start_scd_lock;
+static npth_mutex_t start_scd_lock;
 
 /* A malloced string with the name of the socket to be used for
    additional connections.  May be NULL if not provided by
@@ -92,30 +112,46 @@ static int primary_scd_ctx_reusable;
 
 
 /* Local prototypes.  */
-static assuan_error_t membuf_data_cb (void *opaque,
-                                      const void *buffer, size_t length);
+static gpg_error_t membuf_data_cb (void *opaque,
+                                  const void *buffer, size_t length);
 
 
 
 \f
 /* This function must be called once to initialize this module.  This
    has to be done before a second thread is spawned.  We can't do the
-   static initialization because Pth emulation code might not be able
+   static initialization because NPth emulation code might not be able
    to do a static init; in particular, it is not possible for W32. */
 void
 initialize_module_call_scd (void)
 {
   static int initialized;
+  int err;
 
   if (!initialized)
     {
-      if (!pth_mutex_init (&start_scd_lock))
-        log_fatal ("error initializing mutex: %s\n", strerror (errno));
+      err = npth_mutex_init (&start_scd_lock, NULL);
+      if (err)
+       log_fatal ("error initializing mutex: %s\n", strerror (err));
       initialized = 1;
     }
 }
 
 
+/* This function may be called to print infromation pertaining to the
+   current state of this module to the log. */
+void
+agent_scd_dump_state (void)
+{
+  log_info ("agent_scd_dump_state: primary_scd_ctx=%p pid=%ld reusable=%d\n",
+            primary_scd_ctx,
+            (long)assuan_get_pid (primary_scd_ctx),
+            primary_scd_ctx_reusable);
+  if (socket_name)
+    log_info ("agent_scd_dump_state: socket=`%s'\n", socket_name);
+}
+
+
 /* The unlock_scd function shall be called after having accessed the
    SCD.  It is currently not very useful but gives an opportunity to
    keep track of connections currently calling SCD.  Note that the
@@ -123,7 +159,7 @@ initialize_module_call_scd (void)
    called and error checked before any SCD operation.  CTRL is the
    usual connection context and RC the error code to be passed trhough
    the function. */
-static int 
+static int
 unlock_scd (ctrl_t ctrl, int rc)
 {
   if (ctrl->scd_local->locked != 1)
@@ -142,6 +178,8 @@ unlock_scd (ctrl_t ctrl, int rc)
 static void
 atfork_cb (void *opaque, int where)
 {
+  (void)opaque;
+
   if (!where)
     gcry_control (GCRYCTL_TERM_SECMEM);
 }
@@ -149,7 +187,7 @@ atfork_cb (void *opaque, int where)
 
 /* Fork off the SCdaemon if this has not already been done.  Lock the
    daemon and make sure that a proper context has been setup in CTRL.
-   Thsi fucntion might also lock the daemon, which means that the
+   This function might also lock the daemon, which means that the
    caller must call unlock_scd after this fucntion has returned
    success and the actual Assuan transaction been done. */
 static int
@@ -157,7 +195,7 @@ start_scd (ctrl_t ctrl)
 {
   gpg_error_t err = 0;
   const char *pgmname;
-  assuan_context_t ctx;
+  assuan_context_t ctx = NULL;
   const char *argv[3];
   int no_close_list[3];
   int i;
@@ -172,7 +210,10 @@ start_scd (ctrl_t ctrl)
     {
       ctrl->scd_local = xtrycalloc (1, sizeof *ctrl->scd_local);
       if (!ctrl->scd_local)
-        return gpg_error_from_errno (errno);
+        return gpg_error_from_syserror ();
+      ctrl->scd_local->ctrl_backlink = ctrl;
+      ctrl->scd_local->next_local = scd_local_list;
+      scd_local_list = ctrl->scd_local;
     }
 
 
@@ -187,17 +228,18 @@ start_scd (ctrl_t ctrl)
 
   if (ctrl->scd_local->ctx)
     return 0; /* Okay, the context is fine.  We used to test for an
-                 alive context here and do an disconnect.  How that we
+                 alive context here and do an disconnect.  Now that we
                  have a ticker function to check for it, it is easier
                  not to check here but to let the connection run on an
                  error instead. */
 
 
   /* We need to protect the following code. */
-  if (!pth_mutex_acquire (&start_scd_lock, 0, NULL))
+  rc = npth_mutex_lock (&start_scd_lock);
+  if (rc)
     {
       log_error ("failed to acquire the start_scd lock: %s\n",
-                 strerror (errno));
+                 strerror (rc));
       return gpg_error (GPG_ERR_INTERNAL);
     }
 
@@ -213,13 +255,21 @@ start_scd (ctrl_t ctrl)
       goto leave;
     }
 
+  rc = assuan_new (&ctx);
+  if (rc)
+    {
+      log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc));
+      err = rc;
+      goto leave;
+    }
+
   if (socket_name)
     {
-      rc = assuan_socket_connect (&ctx, socket_name, 0);
+      rc = assuan_socket_connect (ctx, socket_name, 0, 0);
       if (rc)
         {
           log_error ("can't connect to socket `%s': %s\n",
-                     socket_name, assuan_strerror (rc));
+                     socket_name, gpg_strerror (rc));
           err = gpg_error (GPG_ERR_NO_SCDAEMON);
           goto leave;
         }
@@ -239,16 +289,24 @@ start_scd (ctrl_t ctrl)
   /* Nope, it has not been started.  Fire it up now. */
   if (opt.verbose)
     log_info ("no running SCdaemon - starting it\n");
-      
+
   if (fflush (NULL))
     {
-      err = gpg_error (gpg_err_code_from_errno (errno));
+#ifndef HAVE_W32_SYSTEM
+      err = gpg_error_from_syserror ();
+#endif
       log_error ("error flushing pending output: %s\n", strerror (errno));
+      /* At least Windows XP fails here with EBADF.  According to docs
+         and Wine an fflush(NULL) is the same as _flushall.  However
+         the Wime implementaion does not flush stdin,stdout and stderr
+         - see above.  Lets try to ignore the error. */
+#ifndef HAVE_W32_SYSTEM
       goto leave;
+#endif
     }
 
   if (!opt.scdaemon_program || !*opt.scdaemon_program)
-    opt.scdaemon_program = GNUPG_DEFAULT_SCDAEMON;
+    opt.scdaemon_program = gnupg_module_name (GNUPG_MODULE_NAME_SCDAEMON);
   if ( !(pgmname = strrchr (opt.scdaemon_program, '/')))
     pgmname = opt.scdaemon_program;
   else
@@ -262,18 +320,20 @@ start_scd (ctrl_t ctrl)
   if (!opt.running_detached)
     {
       if (log_get_fd () != -1)
-        no_close_list[i++] = log_get_fd ();
-      no_close_list[i++] = fileno (stderr);
+        no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
+      no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
     }
   no_close_list[i] = -1;
 
-  /* Connect to the pinentry and perform initial handshaking */
-  rc = assuan_pipe_connect2 (&ctx, opt.scdaemon_program, (char**)argv,
-                             no_close_list, atfork_cb, NULL);
+  /* Connect to the pinentry and perform initial handshaking.  Use
+     detached flag (128) so that under W32 SCDAEMON does not show up a
+     new window.  */
+  rc = assuan_pipe_connect (ctx, opt.scdaemon_program, argv,
+                           no_close_list, atfork_cb, NULL, 128);
   if (rc)
     {
       log_error ("can't connect to the SCdaemon: %s\n",
-                 assuan_strerror (rc));
+                 gpg_strerror (rc));
       err = gpg_error (GPG_ERR_NO_SCDAEMON);
       goto leave;
     }
@@ -281,6 +341,7 @@ start_scd (ctrl_t ctrl)
   if (opt.verbose)
     log_debug ("first connection to SCdaemon established\n");
 
+
   /* Get the name of the additional socket opened by scdaemon. */
   {
     membuf_t data;
@@ -311,15 +372,22 @@ start_scd (ctrl_t ctrl)
     xfree (databuf);
   }
 
-  /* Tell the scdaemon we want him to send us an event signal. */
-#ifndef HAVE_W32_SYSTEM
-  {
-    char buf[100];
+  /* Tell the scdaemon we want him to send us an event signal.  We
+     don't support this for W32CE.  */
+#ifndef HAVE_W32CE_SYSTEM
+  if (opt.sigusr2_enabled)
+    {
+      char buf[100];
 
-    sprintf (buf, "OPTION event-signal=%d", SIGUSR2);
-    assuan_transact (ctx, buf, NULL, NULL, NULL, NULL, NULL, NULL);
-  }
+#ifdef HAVE_W32_SYSTEM
+      snprintf (buf, sizeof buf, "OPTION event-signal=%lx",
+                (unsigned long)get_agent_scd_notify_event ());
+#else
+      snprintf (buf, sizeof buf, "OPTION event-signal=%d", SIGUSR2);
 #endif
+      assuan_transact (ctx, buf, NULL, NULL, NULL, NULL, NULL, NULL);
+    }
+#endif /*HAVE_W32CE_SYSTEM*/
 
   primary_scd_ctx = ctx;
   primary_scd_ctx_reusable = 0;
@@ -328,63 +396,118 @@ start_scd (ctrl_t ctrl)
   if (err)
     {
       unlock_scd (ctrl, err);
-    } 
+      if (ctx)
+       assuan_release (ctx);
+    }
   else
     {
       ctrl->scd_local->ctx = ctx;
     }
-  if (!pth_mutex_release (&start_scd_lock))
-    log_error ("failed to release the start_scd lock: %s\n", strerror (errno));
+  rc = npth_mutex_unlock (&start_scd_lock);
+  if (rc)
+    log_error ("failed to release the start_scd lock: %s\n", strerror (rc));
   return err;
 }
 
 
+/* Check whether the SCdaemon is active.  This is a fast check without
+   any locking and might give a wrong result if another thread is about
+   to start the daemon or the daemon is about to be stopped.. */
+int
+agent_scd_check_running (void)
+{
+  return !!primary_scd_ctx;
+}
+
+
 /* Check whether the Scdaemon is still alive and clean it up if not. */
 void
 agent_scd_check_aliveness (void)
 {
   pid_t pid;
+#ifdef HAVE_W32_SYSTEM
+  DWORD rc;
+#else
   int rc;
+#endif
+  struct timespec abstime;
+  int err;
 
-  /* We can do so only if there is no more active primary connection.
-     With an active primary connection, this is all no problem because
-     with the end of gpg-agent's session a disconnect is send and the
-     this function will be used at a later time. */
-  if (!primary_scd_ctx || !primary_scd_ctx_reusable)
-    return;
+  if (!primary_scd_ctx)
+    return; /* No scdaemon running. */
 
-  if (!pth_mutex_acquire (&start_scd_lock, 0, NULL))
+  /* This is not a critical function so we use a short timeout while
+     acquiring the lock.  */
+  npth_clock_gettime (&abstime);
+  abstime.tv_sec += 1;
+  err = npth_mutex_timedlock (&start_scd_lock, &abstime);
+  if (err)
     {
-      log_error ("failed to acquire the start_scd lock while"
-                 " doing an aliveness check: %s\n",
-                 strerror (errno));
+      if (err == ETIMEDOUT)
+        {
+          if (opt.verbose > 1)
+            log_info ("failed to acquire the start_scd lock while"
+                      " doing an aliveness check: %s\n", strerror (err));
+        }
+      else
+        log_error ("failed to acquire the start_scd lock while"
+                   " doing an aliveness check: %s\n", strerror (err));
       return;
     }
 
-  if (primary_scd_ctx && primary_scd_ctx_reusable)
+  if (primary_scd_ctx)
     {
       pid = assuan_get_pid (primary_scd_ctx);
+#ifdef HAVE_W32_SYSTEM
+      /* If we have a PID we disconnect if either GetExitProcessCode
+         fails or if ir returns the exit code of the scdaemon.  259 is
+         the error code for STILL_ALIVE.  */
+      if (pid != (pid_t)(void*)(-1) && pid
+          && (!GetExitCodeProcess ((HANDLE)pid, &rc) || rc != 259))
+#else
       if (pid != (pid_t)(-1) && pid
           && ((rc=waitpid (pid, NULL, WNOHANG))==-1 || (rc == pid)) )
+#endif
         {
-          /* Okay, scdaemon died.  Disconnect the primary connection now
-             but take care that it won't do another wait. */
+          /* Okay, scdaemon died.  Disconnect the primary connection
+             now but take care that it won't do another wait. Also
+             cleanup all other connections and release their
+             resources.  The next use will start a new daemon then.
+             Due to the use of the START_SCD_LOCAL we are sure that
+             none of these context are actually in use. */
+          struct scd_local_s *sl;
+
           assuan_set_flag (primary_scd_ctx, ASSUAN_NO_WAITPID, 1);
-          assuan_disconnect (primary_scd_ctx);
+          assuan_release (primary_scd_ctx);
+
+          for (sl=scd_local_list; sl; sl = sl->next_local)
+            {
+              if (sl->ctx)
+                {
+                  if (sl->ctx != primary_scd_ctx)
+                    assuan_release (sl->ctx);
+                  sl->ctx = NULL;
+                }
+            }
+
           primary_scd_ctx = NULL;
           primary_scd_ctx_reusable = 0;
+
           xfree (socket_name);
           socket_name = NULL;
         }
     }
 
-  if (!pth_mutex_release (&start_scd_lock))
+  err = npth_mutex_unlock (&start_scd_lock);
+  if (err)
     log_error ("failed to release the start_scd lock while"
-               " doing the aliveness check: %s\n", strerror (errno));
+               " doing the aliveness check: %s\n", strerror (err));
 }
 
 
-/* Reset the SCD if it has been used. */
+
+/* Reset the SCD if it has been used.  Actually it is not a reset but
+   a cleanup of resources used by the current connection. */
 int
 agent_reset_scd (ctrl_t ctrl)
 {
@@ -398,16 +521,40 @@ agent_reset_scd (ctrl_t ctrl)
              reuse. */
           if (ctrl->scd_local->ctx == primary_scd_ctx)
             {
-              /* The RESET may fail for example if the scdaemon has
-                 already been terminated.  We need to set the reusable
-                 flag anyway to make sure that the aliveness check can
-                 clean it up. */
-              assuan_transact (primary_scd_ctx, "RESET",
+              /* Send a RESTART to the SCD.  This is required for the
+                 primary connection as a kind of virtual EOF; we don't
+                 have another way to tell it that the next command
+                 should be viewed as if a new connection has been
+                 made.  For the non-primary connections this is not
+                 needed as we simply close the socket.  We don't check
+                 for an error here because the RESTART may fail for
+                 example if the scdaemon has already been terminated.
+                 Anyway, we need to set the reusable flag to make sure
+                 that the aliveness check can clean it up. */
+              assuan_transact (primary_scd_ctx, "RESTART",
                                NULL, NULL, NULL, NULL, NULL, NULL);
               primary_scd_ctx_reusable = 1;
             }
           else
-            assuan_disconnect (ctrl->scd_local->ctx);
+            assuan_release (ctrl->scd_local->ctx);
+          ctrl->scd_local->ctx = NULL;
+        }
+
+      /* Remove the local context from our list and release it. */
+      if (!scd_local_list)
+        BUG ();
+      else if (scd_local_list == ctrl->scd_local)
+        scd_local_list = ctrl->scd_local->next_local;
+      else
+        {
+          struct scd_local_s *sl;
+
+          for (sl=scd_local_list; sl->next_local; sl = sl->next_local)
+            if (sl->next_local == ctrl->scd_local)
+              break;
+          if (!sl->next_local)
+            BUG ();
+          sl->next_local = ctrl->scd_local->next_local;
         }
       xfree (ctrl->scd_local);
       ctrl->scd_local = NULL;
@@ -418,44 +565,7 @@ agent_reset_scd (ctrl_t ctrl)
 
 
 \f
-/* Return a new malloced string by unescaping the string S.  Escaping
-   is percent escaping and '+'/space mapping.  A binary Nul will
-   silently be replaced by a 0xFF.  Function returns NULL to indicate
-   an out of memory status. */
-static char *
-unescape_status_string (const unsigned char *s)
-{
-  char *buffer, *d;
-
-  buffer = d = xtrymalloc (strlen (s)+1);
-  if (!buffer)
-    return NULL;
-  while (*s)
-    {
-      if (*s == '%' && s[1] && s[2])
-        { 
-          s++;
-          *d = xtoi_2 (s);
-          if (!*d)
-            *d = '\xff';
-          d++;
-          s += 2;
-        }
-      else if (*s == '+')
-        {
-          *d++ = ' ';
-          s++;
-        }
-      else
-        *d++ = *s++;
-    }
-  *d = 0; 
-  return buffer;
-}
-
-
-\f
-static AssuanError
+static gpg_error_t
 learn_status_cb (void *opaque, const char *line)
 {
   struct learn_parm_s *parm = opaque;
@@ -478,7 +588,7 @@ learn_status_cb (void *opaque, const char *line)
     {
       parm->sinfo_cb (parm->sinfo_cb_arg, keyword, keywordlen, line);
     }
-  
+
   return 0;
 }
 
@@ -511,14 +621,14 @@ agent_card_learn (ctrl_t ctrl,
                         NULL, NULL, NULL, NULL,
                         learn_status_cb, &parm);
   if (rc)
-    return unlock_scd (ctrl, map_assuan_err (rc));
+    return unlock_scd (ctrl, rc);
 
   return unlock_scd (ctrl, 0);
 }
 
 
 \f
-static AssuanError
+static gpg_error_t
 get_serialno_cb (void *opaque, const char *line)
 {
   char **serialno = opaque;
@@ -534,18 +644,18 @@ get_serialno_cb (void *opaque, const char *line)
   if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen))
     {
       if (*serialno)
-        return ASSUAN_Unexpected_Status;
+        return gpg_error (GPG_ERR_CONFLICT); /* Unexpected status line. */
       for (n=0,s=line; hexdigitp (s); s++, n++)
         ;
       if (!n || (n&1)|| !(spacep (s) || !*s) )
-        return ASSUAN_Invalid_Status;
+        return gpg_error (GPG_ERR_ASS_PARAMETER);
       *serialno = xtrymalloc (n+1);
       if (!*serialno)
-        return ASSUAN_Out_Of_Core;
+        return out_of_core ();
       memcpy (*serialno, line, n);
       (*serialno)[n] = 0;
     }
-  
+
   return 0;
 }
 
@@ -567,7 +677,7 @@ agent_card_serialno (ctrl_t ctrl, char **r_serialno)
   if (rc)
     {
       xfree (serialno);
-      return unlock_scd (ctrl, map_assuan_err (rc));
+      return unlock_scd (ctrl, rc);
     }
   *r_serialno = serialno;
   return unlock_scd (ctrl, 0);
@@ -576,7 +686,7 @@ agent_card_serialno (ctrl_t ctrl, char **r_serialno)
 
 
 \f
-static AssuanError
+static gpg_error_t
 membuf_data_cb (void *opaque, const void *buffer, size_t length)
 {
   membuf_t *data = opaque;
@@ -585,9 +695,9 @@ membuf_data_cb (void *opaque, const void *buffer, size_t length)
     put_membuf (data, buffer, length);
   return 0;
 }
-  
+
 /* Handle the NEEDPIN inquiry. */
-static AssuanError
+static gpg_error_t
 inq_needpin (void *opaque, const char *line)
 {
   struct inq_needpin_s *parm = opaque;
@@ -595,40 +705,129 @@ inq_needpin (void *opaque, const char *line)
   size_t pinlen;
   int rc;
 
-  if (!(!strncmp (line, "NEEDPIN", 7) && (line[7] == ' ' || !line[7])))
+  parm->any_inq_seen = 1;
+  if (!strncmp (line, "NEEDPIN", 7) && (line[7] == ' ' || !line[7]))
+    {
+      line += 7;
+      while (*line == ' ')
+        line++;
+
+      pinlen = 90;
+      pin = gcry_malloc_secure (pinlen);
+      if (!pin)
+        return out_of_core ();
+
+      rc = parm->getpin_cb (parm->getpin_cb_arg, line, pin, pinlen);
+      if (!rc)
+        rc = assuan_send_data (parm->ctx, pin, pinlen);
+      xfree (pin);
+    }
+  else if (!strncmp (line, "POPUPKEYPADPROMPT", 17)
+           && (line[17] == ' ' || !line[17]))
+    {
+      line += 17;
+      while (*line == ' ')
+        line++;
+
+      rc = parm->getpin_cb (parm->getpin_cb_arg, line, NULL, 1);
+    }
+  else if (!strncmp (line, "DISMISSKEYPADPROMPT", 19)
+           && (line[19] == ' ' || !line[19]))
+    {
+      rc = parm->getpin_cb (parm->getpin_cb_arg, "", NULL, 0);
+    }
+  else if (parm->passthru)
+    {
+      unsigned char *value;
+      size_t valuelen;
+      int rest;
+      int needrest = !strncmp (line, "KEYDATA", 8);
+
+      /* Pass the inquiry up to our caller.  We limit the maximum
+         amount to an arbitrary value.  As we know that the KEYDATA
+         enquiry is pretty sensitive we disable logging then */
+      if ((rest = (needrest
+                   && !assuan_get_flag (parm->passthru, ASSUAN_CONFIDENTIAL))))
+        assuan_begin_confidential (parm->passthru);
+      rc = assuan_inquire (parm->passthru, line, &value, &valuelen, 8096);
+      if (rest)
+        assuan_end_confidential (parm->passthru);
+      if (!rc)
+        {
+          if ((rest = (needrest
+                       && !assuan_get_flag (parm->ctx, ASSUAN_CONFIDENTIAL))))
+            assuan_begin_confidential (parm->ctx);
+          rc = assuan_send_data (parm->ctx, value, valuelen);
+          if (rest)
+            assuan_end_confidential (parm->ctx);
+          xfree (value);
+        }
+      else
+        log_error ("error forwarding inquiry `%s': %s\n",
+                   line, gpg_strerror (rc));
+    }
+  else
     {
       log_error ("unsupported inquiry `%s'\n", line);
-      return ASSUAN_Inquire_Unknown;
+      rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
     }
-  line += 7;
-  while (*line == ' ')
-    line++;
 
-  pinlen = 90;
-  pin = gcry_malloc_secure (pinlen);
-  if (!pin)
-    return ASSUAN_Out_Of_Core;
+  return rc;
+}
 
-  rc = parm->getpin_cb (parm->getpin_cb_arg, line, pin, pinlen);
-  if (rc)
-    rc = ASSUAN_Canceled;
-  if (!rc)
-    rc = assuan_send_data (parm->ctx, pin, pinlen);
-  xfree (pin);
 
-  return rc;
+/* Helper returning a command option to describe the used hash
+   algorithm.  See scd/command.c:cmd_pksign.  */
+static const char *
+hash_algo_option (int algo)
+{
+  switch (algo)
+    {
+    case GCRY_MD_MD5   : return "--hash=md5";
+    case GCRY_MD_RMD160: return "--hash=rmd160";
+    case GCRY_MD_SHA1  : return "--hash=sha1";
+    case GCRY_MD_SHA224: return "--hash=sha224";
+    case GCRY_MD_SHA256: return "--hash=sha256";
+    case GCRY_MD_SHA384: return "--hash=sha384";
+    case GCRY_MD_SHA512: return "--hash=sha512";
+    default:             return "";
+    }
 }
 
 
+static gpg_error_t
+cancel_inquire (ctrl_t ctrl, gpg_error_t rc)
+{
+  gpg_error_t oldrc = rc;
+
+  /* The inquire callback was called and transact returned a
+     cancel error.  We assume that the inquired process sent a
+     CANCEL.  The passthrough code is not able to pass on the
+     CANCEL and thus scdaemon would stuck on this.  As a
+     workaround we send a CANCEL now.  */
+  rc = assuan_write_line (ctrl->scd_local->ctx, "CAN");
+  if (!rc) {
+    char *line;
+    size_t len;
+
+    rc = assuan_read_line (ctrl->scd_local->ctx, &line, &len);
+    if (!rc)
+      rc = oldrc;
+  }
+
+  return rc;
+}
 
-/* Create a signature using the current card */
+/* Create a signature using the current card.  MDALGO is either 0 or
+   gives the digest algorithm.  */
 int
 agent_card_pksign (ctrl_t ctrl,
                    const char *keyid,
                    int (*getpin_cb)(void *, const char *, char*, size_t),
                    void *getpin_cb_arg,
+                   int mdalgo,
                    const unsigned char *indata, size_t indatalen,
-                   char **r_buf, size_t *r_buflen)
+                   unsigned char **r_buf, size_t *r_buflen)
 {
   int rc, i;
   char *p, line[ASSUAN_LINELENGTH];
@@ -637,6 +836,7 @@ agent_card_pksign (ctrl_t ctrl,
   size_t len;
   unsigned char *sigbuf;
   size_t sigbuflen;
+  int prepend_nul;
 
   *r_buf = NULL;
   rc = start_scd (ctrl);
@@ -653,39 +853,49 @@ agent_card_pksign (ctrl_t ctrl,
   rc = assuan_transact (ctrl->scd_local->ctx, line,
                         NULL, NULL, NULL, NULL, NULL, NULL);
   if (rc)
-    return unlock_scd (ctrl, map_assuan_err (rc));
+    return unlock_scd (ctrl, rc);
 
   init_membuf (&data, 1024);
   inqparm.ctx = ctrl->scd_local->ctx;
   inqparm.getpin_cb = getpin_cb;
   inqparm.getpin_cb_arg = getpin_cb_arg;
-  snprintf (line, DIM(line)-1, 
-            ctrl->use_auth_call? "PKAUTH %s":"PKSIGN %s", keyid);
-  line[DIM(line)-1] = 0;
+  inqparm.passthru = 0;
+  inqparm.any_inq_seen = 0;
+  if (ctrl->use_auth_call)
+    snprintf (line, sizeof line, "PKAUTH %s", keyid);
+  else
+    snprintf (line, sizeof line, "PKSIGN %s %s",
+              hash_algo_option (mdalgo), keyid);
   rc = assuan_transact (ctrl->scd_local->ctx, line,
                         membuf_data_cb, &data,
                         inq_needpin, &inqparm,
                         NULL, NULL);
+  if (inqparm.any_inq_seen && (gpg_err_code(rc) == GPG_ERR_CANCELED ||
+       gpg_err_code(rc) == GPG_ERR_ASS_CANCELED))
+    rc = cancel_inquire (ctrl, rc);
+
   if (rc)
     {
       xfree (get_membuf (&data, &len));
-      return unlock_scd (ctrl, map_assuan_err (rc));
+      return unlock_scd (ctrl, rc);
     }
   sigbuf = get_membuf (&data, &sigbuflen);
 
   /* Create an S-expression from it which is formatted like this:
-     "(7:sig-val(3:rsa(1:sSIGBUFLEN:SIGBUF)))" */
-  *r_buflen = 21 + 11 + sigbuflen + 4;
-  *r_buf = xtrymalloc (*r_buflen);
-  if (!*r_buf)
-    {
-      gpg_error_t tmperr = out_of_core ();
-      xfree (*r_buf);
-      return unlock_scd (ctrl, tmperr);
-    }
-  p = stpcpy (*r_buf, "(7:sig-val(3:rsa(1:s" );
-  sprintf (p, "%u:", (unsigned int)sigbuflen);
+     "(7:sig-val(3:rsa(1:sSIGBUFLEN:SIGBUF)))".  We better make sure
+     that this won't be interpreted as a negative number.  */
+  prepend_nul = (sigbuflen && (*sigbuf & 0x80));
+
+  *r_buflen = 21 + 11 + prepend_nul + sigbuflen + 4;
+  p = xtrymalloc (*r_buflen);
+  *r_buf = (unsigned char*)p;
+  if (!p)
+    return unlock_scd (ctrl, out_of_core ());
+  p = stpcpy (p, "(7:sig-val(3:rsa(1:s" );
+  sprintf (p, "%u:", (unsigned int)sigbuflen + prepend_nul);
   p += strlen (p);
+  if (prepend_nul)
+    *p++ = 0;
   memcpy (p, sigbuf, sigbuflen);
   p += sigbuflen;
   strcpy (p, ")))");
@@ -726,22 +936,28 @@ agent_card_pkdecrypt (ctrl_t ctrl,
   rc = assuan_transact (ctrl->scd_local->ctx, line,
                         NULL, NULL, NULL, NULL, NULL, NULL);
   if (rc)
-    return unlock_scd (ctrl, map_assuan_err (rc));
+    return unlock_scd (ctrl, rc);
 
   init_membuf (&data, 1024);
   inqparm.ctx = ctrl->scd_local->ctx;
   inqparm.getpin_cb = getpin_cb;
   inqparm.getpin_cb_arg = getpin_cb_arg;
+  inqparm.passthru = 0;
+  inqparm.any_inq_seen = 0;
   snprintf (line, DIM(line)-1, "PKDECRYPT %s", keyid);
   line[DIM(line)-1] = 0;
   rc = assuan_transact (ctrl->scd_local->ctx, line,
                         membuf_data_cb, &data,
                         inq_needpin, &inqparm,
                         NULL, NULL);
+  if (inqparm.any_inq_seen && (gpg_err_code(rc) == GPG_ERR_CANCELED ||
+       gpg_err_code(rc) == GPG_ERR_ASS_CANCELED))
+    rc = cancel_inquire (ctrl, rc);
+
   if (rc)
     {
       xfree (get_membuf (&data, &len));
-      return unlock_scd (ctrl, map_assuan_err (rc));
+      return unlock_scd (ctrl, rc);
     }
   *r_buf = get_membuf (&data, r_buflen);
   if (!*r_buf)
@@ -777,7 +993,7 @@ agent_card_readcert (ctrl_t ctrl,
   if (rc)
     {
       xfree (get_membuf (&data, &len));
-      return unlock_scd (ctrl, map_assuan_err (rc));
+      return unlock_scd (ctrl, rc);
     }
   *r_buf = get_membuf (&data, r_buflen);
   if (!*r_buf)
@@ -813,7 +1029,7 @@ agent_card_readkey (ctrl_t ctrl, const char *id, unsigned char **r_buf)
   if (rc)
     {
       xfree (get_membuf (&data, &len));
-      return unlock_scd (ctrl, map_assuan_err (rc));
+      return unlock_scd (ctrl, rc);
     }
   *r_buf = get_membuf (&data, &buflen);
   if (!*r_buf)
@@ -839,7 +1055,7 @@ struct card_getattr_parm_s {
 };
 
 /* Callback function for agent_card_getattr.  */
-static assuan_error_t
+static gpg_error_t
 card_getattr_cb (void *opaque, const char *line)
 {
   struct card_getattr_parm_s *parm = opaque;
@@ -857,11 +1073,11 @@ card_getattr_cb (void *opaque, const char *line)
   if (keywordlen == parm->keywordlen
       && !memcmp (keyword, parm->keyword, keywordlen))
     {
-      parm->data = unescape_status_string (line);
+      parm->data = percent_plus_unescape ((const unsigned char*)line, 0xff);
       if (!parm->data)
         parm->error = errno;
     }
-  
+
   return 0;
 }
 
@@ -889,21 +1105,21 @@ agent_card_getattr (ctrl_t ctrl, const char *name, char **result)
   /* We assume that NAME does not need escaping. */
   if (8 + strlen (name) > DIM(line)-1)
     return gpg_error (GPG_ERR_TOO_LARGE);
-  stpcpy (stpcpy (line, "GETATTR "), name); 
+  stpcpy (stpcpy (line, "GETATTR "), name);
 
   err = start_scd (ctrl);
   if (err)
     return err;
 
-  err = map_assuan_err (assuan_transact (ctrl->scd_local->ctx, line,
-                                         NULL, NULL, NULL, NULL,
-                                         card_getattr_cb, &parm));
+  err = assuan_transact (ctrl->scd_local->ctx, line,
+                         NULL, NULL, NULL, NULL,
+                         card_getattr_cb, &parm);
   if (!err && parm.error)
     err = gpg_error_from_errno (parm.error);
-  
+
   if (!err && !parm.data)
     err = gpg_error (GPG_ERR_NO_DATA);
-  
+
   if (!err)
     *result = parm.data;
   else
@@ -915,30 +1131,42 @@ agent_card_getattr (ctrl_t ctrl, const char *name, char **result)
 
 
 \f
-static AssuanError
+static gpg_error_t
 pass_status_thru (void *opaque, const char *line)
 {
-  ASSUAN_CONTEXT ctx = opaque;
+  assuan_context_t ctx = opaque;
   char keyword[200];
   int i;
 
-  for (i=0; *line && !spacep (line) && i < DIM(keyword)-1; line++, i++)
-    keyword[i] = *line;
-  keyword[i] = 0;
-  /* truncate any remaining keyword stuff. */
-  for (; *line && !spacep (line); line++)
-    ;
-  while (spacep (line))
-    line++;
+  if (line[0] == '#' && (!line[1] || spacep (line+1)))
+    {
+      /* We are called in convey comments mode.  Now, if we see a
+         comment marker as keyword we forward the line verbatim to the
+         the caller.  This way the comment lines from scdaemon won't
+         appear as status lines with keyword '#'.  */
+      assuan_write_line (ctx, line);
+    }
+  else
+    {
+      for (i=0; *line && !spacep (line) && i < DIM(keyword)-1; line++, i++)
+        keyword[i] = *line;
+      keyword[i] = 0;
+
+      /* Truncate any remaining keyword stuff.  */
+      for (; *line && !spacep (line); line++)
+        ;
+      while (spacep (line))
+        line++;
 
-  assuan_write_status (ctx, keyword, line);
+      assuan_write_status (ctx, keyword, line);
+    }
   return 0;
 }
 
-static AssuanError
+static gpg_error_t
 pass_data_thru (void *opaque, const void *buffer, size_t length)
 {
-  ASSUAN_CONTEXT ctx = opaque;
+  assuan_context_t ctx = opaque;
 
   assuan_send_data (ctx, buffer, length);
   return 0;
@@ -947,8 +1175,8 @@ pass_data_thru (void *opaque, const void *buffer, size_t length)
 
 /* Send the line CMDLINE with command for the SCDdaemon to it and send
    all status messages back.  This command is used as a general quoting
-   mechanism to pass everything verbatim to SCDAEMOPN.  The PIN
-   inquirey is handled inside gpg-agent. */
+   mechanism to pass everything verbatim to SCDAEMON.  The PIN
+   inquiry is handled inside gpg-agent.  */
 int
 agent_card_scd (ctrl_t ctrl, const char *cmdline,
                 int (*getpin_cb)(void *, const char *, char*, size_t),
@@ -956,6 +1184,7 @@ agent_card_scd (ctrl_t ctrl, const char *cmdline,
 {
   int rc;
   struct inq_needpin_s inqparm;
+  int saveflag;
 
   rc = start_scd (ctrl);
   if (rc)
@@ -964,16 +1193,22 @@ agent_card_scd (ctrl_t ctrl, const char *cmdline,
   inqparm.ctx = ctrl->scd_local->ctx;
   inqparm.getpin_cb = getpin_cb;
   inqparm.getpin_cb_arg = getpin_cb_arg;
+  inqparm.passthru = assuan_context;
+  inqparm.any_inq_seen = 0;
+  saveflag = assuan_get_flag (ctrl->scd_local->ctx, ASSUAN_CONVEY_COMMENTS);
+  assuan_set_flag (ctrl->scd_local->ctx, ASSUAN_CONVEY_COMMENTS, 1);
   rc = assuan_transact (ctrl->scd_local->ctx, cmdline,
                         pass_data_thru, assuan_context,
                         inq_needpin, &inqparm,
                         pass_status_thru, assuan_context);
+  if (inqparm.any_inq_seen && gpg_err_code(rc) == GPG_ERR_ASS_CANCELED)
+    rc = cancel_inquire (ctrl, rc);
+
+  assuan_set_flag (ctrl->scd_local->ctx, ASSUAN_CONVEY_COMMENTS, saveflag);
   if (rc)
     {
-      return unlock_scd (ctrl, map_assuan_err (rc));
+      return unlock_scd (ctrl, rc);
     }
 
   return unlock_scd (ctrl, 0);
 }
-
-