Finished implementation of the "relax" flag.
authorWerner Koch <wk@gnupg.org>
Tue, 26 Sep 2006 10:00:12 +0000 (10:00 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 26 Sep 2006 10:00:12 +0000 (10:00 +0000)
NEWS
sm/ChangeLog
sm/certchain.c

diff --git a/NEWS b/NEWS
index 5f7d611..7d0e8f1 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,9 @@
 Noteworthy changes in version 1.9.91
 -------------------------------------------------
 
+ * New "relax" flag for trustlist.txt to allow root CA certificates
+   without BasicContraints.
+
 
 Noteworthy changes in version 1.9.90 (2006-09-25)
 -------------------------------------------------
index be3b601..9babf13 100644 (file)
@@ -1,3 +1,9 @@
+2006-09-26  Werner Koch  <wk@g10code.com>
+
+       * certchain.c (gpgsm_validate_chain): More changes for the relax
+       feature.  Use certificate reference counting instead of the old
+       explicit tests. Added a missing free. 
+
 2006-09-25  Werner Koch  <wk@g10code.com>
 
        * gpgsm.h (struct rootca_flags_s): New.
index 8a3a84c..ea4a379 100644 (file)
@@ -695,10 +695,15 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
     gpgsm_dump_cert ("target", cert);
 
   subject_cert = cert;
+  ksba_cert_ref (subject_cert);
   maxdepth = 50;
 
   for (;;)
     {
+      int is_root;
+      gpg_error_t istrusted_rc;
+      struct rootca_flags_s rootca_flags;
+
       xfree (issuer);
       xfree (subject);
       issuer = ksba_cert_get_issuer (subject_cert, 0);
@@ -711,6 +716,20 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
           goto leave;
         }
 
+      /* Is this a self-issued certificate (i.e. the root certificate)? */
+      is_root = (subject && !strcmp (issuer, subject));
+      if (is_root)
+        {
+          /* Check early whether the certificate is listed as trusted.
+             We used to do this only later but changed it to call the
+             check right here so that we can access special flags
+             associated with that specific root certificate.  */
+          istrusted_rc = gpgsm_agent_istrusted (ctrl, subject_cert,
+                                                &rootca_flags);
+        }
+      
+
+      /* Check the validity period. */
       {
         ksba_isotime_t not_before, not_after;
 
@@ -762,10 +781,12 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
           }            
       }
 
+      /* Assert that we understand all critical extensions. */
       rc = unknown_criticals (subject_cert, listmode, fp);
       if (rc)
         goto leave;
 
+      /* Do a policy check. */
       if (!opt.no_policy_check)
         {
           rc = check_cert_policy (subject_cert, listmode, fp);
@@ -780,23 +801,14 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
 
 
       /* Is this a self-issued certificate? */
-      if (subject && !strcmp (issuer, subject))
-        {  /* Yes. */
-          gpg_error_t istrusted_rc;
-          struct rootca_flags_s rootca_flags;
-
-          /* Check early whether the certificate is listed as trusted.
-             We used to do this only later but changed it to call the
-             check right here so that we can access special flags
-             associated with that specific root certificate.  */
-          istrusted_rc = gpgsm_agent_istrusted (ctrl, subject_cert,
-                                                &rootca_flags);
-
-          /* Note, that we could save the following signature check
-             because nobody would be so dump to set up a faked chain
-             and fail in creating a valid self-signed certificate. */
-          if (gpgsm_check_cert_sig (subject_cert, subject_cert) )
+      if (is_root)
+        { 
+          if (!istrusted_rc)
+            ; /* No need to check the certificate for a trusted one. */
+          else if (gpgsm_check_cert_sig (subject_cert, subject_cert) )
             {
+              /* We only check the signature if the certificate is not
+                 trusted for better diagnostics. */
               do_list (1, lm, fp,
                        _("self-signed certificate has a BAD signature"));
               if (DBG_X509)
@@ -920,6 +932,7 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
           break;  /* Okay: a self-signed certicate is an end-point. */
         }
       
+      /* Take care that the chain does not get too long. */
       depth++;
       if (depth > maxdepth)
         {
@@ -928,7 +941,7 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
           goto leave;
         }
 
-      /* find the next cert up the tree */
+      /* Find the next cert up the tree. */
       keydb_search_reset (kh);
       rc = find_up (kh, subject_cert, issuer, 0);
       if (rc)
@@ -1013,10 +1026,38 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
           goto leave;
         }
 
+      is_root = 0;
+      istrusted_rc = -1;
+
+      /* Check that a CA is allowed to issue certificates. */
       {
         int chainlen;
+
         rc = allowed_ca (issuer_cert, &chainlen, listmode, fp);
         if (rc)
+          {
+            /* Not allowed.  Check whether this is a trusted root
+               certificate and whether we allow special exceptions.
+               We could carry the result of the test over to the
+               regular root check at the top of the loop but for
+               clarity we won't do that.  Given that the majority of
+               certificates carry proper BasicContraints our way of
+               overriding an error in the way is justified for
+               performance reasons. */
+            if (gpgsm_is_root_cert (issuer_cert))
+              {
+                is_root = 1;
+                istrusted_rc = gpgsm_agent_istrusted (ctrl, issuer_cert,
+                                                      &rootca_flags);
+                if (!istrusted_rc && rootca_flags.relax)
+                  {
+                    /* Ignore the error due to the relax flag.  */
+                    rc = 0;
+                    chainlen = -1;
+                  }
+              }
+          }
+        if (rc)
           goto leave;
         if (chainlen >= 0 && (depth - 1) > chainlen)
           {
@@ -1028,6 +1069,7 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
           }
       }
 
+      /* Is the certificate allowed to sign other certificates. */
       if (!listmode)
         {
           rc = gpgsm_cert_use_cert_p (issuer_cert);
@@ -1041,9 +1083,14 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
             }
         }
 
-      /* Check for revocations etc. */
+      /* Check for revocations etc.  Note that for a root certioficate
+         this test is done a second time later. This should eventually
+         be fixed. */
       if ((flags & 1))
         rc = 0;
+      else if (is_root && (opt.no_trusted_cert_crl_check
+                           || (!istrusted_rc && rootca_flags.relax)))
+        ; 
       else
         rc = is_cert_still_valid (ctrl, lm, fp,
                                   subject_cert, issuer_cert,
@@ -1054,8 +1101,10 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
 
       if (opt.verbose && !listmode)
         log_info ("certificate is good\n");
-      
+
+      /* For the next round the current issuer becomes the new subject.  */
       keydb_search_reset (kh);
+      ksba_cert_release (subject_cert);
       subject_cert = issuer_cert;
       issuer_cert = NULL;
     } /* End chain traversal. */
@@ -1110,10 +1159,10 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
   if (r_exptime)
     gnupg_copy_time (r_exptime, exptime);
   xfree (issuer);
+  xfree (subject);
   keydb_release (kh); 
   ksba_cert_release (issuer_cert);
-  if (subject_cert != cert)
-    ksba_cert_release (subject_cert);
+  ksba_cert_release (subject_cert);
   return rc;
 }