gpg: Reorder signatures, if appropriate.

XXX

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>
GnuPG-bug-id: 2236
This commit is contained in:
Neal H. Walfield 2016-02-16 15:47:30 +01:00
parent 2f02ed75a9
commit bff529a3ce
5 changed files with 730 additions and 63 deletions

View File

@ -165,6 +165,7 @@ enum cmd_and_opt_values
aPasswd, aPasswd,
aServer, aServer,
aTOFUPolicy, aTOFUPolicy,
aCheckKey,
oTextmode, oTextmode,
oNoTextmode, oNoTextmode,
@ -487,6 +488,7 @@ static ARGPARSE_OPTS opts[] = {
ARGPARSE_c (aServer, "server", N_("run in server mode")), ARGPARSE_c (aServer, "server", N_("run in server mode")),
ARGPARSE_c (aTOFUPolicy, "tofu-policy", ARGPARSE_c (aTOFUPolicy, "tofu-policy",
N_("|VALUE|set the TOFU policy for a key")), N_("|VALUE|set the TOFU policy for a key")),
ARGPARSE_c (aCheckKey, "check-key", N_("Check a key")),
ARGPARSE_group (301, N_("@\nOptions:\n ")), ARGPARSE_group (301, N_("@\nOptions:\n ")),
@ -2464,6 +2466,10 @@ main (int argc, char **argv)
set_cmd (&cmd, pargs.r_opt); set_cmd (&cmd, pargs.r_opt);
break; break;
case aCheckKey:
set_cmd (&cmd, pargs.r_opt);
break;
case oArmor: opt.armor = 1; opt.no_armor=0; break; case oArmor: opt.armor = 1; opt.no_armor=0; break;
case oOutput: opt.outfile = pargs.r.ret_str; break; case oOutput: opt.outfile = pargs.r.ret_str; break;
case oMaxOutput: opt.max_output = pargs.r.ret_ulong; break; case oMaxOutput: opt.max_output = pargs.r.ret_ulong; break;
@ -4589,6 +4595,31 @@ main (int argc, char **argv)
#endif /*USE_TOFU*/ #endif /*USE_TOFU*/
break; break;
case aCheckKey:
{
int i;
if (argc < 1)
wrong_args ("--check-key KEYID...");
for (i = 0; i < argc; i ++)
{
kbnode_t kb;
rc = get_pubkey_byname (ctrl, NULL, NULL, argv[i], &kb,
NULL, 1, 1);
if (rc)
{
log_error (_("looking up key '%s': %s\n"),
argv[i], gpg_strerror (rc));
g10_exit (1);
}
keyblock_check_sigs (kb, 0);
}
}
break;
case aListPackets: case aListPackets:
opt.list_packets=2; opt.list_packets=2;
default: default:

View File

@ -1238,54 +1238,6 @@ change_passphrase (ctrl_t ctrl, kbnode_t keyblock)
/*
* There are some keys out (due to a bug in gnupg), where the sequence
* of the packets is wrong. This function fixes that.
* Returns: true if the keyblock has been fixed.
*
* Note: This function does not work if there is more than one user ID.
*/
static int
fix_key_signature_order (KBNODE keyblock)
{
KBNODE node, last, subkey;
int fixed = 0;
/* Locate key signatures of class 0x10..0x13 behind sub key packets. */
for (subkey = last = NULL, node = keyblock; node;
last = node, node = node->next)
{
switch (node->pkt->pkttype)
{
case PKT_PUBLIC_SUBKEY:
case PKT_SECRET_SUBKEY:
if (!subkey)
subkey = last; /* Actually it is the one before the subkey. */
break;
case PKT_SIGNATURE:
if (subkey)
{
PKT_signature *sig = node->pkt->pkt.signature;
if (sig->sig_class >= 0x10 && sig->sig_class <= 0x13)
{
log_info (_("moving a key signature to the correct place\n"));
last->next = node->next;
node->next = subkey->next;
subkey->next = node;
node = last;
fixed = 1;
}
}
break;
default:
break;
}
}
return fixed;
}
/* Fix various problems in the keyblock. Returns true if the keyblock /* Fix various problems in the keyblock. Returns true if the keyblock
was changed. Note that a pointer to the keyblock must be given and was changed. Note that a pointer to the keyblock must be given and
the function may change it (i.e. replacing the first node). */ the function may change it (i.e. replacing the first node). */
@ -1294,10 +1246,10 @@ fix_keyblock (kbnode_t *keyblockp)
{ {
int changed = 0; int changed = 0;
if (fix_key_signature_order (*keyblockp))
changed++;
if (collapse_uids (keyblockp)) if (collapse_uids (keyblockp))
changed++; changed++;
if (keyblock_check_sigs (*keyblockp, 0))
changed++;
reorder_keyblock (*keyblockp); reorder_keyblock (*keyblockp);
/* If we modified the keyblock, make sure the flags are right. */ /* If we modified the keyblock, make sure the flags are right. */
if (changed) if (changed)
@ -1370,7 +1322,7 @@ enum cmdids
cmdSHOWPREF, cmdSHOWPREF,
cmdSETPREF, cmdPREFKS, cmdNOTATION, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST, cmdSETPREF, cmdPREFKS, cmdNOTATION, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST,
cmdCHKTRUST, cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD, cmdCHKTRUST, cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD,
cmdCLEAN, cmdMINIMIZE, cmdGRIP, cmdNOP cmdCLEAN, cmdMINIMIZE, cmdGRIP, cmdCHECKKEY, cmdNOP
}; };
static struct static struct
@ -1465,6 +1417,8 @@ static struct
N_("compact unusable user IDs and remove unusable signatures from key")}, N_("compact unusable user IDs and remove unusable signatures from key")},
{ "minimize", cmdMINIMIZE, KEYEDIT_NOT_SK, { "minimize", cmdMINIMIZE, KEYEDIT_NOT_SK,
N_("compact unusable user IDs and remove all signatures from key")}, N_("compact unusable user IDs and remove all signatures from key")},
{ "checkkey", cmdCHECKKEY, KEYEDIT_NOT_SK,
N_("check the key")},
{ NULL, cmdNONE, 0, NULL} { NULL, cmdNONE, 0, NULL}
}; };
@ -2280,6 +2234,11 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
redisplay = modified = 1; redisplay = modified = 1;
break; break;
case cmdCHECKKEY:
if (keyblock_check_sigs (keyblock, 0))
redisplay = modified = 1;
break;
case cmdQUIT: case cmdQUIT:
if (have_commands) if (have_commands)
goto leave; goto leave;

