From ff71521d9698c7c5df94831a1398e948213af433 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 13 May 2016 16:24:59 +0200 Subject: [PATCH] gpg: Emit new status line KEY_CONSIDERED. * common/status.h (STATUS_KEY_CONSIDERED): New. * g10/getkey.c: Include status.h. (LOOKUP_NOT_SELECTED, LOOKUP_ALL_SUBKEYS_EXPIRED): New. (finish_lookup): Add arg R_FLAGS. Count expired and revoked keys and set flag. Check a requested usage before checking for expiraion or revocation. (print_status_key_considered): New. (lookup): Print new status. Signed-off-by: Werner Koch --- common/status.h | 1 + doc/DETAILS | 13 ++- g10/getkey.c | 220 ++++++++++++++++++++++++++++++------------------ 3 files changed, 151 insertions(+), 83 deletions(-) diff --git a/common/status.h b/common/status.h index 730a75cfc..966489fa3 100644 --- a/common/status.h +++ b/common/status.h @@ -105,6 +105,7 @@ enum STATUS_INV_SGNR, STATUS_NO_RECP, STATUS_NO_SGNR, + STATUS_KEY_CONSIDERED, STATUS_ALREADY_SIGNED, STATUS_KEYEXPIRED, diff --git a/doc/DETAILS b/doc/DETAILS index 5ceab68e4..271000749 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -548,7 +548,7 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB: that case, this status tag does not appear. *** ATTRIBUTE - The list or argemnts are: + The list or arguments are: - - - @@ -602,18 +602,29 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB: - 13 :: Key disabled - 14 :: Syntax error in specification + If no specific reason was given a previously emitted status code + KEY_CONSIDERED may be used to analyzed the problem. + Note that for historical reasons the INV_RECP status is also used for gpgsm's SIGNER command where it relates to signer's of course. Newer GnuPG versions are using INV_SGNR; applications should ignore the INV_RECP during the sender's command processing once they have seen an INV_SGNR. Different codes are used so that they can be distinguish while doing an encrypt+sign operation. + *** NO_RECP Issued if no recipients are usable. *** NO_SGNR Issued if no senders are usable. +*** KEY_CONSIDERED + Issued to explian the lookup of a key. FPR is the hexified + fingerprint of the primary key. The bit values for FLAGS are: + + - 1 :: The key has not been selected. + - 2 :: All subkeys of the key are expired or have been revoked. + *** KEYEXPIRED The key has expired. expire-timestamp is the expiration time in seconds since Epoch. This status line is not very useful because diff --git a/g10/getkey.c b/g10/getkey.c index 907007b3e..ad0148e51 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -38,6 +38,7 @@ #include "call-agent.h" #include "host2net.h" #include "mbox-util.h" +#include "status.h" #define MAX_PK_CACHE_ENTRIES PK_UID_CACHE_SIZE #define MAX_UID_CACHE_ENTRIES PK_UID_CACHE_SIZE @@ -46,6 +47,13 @@ #error We need the cache for key creation #endif +/* Flags values returned by the lookup code. Note that the values are + * directly used by the KEY_CONSIDERED status line. */ +#define LOOKUP_NOT_SELECTED (1<<0) +#define LOOKUP_ALL_SUBKEYS_EXPIRED (1<<1) /* or revoked */ + + +/* A context object used by the lookup functions. */ struct getkey_ctx_s { /* Part of the search criteria: whether the search is an exact @@ -3045,51 +3053,54 @@ merge_selfsigs (KBNODE keyblock) /* See whether the key satisfies any additional requirements specified - in CTX. If so, return 1 and set CTX->FOUND_KEY to an appropriate - key or subkey. Otherwise, return 0 if there was no appropriate - key. - - In case the primary key is not required, select a suitable subkey. - We need the primary key if PUBKEY_USAGE_CERT is set in - CTX->REQ_USAGE or we are in PGP6 or PGP7 mode and PUBKEY_USAGE_SIG - is set in CTX->REQ_USAGE. - - If any of PUBKEY_USAGE_SIG, PUBKEY_USAGE_ENC and PUBKEY_USAGE_CERT - are set in CTX->REQ_USAGE, we filter by the key's function. - Concretely, if PUBKEY_USAGE_SIG and PUBKEY_USAGE_CERT are set, then - we only return a key if it is (at least) either a signing or a - certification key. - - If CTX->REQ_USAGE is set, then we reject any keys that are not good - (i.e., valid, not revoked, not expired, etc.). This allows the - getkey functions to be used for plain key listings. - - Sets the matched key's user id field (pk->user_id) to the user id - that matched the low-level search criteria or NULL. - - - This function needs to handle several different cases: - - 1. No requested usage and no primary key requested - Examples for this case are that we have a keyID to be used - for decrytion or verification. - 2. No usage but primary key requested - This is the case for all functions which work on an - entire keyblock, e.g. for editing or listing - 3. Usage and primary key requested - FXME - 4. Usage but no primary key requested - FIXME - + * in CTX. If so, return 1 and set CTX->FOUND_KEY to an appropriate + * key or subkey. Otherwise, return 0 if there was no appropriate + * key. + * + * In case the primary key is not required, select a suitable subkey. + * We need the primary key if PUBKEY_USAGE_CERT is set in + * CTX->REQ_USAGE or we are in PGP6 or PGP7 mode and PUBKEY_USAGE_SIG + * is set in CTX->REQ_USAGE. + * + * If any of PUBKEY_USAGE_SIG, PUBKEY_USAGE_ENC and PUBKEY_USAGE_CERT + * are set in CTX->REQ_USAGE, we filter by the key's function. + * Concretely, if PUBKEY_USAGE_SIG and PUBKEY_USAGE_CERT are set, then + * we only return a key if it is (at least) either a signing or a + * certification key. + * + * If CTX->REQ_USAGE is set, then we reject any keys that are not good + * (i.e., valid, not revoked, not expired, etc.). This allows the + * getkey functions to be used for plain key listings. + * + * Sets the matched key's user id field (pk->user_id) to the user id + * that matched the low-level search criteria or NULL. If R_FLAGS is + * not NULL set certain flags for more detailed error reporting. Used + * flags are: + * - LOOKUP_ALL_SUBKEYS_EXPIRED :: All Subkeys are expired or have + * been revoked. + * + * This function needs to handle several different cases: + * + * 1. No requested usage and no primary key requested + * Examples for this case are that we have a keyID to be used + * for decrytion or verification. + * 2. No usage but primary key requested + * This is the case for all functions which work on an + * entire keyblock, e.g. for editing or listing + * 3. Usage and primary key requested + * FIXME + * 4. Usage but no primary key requested + * FIXME + * */ -static KBNODE -finish_lookup (GETKEY_CTX ctx, KBNODE keyblock) +static kbnode_t +finish_lookup (getkey_ctx_t ctx, kbnode_t keyblock, unsigned int *r_flags) { - KBNODE k; + kbnode_t k; /* If CTX->EXACT is set, the key or subkey that actually matched the low-level search criteria. */ - KBNODE foundk = NULL; + kbnode_t foundk = NULL; /* The user id (if any) that matched the low-level search criteria. */ PKT_user_id *foundu = NULL; @@ -3100,21 +3111,23 @@ finish_lookup (GETKEY_CTX ctx, KBNODE keyblock) if signing data while --pgp6 or --pgp7 is on since pgp 6 and 7 do not understand signatures made by a signing subkey. PGP 8 does. */ - int req_prim = (ctx->req_usage & PUBKEY_USAGE_CERT) || - ((PGP6 || PGP7) && (ctx->req_usage & PUBKEY_USAGE_SIG)); + int req_prim = ((ctx->req_usage & PUBKEY_USAGE_CERT) + || ((PGP6 || PGP7) && (ctx->req_usage & PUBKEY_USAGE_SIG))); u32 curtime = make_timestamp (); u32 latest_date; - KBNODE latest_key; + kbnode_t latest_key; PKT_public_key *pk; - log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); + if (r_flags) + *r_flags = 0; + + /* For an exact match mark the primary or subkey that matched the + low-level search criteria. */ if (ctx->exact) - /* Get the key or subkey that matched the low-level search - criteria. */ { for (k = keyblock; k; k = k->next) { @@ -3154,24 +3167,28 @@ finish_lookup (GETKEY_CTX ctx, KBNODE keyblock) latest_date = 0; latest_key = NULL; - /* Set latest_key to the latest (the one with the most recent - timestamp) good (valid, not revoked, not expired, etc.) subkey. - - Don't bother if we are only looking for a primary key or we need - an exact match and the exact match is not a subkey. */ + /* Set LATEST_KEY to the latest (the one with the most recent + * timestamp) good (valid, not revoked, not expired, etc.) subkey. + * + * Don't bother if we are only looking for a primary key or we need + * an exact match and the exact match is not a subkey. */ if (req_prim || (foundk && foundk->pkt->pkttype != PKT_PUBLIC_SUBKEY)) ; else { - KBNODE nextk; + kbnode_t nextk; + int n_subkeys = 0; + int n_revoked_or_expired = 0; /* Either start a loop or check just this one subkey. */ for (k = foundk ? foundk : keyblock; k; k = nextk) { if (foundk) - /* If FOUNDK is not NULL, then only consider that exact - key, i.e., don't iterate. */ - nextk = NULL; + { + /* If FOUNDK is not NULL, then only consider that exact + key, i.e., don't iterate. */ + nextk = NULL; + } else nextk = k->next; @@ -3182,24 +3199,35 @@ finish_lookup (GETKEY_CTX ctx, KBNODE keyblock) if (DBG_LOOKUP) log_debug ("\tchecking subkey %08lX\n", (ulong) keyid_from_pk (pk, NULL)); + if (!pk->flags.valid) { if (DBG_LOOKUP) log_debug ("\tsubkey not valid\n"); continue; } + if (!((pk->pubkey_usage & USAGE_MASK) & req_usage)) + { + if (DBG_LOOKUP) + log_debug ("\tusage does not match: want=%x have=%x\n", + req_usage, pk->pubkey_usage); + continue; + } + + n_subkeys++; if (pk->flags.revoked) { if (DBG_LOOKUP) log_debug ("\tsubkey has been revoked\n"); + n_revoked_or_expired++; continue; } if (pk->has_expired) { if (DBG_LOOKUP) log_debug ("\tsubkey has expired\n"); + n_revoked_or_expired++; continue; - } if (pk->timestamp > curtime && !opt.ignore_valid_from) { @@ -3208,14 +3236,6 @@ finish_lookup (GETKEY_CTX ctx, KBNODE keyblock) continue; } - if (!((pk->pubkey_usage & USAGE_MASK) & req_usage)) - { - if (DBG_LOOKUP) - log_debug ("\tusage does not match: want=%x have=%x\n", - req_usage, pk->pubkey_usage); - continue; - } - if (DBG_LOOKUP) log_debug ("\tsubkey might be fine\n"); /* In case a key has a timestamp of 0 set, we make sure @@ -3228,18 +3248,20 @@ finish_lookup (GETKEY_CTX ctx, KBNODE keyblock) latest_key = k; } } + if (n_subkeys == n_revoked_or_expired && r_flags) + *r_flags |= LOOKUP_ALL_SUBKEYS_EXPIRED; } /* Check if the primary key is ok (valid, not revoke, not expire, - matches requested usage) if: - - - we didn't find an appropriate subkey and we're not doing an - exact search, - - - we're doing an exact match and the exact match was the - primary key, or, - - - we're just considering the primary key. */ + * matches requested usage) if: + * + * - we didn't find an appropriate subkey and we're not doing an + * exact search, + * + * - we're doing an exact match and the exact match was the + * primary key, or, + * + * - we're just considering the primary key. */ if ((!latest_key && !ctx->exact) || foundk == keyblock || req_prim) { if (DBG_LOOKUP && !foundk && !req_prim) @@ -3250,6 +3272,12 @@ finish_lookup (GETKEY_CTX ctx, KBNODE keyblock) if (DBG_LOOKUP) log_debug ("\tprimary key not valid\n"); } + else if (!((pk->pubkey_usage & USAGE_MASK) & req_usage)) + { + if (DBG_LOOKUP) + log_debug ("\tprimary key usage does not match: " + "want=%x have=%x\n", req_usage, pk->pubkey_usage); + } else if (pk->flags.revoked) { if (DBG_LOOKUP) @@ -3260,12 +3288,6 @@ finish_lookup (GETKEY_CTX ctx, KBNODE keyblock) if (DBG_LOOKUP) log_debug ("\tprimary key has expired\n"); } - else if (!((pk->pubkey_usage & USAGE_MASK) & req_usage)) - { - if (DBG_LOOKUP) - log_debug ("\tprimary key usage does not match: " - "want=%x have=%x\n", req_usage, pk->pubkey_usage); - } else /* Okay. */ { if (DBG_LOOKUP) @@ -3309,6 +3331,34 @@ found: } +/* Print a KEY_CONSIDERED status line. */ +static void +print_status_key_considered (kbnode_t keyblock, unsigned int flags) +{ + char hexfpr[2*MAX_FINGERPRINT_LEN + 1]; + kbnode_t node; + char flagbuf[20]; + + if (!is_status_enabled ()) + return; + + for (node=keyblock; node; node = node->next) + if (node->pkt->pkttype == PKT_PUBLIC_KEY + || node->pkt->pkttype == PKT_SECRET_KEY) + break; + if (!node) + { + log_error ("%s: keyblock w/o primary key\n", __func__); + return; + } + + hexfingerprint (node->pkt->pkt.public_key, hexfpr, sizeof hexfpr); + snprintf (flagbuf, sizeof flagbuf, " %u", flags); + write_status_strings (STATUS_KEY_CONSIDERED, hexfpr, flagbuf, NULL); +} + + + /* A high-level function to lookup keys. This function builds on top of the low-level keydb API. It first @@ -3329,6 +3379,7 @@ lookup (getkey_ctx_t ctx, kbnode_t *ret_keyblock, kbnode_t *ret_found_key, int no_suitable_key = 0; KBNODE keyblock = NULL; KBNODE found_key = NULL; + unsigned int infoflags; if (ret_keyblock) *ret_keyblock = NULL; @@ -3360,14 +3411,19 @@ lookup (getkey_ctx_t ctx, kbnode_t *ret_keyblock, kbnode_t *ret_found_key, * merge_selfsigs. For secret keys, premerge transferred the * keys to the keyblock. */ merge_selfsigs (keyblock); - found_key = finish_lookup (ctx, keyblock); + found_key = finish_lookup (ctx, keyblock, &infoflags); + if (!found_key) + infoflags |= LOOKUP_NOT_SELECTED; + print_status_key_considered (keyblock, infoflags); if (found_key) { no_suitable_key = 0; goto found; } else - no_suitable_key = 1; + { + no_suitable_key = 1; + } skip: /* Release resources and continue search. */ @@ -3381,7 +3437,7 @@ lookup (getkey_ctx_t ctx, kbnode_t *ret_keyblock, kbnode_t *ret_found_key, keydb_disable_caching (ctx->kr_handle); } -found: + found: if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("keydb_search failed: %s\n", gpg_strerror (rc));