[svn] dirmngr - r227 - in trunk: . doc src

svn author wk cvs at cvs.gnupg.org
Fri Jun 23 20:56:27 CEST 2006


Author: wk
Date: 2006-06-23 20:56:26 +0200 (Fri, 23 Jun 2006)
New Revision: 227

Added:
   trunk/doc/internals.texi
Modified:
   trunk/ChangeLog
   trunk/configure.ac
   trunk/doc/dirmngr.texi
   trunk/src/ChangeLog
   trunk/src/certcache.c
   trunk/src/crlcache.c
   trunk/src/misc.c
   trunk/src/misc.h
   trunk/src/ocsp.c
   trunk/src/server.c
   trunk/src/validate.c
   trunk/src/validate.h
Log:
Cleanups, more diagnostics and bug fixes.
There are still some problems...  I am working on it.


Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog	2006-06-19 19:39:58 UTC (rev 226)
+++ trunk/ChangeLog	2006-06-23 18:56:26 UTC (rev 227)
@@ -1,3 +1,7 @@
+2006-06-23  Werner Koch  <wk at g10code.com>
+
+	* doc/internals.texi: new.
+
 2006-06-08  Marcus Brinkmann  <marcus at g10code.de>
 
 	* configure.ac (PTH_LIBS): Add --all to pth-config invocation.

Modified: trunk/configure.ac
===================================================================
--- trunk/configure.ac	2006-06-19 19:39:58 UTC (rev 226)
+++ trunk/configure.ac	2006-06-23 18:56:26 UTC (rev 227)
@@ -16,7 +16,8 @@
 # 
 # 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
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
 
 # Process this file with autoconf to produce a configure script.
 AC_PREREQ(2.59)
@@ -31,7 +32,7 @@
 
 NEED_LIBASSUAN_VERSION=0.6.8
 
-NEED_KSBA_VERSION=0.9.11
+NEED_KSBA_VERSION=0.9.13
 
 
 PACKAGE=$PACKAGE_NAME

Modified: trunk/doc/dirmngr.texi
===================================================================
--- trunk/doc/dirmngr.texi	2006-06-19 19:39:58 UTC (rev 226)
+++ trunk/doc/dirmngr.texi	2006-06-23 18:56:26 UTC (rev 227)
@@ -716,7 +716,7 @@
 @node Dirmngr VALIDATE
 @section Validate a certificate for debugging
 
-Validate a certificate using the certificate validation fucntion used
+Validate a certificate using the certificate validation function used
 internally by dirmngr.  This command is only useful for debugging.  To
 get the actual certificate, this command immediately inquires it using
 
@@ -774,7 +774,7 @@
 @item 2 (and other values)
 There was a problem checking the revocation state of the certificate.
 A message to stderr has given more detailed information.  Most likely
-this is due to a missing or tool old CRL or a network problem.
+this is due to a missing or expired CRL or due to a network problem.
 
 @end table
 

