mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
gpg: Reorder signatures, if appropriate.
XXX -- Signed-off-by: Neal H. Walfield <neal@g10code.com> GnuPG-bug-id: 2236
This commit is contained in:
parent
2f02ed75a9
commit
bff529a3ce
31
g10/gpg.c
31
g10/gpg.c
@ -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:
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
690
g10/sig-check.c
690
g10/sig-check.c
@ -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;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user