summaryrefslogtreecommitdiffstats
path: root/dirmngr
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2010-07-23 18:16:14 +0200
committerWerner Koch <wk@gnupg.org>2010-07-23 18:16:14 +0200
commita22c38baad4113ce477b2e312e0bec365c0bd6b3 (patch)
treebfa3cedf6beb152420e22687eed1f29bad758ab9 /dirmngr
parent. (diff)
downloadgnupg2-a22c38baad4113ce477b2e312e0bec365c0bd6b3.tar.xz
gnupg2-a22c38baad4113ce477b2e312e0bec365c0bd6b3.zip
Some work on the dirmngr
Diffstat (limited to 'dirmngr')
-rw-r--r--dirmngr/ChangeLog20
-rw-r--r--dirmngr/Makefile.am2
-rw-r--r--dirmngr/crlcache.c2
-rw-r--r--dirmngr/crlfetch.h5
-rw-r--r--dirmngr/dirmngr.c37
-rw-r--r--dirmngr/ldap-wrapper-ce.c333
-rw-r--r--dirmngr/ldap-wrapper.c747
-rw-r--r--dirmngr/ldap-wrapper.h33
-rw-r--r--dirmngr/ldap.c658
-rw-r--r--dirmngr/ldapserver.h4
10 files changed, 1144 insertions, 697 deletions
diff --git a/dirmngr/ChangeLog b/dirmngr/ChangeLog
index c95a2f2d6..8af2f3047 100644
--- a/dirmngr/ChangeLog
+++ b/dirmngr/ChangeLog
@@ -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.
diff --git a/dirmngr/Makefile.am b/dirmngr/Makefile.am
index 537bdf035..9d58e9cd3 100644
--- a/dirmngr/Makefile.am
+++ b/dirmngr/Makefile.am
@@ -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)
diff --git a/dirmngr/crlcache.c b/dirmngr/crlcache.c
index aeb6304b0..441ae9ee0 100644
--- a/dirmngr/crlcache.c
+++ b/dirmngr/crlcache.c
@@ -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)
diff --git a/dirmngr/crlfetch.h b/dirmngr/crlfetch.h
index e42196dc7..dd282385d 100644
--- a/dirmngr/crlfetch.h
+++ b/dirmngr/crlfetch.h
@@ -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);
diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c
index 5a913905d..52efb9be4 100644
--- a/dirmngr/dirmngr.c
+++ b/dirmngr/dirmngr.c
@@ -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
index 000000000..dd594fdbd
--- /dev/null
+++ b/dirmngr/ldap-wrapper-ce.c
@@ -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
index 000000000..8d03ca756
--- /dev/null
+++ b/dirmngr/ldap-wrapper.c
@@ -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)
+
+
+
+
+/* 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
index 000000000..dfe55eb61
--- /dev/null
+++ b/dirmngr/ldap-wrapper.h
@@ -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*/
diff --git a/dirmngr/ldap.c b/dirmngr/ldap.c
index fd3c3f510..b71a0d3c9 100644
--- a/dirmngr/ldap.c
+++ b/dirmngr/ldap.c
@@ -35,22 +35,8 @@
#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);
-
-
/* 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.
diff --git a/dirmngr/ldapserver.h b/dirmngr/ldapserver.h
index eb1ab226d..6e5f163f4 100644
--- a/dirmngr/ldapserver.h
+++ b/dirmngr/ldapserver.h
@@ -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