dirmngr: Add options --tls and --systrust to the VALIDATE cmd.
authorWerner Koch <wk@gnupg.org>
Fri, 17 Feb 2017 15:39:48 +0000 (16:39 +0100)
committerWerner Koch <wk@gnupg.org>
Fri, 17 Feb 2017 15:41:02 +0000 (16:41 +0100)
* dirmngr/certcache.h (certlist_s, certlist_t): New.
* dirmngr/certcache.c (read_certlist_from_stream): New.
(release_certlist): New.
* dirmngr/server.c (MAX_CERTLIST_LENGTH): New.
(cmd_validate): Add options --tls and --systrust.  Implement them
using a kludge for now.
* dirmngr/validate.c (validate_cert_chain): Support systrust
checking.  Add kludge to disable the CRL checking for tls mode.
--

This can now be used to test a list of certificates as returned by
TLS.  Put the certs PEM encoded into a a file certlist.pem with the
target certificate being the first.  Then run

  gpg-connect-agent --dirmngr \
    '/definqfile CERTLIST wiki-gnupg-chain.pem' \
    'validate --systrust --tls' /bye

CRLS check has been disabled becuase we can't yet pass the systrust
flag to the CRL checking code.

Signed-off-by: Werner Koch <wk@gnupg.org>
dirmngr/certcache.c
dirmngr/certcache.h
dirmngr/dirmngr.h
dirmngr/server.c
dirmngr/validate.c

index cd026c2..ff86f61 100644 (file)
@@ -225,6 +225,7 @@ cert_compute_fpr (ksba_cert_t cert, unsigned char *digest)
 }
 
 
 }
 
 
+\f
 /* Cleanup one slot.  This releases all resourses but keeps the actual
    slot in the cache marked for reuse. */
 static void
 /* Cleanup one slot.  This releases all resourses but keeps the actual
    slot in the cache marked for reuse. */
 static void
@@ -1669,3 +1670,92 @@ find_issuing_cert (ctrl_t ctrl, ksba_cert_t cert, ksba_cert_t *r_cert)
 
   return err;
 }
 
   return err;
 }
+
+
+\f
+/* Read a list of certificates in PEM format from stream FP and store
+ * them on success at R_CERTLIST.  On error NULL is stored at R_CERT
+ * list and an error code returned.  Note that even on success an
+ * empty list of certificates can be returned (i.e. NULL stored at
+ * R_CERTLIST) iff the input stream has no certificates.  */
+gpg_error_t
+read_certlist_from_stream (certlist_t *r_certlist, estream_t fp)
+{
+  gpg_error_t err;
+  gnupg_ksba_io_t ioctx = NULL;
+  ksba_reader_t reader;
+  ksba_cert_t cert = NULL;
+  certlist_t certlist = NULL;
+  certlist_t cl, *cltail;
+
+  *r_certlist = NULL;
+
+  err = gnupg_ksba_create_reader (&ioctx,
+                                  (GNUPG_KSBA_IO_PEM | GNUPG_KSBA_IO_MULTIPEM),
+                                  fp, &reader);
+  if (err)
+    goto leave;
+
+  /* Loop to read all certificates from the stream.  */
+  cltail = &certlist;
+  do
+    {
+      ksba_cert_release (cert);
+      cert = NULL;
+      err = ksba_cert_new (&cert);
+      if (!err)
+        err = ksba_cert_read_der (cert, reader);
+      if (err)
+        {
+          if (gpg_err_code (err) == GPG_ERR_EOF)
+            err = 0;
+          goto leave;
+        }
+
+      /* Append the certificate to the list.  We also store the
+       * fingerprint and check whether we have a cached certificate;
+       * in that case the cached certificate is put into the list to
+       * take advantage of a validation result which might be stored
+       * in the cached certificate.  */
+      cl = xtrycalloc (1, sizeof *cl);
+      if (!cl)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+      cert_compute_fpr (cert, cl->fpr);
+      cl->cert = get_cert_byfpr (cl->fpr);
+      if (!cl->cert)
+        {
+          cl->cert = cert;
+          cert = NULL;
+        }
+      *cltail = cl;
+      cltail = &cl->next;
+      ksba_reader_clear (reader, NULL, NULL);
+    }
+  while (!gnupg_ksba_reader_eof_seen (ioctx));
+
+ leave:
+  ksba_cert_release (cert);
+  gnupg_ksba_destroy_reader (ioctx);
+  if (err)
+    release_certlist (certlist);
+  else
+    *r_certlist = certlist;
+
+  return err;
+}
+
+
+/* Release the certificate list CL.  */
+void
+release_certlist (certlist_t cl)
+{
+  while (cl)
+    {
+      certlist_t next = cl->next;
+      ksba_cert_release (cl->cert);
+      cl = next;
+    }
+}
index ac93ee6..1f86706 100644 (file)
@@ -46,7 +46,6 @@ gpg_error_t cache_cert_silent (ksba_cert_t cert, void *fpr_buffer);
  * provided certificates are considered trusted.  */
 gpg_error_t is_trusted_cert (ksba_cert_t cert, int with_systrust);
 
  * provided certificates are considered trusted.  */
 gpg_error_t is_trusted_cert (ksba_cert_t cert, int with_systrust);
 
