Fix possible realloc overflow for gpgsm and uiserver engines.
[gpgme.git] / src / engine-gpgsm.c
index 2d92732..3a83757 100644 (file)
@@ -1,19 +1,20 @@
 /* engine-gpgsm.c - GpgSM engine.
    Copyright (C) 2000 Werner Koch (dd9jn)
-   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH
+   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009,
+                 2010 g10 Code GmbH
+
    This file is part of GPGME.
 
    GPGME is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation; either version 2.1 of
    the License, or (at your option) any later version.
-   
+
    GPGME is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
-   
+
    You should have received a copy of the GNU Lesser General Public
    License along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 
 #include <stdlib.h>
 #include <string.h>
-#include <sys/types.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
 #include <assert.h>
-#include <unistd.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_LOCALE_H
 #include <locale.h>
+#endif
 #include <fcntl.h> /* FIXME */
 #include <errno.h>
 
@@ -38,9 +45,9 @@
 #include "wait.h"
 #include "priv-io.h"
 #include "sema.h"
+#include "data.h"
 
 #include "assuan.h"
-#include "status-table.h"
 #include "debug.h"
 
 #include "engine-backend.h"
@@ -70,6 +77,8 @@ struct engine_gpgsm
 
   /* Input, output etc are from the servers perspective.  */
   iocb_data_t input_cb;
+  gpgme_data_t input_helper_data;  /* Input helper data object.  */
+  void *input_helper_memory;       /* Input helper memory block.  */
 
   iocb_data_t output_cb;
 
@@ -92,7 +101,7 @@ struct engine_gpgsm
       int linelen;
     } attic;
     int any; /* any data line seen */
-  } colon; 
+  } colon;
 
   gpgme_data_t inline_data;  /* Used to collect D lines.  */
 
@@ -102,7 +111,7 @@ struct engine_gpgsm
 typedef struct engine_gpgsm *engine_gpgsm_t;
 
 
-static void gpgsm_io_event (void *engine, 
+static void gpgsm_io_event (void *engine,
                             gpgme_event_io_t type, void *type_data);
 
 
@@ -111,14 +120,14 @@ static char *
 gpgsm_get_version (const char *file_name)
 {
   return _gpgme_get_program_version (file_name ? file_name
-                                    : _gpgme_get_gpgsm_path ());
+                                    : _gpgme_get_default_gpgsm_name ());
 }
 
 
 static const char *
 gpgsm_get_req_version (void)
 {
-  return NEED_GPGSM_VERSION;
+  return "2.0.4";
 }
 
 \f
@@ -141,6 +150,16 @@ close_notify_handler (int fd, void *opaque)
        (*gpgsm->io_cbs.remove) (gpgsm->input_cb.tag);
       gpgsm->input_cb.fd = -1;
       gpgsm->input_cb.tag = NULL;
+      if (gpgsm->input_helper_data)
+        {
+          gpgme_data_release (gpgsm->input_helper_data);
+          gpgsm->input_helper_data = NULL;
+        }
+      if (gpgsm->input_helper_memory)
+        {
+          free (gpgsm->input_helper_memory);
+          gpgsm->input_helper_memory = NULL;
+        }
     }
   else if (gpgsm->output_cb.fd == fd)
     {
@@ -159,132 +178,6 @@ close_notify_handler (int fd, void *opaque)
 }
 
 
