dirmngr: Implement trust classes for the cert cache.
authorWerner Koch <wk@gnupg.org>
Tue, 21 Feb 2017 11:23:20 +0000 (12:23 +0100)
committerWerner Koch <wk@gnupg.org>
Tue, 21 Feb 2017 11:23:20 +0000 (12:23 +0100)
* dirmngr/certcache.h (CERTTRUST_CLASS_SYSTEM): New.
(CERTTRUST_CLASS_CONFIG): New.
(CERTTRUST_CLASS_HKP): New.
(CERTTRUST_CLASS_HKPSPOOL): New.
* dirmngr/certcache.c (MAX_EXTRA_CACHED_CERTS): Rename to ...
(MAX_NONPERM_CACHED_CERTS): this.
(total_extra_certificates): Rename to ...
(total_nonperm_certificates): this.
(total_config_certificates): Remove.
(total_trusted_certificates): Remove.
(total_system_trusted_certificates): Remove.
(cert_item_s): Remove field 'flags'.  Add fields 'permanent' and
'trustclasses'.
(clean_cache_slot): Clear new fields.
(put_cert): Change for new cert_item_t structure.
(load_certs_from_dir): Rename arg 'are_trusted' to 'trustclass'
(load_certs_from_file): Use CERTTRUST_CLASS_ value for put_cert.
(load_certs_from_w32_store): Ditto.
(cert_cache_init): Ditto.
(cert_cache_print_stats): Rewrite.
(is_trusted_cert): Replace arg 'with_systrust' by 'trustclasses'.
Chnage the test.
* dirmngr/validate.c (allowed_ca): Pass CERTTRUST_CLASS_CONFIG to
is_trusted_cert.
(validate_cert_chain): Pass CERTTRUST_CLASS_ values to
is_trusted_cert.
--

These trust classes make it easier to select certain sets of root
certificates.

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

index ff86f61..0e4071d 100644 (file)
@@ -33,7 +33,7 @@
 #include "crlfetch.h"
 #include "certcache.h"
 
-#define MAX_EXTRA_CACHED_CERTS 1000
+#define MAX_NONPERM_CACHED_CERTS 1000
 
 /* Constants used to classify search patterns.  */
 enum pattern_class
@@ -66,15 +66,14 @@ struct cert_item_s
   char *issuer_dn;          /* The malloced issuer DN.  */
   ksba_sexp_t sn;           /* The malloced serial number  */
   char *subject_dn;         /* The malloced subject DN - maybe NULL.  */
-  struct
-  {
-    unsigned int config:1;  /* This has been loaded from the configuration.  */
-    unsigned int trusted:1; /* This is a trusted root certificate.  */
-    unsigned int systrust:1;/* The certifciate is trusted because it
-                             * is in the system's store of trusted
-                             * certificates (i.e. not configured using
-                             * GnuPG mechanisms.  */
-  } flags;
+
+  /* If this field is set the certificate has been taken from some
+   * configuration and shall not be flushed from the cache.  */
+  unsigned int permanent:1;
+
+  /* If this field is set the certificate is trusted.  The actual
+   * value is a (possible) combination of CERTTRUST_CLASS values.  */
+  unsigned int trustclasses:4;
 };
 typedef struct cert_item_s *cert_item_t;
 
@@ -92,13 +91,8 @@ static npth_rwlock_t cert_cache_lock;
 /* Flag to track whether the cache has been initialized.  */
 static int initialization_done;
 
-/* Total number of certificates loaded during initialization
- * (ie. configured), extra certificates cached during operation,
- * number of trusted and system trusted certificates.  */
-static unsigned int total_config_certificates;
-static unsigned int total_extra_certificates;
-static unsigned int total_trusted_certificates;
-static unsigned int total_system_trusted_certificates;
+/* Total number of non-permanent certificates.  */
+static unsigned int total_nonperm_certificates;
 
 
 #ifdef HAVE_W32_SYSTEM
@@ -245,6 +239,9 @@ clean_cache_slot (cert_item_t ci)
   cert = ci->cert;
   ci->cert = NULL;
 
