* gpgsm.c: New option --auto-issuer-key-retrieve.
[gnupg.git] / sm / certchain.c
index 28c0be5..0457a0a 100644 (file)
@@ -127,7 +127,7 @@ check_cert_policy (KsbaCert cert)
       log_error ("failed to open `%s': %s\n",
                  opt.policy_file, strerror (errno));
       xfree (policies);
-      return GNUPG_Configuration_Error;
+      return GNUPG_No_Policy_Match;
     }
 
   for (;;) 
@@ -196,6 +196,15 @@ check_cert_policy (KsbaCert cert)
 }
 
 
+static void
+find_up_store_certs_cb (void *cb_value, KsbaCert cert)
+{
+  if (keydb_store_cert (cert, 1))
+    log_error ("error storing issuer certificate as ephemeral\n");
+  ++*(int*)cb_value;
+}
+
+
 static int
 find_up (KEYDB_HANDLE kh, KsbaCert cert, const char *issuer)
 {
@@ -211,13 +220,82 @@ find_up (KEYDB_HANDLE kh, KsbaCert cert, const char *issuer)
           rc = keydb_search_issuer_sn (kh, s, authidno);
           if (rc)
               keydb_search_reset (kh);
+          if (rc == -1)
+            { /* And try the ephemeral DB. */
+              int old = keydb_set_ephemeral (kh, 1);
+              if (!old)
+                {
+                  rc = keydb_search_issuer_sn (kh, s, authidno);
+                  if (rc)
+                    keydb_search_reset (kh);
+                }
+              keydb_set_ephemeral (kh, old);
+            }
         }
       ksba_name_release (authid);
       xfree (authidno);
+      /* Fixme: don't know how to do dirmngr lookup with serial+issuer. */
     }
   
-  if (rc)
-    rc = keydb_search_subject (kh, issuer);
+  if (rc) /* not found via authorithyKeyIdentifier, try regular issuer name */
+      rc = keydb_search_subject (kh, issuer);
+  if (rc == -1)
+    {
+      /* Not found, lets see whether we have one in the ephemeral key DB. */
+      int old = keydb_set_ephemeral (kh, 1);
+      if (!old)
+        {
+          keydb_search_reset (kh);
+          rc = keydb_search_subject (kh, issuer);
+        }
+      keydb_set_ephemeral (kh, old);
+    }
+
+  if (rc == -1 && opt.auto_issuer_key_retrieve)
+    {
+      STRLIST names = NULL;
+      int count = 0;
+      char *pattern;
+      const char *s;
+      
+      if (opt.verbose)
+        log_info (_("looking up issuer at external location\n"));
+      /* dirmngr is confused about unknown attributes so has a quick
+         and ugly hack we locate the CN and use this and the
+         following.  Fixme: we should have far ebtter parsing in the
+         dirmngr. */
+      s = strstr (issuer, "CN=");
+      if (!s || s == issuer || s[-1] != ',')
+        s = issuer;
+
+      pattern = xtrymalloc (strlen (s)+2);
+      if (!pattern)
+        return GNUPG_Out_Of_Core;
+      strcpy (stpcpy (pattern, "/"), s);
+      add_to_strlist (&names, pattern);
+      xfree (pattern);
+      rc = gpgsm_dirmngr_lookup (NULL, names, find_up_store_certs_cb, &count);
+      free_strlist (names);
+      if (opt.verbose)
+        log_info (_("number of issuers matching: %d\n"), count);
+      if (rc) 
+        {
+          log_error ("external key lookup failed: %s\n", gnupg_strerror (rc));
+          rc = -1;
+        }
+      else if (!count)
+        rc = -1;
+      else
+        {
+          int old;
+          /* The issuers are currently stored in the ephemeral key
+             DB, so we temporary switch to ephemeral mode. */
+          old = keydb_set_ephemeral (kh, 1);
+          keydb_search_reset (kh);
+          rc = keydb_search_subject (kh, issuer);
+          keydb_set_ephemeral (kh, old);
+        }
+    }
   return rc;
 }
 
@@ -308,7 +386,7 @@ gpgsm_is_root_cert (KsbaCert cert)
 /* Validate a path and optionally return the nearest expiration time
    in R_EXPTIME */
 int
