gpg: Don't check for ambiguous keys.

* g10/gpg.c (struct result): Move from here...
* g10/keydb.h (struct pubkey): ... to here.  Update users.
* g10/gpg.c (check_user_ids): Move from here...
* g10/getkey.c (get_pubkeys): ... to here.  Update users.  Use
get_pubkey_byname to look up the keys (this also prunes invalid keys).
(pubkey_free): New function.
(pubkeys_free): New function.
* g10/gpg.c (main): Don't check for ambiguous key specifications.

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>
Regression-due-to: e8c53fc

This change not only moves the checks for ambiguous key specifications
from gpg.c to getkey.c, it also disables the checks.  The old code was
too divorced from the actual key lookups and, as such, it reproduced
the logic.  Unfortunately, the reproduction was a poor one: despite
fixing some inconsistencies (e.g., 10cca02), it still didn't deal with
group expansion or the auto key lookup functionality.  Given the
amount of instability introduced by this change, we (Neal & Werner)
decided it is better to defer introducing this functionality until
2.3.
This commit is contained in:
Neal H. Walfield 2015-12-22 14:57:53 +01:00
parent dc52995d85
commit 7195b94345
6 changed files with 316 additions and 512 deletions

3
NEWS
View File

@ -1,6 +1,9 @@
Noteworthy changes in version 2.1.11 (unreleased)
-------------------------------------------------
* gpg: Don't check for ambigious or non-matching key specification in
the config file or given to --encrypt-to. This feature will return
in 2.3.x.
Noteworthy changes in version 2.1.10 (2015-12-04)
-------------------------------------------------

View File

