Fix possible realloc overflow for gpgsm and uiserver engines.
[gpgme.git] / src / engine-gpgsm.c
index d140615..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"
@@ -94,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.  */
 
@@ -104,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);
 
 
@@ -113,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
@@ -232,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
@@ -245,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;
@@ -286,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];
@@ -294,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];
@@ -302,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];
@@ -314,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";
@@ -332,13 +342,24 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
   assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks);
 
 #if USE_DESCRIPTOR_PASSING
-  err = assuan_pipe_connect
-    (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
-     argv, NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
+  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, NULL, NULL, 0);
+  {
+    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
@@ -362,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.  */
-  _gpgme_io_close (gpgsm->input_cb.server_fd);
-  _gpgme_io_close (gpgsm->output_cb.server_fd);
-  _gpgme_io_close (gpgsm->message_cb.server_fd);
-
   err = _gpgme_getenv ("DISPLAY", &dft_display);
   if (err)
     goto leave;
@@ -376,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);
@@ -402,7 +417,7 @@ 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,
@@ -419,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);
@@ -502,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)
@@ -510,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)
     {
@@ -524,11 +543,11 @@ 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,
@@ -540,9 +559,6 @@ gpgsm_set_locale (void *engine, int category, const char *value)
 }
 
 
-/* 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,
@@ -585,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);
@@ -630,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)
     {
@@ -653,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);
@@ -663,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];
@@ -690,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
 
@@ -737,27 +754,6 @@ 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)
 {
@@ -799,7 +795,7 @@ 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)
             {
               /* We must tell a colon function about the EOF. We do
@@ -836,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)
@@ -862,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--;
@@ -896,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)
@@ -910,10 +906,10 @@ status_handler (void *opaque, int fd)
                 }
               else
                 *dst++ = *src++;
-              
+
               linelen++;
             }
-          
+
           src = line + 2;
           while (linelen > 0)
             {
@@ -921,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;
@@ -937,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)
            {
@@ -960,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;
@@ -973,7 +969,7 @@ status_handler (void *opaque, int fd)
 
     }
   while (!err && assuan_pending_line (gpgsm->assuan_ctx));
-         
+
   return err;
 }
 
@@ -1001,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
@@ -1058,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)
 {
@@ -1114,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];
@@ -1166,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;
@@ -1191,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;
@@ -1210,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
@@ -1269,7 +1274,7 @@ gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode,
 
   if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
-  
+
   if (mode)
     return gpg_error (GPG_ERR_NOT_SUPPORTED);
 
@@ -1278,7 +1283,7 @@ gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode,
 
   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);
 
@@ -1335,7 +1340,7 @@ gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
     }
   line = malloc (length);
   if (!line)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   strcpy (line, "EXPORT ");
   linep = &line[7];
@@ -1443,8 +1448,8 @@ gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
 
       /* Fist check whether the engine already features the
          --re-import option.  */
-      err = gpgsm_assuan_simple_command 
-        (gpgsm->assuan_ctx, 
+      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);
@@ -1457,7 +1462,7 @@ gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
         {
           if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
               && keyarray[idx]->subkeys
-              && keyarray[idx]->subkeys->fpr 
+              && keyarray[idx]->subkeys->fpr
               && *keyarray[idx]->subkeys->fpr)
             buflen += strlen (keyarray[idx]->subkeys->fpr) + 1;
         }
@@ -1470,11 +1475,11 @@ gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
         {
           if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
               && keyarray[idx]->subkeys
-              && keyarray[idx]->subkeys->fpr 
+              && 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)
@@ -1538,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)
@@ -1551,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 ");
@@ -1614,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)
@@ -1623,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)
@@ -1651,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 ");
@@ -1737,7 +1764,7 @@ 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);
       free (assuan_cmd);
@@ -1760,7 +1787,7 @@ gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
       else
         err = gpg_error (GPG_ERR_INV_VALUE);
       gpgme_key_unref (key);
-      if (err) 
+      if (err)
         return err;
     }
 
@@ -1857,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;
 
@@ -1868,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;
 
@@ -1900,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,
@@ -1920,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 */
@@ -1941,5 +1996,8 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
     gpgsm_set_io_cbs,
     gpgsm_io_event,
     gpgsm_cancel,
-    NULL               /* cancel_op */
+    NULL,              /* cancel_op */
+    gpgsm_passwd,
+    NULL,               /* set_pinentry_mode */
+    NULL                /* opspawn */
   };