Added: trunk/doc/internals.texi
===================================================================
--- trunk/doc/internals.texi	2006-06-19 19:39:58 UTC (rev 226)
+++ trunk/doc/internals.texi	2006-06-23 18:56:26 UTC (rev 227)
@@ -0,0 +1,244 @@
+ at c internals.texi  - this file describes internals of DirMngr.
+
+
+ at section Verifying a Certificate
+
+There are several ways to request services from Dirmngr.  Almost all of
+them are done using the Assuan protocol.  What we describe here is the
+Assuan Command CHECKCRL as used for example by the dirmnr-client tool if
+invoked as
+
+  dirmngr-client foo.crt
+
+This command will send an Assuan request to an already running Dirmngr
+instance.  foo.crt is expected to be a standard X.509 certificate and
+dirmngr will receive the Assuan command
+
+   CHECKCRL [<fingerprint>]
+
+<fingerprint> is optional and expected to be the SHA-1 has of the DER
+encosing of the certificate under question.  It is to be HEX encoded.
+The rationale for sending the fingerprint is that it allows dirmngr to
+rely immediatly if it has already cached such an request.  Only if this
+is not the case and no certificate has been found in dirmngr's internal
+certificate storage, dirmngr will request the certificate using the
+Assuan inquiry
+
+      INQUIRE TARGETCERT
+
+The caller (in our example dirmngr-client) is then expected to return
+the certificate for the request (which should match FINGERPRINT) as a
+binary blob.
+
+Dirmngr now passes control to @func{crl_cache_cert_isvalid}.  This
+function checks whether a CRL item exists for target certificate.  These
+CRL items are kept in a database of already loaded and verified CRLs.
+This mechanism is called the CRL cache.  Obviously timestamps are kept
+there with each item to cope with the expiration date of the CRL.  The
+possible return values are: Success to indicate that a valid CRL is
+available for the certificate and the certificate itself is not listen
+in this CRL, GPG_ERR_CERT_REVOKED to indicate that the certificate is
+listed in the CRL or GPG_ERR_NO_CRL_KNOWN in cases where no or no
+information is available.  he first two codes are immediatly returned to
+the caller and the processing of this request has been done.
+
+Only the GPG_ERR_NO_CRL_KNOWN needs more attention: Dirmngr now call
+ at func(clr_cache_reload_crl} and if this succeeds calls
+ at func(crl_cache_cert_isvald) one more.  All further errors are
+immediately returned to the caller.
+
+ at func(clr_cache_reload_crl} is the actual heart of the CRL management.
+It locates the corresponding CRL for the target certificate, reads and
+verifies this CRL and stores it in the CRL cache.  It works like this:
+
+* Loop over all crlDPs in the target certificate.
+    * If the crlDP is invalid immediately terminate the loop.
+    * Loop over all names in the current crlDP.
+        * If the URL scheme is unknown or not enabled 
+          (--ignore-http-dp, --ignore-ldap-dp) continues with
+          the next name.
+        * @func{crl_fetch} is called to actually retrieve the CRL.
+          In case of problems this name is ignore and we continue with
+          the next name.  Note that @func{crl_fetch} does only return 
+          a descriptor for the CRL for further reading so does the CRL
+          does not yet end up in memory.
+        * @func{crl_cache_insert} is called with that descriptor to
+          actually read the CRL into the cache. See below for a
+          description of this function.  If there is any error (e.g. read
+          problem, CRL not correctly signed or verification of signature
+          not possible), this descriptor is rejected and we continue
+          with the next name.  If the CRL has been successfully loaded,
+          the loop is terminated.
+* If no crlDP has been found in the previous loop use a default CRL.
+  Note, that if any crlDP has been found but loading of the CRL failed,
+  this condition is not true.
+    * Try to load a CRL from all configured servers (ldapservers.conf)
+      in turn.  The first server returning a CRL is used.
+    * @func(crl_cache_insert) is then used to actually insert the CRL
+      into the cache.  If this failed we give up immediatley without
+      checking the rest of the servers from the first step.
+* Ready. 
+
+
+The @func{crl_cache_insert} function takes care of reading the bulk of
+the CRL, parsing it and checking the signature.  It works like this: A
+new database file is created using a temporary file name.  The CRL
+parsing machinery is started and all items of the CRL are put into
+this database file.  At the end the issuer certificate of the CRL
+needs to be retrieved.  Three cases are to be distinguished:
+
+ a) An authorityKeyIdentifier with an issuer and serialno exits: The
+    certificate is retrieved using @func{find_cert_bysn}.  If
+    the certificate is in the certificate cache, it is directly
+    returned. Then the requester (i.e. the client who requested the
+    CRL check) is asked via the Assuan inquiry ``SENDCERT'' whether
+    he can provide this certificate.  If this succeed the returned
+    certificate gets cached and returned.  Note, that dirmngr does not
+    verify in any way whether the expected certificate is returned.
+    It is in the interest of the client to return a useful certificate
+    as otherwise the service request will fail due to a bad signature.
+    The last way to get the certificate is by looking it up at
+    external resources.  This is done using the @func{ca_cert_fetch}
+    and @func{fetch_next_ksba_cert} and comparing the returned
+    certificate to match the requested issuer and seriano (This is
+    needed because the LDAP layer may return several certificates as
+    LDAP as no standard way to retrieve by serial number).
+
+ b) An authorityKeyIdentifier with a key ID exists: The certificate is
+    retrieved using @func{find_cert_bysubject}.  If the certificate is
+    in the certificate cache, it is directly returned.  Then the
+    requester is asked via the Assuan inquiry ``SENDCERT_SKI'' whether
+    he can provide this certificate.  If this succeed the returned
+    certificate gets cached and returned.  Note, that dirmngr does not
+    verify in any way whether the expected certificate is returned.
+    It is in the interest of the client to return a useful certificate
+    as otherwise the service request will fail due to a bad signature.
+    The last way to get the certificate is by looking it up at
+    external resources.  This is done using the @func{ca_cert_fetch}
+    and @func{fetch_next_ksba_cert} and comparing the returned
+    certificate to match the requested subject and key ID.
+
+ c) No authorityKeyIdentifier exits: The certificate is retrieved
+    using @func{find_cert_bysubject} without the key ID argument.  If
+    the certificate is in the certificate cache the first one with a
+    matching subject is is directly returned.  Then the requester is
+    asked via the Assuan inquiry ``SENDCERT'' and an exact
+    specification of the subject whether he can
+    provide this certificate.  If this succeed the returned
+    certificate gets cached and returned.  Note, that dirmngr does not
+    verify in any way whether the expected certificate is returned.
+    It is in the interest of the client to return a useful certificate
+    as otherwise the service request will fail due to a bad signature.
+    The last way to get the certificate is by looking it up at
+    external resources.  This is done using the @func{ca_cert_fetch}
+    and @func{fetch_next_ksba_cert} and comparing the returned
+    certificate to match the requested subject; the first certificate
+    with a matching subject is then returned.
+
+If no certificate was found, the function returns with the error
+GPG_ERR_MISSING_CERT.  Now the signature is verified.  If this fails,
+the erro is returned.  On success the @func{validate_cert_chain} is
+used to verify that the certificate is actually valid. 
+
+Here we may encounter a recursive situation:
+ at func{validate_cert_chain} needs to look at other certificates and
+also at CRLs to check whether tehse other certificates and well, the
+CRL issuer certificate itself are not revoked.  FIXME: We need to make
+sure that @func{validate_cert_chain} does not try to lookup the CRL we
+are currently processing. This would be a catch-22 and may indicate a
+broken PKI.  However, due to overlapping expiring times and imprecise
+clocks thsi may actually happen.
+    
+For historical reasons the Assuan command ISVALID is a bit different
+to CHECKCRL but this is mainly due to different calling conventions.
+In the end the same fucntionality is used, albeit hidden by a couple
+of indirection and argument and result code mangling.  It furthere
+ingetrages OCSP checking depending on options are the way it is
+called.  GPGSM still uses this command but might eventuall switch over
+to CHECKCRL and CHECKOCSP so that ISVALID can be retired.
+  
+
+ at section Validating a certificate
+
+We describe here how the internal function @func{validate_cert_chain}
+works. Note that mainly testing purposes this functionality may be
+called directly using @cmd{dirmngr-client --validate @file{foo.crt}}.
+
+For backward compatibility this function returns success if Dirmngr is
+not used as a system daemon.  Thus not validating the certicates at
+all. FIXME:  This is definitely not correct and should be fixed ASAP.
+
+The function takes the target certificate and a mode argument as
+parameters and returns an error code and optionally the closes
+expiration time of all certificates in the chain.
+
+We first check that the certificate may be used for the requested
+purpose (i.e. OCSP or CRL signing).  If this is not the case
+GPG_ERR_WRONG_KEY_USAGE is returned.
+
+The next step is to find the trust anchor (root certificate) and to
+assemble the chain in memory: Starting with the target certificate,
+the expiration time is checked against the current date, unknown
+critical extensions are detected and certificate policies are matched
+(We only allow 2.289.9.9 but I have no clue about that OID and from
+where I got it - it does not even seem to be assigned - debug cruft?).
+
+Now if this certificate is a self-signed one, we have reached the
+trust anchor.  In this case we check that the signature is good, the
+certificate is allowed to act as a CA, that it is a trusted one (by
+checking whether it is has been put into the trusted-certs
+configuration directory) and finally prepend into to our list
+representing the certificate chain.  This steps ends then.
+
+If it is not a self-signed certificate, we check that the chain won't
+get too long (current limit is 100), if this is the case we terminate
+with the error GPG_ERR_BAD_CERT_CHAIN.
+
+Now the issuer's certificate is looked up: If an
+authorityKeyIdentifier is available, this one is used to locate the
+certificate either using issuer and serialnumber or subject DN
+(i.e. the issuer's DN) and the keyID.  The functions
+ at func{find_cert_bysn) and @func{find_cert_bysubject} are used
+respectively. The have already been described above under the
+description of @func{crl_cache_insert}.  If no certificate was found
+or with no authorityKeyIdentifier, only the cache is consulted using
+ at func{get_cert_bysubject}.  The latter is is done under the assumption
+that a matching certificate has explicitly been put into the
+certificate cache.  If the issuer's certificate could not be found,
+the validation terminates with the error code GPG_ERR_MISSING_CERT.
+
+If the issuer's certificate has been found, the signature of the
+actual certificate is checked and in case this fails the error
+GPG_ERR_BAD_CERT_CHAIN is returned.  IF the signature checks out, the
+maximum cahin length of the issueing certificate is checked as well as
+the capiblity of the certificate (i.e. whether he may be used for
+certificate signing).  Then the certificate is prepended to our list
+representing the certificate chain.  Finally the loop is continued now
+with the issuer's certificate as the current certificate.
+
+After the end of the loop and if no error as been encountered
+(i.e. the certificate chain has been assempled correctly), a check is
+done whether any certificate expired or a critical policy has not been
+met.  In any of these cases the validation terminates with an
+appropriate error. 
+
+Finally the function @func{check_revocations} is called to verify no
+certificate in the assempled chain has been revoked: This is an
+recursive process because a CRL has to be checked for each certificate
+in the chain except for the root certificate, of which we already know
+that it is trusted and we avoid checking a CRL here due to common
+setup problems and the assumption that a revoked root certifcate has
+been removed from the list of trusted certificates.
+
+
+
+
+ at section Looking up certificates through LDAP.
+
+This describes the LDAP layer to retrieve certificates.
+the functions @func{ca_cert_fetch} and @func{fetch_next_ksba_cert} are
+used for this.  The first one starts a search and the second one is
+used to retrieve certificate after certificate.
+
+
+  

Modified: trunk/src/ChangeLog
===================================================================
--- trunk/src/ChangeLog	2006-06-19 19:39:58 UTC (rev 226)
+++ trunk/src/ChangeLog	2006-06-23 18:56:26 UTC (rev 227)
@@ -1,3 +1,37 @@
+2006-06-23  Werner Koch  <wk at 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 VALIDATED_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.
+
+	* ocsp.c (ocsp_isvalid): Make sure it is reset for a status of
+	revoked.
+
+2006-06-22  Werner Koch  <wk at 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 at g10code.com>
 
 	* certcache.c (compare_serialno): Silly me. Using 0 as true is

Modified: trunk/src/certcache.c
===================================================================
--- trunk/src/certcache.c	2006-06-19 19:39:58 UTC (rev 226)
+++ trunk/src/certcache.c	2006-06-23 18:56:26 UTC (rev 227)
@@ -361,22 +361,27 @@
         }
 
       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);
