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_NO_RECP,
STATUS_NO_SGNR,
STATUS_KEY_CONSIDERED,
STATUS_ALREADY_SIGNED,
STATUS_KEYEXPIRED,

View File

@ -548,7 +548,7 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB:
that case, this status tag does not appear.
*** ATTRIBUTE <arguments>
The list or argemnts are:
The list or arguments are:
- <fpr>
- <octets>
- <type>
@ -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 <reserved>
Issued if no recipients are usable.
*** NO_SGNR <reserved>
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>
The key has expired. expire-timestamp is the expiration time in
seconds since Epoch. This status line is not very useful because

View File

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