dirmngr (11 files)

cvs user wk cvs at cvs.gnupg.org
Thu Nov 18 16:31:40 CET 2004


    Date: Thursday, November 18, 2004 @ 16:37:48
  Author: wk
    Path: /cvs/dirmngr/dirmngr

Modified: doc/dirmngr.texi src/ChangeLog src/certcache.c src/certcache.h
          src/crlcache.c src/dirmngr-client.c src/dirmngr.c
          src/dirmngr_ldap.c src/ldap.c src/server.c src/validate.c

* 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.


----------------------+
 doc/dirmngr.texi     |  110 ++++++++++++++
 src/ChangeLog        |   21 ++
 src/certcache.c      |  362 +++++++++++++++++++++++++++++++++++++++++++++++--
 src/certcache.h      |   17 ++
 src/crlcache.c       |   18 ++
 src/dirmngr-client.c |  106 +++++++++++++-
 src/dirmngr.c        |   13 +
 src/dirmngr_ldap.c   |    2 
 src/ldap.c           |   21 +-
 src/server.c         |  110 ++++++++++++++
 src/validate.c       |    8 -
 11 files changed, 754 insertions(+), 34 deletions(-)


Index: dirmngr/doc/dirmngr.texi
diff -u dirmngr/doc/dirmngr.texi:1.14 dirmngr/doc/dirmngr.texi:1.15
--- dirmngr/doc/dirmngr.texi:1.14	Tue Nov 16 19:24:35 2004
+++ dirmngr/doc/dirmngr.texi	Thu Nov 18 16:37:48 2004
@@ -115,6 +115,7 @@
 @menu
 * Dirmngr Commands::    List of all commands.
 * Dirmngr Options::     List of all options.
+* Dirmngr Signals::     Use of signals.
 * Dirmngr Examples::    Some usage examples.
 * Dirmngr Protocol::    The protocol dirmngr uses.
 * Dirmngr Client::      How to use the Dirmngr client tool.
@@ -352,6 +353,44 @@
 
 @end table
 
+
+ at c 
+ at c Dirmngr Signals
+ at c
+ at node Dirmngr Signals
+ at chapter Use of signals.
+A running @command{dirmngr} may be controlled by signals, i.e. using
+the @command{kill} command to send a signal to the process. 
+
+Here is a list of supported signals:
+
+ at table @gnupgtabopt
+
+ at item SIGHUP
+ at cpindex SIGHUP
+This signals flushes all internally cached CRLs as well as any cached
+certificates.  Then the certificate cache is reinitialized as on
+startup.
+
+ at item SIGTERM
+ at cpindex SIGTERM
+Shuts down the process but waits until all current requests are
+fulfilled.  If the process has received 3 of these signals and requests
+are still pending, a shutdown is forced.
+
+ at item SIGINT
+ at cpindex SIGINT
+Shuts down the process immediately.
+
+
+ at item SIGUSR1
+ at cpindex SIGUSR1
+This prints some caching statistics to the log file.
+
+ at end table
+
+
+
 @c
 @c  Examples
 @c
@@ -360,12 +399,30 @@
 
 @c man begin EXAMPLES
 
+The way to start the dirmngr in the foreground (as done by tools if no
+dirmngr is running in the background) is to use:
 @example
-$ dirmngr --server -v
+  dirmngr --server -v
 @end example
 
+If a dirmngr is supposed to be used as a system wide daemon, it should
+be started like:
+ at example 
+  dirmngr --daemon
+ at end example
+This will force it to go into the backround, read the default
+certificates (including the trusted root certificates) and listen on a
+socket for client requests.  It does also print information about the
+socket used but they are only for compatibilty reasons with old GnuPG
+versions and may be ignored.
+
+
+
 @c man end
 
+
+
+
 @c
 @c  Assuan Protocol
 @c
@@ -380,6 +437,8 @@
 * Dirmngr ISVALID::     Validate a certificate using a CRL or OCSP.
 * Dirmngr CHECKCRL::    Validate a certificate using a CRL.
 * Dirmngr CHECKOCSP::   Validate a certificate using OCSP.
+* Dirmngr CACHECERT::   Put a certificate into the internal cache.
+* Dirmngr VALIDATE::    Validate a certificate for debugging.
 @end menu
 
 @node Dirmngr LOOKUP