+  ci->permanent = 0;
+  ci->trustclasses = 0;
+
   ksba_cert_release (cert);
 }
 
@@ -263,7 +260,8 @@ clean_cache_slot (cert_item_t ci)
  * will be stored on success or when the function returns
  * GPG_ERR_DUP_VALUE.  */
 static gpg_error_t
-put_cert (ksba_cert_t cert, int from_config, int is_trusted, void *fpr_buffer)
+put_cert (ksba_cert_t cert, int permanent, unsigned int trustclass,
+          void *fpr_buffer)
 {
   unsigned char help_fpr_buffer[20], *fpr;
   cert_item_t ci;
@@ -281,14 +279,14 @@ put_cert (ksba_cert_t cert, int from_config, int is_trusted, void *fpr_buffer)
    * implementation is not very efficient but compared to the long
    * time it takes to retrieve a certificate from an external resource
    * it seems to be reasonable.  */
-  if (!from_config && total_extra_certificates >= MAX_EXTRA_CACHED_CERTS)
+  if (!permanent && total_nonperm_certificates >= MAX_NONPERM_CACHED_CERTS)
     {
       static int idx;
       cert_item_t ci_mark;
       int i;
       unsigned int drop_count;
 
-      drop_count = MAX_EXTRA_CACHED_CERTS / 20;
+      drop_count = MAX_NONPERM_CACHED_CERTS / 20;
       if (drop_count < 2)
         drop_count = 2;
 
@@ -298,17 +296,13 @@ put_cert (ksba_cert_t cert, int from_config, int is_trusted, void *fpr_buffer)
         {
           ci_mark = NULL;
           for (ci = cert_cache[i]; ci; ci = ci->next)
-            if (ci->cert && !ci->flags.config)
+            if (ci->cert && !ci->permanent)
               ci_mark = ci;
           if (ci_mark)
             {
               clean_cache_slot (ci_mark);
               drop_count--;
-              total_extra_certificates--;
-              if (ci->flags.trusted)
-                total_trusted_certificates--;
-              if (ci->flags.systrust)
-                total_system_trusted_certificates--;
+              total_nonperm_certificates--;
             }
         }
       if (i==idx)
@@ -334,8 +328,6 @@ put_cert (ksba_cert_t cert, int from_config, int is_trusted, void *fpr_buffer)
       ci->next = cert_cache[*fpr];
       cert_cache[*fpr] = ci;
     }
-  else
-    memset (&ci->flags, 0, sizeof ci->flags);
 
   ksba_cert_ref (cert);
   ci->cert = cert;
@@ -348,19 +340,11 @@ put_cert (ksba_cert_t cert, int from_config, int is_trusted, void *fpr_buffer)
       return gpg_error (GPG_ERR_INV_CERT_OBJ);
     }
   ci->subject_dn = ksba_cert_get_subject (cert, 0);
-  ci->flags.config  = !!from_config;
-  ci->flags.trusted = !!is_trusted;
-  ci->flags.systrust = (is_trusted && is_trusted == 2);
+  ci->permanent = !!permanent;
+  ci->trustclasses = trustclass;
 
-  if (ci->flags.trusted)
-    total_trusted_certificates++;
-  if (ci->flags.systrust)
-    total_system_trusted_certificates++;
-
-  if (from_config)
-    total_config_certificates++;
-  else
-    total_extra_certificates++;
+  if (!permanent)
+    total_nonperm_certificates++;
 
   return 0;
 }
@@ -371,7 +355,7 @@ put_cert (ksba_cert_t cert, int from_config, int is_trusted, void *fpr_buffer)
    certificates are DER encoded and not PEM encapsulated. The cache
    should be in a locked state when calling this function.  */
 static gpg_error_t
-load_certs_from_dir (const char *dirname, int are_trusted)
+load_certs_from_dir (const char *dirname, unsigned int trustclass)
 {
   gpg_error_t err;
   DIR *dir;
@@ -428,12 +412,12 @@ load_certs_from_dir (const char *dirname, int are_trusted)
           continue;
         }
 
