dirmngr: Implement trust classes for the cert cache.

* 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>
This commit is contained in:
Werner Koch 2017-02-21 12:23:20 +01:00
parent 493c142e58
commit 50b9828eac
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
3 changed files with 98 additions and 65 deletions

View File

@ -33,7 +33,7 @@
#include "crlfetch.h" #include "crlfetch.h"
#include "certcache.h" #include "certcache.h"
#define MAX_EXTRA_CACHED_CERTS 1000 #define MAX_NONPERM_CACHED_CERTS 1000
/* Constants used to classify search patterns. */ /* Constants used to classify search patterns. */
enum pattern_class enum pattern_class
@ -66,15 +66,14 @@ struct cert_item_s
char *issuer_dn; /* The malloced issuer DN. */ char *issuer_dn; /* The malloced issuer DN. */
ksba_sexp_t sn; /* The malloced serial number */ ksba_sexp_t sn; /* The malloced serial number */
char *subject_dn; /* The malloced subject DN - maybe NULL. */ char *subject_dn; /* The malloced subject DN - maybe NULL. */
struct
{ /* If this field is set the certificate has been taken from some
unsigned int config:1; /* This has been loaded from the configuration. */ * configuration and shall not be flushed from the cache. */
unsigned int trusted:1; /* This is a trusted root certificate. */ unsigned int permanent:1;
unsigned int systrust:1;/* The certifciate is trusted because it
* is in the system's store of trusted /* If this field is set the certificate is trusted. The actual
* certificates (i.e. not configured using * value is a (possible) combination of CERTTRUST_CLASS values. */
* GnuPG mechanisms. */ unsigned int trustclasses:4;
} flags;
}; };
typedef struct cert_item_s *cert_item_t; 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. */ /* Flag to track whether the cache has been initialized. */
static int initialization_done; static int initialization_done;
/* Total number of certificates loaded during initialization /* Total number of non-permanent certificates. */
* (ie. configured), extra certificates cached during operation, static unsigned int total_nonperm_certificates;
* 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;
#ifdef HAVE_W32_SYSTEM #ifdef HAVE_W32_SYSTEM
@ -245,6 +239,9 @@ clean_cache_slot (cert_item_t ci)
cert = ci->cert; cert = ci->cert;
ci->cert = NULL; ci->cert = NULL;
ci->permanent = 0;
ci->trustclasses = 0;
ksba_cert_release (cert); 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 * will be stored on success or when the function returns
* GPG_ERR_DUP_VALUE. */ * GPG_ERR_DUP_VALUE. */
static gpg_error_t 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; unsigned char help_fpr_buffer[20], *fpr;
cert_item_t ci; 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 * implementation is not very efficient but compared to the long
* time it takes to retrieve a certificate from an external resource * time it takes to retrieve a certificate from an external resource
* it seems to be reasonable. */ * 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; static int idx;
cert_item_t ci_mark; cert_item_t ci_mark;
int i; int i;
unsigned int drop_count; unsigned int drop_count;
drop_count = MAX_EXTRA_CACHED_CERTS / 20; drop_count = MAX_NONPERM_CACHED_CERTS / 20;
if (drop_count < 2) if (drop_count < 2)
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; ci_mark = NULL;
for (ci = cert_cache[i]; ci; ci = ci->next) for (ci = cert_cache[i]; ci; ci = ci->next)
if (ci->cert && !ci->flags.config) if (ci->cert && !ci->permanent)
ci_mark = ci; ci_mark = ci;
if (ci_mark) if (ci_mark)
{ {
clean_cache_slot (ci_mark); clean_cache_slot (ci_mark);
drop_count--; drop_count--;
total_extra_certificates--; total_nonperm_certificates--;
if (ci->flags.trusted)
total_trusted_certificates--;
if (ci->flags.systrust)
total_system_trusted_certificates--;
} }
} }
if (i==idx) 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]; ci->next = cert_cache[*fpr];
cert_cache[*fpr] = ci; cert_cache[*fpr] = ci;
} }
else
memset (&ci->flags, 0, sizeof ci->flags);
ksba_cert_ref (cert); ksba_cert_ref (cert);
ci->cert = 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); return gpg_error (GPG_ERR_INV_CERT_OBJ);
} }
ci->subject_dn = ksba_cert_get_subject (cert, 0); ci->subject_dn = ksba_cert_get_subject (cert, 0);
ci->flags.config = !!from_config; ci->permanent = !!permanent;
ci->flags.trusted = !!is_trusted; ci->trustclasses = trustclass;
ci->flags.systrust = (is_trusted && is_trusted == 2);
if (ci->flags.trusted) if (!permanent)
total_trusted_certificates++; total_nonperm_certificates++;
if (ci->flags.systrust)
total_system_trusted_certificates++;
if (from_config)
total_config_certificates++;
else
total_extra_certificates++;
return 0; 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 certificates are DER encoded and not PEM encapsulated. The cache
should be in a locked state when calling this function. */ should be in a locked state when calling this function. */
static gpg_error_t 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; gpg_error_t err;
DIR *dir; DIR *dir;
@ -428,12 +412,12 @@ load_certs_from_dir (const char *dirname, int are_trusted)
continue; 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) if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
log_info (_("certificate '%s' already cached\n"), fname); log_info (_("certificate '%s' already cached\n"), fname);
else if (!err) else if (!err)
{ {
if (are_trusted) if (trustclass)
log_info (_("trusted certificate '%s' loaded\n"), fname); log_info (_("trusted certificate '%s' loaded\n"), fname);
else else
log_info (_("certificate '%s' loaded\n"), fname); log_info (_("certificate '%s' loaded\n"), fname);
@ -509,7 +493,7 @@ load_certs_from_file (const char *fname)
goto leave; 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) if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
log_info (_("certificate '%s' already cached\n"), fname); log_info (_("certificate '%s' already cached\n"), fname);
else if (err) else if (err)
@ -619,7 +603,7 @@ load_certs_from_w32_store (const char *storename)
break; break;
} }
err = put_cert (cert, 1, 2, NULL); err = put_cert (cert, 1, CERTTRUST_CLASS_SYSTEM, NULL);
if (!err) if (!err)
count++; count++;
if (gpg_err_code (err) == GPG_ERR_DUP_VALUE) if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
@ -710,7 +694,7 @@ cert_cache_init (void)
load_certs_from_system (); load_certs_from_system ();
dname = make_filename (gnupg_sysconfdir (), "trusted-certs", NULL); dname = make_filename (gnupg_sysconfdir (), "trusted-certs", NULL);
load_certs_from_dir (dname, 1); load_certs_from_dir (dname, CERTTRUST_CLASS_CONFIG);
xfree (dname); xfree (dname);
dname = make_filename (gnupg_sysconfdir (), "extra-certs", NULL); dname = make_filename (gnupg_sysconfdir (), "extra-certs", NULL);
@ -753,10 +737,7 @@ cert_cache_deinit (int full)
} }
} }
total_config_certificates = 0; total_nonperm_certificates = 0;
total_extra_certificates = 0;
total_trusted_certificates = 0;
total_system_trusted_certificates = 0;
initialization_done = 0; initialization_done = 0;
release_cache_lock (); release_cache_lock ();
} }
@ -765,12 +746,51 @@ cert_cache_deinit (int full)
void void
cert_cache_print_stats (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"), log_info (_("permanently loaded certificates: %u\n"),
total_config_certificates); n_permanent);
log_info (_(" runtime cached certificates: %u\n"), log_info (_(" runtime cached certificates: %u\n"),
total_extra_certificates); n_nonperm);
log_info (_(" trusted certificates: %u (%u)\n"), log_info (_(" trusted certificates: %u (%u,%u,%u,%u)\n"),
total_trusted_certificates, total_system_trusted_certificates); 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 * 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 * case of systems errors. TRUSTCLASSES are the bitwise ORed
* provided certificates are considered trusted. */ * CERTTRUST_CLASS values to use for the check. */
gpg_error_t 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]; unsigned char fpr[20];
cert_item_t ci; 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) for (ci=cert_cache[*fpr]; ci; ci = ci->next)
if (ci->cert && !memcmp (ci->fpr, fpr, 20)) 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 (); release_cache_lock ();
return 0; /* Yes, it is trusted. */ return 0; /* Yes, it is trusted. */
} }

