dirmngr: New option --ignore-cert

* dirmngr/dirmngr.h (struct fingerprint_list_s): Add field binlen.
(opt): Add field ignored_certs.
* dirmngr/dirmngr.c: Add option --ignore-cert
(parse_rereadable_options): Handle that option.
(parse_ocsp_signer): Rename to ...
(parse_fingerprint_item): this and add two args.
* dirmngr/certcache.c (put_cert): Ignore all to be igored certs.
Change callers to handle the new error return.
--

This option is useful as a workaround in case we ill run into other
chain validation errors like what we fixed in
GnuPG-bug-id: 5639
This commit is contained in:
Werner Koch 2021-10-06 10:31:41 +02:00
parent 6879937885
commit 4b3e9a44b5
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
5 changed files with 98 additions and 7 deletions

View File

@ -43,7 +43,8 @@
LENGTH bytes. The function checks that the STRING will convert LENGTH bytes. The function checks that the STRING will convert
exactly to LENGTH bytes. The string is delimited by either end of exactly to LENGTH bytes. The string is delimited by either end of
string or a white space character. The function returns -1 on string or a white space character. The function returns -1 on
error or the length of the parsed string. */ error or the length of the parsed string. In-place conversion is
allowed but the Source string might be garbled on error. */
int int
hex2bin (const char *string, void *buffer, size_t length) hex2bin (const char *string, void *buffer, size_t length)
{ {

View File

@ -262,13 +262,14 @@ clean_cache_slot (cert_item_t ci)
* fingerprint of the certificate will be stored there. FPR_BUFFER * fingerprint of the certificate will be stored there. FPR_BUFFER
* needs to point to a buffer of at least 20 bytes. The fingerprint * needs to point to a buffer of at least 20 bytes. The fingerprint
* 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 or GPG_ERR_NOT_ENABLED. */
static gpg_error_t static gpg_error_t
put_cert (ksba_cert_t cert, int permanent, unsigned int trustclass, put_cert (ksba_cert_t cert, int permanent, unsigned int trustclass,
void *fpr_buffer) 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;
fingerprint_list_t ignored;
fpr = fpr_buffer? fpr_buffer : &help_fpr_buffer; fpr = fpr_buffer? fpr_buffer : &help_fpr_buffer;
@ -317,6 +318,14 @@ put_cert (ksba_cert_t cert, int permanent, unsigned int trustclass,
} }
cert_compute_fpr (cert, fpr); cert_compute_fpr (cert, fpr);
/* Compare against the list of to be ignored certificates. */
for (ignored = opt.ignored_certs; ignored; ignored = ignored->next)
if (ignored->binlen == 20 && !memcmp (fpr, ignored->hexfpr, 20))
{
/* We are configured not to use this certificate. */
return gpg_error (GPG_ERR_NOT_ENABLED);
}
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))
return gpg_error (GPG_ERR_DUP_VALUE); return gpg_error (GPG_ERR_DUP_VALUE);
@ -440,6 +449,8 @@ load_certs_from_dir (const char *dirname, unsigned int trustclass)
cert_log_subject (_(" subject ="), cert); cert_log_subject (_(" subject ="), cert);
} }
} }
else if (gpg_err_code (err) == GPG_ERR_NOT_ENABLED)
log_info ("certificate '%s' skipped due to configuration\n", fname);
else else
log_error (_("error loading certificate '%s': %s\n"), log_error (_("error loading certificate '%s': %s\n"),
fname, gpg_strerror (err)); fname, gpg_strerror (err));
@ -510,6 +521,8 @@ load_certs_from_file (const char *fname, unsigned int trustclasses,
err = put_cert (cert, 1, trustclasses, NULL); err = put_cert (cert, 1, trustclasses, 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 (gpg_err_code (err) == GPG_ERR_NOT_ENABLED)
log_info ("certificate '%s' skipped due to configuration\n", fname);
else if (err) else if (err)
log_error (_("error loading certificate '%s': %s\n"), log_error (_("error loading certificate '%s': %s\n"),
fname, gpg_strerror (err)); fname, gpg_strerror (err));
@ -625,6 +638,9 @@ load_certs_from_w32_store (const char *storename)
if (DBG_X509) if (DBG_X509)
log_debug (_("certificate '%s' already cached\n"), storename); log_debug (_("certificate '%s' already cached\n"), storename);
} }
else if (gpg_err_code (err) == GPG_ERR_NOT_ENABLED)
log_info ("certificate '%s' skipped due to configuration\n",
storename);
else if (err) else if (err)
log_error (_("error loading certificate '%s': %s\n"), log_error (_("error loading certificate '%s': %s\n"),
storename, gpg_strerror (err)); storename, gpg_strerror (err));
@ -852,6 +868,8 @@ cache_cert (ksba_cert_t cert)
log_info (_("certificate already cached\n")); log_info (_("certificate already cached\n"));
else if (!err) else if (!err)
log_info (_("certificate cached\n")); log_info (_("certificate cached\n"));
else if (gpg_err_code (err) == GPG_ERR_NOT_ENABLED)
log_info ("certificate skipped due to configuration\n");
else else
log_error (_("error caching certificate: %s\n"), gpg_strerror (err)); log_error (_("error caching certificate: %s\n"), gpg_strerror (err));
return err; return err;
@ -872,7 +890,10 @@ cache_cert_silent (ksba_cert_t cert, void *fpr_buffer)
release_cache_lock (); release_cache_lock ();
if (gpg_err_code (err) == GPG_ERR_DUP_VALUE) if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
err = 0; err = 0;
if (err)
if (gpg_err_code (err) == GPG_ERR_NOT_ENABLED)
log_info ("certificate skipped due to configuration\n");
else if (err)
log_error (_("error caching certificate: %s\n"), gpg_strerror (err)); log_error (_("error caching certificate: %s\n"), gpg_strerror (err));
return err; return err;
} }

