Some work on the dirmngr
authorWerner Koch <wk@gnupg.org>
Fri, 23 Jul 2010 16:16:14 +0000 (16:16 +0000)
committerWerner Koch <wk@gnupg.org>
Fri, 23 Jul 2010 16:16:14 +0000 (16:16 +0000)
16 files changed:
NEWS
common/ChangeLog
common/utf8conv.c
dirmngr/ChangeLog
dirmngr/Makefile.am
dirmngr/crlcache.c
dirmngr/crlfetch.h
dirmngr/dirmngr.c
dirmngr/ldap-wrapper-ce.c [new file with mode: 0644]
dirmngr/ldap-wrapper.c [new file with mode: 0644]
dirmngr/ldap-wrapper.h [new file with mode: 0644]
dirmngr/ldap.c
dirmngr/ldapserver.h
doc/vuln-announce-2010-kbx-realloc.txt
g10/ChangeLog
g10/mainproc.c

diff --git a/NEWS b/NEWS
index eb23bbd..200e1aa 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -26,8 +26,8 @@ Noteworthy changes in version 2.1.x (under development)
  * If the agent's --use-standard-socket option is active, all tools
    try to start and daemonize the agent on the fly.  In the past this
    was only supported on W32; on non-W32 systems the new configure
-   option --use-standard-socket may now be used to use this feature by
-   default.
+   option --enable-standard-socket may now be used to use this feature
+   by default.
 
  * Dirmngr is now a part of this package.
 
index ae928ad..6c3c1f4 100644 (file)
@@ -1,3 +1,7 @@
+2010-07-19  Werner Koch  <wk@g10code.com>
+
+       * utf8conv.c (utf8_to_wchar): s/malloc/jnlib_malloc/.
+
 2010-07-16  Werner Koch  <wk@g10code.com>
 
        * http.h (HTTP_FLAG_IGNORE_CL): Add flag .
index 4ad5142..16baa0d 100644 (file)
@@ -802,7 +802,7 @@ utf8_to_wchar (const char *string)
       jnlib_set_errno (ENOMEM);
       return NULL;
     }
-  result = malloc (nbytes);
+  result = jnlib_malloc (nbytes);
   if (!result)
     return NULL;
 
index c95a2f2..8af2f30 100644 (file)
@@ -1,3 +1,23 @@
+2010-07-19  Werner Koch  <wk@g10code.com>
+
+       * dirmngr.c: Include ldap-wrapper.h.
+       (launch_reaper_thread): Move code to ...
+       * ldap-wrapper.c (ldap_wrapper_launch_thread): .. here.  Change
+       callers.
+       (ldap_wrapper_thread): Rename to ...
+       (wrapper_thread): this and make local.
+
+       * ldap.c (destroy_wrapper, print_log_line)
+       (read_log_data, ldap_wrapper_thread)
+       (ldap_wrapper_wait_connections, ldap_wrapper_release_context)
+       (ldap_wrapper_connection_cleanup, reader_callback, ldap_wrapper):
+       Factor code out to ...
+       * ldap-wrapper.c: new.
+       (ldap_wrapper): Make public.
+       (read_buffer): Copy from ldap.c.
+       * ldap-wrapper.h: New.
+       * Makefile.am (dirmngr_SOURCES): Add new files.
+
 2010-07-16  Werner Koch  <wk@g10code.com>
 
        * http.c, http.h: Remove.
index 537bdf0..9d58e9c 100644 (file)
@@ -39,7 +39,7 @@ noinst_HEADERS = dirmngr.h crlcache.h crlfetch.h misc.h
 dirmngr_SOURCES = dirmngr.c dirmngr.h server.c crlcache.c crlfetch.c   \
        ldapserver.h ldapserver.c certcache.c certcache.h \
        cdb.h cdblib.c ldap.c misc.c dirmngr-err.h \
-       ocsp.c ocsp.h validate.c validate.h
+       ocsp.c ocsp.h validate.c validate.h ldap-wrapper.c ldap-wrapper.h 
 
 dirmngr_LDADD = $(libcommonpth) ../gl/libgnu.a $(DNSLIBS) $(LIBASSUAN_LIBS) \
        $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(PTH_LIBS) $(LIBINTL) $(LIBICONV)
index aeb6304..441ae9e 100644 (file)
@@ -891,7 +891,7 @@ update_dir (crl_cache_t cache)
 
       xfree (line);
     }
-  if (!es_ferror (fp) && !ferror (es_fpout) && !lineerr)
+  if (!es_ferror (fp) && !es_ferror (fpout) && !lineerr)
     {
       /* Write out the remaining entries. */
       for (e= cache->entries; e; e = e->next)
index e42196d..dd28238 100644 (file)
@@ -61,11 +61,6 @@ void crl_close_reader (ksba_reader_t reader);
 
 
 /*-- ldap.c --*/
-void *ldap_wrapper_thread (void*);
-void ldap_wrapper_wait_connections (void);
-void ldap_wrapper_release_context (ksba_reader_t reader);
-void ldap_wrapper_connection_cleanup (ctrl_t);
-
 gpg_error_t url_fetch_ldap (ctrl_t ctrl,
                             const char *url, const char *host, int port,
                             ksba_reader_t *reader);
index 5a91390..52efb9b 100644 (file)
@@ -54,6 +54,7 @@
 #include "misc.h"
 #include "ldapserver.h"
 #include "asshelp.h"
+#include "ldap-wrapper.h"
 
 /* The plain Windows version uses the windows service system.  For
    example to start the service you may use "sc start dirmngr".
@@ -393,32 +394,6 @@ wrong_args (const char *text)
 }
 
 
-/* Helper to start the reaper thread for the ldap wrapper.  */
-static void
-launch_reaper_thread (void)
-{
-  static int done;
-  pth_attr_t tattr;
-
-  if (done)
-    return;
-  done = 1;
-
-  tattr = pth_attr_new();
-  pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0);
-  pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 256*1024);
-  pth_attr_set (tattr, PTH_ATTR_NAME, "ldap-reaper");
-
-  if (!pth_spawn (tattr, ldap_wrapper_thread, NULL))
-    {
-      log_error (_("error spawning ldap wrapper reaper thread: %s\n"),
-                 strerror (errno) );
-      dirmngr_exit (1);
-    }
-  pth_attr_destroy (tattr);
-}
-
-
 /* Helper to stop the reaper thread for the ldap wrapper.  */
 static void
 shutdown_reaper (void)
@@ -938,7 +913,7 @@ main (int argc, char **argv)
           log_debug ("... okay\n");
         }
 
-      launch_reaper_thread ();
+      ldap_wrapper_launch_thread ();
       cert_cache_init ();
       crl_cache_init ();
       start_command_handler (ASSUAN_INVALID_FD);
@@ -1101,7 +1076,7 @@ main (int argc, char **argv)
         }
 #endif
 
-      launch_reaper_thread ();
+      ldap_wrapper_launch_thread ();
       cert_cache_init ();
       crl_cache_init ();
 #ifdef USE_W32_SERVICE
@@ -1127,7 +1102,7 @@ main (int argc, char **argv)
       /* Just list the CRL cache and exit. */
       if (argc)
         wrong_args ("--list-crls");
-      launch_reaper_thread ();
+      ldap_wrapper_launch_thread ();
       crl_cache_init ();
       crl_cache_list (es_stdout);
     }
@@ -1138,7 +1113,7 @@ main (int argc, char **argv)
       memset (&ctrlbuf, 0, sizeof ctrlbuf);
       dirmngr_init_default_ctrl (&ctrlbuf);
 
-      launch_reaper_thread ();
+      ldap_wrapper_launch_thread ();
       cert_cache_init ();
       crl_cache_init ();
       if (!argc)
@@ -1160,7 +1135,7 @@ main (int argc, char **argv)
       memset (&ctrlbuf, 0, sizeof ctrlbuf);
       dirmngr_init_default_ctrl (&ctrlbuf);
 
-      launch_reaper_thread ();
+      ldap_wrapper_launch_thread ();
       cert_cache_init ();
       crl_cache_init ();
       rc = crl_fetch (&ctrlbuf, argv[0], &reader);