-          if (are_trusted || opt.verbose)
+          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);
+              log_info (_("  SHA1 fingerprint = %s\n"), p);
               xfree (p);
+
+              cert_log_name (_("  name ="), cert);
             }
         }
       else
         log_error (_("error loading certificate `%s': %s\n"),
                      fname, gpg_strerror (err));
+      ksba_cert_release (cert);
     }
 
   xfree (fname);
@@ -626,48 +631,52 @@
 
 
 
-
 /* 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 = NULL;
+  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;
 
-  if (!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)
     {
-      char *hexsn, *buf;
-      /* 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_debug ("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);
+      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);
-      cert = get_cert_local (ctrl, buf);
-      xfree (buf);
-      if (cert)
-        {
-          cache_cert (cert);
-          return cert; /* Done. */
-        }
+      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;
@@ -716,6 +725,7 @@
       if (!compare_serialno (serialno, sn))
         {
           ksba_free (sn);
+          ksba_free (issdn);
           cache_cert (cert);
           if (DBG_LOOKUP)
             log_debug ("   found\n");
@@ -723,7 +733,7 @@
         }
 
       ksba_free (sn);
-
+      ksba_free (issdn);
       ksba_cert_release (cert);
       cert = NULL;
     }
@@ -732,6 +742,7 @@
   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