View File

@ -143,6 +143,7 @@ enum cmd_and_opt_values {
oSocketName, oSocketName,
oLDAPWrapperProgram, oLDAPWrapperProgram,
oHTTPWrapperProgram, oHTTPWrapperProgram,
oIgnoreCert,
oIgnoreCertExtension, oIgnoreCertExtension,
oUseTor, oUseTor,
oNoUseTor, oNoUseTor,
@ -216,6 +217,7 @@ static gpgrt_opt_t opts[] = {
N_("|N|do not return more than N items in one query")), N_("|N|do not return more than N items in one query")),
ARGPARSE_s_u (oFakedSystemTime, "faked-system-time", "@"), /*(epoch time)*/ ARGPARSE_s_u (oFakedSystemTime, "faked-system-time", "@"), /*(epoch time)*/
ARGPARSE_s_n (oDisableCheckOwnSocket, "disable-check-own-socket", "@"), ARGPARSE_s_n (oDisableCheckOwnSocket, "disable-check-own-socket", "@"),
ARGPARSE_s_s (oIgnoreCert,"ignore-cert", "@"),
ARGPARSE_s_s (oIgnoreCertExtension,"ignore-cert-extension", "@"), ARGPARSE_s_s (oIgnoreCertExtension,"ignore-cert-extension", "@"),
@ -419,7 +421,9 @@ static void cleanup (void);
#if USE_LDAP #if USE_LDAP
static ldap_server_t parse_ldapserver_file (const char* filename, int ienoent); static ldap_server_t parse_ldapserver_file (const char* filename, int ienoent);
#endif /*USE_LDAP*/ #endif /*USE_LDAP*/
static fingerprint_list_t parse_ocsp_signer (const char *string); static fingerprint_list_t parse_fingerprint_item (const char *string,
const char *optionname,
int want_binary);
static void netactivity_action (void); static void netactivity_action (void);
static void handle_connections (assuan_fd_t listen_fd); static void handle_connections (assuan_fd_t listen_fd);
static void gpgconf_versions (void); static void gpgconf_versions (void);
@ -667,6 +671,12 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread)
xfree (opt.ocsp_signer); xfree (opt.ocsp_signer);
opt.ocsp_signer = tmp; opt.ocsp_signer = tmp;
} }
while (opt.ignored_certs)
{
fingerprint_list_t tmp = opt.ignored_certs->next;
xfree (opt.ignored_certs);
opt.ignored_certs = tmp;
}
FREE_STRLIST (opt.ignored_cert_extensions); FREE_STRLIST (opt.ignored_cert_extensions);
http_register_tls_ca (NULL); http_register_tls_ca (NULL);
FREE_STRLIST (hkp_cacert_filenames); FREE_STRLIST (hkp_cacert_filenames);
@ -732,7 +742,8 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread)
case oAllowVersionCheck: opt.allow_version_check = 1; break; case oAllowVersionCheck: opt.allow_version_check = 1; break;
case oOCSPResponder: opt.ocsp_responder = pargs->r.ret_str; break; case oOCSPResponder: opt.ocsp_responder = pargs->r.ret_str; break;
case oOCSPSigner: case oOCSPSigner:
opt.ocsp_signer = parse_ocsp_signer (pargs->r.ret_str); opt.ocsp_signer = parse_fingerprint_item (pargs->r.ret_str,
"--ocsp-signer", 0);
break; break;
case oOCSPMaxClockSkew: opt.ocsp_max_clock_skew = pargs->r.ret_int; break; case oOCSPMaxClockSkew: opt.ocsp_max_clock_skew = pargs->r.ret_int; break;
case oOCSPMaxPeriod: opt.ocsp_max_period = pargs->r.ret_int; break; case oOCSPMaxPeriod: opt.ocsp_max_period = pargs->r.ret_int; break;
@ -754,6 +765,24 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread)
} }
break; break;
case oIgnoreCert:
{
fingerprint_list_t item, r;
item = parse_fingerprint_item (pargs->r.ret_str, "--ignore-cert", 20);
if (item)
{ /* Append */
if (!opt.ignored_certs)
opt.ignored_certs = item;
else
{
for (r = opt.ignored_certs; r->next; r = r->next)
;
r->next = item;
}
}
}
break;
case oIgnoreCertExtension: case oIgnoreCertExtension:
add_to_strlist (&opt.ignored_cert_extensions, pargs->r.ret_str); add_to_strlist (&opt.ignored_cert_extensions, pargs->r.ret_str);
break; break;
@ -1709,8 +1738,13 @@ parse_ldapserver_file (const char* filename, int ignore_enoent)
} }
#endif /*USE_LDAP*/ #endif /*USE_LDAP*/
/* Parse a fingerprint entry as used by --ocsc-signer. OPTIONNAME as
* a description on the options used. WANT_BINARY requests to store a
* binary fingerprint. Returns NULL on error and logs that error. */
static fingerprint_list_t static fingerprint_list_t
parse_ocsp_signer (const char *string) parse_fingerprint_item (const char *string,
const char *optionname, int want_binary)
{ {
gpg_error_t err; gpg_error_t err;
char *fname; char *fname;
@ -1735,10 +1769,15 @@ parse_ocsp_signer (const char *string)
if (j != 40 || !(spacep (string+i) || !string[i])) if (j != 40 || !(spacep (string+i) || !string[i]))
{ {
log_error (_("%s:%u: invalid fingerprint detected\n"), log_error (_("%s:%u: invalid fingerprint detected\n"),
"--ocsp-signer", 0); optionname, 0);
xfree (item); xfree (item);
return NULL; return NULL;
} }
if (want_binary)
{
item->binlen = 20;
hex2bin (item->hexfpr, item->hexfpr, 20);
}
return item; return item;
} }
@ -1821,6 +1860,12 @@ parse_ocsp_signer (const char *string)
log_error (_("%s:%u: invalid fingerprint detected\n"), fname, lnr); log_error (_("%s:%u: invalid fingerprint detected\n"), fname, lnr);
errflag = 1; errflag = 1;
} }
else if (want_binary)
{
item->binlen = 20;
hex2bin (item->hexfpr, item->hexfpr, 20);
}
i++; i++;
while (spacep (p+i)) while (spacep (p+i))
i++; i++;