-
 /* Return a certificate object for the given fingerprint.  FPR is
    expected to be a 20 byte binary SHA-1 fingerprint.  If no matching
    certificate is available in the cache NULL is returned.  The caller
 /* Return a certificate object for the given fingerprint.  FPR is
    expected to be a 20 byte binary SHA-1 fingerprint.  If no matching
    certificate is available in the cache NULL is returned.  The caller
@@ -100,5 +99,18 @@ gpg_error_t find_issuing_cert (ctrl_t ctrl,
 
 
 
 
 
 
+/* A simple list of certificates.  */
+struct certlist_s
+{
+  struct certlist_s *next;
+  ksba_cert_t cert;
+  unsigned char fpr[20];  /* of the certificate.  */
+};
+typedef struct certlist_s *certlist_t;
+
+gpg_error_t read_certlist_from_stream (certlist_t *r_certlist, estream_t fp);
+void release_certlist (certlist_t cl);
+
+
 
 #endif /*CERTCACHE_H*/
 
 #endif /*CERTCACHE_H*/
index 3724c00..19d2303 100644 (file)
@@ -155,7 +155,8 @@ struct
 #define DBG_NETWORK (opt.debug & DBG_NETWORK_VALUE)
 #define DBG_LOOKUP  (opt.debug & DBG_LOOKUP_VALUE)
 
 #define DBG_NETWORK (opt.debug & DBG_NETWORK_VALUE)
 #define DBG_LOOKUP  (opt.debug & DBG_LOOKUP_VALUE)
 
-/* A simple list of certificate references. */
+/* A simple list of certificate references.  FIXME: Better use
+   certlist_t also for references (Store NULL at .cert) */
 struct cert_ref_s
 {
   struct cert_ref_s *next;
 struct cert_ref_s
 {
   struct cert_ref_s *next;
@@ -163,6 +164,7 @@ struct cert_ref_s
 };
 typedef struct cert_ref_s *cert_ref_t;
 
 };
 typedef struct cert_ref_s *cert_ref_t;
 
+
 /* Forward references; access only through server.c.  */
 struct server_local_s;
 
 /* Forward references; access only through server.c.  */
 struct server_local_s;
 
index bc373f5..05ef439 100644 (file)
    Dirmngr was a system service and not a user service. */
 #define MAX_CERT_LENGTH (16*1024)
 
    Dirmngr was a system service and not a user service. */
 #define MAX_CERT_LENGTH (16*1024)
 