@@ -746,6 +757,7 @@
   cert_fetch_context_t context = NULL;
   ksba_sexp_t subj;
 
+  /* First we check whether the certificate is cached.  */
   for (seq=0; (cert = get_cert_bysubject (subject_dn, seq)); seq++)
     {
       if (!keyid)
@@ -759,21 +771,43 @@
       xfree (subj);
       ksba_cert_release (cert);
     }
+  if (cert)
+    return cert; /* Done.  */
 
-  if (!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. */
+  if (keyid)
+    cert = get_cert_local_ski (ctrl, subject_dn, keyid);
+  else
     {
-      /* 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. */
-      cert = get_cert_local_ski (ctrl, subject_dn, keyid);
-      if (cert)
+      /* 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)
         {
-          cache_cert (cert);
-          return cert; /* Done. */
+          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;
@@ -816,6 +850,7 @@
         }
       ksba_free (subjdn);
 
+      /* If no key ID has been provided, we return the first match.  */
       if (!keyid)
         {
           cache_cert (cert);
@@ -824,6 +859,7 @@
           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))

Modified: trunk/src/crlcache.c
===================================================================
--- trunk/src/crlcache.c	2006-06-19 19:39:58 UTC (rev 226)
+++ trunk/src/crlcache.c	2006-06-23 18:56:26 UTC (rev 227)
@@ -945,7 +945,7 @@
 
 /* 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 the using the file. */ 
