1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-06-02 22:38:02 +02:00

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 <wk@gnupg.org>
This commit is contained in:
Werner Koch 2016-05-13 16:24:59 +02:00
parent 83a90a916e
commit ff71521d96
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
3 changed files with 151 additions and 83 deletions

View File

@ -105,6 +105,7 @@ enum
STATUS_INV_SGNR, STATUS_INV_SGNR,
STATUS_NO_RECP, STATUS_NO_RECP,
STATUS_NO_SGNR, STATUS_NO_SGNR,
STATUS_KEY_CONSIDERED,
STATUS_ALREADY_SIGNED, STATUS_ALREADY_SIGNED,
STATUS_KEYEXPIRED, STATUS_KEYEXPIRED,

View File

@ -548,7 +548,7 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB:
that case, this status tag does not appear. that case, this status tag does not appear.
*** ATTRIBUTE <arguments> *** ATTRIBUTE <arguments>
The list or argemnts are: The list or arguments are:
- <fpr> - <fpr>
- <octets> - <octets>
- <type> - <type>
@ -602,18 +602,29 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB:
- 13 :: Key disabled - 13 :: Key disabled
- 14 :: Syntax error in specification - 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 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. for gpgsm's SIGNER command where it relates to signer's of course.
Newer GnuPG versions are using INV_SGNR; applications should Newer GnuPG versions are using INV_SGNR; applications should
ignore the INV_RECP during the sender's command processing once ignore the INV_RECP during the sender's command processing once
they have seen an INV_SGNR. Different codes are used so that they they have seen an INV_SGNR. Different codes are used so that they
can be distinguish while doing an encrypt+sign operation. can be distinguish while doing an encrypt+sign operation.
*** NO_RECP <reserved> *** NO_RECP <reserved>
Issued if no recipients are usable. Issued if no recipients are usable.
*** NO_SGNR <reserved> *** NO_SGNR <reserved>
Issued if no senders are usable. Issued if no senders are usable.
*** KEY_CONSIDERED <fpr> <flags>
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 <expire-timestamp> *** KEYEXPIRED <expire-timestamp>
The key has expired. expire-timestamp is the expiration time in The key has expired. expire-timestamp is the expiration time in
seconds since Epoch. This status line is not very useful because seconds since Epoch. This status line is not very useful because

View File