+/* The limit for the CERTLIST inquiry.  We allow for up to 20
+ * certificates but also take PEM encoding into account.  */
+#define MAX_CERTLIST_LENGTH ((MAX_CERT_LENGTH * 20 * 4)/3)
+
 /* The same goes for OpenPGP keyblocks, but here we need to allow for
    much longer blocks; a 200k keyblock is not too unusual for keys
    with a lot of signatures (e.g. 0x5b0358a2).  9C31503C6D866396 even
 /* The same goes for OpenPGP keyblocks, but here we need to allow for
    much longer blocks; a 200k keyblock is not too unusual for keys
    with a lot of signatures (e.g. 0x5b0358a2).  9C31503C6D866396 even
@@ -1729,7 +1733,7 @@ cmd_cachecert (assuan_context_t ctx, char *line)
 
 
 static const char hlp_validate[] =
 
 
 static const char hlp_validate[] =
-  "VALIDATE\n"
+  "VALIDATE [--systrust] [--tls]\n"
   "\n"
   "Validate a certificate using the certificate validation function\n"
   "used internally by dirmngr.  This command is only useful for\n"
   "\n"
   "Validate a certificate using the certificate validation function\n"
   "used internally by dirmngr.  This command is only useful for\n"
@@ -1739,20 +1743,38 @@ static const char hlp_validate[] =
   "  INQUIRE TARGETCERT\n"
   "\n"
   "and the caller is expected to return the certificate for the\n"
   "  INQUIRE TARGETCERT\n"
   "\n"
   "and the caller is expected to return the certificate for the\n"
-  "request as a binary blob.";
+  "request as a binary blob.  The option --tls modifies this by asking\n"
+  "for list of certificates with\n"
+  "\n"
+  "  INQUIRE CERTLIST\n"
+  "\n"
+  "Here the first certificate is the target certificate, the remaining\n"
+  "certificates are suggested intermediary certificates.  All certifciates\n"
+  "need to be PEM encoded.\n"
+  "\n"
+  "The option --systrust changes the behaviour to include the system\n"
+  "provided root certificates as trust anchors.";
 static gpg_error_t
 cmd_validate (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
   gpg_error_t err;
   ksba_cert_t cert = NULL;
 static gpg_error_t
 cmd_validate (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
   gpg_error_t err;
   ksba_cert_t cert = NULL;
+  certlist_t certlist = NULL;
   unsigned char *value = NULL;
   size_t valuelen;
   unsigned char *value = NULL;
   size_t valuelen;
+  int systrust_mode, tls_mode;
 
 
-  (void)line;
+  systrust_mode = has_option (line, "--systrust");
+  tls_mode = has_option (line, "--tls");
+  line = skip_options (line);
 
 
-  err = assuan_inquire (ctrl->server_local->assuan_ctx, "TARGETCERT",
-                       &value, &valuelen, MAX_CERT_LENGTH);
+  if (tls_mode)
+    err = assuan_inquire (ctrl->server_local->assuan_ctx, "CERTLIST",
+                          &value, &valuelen, MAX_CERTLIST_LENGTH);
+  else
+    err = assuan_inquire (ctrl->server_local->assuan_ctx, "TARGETCERT",
+                          &value, &valuelen, MAX_CERT_LENGTH);
   if (err)
     {
       log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
   if (err)
     {
       log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
@@ -1761,6 +1783,27 @@ cmd_validate (assuan_context_t ctx, char *line)
 
   if (!valuelen) /* No data returned; return a comprehensible error. */
     err = gpg_error (GPG_ERR_MISSING_CERT);
 
   if (!valuelen) /* No data returned; return a comprehensible error. */
     err = gpg_error (GPG_ERR_MISSING_CERT);