@@ -504,6 +563,45 @@
 The return code is 0 for success; i.e. the certificate has not been
 revoked or one of the usual error codes from libgpg-error.
 
+ at node Dirmngr CACHECERT
+ at section Put a certificate into the internal cache
+
+Put a certificate into the internal cache.  This command might be
+useful if a client knows in advance certificates required for a test and
+wnats to make sure they get added to the internal cache.  It is also
+helpful for debugging.  To get the actual certificate, this command
+immediately inquires it using
+
+ at example
+  S: INQUIRE TARGETCERT 
+  C: D <DER encoded certificate>
+  C: END
+ at end example
+
+Thus the caller is expected to return the certificate for the request
+as a binary blob. 
+
+ at noindent
+The return code is 0 for success; i.e. the certificate has not been
+succesfully cached or one of the usual error codes from libgpg-error.
+
+ at node Dirmngr VALIDATE
+ at section Validate a certificate for debugging
+
+Validate a certificate using the certificate validation fucntion used
+internally by dirmngr.  This command is only useful for debugging.  To
+get the actual certificate, this command immediately inquires it using
+
+ at example
+  S: INQUIRE TARGETCERT 
+  C: D <DER encoded certificate>
+  C: END
+ at end example
+
+Thus the caller is expected to return the certificate for the request
+as a binary blob. 
+
+
 
 @c -------------------------------------------
 @c Dirmngr Client
@@ -594,6 +692,16 @@
 also implicitly use with @code{--ping}.
 
 
+ at item --cache-cert
+ at opindex cache-cert
+Put the given certificate into the cache of a running dirmngr.  This is
+mainly useful for debugging.
+
+ at item --validate
+ at opindex validate
+Validate the given certificate using dirmngr's internal validation code.
+This is mainly useful for debugging.
+
 @end table
 
 
Index: dirmngr/src/ChangeLog
diff -u dirmngr/src/ChangeLog:1.21 dirmngr/src/ChangeLog:1.22
--- dirmngr/src/ChangeLog:1.21	Tue Nov 16 19:24:35 2004
+++ dirmngr/src/ChangeLog	Thu Nov 18 16:37:48 2004
@@ -1,3 +1,24 @@
+2004-11-18  Werner Koch  <wk at g10code.com>
+
+	* 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.
+
 2004-11-16  Werner Koch  <wk at g10code.com>
 
 	* Makefile.am (AM_CPPFLAGS): Define DIRMNGR_SYSCONFDIR and
Index: dirmngr/src/certcache.c
diff -u dirmngr/src/certcache.c:1.1 dirmngr/src/certcache.c:1.2
--- dirmngr/src/certcache.c:1.1	Tue Nov 16 19:24:35 2004
+++ dirmngr/src/certcache.c	Thu Nov 18 16:37:48 2004
@@ -24,6 +24,9 @@
 #include <stdlib.h>
 #include <errno.h>
 #include <assert.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <pth.h>
 
 #include <ksba.h>
 
@@ -31,6 +34,8 @@
 #include "misc.h"
 #include "certcache.h"
 
+#define MAX_EXTRA_CACHED_CERTS 200 /* FIXME: This is a debugging value.  */
+
 
 /* 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
@@ -46,21 +51,58 @@
   ksba_sexp_t sn;           /* The malloced serial number  */
   struct 
   {
-    int trusted;            /* This is a trusted root certificate.  */
-    
-
-
-
+    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 idnex by
+/* 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.  */
+static pth_rwlock_t cert_cache_lock = PTH_RWLOCK_INIT;
+
+/* 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;
+
 
 
+/* Helper to do the cahce locking.  */
+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
@@ -120,6 +162,296 @@
 }
 
 