-static gpgme_error_t
-map_assuan_error (gpg_error_t err)
-{
-  if (!err)
-    return 0;
-
-  if (err == -1)
-    return gpg_error (GPG_ERR_INV_ENGINE);
-
-  /* New code will use gpg_error_t values.  */
-  if (gpg_err_source (err))
-    return (gpgme_error_t) err;
-
-  /* Legacy code will use old values.  */
-  switch (err)
-    {
-    case ASSUAN_No_Error:
-      return gpg_error (GPG_ERR_NO_ERROR);
-    case ASSUAN_General_Error:
-      return gpg_error (GPG_ERR_GENERAL);
-    case ASSUAN_Out_Of_Core:
-      return gpg_error (GPG_ERR_ENOMEM);
-    case ASSUAN_Invalid_Value:
-      return gpg_error (GPG_ERR_INV_VALUE);
-    case ASSUAN_Timeout:
-      return gpg_error (GPG_ERR_ETIMEDOUT);
-    case ASSUAN_Read_Error:
-      return gpg_error (GPG_ERR_GENERAL);
-    case ASSUAN_Write_Error:
-      return gpg_error (GPG_ERR_GENERAL);
-
-    case ASSUAN_Problem_Starting_Server:
-    case ASSUAN_Not_A_Server:
-    case ASSUAN_Not_A_Client:
-    case ASSUAN_Nested_Commands:
-    case ASSUAN_No_Data_Callback:
-    case ASSUAN_No_Inquire_Callback:
-    case ASSUAN_Connect_Failed:
-    case ASSUAN_Accept_Failed:
-    case ASSUAN_Invalid_Command:
-    case ASSUAN_Unknown_Command:
-    case ASSUAN_Syntax_Error:
-    case ASSUAN_Parameter_Error:
-    case ASSUAN_Parameter_Conflict:
-    case ASSUAN_No_Input:
-    case ASSUAN_No_Output:
-    case ASSUAN_No_Data_Available:
-    case ASSUAN_Too_Much_Data:
-    case ASSUAN_Inquire_Unknown:
-    case ASSUAN_Inquire_Error:
-    case ASSUAN_Invalid_Option:
-    case ASSUAN_Unexpected_Status:
-    case ASSUAN_Unexpected_Data:
-    case ASSUAN_Invalid_Status:
-      return gpg_error (GPG_ERR_ASSUAN);
-
-    case ASSUAN_Invalid_Response:
-      return gpg_error (GPG_ERR_INV_RESPONSE);
-
-    case ASSUAN_Not_Implemented:
-      return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
-    case ASSUAN_Line_Too_Long:
-      return gpg_error (GPG_ERR_LINE_TOO_LONG);
-    case ASSUAN_Line_Not_Terminated:
-      return gpg_error (GPG_ERR_INCOMPLETE_LINE);
-    case ASSUAN_Canceled:
-      return gpg_error (GPG_ERR_CANCELED);
-
-    case ASSUAN_Unsupported_Algorithm:
-      return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
-    case ASSUAN_Server_Resource_Problem:
-      return gpg_error (GPG_ERR_RESOURCE_LIMIT);
-    case ASSUAN_Server_IO_Error:
-      return gpg_error (GPG_ERR_GENERAL);
-    case ASSUAN_Server_Bug:
-      return gpg_error (GPG_ERR_BUG);
-    case ASSUAN_Invalid_Data:
-      return gpg_error (GPG_ERR_INV_DATA);
-    case ASSUAN_Invalid_Index:
-      return gpg_error (GPG_ERR_INV_INDEX);
-    case ASSUAN_Not_Confirmed:
-      return gpg_error (GPG_ERR_NOT_CONFIRMED);
-    case ASSUAN_Bad_Certificate:
-      return gpg_error (GPG_ERR_BAD_CERT);
-    case ASSUAN_Bad_Certificate_Chain:
-      return gpg_error (GPG_ERR_BAD_CERT_CHAIN);
-    case ASSUAN_Missing_Certificate:
-      return gpg_error (GPG_ERR_MISSING_CERT);
-    case ASSUAN_Bad_Signature:
-      return gpg_error (GPG_ERR_BAD_SIGNATURE);
-    case ASSUAN_No_Agent:
-      return gpg_error (GPG_ERR_NO_AGENT);
-    case ASSUAN_Agent_Error:
-      return gpg_error (GPG_ERR_AGENT);
-    case ASSUAN_No_Public_Key:
-      return gpg_error (GPG_ERR_NO_PUBKEY);
-    case ASSUAN_No_Secret_Key:
-      return gpg_error (GPG_ERR_NO_SECKEY);
-    case ASSUAN_Invalid_Name:
-      return gpg_error (GPG_ERR_INV_NAME);
-      
-    case ASSUAN_Cert_Revoked:
-      return gpg_error (GPG_ERR_CERT_REVOKED);
-    case ASSUAN_No_CRL_For_Cert:
-      return gpg_error (GPG_ERR_NO_CRL_KNOWN);
-    case ASSUAN_CRL_Too_Old:
-      return gpg_error (GPG_ERR_CRL_TOO_OLD);
-    case ASSUAN_Not_Trusted:
-      return gpg_error (GPG_ERR_NOT_TRUSTED);
-
-    case ASSUAN_Card_Error:
-      return gpg_error (GPG_ERR_CARD);
-    case ASSUAN_Invalid_Card:
-      return gpg_error (GPG_ERR_INV_CARD);
-    case ASSUAN_No_PKCS15_App:
-      return gpg_error (GPG_ERR_NO_PKCS15_APP);
-    case ASSUAN_Card_Not_Present:
-      return gpg_error (GPG_ERR_CARD_NOT_PRESENT);
-    case ASSUAN_Invalid_Id:
-      return gpg_error (GPG_ERR_INV_ID);
-    default:
-      return gpg_error (GPG_ERR_GENERAL);
-    }
-}
-
-
 /* This is the default inquiry callback.  We use it to handle the
    Pinentry notifications.  */
 static gpgme_error_t