+  else if (tls_mode)
+    {
+      estream_t fp;
+
+      fp = es_fopenmem_init (0, "rb", value, valuelen);
+      if (!fp)
+        err = gpg_error_from_syserror ();
+      else
+        {
+          err = read_certlist_from_stream (&certlist, fp);
+          es_fclose (fp);
+          if (!err && !certlist)
+            err = gpg_error (GPG_ERR_MISSING_CERT);
+          if (!err)
+            {
+              /* Extraxt the first certificate from the list.  */
+              cert = certlist->cert;
+              ksba_cert_ref (cert);
+            }
+        }
+    }
   else
     {
       err = ksba_cert_new (&cert);
   else
     {
       err = ksba_cert_new (&cert);
@@ -1771,26 +1814,47 @@ cmd_validate (assuan_context_t ctx, char *line)
   if(err)
     goto leave;
 
   if(err)
     goto leave;
 
-  /* 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;
+  if (!tls_mode)
+    {
+      /* If we have this certificate already in our cache, use the
+       * cached version for validation because this will take care of
+       * any cached results.  We don't need to do this in tls mode
+       * because this has already been done for certificate in a
+       * certlist_t. */
+      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;
+        }
+    }
+
+  /* Quick hack to make verification work by inserting the supplied
+   * certs into the cache.  */
+  if (tls_mode && certlist)
+    {
+      certlist_t cl;
+
+      for (cl = certlist->next; cl; cl = cl->next)
+        cache_cert (cl->cert);
+    }
 
 
-    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, NULL);
+  err = validate_cert_chain
+    (ctrl, cert, NULL,
+     tls_mode && systrust_mode ? VALIDATE_MODE_TLS_SYSTRUST  :
+     tls_mode                  ? VALIDATE_MODE_TLS           :
+     /**/        systrust_mode ? VALIDATE_MODE_CERT_SYSTRUST :
+     /**/                        VALIDATE_MODE_CERT,
+     NULL);
 
  leave:
   ksba_cert_release (cert);
 
  leave:
   ksba_cert_release (cert);
+  release_certlist (certlist);
   return leave_cmd (ctx, err);
 }
 
   return leave_cmd (ctx, err);
 }
 
index 5081ae0..8fb2df2 100644 (file)
@@ -233,8 +233,8 @@ check_revocations (ctrl_t ctrl, chain_item_t chain)
   int any_crl_too_old = 0;
   chain_item_t ci;
 
   int any_crl_too_old = 0;
   chain_item_t ci;
 
-  assert (ctrl->check_revocations_nest_level >= 0);
-  assert (chain);
+  log_assert (ctrl->check_revocations_nest_level >= 0);
+  log_assert (chain);
 
   if (ctrl->check_revocations_nest_level > 10)
     {
 
   if (ctrl->check_revocations_nest_level > 10)
     {
@@ -551,7 +551,9 @@ validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
           if (err)
             goto leave;  /* No. */
 
           if (err)
             goto leave;  /* No. */
 
-          err = is_trusted_cert (subject_cert, 0);
+          err = is_trusted_cert (subject_cert,
+                                 (mode == VALIDATE_MODE_CERT_SYSTRUST
+                                  || mode == VALIDATE_MODE_TLS_SYSTRUST));
           if (!err)
             ; /* Yes we trust this cert.  */
           else if (gpg_err_code (err) == GPG_ERR_NOT_TRUSTED)
           if (!err)
             ; /* Yes we trust this cert.  */
           else if (gpg_err_code (err) == GPG_ERR_NOT_TRUSTED)
@@ -772,7 +774,9 @@ validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
        * our validity results to avoid double work.  Far worse a
        * catch-22 may happen for an improper setup hierarchy and we
        * need a way to break up such a deadlock.  */
        * our validity results to avoid double work.  Far worse a
        * catch-22 may happen for an improper setup hierarchy and we
        * need a way to break up such a deadlock.  */
-      err = check_revocations (ctrl, chain);
+      if (mode != VALIDATE_MODE_TLS_SYSTRUST)
+        err = check_revocations (ctrl, chain);
+#warning fix the above
     }
 
   if (!err && opt.verbose)
     }
 
   if (!err && opt.verbose)