diff --git a/common/convert.c b/common/convert.c index 1efaccedf..d9b4353b7 100644 --- a/common/convert.c +++ b/common/convert.c @@ -43,7 +43,8 @@ LENGTH bytes. The function checks that the STRING will convert exactly to LENGTH bytes. The string is delimited by either end of 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 hex2bin (const char *string, void *buffer, size_t length) { diff --git a/dirmngr/certcache.c b/dirmngr/certcache.c index a52801b38..7f29ec859 100644 --- a/dirmngr/certcache.c +++ b/dirmngr/certcache.c @@ -262,13 +262,14 @@ clean_cache_slot (cert_item_t ci) * fingerprint of the certificate will be stored there. FPR_BUFFER * needs to point to a buffer of at least 20 bytes. The fingerprint * 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 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; + fingerprint_list_t ignored; 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); + /* 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) if (ci->cert && !memcmp (ci->fpr, fpr, 20)) 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); } } + else if (gpg_err_code (err) == GPG_ERR_NOT_ENABLED) + log_info ("certificate '%s' skipped due to configuration\n", fname); else log_error (_("error loading certificate '%s': %s\n"), 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); if (gpg_err_code (err) == GPG_ERR_DUP_VALUE) 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) log_error (_("error loading certificate '%s': %s\n"), fname, gpg_strerror (err)); @@ -625,6 +638,9 @@ load_certs_from_w32_store (const char *storename) if (DBG_X509) 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) log_error (_("error loading certificate '%s': %s\n"), storename, gpg_strerror (err)); @@ -852,6 +868,8 @@ cache_cert (ksba_cert_t cert) log_info (_("certificate already cached\n")); else if (!err) log_info (_("certificate cached\n")); + else if (gpg_err_code (err) == GPG_ERR_NOT_ENABLED) + log_info ("certificate skipped due to configuration\n"); else log_error (_("error caching certificate: %s\n"), gpg_strerror (err)); return err; @@ -872,7 +890,10 @@ cache_cert_silent (ksba_cert_t cert, void *fpr_buffer) release_cache_lock (); if (gpg_err_code (err) == GPG_ERR_DUP_VALUE) 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)); return err; } diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c index 36ef873c2..51a586e20 100644 --- a/dirmngr/dirmngr.c +++ b/dirmngr/dirmngr.c @@ -143,6 +143,7 @@ enum cmd_and_opt_values { oSocketName, oLDAPWrapperProgram, oHTTPWrapperProgram, + oIgnoreCert, oIgnoreCertExtension, oUseTor, oNoUseTor, @@ -216,6 +217,7 @@ static gpgrt_opt_t opts[] = { N_("|N|do not return more than N items in one query")), ARGPARSE_s_u (oFakedSystemTime, "faked-system-time", "@"), /*(epoch time)*/ ARGPARSE_s_n (oDisableCheckOwnSocket, "disable-check-own-socket", "@"), + ARGPARSE_s_s (oIgnoreCert,"ignore-cert", "@"), ARGPARSE_s_s (oIgnoreCertExtension,"ignore-cert-extension", "@"), @@ -419,7 +421,9 @@ static void cleanup (void); #if USE_LDAP static ldap_server_t parse_ldapserver_file (const char* filename, int ienoent); #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 handle_connections (assuan_fd_t listen_fd); static void gpgconf_versions (void); @@ -667,6 +671,12 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread) xfree (opt.ocsp_signer); 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); http_register_tls_ca (NULL); 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 oOCSPResponder: opt.ocsp_responder = pargs->r.ret_str; break; 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; case oOCSPMaxClockSkew: opt.ocsp_max_clock_skew = 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; + 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: add_to_strlist (&opt.ignored_cert_extensions, pargs->r.ret_str); break; @@ -1709,8 +1738,13 @@ parse_ldapserver_file (const char* filename, int ignore_enoent) } #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 -parse_ocsp_signer (const char *string) +parse_fingerprint_item (const char *string, + const char *optionname, int want_binary) { gpg_error_t err; char *fname; @@ -1735,10 +1769,15 @@ parse_ocsp_signer (const char *string) if (j != 40 || !(spacep (string+i) || !string[i])) { log_error (_("%s:%u: invalid fingerprint detected\n"), - "--ocsp-signer", 0); + optionname, 0); xfree (item); return NULL; } + if (want_binary) + { + item->binlen = 20; + hex2bin (item->hexfpr, item->hexfpr, 20); + } return item; } @@ -1821,6 +1860,12 @@ parse_ocsp_signer (const char *string) log_error (_("%s:%u: invalid fingerprint detected\n"), fname, lnr); errflag = 1; } + else if (want_binary) + { + item->binlen = 20; + hex2bin (item->hexfpr, item->hexfpr, 20); + } + i++; while (spacep (p+i)) i++; diff --git a/dirmngr/dirmngr.h b/dirmngr/dirmngr.h index 498a3d7b1..464aeb76e 100644 --- a/dirmngr/dirmngr.h +++ b/dirmngr/dirmngr.h @@ -74,6 +74,7 @@ typedef struct fingerprint_list_s *fingerprint_list_t; struct fingerprint_list_s { fingerprint_list_t next; + char binlen; /* If this is not 0 hexfpr actually carries a binary fpr. */ char hexfpr[20+20+1]; }; @@ -119,6 +120,10 @@ struct int ignore_ocsp_service_url; /* Ignore OCSP service URLs as given in 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 one can claim that a critical extension has been handled. One OID per string. */ diff --git a/doc/dirmngr.texi b/doc/dirmngr.texi index 1638d7d84..bc6f0ba39 100644 --- a/doc/dirmngr.texi +++ b/doc/dirmngr.texi @@ -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 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} Use the root certificates in @var{file} for verification of the TLS certificates used with @code{hkps} (keyserver access over TLS). If