summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2020-04-16 18:01:37 +0200
committerWerner Koch <wk@gnupg.org>2020-04-16 18:03:13 +0200
commitf5efbd5a1169ca7700f430a4a26ba086e603c887 (patch)
tree285f9b675e1757267dc99cdd58c46d4fc947f88d
parentdirmngr: Allow http URLs with "LOOKUP --url" (diff)
downloadgnupg2-f5efbd5a1169ca7700f430a4a26ba086e603c887.tar.xz
gnupg2-f5efbd5a1169ca7700f430a4a26ba086e603c887.zip
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>
-rw-r--r--sm/call-dirmngr.c52
-rw-r--r--sm/certchain.c149
-rw-r--r--sm/gpgsm.h3
-rw-r--r--sm/keylist.c2
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. */