Fix possible realloc overflow for gpgsm and uiserver engines.
[gpgme.git] / src / engine-gpgsm.c
index 4b7b5a2..3a83757 100644 (file)
@@ -2,19 +2,19 @@
    Copyright (C) 2000 Werner Koch (dd9jn)
    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
@@ -48,7 +48,6 @@
 #include "data.h"
 
 #include "assuan.h"
-#include "status-table.h"
 #include "debug.h"
 
 #include "engine-backend.h"
@@ -102,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.  */
 
@@ -112,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);
 
 
@@ -121,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
@@ -240,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
@@ -322,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";
@@ -340,9 +342,8 @@ 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
   {
     assuan_fd_t achild_fds[4];
@@ -352,9 +353,8 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
     for (i = 0; i < 4; i++)
       achild_fds[i] = (assuan_fd_t) child_fds[i];
 
-    err = assuan_pipe_connect
-      (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
-       argv, achild_fds, NULL, NULL, 0);
+    err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv,
+                               achild_fds, NULL, NULL, 0);
 
     /* For now... */
     for (i = 0; i < 4; i++)
@@ -543,7 +543,7 @@ 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)
@@ -559,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,
@@ -604,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);
@@ -649,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)
     {
@@ -672,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);
@@ -709,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
 
@@ -756,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)
 {
@@ -818,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
@@ -859,7 +836,7 @@ status_handler (void *opaque, int fd)
              else
                {
                  *aline = newline;
-                 gpgsm->colon.attic.linesize += linelen + 1;
+                 gpgsm->colon.attic.linesize = *alinelen + linelen + 1;
                }
            }
          if (!err)
@@ -881,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--;
@@ -915,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)
@@ -929,10 +906,10 @@ status_handler (void *opaque, int fd)
                 }
               else
                 *dst++ = *src++;
-              
+
               linelen++;
             }
-          
+
           src = line + 2;
           while (linelen > 0)
             {
@@ -956,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)
            {
@@ -979,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;
@@ -992,7 +969,7 @@ status_handler (void *opaque, int fd)
 
     }
   while (!err && assuan_pending_line (gpgsm->assuan_ctx));
-         
+
   return err;
 }
 
@@ -1220,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;
@@ -1297,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);
 
@@ -1471,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);
@@ -1485,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;
         }
@@ -1498,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)
@@ -1574,7 +1551,7 @@ gpgsm_keylist (void *engine, const char *pattern, int secret_only,
      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)
+  if (secret_only || (mode & GPGME_KEYLIST_MODE_WITH_SECRET))
     gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "GETINFO agent-check",
                                  NULL, NULL);
 
@@ -1591,18 +1568,23 @@ 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'.  */
@@ -1663,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)
@@ -1800,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;
     }
 
@@ -1897,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;
 
@@ -1908,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;
 
@@ -1952,7 +1939,7 @@ gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags)
 
   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);
@@ -1969,7 +1956,7 @@ gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags)
 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,
@@ -2010,5 +1997,7 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
     gpgsm_io_event,
     gpgsm_cancel,
     NULL,              /* cancel_op */
-    gpgsm_passwd
+    gpgsm_passwd,
+    NULL,               /* set_pinentry_mode */
+    NULL                /* opspawn */
   };