+/* 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;
+  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. */
+static gpg_error_t
+put_cert (ksba_cert_t cert, int is_loaded, int is_trusted)
+{
+  unsigned char fpr[20];
+  cert_item_t ci;
+
+  /* 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;
+    }
+
+  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->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" 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)
+    {
+      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") )
+        continue; /* Not the desired "*.crt" 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);
+      ksba_cert_release (cert);
+      if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
+        log_info (_("certificate `%s' already cached\n"), fname);
+      else if (!err)
+        log_info (_("certificate `%s' loaded\n"), fname);
+      else
+        log_error (_("error loading certificate `%s': %s\n"),
+                     fname, gpg_strerror (err));
+    }
+
+  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;
+  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, "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);
+  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;
+}
+
+
+
 
 /* Return a certificate object for the given fingerprint.  FPR is
    expected to be a 20 byte binary SHA-1 fingerprint.  If no matching
@@ -132,13 +464,16 @@
 {
   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;
 }
 
@@ -151,6 +486,7 @@
   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)
@@ -158,10 +494,12 @@
             && !compare_serialno (ci->sn, serialno))
           {
             ksba_cert_ref (ci->cert);
+            release_cache_lock ();
             return ci->cert;
           }
     }
 
+  release_cache_lock ();
   return NULL;
 }
 
@@ -175,6 +513,7 @@
   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)
@@ -182,10 +521,12 @@
           if (!seq--)
             {
               ksba_cert_ref (ci->cert);
+              release_cache_lock ();
               return ci->cert;
             }
     }
 
+  release_cache_lock ();
   return NULL;
 }
 
@@ -219,18 +560,23 @@
 
   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)
-          return 0; /* Yes it is trusted. */
+          {
+            release_cache_lock ();
+            return 0; /* Yes, it is trusted. */
+          }
         break;
       }
 
+  release_cache_lock ();
   return gpg_error (GPG_ERR_NOT_TRUSTED);
 }
 
-
+
 /* 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.  */
Index: dirmngr/src/certcache.h
diff -u dirmngr/src/certcache.h:1.1 dirmngr/src/certcache.h:1.2
--- dirmngr/src/certcache.h:1.1	Tue Nov 16 19:24:35 2004
+++ dirmngr/src/certcache.h	Thu Nov 18 16:37:48 2004
@@ -21,6 +21,18 @@
 #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);
+
+
+/* Put CERT into the certificate cache.  */
+gpg_error_t cache_cert (ksba_cert_t 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
@@ -35,6 +47,11 @@
 ksba_cert_t get_cert_byfpr (const unsigned char *fpr);
 
 
+/* 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);
+
+
 /* 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.  */
Index: dirmngr/src/crlcache.c
diff -u dirmngr/src/crlcache.c:1.46 dirmngr/src/crlcache.c:1.47
--- dirmngr/src/crlcache.c:1.46	Tue Nov 16 19:24:35 2004
+++ dirmngr/src/crlcache.c	Thu Nov 18 16:37:48 2004
@@ -97,6 +97,8 @@
 #include <sys/utsname.h>
 
 #include "dirmngr.h"
+#include "validate.h"
+#include "certcache.h"
 #include "crlcache.h"
 #include "crlfetch.h"
 #include "misc.h"
@@ -1263,6 +1265,11 @@
   ksba_cert_t issuer_cert = NULL;
   ksba_reader_t reader;
 
+  /* First check whether it has already been cached.  */
+  issuer_cert = get_cert_byissuer (issuer, 0);
+  if (issuer_cert)
+    return issuer_cert; /* Done. */
+    
   /* 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. */
@@ -1297,6 +1304,9 @@
     log_error (_("invalid issuer certificate: %s\n"), gpg_strerror (err) );
   ksba_reader_release (reader);
 
+  if (!err)
+    cache_cert (issuer_cert);
+
   return issuer_cert;
 }
 
@@ -1543,6 +1553,14 @@
                            gpg_strerror (err));
                 goto failure;
               }
+            err = validate_cert_chain (issuer_cert, NULL);
+            if (err)
+              {
+                log_error (_("error checking validity of CRL "
+                             "signing certificate: %s\n"), 
+                           gpg_strerror (err));
+                goto failure;
+              }
           }
           break;
       
Index: dirmngr/src/dirmngr-client.c
diff -u dirmngr/src/dirmngr-client.c:1.1 dirmngr/src/dirmngr-client.c:1.2
--- dirmngr/src/dirmngr-client.c:1.1	Tue Nov 16 19:24:35 2004
+++ dirmngr/src/dirmngr-client.c	Thu Nov 18 16:37:48 2004
@@ -52,6 +52,8 @@
 
     oOCSP         = 500,
     oPing,
