From 2af217ecd7e4242be2b35bc0085eccaf13cc2027 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 21 Apr 2021 18:32:21 +0200 Subject: [PATCH] gpg: Allow fingerprint based lookup with --locate-external-key. * g10/keyserver.c (keyserver_import_fprint_ntds): New. * g10/getkey.c (get_pubkey_byname): Detect an attempt to search by fingerprint in no_local mode. -- See the man page. For testing use gpg --auto-key-locate local,wkd,keyserver --locate-external-key \ FINGERPRINT with at least one LDAP keyserver given in dirmngr.conf. On Windows "ntds" may be used instead or in addtion to "keyserver". Signed-off-by: Werner Koch (cherry picked from commit ec36eca08cdbf6653e7362e8e0e6c5f2c75b4a60) --- doc/gpg.texi | 29 +++++-- g10/call-dirmngr.c | 2 +- g10/getkey.c | 182 +++++++++++++++++++++++++++++++-------- g10/gpgv.c | 10 +++ g10/keyserver-internal.h | 2 + g10/keyserver.c | 22 +++-- g10/test-stubs.c | 10 +++ 7 files changed, 204 insertions(+), 53 deletions(-) diff --git a/doc/gpg.texi b/doc/gpg.texi index 9b6303540..0c53bc1d4 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -353,13 +353,18 @@ numbers 1-9 or "T" for 10 and above to indicate trust signature levels @opindex locate-keys @opindex locate-external-keys Locate the keys given as arguments. This command basically uses the -same algorithm as used when locating keys for encryption or signing -and may thus be used to see what keys @command{@gpgname} might use. -In particular external methods as defined by -@option{--auto-key-locate} may be used to locate a key. Only public -keys are listed. The variant @option{--locate-external-keys} does not -consider a locally existing key and can thus be used to force the -refresh of a key via the defined external methods. +same algorithm as used when locating keys for encryption and may thus +be used to see what keys @command{@gpgname} might use. In particular +external methods as defined by @option{--auto-key-locate} are used to +locate a key if the arguments comain valid mail addresses. Only +public keys are listed. + +The variant @option{--locate-external-keys} does not consider a +locally existing key and can thus be used to force the refresh of a +key via the defined external methods. If a fingerprint is given and +and the methods defined by --auto-key-locate define LDAP servers, the +key is fetched from these resources; defined non-LDAP keyservers are +skipped. @item --show-keys @opindex show-keys @@ -1811,14 +1816,20 @@ list. The default is "local,wkd". PGP Universal method of checking @samp{ldap://keys.(thedomain)}. @item ntds - Locate the key using the Active Directory (Windows only). + Locate the key using the Active Directory (Windows only). This + method also allows to search by fingerprint using the command + @option{--locate-external-key}. @item keyserver - Locate a key using a keyserver. + Locate a key using a keyserver. This method also allows to search + by fingerprint using the command @option{--locate-external-key} if + any of the configured keyservers is an LDAP server. @item keyserver-URL In addition, a keyserver URL as used in the @command{dirmngr} configuration may be used here to query that particular keyserver. + This method also allows to search by fingerprint using the command + @option{--locate-external-key} if the URL specifies an LDAP server. @item local Locate the key using the local keyrings. This mechanism allows the user to diff --git a/g10/call-dirmngr.c b/g10/call-dirmngr.c index 72afad8bc..f5fc8fcde 100644 --- a/g10/call-dirmngr.c +++ b/g10/call-dirmngr.c @@ -710,7 +710,7 @@ gpg_dirmngr_ks_get (ctrl_t ctrl, char **pattern, /* If we have an override keyserver we first indicate that the next user of the context needs to again setup the global keyservers and - them we send the override keyserver. */ + then we send the override keyserver. */ if (override_keyserver) { clear_context_flags (ctrl, ctx); diff --git a/g10/getkey.c b/g10/getkey.c index 27ad007c9..8dd585ea9 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -1020,10 +1020,12 @@ get_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, int rc; strlist_t namelist = NULL; struct akl *akl; - int is_mbox; + int is_mbox, is_fpr; + KEYDB_SEARCH_DESC fprbuf; int nodefault = 0; int anylocalfirst = 0; int mechanism_type = AKL_NODEFAULT; + size_t fprbuf_fprlen = 0; /* If RETCTX is not NULL, then RET_KDBHD must be NULL. */ log_assert (retctx == NULL || ret_kdbhd == NULL); @@ -1045,6 +1047,30 @@ get_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, is_mbox = 1; } + /* If we are called due to --locate-external-key Check whether NAME + * is a fingerprint and then try to lookup that key by configured + * method which support lookup by fingerprint. FPRBUF carries the + * parsed fingerpint iff IS_FPR is true. */ + is_fpr = 0; + if (!is_mbox && mode == GET_PUBKEY_NO_LOCAL) + { + if (!classify_user_id (name, &fprbuf, 1) + && (fprbuf.mode == KEYDB_SEARCH_MODE_FPR16 + || fprbuf.mode == KEYDB_SEARCH_MODE_FPR20 + || fprbuf.mode == KEYDB_SEARCH_MODE_FPR)) + { + /* Note: We should get rid of the FPR16 because we don't + * support v3 keys anymore. However, in 2.3 the fingerprint + * code has already been reworked and thus it is + * questionable whether we should really tackle this here. */ + if (fprbuf.mode == KEYDB_SEARCH_MODE_FPR16) + fprbuf_fprlen = 16; + else + fprbuf_fprlen = 20; + is_fpr = 1; + } + } + /* The auto-key-locate feature works as follows: there are a number * of methods to look up keys. By default, the local keyring is * tried first. Then, each method listed in the --auto-key-locate is @@ -1122,7 +1148,7 @@ get_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, retrieval has been enabled, we try to import the key. */ if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY && mode != GET_PUBKEY_NO_AKL - && is_mbox) + && (is_mbox || is_fpr)) { /* NAME wasn't present in the local keyring (or we didn't try * the local keyring). Since the auto key locate feature is @@ -1148,6 +1174,8 @@ get_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, case AKL_LOCAL: if (mode == GET_PUBKEY_NO_LOCAL) { + /* Note that we get here in is_fpr more, so there is + * no extra check for it required. */ mechanism_string = ""; rc = GPG_ERR_NO_PUBKEY; } @@ -1168,44 +1196,88 @@ get_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, break; case AKL_CERT: - mechanism_string = "DNS CERT"; - glo_ctrl.in_auto_key_retrieve++; - rc = keyserver_import_cert (ctrl, name, 0, &fpr, &fpr_len); - glo_ctrl.in_auto_key_retrieve--; - break; + if (is_fpr) + { + mechanism_string = ""; + rc = GPG_ERR_NO_PUBKEY; + } + else + { + mechanism_string = "DNS CERT"; + glo_ctrl.in_auto_key_retrieve++; + rc = keyserver_import_cert (ctrl, name, 0, &fpr, &fpr_len); + glo_ctrl.in_auto_key_retrieve--; + } + break; case AKL_PKA: - mechanism_string = "PKA"; - glo_ctrl.in_auto_key_retrieve++; - rc = keyserver_import_pka (ctrl, name, &fpr, &fpr_len); - glo_ctrl.in_auto_key_retrieve--; - break; + if (is_fpr) + { + mechanism_string = ""; + rc = GPG_ERR_NO_PUBKEY; + } + else + { + mechanism_string = "PKA"; + glo_ctrl.in_auto_key_retrieve++; + rc = keyserver_import_pka (ctrl, name, &fpr, &fpr_len); + glo_ctrl.in_auto_key_retrieve--; + } + break; case AKL_DANE: - mechanism_string = "DANE"; - glo_ctrl.in_auto_key_retrieve++; - rc = keyserver_import_cert (ctrl, name, 1, &fpr, &fpr_len); - glo_ctrl.in_auto_key_retrieve--; + if (is_fpr) + { + mechanism_string = ""; + rc = GPG_ERR_NO_PUBKEY; + } + else + { + mechanism_string = "DANE"; + glo_ctrl.in_auto_key_retrieve++; + rc = keyserver_import_cert (ctrl, name, 1, &fpr, &fpr_len); + glo_ctrl.in_auto_key_retrieve--; + } break; case AKL_WKD: - mechanism_string = "WKD"; - glo_ctrl.in_auto_key_retrieve++; - rc = keyserver_import_wkd (ctrl, name, 0, &fpr, &fpr_len); - glo_ctrl.in_auto_key_retrieve--; + if (is_fpr) + { + mechanism_string = ""; + rc = GPG_ERR_NO_PUBKEY; + } + else + { + mechanism_string = "WKD"; + glo_ctrl.in_auto_key_retrieve++; + rc = keyserver_import_wkd (ctrl, name, 0, &fpr, &fpr_len); + glo_ctrl.in_auto_key_retrieve--; + } break; case AKL_LDAP: - mechanism_string = "LDAP"; - glo_ctrl.in_auto_key_retrieve++; - rc = keyserver_import_ldap (ctrl, name, &fpr, &fpr_len); - glo_ctrl.in_auto_key_retrieve--; - break; + if (is_fpr) + { + mechanism_string = ""; + rc = GPG_ERR_NO_PUBKEY; + } + else + { + mechanism_string = "LDAP"; + glo_ctrl.in_auto_key_retrieve++; + rc = keyserver_import_ldap (ctrl, name, &fpr, &fpr_len); + glo_ctrl.in_auto_key_retrieve--; + } + break; case AKL_NTDS: mechanism_string = "NTDS"; glo_ctrl.in_auto_key_retrieve++; - rc = keyserver_import_ntds (ctrl, name, &fpr, &fpr_len); + if (is_fpr) + rc = keyserver_import_fprint_ntds (ctrl, + fprbuf.u.fpr, fprbuf_fprlen); + else + rc = keyserver_import_ntds (ctrl, name, &fpr, &fpr_len); glo_ctrl.in_auto_key_retrieve--; break; @@ -1218,8 +1290,25 @@ get_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, { mechanism_string = "keyserver"; glo_ctrl.in_auto_key_retrieve++; - rc = keyserver_import_name (ctrl, name, &fpr, &fpr_len, - opt.keyserver); + if (is_fpr) + { + rc = keyserver_import_fprint (ctrl, + fprbuf.u.fpr, fprbuf_fprlen, + opt.keyserver, + KEYSERVER_IMPORT_FLAG_LDAP); + /* Map error codes because Dirmngr returns NO + * DATA if the keyserver does not have the + * requested key. It returns NO KEYSERVER if no + * LDAP keyservers are configured. */ + if (gpg_err_code (rc) == GPG_ERR_NO_DATA + || gpg_err_code (rc) == GPG_ERR_NO_KEYSERVER) + rc = gpg_error (GPG_ERR_NO_PUBKEY); + } + else + { + rc = keyserver_import_name (ctrl, name, &fpr, &fpr_len, + opt.keyserver); + } glo_ctrl.in_auto_key_retrieve--; } else @@ -1236,8 +1325,21 @@ get_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, mechanism_string = akl->spec->uri; keyserver = keyserver_match (akl->spec); glo_ctrl.in_auto_key_retrieve++; - rc = keyserver_import_name (ctrl, - name, &fpr, &fpr_len, keyserver); + if (is_fpr) + { + rc = keyserver_import_fprint (ctrl, + fprbuf.u.fpr, fprbuf_fprlen, + opt.keyserver, + KEYSERVER_IMPORT_FLAG_LDAP); + if (gpg_err_code (rc) == GPG_ERR_NO_DATA + || gpg_err_code (rc) == GPG_ERR_NO_KEYSERVER) + rc = gpg_error (GPG_ERR_NO_PUBKEY); + } + else + { + rc = keyserver_import_name (ctrl, name, + &fpr, &fpr_len, keyserver); + } glo_ctrl.in_auto_key_retrieve--; } break; @@ -1250,21 +1352,27 @@ get_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, * requirement as the URL might point to a key put in by an * attacker. By forcing the use of the fingerprint, we * won't use the attacker's key here. */ - if (!rc && fpr) + if (!rc && (fpr || is_fpr)) { char fpr_string[MAX_FINGERPRINT_LEN * 2 + 1]; - log_assert (fpr_len <= MAX_FINGERPRINT_LEN); - - free_strlist (namelist); - namelist = NULL; - - bin2hex (fpr, fpr_len, fpr_string); + if (is_fpr) + { + log_assert (fprbuf_fprlen <= MAX_FINGERPRINT_LEN); + bin2hex (fprbuf.u.fpr, fprbuf_fprlen, fpr_string); + } + else + { + log_assert (fpr_len <= MAX_FINGERPRINT_LEN); + bin2hex (fpr, fpr_len, fpr_string); + } if (opt.verbose) log_info ("auto-key-locate found fingerprint %s\n", fpr_string); + free_strlist (namelist); + namelist = NULL; add_to_strlist (&namelist, fpr_string); } else if (!rc && !fpr && !did_akl_local) diff --git a/g10/gpgv.c b/g10/gpgv.c index a788c67c4..3131ba904 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -440,6 +440,16 @@ keyserver_import_fprint (ctrl_t ctrl, const byte *fprint,size_t fprint_len, return -1; } +int +keyserver_import_fprint_ntds (ctrl_t ctrl, + const byte *fprint, size_t fprint_len) +{ + (void)ctrl; + (void)fprint; + (void)fprint_len; + return -1; +} + int keyserver_import_cert (const char *name) { diff --git a/g10/keyserver-internal.h b/g10/keyserver-internal.h index 4439468c0..d1fb682a2 100644 --- a/g10/keyserver-internal.h +++ b/g10/keyserver-internal.h @@ -41,6 +41,8 @@ int keyserver_import (ctrl_t ctrl, strlist_t users); int keyserver_import_fprint (ctrl_t ctrl, const byte *fprint,size_t fprint_len, struct keyserver_spec *keyserver, unsigned int flags); +int keyserver_import_fprint_ntds (ctrl_t ctrl, + const byte *fprint, size_t fprint_len); int keyserver_import_keyid (ctrl_t ctrl, u32 *keyid, struct keyserver_spec *keyserver, unsigned int flags); diff --git a/g10/keyserver.c b/g10/keyserver.c index 7bbfe97ec..c5e70601d 100644 --- a/g10/keyserver.c +++ b/g10/keyserver.c @@ -1175,28 +1175,38 @@ keyserver_import_ntds (ctrl_t ctrl, const char *mbox, int -keyserver_import_fprint (ctrl_t ctrl, const byte *fprint,size_t fprint_len, +keyserver_import_fprint (ctrl_t ctrl, const byte *fprint, size_t fprint_len, struct keyserver_spec *keyserver, unsigned int flags) { KEYDB_SEARCH_DESC desc; - memset(&desc,0,sizeof(desc)); + memset (&desc, 0, sizeof(desc)); if(fprint_len==16) desc.mode=KEYDB_SEARCH_MODE_FPR16; else if(fprint_len==20) desc.mode=KEYDB_SEARCH_MODE_FPR20; else - return -1; + return gpg_error (GPG_ERR_INV_ARG); - memcpy(desc.u.fpr,fprint,fprint_len); + memcpy (desc.u.fpr, fprint, fprint_len); - /* TODO: Warn here if the fingerprint we got doesn't match the one - we asked for? */ return keyserver_get (ctrl, &desc, 1, keyserver, flags, NULL, NULL); } + +int +keyserver_import_fprint_ntds (ctrl_t ctrl, + const byte *fprint, size_t fprint_len) +{ + struct keyserver_spec keyserver = { NULL, "ldap:///" }; + + return keyserver_import_fprint (ctrl, fprint, fprint_len, + &keyserver, KEYSERVER_IMPORT_FLAG_LDAP); +} + + int keyserver_import_keyid (ctrl_t ctrl, u32 *keyid,struct keyserver_spec *keyserver, diff --git a/g10/test-stubs.c b/g10/test-stubs.c index 9125d6a64..c6fa9c024 100644 --- a/g10/test-stubs.c +++ b/g10/test-stubs.c @@ -198,6 +198,16 @@ keyserver_import_fprint (ctrl_t ctrl, const byte *fprint,size_t fprint_len, return -1; } +int +keyserver_import_fprint_ntds (ctrl_t ctrl, + const byte *fprint, size_t fprint_len) +{ + (void)ctrl; + (void)fprint; + (void)fprint_len; + return -1; +} + int keyserver_import_cert (const char *name) {