View File

@ -263,6 +263,15 @@ int check_key_signature2( KBNODE root, KBNODE node, PKT_public_key *check_pk,
PKT_public_key *ret_pk, int *is_selfsig, PKT_public_key *ret_pk, int *is_selfsig,
u32 *r_expiredate, int *r_expired ); u32 *r_expiredate, int *r_expired );
int check_signature_end (PKT_public_key *pk, PKT_signature *sig,
gcry_md_hd_t digest,
int *r_expired, int *r_revoked,
PKT_public_key *ret_pk);
int check_signature_only_end (PKT_public_key *pk, PKT_signature *sig,
gcry_md_hd_t digest);
void hash_uid_node( KBNODE unode, gcry_md_hd_t md, PKT_signature *sig );
/*-- delkey.c --*/ /*-- delkey.c --*/
gpg_error_t delete_keys (strlist_t names, int secret, int allow_both); gpg_error_t delete_keys (strlist_t names, int secret, int allow_both);

View File

@ -651,6 +651,8 @@ int check_signature2 (PKT_signature *sig, gcry_md_hd_t digest,
u32 *r_expiredate, int *r_expired, int *r_revoked, u32 *r_expiredate, int *r_expired, int *r_revoked,
PKT_public_key *ret_pk); PKT_public_key *ret_pk);
/* Checks KB's signatures and possible reorders them. */
int keyblock_check_sigs (KBNODE kb, int only_selfsigs);
/*-- pubkey-enc.c --*/ /*-- pubkey-enc.c --*/
gpg_error_t get_session_key (ctrl_t ctrl, PKT_pubkey_enc *k, DEK *dek); gpg_error_t get_session_key (ctrl_t ctrl, PKT_pubkey_enc *k, DEK *dek);

View File

