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 <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-04-16 18:01:37 +02:00
parent 7f1be1ea52
commit f5efbd5a11
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
4 changed files with 180 additions and 26 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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. */