@ -366,6 +366,282 @@ getkey_disable_caches ()
}
void
pubkey_free (struct pubkey *key)
{
xfree (key->pk);
release_kbnode (key->keyblock);
xfree (key);
}
void
pubkeys_free (struct pubkey *keys)
{
while (keys)
{
struct pubkey *next = keys->next;
pubkey_free (keys);
keys = next;
}
}
/* Returns all keys that match the search specfication SEARCH_TERMS.
This function also checks for and warns about duplicate entries in
the keydb, which can occur if the user has configured multiple
keyrings or keyboxes or if a keyring or keybox was corrupted.
Note: SEARCH_TERMS will not be expanded (i.e., it may not be a
group).
USE is the operation for which the key is required. It must be
either PUBKEY_USAGE_ENC, PUBKEY_USAGE_SIG, PUBKEY_USAGE_CERT or
PUBKEY_USAGE_AUTH.
XXX: Currently, only PUBKEY_USAGE_ENC and PUBKEY_USAGE_SIG are
implemented.
INCLUDE_UNUSABLE indicates whether disabled keys are allowed.
(Recipients specified with --encrypt-to and --hidden-encrypt-to may
be disabled. It is possible to edit disabled keys.)
SOURCE is the context in which SEARCH_TERMS was specified, e.g.,
"--encrypt-to", etc. If this function is called interactively,
then this should be NULL.
If WARN_POSSIBLY_AMBIGUOUS is set, then emits a warning if the user
does not specify a long key id or a fingerprint.
The results are placed in *KEYS. *KEYS must be NULL! */
gpg_error_t
get_pubkeys (ctrl_t ctrl,
char *search_terms, int use, int include_unusable, char *source,
int warn_possibly_ambiguous,
struct pubkey **keys)
{
/* We show a warning when a key appears multiple times in the DB.
This can happen for two reasons:
- The user has configured multiple keyrings or keyboxes.
- The keyring or keybox has been corrupted in some way, e.g., a
bug or a random process changing them.
For each duplicate, we only want to show the key once. Hence,
this list. */
static strlist_t key_dups;
/* USE transformed to a string. */
char *use_str;
gpg_error_t err;
KEYDB_SEARCH_DESC desc;
GETKEY_CTX ctx;
struct pubkey *results = NULL;
struct pubkey *r;
int count;
char fingerprint[2 * MAX_FINGERPRINT_LEN + 1];
if (DBG_LOOKUP)
{
log_debug ("\n");
log_debug ("%s: Checking %s=%s\n",
__func__, source ? source : "user input", search_terms);
}
if (*keys)
log_bug ("%s: KEYS should be NULL!\n", __func__);
switch (use)
{
case PUBKEY_USAGE_ENC: use_str = "encrypt"; break;
case PUBKEY_USAGE_SIG: use_str = "sign"; break;
case PUBKEY_USAGE_CERT: use_str = "cetify"; break;
case PUBKEY_USAGE_AUTH: use_str = "authentication"; break;
default: log_bug ("%s: Bad value for USE (%d)\n", __func__, use);
}
if (use == PUBKEY_USAGE_CERT || use == PUBKEY_USAGE_AUTH)
log_bug ("%s: use=%s is unimplemented.\n", __func__, use_str);
err = classify_user_id (search_terms, &desc, 1);
if (err)
{
log_info (_("key \"%s\" not found: %s\n"),
search_terms, gpg_strerror (err));
if (!opt.quiet && source)
log_info (_("(check argument of option '%s')\n"), source);
goto out;
}
if (warn_possibly_ambiguous
&& ! (desc.mode == KEYDB_SEARCH_MODE_LONG_KID
|| desc.mode == KEYDB_SEARCH_MODE_FPR16
|| desc.mode == KEYDB_SEARCH_MODE_FPR20
|| desc.mode == KEYDB_SEARCH_MODE_FPR))
{
log_info (_("Warning: '%s' should be a long key ID or a fingerprint\n"),
search_terms);
if (!opt.quiet && source)
log_info (_("(check argument of option '%s')\n"), source);
}
/* Gather all of the results. */
ctx = NULL;
count = 0;
do
{
PKT_public_key *pk = xmalloc_clear (sizeof *pk);
KBNODE kb;
pk->req_usage = use;
if (! ctx)
err = get_pubkey_byname (ctrl, &ctx, pk, search_terms, &kb, NULL,
include_unusable, 1);
else
err = getkey_next (ctx, pk, &kb);
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
/* No more results. */
{
xfree (pk);
break;
}
else if (err)
/* An error (other than "not found"). */
{
log_error (_("error looking up: %s\n"),
gpg_strerror (err));
xfree (pk);
break;
}
/* Another result! */
count ++;
r = xmalloc_clear (sizeof (*r));
r->pk = pk;
r->keyblock = kb;
r->next = results;
results = r;
}
while (ctx);
getkey_end (ctx);
if (DBG_LOOKUP)
{
log_debug ("%s resulted in %d matches.\n", search_terms, count);
for (r = results; r; r = r->next)
log_debug (" %s\n",
hexfingerprint (r->keyblock->pkt->pkt.public_key,
fingerprint, sizeof (fingerprint)));
}
if (! results && gpg_err_code (err) == GPG_ERR_NOT_FOUND)
/* No match. */
{
if (DBG_LOOKUP)
log_debug ("%s: '%s' not found.\n", __func__, search_terms);
log_info (_("key \"%s\" not found\n"), search_terms);
if (!opt.quiet && source)
log_info (_("(check argument of option '%s')\n"), source);
goto out;
}
else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
/* No more matches. */
;
else if (err)
/* Some other error. An error message was already printed
out. Free RESULTS and continue. */
goto out;
/* Check for duplicates. */
if (DBG_LOOKUP)
log_debug ("%s: Checking results of %s='%s' for dups\n",
__func__, source ? source : "user input", search_terms);
count = 0;
for (r = results; r; r = r->next)
{
struct pubkey **prevp;
struct pubkey *next;
struct pubkey *r2;
int dups = 0;
prevp = &r->next;
next = r->next;
while ((r2 = next))
{
if (cmp_public_keys (r->keyblock->pkt->pkt.public_key,
r2->keyblock->pkt->pkt.public_key) != 0)
/* Not a dup. */
{
prevp = &r2->next;
next = r2->next;
continue;
}
dups ++;
count ++;
/* Remove R2 from the list. */
*prevp = r2->next;
release_kbnode (r2->keyblock);
next = r2->next;
xfree (r2);
}
if (dups)
{
hexfingerprint (r->keyblock->pkt->pkt.public_key,
fingerprint, sizeof fingerprint);
if (! strlist_find (key_dups, fingerprint))
{
char fingerprint_formatted[MAX_FORMATTED_FINGERPRINT_LEN + 1];
log_info (_("Warning: %s appears in the keyring %d times.\n"),
format_hexfingerprint (fingerprint,
fingerprint_formatted,
sizeof fingerprint_formatted),
1 + dups);
add_to_strlist (&key_dups, fingerprint);
}
}
}
if (DBG_LOOKUP && count)
{
log_debug ("After removing %d dups:\n", count);
for (r = results, count = 0; r; r = r->next)
log_debug (" %d: %s\n",
count,
hexfingerprint (r->keyblock->pkt->pkt.public_key,
fingerprint, sizeof fingerprint));
}
out:
if (err)
{
while ((r = results))
{
results = results->next;
pubkey_free (r);
release_kbnode (r->keyblock);
xfree (r);
}
}
else
*keys = results;
return err;
}
static void
pk_from_block (GETKEY_CTX ctx, PKT_public_key * pk, KBNODE keyblock,
KBNODE found_key)

