From 404fa8211b6188a0abe83ef43a4b44d528c0b035 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Tue, 13 Jun 2017 15:34:03 +0200 Subject: [PATCH] gpg: Refactor key checking and fixing. * g10/Makefile.am (gpg_sources): Add new files. * g10/gpgcompose.c (keyedit_print_one_sig): New stub. * g10/keyedit.c (sig_comparison): Move to new module. (check_all_keysigs): Likewise. (fix_keyblock): Adapt callsite. (keyedit_menu): Likewise. * g10/key-check.c: New file. * g10/key-check.h: Likewise. GnuPG-bug-id: 2236 Signed-off-by: Justus Winter --- g10/Makefile.am | 3 +- g10/gpgcompose.c | 18 ++ g10/key-check.c | 640 +++++++++++++++++++++++++++++++++++++++++++++++ g10/key-check.h | 28 +++ g10/keyedit.c | 618 +-------------------------------------------- 5 files changed, 693 insertions(+), 614 deletions(-) create mode 100644 g10/key-check.c create mode 100644 g10/key-check.h diff --git a/g10/Makefile.am b/g10/Makefile.am index 9994ea191..d5c92f896 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -150,7 +150,8 @@ gpg_sources = server.c \ call-agent.c call-agent.h \ trust.c $(trust_source) $(tofu_source) \ $(card_source) \ - exec.c exec.h + exec.c exec.h \ + key-check.c key-check.h gpg_SOURCES = gpg.c \ keyedit.c keyedit.h \ diff --git a/g10/gpgcompose.c b/g10/gpgcompose.c index 8e29c2fcd..f38d75579 100644 --- a/g10/gpgcompose.c +++ b/g10/gpgcompose.c @@ -3063,3 +3063,21 @@ show_basic_key_info (ctrl_t ctrl, KBNODE keyblock) (void)ctrl; (void) keyblock; } + +int +keyedit_print_one_sig (ctrl_t ctrl, int rc, kbnode_t keyblock, kbnode_t node, + int *inv_sigs, int *no_key, int *oth_err, + int is_selfsig, int print_without_key, int extended) +{ + (void) ctrl; + (void) rc; + (void) keyblock; + (void) node; + (void) inv_sigs; + (void) no_key; + (void) oth_err; + (void) is_selfsig; + (void) print_without_key; + (void) extended; + return 0; +} diff --git a/g10/key-check.c b/g10/key-check.c new file mode 100644 index 000000000..a22394de8 --- /dev/null +++ b/g10/key-check.c @@ -0,0 +1,640 @@ +/* key-check.c - Detect and fix various problems with keys + * Copyright (C) 1998-2010 Free Software Foundation, Inc. + * Copyright (C) 1998-2017 Werner Koch + * Copyright (C) 2015-2017 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include + +#include "gpg.h" +#include "options.h" +#include "packet.h" +#include "keydb.h" +#include "main.h" +#include "../common/ttyio.h" +#include "../common/i18n.h" +#include "keyedit.h" + +#include "key-check.h" + +/* 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; + + log_assert (an->pkt->pkttype == PKT_SIGNATURE); + log_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 (b->pubkey_algo); + if (ndataa != ndatab) + return (ndataa < ndatab)? -1 : 1; + + 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; +} + +/* Perform a few sanity checks on 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 user id X located under subkey Y). + + Note: this function does not remove signatures that don't belong or + components that are not signed! (Although it would be trivial to + do so.) + + 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 +key_check_all_keysigs (ctrl_t ctrl, kbnode_t kb, + int only_selected, int only_selfsigs) +{ + gpg_error_t err; + PKT_public_key *pk; + KBNODE n, n_next, *n_prevp, n2; + char *pending_desc = NULL; + PKT_public_key *issuer; + KBNODE last_printed_component; + KBNODE current_component = NULL; + int dups = 0; + int missing_issuer = 0; + int reordered = 0; + int bad_signature = 0; + int missing_selfsig = 0; + int modified = 0; + + log_assert (kb->pkt->pkttype == PKT_PUBLIC_KEY); + pk = kb->pkt->pkt.public_key; + + /* First we look for duplicates. */ + { + int nsigs; + kbnode_t *sigs; + int i; + int last_i; + + /* Count the sigs. */ + for (nsigs = 0, n = kb; n; n = n->next) + { + if (is_deleted_kbnode (n)) + continue; + else if (n->pkt->pkttype == PKT_SIGNATURE) + nsigs ++; + } + + if (!nsigs) + return 0; /* No signatures at all. */ + + /* Add them all to the SIGS array. */ + sigs = xtrycalloc (nsigs, sizeof *sigs); + if (!sigs) + { + log_error (_("error allocating memory: %s\n"), + gpg_strerror (gpg_error_from_syserror ())); + return 0; + } + + 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 ++; + } + log_assert (i == nsigs); + + qsort (sigs, nsigs, sizeof (sigs[0]), sig_comparison); + + last_i = 0; + for (i = 1; i < nsigs; i ++) + { + log_assert (sigs[last_i]); + log_assert (sigs[last_i]->pkt->pkttype == PKT_SIGNATURE); + log_assert (sigs[i]); + log_assert (sigs[i]->pkt->pkttype == PKT_SIGNATURE); + + if (sig_comparison (&sigs[last_i], &sigs[i]) == 0) + /* They are the same. Kill the latter. */ + { + if (DBG_PACKET) + { + PKT_signature *sig = sigs[i]->pkt->pkt.signature; + + log_debug ("Signature appears multiple times, " + "deleting duplicate:\n"); + log_debug (" 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 = last_i; + last_i = 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; + } + + xfree (sigs); + } + + /* Make sure the sigs occur after the component (public key, subkey, + user id) that they sign. */ + issuer = NULL; + last_printed_component = NULL; + for (n_prevp = &kb, n = kb; + n; + /* If we moved n, then n_prevp is need valid. */ + n_prevp = (n->next == n_next ? &n->next : n_prevp), n = n_next) + { + PACKET *p; + int processed_current_component; + PKT_signature *sig; + int rc; + int dump_sig_params = 0; + + n_next = n->next; + + if (is_deleted_kbnode (n)) + continue; + + p = n->pkt; + + if (issuer && issuer != pk) + { + free_public_key (issuer); + issuer = NULL; + } + + xfree (pending_desc); + pending_desc = NULL; + + switch (p->pkttype) + { + case PKT_PUBLIC_KEY: + log_assert (p->pkt.public_key == pk); + if (only_selected && ! (n->flag & NODFLG_SELKEY)) + { + current_component = NULL; + break; + } + + if (DBG_PACKET) + log_debug ("public key %s: timestamp: %s (%lld)\n", + pk_keyid_str (pk), + isotimestamp (pk->timestamp), + (long long) pk->timestamp); + current_component = n; + break; + case PKT_PUBLIC_SUBKEY: + if (only_selected && ! (n->flag & NODFLG_SELKEY)) + { + current_component = NULL; + break; + } + + if (DBG_PACKET) + log_debug ("subkey %s: timestamp: %s (%lld)\n", + pk_keyid_str (p->pkt.public_key), + isotimestamp (p->pkt.public_key->timestamp), + (long long) p->pkt.public_key->timestamp); + current_component = n; + break; + case PKT_USER_ID: + if (only_selected && ! (n->flag & NODFLG_SELUID)) + { + current_component = NULL; + break; + } + + if (DBG_PACKET) + log_debug ("user id: %s\n", + p->pkt.user_id->attrib_data + ? "[ photo id ]" + : p->pkt.user_id->name); + current_component = n; + break; + case PKT_SIGNATURE: + if (! current_component) + /* The current component is not selected, don't check the + sigs under it. */ + break; + + sig = n->pkt->pkt.signature; + + 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]); + + + if (keyid_cmp (pk_keyid (pk), sig->keyid) == 0) + issuer = pk; + else /* Issuer is a different key. */ + { + if (only_selfsigs) + continue; + + issuer = xmalloc (sizeof (*issuer)); + err = get_pubkey (ctrl, issuer, sig->keyid); + if (err) + { + xfree (issuer); + issuer = NULL; + if (DBG_PACKET) + { + if (pending_desc) + log_debug ("%s", pending_desc); + log_debug (" 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 (DBG_PACKET && pending_desc) + log_debug ("%s", pending_desc); + tty_printf (_("can't check signature with unsupported" + " public-key algorithm (%d): %s.\n"), + sig->pubkey_algo, gpg_strerror (err)); + break; + } + if ((err = openpgp_md_test_algo (sig->digest_algo))) + { + if (DBG_PACKET && pending_desc) + log_debug ("%s", pending_desc); + tty_printf (_("can't check signature with unsupported" + " message-digest algorithm %d: %s.\n"), + sig->digest_algo, 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 + { + err = check_signature_over_key_or_uid (ctrl, + issuer, sig, kb, n2->pkt, + NULL, NULL); + if (! err) + 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) + { + if (DBG_PACKET) + { + log_debug ("%s", pending_desc); + log_debug (" Good signature over last key or uid!\n"); + } + + rc = 0; + } + else if (n2) + { + log_assert (n2->pkt->pkttype == PKT_USER_ID + || n2->pkt->pkttype == PKT_PUBLIC_KEY + || n2->pkt->pkttype == PKT_PUBLIC_SUBKEY); + + if (DBG_PACKET) + { + log_debug ("%s", pending_desc); + log_debug (" 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 + : pk_keyid_str (n2->pkt->pkt.public_key)); + } + + /* Reorder the packets: move the signature n to be just + after n2. */ + + /* Unlink the signature. */ + log_assert (n_prevp); + *n_prevp = n->next; + + /* Insert the sig immediately after the component. */ + n->next = n2->next; + n2->next = n; + + reordered ++; + modified = 1; + + rc = 0; + } + else + { + if (DBG_PACKET) + { + log_debug ("%s", pending_desc); + log_debug (" Bad signature.\n"); + } + + if (DBG_PACKET) + dump_sig_params = 1; + + bad_signature ++; + + rc = GPG_ERR_BAD_SIGNATURE; + } + + /* We don't cache the result here, because we haven't + completely checked that the signature is legitimate. For + instance, if we have a revocation certificate on Alice's + key signed by Bob, the signature may be good, but we + haven't checked that Bob is a designated revoker. */ + /* cache_sig_result (sig, rc); */ + + { + int has_selfsig = 0; + if (! rc && issuer == pk) + { + if (n2->pkt->pkttype == PKT_PUBLIC_KEY + && (/* Direct key signature. */ + sig->sig_class == 0x1f + /* Key revocation signature. */ + || sig->sig_class == 0x20)) + has_selfsig = 1; + if (n2->pkt->pkttype == PKT_PUBLIC_SUBKEY + && (/* Subkey binding sig. */ + sig->sig_class == 0x18 + /* Subkey revocation sig. */ + || sig->sig_class == 0x28)) + has_selfsig = 1; + if (n2->pkt->pkttype == PKT_USER_ID + && (/* Certification sigs. */ + sig->sig_class == 0x10 + || sig->sig_class == 0x11 + || sig->sig_class == 0x12 + || sig->sig_class == 0x13 + /* Certification revocation sig. */ + || sig->sig_class == 0x30)) + has_selfsig = 1; + } + + if ((n2 && n2 != last_printed_component) + || (! n2 && last_printed_component != current_component)) + { + int is_reordered = n2 && n2 != current_component; + if (n2) + last_printed_component = n2; + else + last_printed_component = current_component; + + if (!modified) + ; + else if (last_printed_component->pkt->pkttype == PKT_USER_ID) + { + tty_printf ("uid "); + tty_print_utf8_string (last_printed_component + ->pkt->pkt.user_id->name, + last_printed_component + ->pkt->pkt.user_id->len); + } + else if (last_printed_component->pkt->pkttype + == PKT_PUBLIC_KEY) + tty_printf ("pub %s", + pk_keyid_str (last_printed_component + ->pkt->pkt.public_key)); + else + tty_printf ("sub %s", + pk_keyid_str (last_printed_component + ->pkt->pkt.public_key)); + + if (modified) + { + if (is_reordered) + tty_printf (_(" (reordered signatures follow)")); + tty_printf ("\n"); + } + } + + if (modified) + keyedit_print_one_sig (ctrl, rc, kb, n, NULL, NULL, NULL, + has_selfsig, 0, only_selfsigs); + } + + 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; + + /* Identify keys / uids that don't have a self-sig. */ + { + int has_selfsig = 0; + PACKET *p; + PKT_signature *sig; + + current_component = NULL; + for (n = kb; n; n = n->next) + { + if (is_deleted_kbnode (n)) + continue; + + p = n->pkt; + + switch (p->pkttype) + { + case PKT_PUBLIC_KEY: + case PKT_PUBLIC_SUBKEY: + case PKT_USER_ID: + if (current_component && ! has_selfsig) + missing_selfsig ++; + current_component = n; + has_selfsig = 0; + break; + + case PKT_SIGNATURE: + if (! current_component || has_selfsig) + break; + + sig = n->pkt->pkt.signature; + + if (! (sig->flags.checked && sig->flags.valid)) + break; + + if (keyid_cmp (pk_keyid (pk), sig->keyid) != 0) + /* Different issuer, couldn't be a self-sig. */ + break; + + if (current_component->pkt->pkttype == PKT_PUBLIC_KEY + && (/* Direct key signature. */ + sig->sig_class == 0x1f + /* Key revocation signature. */ + || sig->sig_class == 0x20)) + has_selfsig = 1; + if (current_component->pkt->pkttype == PKT_PUBLIC_SUBKEY + && (/* Subkey binding sig. */ + sig->sig_class == 0x18 + /* Subkey revocation sig. */ + || sig->sig_class == 0x28)) + has_selfsig = 1; + if (current_component->pkt->pkttype == PKT_USER_ID + && (/* Certification sigs. */ + sig->sig_class == 0x10 + || sig->sig_class == 0x11 + || sig->sig_class == 0x12 + || sig->sig_class == 0x13 + /* Certification revocation sig. */ + || sig->sig_class == 0x30)) + has_selfsig = 1; + + break; + + default: + if (current_component && ! has_selfsig) + missing_selfsig ++; + current_component = NULL; + } + } + } + + if (dups || missing_issuer || bad_signature || reordered) + tty_printf (_("key %s:\n"), pk_keyid_str (pk)); + + if (dups) + tty_printf (ngettext ("%d duplicate signature removed\n", + "%d duplicate signatures removed\n", dups), dups); + if (missing_issuer) + tty_printf (ngettext ("%d signature not checked due to a missing key\n", + "%d signatures not checked due to missing keys\n", + missing_issuer), missing_issuer); + if (bad_signature) + tty_printf (ngettext ("%d bad signature\n", + "%d bad signatures\n", + bad_signature), bad_signature); + if (reordered) + tty_printf (ngettext ("%d signature reordered\n", + "%d signatures reordered\n", + reordered), reordered); + + if (only_selfsigs && (bad_signature || reordered)) + tty_printf (_("Warning: errors found and only checked self-signatures," + " run '%s' to check all signatures.\n"), "check"); + + return modified; +} diff --git a/g10/key-check.h b/g10/key-check.h new file mode 100644 index 000000000..3e4dd1031 --- /dev/null +++ b/g10/key-check.h @@ -0,0 +1,28 @@ +/* key-check.h - Detect and fix various problems with keys + * Copyright (C) 2017 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef GNUPG_G10_PACKET_TOOLS_H +#define GNUPG_G10_PACKET_TOOLS_H + +#include "gpg.h" + +int key_check_all_keysigs (ctrl_t ctrl, kbnode_t kb, + int only_selected, int only_selfsigs); + +#endif /* GNUPG_G10_PACKET_TOOLS_H */ diff --git a/g10/keyedit.c b/g10/keyedit.c index 30cf012d9..29fe4667a 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -48,6 +48,7 @@ #include "call-agent.h" #include "../common/host2net.h" #include "tofu.h" +#include "key-check.h" #include "keyedit.h" static void show_prefs (PKT_user_id * uid, PKT_signature * selfsig, @@ -322,615 +323,6 @@ print_and_check_one_sig (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node, } - -/* 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; - - log_assert (an->pkt->pkttype == PKT_SIGNATURE); - log_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 (b->pubkey_algo); - if (ndataa != ndatab) - return (ndataa < ndatab)? -1 : 1; - - 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; -} - -/* Perform a few sanity checks on 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 user id X located under subkey Y). - - Note: this function does not remove signatures that don't belong or - components that are not signed! (Although it would be trivial to - do so.) - - 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. */ -static int -check_all_keysigs (ctrl_t ctrl, kbnode_t kb, - int only_selected, int only_selfsigs) -{ - gpg_error_t err; - PKT_public_key *pk; - KBNODE n, n_next, *n_prevp, n2; - char *pending_desc = NULL; - PKT_public_key *issuer; - KBNODE last_printed_component; - KBNODE current_component = NULL; - int dups = 0; - int missing_issuer = 0; - int reordered = 0; - int bad_signature = 0; - int missing_selfsig = 0; - int modified = 0; - - log_assert (kb->pkt->pkttype == PKT_PUBLIC_KEY); - pk = kb->pkt->pkt.public_key; - - /* First we look for duplicates. */ - { - int nsigs; - kbnode_t *sigs; - int i; - int last_i; - - /* Count the sigs. */ - for (nsigs = 0, n = kb; n; n = n->next) - { - if (is_deleted_kbnode (n)) - continue; - else if (n->pkt->pkttype == PKT_SIGNATURE) - nsigs ++; - } - - if (!nsigs) - return 0; /* No signatures at all. */ - - /* Add them all to the SIGS array. */ - sigs = xtrycalloc (nsigs, sizeof *sigs); - if (!sigs) - { - log_error (_("error allocating memory: %s\n"), - gpg_strerror (gpg_error_from_syserror ())); - return 0; - } - - 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 ++; - } - log_assert (i == nsigs); - - qsort (sigs, nsigs, sizeof (sigs[0]), sig_comparison); - - last_i = 0; - for (i = 1; i < nsigs; i ++) - { - log_assert (sigs[last_i]); - log_assert (sigs[last_i]->pkt->pkttype == PKT_SIGNATURE); - log_assert (sigs[i]); - log_assert (sigs[i]->pkt->pkttype == PKT_SIGNATURE); - - if (sig_comparison (&sigs[last_i], &sigs[i]) == 0) - /* They are the same. Kill the latter. */ - { - if (DBG_PACKET) - { - PKT_signature *sig = sigs[i]->pkt->pkt.signature; - - log_debug ("Signature appears multiple times, " - "deleting duplicate:\n"); - log_debug (" 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 = last_i; - last_i = 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; - } - - xfree (sigs); - } - - /* Make sure the sigs occur after the component (public key, subkey, - user id) that they sign. */ - issuer = NULL; - last_printed_component = NULL; - for (n_prevp = &kb, n = kb; - n; - /* If we moved n, then n_prevp is need valid. */ - n_prevp = (n->next == n_next ? &n->next : n_prevp), n = n_next) - { - PACKET *p; - int processed_current_component; - PKT_signature *sig; - int rc; - int dump_sig_params = 0; - - n_next = n->next; - - if (is_deleted_kbnode (n)) - continue; - - p = n->pkt; - - if (issuer && issuer != pk) - { - free_public_key (issuer); - issuer = NULL; - } - - xfree (pending_desc); - pending_desc = NULL; - - switch (p->pkttype) - { - case PKT_PUBLIC_KEY: - log_assert (p->pkt.public_key == pk); - if (only_selected && ! (n->flag & NODFLG_SELKEY)) - { - current_component = NULL; - break; - } - - if (DBG_PACKET) - log_debug ("public key %s: timestamp: %s (%lld)\n", - pk_keyid_str (pk), - isotimestamp (pk->timestamp), - (long long) pk->timestamp); - current_component = n; - break; - case PKT_PUBLIC_SUBKEY: - if (only_selected && ! (n->flag & NODFLG_SELKEY)) - { - current_component = NULL; - break; - } - - if (DBG_PACKET) - log_debug ("subkey %s: timestamp: %s (%lld)\n", - pk_keyid_str (p->pkt.public_key), - isotimestamp (p->pkt.public_key->timestamp), - (long long) p->pkt.public_key->timestamp); - current_component = n; - break; - case PKT_USER_ID: - if (only_selected && ! (n->flag & NODFLG_SELUID)) - { - current_component = NULL; - break; - } - - if (DBG_PACKET) - log_debug ("user id: %s\n", - p->pkt.user_id->attrib_data - ? "[ photo id ]" - : p->pkt.user_id->name); - current_component = n; - break; - case PKT_SIGNATURE: - if (! current_component) - /* The current component is not selected, don't check the - sigs under it. */ - break; - - sig = n->pkt->pkt.signature; - - 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]); - - - if (keyid_cmp (pk_keyid (pk), sig->keyid) == 0) - issuer = pk; - else /* Issuer is a different key. */ - { - if (only_selfsigs) - continue; - - issuer = xmalloc (sizeof (*issuer)); - err = get_pubkey (ctrl, issuer, sig->keyid); - if (err) - { - xfree (issuer); - issuer = NULL; - if (DBG_PACKET) - { - if (pending_desc) - log_debug ("%s", pending_desc); - log_debug (" 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 (DBG_PACKET && pending_desc) - log_debug ("%s", pending_desc); - tty_printf (_("can't check signature with unsupported" - " public-key algorithm (%d): %s.\n"), - sig->pubkey_algo, gpg_strerror (err)); - break; - } - if ((err = openpgp_md_test_algo (sig->digest_algo))) - { - if (DBG_PACKET && pending_desc) - log_debug ("%s", pending_desc); - tty_printf (_("can't check signature with unsupported" - " message-digest algorithm %d: %s.\n"), - sig->digest_algo, 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 - { - err = check_signature_over_key_or_uid (ctrl, - issuer, sig, kb, n2->pkt, - NULL, NULL); - if (! err) - 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) - { - if (DBG_PACKET) - { - log_debug ("%s", pending_desc); - log_debug (" Good signature over last key or uid!\n"); - } - - rc = 0; - } - else if (n2) - { - log_assert (n2->pkt->pkttype == PKT_USER_ID - || n2->pkt->pkttype == PKT_PUBLIC_KEY - || n2->pkt->pkttype == PKT_PUBLIC_SUBKEY); - - if (DBG_PACKET) - { - log_debug ("%s", pending_desc); - log_debug (" 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 - : pk_keyid_str (n2->pkt->pkt.public_key)); - } - - /* Reorder the packets: move the signature n to be just - after n2. */ - - /* Unlink the signature. */ - log_assert (n_prevp); - *n_prevp = n->next; - - /* Insert the sig immediately after the component. */ - n->next = n2->next; - n2->next = n; - - reordered ++; - modified = 1; - - rc = 0; - } - else - { - if (DBG_PACKET) - { - log_debug ("%s", pending_desc); - log_debug (" Bad signature.\n"); - } - - if (DBG_PACKET) - dump_sig_params = 1; - - bad_signature ++; - - rc = GPG_ERR_BAD_SIGNATURE; - } - - /* We don't cache the result here, because we haven't - completely checked that the signature is legitimate. For - instance, if we have a revocation certificate on Alice's - key signed by Bob, the signature may be good, but we - haven't checked that Bob is a designated revoker. */ - /* cache_sig_result (sig, rc); */ - - { - int has_selfsig = 0; - if (! rc && issuer == pk) - { - if (n2->pkt->pkttype == PKT_PUBLIC_KEY - && (/* Direct key signature. */ - sig->sig_class == 0x1f - /* Key revocation signature. */ - || sig->sig_class == 0x20)) - has_selfsig = 1; - if (n2->pkt->pkttype == PKT_PUBLIC_SUBKEY - && (/* Subkey binding sig. */ - sig->sig_class == 0x18 - /* Subkey revocation sig. */ - || sig->sig_class == 0x28)) - has_selfsig = 1; - if (n2->pkt->pkttype == PKT_USER_ID - && (/* Certification sigs. */ - sig->sig_class == 0x10 - || sig->sig_class == 0x11 - || sig->sig_class == 0x12 - || sig->sig_class == 0x13 - /* Certification revocation sig. */ - || sig->sig_class == 0x30)) - has_selfsig = 1; - } - - if ((n2 && n2 != last_printed_component) - || (! n2 && last_printed_component != current_component)) - { - int is_reordered = n2 && n2 != current_component; - if (n2) - last_printed_component = n2; - else - last_printed_component = current_component; - - if (!modified) - ; - else if (last_printed_component->pkt->pkttype == PKT_USER_ID) - { - tty_printf ("uid "); - tty_print_utf8_string (last_printed_component - ->pkt->pkt.user_id->name, - last_printed_component - ->pkt->pkt.user_id->len); - } - else if (last_printed_component->pkt->pkttype - == PKT_PUBLIC_KEY) - tty_printf ("pub %s", - pk_keyid_str (last_printed_component - ->pkt->pkt.public_key)); - else - tty_printf ("sub %s", - pk_keyid_str (last_printed_component - ->pkt->pkt.public_key)); - - if (modified) - { - if (is_reordered) - tty_printf (_(" (reordered signatures follow)")); - tty_printf ("\n"); - } - } - - if (modified) - keyedit_print_one_sig (ctrl, rc, kb, n, NULL, NULL, NULL, - has_selfsig, 0, only_selfsigs); - } - - 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; - - /* Identify keys / uids that don't have a self-sig. */ - { - int has_selfsig = 0; - PACKET *p; - PKT_signature *sig; - - current_component = NULL; - for (n = kb; n; n = n->next) - { - if (is_deleted_kbnode (n)) - continue; - - p = n->pkt; - - switch (p->pkttype) - { - case PKT_PUBLIC_KEY: - case PKT_PUBLIC_SUBKEY: - case PKT_USER_ID: - if (current_component && ! has_selfsig) - missing_selfsig ++; - current_component = n; - has_selfsig = 0; - break; - - case PKT_SIGNATURE: - if (! current_component || has_selfsig) - break; - - sig = n->pkt->pkt.signature; - - if (! (sig->flags.checked && sig->flags.valid)) - break; - - if (keyid_cmp (pk_keyid (pk), sig->keyid) != 0) - /* Different issuer, couldn't be a self-sig. */ - break; - - if (current_component->pkt->pkttype == PKT_PUBLIC_KEY - && (/* Direct key signature. */ - sig->sig_class == 0x1f - /* Key revocation signature. */ - || sig->sig_class == 0x20)) - has_selfsig = 1; - if (current_component->pkt->pkttype == PKT_PUBLIC_SUBKEY - && (/* Subkey binding sig. */ - sig->sig_class == 0x18 - /* Subkey revocation sig. */ - || sig->sig_class == 0x28)) - has_selfsig = 1; - if (current_component->pkt->pkttype == PKT_USER_ID - && (/* Certification sigs. */ - sig->sig_class == 0x10 - || sig->sig_class == 0x11 - || sig->sig_class == 0x12 - || sig->sig_class == 0x13 - /* Certification revocation sig. */ - || sig->sig_class == 0x30)) - has_selfsig = 1; - - break; - - default: - if (current_component && ! has_selfsig) - missing_selfsig ++; - current_component = NULL; - } - } - } - - if (dups || missing_issuer || bad_signature || reordered) - tty_printf (_("key %s:\n"), pk_keyid_str (pk)); - - if (dups) - tty_printf (ngettext ("%d duplicate signature removed\n", - "%d duplicate signatures removed\n", dups), dups); - if (missing_issuer) - tty_printf (ngettext ("%d signature not checked due to a missing key\n", - "%d signatures not checked due to missing keys\n", - missing_issuer), missing_issuer); - if (bad_signature) - tty_printf (ngettext ("%d bad signature\n", - "%d bad signatures\n", - bad_signature), bad_signature); - if (reordered) - tty_printf (ngettext ("%d signature reordered\n", - "%d signatures reordered\n", - reordered), reordered); - - if (only_selfsigs && (bad_signature || reordered)) - tty_printf (_("Warning: errors found and only checked self-signatures," - " run '%s' to check all signatures.\n"), "check"); - - return modified; -} - - static int sign_mk_attrib (PKT_signature * sig, void *opaque) { @@ -1774,7 +1166,7 @@ fix_keyblock (ctrl_t ctrl, kbnode_t *keyblockp) if (collapse_uids (keyblockp)) changed++; - if (check_all_keysigs (ctrl, *keyblockp, 0, 1)) + if (key_check_all_keysigs (ctrl, *keyblockp, 0, 1)) changed++; reorder_keyblock (*keyblockp); /* If we modified the keyblock, make sure the flags are right. */ @@ -2221,9 +1613,9 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, break; case cmdCHECK: - if (check_all_keysigs (ctrl, keyblock, - count_selected_uids (keyblock), - !strcmp (arg_string, "selfsig"))) + if (key_check_all_keysigs (ctrl, keyblock, + count_selected_uids (keyblock), + !strcmp (arg_string, "selfsig"))) modified = 1; break;