+    oCacheCert,
+    oValidate,
     oUseDaemon,
     oDummy
   };
@@ -61,9 +63,11 @@
 static ARGPARSE_OPTS opts[] = {
   { oVerbose,  "verbose",   0, N_("verbose") },
   { oQuiet,    "quiet",     0, N_("be somewhat more quiet") },
+  { oUseDaemon,"use-daemon",0, N_("force use of the daemon")},
   { oOCSP,     "ocsp",      0, N_("use OCSP instead of CRLs") },
   { oPing,     "ping",      0, N_("check whether a dirmngr is running")},
-  { oUseDaemon,"use-daemon",0, N_("force use of the daemon")},
+  { oCacheCert,"cache-cert",0, N_("add a certificate to the cache")},
+  { oValidate, "validate",  0, N_("validate a certificate")},
   {0}
 };
  
@@ -97,6 +101,10 @@
                                      unsigned char **rbuf, size_t *rbuflen);
 static gpg_error_t do_check (assuan_context_t ctx,
                              const unsigned char *cert, size_t certlen);
+static gpg_error_t do_cache (assuan_context_t ctx,
+                             const unsigned char *cert, size_t certlen);
+static gpg_error_t do_validate (assuan_context_t ctx,
+                                const unsigned char *cert, size_t certlen);
 
 
 
@@ -156,9 +164,11 @@
   gpg_error_t err;
   unsigned char *certbuf;
   size_t certbuflen;
-  int do_ping = 0;
+  int cmd_ping = 0;
   int use_daemon = 0;
-  
+  int cmd_cache_cert = 0;
+  int cmd_validate = 0;
+
   set_strusage (my_strusage);
   log_set_prefix ("dirmngr-client",
                   JNLIB_LOG_WITH_PREFIX); 
@@ -180,9 +190,12 @@
         {
         case oVerbose: opt.verbose++; break;
         case oQuiet: opt.quiet++; break;
-        case oOCSP: opt.use_ocsp++; break;
-        case oPing: do_ping = 1; break;
         case oUseDaemon: use_daemon = 1; break;
+          
+        case oOCSP: opt.use_ocsp++; break;
+        case oPing: cmd_ping = 1; break;
+        case oCacheCert: cmd_cache_cert = 1; break;
+        case oValidate: cmd_validate = 1; break;
 
         default : pargs.err = 2; break;
 	}
@@ -190,7 +203,7 @@
   if (log_get_errorcount (0))
     exit (2);
 
-  if (do_ping)
+  if (cmd_ping)
     err = 0;
   else if (!argc)
     {
@@ -221,11 +234,23 @@
       exit (2);
     }
 
-  ctx = start_dirmngr (do_ping || use_daemon);
+  ctx = start_dirmngr (cmd_ping || use_daemon);
   if (!ctx)
     exit (2);
 