-gpgsm_validate_path (KsbaCert cert, time_t *r_exptime)
+gpgsm_validate_path (CTRL ctrl, KsbaCert cert, time_t *r_exptime)
 {
   int rc = 0, depth = 0, maxdepth;
   char *issuer = NULL;
@@ -317,11 +395,16 @@ gpgsm_validate_path (KsbaCert cert, time_t *r_exptime)
   KsbaCert subject_cert = NULL, issuer_cert = NULL;
   time_t current_time = gnupg_get_time ();
   time_t exptime = 0;
+  int any_expired = 0;
+  int any_revoked = 0;
+  int any_no_crl = 0;
+  int any_crl_too_old = 0;
+  int any_no_policy_match = 0;
 
   if (r_exptime)
     *r_exptime = 0;
 
-  if ((opt.debug & 4096))
+  if (opt.no_path_validation)
     {
       log_info ("WARNING: bypassing path validation\n");
       return 0;
@@ -376,7 +459,7 @@ gpgsm_validate_path (KsbaCert cert, time_t *r_exptime)
 
         if (not_before && current_time < not_before)
           {
-            log_error ("certificate to young; valid from ");
+            log_error ("certificate too young; valid from ");
             gpgsm_dump_time (not_before);
             log_printf ("\n");
             rc = GNUPG_Certificate_Too_Young;
@@ -387,8 +470,7 @@ gpgsm_validate_path (KsbaCert cert, time_t *r_exptime)
             log_error ("certificate has expired at ");
             gpgsm_dump_time (not_after);
             log_printf ("\n");
-            rc = GNUPG_Certificate_Expired;
-            goto leave;
+            any_expired = 1;
           }            
       }
 
@@ -399,7 +481,12 @@ gpgsm_validate_path (KsbaCert cert, time_t *r_exptime)
       if (!opt.no_policy_check)
         {
           rc = check_cert_policy (subject_cert);
-          if (rc)
+          if (rc == GNUPG_No_Policy_Match)
+            {
+              any_no_policy_match = 1;
+              rc = 1;
+            }
+          else if (rc)
             goto leave;
         }
 
@@ -412,21 +499,24 @@ gpgsm_validate_path (KsbaCert cert, time_t *r_exptime)
                 {
                 case GNUPG_Certificate_Revoked:
                   log_error (_("the certificate has been revoked\n"));
+                  any_revoked = 1;
                   break;
                 case GNUPG_No_CRL_Known:
                   log_error (_("no CRL found for certificate\n"));
+                  any_no_crl = 1;
                   break;
                 case GNUPG_CRL_Too_Old:
                   log_error (_("the available CRL is too old\n"));
                   log_info (_("please make sure that the "
                               "\"dirmngr\" is properly installed\n"));
+                  any_crl_too_old = 1;
                   break;
                 default:
                   log_error (_("checking the CRL failed: %s\n"),
                              gnupg_strerror (rc));
-                  break;
+                  goto leave;
                 }
-              goto leave;
+              rc = 0;
             }
         }
 
@@ -539,6 +629,14 @@ gpgsm_validate_path (KsbaCert cert, time_t *r_exptime)
           }
       }
 
+      rc = gpgsm_cert_use_cert_p (issuer_cert);
+      if (rc)
+        {
+          gpgsm_status2 (ctrl, STATUS_ERROR, "certpath.issuer.keyusage",
+                         gnupg_error_token (rc), NULL);
+          rc = 0;
+        }
+
       if (opt.verbose)
         log_info ("certificate is good\n");
       
@@ -551,6 +649,21 @@ gpgsm_validate_path (KsbaCert cert, time_t *r_exptime)
     log_info ("policies not checked due to --disable-policy-checks option\n");
   if (opt.no_crl_check)
     log_info ("CRLs not checked due to --disable-crl-checks option\n");
+
+  if (!rc)
+    { /* If we encountered an error somewhere during the checks, set
+         the error code to the most critical one */
+      if (any_revoked)
+        rc = GNUPG_Certificate_Revoked;
+      else if (any_no_crl)
+        rc = GNUPG_No_CRL_Known;
+      else if (any_crl_too_old)
+        rc = GNUPG_CRL_Too_Old;
+      else if (any_no_policy_match)
+        rc = GNUPG_No_Policy_Match;
+      else if (any_expired)
+        rc = GNUPG_Certificate_Expired;
+    }
   
  leave:
   if (r_exptime)
@@ -577,7 +690,7 @@ gpgsm_basic_cert_check (KsbaCert cert)
   KEYDB_HANDLE kh = keydb_new (0);
   KsbaCert issuer_cert = NULL;
 
-  if ((opt.debug & 4096))
+  if (opt.no_path_validation)
     {
       log_info ("WARNING: bypassing basic certificate checks\n");
       return 0;