-      err = put_cert (cert, 1, !!are_trusted, NULL);
+      err = put_cert (cert, 1, trustclass, NULL);
       if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
         log_info (_("certificate '%s' already cached\n"), fname);
       else if (!err)
         {
-          if (are_trusted)
+          if (trustclass)
             log_info (_("trusted certificate '%s' loaded\n"), fname);
           else
             log_info (_("certificate '%s' loaded\n"), fname);
@@ -509,7 +493,7 @@ load_certs_from_file (const char *fname)
           goto leave;
         }
 
-      err = put_cert (cert, 1, 2, NULL);
+      err = put_cert (cert, 1, CERTTRUST_CLASS_SYSTEM, NULL);
       if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
         log_info (_("certificate '%s' already cached\n"), fname);
       else if (err)
@@ -619,7 +603,7 @@ load_certs_from_w32_store (const char *storename)
               break;
             }
 
-          err = put_cert (cert, 1, 2, NULL);
+          err = put_cert (cert, 1, CERTTRUST_CLASS_SYSTEM, NULL);
           if (!err)
             count++;
           if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
@@ -710,7 +694,7 @@ cert_cache_init (void)
   load_certs_from_system ();
 
   dname = make_filename (gnupg_sysconfdir (), "trusted-certs", NULL);
-  load_certs_from_dir (dname, 1);
+  load_certs_from_dir (dname, CERTTRUST_CLASS_CONFIG);
   xfree (dname);
 
   dname = make_filename (gnupg_sysconfdir (), "extra-certs", NULL);
@@ -753,10 +737,7 @@ cert_cache_deinit (int full)
         }
     }
 
-  total_config_certificates = 0;
-  total_extra_certificates = 0;
-  total_trusted_certificates = 0;
-  total_system_trusted_certificates = 0;
+  total_nonperm_certificates = 0;
   initialization_done = 0;
   release_cache_lock ();
 }
@@ -765,12 +746,51 @@ cert_cache_deinit (int full)
 void
 cert_cache_print_stats (void)
 {
+  cert_item_t ci;
+  int idx;
+  unsigned int n_nonperm = 0;
+  unsigned int n_permanent = 0;
+  unsigned int n_trusted = 0;
+  unsigned int n_trustclass_system = 0;
+  unsigned int n_trustclass_config = 0;
+  unsigned int n_trustclass_hkp = 0;
+  unsigned int n_trustclass_hkpspool = 0;
+
+  acquire_cache_read_lock ();
+  for (idx = 0; idx < 256; idx++)
+    for (ci=cert_cache[idx]; ci; ci = ci->next)
+      if (ci->cert)
+        {
+          if (ci->permanent)
+            n_permanent++;
+          else
+            n_nonperm++;
+          if (ci->trustclasses)
+            {
+              n_trusted++;
+              if ((ci->trustclasses & CERTTRUST_CLASS_SYSTEM))
+                n_trustclass_system++;
+              if ((ci->trustclasses & CERTTRUST_CLASS_CONFIG))
+                n_trustclass_config++;
+              if ((ci->trustclasses & CERTTRUST_CLASS_HKP))
+                n_trustclass_hkp++;
+              if ((ci->trustclasses & CERTTRUST_CLASS_HKPSPOOL))
+                n_trustclass_hkpspool++;
+            }
+        }
+
+  release_cache_lock ();
+
   log_info (_("permanently loaded certificates: %u\n"),
-            total_config_certificates);
+            n_permanent);
   log_info (_("    runtime cached certificates: %u\n"),
-            total_extra_certificates);
-  log_info (_("           trusted certificates: %u (%u)\n"),
-            total_trusted_certificates, total_system_trusted_certificates);
+            n_nonperm);
+  log_info (_("           trusted certificates: %u (%u,%u,%u,%u)\n"),
+            n_trusted,
+            n_trustclass_system,
+            n_trustclass_config,
+            n_trustclass_hkp,
+            n_trustclass_hkpspool);
 }
 
 