-  if (!do_ping)
+  if (cmd_ping)
+    ;
+  else if (cmd_cache_cert)
+    {
+      err = do_cache (ctx, certbuf, certbuflen);
+      xfree (certbuf);
+    }
+  else if (cmd_validate)
+    {
+      err = do_validate (ctx, certbuf, certbuflen);
+      xfree (certbuf);
+    }
+  else
     {
       err = do_check (ctx, certbuf, certbuflen);
       xfree (certbuf);
@@ -233,12 +258,33 @@
 
   assuan_disconnect (ctx);
 
-  if (do_ping)
+  if (cmd_ping)
     {
       if (!opt.quiet)
         log_info (_("a dirmngr daemon is up and running\n"));
       return 0;
     }
+  else if (cmd_cache_cert)
+    {
+      if (err && gpg_err_code (err) == GPG_ERR_DUP_VALUE )
+        {
+          if (!opt.quiet)
+            log_info (_("certificate already cached\n"));
+        }
+      else if (err)
+        {
+          log_error (_("error caching certificate: %s\n"),
+                     gpg_strerror (err));
+          return 1;
+        }
+      return 0;
+    }
+  else if (cmd_validate && err)
+    {
+      log_error (_("validation of certificate failed: %s\n"),
+                 gpg_strerror (err));
+      return 1;
+    }
   else if (!err)
     {
       if (!opt.quiet)
@@ -472,3 +518,45 @@
     log_info ("response of dirmngr: %s\n", ae? assuan_strerror (ae): "okay");
   return map_assuan_err (ae);
 }
+
+/* Check the certificate CERT,CERTLEN for validity using a CRL or OCSP.
+   Return a proper error code. */
+static gpg_error_t
+do_cache (assuan_context_t ctx, const unsigned char *cert, size_t certlen)
+{
+  assuan_error_t ae;
+  struct inq_cert_parm_s parm;
+
+  memset (&parm, 0, sizeof parm);
+  parm.ctx = ctx;
+  parm.cert = cert;
+  parm.certlen = certlen;
+
+  ae = assuan_transact (ctx, "CACHECERT", NULL, NULL,
+                        inq_cert, &parm,
+                        NULL, NULL);
+  if (opt.verbose > 1)
+    log_info ("response of dirmngr: %s\n", ae? assuan_strerror (ae): "okay");
+  return map_assuan_err (ae);
+}
+
+/* Check the certificate CERT,CERTLEN for validity using dirmngrs
+   internal validate feature.  Return a proper error code. */
+static gpg_error_t
+do_validate (assuan_context_t ctx, const unsigned char *cert, size_t certlen)
+{
+  assuan_error_t ae;
+  struct inq_cert_parm_s parm;
+
+  memset (&parm, 0, sizeof parm);
+  parm.ctx = ctx;
+  parm.cert = cert;
+  parm.certlen = certlen;
+
+  ae = assuan_transact (ctx, "VALIDATE", NULL, NULL,
+                        inq_cert, &parm,
+                        NULL, NULL);
+  if (opt.verbose > 1)
+    log_info ("response of dirmngr: %s\n", ae? assuan_strerror (ae): "okay");
+  return map_assuan_err (ae);
+}
Index: dirmngr/src/dirmngr.c
diff -u dirmngr/src/dirmngr.c:1.41 dirmngr/src/dirmngr.c:1.42
--- dirmngr/src/dirmngr.c:1.41	Tue Nov 16 19:24:35 2004
+++ dirmngr/src/dirmngr.c	Thu Nov 18 16:37:48 2004
@@ -43,6 +43,7 @@
 
 #define JNLIB_NEED_LOG_LOGV
 #include "dirmngr.h"
+#include "certcache.h"
 #include "crlcache.h"
 #include "crlfetch.h"
 #include "misc.h"
@@ -137,6 +138,7 @@
   { oDebugAll, "debug-all" ,0,    "@"},
   { oDebugWait, "debug-wait", 1, "@"},
   { oNoGreeting, "no-greeting", 0, "@"},
+  { oHomedir, "homedir", 2, "@" },  
   {0}
 };
 
@@ -679,6 +681,7 @@
         }
 
       launch_ripper_thread ();
+      cert_cache_init ();
       crl_cache_init ();
       start_command_handler (-1);
     }
@@ -830,6 +833,7 @@
       
 
       launch_ripper_thread ();
+      cert_cache_init ();
       crl_cache_init ();
       handle_connections (fd);
       close (fd);