@ -34,11 +34,7 @@
#include "i18n.h" #include "i18n.h"
#include "options.h" #include "options.h"
#include "pkglue.h" #include "pkglue.h"
#include "host2net.h"
static int check_signature_end (PKT_public_key *pk, PKT_signature *sig,
gcry_md_hd_t digest,
int *r_expired, int *r_revoked,
PKT_public_key *ret_pk);
/* Check a signature. This is shorthand for check_signature2 with /* Check a signature. This is shorthand for check_signature2 with
the unnamed arguments passed as NULL. */ the unnamed arguments passed as NULL. */
@ -371,19 +367,34 @@ check_signature_metadata_validity (PKT_public_key *pk, PKT_signature *sig,
* If RET_PK is not NULL, PK is copied into RET_PK on success. * If RET_PK is not NULL, PK is copied into RET_PK on success.
* *
* Returns 0 on success. An error code other. */ * Returns 0 on success. An error code other. */
static int int
check_signature_end (PKT_public_key *pk, PKT_signature *sig, check_signature_end (PKT_public_key *pk, PKT_signature *sig,
gcry_md_hd_t digest, gcry_md_hd_t digest,
int *r_expired, int *r_revoked, PKT_public_key *ret_pk) int *r_expired, int *r_revoked, PKT_public_key *ret_pk)
{ {
gcry_mpi_t result = NULL;
int rc = 0; int rc = 0;
const struct weakhash *weak;
if ((rc = check_signature_metadata_validity (pk, sig, if ((rc = check_signature_metadata_validity (pk, sig,
r_expired, r_revoked))) r_expired, r_revoked)))
return rc; return rc;
if ((rc = check_signature_only_end (pk, sig, digest)))
return rc;
if(!rc && ret_pk)
copy_public_key(ret_pk,pk);
return rc;
}
int
check_signature_only_end (PKT_public_key *pk, PKT_signature *sig,
gcry_md_hd_t digest)
{
gcry_mpi_t result = NULL;
int rc = 0;
const struct weakhash *weak;
if (!opt.flags.allow_weak_digest_algos) if (!opt.flags.allow_weak_digest_algos)
for (weak = opt.weak_digests; weak; weak = weak->next) for (weak = opt.weak_digests; weak; weak = weak->next)
if (sig->digest_algo == weak->algo) if (sig->digest_algo == weak->algo)
@ -453,16 +464,13 @@ check_signature_end (PKT_public_key *pk, PKT_signature *sig,
rc = GPG_ERR_BAD_SIGNATURE; rc = GPG_ERR_BAD_SIGNATURE;
} }
if(!rc && ret_pk)
copy_public_key(ret_pk,pk);
return rc; return rc;
} }
/* Add a uid node to a hash context. See section 5.2.4, paragraph 4 /* Add a uid node to a hash context. See section 5.2.4, paragraph 4
of RFC 4880. */ of RFC 4880. */
static void void
hash_uid_node( KBNODE unode, gcry_md_hd_t md, PKT_signature *sig ) hash_uid_node( KBNODE unode, gcry_md_hd_t md, PKT_signature *sig )
{ {
PKT_user_id *uid = unode->pkt->pkt.user_id; PKT_user_id *uid = unode->pkt->pkt.user_id;
@ -893,3 +901,661 @@ check_key_signature2 (kbnode_t root, kbnode_t node, PKT_public_key *check_pk,
return rc; return rc;
} }
void
sig_print (estream_t fp,
PKT_public_key *pk, PKT_signature *sig, gpg_error_t sig_status,
int print_without_key, int extended)
{
int sigrc;
int is_rev = sig->sig_class == 0x30;
switch (gpg_err_code (sig_status))
{
case GPG_ERR_NO_VALUE: /* Unknown. */
sigrc = ' ';
break;
case 0:
sigrc = '!';
break;
case GPG_ERR_BAD_SIGNATURE:
sigrc = '-';
break;
case GPG_ERR_NO_PUBKEY:
case GPG_ERR_UNUSABLE_PUBKEY:
sigrc = '?';
break;
default:
sigrc = '%';
break;
}
if (sigrc != '?' || print_without_key)
{
es_fprintf (fp, "%s%c%c %c%c%c%c%c%c %s %s",
is_rev ? "rev" : "sig", sigrc,
(sig->sig_class - 0x10 > 0 &&
sig->sig_class - 0x10 <
4) ? '0' + sig->sig_class - 0x10 : ' ',
sig->flags.exportable ? ' ' : 'L',
sig->flags.revocable ? ' ' : 'R',
sig->flags.policy_url ? 'P' : ' ',
sig->flags.notation ? 'N' : ' ',
sig->flags.expired ? 'X' : ' ',
(sig->trust_depth > 9) ? 'T' : (sig->trust_depth >
0) ? '0' +
sig->trust_depth : ' ',
keystr (sig->keyid),
datestr_from_sig (sig));
if ((opt.list_options & LIST_SHOW_SIG_EXPIRE) || extended )
es_fprintf (fp, " %s", expirestr_from_sig (sig));
es_fprintf (fp, " ");
if (sigrc == '%')
es_fprintf (fp, "[%s] ", gpg_strerror (sig_status));
else if (sigrc == '?')
;
else
{
size_t n;
char *p = get_user_id (sig->keyid, &n);
tty_print_utf8_string2 (fp, p, n,
opt.screen_columns - keystrlen () - 26 -
((opt.
list_options & LIST_SHOW_SIG_EXPIRE) ? 11
: 0));
xfree (p);
}
es_fprintf (fp, "\n");
if (sig->flags.policy_url
&& ((opt.list_options & LIST_SHOW_POLICY_URLS) || extended))
/* XXX: Change to print to FP. */
show_policy_url (sig, 3, 0);
if (sig->flags.notation
&& ((opt.list_options & LIST_SHOW_NOTATIONS) || extended))
/* XXX: Change to print to FP. */
show_notation (sig, 3, 0,
((opt.
list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0) +
((opt.
list_options & LIST_SHOW_USER_NOTATIONS) ? 2 : 0));
if (sig->flags.pref_ks
&& ((opt.list_options & LIST_SHOW_KEYSERVER_URLS) || extended))
/* XXX: Change to print to FP. */
show_keyserver_url (sig, 3, 0);
if (extended)
{
const unsigned char *s;
s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PRIMARY_UID, NULL);
if (s && *s)
es_fprintf (fp, " [primary]\n");
s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL);
if (s && buf32_to_u32 (s))
es_fprintf (fp, " [expires: %s]\n",
isotimestamp (pk->timestamp + buf32_to_u32 (s)));
}
}
}
char *
sig_format (PKT_public_key *pk, PKT_signature *sig, gpg_error_t sig_status,
int print_without_key, int extended)
{
estream_t fp;
char *s;
fp = es_fopenmem (0, "rw,samethread");
if (! fp)
log_fatal ("Error creating memory stream\n");
sig_print (fp, pk, sig, sig_status, print_without_key, extended);
es_fputc (0, fp);
if (es_fclose_snatch (fp, (void **) &s, NULL))
log_fatal ("error snatching memory stream\n");
if (s[strlen (s) - 1] == '\n')
s[strlen (s) - 1] = '\0';
return s;
}
/* Order two signatures. The actual ordering isn't important. Our
goal is to ensure that identical signatures occur together. */
static int
sig_comparison (const void *av, const void *bv)
{
const KBNODE an = *(const KBNODE *) av;
const KBNODE bn = *(const KBNODE *) bv;
const PKT_signature *a;
const PKT_signature *b;
int ndataa;
int ndatab;
int i;
assert (an->pkt->pkttype == PKT_SIGNATURE);
assert (bn->pkt->pkttype == PKT_SIGNATURE);
a = an->pkt->pkt.signature;
b = bn->pkt->pkt.signature;
if (a->digest_algo < b->digest_algo)
return -1;
if (a->digest_algo > b->digest_algo)
return 1;
ndataa = pubkey_get_nsig (a->pubkey_algo);
ndatab = pubkey_get_nsig (a->pubkey_algo);
assert (ndataa == ndatab);
for (i = 0; i < ndataa; i ++)
{
int c = gcry_mpi_cmp (a->data[i], b->data[i]);
if (c != 0)
return c;
}
/* Okay, they are equal. */
return 0;
}
/* Check that a keyblock is okay and possibly repair some damage.
Concretely:
- Detect duplicate signatures and remove them.
- Detect out of order signatures and relocate them (e.g., a sig
over a user id located under a subkey)
Note: this function does not remove signatures that don't belong or
components that are not signed! (Although it would be trivial to
do.)
If ONLY_SELFSIGS is true, then this function only reorders self
signatures (it still checks all signatures for duplicates,
however).
Returns 1 if the keyblock was modified, 0 otherwise.
*/
int
keyblock_check_sigs (KBNODE kb, int only_selfsigs)
{
gpg_error_t err;
PKT_public_key *pk;
u32 pk_keyid[2];
KBNODE n, n_next, *n_prevp, n2;
char *pending_desc = NULL;
PKT_public_key *issuer;
KBNODE current_component = NULL;
int dups = 0;
int missing_issuer = 0;
int reordered = 0;
int bad_signature = 0;
int modified = 0;
assert (kb->pkt->pkttype == PKT_PUBLIC_KEY);
pk = kb->pkt->pkt.public_key;
keyid_from_pk (pk, pk_keyid);
/* First we look for duplicates. */
{
int nsigs = 0;
KBNODE *sigs;
int i;
int last_i;
/* Count the sigs. */
for (n = kb; n; n = n->next)
if (is_deleted_kbnode (n))
continue;
else if (n->pkt->pkttype == PKT_SIGNATURE)
nsigs ++;
/* Add them all to the SIGS array. */
sigs = xmalloc_clear (sizeof (*sigs) * nsigs);
i = 0;
for (n = kb; n; n = n->next)
{
if (is_deleted_kbnode (n))
continue;
if (n->pkt->pkttype != PKT_SIGNATURE)
continue;
sigs[i] = n;
i ++;
}
assert (i == nsigs);
qsort (sigs, nsigs, sizeof (sigs[0]), sig_comparison);
last_i = 0;
for (i = 1; i < nsigs; i ++)
{
assert (sigs[last_i]);
assert (sigs[last_i]->pkt->pkttype == PKT_SIGNATURE);
assert (sigs[i]);
assert (sigs[i]->pkt->pkttype == PKT_SIGNATURE);
if (sig_comparison (&sigs[last_i], &sigs[i]) == 0)
/* They are the same. Kill the latter. */
{
if (opt.verbose)
{
PKT_signature *sig = sigs[i]->pkt->pkt.signature;
log_info (_("Signature appears multiple times, deleting duplicate:\n"));
log_info (" sig: class 0x%x, issuer: %s, timestamp: %s (%lld), digest: %02x %02x\n",
sig->sig_class, keystr (sig->keyid),
isotimestamp (sig->timestamp),
(long long) sig->timestamp,
sig->digest_start[0], sig->digest_start[1]);
}
/* Remove sigs[i] from the keyblock. */
{
KBNODE z, *prevp;
int to_kill = i;
for (prevp = &kb, z = kb; z; prevp = &z->next, z = z->next)
if (z == sigs[to_kill])
break;
*prevp = sigs[to_kill]->next;
sigs[to_kill]->next = NULL;
release_kbnode (sigs[to_kill]);
sigs[to_kill] = NULL;
dups ++;
modified = 1;
}
}
else
last_i = i;
}
if (dups)
log_info (_("Ignored %d duplicate signatures (total: %d).\n"),
dups, nsigs);
xfree (sigs);
}
/* Make sure the sigs occur after the component (public key, subkey,
user id) that they sign. */
issuer = NULL;
for (n_prevp = &kb, n = kb; n; n_prevp = &n->next, n = n_next)
{
PACKET *p;
int processed_current_component;
KBNODE sig_over = NULL;
PKT_signature *sig;
int algo;
int pkttype;
gcry_md_hd_t md;
int dump_sig_params = 0;
n_next = n->next;
if (is_deleted_kbnode (n))
continue;
p = n->pkt;
if (issuer != pk)
free_public_key (issuer);
issuer = NULL;
xfree (pending_desc);
pending_desc = NULL;
switch (p->pkttype)
{
case PKT_PUBLIC_KEY:
assert (p->pkt.public_key == pk);
keyid_from_pk (pk, NULL);
log_info ("public key %s: timestamp: %s (%lld)\n",
keystr (pk->keyid),
isotimestamp (pk->timestamp),
(long long) pk->timestamp);
current_component = n;
break;
case PKT_PUBLIC_SUBKEY:
keyid_from_pk (p->pkt.public_key, NULL);
log_info ("subkey %s: timestamp: %s (%lld)\n",
keystr (p->pkt.public_key->keyid),
isotimestamp (p->pkt.public_key->timestamp),
(long long) p->pkt.public_key->timestamp);
current_component = n;
break;
case PKT_USER_ID:
log_info ("user id: %s\n",
p->pkt.user_id->attrib_data
? "[ photo id ]"
: p->pkt.user_id->name);
current_component = n;
break;
case PKT_SIGNATURE:
sig = n->pkt->pkt.signature;
algo = sig->digest_algo;
#if 1
pending_desc = xasprintf (" sig: class: 0x%x, issuer: %s, timestamp: %s (%lld), digest: %02x %02x",
sig->sig_class,
keystr (sig->keyid),
isotimestamp (sig->timestamp),
(long long) sig->timestamp,
sig->digest_start[0], sig->digest_start[1]);
#else
pending_desc = sig_format (pk, sig, GPG_ERR_NO_VALUE, 1, 0);
#endif
if (pk_keyid[0] == sig->keyid[0] && pk_keyid[1] == sig->keyid[1])
issuer = pk;
else
/* Issuer is a different key. */
{
if (only_selfsigs)
continue;
issuer = xmalloc (sizeof (*issuer));
err = get_pubkey (issuer, sig->keyid);
if (err)
{
xfree (issuer);
issuer = NULL;
if (opt.verbose)
{
if (pending_desc)
log_info ("%s", pending_desc);
log_info (_(" Can't check signature allegedly issued by %s: %s\n"),
keystr (sig->keyid), gpg_strerror (err));
}
missing_issuer ++;
break;
}
}
if ((err = openpgp_pk_test_algo (sig->pubkey_algo)))
{
if (pending_desc)
log_info ("%s", pending_desc);
log_info (_(" Unsupported algorithm: %s.\n"),
gpg_strerror (err));
break;
}
if ((err = openpgp_md_test_algo(algo)))
{
if (pending_desc)
log_info ("%s", pending_desc);
log_info (_(" Unimplemented algorithm: %s.\n"),
gpg_strerror (err));
break;
}
/* We iterate over the keyblock. Most likely, the matching
component is the current component so always try that
first. */
processed_current_component = 0;
for (n2 = current_component;
n2;
n2 = (processed_current_component ? n2->next : kb),
processed_current_component = 1)
if (is_deleted_kbnode (n2))
continue;
else if (processed_current_component && n2 == current_component)
/* Don't process it twice. */
continue;
else if (! ((pkttype = n2->pkt->pkttype)
&& (pkttype == PKT_PUBLIC_KEY
|| pkttype == PKT_PUBLIC_SUBKEY
|| pkttype == PKT_USER_ID)))
continue;
else if (sig->sig_class == 0x20)
{
PKT_public_key *k;
if (pkttype != PKT_PUBLIC_KEY)
continue;
k = n2->pkt->pkt.public_key;
/* If issuer != pk, then we (may) have a designated
revoker. */
if (gcry_md_open (&md, algo, 0))
BUG ();
hash_public_key (md, k);
err = check_signature_only_end (issuer, sig, md);
gcry_md_close (md);
if (! err)
{
assert (! sig_over);
sig_over = n2;
break;
}
}
else if (sig->sig_class == 0x28)
/* subkey revocation */
{
PKT_public_key *k;
if (pkttype != PKT_PUBLIC_SUBKEY)
continue;
if (issuer != pk)
/* Definately invalid: class 0x28 keys must be made
by the primary key. */
{
n2 = NULL;
break;
}
k = n2->pkt->pkt.public_key;
if (gcry_md_open (&md, algo, 0))
BUG ();
hash_public_key (md, pk);
hash_public_key (md, k);
err = check_signature_only_end (pk, sig, md);
gcry_md_close (md);
if (! err)
{
assert (! sig_over);
sig_over = n2;
break;
}
}
else if (sig->sig_class == 0x18)
/* key binding */
{
PKT_public_key *k;
if (pkttype != PKT_PUBLIC_SUBKEY)
continue;
if (issuer != pk)
/* Definately invalid: class 0x18 keys must be made
by the primary key. */
{
n2 = NULL;
break;
}
k = n2->pkt->pkt.public_key;
if (gcry_md_open (&md, algo, 0))
BUG ();
hash_public_key (md, pk);
hash_public_key (md, k);
err = check_signature_only_end (pk, sig, md);
gcry_md_close (md);
if (! err)
{
assert (! sig_over);
sig_over = n2;
break;
}
}
else if (sig->sig_class == 0x1f)
/* direct key signature */
{
if (pkttype != PKT_PUBLIC_KEY)
continue;
if (issuer != pk)
/* Definately invalid: class 0x1f keys must be made
by the primary key. */
{
n2 = NULL;
break;
}
if (gcry_md_open (&md, algo, 0 ))
BUG ();
hash_public_key (md, pk);
err = check_signature_only_end (pk, sig, md);
gcry_md_close (md);
if (! err)
{
assert (! sig_over);
sig_over = n2;
break;
}
}
else
/* all other classes */
{
if (pkttype != PKT_USER_ID)
continue;
if (gcry_md_open (&md, algo, 0))
BUG ();
hash_public_key (md, pk);
hash_uid_node (n2, md, sig);
err = check_signature_only_end (issuer, sig, md);
gcry_md_close (md);
if (! err)
{
assert (! sig_over);
sig_over = n2;
break;
}
}
/* n/sig is a signature and n2 is the component (public key,
subkey or user id) that it signs, if any.
current_component is that component that it appears to
apply to (according to the ordering). */
if (current_component == n2)
{
log_info ("%s", pending_desc);
log_info (_(" Good signature over last major component!\n"));
cache_sig_result (sig, 0);
}
else if (n2)
{
assert (n2->pkt->pkttype == PKT_USER_ID
|| n2->pkt->pkttype == PKT_PUBLIC_KEY
|| n2->pkt->pkttype == PKT_PUBLIC_SUBKEY);
log_info ("%s", pending_desc);
log_info (_(" Good signature out of order! (Over %s (%d) '%s')\n"),
n2->pkt->pkttype == PKT_USER_ID
? "user id"
: n2->pkt->pkttype == PKT_PUBLIC_SUBKEY
? "subkey"
: "primary key",
n2->pkt->pkttype,
n2->pkt->pkttype == PKT_USER_ID
? n2->pkt->pkt.user_id->name
: keystr (n2->pkt->pkt.public_key->keyid));
/* Reorder the packets: move the signature n to be just
after n2. */
assert (n_prevp);
*n_prevp = n->next;
n->next = n2->next;
n2->next = n;
cache_sig_result (sig, 0);
reordered ++;
modified = 1;
}
else
{
log_info ("%s", pending_desc);
#if 0
log_info (_(" Bad signature, removing from key block.\n"));
/* Remove the signature n. */
*n_prevp = n->next;
n->next = NULL;
release_kbnode (n);
modified = 1;
#else
log_info (_(" Bad signature.\n"));
#endif
cache_sig_result (sig, GPG_ERR_BAD_SIGNATURE);
if (opt.verbose)
dump_sig_params = 1;
bad_signature ++;
}
if (dump_sig_params)
{
int i;
for (i = 0; i < pubkey_get_nsig (sig->pubkey_algo); i ++)
{
char buffer[1024];
size_t len;
char *printable;
gcry_mpi_print (GCRYMPI_FMT_USG,
buffer, sizeof (buffer), &len,
sig->data[i]);
printable = bin2hex (buffer, len, NULL);
log_info (" %d: %s\n", i, printable);
xfree (printable);
}
}
break;
default:
if (DBG_PACKET)
log_debug ("unhandled packet: %d\n", p->pkttype);
break;
}
}
xfree (pending_desc);
pending_desc = NULL;
if (issuer != pk)
free_public_key (issuer);
issuer = NULL;
if (missing_issuer)
log_info (_("Couldn't check %d signatures due to missing issuer keys.\n"),
missing_issuer);
if (bad_signature)
log_info (_("%d bad signatures.\n"), bad_signature);
if (reordered)
log_info (_("Reordered %d packets.\n"), reordered);
return modified;
}