@@ -318,7 +211,7 @@ gpgsm_cancel (void *engine)
 
   if (gpgsm->assuan_ctx)
     {
-      assuan_disconnect (gpgsm->assuan_ctx);
+      assuan_release (gpgsm->assuan_ctx);
       gpgsm->assuan_ctx = NULL;
     }
 
@@ -346,6 +239,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
 {
   gpgme_error_t err = 0;
   engine_gpgsm_t gpgsm;
+  const char *pgmname;
   const char *argv[5];
   int argc;
 #if !USE_DESCRIPTOR_PASSING
@@ -359,7 +253,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
 
   gpgsm = calloc (1, sizeof *gpgsm);
   if (!gpgsm)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   gpgsm->status_cb.fd = -1;
   gpgsm->status_cb.dir = 1;
@@ -400,7 +294,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
 #if !USE_DESCRIPTOR_PASSING
   if (_gpgme_io_pipe (fds, 0) < 0)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto leave;
     }
   gpgsm->input_cb.fd = fds[1];
@@ -408,7 +302,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
 
   if (_gpgme_io_pipe (fds, 1) < 0)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto leave;
     }
   gpgsm->output_cb.fd = fds[0];
@@ -416,7 +310,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
 
   if (_gpgme_io_pipe (fds, 0) < 0)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto leave;
     }
   gpgsm->message_cb.fd = fds[1];
@@ -428,8 +322,10 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
   child_fds[3] = -1;
 #endif
 
+  pgmname = file_name ? file_name : _gpgme_get_default_gpgsm_name ();
+
   argc = 0;
-  argv[argc++] = "gpgsm";
+  argv[argc++] = _gpgme_get_basename (pgmname);
   if (home_dir)
     {
       argv[argc++] = "--homedir";
@@ -438,14 +334,32 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
   argv[argc++] = "--server";
   argv[argc++] = NULL;
 
+  err = assuan_new_ext (&gpgsm->assuan_ctx, GPG_ERR_SOURCE_GPGME,
+                       &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
+                       NULL);
+  if (err)
+    goto leave;
+  assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks);
+
 #if USE_DESCRIPTOR_PASSING
-  err = assuan_pipe_connect_ext
-    (&gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
-     argv, NULL, NULL, NULL, 1);
+  err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv,
+                             NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
 #else
-  err = assuan_pipe_connect
-    (&gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
-     argv, child_fds);
+  {
+    assuan_fd_t achild_fds[4];
+    int i;
+
+    /* For now... */
+    for (i = 0; i < 4; i++)
+      achild_fds[i] = (assuan_fd_t) child_fds[i];
+
+    err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv,
+                               achild_fds, NULL, NULL, 0);
+
+    /* For now... */
+    for (i = 0; i < 4; i++)
+      child_fds[i] = (int) achild_fds[i];
+  }
 
   /* On Windows, handles are inserted in the spawned process with
      DuplicateHandle, and child_fds contains the server-local names
@@ -469,12 +383,6 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
   if (err)
     goto leave;
 
-  /* assuan_pipe_connect in this case uses _gpgme_io_spawn which
-     closes the child fds for us.  */
-  gpgsm->input_cb.server_fd = -1;
-  gpgsm->output_cb.server_fd = -1;
-  gpgsm->message_cb.server_fd = -1;
-
   err = _gpgme_getenv ("DISPLAY", &dft_display);
   if (err)
     goto leave;
@@ -483,7 +391,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
       if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
         {
          free (dft_display);
-         err = gpg_error_from_errno (errno);
+         err = gpg_error_from_syserror ();
          goto leave;
        }
       free (dft_display);
@@ -492,10 +400,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
                             NULL, NULL, NULL);
       free (optstr);
       if (err)
-       {
-         err = map_assuan_error (err);
-         goto leave;
-       }
+       goto leave;
     }
 
   if (isatty (1))
@@ -512,17 +417,14 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
        {
          if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
            {
-             err = gpg_error_from_errno (errno);
+             err = gpg_error_from_syserror ();
              goto leave;
            }
          err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
                                 NULL, NULL, NULL);
          free (optstr);
          if (err)
-           {
-             err = map_assuan_error (err);
-             goto leave;
-           }
+           goto leave;
 
          err = _gpgme_getenv ("TERM", &dft_ttytype);
          if (err)
@@ -532,7 +434,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
              if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
                {
                  free (dft_ttytype);
-                 err = gpg_error_from_errno (errno);
+                 err = gpg_error_from_syserror ();
                  goto leave;
                }
              free (dft_ttytype);
@@ -541,10 +443,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
                                     NULL, NULL, NULL, NULL);
              free (optstr);
              if (err)
-               {
-                 err = map_assuan_error (err);
-                 goto leave;
-               }
+               goto leave;
            }
        }
     }