View File

@ -21,6 +21,15 @@
#ifndef CERTCACHE_H #ifndef CERTCACHE_H
#define 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. */ /* First time initialization of the certificate cache. */
void cert_cache_init (void); 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 /* Return 0 if the certificate is a trusted certificate. Returns
* GPG_ERR_NOT_TRUSTED if it is not trusted or other error codes in * 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 * case of systems errors. TRUSTCLASSES are the bitwise ORed
* provided certificates are considered trusted. */ * CERTTRUST_CLASS values to use for the check. */
gpg_error_t is_trusted_cert (ksba_cert_t cert, int with_systrust); gpg_error_t is_trusted_cert (ksba_cert_t cert, unsigned trustclasses);
/* Return a certificate object for the given fingerprint. FPR is /* Return a certificate object for the given fingerprint. FPR is
expected to be a 20 byte binary SHA-1 fingerprint. If no matching expected to be a 20 byte binary SHA-1 fingerprint. If no matching

View File

@ -203,7 +203,7 @@ allowed_ca (ksba_cert_t cert, int *chainlen)
return err; return err;
if (!flag) 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 /* The German SigG Root CA's certificate does not flag
itself as a CA; thus we relax this requirement if we 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) if (err)
goto leave; /* No. */ goto leave; /* No. */
err = is_trusted_cert (subject_cert, err = is_trusted_cert
!!(flags & VALIDATE_FLAG_SYSTRUST)); (subject_cert,
(CERTTRUST_CLASS_CONFIG
| (flags & VALIDATE_FLAG_SYSTRUST)? CERTTRUST_CLASS_SYSTEM : 0));
if (!err) if (!err)
; /* Yes we trust this cert. */ ; /* Yes we trust this cert. */
else if (gpg_err_code (err) == GPG_ERR_NOT_TRUSTED) else if (gpg_err_code (err) == GPG_ERR_NOT_TRUSTED)