diff --git a/dirmngr/ldap-wrapper-ce.c b/dirmngr/ldap-wrapper-ce.c
new file mode 100644 (file)
index 0000000..dd594fd
--- /dev/null
@@ -0,0 +1,333 @@
+/* ldap-wrapper-ce.c - LDAP access via W32 threads
+ * Copyright (C) 2010 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* 
+   Alternative wrapper for use with WindowsCE.  Under WindowsCE the
+   number of processes is strongly limited (32 processes including the
+   kernel processes) and thus we don't use the process approach but
+   implement a wrapper based on native threads.
+
+   See ldap-wrapper.c for  the standard wrapper interface.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <pth.h>
+
+#include "dirmngr.h"
+#include "misc.h"
+#include "ldap-wrapper.h"
+
+
+
+/* To keep track of the LDAP wrapper state we use this structure.  */
+struct wrapper_context_s
+{
+  struct wrapper_context_s *next;
+
+  pid_t pid;    /* The pid of the wrapper process. */
+  int printable_pid; /* Helper to print diagnostics after the process has
+                        been cleaned up. */
+  int fd;       /* Connected with stdout of the ldap wrapper.  */
+  gpg_error_t fd_error; /* Set to the gpg_error of the last read error
+                           if any.  */
+  int log_fd;   /* Connected with stderr of the ldap wrapper.  */
+  pth_event_t log_ev;
+  ctrl_t ctrl;  /* Connection data. */
+  int ready;    /* Internally used to mark to be removed contexts. */
+  ksba_reader_t reader; /* The ksba reader object or NULL. */
+  char *line;     /* Used to print the log lines (malloced). */
+  size_t linesize;/* Allocated size of LINE.  */
+  size_t linelen; /* Use size of LINE.  */
+  time_t stamp;   /* The last time we noticed ativity.  */
+};
+
+
+
+/* We keep a global list of spawed wrapper process.  A separate thread
+   makes use of this list to log error messages and to watch out for
+   finished processes. */
+static struct wrapper_context_s *wrapper_list;
+
+/* We need to know whether we are shutting down the process.  */
+static int shutting_down;
+
+
+
+/* Start the reaper thread for this wrapper.  */
+void
+ldap_wrapper_launch_thread (void)
+{
+  static int done;
+  pth_attr_t tattr;
+
+  if (done)
+    return;
+  done = 1;
+
+  tattr = pth_attr_new();
+  pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0);
+  pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 256*1024);
+  pth_attr_set (tattr, PTH_ATTR_NAME, "ldap-reaper");
+
+  if (!pth_spawn (tattr, ldap_wrapper_thread, NULL))
+    {
+      log_error (_("error spawning ldap wrapper reaper thread: %s\n"),
+                 strerror (errno) );
+      dirmngr_exit (1);
+    }
+  pth_attr_destroy (tattr);
+}
+
+
+
+
+
+/* Wait until all ldap wrappers have terminated.  We assume that the
+   kill has already been sent to all of them.  */
+void
+ldap_wrapper_wait_connections ()
+{
+  shutting_down = 1;
+  while (wrapper_list)
+    pth_yield (NULL);
+}
+
+
+/* This function is to be used to release a context associated with the
+   given reader object. */
+void
+ldap_wrapper_release_context (ksba_reader_t reader)
+{
+  if (!reader )
+    return;
+    
+  for (ctx=wrapper_list; ctx; ctx=ctx->next)
+    if (ctx->reader == reader)
+      {
+        if (DBG_LOOKUP)
+          log_info ("releasing ldap worker c=%p pid=%d/%d rdr=%p ctrl=%p/%d\n",
+                    ctx, 
+                    (int)ctx->pid, (int)ctx->printable_pid,
+                    ctx->reader,
+                    ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0);
+
+        ctx->reader = NULL;
+        SAFE_PTH_CLOSE (ctx->fd);
+        if (ctx->ctrl)
+          {
+            ctx->ctrl->refcount--;
+            ctx->ctrl = NULL;
+          }
+        if (ctx->fd_error)
+          log_info (_("reading from ldap wrapper %d failed: %s\n"),
+                    ctx->printable_pid, gpg_strerror (ctx->fd_error));
+        break;
+      }
+}
+
+/* Cleanup all resources held by the connection associated with
+   CTRL.  This is used after a cancel to kill running wrappers.  */
+void
+ldap_wrapper_connection_cleanup (ctrl_t ctrl)
+{
+  struct wrapper_context_s *ctx;
+
+  for (ctx=wrapper_list; ctx; ctx=ctx->next)
+    if (ctx->ctrl && ctx->ctrl == ctrl)
+      {
+        ctx->ctrl->refcount--;
+        ctx->ctrl = NULL;
+        if (ctx->pid != (pid_t)(-1))
+          gnupg_kill_process (ctx->pid);
+        if (ctx->fd_error)
+          log_info (_("reading from ldap wrapper %d failed: %s\n"),
+                    ctx->printable_pid, gpg_strerror (ctx->fd_error));
+      }
+}
+
+/* Fork and exec the LDAP wrapper and returns a new libksba reader
+   object at READER.  ARGV is a NULL terminated list of arguments for
+   the wrapper.  The function returns 0 on success or an error code.
+
+   Special hack to avoid passing a password through the command line
+   which is globally visible: If the first element of ARGV is "--pass"
+   it will be removed and instead the environment variable
+   DIRMNGR_LDAP_PASS will be set to the next value of ARGV.  On modern
+   OSes the environment is not visible to other users.  For those old
+   systems where it can't be avoided, we don't want to go into the
+   hassle of passing the password via stdin; it's just too complicated
+   and an LDAP password used for public directory lookups should not
+   be that confidential.  */
+gpg_error_t
+ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[])
+{
+  gpg_error_t err;
+  pid_t pid;
+  struct wrapper_context_s *ctx;
+  int i;
+  int j;
+  const char **arg_list;
+  const char *pgmname;
+  int outpipe[2], errpipe[2];
+
+  /* It would be too simple to connect stderr just to our logging
+     stream.  The problem is that if we are running multi-threaded
+     everything gets intermixed.  Clearly we don't want this.  So the
+     only viable solutions are either to have another thread
+     responsible for logging the messages or to add an option to the
+     wrapper module to do the logging on its own.  Given that we anyway
+     need a way to rip the child process and this is best done using a
+     general ripping thread, that thread can do the logging too. */
+
+  *reader = NULL;
+
+  /* Files: We need to prepare stdin and stdout.  We get stderr from
+     the function.  */
+  if (!opt.ldap_wrapper_program || !*opt.ldap_wrapper_program)
+    pgmname = gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR_LDAP);
+  else
+    pgmname = opt.ldap_wrapper_program;
+
+  /* Create command line argument array.  */
+  for (i = 0; argv[i]; i++)
+    ;
+  arg_list = xtrycalloc (i + 2, sizeof *arg_list);
+  if (!arg_list)
+    {
+      err = gpg_error_from_syserror ();
+      log_error (_("error allocating memory: %s\n"), strerror (errno));
+      return err;
+    }
+  for (i = j = 0; argv[i]; i++, j++)
+    if (!i && argv[i + 1] && !strcmp (*argv, "--pass"))
+      {
+       arg_list[j] = "--env-pass";
+       setenv ("DIRMNGR_LDAP_PASS", argv[1], 1);
+       i++;
+      }
+    else
+      arg_list[j] = (char*) argv[i];
+
+  ctx = xtrycalloc (1, sizeof *ctx);
+  if (!ctx)
+    {
+      err = gpg_error_from_syserror ();
+      log_error (_("error allocating memory: %s\n"), strerror (errno));
+      xfree (arg_list);
+      return err;
+    }
+
+  err = gnupg_create_inbound_pipe (outpipe);
+  if (!err)
+    {
+      err = gnupg_create_inbound_pipe (errpipe);
+      if (err)
+        {
+          close (outpipe[0]);
+          close (outpipe[1]);
+        }
+    }
+  if (err)
+    {
+      log_error (_("error creating pipe: %s\n"), gpg_strerror (err));
+      xfree (arg_list);
+      xfree (ctx);
+      return err;
+    }
+
+  err = gnupg_spawn_process_fd (pgmname, arg_list,
+                                -1, outpipe[1], errpipe[1], &pid);
+  xfree (arg_list);
+  close (outpipe[1]);
+  close (errpipe[1]);
+  if (err)
+    {
+      close (outpipe[0]);
+      close (errpipe[0]);
+      xfree (ctx);
+      return err;
+    }
+
+  ctx->pid = pid;
+  ctx->printable_pid = (int) pid;
+  ctx->fd = outpipe[0];
+  ctx->log_fd = errpipe[0];
+  ctx->log_ev = pth_event (PTH_EVENT_FD | PTH_UNTIL_FD_READABLE, ctx->log_fd);
+  if (! ctx->log_ev)
+    {
+      xfree (ctx);
+      return gpg_error_from_syserror ();
+    }
+  ctx->ctrl = ctrl;
+  ctrl->refcount++;
+  ctx->stamp = time (NULL);
+
+  err = ksba_reader_new (reader);
+  if (!err)
+    err = ksba_reader_set_cb (*reader, reader_callback, ctx);
+  if (err)
+    {
+      log_error (_("error initializing reader object: %s\n"),
+                 gpg_strerror (err));
+      destroy_wrapper (ctx);
+      ksba_reader_release (*reader);
+      *reader = NULL;
+      return err;
+    }
+
+  /* Hook the context into our list of running wrappers.  */
+  ctx->reader = *reader;
+  ctx->next = wrapper_list;
+  wrapper_list = ctx;
+  if (opt.verbose)
+    log_info ("ldap wrapper %d started (reader %p)\n",
+              (int)ctx->pid, ctx->reader);
+
+  /* Need to wait for the first byte so we are able to detect an empty
+     output and not let the consumer see an EOF without further error
+     indications.  The CRL loading logic assumes that after return
+     from this function, a failed search (e.g. host not found ) is
+     indicated right away. */
+  {
+    unsigned char c;
+
+    err = read_buffer (*reader, &c, 1);
+    if (err)
+      {
+        ldap_wrapper_release_context (*reader);
+        ksba_reader_release (*reader);
+        *reader = NULL;
+        if (gpg_err_code (err) == GPG_ERR_EOF)
+          return gpg_error (GPG_ERR_NO_DATA);
+        else
+          return err;
+      }
+    ksba_reader_unread (*reader, &c, 1);
+  }
+
+  return 0;
+}
diff --git a/dirmngr/ldap-wrapper.c b/dirmngr/ldap-wrapper.c
new file mode 100644 (file)
index 0000000..8d03ca7
--- /dev/null
@@ -0,0 +1,747 @@
+/* ldap-wrapper.c - LDAP access via a wrapper process
+ * Copyright (C) 2004, 2005, 2007, 2008 g10 Code GmbH
+ * Copyright (C) 2010 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+   We can't use LDAP directly for these reasons:
+
+   1. On some systems the LDAP library uses (indirectly) pthreads and
+      that is not compatible with PTh.
+  
+   2. It is huge library in particular if TLS comes into play.  So
+      problems with unfreed memory might turn up and we don't want
+      this in a long running daemon.
+
+   3. There is no easy way for timeouts. In particular the timeout
+      value does not work for DNS lookups (well, this is usual) and it
+      seems not to work while loading a large attribute like a
+      CRL. Having a separate process allows us to either tell the
+      process to commit suicide or have our own housekepping function
+      kill it after some time.  The latter also allows proper
+      cancellation of a query at any point of time.
+      
+   4. Given that we are going out to the network and usually get back
+      a long response, the fork/exec overhead is acceptable.
+
+   Note that under WindowsCE the number of processes is strongly
+   limited (32 processes including the kernel processes) and thus we
+   don't use the process approach but implement a different wrapper in
+   ldap-wrapper-ce.c.
+*/
+
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <pth.h>
+
+#include "dirmngr.h"
+#include "exechelp.h"
+#include "misc.h"
+#include "ldap-wrapper.h"
+
+
+#ifdef HAVE_W32_SYSTEM
+#define setenv(a,b,c) SetEnvironmentVariable ((a),(b))
+#else
+#define pth_close(fd) close(fd)
+#endif
+
+
+/* In case sysconf does not return a value we need to have a limit. */
+#ifdef _POSIX_OPEN_MAX
+#define MAX_OPEN_FDS _POSIX_OPEN_MAX
+#else
+#define MAX_OPEN_FDS 20
+#endif
+
+#define INACTIVITY_TIMEOUT (opt.ldaptimeout + 60*5)  /* seconds */
+
+
+
+/* To keep track of the LDAP wrapper state we use this structure.  */
+struct wrapper_context_s
+{
+  struct wrapper_context_s *next;
+
+  pid_t pid;    /* The pid of the wrapper process. */
+  int printable_pid; /* Helper to print diagnostics after the process has
+                        been cleaned up. */
+  int fd;       /* Connected with stdout of the ldap wrapper.  */
+  gpg_error_t fd_error; /* Set to the gpg_error of the last read error
+                           if any.  */
+  int log_fd;   /* Connected with stderr of the ldap wrapper.  */
+  pth_event_t log_ev;
+  ctrl_t ctrl;  /* Connection data. */
+  int ready;    /* Internally used to mark to be removed contexts. */
+  ksba_reader_t reader; /* The ksba reader object or NULL. */
+  char *line;     /* Used to print the log lines (malloced). */
+  size_t linesize;/* Allocated size of LINE.  */
+  size_t linelen; /* Use size of LINE.  */
+  time_t stamp;   /* The last time we noticed ativity.  */
+};
+
+
+
+/* We keep a global list of spawed wrapper process.  A separate thread
+   makes use of this list to log error messages and to watch out for
+   finished processes. */
+static struct wrapper_context_s *wrapper_list;
+
+/* We need to know whether we are shutting down the process.  */
+static int shutting_down;
+
+/* Close the pth file descriptor FD and set it to -1.  */
+#define SAFE_PTH_CLOSE(fd) \
+  do { int _fd = fd; if (_fd != -1) { pth_close (_fd); fd = -1;} } while (0)
+
+
+
+\f
+/* Read a fixed amount of data from READER into BUFFER.  */
+static gpg_error_t
+read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count)
+{
+  gpg_error_t err;
+  size_t nread;
+  
+  while (count)
+    {
+      err = ksba_reader_read (reader, buffer, count, &nread);
+      if (err)
+        return err;
+      buffer += nread;
+      count -= nread;
+    }
+  return 0;
+}
+
+
+/* Release the wrapper context and kill a running wrapper process. */
+static void
+destroy_wrapper (struct wrapper_context_s *ctx) 
+{
+  if (ctx->pid != (pid_t)(-1))
+    {
+      gnupg_kill_process (ctx->pid);
+      gnupg_release_process (ctx->pid);
+    }
+  ksba_reader_release (ctx->reader);
+  SAFE_PTH_CLOSE (ctx->fd);
+  SAFE_PTH_CLOSE (ctx->log_fd);
+  if (ctx->log_ev)
+    pth_event_free (ctx->log_ev, PTH_FREE_THIS);
+  xfree (ctx->line);
+  xfree (ctx);
+}
+
+
+/* Print the content of LINE to thye log stream but make sure to only
+   print complete lines.  Using NULL for LINE will flush any pending
+   output.  LINE may be modified by this fucntion. */
+static void
+print_log_line (struct wrapper_context_s *ctx, char *line)
+{
+  char *s;
+  size_t n;
+
+  if (!line)
+    {
+      if (ctx->line && ctx->linelen)
+        {
+
+          log_info ("%s\n", ctx->line); 
+          ctx->linelen = 0;
+        }
+      return;
+    }
+  
+  while ((s = strchr (line, '\n')))
+    {
+      *s = 0;
+      if (ctx->line && ctx->linelen)
+        {
+          log_info ("%s", ctx->line); 
+          ctx->linelen = 0;
+          log_printf ("%s\n", line);
+        }
+      else
+        log_info ("%s\n", line);
+      line = s + 1;
+    }
+  n = strlen (line);
+  if (n)
+    {
+      if (ctx->linelen + n + 1 >= ctx->linesize)
+        {
+          char *tmp;
+          size_t newsize;
+
+          newsize = ctx->linesize + ((n + 255) & ~255) + 1;
+          tmp = (ctx->line ? xtryrealloc (ctx->line, newsize)
+                           : xtrymalloc (newsize));
+          if (!tmp)
+            {
+              log_error (_("error printing log line: %s\n"), strerror (errno));
+              return;
+            }
+          ctx->line = tmp;
+          ctx->linesize = newsize;
+        }
+      memcpy (ctx->line + ctx->linelen, line, n);
+      ctx->linelen += n;
+      ctx->line[ctx->linelen] = 0;
+    }
+}
+
+
+/* Read data from the log stream.  Returns true if the log stream
+   indicated EOF or error.  */
+static int
+read_log_data (struct wrapper_context_s *ctx)
+{
+  int n;
+  char line[256];
+
+  /* We must use the pth_read function for pipes, always.  */
+  do 
+    n = pth_read (ctx->log_fd, line, sizeof line - 1);
+  while (n < 0 && errno == EINTR);
+
+  if (n <= 0) /* EOF or error. */
+    {
+      if (n < 0)
+        log_error (_("error reading log from ldap wrapper %d: %s\n"),
+                   ctx->pid, strerror (errno));
+      print_log_line (ctx, NULL);
+      SAFE_PTH_CLOSE (ctx->log_fd);
+      pth_event_free (ctx->log_ev, PTH_FREE_THIS);
+      ctx->log_ev = NULL;
+      return 1;
+    }
+
+  line[n] = 0;
+  print_log_line (ctx, line);
+  if (ctx->stamp != (time_t)(-1))
+    ctx->stamp = time (NULL);
+  return 0;
+}
+
+
+/* This function is run by a separate thread to maintain the list of
+   wrappers and to log error messages from these wrappers.  */
+void *
+ldap_wrapper_thread (void *dummy)
+{
+  int nfds;
+  struct wrapper_context_s *ctx;
+  struct wrapper_context_s *ctx_prev;
+  time_t current_time;
+
+  (void)dummy;
+
+  for (;;)
+    {
+      pth_event_t timeout_ev;
+      int any_action = 0;
+
+      timeout_ev = pth_event (PTH_EVENT_TIME, pth_timeout (1, 0));
+      if (! timeout_ev)
+       {
+          log_error (_("pth_event failed: %s\n"), strerror (errno));
+          pth_sleep (10);
+         continue;
+       }
+
+      for (ctx = wrapper_list; ctx; ctx = ctx->next)
+        {
+          if (ctx->log_fd != -1)
+            {
+             pth_event_isolate (ctx->log_ev);
+             pth_event_concat (timeout_ev, ctx->log_ev, NULL);
+            }
+        }
+
+      /* Note that the read FDs are actually handles.  Thus, we can
+        not use pth_select, but have to use pth_wait.  */
+      nfds = pth_wait (timeout_ev);
+      if (nfds < 0)
+        {
+          pth_event_free (timeout_ev, PTH_FREE_THIS);
+          log_error (_("pth_wait failed: %s\n"), strerror (errno));
+          pth_sleep (10);
+         continue;
+        }
+      if (pth_event_status (timeout_ev) == PTH_STATUS_OCCURRED)
+       nfds--;
+      pth_event_free (timeout_ev, PTH_FREE_THIS);
+
+      current_time = time (NULL);
+      if (current_time > INACTIVITY_TIMEOUT)
+        current_time -= INACTIVITY_TIMEOUT;
+
+      /* Note that there is no need to lock the list because we always
+         add entries at the head (with a pending event status) and
+         thus traversing the list will even work if we have a context
+         switch in waitpid (which should anyway only happen with Pth's
+         hard system call mapping).  */
+      for (ctx = wrapper_list; ctx; ctx = ctx->next)
+        {
+          /* Check whether there is any logging to be done. */
+          if (nfds && ctx->log_fd != -1
+             && pth_event_status (ctx->log_ev) == PTH_STATUS_OCCURRED)
+            {
+              if (read_log_data (ctx))
+                any_action = 1;
+            }
+
+          /* Check whether the process is still running.  */
+          if (ctx->pid != (pid_t)(-1))
+            {
+              gpg_error_t err;
+             int status;
+              
+             err = gnupg_wait_process ("[dirmngr_ldap]", ctx->pid, 0,
+                                        &status);
+              if (!err)
+                {
+                 log_info (_("ldap wrapper %d ready"), (int)ctx->pid);
+                  ctx->ready = 1;
+                 gnupg_release_process (ctx->pid);
+                  ctx->pid = (pid_t)(-1);
+                  any_action = 1;
+                }
+              else if (gpg_err_code (err) == GPG_ERR_GENERAL)
+                {
+                  if (status == 10)
+                    log_info (_("ldap wrapper %d ready: timeout\n"),
+                              (int)ctx->pid);
+                  else
+                    log_info (_("ldap wrapper %d ready: exitcode=%d\n"),
+                              (int)ctx->pid, status);
+                  ctx->ready = 1;
+                 gnupg_release_process (ctx->pid);
+                  ctx->pid = (pid_t)(-1);
+                  any_action = 1;
+                }
+              else if (gpg_err_code (err) != GPG_ERR_TIMEOUT)
+                {
+                  log_error (_("waiting for ldap wrapper %d failed: %s\n"),
+                             (int)ctx->pid, gpg_strerror (err));
+                  any_action = 1;
+                }
+            }
+
+          /* Check whether we should terminate the process. */
+          if (ctx->pid != (pid_t)(-1)
+              && ctx->stamp != (time_t)(-1) && ctx->stamp < current_time)
+            {
+              gnupg_kill_process (ctx->pid);
+              ctx->stamp = (time_t)(-1);
+              log_info (_("ldap wrapper %d stalled - killing\n"),
+                        (int)ctx->pid);
+              /* We need to close the log fd because the cleanup loop
+                 waits for it.  */
+              SAFE_PTH_CLOSE (ctx->log_fd);
+              any_action = 1;
+            }
+        }
+
+      /* If something has been printed to the log file or we got an
+         EOF from a wrapper, we now print the list of active
+         wrappers.  */
+      if (any_action && DBG_LOOKUP) 
+        {
+          log_info ("ldap worker stati:\n");
+          for (ctx = wrapper_list; ctx; ctx = ctx->next)
+            log_info ("  c=%p pid=%d/%d rdr=%p ctrl=%p/%d la=%lu rdy=%d\n",
+                      ctx, 
+                      (int)ctx->pid, (int)ctx->printable_pid,
+                      ctx->reader,
+                      ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0, 
+                      (unsigned long)ctx->stamp, ctx->ready);
+        }
+
+
+      /* Use a separate loop to check whether ready marked wrappers
+         may be removed.  We may only do so if the ksba reader object
+         is not anymore in use or we are in shutdown state.  */
+     again:
+      for (ctx_prev=NULL, ctx=wrapper_list; ctx; ctx_prev=ctx, ctx=ctx->next)
+        if (ctx->ready 
+            && ((ctx->log_fd == -1 && !ctx->reader) || shutting_down))
+          {
+            if (ctx_prev)
+              ctx_prev->next = ctx->next;
+            else
+              wrapper_list = ctx->next;
+            destroy_wrapper (ctx);
+            /* We need to restart because destroy_wrapper might have
+               done a context switch. */
+            goto again;
+          }
+    }
+  /*NOTREACHED*/
+  return NULL; /* Make the compiler happy.  */
+}
+
+
+
+/* Start the reaper thread for the ldap wrapper.  */
+void
+ldap_wrapper_launch_thread (void)
+{
+  static int done;
+  pth_attr_t tattr;
+
+  if (done)
+    return;
+  done = 1;
+
+  tattr = pth_attr_new();
+  pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0);
+  pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 256*1024);
+  pth_attr_set (tattr, PTH_ATTR_NAME, "ldap-reaper");
+
+  if (!pth_spawn (tattr, ldap_wrapper_thread, NULL))
+    {
+      log_error (_("error spawning ldap wrapper reaper thread: %s\n"),
+                 strerror (errno) );
+      dirmngr_exit (1);
+    }
+  pth_attr_destroy (tattr);
+}
+
+
+
+
+
+/* Wait until all ldap wrappers have terminated.  We assume that the
+   kill has already been sent to all of them.  */
+void
+ldap_wrapper_wait_connections ()
+{
+  shutting_down = 1;
+  while (wrapper_list)
+    pth_yield (NULL);
+}
+
+
+/* This function is to be used to release a context associated with the
+   given reader object. */
+void
+ldap_wrapper_release_context (ksba_reader_t reader)
+{
+  struct wrapper_context_s *ctx;
+
+  if (!reader )
+    return;
+    
+  for (ctx=wrapper_list; ctx; ctx=ctx->next)
+    if (ctx->reader == reader)
+      {
+        if (DBG_LOOKUP)
+          log_info ("releasing ldap worker c=%p pid=%d/%d rdr=%p ctrl=%p/%d\n",
+                    ctx, 
+                    (int)ctx->pid, (int)ctx->printable_pid,
+                    ctx->reader,
+                    ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0);
+
+        ctx->reader = NULL;
+        SAFE_PTH_CLOSE (ctx->fd);
+        if (ctx->ctrl)
+          {
+            ctx->ctrl->refcount--;
+            ctx->ctrl = NULL;
+          }
+        if (ctx->fd_error)
+          log_info (_("reading from ldap wrapper %d failed: %s\n"),
+                    ctx->printable_pid, gpg_strerror (ctx->fd_error));
+        break;
+      }
+}
+
+/* Cleanup all resources held by the connection associated with
+   CTRL.  This is used after a cancel to kill running wrappers.  */
+void
+ldap_wrapper_connection_cleanup (ctrl_t ctrl)
+{
+  struct wrapper_context_s *ctx;
+
+  for (ctx=wrapper_list; ctx; ctx=ctx->next)
+    if (ctx->ctrl && ctx->ctrl == ctrl)
+      {
+        ctx->ctrl->refcount--;
+        ctx->ctrl = NULL;
+        if (ctx->pid != (pid_t)(-1))
+          gnupg_kill_process (ctx->pid);
+        if (ctx->fd_error)
+          log_info (_("reading from ldap wrapper %d failed: %s\n"),
+                    ctx->printable_pid, gpg_strerror (ctx->fd_error));
+      }
+}
+
+/* This is the callback used by the ldap wrapper to feed the ksba
+   reader with the wrappers stdout.  See the description of
+   ksba_reader_set_cb for details.  */
+static int 
+reader_callback (void *cb_value, char *buffer, size_t count,  size_t *nread)
+{
+  struct wrapper_context_s *ctx = cb_value;
+  size_t nleft = count;
+
+  /* FIXME: We might want to add some internal buffering because the
+     ksba code does not do any buffering for itself (because a ksba
+     reader may be detached from another stream to read other data and
+     the it would be cumbersome to get back already buffered
+     stuff).  */
+
+  if (!buffer && !count && !nread)
+    return -1; /* Rewind is not supported. */
+
+  /* If we ever encountered a read error don't allow to continue and
+     possible overwrite the last error cause.  Bail out also if the
+     file descriptor has been closed. */
+  if (ctx->fd_error || ctx->fd == -1)
+    {
+      *nread = 0;
+      return -1;
+    }
+
+  while (nleft > 0)
+    {
+      int n;
+      pth_event_t evt;
+      gpg_error_t err;
+
+      evt = pth_event (PTH_EVENT_TIME, pth_timeout (1, 0));
+      n = pth_read_ev (ctx->fd, buffer, nleft, evt);
+      if (n < 0 && evt && pth_event_occurred (evt))
+        {
+          n = 0;
+          err = dirmngr_tick (ctx->ctrl);
+          if (err)
+            {
+              ctx->fd_error = err;
+              SAFE_PTH_CLOSE (ctx->fd);
+              if (evt)
+                pth_event_free (evt, PTH_FREE_THIS);
+              return -1;
+            }
+
+        }
+      else if (n < 0)
+        {
+          ctx->fd_error = gpg_error_from_errno (errno);
+          SAFE_PTH_CLOSE (ctx->fd);
+          if (evt)
+            pth_event_free (evt, PTH_FREE_THIS);
+          return -1;
+        }
+      else if (!n)
+        {
+          if (nleft == count)
+            {
+              if (evt)
+                pth_event_free (evt, PTH_FREE_THIS);
+              return -1; /* EOF. */
+            }
+          break; 
+        }
+      nleft -= n;
+      buffer += n;
+      if (evt)
+        pth_event_free (evt, PTH_FREE_THIS);
+      if (n > 0 && ctx->stamp != (time_t)(-1))
+        ctx->stamp = time (NULL);
+    }
+  *nread = count - nleft;
+
+  return 0;
+
+}
+
+/* Fork and exec the LDAP wrapper and returns a new libksba reader
+   object at READER.  ARGV is a NULL terminated list of arguments for
+   the wrapper.  The function returns 0 on success or an error code.
+
+   Special hack to avoid passing a password through the command line
+   which is globally visible: If the first element of ARGV is "--pass"
+   it will be removed and instead the environment variable
+   DIRMNGR_LDAP_PASS will be set to the next value of ARGV.  On modern
+   OSes the environment is not visible to other users.  For those old
+   systems where it can't be avoided, we don't want to go into the
+   hassle of passing the password via stdin; it's just too complicated
+   and an LDAP password used for public directory lookups should not
+   be that confidential.  */
+gpg_error_t
+ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[])
+{
+  gpg_error_t err;
+  pid_t pid;
+  struct wrapper_context_s *ctx;
+  int i;
+  int j;
+  const char **arg_list;
+  const char *pgmname;
+  int outpipe[2], errpipe[2];
+
+  /* It would be too simple to connect stderr just to our logging
+     stream.  The problem is that if we are running multi-threaded
+     everything gets intermixed.  Clearly we don't want this.  So the
+     only viable solutions are either to have another thread
+     responsible for logging the messages or to add an option to the
+     wrapper module to do the logging on its own.  Given that we anyway
+     need a way to rip the child process and this is best done using a
+     general ripping thread, that thread can do the logging too. */
+
+  *reader = NULL;
+
+  /* Files: We need to prepare stdin and stdout.  We get stderr from
+     the function.  */
+  if (!opt.ldap_wrapper_program || !*opt.ldap_wrapper_program)
+    pgmname = gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR_LDAP);
+  else
+    pgmname = opt.ldap_wrapper_program;
+
+  /* Create command line argument array.  */
+  for (i = 0; argv[i]; i++)
+    ;
+  arg_list = xtrycalloc (i + 2, sizeof *arg_list);
+  if (!arg_list)
+    {
+      err = gpg_error_from_syserror ();
+      log_error (_("error allocating memory: %s\n"), strerror (errno));
+      return err;
+    }
+  for (i = j = 0; argv[i]; i++, j++)
+    if (!i && argv[i + 1] && !strcmp (*argv, "--pass"))
+      {
+       arg_list[j] = "--env-pass";
+       setenv ("DIRMNGR_LDAP_PASS", argv[1], 1);
+       i++;
+      }
+    else
+      arg_list[j] = (char*) argv[i];
+
+  ctx = xtrycalloc (1, sizeof *ctx);
+  if (!ctx)
+    {
+      err = gpg_error_from_syserror ();
+      log_error (_("error allocating memory: %s\n"), strerror (errno));
+      xfree (arg_list);
+      return err;
+    }
+
+  err = gnupg_create_inbound_pipe (outpipe);
+  if (!err)
+    {
+      err = gnupg_create_inbound_pipe (errpipe);
+      if (err)
+        {
+          close (outpipe[0]);
+          close (outpipe[1]);
+        }
+    }
+  if (err)
+    {
+      log_error (_("error creating pipe: %s\n"), gpg_strerror (err));
+      xfree (arg_list);
+      xfree (ctx);
+      return err;
+    }
+
+  err = gnupg_spawn_process_fd (pgmname, arg_list,
+                                -1, outpipe[1], errpipe[1], &pid);
+  xfree (arg_list);
+  close (outpipe[1]);
+  close (errpipe[1]);
+  if (err)
+    {
+      close (outpipe[0]);
+      close (errpipe[0]);
+      xfree (ctx);
+      return err;
+    }
+
+  ctx->pid = pid;
+  ctx->printable_pid = (int) pid;
+  ctx->fd = outpipe[0];
+  ctx->log_fd = errpipe[0];
+  ctx->log_ev = pth_event (PTH_EVENT_FD | PTH_UNTIL_FD_READABLE, ctx->log_fd);
+  if (! ctx->log_ev)
+    {
+      xfree (ctx);
+      return gpg_error_from_syserror ();
+    }
+  ctx->ctrl = ctrl;
+  ctrl->refcount++;
+  ctx->stamp = time (NULL);
+
+  err = ksba_reader_new (reader);
+  if (!err)
+    err = ksba_reader_set_cb (*reader, reader_callback, ctx);
+  if (err)
+    {
+      log_error (_("error initializing reader object: %s\n"),
+                 gpg_strerror (err));
+      destroy_wrapper (ctx);
+      ksba_reader_release (*reader);
+      *reader = NULL;
+      return err;
+    }
+
+  /* Hook the context into our list of running wrappers.  */
+  ctx->reader = *reader;
+  ctx->next = wrapper_list;
+  wrapper_list = ctx;
+  if (opt.verbose)
+    log_info ("ldap wrapper %d started (reader %p)\n",
+              (int)ctx->pid, ctx->reader);
+
+  /* Need to wait for the first byte so we are able to detect an empty
+     output and not let the consumer see an EOF without further error
+     indications.  The CRL loading logic assumes that after return
+     from this function, a failed search (e.g. host not found ) is
+     indicated right away. */
+  {
+    unsigned char c;
+
+    err = read_buffer (*reader, &c, 1);
+    if (err)
+      {
+        ldap_wrapper_release_context (*reader);
+        ksba_reader_release (*reader);
+        *reader = NULL;
+        if (gpg_err_code (err) == GPG_ERR_EOF)
+          return gpg_error (GPG_ERR_NO_DATA);
+        else
+          return err;
+      }
+    ksba_reader_unread (*reader, &c, 1);
+  }
+
+  return 0;
+}
diff --git a/dirmngr/ldap-wrapper.h b/dirmngr/ldap-wrapper.h
new file mode 100644 (file)
index 0000000..dfe55eb
--- /dev/null
@@ -0,0 +1,33 @@
+/* ldap-wrapper.h - Interface to an LDAP access wrapper.
+ * Copyright (C) 2010 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LDAP_WRAPPER_H
+#define LDAP_WRAPPER_H
+
+void ldap_wrapper_launch_thread (void);
+void ldap_wrapper_wait_connections (void);
+void ldap_wrapper_release_context (ksba_reader_t reader);
+void ldap_wrapper_connection_cleanup (ctrl_t);
+gpg_error_t ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader,
+                          const char *argv[]);
+
+
+
+
+#endif /*LDAP_WRAPPER_H*/
index fd3c3f5..b71a0d3 100644 (file)
 #include "crlfetch.h"
 #include "ldapserver.h"
 #include "misc.h"