@@ -1543,12 +1563,12 @@ find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid)
 }
 
 
-/* Return 0 if the certificate is a trusted certificate.  Returns
+/* Return 0 if the certificate is a trusted certificate. Returns
  * GPG_ERR_NOT_TRUSTED if it is not trusted or other error codes in
- * case of systems errors.  If WITH_SYSTRUST is set also system
- * provided certificates are considered trusted.  */
+ * case of systems errors.  TRUSTCLASSES are the bitwise ORed
+ * CERTTRUST_CLASS values to use for the check.  */
 gpg_error_t
-is_trusted_cert (ksba_cert_t cert, int with_systrust)
+is_trusted_cert (ksba_cert_t cert, unsigned int trustclasses)
 {
   unsigned char fpr[20];
   cert_item_t ci;
@@ -1559,8 +1579,10 @@ is_trusted_cert (ksba_cert_t cert, int with_systrust)
   for (ci=cert_cache[*fpr]; ci; ci = ci->next)
     if (ci->cert && !memcmp (ci->fpr, fpr, 20))
       {
-        if (ci->flags.trusted && (with_systrust || !ci->flags.systrust))
+        if ((ci->trustclasses & trustclasses))
           {
+            /* The certificate is trusted in one of the given
+             * TRUSTCLASSES.  */
             release_cache_lock ();
             return 0; /* Yes, it is trusted. */
           }
index 1f86706..fec2ff4 100644 (file)
 #ifndef CERTCACHE_H
 #define CERTCACHE_H
 
+/* The origin of the trusted root certificates.  */
+enum {
+  CERTTRUST_CLASS_SYSTEM  = 1, /* From the system's list of trusted certs. */
+  CERTTRUST_CLASS_CONFIG  = 2, /* From dirmngr's config files.         */
+  CERTTRUST_CLASS_HKP     = 4, /* From --hkp-cacert                    */
+  CERTTRUST_CLASS_HKPSPOOL= 8, /* The one and only from sks-keyservers */
+};
+
+
 /* First time initialization of the certificate cache.  */
 void cert_cache_init (void);
 
@@ -42,9 +51,9 @@ gpg_error_t cache_cert_silent (ksba_cert_t cert, void *fpr_buffer);
 
 /* Return 0 if the certificate is a trusted certificate. Returns
  * GPG_ERR_NOT_TRUSTED if it is not trusted or other error codes in
- * case of systems errors.  If WITH_SYSTRUST is set also system
- * provided certificates are considered trusted.  */
-gpg_error_t is_trusted_cert (ksba_cert_t cert, int with_systrust);
+ * case of systems errors.  TRUSTCLASSES are the bitwise ORed
+ * CERTTRUST_CLASS values to use for the check.  */
+gpg_error_t is_trusted_cert (ksba_cert_t cert, unsigned trustclasses);
 
 /* Return a certificate object for the given fingerprint.  FPR is
    expected to be a 20 byte binary SHA-1 fingerprint.  If no matching
index 1599a8d..5bd784f 100644 (file)
@@ -203,7 +203,7 @@ allowed_ca (ksba_cert_t cert, int *chainlen)
     return err;
   if (!flag)
     {
-      if (!is_trusted_cert (cert, 0))
+      if (!is_trusted_cert (cert, CERTTRUST_CLASS_CONFIG))
         {
           /* The German SigG Root CA's certificate does not flag
              itself as a CA; thus we relax this requirement if we
@@ -540,8 +540,10 @@ validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
           if (err)
             goto leave;  /* No. */
 
-          err = is_trusted_cert (subject_cert,
-                                 !!(flags & VALIDATE_FLAG_SYSTRUST));
+          err = is_trusted_cert
+            (subject_cert,
+             (CERTTRUST_CLASS_CONFIG
+              | (flags & VALIDATE_FLAG_SYSTRUST)? CERTTRUST_CLASS_SYSTEM : 0));
           if (!err)
             ; /* Yes we trust this cert.  */
           else if (gpg_err_code (err) == GPG_ERR_NOT_TRUSTED)