View File

@ -74,6 +74,7 @@ typedef struct fingerprint_list_s *fingerprint_list_t;
struct fingerprint_list_s struct fingerprint_list_s
{ {
fingerprint_list_t next; fingerprint_list_t next;
char binlen; /* If this is not 0 hexfpr actually carries a binary fpr. */
char hexfpr[20+20+1]; char hexfpr[20+20+1];
}; };
@ -119,6 +120,10 @@ struct
int ignore_ocsp_service_url; /* Ignore OCSP service URLs as given in int ignore_ocsp_service_url; /* Ignore OCSP service URLs as given in
the certificate. */ the certificate. */
/* A list of fingerprints of certififcates we should completely
* ignore. These are all stored in binary format. */
fingerprint_list_t ignored_certs;
/* A list of certificate extension OIDs which are ignored so that /* A list of certificate extension OIDs which are ignored so that
one can claim that a critical extension has been handled. One one can claim that a critical extension has been handled. One
OID per string. */ OID per string. */

View File

@ -588,6 +588,25 @@ won't be rejected due to an unknown critical extension. Use this
option with care because extensions are usually flagged as critical option with care because extensions are usually flagged as critical
for a reason. for a reason.
@item --ignore-cert @var{fpr}|@var{file}
@opindex ignore-cert
Entirely ignore certificates with the fingerprint @var{fpr}. As an
alternative to the fingerprint a filename can be given in which case
all certificates described in that file are ignored. Any argument
which contains a slash, dot or tilde is considered a filename. Usual
filename expansion takes place: A tilde at the start followed by a
slash is replaced by the content of @env{HOME}, no slash at start
describes a relative filename which will be searched at the home
directory. To make sure that the @var{file} is searched in the home
directory, either prepend the name with "./" or use a name which
contains a dot. The format of such a file is a list of SHA-1
fingerprint, one per line with optional colons between the bytes.
Empty lines and lines prefixed with a hash mark are ignored.
This option is useful as a quick workaround to exclude certain
certificates from the system store.
@item --hkp-cacert @var{file} @item --hkp-cacert @var{file}
Use the root certificates in @var{file} for verification of the TLS Use the root certificates in @var{file} for verification of the TLS
certificates used with @code{hkps} (keyserver access over TLS). If certificates used with @code{hkps} (keyserver access over TLS). If