+#include "ldap-wrapper.h"
 
-#ifdef HAVE_W32_SYSTEM
-#define setenv(a,b,c) SetEnvironmentVariable ((a),(b))
-#else
-#define pth_close(fd) close(fd)
-#endif
-
-
-/* In case sysconf does not return a value we need to have a limit. */
-#ifdef _POSIX_OPEN_MAX
-#define MAX_OPEN_FDS _POSIX_OPEN_MAX
-#else
-#define MAX_OPEN_FDS 20
-#endif
-
-#define INACTIVITY_TIMEOUT (opt.ldaptimeout + 60*5)  /* seconds */
 
 #define UNENCODED_URL_CHARS "abcdefghijklmnopqrstuvwxyz"   \
                             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"   \
@@ -72,50 +58,6 @@ struct cert_fetch_context_s
 };
 
 
-/* To keep track of the LDAP wrapper state we use this structure.  */
-struct wrapper_context_s
-{
-  struct wrapper_context_s *next;
-
-  pid_t pid;    /* The pid of the wrapper process. */
-  int printable_pid; /* Helper to print diagnostics after the process has
-                        been cleaned up. */
-  int fd;       /* Connected with stdout of the ldap wrapper.  */
-  gpg_error_t fd_error; /* Set to the gpg_error of the last read error
-                           if any.  */
-  int log_fd;   /* Connected with stderr of the ldap wrapper.  */
-  pth_event_t log_ev;
-  ctrl_t ctrl;  /* Connection data. */
-  int ready;    /* Internally used to mark to be removed contexts. */
-  ksba_reader_t reader; /* The ksba reader object or NULL. */
-  char *line;     /* Used to print the log lines (malloced). */
-  size_t linesize;/* Allocated size of LINE.  */
-  size_t linelen; /* Use size of LINE.  */
-  time_t stamp;   /* The last time we noticed ativity.  */
-};
-
-
-
-
-
-/* We keep a global list of spawed wrapper process.  A separate thread
-   makes use of this list to log error messages and to watch out for
-   finished processes. */
-static struct wrapper_context_s *wrapper_list;
-
-/* We need to know whether we are shutting down the process.  */
-static int shutting_down;
-
-/* Close the pth file descriptor FD and set it to -1.  */
-#define SAFE_PTH_CLOSE(fd) \
-  do { int _fd = fd; if (_fd != -1) { pth_close (_fd); fd = -1;} } while (0)
-
-
-/* Prototypes.  */
-static gpg_error_t read_buffer (ksba_reader_t reader,
-                                unsigned char *buffer, size_t count);
-
-
 
 \f
 /* Add HOST and PORT to our list of LDAP servers.  Fixme: We should
@@ -165,604 +107,6 @@ add_server_to_servers (const char *host, int port)
 }
 
 
-/* Release the wrapper context and kill a running wrapper process. */
-static void
-destroy_wrapper (struct wrapper_context_s *ctx) 
-{
-  if (ctx->pid != (pid_t)(-1))
-    {
-      gnupg_kill_process (ctx->pid);
-      gnupg_release_process (ctx->pid);
-    }
-  ksba_reader_release (ctx->reader);
-  SAFE_PTH_CLOSE (ctx->fd);
-  SAFE_PTH_CLOSE (ctx->log_fd);
-  if (ctx->log_ev)
-    pth_event_free (ctx->log_ev, PTH_FREE_THIS);
-  xfree (ctx->line);
-  xfree (ctx);
-}
-
-
-/* Print the content of LINE to thye log stream but make sure to only
-   print complete lines.  Using NULL for LINE will flush any pending
-   output.  LINE may be modified by this fucntion. */
-static void
-print_log_line (struct wrapper_context_s *ctx, char *line)
-{
-  char *s;
-  size_t n;
-
-  if (!line)
-    {
-      if (ctx->line && ctx->linelen)
-        {
-
-          log_info ("%s\n", ctx->line); 
-          ctx->linelen = 0;
-        }
-      return;
-    }
-  
-  while ((s = strchr (line, '\n')))
-    {
-      *s = 0;
-      if (ctx->line && ctx->linelen)
-        {
-          log_info ("%s", ctx->line); 
-          ctx->linelen = 0;
-          log_printf ("%s\n", line);
-        }
-      else
-        log_info ("%s\n", line);
-      line = s + 1;
-    }
-  n = strlen (line);
-  if (n)
-    {
-      if (ctx->linelen + n + 1 >= ctx->linesize)
-        {
-          char *tmp;
-          size_t newsize;
-
-          newsize = ctx->linesize + ((n + 255) & ~255) + 1;
-          tmp = (ctx->line ? xtryrealloc (ctx->line, newsize)
-                           : xtrymalloc (newsize));
-          if (!tmp)
-            {
-              log_error (_("error printing log line: %s\n"), strerror (errno));
-              return;
-            }
-          ctx->line = tmp;
-          ctx->linesize = newsize;
-        }
-      memcpy (ctx->line + ctx->linelen, line, n);
-      ctx->linelen += n;
-      ctx->line[ctx->linelen] = 0;
-    }
-}
-
-
-/* Read data from the log stream.  Returns true if the log stream
-   indicated EOF or error.  */
-static int
-read_log_data (struct wrapper_context_s *ctx)
-{
-  int n;
-  char line[256];
-
-  /* We must use the pth_read function for pipes, always.  */
-  do 
-    n = pth_read (ctx->log_fd, line, sizeof line - 1);
-  while (n < 0 && errno == EINTR);
-
-  if (n <= 0) /* EOF or error. */
-    {
-      if (n < 0)
-        log_error (_("error reading log from ldap wrapper %d: %s\n"),
-                   ctx->pid, strerror (errno));
-      print_log_line (ctx, NULL);
-      SAFE_PTH_CLOSE (ctx->log_fd);
-      pth_event_free (ctx->log_ev, PTH_FREE_THIS);
-      ctx->log_ev = NULL;
-      return 1;
-    }
-
-  line[n] = 0;
-  print_log_line (ctx, line);
-  if (ctx->stamp != (time_t)(-1))
-    ctx->stamp = time (NULL);
-  return 0;
-}
-
-
-/* This function is run by a separate thread to maintain the list of
-   wrappers and to log error messages from these wrappers. */
-void *
-ldap_wrapper_thread (void *dummy)
-{
-  int nfds;
-  struct wrapper_context_s *ctx;
-  struct wrapper_context_s *ctx_prev;
-  time_t current_time;
-
-  (void)dummy;
-
-  for (;;)
-    {
-      pth_event_t timeout_ev;
-      int any_action = 0;
-
-      timeout_ev = pth_event (PTH_EVENT_TIME, pth_timeout (1, 0));
-      if (! timeout_ev)
-       {
-          log_error (_("pth_event failed: %s\n"), strerror (errno));
-          pth_sleep (10);
-         continue;
-       }
-
-      for (ctx = wrapper_list; ctx; ctx = ctx->next)
-        {
-          if (ctx->log_fd != -1)
-            {
-             pth_event_isolate (ctx->log_ev);
-             pth_event_concat (timeout_ev, ctx->log_ev, NULL);
-            }
-        }
-
-      /* Note that the read FDs are actually handles.  Thus, we can
-        not use pth_select, but have to use pth_wait.  */
-      nfds = pth_wait (timeout_ev);
-      if (nfds < 0)
-        {
-          pth_event_free (timeout_ev, PTH_FREE_THIS);
-          log_error (_("pth_wait failed: %s\n"), strerror (errno));
-          pth_sleep (10);
-         continue;
-        }
-      if (pth_event_status (timeout_ev) == PTH_STATUS_OCCURRED)
-       nfds--;
-      pth_event_free (timeout_ev, PTH_FREE_THIS);
-
-      current_time = time (NULL);
-      if (current_time > INACTIVITY_TIMEOUT)
-        current_time -= INACTIVITY_TIMEOUT;
-
-      /* Note that there is no need to lock the list because we always
-         add entries at the head (with a pending event status) and
-         thus traversing the list will even work if we have a context
-         switch in waitpid (which should anyway only happen with Pth's
-         hard system call mapping).  */
-      for (ctx = wrapper_list; ctx; ctx = ctx->next)
-        {
-          /* Check whether there is any logging to be done. */
-          if (nfds && ctx->log_fd != -1
-             && pth_event_status (ctx->log_ev) == PTH_STATUS_OCCURRED)
-            {
-              if (read_log_data (ctx))
-                any_action = 1;
-            }
-
-          /* Check whether the process is still running.  */
-          if (ctx->pid != (pid_t)(-1))
-            {
-              gpg_error_t err;
-             int status;
-              
-             err = gnupg_wait_process ("[dirmngr_ldap]", ctx->pid, 0,
-                                        &status);
-              if (!err)
-                {
-                 log_info (_("ldap wrapper %d ready"), (int)ctx->pid);
-                  ctx->ready = 1;
-                 gnupg_release_process (ctx->pid);
-                  ctx->pid = (pid_t)(-1);
-                  any_action = 1;
-                }
-              else if (gpg_err_code (err) == GPG_ERR_GENERAL)
-                {
-                  if (status == 10)
-                    log_info (_("ldap wrapper %d ready: timeout\n"),
-                              (int)ctx->pid);
-                  else
-                    log_info (_("ldap wrapper %d ready: exitcode=%d\n"),
-                              (int)ctx->pid, status);
-                  ctx->ready = 1;
-                 gnupg_release_process (ctx->pid);
-                  ctx->pid = (pid_t)(-1);
-                  any_action = 1;
-                }
-              else if (gpg_err_code (err) != GPG_ERR_TIMEOUT)
-                {
-                  log_error (_("waiting for ldap wrapper %d failed: %s\n"),
-                             (int)ctx->pid, gpg_strerror (err));
-                  any_action = 1;
-                }
-            }
-
-          /* Check whether we should terminate the process. */
-          if (ctx->pid != (pid_t)(-1)
-              && ctx->stamp != (time_t)(-1) && ctx->stamp < current_time)
-            {
-              gnupg_kill_process (ctx->pid);
-              ctx->stamp = (time_t)(-1);
-              log_info (_("ldap wrapper %d stalled - killing\n"),
-                        (int)ctx->pid);
-              /* We need to close the log fd because the cleanup loop
-                 waits for it.  */
-              SAFE_PTH_CLOSE (ctx->log_fd);
-              any_action = 1;
-            }
-        }
-
-      /* If something has been printed to the log file or we got an
-         EOF from a wrapper, we now print the list of active
-         wrappers.  */
-      if (any_action && DBG_LOOKUP) 
-        {
-          log_info ("ldap worker stati:\n");
-          for (ctx = wrapper_list; ctx; ctx = ctx->next)
-            log_info ("  c=%p pid=%d/%d rdr=%p ctrl=%p/%d la=%lu rdy=%d\n",
-                      ctx, 
-                      (int)ctx->pid, (int)ctx->printable_pid,
-                      ctx->reader,
-                      ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0, 
-                      (unsigned long)ctx->stamp, ctx->ready);
-        }
-
-
-      /* Use a separate loop to check whether ready marked wrappers
-         may be removed.  We may only do so if the ksba reader object
-         is not anymore in use or we are in shutdown state.  */
-     again:
-      for (ctx_prev=NULL, ctx=wrapper_list; ctx; ctx_prev=ctx, ctx=ctx->next)
-        if (ctx->ready 
-            && ((ctx->log_fd == -1 && !ctx->reader) || shutting_down))
-          {
-            if (ctx_prev)
-              ctx_prev->next = ctx->next;
-            else
-              wrapper_list = ctx->next;
-            destroy_wrapper (ctx);
-            /* We need to restart because destroy_wrapper might have
-               done a context switch. */
-            goto again;
-          }
-    }
-  /*NOTREACHED*/
-  return NULL; /* Make the compiler happy.  */
-}
-
-
-
-/* Wait until all ldap wrappers have terminated.  We assume that the
-   kill has already been sent to all of them.  */
-void
-ldap_wrapper_wait_connections ()
-{
-  shutting_down = 1;
-  while (wrapper_list)
-    pth_yield (NULL);
-}
-
-
-/* This function is to be used to release a context associated with the
-   given reader object. */
-void
-ldap_wrapper_release_context (ksba_reader_t reader)
-{
-  struct wrapper_context_s *ctx;
-
-  if (!reader )
-    return;
-    
-  for (ctx=wrapper_list; ctx; ctx=ctx->next)
-    if (ctx->reader == reader)
-      {
-        if (DBG_LOOKUP)
-          log_info ("releasing ldap worker c=%p pid=%d/%d rdr=%p ctrl=%p/%d\n",
-                    ctx, 
-                    (int)ctx->pid, (int)ctx->printable_pid,
-                    ctx->reader,
-                    ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0);
-
-        ctx->reader = NULL;
-        SAFE_PTH_CLOSE (ctx->fd);
-        if (ctx->ctrl)
-          {
-            ctx->ctrl->refcount--;
-            ctx->ctrl = NULL;
-          }
-        if (ctx->fd_error)
-          log_info (_("reading from ldap wrapper %d failed: %s\n"),
-                    ctx->printable_pid, gpg_strerror (ctx->fd_error));
-        break;
-      }
-}
-
-/* Cleanup all resources held by the connection associated with
-   CTRL.  This is used after a cancel to kill running wrappers.  */
-void
-ldap_wrapper_connection_cleanup (ctrl_t ctrl)
-{
-  struct wrapper_context_s *ctx;
-
-  for (ctx=wrapper_list; ctx; ctx=ctx->next)
-    if (ctx->ctrl && ctx->ctrl == ctrl)
-      {
-        ctx->ctrl->refcount--;
-        ctx->ctrl = NULL;
-        if (ctx->pid != (pid_t)(-1))
-          gnupg_kill_process (ctx->pid);
-        if (ctx->fd_error)
-          log_info (_("reading from ldap wrapper %d failed: %s\n"),
-                    ctx->printable_pid, gpg_strerror (ctx->fd_error));
-      }
-}
-
-/* This is the callback used by the ldap wrapper to feed the ksba
-   reader with the wrappers stdout.  See the description of
-   ksba_reader_set_cb for details.  */
-static int 
-reader_callback (void *cb_value, char *buffer, size_t count,  size_t *nread)
-{
-  struct wrapper_context_s *ctx = cb_value;
-  size_t nleft = count;
-
-  /* FIXME: We might want to add some internal buffering because the
-     ksba code does not do any buffering for itself (because a ksba
-     reader may be detached from another stream to read other data and
-     the it would be cumbersome to get back already buffered
-     stuff).  */
-
-  if (!buffer && !count && !nread)
-    return -1; /* Rewind is not supported. */
-
-  /* If we ever encountered a read error don't allow to continue and
-     possible overwrite the last error cause.  Bail out also if the
-     file descriptor has been closed. */
-  if (ctx->fd_error || ctx->fd == -1)
-    {
-      *nread = 0;
-      return -1;
-    }
-
-  while (nleft > 0)
-    {
-      int n;
-      pth_event_t evt;
-      gpg_error_t err;
-
-      evt = pth_event (PTH_EVENT_TIME, pth_timeout (1, 0));
-      n = pth_read_ev (ctx->fd, buffer, nleft, evt);
-      if (n < 0 && evt && pth_event_occurred (evt))
-        {
-          n = 0;
-          err = dirmngr_tick (ctx->ctrl);
-          if (err)
-            {
-              ctx->fd_error = err;
-              SAFE_PTH_CLOSE (ctx->fd);
-              if (evt)
-                pth_event_free (evt, PTH_FREE_THIS);
-              return -1;
-            }
-
-        }
-      else if (n < 0)
-        {
-          ctx->fd_error = gpg_error_from_errno (errno);
-          SAFE_PTH_CLOSE (ctx->fd);
-          if (evt)
-            pth_event_free (evt, PTH_FREE_THIS);
-          return -1;
-        }
-      else if (!n)
-        {
-          if (nleft == count)
-            {
-              if (evt)
-                pth_event_free (evt, PTH_FREE_THIS);
-              return -1; /* EOF. */
-            }
-          break; 
-        }
-      nleft -= n;
-      buffer += n;
-      if (evt)
-        pth_event_free (evt, PTH_FREE_THIS);
-      if (n > 0 && ctx->stamp != (time_t)(-1))
-        ctx->stamp = time (NULL);
-    }
-  *nread = count - nleft;
-
-  return 0;
-
-}
-
-/* Fork and exec the LDAP wrapper and returns a new libksba reader
-   object at READER.  ARGV is a NULL terminated list of arguments for
-   the wrapper.  The function returns 0 on success or an error code.
-
-   We can't use LDAP directly for these reasons:
-
-   1. On some systems the LDAP library uses (indirectly) pthreads and
-      that is not compatible with PTh.
-  
-   2. It is huge library in particular if TLS comes into play.  So
-      problems with unfreed memory might turn up and we don't want
-      this in a long running daemon.
-
-   3. There is no easy way for timeouts. In particular the timeout
-      value does not work for DNS lookups (well, this is usual) and it
-      seems not to work while loading a large attribute like a
-      CRL. Having a separate process allows us to either tell the
-      process to commit suicide or have our own housekepping function
-      kill it after some time.  The latter also allows proper
-      cancellation of a query at any point of time.
-      
-   4. Given that we are going out to the network and usually get back
-      a long response, the fork/exec overhead is acceptable.
-
-   Special hack to avoid passing a password through the command line
-   which is globally visible: If the first element of ARGV is "--pass"
-   it will be removed and instead the environment variable
-   DIRMNGR_LDAP_PASS will be set to the next value of ARGV.  On modern
-   OSes the environment is not visible to other users.  For those old
-   systems where it can't be avoided, we don't want to go into the
-   hassle of passing the password via stdin; it's just too complicated
-   and an LDAP password used for public directory lookups should not
-   be that confidential.  */
-static gpg_error_t
-ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[])
-{
-  gpg_error_t err;
-  pid_t pid;
-  struct wrapper_context_s *ctx;
-  int i;
-  int j;
-  const char **arg_list;
-  const char *pgmname;
-  int outpipe[2], errpipe[2];
-
-  /* It would be too simple to connect stderr just to our logging
-     stream.  The problem is that if we are running multi-threaded
-     everything gets intermixed.  Clearly we don't want this.  So the
-     only viable solutions are either to have another thread
-     responsible for logging the messages or to add an option to the
-     wrapper module to do the logging on its own.  Given that we anyway
-     need a way to rip the child process and this is best done using a
-     general ripping thread, that thread can do the logging too. */
-
-  *reader = NULL;
-
-  /* Files: We need to prepare stdin and stdout.  We get stderr from
-     the function.  */
-  if (!opt.ldap_wrapper_program || !*opt.ldap_wrapper_program)
-    pgmname = gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR_LDAP);
-  else
-    pgmname = opt.ldap_wrapper_program;
-
-  /* Create command line argument array.  */
-  for (i = 0; argv[i]; i++)
-    ;
-  arg_list = xtrycalloc (i + 2, sizeof *arg_list);
-  if (!arg_list)
-    {
-      err = gpg_error_from_syserror ();
-      log_error (_("error allocating memory: %s\n"), strerror (errno));
-      return err;
-    }
-  for (i = j = 0; argv[i]; i++, j++)
-    if (!i && argv[i + 1] && !strcmp (*argv, "--pass"))
-      {
-       arg_list[j] = "--env-pass";
-       setenv ("DIRMNGR_LDAP_PASS", argv[1], 1);
-       i++;
-      }
-    else
-      arg_list[j] = (char*) argv[i];
-
-  ctx = xtrycalloc (1, sizeof *ctx);
-  if (!ctx)
-    {
-      err = gpg_error_from_syserror ();
-      log_error (_("error allocating memory: %s\n"), strerror (errno));
-      xfree (arg_list);
-      return err;
-    }
-
-  err = gnupg_create_inbound_pipe (outpipe);
-  if (!err)
-    {
-      err = gnupg_create_inbound_pipe (errpipe);
-      if (err)
-        {
-          close (outpipe[0]);
-          close (outpipe[1]);
-        }
-    }
-  if (err)
-    {
-      log_error (_("error creating pipe: %s\n"), gpg_strerror (err));
-      xfree (arg_list);
-      xfree (ctx);
-      return err;
-    }
-
-  err = gnupg_spawn_process_fd (pgmname, arg_list,
-                                -1, outpipe[1], errpipe[1], &pid);
-  xfree (arg_list);
-  close (outpipe[1]);
-  close (errpipe[1]);
-  if (err)
-    {
-      close (outpipe[0]);
-      close (errpipe[0]);
-      xfree (ctx);
-      return err;
-    }
-
-  ctx->pid = pid;
-  ctx->printable_pid = (int) pid;
-  ctx->fd = outpipe[0];
-  ctx->log_fd = errpipe[0];
-  ctx->log_ev = pth_event (PTH_EVENT_FD | PTH_UNTIL_FD_READABLE, ctx->log_fd);
-  if (! ctx->log_ev)
-    {
-      xfree (ctx);
-      return gpg_error_from_syserror ();
-    }
-  ctx->ctrl = ctrl;
-  ctrl->refcount++;
-  ctx->stamp = time (NULL);
-
-  err = ksba_reader_new (reader);
-  if (!err)
-    err = ksba_reader_set_cb (*reader, reader_callback, ctx);
-  if (err)
-    {
-      log_error (_("error initializing reader object: %s\n"),
-                 gpg_strerror (err));
-      destroy_wrapper (ctx);
-      ksba_reader_release (*reader);
-      *reader = NULL;
-      return err;
-    }
-
-  /* Hook the context into our list of running wrappers.  */
-  ctx->reader = *reader;
-  ctx->next = wrapper_list;
-  wrapper_list = ctx;
-  if (opt.verbose)
-    log_info ("ldap wrapper %d started (reader %p)\n",
-              (int)ctx->pid, ctx->reader);
-
-  /* Need to wait for the first byte so we are able to detect an empty
-     output and not let the consumer see an EOF without further error
-     indications.  The CRL loading logic assumes that after return
-     from this function, a failed search (e.g. host not found ) is
-     indicated right away. */
-  {
-    unsigned char c;
-
-    err = read_buffer (*reader, &c, 1);
-    if (err)
-      {
-        ldap_wrapper_release_context (*reader);
-        ksba_reader_release (*reader);
-        *reader = NULL;
-        if (gpg_err_code (err) == GPG_ERR_EOF)
-          return gpg_error (GPG_ERR_NO_DATA);
-        else
-          return err;
-      }
-    ksba_reader_unread (*reader, &c, 1);
-  }
-
-  return 0;
-}
-
 
 
 /* Perform an LDAP query.  Returns an gpg error code or 0 on success.
index eb1ab22..6e5f163 100644 (file)
@@ -27,8 +27,8 @@ void ldapserver_list_free (ldap_server_t servers);
 
 
 /* Parse a single LDAP server configuration line.  Returns the server
-   or NULL in case of errors.  The configuration lineis assumed to be
-   colon seprated with these fields:
+   or NULL in case of errors.  The configuration line is assumed to be
+   colon separated with these fields:
 
    1. field: Hostname
    2. field: Portnumber
index b2e8bc7..fccd127 100644 (file)
@@ -23,7 +23,9 @@ All applications using GnuPG's GPGSM tool to process S/MIME messages
 or manage X.509 certificates are affected.  The bug exists in all
 versions of GnuPG including the recently released GnuPG 2.0.16.
 
-GnuPG 1.x is not affected because it does not come with the GPGSM
+GPG (i.e. OpenPGP) is NOT affected.
+
+GnuPG 1.x is NOT affected because it does not come with the GPGSM
 tool.
 
 An exploit is not yet known but it can't be ruled out for sure that
index e54a8ed..4ec287b 100644 (file)
@@ -1,3 +1,8 @@
+2010-07-20  Werner Koch  <wk@g10code.com>
+
+       * mainproc.c (print_pkenc_list): Write a STATUS_ERROR.  Fixes
+       bug#1255.
+
 2010-06-17  Werner Koch  <wk@g10code.com>
 
        * gpg.c (main): Use CAST5 as default s2k algo.  The macro
index d397a4f..87e9912 100644 (file)
@@ -481,8 +481,11 @@ print_pkenc_list( struct kidlist_item *list, int failed )
            }
        }
        else if (list->reason)
+          {
            log_info(_("public key decryption failed: %s\n"),
                                                g10_errstr(list->reason));
+            write_status_error ("pkdecrypt_failed", list->reason);
+          }
     }
 }