+   unlock_db_file after using the file. */ 
 static struct cdb *
 lock_db_file (crl_cache_t cache, crl_cache_entry_t entry)
 {
@@ -1365,92 +1365,6 @@
 }  
 
 
-/* Return the certificate for NAME or NULL if it can't be found.  Note
-   that we will only return the first matching certificate. */
-static ksba_cert_t 
-get_crlissuer_cert (ctrl_t ctrl, const char *name)
-{
-  gpg_error_t err;
-  ksba_cert_t cert = NULL;
-  cert_fetch_context_t context;
-
-  /* First check whether it has already been cached.  */
-  cert = get_cert_bysubject (name, 0); 
-  if (cert)
-    return 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. */
-  cert = get_cert_local (ctrl, name);
-  if (cert)
-    return cert; /* Done. */
-
-  /* If this fails do a regular certificate lookup. */
-  if (DBG_LOOKUP)
-    log_debug ("certificate for CRL issuer not returned by caller"
-               " - doing lookup\n");
-
-  err = ca_cert_fetch (ctrl, &context, name);
-  if (err)
-    {
-      log_error (_("error fetching certificate for CRL issuer: %s\n"),
-                 gpg_strerror (err));
-      return NULL;
-    }
-
-  err = fetch_next_ksba_cert (context, &cert);
-  if (err)
-    log_error (_("invalid CRL issuer certificate: %s\n"), gpg_strerror (err) );
-  end_cert_fetch (context);
-
-  if (!err)
-    cache_cert (cert);
-
-  return cert;
-}
-
-
-/* Return the certificate from the issuer DN NAME and the S/N SN.
-   First tries the local chache, then asks the caller and then tries
-   an ldap search. */
-static ksba_cert_t 
-get_crlissuer_cert_bysn (ctrl_t ctrl, const char *name, ksba_sexp_t sn)
-{
-  ksba_cert_t cert;
-  char *snbuf, *buf;
-
-  /* First check whether it has already been cached.  */
-  cert = get_cert_bysn (name, sn);
-  if (cert)
-    return 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. */
-  snbuf = serial_hex (sn);
-  if (!snbuf)
-    snbuf = "";
-  buf = xtrymalloc (1 + strlen (snbuf) + 1 + strlen (name) + 1 );
-  if (!buf)
-    {
-      xfree (snbuf);
-      return NULL;
-    }
-  strcpy (stpcpy (stpcpy (stpcpy (buf, "#"), snbuf), "/"), name);
-  xfree (snbuf);
-  cert = get_cert_local (ctrl, buf);
-  xfree (buf);
-  if (cert)
-    {
-      cache_cert (cert);
-      return cert; /* Done. */
-    }
-
-  return find_cert_bysn (ctrl, name, sn);
-}
-
-
 /* 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. */
@@ -1576,7 +1490,7 @@
    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
-   correspondind data from the CRL.  Note that these values might get
+   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.
@@ -1701,7 +1615,7 @@
                 
                 s = ksba_name_enum (authid, 0);
                 if (s && *authidsn)
-                  crlissuer_cert = get_crlissuer_cert_bysn (ctrl, s, authidsn);
+                  crlissuer_cert = find_cert_bysn (ctrl, s, authidsn);
                 if (!crlissuer_cert && keyid)
                   crlissuer_cert = find_cert_bysubject (ctrl,
                                                         crlissuer, keyid);
@@ -1730,7 +1644,7 @@
                 xfree (keyid);
               }
             else
-              crlissuer_cert = get_crlissuer_cert (ctrl, crlissuer);
+              crlissuer_cert = find_cert_bysubject (ctrl, crlissuer, NULL);
             err = 0;
             if (!crlissuer_cert)
               {

Modified: trunk/src/misc.c
===================================================================
--- trunk/src/misc.c	2006-06-19 19:39:58 UTC (rev 226)
+++ trunk/src/misc.c	2006-06-23 18:56:26 UTC (rev 227)
@@ -558,7 +558,6 @@
     }
 }
 
-
 /* Dump an KSBA cert object to the log stream. Prefix the output with
    TEXT.  This is used for debugging. */
 void 
@@ -601,13 +600,43 @@
       log_debug ("  hash algo: %s\n", ksba_cert_get_digest_algo (cert));
 
       p = get_fingerprint_hexstring (cert);
-      log_debug ("  SHA1 Fingerprint: %s\n", p);
+      log_debug ("  SHA1 fingerprint: %s\n", p);
       xfree (p);
     }
   log_debug ("END Certificate\n");
 }
 
 
