1
0
Fork 0
mirror of git://git.gnupg.org/gnupg.git synced 2025-07-03 22:56:33 +02:00

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

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 )
{