@@ -618,7 +517,10 @@ gpgsm_set_locale (void *engine, int category, const char *value)
   /* FIXME: If value is NULL, we need to reset the option to default.
      But we can't do this.  So we error out here.  GPGSM needs support
      for this.  */
-  if (category == LC_CTYPE)
+  if (0)
+    ;
+#ifdef LC_CTYPE
+  else if (category == LC_CTYPE)
     {
       catstr = "lc-ctype";
       if (!value && gpgsm->lc_ctype_set)
@@ -626,6 +528,7 @@ gpgsm_set_locale (void *engine, int category, const char *value)
       if (value)
        gpgsm->lc_ctype_set = 1;
     }
+#endif
 #ifdef LC_MESSAGES
   else if (category == LC_MESSAGES)
     {
@@ -640,27 +543,22 @@ gpgsm_set_locale (void *engine, int category, const char *value)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   /* FIXME: Reset value to default.  */
-  if (!value) 
+  if (!value)
     return 0;
 
   if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
-    err = gpg_error_from_errno (errno);
+    err = gpg_error_from_syserror ();
   else
     {
       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
                             NULL, NULL, NULL, NULL);
       free (optstr);
-      if (err)
-       err = map_assuan_error (err);
     }
 
   return err;
 }
 
 
-/* Forward declaration.  */
-static gpgme_status_code_t parse_status (const char *name);
-
 static gpgme_error_t
 gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd,
                             engine_status_handler_t status_fnc,
@@ -672,13 +570,13 @@ gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd,
 
   err = assuan_write_line (ctx, cmd);
   if (err)