+
+/* Log the certificate's name in "#SN/ISSUERDN" format along with
+   TEXT. */
+void 
+cert_log_name (const char *text, ksba_cert_t cert)
+{
+  log_info ("%s", text? text:"certificate" );
+  if (cert)
+    {
+      ksba_sexp_t sn;
+      char *p;
+
+      p = ksba_cert_get_issuer (cert, 0);
+      sn = ksba_cert_get_serial (cert);
+      if (p && sn)
+        {
+          log_printf (" #");
+          dump_serial (sn);
+          log_printf ("/");
+          dump_string (p);
+        }
+      else
+        log_printf (" [invalid]");
+      ksba_free (sn);
+      xfree (p);
+    }
+  log_printf ("\n");
+}
+
+
 /****************
  * Remove all %xx escapes; this is done inplace.
  * Returns: New length of the string.

Modified: trunk/src/misc.h
===================================================================
--- trunk/src/misc.h	2006-06-19 19:39:58 UTC (rev 226)
+++ trunk/src/misc.h	2006-06-23 18:56:26 UTC (rev 227)
@@ -88,6 +88,8 @@
    CERT.  This version inserts the usual colons. */
 char *get_fingerprint_hexstring_colon (ksba_cert_t cert);
 
+/* Log CERT in short format with s/n and issuer DN prefixed by TEXT.  */
+void cert_log_name (const char *text, ksba_cert_t cert);
 
 /* Dump the serial number SERIALNO to the log stream.  */
 void dump_serial (ksba_sexp_t serialno);

Modified: trunk/src/ocsp.c
===================================================================
--- trunk/src/ocsp.c	2006-06-19 19:39:58 UTC (rev 226)
+++ trunk/src/ocsp.c	2006-06-23 18:56:26 UTC (rev 227)
@@ -390,7 +390,7 @@
 
 
 /* Check whether the certificate either given by fingerprint CERT_FPR
-   or directly through a the CERT object is valid by running an OCSP
+   or directly through the CERT object is valid by running an OCSP
    transaction.  */
 gpg_error_t
 ocsp_isvalid (ctrl_t ctrl, ksba_cert_t cert, const char *cert_fpr)
@@ -548,6 +548,24 @@
                  gpg_strerror (err));
       goto leave;
     }