@@ -846,6 +850,7 @@
   else if (cmd == aLoadCRL)
     {
       launch_ripper_thread ();
+      cert_cache_init ();
       crl_cache_init ();
       if (!argc)
         rc = crl_cache_load (NULL, NULL);
@@ -863,6 +868,7 @@
         wrong_args ("--fetch-crl URL");
 
       launch_ripper_thread ();
+      cert_cache_init ();
       crl_cache_init ();
       rc = crl_fetch (argv[0], &reader);
       if (rc)
@@ -947,6 +953,7 @@
 cleanup (void)
 {
   crl_cache_deinit ();
+  cert_cache_deinit (1);
 
   free_ldapservers_list (opt.ldapservers);
   opt.ldapservers = NULL;
@@ -1140,10 +1147,14 @@
       log_info (_("SIGHUP received - "
                   "re-reading configuration and flushing caches\n"));
 /*       reread_configuration (); */
+      cert_cache_deinit (0);
+      crl_cache_deinit ();
+      cert_cache_init ();
+      crl_cache_init ();
       break;
       
     case SIGUSR1:
-      log_info (_("SIGUSR1 received - no action defined\n"));
+      cert_cache_print_stats ();
       break;
       
     case SIGUSR2:
Index: dirmngr/src/dirmngr_ldap.c
diff -u dirmngr/src/dirmngr_ldap.c:1.1 dirmngr/src/dirmngr_ldap.c:1.2
--- dirmngr/src/dirmngr_ldap.c:1.1	Tue Nov 16 19:24:35 2004
+++ dirmngr/src/dirmngr_ldap.c	Thu Nov 18 16:37:48 2004
@@ -209,8 +209,6 @@
   if (opt.port < 0 || opt.port > 65535)
     log_error (_("invalid port number %d\n"), opt.port);
 
-  sleep (5);
-
   if (log_get_errorcount (0))
     exit (2);
 
Index: dirmngr/src/ldap.c
diff -u dirmngr/src/ldap.c:1.33 dirmngr/src/ldap.c:1.34
--- dirmngr/src/ldap.c:1.33	Tue Nov 16 19:24:35 2004
+++ dirmngr/src/ldap.c	Thu Nov 18 16:37:48 2004
@@ -472,8 +472,8 @@
       return err;
     }
       
-  pid = fork ();
-  if (pid == -1)
+  pid = pth_fork ();
+  if (pid == (pid_t)(-1))
     {
       err = gpg_error_from_errno (errno);
       log_error (_("error forking process: %s\n"), strerror (errno));
@@ -510,13 +510,6 @@
         else
           arg_list[j] = (char*)argv[i];
 
-      /* Connect stderr to the second pipe.  */
-      if (rp2[1] != STDERR_FILENO && dup2 (rp2[1], STDERR_FILENO) == -1)
-        {
-          log_error (_("dup2 failed in child: %s\n"), strerror (errno));
-          _exit (4);
-        }
-
       /* Connect stdin to /dev/null.  */
       fd = open ("/dev/null", O_RDONLY);
       if (fd == -1)
@@ -536,6 +529,14 @@
           log_error (_("dup2 failed in child: %s\n"), strerror (errno));
           _exit (4);
         }
+
+      /* Connect stderr to the second pipe.  */
+      if (rp2[1] != STDERR_FILENO && dup2 (rp2[1], STDERR_FILENO) == -1)
+        {
+          log_error (_("dup2 failed in child: %s\n"), strerror (errno));
+          _exit (4);
+        }
+
   
       /* Close all files which will not be duped. */
       n = sysconf (_SC_OPEN_MAX);
@@ -557,7 +558,7 @@
   /* Parent. */
   close (rp[1]);
   close (rp2[1]);
-
+  
   ctx = xtrycalloc (1, sizeof *ctx);
   if (!ctx)
     {
Index: dirmngr/src/server.c
diff -u dirmngr/src/server.c:1.43 dirmngr/src/server.c:1.44
--- dirmngr/src/server.c:1.43	Tue Nov 16 19:24:35 2004
+++ dirmngr/src/server.c	Thu Nov 18 16:37:48 2004
@@ -38,6 +38,7 @@
 #include "dirmngr.h"
 #include "ocsp.h"
 #include "certcache.h"
+#include "validate.h"
 
 /* To avoid DoS attacks we limit the size of a certificate to
    something reasonable. */
@@ -905,6 +906,113 @@
 }
 
 
+/* CACHECERT 
+
+   Put a certificate into the internal cache.  This command might be
+   usedful if a client knows in advance certificates required for a
+   test and wnats to make sure they get added to the internal cache.
+   It is also helpful for debugging.  To get the actual certificate,
+   this command immediately inquires it using
+
+      INQUIRE TARGETCERT
+
+   and the caller is expected to return the certificate for the
+   request as a binary blob.
+*/
+static int
+cmd_cachecert (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err;
+  ksba_cert_t cert = NULL;
+  assuan_error_t ae;
+  unsigned char *value = NULL;
+  size_t valuelen; 
+      
+  ae = assuan_inquire (ctrl->server_local->assuan_ctx, "TARGETCERT",
+                       &value, &valuelen, MAX_CERT_LENGTH);
+  if (ae)
+    {
+      log_error (_("assuan_inquire failed: %s\n"), assuan_strerror (ae));
+      err = map_assuan_err (ae);
+      goto leave;
+    }
+  
+  if (!valuelen) /* No data returned; return a comprehensible error. */
+    err = gpg_error (GPG_ERR_MISSING_CERT);
+  else
+    {
+      err = ksba_cert_new (&cert);
+      if (!err)
+        err = ksba_cert_init_from_mem (cert, value, valuelen);
+    }
+  xfree (value);
+  if(err)
+    goto leave;
+
+  err = cache_cert (cert);
+
+ leave:
+  if (err)
+    log_error (_("command %s failed: %s\n"), "CACHECERT", gpg_strerror (err));
+  ksba_cert_release (cert);
+  return map_to_assuan_status (err);
+}
+
+
+/* VALIDATE 
+
+   Validate a certificate using the certificate validationj fucntion
+   used internally by dirmngr.  This command is only useful for
+   debugging.  To get the actual certificate, this command immediately
+   inquires it using
+
+      INQUIRE TARGETCERT
+
+   and the caller is expected to return the certificate for the
+   request as a binary blob.
+*/
+static int
+cmd_validate (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err;
+  ksba_cert_t cert = NULL;
+  assuan_error_t ae;
+  unsigned char *value = NULL;
+  size_t valuelen; 
+      
+  ae = assuan_inquire (ctrl->server_local->assuan_ctx, "TARGETCERT",
+                       &value, &valuelen, MAX_CERT_LENGTH);
+  if (ae)
+    {
+      log_error (_("assuan_inquire failed: %s\n"), assuan_strerror (ae));
+      err = map_assuan_err (ae);
+      goto leave;
+    }
+  
+  if (!valuelen) /* No data returned; return a comprehensible error. */
+    err = gpg_error (GPG_ERR_MISSING_CERT);
+  else
+    {
+      err = ksba_cert_new (&cert);
+      if (!err)
+        err = ksba_cert_init_from_mem (cert, value, valuelen);
+    }
+  xfree (value);
+  if(err)
+    goto leave;
+
+  err = validate_cert_chain (cert, NULL);
+
+ leave:
+  if (err)
+    log_error (_("command %s failed: %s\n"), "VALIDATE", gpg_strerror (err));
+  ksba_cert_release (cert);
+  return map_to_assuan_status (err);
+}
+
+
 
 /* Tell the assuan library about our commands. */
 static int
@@ -920,6 +1028,8 @@
     { "LOOKUP",     cmd_lookup },
     { "LOADCRL",    cmd_loadcrl },
     { "LISTCRLS",   cmd_listcrls },
+    { "CACHECERT",  cmd_cachecert },
+    { "VALIDATE",   cmd_validate },
     { "INPUT",      NULL },
     { "OUTPUT",     NULL },
     { NULL }
Index: dirmngr/src/validate.c
diff -u dirmngr/src/validate.c:1.1 dirmngr/src/validate.c:1.2
--- dirmngr/src/validate.c:1.1	Tue Nov 16 19:24:35 2004
+++ dirmngr/src/validate.c	Thu Nov 18 16:37:48 2004
@@ -169,6 +169,8 @@
 /* Validate the certificate CHAIN up to the trust anchor. Optionally
    return the closest expiration time in R_EXPTIME (this is useful for
    caching issues).  */
+
+ /* FIXME: We need to check for evoked certifciates too.  */
 gpg_error_t
 validate_cert_chain (ksba_cert_t cert, ksba_isotime_t r_exptime)
 {
@@ -320,7 +322,7 @@
           break;  /* Okay: a self-signed certicate is an end-point. */
         }
 
-      /* To avoid loops, we use an arbitary limit on tghe length of
+      /* To avoid loops, we use an arbitary limit on the length of
          the chain. */
       depth++;
       if (depth > maxdepth)
@@ -357,8 +359,8 @@
           dump_cert ("issuer", issuer_cert);
         }
 
-      /* Now check the signature of the certificate.  Well, not
-         really: We delay this until later so that fakedcertificates
+      /* Now check the signature of the certificate.  Well, we
+         shouldnot delay this until later so that faked certificates
          can't be turned into a DoS easily.  */
       err = check_cert_sig (issuer_cert, subject_cert);
       if (err)




More information about the Gnupg-commits mailing list