diff --git a/g10/key-check.c b/g10/key-check.c index 17f2daef8..c17b12c94 100644 --- a/g10/key-check.c +++ b/g10/key-check.c @@ -1,728 +1,747 @@ /* 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 + * Copyright (C) 2015-2018 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" /* Print PREFIX followed by TEXT. With mode > 0 use log_info, with * mode < 0 use ttyio, else print to stdout. If TEXT is not NULL, it * may be modified by this function. */ static void print_info (int mode, const char *prefix, char *text) { char *p; if (!text) text = ""; else if ((p = strchr (text,'\n'))) *p = 0; /* Strip LF. */ if (mode > 0) log_info ("%s %s\n", prefix, text); else tty_fprintf (mode? NULL:es_stdout, "%s %s\n", prefix, text); } /* 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; /* Signatures with a different help counter are not identical for * our purpose. */ if (a->help_counter < b->help_counter) return -1; if (a->help_counter > b->help_counter) return 1; 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; } +static gpg_error_t +remove_duplicate_sigs (kbnode_t kb, int *dups, int *modified) +{ + gpg_error_t err; + kbnode_t n; + int nsigs; + kbnode_t *sigs; /* Allocated array with the signature packet. */ + int i; + int last_i; + int block; + PKT_signature *sig; + + /* 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) + { + err = gpg_error_from_syserror (); + log_error (_("error allocating memory: %s\n"), gpg_strerror (err)); + return err; + } + + block = 0; + i = 0; + for (n = kb; n; n = n->next) + { + if (is_deleted_kbnode (n)) + continue; + + if (n->pkt->pkttype != PKT_SIGNATURE) + { + switch (n->pkt->pkttype) + { + case PKT_PUBLIC_SUBKEY: + case PKT_SECRET_SUBKEY: + case PKT_USER_ID: + case PKT_ATTRIBUTE: + /* Bump the block number so that we only consider + * signatures below the same object as duplicates. */ + block++; + break; + default: + break; + } + continue; + } + sig = n->pkt->pkt.signature; + sig->help_counter = block; + sigs[i++] = n; + } + 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) + { + 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_t 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); + 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). * * Allowed values for MODE are: * -1 - print to the TTY * 0 - print to stdout * 1 - use log_info. * * Returns true if the keyblock was modified. */ int key_check_all_keysigs (ctrl_t ctrl, int mode, 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; PKT_signature *sig; 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; - int block; - - /* 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; - block = 0; - for (n = kb; n; n = n->next) - { - if (is_deleted_kbnode (n)) - continue; - - if (n->pkt->pkttype != PKT_SIGNATURE) - { - switch (n->pkt->pkttype) - { - case PKT_PUBLIC_SUBKEY: - case PKT_SECRET_SUBKEY: - case PKT_USER_ID: - case PKT_ATTRIBUTE: - /* Bump the block number so that we only consider - * signatures below the same object as duplicates. */ - block++; - break; - default: - break; - } - continue; - } - sig = n->pkt->pkt.signature; - sig->help_counter = block; - sigs[i] = n; - i ++; - } - log_assert (i == nsigs); - - qsort (sigs, nsigs, sizeof (sigs[0]), sig_comparison); + if (remove_duplicate_sigs (kb, &dups, &modified)) + goto leave; /* Error */ - 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) - { - 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. */ + /* Now make sure the sigs occur after the component (aka block) + * (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; 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); log_info (_("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); log_info (_("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 (DBG_PACKET && ((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) { log_debug ("uid "); print_utf8_buffer (log_get_stream (), last_printed_component ->pkt->pkt.user_id->name, last_printed_component ->pkt->pkt.user_id->len); log_flush (); } else if (last_printed_component->pkt->pkttype == PKT_PUBLIC_KEY) log_debug ("pub %s\n", pk_keyid_str (last_printed_component ->pkt->pkt.public_key)); else log_debug ("sub %s\n", pk_keyid_str (last_printed_component ->pkt->pkt.public_key)); if (modified) { if (is_reordered) log_debug ("%s\n", _(" (reordered signatures follow)")); } } if (DBG_PACKET && modified) keyedit_print_one_sig (ctrl, log_get_stream (), 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_debug (" %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 we reordered signatures we need to de-duplicate again because + * a signature can now be a duplicate in another block. */ + if (reordered) + { + if (remove_duplicate_sigs (kb, &dups, &modified)) + goto leave; + } + /* Identify keys / uids that don't have a self-sig. */ { int has_selfsig = 0; PACKET *p; 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; } } } + + leave: if (!opt.quiet) { char prefix[100]; char *p; /* To avoid string changes in 2.2 we strip the LF here. */ snprintf (prefix, sizeof prefix, _("key %s:\n"), pk_keyid_str (pk)); p = strrchr (prefix, '\n'); if (p) *p = 0; if (dups) { p = xtryasprintf (ngettext ("%d duplicate signature removed\n", "%d duplicate signatures removed\n", dups), dups); print_info (mode, prefix, p); xfree (p); } if (missing_issuer) { p = xtryasprintf (ngettext ("%d signature not checked due to a missing key\n", "%d signatures not checked due to missing keys\n", missing_issuer), missing_issuer); print_info (mode, prefix, p); xfree (p); } if (bad_signature) { p = xtryasprintf (ngettext ("%d bad signature\n", "%d bad signatures\n", bad_signature), bad_signature); print_info (mode, prefix, p); xfree (p); } if (reordered) { p = xtryasprintf (ngettext ("%d signature reordered\n", "%d signatures reordered\n", reordered), reordered); print_info (mode, prefix, p); xfree (p); } if (only_selfsigs && (bad_signature || reordered)) { p = xtryasprintf (_("Warning: errors found and only checked self-signatures," " run '%s' to check all signatures.\n"), "check"); print_info (mode, prefix, p); xfree (p); } } return modified; }