+
+  /* In case the certificate has been revoked, we better invalidate
+     our cached validation status. */
+  if (status == KSBA_STATUS_REVOKED)
+    {
+      time_t validated_at = 0; /* That is: No cached validation available. */
+      err = ksba_cert_set_user_data (cert, "validated_at",
+                                     &validated_at, sizeof (validated_at));
+      if (err)
+        {
+          log_error ("set_user_data(validated_at) failed: %s\n",
+                     gpg_strerror (err)); 
+          err = 0; /* The certificate is anyway revoked, and that is a
+                      more important message than the failure of our
+                      cache. */
+        }
+    }
+
   
   if (opt.verbose)
     {

Modified: trunk/src/server.c
===================================================================
--- trunk/src/server.c	2006-06-19 19:39:58 UTC (rev 226)
+++ trunk/src/server.c	2006-06-23 18:56:26 UTC (rev 227)
@@ -838,7 +838,7 @@
 /* CACHECERT 
 
    Put a certificate into the internal cache.  This command might be
-   usedful if a client knows in advance certificates required for a
+   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
@@ -891,7 +891,7 @@
 
 /* VALIDATE 
 
-   Validate a certificate using the certificate validationj fucntion
+   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
@@ -910,7 +910,7 @@
   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)
@@ -932,8 +932,24 @@
   if(err)
     goto leave;
 
-  err = validate_cert_chain (ctrl, cert, NULL, VALIDATE_MODE_CRL_RECURSIVE);
+  /* If we have this certificate already in our cache, use the cached
+     version for validation because this will take care of any cached
+     results. */
+  { 
+    unsigned char fpr[20];
+    ksba_cert_t tmpcert;
 
+    cert_compute_fpr (cert, fpr);
+    tmpcert = get_cert_byfpr (fpr);
+    if (tmpcert)
+      {
+        ksba_cert_release (cert);
+        cert = tmpcert;
+      }
+  }
+
+  err = validate_cert_chain (ctrl, cert, NULL, VALIDATE_MODE_CERT);
+
  leave:
   if (err)
     log_error (_("command %s failed: %s\n"), "VALIDATE", gpg_strerror (err));

Modified: trunk/src/validate.c
===================================================================
--- trunk/src/validate.c	2006-06-19 19:39:58 UTC (rev 226)
+++ trunk/src/validate.c	2006-06-23 18:56:26 UTC (rev 227)
@@ -1,6 +1,6 @@
 /* validate.c - Validate a certificate chain.
  *	Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
- *      Copyright (C) 2004 g10 Code GmbH
+ *      Copyright (C) 2004, 2006 g10 Code GmbH
  *
  * This file is part of DirMngr.
  *
@@ -35,8 +35,8 @@
 #include "validate.h"
 #include "misc.h"
 
-/* While running the validation function we need to keep tarckl of the
-   cretificates and the validation outcome of each.  We use this type
+/* While running the validation function we need to keep track of the
+   certificates and the validation outcome of each.  We use this type
    for it.  */
 struct chain_item_s
 {
@@ -192,16 +192,7 @@
   chain_item_t ci;
 
   assert (ctrl->check_revocations_nest_level >= 0);
-
   assert (chain);
-  if (!chain->next)
-    {
-      /* We only have one certificate in the chain.  This is the
-         traget and the Root CA certificate.  We can't do a
-         revocation check for the root certificate because this will
-         in most cases lead to a catch-22.  */
-      return 0; /* Target certificate is a trusted root certificate.  */
-    }
 
   if (ctrl->check_revocations_nest_level > 10)
     {
@@ -214,13 +205,27 @@
   for (ci=chain; ci; ci = ci->next)
     {
       assert (ci->cert);
+      if (ci == chain)
+        {
+          /* It does not make sense to check the root certificate for
+             revocations.  In almost all cases this will lead to a
+             catch-22 as the root certificate is the final trust
+             anchor for the certificates and the CRLs.  We expect the
+             user to remove root certificates from the list of trusted
+             certificates in case they have been revoked. */
+          if (opt.verbose)
+            cert_log_name (_("not checking CRL for"), ci->cert);
+          continue; 
+        }
       
-      err = crl_cache_cert_isvalid (ctrl, chain->cert, 0);
+      if (opt.verbose)
+        cert_log_name (_("checking CRL for"), ci->cert);
+      err = crl_cache_cert_isvalid (ctrl, ci->cert, 0);
       if (gpg_err_code (err) == GPG_ERR_NO_CRL_KNOWN)
         {
-          err = crl_cache_reload_crl (ctrl, chain->cert);
+          err = crl_cache_reload_crl (ctrl, ci->cert);
           if (!err)
-            err = crl_cache_cert_isvalid (ctrl, chain->cert, 0);
+            err = crl_cache_cert_isvalid (ctrl, ci->cert, 0);
         }
       switch (gpg_err_code (err))
         {
@@ -273,31 +278,68 @@
   *exptime = 0;
 
   if (!opt.system_daemon)
-    return 0; /* For backward compatibility we only do this in daemon
-                 mode.  */
+    {
+      /* For backward compatibility we only do this in daemon mode.  */
+      log_info (_("running in compatibility mode - "
+                  "certificate chain not checked!\n"));
+      return 0; /* Okay. */
+    }
 
-  /* Get the current time. */
-  get_isotime (current_time);
-
   if (DBG_X509)
     dump_cert ("subject", cert);
   
-  subject_cert = cert;
-  maxdepth = 50;
-  chain = NULL;
-  depth = 0;
-
-  /* May the target certificate be used for OCSP response signing?  */
-  if (mode == VALIDATE_MODE_OCSP)
+  /* May the target certificate be used for this purpose?  */
+  switch (mode)
     {
+    case VALIDATE_MODE_OCSP:
       err = cert_use_ocsp_p (cert);
+      break;
+    case VALIDATE_MODE_CRL:
+    case VALIDATE_MODE_CRL_RECURSIVE:
+      err = cert_use_crl_p (cert);
+      break;
+    default:
+      err = 0;
+      break;
     }
-  /* May the certificate be used for CR signing.  */
-  /* FIXME. */
   if (err)
-    goto leave;
+    return err;
 
-  /* We walk up the the path until we find a trust anchor. */
+  /* If we already validated the certificate not too long ago, we can
+     avoid the excessive computations and lookups unless the caller
+     asked for the expiration time.  */
+  if (!r_exptime)
+    {
+      size_t buflen;
+      time_t validated_at;
+      
+      err = ksba_cert_get_user_data (cert, "validated_at", 
+                                     &validated_at, sizeof (validated_at),
+                                     &buflen);
+      if (err || buflen != sizeof (validated_at) || !validated_at)
+        err = 0; /* Not available or other error. */
+      else
+        {
+          /* If the validation is not older than 30 minutes we are ready. */
+          if (validated_at < get_time () + (30*60))
+            {
+              if (opt.verbose)
+                log_info ("certificate is good (cached)\n");
+              /* Note, that we can't jump to leave here as this would
+                 falsely updated the validation timestamp.  */
+              return 0;
+            }
+        }
+    }
+
+  /* Get the current time. */
+  get_isotime (current_time);
+
+  /* We walk up the chain until we find a trust anchor. */
+  subject_cert = cert;
+  maxdepth = 10;
+  chain = NULL;
+  depth = 0;
   for (;;)
     {
       /* Get the subject and issuer name from the current
@@ -430,6 +472,10 @@
             ci->next = chain;
             chain = ci;
           }
+
+          if (opt.verbose)
+            log_info ("root certificate is good\n");
+
           break;  /* Okay: a self-signed certicate is an end-point. */
         }
 
@@ -555,7 +601,7 @@
       }
 
       if (opt.verbose)
-        log_info ("certificate is good\n");
+        log_info (_("certificate is good\n"));
 
       /* Now to the next level up.  */
       subject_cert = issuer_cert;
@@ -571,6 +617,15 @@
         err = gpg_error (GPG_ERR_NO_POLICY_MATCH);
     }
 
+  if (!err && opt.verbose)
+    {
+      chain_item_t citem;
+
+      log_info (_("certificate chain is good\n"));
+      for (citem = chain; citem; citem = citem->next)
+        cert_log_name ("  certificate", citem->cert);
+    }
+  
   if (!err)
     { /* Now that everything is fine, walk the chain and check each
          certificate for revocations. 
@@ -585,12 +640,37 @@
          might become a recursive process and we should better cache
          our validity results to avoid double work.  Far worse a
          catch-22 may happen for an improper setup hierachy and we
-         need a way to break up this deadlock. */
+         need a way to break up such a deadlock. */
       err = check_revocations (ctrl, chain);
     }
 
+  if (!err && opt.verbose)
+    log_info ("target certificate is valid\n");
+  else if (err && opt.verbose)
+    log_info ("target certificate is NOT valid\n");
+
   
  leave:
+  if (!err)
+    {
+      /* With no error we can update the validation cache.  We do this
+         for all certificates in the chain. */
+      chain_item_t citem;
+      time_t validated_at = get_time ();
+
+      for (citem = chain; citem; citem = citem->next)
+        {
+          err = ksba_cert_set_user_data (citem->cert, "validated_at",
+                                         &validated_at, sizeof (validated_at));
+          if (err)
+            {
+              log_error ("set_user_data(validated_at) failed: %s\n",
+                         gpg_strerror (err)); 
+              err = 0;
+            }
+        }
+    }
+
   if (r_exptime)
     copy_time (r_exptime, exptime);
   xfree (issuer);
@@ -789,7 +869,7 @@
    for signing, a MODE of 1 checks for encryption, a MODE of 2 checks
    for verification and a MODE of 3 for decryption (just for
    debugging).  MODE 4 is for certificate signing, MODE 5 for OCSP
-   response signing. */
+   response signing, MODE 6 is for CRL signing. */
 static int
 cert_usage_p (ksba_cert_t cert, int mode)
 {
@@ -899,6 +979,15 @@
       return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
     }
 
+  if (mode == 6)
+    {
+      if ((use & (KSBA_KEYUSAGE_CRL_SIGN)))
+        return 0;
+      log_info (_("certificate should have not "
+                  "been used for CRL signing\n"));
+      return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+    }
+
   if ((use & ((mode&1)?
               (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT):
               (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION)))
@@ -928,3 +1017,10 @@
   return cert_usage_p (cert, 5);
 }
 
+/* Return 0 if the certificate CERT is usable for signing CRLs. */
+gpg_error_t
+cert_use_crl_p (ksba_cert_t cert)
+{
+  return cert_usage_p (cert, 6);
+}
+

Modified: trunk/src/validate.h
===================================================================
--- trunk/src/validate.h	2006-06-19 19:39:58 UTC (rev 226)
+++ trunk/src/validate.h	2006-06-23 18:56:26 UTC (rev 227)
@@ -23,6 +23,7 @@
 
 
 enum {
+  VALIDATE_MODE_CERT = 0,
   VALIDATE_MODE_CRL = 1,
   VALIDATE_MODE_CRL_RECURSIVE = 2,
   VALIDATE_MODE_OCSP = 3
@@ -42,5 +43,8 @@
    responses.  */
 gpg_error_t cert_use_ocsp_p (ksba_cert_t cert);
 
+/* Return 0 if the certificate CERT is usable for signing CRLs. */
+gpg_error_t cert_use_crl_p (ksba_cert_t cert);
 
+
 #endif /*VALIDATE_H*/




More information about the Gnupg-commits mailing list