512
g10/gpg.c
View File

@ -2101,506 +2101,6 @@ get_default_configname (void)
return configname;
}
struct result
{
struct result *next;
kbnode_t keyblock;
int processed;
};
/* We show a warning when a key appears multiple times in the DB. */
static strlist_t key_dups;
static gpg_error_t
check_user_ids (strlist_t *sp,
int warn_possibly_ambiguous,
int error_if_not_found)
{
strlist_t s = *sp;
strlist_t s2 = NULL;
strlist_t t;
gpg_error_t rc = 0;
gpg_error_t err;
KEYDB_HANDLE hd = NULL;
char fingerprint_formatted[MAX_FORMATTED_FINGERPRINT_LEN + 1];
/* A quick check to avoid allocating a new strlist if we can skip
all keys. Handles also the case of !SP. See below for details. */
for (t = s; t && (!(t->flags & PK_LIST_CONFIG)
&& !(t->flags & PK_LIST_ENCRYPT_TO)); t = t->next)
;
if (!t)
return 0;
for (t = s; t; t = t->next)
{
const char *option_str;
int option;
KEYDB_SEARCH_DESC desc;
struct result *results = NULL;
struct result *r;
int count;
/* We also potentially need a ! at the end. */
char fingerprint[2 * MAX_FINGERPRINT_LEN + 1 + 1];
char fingerprint2[2 * MAX_FINGERPRINT_LEN + 1];
KBNODE best_kb;
PKT_public_key *best_pk;
/* Whether the key is for encryption or signing. */
int encrypt = 1;
/* If the key has been given on the command line and it has not
been given by one of the encrypt-to options, we skip the
checks. The reason is that the actual key selection code
does its own checks and provides proper status message to the
caller to detect the wrong keys. */
if (!(t->flags & PK_LIST_CONFIG) && !(t->flags & PK_LIST_ENCRYPT_TO))
{
add_to_strlist (&s2, t->d);
s2->flags = t->flags;
continue;
}
option = t->flags >> PK_LIST_SHIFT;
switch (option)
{
case oDefaultKey:
option_str = "--default-key";
encrypt = 0;
break;
case oLocalUser:
option_str = "--local-user";
encrypt = 0;
break;
case oEncryptTo: option_str = "--encrypt-to"; break;
case oHiddenEncryptTo: option_str = "--hidden-encrypt-to"; break;
case oEncryptToDefaultKey: option_str = "--encrypt-to-default-key"; break;
case oRecipient: option_str = "--recipient"; break;
case oHiddenRecipient: option_str = "--hidden-recipient"; break;
default:
log_bug ("Unsupport option: %d\n", (t->flags >> PK_LIST_SHIFT));
}
if (DBG_LOOKUP)
{
log_debug ("\n");
log_debug ("%s: Checking %s=%s\n", __func__, option_str, t->d);
}
err = classify_user_id (t->d, &desc, 1);
if (err)
{
if (! rc)
rc = err;
log_error (_("key \"%s\" not found: %s\n"),
t->d, gpg_strerror (err));
if (!opt.quiet)
log_info (_("(check argument of option '%s')\n"), option_str);
continue;
}
if (warn_possibly_ambiguous
&& ! (desc.mode == KEYDB_SEARCH_MODE_LONG_KID
|| desc.mode == KEYDB_SEARCH_MODE_FPR16
|| desc.mode == KEYDB_SEARCH_MODE_FPR20
|| desc.mode == KEYDB_SEARCH_MODE_FPR))
log_info (_("Warning: value '%s' for option '%s'"
" should be a long key ID or a fingerprint\n"),
t->d, option_str);
if (! hd)
{
hd = keydb_new ();
if (!hd)
{
rc = gpg_error_from_syserror ();
break;
}
}
else
keydb_search_reset (hd);
/* Gather all of the results. */
count = 0;
while (1)
{
KBNODE kb;
err = keydb_search (hd, &desc, 1, NULL);
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND
|| gpg_err_code (err) == GPG_ERR_EOF)
/* No more results. */
break;
else if (err)
/* An error (other than "not found"). */
{
log_error (_("error searching the keyring: %s\n"),
gpg_strerror (err));
break;
}
err = keydb_get_keyblock (hd, &kb);
if (err)
{
log_error (_("error reading keyblock: %s\n"), gpg_strerror (err));
break;
}
/* Another result! */
count ++;
r = xmalloc_clear (sizeof (*r));
r->keyblock = kb;
r->next = results;
results = r;
}
if (DBG_LOOKUP)
{
log_debug ("%s resulted in %d matches.\n", t->d, count);
for (r = results; r; r = r->next)
log_debug (" %s\n",
hexfingerprint (r->keyblock->pkt->pkt.public_key,
fingerprint, sizeof (fingerprint)));
}
if (! results && (gpg_err_code (err) == GPG_ERR_NOT_FOUND
|| gpg_err_code (err) == GPG_ERR_EOF))
/* No match. */
{
if (DBG_LOOKUP)
log_debug ("%s: '%s' not found.\n", __func__, t->d);
if (error_if_not_found)
{
if (! rc)
rc = err;
log_error (_("key \"%s\" not found\n"), t->d);
if (!opt.quiet)
log_info (_("(check argument of option '%s')\n"), option_str);
}
continue;
}
else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND
|| gpg_err_code (err) == GPG_ERR_EOF)
/* No more matches. */
;
else if (err)
/* Some other error. An error message was already printed
out. Free RESULTS and continue. */
{
if (! rc)
rc = err;
while ((r = results))
{
results = results->next;
release_kbnode (r->keyblock);
xfree (r);
}
continue;
}
/* Check for duplicates. */
if (DBG_LOOKUP)
log_debug ("%s: Checking results of %s='%s' for dups\n",
__func__, option_str, t->d);
while (1)
{
struct result **prevp;
struct result *next;
struct result *r2;
int dups = 0;
/* After checking a result, we set R->PROCESSED. Find the
next unprocessed result. */
for (r = results; r; r = r->next)
if (! r->processed)
break;
if (! r)
/* There is nothing left to check. */
break;
hexfingerprint (r->keyblock->pkt->pkt.public_key,
fingerprint, sizeof fingerprint);
r->processed = 1;
prevp = &results;
next = results;
while ((r2 = next))
{
if (r2->processed)
{
prevp = &r2->next;
next = r2->next;
continue;
}
hexfingerprint (r2->keyblock->pkt->pkt.public_key,
fingerprint2, sizeof fingerprint2);
if (strcmp (fingerprint, fingerprint2) != 0)
/* Not a dup. */
{
prevp = &r2->next;
next = r2->next;
continue;
}
dups ++;
/* Remove R2 from the list. */
*prevp = r2->next;
release_kbnode (r2->keyblock);
next = r2->next;
xfree (r2);
}
if (dups && ! strlist_find (key_dups, fingerprint))
{
log_info (_("Warning: %s appears in the keyring %d times.\n"),
format_hexfingerprint (fingerprint,
fingerprint_formatted,
sizeof fingerprint_formatted),
1 + dups);
add_to_strlist (&key_dups, fingerprint);
}
}
if (DBG_LOOKUP)
{
log_debug ("After removing dups:\n");
for (r = results, count = 0; r; r = r->next)
{
count ++;
log_debug (" %d: %s\n",
count,
hexfingerprint (r->keyblock->pkt->pkt.public_key,
fingerprint, sizeof fingerprint));
}
}
/* Now we find the best key. */
assert (results);
/* Prune invalid keys. */
{
int ambiguous = 0;
if (DBG_LOOKUP)
log_debug ("Pruning bad keys.\n");
best_pk = NULL;
for (r = results; r; r = r->next)
{
KBNODE kb = r->keyblock;
PKT_public_key *pk = kb->pkt->pkt.public_key;
KBNODE n;
/* Merge in the data from the self sigs so that things
like the revoked status are available. */
merge_keys_and_selfsig (kb);
if (/* Using disabled keys with --encrypt-to is allowed. */
! (option == oEncryptTo || option == oHiddenEncryptTo)
&& pk_is_disabled (pk))
{
if (DBG_LOOKUP)
log_debug (" Skipping disabled key: %s\n",
hexfingerprint (pk, fingerprint,
sizeof fingerprint));
continue;
}
if (pk->flags.revoked)
{
if (DBG_LOOKUP)
log_debug (" Skipping revoked key: %s\n",
hexfingerprint (pk, fingerprint,
sizeof fingerprint));
continue;
}
if (pk->has_expired)
{
if (DBG_LOOKUP)
log_debug (" Skipping expired key: %s\n",
hexfingerprint (pk, fingerprint,
sizeof fingerprint));
continue;
}
/* Check for the required encryption or signing
capability. */
n = kb;
do
{
PKT_public_key *key = n->pkt->pkt.public_key;
if ((/* Using disabled keys with --encrypt-to is allowed. */
pk_is_disabled (key)
&& ! (option == oEncryptTo
|| option == oHiddenEncryptTo))
|| key->flags.revoked
|| key->has_expired)
/* Invalid. */
continue;
if (encrypt && ! (key->pubkey_usage & PUBKEY_USAGE_ENC))
continue;
if (! encrypt && ! (key->pubkey_usage & PUBKEY_USAGE_SIG))
continue;
/* Key passes basic tests. */
break;
}
while ((n = find_next_kbnode (n, PKT_PUBLIC_SUBKEY)));
if (! n)
{
if (DBG_LOOKUP)
log_debug (" Skipping %s, which does not have %s capability.\n",
hexfingerprint (r->keyblock->pkt->pkt.public_key,
fingerprint, sizeof fingerprint),
encrypt ? "encrypt" : "sign");
continue;
}
else if (DBG_LOOKUP)
log_debug (" %s is valid and has %s capability.\n",
hexfingerprint (r->keyblock->pkt->pkt.public_key,
fingerprint, sizeof fingerprint),
encrypt ? "encrypt" : "sign");
if (! best_pk)
{
best_pk = pk;
best_kb = kb;
continue;
}
/* We have multiple candidates. Prefer the newer key.
XXX: we should also consider key capabilities (if we
are encrypting to the key, does it have an encryption
capability?).
XXX: if we are signing, then we should consider the
key that is actually available (e.g., if one is on a
smart card). */
ambiguous = 1;
if (best_pk->timestamp < pk->timestamp)
best_pk = pk;
}
if (! results)
{
if (encrypt)
log_error (_("%s: no matching keys are valid encryption keys"),
t->d);
else
log_error (_("%s: no matching keys are valid signing keys"),
t->d);
if (!opt.quiet)
log_info (_("(check argument of option '%s')\n"), option_str);
continue;
}
if (ambiguous)
{
/* TRANSLATORS: The %s prints a key specification which
for example has been given at the command line.
Lines with fingerprints are printed after this
message. */
log_error (_("key specification '%s' is ambiguous\n"),
t->d);
if (!opt.quiet)
log_info (_("(check argument of option '%s')\n"),
option_str);
log_info (_("'%s' matches at least:\n"), t->d);
for (r = results; r; r = r->next)
log_info (" %s\n",
format_hexfingerprint
(hexfingerprint (r->keyblock->pkt->pkt.public_key,
fingerprint, sizeof fingerprint),
fingerprint_formatted,
sizeof fingerprint_formatted));
if (! rc)
rc = GPG_ERR_AMBIGUOUS_NAME;
}
}
if ((desc.mode == KEYDB_SEARCH_MODE_SHORT_KID
|| desc.mode == KEYDB_SEARCH_MODE_LONG_KID
|| desc.mode == KEYDB_SEARCH_MODE_FPR16
|| desc.mode == KEYDB_SEARCH_MODE_FPR20)
&& strchr (t->d, '!'))
/* Exact search. In this case we want to set FINGERPRINT not
to the primary key, but the key (primary or sub) that
matched the search criteria. Note: there will always be
exactly one match. */
{
kbnode_t n = best_kb;
PKT_public_key *match = NULL;
int i;
do
{
if ((n->flag & 1))
/* The matched node. */
{
assert (! match);
match = n->pkt->pkt.public_key;
}
}
while ((n = find_next_kbnode (n, PKT_PUBLIC_SUBKEY)));
assert (match);
hexfingerprint (match, fingerprint, sizeof fingerprint);
i = strlen (fingerprint);
fingerprint[i] = '!';
fingerprint[i + 1] = '\0';
}
else
hexfingerprint (best_pk, fingerprint, sizeof fingerprint);
add_to_strlist (&s2, fingerprint);
s2->flags = s->flags;
{
struct result *next = results;
while ((r = next))
{
next = r->next;
release_kbnode (r->keyblock);
xfree (r);
}
}
}
strlist_rev (&s2);
keydb_release (hd);
free_strlist (s);
*sp = s2;
return rc;
}
int
main (int argc, char **argv)
{
@ -4280,18 +3780,6 @@ main (int argc, char **argv)
break;
}
{
rc = check_user_ids (&locusr, 1, 1);
if (rc)
g10_exit (1);
rc = check_user_ids (&remusr, 0, 1);
if (rc)
g10_exit (1);
rc = check_user_ids (&opt.def_secret_key, 1, 0);
if (rc)
g10_exit (1);
}
/* The command dispatcher. */
switch( cmd )
{

View File

@ -485,6 +485,29 @@ int get_pubkey_fast ( PKT_public_key *pk, u32 *keyid );
using merge_selfsigs. */
KBNODE get_pubkeyblock( u32 *keyid );
/* A list used by get_pubkeys to gather all of the matches. */
struct pubkey
{
struct pubkey *next;
/* The key to use (either the public key or the subkey). */
PKT_public_key *pk;
kbnode_t keyblock;
};
/* Free a single key. This does not remove key from any list! */
void pubkey_free (struct pubkey *key);
/* Free a list of public keys. */
void pubkeys_free (struct pubkey *keys);
/* Returns all keys that match the search specfication SEARCH_TERMS.
The returned keys should be freed using pubkeys_free. */
gpg_error_t
get_pubkeys (ctrl_t ctrl,
char *search_terms, int use, int include_unusable, char *source,
int warn_possibly_ambiguous,
struct pubkey **keys);
/* Find a public key identified by the name NAME.
If name appears to be a valid valid RFC822 mailbox (i.e., email

View File

@ -914,6 +914,13 @@ build_pk_list (ctrl_t ctrl, strlist_t rcpts, PK_LIST *ret_pk_list)
else
remusr = rcpts;
/* XXX: Change this function to use get_pubkeys instead of
get_pubkey_byname to detect ambiguous key specifications and warn
about duplicate keyblocks. For ambiguous key specifications on
the command line or provided interactively, prompt the user to
select the best key. If a key specification is ambiguous and we
are in batch mode, die. */
if (opt.encrypt_to_default_key)
{
static int warned;

View File

@ -120,6 +120,13 @@ build_sk_list (ctrl_t ctrl,
gpg_error_t err;
SK_LIST sk_list = NULL;
/* XXX: Change this function to use get_pubkeys instead of
getkey_byname to detect ambiguous key specifications and warn
about duplicate keyblocks. For ambiguous key specifications on
the command line or provided interactively, prompt the user to
select the best key. If a key specification is ambiguous and we
are in batch mode, die. */
if (!locusr) /* No user ids given - use the default key. */
{
PKT_public_key *pk;