From f5efbd5a1169ca7700f430a4a26ba086e603c887 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 16 Apr 2020 18:01:37 +0200 Subject: [PATCH] sm: Lookup missing issuers first using authorityInfoAccess. * sm/call-dirmngr.c (gpgsm_dirmngr_lookup): Add optional arg URL and adjust all callers. * sm/certchain.c (oidstr_caIssuers): New. (struct find_up_store_certs_s): Add additional fields. (find_up_store_certs_cb): Store the fingerprint. (find_up_via_auth_info_access): New. (find_up): Try the AIA URI first. -- Note that --auto-issuer-key-retrieve is required to use that. GnuPG-bug-id: 4898 Signed-off-by: Werner Koch --- sm/call-dirmngr.c | 52 +++++++++++----- sm/certchain.c | 149 +++++++++++++++++++++++++++++++++++++++++++--- sm/gpgsm.h | 3 +- sm/keylist.c | 2 +- 4 files changed, 180 insertions(+), 26 deletions(-) diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c index 4c764ea2c..d9083c9c9 100644 --- a/sm/call-dirmngr.c +++ b/sm/call-dirmngr.c @@ -757,20 +757,24 @@ lookup_status_cb (void *opaque, const char *line) /* Run the Directory Manager's lookup command using the pattern - compiled from the strings given in NAMES. The caller must provide - the callback CB which will be passed cert by cert. Note that CTRL - is optional. With CACHE_ONLY the dirmngr will search only its own - key cache. */ + compiled from the strings given in NAMES or from URI. The caller + must provide the callback CB which will be passed cert by cert. + Note that CTRL is optional. With CACHE_ONLY the dirmngr will + search only its own key cache. */ int -gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, int cache_only, +gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, const char *uri, + int cache_only, void (*cb)(void*, ksba_cert_t), void *cb_value) { int rc; - char *pattern; char line[ASSUAN_LINELENGTH]; struct lookup_parm_s parm; size_t len; assuan_context_t ctx; + const char *s; + + if ((names && uri) || (!names && !uri)) + return gpg_error (GPG_ERR_INV_ARG); /* The lookup function can be invoked from the callback of a lookup function, for example to walk the chain. */ @@ -793,19 +797,35 @@ gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, int cache_only, log_fatal ("both dirmngr contexts are in use\n"); } - pattern = pattern_from_strlist (names); - if (!pattern) + if (names) { - if (ctx == dirmngr_ctx) - release_dirmngr (ctrl); - else - release_dirmngr2 (ctrl); + char *pattern = pattern_from_strlist (names); + if (!pattern) + { + if (ctx == dirmngr_ctx) + release_dirmngr (ctrl); + else + release_dirmngr2 (ctrl); - return out_of_core (); + return out_of_core (); + } + snprintf (line, DIM(line), "LOOKUP%s %s", + cache_only? " --cache-only":"", pattern); + xfree (pattern); + } + else + { + for (s=uri; *s; s++) + if (*s <= ' ') + { + if (ctx == dirmngr_ctx) + release_dirmngr (ctrl); + else + release_dirmngr2 (ctrl); + return gpg_error (GPG_ERR_INV_URI); + } + snprintf (line, DIM(line), "LOOKUP --url %s", uri); } - snprintf (line, DIM(line), "LOOKUP%s %s", - cache_only? " --cache-only":"", pattern); - xfree (pattern); parm.ctrl = ctrl; parm.ctx = ctx; diff --git a/sm/certchain.c b/sm/certchain.c index c30be324e..2d2aec338 100644 --- a/sm/certchain.c +++ b/sm/certchain.c @@ -38,6 +38,10 @@ #include "../common/tlv.h" +/* The OID for the authorityInfoAccess's caIssuers. */ +static const char oidstr_caIssuers[] = "1.3.6.1.5.5.7.48.2"; + + /* Object to keep track of certain root certificates. */ struct marktrusted_info_s { @@ -574,6 +578,9 @@ struct find_up_store_certs_s { ctrl_t ctrl; int count; + unsigned int want_fpr:1; + unsigned int got_fpr:1; + unsigned char fpr[20]; }; static void @@ -583,6 +590,13 @@ find_up_store_certs_cb (void *cb_value, ksba_cert_t cert) if (keydb_store_cert (parm->ctrl, cert, 1, NULL)) log_error ("error storing issuer certificate as ephemeral\n"); + else if (parm->want_fpr && !parm->got_fpr) + { + if (!gpgsm_get_fingerprint (cert, 0, parm->fpr, NULL)) + log_error (_("failed to get the fingerprint\n")); + else + parm->got_fpr = 1; + } parm->count++; } @@ -603,6 +617,8 @@ find_up_external (ctrl_t ctrl, KEYDB_HANDLE kh, const char *s; find_up_store_certs_parm.ctrl = ctrl; + find_up_store_certs_parm.want_fpr = 0; + find_up_store_certs_parm.got_fpr = 0; find_up_store_certs_parm.count = 0; if (opt.verbose) @@ -621,7 +637,7 @@ find_up_external (ctrl_t ctrl, KEYDB_HANDLE kh, add_to_strlist (&names, pattern); xfree (pattern); - rc = gpgsm_dirmngr_lookup (ctrl, names, 0, find_up_store_certs_cb, + rc = gpgsm_dirmngr_lookup (ctrl, names, NULL, 0, find_up_store_certs_cb, &find_up_store_certs_parm); free_strlist (names); @@ -654,6 +670,105 @@ find_up_external (ctrl_t ctrl, KEYDB_HANDLE kh, } +/* Helper for find_up(). Locate the certificate for CERT using the + * caIssuer from the authorityInfoAccess. KH is the keydb context we + * are currently using. On success 0 is returned and the certificate + * may be retrieved from the keydb using keydb_get_cert(). If no + * suitable authorityInfoAccess is encoded in the certificate + * GPG_ERR_NOT_FOUND is returned. */ +static gpg_error_t +find_up_via_auth_info_access (ctrl_t ctrl, KEYDB_HANDLE kh, ksba_cert_t cert) +{ + gpg_error_t err; + struct find_up_store_certs_s find_up_store_certs_parm; + char *url, *ldapurl; + int idx, i; + char *oid; + ksba_name_t name; + + find_up_store_certs_parm.ctrl = ctrl; + find_up_store_certs_parm.want_fpr = 1; + find_up_store_certs_parm.got_fpr = 0; + find_up_store_certs_parm.count = 0; + + /* Find suitable URLs; if there is a http scheme we prefer that. */ + url = ldapurl = NULL; + for (idx=0; + !url && !(err = ksba_cert_get_authority_info_access (cert, idx, + &oid, &name)); + idx++) + { + if (!strcmp (oid, oidstr_caIssuers)) + { + for (i=0; !url && ksba_name_enum (name, i); i++) + { + char *p = ksba_name_get_uri (name, i); + if (p) + { + if (!strncmp (p, "http:", 5) || !strncmp (p, "https:", 6)) + url = p; + else if (ldapurl) + xfree (p); /* We already got one. */ + else if (!strncmp (p, "ldap:",5) || !strncmp (p, "ldaps:",6)) + ldapurl = p; + } + else + xfree (p); + } + } + ksba_name_release (name); + ksba_free (oid); + } + if (err && gpg_err_code (err) != GPG_ERR_EOF) + { + log_error (_("can't get authorityInfoAccess: %s\n"), gpg_strerror (err)); + return err; + } + if (!url && ldapurl) + { + /* No HTTP scheme; fallback to LDAP if available. */ + url = ldapurl; + ldapurl = NULL; + } + xfree (ldapurl); + if (!url) + return gpg_error (GPG_ERR_NOT_FOUND); + + if (opt.verbose) + log_info ("looking up issuer via authorityInfoAccess.caIssuers\n"); + + err = gpgsm_dirmngr_lookup (ctrl, NULL, url, 0, find_up_store_certs_cb, + &find_up_store_certs_parm); + + /* Although we might receive several certificates we use only the + * first one. Or more exacty the first one for which we retrieved + * the fingerprint. */ + if (opt.verbose) + log_info ("number of caIssuers found: %d\n", + find_up_store_certs_parm.count); + if (err) + { + log_error ("external URL lookup failed: %s\n", gpg_strerror (err)); + err = gpg_error (GPG_ERR_NOT_FOUND); + } + else if (!find_up_store_certs_parm.got_fpr) + err = gpg_error (GPG_ERR_NOT_FOUND); + else + { + int old; + /* The retrieved certificates are currently stored in the + * ephemeral key DB, so we temporary switch to ephemeral + * mode. */ + old = keydb_set_ephemeral (kh, 1); + keydb_search_reset (kh); + err = keydb_search_fpr (ctrl, kh, find_up_store_certs_parm.fpr); + keydb_set_ephemeral (kh, old); + } + + return err; +} + + /* Helper for find_up(). Ask the dirmngr for the certificate for ISSUER with optional SERIALNO. KH is the keydb context we are currently using. With SUBJECT_MODE set, ISSUER is searched as the @@ -694,7 +809,7 @@ find_up_dirmngr (ctrl_t ctrl, KEYDB_HANDLE kh, add_to_strlist (&names, pattern); xfree (pattern); - rc = gpgsm_dirmngr_lookup (ctrl, names, 1, find_up_store_certs_cb, + rc = gpgsm_dirmngr_lookup (ctrl, names, NULL, 1, find_up_store_certs_cb, &find_up_store_certs_parm); free_strlist (names); @@ -816,9 +931,18 @@ find_up (ctrl_t ctrl, KEYDB_HANDLE kh, /* If we still didn't found it, try an external lookup. */ if (rc == -1 && opt.auto_issuer_key_retrieve && !find_next) { - rc = find_up_external (ctrl, kh, issuer, keyid); - if (!rc && DBG_X509) - log_debug (" found via authid and external lookup\n"); + if (!find_up_via_auth_info_access (ctrl, kh, cert)) + { + if (DBG_X509) + log_debug (" found via authorityInfoAccess.caIssuers\n"); + rc = 0; + } + else + { + rc = find_up_external (ctrl, kh, issuer, keyid); + if (!rc && DBG_X509) + log_debug (" found via authid and external lookup\n"); + } } @@ -879,9 +1003,18 @@ find_up (ctrl_t ctrl, KEYDB_HANDLE kh, /* Still not found. If enabled, try an external lookup. */ if (rc == -1 && opt.auto_issuer_key_retrieve && !find_next) { - rc = find_up_external (ctrl, kh, issuer, NULL); - if (!rc && DBG_X509) - log_debug (" found via issuer and external lookup\n"); + if (!find_up_via_auth_info_access (ctrl, kh, cert)) + { + if (DBG_X509) + log_debug (" found via authorityInfoAccess.caIssuers\n"); + rc = 0; + } + else + { + rc = find_up_external (ctrl, kh, issuer, NULL); + if (!rc && DBG_X509) + log_debug (" found via issuer and external lookup\n"); + } } return rc; diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 6c68cdab3..60b20cc22 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -447,7 +447,8 @@ gpg_error_t gpgsm_agent_export_key (ctrl_t ctrl, const char *keygrip, int gpgsm_dirmngr_isvalid (ctrl_t ctrl, ksba_cert_t cert, ksba_cert_t issuer_cert, int use_ocsp); -int gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, int cache_only, +int gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, const char *uri, + int cache_only, void (*cb)(void*, ksba_cert_t), void *cb_value); int gpgsm_dirmngr_run_command (ctrl_t ctrl, const char *command, int argc, char **argv); diff --git a/sm/keylist.c b/sm/keylist.c index 4eecb8720..1fd2892ce 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -1648,7 +1648,7 @@ list_external_keys (ctrl_t ctrl, strlist_t names, estream_t fp, int raw_mode) parm.with_chain = ctrl->with_chain; parm.raw_mode = raw_mode; - rc = gpgsm_dirmngr_lookup (ctrl, names, 0, list_external_cb, &parm); + rc = gpgsm_dirmngr_lookup (ctrl, names, NULL, 0, list_external_cb, &parm); if (gpg_err_code (rc) == GPG_ERR_EOF || rc == -1 || gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; /* "Not found" is not an error here. */