-    return map_assuan_error (err);
+    return err;
 
   do
     {
       err = assuan_read_line (ctx, &line, &linelen);
       if (err)
-       return map_assuan_error (err);
+       return err;
 
       if (*line == '#' || !linelen)
        continue;
@@ -690,7 +588,7 @@ gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd,
       else if (linelen >= 4
          && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
          && line[3] == ' ')
-       err = map_assuan_error (atoi (&line[4]));
+       err = atoi (&line[4]);
       else if (linelen >= 2
               && line[0] == 'S' && line[1] == ' ')
        {
@@ -703,7 +601,7 @@ gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd,
          else
            *(rest++) = 0;
 
-         r = parse_status (line + 2);
+         r = _gpgme_parse_status (line + 2);
 
          if (r >= 0 && status_fnc)
            err = status_fnc (status_fnc_value, r, rest);
@@ -748,7 +646,9 @@ gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
   char line[COMMANDLINELEN];
   char *which;
   iocb_data_t *iocb_data;
+#if USE_DESCRIPTOR_PASSING
   int dir;
+#endif
 
   switch (fd_type)
     {
@@ -771,9 +671,8 @@ gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
       return gpg_error (GPG_ERR_INV_VALUE);
     }
 
-  dir = iocb_data->dir;
-
 #if USE_DESCRIPTOR_PASSING
+  dir = iocb_data->dir;
   /* We try to short-cut the communication by giving GPGSM direct
      access to the file descriptor, rather than using a pipe.  */
   iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
@@ -781,8 +680,8 @@ gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
     {
       int fds[2];
 
-      if (_gpgme_io_pipe (fds, 0) < 0)
-       return gpg_error_from_errno (errno);
+      if (_gpgme_io_pipe (fds, dir) < 0)
+       return gpg_error_from_syserror ();
 
       iocb_data->fd = dir ? fds[0] : fds[1];
       iocb_data->server_fd = dir ? fds[1] : fds[0];
@@ -808,10 +707,10 @@ gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
     snprintf (line, COMMANDLINELEN, "%s FD", which);
 #else
   if (opt)
-    snprintf (line, COMMANDLINELEN, "%s FD=%s %s", 
+    snprintf (line, COMMANDLINELEN, "%s FD=%s %s",
               which, iocb_data->server_fd_str, opt);
   else
-    snprintf (line, COMMANDLINELEN, "%s FD=%s", 
+    snprintf (line, COMMANDLINELEN, "%s FD=%s",
               which, iocb_data->server_fd_str);
 #endif
 
@@ -855,55 +754,33 @@ map_data_enc (gpgme_data_t d)
 }
 
 
-static int
-status_cmp (const void *ap, const void *bp)
-{
-  const struct status_table_s *a = ap;
-  const struct status_table_s *b = bp;
-
-  return strcmp (a->name, b->name);
-}
-
-
-static gpgme_status_code_t
-parse_status (const char *name)
-{
-  struct status_table_s t, *r;
-  t.name = name;
-  r = bsearch (&t, status_table, DIM(status_table) - 1,
-              sizeof t, status_cmp);
-  return r ? r->code : -1;
-}
-
-
 static gpgme_error_t
 status_handler (void *opaque, int fd)
 {
-  gpg_error_t assuan_err;
+  struct io_cb_data *data = (struct io_cb_data *) opaque;
+  engine_gpgsm_t gpgsm = (engine_gpgsm_t) data->handler_value;
   gpgme_error_t err = 0;
-  engine_gpgsm_t gpgsm = opaque;
   char *line;
   size_t linelen;
 
   do
     {
-      assuan_err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
-      if (assuan_err)
+      err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
+      if (err)
        {
          /* Try our best to terminate the connection friendly.  */
          /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
-         err = map_assuan_error (assuan_err);
           TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
                  "fd 0x%x: error from assuan (%d) getting status line : %s",
-                  fd, assuan_err, gpg_strerror (err));
+                  fd, err, gpg_strerror (err));
        }
       else if (linelen >= 3
               && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
               && (line[3] == '\0' || line[3] == ' '))
        {
          if (line[3] == ' ')
-           err = map_assuan_error (atoi (&line[4]));
-         else
+           err = atoi (&line[4]);
+         if (! err)
            err = gpg_error (GPG_ERR_GENERAL);
           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
                  "fd 0x%x: ERR line - mapped to: %s",
@@ -918,8 +795,8 @@ status_handler (void *opaque, int fd)
          if (gpgsm->status.fnc)
            err = gpgsm->status.fnc (gpgsm->status.fnc_value,
                                     GPGME_STATUS_EOF, "");
-         
-         if (!err && gpgsm->colon.fnc && gpgsm->colon.any )
+
+         if (!err && gpgsm->colon.fnc && gpgsm->colon.any)
             {
               /* We must tell a colon function about the EOF. We do
                  this only when we have seen any data lines.  Note
@@ -955,11 +832,11 @@ status_handler (void *opaque, int fd)
            {
              char *newline = realloc (*aline, *alinelen + linelen + 1);
              if (!newline)
-               err = gpg_error_from_errno (errno);
+               err = gpg_error_from_syserror ();
              else
                {
                  *aline = newline;
-                 gpgsm->colon.attic.linesize += linelen + 1;
+                 gpgsm->colon.attic.linesize = *alinelen + linelen + 1;
                }
            }
          if (!err)
@@ -981,12 +858,12 @@ status_handler (void *opaque, int fd)
                      *dst = *src++;
                      (*alinelen)++;
                    }
-                 
+
                  if (*dst == '\n')
                    {
                      /* Terminate the pending line, pass it to the colon
                         handler and reset it.  */
-                     
+
                      gpgsm->colon.any = 1;
                      if (*alinelen > 1 && *(dst - 1) == '\r')
                        dst--;
@@ -1015,7 +892,7 @@ status_handler (void *opaque, int fd)
           char *src = line + 2;
          char *end = line + linelen;
          char *dst = src;
-          ssize_t nwritten;
+          gpgme_ssize_t nwritten;
 
           linelen = 0;
           while (src < end)
@@ -1029,10 +906,10 @@ status_handler (void *opaque, int fd)
                 }
               else
                 *dst++ = *src++;
-              
+
               linelen++;
             }
-          
+
           src = line + 2;
           while (linelen > 0)
             {
@@ -1040,7 +917,7 @@ status_handler (void *opaque, int fd)
               if (!nwritten || (nwritten < 0 && errno != EINTR)
                   || nwritten > linelen)
                 {
-                  err = gpg_error_from_errno (errno);
+                  err = gpg_error_from_syserror ();
                   break;
                 }
               src += nwritten;
@@ -1056,14 +933,14 @@ status_handler (void *opaque, int fd)
        {
          char *rest;
          gpgme_status_code_t r;
-         
+
          rest = strchr (line + 2, ' ');
          if (!rest)
            rest = line + linelen; /* set to an empty string */
          else
            *(rest++) = 0;
 
-         r = parse_status (line + 2);
+         r = _gpgme_parse_status (line + 2);
 
          if (r >= 0)
            {
@@ -1079,7 +956,7 @@ status_handler (void *opaque, int fd)
       else if (linelen >= 7
                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
-               && line[6] == 'E' 
+               && line[6] == 'E'
                && (line[7] == '\0' || line[7] == ' '))
         {
           char *keyword = line+7;
@@ -1092,7 +969,7 @@ status_handler (void *opaque, int fd)
 
     }
   while (!err && assuan_pending_line (gpgsm->assuan_ctx));
-         
+
   return err;
 }
 
@@ -1120,16 +997,21 @@ static gpgme_error_t
 start (engine_gpgsm_t gpgsm, const char *command)
 {
   gpgme_error_t err;
+  assuan_fd_t afdlist[5];
   int fdlist[5];
   int nfds;
+  int i;
 
   /* We need to know the fd used by assuan for reads.  We do this by
      using the assumption that the first returned fd from
      assuan_get_active_fds() is always this one.  */
   nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
-                                fdlist, DIM (fdlist));
+                                afdlist, DIM (afdlist));
   if (nfds < 1)
     return gpg_error (GPG_ERR_GENERAL);        /* FIXME */
+  /* For now... */
+  for (i = 0; i < nfds; i++)
+    fdlist[i] = (int) afdlist[i];
 
   /* We "duplicate" the file descriptor, so we can close it here (we
      can't close fdlist[0], as that is closed by libassuan, and
@@ -1162,7 +1044,7 @@ start (engine_gpgsm_t gpgsm, const char *command)
     err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);
 
   if (!err)
-    err = map_assuan_error (assuan_write_line (gpgsm->assuan_ctx, command));
+    err = assuan_write_line (gpgsm->assuan_ctx, command);
 
   if (!err)
     gpgsm_io_event (gpgsm, GPGME_EVENT_START, NULL);
@@ -1177,13 +1059,18 @@ gpgsm_reset (void *engine)
 {
   engine_gpgsm_t gpgsm = engine;
 
-  /* We must send a reset because we need to reset the list of
-     signers.  Note that RESET does not reset OPTION commands. */
-  return gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET", NULL, NULL);
+  /* IF we have an active connection we must send a reset because we
+     need to reset the list of signers.  Note that RESET does not
+     reset OPTION commands. */
+  return (gpgsm->assuan_ctx
+          ? gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET",
+                                         NULL, NULL)
+          : 0);
 }
 #endif
 
 
+
 static gpgme_error_t
 gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
 {
@@ -1233,7 +1120,7 @@ gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret)
 
   line = malloc (length);
   if (!line)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   strcpy (line, "DELKEYS ");
   linep = &line[8];
@@ -1285,14 +1172,14 @@ set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
   char *line;
   int linelen;
   int invalid_recipients = 0;
-  int i = 0;
+  int i;
 
   linelen = 10 + 40 + 1;       /* "RECIPIENT " + guess + '\0'.  */
   line = malloc (10 + 40 + 1);
   if (!line)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
   strcpy (line, "RECIPIENT ");
-  while (!err && recp[i])
+  for (i =0; !err && recp[i]; i++)
     {
       char *fpr;
       int newlen;
@@ -1310,9 +1197,9 @@ set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
          char *newline = realloc (line, newlen);
          if (! newline)
            {
-             int saved_errno = errno;
+             int saved_err = gpg_error_from_syserror ();
              free (line);
-             return gpg_error_from_errno (saved_errno);
+             return saved_err;
            }
          line = newline;
          linelen = newlen;
@@ -1329,7 +1216,6 @@ set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
          free (line);
          return err;
        }
-      i++;
     }
   free (line);
   return gpg_error (invalid_recipients
@@ -1379,22 +1265,25 @@ gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
 
 
 static gpgme_error_t
-gpgsm_export (void *engine, const char *pattern, unsigned int reserved,
+gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode,
              gpgme_data_t keydata, int use_armor)
 {
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err = 0;
   char *cmd;
 
-  if (!gpgsm || reserved)
+  if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
+  if (mode)
+    return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
   if (!pattern)
     pattern = "";
 
   cmd = malloc (7 + strlen (pattern) + 1);
   if (!cmd)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
   strcpy (cmd, "EXPORT ");
   strcpy (&cmd[7], pattern);
 
@@ -1414,7 +1303,7 @@ gpgsm_export (void *engine, const char *pattern, unsigned int reserved,
 
 
 static gpgme_error_t
-gpgsm_export_ext (void *engine, const char *pattern[], unsigned int reserved,
+gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
                  gpgme_data_t keydata, int use_armor)
 {
   engine_gpgsm_t gpgsm = engine;
@@ -1424,9 +1313,12 @@ gpgsm_export_ext (void *engine, const char *pattern[], unsigned int reserved,
   int length = 7 + 1;
   char *linep;
 
-  if (!gpgsm || reserved)
+  if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
+  if (mode)
+    return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
   if (pattern && *pattern)
     {
       const char **pat = pattern;
@@ -1448,7 +1340,7 @@ gpgsm_export_ext (void *engine, const char *pattern[], unsigned int reserved,
     }
   line = malloc (length);
   if (!line)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   strcpy (line, "EXPORT ");
   linep = &line[7];
@@ -1534,24 +1426,103 @@ gpgsm_genkey (void *engine, gpgme_data_t help_data, int use_armor,
 
 
 static gpgme_error_t
-gpgsm_import (void *engine, gpgme_data_t keydata)
+gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
 {
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err;
+  gpgme_data_encoding_t dataenc;
+  int idx;
 
   if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
-  gpgsm->input_cb.data = keydata;
-  err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
-  if (err)
-    return err;
-  gpgsm_clear_fd (gpgsm, OUTPUT_FD);
-  gpgsm_clear_fd (gpgsm, MESSAGE_FD);
-  gpgsm->inline_data = NULL;
+  if (keydata && keyarray)
+    return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed.  */
 
-  err = start (gpgsm, "IMPORT");
-  return err;
+  dataenc = gpgme_data_get_encoding (keydata);
+
+  if (keyarray)
+    {
+      size_t buflen;
+      char *buffer, *p;
+
+      /* Fist check whether the engine already features the
+         --re-import option.  */
+      err = gpgsm_assuan_simple_command
+        (gpgsm->assuan_ctx,
+         "GETINFO cmd_has_option IMPORT re-import", NULL, NULL);
+      if (err)
+       return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+      /* Create an internal data object with a list of all
+         fingerprints.  The data object and its memory (to avoid an
+         extra copy by gpgme_data_new_from_mem) are stored in two
+         variables which are released by the close_notify_handler.  */
+      for (idx=0, buflen=0; keyarray[idx]; idx++)
+        {
+          if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
+              && keyarray[idx]->subkeys
+              && keyarray[idx]->subkeys->fpr
+              && *keyarray[idx]->subkeys->fpr)
+            buflen += strlen (keyarray[idx]->subkeys->fpr) + 1;
+        }
+      /* Allocate a bufer with extra space for the trailing Nul
+         introduced by the use of stpcpy.  */
+      buffer = malloc (buflen+1);
+      if (!buffer)
+        return gpg_error_from_syserror ();
+      for (idx=0, p = buffer; keyarray[idx]; idx++)
+        {
+          if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
+              && keyarray[idx]->subkeys
+              && keyarray[idx]->subkeys->fpr
+              && *keyarray[idx]->subkeys->fpr)
+            p = stpcpy (stpcpy (p, keyarray[idx]->subkeys->fpr), "\n");
+        }
+
+      err = gpgme_data_new_from_mem (&gpgsm->input_helper_data,
+                                     buffer, buflen, 0);
+      if (err)
+        {
+          free (buffer);
+          return err;
+        }
+      gpgsm->input_helper_memory = buffer;
+
+      gpgsm->input_cb.data = gpgsm->input_helper_data;
+      err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
+      if (err)
+        {
+          gpgme_data_release (gpgsm->input_helper_data);
+          gpgsm->input_helper_data = NULL;
+          free (gpgsm->input_helper_memory);
+          gpgsm->input_helper_memory = NULL;
+          return err;
+        }
+      gpgsm_clear_fd (gpgsm, OUTPUT_FD);
+      gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+      gpgsm->inline_data = NULL;
+
+      return start (gpgsm, "IMPORT --re-import");
+    }
+  else if (dataenc == GPGME_DATA_ENCODING_URL
+           || dataenc == GPGME_DATA_ENCODING_URL0
+           || dataenc == GPGME_DATA_ENCODING_URLESC)
+    {
+      return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+    }
+  else
+    {
+      gpgsm->input_cb.data = keydata;
+      err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
+      if (err)
+        return err;
+      gpgsm_clear_fd (gpgsm, OUTPUT_FD);
+      gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+      gpgsm->inline_data = NULL;
+
+      return start (gpgsm, "IMPORT");
+    }
 }
 
 
@@ -1572,9 +1543,21 @@ gpgsm_keylist (void *engine, const char *pattern, int secret_only,
   if (!pattern)
     pattern = "";
 
+  /* Hack to make sure that the agent is started.  Only if the agent
+     has been started an application may connect to the agent via
+     GPGME_PROTOCOL_ASSUAN - for example to look for smartcards.  We
+     do this only if a secret key listing has been requested.  In
+     general this is not needed because a secret key listing starts
+     the agent.  However on a fresh installation no public keys are
+     available and thus there is no need for gpgsm to ask the agent
+     whether a secret key exists for the public key.  */
+  if (secret_only || (mode & GPGME_KEYLIST_MODE_WITH_SECRET))
+    gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "GETINFO agent-check",
+                                 NULL, NULL);
+
   /* Always send list-mode option because RESET does not reset it.  */
   if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
   free (line);
   if (err)
@@ -1585,24 +1568,29 @@ gpgsm_keylist (void *engine, const char *pattern, int secret_only,
 
   /* Use the validation mode if requested.  We don't check for an error
      yet because this is a pretty fresh gpgsm features. */
-  gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
+  gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
                                "OPTION with-validation=1":
                                "OPTION with-validation=0" ,
                                NULL, NULL);
   /* Include the ephemeral keys if requested.  We don't check for an error
      yet because this is a pretty fresh gpgsm features. */
-  gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
+  gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
                                (mode & GPGME_KEYLIST_MODE_EPHEMERAL)?
                                "OPTION with-ephemeral-keys=1":
                                "OPTION with-ephemeral-keys=0" ,
                                NULL, NULL);
+  gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
+                               (mode & GPGME_KEYLIST_MODE_WITH_SECRET)?
+                               "OPTION with-secret=1":
+                               "OPTION with-secret=0" ,
+                               NULL, NULL);
 
 
   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
   line = malloc (15 + strlen (pattern) + 1);
   if (!line)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
   if (secret_only)
     {
       strcpy (line, "LISTSECRETKEYS ");
@@ -1648,7 +1636,7 @@ gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
 
   /* Always send list-mode option because RESET does not reset it.  */
   if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
   free (line);
   if (err)
@@ -1657,11 +1645,16 @@ gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
   /* Always send key validation because RESET does not reset it.  */
   /* Use the validation mode if required.  We don't check for an error
      yet because this is a pretty fresh gpgsm features. */
-  gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
+  gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
                                "OPTION with-validation=1":
                                "OPTION with-validation=0" ,
                                NULL, NULL);
+  gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
+                               (mode & GPGME_KEYLIST_MODE_WITH_SECRET)?
+                               "OPTION with-secret=1":
+                               "OPTION with-secret=0" ,
+                               NULL, NULL);
 
 
   if (pattern && *pattern)
@@ -1685,7 +1678,7 @@ gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
     }
   line = malloc (length);
   if (!line)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
   if (secret_only)
     {
       strcpy (line, "LISTSECRETKEYS ");
@@ -1771,9 +1764,9 @@ gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
         requested.  */
 
       if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
-       return gpg_error_from_errno (errno);
+       return gpg_error_from_syserror ();
       err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd,
-                                        NULL, NULL);
+                                         NULL, NULL);
       free (assuan_cmd);
       if (err)
        return err;
@@ -1788,12 +1781,13 @@ gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
 
           strcpy (stpcpy (buf, "SIGNER "), s);
           err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf,
-                                             NULL, NULL);
+                                             gpgsm->status.fnc,
+                                             gpgsm->status.fnc_value);
        }
       else
         err = gpg_error (GPG_ERR_INV_VALUE);
       gpgme_key_unref (key);
