Merged Dirmngr with GnuPG.
authorWerner Koch <wk@gnupg.org>
Wed, 9 Jun 2010 16:53:51 +0000 (16:53 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 9 Jun 2010 16:53:51 +0000 (16:53 +0000)
A few code changes to support dirmngr.

61 files changed:
ChangeLog
Makefile.am
NEWS
agent/genkey.c
am/cmacros.am
autogen.sh
common/ChangeLog
common/asshelp.c
common/exechelp-posix.c
common/exechelp-w32.c
common/exechelp-w32ce.c
common/exechelp.h
common/homedir.c
common/logging.c
common/logging.h
common/util.h
configure.ac
dirmngr/ChangeLog [new file with mode: 0644]
dirmngr/ChangeLog.1 [new file with mode: 0644]
dirmngr/Makefile.am [new file with mode: 0644]
dirmngr/OAUTHORS [new file with mode: 0644]
dirmngr/ONEWS [new file with mode: 0644]
dirmngr/b64dec.c [new file with mode: 0644]
dirmngr/b64enc.c [new file with mode: 0644]
dirmngr/cdb.h [new file with mode: 0644]
dirmngr/cdblib.c [new file with mode: 0644]
dirmngr/certcache.c [new file with mode: 0644]
dirmngr/certcache.h [new file with mode: 0644]
dirmngr/crlcache.c [new file with mode: 0644]
dirmngr/crlcache.h [new file with mode: 0644]
dirmngr/crlfetch.c [new file with mode: 0644]
dirmngr/crlfetch.h [new file with mode: 0644]
dirmngr/dirmngr-client.c [new file with mode: 0644]
dirmngr/dirmngr.c [new file with mode: 0644]
dirmngr/dirmngr.h [new file with mode: 0644]
dirmngr/dirmngr_ldap.c [new file with mode: 0644]
dirmngr/get-path.c [new file with mode: 0644]
dirmngr/http.c [new file with mode: 0644]
dirmngr/http.h [new file with mode: 0644]
dirmngr/ldap-url.c [new file with mode: 0644]
dirmngr/ldap-url.h [new file with mode: 0644]
dirmngr/ldap.c [new file with mode: 0644]
dirmngr/ldapserver.c [new file with mode: 0644]
dirmngr/ldapserver.h [new file with mode: 0644]
dirmngr/misc.c [new file with mode: 0644]
dirmngr/misc.h [new file with mode: 0644]
dirmngr/no-libgcrypt.c [new file with mode: 0644]
dirmngr/ocsp.c [new file with mode: 0644]
dirmngr/ocsp.h [new file with mode: 0644]
dirmngr/server.c [new file with mode: 0644]
dirmngr/validate.c [new file with mode: 0644]
dirmngr/validate.h [new file with mode: 0644]
g13/be-encfs.c
g13/runner.c
m4/ChangeLog
m4/ldap.m4
scd/ChangeLog
scd/scdaemon.c
sm/export.c
sm/import.c
tools/gpgconf-comp.c

index 79d7a71..cc59259 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,23 @@
+2010-06-09  Werner Koch  <wk@g10code.com>
+
+       * configure.ac (GNUPG_DIRMNGR_LDAP_PGM): Add option
+       --with-dirmngr-ldap-pgm.
+
+       * am/cmacros.am (-DGNUPG_LOCALSTATEDIR): New.
+       (GNUPG_DEFAULT_DIRMNGR_LDAP): New.
+
+2010-06-08  Werner Koch  <wk@g10code.com>
+
+       * configure.ac: Add build support for dirmngr.
+       (try_ldap): Rename to try_ks_ldap.
+       (GNUPG_CHECK_LDAP): Also test if dirmngr is to be build.
+
+       * Makefile.am (SUBDIRS): Add dirmngr.
+
 2010-06-07  Werner Koch  <wk@g10code.com>
 
+       * dirmngr/: New.
+
        * configure.ac: Add option --enable-gpgtar.
 
 2010-05-31  Werner Koch  <wk@g10code.com>
index a51b5ff..1bda6ff 100644 (file)
@@ -61,6 +61,11 @@ g13 = g13
 else
 g13 =
 endif
+if BUILD_DIRMNGR
+dirmngr = dirmngr
+else
+dirmngr =
+endif
 if BUILD_TOOLS
 tools = tools
 else
@@ -79,7 +84,8 @@ tests = tests
 endif
 
 SUBDIRS = m4 gl include common ${kbx} \
- ${gpg} ${keyserver} ${sm} ${agent} ${scd} ${g13} ${tools} po ${doc} ${tests}
+          ${gpg} ${keyserver} ${sm} ${agent} ${scd} ${g13} ${dirmngr} \
+          ${tools} po ${doc} ${tests}
 
 dist_doc_DATA = README
 
diff --git a/NEWS b/NEWS
index 6f39200..eb23bbd 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -29,6 +29,8 @@ Noteworthy changes in version 2.1.x (under development)
    option --use-standard-socket may now be used to use this feature by
    default.
 
+ * Dirmngr is now a part of this package.
+
 
 Noteworthy changes in version 2.0.13 (2009-09-04)
 -------------------------------------------------
index 9e2f324..c5d2c9e 100644 (file)
@@ -129,10 +129,11 @@ check_passphrase_pattern (ctrl_t ctrl, const char *pw)
 
   if (gnupg_spawn_process_fd (pgmname, argv, fileno (infp), -1, -1, &pid))
     result = 1; /* Execute error - assume password should no be used.  */
-  else if (gnupg_wait_process (pgmname, pid, NULL))
+  else if (gnupg_wait_process (pgmname, pid, 0, NULL))
     result = 1; /* Helper returned an error - probably a match.  */
   else
     result = 0; /* Success; i.e. no match.  */
+  gnupg_release_process (pid);
 
   /* Overwrite our temporary file. */
   fseek (infp, 0, SEEK_SET);
index da45a67..6668b25 100644 (file)
@@ -25,7 +25,8 @@ AM_CPPFLAGS += -DGNUPG_BINDIR="\"$(bindir)\""            \
                -DGNUPG_LIBEXECDIR="\"$(libexecdir)\""    \
                -DGNUPG_LIBDIR="\"$(libdir)/@PACKAGE@\""  \
                -DGNUPG_DATADIR="\"$(datadir)/@PACKAGE@\"" \
-               -DGNUPG_SYSCONFDIR="\"$(sysconfdir)/@PACKAGE@\""
+               -DGNUPG_SYSCONFDIR="\"$(sysconfdir)/@PACKAGE@\"" \
+               -DGNUPG_LOCALSTATEDIR="\"$(localstatedir)\""
 endif
 
 
@@ -47,6 +48,9 @@ endif
 if GNUPG_PROTECT_TOOL_PGM
 AM_CPPFLAGS += -DGNUPG_DEFAULT_PROTECT_TOOL="\"@GNUPG_PROTECT_TOOL_PGM@\""
 endif
+if GNUPG_DIRMNGR_LDAP_PGM
+AM_CPPFLAGS += -DGNUPG_DEFAULT_DIRMNGR_LDAP="\"@GNUPG_DIRMNGR_LDAP_PGM@\""
+endif
 
 # Under Windows we use LockFileEx.  WindowsCE provides this only on
 # the WindowsMobile 6 platform and thus we need to use the coredll6
index 092e35e..5dc8669 100755 (executable)
@@ -103,7 +103,7 @@ if [ "$myhost" = "w32" ]; then
           w32root="$w32ce_root"
           [ -z "$w32root" ] && w32root="$HOME/w32ce_root"
           toolprefixes="$w32ce_toolprefixes arm-mingw32ce"
-          extraoptions="--disable-scdaemon --disable-zip $w32ce_extraoptions"
+          extraoptions="--disable-scdaemon --disable-zip --disable-ldap --disable-dirmngr $w32ce_extraoptions"
           ;;
         *)
           [ -z "$w32root" ] && w32root="$HOME/w32root"
index 5247415..849d1de 100644 (file)
@@ -1,3 +1,21 @@
+2010-06-09  Werner Koch  <wk@g10code.com>
+
+       * exechelp-posix.c, exechelp-w32.c
+       * exechelp-w32ce.c (gnupg_wait_process): Add new arg HANG.  Change
+       all callers.
+       (gnupg_release_process): New.  Use it after all calls to
+       gnupg_wait_process.
+
+       * util.h (GNUPG_MODULE_NAME_DIRMNGR_LDAP): New.
+       * homedir.c (gnupg_cachedir): New.
+       (w32_try_mkdir): New.
+       (dirmngr_socket_name): Chanmge standard socket name.
+       (gnupg_module_name): Support GNUPG_MODULE_NAME_DIRMNGR_LDAP.
+
+       * logging.c (log_set_get_tid_callback): Replace by ...
+       (log_set_pid_suffix_cb): .. new.
+       (do_logv): Change accordingly.
+
 2010-06-08  Marcus Brinkmann  <marcus@g10code.de>
 
        * Makefile.am (AM_CFLAGS): Add $(LIBASSUAN_CFLAGS).
@@ -5,7 +23,7 @@
        * sysutils.c: Include <assuan.h>.
        (translate_sys2libc_fd_int): Cast to silence gcc warning.
        * iobuf.c: Include <assuan.h>
-       (translate_file_handle): Fix syntax error.      
+       (translate_file_handle): Fix syntax error.
 
 2010-06-08  Werner Koch  <wk@g10code.com>
 
index 95c7747..bd7aa8d 100644 (file)
@@ -362,12 +362,13 @@ start_new_gpg_agent (assuan_context_t *r_ctx,
           if (err)
             log_debug ("starting `%s' for testing failed: %s\n",
                        agent_program, gpg_strerror (err));
-          else if ((err = gnupg_wait_process (agent_program, pid, &excode)))
+          else if ((err = gnupg_wait_process (agent_program, pid, 0, &excode)))
             {
               if (excode == -1)
                 log_debug ("running `%s' for testing failed: %s\n",
                            agent_program, gpg_strerror (err));
             }          
+          gnupg_release_process (pid);
 
           if (!err && !excode)
             {
index 1f4cca6..5a8e028 100644 (file)
@@ -416,37 +416,39 @@ gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
 }
 
 
-/* Wait for the process identified by PID to terminate. PGMNAME should
-   be the same as supplied to the spawn function and is only used for
-   diagnostics. Returns 0 if the process succeeded, GPG_ERR_GENERAL
-   for any failures of the spawned program or other error codes.  If
-   EXITCODE is not NULL the exit code of the process is stored at this
-   address or -1 if it could not be retrieved and no error message is
-   logged.  */
+/* See exechelp.h for the description.  */
 gpg_error_t
-gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode)
+gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
 {
   gpg_err_code_t ec;
-
   int i, status;
 
-  if (exitcode)
-    *exitcode = -1;
+  if (r_exitcode)
+    *r_exitcode = -1;
 
   if (pid == (pid_t)(-1))
     return gpg_error (GPG_ERR_INV_VALUE);
 
 #ifdef USE_GNU_PTH
-  i = pth_waitpid ? pth_waitpid (pid, &status, 0) : waitpid (pid, &status, 0);
-#else
-  while ( (i=waitpid (pid, &status, 0)) == -1 && errno == EINTR)
-    ;
+  if (pth_waitpid)
+    i = pth_waitpid (pid, &status, hang? 0:WNOHANG);
+  else
 #endif
+    {
+      while ((i=waitpid (pid, &status, hang? 0:WNOHANG)) == (pid_t)(-1)
+             && errno == EINTR)
+        ;
+    }
+  
   if (i == (pid_t)(-1))
     {
+      ec = gpg_err_code_from_errno (errno);
       log_error (_("waiting for process %d to terminate failed: %s\n"),
                  (int)pid, strerror (errno));
-      ec = gpg_err_code_from_errno (errno);
+    }
+  else if (!i)
+    {
+      ec = GPG_ERR_TIMEOUT; /* Still running.  */
     }
   else if (WIFEXITED (status) && WEXITSTATUS (status) == 127)
     {
@@ -455,11 +457,11 @@ gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode)
     }
   else if (WIFEXITED (status) && WEXITSTATUS (status))
     {
-      if (!exitcode)
+      if (!r_exitcode)
         log_error (_("error running `%s': exit status %d\n"), pgmname,
                    WEXITSTATUS (status));
       else
-        *exitcode = WEXITSTATUS (status);
+        *r_exitcode = WEXITSTATUS (status);
       ec = GPG_ERR_GENERAL;
     }
   else if (!WIFEXITED (status))
@@ -469,8 +471,8 @@ gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode)
     }
   else 
     {
-      if (exitcode)
-        *exitcode = 0;
+      if (r_exitcode)
+        *r_exitcode = 0;
       ec = 0;
     }
 
@@ -478,7 +480,14 @@ gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode)
 }
 
 
-/* Spawn a new process and immediatley detach from it.  The name of
+void
+gnupg_release_process (pid_t pid)
+{
+  (void)pid;
+}
+
+
+/* Spawn a new process and immediately detach from it.  The name of
    the program to exec is PGMNAME and its arguments are in ARGV (the
    programname is automatically passed as first argument).
    Environment strings in ENVP are set.  An error is returned if
index 4616bec..297f6f8 100644 (file)
@@ -382,7 +382,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
   int cr_flags;
   char *cmdline;
   int fd, fdout, rp[2];
-  HANDLE nullhd[];
+  HANDLE nullhd[2];
   int i;
 
   (void)preexec;
@@ -428,7 +428,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
     }
   
   nullhd[0] =    fd == -1? w32_open_null (0) : INVALID_HANDLE_VALUE;
-  nullhd[1] = outfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
+  nullhd[1] = fdout == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
 
   /* Start the process.  Note that we can't run the PREEXEC function
      because this would change our own environment. */
@@ -437,7 +437,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
   si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
   si.hStdInput  =    fd == -1? nullhd[0] : fd_to_handle (fd);
-  si.hStdOutput = outfd == -1? nullhd[1] : fd_to_handle (fdout);
+  si.hStdOutput = fdout == -1? nullhd[1] : fd_to_handle (fdout);
   si.hStdError  = fd_to_handle (rp[1]);
 
   cr_flags = (CREATE_DEFAULT_ERROR_MODE
@@ -599,22 +599,17 @@ gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
 }
 
 
-/* Wait for the process identified by PID to terminate. PGMNAME should
-   be the same as supplied to the spawn function and is only used for
-   diagnostics. Returns 0 if the process succeeded, GPG_ERR_GENERAL
-   for any failures of the spawned program or other error codes.  If
-   EXITCODE is not NULL the exit code of the process is stored at this
-   address or -1 if it could not be retrieved. */
+/* See exechelp.h for a description.  */
 gpg_error_t
-gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode)
+gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
 {
   gpg_err_code_t ec;
   HANDLE proc = fd_to_handle (pid);
   int code;
   DWORD exc;
 
-  if (exitcode)
-    *exitcode = -1;
+  if (r_exitcode)
+    *r_exitcode = -1;
 
   if (pid == (pid_t)(-1))
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -622,50 +617,66 @@ gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode)
   /* FIXME: We should do a pth_waitpid here.  However this has not yet
      been implemented.  A special W32 pth system call would even be
      better.  */
-  code = WaitForSingleObject (proc, INFINITE);
+  code = WaitForSingleObject (proc, hang? INFINITE : 0);
   switch (code) 
     {
-      case WAIT_FAILED:
-        log_error (_("waiting for process %d to terminate failed: %s\n"),
-                   (int)pid, w32_strerror (-1));
-        ec = GPG_ERR_GENERAL;
-        break;
-
-      case WAIT_OBJECT_0:
-        if (!GetExitCodeProcess (proc, &exc))
-          {
-            log_error (_("error getting exit code of process %d: %s\n"),
-                         (int)pid, w32_strerror (-1) );
-            ec = GPG_ERR_GENERAL;
-          }
-        else if (exc)
-          {
-            log_error (_("error running `%s': exit status %d\n"),
-                       pgmname, (int)exc );
-            if (exitcode)
-              *exitcode = (int)exc;
-            ec = GPG_ERR_GENERAL;
-          }
-        else
-          {
-            if (exitcode)
-              *exitcode = 0;
-            ec = 0;
-          }
-        CloseHandle (proc);
-        break;
-
-      default:
-        log_error ("WaitForSingleObject returned unexpected "
-                   "code %d for pid %d\n", code, (int)pid );
-        ec = GPG_ERR_GENERAL;
-        break;
+    case WAIT_TIMEOUT:
+      ec = GPG_ERR_TIMEOUT;
+      break;
+
+    case WAIT_FAILED:
+      log_error (_("waiting for process %d to terminate failed: %s\n"),
+                 (int)pid, w32_strerror (-1));
+      ec = GPG_ERR_GENERAL;
+      break;
+
+    case WAIT_OBJECT_0:
+      if (!GetExitCodeProcess (proc, &exc))
+        {
+          log_error (_("error getting exit code of process %d: %s\n"),
+                     (int)pid, w32_strerror (-1) );
+          ec = GPG_ERR_GENERAL;
+        }
+      else if (exc)
+        {
+          log_error (_("error running `%s': exit status %d\n"),
+                     pgmname, (int)exc );
+          if (r_exitcode)
+            *r_exitcode = (int)exc;
+          ec = GPG_ERR_GENERAL;
+        }
+      else
+        {
+          if (r_exitcode)
+            *r_exitcode = 0;
+          ec = 0;
+        }
+      break;
+      
+    default:
+      log_error ("WaitForSingleObject returned unexpected "
+                 "code %d for pid %d\n", code, (int)pid );
+      ec = GPG_ERR_GENERAL;
+      break;
     }
-
+  
   return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
 }
 
 
+
+void
+gnupg_release_process (pid_t pid)
+{
+  if (pid != (pid_t)INVALID_HANDLE_VALUE)
+    {
+      HANDLE process = (HANDLE)pid;
+      
+      CloseHandle (process);
+    }
+}
+
+
 /* Spawn a new process and immediatley detach from it.  The name of
    the program to exec is PGMNAME and its arguments are in ARGV (the
    programname is automatically passed as first argument).
index d206052..5a84c9f 100644 (file)
@@ -653,14 +653,10 @@ gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
   return 0;
 }
 
-/* Wait for the process identified by PID to terminate. PGMNAME should
-   be the same as supplied to the spawn function and is only used for
-   diagnostics. Returns 0 if the process succeeded, GPG_ERR_GENERAL
-   for any failures of the spawned program or other error codes.  If
-   EXITCODE is not NULL the exit code of the process is stored at this
-   address or -1 if it could not be retrieved. */
+
+/* See exechelp.h for a description.  */
 gpg_error_t
-gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode)
+gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *exitcode)
 {
   gpg_err_code_t ec;
   HANDLE proc = fd_to_handle (pid);
@@ -676,50 +672,65 @@ gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode)
   /* FIXME: We should do a pth_waitpid here.  However this has not yet
      been implemented.  A special W32 pth system call would even be
      better.  */
-  code = WaitForSingleObject (proc, INFINITE);
+  code = WaitForSingleObject (proc, hang? INFINITE : 0);
   switch (code) 
     {
-      case WAIT_FAILED:
-        log_error (_("waiting for process %d to terminate failed: %s\n"),
-                   (int)pid, w32_strerror (-1));
-        ec = GPG_ERR_GENERAL;
-        break;
-
-      case WAIT_OBJECT_0:
-        if (!GetExitCodeProcess (proc, &exc))
-          {
-            log_error (_("error getting exit code of process %d: %s\n"),
-                         (int)pid, w32_strerror (-1) );
-            ec = GPG_ERR_GENERAL;
+    case WAIT_TIMEOUT:
+      ec = GPG_ERR_TIMEOUT;
+      break;
+      
+    case WAIT_FAILED:
+      log_error (_("waiting for process %d to terminate failed: %s\n"),
+                 (int)pid, w32_strerror (-1));
+      ec = GPG_ERR_GENERAL;
+      break;
+
+    case WAIT_OBJECT_0:
+      if (!GetExitCodeProcess (proc, &exc))
+        {
+          log_error (_("error getting exit code of process %d: %s\n"),
+                     (int)pid, w32_strerror (-1) );
+          ec = GPG_ERR_GENERAL;
           }
-        else if (exc)
-          {
-            log_error (_("error running `%s': exit status %d\n"),
+      else if (exc)
+        {
+          log_error (_("error running `%s': exit status %d\n"),
                        pgmname, (int)exc );
-            if (exitcode)
-              *exitcode = (int)exc;
-            ec = GPG_ERR_GENERAL;
-          }
-        else
-          {
-            if (exitcode)
-              *exitcode = 0;
-            ec = 0;
-          }
-        CloseHandle (proc);
-        break;
-
-      default:
-        log_error ("WaitForSingleObject returned unexpected "
-                   "code %d for pid %d\n", code, (int)pid );
-        ec = GPG_ERR_GENERAL;
-        break;
+          if (exitcode)
+            *exitcode = (int)exc;
+          ec = GPG_ERR_GENERAL;
+        }
+      else
+        {
+          if (exitcode)
+            *exitcode = 0;
+          ec = 0;
+        }
+      break;
+      
+    default:
+      log_error ("WaitForSingleObject returned unexpected "
+                 "code %d for pid %d\n", code, (int)pid );
+      ec = GPG_ERR_GENERAL;
+      break;
     }
 
   return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
 }
 
 
+void
+gnupg_release_process (pid_t pid)
+{
+  if (pid != (pid_t)INVALID_HANDLE_VALUE)
+    {
+      HANDLE process = (HANDLE)pid;
+      
+      CloseHandle (process);
+    }
+}
+
+
 /* Spawn a new process and immediatley detach from it.  The name of
    the program to exec is PGMNAME and its arguments are in ARGV (the
    programname is automatically passed as first argument).
index 56d0c1b..3a5b9e2 100644 (file)
@@ -59,8 +59,8 @@ gpg_error_t gnupg_create_outbound_pipe (int filedes[2]);
    process are expected in the NULL terminated array ARGV.  The
    program name itself should not be included there.  If PREEXEC is
    not NULL, that function will be called right before the exec.
-   Calling gnupg_wait_process is required.  Returns 0 on success or an
-   error code.
+   Calling gnupg_wait_process and gnupg_release_process is required.
+   Returns 0 on success or an error code.
 
    FLAGS is a bit vector:
 
@@ -85,21 +85,41 @@ gpg_error_t gnupg_spawn_process (const char *pgmname, const char *argv[],
    and ERRFD to stderr (any of them may be -1 to connect them to
    /dev/null).  The arguments for the process are expected in the NULL
    terminated array ARGV.  The program name itself should not be
-   included there.  Calling gnupg_wait_process is required.  Returns 0
-   on success or an error code. */
+   included there.  Calling gnupg_wait_process and
+   gnupg_release_process is required.  Returns 0 on success or an
+   error code. */
 gpg_error_t gnupg_spawn_process_fd (const char *pgmname, 
                                     const char *argv[],
                                     int infd, int outfd, int errfd,
                                     pid_t *pid);
 
 
-/* Wait for the process identified by PID to terminate. PGMNAME should
-   be the same as supplied to the spawn fucntion and is only used for
-   diagnostics.  Returns 0 if the process succeded, GPG_ERR_GENERAL
-   for any failures of the spawned program or other error codes.  If
-   EXITCODE is not NULL the exit code of the process is stored at this
-   address or -1 if it could not be retrieved.  */
-gpg_error_t gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode);
+/* If HANG is true, waits for the process identified by PID to exit;
+   if HANG is false, checks whether the process has terminated.
+   PGMNAME should be the same as supplied to the spawn function and is
+   only used for diagnostics.  Return values:
+
+   0
+       The process exited successful.  0 is stored at R_EXITCODE.
+
+   GPG_ERR_GENERAL
+       The process exited without success.  The exit code of process
+       is then stored at R_EXITCODE.  An exit code of -1 indicates
+       that the process terminated abnormally (e.g. due to a signal).
+
+   GPG_ERR_TIMEOUT 
+       The process is still running (returned only if HANG is false).
+
+   GPG_ERR_INV_VALUE 
+       An invalid PID has been specified.  
+
+   Other error codes may be returned as well.  Unless otherwise noted,
+   -1 will be stored at R_EXITCODE.  R_EXITCODE may be passed as NULL
+   if the exit code is not required (in that case an error messge will
+   be printed).  Note that under Windows PID is not the process id but
+   the handle of the process.  */
+gpg_error_t gnupg_wait_process (const char *pgmname, pid_t pid, int hang,
+                                int *r_exitcode);
 
 
 /* Kill a process; that is send an appropriate signal to the process.
@@ -107,6 +127,11 @@ gpg_error_t gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode);
    from the system.  An invalid PID is ignored.  */
 void gnupg_kill_process (pid_t pid);
 
+/* Release the process identified by PID.  This function is actually
+   only required for Windows but it does not harm to always call it.
+   It is a nop if PID is invalid.  */
+void gnupg_release_process (pid_t pid);
+
 
 /* Spawn a new process and immediatley detach from it.  The name of
    the program to exec is PGMNAME and its arguments are in ARGV (the
index a8bec42..3cd8e9d 100644 (file)
 #include "sysutils.h"
 
 
+#ifdef HAVE_W32_SYSTEM
+static void
+w32_try_mkdir (const char *dir)
+{
+#ifdef HAVE_W32CE_SYSTEM
+  wchar_t *wdir = utf8_to_wchar (dir);
+  if (wdir)
+    {
+      CreateDirectory (wdir, NULL);
+      xfree (wdir);
+    }
+#else              
+  CreateDirectory (dir, NULL);
+#endif
+}
+#endif
+
+
 /* This is a helper function to load a Windows function from either of
    one DLLs. */
 #ifdef HAVE_W32_SYSTEM
@@ -114,18 +132,7 @@ standard_homedir (void)
           
           /* Try to create the directory if it does not yet exists.  */
           if (access (dir, F_OK))
-            {
-#ifdef HAVE_W32CE_SYSTEM
-              wchar_t *wdir = utf8_to_wchar (dir);
-              if (wdir)
-                {
-                  CreateDirectory (wdir, NULL);
-                  xfree (wdir);
-                }
-#else              
-              CreateDirectory (dir, NULL);
-#endif
-            }
+            w32_try_mkdir (dir);
         }
       else
         dir = GNUPG_DEFAULT_HOMEDIR;
@@ -366,6 +373,54 @@ gnupg_localedir (void)
 }
 
 
+/* Return the name of the cache directory.  The name is allocated in a
+   static area on the first use.  Windows only: If the directory does
+   not exist it is created.  */
+const char *
+gnupg_cachedir (void)
+{
+#ifdef HAVE_W32_SYSTEM
+  static const char *dir;
+
+  if (!dir)
+    {
+      char path[MAX_PATH];
+      const char *s1[] = { "GNU", "cache", "gnupg", NULL };
+      int s1_len;
+      const char **comp;
+
+      s1_len = 0;
+      for (comp = s1; *comp; comp++)
+        s1_len += 1 + strlen (*comp);
+
+      if (w32_shgetfolderpath (NULL, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, 
+                               NULL, 0, path) >= 0) 
+        {
+          char *tmp = xmalloc (strlen (path) + s1_len + 1);
+         char *p;
+
+         p = stpcpy (tmp, path);
+          for (comp = s1; *comp; comp++)
+           {
+             p = stpcpy (p, "\\");
+             p = stpcpy (p, *comp);
+
+             if (access (tmp, F_OK))
+               w32_try_mkdir (tmp);
+           }
+
+          dir = tmp;
+        }
+      else
+        dir = "c:\\temp\\cache\\dirmngr";
+    }
+  return dir;
+#else /*!HAVE_W32_SYSTEM*/
+  return GNUPG_LOCALSTATEDIR "/cache/" PACKAGE_NAME;
+#endif /*!HAVE_W32_SYSTEM*/
+}
+
+
 /* Return the default socket name used by DirMngr. */
 const char *
 dirmngr_socket_name (void)
@@ -379,7 +434,10 @@ dirmngr_socket_name (void)
       const char *s2;
 
       /* We need something akin CSIDL_COMMON_PROGRAMS, but local
-        (non-roaming).  */
+        (non-roaming).  This is becuase the file needs to be on the
+        local machine and makes only sense on that machine.
+        CSIDL_WINDOWS seems to be the only location which guarantees
+        that. */
       if (w32_shgetfolderpath (NULL, CSIDL_WINDOWS, NULL, 0, s1) < 0)
        strcpy (s1, "C:\\WINDOWS");
       s2 = DIRSEP_S "S.dirmngr";
@@ -388,7 +446,7 @@ dirmngr_socket_name (void)
     }
   return name;
 #else /*!HAVE_W32_SYSTEM*/
-  return "/var/run/dirmngr/socket";
+  return GNUPG_LOCALSTATEDIR "/run/" PACKAGE_NAME "/S.dirmngr";
 #endif /*!HAVE_W32_SYSTEM*/
 }
 
@@ -450,6 +508,13 @@ gnupg_module_name (int which)
       X(libexecdir, "gpg-protect-tool");
 #endif
 
+    case GNUPG_MODULE_NAME_DIRMNGR_LDAP:
+#ifdef GNUPG_DEFAULT_DIRMNGR_LDAP
+      return GNUPG_DEFAULT_DIRMNGR_LDAP;
+#else 
+      X(libexecdir, "dirmngr_ldap");
+#endif
+
     case GNUPG_MODULE_NAME_CHECK_PATTERN:
       X(libexecdir, "gpg-check-pattern");
 
index f9ac692..dbf9de4 100644 (file)
@@ -63,7 +63,7 @@ static char prefix_buffer[80];
 static int with_time;
 static int with_prefix;
 static int with_pid;
-static unsigned long (*get_tid_callback)(void);
+static int (*get_pid_suffix_cb)(unsigned long *r_value);
 static int running_detached;
 static int force_prefixes;
 
@@ -336,9 +336,9 @@ log_set_fd (int fd)
 
 
 void
-log_set_get_tid_callback (unsigned long (*cb)(void))
+log_set_pid_suffix_cb (int (*cb)(unsigned long *r_value))
 {
-  get_tid_callback = cb;
+  get_pid_suffix_cb = cb;
 }
 
 
@@ -441,9 +441,12 @@ do_logv (int level, int ignore_arg_ptr, const char *fmt, va_list arg_ptr)
         es_fputs_unlocked (prefix_buffer, logstream);
       if (with_pid || force_prefixes)
         {
-          if (get_tid_callback)
-            es_fprintf_unlocked (logstream, "[%u.%lx]", 
-                        (unsigned int)getpid (), get_tid_callback ());
+          unsigned long pidsuf;
+          int pidfmt;
+
+          if (get_pid_suffix_cb && (pidfmt=get_pid_suffix_cb (&pidsuf)))
+            es_fprintf_unlocked (logstream, pidfmt == 1? "[%u.%lu]":"[%u.%lx]",
+                                 (unsigned int)getpid (), pidsuf);
           else
             es_fprintf_unlocked (logstream, "[%u]", (unsigned int)getpid ());
         }
index 2c29a0b..9161917 100644 (file)
@@ -35,7 +35,7 @@ int  log_get_errorcount (int clear);
 void log_inc_errorcount (void);
 void log_set_file( const char *name );
 void log_set_fd (int fd);
-void log_set_get_tid_callback (unsigned long (*cb)(void));
+void log_set_pid_suffix_cb (int (*cb)(unsigned long *r_value));
 void log_set_prefix (const char *text, unsigned int flags);
 const char *log_get_prefix (unsigned int *flags);
 int log_test_fd (int fd);
index 6fd8741..97ecef1 100644 (file)
@@ -192,6 +192,7 @@ const char *gnupg_libexecdir (void);
 const char *gnupg_libdir (void);
 const char *gnupg_datadir (void);
 const char *gnupg_localedir (void);
+const char *gnupg_cachedir (void);
 const char *dirmngr_socket_name (void);
 
 /* All module names.  We also include gpg and gpgsm for the sake for
@@ -206,6 +207,7 @@ const char *dirmngr_socket_name (void);
 #define GNUPG_MODULE_NAME_GPG           8
 #define GNUPG_MODULE_NAME_CONNECT_AGENT 9
 #define GNUPG_MODULE_NAME_GPGCONF       10
+#define GNUPG_MODULE_NAME_DIRMNGR_LDAP  11
 const char *gnupg_module_name (int which);
 
 
index 6ecfbfb..c06defd 100644 (file)
@@ -1,6 +1,6 @@
 # configure.ac - for GnuPG 2.1
 # Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
-#               2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+#               2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
 # 
 # This file is part of GnuPG.
 # 
@@ -81,11 +81,14 @@ disable_keyserver_path=no
 use_ccid_driver=yes
 use_standard_socket=no
 
+try_ks_ldap=no
+
 GNUPG_BUILD_PROGRAM(gpg, yes)
 GNUPG_BUILD_PROGRAM(gpgsm, yes)
 GNUPG_BUILD_PROGRAM(agent, yes)
 GNUPG_BUILD_PROGRAM(scdaemon, yes)
 GNUPG_BUILD_PROGRAM(g13, yes)
+GNUPG_BUILD_PROGRAM(dirmngr, yes)
 GNUPG_BUILD_PROGRAM(tools, yes)
 GNUPG_BUILD_PROGRAM(doc, yes)
 GNUPG_BUILD_PROGRAM(symcryptrun, no)
@@ -155,6 +158,15 @@ show_gnupg_protect_tool_pgm="(default)"
 test -n "$GNUPG_PROTECT_TOOL_PGM" \
       && show_gnupg_protect_tool_pgm="$GNUPG_PROTECT_TOOL_PGM"
 
+AC_ARG_WITH(dirmngr-ldap-pgm,
+    [  --with-dirmngr-ldap-pgm=PATH  Use PATH as the default for the dirmnge ldap wrapper)],
+          GNUPG_DIRMNGR_LDAP_PGM="$withval", GNUPG_DIRMNGR_LDAP_PGM="" )
+AC_SUBST(GNUPG_DIRMNGR_LDAP_PGM)
+AM_CONDITIONAL(GNUPG_DIRMNGR_LDAP_PGM, test -n "$GNUPG_DIRMNGR_LDAP_PGM")
+show_gnupg_dirmngr_ldap_pgm="(default)"
+test -n "$GNUPG_DIRMNGR_LDAP_PGM" \
+      && show_gnupg_dirmngr_ldap_pgm="$GNUPG_DIRMNGR_LDAP_PGM"
+
 
 # Some folks want to use only the agent from this packet.  Make it
 # easier for them by providing the configure option
@@ -239,8 +251,8 @@ if test "$use_exec" = yes ; then
     AC_MSG_CHECKING([whether LDAP keyserver support is requested])
     AC_ARG_ENABLE(ldap,
       AC_HELP_STRING([--disable-ldap],[disable LDAP keyserver interface only]),
-      try_ldap=$enableval, try_ldap=yes)
-    AC_MSG_RESULT($try_ldap)
+      try_ks_ldap=$enableval, try_ks_ldap=yes)
+    AC_MSG_RESULT($try_ks_ldap)
 
     AC_MSG_CHECKING([whether HKP keyserver support is requested])
     AC_ARG_ENABLE(hkp,
@@ -528,6 +540,7 @@ have_dosish_system=no
 have_w32_system=no
 have_w32ce_system=no
 use_simple_gettext=no
+mmap_needed=yes
 case "${host}" in
     *-mingw32*)
         # special stuff for Windoze NT
@@ -552,6 +565,7 @@ case "${host}" in
         esac
         try_gettext="no"
        use_simple_gettext=yes
+       mmap_needed=no
         ;;
     i?86-emx-os2 | i?86-*-os2*emx )
         # OS/2 with the EMX environment
@@ -738,6 +752,10 @@ AC_PATH_PROG(FUSERMOUNT, fusermount, /usr/bin/fusermount)
 AC_DEFINE_UNQUOTED(FUSERMOUNT,
        "${FUSERMOUNT}", [defines the filename of the fusermount program])
 
+
+# Checks for dirmngr
+
+
 #
 # Checks for symcryptrun:
 #
@@ -943,7 +961,7 @@ AM_CONDITIONAL(USE_DNS_SRV, test x"$use_dns_srv" = xyes)
 #
 # Check for LDAP
 #
-if test "$try_ldap" = yes ; then
+if test "$try_ks_ldap" = yes || test "$build_dirmngr" = "yes" ; then
    GNUPG_CHECK_LDAP($NETLIBS)
 fi
 
@@ -1152,9 +1170,9 @@ AC_CHECK_DECLS(getpagesize)
 AC_FUNC_FSEEKO
 AC_FUNC_VPRINTF
 AC_FUNC_FORK
-AC_CHECK_FUNCS([strerror strlwr tcgetattr mmap])
-AC_CHECK_FUNCS([strcasecmp strncasecmp ctermid times gmtime_r])
-AC_CHECK_FUNCS([unsetenv fcntl ftruncate])
+AC_CHECK_FUNCS([strerror strlwr tcgetattr mmap canonicalize_file_name])
+AC_CHECK_FUNCS([strcasecmp strncasecmp ctermid times gmtime_r strtoull])
+AC_CHECK_FUNCS([unsetenv fcntl ftruncate canonicalize_file_name])
 AC_CHECK_FUNCS([gettimeofday getrusage getrlimit setrlimit clock_gettime])
 AC_CHECK_FUNCS([atexit raise getpagesize strftime nl_langinfo setlocale])
 AC_CHECK_FUNCS([waitpid wait4 sigaction sigprocmask pipe getaddrinfo])
@@ -1162,6 +1180,11 @@ AC_CHECK_FUNCS([ttyname rand ftello fsync stat lstat])
 
 AC_CHECK_TYPES([struct sigaction, sigset_t],,,[#include <signal.h>])
 
+# Dirmngr requires mmap on Unix systems.
+if test $ac_cv_func_mmap != yes -a $mmap_needed = yes; then
+  AC_MSG_ERROR([[Sorry, the current implemenation requires mmap.]])
+fi
+
 #
 # These are needed by the jnlib parts in common.
 # Note:  We already checked pwd.h.
@@ -1170,6 +1193,9 @@ AC_CHECK_FUNCS([memicmp stpcpy strsep strlwr strtoul memmove stricmp strtol \
                 memrchr isascii timegm getrusage setrlimit stat setlocale   \
                 flockfile funlockfile fopencookie funopen getpwnam getpwuid \
                 getenv ])
+# end jnlib checks.
+
+
 
 #
 # gnulib checks
@@ -1466,18 +1492,19 @@ if test "$build_agent_only" = "yes" ; then
 fi
 
 
-AM_CONDITIONAL(BUILD_GPG,   test "$build_gpg" = "yes")
-AM_CONDITIONAL(BUILD_GPGSM, test "$build_gpgsm" = "yes")
-AM_CONDITIONAL(BUILD_AGENT, test "$build_agent" = "yes")
-AM_CONDITIONAL(BUILD_SCDAEMON, test "$build_scdaemon" = "yes")
-AM_CONDITIONAL(BUILD_G13,   test "$build_g13" = "yes")
-AM_CONDITIONAL(BUILD_TOOLS, test "$build_tools" = "yes")
-AM_CONDITIONAL(BUILD_DOC,   test "$build_doc" = "yes")
+AM_CONDITIONAL(BUILD_GPG,         test "$build_gpg" = "yes")
+AM_CONDITIONAL(BUILD_GPGSM,       test "$build_gpgsm" = "yes")
+AM_CONDITIONAL(BUILD_AGENT,       test "$build_agent" = "yes")
+AM_CONDITIONAL(BUILD_SCDAEMON,    test "$build_scdaemon" = "yes")
+AM_CONDITIONAL(BUILD_G13,         test "$build_g13" = "yes")
+AM_CONDITIONAL(BUILD_DIRMNGR,     test "$build_dirmngr" = "yes")
+AM_CONDITIONAL(BUILD_TOOLS,       test "$build_tools" = "yes")
+AM_CONDITIONAL(BUILD_DOC,         test "$build_doc" = "yes")
 AM_CONDITIONAL(BUILD_SYMCRYPTRUN, test "$build_symcryptrun" = "yes")
-AM_CONDITIONAL(BUILD_GPGTAR,test "$build_gpgtar" = "yes")
+AM_CONDITIONAL(BUILD_GPGTAR,      test "$build_gpgtar" = "yes")
 
 AM_CONDITIONAL(RUN_GPG_TESTS,
-       test x$cross_compiling = xno -a "$build_gpg" = yes )
+               test x$cross_compiling = xno -a "$build_gpg" = yes )
 
 
 #
@@ -1524,6 +1551,16 @@ if test "$have_ksba" = "no"; then
 *** (at least version $NEED_KSBA_VERSION using API $NEED_KSBA_API is required).
 ***]])
 fi
+if test "$gnupg_have_ldap" = "no"; then
+    die=yes
+    AC_MSG_NOTICE([[
+***
+*** You need a LDAP library to build this program.
+*** Check out 
+***    http://www.openldap.org 
+*** for a suitable implementation. 
+***]])
+fi
 if test "$missing_pth" = "yes"; then
     AC_MSG_NOTICE([[
 ***
@@ -1562,6 +1599,7 @@ sm/Makefile
 agent/Makefile
 scd/Makefile
 g13/Makefile
+dirmngr/Makefile
 keyserver/Makefile
 keyserver/gpg2keys_mailto
 keyserver/gpg2keys_test
@@ -1585,9 +1623,11 @@ echo "
         Agent:     $build_agent $build_agent_threaded
         Smartcard: $build_scdaemon $build_scdaemon_extra
         G13:       $build_g13
+        Dirmngr:   $build_dirmngr
         Gpgtar:    $build_gpgtar
 
         Protect tool:      $show_gnupg_protect_tool_pgm
+        LDAP wrapper:      $show_gnupg_dirmngr_ldap_pgm
         Default agent:     $show_gnupg_agent_pgm
         Default pinentry:  $show_gnupg_pinentry_pgm
         Default scdaemon:  $show_gnupg_scdaemon_pgm
diff --git a/dirmngr/ChangeLog b/dirmngr/ChangeLog
new file mode 100644 (file)
index 0000000..b529464
--- /dev/null
@@ -0,0 +1,1345 @@
+2010-06-09  Werner Koch  <wk@g10code.com>
+
+       * i18n.h: Remove.
+
+       * Makefile.am (no-libgcrypt.c): New rule.
+
+       * exechelp.h: Remove.
+       * exechelp.c: Remove.
+       (dirmngr_release_process): Change callers to use the gnupg func.
+       (dirmngr_wait_process): Likewise.
+       (dirmngr_kill_process): Likewise.  This actually implements it for
+       W32.
+       * ldap.c (ldap_wrapper): s/get_dirmngr_ldap_path/gnupg_module_name/.
+       (ldap_wrapper_thread): Use gnupg_wait_process and adjust for
+       changed semantics.
+       (ldap_wrapper): Replace xcalloc by xtrycalloc.  Replace spawn
+       mechanism.
+
+       * server.c (start_command_handler): Remove assuan_set_log_stream.
+
+       * validate.c: Remove gcrypt.h and ksba.h.
+
+       * ldapserver.c: s/util.h/dirmngr.h/.
+
+       * dirmngr.c (sleep) [W32]: Remove macro.
+       (main): s/sleep/gnupg_sleep/.
+       (pid_suffix_callback): Change arg type.
+       (my_gcry_logger): Remove.
+       (fixed_gcry_pth_init): New.
+       (main): Use it.
+       (FD2INT): Remove.
+
+2010-06-08  Werner Koch  <wk@g10code.com>
+
+       * misc.h (copy_time): Remove and replace by gnupg_copy_time which
+       allows to set a null date.
+       * misc.c (dump_isotime, get_time, get_isotime, set_time)
+       (check_isotime, add_isotime): Remove and replace all calls by the
+       versions from common/gettime.c.
+
+       * crlcache.c, misc.c, misc.h: s/dirmngr_isotime_t/gnupg_isotime_t/.
+       * server.c, ldap.c: Reorder include directives.
+       * crlcache.h, misc.h: Remove all include directives.
+
+       * certcache.c (cmp_simple_canon_sexp): Remove.
+       (compare_serialno): Rewrite using cmp_simple_canon_sexp from
+       common/sexputil.c
+
+       * error.h: Remove.
+
+       * dirmngr.c: Remove transitional option "--ignore-ocsp-servic-url".
+       (opts): Use ARGPARSE macros.
+       (i18n_init): Remove.
+       (main): Use GnuPG init functions.
+
+       * dirmngr.h: Remove duplicated stuff now taken from ../common.
+
+       * get-path.c, util.h: Remove.
+
+       * Makefile.am: Adjust to GnuPG system.
+       * estream.c, estream.h, estream-printf.c, estream-printf.h: Remove.
+
+2010-06-07  Werner Koch  <wk@g10code.com>
+
+       * OAUTHORS, ONEWS, ChangeLog.1: New.
+
+       * ChangeLog, Makefile.am, b64dec.c, b64enc.c, cdb.h, cdblib.c
+       * certcache.c, certcache.h, crlcache.c, crlcache.h, crlfetch.c
+       * crlfetch.h, dirmngr-client.c, dirmngr.c, dirmngr.h
+       * dirmngr_ldap.c, error.h, estream-printf.c, estream-printf.h
+       * estream.c, estream.h, exechelp.c, exechelp.h, get-path.c, http.c
+       * http.h, i18n.h, ldap-url.c, ldap-url.h, ldap.c, ldapserver.c
+       * ldapserver.h, misc.c, misc.h, ocsp.c, ocsp.h, server.c, util.h
+       * validate.c, validate.h: Imported from the current SVN of the
+       dirmngr package (only src/).
+
+2010-03-13  Werner Koch  <wk@g10code.com>
+
+       * dirmngr.c (int_and_ptr_u): New.
+       (pid_suffix_callback): Trick out compiler.
+       (start_connection_thread): Ditto.
+       (handle_connections): Ditto.
+
+2010-03-09  Werner Koch  <wk@g10code.com>
+
+       * dirmngr.c (set_debug): Allow numerical values.
+
+2009-12-15  Werner Koch  <wk@g10code.com>
+
+       * dirmngr.c: Add option --ignore-cert-extension.
+       (parse_rereadable_options): Implement.
+       * dirmngr.h (opt): Add IGNORED_CERT_EXTENSIONS.
+       * validate.c (unknown_criticals): Handle ignored extensions.
+
+2009-12-08  Marcus Brinkmann  <marcus@g10code.de>
+
+       * dirmngr-client.c (start_dirmngr): Convert posix FDs to assuan fds.
+
+2009-11-25  Marcus Brinkmann  <marcus@g10code.de>
+
+       * server.c (start_command_handler): Use assuan_fd_t and
+       assuan_fdopen on fds.
+
+2009-11-05  Marcus Brinkmann  <marcus@g10code.de>
+
+       * server.c (start_command_handler): Update use of
+       assuan_init_socket_server.
+       * dirmngr-client.c (start_dirmngr): Update use of
+       assuan_pipe_connect and assuan_socket_connect.
+
+2009-11-04  Werner Koch  <wk@g10code.com>
+
+       * server.c (register_commands): Add help arg to
+       assuan_register_command.  Change all command comments to strings.
+
+2009-11-02  Marcus Brinkmann  <marcus@g10code.de>
+
+       * server.c (reset_notify): Take LINE argument, return gpg_error_t.
+
+2009-10-16  Marcus Brinkmann  <marcus@g10code.com>
+
+       * Makefile.am: (dirmngr_LDADD): Link to $(LIBASSUAN_LIBS) instead
+       of $(LIBASSUAN_PTH_LIBS).
+       * dirmngr.c: Invoke ASSUAN_SYSTEM_PTH_IMPL.
+       (main): Call assuan_set_system_hooks and assuan_sock_init.
+
+2009-09-22  Marcus Brinkmann  <marcus@g10code.de>
+
+       * dirmngr.c (main): Update to new Assuan interface.
+       * server.c (option_handler, cmd_ldapserver, cmd_isvalid)
+       (cmd_checkcrl, cmd_checkocsp, cmd_lookup, cmd_loadcrl)
+       (cmd_listcrls, cmd_cachecert, cmd_validate): Return gpg_error_t
+       instead int.
+       (register_commands): Likewise for member HANDLER.
+       (start_command_handler): Allocate context with assuan_new before
+       starting server.  Release on error.
+       * dirmngr-client.c (main): Update to new Assuan interface.
+       (start_dirmngr): Allocate context with assuan_new before
+       connecting to server.  Release on error.
+
+2009-08-12  Werner Koch  <wk@g10code.com>
+
+       * dirmngr-client.c (squid_loop_body): Flush stdout.  Suggested by
+       Philip Shin.
+
+2009-08-07  Werner Koch  <wk@g10code.com>
+
+       * crlfetch.c (my_es_read): Add explicit check for EOF.
+
+       * http.c (struct http_context_s): Turn IN_DATA and IS_HTTP_0_9 to
+       bit fields.
+       (struct cookie_s): Add CONTENT_LENGTH_VALID and CONTENT_LENGTH.
+       (parse_response): Parse the Content-Length header.
+       (cookie_read): Handle content length.
+       (http_open): Make NEED_HEADER the semi-default.
+
+       * http.h (HTTP_FLAG_IGNORE_CL): New.
+
+2009-08-04  Werner Koch  <wk@g10code.com>
+
+       * ldap.c (ldap_wrapper_thread): Factor some code out to ...
+       (read_log_data): ... new.  Close the log fd on error.
+       (ldap_wrapper_thread): Delay cleanup until the log fd is closed.
+       (SAFE_PTH_CLOSE): New.  Use it instead of pth_close.
+
+2009-07-31  Werner Koch  <wk@g10code.com>
+
+       * server.c (cmd_loadcrl): Add option --url.
+       * dirmngr-client.c (do_loadcrl): Make use of --url.
+
+       * crlfetch.c (crl_fetch): Remove HTTP_FLAG_NO_SHUTDOWN.  Add
+       flag HTTP_FLAG_LOG_RESP with active DBG_LOOKUP.
+
+       * http.c: Require estream.  Remove P_ES macro.
+       (write_server): Remove.
+       (my_read_line): Remove.  Replace all callers by es_read_line.
+       (send_request): Use es_asprintf.  Always store the cookie.
+       (http_wait_response): Remove the need to dup the socket.  USe new
+       shutdown flag.
+       * http.h (HTTP_FLAG_NO_SHUTDOWN): Rename to HTTP_FLAG_SHUTDOWN.
+
+       * estream.c, estream.h, estream-printf.c, estream-printf.h: Update
+       from current libestream.  This is provide es_asprintf.
+
+2009-07-20  Werner Koch  <wk@g10code.com>
+
+       * dirmngr.c (pid_suffix_callback): New.
+       (main): Use log_set_pid_suffix_cb.
+       (start_connection_thread): Put the fd into the tls.
+
+       * ldap.c (ldap_wrapper_thread): Print ldap worker stati.
+       (ldap_wrapper_release_context): Print a debug info.
+       (end_cert_fetch_ldap): Release the reader.  Might fix bug#999.
+
+2009-06-17  Werner Koch  <wk@g10code.com>
+
+       * util.h: Remove unused dotlock.h.
+
+2009-05-26  Werner Koch  <wk@g10code.com>
+
+       * ldap.c (ldap_wrapper): Show reader object in diagnostics.
+       * crlcache.c (crl_cache_reload_crl): Ditto.  Change debug messages
+       to regular diagnostics.
+       * dirmngr_ldap.c (print_ldap_entries): Add extra diagnostics.
+
+2009-04-03  Werner Koch  <wk@g10code.com>
+
+       * dirmngr.h (struct server_local_s): Move back to ...
+       * server.c (struct server_local_s): ... here.
+       (get_ldapservers_from_ctrl): New.
+       * ldapserver.h (ldapserver_iter_begin): Use it.
+
+2008-10-29  Marcus Brinkmann  <marcus@g10code.de>
+
+       * estream.c (es_getline): Add explicit cast to silence gcc -W
+       warning.
+       * crlcache.c (finish_sig_check): Likewise.
+
+       * dirmngr.c (opts): Add missing initializer to silence gcc
+       -W warning.
+       * server.c (register_commands): Likewise.
+       * dirmngr-client.c (opts): Likewise.
+       * dirmngr_ldap.c (opts): Likewise.
+
+       * dirmngr-client.c (status_cb, inq_cert, data_cb): Change return
+       type to gpg_error_t to silence gcc warning.
+
+2008-10-21  Werner Koch  <wk@g10code.com>
+
+       * certcache.c (load_certs_from_dir): Accept ".der" files.
+
+       * server.c (get_istrusted_from_client): New.
+       * validate.c (validate_cert_chain): Add new optional arg
+       R_TRUST_ANCHOR.  Adjust all callers
+       * crlcache.c (crl_cache_entry_s): Add fields USER_TRUST_REQ
+       and CHECK_TRUST_ANCHOR.
+       (release_one_cache_entry): Release CHECK_TRUST_ANCHOR.
+       (list_one_crl_entry): Print info about the new fields.
+       (open_dir, write_dir_line_crl): Support the new U-flag.
+       (crl_parse_insert): Add arg R_TRUST_ANCHOR and set it accordingly.
+       (crl_cache_insert): Store trust anchor in entry object.
+       (cache_isvalid): Ask client for trust is needed.
+
+       * crlcache.c (open_dir): Replace xcalloc by xtrycalloc.
+       (next_line_from_file): Ditt.  Add arg to return the gpg error.
+       Change all callers.
+       (update_dir): Replace sprintf and malloc by estream_asprintf.
+       (crl_cache_insert): Ditto.
+       (crl_cache_isvalid): Replace xmalloc by xtrymalloc.
+       (get_auth_key_id): Ditto.
+       (crl_cache_insert): Ditto.
+
+       * crlcache.c (start_sig_check): Remove HAVE_GCRY_MD_DEBUG test.
+       * validate.c (check_cert_sig): Ditto.  Remove workaround for bug
+       in libgcrypt 1.2.
+
+       * estream.c, estream.h, estream-printf.c, estream-printf.h: Update
+       from current libestream (svn rev 61).
+
+2008-09-30  Marcus Brinkmann  <marcus@g10code.com>
+
+       * get-path.c (get_dirmngr_ldap_path): Revert last change.
+       Instead, use dirmngr_libexecdir().
+       (find_program_at_standard_place): Don't define for now.
+
+2008-09-30  Marcus Brinkmann  <marcus@g10code.com>
+
+       * get-path.c (dirmngr_cachedir): Make COMP a pointer to const to
+       silence gcc warning.
+       (get_dirmngr_ldap_path): Look for dirmngr_ldap in the installation
+       directory.
+
+2008-08-06  Marcus Brinkmann  <marcus@g10code.com>
+
+       * dirmngr.c (main): Mark the ldapserverlist-file option as
+       read-only.
+
+2008-07-31  Werner Koch  <wk@g10code.com>
+
+       * crlcache.c (start_sig_check) [!HAVE_GCRY_MD_DEBUG]: Use
+       gcry_md_start_debug
+
+2008-06-16  Werner Koch  <wk@g10code.com>
+
+       * get-path.c (w32_commondir): New.
+       (dirmngr_sysconfdir): Use it here.
+       (dirmngr_datadir): Ditto.
+
+2008-06-12  Marcus Brinkmann  <marcus@g10code.de>
+
+       * Makefile.am (dirmngr_SOURCES): Add ldapserver.h and ldapserver.c.
+       * ldapserver.h, ldapserver.c: New files.
+       * ldap.c: Include "ldapserver.h".
+       (url_fetch_ldap): Use iterator to get session servers as well.
+       (attr_fetch_ldap, start_default_fetch_ldap): Likewise.
+       * dirmngr.c: Include "ldapserver.h".
+       (free_ldapservers_list): Removed.  Change callers to
+       ldapserver_list_free.
+       (parse_ldapserver_file): Use ldapserver_parse_one.
+       * server.c: Include "ldapserver.h".
+       (cmd_ldapserver): New command.
+       (register_commands): Add new command LDAPSERVER.
+       (reset_notify): New function.
+       (start_command_handler): Register reset notify handler.
+       Deallocate session server list.
+       (lookup_cert_by_pattern): Use iterator to get session servers as well.
+       (struct server_local_s): Move to ...
+       * dirmngr.h (struct server_local_s): ... here.  Add new member
+       ldapservers.
+
+2008-06-10  Werner Koch  <wk@g10code.com>
+
+       Support PEM encoded CRLs.  Fixes bug#927.
+
+       * crlfetch.c (struct reader_cb_context_s): New.
+       (struct file_reader_map_s): Replace FP by new context.
+       (register_file_reader, get_file_reader): Adjust accordingly.
+       (my_es_read): Detect Base64 encoded CRL and decode if needed.
+       (crl_fetch): Pass new context to the callback.
+       (crl_close_reader): Cleanup the new context.
+       * b64dec.c: New.  Taken from GnuPG.
+       * util.h (struct b64state): Add new fields STOP_SEEN and
+       INVALID_ENCODING.
+
+2008-05-26  Marcus Brinkmann  <marcus@g10code.com>
+
+       * dirmngr.c (main) [HAVE_W32_SYSTEM]: Switch to system
+       configuration on gpgconf related commands, and make all options
+       unchangeable.
+
+2008-03-25  Marcus Brinkmann  <marcus@g10code.de>
+
+       * dirmngr_ldap.c (print_ldap_entries): Add code alternative for
+       W32 console stdout (unused at this point).
+
+2008-03-21  Marcus Brinkmann  <marcus@g10code.de>
+
+       * estream.c (ESTREAM_MUTEX_DESTROY): New macro.
+       (es_create, es_destroy): Use it.
+
+2008-02-21  Werner Koch  <wk@g10code.com>
+
+       * validate.c (check_cert_sig) [HAVE_GCRY_MD_DEBUG]: Use new debug
+       function if available.
+
+       * crlcache.c (abort_sig_check): Mark unused arg.
+
+       * exechelp.c (dirmngr_release_process) [!W32]: Mark unsed arg.
+
+       * validate.c (is_root_cert): New.  Taken from GnuPG.
+       (validate_cert_chain): Use it in place of the simple DN compare.
+
+2008-02-15  Marcus Brinkmann  <marcus@g10code.de>
+
+       * dirmngr.c (main): Reinitialize assuan log stream if necessary.
+
+       * crlcache.c (update_dir) [HAVE_W32_SYSTEM]: Remove destination
+       file before rename.
+       (crl_cache_insert) [HAVE_W32_SYSTEM]: Remove destination file
+       before rename.
+
+2008-02-14  Marcus Brinkmann  <marcus@g10code.de>
+
+       * validate.c (check_cert_policy): Use ksba_free instead of xfree.
+       (validate_cert_chain): Likewise.  Free SUBJECT on error.
+       (cert_usage_p): Likewise.
+
+       * crlcache.c (finish_sig_check): Undo last change.
+       (finish_sig_check): Close md.
+       (abort_sig_check): New function.
+       (crl_parse_insert): Use abort_sig_check to clean up.
+
+       * crlcache.c (crl_cache_insert): Clean up CDB on error.
+
+2008-02-13  Marcus Brinkmann  <marcus@g10code.de>
+
+       * crlcache.c (finish_sig_check): Call gcry_md_stop_debug.
+       * exechelp.h (dirmngr_release_process): New prototype.
+       * exechelp.c (dirmngr_release_process): New function.
+       * ldap.c (ldap_wrapper_thread): Release pid.
+       (destroy_wrapper): Likewise.
+
+       * dirmngr.c (launch_reaper_thread): Destroy tattr.
+       (handle_connections): Likewise.
+
+2008-02-12  Marcus Brinkmann  <marcus@g10code.de>
+
+       * ldap.c (pth_close) [! HAVE_W32_SYSTEM]: New macro.
+       (struct wrapper_context_s): New member log_ev.
+       (destroy_wrapper): Check FDs for != -1 rather than != 0.  Use
+       pth_close instead of close.  Free CTX->log_ev.
+       (ldap_wrapper_thread): Rewritten to use pth_wait instead of
+       select.  Also use pth_read instead of read and pth_close instead
+       of close.
+       (ldap_wrapper): Initialize CTX->log_ev.
+       (reader_callback): Use pth_close instead of close.
+       * exechelp.c (create_inheritable_pipe) [HAVE_W32_SYSTEM]: Removed.
+       (dirmngr_spawn_process) [HAVE_W32_SYSTEM]: Use pth_pipe instead.
+       * dirmngr_ldap.c [HAVE_W32_SYSTEM]: Include <fcntl.h>.
+       (main) [HAVE_W32_SYSTEM]: Set mode of stdout to binary.
+
+2008-02-01  Werner Koch  <wk@g10code.com>
+
+       * ldap.c: Remove all ldap headers as they are unused.
+
+       * dirmngr_ldap.c (LDAP_DEPRECATED): New, to have OpenLDAP use the
+       old standard API.
+
+2008-01-10  Werner Koch  <wk@g10code.com>
+
+       * dirmngr-client.c: New option --local.
+       (do_lookup): Use it.
+
+       * server.c (lookup_cert_by_pattern): Implement local lookup.
+       (return_one_cert): New.
+       * certcache.c (hexsn_to_sexp): New.
+       (classify_pattern, get_certs_bypattern): New.
+
+       * misc.c (unhexify): Allow passing NULL for RESULT.
+       (cert_log_subject): Do not call ksba_free on an unused variable.
+
+2008-01-02  Marcus Brinkmann  <marcus@g10code.de>
+
+       * Makefile.am (dirmngr_LDADD, dirmngr_ldap_LDADD)
+       (dirmngr_client_LDADD): Add $(LIBICONV).  Reported by Michael
+       Nottebrock.
+
+2007-12-11  Werner Koch  <wk@g10code.com>
+
+       * server.c (option_handler): New option audit-events.
+       * dirmngr.h (struct server_control_s): Add member AUDIT_EVENTS.
+
+2007-11-26  Marcus Brinkmann  <marcus@g10code.de>
+
+       * get-path.c (dirmngr_cachedir): Create intermediate directories.
+       (default_socket_name): Use CSIDL_WINDOWS.
+
+2007-11-21  Werner Koch  <wk@g10code.com>
+
+       * server.c (lookup_cert_by_pattern): Add args SINGLE and CACHE_ONLY.
+       (cmd_lookup): Add options --single and --cache-only.
+
+2007-11-16  Werner Koch  <wk@g10code.com>
+
+       * certcache.c (load_certs_from_dir): Also log the subject DN.
+       * misc.c (cert_log_subject): New.
+
+2007-11-14  Werner Koch  <wk@g10code.com>
+
+       * dirmngr-client.c: Replace --lookup-url by --url.
+       (main): Remove extra code for --lookup-url.
+       (do_lookup): Remove LOOKUP_URL arg and use the
+       global option OPT.URL.
+
+       * server.c (has_leading_option): New.
+       (cmd_lookup): Use it.
+
+       * crlfetch.c (fetch_cert_by_url): Use GPG_ERR_INV_CERT_OBJ.
+       (fetch_cert_by_url): Use gpg_error_from_syserror.
+
+2007-11-14  Moritz  <moritz@gnu.org>  (wk)
+
+       * dirmngr-client.c: New command: --lookup-url <URL>.
+       (do_lookup): New parameter: lookup_url.  If TRUE, include "--url"
+       switch in LOOKUP transaction.
+       (enum): New entry: oLookupUrl.
+       (opts): Likewise.
+       (main): Handle oLookupUrl.  New variable: cmd_lookup_url, set
+       during option parsing, pass to do_lookup() and substitute some
+       occurences of "cmd_lookup" with "cmd_lookup OR cmd_lookup_url".
+       * crlfetch.c (fetch_cert_by_url): New function, uses
+       url_fetch_ldap() to create a reader object and libksba functions
+       to read a single cert from that reader.
+       * server.c (lookup_cert_by_url, lookup_cert_by_pattern): New
+       functions.
+       (cmd_lookup): Moved almost complete code ...
+       (lookup_cert_by_pattern): ... here.
+       (cmd_lookup): Support new optional argument: --url.  Depending on
+       the presence of that switch, call lookup_cert_by_url() or
+       lookup_cert_by_pattern().
+       (lookup_cert_by_url): Heavily stripped down version of
+       lookup_cert_by_pattern(), using fetch_cert_by_url.
+
+2007-10-24  Marcus Brinkmann  <marcus@g10code.de>
+
+       * exechelp.c (dirmngr_spawn_process): Fix child handles.
+
+2007-10-05  Marcus Brinkmann  <marcus@g10code.de>
+
+       * dirmngr.h: Include assuan.h.
+       (start_command_handler): Change type of FD to assuan_fd_t.
+       * dirmngr.c: Do not include w32-afunix.h.
+        (socket_nonce): New global variable.
+        (create_server_socket): Use assuan socket wrappers.  Remove W32
+       specific stuff.  Save the server nonce.
+        (check_nonce): New function.
+        (start_connection_thread): Call it.
+        (handle_connections): Change args to assuan_fd_t.
+       * server.c (start_command_handler): Change type of FD to assuan_fd_t.
+
+2007-09-12  Marcus Brinkmann  <marcus@g10code.de>
+
+       * dirmngr.c (main): Percent escape pathnames in --gpgconf-list output.
+
+2007-08-27  Moritz Schulte  <moritz@g10code.com>
+
+       * src/Makefile.am (AM_CPPFLAGS): Define DIRMNGR_SOCKETDIR based on
+       $(localstatedir).
+       * src/get-path.c (default_socket_name): Use DIRMNGR_SOCKETDIR
+       instead of hard-coded "/var/run/dirmngr".
+
+2007-08-16  Werner Koch  <wk@g10code.com>
+
+       * get-path.c (get_dirmngr_ldap_path): Make PATHNAME const.
+
+       * dirmngr.c (my_ksba_hash_buffer): Mark unused arg.
+       (dirmngr_init_default_ctrl): Ditto.
+       (my_gcry_logger): Ditto.
+       * dirmngr-client.c (status_cb): Ditto.
+       * dirmngr_ldap.c (catch_alarm): Ditto.
+       * estream-printf.c (pr_bytes_so_far): Ditto.
+       * estream.c (es_func_fd_create): Ditto.
+       (es_func_fp_create): Ditto.
+       (es_write_hexstring): Ditto.
+       * server.c (cmd_listcrls): Ditto.
+       (cmd_cachecert): Ditto.
+       * crlcache.c (cache_isvalid): Ditto.
+       * ocsp.c (do_ocsp_request): Ditto.
+       * ldap.c (ldap_wrapper_thread): Ditto.
+       * http.c (http_register_tls_callback): Ditto.
+       (connect_server): Ditto.
+       (write_server) [!HTTP_USE_ESTREAM]: Don't build.
+
+2007-08-14  Werner Koch  <wk@g10code.com>
+
+       * get-path.c (dirmngr_cachedir) [W32]: Use CSIDL_LOCAL_APPDATA.
+
+2007-08-13  Werner Koch  <wk@g10code.com>
+
+       * dirmngr.c (handle_connections): Use a timeout in the accept
+       function.  Block signals while creating a new thread.
+       (shutdown_pending): Needs to be volatile as also accessed bt the
+       service function.
+       (w32_service_control): Do not use the regular log fucntions here.
+       (handle_tick): New.
+       (main): With system_service in effect use aDaemon as default
+       command.
+       (main) [W32]: Only temporary redefine main for the sake of Emacs's
+       "C-x 4 a".
+
+       * dirmngr-client.c (main) [W32]: Initialize sockets.
+       (start_dirmngr): Use default_socket_name instead of a constant.
+       * Makefile.am (dirmngr_client_SOURCES): Add get-path.c
+
+2007-08-09  Werner Koch  <wk@g10code.com>
+
+       * dirmngr.c (parse_ocsp_signer): New.
+       (parse_rereadable_options): Set opt.ocsp_signer to this.
+       * dirmngr.h (fingerprint_list_t): New.
+       * ocsp.c (ocsp_isvalid, check_signature, validate_responder_cert):
+       Allow for several default ocscp signers.
+       (ocsp_isvalid): Return GPG_ERR_NO_DATA for an unknwon status.
+
+       * dirmngr-client.c: New option --force-default-responder.
+
+       * server.c (has_option, skip_options): New.
+       (cmd_checkocsp): Add option --force-default-responder.
+       (cmd_isvalid): Ditto.  Also add option --only-ocsp.
+
+       * ocsp.c (ocsp_isvalid): New arg FORCE_DEFAULT_RESPONDER.
+
+       * dirmngr.c: New option --ocsp-max-period.
+       * ocsp.c (ocsp_isvalid): Implement it and take care that a missing
+       next_update is to be ignored.
+
+       * crlfetch.c (my_es_read): New.  Use it instead of es_read.
+
+       * estream.h, estream.c, estream-printf.c: Updated from current
+       libestream SVN.
+
+2007-08-08  Werner Koch  <wk@g10code.com>
+
+       * crlcache.c (crl_parse_insert): Hack to allow for a missing
+       nextUpdate.
+
+       * dirmngr_ldap.c (print_ldap_entries): Strip the extension from
+       the want_attr.
+
+       * exechelp.c (dirmngr_wait_process): Reworked for clear error
+       semantics.
+       * ldap.c (ldap_wrapper_thread): Adjust for new
+       dirmngr_wait_process semantics.
+
+2007-08-07  Werner Koch  <wk@g10code.com>
+
+       * get-path.c (default_socket_name) [!W32]: Fixed syntax error.
+
+       * ldap.c (X509CACERT, make_url, fetch_next_cert_ldap): Support
+       x509caCert as used by the Bundesnetzagentur.
+       (ldap_wrapper): Do not pass the prgtram name as the first
+       argument.  dirmngr_spawn_process takes care of that.
+
+2007-08-04  Marcus Brinkmann  <marcus@g10code.de>
+
+       * dirmngr.h (opt): Add member system_service.
+       * dirmngr.c (opts) [HAVE_W32_SYSTEM]: New entry for option
+       --service.
+       (DEFAULT_SOCKET_NAME): Removed.
+       (service_handle, service_status,
+       w32_service_control) [HAVE_W32_SYSTEM]: New symbols.
+       (main) [HAVE_W32_SYSTEM]: New entry point for --service.  Rename
+       old function to ...
+       (real_main) [HAVE_W32_SYSTEM]: ... this.  Use default_socket_name
+       instead of DEFAULT_SOCKET_NAME, and similar for other paths.
+       Allow colons in Windows socket path name, and implement --service
+       option.
+       * util.h (dirmngr_sysconfdir, dirmngr_libexecdir, dirmngr_datadir,
+       dirmngr_cachedir, default_socket_name): New prototypes.
+       * get-path.c (dirmngr_sysconfdir, dirmngr_libexecdir)
+       (dirmngr_datadir, dirmngr_cachedir, default_socket_name): New
+       functions.
+       (DIRSEP_C, DIRSEP_S): New macros.
+
+2007-08-03  Marcus Brinkmann  <marcus@g10code.de>
+
+       * get-path.c: Really add the file this time.
+
+2007-07-31  Marcus Brinkmann  <marcus@g10code.de>
+
+       * crlfetch.c: Include "estream.h".
+       (crl_fetch): Use es_read callback instead a file handle.
+       (crl_close_reader): Use es_fclose instead of fclose.
+       (struct file_reader_map_s): Change type of FP to estream_t.
+       (register_file_reader, crl_fetch, crl_close_reader): Likewise.
+       * ocsp.c: Include "estream.h".
+       (read_response): Change type of FP to estream_t.
+       (read_response, do_ocsp_request): Use es_* variants of I/O
+       functions.
+
+       * http.c: Include <pth.h>.
+       (http_wait_response) [HAVE_W32_SYSTEM]: Use DuplicateHandle.
+       (cookie_read): Use pth_read instead read.
+       (cookie_write): Use pth_write instead write.
+
+2007-07-30  Marcus Brinkmann  <marcus@g10code.de>
+
+       * ldap-url.c (ldap_str2charray): Fix buglet in ldap_utf8_strchr
+       invocation.
+
+2007-07-27  Marcus Brinkmann  <marcus@g10code.de>
+
+       * estream.h, estream.c: Update from recent GnuPG.
+
+       * get-path.c: New file.
+       * Makefile.am (dirmngr_SOURCES): Add get-path.c.
+       * util.h (default_homedir, get_dirmngr_ldap_path): New prototypes.
+       * dirmngr.c (main): Use default_homedir().
+       * ldap-url.h: Remove japanese white space (sorry!).
+
+2007-07-26  Marcus Brinkmann  <marcus@g10code.de>
+
+       * ldap.c (pth_yield): Remove macro.
+
+       * ldap.c (pth_yield) [HAVE_W32_SYSTEM]: Define to Sleep(0).
+
+       * dirmngr_ldap.c [HAVE_W32_SYSTEM]: Do not include <ldap.h>, but
+       <winsock2.h>, <winldap.h> and "ldap-url.h".
+       * ldap.c [HAVE_W32_SYSTEM]: Do not include <ldap.h>, but
+       <winsock2.h> and <winldap.h>.
+
+       * ldap-url.c: Do not include <ldap.h>, but <winsock2.h>,
+       <winldap.h> and "ldap-url.h".
+       (LDAP_P): New macro.
+       * ldap-url.h: New file.
+       * Makefile.am (ldap_url): Add ldap-url.h.
+
+       * Makefile.am (ldap_url): New variable.
+       (dirmngr_ldap_SOURCES): Add $(ldap_url).
+       (dirmngr_ldap_LDADD): Add $(LIBOBJS).
+       * ldap-url.c: New file, excerpted from OpenLDAP.
+       * dirmngr.c (main) [HAVE_W32_SYSTEM]: Avoid the daemonization.
+       * dirmngr_ldap.c: Include "util.h".
+       (main) [HAVE_W32_SYSTEM]: Don't set up alarm.
+       (set_timeout) [HAVE_W32_SYSTEM]: Likewise.
+       * ldap.c [HAVE_W32_SYSTEM]: Add macros for setenv and pth_yield.
+       * no-libgcrypt.h (NO_LIBGCRYPT): Define.
+       * util.h [NO_LIBGCRYPT]: Don't include <gcrypt.h>.
+
+2007-07-23  Marcus Brinkmann  <marcus@g10code.de>
+
+       * Makefile.am (dirmngr_SOURCES): Add exechelp.h and exechelp.c.
+       * exechelp.h, exechelp.c: New files.
+       * ldap.c: Don't include <sys/wait.h> but "exechelp.h".
+       (destroy_wrapper, ldap_wrapper_thread,
+       ldap_wrapper_connection_cleanup): Use dirmngr_kill_process instead
+       of kill.
+       (ldap_wrapper_thread): Use dirmngr_wait_process instead of
+       waitpid.
+       (ldap_wrapper): Use dirmngr_spawn_process.
+
+2007-07-20  Marcus Brinkmann  <marcus@g10code.de>
+
+       * certcache.c (cert_cache_lock): Do not initialize statically.
+       (init_cache_lock): New function.
+       (cert_cache_init): Call init_cache_lock.
+
+       * estream.h, estream.c, estream-printf.h, estream-printf.c: New
+       files.
+       * Makefile.am (dirmngr_SOURCES): Add estream.c, estream.h,
+       estream-printf.c, estream-printf.h.
+
+       * http.c: Update to latest version from GnuPG.
+
+       * Makefile.am (cdb_sources)
+       * cdblib.c: Port to windows (backport from tinycdb 0.76).
+
+       * crlcache.c [HAVE_W32_SYSTEM]: Don't include sys/utsname.h.
+       [MKDIR_TAKES_ONE_ARG]: Define mkdir as a macro for such systems.
+       (update_dir, crl_cache_insert) [HAVE_W32_SYSTEM]: Don't get uname.
+       * server.c (start_command_handler) [HAVE_W32_SYSTEM]: Don't log
+       peer credentials.
+
+       * dirmngr.c [HAVE_W32_SYSTEM]: Do not include sys/socket.h or
+       sys/un.h, but ../jnlib/w32-afunix.h.
+       (sleep) [HAVE_W32_SYSTEM]: New macro.
+       (main) [HAVE_W32_SYSTEM]: Don't mess with SIGPIPE.  Use W32 socket
+       API.
+       (handle_signal) [HAVE_W32_SYSTEM]: Deactivate the bunch of the
+       code.
+       (handle_connections) [HAVE_W32_SYSTEM]: don't handle signals.
+
+2006-11-29  Werner Koch  <wk@g10code.com>
+
+       * dirmngr.c (my_strusage): Use macro for the bug report address
+       and the copyright line.
+       * dirmngr-client.c (my_strusage): Ditto.
+       * dirmngr_ldap.c (my_strusage): Ditto.
+
+       * Makefile.am: Do not link against LIBICONV.
+
+2006-11-19  Werner Koch  <wk@g10code.com>
+
+       * dirmngr.c: Include i18n.h.
+
+2006-11-17  Werner Koch  <wk@g10code.com>
+
+       * Makefile.am (dirmngr_LDADD): Use LIBASSUAN_PTH_LIBS.
+
+2006-11-16  Werner Koch  <wk@g10code.com>
+
+       * server.c (start_command_handler): Replaced
+       assuan_init_connected_socket_server by assuan_init_socket_server_ext.
+
+       * crlcache.c (update_dir): Put a diagnostic into DIR.txt.
+       (open_dir): Detect invalid and duplicate entries.
+       (update_dir): Fixed search for second field.
+
+2006-10-23  Werner Koch  <wk@g10code.com>
+
+       * dirmngr.c (main): New command --gpgconf-test.
+
+2006-09-14  Werner Koch  <wk@g10code.com>
+
+       * server.c (start_command_handler): In vebose mode print
+       information about the peer.  This may later be used to restrict
+       certain commands.
+
+2006-09-12  Werner Koch  <wk@g10code.com>
+
+       * server.c (start_command_handler): Print a more informative hello
+       line.
+       * dirmngr.c: Moved config_filename into the opt struct.
+
+2006-09-11  Werner Koch  <wk@g10code.com>
+
+       Changed everything to use Assuan with gpg-error codes.
+       * maperror.c: Removed.
+       * server.c (map_to_assuan_status): Removed.
+       * dirmngr.c (main): Set assuan error source.
+       * dirmngr-client.c (main): Ditto.
+
+2006-09-04  Werner Koch  <wk@g10code.com>
+
+       * crlfetch.c (crl_fetch): Implement HTTP redirection.
+       * ocsp.c (do_ocsp_request): Ditto.
+
+       New HTTP code version taken from gnupg svn release 4236.
+       * http.c (http_get_header): New.
+       (capitalize_header_name, store_header): New.
+       (parse_response): Store headers away.
+       (send_request): Return GPG_ERR_NOT_FOUND if connect_server failed.
+       * http.h: New flag HTTP_FLAG_NEED_HEADER.
+
+2006-09-01  Werner Koch  <wk@g10code.com>
+
+       * crlfetch.c (register_file_reader, get_file_reader): New.
+       (crl_fetch): Register the file pointer for HTTP.
+       (crl_close_reader): And release it.
+
+       * http.c, http.h: Updated from GnuPG SVN trunk.  Changed all users
+       to adopt the new API.
+       * dirmngr.h: Moved inclusion of jnlib header to ...
+       * util.h: .. here.  This is required becuase http.c includes only
+       a file util.h but makes use of log_foo. Include gcrypt.h so that
+       gcry_malloc et al are declared.
+
+2006-08-31  Werner Koch  <wk@g10code.com>
+
+       * ocsp.c (check_signature): Make use of the responder id.
+
+2006-08-30  Werner Koch  <wk@g10code.com>
+
+       * validate.c (check_cert_sig): Workaround for rimemd160.
+       (allowed_ca): Always allow trusted CAs.
+
+       * dirmngr.h (cert_ref_t): New.
+       (struct server_control_s): Add field OCSP_CERTS.
+       * server.c (start_command_handler): Release new field
+       * ocsp.c (release_ctrl_ocsp_certs): New.
+       (check_signature): Store certificates in OCSP_CERTS.
+
+       * certcache.c (find_issuing_cert): Reset error if cert was found
+       by subject.
+       (put_cert): Add new arg FPR_BUFFER.  Changed callers.
+       (cache_cert_silent): New.
+
+       * dirmngr.c (parse_rereadable_options): New options
+       --ocsp-max-clock-skew and --ocsp-current-period.
+       * ocsp.c (ocsp_isvalid): Use them here.
+
+       * ocsp.c (validate_responder_cert): New optional arg signer_cert.
+       (check_signature_core): Ditto.
+       (check_signature): Use the default signer certificate here.
+
+2006-06-27  Werner Koch  <wk@g10code.com>
+
+       * dirmngr-client.c (inq_cert): Take care of SENDCERT_SKI.
+
+2006-06-26  Werner Koch  <wk@g10code.com>
+
+       * crlcache.c (lock_db_file): Count open files when needed.
+       (find_entry): Fixed deleted case.
+
+2006-06-23  Werner Koch  <wk@g10code.com>
+
+       * misc.c (cert_log_name): New.
+
+       * certcache.c (load_certs_from_dir): Also print certificate name.
+       (find_cert_bysn): Release ISSDN.
+
+       * validate.h: New VALIDATE_MODE_CERT.
+       * server.c (cmd_validate): Use it here so that no policy checks
+       are done.  Try to validated a cached copy of the target.
+
+       * validate.c (validate_cert_chain): Implement a validation cache.
+       (check_revocations): Print more diagnostics.  Actually use the
+       loop variable and not the head of the list.
+       (validate_cert_chain): Do not check revocations of CRL issuer
+       certificates in plain CRL check mode.
+       * ocsp.c (ocsp_isvalid): Make sure it is reset for a status of
+       revoked.
+
+2006-06-22  Werner Koch  <wk@g10code.com>
+
+       * validate.c (cert_use_crl_p): New.
+       (cert_usage_p): Add a mode 6 for CRL signing.
+       (validate_cert_chain): Check that the certificate may be used for
+       CRL signing.  Print a note when not running as system daemon.
+       (validate_cert_chain): Reduce the maximum depth from 50 to 10.
+
+       * certcache.c (find_cert_bysn): Minor restructuring
+       (find_cert_bysubject): Ditto.  Use get_cert_local when called
+       without KEYID.
+       * crlcache.c (get_crlissuer_cert_bysn): Removed.
+       (get_crlissuer_cert): Removed.
+       (crl_parse_insert): Use find_cert_bysubject and find_cert_bysn
+       instead of the removed functions.
+
+2006-06-19  Werner Koch  <wk@g10code.com>
+
+       * certcache.c (compare_serialno): Silly me. Using 0 as true is
+       that hard; tsss. Fixed call cases except for the only working one
+       which are both numbers of the same length.
+
+2006-05-15  Werner Koch  <wk@g10code.com>
+
+       * crlfetch.c (crl_fetch): Use no-shutdown flag for HTTP.  This
+       seems to be required for "IBM_HTTP_Server/2.0.47.1 Apache/2.0.47
+       (Unix)".
+
+       * http.c (parse_tuple): Set flag to to indicate no value.
+       (build_rel_path): Take care of it.
+
+       * crlcache.c (crl_cache_reload_crl): Also iterate over all names
+       within a DP.
+
+2005-09-28  Marcus Brinkmann  <marcus@g10code.de>
+
+       * Makefile.am (dirmngr_LDADD): Add @LIBINTL@ and @LIBICONV@.
+       (dirmngr_ldap_LDADD): Likewise.
+       (dirmngr_client_LDADD): Likewise.
+
+2005-09-12  Werner Koch  <wk@g10code.com>
+
+       * dirmngr.c: Fixed description to match the one in gpgconf.
+
+2005-06-15  Werner Koch  <wk@g10code.com>
+
+       * server.c (cmd_lookup): Take care of NO_DATA which might get
+       returned also by start_cert_fetch().
+
+2005-04-20  Werner Koch  <wk@g10code.com>
+
+       * ldap.c (ldap_wrapper_wait_connections): Set a shutdown flag.
+       (ldap_wrapper_thread): Handle shutdown in a special way.
+
+2005-04-19  Werner Koch  <wk@g10code.com>
+
+       * server.c (get_cert_local, get_issuing_cert_local)
+       (get_cert_local_ski): Bail out if called without a local context.
+
+2005-04-18  Werner Koch  <wk@g10code.com>
+
+       * certcache.c (find_issuing_cert): Fixed last resort method which
+       should be finding by subject and not by issuer. Try to locate it
+       also using the keyIdentifier method.  Improve error reporting.
+       (cmp_simple_canon_sexp): New.
+       (find_cert_bysubject): New.
+       (find_cert_bysn): Ask back to the caller before trying an extarnl
+       lookup.
+       * server.c (get_cert_local_ski): New.
+       * crlcache.c (crl_parse_insert): Also try to locate issuer
+       certificate using the keyIdentifier.  Improved error reporting.
+
+2005-04-14  Werner Koch  <wk@g10code.com>
+
+       * ldap.c (start_cert_fetch_ldap): Really return ERR.
+
+2005-03-17  Werner Koch  <wk@g10code.com>
+
+       * http.c (parse_response): Changed MAXLEN and LEN to size_t to
+       match the requirement of read_line.
+       * http.h (http_context_s): Ditto for BUFFER_SIZE.
+
+2005-03-15  Werner Koch  <wk@g10code.com>
+
+       * ldap.c: Included time.h.  Reported by Bernhard Herzog.
+
+2005-03-09  Werner Koch  <wk@g10code.com>
+
+       * dirmngr.c: Add a note to the help listing check the man page for
+       other options.
+
+2005-02-01  Werner Koch  <wk@g10code.com>
+
+       * crlcache.c (crl_parse_insert): Renamed a few variables and
+       changed diagnostic strings for clarity.
+       (get_issuer_cert): Renamed to get_crlissuer_cert. Try to locate
+       the certificate from the cache using the subject name.  Use new
+       fetch function.
+       (get_crlissuer_cert_bysn): New.
+       (crl_parse_insert): Use it here.
+       * crlfetch.c (ca_cert_fetch): Changed interface.
+       (fetch_next_ksba_cert): New.
+       * ldap.c (run_ldap_wrapper): Add arg MULTI_MODE.  Changed all
+       callers.
+       (start_default_fetch_ldap): New
+       * certcache.c (get_cert_bysubject): New.
+       (clean_cache_slot, put_cert): Store the subject DN if available.
+       (MAX_EXTRA_CACHED_CERTS): Increase limit of cachable certificates
+       to 1000.
+       (find_cert_bysn): Loop until a certificate with a matching S/N has
+       been found.
+
+       * dirmngr.c (main): Add honor-http-proxy to the gpgconf list.
+
+2005-01-31  Werner Koch  <wk@g10code.com>
+
+       * ldap.c: Started to work on support for userSMIMECertificates.
+
+       * dirmngr.c (main): Make sure to always pass a server control
+       structure to the caching functions.  Reported by Neil Dunbar.
+
+2005-01-05  Werner Koch  <wk@g10code.com>
+
+       * dirmngr-client.c (read_pem_certificate): Skip trailing percent
+       escaped linefeeds.
+
+2005-01-03  Werner Koch  <wk@g10code.com>
+
+       * dirmngr-client.c (read_pem_certificate): New.
+       (read_certificate): Divert to it depending on pem option.
+       (squid_loop_body): New.
+       (main): New options --pem and --squid-mode.
+
+2004-12-17  Werner Koch  <wk@g10code.com>
+
+       * dirmngr.c (launch_ripper_thread): Renamed to launch_reaper_thread.
+       (shutdown_reaper): New.  Use it for --server and --daemon.
+       * ldap.c (ldap_wrapper_wait_connections): New.
+
+2004-12-17  Werner Koch  <wk@g10code.com>
+
+       * Makefile.am (dirmngr_ldap_LDADD): Adjusted for new LDAP checks.
+
+2004-12-16  Werner Koch  <wk@g10code.com>
+
+       * ldap.c (ldap_wrapper): Peek on the output to detect empty output
+       early.
+
+2004-12-15  Werner Koch  <wk@g10code.com>
+
+       * ldap.c (ldap_wrapper): Print a diagnostic after forking for the
+       ldap wrapper.
+       * certcache.h (find_cert_bysn): Add this prototype.
+       * crlcache.c (start_sig_check): Write CRL hash debug file.
+       (finish_sig_check): Dump the signer's certificate.
+       (crl_parse_insert): Try to get the issuing cert by authKeyId.
+       Moved certificate retrieval after item processing.
+
+2004-12-13  Werner Koch  <wk@g10code.com>
+
+       * dirmngr_ldap.c (catch_alarm, set_timeout): new.
+       (main): Install alarm handler. Add new option --only-search-timeout.
+       (print_ldap_entries, fetch_ldap): Use set_timeout ();
+       * dirmngr.h: Make LDAPTIMEOUT a simple unsigned int.  Change all
+       initializations.
+       * ldap.c (start_cert_fetch_ldap, run_ldap_wrapper): Pass timeout
+       option to the wrapper.
+       (INACTIVITY_TIMEOUT): Depend on LDAPTIMEOUT.
+       (run_ldap_wrapper): Add arg IGNORE_TIMEOUT.
+       (ldap_wrapper_thread): Check for special timeout exit code.
+
+       * dirmngr.c: Workaround a typo in gpgconf for
+       ignore-ocsp-service-url.
+
+2004-12-10  Werner Koch  <wk@g10code.com>
+
+       * ldap.c (url_fetch_ldap): Use TMP and not a HOST which is always
+       NULL.
+       * misc.c (host_and_port_from_url): Fixed bad encoding detection.
+
+2004-12-03  Werner Koch  <wk@g10code.com>
+
+       * crlcache.c (crl_cache_load): Re-implement it.
+
+       * dirmngr-client.c: New command --load-crl
+       (do_loadcrl): New.
+
+       * dirmngr.c (parse_rereadable_options, main): Make --allow-ocsp,
+       --ocsp-responder, --ocsp-signer and --max-replies re-readable.
+
+       * ocsp.c (check_signature): try to get the cert from the cache
+       first.
+       (ocsp_isvalid): Print the next and this update times on time
+       conflict.
+
+       * certcache.c (load_certs_from_dir): Print the fingerprint for
+       trusted certificates.
+       (get_cert_byhexfpr): New.
+       * misc.c (get_fingerprint_hexstring_colon): New.
+
+2004-12-01  Werner Koch  <wk@g10code.com>
+
+       * Makefile.am (dirmngr_LDADD): Don't use LDAP_LIBS.
+
+       * validate.c (validate_cert_chain): Fixed test; as written in the
+       comment we want to do this only in daemon mode.  For clarity
+       reworked by using a linked list of certificates and include root
+       and tragte certificate.
+       (check_revocations): Likewise.  Introduced a recursion sentinel.
+
+2004-11-30  Werner Koch  <wk@g10code.com>
+
+       * crlfetch.c (ca_cert_fetch, crl_fetch_default): Do not use the
+       binary prefix as this will be handled in the driver.
+
+       * dirmngr_ldap.c: New option --log-with-pid.
+       (fetch_ldap): Handle LDAP_NO_SUCH_OBJECT.
+       * ldap.c (run_ldap_wrapper, start_cert_fetch_ldap): Use new log
+       option.
+
+
+2004-11-25  Werner Koch  <wk@g10code.com>
+
+       * Makefile.am (dirmngr_ldap_CFLAGS): Added GPG_ERROR_CFLAGS.
+       Noted by Bernhard Herzog.
+
+2004-11-24  Werner Koch  <wk@g10code.com>
+
+       * ldap.c (ldap_wrapper): Fixed default name of the ldap wrapper.
+
+       * b64enc.c (b64enc_start, b64enc_finish): Use standard strdup/free
+       to manage memory.
+
+       * dirmngr.c: New options --ignore-http-dp, --ignore-ldap-dp and
+       --ignore-ocsp-service-url.
+       * crlcache.c (crl_cache_reload_crl): Implement them.
+       * ocsp.c (ocsp_isvalid): Ditto.
+
+2004-11-23  Werner Koch  <wk@g10code.com>
+
+       * ldap.c (ldap_wrapper_thread, reader_callback, ldap_wrapper):
+       Keep a timestamp and terminate the wrapper after some time of
+       inactivity.
+
+       * dirmngr-client.c (do_lookup): New.
+       (main): New option --lookup.
+       (data_cb): New.
+       * b64enc.c: New. Taken from GnuPG 1.9.
+       * no-libgcrypt.c (gcry_strdup): Added.
+
+       * ocsp.c (ocsp_isvalid): New arg CERT and lookup the issuer
+       certificate using the standard methods.
+
+       * server.c (cmd_lookup): Truncation is now also an indication for
+       error.
+       (cmd_checkocsp): Implemented.
+
+       * dirmngr_ldap.c (fetch_ldap): Write an error marker for a
+       truncated search.
+       * ldap.c (add_server_to_servers): Reactivated.
+       (url_fetch_ldap): Call it here and try all configured servers in
+       case of a a failed lookup.
+       (fetch_next_cert_ldap): Detect the truncation error flag.
+       * misc.c (host_and_port_from_url, remove_percent_escapes): New.
+
+2004-11-22  Werner Koch  <wk@g10code.com>
+
+       * dirmngr_ldap.c (main): New option --proxy.
+       * ocsp.c (do_ocsp_request): Take care of opt.disable_http.
+       * crlfetch.c (crl_fetch): Honor the --honor-http-proxy variable.
+       (crl_fetch): Take care of  opt.disable_http and disable_ldap.
+       (crl_fetch_default, ca_cert_fetch, start_cert_fetch):
+       * ldap.c (run_ldap_wrapper): New arg PROXY.
+       (url_fetch_ldap, attr_fetch_ldap, start_cert_fetch_ldap): Pass it.
+
+       * http.c (http_open_document): Add arg PROXY.
+       (http_open): Ditto.
+       (send_request): Ditto and implement it as an override.
+
+       * ocsp.c (validate_responder_cert): Use validate_cert_chain.
+
+       * Makefile.am (AM_CPPFLAGS): Add macros for a few system
+       directories.
+       * dirmngr.h (opt): New members homedir_data, homedir_cache,
+       ldap_wrapper_program, system_daemon, honor_http_proxy, http_proxy,
+       ldap_proxy, only_ldap_proxy, disable_ldap, disable_http.
+       * dirmngr.c (main): Initialize new opt members HOMEDIR_DATA and
+       HOMEDIR_CACHE.
+       (parse_rereadable_options): New options --ldap-wrapper-program,
+       --http-wrapper-program, --disable-ldap, --disable-http,
+       --honor-http-proxy, --http-proxy, --ldap-proxy, --only-ldap-proxy.
+       (reread_configuration): New.
+
+       * ldap.c (ldap_wrapper): Use the correct name for the wrapper.
+
+       * crlcache.c (DBDIR_D): Make it depend on opt.SYSTEM_DAEMON.
+       (cleanup_cache_dir, open_dir, update_dir, make_db_file_name)
+       (crl_cache_insert, create_directory_if_needed): Use opt.HOMEDIR_CACHE
+
+       * validate.c (check_revocations): New.
+       * crlcache.c (crl_cache_isvalid): Factored most code out to
+       (cache_isvalid): .. new.
+       (crl_cache_cert_isvalid): New.
+       * server.c (cmd_checkcrl): Cleaned up by using this new function.
+       (reload_crl): Moved to ..
+       * crlcache.c (crl_cache_reload_crl): .. here and made global.
+
+       * certcache.c (cert_compute_fpr): Renamed from computer_fpr and
+       made global.
+       (find_cert_bysn): Try to lookup missing certs.
+       (cert_cache_init): Intialize using opt.HOMEDIR_DATA.
+
+
+2004-11-19  Werner Koch  <wk@g10code.com>
+
+       * dirmngr-client.c (status_cb): New.  Use it in very verbose mode.
+
+       * server.c (start_command_handler): Malloc the control structure
+       and properly release it.  Removed the primary_connection
+       hack. Cleanup running wrappers.
+       (dirmngr_status): Return an error code.
+       (dirmngr_tick): Return an error code and detect a
+       cancellation. Use wall time and not CPU time.
+       * validate.c (validate_cert_chain): Add CTRL arg and changed callers.
+       * crlcache.c (crl_cache_isvalid):
+       * crlfetch.c (ca_cert_fetch, start_cert_fetch, crl_fetch_default)
+       (crl_fetch): Ditto.
+       * ldap.c (ldap_wrapper, run_ldap_wrapper, url_fetch_ldap)
+       (attr_fetch_ldap, start_cert_fetch_ldap): Ditto.
+       (ldap_wrapper_release_context): Reset the stored CTRL.
+       (reader_callback): Periodically call dirmngr_tick.
+       (ldap_wrapper_release_context): Print an error message for read
+       errors.
+       (ldap_wrapper_connection_cleanup): New.
+
+2004-11-18  Werner Koch  <wk@g10code.com>
+
+       * dirmngr.c (main): Do not cd / if not running detached.
+
+       * dirmngr-client.c: New options --cache-cert and --validate.
+       (do_cache, do_validate): New.
+       * server.c (cmd_cachecert, cmd_validate): New.
+
+       * crlcache.c (get_issuer_cert): Make use of the certificate cache.
+       (crl_parse_insert): Validate the issuer certificate.
+
+       * dirmngr.c (handle_signal): Reinitialize the certificate cache on
+       a HUP.
+       (struct opts): Add --homedir to enable the already implemented code.
+       (handle_signal): Print stats on SIGUSR1.
+
+       * certcache.c (clean_cache_slot, cert_cache_init)
+       (cert_cache_deinit): New.
+       (acquire_cache_read_lock, acquire_cache_write_lock)
+       (release_cache_lock): New.  Use them where needed.
+       (put_cert): Renamed from put_loaded_cert.
+       (cache_cert): New.
+       (cert_cache_print_stats): New.
+       (compare_serialno): Fixed.
+
+2004-11-16  Werner Koch  <wk@g10code.com>
+
+       * Makefile.am (AM_CPPFLAGS): Define DIRMNGR_SYSCONFDIR and
+       DIRMNGR_LIBEXECDIR.
+
+       * misc.c (dump_isotime, dump_string, dump_cert): New.  Taken from
+       gnupg 1.9.
+       (dump_serial): New.
+
+2004-11-15  Werner Koch  <wk@g10code.com>
+
+       * validate.c: New. Based on gnupg's certchain.c
+
+       * ldap.c (get_cert_ldap): Removed.
+       (read_buffer): New.
+       (start_cert_fetch_ldap, fetch_next_cert_ldap)
+       (end_cert_fetch_ldap): Rewritten to make use of the ldap wrapper.
+
+2004-11-12  Werner Koch  <wk@g10code.com>
+
+       * http.c (insert_escapes): Print the percent sign too.
+
+       * dirmngr-client.c (inq_cert): Ignore "SENDCERT" and
+       "SENDISSUERCERT".
+
+       * server.c (do_get_cert_local): Limit the length of a retruned
+       certificate.  Return NULL without an error if an empry value has
+       been received.
+
+       * crlfetch.c (ca_cert_fetch): Use the ksba_reader_object.
+       (setup_funopen, fun_reader, fun_closer): Removed.
+
+       * crlcache.c (get_issuer_cert): Adjust accordingly.
+
+       * ldap.c (attr_fetch_ldap_internal, attr_fetch_fun_closer)
+       (attr_fetch_fun_reader, url_fetch_ldap_internal)
+       (get_attr_from_result_ldap): Removed.
+       (destroy_wrapper, print_log_line, ldap_wrapper_thread)
+       (ldap_wrapper_release_context, reader_callback, ldap_wrapper)
+       (run_ldap_wrapper): New.
+       (url_fetch_ldap): Make use of the new ldap wrapper and return a
+       ksba reader object instead of a stdio stream.
+       (attr_fetch_ldap): Ditto.
+       (make_url, escape4url): New.
+
+2004-11-11  Werner Koch  <wk@g10code.com>
+
+       * dirmngr.c (launch_ripper_thread): New.
+       (main): Start it wheere appropriate.  Always ignore SIGPIPE.
+       (start_connection_thread): Maintain a connection count.
+       (handle_signal, handle_connections): Use it here instead of the
+       thread count.
+
+       * crlcache.c (crl_cache_insert): Changed to use ksba reader
+       object.  Changed all callers to pass this argument.
+
+2004-11-08  Werner Koch  <wk@g10code.com>
+
+       * dirmngr_ldap.c: New.
+
+       * crlcache.c (crl_cache_init): Don't return a cache object but
+       keep it module local.  We only need one.
+       (crl_cache_deinit): Don't take cache object but work on existing
+       one.
+       (get_current_cache): New.
+       (crl_cache_insert, crl_cache_list, crl_cache_load): Use the global
+       cache object and removed the cache arg.  Changed all callers.
+
+       * dirmngr-client.c: New option --ping.
+
+       * dirmngr.c (main): New option --daemon. Initialize PTH.
+       (handle_connections, start_connection_thread): New.
+       (handle_signal): New.
+       (parse_rereadable_options): New. Changed main to make use of it.
+       (set_debug): Don't bail out on invalid debug levels.
+       (main): Init the crl_chache for server and daemon mode.
+
+       * server.c (start_command_handler): New arg FD.  Changed callers.
+
+2004-11-06  Werner Koch  <wk@g10code.com>
+
+       * server.c (map_assuan_err): Factored out to ..
+       * maperror.c: .. new file.
+       * util.h: Add prototype
+
+2004-11-05  Werner Koch  <wk@g10code.com>
+
+       * no-libgcrypt.c: New, used as helper for dirmngr-client which
+       does not need libgcrypt proper but jnlib references the memory
+       functions.  Taken from gnupg 1.9.12.
+
+       * dirmngr.h: Factored i18n and xmalloc code out to ..
+       * i18n.h, util.h: .. New.
+
+       * dirmngr-client.c: New.  Some code taken from gnupg 1.9.12.
+       * Makefile.am (bin_PROGRAMS) Add dirmngr-client.
+
+2004-11-04  Werner Koch  <wk@g10code.com>
+
+       * src/server.c (get_fingerprint_from_line, cmd_checkcrl)
+       (cmd_checkocsp): New.
+       (register_commands): Register new commands.
+       (inquire_cert_and_load_crl): Factored most code out to ..
+       (reload_crl): .. new function.
+       * src/certcache.h, src/certcache.c: New.
+       * src/Makefile.am (dirmngr_SOURCES): Add new files.
+
+2004-11-04  Werner Koch  <wk@g10code.com>
+
+       Please note that earlier entries are found in the top level
+       ChangeLog.
+       [Update after merge with GnuPG: see ./ChangeLog.1]
+
+
+ Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010 g10 Code GmbH
+
+ This file is free software; as a special exception the author gives
+ unlimited permission to copy and/or distribute it, with or without
+ modifications, as long as this notice is preserved.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/dirmngr/ChangeLog.1 b/dirmngr/ChangeLog.1
new file mode 100644 (file)
index 0000000..6d7a513
--- /dev/null
@@ -0,0 +1,802 @@
+There are old Dirmngr ChangeLog entries.
+
+2004-10-04  Werner Koch  <wk@g10code.com>
+
+       * src/dirmngr.c: Changed an help entry description.
+
+2004-09-30  Werner Koch  <wk@g10code.com>
+
+       * src/dirmngr.c (i18n_init): Always use LC_ALL.
+
+2004-09-28  Werner Koch  <wk@g10code.com>
+
+       Released 0.5.6.
+
+       * config.guess, config.sub: Updated.
+
+2004-06-21  Werner Koch  <wk@g10code.com>
+
+       * src/crlfetch.c (crl_fetch): Bad hack to use the right attribute.
+
+2004-05-13  Werner Koch  <wk@gnupg.org>
+
+        Released 0.5.5.
+
+       * src/ldap.c (start_cert_fetch_ldap, start_cert_fetch_ldap): More
+       detailed error messages.
+
+       * src/crlcache.c (update_dir): Handle i-records properly.
+
+2004-04-29  Werner Koch  <wk@gnupg.org>
+
+       Released 0.5.4.
+
+       * src/crlcache.h (crl_cache_result_t): Add CRL_CACHE_CANTUSE.
+       * src/server.c (cmd_isvalid): Handle it here.
+       * src/crlcache.c (crl_cache_isvalid): Issue this code if the CRL
+       cant be used.
+       (open_dir): Parse new fields 8,9 and 10 as well as the invalid flag.
+       (write_dir_line_crl): Write new fields.
+       (get_crl_number, get_auth_key_id): New.
+       (crl_cache_insert): Fill new fields.  Mark the entry invalid if
+       the CRL is too old after an update or an unknown critical
+       extension was seen.
+       (list_one_crl_entry): Print the new fields.
+
+2004-04-28  Werner Koch  <wk@gnupg.org>
+
+       * configure.ac: Requires libksba 0.9.6.
+
+       * src/dirmngr.c: New option --ocsp-signer.
+       * src/dirmngr.h (opt): Renamed member OCSP_REPONDERS to
+       OCSP_RESPONDER and made ist a simple string. Add OCSP_SIGNER.
+       * src/ocsp.c (ocsp_isvalid): Changed it accordingly.
+       (ocsp_isvalid): Pass the ocsp_signer to check_signature.
+       (check_signature): New arg SIGNER_FPR.  Use it to retrieve the
+       certificate. Factored out common code to ..
+       (check_signature_core): .. New.
+
+2004-04-27  Werner Koch  <wk@gnupg.org>
+
+       * src/server.c (start_command_handler): Keep track of the first
+       connection.
+       (dirmngr_tick): New.
+       * src/ldap.c (attr_fetch_fun_reader): Call it from time to time.
+
+2004-04-23  Werner Koch  <wk@gnupg.org>
+
+       * src/dirmngr.c (main): Removed the add-servers option from the
+       gpgconf list.  It is not really useful.
+
+2004-04-02  Thomas Schwinge  <schwinge@nic-nac-project.de>
+
+       * autogen.sh: Added ACLOCAL_FLAGS.
+
+2004-04-13  Werner Koch  <wk@gnupg.org>
+
+       * src/crlcache.c (update_dir): Do not double close FPOUT.
+
+2004-04-09  Werner Koch  <wk@gnupg.org>
+
+       * src/cdblib.c (cdb_make_start): Wipeout the entire buffer to
+       shutup valgrind.
+       (ewrite): Fixed writing bad data on EINTR.
+
+       * src/ldap.c (get_attr_from_result_ldap): Fixed bad copy and
+       terminate of a string.
+
+       * src/crlfetch.c (crl_fetch): Fixed freeing of VALUE on error.
+
+2004-04-07  Werner Koch  <wk@gnupg.org>
+
+       * src/dirmngr.h (server_control_s): Add member force_crl_refresh.
+       * src/server.c (option_handler): New.
+       (start_command_handler): Register option handler
+       * src/crlcache.c (crl_cache_isvalid): Add arg FORCE_REFRESH.
+       (crl_cache_insert): Record last refresh in memory.
+
+       * src/server.c (inquire_cert_and_load_crl): Renamed from
+       inquire_cert.
+
+2004-04-06  Werner Koch  <wk@gnupg.org>
+
+       Released 0.5.3
+
+       * doc/dirmngr.texi: Updated.
+       * doc/texinfo.tex: Updated.
+
+2004-04-05  Werner Koch  <wk@gnupg.org>
+
+       * src/ocsp.c (ocsp_isvalid): Check THIS_UPDATE.
+
+       * src/misc.c (add_isotime): New.
+       (date2jd, jd2date, days_per_month, days_per_year): New. Taken from
+       my ancient (1988) code used in Wedit (time2.c).
+
+2004-04-02  Werner Koch  <wk@gnupg.org>
+
+       * autogen.sh: Check gettext version.
+       * configure.ac: Add AM_GNU_GETTEXT.
+
+2004-04-02  gettextize  <bug-gnu-gettext@gnu.org>
+
+       * Makefile.am (SUBDIRS): Add intl.
+       (EXTRA_DIST): Add config.rpath.
+       * configure.ac (AC_CONFIG_FILES): Add intl/Makefile,
+
+2004-04-02  Werner Koch  <wk@gnupg.org>
+
+       Add i18n at most places.
+
+       * src/dirmngr.c (i18n_init): New.
+       (main): Call it.
+       * src/dirmngr.h: Add i18n stuff.
+
+2004-04-01  Werner Koch  <wk@gnupg.org>
+
+       * src/misc.c (get_fingerprint_hexstring): New.
+
+       * src/server.c (dirmngr_status): New.
+
+2004-03-26  Werner Koch  <wk@gnupg.org>
+
+       * configure.ac: Add AC_SYS_LARGEFILE.
+
+       * doc/dirmngr.texi: Changed the license to the GPL as per message
+       by Mathhias Kalle Dalheimer of Klaralvdalens-Datakonsult dated
+       Jan 7, 2004.
+       * doc/fdl.texi: Removed.
+
+2004-03-25  Werner Koch  <wk@gnupg.org>
+
+       * src/dirmngr.c (main): New command --fetch-crl.
+
+2004-03-23  Werner Koch  <wk@gnupg.org>
+
+       * src/dirmngr.c: New option --allow-ocsp.
+       * src/server.c (cmd_isvalid): Make use of allow_ocsp.
+
+2004-03-17  Werner Koch  <wk@gnupg.org>
+
+       * src/dirmngr.c (main) <gpgconf>: Fixed default value quoting.
+
+2004-03-16  Werner Koch  <wk@gnupg.org>
+
+       * src/dirmngr.c (main): Add ocsp-responder to the gpgconf list.
+       Add option --debug-level.
+       (set_debug): New.
+
+2004-03-15  Werner Koch  <wk@gnupg.org>
+
+       * src/misc.c (canon_sexp_to_grcy): New.
+
+2004-03-12  Werner Koch  <wk@gnupg.org>
+
+       * src/crlfetch.c (crl_fetch): Hack to substitute http for https.
+
+2004-03-10  Werner Koch  <wk@gnupg.org>
+
+       * src/dirmngr.c (parse_ldapserver_file): Don't skip the entire
+       file on errors.
+
+2004-03-09  Werner Koch  <wk@gnupg.org>
+
+       * src/dirmngr.c (my_ksba_hash_buffer): New.
+       (main): Initialize the internal libksba hashing.
+
+       * src/server.c (get_issuer_cert_local): Renamed to ...
+       (get_cert_local): ... this.  Changed all callers.  Allow NULL for
+       ISSUER to return the current target cert.
+       (get_issuing_cert_local): New.
+       (do_get_cert_local): Moved common code to here.
+
+2004-03-06  Werner Koch  <wk@gnupg.org>
+
+       Released 0.5.2.
+
+       * configure.ac: Fixed last change to check the API version of
+       libgcrypt.
+
+2004-03-05  Werner Koch  <wk@gnupg.org>
+
+       * configure.ac: Also check the SONAME of libgcrypt.
+
+2004-03-03  Werner Koch  <wk@gnupg.org>
+
+       * src/dirmngr.c: New option --ocsp-responder.
+       * src/dirmngr.h (opt): Add member OCSP_RESPONDERS.
+
+2004-02-26  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * src/server.c (start_command_handler): Corrected typo and made
+       dirmngr output it's version in the greeting message.
+
+2004-02-24  Marcus Brinkmann  <marcus@g10code.de>
+
+       * src/dirmngr.c (DEFAULT_ADD_SERVERS): Removed.  If this were
+       true, there'd be no way to disable it.
+       (main): Dump options in new gpgconf format.
+
+2004-02-11  Werner Koch  <wk@gnupg.org>
+
+       * autogen.sh (check_version): Removed bashism and simplified.
+
+2004-02-06  Moritz Schulte  <mo@g10code.com>
+
+       * src/crlfetch.c (crl_fetch_default): Do not dereference VALUE,
+       when checking for non-zero.
+
+2004-02-01  Marcus Brinkmann  <marcus@g10code.de>
+
+       * src/dirmngr.c (DEFAULT_ADD_SERVERS, DEFAULT_MAX_REPLIES)
+       (DEFAULT_LDAP_TIMEOUT): New macros.
+       (main): Use them.
+       (enum cmd_and_opt_values): New command aGPGConfList.
+       (main): Add handler here.
+
+2004-01-17  Werner Koch  <wk@gnupg.org>
+
+       * configure.ac: Added AC_CHECK_FUNCS tests again, because the
+       other test occurrences belong to the jnlib tests block.
+
+2004-01-15  Moritz Schulte  <mo@g10code.com>
+
+       * configure.ac: Fixed funopen replacement mechanism; removed
+       unnecessary AC_CHECK_FUNCS calls.
+
+2004-01-14  Werner Koch  <wk@gnupg.org>
+
+       * src/crlcache.c (list_one_crl_entry): Don't use putchar.
+
+       * src/server.c (cmd_listcrls): New.
+
+2003-12-23  Werner Koch  <wk@gnupg.org>
+
+       Released 0.5.1.
+
+2003-12-17  Werner Koch  <wk@gnupg.org>
+
+       * configure.ac (CFLAGS): Add -Wformat-noliteral in gcc +
+       maintainer mode.
+       (NEED_LIBASSUAN_VERSION): Bump up to 0.6.2.
+
+2003-12-16  Werner Koch  <wk@gnupg.org>
+
+       * configure.ac: Update the tests for jnlib.
+       * src/dirmngr.c (main): Ignore SIGPIPE in server mode.
+
+2003-12-12  Werner Koch  <wk@gnupg.org>
+
+       * src/crlcache.c (hash_dbfile): Also hash version info of the
+       cache file format.
+
+       * src/Makefile.am (dirmngr_SOURCES): Add http.h.
+
+       * configure.ac: Removed checking for DB2. Add checking for mmap.
+       * src/cdb.h, src/cdblib.h: New.  Add a few comments from the
+       original man page and fixed typos.
+       * src/cdblib.c (cdb_findinit, cdb_findnext): Modified to allow
+       walking over all entries.
+       * src/crlcache.h: Removed DB2/4 cruft.
+       (release_one_cache_entry, lock_db_file, crl_parse_insert)
+       (crl_cache_insert, crl_cache_isvalid, list_one_crl_entry): Use the
+       new CDB interface.
+
+       * src/dirmngr.c: Beautified the help messages.
+       (wrong_args): New.
+       (main): new option --force.  Revamped the command handling code.
+       Allow to pass multiple CRLS as well as stdin to --local-crl.
+       * src/crlcache.c (crl_cache_insert): Make --force work.
+
+2003-12-11  Werner Koch  <wk@gnupg.org>
+
+       * src/crlfetch.c (crl_fetch): Enhanced to allow fetching binary
+       data using HTTP.
+       * src/http.c, src/http.h: Replaced by the code from gnupg 1.3 and
+       modified acording to our needs.
+       (read_line): New. Based on the code from GnuPG's iobuf_read_line.
+       * configure.ac: Check for getaddrinfo.
+
+       * src/dirmngr.c (parse_ldapserver_file): Close the stream.
+       (main): Free ldapfile.
+
+       * src/ocsp.c, src/ocsp.h: New. Albeit not functionality.
+
+       * src/server.c (inquire_cert): Catch EOF when reading dist points.
+
+       * src/crlcache.c (hash_dbfile, check_dbfile): New.
+       (lock_db_file, crl_cache_insert): Use them here to detect
+       corrupted CRL files.
+       (open_dir): Read the new dbfile hash field.
+
+       * src/crlfetch.c (crl_fetch, crl_fetch_default): Changed to retrun
+       a stream.
+       (fun_reader, fun_closer, setup_funopen): New.
+       * src/server.c (inquire_cert): Changed to use the new stream interface
+       of crlfetch.c.
+
+2003-12-10  Werner Koch  <wk@gnupg.org>
+
+       * src/funopen.c: New.
+       * configure.ac (funopen): Add test.
+       * src/Makefile.am (dirmngr_LDADD): Add LIBOBJS.
+
+       * src/crlcache.c (next_line_from_file): Remove the limit on the
+       line length.
+       (crl_cache_new): Removed.
+       (open_dbcontent): New.
+       (crl_cache_init): Use it here.
+       (crl_cache_flush): The DB content fie is now in the cache
+       directory, so we can simplify it.
+       (make_db_file_name, lock_db_file, unlock_db_file): New.
+       (release_cache): Close the cached DB files.
+       (crl_cache_isvalid): Make use of the new lock_db_file.
+       (crl_cache_insert): Changed to take a stream as argument.
+       (crl_parse_insert): Rewritten to use a temporary DB and to avoid
+       using up large amounts of memory.
+       (db_entry_new): Removed.
+       (release_cache,release_one_cache_entry): Splitted up.
+       (find_entry): Take care of the new deleted flag.
+       (crl_cache_load): Simplified becuase we can now pass a FP to the
+       insert code.
+       (save_contents): Removed.
+       (update_dir): New.
+       (open_dbcontent_file): Renamed to open_dir_file.
+       (check_dbcontent_version): Renamed to check_dir_version.
+       (open_dbcontent): Renamed to open_dir.
+
+       * src/dirmngr.c: New option --faked-system-time.
+       * src/misc.c (faked_time_p, set_time, get_time): New.  Taken from GnuPG.
+       (check_isotime): New.
+       (unpercent_string): New.
+
+2003-12-09  Werner Koch  <wk@gnupg.org>
+
+       * src/crlcache.h (DBDIR,DBCONTENTFILE): Changed value.
+
+       * autogen.sh: Reworked.
+       * README.CVS: New.
+       * configure.ac: Added min_automake_version.
+
+2003-12-03  Werner Koch  <wk@gnupg.org>
+
+       * src/server.c (cmd_lookup): Send an END line after each
+       certificate.
+
+2003-11-28  Werner Koch  <wk@gnupg.org>
+
+       * src/Makefile.am (dirmngr_LDADD): Remove DB_LIBS
+       because it never got defined and -ldb{2,4} is implictly set
+       by the AC_CHECK_LIB test in configure.
+
+       * src/crlcache.c (mydbopen): DB4 needs an extra parameter; I
+       wonder who ever tested DB4 support.  Add an error statement in
+       case no DB support is configured.
+
+       * tests/Makefile.am: Don't use AM_CPPFLAGS but AM_CFLAGS, replaced
+       variables by configure templates.
+       * src/Makefile.am: Ditto.
+
+2003-11-19  Werner Koch  <wk@gnupg.org>
+
+       * src/crlcache.c (list_one_crl_entry): Define X to nothing for non
+       DB4 systems.  Thanks to Luca M. G. Centamore.
+
+2003-11-17  Werner Koch  <wk@gnupg.org>
+
+       Released 0.5.0
+
+       * src/crlcache.c (crl_cache_new): Fixed eof detection.
+
+       * src/server.c (cmd_loadcrl): Do the unescaping.
+
+       * doc/dirmngr.texi: Added a history section for this modified
+       version.
+
+2003-11-14  Werner Koch  <wk@gnupg.org>
+
+       * tests/asschk.c: New.  Taken from GnuPG.
+       * tests/Makefile.am: Added asschk.
+
+2003-11-13  Werner Koch  <wk@gnupg.org>
+
+       * src/ldap.c (fetch_next_cert_ldap): Get the pattern switching
+       right.
+
+       * tests/test-dirmngr.c: Replaced a couple of deprecated types.
+
+       * configure.ac (GPG_ERR_SOURCE_DEFAULT): Added.
+       (fopencookie, asprintf): Removed unneeded test.
+       (PRINTABLE_OS_NAME): Updated the test from gnupg.
+       (CFLAGS): Do full warnings only in maintainer mode. Add flag
+       --enable gcc-warnings to override it and to enable even more
+       warnings.
+       * acinclude.m4: Removed the libgcrypt test.
+
+       * src/ldap.c (get_attr_from_result_ldap): Simplified the binary
+       hack and return a proper gpg error.
+       (attr_fetch_ldap_internal): Changed error handling.
+       (attr_fetch_ldap): Reworked.  Return configuration error if no
+       servers are configured.
+       (url_fetch_ldap, add_server_to_servers)
+       (url_fetch_ldap_internal): Reworked.
+       (struct cert_fetch_context_s): New to get rid of a global state.
+       (start_cert_fetch_ldap): Allocate context and do a bind with a
+       timeout.  Parse pattern.
+       (end_cert_fetch_ldap): Take context and don't return anything.
+       (find_next_pattern): Removed.
+       (parse_one_pattern): Redone.
+       (get_cert_ldap): Redone.
+       * src/server.c (cmd_lookup): Changed for changed fetch functions.
+
+       * doc/dirmngr.texi: Reworked a bit to get rid of tex errors.
+
+       * configure.ac: Enable makeinfo test.
+
+       * src/crlcache.c (crl_cache_insert): Fixed for latest KSBA API
+       changes.
+       * tests/test-dirmngr.c (main): Ditto.  Also added some more error
+       checking.
+
+2003-11-11  Werner Koch  <wk@gnupg.org>
+
+       * src/cert.c (hashify_data, hexify_data, serial_hex)
+       (serial_to_buffer): Moved all to ...
+       * src/misc.c: .. here.
+       * src/Makefile.am (cert.c, cert.h): Removed.
+       * cert.c, cert.h: Removed.
+
+       * m4/: New.
+       * configure.ac, Makefile.am: Include m4 directory support, updated
+       required library versions.
+
+       * src/cert.c (make_cert): Removed.
+
+       * src/ldap.c (fetch_next_cert_ldap): Return a gpg style error.
+
+       * src/misc.h (copy_time): New.
+       * src/misc.c (get_isotime): New.
+       (iso_string2time, iso_time2string): Removed.
+       (unhexify): New.
+
+       * src/crlcache.h (DBCONTENTSVERSION): Bumbed to 0.6.
+       * src/crlcache.c (finish_sig_check): New.  Factored out from
+       crl_parse_insert and entirely redone.
+       (do_encode_md): Removed.
+       (print_time): Removed
+       (crl_cache_isvalid): Reworked.
+
+2003-11-10  Werner Koch  <wk@gnupg.org>
+
+       * src/crlcache.c (make_db_val, parse_db_val): Removed.
+
+       * src/cert.c (serial_to_buffer): New.
+
+       * src/server.c (get_issuer_cert_local): Rewritten.
+
+       * src/crlcache.c (crl_parse_insert): Rewritten.  Takes now a CTRL
+       instead of the Assuan context. Changed caller accordingly.
+       (get_issuer_cert): Cleaned up.
+
+       * src/crlfetch.c (crl_fetch): Changed VALUE to unsigned char* for
+       documentation reasons.  Make sure that VALUE is released on error.
+       (crl_fetch_default, ca_cert_fetch): Ditto.
+
+       * src/crlcache.c (release_cache): New.
+       (crl_cache_deinit): Use it here.
+       (crl_cache_flush): Redone.
+       (save_contents): Redone.
+       (crl_cache_list, list_one_crl_entry): Print error messages.
+
+2003-11-06  Werner Koch  <wk@gnupg.org>
+
+       * src/crlcache.c (create_directory_if_needed, cleanup_cache_dir):
+       New.  Factored out from crl_cache_new and mostly rewritten.
+       (crl_cache_new): Rewritten.
+       (next_line_from_file): New.
+       (find_entry): Cleaned up.
+       (crl_cache_deinit): Cleaned up.
+
+       * src/dirmngr.c (dirmngr_init_default_ctrl): New stub.
+       * src/dirmngr.h (ctrl_t): New.
+       (DBG_ASSUAN,...): Added the usual debug test macros.
+       * src/server.c: Removed the GET_PTR cruft, replaced it by ctrl_t.
+       Removed the recursion flag.
+       (get_issuer_cert_local): Allow for arbitary large
+       certificates. 4096 is definitely too small.
+       (inquire_cert): Ditto.
+       (start_command_handler): Set a hello line and call the default
+       init function.
+       (cmd_isvalid): Rewritten.
+       (inquire_cert): Removed unused arg LINE. General cleanup.
+       (map_assuan_err,map_to_assuan_status): New.  Taken from gnupg 1.9.
+       (cmd_lookup): Rewritten.
+       (cmd_loadcrl): Started to rewrite it.
+
+2003-10-29  Werner Koch  <wk@gnupg.org>
+
+       * src/dirmngr.c (parse_ldapserver_file): Entirely rewritten.
+       (cleanup): New.
+       (main): Cleaned up.
+
+2003-10-28  Werner Koch  <wk@gnupg.org>
+
+       * src/dirmngr.h: Renamed dirmngr_opt to opt.
+
+       * src/dirmngr.c (parse_ldapserver_file, free_ldapservers_list):
+       Moved with this file.  Cleaned up.  Replaced too deep recursion in
+       the free function.
+
+2003-10-21  Werner Koch  <wk@gnupg.org>
+
+       Changed all occurrences of assuan.h to use use the system provided
+       one.
+       * src/server.c (register_commands): Adjusted for Assuan API change.
+
+2003-08-14  Werner Koch  <wk@gnupg.org>
+
+       * src/Makefile.am: s/LIBKSBA_/KSBA_/. Changed for external Assuan lib.
+       * tests/Makefile.am: Ditto.
+
+       * configure.ac: Partly restructured, add standard checks for
+       required libraries, removed included libassuan.
+       * Makefile.am (SUBDIRS): Removed assuan becuase we now use the
+       libassuan package.
+
+       * src/dirmngr.c (main): Properly initialize Libgcrypt and libksba.
+
+2003-08-13  Werner Koch  <wk@gnupg.org>
+
+       * src/server.c (get_issuer_cert_local): Print error using
+       assuan_strerror.
+
+       * src/crlcache.c (do_encode_md, start_sig_check): Adjust for
+       changed Libgcrypt API.
+
+2003-06-19  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * configure.ac: Upped version to 0.4.7-cvs.
+
+2003-06-19  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * configure.ac: Release 0.4.6.
+
+2003-06-17  Bernhard Reiter <bernhard@intevation.de>
+
+       * src/ldap.c (url_fetch_ldap()):
+         try other default servers when an url with hostname failed
+       * AUTHORS:  added Steffen and Werner
+       * THANKS: Thanked people in the ChangeLog and the Ägypten-Team
+
+
+2003-06-16  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * configure.ac, src/crlcache.h, src/crlcache.c: Added db4 support.
+       * src/Makefile.am, tests/Makefile.am: Removed automake warning.
+       * tests/test-dirmngr.c: Removed a warning.
+
+2003-05-12  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * doc/Makefile.am: Added dirmngr.ops to DISTCLEANFILES.
+       * ChangeLog, doc/ChangeLog, src/ChangeLog: Merged dirmngr ChangeLogs
+       into one toplevel file.
+       * acinclude.m4, configure.ac: Renamed PFX to PATH for consistency.
+
+2003-05-12  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * src/ldap.c: Fixed end-of-certificates-list indication.
+
+2003-05-08  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * src/server.c: Fixed iteration over server list
+
+2003-02-23  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * src/crlcache.h, src/crlcache.c, src/dirmngr.c: Implemented --flush command.
+
+2003-02-07  Marcus Brinkmann  <marcus@g10code.de>
+
+       * configure.ac: Release 0.4.4.
+
+2003-02-05  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * src/ldap.c: Try harder with and without ";binary" in the
+       attribute name when fetching certificates.
+       * src/ldap.c, src/server.c: Support multiple userCertificate attributes
+       per entry.
+
+2003-02-04  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * src/ldap.c: Include the sn attribute in the search filter.
+       Better log messages.
+
+2002-11-20  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * Doc updates (fixes #1373)
+       * Fix for #1419 (crash in free_ldapservers_list())
+       * Fix for #1375. Dirmngr now asks back with an INQUIRE SENDCERT before
+         querying the LDAP servers for an issuer certificate to validate a CRL
+
+2002-11-12  Werner Koch  <wk@gnupg.org>
+
+       * config.sub, config.guess: Updated from ftp.gnu.org/gnu/config
+       to version 2002-11-08.
+
+2002-11-12  Werner Koch  <wk@gnupg.org>
+
+       * dirmngr.c (main) <load_crl_filename>: Better pass NULL instead
+       of an unitialized Assuan context.  Let's hope that the other
+       functions can cope with this.
+
+2002-10-25  Bernhard Reiter <bernhard@intevation.de>
+
+       * src/ldap.c (get_attr_from_result_ldap()):
+        added value extraction retry for CRLs and Certs without ";binary"
+       * changed version number to reflect cvs status to "0.4.3-cvs"
+
+2002-08-21  Werner Koch  <wk@gnupg.org>
+
+       * dirmngr.c (main): Changed default homedir to .gnupg.
+
+2002-08-07  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * Added configure check to examine whether db2 cursor() uses 3 or
+       4 parameters.
+
+2002-07-31  Werner Koch  <wk@gnupg.org>
+
+       * doc/dirmngr.texi: Fixed the structure and added menu entries
+       for the other nodes.
+
+2002-07-30  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * Added doc dir and first steps towards manual.
+
+2002-07-29  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * Got rid of the default server for CRL lookup. We now use the
+       same list of servers that we use for cert. lookup.
+
+2002-07-29  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * New option --add-servers to allow dirmngr to add LDAP servers
+       found in CRL distribution points to the list of servers it
+       searches. NOTE: The added servers are only active in the currently
+       running dirmngr -- the info isn't written to persistens storage.
+
+2002-07-26  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * Default LDAP timeout is 100 seconds now.
+
+       * Use DB2 instead of DB1. Check for libresolv, fixed bug when
+       libldap was found in the default search path.
+
+2002-07-22  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * Implemented --load-crl <filename> option. Also available as
+       LOADCRL assuan command when in server mode.
+
+2002-07-22  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * Implemented new option --ldaptimeout to specify the number of seconds to
+       wait for an LDAP request before timeout.
+
+       * Added --list-crls option to print the contents of the CRL cache
+       * Added some items to the dbcontents file to make printout nicer
+         and updated it's version number
+
+2002-07-02  Werner Koch  <wk@gnupg.org>
+
+       * crlcache.c (crl_parse_insert): Fixed log_debug format string.
+
+2002-07-02  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * configure.ac: Use DB->get() return value correctly.
+
+2002-06-28  Werner Koch  <wk@gnupg.org>
+
+       * crlcache.c (crl_parse_insert): Keep track of newly allocated
+       ENTRY so that we don't free existing errors after a bad signature.
+
+       * dirmngr.h: Include prototype for start_command_handler.
+
+       * crlfetch.c, crlcache.c, http.c, cert.c, ldap.c: Include
+       config.h.
+
+       * crlcache.c (crl_parse_insert): Fixed format type specifiers for
+       time_t variables in log_debug.
+
+       * error.h: Use log_debug instead of dirmngr_debug.  Changed all
+       callers.
+       * Makefile.am (dirmngr_SOURCES): Removed error.c
+
+       * dirmngr.c (main): Register gcrypt malloc functions with ksba so
+       that we don't run into problems by using the wrong free function.
+       The gcrypt malloc function have the additional benefit of a
+       providing allocation sanity checks when compiled with that
+       feature.
+
+       * crlcache.c (get_issuer_cert): Use xfree instead of ksba_free.
+
+
+2002-06-27  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * ldap.c: Look for both userCertificate and caCertificate
+
+2002-06-26  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * configure.ac: Upped version number to 0.3.1
+
+2002-06-25  Werner Koch  <wk@gnupg.org>
+
+       * server.c (cmd_lookup): Use assuan_write_status which ensures a
+       correct syntax.
+
+2002-06-20  Werner Koch  <wk@gnupg.org>
+
+       * crlcache.c (crl_cache_isvalid): Started with some nicer logging.
+       However, this will need a lot more work.
+       (get_issuer_cert): Ditto.
+
+       * dirmngr.c (main): Changed required libgcrypt version and don't
+       print the prefix when using a logfile.
+
+2002-06-20  Werner Koch  <wk@gnupg.org>
+
+       * tests/Makefile.am (TESTS): Removed test-dirmngr because it
+       is not a proper test program.
+       (EXTRA_DIST): Removed the non-existent test certificate.
+
+2002-05-21  Werner Koch  <wk@gnupg.org>
+
+       * server.c (start_command_handler): Enable assuan debugging.
+
+2002-05-08  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * Replaced gdbm check with db1 check
+
+2002-05-08  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * Replaced gdbm with db1, updated file format version
+
+2002-03-01  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * Added gdbm configure check
+
+2002-01-23  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * Return ASSUAN_CRL_Too_Old if the CRL is too old
+
+
+2002-01-17  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       Added commandline options --ldapserver <host> --ldapport <port>
+       --ldapuser <user> --ldappassword <passwd>.
+
+       Cleaned up CRL parsing, signature evaluation a bit, changed
+       datetime format in config file to ISO, added version string to
+       contents format and cache file clean up code in case of mismatch.
+
+2002-01-14  Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+
+       * Use dirmngr_opt.homedir for storing the db. Added Makefile.am to
+       tests, bugfixes.
+
+       * First code.
+         Things that work:
+               Loading/saving database (paths hardcoded)
+               Fetching CRL from hardcoded server, parsing and inserting in database
+               Answer ISVALID xxx.yyy requests
+
+         Things that are missing:
+               Some error-checking/handling
+               Proper autoconf handling of gdbm and OpenLDAP
+               Signature checking downloaded CRLs
+               Answer LOOKUP requests
+               ...
+
+         How to test:
+               cd tests
+               ldapsearch -v -x -h www.trustcenter.de -b '<some-users-DN>' userCertificate -t
+               cp /tmp/<cert-file> testcert.der
+               ./test-dirmngr
diff --git a/dirmngr/Makefile.am b/dirmngr/Makefile.am
new file mode 100644 (file)
index 0000000..b4ea8c6
--- /dev/null
@@ -0,0 +1,65 @@
+# Makefile.am - dirmngr
+# Copyright (C) 2002 Klarälvdalens Datakonsult AB
+# Copyright (C) 2004, 2007, 2010 g10 Code GmbH
+#
+# 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/>.
+
+## Process this file with automake to produce Makefile.in
+
+EXTRA_DIST = OAUTHORS ONEWS ChangeLog.1
+
+bin_PROGRAMS = dirmngr dirmngr-client
+
+libexec_PROGRAMS = dirmngr_ldap
+
+AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/common
+
+include $(top_srcdir)/am/cmacros.am
+
+AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) \
+            $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) $(PTH_CFLAGS)
+
+BUILT_SOURCES = no-libgcrypt.c
+
+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 \
+       b64dec.c cdb.h cdblib.c ldap.c http.c http.h misc.c     \
+       ocsp.c ocsp.h validate.c validate.h
+
+dirmngr_LDADD = $(libcommonpth) $(DNSLIBS) $(LIBASSUAN_LIBS)  \
+       $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(PTH_LIBS) $(LIBINTL) $(LIBICONV)
+
+if HAVE_W32_SYSTEM
+ldap_url = ldap-url.h ldap-url.c
+else
+ldap_url =
+endif
+
+dirmngr_ldap_SOURCES = dirmngr_ldap.c $(ldap_url) no-libgcrypt.c
+dirmngr_ldap_CFLAGS = $(GPG_ERROR_CFLAGS)
+dirmngr_ldap_LDFLAGS =
+dirmngr_ldap_LDADD = $(libcommon) $(DNSLIBS) \
+                    $(GPG_ERROR_LIBS) $(LDAPLIBS) $(LIBINTL) $(LIBICONV)
+
+dirmngr_client_SOURCES = dirmngr-client.c b64enc.c no-libgcrypt.c
+dirmngr_client_LDADD = $(libcommon) $(LIBASSUAN_LIBS) \
+                      $(GPG_ERROR_LIBS) $(LIBINTL) $(LIBICONV)
+
+
+no-libgcrypt.c : $(top_srcdir)/tools/no-libgcrypt.c
+       cat $(top_srcdir)/tools/no-libgcrypt.c > no-libgcrypt.c
diff --git a/dirmngr/OAUTHORS b/dirmngr/OAUTHORS
new file mode 100644 (file)
index 0000000..f9adc32
--- /dev/null
@@ -0,0 +1,40 @@
+The old AUTHORS file from the separate dirmngr package.
+
+ Package: dirmngr
+ Maintainer: Werner Koch <wk@gnupg.org>
+ Bug reports: bug-dirmngr@gnupg.org
+ Security related bug reports: security@gnupg.org
+ License: GPLv2+
+
+
+Steffen Hansen  <steffen@klaralvdalens-datakonsult.se>
+ - Initial code
+
+g10 Code GmbH <code@g10code.com>
+ - All stuff written since October 2003.
+
+Werner Koch     <wk@gnupg.org>, <wk@g10code.com>
+ - Help with initial code.
+
+Free Software Foundation <gnu@gnu.org>
+ - Code taken from GnuPG.
+
+Michael Tokarev <mjt@corpit.ru>
+ - src/cdb.h and src/cdblib.c from the public domain tinycdb 0.73.
+
+
+The actual code is under the GNU GPL, except for src/cdb.h and
+src/cdblib.h which are in the public domain.
+
+
+ Copyright 2003, 2004, 2006, 2007, 2008, 2010 g10 Code GmbH
+
+ This file is free software; as a special exception the author gives
+ unlimited permission to copy and/or distribute it, with or without
+ modifications, as long as this notice is preserved.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+
diff --git a/dirmngr/ONEWS b/dirmngr/ONEWS
new file mode 100644 (file)
index 0000000..a9ec4d7
--- /dev/null
@@ -0,0 +1,240 @@
+These are NEWS entries from the old separate dirmngr package
+
+Noteworthy changes in version 1.1.0 (unreleased)
+------------------------------------------------
+
+ * Fixed a resource problem with LDAP CRLs.
+
+ * Fixed a bad EOF detection with HTTP CRLs.
+
+ * Made "dirmngr-client --url --load-crl URL" work.
+
+ * New option --ignore-cert-extension.
+
+ * Make use of libassuan 2.0 which is available as a DSO.
+
+
+Noteworthy changes in version 1.0.3 (2009-06-17)
+------------------------------------------------
+
+ * Client based trust anchors are now supported.
+
+ * Configured certificates with the suffix ".der" are now also used.
+
+ * Libgcrypt 1.4 is now required.
+
+
+Noteworthy changes in version 1.0.2 (2008-07-31)
+------------------------------------------------
+
+ * New option --url for the LOOKUP command and dirmngr-client.
+
+ * The LOOKUP command does now also consults the local cache.  New
+   option --cache-only for it and --local for dirmngr-client.
+
+ * Port to Windows completed.
+
+ * Improved certificate chain construction.
+
+ * Support loading of PEM encoded CRLs via HTTP.
+
+
+Noteworthy changes in version 1.0.1 (2007-08-16)
+------------------------------------------------
+
+ * The option --ocsp-signer may now take a filename to allow several
+   certificates to be valid signers for the default responder.
+
+ * New option --ocsp-max-period and improved the OCSP time checks.
+
+ * New option --force-default-signer for dirmngr-client.
+
+ * Ported to Windows.
+
+
+Noteworthy changes in version 1.0.0 (2006-11-29)
+------------------------------------------------
+
+ * Bumbed the version number.
+
+ * Removed included gettext.  We now require the system to provide a
+   suitable installation.
+
+
+Noteworthy changes in version 0.9.7 (2006-11-17)
+------------------------------------------------
+
+ * Internal cleanups.
+
+ * Fixed updating of DIR.txt.  Add additional diagnostics.
+
+ * Updated gettext package.
+
+
+Noteworthy changes in version 0.9.6 (2006-09-04)
+------------------------------------------------
+
+ * A couple of bug fixes for OCSP.
+
+ * OCSP does now make use of the responder ID and optionally included
+   certificates in the response to locate certificates.
+
+ * No more lost file descriptors when loading CRLs via HTTP.
+
+ * HTTP redirection for CRL and OCSP has been implemented.
+
+ * Man pages are now build and installed from the texinfo source.
+
+
+Noteworthy changes in version 0.9.5 (2006-06-27)
+------------------------------------------------
+
+ * Fixed a problems with the CRL caching and CRL certificate
+   validation.
+
+ * Improved diagnostics.
+
+
+Noteworthy changes in version 0.9.4 (2006-05-16)
+------------------------------------------------
+
+ * Try all names of each crlDP.
+
+ * Don't shutdown the socket after sending the HTTP request.
+
+
+Noteworthy changes in version 0.9.3 (2005-10-26)
+------------------------------------------------
+
+ * Minor bug fixes.
+
+
+Noteworthy changes in version 0.9.2 (2005-04-21)
+------------------------------------------------
+
+ * Make use of authorityKeyidentifier.keyIdentifier.
+
+ * Fixed a possible hang on exit.
+
+
+Noteworthy changes in version 0.9.1 (2005-02-08)
+------------------------------------------------
+
+ * New option --pem for dirmngr-client to allow requesting service
+   using a PEM encoded certificate.
+
+ * New option --squid-mode to allow using dirmngr-client directly as a
+   Squid helper.
+
+ * Bug fixes.
+
+
+Noteworthy changes in version 0.9.0 (2004-12-17)
+------------------------------------------------
+
+ * New option --daemon to start dirmngr as a system daemon.  This
+   switches to the use of different directories and also does
+   CRL signing certificate validation on its own.
+
+ * New tool dirmngr-client.
+
+ * New options: --ldap-wrapper-program, --http-wrapper-program,
+   --disable-ldap, --disable-http, --honor-http-proxy, --http-proxy,
+   --ldap-proxy, --only-ldap-proxy, --ignore-ldap-dp and
+   --ignore-http-dp.
+
+ * Uses an external ldap wrapper to cope with timeouts and general
+   LDAP problems.
+
+ * SIGHUP may be used to reread the configuration and to flush the
+   certificate cache.
+
+ * An authorithyKeyIdentifier in a CRL is now handled correctly.
+
+
+Noteworthy changes in version 0.5.6 (2004-09-28)
+------------------------------------------------
+
+ * LDAP fix.
+ * Logging fixes.
+
+ * Updated some configuration files.
+
+
+Noteworthy changes in version 0.5.5 (2004-05-13)
+------------------------------------------------
+
+ * Fixed the growing-dir.txt bug.
+
+ * Better LDAP error logging.
+
+
+Noteworthy changes in version 0.5.4 (2004-04-29)
+------------------------------------------------
+
+ * New commands --ocsp-responder and --ocsp-signer to define a default
+   OCSP reponder if a certificate does not contain an assigned OCSP
+   responder.
+
+
+Noteworthy changes in version 0.5.3 (2004-04-06)
+------------------------------------------------
+
+ * Basic OCSP support.
+
+
+Noteworthy changes in version 0.5.2 (2004-03-06)
+------------------------------------------------
+
+ * New Assuan command LISTCRLS.
+
+ * A couple of minor bug fixes.
+
+
+Noteworthy changes in version 0.5.1 (2003-12-23)
+------------------------------------------------
+
+* New options --faked-system-time and --force.
+
+* Changed the name of the cache directory to $HOMEDIR/dirmngr-cache.d
+  and renamed the dbcontents file.  You may delete the now obsolete
+  cache/ directory and the dbcontents file.
+
+* Dropped DB2 or DB4 use.  There is no need for it because a constant
+  database fits our needs far better.
+
+* Experimental support for retrieving CRLs via http.
+
+* The --log-file option may now be used to print logs to a socket.
+  Prefix the socket name with "socket://" to enable this.  This does
+  not work on all systems and falls back to stderr if there is a
+  problem with the socket.
+
+
+Noteworthy changes in version 0.5.0 (2003-11-17)
+------------------------------------------------
+
+* Revamped the entire thing.
+
+* Does now require Libgcrypt 1.1.90 or higher, as well as the latest
+  libksba and libassuan.
+
+* Fixed a bug in the assuan inquire processing.
+
+
+Noteworthy changes as of 2002-08-21
+------------------------------------
+
+* The default home directory is now .gnupg
+
+
+ Copyright 2003, 2004, 2005 g10 Code GmbH
+
+ This file is free software; as a special exception the author gives
+ unlimited permission to copy and/or distribute it, with or without
+ modifications, as long as this notice is preserved.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/dirmngr/b64dec.c b/dirmngr/b64dec.c
new file mode 100644 (file)
index 0000000..af223ae
--- /dev/null
@@ -0,0 +1,217 @@
+/* b64dec.c - Simple Base64 decoder.
+ * Copyright (C) 2008 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/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "i18n.h"
+#include "util.h"
+
+
+/* The reverse base-64 list used for base-64 decoding. */
+static unsigned char const asctobin[128] = 
+  {
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+    0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+    0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+    0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+    0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+    0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+    0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+    0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
+  };
+
+enum decoder_states 
+  {
+    s_init, s_idle, s_lfseen, s_begin,
+    s_b64_0, s_b64_1, s_b64_2, s_b64_3,
+    s_waitendtitle, s_waitend
+  };
+
+
+
+/* Initialize the context for the base64 decoder.  If TITLE is NULL a
+   plain base64 decoding is done.  If it is the empty string the
+   decoder will skip everything until a "-----BEGIN " line has been
+   seen, decoding ends at a "----END " line.
+   
+   Not yet implemented: If TITLE is either "PGP" or begins with "PGP "
+   the PGP armor lines are skipped as well.  */
+gpg_error_t
+b64dec_start (struct b64state *state, const char *title)
+{
+  memset (state, 0, sizeof *state);
+  if (title)
+    {
+      if (!strncmp (title, "PGP", 3) && (!title[3] || title[3] == ' '))
+        return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+      state->title = xtrystrdup (title);
+      if (!state->title)
+        return gpg_error_from_syserror ();
+      state->idx = s_init;
+    }
+  else
+    state->idx = s_b64_0;
+  return 0;
+}
+
+
+/* Do in-place decoding of base-64 data of LENGTH in BUFFER.  Stores the
+   new length of the buffer at R_NBYTES. */
+gpg_error_t
+b64dec_proc (struct b64state *state, void *buffer, size_t length,
+             size_t *r_nbytes)
+{
+  enum decoder_states ds = state->idx;
+  unsigned char val = state->radbuf[0];
+  int pos = state->quad_count; 
+  char *d, *s;
+
+  if (state->stop_seen)
+    {
+      *r_nbytes = 0;
+      return gpg_error (GPG_ERR_EOF);
+    }
+
+  for (s=d=buffer; length && !state->stop_seen; length--, s++)
+    {
+      switch (ds)
+        {
+        case s_idle:
+          if (*s == '\n')
+            {
+              ds = s_lfseen;
+              pos = 0;
+            }
+          break;
+        case s_init:
+          ds = s_lfseen;
+        case s_lfseen:
+          if (*s != "-----BEGIN "[pos])
+            ds = s_idle;
+          else if (pos == 10)
+            ds = s_begin;
+          else
+            pos++;
+          break;
+        case s_begin:
+          if (*s == '\n')
+            ds = s_b64_0;
+          break;
+        case s_b64_0:
+        case s_b64_1:
+        case s_b64_2:
+        case s_b64_3:
+          {
+            int c;
+
+            if (*s == '-' && state->title) 
+              {
+                /* Not a valid Base64 character: assume end
+                   header.  */
+                ds = s_waitend;
+              }
+            else if (*s == '=')
+              {
+                /* Pad character: stop */
+                if (ds == s_b64_1)
+                  *d++ = val;
+                ds = state->title? s_waitendtitle : s_waitend;
+              }
+            else if (*s == '\n' || *s == ' ' || *s == '\r' || *s == '\t')
+              ; /* Skip white spaces. */
+            else if ( (*s & 0x80) 
+                      || (c = asctobin[*(unsigned char *)s]) == 255)
+              {
+                /* Skip invalid encodings.  */
+                state->invalid_encoding = 1;
+              }
+            else if (ds == s_b64_0)
+              {
+                val = c << 2;
+                ds = s_b64_1;
+              }
+            else if (ds == s_b64_1)
+              {
+                val |= (c>>4)&3;
+                *d++ = val;
+                val = (c<<4)&0xf0;
+                ds = s_b64_2;
+              }
+            else if (ds == s_b64_2)
+              {
+                val |= (c>>2)&15;
+                *d++ = val;
+                val = (c<<6)&0xc0;
+                ds = s_b64_3;
+              }
+            else
+              {
+                val |= c&0x3f;
+                *d++ = val;
+                ds = s_b64_0;
+              }
+          }
+          break;
+        case s_waitendtitle:
+          if (*s == '-')
+            ds = s_waitend;
+          break;
+        case s_waitend:
+          if ( *s == '\n')
+            state->stop_seen = 1;
+          break; 
+        default: 
+          BUG();
+        }
+    }
+
+
+  state->idx = ds;
+  state->radbuf[0] = val;
+  state->quad_count = pos; 
+  *r_nbytes = (d -(char*) buffer);
+  return 0;
+}
+
+
+/* This function needs to be called before releasing the decoder
+   state.  It may return an error code in case an encoding error has
+   been found during decoding. */
+gpg_error_t
+b64dec_finish (struct b64state *state)
+{
+  xfree (state->title);
+  state->title = NULL;
+  return state->invalid_encoding? gpg_error(GPG_ERR_BAD_DATA): 0;
+}
+
diff --git a/dirmngr/b64enc.c b/dirmngr/b64enc.c
new file mode 100644 (file)
index 0000000..4429a8e
--- /dev/null
@@ -0,0 +1,213 @@
+/* b64enc.c - Simple Base64 encoder.
+ *     Copyright (C) 2001, 2003, 2004 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "i18n.h"
+#include "util.h"
+
+#define B64ENC_DID_HEADER   1
+#define B64ENC_DID_TRAILER  2
+#define B64ENC_NO_LINEFEEDS 16
+
+
+/* The base-64 character list */
+static unsigned char bintoasc[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 
+                                    "abcdefghijklmnopqrstuvwxyz" 
+                                    "0123456789+/"; 
+
+/* Prepare for base-64 writing to the stream FP.  If TITLE is not NULL
+   and not an empty string, this string will be used as the title for
+   the armor lines, with TITLE being an empty string, we don't write
+   the header lines and furthermore even don't write any linefeeds.
+   With TITLE beeing NULL, we merely don't write header but make sure
+   that lines are not too long. Note, that we don't write any output
+   unless at least one byte get written using b64enc_write. */
+gpg_error_t
+b64enc_start (struct b64state *state, FILE *fp, const char *title)
+{
+  memset (state, 0, sizeof *state);
+  state->fp = fp;
+  if (title && !*title)
+    state->flags |= B64ENC_NO_LINEFEEDS;
+  else if (title)
+    {
+      state->title = strdup (title);
+      if (!state->title)
+        return  gpg_error_from_errno (errno);
+    }
+  return 0;
+}
+
+
+/* Write NBYTES from BUFFER to the Base 64 stream identified by
+   STATE. With BUFFER and NBYTES being 0, merely do a fflush on the
+   stream. */
+gpg_error_t
+b64enc_write (struct b64state *state, const void *buffer, size_t nbytes)
+{
+  unsigned char radbuf[4];
+  int idx, quad_count;
+  const unsigned char *p;
+  FILE *fp = state->fp;
+
+
+  if (!nbytes)
+    {
+      if (buffer && fflush (fp))
+        goto write_error;
+      return 0;
+    }
+
+  if (!(state->flags & B64ENC_DID_HEADER))
+    {
+      if (state->title)
+        {
+          if ( fputs ("-----BEGIN ", fp) == EOF
+               || fputs (state->title, fp) == EOF
+               || fputs ("-----\n", fp) == EOF)
+            goto write_error;
+        }
+      state->flags |= B64ENC_DID_HEADER;
+    }
+
+  idx = state->idx;
+  quad_count = state->quad_count;
+  assert (idx < 4);
+  memcpy (radbuf, state->radbuf, idx);
+  
+  for (p=buffer; nbytes; p++, nbytes--)
+    {
+      radbuf[idx++] = *p;
+      if (idx > 2)
+        {
+          char tmp[4];
+
+          tmp[0] = bintoasc[(*radbuf >> 2) & 077];
+          tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
+          tmp[2] = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
+          tmp[3] = bintoasc[radbuf[2]&077];
+          for (idx=0; idx < 4; idx++)
+            putc (tmp[idx], fp);
+          idx = 0;
+          if (ferror (fp))
+            goto write_error;
+          if (++quad_count >= (64/4)) 
+            {
+              quad_count = 0;
+              if (!(state->flags & B64ENC_NO_LINEFEEDS)
+                  && fputs ("\n", fp) == EOF)
+                goto write_error;
+            }
+        }
+    }
+  memcpy (state->radbuf, radbuf, idx);
+  state->idx = idx;
+  state->quad_count = quad_count;
+  return 0;
+
+ write_error:
+  return gpg_error_from_errno (errno);
+}
+
+gpg_error_t
+b64enc_finish (struct b64state *state)
+{
+  gpg_error_t err = 0;
+  unsigned char radbuf[4];
+  int idx, quad_count;
+  FILE *fp;
+
+  if (!(state->flags & B64ENC_DID_HEADER))
+    goto cleanup;
+
+  /* Flush the base64 encoding */
+  fp = state->fp;
+  idx = state->idx;
+  quad_count = state->quad_count;
+  assert (idx < 4);
+  memcpy (radbuf, state->radbuf, idx);
+
+  if (idx)
+    {
+      char tmp[4];
+      
+      tmp[0] = bintoasc[(*radbuf>>2)&077];
+      if (idx == 1)
+        {
+          tmp[1] = bintoasc[((*radbuf << 4) & 060) & 077];
+          tmp[2] = '=';
+          tmp[3] = '=';
+        }
+      else 
+        { 
+          tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
+          tmp[2] = bintoasc[((radbuf[1] << 2) & 074) & 077];
+          tmp[3] = '=';
+        }
+      for (idx=0; idx < 4; idx++)
+        putc (tmp[idx], fp);
+      idx = 0;
+      if (ferror (fp))
+        goto write_error;
+      
+      if (++quad_count >= (64/4)) 
+        {
+          quad_count = 0;
+          if (!(state->flags & B64ENC_NO_LINEFEEDS)
+              && fputs ("\n", fp) == EOF)
+            goto write_error;
+        }
+    }
+
+  /* Finish the last line and write the trailer. */
+  if (quad_count
+      && !(state->flags & B64ENC_NO_LINEFEEDS)
+      && fputs ("\n", fp) == EOF)
+    goto write_error;
+
+  if (state->title)
+    {
+      if ( fputs ("-----END ", fp) == EOF
+           || fputs (state->title, fp) == EOF
+           || fputs ("-----\n", fp) == EOF)
+        goto write_error;
+    }
+
+  goto cleanup;
+
+ write_error:
+  err = gpg_error_from_errno (errno);
+
+ cleanup:
+  if (state->title)
+    {
+      free (state->title);
+      state->title = NULL;
+    }
+  state->fp = NULL;
+  return err;
+}
+
diff --git a/dirmngr/cdb.h b/dirmngr/cdb.h
new file mode 100644 (file)
index 0000000..73cc995
--- /dev/null
@@ -0,0 +1,91 @@
+/* $Id: cdb.h 106 2003-12-12 17:36:49Z werner $
+ * public cdb include file
+ *
+ * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+ * Public domain.
+ *
+ * Taken from tinycdb-0.73. By Werner Koch <wk@gnupg.org> 2003-12-12.
+ */
+
+#ifndef TINYCDB_VERSION
+#define TINYCDB_VERSION 0.73
+
+typedef unsigned int cdbi_t; /*XXX should be at least 32 bits long */
+
+/* common routines */
+cdbi_t cdb_hash(const void *buf, cdbi_t len);
+cdbi_t cdb_unpack(const unsigned char buf[4]);
+void cdb_pack(cdbi_t num, unsigned char buf[4]);
+
+struct cdb {
+  int cdb_fd;                  /* file descriptor */
+  /* private members */
+  cdbi_t cdb_fsize;            /* datafile size */
+  const unsigned char *cdb_mem; /* mmap'ed file memory */
+  cdbi_t cdb_vpos, cdb_vlen;   /* found data */
+  cdbi_t cdb_kpos, cdb_klen;    /* found key (only set if cdb_findinit
+                                   was called with KEY set to NULL). */
+};
+
+#define cdb_datapos(c) ((c)->cdb_vpos)
+#define cdb_datalen(c) ((c)->cdb_vlen)
+#define cdb_keypos(c) ((c)->cdb_kpos)
+#define cdb_keylen(c) ((c)->cdb_klen)
+#define cdb_fileno(c) ((c)->cdb_fd)
+
+int cdb_init(struct cdb *cdbp, int fd);
+void cdb_free(struct cdb *cdbp);
+
+int cdb_read(const struct cdb *cdbp,
+            void *buf, unsigned len, cdbi_t pos);
+int cdb_find(struct cdb *cdbp, const void *key, unsigned klen);
+
+struct cdb_find {
+  struct cdb *cdb_cdbp;
+  cdbi_t cdb_hval;
+  const unsigned char *cdb_htp, *cdb_htab, *cdb_htend;
+  cdbi_t cdb_httodo;
+  const void *cdb_key;
+  cdbi_t cdb_klen;
+};
+
+int cdb_findinit(struct cdb_find *cdbfp, struct cdb *cdbp,
+                const void *key, cdbi_t klen);
+int cdb_findnext(struct cdb_find *cdbfp);
+
+/* old simple interface */
+/* open file using standard routine, then: */
+int cdb_seek(int fd, const void *key, unsigned klen, cdbi_t *dlenp);
+int cdb_bread(int fd, void *buf, int len);
+
+/* cdb_make */
+
+struct cdb_make {
+  int cdb_fd;                  /* file descriptor */
+  /* private */
+  cdbi_t cdb_dpos;             /* data position so far */
+  cdbi_t cdb_rcnt;             /* record count so far */
+  char cdb_buf[4096];          /* write buffer */
+  char *cdb_bpos;              /* current buf position */
+  struct cdb_rl *cdb_rec[256]; /* list of arrays of record infos */
+};
+
+
+
+int cdb_make_start(struct cdb_make *cdbmp, int fd);
+int cdb_make_add(struct cdb_make *cdbmp,
+                const void *key, cdbi_t klen,
+                const void *val, cdbi_t vlen);
+int cdb_make_exists(struct cdb_make *cdbmp,
+                   const void *key, cdbi_t klen);
+int cdb_make_put(struct cdb_make *cdbmp,
+                const void *key, cdbi_t klen,
+                const void *val, cdbi_t vlen,
+                int flag);
+#define CDB_PUT_ADD    0       /* add unconditionnaly, like cdb_make_add() */
+#define CDB_PUT_REPLACE        1       /* replace: do not place to index OLD record */
+#define CDB_PUT_INSERT 2       /* add only if not already exists */
+#define CDB_PUT_WARN   3       /* add unconditionally but ret. 1 if exists */
+int cdb_make_finish(struct cdb_make *cdbmp);
+
+#endif /* include guard */
diff --git a/dirmngr/cdblib.c b/dirmngr/cdblib.c
new file mode 100644 (file)
index 0000000..de60fe9
--- /dev/null
@@ -0,0 +1,925 @@
+/* cdblib.c - all CDB library functions.
+ *
+ * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+ * Public domain.
+ *
+ * Taken from tinycdb-0.73 and merged into one file for easier
+ * inclusion into Dirmngr.  By Werner Koch <wk@gnupg.org> 2003-12-12.
+ */
+
+/* A cdb database is a single file used to map `keys' to `values',
+   having records of (key,value) pairs.  File consists of 3 parts: toc
+   (table of contents), data and index (hash tables).
+
+   Toc has fixed length of 2048 bytes, containing 256 pointers to hash
+   tables inside index sections.  Every pointer consists of position
+   of a hash table in bytes from the beginning of a file, and a size
+   of a hash table in entries, both are 4-bytes (32 bits) unsigned
+   integers in little-endian form.  Hash table length may have zero
+   length, meaning that corresponding hash table is empty.
+
+   Right after toc section, data section follows without any
+   alingment.  It consists of series of records, each is a key length,
+   value (data) length, key and value.  Again, key and value length
+   are 4-byte unsigned integers.  Each next record follows previous
+   without any special alignment.
+
+   After data section, index (hash tables) section follows.  It should
+   be looked to in conjunction with toc section, where each of max 256
+   hash tables are defined.  Index section consists of series of hash
+   tables, with starting position and length defined in toc section.
+   Every hash table is a sequence of records each holds two numbers:
+   key's hash value and record position inside data section (bytes
+   from the beginning of a file to first byte of key length starting
+   data record).  If record position is zero, then this is an empty
+   hash table slot, pointed to nowhere.
+
+   CDB hash function is
+     hv = ((hv << 5) + hv) ^ c
+   for every single c byte of a key, starting with hv = 5381.
+
+   Toc section indexed by (hv % 256), i.e. hash value modulo 256
+   (number of entries in toc section).
+
+   In order to find a record, one should: first, compute the hash
+   value (hv) of a key.  Second, look to hash table number hv modulo
+   256.  If it is empty, then there is no such key exists.  If it is
+   not empty, then third, loop by slots inside that hash table,
+   starting from slot with number hv divided by 256 modulo length of
+   that table, or ((hv / 256) % htlen), searching for this hv in hash
+   table.  Stop search on empty slot (if record position is zero) or
+   when all slots was probed (note cyclic search, jumping from end to
+   beginning of a table).  When hash value in question is found in
+   hash table, look to key of corresponding record, comparing it with
+   key in question.  If them of the same length and equals to each
+   other, then record is found, overwise, repeat with next hash table
+   slot.  Note that there may be several records with the same key.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h> 
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifdef _WIN32
+# include <windows.h>
+#else
+# include <sys/mman.h>
+# ifndef MAP_FAILED
+#  define MAP_FAILED ((void*)-1)
+# endif
+#endif
+#include <sys/stat.h>
+#include "cdb.h"
+
+#ifndef EPROTO
+# define EPROTO EINVAL
+#endif
+#ifndef SEEK_SET
+# define SEEK_SET 0
+#endif
+
+
+struct cdb_rec {
+  cdbi_t hval;
+  cdbi_t rpos;
+};
+  
+struct cdb_rl {
+  struct cdb_rl *next;
+  cdbi_t cnt;
+  struct cdb_rec rec[254];
+};
+
+static int make_find(struct cdb_make *cdbmp,
+                  const void *key, cdbi_t klen, cdbi_t hval,
+                  struct cdb_rl **rlp);
+static int make_write(struct cdb_make *cdbmp,
+                   const char *ptr, cdbi_t len);
+
+
+
+/* Initializes structure given by CDBP pointer and associates it with
+   the open file descriptor FD.  Allocate memory for the structure
+   itself if needed and file open operation should be done by
+   application.  File FD should be opened at least read-only, and
+   should be seekable.  Routine returns 0 on success or negative value
+   on error. */
+int
+cdb_init(struct cdb *cdbp, int fd)
+{
+  struct stat st;
+  unsigned char *mem;
+  unsigned fsize;
+#ifdef _WIN32
+  HANDLE hFile, hMapping;
+#endif
+
+  /* get file size */
+  if (fstat(fd, &st) < 0)
+    return -1;
+  /* trivial sanity check: at least toc should be here */
+  if (st.st_size < 2048) {
+    errno = EPROTO;
+    return -1;
+  }
+  fsize = (unsigned)(st.st_size & 0xffffffffu);
+  /* memory-map file */
+#ifdef _WIN32
+  hFile = (HANDLE) _get_osfhandle(fd);
+  if (hFile == (HANDLE) -1)
+    return -1;
+  hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
+  if (!hMapping)
+    return -1;
+  mem = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
+  if (!mem)
+    return -1;
+#else
+  mem = (unsigned char*)mmap(NULL, fsize, PROT_READ, MAP_SHARED, fd, 0);
+  if (mem == MAP_FAILED)
+    return -1;
+#endif /* _WIN32 */
+
+  cdbp->cdb_fd = fd;
+  cdbp->cdb_fsize = st.st_size;
+  cdbp->cdb_mem = mem;
+
+#if 0
+  /* XXX don't know well about madvise syscall -- is it legal
+     to set different options for parts of one mmap() region?
+     There is also posix_madvise() exist, with POSIX_MADV_RANDOM etc...
+  */
+#ifdef MADV_RANDOM
+  /* set madvise() parameters. Ignore errors for now if system
+     doesn't support it */
+  madvise(mem, 2048, MADV_WILLNEED);
+  madvise(mem + 2048, cdbp->cdb_fsize - 2048, MADV_RANDOM);
+#endif
+#endif
+
+  cdbp->cdb_vpos = cdbp->cdb_vlen = 0;
+
+  return 0;
+}
+
+
+/* Frees the internal resources held by structure.  Note that this
+   routine does not close the file. */
+void
+cdb_free(struct cdb *cdbp)
+{
+  if (cdbp->cdb_mem) {
+#ifdef _WIN32
+    HANDLE hFile, hMapping;
+#endif
+#ifdef _WIN32
+    hFile = (HANDLE) _get_osfhandle(cdbp->cdb_fd);
+    hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
+    UnmapViewOfFile((void*) cdbp->cdb_mem);
+    CloseHandle(hMapping);
+#else
+    munmap((void*)cdbp->cdb_mem, cdbp->cdb_fsize);
+#endif /* _WIN32 */
+    cdbp->cdb_mem = NULL;
+  }
+  cdbp->cdb_fsize = 0;
+}
+
+
+/* Read data from cdb file, starting at position pos of length len,
+   placing result to buf.  This routine may be used to get actual
+   value found by cdb_find() or other routines that returns position
+   and length of a data.  Returns 0 on success or negative value on
+   error. */
+int
+cdb_read(const struct cdb *cdbp, void *buf, unsigned len, cdbi_t pos)
+{
+  if (pos > cdbp->cdb_fsize || cdbp->cdb_fsize - pos < len) {
+    errno = EPROTO;
+    return -1;
+  }
+  memcpy(buf, cdbp->cdb_mem + pos, len);
+  return 0;
+}
+
+
+/* Attempts to find a key given by (key,klen) parameters.  If key
+   exists in database, routine returns 1 and places position and
+   length of value associated with this key to internal fields inside
+   cdbp structure, to be accessible by cdb_datapos() and
+   cdb_datalen().  If key is not in database, routines returns 0.  On
+   error, negative value is returned.  Note that using cdb_find() it
+   is possible to lookup only first record with a given key. */
+int
+cdb_find(struct cdb *cdbp, const void *key, cdbi_t klen)
+{
+  const unsigned char *htp;    /* hash table pointer */
+  const unsigned char *htab;   /* hash table */
+  const unsigned char *htend;  /* end of hash table */
+  cdbi_t httodo;               /* ht bytes left to look */
+  cdbi_t pos, n;
+
+  cdbi_t hval;
+
+  if (klen > cdbp->cdb_fsize)  /* if key size is larger than file */
+    return 0;
+
+  hval = cdb_hash(key, klen);
+
+  /* find (pos,n) hash table to use */
+  /* first 2048 bytes (toc) are always available */
+  /* (hval % 256) * 8 */
+  htp = cdbp->cdb_mem + ((hval << 3) & 2047); /* index in toc (256x8) */
+  n = cdb_unpack(htp + 4);     /* table size */
+  if (!n)                      /* empty table */
+    return 0;                  /* not found */
+  httodo = n << 3;             /* bytes of htab to lookup */
+  pos = cdb_unpack(htp);       /* htab position */
+  if (n > (cdbp->cdb_fsize >> 3) /* overflow of httodo ? */
+      || pos > cdbp->cdb_fsize /* htab start within file ? */
+      || httodo > cdbp->cdb_fsize - pos) /* entrie htab within file ? */
+  {
+    errno = EPROTO;
+    return -1;
+  }
+
+  htab = cdbp->cdb_mem + pos;  /* htab pointer */
+  htend = htab + httodo;       /* after end of htab */
+  /* htab starting position: rest of hval modulo htsize, 8bytes per elt */
+  htp = htab + (((hval >> 8) % n) << 3);
+
+  for(;;) {
+    pos = cdb_unpack(htp + 4); /* record position */
+    if (!pos)
+      return 0;
+    if (cdb_unpack(htp) == hval) {
+      if (pos > cdbp->cdb_fsize - 8) { /* key+val lengths */
+       errno = EPROTO;
+       return -1;
+      }
+      if (cdb_unpack(cdbp->cdb_mem + pos) == klen) {
+       if (cdbp->cdb_fsize - klen < pos + 8) {
+         errno = EPROTO;
+         return -1;
+       }
+       if (memcmp(key, cdbp->cdb_mem + pos + 8, klen) == 0) {
+         n = cdb_unpack(cdbp->cdb_mem + pos + 4);
+         pos += 8 + klen;
+         if (cdbp->cdb_fsize < n || cdbp->cdb_fsize - n < pos) {
+           errno = EPROTO;
+           return -1;
+         }
+         cdbp->cdb_vpos = pos;
+         cdbp->cdb_vlen = n;
+         return 1;
+       }
+      }
+    }
+    httodo -= 8;
+    if (!httodo)
+      return 0;
+    if ((htp += 8) >= htend)
+      htp = htab;
+  }
+
+}
+
+
+
+/* Sequential-find routines that used separate structure.  It is
+   possible to have many than one record with the same key in a
+   database, and these routines allows to enumerate all them.
+   cdb_findinit() initializes search structure pointed to by cdbfp.
+   It will return negative value on error or 0 on success.  cdb_find­
+   next() attempts to find next matching key, setting value position
+   and length in cdbfp structure.  It will return positive value if
+   given key was found, 0 if there is no more such key(s), or negative
+   value on error.  To access value position and length after
+   successeful call to cdb_findnext() (when it returned positive
+   result), use cdb_datapos() and cdb_datalen() macros with cdbp
+   pointer.  It is error to use cdb_findnext() after it returned 0 or
+   error condition.  These routines is a bit slower than
+   cdb_find(). 
+
+   Setting KEY to NULL will start a sequential search through the
+   entire DB.
+*/
+int
+cdb_findinit(struct cdb_find *cdbfp, struct cdb *cdbp,
+             const void *key, cdbi_t klen)
+{
+  cdbi_t n, pos;
+
+  cdbfp->cdb_cdbp = cdbp;
+  cdbfp->cdb_key  = key;
+  cdbfp->cdb_klen = klen;
+  cdbfp->cdb_hval = key? cdb_hash(key, klen) : 0;
+
+  if (key)
+    {
+      cdbfp->cdb_htp = cdbp->cdb_mem + ((cdbfp->cdb_hval << 3) & 2047);
+      n = cdb_unpack(cdbfp->cdb_htp + 4);
+      cdbfp->cdb_httodo = n << 3; /* Set to size of hash table. */
+      if (!n)
+        return 0; /* The hash table is empry. */
+      pos = cdb_unpack(cdbfp->cdb_htp);
+      if (n > (cdbp->cdb_fsize >> 3)
+          || pos > cdbp->cdb_fsize
+          || cdbfp->cdb_httodo > cdbp->cdb_fsize - pos)
+        {
+          errno = EPROTO;
+          return -1;
+        }
+
+      cdbfp->cdb_htab = cdbp->cdb_mem + pos;
+      cdbfp->cdb_htend = cdbfp->cdb_htab + cdbfp->cdb_httodo;
+      cdbfp->cdb_htp = cdbfp->cdb_htab + (((cdbfp->cdb_hval >> 8) % n) << 3);
+    }
+  else /* Walk over all entries. */
+    {
+      cdbfp->cdb_hval = 0; 
+      /* Force stepping in findnext. */
+      cdbfp->cdb_htp = cdbfp->cdb_htend = cdbp->cdb_mem;
+    }
+  return 0;
+}
+
+
+/* See cdb_findinit. */
+int 
+cdb_findnext(struct cdb_find *cdbfp)
+{
+  cdbi_t pos, n;
+  struct cdb *cdbp = cdbfp->cdb_cdbp;
+
+  if (cdbfp->cdb_key)
+    {
+      while(cdbfp->cdb_httodo) {
+        pos = cdb_unpack(cdbfp->cdb_htp + 4);
+        if (!pos)
+          return 0;
+        n = cdb_unpack(cdbfp->cdb_htp) == cdbfp->cdb_hval;
+        if ((cdbfp->cdb_htp += 8) >= cdbfp->cdb_htend)
+          cdbfp->cdb_htp = cdbfp->cdb_htab;
+        cdbfp->cdb_httodo -= 8;
+        if (n) {
+          if (pos > cdbp->cdb_fsize - 8) {
+            errno = EPROTO;
+            return -1;
+          }
+          if (cdb_unpack(cdbp->cdb_mem + pos) == cdbfp->cdb_klen) {
+            if (cdbp->cdb_fsize - cdbfp->cdb_klen < pos + 8) {
+              errno = EPROTO;
+              return -1;
+            }
+            if (memcmp(cdbfp->cdb_key,
+                       cdbp->cdb_mem + pos + 8, cdbfp->cdb_klen) == 0) {
+              n = cdb_unpack(cdbp->cdb_mem + pos + 4);
+              pos += 8 + cdbfp->cdb_klen;
+              if (cdbp->cdb_fsize < n || cdbp->cdb_fsize - n < pos) {
+                errno = EPROTO;
+                return -1;
+              }
+              cdbp->cdb_vpos = pos;
+              cdbp->cdb_vlen = n;
+              return 1;
+            }
+          }
+        }
+      }
+    }
+  else /* Walk over all entries. */
+    {
+      do
+        {
+          while (cdbfp->cdb_htp >= cdbfp->cdb_htend)
+            {
+              if (cdbfp->cdb_hval > 255)
+                return 0; /* No more items. */
+              
+              cdbfp->cdb_htp = cdbp->cdb_mem + cdbfp->cdb_hval * 8;
+              cdbfp->cdb_hval++; /* Advance for next round. */
+              pos = cdb_unpack (cdbfp->cdb_htp);     /* Offset of table. */
+              n   = cdb_unpack (cdbfp->cdb_htp + 4); /* Number of entries. */
+              cdbfp->cdb_httodo = n * 8;             /* Size of table. */
+              if (n > (cdbp->cdb_fsize / 8)
+                  || pos > cdbp->cdb_fsize
+                  || cdbfp->cdb_httodo > cdbp->cdb_fsize - pos)
+                {
+                  errno = EPROTO;
+                  return -1;
+                }
+              
+              cdbfp->cdb_htab  = cdbp->cdb_mem + pos;
+              cdbfp->cdb_htend = cdbfp->cdb_htab + cdbfp->cdb_httodo;
+              cdbfp->cdb_htp   = cdbfp->cdb_htab;
+            }
+          
+          pos = cdb_unpack (cdbfp->cdb_htp + 4); /* Offset of record. */
+          cdbfp->cdb_htp += 8;
+        } 
+      while (!pos);
+      if (pos > cdbp->cdb_fsize - 8)
+        {
+          errno = EPROTO;
+          return -1;
+        }
+      
+      cdbp->cdb_kpos = pos + 8;
+      cdbp->cdb_klen = cdb_unpack(cdbp->cdb_mem + pos);
+      cdbp->cdb_vpos = pos + 8 + cdbp->cdb_klen;
+      cdbp->cdb_vlen = cdb_unpack(cdbp->cdb_mem + pos + 4);
+      n = 8 + cdbp->cdb_klen + cdbp->cdb_vlen;
+      if ( pos > cdbp->cdb_fsize || pos > cdbp->cdb_fsize - n)
+        {
+          errno = EPROTO;
+          return -1;
+        }
+      return 1; /* Found. */
+    }
+  return 0;
+}
+
+/* Read a chunk from file, ignoring interrupts (EINTR) */
+int
+cdb_bread(int fd, void *buf, int len)
+{
+  int l;
+  while(len > 0) {
+    do l = read(fd, buf, len);
+    while(l < 0 && errno == EINTR);
+    if (l <= 0) {
+      if (!l)
+        errno = EIO;
+      return -1;
+    }
+    buf = (char*)buf + l;
+    len -= l;
+  }
+  return 0;
+}
+
+/* Find a given key in cdb file, seek a file pointer to it's value and
+   place data length to *dlenp. */
+int
+cdb_seek(int fd, const void *key, unsigned klen, cdbi_t *dlenp)
+{
+  cdbi_t htstart;              /* hash table start position */
+  cdbi_t htsize;               /* number of elements in a hash table */
+  cdbi_t httodo;               /* hash table elements left to look */
+  cdbi_t hti;                  /* hash table index */
+  cdbi_t pos;                  /* position in a file */
+  cdbi_t hval;                 /* key's hash value */
+  unsigned char rbuf[64];      /* read buffer */
+  int needseek = 1;            /* if we should seek to a hash slot */
+
+  hval = cdb_hash(key, klen);
+  pos = (hval & 0xff) << 3; /* position in TOC */
+  /* read the hash table parameters */
+  if (lseek(fd, pos, SEEK_SET) < 0 || cdb_bread(fd, rbuf, 8) < 0)
+    return -1;
+  if ((htsize = cdb_unpack(rbuf + 4)) == 0)
+    return 0;
+  hti = (hval >> 8) % htsize;  /* start position in hash table */
+  httodo = htsize;
+  htstart = cdb_unpack(rbuf);
+
+  for(;;) {
+    if (needseek && lseek(fd, htstart + (hti << 3), SEEK_SET) < 0)
+      return -1;
+    if (cdb_bread(fd, rbuf, 8) < 0)
+      return -1;
+    if ((pos = cdb_unpack(rbuf + 4)) == 0) /* not found */
+      return 0;
+
+    if (cdb_unpack(rbuf) != hval) /* hash value not matched */
+      needseek = 0;
+    else { /* hash value matched */
+      if (lseek(fd, pos, SEEK_SET) < 0 || cdb_bread(fd, rbuf, 8) < 0)
+       return -1;
+      if (cdb_unpack(rbuf) == klen) { /* key length matches */
+       /* read the key from file and compare with wanted */
+       cdbi_t l = klen, c;
+       const char *k = (const char*)key;
+       if (*dlenp)
+         *dlenp = cdb_unpack(rbuf + 4); /* save value length */
+       for(;;) {
+         if (!l) /* the whole key read and matches, return */
+           return 1;
+         c = l > sizeof(rbuf) ? sizeof(rbuf) : l;
+         if (cdb_bread(fd, rbuf, c) < 0)
+           return -1;
+         if (memcmp(rbuf, k, c) != 0) /* no, it differs, stop here */
+           break;
+         k += c; l -= c;
+       }
+      }
+      needseek = 1; /* we're looked to other place, should seek back */
+    }
+    if (!--httodo)
+      return 0;
+    if (++hti == htsize) {
+      hti = htstart;
+      needseek = 1;
+    }
+  }
+}
+
+cdbi_t
+cdb_unpack(const unsigned char buf[4])
+{
+  cdbi_t n = buf[3];
+  n <<= 8; n |= buf[2];
+  n <<= 8; n |= buf[1];
+  n <<= 8; n |= buf[0];
+  return n;
+}
+
+/* Add record with key (KEY,KLEN) and value (VAL,VLEN) to a database.
+   Returns 0 on success or negative value on error.  Note that this
+   routine does not checks if given key already exists, but cdb_find()
+   will not see second record with the same key.  It is not possible
+   to continue building a database if cdb_make_add() returned an error
+   indicator. */
+int
+cdb_make_add(struct cdb_make *cdbmp,
+            const void *key, cdbi_t klen,
+            const void *val, cdbi_t vlen)
+{
+  unsigned char rlen[8];
+  cdbi_t hval;
+  struct cdb_rl *rl;
+  if (klen > 0xffffffff - (cdbmp->cdb_dpos + 8) ||
+      vlen > 0xffffffff - (cdbmp->cdb_dpos + klen + 8)) {
+    errno = ENOMEM;
+    return -1;
+  }
+  hval = cdb_hash(key, klen);
+  rl = cdbmp->cdb_rec[hval&255];
+  if (!rl || rl->cnt >= sizeof(rl->rec)/sizeof(rl->rec[0])) {
+    rl = (struct cdb_rl*)malloc(sizeof(struct cdb_rl));
+    if (!rl) {
+      errno = ENOMEM;
+      return -1;
+    }
+    rl->cnt = 0;
+    rl->next = cdbmp->cdb_rec[hval&255];
+    cdbmp->cdb_rec[hval&255] = rl;
+  }
+  rl->rec[rl->cnt].hval = hval;
+  rl->rec[rl->cnt].rpos = cdbmp->cdb_dpos;
+  ++rl->cnt;
+  ++cdbmp->cdb_rcnt;
+  cdb_pack(klen, rlen);
+  cdb_pack(vlen, rlen + 4);
+  if (make_write(cdbmp, rlen, 8) < 0 ||
+      make_write(cdbmp, key, klen) < 0 ||
+      make_write(cdbmp, val, vlen) < 0)
+    return -1;
+  return 0;
+}
+
+int
+cdb_make_put(struct cdb_make *cdbmp,
+            const void *key, cdbi_t klen,
+            const void *val, cdbi_t vlen,
+            int flags)
+{
+  unsigned char rlen[8];
+  cdbi_t hval = cdb_hash(key, klen);
+  struct cdb_rl *rl;
+  int c, r;
+
+  switch(flags) {
+    case CDB_PUT_REPLACE:
+    case CDB_PUT_INSERT:
+    case CDB_PUT_WARN:
+      c = make_find(cdbmp, key, klen, hval, &rl);
+      if (c < 0)
+       return -1;
+      if (c) {
+       if (flags == CDB_PUT_INSERT) {
+         errno = EEXIST;
+         return 1;
+       }
+       else if (flags == CDB_PUT_REPLACE) {
+         --c;
+         r = 1;
+         break;
+       }
+       else
+         r = 1;
+      }
+      /* fall */
+
+    case CDB_PUT_ADD:
+      rl = cdbmp->cdb_rec[hval&255];
+      if (!rl || rl->cnt >= sizeof(rl->rec)/sizeof(rl->rec[0])) {
+       rl = (struct cdb_rl*)malloc(sizeof(struct cdb_rl));
+       if (!rl) {
+         errno = ENOMEM;
+         return -1;
+       }
+       rl->cnt = 0;
+       rl->next = cdbmp->cdb_rec[hval&255];
+       cdbmp->cdb_rec[hval&255] = rl;
+      }
+      c = rl->cnt;
+      r = 0;
+      break;
+
+    default:
+      errno = EINVAL;
+      return -1;
+  }
+
+  if (klen > 0xffffffff - (cdbmp->cdb_dpos + 8) ||
+      vlen > 0xffffffff - (cdbmp->cdb_dpos + klen + 8)) {
+    errno = ENOMEM;
+    return -1;
+  }
+  rl->rec[c].hval = hval;
+  rl->rec[c].rpos = cdbmp->cdb_dpos;
+  if (c == rl->cnt) {
+    ++rl->cnt;
+    ++cdbmp->cdb_rcnt;
+  }
+  cdb_pack(klen, rlen);
+  cdb_pack(vlen, rlen + 4);
+  if (make_write(cdbmp, rlen, 8) < 0 ||
+      make_write(cdbmp, key, klen) < 0 ||
+      make_write(cdbmp, val, vlen) < 0)
+    return -1;
+  return r;
+}
+
+
+static int
+match(int fd, cdbi_t pos, const char *key, cdbi_t klen)
+{
+  unsigned char buf[64]; /*XXX cdb_buf may be used here instead */
+  if (lseek(fd, pos, SEEK_SET) < 0 || read(fd, buf, 8) != 8)
+    return -1;
+  if (cdb_unpack(buf) != klen)
+    return 0;
+
+  while(klen > sizeof(buf)) {
+    if (read(fd, buf, sizeof(buf)) != sizeof(buf))
+      return -1;
+    if (memcmp(buf, key, sizeof(buf)) != 0)
+      return 0;
+    key += sizeof(buf);
+    klen -= sizeof(buf);
+  }
+  if (klen) {
+    if (read(fd, buf, klen) != klen)
+      return -1;
+    if (memcmp(buf, key, klen) != 0)
+      return 0;
+  }
+  return 1;
+}
+
+
+static int
+make_find (struct cdb_make *cdbmp,
+           const void *key, cdbi_t klen, cdbi_t hval,
+           struct cdb_rl **rlp)
+{
+  struct cdb_rl *rl = cdbmp->cdb_rec[hval&255];
+  int r, i;
+  int seeked = 0;
+  while(rl) {
+    for(i = rl->cnt - 1; i >= 0; --i) { /* search backward */
+      if (rl->rec[i].hval != hval)
+       continue;
+      /*XXX this explicit flush may be unnecessary having
+       * smarter match() that looks to cdb_buf too, but
+       * most of a time here spent in finding hash values
+       * (above), not keys */
+      if (cdbmp->cdb_bpos != cdbmp->cdb_buf) {
+        if (write(cdbmp->cdb_fd, cdbmp->cdb_buf,
+                 cdbmp->cdb_bpos - cdbmp->cdb_buf) < 0)
+          return -1;
+        cdbmp->cdb_bpos = cdbmp->cdb_buf;
+      }
+      seeked = 1;
+      r = match(cdbmp->cdb_fd, rl->rec[i].rpos, key, klen);
+      if (!r)
+       continue;
+      if (r < 0)
+       return -1;
+      if (lseek(cdbmp->cdb_fd, cdbmp->cdb_dpos, SEEK_SET) < 0)
+        return -1;
+      if (rlp)
+       *rlp = rl;
+      return i + 1;
+    }
+    rl = rl->next;
+  }
+  if (seeked && lseek(cdbmp->cdb_fd, cdbmp->cdb_dpos, SEEK_SET) < 0)
+    return -1;
+  return 0;
+}
+
+int
+cdb_make_exists(struct cdb_make *cdbmp,
+                const void *key, cdbi_t klen)
+{
+  return make_find(cdbmp, key, klen, cdb_hash(key, klen), NULL);
+}
+
+
+void
+cdb_pack(cdbi_t num, unsigned char buf[4])
+{
+  buf[0] = num & 255; num >>= 8;
+  buf[1] = num & 255; num >>= 8;
+  buf[2] = num & 255;
+  buf[3] = num >> 8;
+}
+
+
+/* Initializes structure to create a database.  File FD should be
+   opened read-write and should be seekable.  Returns 0 on success or
+   negative value on error. */
+int
+cdb_make_start(struct cdb_make *cdbmp, int fd)
+{
+  memset (cdbmp, 0, sizeof *cdbmp);
+  cdbmp->cdb_fd = fd;
+  cdbmp->cdb_dpos = 2048;
+  cdbmp->cdb_bpos = cdbmp->cdb_buf + 2048;
+  return 0;
+}
+
+
+static int
+ewrite(int fd, const char *buf, int len)
+{
+  while(len) {
+    int l = write(fd, buf, len);
+    if (l < 0 && errno != EINTR)
+      return -1;
+    if (l > 0)
+      {
+        len -= l;
+        buf += l;
+      }
+  }
+  return 0;
+}
+
+static int
+make_write(struct cdb_make *cdbmp, const char *ptr, cdbi_t len)
+{
+  cdbi_t l = sizeof(cdbmp->cdb_buf) - (cdbmp->cdb_bpos - cdbmp->cdb_buf);
+  cdbmp->cdb_dpos += len;
+  if (len > l) {
+    memcpy(cdbmp->cdb_bpos, ptr, l);
+    if (ewrite(cdbmp->cdb_fd, cdbmp->cdb_buf, sizeof(cdbmp->cdb_buf)) < 0)
+      return -1;
+    ptr += l; len -= l;
+    l = len / sizeof(cdbmp->cdb_buf);
+    if (l) {
+      l *= sizeof(cdbmp->cdb_buf);
+      if (ewrite(cdbmp->cdb_fd, ptr, l) < 0)
+       return -1;
+      ptr += l; len -= l;
+    }
+    cdbmp->cdb_bpos = cdbmp->cdb_buf;
+  }
+  if (len) {
+    memcpy(cdbmp->cdb_bpos, ptr, len);
+    cdbmp->cdb_bpos += len;
+  }
+  return 0;
+}
+
+static int
+cdb_make_finish_internal(struct cdb_make *cdbmp)
+{
+  cdbi_t hcnt[256];            /* hash table counts */
+  cdbi_t hpos[256];            /* hash table positions */
+  struct cdb_rec *htab;
+  unsigned char *p;
+  struct cdb_rl *rl;
+  cdbi_t hsize;
+  unsigned t, i;
+
+  if (((0xffffffff - cdbmp->cdb_dpos) >> 3) < cdbmp->cdb_rcnt) {
+    errno = ENOMEM;
+    return -1;
+  }
+
+  /* count htab sizes and reorder reclists */
+  hsize = 0;
+  for (t = 0; t < 256; ++t) {
+    struct cdb_rl *rlt = NULL;
+    i = 0;
+    rl = cdbmp->cdb_rec[t];
+    while(rl) {
+      struct cdb_rl *rln = rl->next;
+      rl->next = rlt;
+      rlt = rl;
+      i += rl->cnt;
+      rl = rln;
+    }
+    cdbmp->cdb_rec[t] = rlt;
+    if (hsize < (hcnt[t] = i << 1))
+      hsize = hcnt[t];
+  }
+
+  /* allocate memory to hold max htable */
+  htab = (struct cdb_rec*)malloc((hsize + 2) * sizeof(struct cdb_rec));
+  if (!htab) {
+    errno = ENOENT;
+    return -1;
+  }
+  p = (unsigned char *)htab;
+  htab += 2;
+
+  /* build hash tables */
+  for (t = 0; t < 256; ++t) {
+    cdbi_t len, hi;
+    hpos[t] = cdbmp->cdb_dpos;
+    if ((len = hcnt[t]) == 0)
+      continue;
+    for (i = 0; i < len; ++i)
+      htab[i].hval = htab[i].rpos = 0;
+    for (rl = cdbmp->cdb_rec[t]; rl; rl = rl->next)
+      for (i = 0; i < rl->cnt; ++i) {
+       hi = (rl->rec[i].hval >> 8) % len;
+       while(htab[hi].rpos)
+         if (++hi == len)
+           hi = 0;
+       htab[hi] = rl->rec[i];
+      }
+    for (i = 0; i < len; ++i) {
+      cdb_pack(htab[i].hval, p + (i << 3));
+      cdb_pack(htab[i].rpos, p + (i << 3) + 4);
+    }
+    if (make_write(cdbmp, p, len << 3) < 0) {
+      free(p);
+      return -1;
+    }
+  }
+  free(p);
+  if (cdbmp->cdb_bpos != cdbmp->cdb_buf &&
+      ewrite(cdbmp->cdb_fd, cdbmp->cdb_buf,
+            cdbmp->cdb_bpos - cdbmp->cdb_buf) != 0)
+      return -1;
+  p = cdbmp->cdb_buf;
+  for (t = 0; t < 256; ++t) {
+    cdb_pack(hpos[t], p + (t << 3));
+    cdb_pack(hcnt[t], p + (t << 3) + 4);
+  }
+  if (lseek(cdbmp->cdb_fd, 0, 0) != 0 ||
+      ewrite(cdbmp->cdb_fd, p, 2048) != 0)
+    return -1;
+
+  return 0;
+}
+
+static void
+cdb_make_free(struct cdb_make *cdbmp)
+{
+  unsigned t;
+  for(t = 0; t < 256; ++t) {
+    struct cdb_rl *rl = cdbmp->cdb_rec[t];
+    while(rl) {
+      struct cdb_rl *tm = rl;
+      rl = rl->next;
+      free(tm);
+    }
+  }
+}
+
+
+
+/* Finalizes database file, constructing all needed indexes, and frees
+   memory structures.  It does not close the file descriptor.  Returns
+   0 on success or a negative value on error. */
+int
+cdb_make_finish(struct cdb_make *cdbmp)
+{
+  int r = cdb_make_finish_internal(cdbmp);
+  cdb_make_free(cdbmp);
+  return r;
+}
+
+
+cdbi_t
+cdb_hash(const void *buf, cdbi_t len)
+{
+  register const unsigned char *p = (const unsigned char *)buf;
+  register const unsigned char *end = p + len;
+  register cdbi_t hash = 5381; /* start value */
+  while (p < end)
+    hash = (hash + (hash << 5)) ^ *p++;
+  return hash;
+}
diff --git a/dirmngr/certcache.c b/dirmngr/certcache.c
new file mode 100644 (file)
index 0000000..c40bb17
--- /dev/null
@@ -0,0 +1,1384 @@
+/* certcache.c - Certificate caching
+ *      Copyright (C) 2004, 2005, 2007, 2008 g10 Code GmbH
+ *
+ * This file is part of DirMngr.
+ *
+ * DirMngr 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * DirMngr 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/>.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <pth.h>
+
+#include "dirmngr.h"
+#include "misc.h"
+#include "crlfetch.h"
+#include "certcache.h"
+
+
+#define MAX_EXTRA_CACHED_CERTS 1000
+
+/* Constants used to classify search patterns.  */
+enum pattern_class 
+  {
+    PATTERN_UNKNOWN = 0,
+    PATTERN_EMAIL,
+    PATTERN_EMAIL_SUBSTR,
+    PATTERN_FINGERPRINT16,
+    PATTERN_FINGERPRINT20,
+    PATTERN_SHORT_KEYID,
+    PATTERN_LONG_KEYID,
+    PATTERN_SUBJECT,
+    PATTERN_SERIALNO,
+    PATTERN_SERIALNO_ISSUER,
+    PATTERN_ISSUER,
+    PATTERN_SUBSTR
+  };
+
+
+/* A certificate cache item.  This consists of a the KSBA cert object
+   and some meta data for easier lookup.  We use a hash table to keep
+   track of all items and use the (randomly distributed) first byte of
+   the fingerprint directly as the hash which makes it pretty easy. */
+struct cert_item_s
+{
+  struct cert_item_s *next; /* Next item with the same hash value. */
+  ksba_cert_t cert;         /* The KSBA cert object or NULL is this is
+                               not a valid item.  */
+  unsigned char fpr[20];    /* The fingerprint of this object. */
+  char *issuer_dn;          /* The malloced issuer DN.  */
+  ksba_sexp_t sn;           /* The malloced serial number  */
+  char *subject_dn;         /* The malloced subject DN - maybe NULL.  */
+  struct 
+  {
+    unsigned int loaded:1;  /* It has been explicitly loaded.  */
+    unsigned int trusted:1; /* This is a trusted root certificate.  */
+  } flags;
+};
+typedef struct cert_item_s *cert_item_t;
+
+/* The actual cert cache consisting of 256 slots for items indexed by
+   the first byte of the fingerprint.  */
+static cert_item_t cert_cache[256];
+
+/* This is the global cache_lock variable. In general looking is not
+   needed but it would take extra efforts to make sure that no
+   indirect use of pth functions is done, so we simply lock it always.
+   Note: We can't use static initialization, as that is not available
+   through w32-pth.  */
+static pth_rwlock_t cert_cache_lock;
+
+/* Flag to track whether the cache has been initialized.  */
+static int initialization_done;
+
+/* Total number of certificates loaded during initialization and
+   cached during operation.  */
+static unsigned int total_loaded_certificates;
+static unsigned int total_extra_certificates;
+
+
+\f
+/* Helper to do the cache locking.  */
+static void
+init_cache_lock (void)
+{
+  if (!pth_rwlock_init (&cert_cache_lock))
+    log_fatal (_("can't initialize certificate cache lock: %s\n"),
+              strerror (errno));
+}
+
+static void
+acquire_cache_read_lock (void)
+{
+  if (!pth_rwlock_acquire (&cert_cache_lock, PTH_RWLOCK_RD, FALSE, NULL))
+    log_fatal (_("can't acquire read lock on the certificate cache: %s\n"),
+               strerror (errno));
+}
+
+static void
+acquire_cache_write_lock (void)
+{
+  if (!pth_rwlock_acquire (&cert_cache_lock, PTH_RWLOCK_RW, FALSE, NULL))
+    log_fatal (_("can't acquire write lock on the certificate cache: %s\n"),
+               strerror (errno));
+}
+
+static void
+release_cache_lock (void)
+{
+  if (!pth_rwlock_release (&cert_cache_lock))
+    log_fatal (_("can't release lock on the certificate cache: %s\n"),
+               strerror (errno));
+}
+
+
+/* Return false if both serial numbers match.  Can't be used for
+   sorting. */
+static int
+compare_serialno (ksba_sexp_t serial1, ksba_sexp_t serial2 )
+{
+  unsigned char *a = serial1;
+  unsigned char *b = serial2;
+  return cmp_simple_canon_sexp (a, b);
+}
+
+
+
+/* Return a malloced canonical S-Expression with the serialnumber
+   converted from the hex string HEXSN.  Return NULL on memory
+   error. */
+ksba_sexp_t 
+hexsn_to_sexp (const char *hexsn)
+{
+  char *buffer, *p;
+  size_t len;
+  char numbuf[40];
+
+  len = unhexify (NULL, hexsn);
+  snprintf (numbuf, sizeof numbuf, "(%u:", (unsigned int)len);
+  buffer = xtrymalloc (strlen (numbuf) + len + 2 );
+  if (!buffer)
+    return NULL;
+  p = stpcpy (buffer, numbuf);
+  len = unhexify (p, hexsn);
+  p[len] = ')';
+  p[len+1] = 0; 
+  
+  return buffer;
+}
+
+
+/* Compute the fingerprint of the certificate CERT and put it into
+   the 20 bytes large buffer DIGEST.  Return address of this buffer.  */
+unsigned char *
+cert_compute_fpr (ksba_cert_t cert, unsigned char *digest)
+{
+  gpg_error_t err;
+  gcry_md_hd_t md;
+
+  err = gcry_md_open (&md, GCRY_MD_SHA1, 0);
+  if (err)
+    log_fatal ("gcry_md_open failed: %s\n", gpg_strerror (err));
+
+  err = ksba_cert_hash (cert, 0, HASH_FNC, md);
+  if (err)
+    {
+      log_error ("oops: ksba_cert_hash failed: %s\n", gpg_strerror (err));
+      memset (digest, 0xff, 20); /* Use a dummy value. */
+    }
+  else
+    {
+      gcry_md_final (md);
+      memcpy (digest, gcry_md_read (md, GCRY_MD_SHA1), 20);
+    }
+  gcry_md_close (md);
+  return digest;
+}
+
+
+/* Cleanup one slot.  This releases all resourses but keeps the actual
+   slot in the cache marked for reuse. */
+static void
+clean_cache_slot (cert_item_t ci)
+{
+  ksba_cert_t cert;
+
+  if (!ci->cert)
+    return; /* Already cleaned.  */
+
+  ksba_free (ci->sn);
+  ci->sn = NULL;
+  ksba_free (ci->issuer_dn);
+  ci->issuer_dn = NULL;
+  ksba_free (ci->subject_dn);
+  ci->subject_dn = NULL;
+  cert = ci->cert;
+  ci->cert = NULL;
+
+  ksba_cert_release (cert);
+}
+
+
+/* Put the certificate CERT into the cache.  It is assumed that the
+   cache is locked while this function is called. If FPR_BUFFER is not
+   NULL the fingerprint of the certificate will be stored there.
+   FPR_BUFFER neds to point to a buffer of at least 20 bytes. The
+   fingerprint will be stored on success or when the function returns
+   gpg_err_code(GPG_ERR_DUP_VALUE). */
+static gpg_error_t
+put_cert (ksba_cert_t cert, int is_loaded, int is_trusted, void *fpr_buffer)
+{
+  unsigned char help_fpr_buffer[20], *fpr;
+  cert_item_t ci;
+
+  fpr = fpr_buffer? fpr_buffer : &help_fpr_buffer;
+
+  /* If we already reached the caching limit, drop a couple of certs
+     from the cache.  Our dropping strategy is simple: We keep a
+     static index counter and use this to start looking for
+     certificates, then we drop 5 percent of the oldest certificates
+     starting at that index.  For a large cache this is a fair way of
+     removing items. An LRU strategy would be better of course.
+     Because we append new entries to the head of the list and we want
+     to remove old ones first, we need to do this from the tail.  The
+     implementation is not very efficient but compared to the long
+     time it takes to retrieve a certifciate from an external resource
+     it seems to be reasonable. */
+  if (!is_loaded && total_extra_certificates >= MAX_EXTRA_CACHED_CERTS)
+    {
+      static int idx;
+      cert_item_t ci_mark;
+      int i;
+      unsigned int drop_count;
+
+      drop_count = MAX_EXTRA_CACHED_CERTS / 20;
+      if (drop_count < 2)
+        drop_count = 2;
+      
+      log_info (_("dropping %u certificates from the cache\n"), drop_count);
+      assert (idx < 256);
+      for (i=idx; drop_count; i = ((i+1)%256))
+        {
+          ci_mark = NULL;
+          for (ci = cert_cache[i]; ci; ci = ci->next)
+            if (ci->cert && !ci->flags.loaded)
+              ci_mark = ci;
+          if (ci_mark)
+            {
+              clean_cache_slot (ci_mark);
+              drop_count--;
+              total_extra_certificates--;
+            }
+        }
+      if (i==idx)
+        idx++;
+      else
+        idx = i;
+      idx %= 256;
+    }
+
+  cert_compute_fpr (cert, fpr);
+  for (ci=cert_cache[*fpr]; ci; ci = ci->next)
+    if (ci->cert && !memcmp (ci->fpr, fpr, 20))
+      return gpg_error (GPG_ERR_DUP_VALUE);          
+  /* Try to reuse an existing entry.  */
+  for (ci=cert_cache[*fpr]; ci; ci = ci->next)
+    if (!ci->cert)
+      break;
+  if (!ci)
+    { /* No: Create a new entry.  */
+      ci = xtrycalloc (1, sizeof *ci);
+      if (!ci)
+        return gpg_error_from_errno (errno);
+      ci->next = cert_cache[*fpr];
+      cert_cache[*fpr] = ci;
+    }
+  else
+    memset (&ci->flags, 0, sizeof ci->flags);
+
+  ksba_cert_ref (cert);
+  ci->cert = cert;
+  memcpy (ci->fpr, fpr, 20);
+  ci->sn = ksba_cert_get_serial (cert);
+  ci->issuer_dn = ksba_cert_get_issuer (cert, 0);
+  if (!ci->issuer_dn || !ci->sn)
+    {
+      clean_cache_slot (ci);
+      return gpg_error (GPG_ERR_INV_CERT_OBJ);
+    }
+  ci->subject_dn = ksba_cert_get_subject (cert, 0);
+  ci->flags.loaded  = !!is_loaded;
+  ci->flags.trusted = !!is_trusted;
+
+  if (is_loaded)
+    total_loaded_certificates++;
+  else
+    total_extra_certificates++;
+
+  return 0;
+}
+
+
+/* Load certificates from the directory DIRNAME.  All certificates
+   matching the pattern "*.crt" or "*.der"  are loaded.  We assume that
+   certificates are DER encoded and not PEM encapsulated. The cache
+   should be in a locked state when calling this fucntion.  */
+static gpg_error_t
+load_certs_from_dir (const char *dirname, int are_trusted)
+{
+  gpg_error_t err;
+  DIR *dir;
+  struct dirent *ep;
+  char *p;
+  size_t n;
+  FILE *fp;
+  ksba_reader_t reader;
+  ksba_cert_t cert;
+  char *fname = NULL;
+
+  dir = opendir (dirname);
+  if (!dir)
+    {
+      if (opt.system_daemon)
+        log_info (_("can't access directory `%s': %s\n"),
+                  dirname, strerror (errno));
+      return 0; /* We do not consider this a severe error.  */
+    }
+
+  while ( (ep=readdir (dir)) )
+    {
+      p = ep->d_name;
+      if (*p == '.' || !*p)
+        continue; /* Skip any hidden files and invalid entries.  */
+      n = strlen (p);
+      if ( n < 5 || (strcmp (p+n-4,".crt") && strcmp (p+n-4,".der")))
+        continue; /* Not the desired "*.crt" or "*.der" pattern.  */
+      
+      xfree (fname);
+      fname = make_filename (dirname, p, NULL);
+      fp = fopen (fname, "rb");
+      if (!fp)
+        {
+          log_error (_("can't open `%s': %s\n"),
+                     fname, strerror (errno));
+          continue;
+        }
+      err = ksba_reader_new (&reader);
+      if (!err)
+        err = ksba_reader_set_file (reader, fp);
+      if (err)
+        {
+          log_error (_("can't setup KSBA reader: %s\n"), gpg_strerror (err));
+          ksba_reader_release (reader);
+          fclose (fp);
+          continue;
+        }
+
+      err = ksba_cert_new (&cert);
+      if (!err)
+        err = ksba_cert_read_der (cert, reader);
+      ksba_reader_release (reader);
+      fclose (fp);
+      if (err)
+        {
+          log_error (_("can't parse certificate `%s': %s\n"),
+                     fname, gpg_strerror (err));
+          ksba_cert_release (cert);
+          continue;
+        }
+
+      err = put_cert (cert, 1, are_trusted, NULL);
+      if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
+        log_info (_("certificate `%s' already cached\n"), fname);
+      else if (!err)
+        {
+          if (are_trusted)
+            log_info (_("trusted certificate `%s' loaded\n"), fname);
+          else
+            log_info (_("certificate `%s' loaded\n"), fname);
+          if (opt.verbose)
+            {
+              p = get_fingerprint_hexstring_colon (cert);
+              log_info (_("  SHA1 fingerprint = %s\n"), p);
+              xfree (p);
+
+              cert_log_name (_("   issuer ="), cert);
+              cert_log_subject (_("  subject ="), cert);
+            }
+        }
+      else
+        log_error (_("error loading certificate `%s': %s\n"),
+                     fname, gpg_strerror (err));
+      ksba_cert_release (cert);
+    }
+
+  xfree (fname);
+  closedir (dir);
+  return 0;
+}
+
+
+/* Initialize the certificate cache if not yet done.  */
+void
+cert_cache_init (void)
+{
+  char *dname;
+  
+  if (initialization_done)
+    return;
+  init_cache_lock ();
+  acquire_cache_write_lock ();
+
+  dname = make_filename (opt.homedir, "trusted-certs", NULL);
+  load_certs_from_dir (dname, 1);
+  xfree (dname);
+
+  dname = make_filename (opt.homedir_data, "extra-certs", NULL);
+  load_certs_from_dir (dname, 0);
+  xfree (dname);
+
+  initialization_done = 1;
+  release_cache_lock ();
+            
+  cert_cache_print_stats ();
+}
+
+/* Deinitialize the certificate cache.  With FULL set to true even the
+   unused certificate slots are released. */
+void
+cert_cache_deinit (int full)
+{
+  cert_item_t ci, ci2;
+  int i;
+
+  if (!initialization_done)
+    return;
+
+  acquire_cache_write_lock ();
+
+  for (i=0; i < 256; i++)
+    for (ci=cert_cache[i]; ci; ci = ci->next)
+      clean_cache_slot (ci);
+
+  if (full)
+    {
+      for (i=0; i < 256; i++)
+        {
+          for (ci=cert_cache[i]; ci; ci = ci2)
+            {
+              ci2 = ci->next;
+              xfree (ci);
+            }
+          cert_cache[i] = NULL;
+        }
+    }
+
+  total_loaded_certificates = 0;
+  total_extra_certificates = 0;
+  initialization_done = 0;
+  release_cache_lock ();
+}
+
+/* Print some statistics to the log file.  */
+void
+cert_cache_print_stats (void)
+{
+  log_info (_("permanently loaded certificates: %u\n"),
+            total_loaded_certificates);
+  log_info (_("    runtime cached certificates: %u\n"),
+            total_extra_certificates);
+}
+
+
+/* Put CERT into the certificate cache.  */
+gpg_error_t
+cache_cert (ksba_cert_t cert)
+{
+  gpg_error_t err;
+
+  acquire_cache_write_lock ();
+  err = put_cert (cert, 0, 0, NULL);
+  release_cache_lock ();
+  if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
+    log_info (_("certificate already cached\n"));
+  else if (!err)
+    log_info (_("certificate cached\n"));
+  else
+    log_error (_("error caching certificate: %s\n"), gpg_strerror (err));
+  return err;
+}
+
+
+/* Put CERT into the certificate cache and store the fingerprint of
+   the certificate into FPR_BUFFER.  If the certificate is already in
+   the cache do not print a warning; just store the
+   fingerprint. FPR_BUFFER needs to be at least 20 bytes. */
+gpg_error_t
+cache_cert_silent (ksba_cert_t cert, void *fpr_buffer)
+{
+  gpg_error_t err;
+
+  acquire_cache_write_lock ();
+  err = put_cert (cert, 0, 0, fpr_buffer);
+  release_cache_lock ();
+  if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
+    err = 0;
+  if (err)
+    log_error (_("error caching certificate: %s\n"), gpg_strerror (err));
+  return err;
+}
+
+
+\f
+/* Return a certificate object for the given fingerprint.  FPR is
+   expected to be a 20 byte binary SHA-1 fingerprint.  If no matching
+   certificate is available in the cache NULL is returned.  The caller
+   must release a returned certificate.  Note that although we are
+   using reference counting the caller should not just compare the
+   pointers to check for identical certificates. */
+ksba_cert_t
+get_cert_byfpr (const unsigned char *fpr)
+{
+  cert_item_t ci;
+
+  acquire_cache_read_lock ();
+  for (ci=cert_cache[*fpr]; ci; ci = ci->next)
+    if (ci->cert && !memcmp (ci->fpr, fpr, 20))
+      {
+        ksba_cert_ref (ci->cert);
+        release_cache_lock ();
+        return ci->cert;
+      }
+
+  release_cache_lock ();
+  return NULL;
+}
+
+/* Return a certificate object for the given fingerprint.  STRING is
+   expected to be a SHA-1 fingerprint in standard hex notation with or
+   without colons.  If no matching certificate is available in the
+   cache NULL is returned.  The caller must release a returned
+   certificate.  Note that although we are using reference counting
+   the caller should not just compare the pointers to check for
+   identical certificates. */
+ksba_cert_t
+get_cert_byhexfpr (const char *string)
+{
+  unsigned char fpr[20];
+  const char *s;
+  int i;
+
+  if (strchr (string, ':'))
+    {
+      for (s=string,i=0; i < 20 && hexdigitp (s) && hexdigitp(s+1);)
+        {
+          if (s[2] && s[2] != ':')
+            break; /* Invalid string. */
+          fpr[i++] = xtoi_2 (s);
+          s += 2;
+          if (i!= 20 && *s == ':')
+            s++;
+        }
+    }
+  else
+    {
+      for (s=string,i=0; i < 20 && hexdigitp (s) && hexdigitp(s+1); s+=2 )
+        fpr[i++] = xtoi_2 (s);
+    }
+  if (i!=20 || *s)
+    {
+      log_error (_("invalid SHA1 fingerprint string `%s'\n"), string);
+      return NULL;
+    }
+
+  return get_cert_byfpr (fpr);
+}
+
+
+
+/* Return the certificate matching ISSUER_DN and SERIALNO.  */
+ksba_cert_t
+get_cert_bysn (const char *issuer_dn, ksba_sexp_t serialno)
+{
+  /* Simple and inefficient implementation.   fixme! */
+  cert_item_t ci;
+  int i;
+
+  acquire_cache_read_lock ();
+  for (i=0; i < 256; i++)
+    {
+      for (ci=cert_cache[i]; ci; ci = ci->next)
+        if (ci->cert && !strcmp (ci->issuer_dn, issuer_dn)
+            && !compare_serialno (ci->sn, serialno))
+          {
+            ksba_cert_ref (ci->cert);
+            release_cache_lock ();
+            return ci->cert;
+          }
+    }
+
+  release_cache_lock ();
+  return NULL;
+}
+
+
+/* Return the certificate matching ISSUER_DN.  SEQ should initially be
+   set to 0 and bumped up to get the next issuer with that DN. */
+ksba_cert_t
+get_cert_byissuer (const char *issuer_dn, unsigned int seq)
+{
+  /* Simple and very inefficient implementation and API.  fixme! */
+  cert_item_t ci;
+  int i;
+
+  acquire_cache_read_lock ();
+  for (i=0; i < 256; i++)
+    {
+      for (ci=cert_cache[i]; ci; ci = ci->next)
+        if (ci->cert && !strcmp (ci->issuer_dn, issuer_dn))
+          if (!seq--)
+            {
+              ksba_cert_ref (ci->cert);
+              release_cache_lock ();
+              return ci->cert;
+            }
+    }
+
+  release_cache_lock ();
+  return NULL;
+}
+
+
+/* Return the certificate matching SUBJECT_DN.  SEQ should initially be
+   set to 0 and bumped up to get the next subject with that DN. */
+ksba_cert_t
+get_cert_bysubject (const char *subject_dn, unsigned int seq)
+{
+  /* Simple and very inefficient implementation and API.  fixme! */
+  cert_item_t ci;
+  int i;
+
+  acquire_cache_read_lock ();
+  for (i=0; i < 256; i++)
+    {
+      for (ci=cert_cache[i]; ci; ci = ci->next)
+        if (ci->cert && ci->subject_dn
+            && !strcmp (ci->subject_dn, subject_dn))
+          if (!seq--)
+            {
+              ksba_cert_ref (ci->cert);
+              release_cache_lock ();
+              return ci->cert;
+            }
+    }
+
+  release_cache_lock ();
+  return NULL;
+}
+
+
+
+/* Return a value decribing the the class of PATTERN.  The offset of
+   the actual string to be used for the comparison is stored at
+   R_OFFSET.  The offset of the serialnumer is stored at R_SN_OFFSET. */
+static enum pattern_class
+classify_pattern (const char *pattern, size_t *r_offset, size_t *r_sn_offset)
+{
+  enum pattern_class result = PATTERN_UNKNOWN;
+  const char *s;
+  int hexprefix = 0;
+  int hexlength;
+  int mode = 0;   
+    
+  *r_offset = *r_sn_offset = 0;
+
+  /* Skip leading spaces. */
+  for(s = pattern; *s && spacep (s); s++ )
+    ;
+
+  switch (*s) 
+    {
+    case 0:  /* Empty string is an error. */
+      result = PATTERN_UNKNOWN;
+      break;
+
+    case '.': /* An email address, compare from end.  */
+      result = PATTERN_UNKNOWN;  /* Not implemented.  */
+      break;
+
+    case '<': /* An email address.  */
+      result = PATTERN_EMAIL;
+      s++;
+      break;
+
+    case '@': /* Part of an email address.  */
+      result = PATTERN_EMAIL_SUBSTR;
+      s++;
+      break;
+
+    case '=':  /* Exact compare. */
+      result = PATTERN_UNKNOWN; /* Does not make sense for X.509.  */
+      break;
+
+    case '*':  /* Case insensitive substring search.  */
+      mode = PATTERN_SUBSTR;
+      s++;
+      break;
+
+    case '+':  /* Compare individual words. */
+      result = PATTERN_UNKNOWN;  /* Not implemented.  */
+      break;
+
+    case '/': /* Subject's DN. */
+      s++;
+      if (!*s || spacep (s))
+        result = PATTERN_UNKNOWN; /* No DN or prefixed with a space. */
+      else
+        result = PATTERN_SUBJECT;
+      break;
+
+    case '#': /* Serial number or issuer DN. */
+      { 
+        const char *si;
+        
+        s++;
+        if ( *s == '/')
+          { 
+            /* An issuer's DN is indicated by "#/" */
+            s++;
+            if (!*s || spacep (s))
+              result = PATTERN_UNKNOWN; /* No DN or prefixed with a space. */
+            else
+              result = PATTERN_ISSUER;
+          }
+        else 
+          { /* Serialnumber + optional issuer ID. */
+            for (si=s; *si && *si != '/'; si++)
+              if (!strchr("01234567890abcdefABCDEF", *si))
+                break;
+            if (*si && *si != '/')
+              result = PATTERN_UNKNOWN; /* Invalid digit in serial number. */
+            else
+              {
+                *r_sn_offset = s - pattern;
+                if (!*si)
+                  result = PATTERN_SERIALNO;
+                else
+                  {
+                    s = si+1;
+                    if (!*s || spacep (s))
+                      result = PATTERN_UNKNOWN; /* No DN or prefixed
+                                                   with a space. */
+                    else
+                      result = PATTERN_SERIALNO_ISSUER;
+                  }
+              }
+          }
+      }
+      break;
+
+    case ':': /* Unified fingerprint. */
+      {  
+        const char *se, *si;
+        int i;
+        
+        se = strchr (++s, ':');
+        if (!se)
+          result = PATTERN_UNKNOWN;
+        else
+          {
+            for (i=0, si=s; si < se; si++, i++ )
+              if (!strchr("01234567890abcdefABCDEF", *si))
+                break;
+            if ( si < se )
+              result = PATTERN_UNKNOWN; /* Invalid digit. */
+            else if (i == 32)
+              result = PATTERN_FINGERPRINT16;
+            else if (i == 40)
+              result = PATTERN_FINGERPRINT20;
+            else
+              result = PATTERN_UNKNOWN; /* Invalid length for a fingerprint. */
+          }
+      } 
+      break;
+
+    case '&': /* Keygrip. */
+      result = PATTERN_UNKNOWN;  /* Not implemented.  */
+      break;
+
+    default:
+      if (s[0] == '0' && s[1] == 'x')
+        {
+          hexprefix = 1;
+          s += 2;
+        }
+
+      hexlength = strspn(s, "0123456789abcdefABCDEF");
+
+      /* Check if a hexadecimal number is terminated by EOS or blank. */
+      if (hexlength && s[hexlength] && !spacep (s+hexlength)) 
+        {
+          /* If the "0x" prefix is used a correct termination is required. */
+          if (hexprefix) 
+            {
+              result = PATTERN_UNKNOWN;   
+              break; /* switch */
+            }
+          hexlength = 0;  /* Not a hex number.  */
+        }
+      
+      if (hexlength == 8 || (!hexprefix && hexlength == 9 && *s == '0'))
+        { 
+          if (hexlength == 9)
+            s++;
+          result = PATTERN_SHORT_KEYID;
+        }
+      else if (hexlength == 16 || (!hexprefix && hexlength == 17 && *s == '0'))
+        { 
+          if (hexlength == 17)
+            s++;
+          result = PATTERN_LONG_KEYID;
+        }
+      else if (hexlength == 32 || (!hexprefix && hexlength == 33 && *s == '0'))
+        { 
+          if (hexlength == 33)
+            s++;
+          result = PATTERN_FINGERPRINT16;
+        }
+      else if (hexlength == 40 || (!hexprefix && hexlength == 41 && *s == '0'))
+        { 
+          if (hexlength == 41)
+            s++;
+          result = PATTERN_FINGERPRINT20;
+        }
+      else if (!hexprefix)
+        { 
+          /* The fingerprints used with X.509 are often delimited by
+             colons, so we try to single this case out. */
+          result = PATTERN_UNKNOWN;
+          hexlength = strspn (s, ":0123456789abcdefABCDEF");
+          if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength))) 
+            {
+              int i, c;
+
+              for (i=0; i < 20; i++, s += 3) 
+                {
+                  c = hextobyte(s);
+                  if (c == -1 || (i < 19 && s[2] != ':'))
+                    break;
+                }
+              if (i == 20)
+                result = PATTERN_FINGERPRINT20;
+            }
+          if (result == PATTERN_UNKNOWN) /* Default to substring match. */
+            { 
+              result = PATTERN_SUBSTR;
+            }
+        }
+      else /* A hex number with a prefix but with a wrong length.  */
+        result = PATTERN_UNKNOWN;
+    }
+  
+  if (result != PATTERN_UNKNOWN)
+    *r_offset = s - pattern;
+  return result;
+}
+
+
+
+/* Given PATTERN, which is a string as used by GnuPG to specify a
+   certificate, return all matching certificates by calling the
+   supplied function RETFNC.  */
+gpg_error_t
+get_certs_bypattern (const char *pattern, 
+                     gpg_error_t (*retfnc)(void*,ksba_cert_t),
+                     void *retfnc_data)
+{
+  gpg_error_t err = GPG_ERR_BUG;
+  enum pattern_class class;
+  size_t offset, sn_offset;
+  const char *hexserialno;
+  ksba_sexp_t serialno = NULL;
+  ksba_cert_t cert = NULL;
+  unsigned int seq;
+
+  if (!pattern || !retfnc)
+    return gpg_error (GPG_ERR_INV_ARG);
+
+  class = classify_pattern (pattern, &offset, &sn_offset);
+  hexserialno = pattern + sn_offset;
+  pattern += offset;
+  switch (class)
+    {
+    case PATTERN_UNKNOWN: 
+      err = gpg_error (GPG_ERR_INV_NAME);
+      break;
+
+    case PATTERN_FINGERPRINT20:
+      cert = get_cert_byhexfpr (pattern);
+      err = cert? 0 : gpg_error (GPG_ERR_NOT_FOUND);
+      break;
+
+    case PATTERN_SERIALNO_ISSUER:
+      serialno = hexsn_to_sexp (hexserialno);
+      if (!serialno)
+        err = gpg_error_from_syserror ();
+      else
+        {
+          cert = get_cert_bysn (pattern, serialno);
+          err = cert? 0 : gpg_error (GPG_ERR_NOT_FOUND);
+        }
+      break;
+
+    case PATTERN_ISSUER:
+      for (seq=0,err=0; !err && (cert = get_cert_byissuer (pattern, seq)); seq++)
+        {
+          err = retfnc (retfnc_data, cert);
+          ksba_cert_release (cert);
+          cert = NULL;
+        }
+      if (!err && !seq)
+        err = gpg_error (GPG_ERR_NOT_FOUND);
+      break;
+
+    case PATTERN_SUBJECT:
+      for (seq=0,err=0; !err && (cert = get_cert_bysubject (pattern, seq));seq++)
+        {
+          err = retfnc (retfnc_data, cert);
+          ksba_cert_release (cert);
+          cert = NULL;
+        }
+      if (!err && !seq)
+        err = gpg_error (GPG_ERR_NOT_FOUND);
+      break;
+      
+    case PATTERN_EMAIL:
+    case PATTERN_EMAIL_SUBSTR:
+    case PATTERN_FINGERPRINT16:
+    case PATTERN_SHORT_KEYID:
+    case PATTERN_LONG_KEYID:
+    case PATTERN_SUBSTR:
+    case PATTERN_SERIALNO:
+      /* Not supported.  */
+      err = gpg_error (GPG_ERR_INV_NAME);
+    }
+
+
+  if (!err && cert)
+    err = retfnc (retfnc_data, cert);
+  ksba_cert_release (cert);
+  xfree (serialno);
+  return err;
+}
+
+
+
+
+\f
+/* Return the certificate matching ISSUER_DN and SERIALNO; if it is
+   not already in the cache, try to find it from other resources.  */
+ksba_cert_t
+find_cert_bysn (ctrl_t ctrl, const char *issuer_dn, ksba_sexp_t serialno)
+{
+  gpg_error_t err;
+  ksba_cert_t cert;
+  cert_fetch_context_t context = NULL;
+  char *hexsn, *buf;
+
+  /* First check whether it has already been cached.  */
+  cert = get_cert_bysn (issuer_dn, serialno);
+  if (cert)
+    return cert;
+
+  /* Ask back to the service requester to return the certificate.
+     This is because we can assume that he already used the
+     certificate while checking for the CRL. */
+  hexsn = serial_hex (serialno);
+  if (!hexsn)
+    {
+      log_error ("serial_hex() failed\n");
+      return NULL;
+    }
+  buf = xtrymalloc (1 + strlen (hexsn) + 1 + strlen (issuer_dn) + 1);
+  if (!buf)
+    {
+      log_error ("can't allocate enough memory: %s\n", strerror (errno));
+      xfree (hexsn);
+      return NULL;
+    }
+  strcpy (stpcpy (stpcpy (stpcpy (buf, "#"), hexsn),"/"), issuer_dn);
+  xfree (hexsn);
+  cert = get_cert_local (ctrl, buf);
+  xfree (buf);
+  if (cert)
+    {
+      cache_cert (cert);
+      return cert; /* Done. */
+    }
+
+  if (DBG_LOOKUP)
+    log_debug ("find_cert_bysn: certificate not returned by caller"
+               " - doing lookup\n");
+
+  /* Retrieve the certificate from external resources. */
+  while (!cert)
+    {
+      ksba_sexp_t sn;
+      char *issdn;
+
+      if (!context)
+        {
+          err = ca_cert_fetch (ctrl, &context, issuer_dn);
+          if (err)
+            {
+              log_error (_("error fetching certificate by S/N: %s\n"),
+                         gpg_strerror (err));
+              break;
+            }
+        }
+      
+      err = fetch_next_ksba_cert (context, &cert);
+      if (err)
+        {
+          log_error (_("error fetching certificate by S/N: %s\n"),
+                     gpg_strerror (err) );
+          break;
+        }
+      
+      issdn = ksba_cert_get_issuer (cert, 0);
+      if (strcmp (issuer_dn, issdn))
+        {
+          log_debug ("find_cert_bysn: Ooops: issuer DN does not match\n");
+          ksba_cert_release (cert);
+          cert = NULL;
+          ksba_free (issdn);
+          break; 
+        }
+
+      sn = ksba_cert_get_serial (cert);
+
+      if (DBG_LOOKUP)
+        {
+          log_debug ("   considering certificate (#");
+          dump_serial (sn);
+          log_printf ("/");
+          dump_string (issdn);
+          log_printf (")\n");
+        }
+
+      if (!compare_serialno (serialno, sn))
+        {
+          ksba_free (sn);
+          ksba_free (issdn);
+          cache_cert (cert);
+          if (DBG_LOOKUP)
+            log_debug ("   found\n");
+          break; /* Ready.  */
+        }
+
+      ksba_free (sn);
+      ksba_free (issdn);
+      ksba_cert_release (cert);
+      cert = NULL;
+    }
+
+  end_cert_fetch (context);
+  return cert;
+}
+
+
+/* Return the certificate matching SUBJECT_DN and (if not NULL)
+   KEYID. If it is not already in the cache, try to find it from other
+   resources.  Note, that the external search does not work for user
+   certificates because the LDAP lookup is on the caCertificate
+   attribute. For our purposes this is just fine.  */
+ksba_cert_t
+find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid)
+{
+  gpg_error_t err;
+  int seq;
+  ksba_cert_t cert = NULL;
+  cert_fetch_context_t context = NULL;
+  ksba_sexp_t subj;
+
+  /* If we have certificates from an OCSP request we first try to use
+     them.  This is because these certificates will really be the
+     required ones and thus even in the case that they can't be
+     uniquely located by the following code we can use them.  This is
+     for example required by Telesec certificates where a keyId is
+     used but the issuer certificate comes without a subject keyId! */
+  if (ctrl->ocsp_certs)
+    {
+      cert_item_t ci;
+      cert_ref_t cr;
+      int i;
+
+      /* For efficiency reasons we won't use get_cert_bysubject here. */
+      acquire_cache_read_lock ();
+      for (i=0; i < 256; i++)
+        for (ci=cert_cache[i]; ci; ci = ci->next)
+          if (ci->cert && ci->subject_dn
+              && !strcmp (ci->subject_dn, subject_dn))
+            for (cr=ctrl->ocsp_certs; cr; cr = cr->next)
+              if (!memcmp (ci->fpr, cr->fpr, 20))
+                {
+                  ksba_cert_ref (ci->cert);
+                  release_cache_lock ();
+                  return ci->cert; /* We use this certificate. */
+                }
+      release_cache_lock ();
+      if (DBG_LOOKUP)
+        log_debug ("find_cert_bysubject: certificate not in ocsp_certs\n");
+    }
+
+
+  /* First we check whether the certificate is cached.  */
+  for (seq=0; (cert = get_cert_bysubject (subject_dn, seq)); seq++)
+    {
+      if (!keyid)
+        break; /* No keyid requested, so return the first one found. */
+      if (!ksba_cert_get_subj_key_id (cert, NULL, &subj)
+          && !cmp_simple_canon_sexp (keyid, subj))
+        {
+          xfree (subj);
+          break; /* Found matching cert. */
+        }
+      xfree (subj);
+      ksba_cert_release (cert);
+    }
+  if (cert)
+    return cert; /* Done.  */
+
+  if (DBG_LOOKUP)
+    log_debug ("find_cert_bysubject: certificate not in cache\n");
+
+  /* Ask back to the service requester to return the certificate.
+     This is because we can assume that he already used the
+     certificate while checking for the CRL. */
+  if (keyid)
+    cert = get_cert_local_ski (ctrl, subject_dn, keyid);
+  else
+    {
+      /* In contrast to get_cert_local_ski, get_cert_local uses any
+         passed pattern, so we need to make sure that an exact subject
+         search is done. */
+      char *buf;
+
+      buf = xtrymalloc (1 + strlen (subject_dn) + 1);
+      if (!buf)
+        {
+          log_error ("can't allocate enough memory: %s\n", strerror (errno));
+          return NULL;
+        }
+      strcpy (stpcpy (buf, "/"), subject_dn);
+      cert = get_cert_local (ctrl, buf);
+      xfree (buf);
+    }
+  if (cert)
+    {
+      cache_cert (cert);
+      return cert; /* Done. */
+    }
+
+  if (DBG_LOOKUP)
+    log_debug ("find_cert_bysubject: certificate not returned by caller"
+               " - doing lookup\n");
+
+  /* Locate the certificate using external resources. */
+  while (!cert)
+    {
+      char *subjdn;
+
+      if (!context)
+        {
+          err = ca_cert_fetch (ctrl, &context, subject_dn);
+          if (err)
+            {
+              log_error (_("error fetching certificate by subject: %s\n"),
+                         gpg_strerror (err));
+              break;
+            }
+        }
+      
+      err = fetch_next_ksba_cert (context, &cert);
+      if (err)
+        {
+          log_error (_("error fetching certificate by subject: %s\n"),
+                     gpg_strerror (err) );
+          break;
+        }
+      
+      subjdn = ksba_cert_get_subject (cert, 0);
+      if (strcmp (subject_dn, subjdn))
+        {
+          log_info ("find_cert_bysubject: subject DN does not match\n");
+          ksba_cert_release (cert);
+          cert = NULL;
+          ksba_free (subjdn);
+          continue; 
+        }
+
+
+      if (DBG_LOOKUP)
+        {
+          log_debug ("   considering certificate (/");
+          dump_string (subjdn);
+          log_printf (")\n");
+        }
+      ksba_free (subjdn);
+
+      /* If no key ID has been provided, we return the first match.  */
+      if (!keyid)
+        {
+          cache_cert (cert);
+          if (DBG_LOOKUP)
+            log_debug ("   found\n");
+          break; /* Ready.  */
+        }
+
+      /* With the key ID given we need to compare it.  */
+      if (!ksba_cert_get_subj_key_id (cert, NULL, &subj))
+        {
+          if (!cmp_simple_canon_sexp (keyid, subj))
+            {
+              ksba_free (subj);
+              cache_cert (cert);
+              if (DBG_LOOKUP)
+                log_debug ("   found\n");
+              break; /* Ready.  */
+            }
+        }
+
+      ksba_free (subj);
+      ksba_cert_release (cert);
+      cert = NULL;
+    }
+
+  end_cert_fetch (context);
+  return cert;
+}
+
+
+
+/* Return 0 if the certificate is a trusted certificate. Returns
+   GPG_ERR_NOT_TRUSTED if it is not trusted or other error codes in
+   case of systems errors. */
+gpg_error_t 
+is_trusted_cert (ksba_cert_t cert)
+{
+  unsigned char fpr[20];
+  cert_item_t ci;
+
+  cert_compute_fpr (cert, fpr);
+
+  acquire_cache_read_lock ();
+  for (ci=cert_cache[*fpr]; ci; ci = ci->next)
+    if (ci->cert && !memcmp (ci->fpr, fpr, 20))
+      {
+        if (ci->flags.trusted)
+          {
+            release_cache_lock ();
+            return 0; /* Yes, it is trusted. */
+          }
+        break;
+      }
+
+  release_cache_lock ();
+  return gpg_error (GPG_ERR_NOT_TRUSTED);
+}
+
+
+\f
+/* Given the certificate CERT locate the issuer for this certificate
+   and return it at R_CERT.  Returns 0 on success or
+   GPG_ERR_NOT_FOUND.  */
+gpg_error_t
+find_issuing_cert (ctrl_t ctrl, ksba_cert_t cert, ksba_cert_t *r_cert)
+{
+  gpg_error_t err;
+  char *issuer_dn;
+  ksba_cert_t issuer_cert = NULL;
+  ksba_name_t authid;
+  ksba_sexp_t authidno;
+  ksba_sexp_t keyid;
+
+  *r_cert = NULL;
+
+  issuer_dn = ksba_cert_get_issuer (cert, 0);
+  if (!issuer_dn)
+    {
+      log_error (_("no issuer found in certificate\n"));
+      err = gpg_error (GPG_ERR_BAD_CERT);
+      goto leave;
+    }
+
+  /* First we need to check whether we can return that certificate
+     using the authorithyKeyIdentifier.  */
+  err = ksba_cert_get_auth_key_id (cert, &keyid, &authid, &authidno);
+  if (err)
+    {
+      log_info (_("error getting authorityKeyIdentifier: %s\n"),
+                gpg_strerror (err));
+    }
+  else
+    {
+      const char *s = ksba_name_enum (authid, 0);
+      if (s && *authidno)
+        {
+          issuer_cert = find_cert_bysn (ctrl, s, authidno);
+        }
+      if (!issuer_cert && keyid)
+        {
+          /* Not found by issuer+s/n.  Now that we have an AKI
+             keyIdentifier look for a certificate with a matching
+             SKI. */
+          issuer_cert = find_cert_bysubject (ctrl, issuer_dn, keyid);
+        }
+      /* Print a note so that the user does not feel too helpless when
+         an issuer certificate was found and gpgsm prints BAD
+         signature because it is not the correct one. */
+      if (!issuer_cert)
+        {
+          log_info ("issuer certificate ");
+          if (keyid)
+            {
+              log_printf ("{");
+              dump_serial (keyid);
+              log_printf ("} ");
+            }
+          if (authidno)
+            {
+              log_printf ("(#");
+              dump_serial (authidno);
+              log_printf ("/");
+              dump_string (s);
+              log_printf (") ");
+            }
+          log_printf ("not found using authorityKeyIdentifier\n");
+        }
+      ksba_name_release (authid);
+      xfree (authidno);
+      xfree (keyid);
+    }
+
+  /* If this did not work, try just with the issuer's name and assume
+     that there is only one such certificate.  We only look into our
+     cache then. */
+  if (err || !issuer_cert)
+    {
+      issuer_cert = get_cert_bysubject (issuer_dn, 0);
+      if (issuer_cert)
+        err = 0;
+    }
+
+ leave:  
+  if (!err && !issuer_cert)
+    err = gpg_error (GPG_ERR_NOT_FOUND);
+
+  xfree (issuer_dn);
+
+  if (err)
+    ksba_cert_release (issuer_cert);
+  else
+    *r_cert = issuer_cert;
+
+  return err;
+}
+
diff --git a/dirmngr/certcache.h b/dirmngr/certcache.h
new file mode 100644 (file)
index 0000000..2b7aeaf
--- /dev/null
@@ -0,0 +1,103 @@
+/* certcache.h - Certificate caching
+ *      Copyright (C) 2004, 2008 g10 Code GmbH
+ *
+ * This file is part of DirMngr.
+ *
+ * DirMngr 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * DirMngr 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef CERTCACHE_H
+#define CERTCACHE_H
+
+/* First time initialization of the certificate cache.  */
+void cert_cache_init (void);
+
+/* Deinitialize the certificate cache.  */
+void cert_cache_deinit (int full);
+
+/* Print some statistics to the log file.  */
+void cert_cache_print_stats (void);
+
+/* Compute the fingerprint of the certificate CERT and put it into
+   the 20 bytes large buffer DIGEST.  Return address of this buffer.  */
+unsigned char *cert_compute_fpr (ksba_cert_t cert, unsigned char *digest);
+
+/* Put CERT into the certificate cache.  */
+gpg_error_t cache_cert (ksba_cert_t cert);
+
+/* Put CERT into the certificate cache and return the fingerprint. */
+gpg_error_t cache_cert_silent (ksba_cert_t cert, void *fpr_buffer);
+
+/* Return 0 if the certificate is a trusted certificate. Returns
+   GPG_ERR_NOT_TRUSTED if it is not trusted or other error codes in
+   case of systems errors. */
+gpg_error_t is_trusted_cert (ksba_cert_t cert);
+
+
+/* Return a certificate object for the given fingerprint.  FPR is
+   expected to be a 20 byte binary SHA-1 fingerprint.  If no matching
+   certificate is available in the cache NULL is returned.  The caller
+   must release a returned certificate.  */
+ksba_cert_t get_cert_byfpr (const unsigned char *fpr);
+
+/* Return a certificate object for the given fingerprint.  STRING is
+   expected to be a SHA-1 fingerprint in standard hex notation with or
+   without colons.  If no matching certificate is available in the
+   cache NULL is returned.  The caller must release a returned
+   certificate.  */
+ksba_cert_t get_cert_byhexfpr (const char *string);
+
+/* Return the certificate matching ISSUER_DN and SERIALNO.  */
+ksba_cert_t get_cert_bysn (const char *issuer_dn, ksba_sexp_t serialno);
+
+/* Return the certificate matching ISSUER_DN.  SEQ should initially be
+   set to 0 and bumped up to get the next issuer with that DN. */
+ksba_cert_t get_cert_byissuer (const char *issuer_dn, unsigned int seq);
+
+/* Return the certificate matching SUBJECT_DN.  SEQ should initially be
+   set to 0 and bumped up to get the next issuer with that DN. */
+ksba_cert_t get_cert_bysubject (const char *subject_dn, unsigned int seq);
+
+/* Given PATTERN, which is a string as used by GnuPG to specify a
+   certificate, return all matching certificates by calling the
+   supplied function RETFNC.  */
+gpg_error_t get_certs_bypattern (const char *pattern, 
+                                 gpg_error_t (*retfnc)(void*,ksba_cert_t),
+                                 void *retfnc_data);
+
+/* Return the certificate matching ISSUER_DN and SERIALNO; if it is
+   not already in the cache, try to find it from other resources.  */
+ksba_cert_t find_cert_bysn (ctrl_t ctrl,
+                            const char *issuer_dn, ksba_sexp_t serialno);
+
+
+/* Return the certificate matching SUBJECT_DN and (if not NULL) KEYID. If
+   it is not already in the cache, try to find it from other
+   resources.  Note, that the external search does not work for user
+   certificates because the LDAP lookup is on the caCertificate
+   attribute. For our purposes this is just fine.  */
+ksba_cert_t find_cert_bysubject (ctrl_t ctrl,
+                                 const char *subject_dn, ksba_sexp_t keyid);
+
+/* Given the certificate CERT locate the issuer for this certificate
+   and return it at R_CERT.  Returns 0 on success or
+   GPG_ERR_NOT_FOUND.  */
+gpg_error_t find_issuing_cert (ctrl_t ctrl,
+                               ksba_cert_t cert, ksba_cert_t *r_cert);
+
+
+
+
+#endif /*CERTCACHE_H*/
diff --git a/dirmngr/crlcache.c b/dirmngr/crlcache.c
new file mode 100644 (file)
index 0000000..9ec5414
--- /dev/null
@@ -0,0 +1,2544 @@
+/* crlcache.c - LDAP access
+ * Copyright (C) 2002 Klarälvdalens Datakonsult AB
+ * Copyright (C) 2003, 2004, 2005, 2008 g10 Code GmbH
+ *
+ * This file is part of DirMngr.
+ *
+ * DirMngr 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * DirMngr 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/>.
+ */
+
+/* 
+
+   1. To keep track of the CRLs actually cached and to store the meta
+      information of the CRLs a simple record oriented text file is
+      used.  Fields in the file are colon (':') separated and values
+      containing colons or linefeeds are percent escaped (e.g. a colon
+      itself is represented as "%3A"). 
+
+      The first field is a record type identifier, so that the file is
+      useful to keep track of other meta data too.  
+
+      The name of the file is "DIR.txt".
+
+
+   1.1. Comment record
+
+        Field 1: Constant beginning with "#".
+
+        Other fields are not defined and such a record is simply
+        skipped during processing.
+
+   1.2. Version record
+
+        Field 1: Constant "v"
+        Field 2: Version number of this file.  Must be 1.
+
+        This record must be the first non-comment record record and
+        there shall only exist one record of this type.
+
+   1.3. CRL cache record
+
+        Field 1: Constant "c", "u" or "i".
+                 A "c" or "u" indicate a valid cache entry, however
+                 "u" requires that a user root certificate check needs
+                 to be done.
+                 An "i" indicates an invalid Cache entry which should
+                 not be used but still exists so that it can be
+                 updated at NEXT_UPDATE. 
+        Field 2: Hexadecimal encoded SHA-1 hash of the issuer DN using
+                 uppercase letters.
+        Field 3: Issuer DN in RFC-2253 notation.
+        Field 4: URL used to retrieve the corresponding CRL.
+        Field 5: 15 character ISO timestamp with THIS_UPDATE.
+        Field 6: 15 character ISO timestamp with NEXT_UPDATE.
+        Field 7: Hexadecimal encoded MD-5 hash of the DB file to detect
+                 accidental modified (i.e. deleted and created) cache files.
+        Field 8: optional CRL number as a hex string.
+        Field 9:  AuthorityKeyID.issuer, each Name separated by 0x01
+        Field 10: AuthorityKeyID.serial
+        Field 11: Hex fingerprint of trust anchor if field 1 is 'u'.
+
+   2. Layout of the standard CRL Cache DB file:
+
+      We use records of variable length with this structure
+
+      n  bytes  Serialnumber (binary) used as key
+                thus there is no need to store the length explicitly with DB2.
+      1  byte   Reason for revocation 
+                (currently the KSBA reason flags are used)
+      15 bytes  ISO date of revocation (e.g. 19980815T142000)
+                Note that there is no terminating 0 stored.
+
+      The filename used is the hexadecimal (using uppercase letters)
+      SHA-1 hash value of the issuer DN prefixed with a "crl-" and
+      suffixed with a ".db".  Thus the length of the filename is 47.
+      
+
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#ifndef HAVE_W32_SYSTEM
+#include <sys/utsname.h>
+#endif
+#ifdef MKDIR_TAKES_ONE_ARG
+#undef mkdir
+#define mkdir(a,b) mkdir(a)
+#endif
+
+#include "dirmngr.h"
+#include "validate.h"
+#include "certcache.h"
+#include "crlcache.h"
+#include "crlfetch.h"
+#include "misc.h"
+#include "cdb.h"
+#include "estream-printf.h"
+
+/* Change this whenever the format changes */
+#define DBDIR_D (opt.system_daemon? "crls.d" : "dirmngr-cache.d")
+#define DBDIRFILE "DIR.txt"
+#define DBDIRVERSION 1
+
+/* The number of DB files we may have open at one time.  We need to
+   limit this because there is no guarantee that the number of issuers
+   has a upper limit.  We are currently using mmap, so it is a good
+   idea anyway to limit the number of opened cache files. */
+#define MAX_OPEN_DB_FILES 5
+
+
+static const char oidstr_crlNumber[] = "2.5.29.20";
+static const char oidstr_issuingDistributionPoint[] = "2.5.29.28";
+static const char oidstr_authorityKeyIdentifier[] = "2.5.29.35";
+
+
+/* Definition of one cached item. */
+struct crl_cache_entry_s 
+{
+  struct crl_cache_entry_s *next;
+  int deleted;        /* True if marked for deletion. */
+  int mark;           /* Internally used by update_dir. */
+  unsigned int lineno;/* A 0 indicates a new entry. */
+  char *release_ptr;  /* The actual allocated memory. */
+  char *url;          /* Points into RELEASE_PTR. */
+  char *issuer;       /* Ditto. */
+  char *issuer_hash;  /* Ditto. */
+  char *dbfile_hash;  /* MD5 sum of the cache file, points into RELEASE_PTR.*/
+  int invalid;        /* Can't use this CRL. */
+  int user_trust_req; /* User supplied root certificate required.  */
+  char *check_trust_anchor;  /* Malloced fingerprint.  */
+  ksba_isotime_t this_update;
+  ksba_isotime_t next_update;
+  ksba_isotime_t last_refresh; /* Use for the force_crl_refresh feature. */
+  char *crl_number;
+  char *authority_issuer;
+  char *authority_serialno;
+
+  struct cdb *cdb;             /* The cache file handle or NULL if not open. */
+
+  unsigned int cdb_use_count;  /* Current use count. */
+  unsigned int cdb_lru_count;  /* Used for LRU purposes. */
+  int dbfile_checked;          /* Set to true if the dbfile_hash value has
+                                  been checked one. */
+};
+
+
+/* Definition of the entire cache object. */
+struct crl_cache_s 
+{
+  crl_cache_entry_t entries;
+};
+
+typedef struct crl_cache_s *crl_cache_t;
+
+
+/* Prototypes.  */
+static crl_cache_entry_t find_entry (crl_cache_entry_t first,
+                                     const char *issuer_hash);
+
+
+
+/* The currently loaded cache object.  This isi usually initialized
+   right at startup.  */
+static crl_cache_t current_cache;
+
+
+
+
+\f
+/* Return the current cache object or bail out if it is has not yet
+   been initialized.  */
+static crl_cache_t
+get_current_cache (void)
+{
+  if (!current_cache)
+    log_fatal ("CRL cache has not yet been initialized\n");
+  return current_cache;
+}
+
+
+/* 
+   Create ae directory if it does not yet exists.  Returns on
+   success, or -1 on error.
+ */
+static int
+create_directory_if_needed (const char *name)
+{
+  DIR *dir;
+  char *fname;
+
+  fname = make_filename (opt.homedir_cache, name, NULL);
+  dir = opendir (fname);
+  if (!dir)
+    {
+      log_info (_("creating directory `%s'\n"), fname);
+      if (mkdir (fname, S_IRUSR|S_IWUSR|S_IXUSR) )
+        {
+          int save_errno = errno;
+          log_error (_("error creating directory `%s': %s\n"),
+                     fname, strerror (errno));
+          xfree (fname);
+          errno = save_errno;
+          return -1;
+        }
+    } 
+  else
+    closedir (dir);
+  xfree (fname);
+  return 0;
+}
+
+/* Remove all files from the cache directory.  If FORCE is not true,
+   some sanity checks on the filenames are done. Return 0 if
+   everything went fine. */
+static int
+cleanup_cache_dir (int force)
+{
+  char *dname = make_filename (opt.homedir_cache, DBDIR_D, NULL);
+  DIR *dir;
+  struct dirent *de;
+  int problem = 0;
+
+  if (!force)
+    { /* Very minor sanity checks. */
+      if (!strcmp (dname, "~/") || !strcmp (dname, "/" ))
+        {
+          log_error (_("ignoring database dir `%s'\n"), dname);
+          xfree (dname);
+          return -1;
+        }
+    }
+
+  dir = opendir (dname);
+  if (!dir)
+    {
+      log_error (_("error reading directory `%s': %s\n"),
+                 dname, strerror (errno));
+      xfree (dname);
+      return -1;
+    }
+
+  while ((de = readdir (dir)))
+    {
+      if (strcmp (de->d_name, "." ) && strcmp (de->d_name, ".."))
+        {
+          char *cdbname = make_filename (dname, de->d_name, NULL);
+          int okay;
+          struct stat sbuf;
+     
+          if (force)
+            okay = 1;
+          else
+            okay = (!stat (cdbname, &sbuf) && S_ISREG (sbuf.st_mode));
+
+          if (okay)
+            {
+              log_info (_("removing cache file `%s'\n"), cdbname);
+              if (unlink (cdbname))
+                {
+                  log_error ("failed to remove `%s': %s\n",
+                             cdbname, strerror (errno));
+                  problem = -1;
+                }
+            }
+          else
+            log_info (_("not removing file `%s'\n"), cdbname);
+          xfree (cdbname);
+        }
+    }    
+  xfree (dname);
+  closedir (dir);
+  return problem;
+}
+
+
+/* Read the next line from the file FP and return the line in an
+   malloced buffer.  Return NULL on error or EOF.  There is no
+   limitation os the line length.  The trailing linefeed has been
+   removed, the function will read the last line of a file, even if
+   that is not terminated by a LF. */
+static char *
+next_line_from_file (FILE *fp, gpg_error_t *r_err)
+{
+  char buf[300];
+  char *largebuf = NULL;
+  size_t buflen;
+  size_t len = 0;
+  unsigned char *p;
+  int c;
+  char *tmpbuf;
+
+  *r_err = 0;
+  p = buf;
+  buflen = sizeof buf - 1;
+  while ((c=getc (fp)) != EOF && c != '\n')
+    {
+      if (len >= buflen)
+        {
+          if (!largebuf)
+            {
+              buflen += 1024;
+              largebuf = xtrymalloc ( buflen + 1 );
+              if (!largebuf)
+                {
+                  *r_err = gpg_error_from_syserror ();
+                  return NULL;
+                }
+              memcpy (largebuf, buf, len);
+            }
+          else
+            {
+              buflen += 1024;
+              tmpbuf = xtryrealloc (largebuf, buflen + 1);
+              if (!tmpbuf)
+                {
+                  *r_err = gpg_error_from_syserror ();
+                  xfree (largebuf);
+                  return NULL;
+                }
+              largebuf = tmpbuf;
+            }
+          p = largebuf;
+        }
+      p[len++] = c;
+    }
+  if (c == EOF && !len)
+    return NULL;
+  p[len] = 0;
+      
+  if (largebuf)
+    tmpbuf = xtryrealloc (largebuf, len+1);
+  else
+    tmpbuf = xtrystrdup (buf);
+  if (!tmpbuf)
+    {
+      *r_err = gpg_error_from_syserror ();
+      xfree (largebuf);
+    }
+  return tmpbuf;
+}
+
+
+/* Release one cache entry.  */
+static void
+release_one_cache_entry (crl_cache_entry_t entry)
+{
+  if (entry)
+    {
+      if (entry->cdb)
+        {
+          int fd = cdb_fileno (entry->cdb);
+          cdb_free (entry->cdb);
+          xfree (entry->cdb);
+          if (close (fd))
+            log_error (_("error closing cache file: %s\n"), strerror(errno));
+        }
+      xfree (entry->release_ptr);
+      xfree (entry->check_trust_anchor);
+      xfree (entry);
+    }
+}
+
+
+/* Release the CACHE object. */
+static void
+release_cache (crl_cache_t cache)
+{
+  crl_cache_entry_t entry, entry2;
+
+  if (!cache)
+    return;
+
+  for (entry = cache->entries; entry; entry = entry2)
+    {
+      entry2 = entry->next;
+      release_one_cache_entry (entry);
+  }
+  cache->entries = NULL;
+  xfree (cache);
+}
+
+
+/* Open the dir file FNAME or create a new one if it does not yet
+   exist. */
+static FILE *
+open_dir_file (const char *fname)
+{
+  FILE *fp;
+
+  fp = fopen (fname, "r");
+  if (!fp)
+    {
+      log_error (_("failed to open cache dir file `%s': %s\n"),
+                 fname, strerror (errno));
+
+      /* Make sure that the directory exists, try to create if otherwise. */
+      if (create_directory_if_needed (NULL) 
+          || create_directory_if_needed (DBDIR_D)) 
+        return NULL;
+      fp = fopen (fname, "w");
+      if (!fp)
+        {
+          log_error (_("error creating new cache dir file `%s': %s\n"),
+                     fname, strerror (errno));
+          return NULL;
+        }
+      fprintf (fp, "v:%d:\n", DBDIRVERSION);
+      if (ferror (fp))
+        {
+          log_error (_("error writing new cache dir file `%s': %s\n"),
+                     fname, strerror (errno));
+          fclose (fp);
+          return NULL;
+        }
+      if (fclose (fp))
+        {
+          log_error (_("error closing new cache dir file `%s': %s\n"),
+                     fname, strerror (errno));
+          return NULL;
+        }
+
+      log_info (_("new cache dir file `%s' created\n"), fname);
+
+      fp = fopen (fname, "r");
+      if (!fp)
+        {
+          log_error (_("failed to re-open cache dir file `%s': %s\n"),
+                     fname, strerror (errno));
+          return NULL;
+        }
+    }
+
+  return fp;
+}
+
+/* Helper for open_dir. */
+static gpg_error_t
+check_dir_version (FILE **fpadr, const char *fname,
+                         unsigned int *lineno,
+                         int cleanup_on_mismatch)
+{
+  char *line;
+  gpg_error_t lineerr = 0;
+  FILE *fp = *fpadr;
+  int created = 0;
+
+ retry:
+  while ((line = next_line_from_file (fp, &lineerr)))
+    {
+      ++*lineno;
+      if (*line == 'v' && line[1] == ':')
+        break;
+      else if (*line != '#')
+        {
+          log_error (_("first record of `%s' is not the version\n"), fname);
+          xfree (line);
+          return gpg_error (GPG_ERR_CONFIGURATION);
+        }
+      xfree (line);
+    }
+  if (lineerr)
+    return lineerr;
+
+  if (strtol (line+2, NULL, 10) != DBDIRVERSION)
+    {
+      if (!created && cleanup_on_mismatch)
+        {
+          log_error (_("old version of cache directory - cleaning up\n"));
+          fclose (fp);
+          *fpadr = NULL;
+          if (!cleanup_cache_dir (1))
+            {
+              *lineno = 0;
+              fp = *fpadr = open_dir_file (fname);
+              if (!fp)
+                {
+                  xfree (line);
+                  return gpg_error (GPG_ERR_CONFIGURATION);
+                }
+              created = 1;
+              goto retry;
+            }
+        }
+      log_error (_("old version of cache directory - giving up\n"));
+      xfree (line);
+      return gpg_error (GPG_ERR_CONFIGURATION);
+    }
+  xfree (line);
+  return 0;
+}
+
+
+/* Open the dir file and read in all available information.  Store
+   that in a newly allocated cache object and return that if
+   everything worked out fine.  Create the cache directory and the dir
+   if it does not yet exist.  Remove all files in that directory if
+   the version does not match. */
+static gpg_error_t
+open_dir (crl_cache_t *r_cache)
+{
+  crl_cache_t cache;
+  char *fname; 
+  char *line = NULL;
+  gpg_error_t lineerr = 0;
+  FILE *fp;
+  crl_cache_entry_t entry, *entrytail;
+  unsigned int lineno;
+  gpg_error_t err = 0;
+  int anyerr = 0;
+
+  cache = xtrycalloc (1, sizeof *cache); 
+  if (!cache)
+    return gpg_error_from_syserror ();
+
+  fname = make_filename (opt.homedir_cache, DBDIR_D, DBDIRFILE, NULL);
+
+  lineno = 0;
+  fp = open_dir_file (fname);
+  if (!fp)
+    {
+      err = gpg_error (GPG_ERR_CONFIGURATION);
+      goto leave;
+    }
+
+  err = check_dir_version (&fp, fname, &lineno, 1);
+  if (err)
+    goto leave;
+
+
+  /* Read in all supported entries from the dir file. */
+  cache->entries = NULL;
+  entrytail = &cache->entries;
+  xfree (line);
+  while ((line = next_line_from_file (fp, &lineerr)))
+    {
+      int fieldno;
+      char *p, *endp;
+
+      lineno++;
+      if ( *line == 'c' || *line == 'u' || *line == 'i' )
+        {
+          entry = xtrycalloc (1, sizeof *entry);
+          if (!entry)
+            {
+              err = gpg_error_from_syserror ();
+              goto leave;
+            }
+          entry->lineno = lineno;
+          entry->release_ptr = line;
+          if (*line == 'i')
+            {
+              entry->invalid = atoi (line+1);
+              if (entry->invalid < 1)
+                entry->invalid = 1;
+            }
+          else if (*line == 'u')
+            entry->user_trust_req = 1;
+
+          for (fieldno=1, p = line; p; p = endp, fieldno++)
+            {
+              endp = strchr (p, ':');
+              if (endp)
+                *endp++ = '\0';
+
+              switch (fieldno)
+                {
+                case 1: /* record type */ break;
+                case 2: entry->issuer_hash = p; break;
+                case 3: entry->issuer = unpercent_string (p); break;
+                case 4: entry->url = unpercent_string (p); break;
+                case 5: strncpy (entry->this_update, p, 15); break;
+                case 6: strncpy (entry->next_update, p, 15); break;
+                case 7: entry->dbfile_hash = p; break;
+                case 8: if (*p) entry->crl_number = p; break;
+                case 9: 
+                  if (*p)
+                    entry->authority_issuer = unpercent_string (p);
+                  break;
+                case 10: 
+                  if (*p)
+                    entry->authority_serialno = unpercent_string (p);
+                  break;
+                case 11: 
+                  if (*p)
+                    entry->check_trust_anchor = xtrystrdup (p);
+                  break;
+                default:
+                  if (*p)
+                    log_info (_("extra field detected in crl record of "
+                                "`%s' line %u\n"), fname, lineno);
+                  break;
+                }
+            }
+
+          if (!entry->issuer_hash)
+            {
+              log_info (_("invalid line detected in `%s' line %u\n"),
+                        fname, lineno);
+              xfree (entry);
+              entry = NULL;
+            }
+          else if (find_entry (cache->entries, entry->issuer_hash))
+            {
+              /* Fixme: The duplicate checking used is not very
+                 effective for large numbers of issuers. */
+              log_info (_("duplicate entry detected in `%s' line %u\n"),
+                        fname, lineno);
+              xfree (entry);
+              entry = NULL;
+            }
+          else
+            {
+              line = NULL; 
+              *entrytail = entry;
+              entrytail = &entry->next;
+            }
+        }
+      else if (*line == '#')
+        ;
+      else
+        log_info (_("unsupported record type in `%s' line %u skipped\n"),
+                  fname, lineno);
+
+      if (line)
+        xfree (line);
+    }
+  if (lineerr)
+    {
+      err = lineerr;
+      log_error (_("error reading `%s': %s\n"), fname, gpg_strerror (err));
+      goto leave;
+    }
+  if (ferror (fp))
+    {
+      log_error (_("error reading `%s': %s\n"), fname, strerror (errno));
+      err = gpg_error (GPG_ERR_CONFIGURATION);
+      goto leave;
+    }
+
+  /* Now do some basic checks on the data. */
+  for (entry = cache->entries; entry; entry = entry->next)
+    {
+      assert (entry->lineno);
+      if (strlen (entry->issuer_hash) != 40)
+        {
+          anyerr++;
+          log_error (_("invalid issuer hash in `%s' line %u\n"),
+                     fname, entry->lineno);
+        }
+      else if ( !*entry->issuer )
+        {
+          anyerr++;
+          log_error (_("no issuer DN in `%s' line %u\n"),
+                     fname, entry->lineno);
+        }
+      else if ( check_isotime (entry->this_update)
+                || check_isotime (entry->next_update))
+        {
+          anyerr++;
+          log_error (_("invalid timestamp in `%s' line %u\n"),
+                     fname, entry->lineno);
+        }
+
+      /* Checks not leading to an immediate fail. */
+      if (strlen (entry->dbfile_hash) != 32)
+        log_info (_("WARNING: invalid cache file hash in `%s' line %u\n"),
+                  fname, entry->lineno); 
+    }
+     
+  if (anyerr)
+    {
+      log_error (_("detected errors in cache dir file\n"));
+      log_info (_("please check the reason and manually delete that file\n"));
+      err = gpg_error (GPG_ERR_CONFIGURATION);
+    }
+
+
+ leave:
+  if (fp)
+    fclose (fp);
+  xfree (line);
+  xfree (fname);
+  if (err)
+    {
+      release_cache (cache);
+      cache = NULL;
+    }
+  *r_cache = cache;
+  return err;
+}
+
+static void
+write_percented_string (const char *s, FILE *fp)
+{
+  for (; *s; s++)
+    if (*s == ':')
+      fputs ("%3A", fp);
+    else if (*s == '\n')
+      fputs ("%0A", fp);
+    else if (*s == '\r')
+      fputs ("%0D", fp);
+    else
+      putc (*s, fp);
+}
+
+
+static void
+write_dir_line_crl (FILE *fp, crl_cache_entry_t e)
+{
+  if (e->invalid)
+    fprintf (fp, "i%d", e->invalid);
+  else if (e->user_trust_req)
+    putc ('u', fp);
+  else
+    putc ('c', fp);
+  putc (':', fp);
+  fputs (e->issuer_hash, fp);
+  putc (':', fp);
+  write_percented_string (e->issuer, fp);
+  putc (':', fp);
+  write_percented_string (e->url, fp);
+  putc (':', fp);
+  fwrite (e->this_update, 15, 1, fp); 
+  putc (':', fp);
+  fwrite (e->next_update, 15, 1, fp); 
+  putc (':', fp);
+  fputs (e->dbfile_hash, fp);
+  putc (':', fp);
+  if (e->crl_number)
+    fputs (e->crl_number, fp);
+  putc (':', fp);
+  if (e->authority_issuer)
+    write_percented_string (e->authority_issuer, fp);
+  putc (':', fp);
+  if (e->authority_serialno)
+    fputs (e->authority_serialno, fp);
+  putc (':', fp);
+  if (e->check_trust_anchor && e->user_trust_req)
+    fputs (e->check_trust_anchor, fp);
+  putc ('\n', fp);
+}
+
+
+/* Update the current dir file using the cache. */
+static gpg_error_t
+update_dir (crl_cache_t cache)
+{
+  char *fname = NULL;
+  char *tmpfname = NULL;
+  char *line = NULL;
+  gpg_error_t lineerr = 0;
+  FILE *fp, *fpout = NULL;
+  crl_cache_entry_t e;
+  unsigned int lineno;
+  gpg_error_t err = 0;
+
+  fname = make_filename (opt.homedir_cache, DBDIR_D, DBDIRFILE, NULL);
+
+  /* Fixme: Take an update file lock here. */
+
+  for (e= cache->entries; e; e = e->next)
+    e->mark = 1;
+
+  lineno = 0;
+  fp = fopen (fname, "r");
+  if (!fp)
+    {
+      err = gpg_error_from_errno (errno);
+      log_error (_("failed to open cache dir file `%s': %s\n"),
+                 fname, strerror (errno));
+      goto leave;
+    }
+  err = check_dir_version (&fp, fname, &lineno, 0);
+  if (err)
+    goto leave;
+  rewind (fp);
+  lineno = 0;
+
+  /* Create a temporary DIR file. */
+  {
+    char *tmpbuf, *p;
+    const char *nodename;
+#ifndef HAVE_W32_SYSTEM
+    struct utsname utsbuf;
+#endif
+    
+#ifdef HAVE_W32_SYSTEM
+    nodename = "unknown";
+#else
+    if (uname (&utsbuf))
+      nodename = "unknown";
+    else
+      nodename = utsbuf.nodename;
+#endif
+
+    estream_asprintf (&tmpbuf, "DIR-tmp-%s-%u-%p.txt.tmp",
+                      nodename, (unsigned int)getpid (), &tmpbuf);
+    if (!tmpbuf)
+      {
+        err = gpg_error_from_errno (errno);
+        log_error (_("failed to create temporary cache dir file `%s': %s\n"),
+                   tmpfname, strerror (errno));
+        goto leave;
+      }
+    for (p=tmpbuf; *p; p++)
+      if (*p == '/')
+        *p = '.';
+    tmpfname = make_filename (opt.homedir_cache, DBDIR_D, tmpbuf, NULL);
+    xfree (tmpbuf);
+  }
+  fpout = fopen (tmpfname, "w");
+  if (!fpout)
+    {
+      err = gpg_error_from_errno (errno);
+      log_error (_("failed to create temporary cache dir file `%s': %s\n"),
+                 tmpfname, strerror (errno));
+      goto leave;
+    }
+
+  while ((line = next_line_from_file (fp, &lineerr)))
+    {
+      lineno++;
+      if (*line == 'c' || *line == 'u' || *line == 'i')
+        {
+          /* Extract the issuer hash field. */
+          char *fieldp, *endp;
+
+          fieldp = strchr (line, ':');
+          endp = fieldp? strchr (++fieldp, ':') : NULL;
+          if (endp)
+            {
+              /* There should be no percent within the issuer hash
+                 field, thus we can compare it pretty easily. */
+              *endp = 0;
+              e = find_entry ( cache->entries, fieldp);
+              *endp = ':'; /* Restore orginal line. */
+              if (e && e->deleted) 
+                {
+                  /* Marked for deletion, so don't write it. */
+                  e->mark = 0; 
+                }
+              else if (e)
+                {
+                  /* Yep, this is valid entry we know about; write it out */
+                  write_dir_line_crl (fpout, e);
+                  e->mark = 0;
+                }
+              else
+                { /* We ignore entries we don't have in our cache
+                     because they may have been added in the meantime
+                     by other instances of dirmngr. */
+                  fprintf (fpout, "# Next line added by "
+                           "another process; our pid is %lu\n", 
+                           (unsigned long)getpid ());
+                  fputs (line, fpout);
+                  putc ('\n', fpout);
+                }
+            }
+          else
+            {
+              fputs ("# Invalid line detected: ", fpout);
+              fputs (line, fpout);
+              putc ('\n', fpout);
+            }
+        }
+      else 
+        {
+          /* Write out all non CRL lines as they are. */
+          fputs (line, fpout);
+          putc ('\n', fpout);
+        }
+
+      xfree (line);
+    }
+  if (!ferror (fp) && !ferror (fpout) && !lineerr)
+    {
+      /* Write out the remaining entries. */
+      for (e= cache->entries; e; e = e->next)
+        if (e->mark)
+          {
+            if (!e->deleted)
+              write_dir_line_crl (fpout, e);
+            e->mark = 0;
+          }
+    }
+  if (lineerr)
+    {
+      err = lineerr;
+      log_error (_("error reading `%s': %s\n"), fname, gpg_strerror (err));
+      goto leave;
+    }
+  if (ferror (fp))
+    {
+      err = gpg_error_from_errno (errno);
+      log_error (_("error reading `%s': %s\n"), fname, strerror (errno));
+    }
+  if (ferror (fpout))
+    {
+      err = gpg_error_from_errno (errno);
+      log_error (_("error writing `%s': %s\n"), tmpfname, strerror (errno));
+    }
+  if (err)
+    goto leave;
+
+  /* Rename the files. */
+  fclose (fp);
+  fp = NULL;
+  if (fclose (fpout))
+    {
+      err = gpg_error_from_errno (errno);
+      log_error (_("error closing `%s': %s\n"), tmpfname, strerror (errno));
+      goto leave;
+    }
+  fpout = NULL;
+
+#ifdef HAVE_W32_SYSTEM
+  /* No atomic mv on W32 systems.  */
+  unlink (fname);
+#endif
+  if (rename (tmpfname, fname))
+    {
+      err = gpg_error_from_errno (errno);
+      log_error (_("error renaming `%s' to `%s': %s\n"),
+                 tmpfname, fname, strerror (errno));
+      goto leave;
+    }
+
+ leave:
+  /* Fixme: Relinquish update lock. */
+  xfree (line);
+  if (fp)
+    fclose (fp);
+  xfree (fname);
+  if (fpout)
+    {
+      fclose (fpout);
+      if (err && tmpfname)
+        remove (tmpfname);
+    }
+  xfree (tmpfname);
+  return err;
+}
+
+
+
+
+/* Create the filename for the cache file from the 40 byte ISSUER_HASH
+   string. Caller must release the return string. */
+static char *
+make_db_file_name (const char *issuer_hash)
+{
+  char bname[50];
+
+  assert (strlen (issuer_hash) == 40);
+  memcpy (bname, "crl-", 4);
+  memcpy (bname + 4, issuer_hash, 40);
+  strcpy (bname + 44, ".db");
+  return make_filename (opt.homedir_cache, DBDIR_D, bname, NULL);
+}
+
+
+/* Hash the file FNAME and return the MD5 digest in MD5BUFFER. The
+   caller must allocate MD%buffer wityh at least 16 bytes. Returns 0
+   on success. */
+static int
+hash_dbfile (const char *fname, unsigned char *md5buffer)
+{
+  FILE *fp;
+  char *buffer;
+  size_t n;
+  gcry_md_hd_t md5;
+  gpg_err_code_t err;
+
+  buffer = xtrymalloc (65536);
+  fp = buffer? fopen (fname, "rb") : NULL;
+  if (!fp)
+    {
+      log_error (_("can't hash `%s': %s\n"), fname, strerror (errno));
+      xfree (buffer);
+      return -1;
+    }
+
+  err = gcry_md_open (&md5, GCRY_MD_MD5, 0);
+  if (err)
+    {
+      log_error (_("error setting up MD5 hash context: %s\n"),
+                 gpg_strerror (err));
+      xfree (buffer);
+      fclose (fp);
+      return -1;
+    }
+
+  /* We better hash some information about the cache file layout in. */
+  sprintf (buffer, "%.100s/%.100s:%d", DBDIR_D, DBDIRFILE, DBDIRVERSION);
+  gcry_md_write (md5, buffer, strlen (buffer));
+    
+  for (;;)
+    {
+      n = fread (buffer, 1, 65536, fp);
+      if (n < 65536 && ferror (fp))
+        {
+          log_error (_("error hashing `%s': %s\n"), fname, strerror (errno));
+          xfree (buffer);
+          fclose (fp);
+          gcry_md_close (md5);
+          return -1;
+        }
+      if (!n)
+        break;
+      gcry_md_write (md5, buffer, n);
+    }
+  fclose (fp);
+  xfree (buffer);
+  gcry_md_final (md5);
+
+  memcpy (md5buffer, gcry_md_read (md5, GCRY_MD_MD5), 16);
+  gcry_md_close (md5);
+  return 0;
+}
+
+/* Compare the file FNAME against the dexified MD5 hash MD5HASH and
+   return 0 if they match. */
+static int
+check_dbfile (const char *fname, const char *md5hexvalue)
+{
+  unsigned char buffer1[16], buffer2[16];
+
+  if (strlen (md5hexvalue) != 32)
+    {
+      log_error (_("invalid formatted checksum for `%s'\n"), fname);
+      return -1;
+    }
+  unhexify (buffer1, md5hexvalue);
+
+  if (hash_dbfile (fname, buffer2))
+    return -1;
+
+  return memcmp (buffer1, buffer2, 16);
+}
+
+
+/* Open the cache file for ENTRY.  This function implements a caching
+   strategy and might close unused cache files. It is required to use
+   unlock_db_file after using the file. */ 
+static struct cdb *
+lock_db_file (crl_cache_t cache, crl_cache_entry_t entry)
+{
+  char *fname;
+  int fd;
+  int open_count;
+  crl_cache_entry_t e;
+
+  if (entry->cdb)
+    {
+      entry->cdb_use_count++;
+      return entry->cdb;
+    }
+
+  for (open_count = 0, e = cache->entries; e; e = e->next)
+    {
+      if (e->cdb)
+        open_count++;
+/*       log_debug ("CACHE: cdb=%p use_count=%u lru_count=%u\n", */
+/*                  e->cdb,e->cdb_use_count,e->cdb_lru_count); */
+    }
+
+  /* If there are too many file open, find the least recent used DB
+     file and close it.  Note that for Pth thread safeness we need to
+     use a loop here. */
+  while (open_count >= MAX_OPEN_DB_FILES )
+    {
+      crl_cache_entry_t last_e = NULL;
+      unsigned int last_lru = (unsigned int)(-1);
+
+      for (e = cache->entries; e; e = e->next)
+        if (e->cdb && !e->cdb_use_count && e->cdb_lru_count < last_lru)
+          {
+            last_lru = e->cdb_lru_count;
+            last_e = e;
+          }
+      if (!last_e)
+        {
+          log_error (_("too many open cache files; can't open anymore\n"));
+          return NULL;
+        }
+
+/*       log_debug ("CACHE: closing file at cdb=%p\n", last_e->cdb); */
+
+      fd = cdb_fileno (last_e->cdb);
+      cdb_free (last_e->cdb);
+      xfree (last_e->cdb);
+      last_e->cdb = NULL;
+      if (close (fd))
+        log_error (_("error closing cache file: %s\n"), strerror(errno));
+      open_count--;
+    }
+
+
+  fname = make_db_file_name (entry->issuer_hash);
+  if (opt.verbose)
+    log_info (_("opening cache file `%s'\n"), fname );
+
+  if (!entry->dbfile_checked)
+    {
+      if (!check_dbfile (fname, entry->dbfile_hash))
+        entry->dbfile_checked = 1;
+      /* Note, in case of an error we don't print an error here but
+         let require the caller to do that check. */
+    }
+
+  entry->cdb = xtrycalloc (1, sizeof *entry->cdb);
+  if (!entry->cdb)
+    {
+      xfree (fname);
+      return NULL;
+    }
+  fd = open (fname, O_RDONLY);
+  if (fd == -1)
+    {
+      log_error (_("error opening cache file `%s': %s\n"),
+                 fname, strerror (errno));
+      xfree (entry->cdb);
+      entry->cdb = NULL;
+      xfree (fname);
+      return NULL;
+    }
+  if (cdb_init (entry->cdb, fd))
+    {
+      log_error (_("error initializing cache file `%s' for reading: %s\n"),
+                 fname, strerror (errno));
+      xfree (entry->cdb);
+      entry->cdb = NULL;
+      close (fd);
+      xfree (fname);
+      return NULL;
+    }
+  xfree (fname);
+
+  entry->cdb_use_count = 1;
+  entry->cdb_lru_count = 0;
+
+  return entry->cdb;
+}
+
+/* Unlock a cache file, so that it can be reused. */
+static void
+unlock_db_file (crl_cache_t cache, crl_cache_entry_t entry)
+{
+  if (!entry->cdb)
+    log_error (_("calling unlock_db_file on a closed file\n"));
+  else if (!entry->cdb_use_count)
+    log_error (_("calling unlock_db_file on an unlocked file\n"));
+  else 
+    {
+      entry->cdb_use_count--;
+      entry->cdb_lru_count++;
+    }
+
+  /* If the entry was marked for deletion in the meantime do it now.
+     We do this for the sake of Pth thread safeness. */
+  if (!entry->cdb_use_count && entry->deleted)
+    {
+      crl_cache_entry_t eprev, enext;
+
+      enext = entry->next;
+      for (eprev = cache->entries;
+           eprev && eprev->next != entry; eprev = eprev->next)
+        ;
+      assert (eprev);
+      if (eprev == cache->entries)
+        cache->entries = enext;
+      else
+        eprev->next = enext;
+    }
+}
+
+
+/* Find ISSUER_HASH in our cache FIRST. This may be used to enumerate
+   the linked list we use to keep the CRLs of an issuer. */
+static crl_cache_entry_t 
+find_entry (crl_cache_entry_t first, const char *issuer_hash)
+{
+  while (first && (first->deleted || strcmp (issuer_hash, first->issuer_hash)))
+    first = first->next;
+  return first;    
+}
+
+
+
+/* Create a new CRL cache. This fucntion is usually called only once.
+   never fail. */
+void
+crl_cache_init(void)
+{
+  crl_cache_t cache = NULL;
+  gpg_error_t err;
+
+  if (current_cache)
+    {
+      log_error ("crl cache has already been initialized - not doing twice\n");
+      return;
+    }
+
+  err = open_dir (&cache);
+  if (err) 
+    log_fatal (_("failed to create a new cache object: %s\n"),
+               gpg_strerror (err));
+  current_cache = cache;
+}
+
+
+/* Remove the cache information and all its resources.  Note that we
+   still keep the cache on disk. */
+void 
+crl_cache_deinit (void)
+{
+  if (current_cache)
+    {
+      release_cache (current_cache);
+      current_cache = NULL;
+    }
+}
+
+
+/* Delete the cache from disk. Return 0 on success.*/
+int 
+crl_cache_flush (void)
+{
+  int rc;
+
+  rc = cleanup_cache_dir (0)? -1 : 0;
+
+  return rc;
+}
+
+
+/* Check whether the certificate identified by ISSUER_HASH and
+   SN/SNLEN is valid; i.e. not listed in our cache.  With
+   FORCE_REFRESH set to true, a new CRL will be retrieved even if the
+   cache has not yet expired.  We use a 30 minutes threshold here so
+   that invoking this function several times won't load the CRL over
+   and over.  */
+static crl_cache_result_t 
+cache_isvalid (ctrl_t ctrl, const char *issuer_hash,
+               const unsigned char *sn, size_t snlen,
+               int force_refresh)
+{
+  crl_cache_t cache = get_current_cache ();
+  crl_cache_result_t retval;
+  struct cdb *cdb;
+  int rc;
+  crl_cache_entry_t entry;
+  gnupg_isotime_t current_time;
+  size_t n;
+
+  (void)ctrl;
+
+  entry = find_entry (cache->entries, issuer_hash);
+  if (!entry)
+    {
+      log_info (_("no CRL available for issuer id %s\n"), issuer_hash );
+      return CRL_CACHE_DONTKNOW;
+    }
+
+  gnupg_get_isotime (current_time);
+  if (strcmp (entry->next_update, current_time) < 0 )
+    {
+      log_info (_("cached CRL for issuer id %s too old; update required\n"),
+                issuer_hash);
+      return CRL_CACHE_DONTKNOW;
+    }
+  if (force_refresh)
+    {
+      gnupg_isotime_t tmptime;
+      
+      if (*entry->last_refresh)
+        {
+          gnupg_copy_time (tmptime, entry->last_refresh);
+          add_seconds_to_isotime (tmptime, 30 * 60);
+          if (strcmp (tmptime, current_time) < 0 )
+            {
+              log_info (_("force-crl-refresh active and %d minutes passed for"
+                          " issuer id %s; update required\n"), 
+                        30, issuer_hash);
+              return CRL_CACHE_DONTKNOW;
+            }
+        }
+      else
+        {
+          log_info (_("force-crl-refresh active for"
+                      " issuer id %s; update required\n"), 
+                    issuer_hash);
+          return CRL_CACHE_DONTKNOW;
+        }
+    }
+
+  if (entry->invalid)
+    {
+      log_info (_("available CRL for issuer ID %s can't be used\n"),
+                issuer_hash);
+      return CRL_CACHE_CANTUSE;
+    }
+
+  cdb = lock_db_file (cache, entry);
+  if (!cdb)
+    return CRL_CACHE_DONTKNOW; /* Hmmm, not the best error code. */
+  
+  if (!entry->dbfile_checked)
+    {
+      log_error (_("cached CRL for issuer id %s tampered; we need to update\n")
+                 , issuer_hash);
+      unlock_db_file (cache, entry);
+      return CRL_CACHE_DONTKNOW;
+    }
+
+  rc = cdb_find (cdb, sn, snlen);
+  if (rc == 1) 
+    {
+      n = cdb_datalen (cdb);
+      if (n != 16)
+        {
+          log_error (_("WARNING: invalid cache record length for S/N "));
+          log_printhex ("", sn, snlen);
+        }
+      else if (opt.verbose)
+        {
+          unsigned char record[16];
+          char *tmp = hexify_data (sn, snlen);
+
+          if (cdb_read (cdb, record, n, cdb_datapos (cdb)))
+            log_error (_("problem reading cache record for S/N %s: %s\n"),
+                       tmp, strerror (errno));
+          else
+            log_info (_("S/N %s is not valid; reason=%02X  date=%.15s\n"),
+                      tmp, *record, record+1);
+          xfree (tmp);
+        }
+      retval = CRL_CACHE_INVALID;
+    }
+  else if (!rc)
+    {
+      if (opt.verbose)
+        {
+          char *serialno = hexify_data (sn, snlen);
+          log_info (_("S/N %s is valid, it is not listed in the CRL\n"),
+                    serialno );
+          xfree (serialno);
+        }
+      retval = CRL_CACHE_VALID;
+    }
+  else 
+    {
+      log_error (_("error getting data from cache file: %s\n"),
+                 strerror (errno));
+      retval = CRL_CACHE_DONTKNOW;
+    }
+
+
+  if (entry->user_trust_req
+      && (retval == CRL_CACHE_VALID || retval == CRL_CACHE_INVALID))
+    {
+      if (!entry->check_trust_anchor)
+        {
+          log_error ("inconsistent data on user trust check\n");
+          retval = CRL_CACHE_CANTUSE;
+        }
+      else if (get_istrusted_from_client (ctrl, entry->check_trust_anchor))
+        {
+          if (opt.verbose)
+            log_info ("no system trust and client does not trust either\n");
+          retval = CRL_CACHE_CANTUSE;
+        }
+      else
+        {
+          /* Okay, the CRL is considered valid by the client and thus
+             we can return the result as is.  */
+        }
+    }
+
+  unlock_db_file (cache, entry);
+
+  return retval;
+}
+
+
+/* Check whether the certificate identified by ISSUER_HASH and
+   SERIALNO is valid; i.e. not listed in our cache.  With
+   FORCE_REFRESH set to true, a new CRL will be retrieved even if the
+   cache has not yet expired.  We use a 30 minutes threshold here so
+   that invoking this function several times won't load the CRL over
+   and over.  */
+crl_cache_result_t 
+crl_cache_isvalid (ctrl_t ctrl, const char *issuer_hash, const char *serialno,
+                   int force_refresh)
+{
+  crl_cache_result_t result;
+  unsigned char snbuf_buffer[50];
+  unsigned char *snbuf;
+  size_t n;
+
+  n = strlen (serialno)/2+1;
+  if (n < sizeof snbuf_buffer - 1)
+    snbuf = snbuf_buffer;
+  else
+    {
+      snbuf = xtrymalloc (n);
+      if (!snbuf)
+        return CRL_CACHE_DONTKNOW;
+    }
+
+  n = unhexify (snbuf, serialno);
+
+  result = cache_isvalid (ctrl, issuer_hash, snbuf, n, force_refresh);
+
+  if (snbuf != snbuf_buffer)
+    xfree (snbuf);
+
+  return result;
+}  
+
+
+/* Check whether the certificate CERT is valid; i.e. not listed in our
+   cache.  With FORCE_REFRESH set to true, a new CRL will be retrieved
+   even if the cache has not yet expired.  We use a 30 minutes
+   threshold here so that invoking this function several times won't
+   load the CRL over and over.  */
+gpg_error_t
+crl_cache_cert_isvalid (ctrl_t ctrl, ksba_cert_t cert,
+                        int force_refresh)
+{
+  gpg_error_t err;
+  crl_cache_result_t result;
+  unsigned char issuerhash[20];
+  char issuerhash_hex[41];
+  ksba_sexp_t serial;
+  unsigned char *sn;
+  size_t snlen;
+  char *endp, *tmp;
+  int i;
+
+  /* Compute the hash value of the issuer name.  */
+  tmp = ksba_cert_get_issuer (cert, 0);
+  if (!tmp)
+    {
+      log_error ("oops: issuer missing in certificate\n");
+      return gpg_error (GPG_ERR_INV_CERT_OBJ);
+    }
+  gcry_md_hash_buffer (GCRY_MD_SHA1, issuerhash, tmp, strlen (tmp));
+  xfree (tmp);
+  for (i=0,tmp=issuerhash_hex; i < 20; i++, tmp += 2)
+    sprintf (tmp, "%02X", issuerhash[i]);
+  
+  /* Get the serial number.  */
+  serial = ksba_cert_get_serial (cert);
+  if (!serial)
+    {
+      log_error ("oops: S/N missing in certificate\n");
+      return gpg_error (GPG_ERR_INV_CERT_OBJ);
+    }
+  sn = serial;
+  if (*sn != '(')
+    {
+      log_error ("oops: invalid S/N\n");
+      xfree (serial);
+      return gpg_error (GPG_ERR_INV_CERT_OBJ);
+    }
+  sn++;
+  snlen = strtoul (sn, &endp, 10);
+  sn = endp;
+  if (*sn != ':')
+    {
+      log_error ("oops: invalid S/N\n");
+      xfree (serial);
+      return gpg_error (GPG_ERR_INV_CERT_OBJ);
+    }
+  sn++;
+
+  /* Check the cache.  */
+  result = cache_isvalid (ctrl, issuerhash_hex, sn, snlen, force_refresh);
+  switch (result)
+    {
+    case CRL_CACHE_VALID:
+      err = 0;
+      break;
+    case CRL_CACHE_INVALID:
+      err = gpg_error (GPG_ERR_CERT_REVOKED);
+      break;
+    case CRL_CACHE_DONTKNOW: 
+      err = gpg_error (GPG_ERR_NO_CRL_KNOWN);
+    case CRL_CACHE_CANTUSE: 
+      err = gpg_error (GPG_ERR_NO_CRL_KNOWN);
+      break;
+    default:
+      log_fatal ("cache_isvalid returned invalid status code %d\n", result);
+    }
+
+  xfree (serial);
+  return err;
+}  
+
+
+/* Prepare a hash context for the signature verification.  Input is
+   the CRL and the output is the hash context MD as well as the uses
+   algorithm identifier ALGO. */
+static gpg_error_t
+start_sig_check (ksba_crl_t crl, gcry_md_hd_t *md, int *algo) 
+{
+  gpg_error_t err;
+  const char *algoid;
+
+  algoid = ksba_crl_get_digest_algo (crl);
+  *algo = gcry_md_map_name (algoid);
+  if (!*algo) 
+    {
+      log_error (_("unknown hash algorithm `%s'\n"), algoid? algoid:"?");
+      return gpg_error (GPG_ERR_DIGEST_ALGO);
+    }
+
+  err = gcry_md_open (md, *algo, 0);
+  if (err)
+    {
+      log_error (_("gcry_md_open for algorithm %d failed: %s\n"),
+                 *algo, gcry_strerror (err));
+      return err;
+    }
+  if (DBG_HASHING)
+    gcry_md_debug (*md, "hash.cert");
+
+  ksba_crl_set_hash_function (crl, HASH_FNC, *md);  
+  return 0;
+}
+
+
+/* Finish a hash context and verify the signature.  This function
+   should return 0 on a good signature, GPG_ERR_BAD_SIGNATURE if the
+   signature does not verify or any other error code. CRL is the CRL
+   object we are working on, MD the hash context and ISSUER_CERT the
+   certificate of the CRL issuer.  This function closes MD.  */
+static gpg_error_t
+finish_sig_check (ksba_crl_t crl, gcry_md_hd_t md, int algo,
+                  ksba_cert_t issuer_cert)
+{
+  gpg_error_t err;
+  ksba_sexp_t sigval = NULL, pubkey = NULL;
+  const char *s;
+  char algoname[50];
+  size_t n;
+  gcry_sexp_t s_sig = NULL, s_hash = NULL, s_pkey = NULL;
+  unsigned int i;
+
+  /* This also stops debugging on the MD.  */
+  gcry_md_final (md);
+
+  /* Get and convert the signature value. */
+  sigval = ksba_crl_get_sig_val (crl);
+  n = gcry_sexp_canon_len (sigval, 0, NULL, NULL);
+  if (!n) 
+    {
+      log_error (_("got an invalid S-expression from libksba\n"));
+      err = gpg_error (GPG_ERR_INV_SEXP);
+      goto leave;
+    }
+  err = gcry_sexp_sscan (&s_sig, NULL, sigval, n);
+  if (err) 
+    {
+      log_error (_("converting S-expression failed: %s\n"),
+                 gcry_strerror (err));
+      goto leave;
+    }
+       
+  /* Get and convert the public key for the issuer certificate. */
+  if (DBG_X509)
+    dump_cert ("crl_issuer_cert", issuer_cert);
+  pubkey = ksba_cert_get_public_key (issuer_cert);
+  n = gcry_sexp_canon_len (pubkey, 0, NULL, NULL);
+  if (!n) 
+    {
+      log_error (_("got an invalid S-expression from libksba\n"));
+      err = gpg_error (GPG_ERR_INV_SEXP);
+      goto leave;
+    }
+  err = gcry_sexp_sscan (&s_pkey, NULL, pubkey, n);
+  if (err) 
+    {
+      log_error (_("converting S-expression failed: %s\n"),
+                 gcry_strerror (err));
+      goto leave;
+    }
+
+  /* Create an S-expression with the actual hash value. */
+  s = gcry_md_algo_name (algo);
+  for (i = 0; *s && i < sizeof(algoname) - 1; s++, i++)
+    algoname[i] = ascii_tolower (*s);
+  algoname[i] = 0;
+  err = gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))", 
+                         algoname,
+                         gcry_md_get_algo_dlen (algo), gcry_md_read (md, algo));
+  if (err) 
+    {
+      log_error (_("creating S-expression failed: %s\n"), gcry_strerror (err));
+      goto leave;
+    }
+
+  /* Pass this on to the signature verification. */
+  err = gcry_pk_verify (s_sig, s_hash, s_pkey);
+  if (DBG_X509)
+    log_debug ("gcry_pk_verify: %s\n", gpg_strerror (err));
+
+ leave:
+  xfree (sigval);
+  xfree (pubkey);
+  gcry_sexp_release (s_sig);
+  gcry_sexp_release (s_hash);
+  gcry_sexp_release (s_pkey);
+  gcry_md_close (md);
+
+  return err;
+}
+
+
+/* Call this to match a start_sig_check that can not be completed
+   normally.  */
+static void
+abort_sig_check (ksba_crl_t crl, gcry_md_hd_t md)
+{
+  (void)crl;
+  gcry_md_close (md);
+}
+
+
+/* Workhorse of the CRL loading machinery.  The CRL is read using the
+   CRL object and stored in the data base file DB with the name FNAME
+   (only used for printing error messages).  That DB should be a
+   temporary one and not the actual one.  If the function fails the
+   caller should delete this temporary database file.  CTRL is
+   required to retrieve certificates using the general dirmngr
+   callback service.  R_CRLISSUER returns an allocated string with the
+   crl-issuer DN, THIS_UPDATE and NEXT_UPDATE are filled with the
+   corresponding data from the CRL.  Note that these values might get
+   set even if the CRL processing fails at a later step; thus the
+   caller should free *R_ISSUER even if the function returns with an
+   error.  R_TRUST_ANCHOR is set on exit to NULL or a string with the
+   hexified fingerprint of the root certificate, if checking this
+   certificate for trustiness is required.
+*/ 
+static int 
+crl_parse_insert (ctrl_t ctrl, ksba_crl_t crl,
+                  struct cdb_make *cdb, const char *fname,
+                  char **r_crlissuer,
+                  ksba_isotime_t thisupdate, ksba_isotime_t nextupdate,
+                  char **r_trust_anchor)
+{  
+  gpg_error_t err;
+  ksba_stop_reason_t stopreason;
+  ksba_cert_t crlissuer_cert = NULL;
+  gcry_md_hd_t md = NULL;
+  int algo = 0;
+  size_t n;
+  
+  (void)fname;
+
+  *r_crlissuer = NULL;
+  *thisupdate = *nextupdate = 0;
+  *r_trust_anchor = NULL;
+
+  /* Start of the KSBA parser loop. */
+  do
+    {
+      err = ksba_crl_parse (crl, &stopreason);
+      if (err)
+        {
+          log_error (_("ksba_crl_parse failed: %s\n"), gpg_strerror (err) );
+          goto failure;
+        }
+
+      switch (stopreason)
+        {
+        case KSBA_SR_BEGIN_ITEMS:
+          {
+            if (start_sig_check (crl, &md, &algo ))
+              goto failure;
+          
+            err = ksba_crl_get_update_times (crl, thisupdate, nextupdate);
+            if (err)
+              {
+                log_error (_("error getting update times of CRL: %s\n"),
+                           gpg_strerror (err));          
+                err = gpg_error (GPG_ERR_INV_CRL);
+                goto failure;
+              }
+
+            if (opt.verbose || !*nextupdate)
+              log_info (_("update times of this CRL: this=%s next=%s\n"), 
+                        thisupdate, nextupdate);   
+            if (!*nextupdate)
+              {
+                log_info (_("nextUpdate not given; "
+                            "assuming a validity period of one day\n"));
+                gnupg_copy_time (nextupdate, thisupdate);
+                add_seconds_to_isotime (nextupdate, 86400);
+              }
+          }
+          break;
+      
+        case KSBA_SR_GOT_ITEM:
+          {
+            ksba_sexp_t serial;
+            const unsigned char *p;
+            ksba_isotime_t rdate;
+            ksba_crl_reason_t reason;
+            int rc;
+            unsigned char record[1+15];
+
+            err = ksba_crl_get_item (crl, &serial, rdate, &reason);
+            if (err)
+              {
+                log_error (_("error getting CRL item: %s\n"),
+                           gpg_strerror (err));
+                err = gpg_error (GPG_ERR_INV_CRL);
+                ksba_free (serial);
+                goto failure;
+              }
+            p = serial_to_buffer (serial, &n);
+            if (!p)
+              BUG ();
+            record[0] = (reason & 0xff);
+            memcpy (record+1, rdate, 15); 
+            rc = cdb_make_add (cdb, p, n, record, 1+15);
+            if (rc)
+              {
+                err = gpg_error_from_errno (errno);
+                log_error (_("error inserting item into "
+                             "temporary cache file: %s\n"),
+                           strerror (errno));
+                goto failure;
+              }
+
+            ksba_free (serial);
+          }
+          break;
+      
+        case KSBA_SR_END_ITEMS:
+          break;
+      
+        case KSBA_SR_READY:
+          {
+            char *crlissuer;
+            ksba_name_t authid;
+            ksba_sexp_t authidsn;
+            ksba_sexp_t keyid;
+
+            /* We need to look for the issuer only after having read
+               all items.  The issuer itselfs comes before the items
+               but the optional authorityKeyIdentifier comes after the
+               items. */
+            err = ksba_crl_get_issuer (crl, &crlissuer);
+            if( err )
+              {
+                log_error (_("no CRL issuer found in CRL: %s\n"),
+                           gpg_strerror (err) );
+                err = gpg_error (GPG_ERR_INV_CRL);
+                goto failure;
+              }
+           /* Note: This should be released by ksba_free, not xfree.
+              May need a memory reallocation dance.  */
+            *r_crlissuer = crlissuer; /* (Do it here so we don't need
+                                         to free it later) */
+
+            if (!ksba_crl_get_auth_key_id (crl, &keyid, &authid, &authidsn))
+              {
+                const char *s;
+
+                if (opt.verbose)
+                  log_info (_("locating CRL issuer certificate by "
+                              "authorityKeyIdentifier\n"));
+                
+                s = ksba_name_enum (authid, 0);
+                if (s && *authidsn)
+                  crlissuer_cert = find_cert_bysn (ctrl, s, authidsn);
+                if (!crlissuer_cert && keyid)
+                  crlissuer_cert = find_cert_bysubject (ctrl,
+                                                        crlissuer, keyid);
+
+                if (!crlissuer_cert)
+                  {
+                    log_info ("CRL issuer certificate ");
+                    if (keyid)
+                      {
+                        log_printf ("{");
+                        dump_serial (keyid);
+                        log_printf ("} ");
+                      }
+                    if (authidsn)
+                      {
+                        log_printf ("(#");
+                        dump_serial (authidsn);
+                        log_printf ("/");
+                        dump_string (s);
+                        log_printf (") ");
+                      }
+                    log_printf ("not found\n");
+                  }
+                ksba_name_release (authid);
+                xfree (authidsn);
+                xfree (keyid);
+              }
+            else
+              crlissuer_cert = find_cert_bysubject (ctrl, crlissuer, NULL);
+            err = 0;
+            if (!crlissuer_cert)
+              {
+                err = gpg_error (GPG_ERR_MISSING_CERT);
+                goto failure;
+              }
+        
+            err = finish_sig_check (crl, md, algo, crlissuer_cert);
+            if (err)
+              {
+                log_error (_("CRL signature verification failed: %s\n"), 
+                           gpg_strerror (err));
+                goto failure;
+              }
+           md = NULL;
+
+            err = validate_cert_chain (ctrl, crlissuer_cert, NULL,
+                                       VALIDATE_MODE_CRL_RECURSIVE,
+                                       r_trust_anchor);
+            if (err)
+              {
+                log_error (_("error checking validity of CRL "
+                             "issuer certificate: %s\n"), 
+                           gpg_strerror (err));
+                goto failure;
+              }
+
+          }
+          break;
+      
+        default:
+          log_debug ("crl_parse_insert: unknown stop reason\n");
+          err = gpg_error (GPG_ERR_BUG);
+          goto failure;
+        }
+    } 
+  while (stopreason != KSBA_SR_READY);
+  assert (!err);
+
+
+ failure:
+  if (md)
+    abort_sig_check (crl, md);
+  ksba_cert_release (crlissuer_cert);
+  return err;
+}
+
+
+
+/* Return the crlNumber extension as an allocated hex string or NULL
+   if there is none. */
+static char *
+get_crl_number (ksba_crl_t crl)
+{
+  gpg_error_t err;
+  ksba_sexp_t number;
+  char *string;
+
+  err = ksba_crl_get_crl_number (crl, &number);
+  if (err)
+    return NULL;
+  string = serial_hex (number);
+  ksba_free (number);
+  return string;
+}
+
+
+/* Return the authorityKeyIdentifier or NULL if it is not available.
+   The issuer name may consists of several parts - they are delimted by
+   0x01. */
+static char *
+get_auth_key_id (ksba_crl_t crl, char **serialno)
+{
+  gpg_error_t err;
+  ksba_name_t name;
+  ksba_sexp_t sn;
+  int idx;
+  const char *s;
+  char *string;
+  size_t length;
+
+  *serialno = NULL;
+  err = ksba_crl_get_auth_key_id (crl, NULL, &name, &sn);
+  if (err)
+    return NULL;
+  *serialno = serial_hex (sn);
+  ksba_free (sn);
+
+  if (!name)
+    return xstrdup ("");
+  
+  length = 0;
+  for (idx=0; (s = ksba_name_enum (name, idx)); idx++)
+    {
+      char *p = ksba_name_get_uri (name, idx);
+      length += strlen (p?p:s) + 1;
+      xfree (p);
+    }
+  string = xtrymalloc (length+1);
+  if (string)
+    {
+      *string = 0;
+      for (idx=0; (s = ksba_name_enum (name, idx)); idx++)
+        {
+          char *p = ksba_name_get_uri (name, idx);
+          if (*string)
+            strcat (string, "\x01");
+          strcat (string, p?p:s);
+          xfree (p);
+        }
+    }
+  ksba_name_release (name);
+  return string;
+}
+
+
+
+/* Insert the CRL retrieved using URL into the cache specified by
+   CACHE.  The CRL itself will be read from the stream FP and is
+   expected in binary format. */
+gpg_error_t
+crl_cache_insert (ctrl_t ctrl, const char *url, ksba_reader_t reader)
+{
+  crl_cache_t cache = get_current_cache ();
+  gpg_error_t err, err2;
+  ksba_crl_t crl;
+  char *fname = NULL;
+  char *newfname = NULL;
+  struct cdb_make cdb;
+  int fd_cdb = -1;
+  char *issuer = NULL;
+  char *issuer_hash = NULL;
+  ksba_isotime_t thisupdate, nextupdate;
+  crl_cache_entry_t entry = NULL;
+  crl_cache_entry_t e;
+  gnupg_isotime_t current_time;
+  char *checksum = NULL;
+  int invalidate_crl = 0;
+  int idx;
+  const char *oid;
+  int critical;
+  char *trust_anchor = NULL;
+
+  /* FIXME: We should acquire a mutex for the URL, so that we don't
+     simultaneously enter the same CRL twice.  However this needs to be
+     interweaved with the checking function.*/
+  err2 = 0;
+
+  err = ksba_crl_new (&crl);
+  if (err)
+    {
+      log_error (_("ksba_crl_new failed: %s\n"), gpg_strerror (err));
+      goto leave;
+    }
+  
+  err = ksba_crl_set_reader (crl, reader);
+  if ( err )
+    {
+      log_error (_("ksba_crl_set_reader failed: %s\n"), gpg_strerror (err));
+      goto leave;
+    }
+
+  /* Create a temporary cache file to load the CRL into. */
+  {
+    char *tmpfname, *p;
+    const char *nodename;
+#ifndef HAVE_W32_SYSTEM
+    struct utsname utsbuf;
+#endif
+
+#ifdef HAVE_W32_SYSTEM
+    nodename = "unknown";
+#else
+    if (uname (&utsbuf))
+      nodename = "unknown";
+    else
+      nodename = utsbuf.nodename;
+#endif
+
+    estream_asprintf (&tmpfname, "crl-tmp-%s-%u-%p.db.tmp",
+                      nodename, (unsigned int)getpid (), &tmpfname);
+    if (!tmpfname)
+      {
+        err = gpg_error_from_syserror ();
+        goto leave;
+      }
+    for (p=tmpfname; *p; p++)
+      if (*p == '/')
+        *p = '.';
+    fname = make_filename (opt.homedir_cache, DBDIR_D, tmpfname, NULL);
+    xfree (tmpfname);
+    if (!remove (fname))
+      log_info (_("removed stale temporary cache file `%s'\n"), fname);
+    else if (errno != ENOENT) 
+      {
+        err = gpg_error_from_syserror ();
+        log_error (_("problem removing stale temporary cache file `%s': %s\n"),
+                   fname, gpg_strerror (err));
+        goto leave;
+      }
+  }
+
+  fd_cdb = open (fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+  if (fd_cdb == -1)
+    {
+      err = gpg_error_from_errno (errno);
+      log_error (_("error creating temporary cache file `%s': %s\n"),
+                 fname, strerror (errno));
+      goto leave;
+    }
+  cdb_make_start(&cdb, fd_cdb);
+
+  err = crl_parse_insert (ctrl, crl, &cdb, fname,
+                          &issuer, thisupdate, nextupdate, &trust_anchor);
+  if (err)
+    {
+      log_error (_("crl_parse_insert failed: %s\n"), gpg_strerror (err));
+      /* Error in cleanup ignored.  */
+      cdb_make_finish (&cdb);
+      goto leave;
+    }
+
+  /* Finish the database. */
+  if (cdb_make_finish (&cdb))
+    {
+      err = gpg_error_from_errno (errno);
+      log_error (_("error finishing temporary cache file `%s': %s\n"),
+                 fname, strerror (errno));
+      goto leave;
+    }
+  if (close (fd_cdb))
+    {
+      err = gpg_error_from_errno (errno);
+      log_error (_("error closing temporary cache file `%s': %s\n"),
+                 fname, strerror (errno));
+      goto leave;
+    }
+  fd_cdb = -1;
+
+
+  /* Create a checksum. */
+  {
+    unsigned char md5buf[16];
+
+    if (hash_dbfile (fname, md5buf))
+      {
+        err = gpg_error (GPG_ERR_CHECKSUM);
+        goto leave;
+      }
+    checksum = hexify_data (md5buf, 16);
+  }
+
+
+  /* Check whether that new CRL is still not expired. */
+  gnupg_get_isotime (current_time);
+  if (strcmp (nextupdate, current_time) < 0 )
+    {
+      if (opt.force)
+        log_info (_("WARNING: new CRL still too old; it expired on %s "
+                    "- loading anyway\n"),  nextupdate);
+      else
+        {
+          log_error (_("new CRL still too old; it expired on %s\n"),
+                     nextupdate);
+          if (!err2)
+            err2 = gpg_error (GPG_ERR_CRL_TOO_OLD);
+          invalidate_crl |= 1;
+        }
+    }
+
+  /* Check for unknown critical extensions. */
+  for (idx=0; !(err=ksba_crl_get_extension (crl, idx, &oid, &critical,
+                                              NULL, NULL)); idx++)
+    {
+      if (!critical
+          || !strcmp (oid, oidstr_authorityKeyIdentifier)
+          || !strcmp (oid, oidstr_crlNumber) )
+        continue;
+      log_error (_("unknown critical CRL extension %s\n"), oid);
+      if (!err2)
+        err2 = gpg_error (GPG_ERR_INV_CRL);
+      invalidate_crl |= 2;
+    }
+  if (gpg_err_code (err) == GPG_ERR_EOF 
+      || gpg_err_code (err) == GPG_ERR_NO_DATA )
+    err = 0;
+  if (err)
+    {
+      log_error (_("error reading CRL extensions: %s\n"), gpg_strerror (err));
+      err = gpg_error (GPG_ERR_INV_CRL);
+    }
+
+
+  /* Create an hex encoded SHA-1 hash of the issuer DN to be
+     used as the key for the cache. */
+  issuer_hash = hashify_data (issuer, strlen (issuer));
+
+  /* Create an ENTRY. */
+  entry = xtrycalloc (1, sizeof *entry);
+  if (!entry)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  entry->release_ptr = xtrymalloc (strlen (issuer_hash) + 1 
+                                   + strlen (issuer) + 1
+                                   + strlen (url) + 1 
+                                   + strlen (checksum) + 1);
+  if (!entry->release_ptr)
+    {
+      err = gpg_error_from_syserror ();
+      xfree (entry);
+      entry = NULL;
+      goto leave;
+    }
+  entry->issuer_hash = entry->release_ptr;
+  entry->issuer = stpcpy (entry->issuer_hash, issuer_hash) + 1;
+  entry->url = stpcpy (entry->issuer, issuer) + 1;
+  entry->dbfile_hash = stpcpy (entry->url, url) + 1;
+  strcpy (entry->dbfile_hash, checksum);
+  gnupg_copy_time (entry->this_update, thisupdate); 
+  gnupg_copy_time (entry->next_update, nextupdate); 
+  gnupg_copy_time (entry->last_refresh, current_time);
+  entry->crl_number = get_crl_number (crl);
+  entry->authority_issuer = get_auth_key_id (crl, &entry->authority_serialno);
+  entry->invalid = invalidate_crl;
+  entry->user_trust_req = !!trust_anchor;
+  entry->check_trust_anchor = trust_anchor;
+  trust_anchor = NULL;
+
+  /* Check whether we already have an entry for this issuer and mark
+     it as deleted. We better use a loop, just in case duplicates got
+     somehow into the list. */
+  for (e = cache->entries; (e=find_entry (e, entry->issuer_hash)); e = e->next)
+    e->deleted = 1;
+  
+  /* Rename the temporary DB to the real name. */
+  newfname = make_db_file_name (entry->issuer_hash);
+  if (opt.verbose)
+    log_info (_("creating cache file `%s'\n"), newfname);
+#ifdef HAVE_W32_SYSTEM
+  unlink (newfname);
+#endif
+  if (rename (fname, newfname))
+    {
+      err = gpg_error_from_syserror ();
+      log_error (_("problem renaming `%s' to `%s': %s\n"),
+                 fname, newfname, gpg_strerror (err));
+      goto leave;
+    }
+  xfree (fname); fname = NULL; /*(let the cleanup code not try to remove it)*/
+
+  /* Link the new entry in. */
+  entry->next = cache->entries;
+  cache->entries = entry;
+  entry = NULL;
+
+  err = update_dir (cache);
+  if (err)
+    {
+      log_error (_("updating the DIR file failed - "
+                   "cache entry will get lost with the next program start\n"));
+      err = 0; /* Keep on running. */
+    }
+
+
+ leave:
+  release_one_cache_entry (entry);
+  if (fd_cdb != -1)
+    close (fd_cdb);
+  if (fname)
+    {
+      remove (fname);
+      xfree (fname);
+    }
+  xfree (newfname);
+  ksba_crl_release (crl);
+  xfree (issuer);
+  xfree (issuer_hash);
+  xfree (checksum);
+  xfree (trust_anchor);
+  return err ? err : err2;
+}
+
+
+/* Print one cached entry E in a human readable format to stream
+   FP. Return 0 on success. */
+static gpg_error_t
+list_one_crl_entry (crl_cache_t cache, crl_cache_entry_t e, FILE *fp)
+{
+  struct cdb_find cdbfp;
+  struct cdb *cdb;
+  int rc;
+  int warn = 0;
+  const unsigned char *s;
+
+  fputs ("--------------------------------------------------------\n", fp );
+  fprintf (fp, _("Begin CRL dump (retrieved via %s)\n"), e->url );
+  fprintf (fp, " Issuer:\t%s\n", e->issuer );
+  fprintf (fp, " Issuer Hash:\t%s\n", e->issuer_hash );
+  fprintf (fp, " This Update:\t%s\n", e->this_update ); 
+  fprintf (fp, " Next Update:\t%s\n", e->next_update ); 
+  fprintf (fp, " CRL Number :\t%s\n", e->crl_number? e->crl_number: "none");
+  fprintf (fp, " AuthKeyId  :\t%s\n",
+           e->authority_serialno? e->authority_serialno:"none");
+  if (e->authority_serialno && e->authority_issuer)
+    {
+      fputs ("             \t", fp);
+      for (s=e->authority_issuer; *s; s++)
+        if (*s == '\x01')
+          fputs ("\n             \t", fp);
+        else
+          putc (*s, fp);
+      putc ('\n', fp);
+    }
+  fprintf (fp, " Trust Check:\t%s\n", 
+           !e->user_trust_req? "[system]" :
+           e->check_trust_anchor? e->check_trust_anchor:"[missing]");
+
+  if ((e->invalid & 1))
+    fprintf (fp, _(" ERROR: The CRL will not be used because it was still too old after an update!\n"));
+  if ((e->invalid & 2))
+    fprintf (fp, _(" ERROR: The CRL will not be used due to an unknown critical extension!\n"));
+  if ((e->invalid & ~3))
+    fprintf (fp, _(" ERROR: The CRL will not be used\n"));
+
+  cdb = lock_db_file (cache, e);
+  if (!cdb)
+    return gpg_error (GPG_ERR_GENERAL);
+
+  if (!e->dbfile_checked)
+    fprintf (fp, _(" ERROR: This cached CRL may has been tampered with!\n"));
+
+  putc ('\n', fp);
+
+  rc = cdb_findinit (&cdbfp, cdb, NULL, 0);
+  while (!rc && (rc=cdb_findnext (&cdbfp)) > 0 )
+    {
+      unsigned char keyrecord[256];
+      unsigned char record[16];
+      int reason;
+      int any = 0;
+      cdbi_t n;
+      cdbi_t i;
+
+      rc = 0;
+      n = cdb_datalen (cdb);
+      if (n != 16)
+        {
+          log_error (_(" WARNING: invalid cache record length\n"));
+          warn = 1;
+          continue;
+        }
+
+      if (cdb_read (cdb, record, n, cdb_datapos (cdb)))
+        {
+          log_error (_("problem reading cache record: %s\n"),
+                     strerror (errno));
+          warn = 1;
+          continue;
+        }
+    
+      n = cdb_keylen (cdb);
+      if (n > sizeof keyrecord)
+        n = sizeof keyrecord;
+      if (cdb_read (cdb, keyrecord, n, cdb_keypos (cdb)))
+        {
+          log_error (_("problem reading cache key: %s\n"), strerror (errno));
+          warn = 1;
+          continue;
+        }
+
+      reason = *record;
+      fputs ("  ", fp);
+      for (i = 0; i < n; i++)
+        fprintf (fp, "%02X", keyrecord[i]);
+      fputs (":\t reasons( ", fp);
+    
+      if (reason & KSBA_CRLREASON_UNSPECIFIED)
+        fputs( "unspecified ", fp ), any = 1;
+      if (reason & KSBA_CRLREASON_KEY_COMPROMISE )
+        fputs( "key_compromise ", fp ), any = 1; 
+      if (reason & KSBA_CRLREASON_CA_COMPROMISE )
+        fputs( "ca_compromise ", fp ), any = 1; 
+      if (reason & KSBA_CRLREASON_AFFILIATION_CHANGED )
+        fputs( "affiliation_changed ", fp ), any = 1; 
+      if (reason & KSBA_CRLREASON_SUPERSEDED )
+        fputs( "superseeded", fp ), any = 1; 
+      if (reason & KSBA_CRLREASON_CESSATION_OF_OPERATION )
+        fputs( "cessation_of_operation", fp ), any = 1; 
+      if (reason & KSBA_CRLREASON_CERTIFICATE_HOLD )
+        fputs( "certificate_hold", fp ), any = 1; 
+      if (reason && !any)
+        fputs( "other", fp ); 
+      
+      fprintf (fp, ") rdate: %.15s\n", record+1);
+    } 
+  if (rc)
+    log_error (_("error reading cache entry from db: %s\n"), strerror (rc));
+
+  unlock_db_file (cache, e);
+  fprintf (fp, _("End CRL dump\n") );
+  putc ('\n', fp);
+
+  return (rc||warn)? gpg_error (GPG_ERR_GENERAL) : 0;
+}
+
+
+/* Print the contents of the CRL CACHE in a human readable format to
+   stream FP. */
+gpg_error_t 
+crl_cache_list (FILE *fp) 
+{
+  crl_cache_t cache = get_current_cache ();
+  crl_cache_entry_t entry;
+  gpg_error_t err = 0;
+
+  for (entry = cache->entries;
+       entry && !entry->deleted && !err;
+       entry = entry->next ) 
+    err = list_one_crl_entry (cache, entry, fp);
+
+  return err;
+}
+
+
+/* Load the CRL containing the file named FILENAME into our CRL cache. */
+gpg_error_t
+crl_cache_load (ctrl_t ctrl, const char *filename)
+{
+  gpg_error_t err;
+  FILE *fp;
+  ksba_reader_t reader;
+
+  fp = fopen (filename, "r");
+  if (!fp)
+    {
+      err = gpg_error_from_errno (errno);
+      log_error (_("can't open `%s': %s\n"), filename, strerror (errno));
+      return err;
+    }
+
+  err = ksba_reader_new (&reader);
+  if (!err)
+    err = ksba_reader_set_file (reader, fp);
+  if (err)
+    {
+      log_error (_("error initializing reader object: %s\n"),
+                 gpg_strerror (err));
+      ksba_reader_release (reader);
+      return err;
+    }
+  err = crl_cache_insert (ctrl, filename, reader);
+  ksba_reader_release (reader);
+  fclose (fp);
+  return err;
+}
+
+
+/* Locate the corresponding CRL for the certificate CERT, read and
+   verify the CRL and store it in the cache.  */
+gpg_error_t
+crl_cache_reload_crl (ctrl_t ctrl, ksba_cert_t cert) 
+{
+  gpg_error_t err;
+  ksba_reader_t reader = NULL;
+  char *issuer = NULL;
+  ksba_name_t distpoint = NULL;
+  ksba_name_t issuername = NULL;
+  char *distpoint_uri = NULL;
+  char *issuername_uri = NULL;
+  int any_dist_point = 0;
+  int seq;
+
+  /* Loop over all distribution points, get the CRLs and put them into
+     the cache. */
+  if (opt.verbose)
+    log_info ("checking distribution points\n");
+  seq = 0;
+  while ( !(err = ksba_cert_get_crl_dist_point (cert, seq++,
+                                                &distpoint,
+                                                &issuername, NULL )))
+    {
+      int name_seq; 
+      gpg_error_t last_err = 0;
+
+      if (!distpoint && !issuername)
+        {
+          if (opt.verbose)
+            log_info ("no issuer name and no distribution point\n");
+          break; /* Not allowed; i.e. an invalid certificate.  We give
+                    up here and hope that the default method returns a
+                    suitable CRL. */
+        }
+
+      xfree (issuername_uri); issuername_uri = NULL;
+
+      /* Get the URIs.  We do this in a loop to iterate over all names
+         in the crlDP. */
+      for (name_seq=0; ksba_name_enum (distpoint, name_seq); name_seq++)
+        {
+          xfree (distpoint_uri); distpoint_uri = NULL;
+          distpoint_uri = ksba_name_get_uri (distpoint, name_seq);
+          if (!distpoint_uri)
+            continue;
+          
+          if (!strncmp (distpoint_uri, "ldap:", 5)
+              || !strncmp (distpoint_uri, "ldaps:", 6))
+            {
+              if (opt.ignore_ldap_dp)
+                continue;
+            }
+          else if (!strncmp (distpoint_uri, "http:", 5)
+                   || !strncmp (distpoint_uri, "https:", 6))
+            {
+              if (opt.ignore_http_dp)
+                continue;
+            }
+          else
+            continue; /* Skip unknown schemes. */
+          
+          any_dist_point = 1;
+          
+          if (opt.verbose)
+            log_info ("fetching CRL from `%s'\n", distpoint_uri);
+          err = crl_fetch (ctrl, distpoint_uri, &reader);
+          if (err)
+            {
+              log_error (_("crl_fetch via DP failed: %s\n"),
+                         gpg_strerror (err));
+              last_err = err;
+              continue; /* with the next name. */
+            }
+          
+          if (opt.verbose)
+            log_info ("inserting CRL (reader %p)\n", reader);
+          err = crl_cache_insert (ctrl, distpoint_uri, reader); 
+          if (err)
+            {
+              log_error (_("crl_cache_insert via DP failed: %s\n"),
+                         gpg_strerror (err));
+              last_err = err;
+              continue; /* with the next name. */
+            }
+          last_err = 0;
+          break; /* Ready. */
+        }
+      if (last_err)
+        {
+          err = last_err;
+          goto leave;
+        }
+      
+      ksba_name_release (distpoint); distpoint = NULL;
+
+      /* We don't do anything with issuername_uri yet but we keep the
+         code for documentation. */
+      issuername_uri =  ksba_name_get_uri (issuername, 0); 
+      ksba_name_release (issuername); issuername = NULL;
+
+    }
+  if (gpg_err_code (err) == GPG_ERR_EOF)
+    err = 0;
+
+  /* If we did not found any distpoint, try something reasonable. */
+  if (!any_dist_point )
+    {
+      if (opt.verbose)
+        log_info ("no distribution point - trying issuer name\n");
+      
+      if (reader)
+        {
+          crl_close_reader (reader);
+          reader = NULL;
+        }
+
+      issuer = ksba_cert_get_issuer (cert, 0);
+      if (!issuer) 
+        {
+          log_error ("oops: issuer missing in certificate\n");
+          err = gpg_error (GPG_ERR_INV_CERT_OBJ); 
+          goto leave;
+        }
+
+      if (opt.verbose)
+        log_info ("fetching CRL from default location\n");
+      err = crl_fetch_default (ctrl, issuer, &reader);
+      if (err)
+          {
+            log_error ("crl_fetch via issuer failed: %s\n",
+                       gpg_strerror (err));
+            goto leave;
+          }
+
+      if (opt.verbose)
+        log_info ("inserting CRL (reader %p)\n", reader);
+      err = crl_cache_insert (ctrl, "default location(s)", reader);
+      if (err)
+        {
+          log_error (_("crl_cache_insert via issuer failed: %s\n"),
+                     gpg_strerror (err));
+          goto leave;
+        }
+    }
+
+ leave:
+  if (reader)
+    crl_close_reader (reader);
+  xfree (distpoint_uri);
+  xfree (issuername_uri);
+  ksba_name_release (distpoint); 
+  ksba_name_release (issuername); 
+  ksba_free (issuer);
+  return err;
+}
+
diff --git a/dirmngr/crlcache.h b/dirmngr/crlcache.h
new file mode 100644 (file)
index 0000000..b9e4874
--- /dev/null
@@ -0,0 +1,70 @@
+/* crlcache.h - LDAP access
+ *      Copyright (C) 2002 Klarälvdalens Datakonsult AB
+ *
+ * This file is part of DirMngr.
+ *
+ * DirMngr 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * DirMngr 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef CRLCACHE_H
+#define CRLCACHE_H
+
+
+typedef enum 
+  {
+    CRL_CACHE_VALID = 0, 
+    CRL_CACHE_INVALID,
+    CRL_CACHE_DONTKNOW,
+    CRL_CACHE_CANTUSE
+  }
+crl_cache_result_t;
+
+typedef enum foo
+  { 
+    CRL_SIG_OK = 0,
+    CRL_SIG_NOT_OK,
+    CRL_TOO_OLD,
+    CRL_SIG_ERROR, 
+    CRL_GENERAL_ERROR
+  }
+crl_sig_result_t;
+
+struct crl_cache_entry_s;
+typedef struct crl_cache_entry_s *crl_cache_entry_t;
+
+
+void crl_cache_init (void);
+void crl_cache_deinit (void);
+int crl_cache_flush(void);
+
+crl_cache_result_t crl_cache_isvalid (ctrl_t ctrl,
+                                      const char *issuer_hash,
+                                      const char *cert_id,
+                                      int force_refresh);
+
+gpg_error_t crl_cache_cert_isvalid (ctrl_t ctrl, ksba_cert_t cert,
+                                    int force_refresh);
+
+gpg_error_t crl_cache_insert (ctrl_t ctrl, const char *url,
+                              ksba_reader_t reader);
+
+gpg_error_t crl_cache_list (FILE* fp);
+
+gpg_error_t crl_cache_load (ctrl_t ctrl, const char *filename);
+
+gpg_error_t crl_cache_reload_crl (ctrl_t ctrl, ksba_cert_t cert);
+
+
+#endif /* CRLCACHE_H */
diff --git a/dirmngr/crlfetch.c b/dirmngr/crlfetch.c
new file mode 100644 (file)
index 0000000..ca6c77a
--- /dev/null
@@ -0,0 +1,479 @@
+/* crlfetch.c - LDAP access
+ *      Copyright (C) 2002 Klarälvdalens Datakonsult AB
+ *      Copyright (C) 2003, 2004, 2005, 2006, 2007 g10 Code GmbH
+ *
+ * This file is part of DirMngr.
+ *
+ * DirMngr 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * DirMngr 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/>.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <pth.h>
+
+#include "crlfetch.h"
+#include "dirmngr.h"
+#include "misc.h"
+#include "http.h"
+
+#include "estream.h"
+
+
+/* For detecting armored CRLs received via HTTP (yes, such CRLS really
+   exits, e.g. http://grid.fzk.de/ca/gridka-crl.pem at least in June
+   2008) we need a context in the reader callback.  */
+struct reader_cb_context_s
+{
+  estream_t fp;             /* The stream used with the ksba reader.  */
+  int checked:1;            /* PEM/binary detection ahs been done.    */
+  int is_pem:1;             /* The file stream is PEM encoded.        */
+  struct b64state b64state; /* The state used for Base64 decoding.    */
+};
+
+
+/* We need to associate a reader object with the reader callback
+   context.  This table is used for it. */
+struct file_reader_map_s
+{
+  ksba_reader_t reader;
+  struct reader_cb_context_s *cb_ctx;
+};
+#define MAX_FILE_READER 50
+static struct file_reader_map_s file_reader_map[MAX_FILE_READER];
+
+/* Associate FP with READER.  If the table is full wait until another
+   thread has removed an entry.  */
+static void
+register_file_reader (ksba_reader_t reader, struct reader_cb_context_s *cb_ctx)
+{
+  int i;
+  
+  for (;;)
+    {
+      for (i=0; i < MAX_FILE_READER; i++)
+        if (!file_reader_map[i].reader)
+          {
+            file_reader_map[i].reader = reader;
+            file_reader_map[i].cb_ctx = cb_ctx;
+            return;
+          }
+      log_info (_("reader to file mapping table full - waiting\n"));
+      pth_sleep (2); 
+    }
+}
+
+/* Scan the table for an entry matching READER, remove that entry and
+   return the associated file pointer. */
+static struct reader_cb_context_s *
+get_file_reader (ksba_reader_t reader)
+{
+  struct reader_cb_context_s *cb_ctx = NULL;
+  int i;
+
+  for (i=0; i < MAX_FILE_READER; i++)
+    if (file_reader_map[i].reader == reader)
+      {
+        cb_ctx = file_reader_map[i].cb_ctx;
+        file_reader_map[i].reader = NULL;
+        file_reader_map[i].cb_ctx = NULL;
+        break;
+      }
+  return cb_ctx;
+}
+
+
+
+static int 
+my_es_read (void *opaque, char *buffer, size_t nbytes, size_t *nread)
+{
+  struct reader_cb_context_s *cb_ctx = opaque;
+  int result;
+
+  result = es_read (cb_ctx->fp, buffer, nbytes, nread);
+  if (result)
+    return result;
+  /* Fixme we should check whether the semantics of es_read are okay
+     and well defined.  I have some doubts.  */
+  if (nbytes && !*nread && es_feof (cb_ctx->fp))
+    return gpg_error (GPG_ERR_EOF);
+  if (!nread && es_ferror (cb_ctx->fp))
+    return gpg_error (GPG_ERR_EIO);
+
+  if (!cb_ctx->checked && *nread)
+    {
+      int c = *(unsigned char *)buffer;
+
+      cb_ctx->checked = 1;
+      if ( ((c & 0xc0) >> 6) == 0 /* class: universal */
+           && (c & 0x1f) == 16    /* sequence */
+           && (c & 0x20)          /* is constructed */ )
+        ; /* Binary data.  */
+      else
+        {
+          cb_ctx->is_pem = 1;
+          b64dec_start (&cb_ctx->b64state, "");
+        }
+    }
+  if (cb_ctx->is_pem && *nread)
+    {
+      size_t nread2;
+
+      if (b64dec_proc (&cb_ctx->b64state, buffer, *nread, &nread2))
+        {
+          /* EOF from decoder. */
+          *nread = 0;
+          result = gpg_error (GPG_ERR_EOF);
+        }
+      else
+        *nread = nread2;
+    }
+
+  return result;
+}
+           
+
+/* Fetch CRL from URL and return the entire CRL using new ksba reader
+   object in READER.  Note that this reader object should be closed
+   only using ldap_close_reader. */
+gpg_error_t
+crl_fetch (ctrl_t ctrl, const char *url, ksba_reader_t *reader)
+{
+  gpg_error_t err;
+  parsed_uri_t uri;
+  char *free_this = NULL;
+  int redirects_left = 2; /* We allow for 2 redirect levels.  */
+
+  *reader = NULL;
+
+ once_more:
+  err = http_parse_uri (&uri, url);
+  http_release_parsed_uri (uri);
+  if (err && url && !strncmp (url, "https:", 6))
+    {
+      /* Our HTTP code does not support TLS, thus we can't use this
+         scheme and it is frankly not useful for CRL retrieval anyway.
+         We resort to using http, assuming that the server also
+         provides plain http access. */
+      free_this = xtrymalloc (strlen (url) + 1);
+      if (free_this)
+        {
+          strcpy (stpcpy (free_this,"http:"), url+6);
+          err = http_parse_uri (&uri, free_this);
+          http_release_parsed_uri (uri);
+          if (!err)
+            {
+              log_info (_("using \"http\" instead of \"https\"\n"));
+              url = free_this;
+            }
+        }
+    }
+  if (!err) /* Yes, our HTTP code groks that. */
+    {
+      http_t hd;
+
+      if (opt.disable_http)
+        {
+          log_error (_("CRL access not possible due to disabled %s\n"),
+                     "HTTP");
+          err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+        }
+      else
+        err = http_open_document (&hd, url, NULL,
+                                  (opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0)
+                                  |HTTP_FLAG_NEED_HEADER
+                                  |(DBG_LOOKUP? HTTP_FLAG_LOG_RESP:0), 
+                                  opt.http_proxy, NULL);
+
+      switch ( err? 99999 : http_get_status_code (hd) )
+        {
+        case 200:
+          {
+            estream_t fp = http_get_read_ptr (hd);
+            struct reader_cb_context_s *cb_ctx;
+
+            cb_ctx = xtrycalloc (1, sizeof *cb_ctx);
+            if (!cb_ctx)
+              err = gpg_error_from_syserror ();
+            if (!err)
+              err = ksba_reader_new (reader);
+            if (!err)
+              {
+                cb_ctx->fp = fp;
+                err = ksba_reader_set_cb (*reader, &my_es_read, cb_ctx);
+              }
+            if (err)
+              {
+                log_error (_("error initializing reader object: %s\n"),
+                           gpg_strerror (err));
+                ksba_reader_release (*reader);
+                *reader = NULL;
+                http_close (hd, 0);
+              }
+            else
+              {
+                /* The ksba reader misses a user pointer thus we need
+                   to come up with our own way of associating a file
+                   pointer (or well the callback context) with the
+                   reader.  It is only required when closing the
+                   reader thus there is no performance issue doing it
+                   this way.  */
+                register_file_reader (*reader, cb_ctx);
+                http_close (hd, 1);
+              }
+          }
+          break;
+        
+        case 301: /* Redirection (perm.). */
+        case 302: /* Redirection (temp.). */
+          {
+            const char *s = http_get_header (hd, "Location");
+
+            log_info (_("URL `%s' redirected to `%s' (%u)\n"),
+                      url, s?s:"[none]", http_get_status_code (hd));
+            if (s && *s && redirects_left-- )
+              {
+                xfree (free_this); url = NULL;
+                free_this = xtrystrdup (s);
+                if (!free_this)
+                  err = gpg_error_from_errno (errno);
+                else
+                  {
+                    url = free_this;
+                    http_close (hd, 0);
+                    /* Note, that our implementation of redirection
+                       actually handles a redirect to LDAP.  */
+                    goto once_more;
+                  }
+              }
+            else
+              err = gpg_error (GPG_ERR_NO_DATA);
+            log_error (_("too many redirections\n")); /* Or no "Location". */
+            http_close (hd, 0);
+          }
+          break;
+  
+        case 99999: /* Made up status code foer error reporting.  */
+          log_error (_("error retrieving `%s': %s\n"),
+                     url, gpg_strerror (err));
+          break;
+
+        default:
+          log_error (_("error retrieving `%s': http status %u\n"),
+                     url, http_get_status_code (hd));
+          err = gpg_error (GPG_ERR_NO_DATA);
+          http_close (hd, 0);
+        }
+    }
+  else /* Let the LDAP code try other schemes. */
+    {
+      if (opt.disable_ldap)
+        {
+          log_error (_("CRL access not possible due to disabled %s\n"),
+                     "LDAP");
+          err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+        }
+      else
+        err = url_fetch_ldap (ctrl, url, NULL, 0, reader);
+    }
+
+  xfree (free_this);
+  return err;
+}
+
+
+/* Fetch CRL for ISSUER using a default server. Return the entire CRL
+   as a newly opened stream returned in R_FP. */
+gpg_error_t
+crl_fetch_default (ctrl_t ctrl, const char *issuer, ksba_reader_t *reader)
+{
+  if (opt.disable_ldap)
+    {
+      log_error (_("CRL access not possible due to disabled %s\n"),
+                 "LDAP");
+      return gpg_error (GPG_ERR_NOT_SUPPORTED);
+    }
+  return attr_fetch_ldap (ctrl, issuer, "certificateRevocationList",
+                          reader);
+}
+
+
+/* Fetch a CA certificate for DN using the default server. This
+   function only initiates the fetch; fetch_next_cert must be used to
+   actually read the certificate; end_cert_fetch to end the
+   operation. */
+gpg_error_t
+ca_cert_fetch (ctrl_t ctrl, cert_fetch_context_t *context, const char *dn)
+{
+  if (opt.disable_ldap)
+    {
+      log_error (_("CRL access not possible due to disabled %s\n"),
+                 "LDAP");
+      return gpg_error (GPG_ERR_NOT_SUPPORTED);
+    }
+  return start_default_fetch_ldap (ctrl, context, dn, "cACertificate");
+}
+
+
+gpg_error_t
+start_cert_fetch (ctrl_t ctrl, cert_fetch_context_t *context,
+                  strlist_t patterns, const ldap_server_t server)
+{
+  if (opt.disable_ldap)
+    {
+      log_error (_("certificate search not possible due to disabled %s\n"),
+                 "LDAP");
+      return gpg_error (GPG_ERR_NOT_SUPPORTED);
+    }
+  return start_cert_fetch_ldap (ctrl, context, patterns, server);
+}
+
+
+gpg_error_t
+fetch_next_cert (cert_fetch_context_t context,
+                 unsigned char **value, size_t * valuelen)
+{
+  return fetch_next_cert_ldap (context, value, valuelen);
+}
+
+
+/* Fetch the next data from CONTEXT, assuming it is a certificate and return
+   it as a cert object in R_CERT.  */
+gpg_error_t
+fetch_next_ksba_cert (cert_fetch_context_t context, ksba_cert_t *r_cert)
+{
+  gpg_error_t err;
+  unsigned char *value;
+  size_t valuelen;
+  ksba_cert_t cert;
+  
+  *r_cert = NULL;
+
+  err = fetch_next_cert_ldap (context, &value, &valuelen);
+  if (!err && !value)
+    err = gpg_error (GPG_ERR_BUG);
+  if (err)
+    return err;
+
+  err = ksba_cert_new (&cert);
+  if (err)
+    {
+      xfree (value);
+      return err;
+    }
+
+  err = ksba_cert_init_from_mem (cert, value, valuelen);
+  xfree (value);
+  if (err)
+    {
+      ksba_cert_release (cert);
+      return err;
+    }
+  *r_cert = cert;
+  return 0;
+}
+
+
+void
+end_cert_fetch (cert_fetch_context_t context)
+{
+  return end_cert_fetch_ldap (context);
+}
+
+
+/* Lookup a cert by it's URL.  */
+gpg_error_t
+fetch_cert_by_url (ctrl_t ctrl, const char *url,
+                  unsigned char **value, size_t *valuelen)
+{
+  const unsigned char *cert_image;
+  size_t cert_image_n;
+  ksba_reader_t reader;
+  ksba_cert_t cert;
+  gpg_error_t err;
+
+  *value = NULL;
+  *valuelen = 0;
+  cert_image = NULL;
+  reader = NULL;
+  cert = NULL;
+
+  err = url_fetch_ldap (ctrl, url, NULL, 0, &reader);
+  if (err)
+    goto leave;
+
+  err = ksba_cert_new (&cert);
+  if (err)
+    goto leave;
+
+  err = ksba_cert_read_der (cert, reader);
+  if (err)
+    goto leave;
+
+  cert_image = ksba_cert_get_image (cert, &cert_image_n);
+  if (!cert_image || !cert_image_n)
+    {
+      err = gpg_error (GPG_ERR_INV_CERT_OBJ);
+      goto leave;
+    }
+
+  *value = xtrymalloc (cert_image_n);
+  if (!*value)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+
+  memcpy (*value, cert_image, cert_image_n);
+  *valuelen = cert_image_n;
+
+ leave:
+
+  ksba_cert_release (cert);
+  ldap_wrapper_release_context (reader);
+
+  return err;  
+}
+
+/* This function is to be used to close the reader object.  In
+   addition to running ksba_reader_release it also releases the LDAP
+   or HTTP contexts associated with that reader.  */
+void
+crl_close_reader (ksba_reader_t reader)
+{
+  struct reader_cb_context_s *cb_ctx;
+
+  if (!reader)
+    return;
+
+  /* Check whether this is a HTTP one. */
+  cb_ctx = get_file_reader (reader);
+  if (cb_ctx)
+    {
+      /* This is an HTTP context. */
+      if (cb_ctx->fp) 
+        es_fclose (cb_ctx->fp);
+      /* Release the base64 decoder state.  */
+      if (cb_ctx->is_pem)
+        b64dec_finish (&cb_ctx->b64state);
+      /* Release the callback context.  */
+      xfree (cb_ctx);
+    }
+  else /* This is an ldap wrapper context (Currently not used). */
+    ldap_wrapper_release_context (reader);
+
+  /* Now get rid of the reader object. */
+  ksba_reader_release (reader);
+}
diff --git a/dirmngr/crlfetch.h b/dirmngr/crlfetch.h
new file mode 100644 (file)
index 0000000..e42196d
--- /dev/null
@@ -0,0 +1,93 @@
+/* crlfetch.h - LDAP access
+ *      Copyright (C) 2002 Klarälvdalens Datakonsult AB
+ *
+ * This file is part of DirMngr.
+ *
+ * DirMngr 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * DirMngr 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 CRLFETCH_H
+#define CRLFETCH_H
+
+#include "dirmngr.h"
+
+
+struct cert_fetch_context_s;
+typedef struct cert_fetch_context_s *cert_fetch_context_t;
+
+
+/* Fetch CRL from URL. */
+gpg_error_t crl_fetch (ctrl_t ctrl, const char* url, ksba_reader_t *reader);
+
+/* Fetch CRL for ISSUER using default server. */
+gpg_error_t crl_fetch_default (ctrl_t ctrl,
+                               const char* issuer, ksba_reader_t *reader);
+
+
+/* Fetch cert for DN. */
+gpg_error_t ca_cert_fetch (ctrl_t ctrl, cert_fetch_context_t *context,
+                           const char *dn);
+
+
+/* Query the server for certs matching patterns. */
+gpg_error_t start_cert_fetch (ctrl_t ctrl,
+                              cert_fetch_context_t *context,
+                              strlist_t patterns,
+                              const ldap_server_t server);
+gpg_error_t fetch_next_cert(cert_fetch_context_t context,
+                            unsigned char **value, size_t *valuelen);
+gpg_error_t fetch_next_ksba_cert (cert_fetch_context_t context,
+                                  ksba_cert_t *r_cert);
+void end_cert_fetch (cert_fetch_context_t context);
+
+/* Lookup a cert by it's URL.  */
+g