@ -38,6 +38,7 @@
#include "call-agent.h" #include "call-agent.h"
#include "host2net.h" #include "host2net.h"
#include "mbox-util.h" #include "mbox-util.h"
#include "status.h"
#define MAX_PK_CACHE_ENTRIES PK_UID_CACHE_SIZE #define MAX_PK_CACHE_ENTRIES PK_UID_CACHE_SIZE
#define MAX_UID_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 #error We need the cache for key creation
#endif #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 struct getkey_ctx_s
{ {
/* Part of the search criteria: whether the search is an exact /* 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 /* See whether the key satisfies any additional requirements specified
in CTX. If so, return 1 and set CTX->FOUND_KEY to an appropriate * 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 or subkey. Otherwise, return 0 if there was no appropriate
key. * key.
*
In case the primary key is not required, select a suitable subkey. * In case the primary key is not required, select a suitable subkey.
We need the primary key if PUBKEY_USAGE_CERT is set in * 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 * CTX->REQ_USAGE or we are in PGP6 or PGP7 mode and PUBKEY_USAGE_SIG
is set in CTX->REQ_USAGE. * is set in CTX->REQ_USAGE.
*
If any of PUBKEY_USAGE_SIG, PUBKEY_USAGE_ENC and PUBKEY_USAGE_CERT * 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. * 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 * 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 * we only return a key if it is (at least) either a signing or a
certification key. * certification key.
*
If CTX->REQ_USAGE is set, then we reject any keys that are not good * 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 * (i.e., valid, not revoked, not expired, etc.). This allows the
getkey functions to be used for plain key listings. * getkey functions to be used for plain key listings.
*
Sets the matched key's user id field (pk->user_id) to the user id * Sets the matched key's user id field (pk->user_id) to the user id
that matched the low-level search criteria or NULL. * 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:
This function needs to handle several different cases: * - LOOKUP_ALL_SUBKEYS_EXPIRED :: All Subkeys are expired or have
* been revoked.
1. No requested usage and no primary key requested *
Examples for this case are that we have a keyID to be used * This function needs to handle several different cases:
for decrytion or verification. *
2. No usage but primary key requested * 1. No requested usage and no primary key requested
This is the case for all functions which work on an * Examples for this case are that we have a keyID to be used
entire keyblock, e.g. for editing or listing * for decrytion or verification.
3. Usage and primary key requested * 2. No usage but primary key requested
FXME * This is the case for all functions which work on an
4. Usage but no primary key requested * entire keyblock, e.g. for editing or listing
FIXME * 3. Usage and primary key requested
* FIXME
* 4. Usage but no primary key requested
* FIXME
*
*/ */
static KBNODE static kbnode_t
finish_lookup (GETKEY_CTX ctx, KBNODE keyblock) 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 /* If CTX->EXACT is set, the key or subkey that actually matched the
low-level search criteria. */ low-level search criteria. */
KBNODE foundk = NULL; kbnode_t foundk = NULL;
/* The user id (if any) that matched the low-level search criteria. */ /* The user id (if any) that matched the low-level search criteria. */
PKT_user_id *foundu = NULL; 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 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 do not understand signatures made by a signing subkey. PGP 8
does. */ does. */
int req_prim = (ctx->req_usage & PUBKEY_USAGE_CERT) || int req_prim = ((ctx->req_usage & PUBKEY_USAGE_CERT)
((PGP6 || PGP7) && (ctx->req_usage & PUBKEY_USAGE_SIG)); || ((PGP6 || PGP7) && (ctx->req_usage & PUBKEY_USAGE_SIG)));
u32 curtime = make_timestamp (); u32 curtime = make_timestamp ();
u32 latest_date; u32 latest_date;
KBNODE latest_key; kbnode_t latest_key;
PKT_public_key *pk; PKT_public_key *pk;
log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); 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) if (ctx->exact)
/* Get the key or subkey that matched the low-level search
criteria. */
{ {
for (k = keyblock; k; k = k->next) for (k = keyblock; k; k = k->next)
{ {
@ -3154,24 +3167,28 @@ finish_lookup (GETKEY_CTX ctx, KBNODE keyblock)
latest_date = 0; latest_date = 0;
latest_key = NULL; latest_key = NULL;
/* Set latest_key to the latest (the one with the most recent /* Set LATEST_KEY to the latest (the one with the most recent
timestamp) good (valid, not revoked, not expired, etc.) subkey. * timestamp) good (valid, not revoked, not expired, etc.) subkey.
*
Don't bother if we are only looking for a primary key or we need * 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. */ * an exact match and the exact match is not a subkey. */
if (req_prim || (foundk && foundk->pkt->pkttype != PKT_PUBLIC_SUBKEY)) if (req_prim || (foundk && foundk->pkt->pkttype != PKT_PUBLIC_SUBKEY))
; ;
else 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. */ /* Either start a loop or check just this one subkey. */
for (k = foundk ? foundk : keyblock; k; k = nextk) for (k = foundk ? foundk : keyblock; k; k = nextk)
{ {
if (foundk) if (foundk)
/* If FOUNDK is not NULL, then only consider that exact {
key, i.e., don't iterate. */ /* If FOUNDK is not NULL, then only consider that exact
nextk = NULL; key, i.e., don't iterate. */
nextk = NULL;
}
else else
nextk = k->next; nextk = k->next;
@ -3182,24 +3199,35 @@ finish_lookup (GETKEY_CTX ctx, KBNODE keyblock)
if (DBG_LOOKUP) if (DBG_LOOKUP)
log_debug ("\tchecking subkey %08lX\n", log_debug ("\tchecking subkey %08lX\n",
(ulong) keyid_from_pk (pk, NULL)); (ulong) keyid_from_pk (pk, NULL));
if (!pk->flags.valid) if (!pk->flags.valid)
{ {
if (DBG_LOOKUP) if (DBG_LOOKUP)
log_debug ("\tsubkey not valid\n"); log_debug ("\tsubkey not valid\n");
continue; 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 (pk->flags.revoked)
{ {
if (DBG_LOOKUP) if (DBG_LOOKUP)
log_debug ("\tsubkey has been revoked\n"); log_debug ("\tsubkey has been revoked\n");
n_revoked_or_expired++;
continue; continue;
} }
if (pk->has_expired) if (pk->has_expired)
{ {
if (DBG_LOOKUP) if (DBG_LOOKUP)
log_debug ("\tsubkey has expired\n"); log_debug ("\tsubkey has expired\n");
n_revoked_or_expired++;
continue; continue;
} }
if (pk->timestamp > curtime && !opt.ignore_valid_from) if (pk->timestamp > curtime && !opt.ignore_valid_from)
{ {
@ -3208,14 +3236,6 @@ finish_lookup (GETKEY_CTX ctx, KBNODE keyblock)
continue; 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) if (DBG_LOOKUP)
log_debug ("\tsubkey might be fine\n"); log_debug ("\tsubkey might be fine\n");
/* In case a key has a timestamp of 0 set, we make sure /* 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; 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, /* Check if the primary key is ok (valid, not revoke, not expire,
matches requested usage) if: * matches requested usage) if:
*
- we didn't find an appropriate subkey and we're not doing an * - we didn't find an appropriate subkey and we're not doing an
exact search, * exact search,
*
- we're doing an exact match and the exact match was the * - we're doing an exact match and the exact match was the
primary key, or, * primary key, or,
*
- we're just considering the primary key. */ * - we're just considering the primary key. */
if ((!latest_key && !ctx->exact) || foundk == keyblock || req_prim) if ((!latest_key && !ctx->exact) || foundk == keyblock || req_prim)
{ {
if (DBG_LOOKUP && !foundk && !req_prim) if (DBG_LOOKUP && !foundk && !req_prim)
@ -3250,6 +3272,12 @@ finish_lookup (GETKEY_CTX ctx, KBNODE keyblock)
if (DBG_LOOKUP) if (DBG_LOOKUP)
log_debug ("\tprimary key not valid\n"); 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) else if (pk->flags.revoked)
{ {
if (DBG_LOOKUP) if (DBG_LOOKUP)
@ -3260,12 +3288,6 @@ finish_lookup (GETKEY_CTX ctx, KBNODE keyblock)
if (DBG_LOOKUP) if (DBG_LOOKUP)
log_debug ("\tprimary key has expired\n"); 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. */ else /* Okay. */
{ {
if (DBG_LOOKUP) 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. /* A high-level function to lookup keys.
This function builds on top of the low-level keydb API. It first 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; int no_suitable_key = 0;
KBNODE keyblock = NULL; KBNODE keyblock = NULL;
KBNODE found_key = NULL; KBNODE found_key = NULL;
unsigned int infoflags;
if (ret_keyblock) if (ret_keyblock)
*ret_keyblock = NULL; *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 * merge_selfsigs. For secret keys, premerge transferred the
* keys to the keyblock. */ * keys to the keyblock. */
merge_selfsigs (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) if (found_key)
{ {
no_suitable_key = 0; no_suitable_key = 0;
goto found; goto found;
} }
else else
no_suitable_key = 1; {
no_suitable_key = 1;
}
skip: skip:
/* Release resources and continue search. */ /* 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); keydb_disable_caching (ctx->kr_handle);
} }
found: found:
if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND) if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND)
log_error ("keydb_search failed: %s\n", gpg_strerror (rc)); log_error ("keydb_search failed: %s\n", gpg_strerror (rc));