-      if (err) 
+      if (err)
         return err;
     }
 
@@ -1890,7 +1884,7 @@ gpgsm_getauditlog (void *engine, gpgme_data_t output, unsigned int flags)
 
 static void
 gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc,
-                         void *fnc_value) 
+                         void *fnc_value)
 {
   engine_gpgsm_t gpgsm = engine;
 
@@ -1901,7 +1895,7 @@ gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc,
 
 static gpgme_error_t
 gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
-                             void *fnc_value) 
+                             void *fnc_value)
 {
   engine_gpgsm_t gpgsm = engine;
 
@@ -1933,10 +1927,36 @@ gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data)
 }
 
 
+static gpgme_error_t
+gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags)
+{
+  engine_gpgsm_t gpgsm = engine;
+  gpgme_error_t err;
+  char *line;
+
+  if (!key || !key->subkeys || !key->subkeys->fpr)
+    return gpg_error (GPG_ERR_INV_CERT_OBJ);
+
+  if (asprintf (&line, "PASSWD -- %s", key->subkeys->fpr) < 0)
+    return gpg_error_from_syserror ();
+
+  gpgsm_clear_fd (gpgsm, OUTPUT_FD);
+  gpgsm_clear_fd (gpgsm, INPUT_FD);
+  gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+  gpgsm->inline_data = NULL;
+
+  err = start (gpgsm, line);
+  free (line);
+
+  return err;
+}
+
+
+
 struct engine_ops _gpgme_engine_ops_gpgsm =
   {
     /* Static functions.  */
-    _gpgme_get_gpgsm_path,
+    _gpgme_get_default_gpgsm_name,
     NULL,
     gpgsm_get_version,
     gpgsm_get_req_version,
@@ -1953,8 +1973,10 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
     NULL,              /* set_command_handler */
     gpgsm_set_colon_line_handler,
     gpgsm_set_locale,
+    NULL,              /* set_protocol */
+    gpgsm_decrypt,
     gpgsm_decrypt,
-    gpgsm_delete,
+    gpgsm_delete,      /* decrypt_verify */
     NULL,              /* edit */
     gpgsm_encrypt,
     NULL,              /* encrypt_sign */
@@ -1973,5 +1995,9 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
     NULL,              /* conf_save */
     gpgsm_set_io_cbs,
     gpgsm_io_event,
-    gpgsm_cancel
+    gpgsm_cancel,
+    NULL,              /* cancel_op */
+    gpgsm_passwd,
+    NULL,               /* set_pinentry_mode */
+    NULL                /* opspawn */
   };