From 257661d6ae0ca376df758c38fabab2316d10e3a9 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 6 Jun 2018 11:50:58 +0200 Subject: gpg: New command --show-keys. * g10/gpg.c (aShowKeys): New const. (opts): New command --show-keys. (main): Implement command. * g10/import.c (import_keys_internal): Don't print stats in show-only mode. (import_one): Be silent in show-only mode. -- Using --import --import-options show-only to look at a key is too cumbersome. Provide this shortcut and also remove some diagnostic cruft in this case. Signed-off-by: Werner Koch --- g10/gpg.c | 11 +++++++++++ g10/import.c | 8 +++++++- 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'g10') diff --git a/g10/gpg.c b/g10/gpg.c index 70bdddfda..499c00537 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -150,6 +150,7 @@ enum cmd_and_opt_values aSearchKeys, aRefreshKeys, aFetchKeys, + aShowKeys, aExport, aExportSecret, aExportSecretSub, @@ -500,6 +501,7 @@ static ARGPARSE_OPTS opts[] = { N_("update all keys from a keyserver")), ARGPARSE_c (aLocateKeys, "locate-keys", "@"), ARGPARSE_c (aFetchKeys, "fetch-keys" , "@" ), + ARGPARSE_c (aShowKeys, "show-keys" , "@" ), ARGPARSE_c (aExportSecret, "export-secret-keys" , "@" ), ARGPARSE_c (aExportSecretSub, "export-secret-subkeys" , "@" ), ARGPARSE_c (aExportSshKey, "export-ssh-key", "@" ), @@ -740,6 +742,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_c (aListKeys, "list-key", "@"), /* alias */ ARGPARSE_c (aListSigs, "list-sig", "@"), /* alias */ ARGPARSE_c (aCheckKeys, "check-sig", "@"), /* alias */ + ARGPARSE_c (aShowKeys, "show-key", "@"), /* alias */ ARGPARSE_s_n (oSkipVerify, "skip-verify", "@"), ARGPARSE_s_n (oSkipHiddenRecipients, "skip-hidden-recipients", "@"), ARGPARSE_s_n (oNoSkipHiddenRecipients, "no-skip-hidden-recipients", "@"), @@ -2642,6 +2645,13 @@ main (int argc, char **argv) greeting=1; break; + case aShowKeys: + set_cmd (&cmd, pargs.r_opt); + opt.import_options |= IMPORT_SHOW; + opt.import_options |= IMPORT_DRY_RUN; + opt.import_options &= ~IMPORT_REPAIR_KEYS; + break; + case aDetachedSign: detached_sig = 1; set_cmd( &cmd, aSign ); break; case aDecryptFiles: multifile=1; /* fall through */ @@ -4638,6 +4648,7 @@ main (int argc, char **argv) case aFastImport: opt.import_options |= IMPORT_FAST; /* fall through */ case aImport: + case aShowKeys: import_keys (ctrl, argc? argv:NULL, argc, NULL, opt.import_options, opt.key_origin, opt.key_origin_url); break; diff --git a/g10/import.c b/g10/import.c index 9fc769df9..d35c720b7 100644 --- a/g10/import.c +++ b/g10/import.c @@ -494,7 +494,9 @@ import_keys_internal (ctrl_t ctrl, iobuf_t inp, char **fnames, int nnames, if (!stats_handle) { - import_print_stats (stats); + if ((options & (IMPORT_SHOW | IMPORT_DRY_RUN)) + != (IMPORT_SHOW | IMPORT_DRY_RUN)) + import_print_stats (stats); import_release_stats_handle (stats); } @@ -1653,6 +1655,10 @@ import_one (ctrl_t ctrl, int any_filter = 0; KEYDB_HANDLE hd = NULL; + /* If show-only is active we don't won't any extra output. */ + if ((options & (IMPORT_SHOW | IMPORT_DRY_RUN))) + silent = 1; + /* Get the key and print some info about it. */ node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); if (!node ) -- cgit v1.2.3 From 344b548dc71657d0285d93f78f17a2663b5e586f Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 6 Jun 2018 15:46:24 +0200 Subject: gpg: Also detect a plaintext packet before an encrypted packet. * g10/mainproc.c (proc_encrypted): Print warning and later force an error. -- Note that when this error is triggered the plaintext from the literal data packet has already been outputted before the BEGIN_DECRYPTION status line. We fail only later to get more information. Callers need to check and act upon the decryption error code anyway. Thanks to Marcus for pointing out this case. GnuPG-bug-id: 4000 Signed-off-by: Werner Koch --- g10/mainproc.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'g10') diff --git a/g10/mainproc.c b/g10/mainproc.c index 5689d73a8..d2ceec2fd 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -615,6 +615,14 @@ static void proc_encrypted (CTX c, PACKET *pkt) { int result = 0; + int early_plaintext = literals_seen; + + if (early_plaintext) + { + log_info (_("WARNING: multiple plaintexts seen\n")); + write_status_errcode ("decryption.early_plaintext", GPG_ERR_BAD_DATA); + /* We fail only later so that we can print some more info first. */ + } if (!opt.quiet) { @@ -734,6 +742,10 @@ proc_encrypted (CTX c, PACKET *pkt) if (!result) result = decrypt_data (c->ctrl, c, pkt->pkt.encrypted, c->dek ); + /* Trigger the deferred error. */ + if (!result && early_plaintext) + result = gpg_error (GPG_ERR_BAD_DATA); + if (result == -1) ; else if (!result -- cgit v1.2.3 From 1bc6b5174248ba4d83d648ef6d6f4550540d1f20 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 7 Jun 2018 10:30:07 +0200 Subject: gpg: Improve verbose output during import. * g10/import.c (chk_self_sigs): Print the subkeyid in addition to the keyid. (delete_inv_parts): Ditto. Signed-off-by: Werner Koch --- g10/import.c | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) (limited to 'g10') diff --git a/g10/import.c b/g10/import.c index d35c720b7..1a98e2aa1 100644 --- a/g10/import.c +++ b/g10/import.c @@ -2750,8 +2750,9 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats) } -/* Loop over the keyblock and check all self signatures. On return - * the following bis in the node flags are set: +/* Loop over the KEYBLOCK and check all self signatures. KEYID is the + * keyid of the primary key for reporting purposes. On return the + * following bits in the node flags are set: * * - NODE_GOOD_SELFSIG :: User ID or subkey has a self-signature * - NODE_BAD_SELFSIG :: Used ID or subkey has an invalid self-signature @@ -2766,17 +2767,22 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats) static int chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, int *non_self) { - kbnode_t n, knode = NULL; + kbnode_t knode = NULL; /* The node of the current subkey. */ + PKT_public_key *subpk = NULL; /* and its packet. */ + kbnode_t bsnode = NULL; /* Subkey binding signature node. */ + u32 bsdate = 0; /* Timestamp of that node. */ + kbnode_t rsnode = NULL; /* Subkey recocation signature node. */ + u32 rsdate = 0; /* Timestamp of tha node. */ PKT_signature *sig; int rc; - u32 bsdate=0, rsdate=0; - kbnode_t bsnode = NULL, rsnode = NULL; + kbnode_t n; for (n=keyblock; (n = find_next_kbnode (n, 0)); ) { if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY) { knode = n; + subpk = knode->pkt->pkt.public_key; bsdate = 0; rsdate = 0; bsnode = NULL; @@ -2865,11 +2871,14 @@ chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, int *non_self) if ( rc ) { if (opt.verbose) - log_info (gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ? - _("key %s: unsupported public key" - " algorithm\n"): - _("key %s: invalid subkey binding\n"), - keystr (keyid)); + { + keyid_from_pk (subpk, NULL); + log_info (gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ? + _("key %s: unsupported public key" + " algorithm\n"): + _("key %s: invalid subkey binding\n"), + keystr_with_sub (keyid, subpk->keyid)); + } n->flag |= NODE_DELETION_MARK; } else @@ -2884,8 +2893,12 @@ chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, int *non_self) one is newer */ bsnode->flag |= NODE_DELETION_MARK; if (opt.verbose) - log_info (_("key %s: removed multiple subkey" - " binding\n"),keystr(keyid)); + { + keyid_from_pk (subpk, NULL); + log_info (_("key %s: removed multiple subkey" + " binding\n"), + keystr_with_sub (keyid, subpk->keyid)); + } } bsnode = n; @@ -2964,6 +2977,7 @@ delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, { kbnode_t node; int nvalid=0, uid_seen=0, subkey_seen=0; + PKT_public_key *pk; for (node=keyblock->next; node; node = node->next ) { @@ -3001,7 +3015,12 @@ delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, || !(node->flag & NODE_GOOD_SELFSIG)) { if (opt.verbose ) - log_info( _("key %s: skipped subkey\n"),keystr(keyid)); + { + pk = node->pkt->pkt.public_key; + keyid_from_pk (pk, NULL); + log_info (_("key %s: skipped subkey\n"), + keystr_with_sub (keyid, pk->keyid)); + } delete_kbnode( node ); /* the subkey */ /* and all following signature packets */ -- cgit v1.2.3 From 26bce2f01d2029ea2b8a8dbbe36118e3c83c5cba Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 7 Jun 2018 17:22:58 +0200 Subject: gpg: Fix import's repair-key duplicate signature detection. * g10/packet.h (PKG_siganture): Add field 'help_counter'. * g10/key-check.c (sig_comparison): Take care of HELP_COUNTER. (key_check_all_keysigs): De-duplicate on a per-block base. -- The key_check_all_keysigs first does a detection of duplicate signature. This is done over all signatures at once. The problem here is for example: key uid_1 sig_uid_1.1 sig_uid_1.2 subkey_1 sig_sub_1.1 subkey_2 sig_sub_2.1 sig_sub_2.2 (duplicate of sig_sub_1.1) Now the de-duplication deletes the first signature and keeps the second. That works in most cases for foreign signature on userids but in the above constellation the code simply removes sig_sub_1.1 so that subkey_1 has no binding signature anymore. In a later step during import the missing binding is detected and subkey_1 is removed because it is not anymore valid. The sig_sub_2.2 will also be removed later because it does not check out for subkey_2 (that is as expected). The fix is to let the de-duplication work only on blocks (ie. within the signatures of a user id or a subkey). This will not detect all duplicates but that does not harm because later steps will detect and remove them. In the above case (with this patch applied) the second phase of key_check_all_keysigs will reorder key signatures and move the duplicate sig_sub_2.2 directly after sig_sub_1.1. This duplicates the signature and for cleanness we should kick the de-duplication process again. This will be done with a followup patch. GnuPG-bug-id: 3994 Signed-off-by: Werner Koch --- g10/key-check.c | 34 +++++++++++++++++++++++++++++----- g10/packet.h | 1 + 2 files changed, 30 insertions(+), 5 deletions(-) (limited to 'g10') diff --git a/g10/key-check.c b/g10/key-check.c index 86b1e769d..17f2daef8 100644 --- a/g10/key-check.c +++ b/g10/key-check.c @@ -72,6 +72,13 @@ sig_comparison (const void *av, const void *bv) 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) @@ -133,6 +140,7 @@ key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb, 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; @@ -143,6 +151,7 @@ key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb, kbnode_t *sigs; int i; int last_i; + int block; /* Count the sigs. */ for (nsigs = 0, n = kb; n; n = n->next) @@ -166,14 +175,31 @@ key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb, } i = 0; + block = 0; for (n = kb; n; n = n->next) { if (is_deleted_kbnode (n)) continue; if (n->pkt->pkttype != PKT_SIGNATURE) - continue; - + { + 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 ++; } @@ -194,7 +220,7 @@ key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb, { if (DBG_PACKET) { - PKT_signature *sig = sigs[i]->pkt->pkt.signature; + sig = sigs[i]->pkt->pkt.signature; log_debug ("Signature appears multiple times, " "deleting duplicate:\n"); @@ -244,7 +270,6 @@ key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb, { PACKET *p; int processed_current_component; - PKT_signature *sig; int rc; int dump_sig_params = 0; @@ -577,7 +602,6 @@ key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb, { int has_selfsig = 0; PACKET *p; - PKT_signature *sig; current_component = NULL; for (n = kb; n; n = n->next) diff --git a/g10/packet.h b/g10/packet.h index e8397eaee..40a8c4bf6 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -234,6 +234,7 @@ typedef struct const byte *trust_regexp; struct revocation_key *revkey; int numrevkeys; + int help_counter; /* Used internally bu some fucntions. */ pka_info_t *pka_info; /* Malloced PKA data or NULL if not available. See also flags.pka_tried. */ char *signers_uid; /* Malloced value of the SIGNERS_UID -- cgit v1.2.3 From 26746fe65d14a00773473c2d0d271406a5105bca Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 7 Jun 2018 18:41:17 +0200 Subject: gpg: Improve import's repair-key duplicate signature detection. * g10/key-check.c (key_check_all_keysigs): Factor some code out to ... (remove_duplicate_sigs): new. (key_check_all_keysigs): Call remove_duplicate_sigs again after reordering. -- This is a follupup for commit 26bce2f01d2029ea2b8a8dbbe36118e3c83c5cba to cleanup the code and to add a second de-duplicate step when needed. GnuPG-bug-id: 3994 Signed-off-by: Werner Koch --- g10/key-check.c | 249 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 134 insertions(+), 115 deletions(-) (limited to 'g10') 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,7 +1,7 @@ /* 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. * @@ -101,6 +101,125 @@ sig_comparison (const void *av, const void *bv) } +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: * @@ -146,121 +265,11 @@ key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb, 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; @@ -598,6 +607,14 @@ key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb, 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; @@ -667,6 +684,8 @@ key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb, } } + + leave: if (!opt.quiet) { char prefix[100]; -- cgit v1.2.3 From 13f135c7a252cc46cff96e75968d92b6dc8dce1b Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 8 Jun 2018 10:45:21 +0200 Subject: gpg: Sanitize diagnostic with the original file name. * g10/mainproc.c (proc_plaintext): Sanitize verbose output. -- This fixes a forgotten sanitation of user supplied data in a verbose mode diagnostic. The mention CVE is about using this to inject status-fd lines into the stderr output. Other harm good as well be done. Note that GPGME based applications are not affected because GPGME does not fold status output into stderr. CVE-id: CVE-2018-12020 GnuPG-bug-id: 4012 --- g10/mainproc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'g10') diff --git a/g10/mainproc.c b/g10/mainproc.c index d2ceec2fd..a9da08f74 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -851,7 +851,14 @@ proc_plaintext( CTX c, PACKET *pkt ) if (pt->namelen == 8 && !memcmp( pt->name, "_CONSOLE", 8)) log_info (_("Note: sender requested \"for-your-eyes-only\"\n")); else if (opt.verbose) - log_info (_("original file name='%.*s'\n"), pt->namelen, pt->name); + { + /* We don't use print_utf8_buffer because that would require a + * string change which we don't want in 2.2. It is also not + * clear whether the filename is always utf-8 encoded. */ + char *tmp = make_printable_string (pt->name, pt->namelen, 0); + log_info (_("original file name='%.*s'\n"), (int)strlen (tmp), tmp); + xfree (tmp); + } free_md_filter_context (&c->mfx); if (gcry_md_open (&c->mfx.md, 0, 0)) -- cgit v1.2.3 From d2bc66f241a66cc95140cbb3a07555f6301290ed Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 11 Jun 2018 08:46:37 +0200 Subject: gpg: Set some list options with --show-keys * g10/gpg.c (main): Set some list options. -- The new command --show-keys is commonly used to check the content of a file with keys. In this case it can be expected that all included subkeys and uids are of interested, even when they are already expired or have been revoked. Signed-off-by: Werner Koch --- g10/gpg.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'g10') diff --git a/g10/gpg.c b/g10/gpg.c index 499c00537..8effc535c 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -2650,6 +2650,10 @@ main (int argc, char **argv) opt.import_options |= IMPORT_SHOW; opt.import_options |= IMPORT_DRY_RUN; opt.import_options &= ~IMPORT_REPAIR_KEYS; + opt.list_options |= LIST_SHOW_UNUSABLE_UIDS; + opt.list_options |= LIST_SHOW_UNUSABLE_SUBKEYS; + opt.list_options |= LIST_SHOW_NOTATIONS; + opt.list_options |= LIST_SHOW_POLICY_URLS; break; case aDetachedSign: detached_sig = 1; set_cmd( &cmd, aSign ); break; -- cgit v1.2.3 From 2ddfb5bef920919443309ece9fa2930282bbce85 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Tue, 12 Jun 2018 00:41:59 -0400 Subject: gpg: Add new usage option for drop-subkey filters. * g10/import.c (impex_filter_getval): Add new "usage" property for drop-subkey filter. -- For example, this permits extraction of only encryption-capable subkeys like so: gpg --export-filter 'drop-subkey=usage !~ e' --export $FPR GnuPG-Bug-id: 4019 Signed-off-by: Daniel Kahn Gillmor --- g10/import.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'g10') diff --git a/g10/import.c b/g10/import.c index 1a98e2aa1..28039977b 100644 --- a/g10/import.c +++ b/g10/import.c @@ -1314,6 +1314,16 @@ impex_filter_getval (void *cookie, const char *propname) { result = pk_is_disabled (pk)? "1":"0"; } + else if (!strcmp (propname, "usage")) + { + snprintf (numbuf, sizeof numbuf, "%s%s%s%s%s", + (pk->pubkey_usage & PUBKEY_USAGE_ENC)?"e":"", + (pk->pubkey_usage & PUBKEY_USAGE_SIG)?"s":"", + (pk->pubkey_usage & PUBKEY_USAGE_CERT)?"c":"", + (pk->pubkey_usage & PUBKEY_USAGE_AUTH)?"a":"", + (pk->pubkey_usage & PUBKEY_USAGE_UNKNOWN)?"?":""); + result = numbuf; + } else result = NULL; } -- cgit v1.2.3 From 8f99299a54a0ac09f9c90c1085b704db78973fda Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Tue, 12 Jun 2018 15:54:18 +0900 Subject: card: Fix memory leak for fetch-url sub command. * g10/card-util.c (fetch_url): Release INFO. Signed-off-by: NIIBE Yutaka --- g10/card-util.c | 1 + 1 file changed, 1 insertion(+) (limited to 'g10') diff --git a/g10/card-util.c b/g10/card-util.c index 587f181f2..b7eedc0c8 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -851,6 +851,7 @@ fetch_url (ctrl_t ctrl) } } + agent_release_card_info (&info); return rc; } -- cgit v1.2.3 From fe621cc64b13b00914633630f28b4b417892d629 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 12 Jun 2018 08:44:55 +0200 Subject: gpg: Do not import revocations with --show-keys. * g10/import.c (import_revoke_cert): Add arg 'options'. Take care of IMPORT_DRY_RUN. -- GnuPG-bug-id: 4017 Signed-off-by: Werner Koch --- g10/import.c | 56 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 26 deletions(-) (limited to 'g10') diff --git a/g10/import.c b/g10/import.c index 28039977b..9b693e919 100644 --- a/g10/import.c +++ b/g10/import.c @@ -113,8 +113,8 @@ static int import_secret_one (ctrl_t ctrl, kbnode_t keyblock, struct import_stats_s *stats, int batch, unsigned int options, int for_migration, import_screener_t screener, void *screener_arg); -static int import_revoke_cert (ctrl_t ctrl, - kbnode_t node, struct import_stats_s *stats); +static int import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options, + struct import_stats_s *stats); static int chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, int *non_self); static int delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, @@ -590,7 +590,7 @@ import (ctrl_t ctrl, IOBUF inp, const char* fname,struct import_stats_s *stats, screener, screener_arg); else if (keyblock->pkt->pkttype == PKT_SIGNATURE && IS_KEY_REV (keyblock->pkt->pkt.signature) ) - rc = import_revoke_cert (ctrl, keyblock, stats); + rc = import_revoke_cert (ctrl, keyblock, options, stats); else { log_info (_("skipping block of type %d\n"), keyblock->pkt->pkttype); @@ -2636,7 +2636,8 @@ import_secret_one (ctrl_t ctrl, kbnode_t keyblock, * Import a revocation certificate; this is a single signature packet. */ static int -import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats) +import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options, + struct import_stats_s *stats) { PKT_public_key *pk = NULL; kbnode_t onode; @@ -2726,31 +2727,34 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats) /* insert it */ insert_kbnode( keyblock, clone_kbnode(node), 0 ); - /* and write the keyblock back */ - rc = keydb_update_keyblock (ctrl, hd, keyblock ); - if (rc) - log_error (_("error writing keyring '%s': %s\n"), - keydb_get_resource_name (hd), gpg_strerror (rc) ); - keydb_release (hd); - hd = NULL; - - /* we are ready */ - if (!opt.quiet ) + /* and write the keyblock back unless in dry run mode. */ + if (!(opt.dry_run || (options & IMPORT_DRY_RUN))) { - char *p=get_user_id_native (ctrl, keyid); - log_info( _("key %s: \"%s\" revocation certificate imported\n"), - keystr(keyid),p); - xfree(p); - } - stats->n_revoc++; + rc = keydb_update_keyblock (ctrl, hd, keyblock ); + if (rc) + log_error (_("error writing keyring '%s': %s\n"), + keydb_get_resource_name (hd), gpg_strerror (rc) ); + keydb_release (hd); + hd = NULL; - /* If the key we just revoked was ultimately trusted, remove its - ultimate trust. This doesn't stop the user from putting the - ultimate trust back, but is a reasonable solution for now. */ - if (get_ownertrust (ctrl, pk) == TRUST_ULTIMATE) - clear_ownertrusts (ctrl, pk); + /* we are ready */ + if (!opt.quiet ) + { + char *p=get_user_id_native (ctrl, keyid); + log_info( _("key %s: \"%s\" revocation certificate imported\n"), + keystr(keyid),p); + xfree(p); + } - revalidation_mark (ctrl); + /* If the key we just revoked was ultimately trusted, remove its + * ultimate trust. This doesn't stop the user from putting the + * ultimate trust back, but is a reasonable solution for now. */ + if (get_ownertrust (ctrl, pk) == TRUST_ULTIMATE) + clear_ownertrusts (ctrl, pk); + + revalidation_mark (ctrl); + } + stats->n_revoc++; leave: keydb_release (hd); -- cgit v1.2.3 From cb52eb76b3ba0269742c5322e10a2b5151dafaf2 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 12 Jun 2018 16:11:19 +0200 Subject: Some preparations to eventuallt use gpgrt_argparse. * configure.ac (GNUPG_DEF_COPYRIGHT_LINE: New. * tools/watchgnupg.c (print_version): USe this macro. * common/init.c (_init_common_subsystems): Register argparse functions. Signed-off-by: Werner Koch --- g10/gpg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'g10') diff --git a/g10/gpg.c b/g10/gpg.c index 8effc535c..600f8440d 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -3625,7 +3625,7 @@ main (int argc, char **argv) else { pargs.err = ARGPARSE_PRINT_ERROR; - /* The argparse fucntion calls a plain exit and thus + /* The argparse function calls a plain exit and thus * we need to print a status here. */ write_status_failure ("option-parser", gpg_error(GPG_ERR_GENERAL)); -- cgit v1.2.3 From 386b9c4f25b28fd769d7563f2d86ac3a19cc3011 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 21 Jun 2018 15:06:30 +0200 Subject: gpg: Let --show-keys print revocation certificates. * g10/import.c (list_standalone_revocation): New. (import_revoke_cert): Call new function. -- GnuPG-bug-id: 4018 Signed-off-by: Werner Koch --- g10/import.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 114 insertions(+), 7 deletions(-) (limited to 'g10') diff --git a/g10/import.c b/g10/import.c index 9b693e919..022f077d4 100644 --- a/g10/import.c +++ b/g10/import.c @@ -2632,6 +2632,95 @@ import_secret_one (ctrl_t ctrl, kbnode_t keyblock, } + +/* List the recocation signature as a "rvs" record. SIGRC shows the + * character from the signature verification or 0 if no public key was + * found. */ +static void +list_standalone_revocation (ctrl_t ctrl, PKT_signature *sig, int sigrc) +{ + char *siguid = NULL; + size_t siguidlen = 0; + char *issuer_fpr = NULL; + + if (sigrc != '%' && sigrc != '?' && !opt.fast_list_mode) + { + int nouid; + siguid = get_user_id (ctrl, sig->keyid, &siguidlen, &nouid); + if (nouid) + sigrc = '?'; + } + + if (opt.with_colons) + { + es_fputs ("rvs:", es_stdout); + if (sigrc) + es_putc (sigrc, es_stdout); + es_fprintf (es_stdout, "::%d:%08lX%08lX:%s:%s:::", + sig->pubkey_algo, + (ulong) sig->keyid[0], (ulong) sig->keyid[1], + colon_datestr_from_sig (sig), + colon_expirestr_from_sig (sig)); + + if (siguid) + es_write_sanitized (es_stdout, siguid, siguidlen, ":", NULL); + + es_fprintf (es_stdout, ":%02x%c::", sig->sig_class, + sig->flags.exportable ? 'x' : 'l'); + + if ((issuer_fpr = issuer_fpr_string (sig))) + es_fputs (issuer_fpr, es_stdout); + + es_fprintf (es_stdout, ":::%d:\n", sig->digest_algo); + + if (opt.show_subpackets) + print_subpackets_colon (sig); + } + else /* Human readable. */ + { + es_fputs ("rvs", es_stdout); + es_fprintf (es_stdout, "%c%c %c%c%c%c%c%c %s %s", + 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 (siguid) + { + es_fprintf (es_stdout, " "); + print_utf8_buffer (es_stdout, siguid, siguidlen); + } + es_putc ('\n', es_stdout); + + if (sig->flags.policy_url + && (opt.list_options & LIST_SHOW_POLICY_URLS)) + show_policy_url (sig, 3, 0); + + if (sig->flags.notation && (opt.list_options & LIST_SHOW_NOTATIONS)) + 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)) + show_keyserver_url (sig, 3, 0); + } + + es_fflush (es_stdout); + + xfree (siguid); + xfree (issuer_fpr); +} + + /**************** * Import a revocation certificate; this is a single signature packet. */ @@ -2645,6 +2734,11 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options, KEYDB_HANDLE hd = NULL; u32 keyid[2]; int rc = 0; + int sigrc = 0; + int silent; + + /* No error output for --show-keys. */ + silent = (options & (IMPORT_SHOW | IMPORT_DRY_RUN)); log_assert (!node->next ); log_assert (node->pkt->pkttype == PKT_SIGNATURE ); @@ -2657,15 +2751,16 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options, rc = get_pubkey (ctrl, pk, keyid ); if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY ) { - log_error(_("key %s: no public key -" - " can't apply revocation certificate\n"), keystr(keyid)); + if (!silent) + log_error (_("key %s: no public key -" + " can't apply revocation certificate\n"), keystr(keyid)); rc = 0; goto leave; } else if (rc ) { - log_error(_("key %s: public key not found: %s\n"), - keystr(keyid), gpg_strerror (rc)); + log_error (_("key %s: public key not found: %s\n"), + keystr(keyid), gpg_strerror (rc)); goto leave; } @@ -2702,12 +2797,21 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options, /* it is okay, that node is not in keyblock because * check_key_signature works fine for sig_class 0x20 (KEY_REV) in - * this special case. */ + * this special case. SIGRC is only used for IMPORT_SHOW. */ rc = check_key_signature (ctrl, keyblock, node, NULL); + switch (gpg_err_code (rc)) + { + case 0: sigrc = '!'; break; + case GPG_ERR_BAD_SIGNATURE: sigrc = '-'; break; + case GPG_ERR_NO_PUBKEY: sigrc = '?'; break; + case GPG_ERR_UNUSABLE_PUBKEY: sigrc = '?'; break; + default: sigrc = '%'; break; + } if (rc ) { - log_error( _("key %s: invalid revocation certificate" - ": %s - rejected\n"), keystr(keyid), gpg_strerror (rc)); + if (!silent) + log_error (_("key %s: invalid revocation certificate" + ": %s - rejected\n"), keystr(keyid), gpg_strerror (rc)); goto leave; } @@ -2757,6 +2861,9 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options, stats->n_revoc++; leave: + if ((options & IMPORT_SHOW)) + list_standalone_revocation (ctrl, node->pkt->pkt.signature, sigrc); + keydb_release (hd); release_kbnode( keyblock ); free_public_key( pk ); -- cgit v1.2.3 From b7cd2c2093ae1b47645be50fa1d431a028187cad Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 21 Jun 2018 18:32:13 +0200 Subject: gpg: Print revocation reason for "rvs" records. * g10/import.c (get_revocation_reason): New. (list_standalone_revocation): Extend function. -- Note that this function extends the "rvs" field signature-class (field 11) with the revocation reason. GPGME does not yet parse this but it can be expected that the comma delimiter does not break other parsers. A new field is added to the "rvs" (and in future also the "rev") record to carry a record specific comment. Hopefully all parsers meanwhile learned the lesson from other new fields and don't bail out on more fields than they know about. This is partial solution to GnuPG-bug-id: 1173 Signed-off-by: Werner Koch --- g10/import.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- g10/pkclist.c | 2 +- 2 files changed, 118 insertions(+), 3 deletions(-) (limited to 'g10') diff --git a/g10/import.c b/g10/import.c index 022f077d4..b20879c48 100644 --- a/g10/import.c +++ b/g10/import.c @@ -2633,6 +2633,69 @@ import_secret_one (ctrl_t ctrl, kbnode_t keyblock, +/* Return the recocation reason from signature SIG. If no revocation + * reason is availabale 0 is returned, in other cases the reason + * (0..255). If R_REASON is not NULL a malloced textual + * representation of the code is stored there. If R_COMMENT is not + * NULL the comment from the reason is stored there and its length at + * R_COMMENTLEN. Note that the value at R_COMMENT is not filtered but + * user supplied data in UTF8; thus it needs to be escaped for display + * purposes. Both return values are either NULL or a malloced + * string/buffer. */ +int +get_revocation_reason (PKT_signature *sig, char **r_reason, + char **r_comment, size_t *r_commentlen) +{ + int reason_seq = 0; + size_t reason_n; + const byte *reason_p; + char reason_code_buf[20]; + const char *reason_text = NULL; + int reason_code = 0; + + if (r_reason) + *r_reason = NULL; + if (r_comment) + *r_comment = NULL; + + /* Skip over empty reason packets. */ + while ((reason_p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_REVOC_REASON, + &reason_n, &reason_seq, NULL)) + && !reason_n) + ; + if (reason_p) + { + reason_code = *reason_p; + reason_n--; reason_p++; + switch (reason_code) + { + case 0x00: reason_text = _("No reason specified"); break; + case 0x01: reason_text = _("Key is superseded"); break; + case 0x02: reason_text = _("Key has been compromised"); break; + case 0x03: reason_text = _("Key is no longer used"); break; + case 0x20: reason_text = _("User ID is no longer valid"); break; + default: + snprintf (reason_code_buf, sizeof reason_code_buf, + "code=%02x", reason_code); + reason_text = reason_code_buf; + break; + } + + if (r_reason) + *r_reason = xstrdup (reason_text); + + if (r_comment && reason_n) + { + *r_comment = xmalloc (reason_n); + memcpy (*r_comment, reason_p, reason_n); + *r_commentlen = reason_n; + } + } + + return reason_code; +} + + /* List the recocation signature as a "rvs" record. SIGRC shows the * character from the signature verification or 0 if no public key was * found. */ @@ -2642,6 +2705,10 @@ list_standalone_revocation (ctrl_t ctrl, PKT_signature *sig, int sigrc) char *siguid = NULL; size_t siguidlen = 0; char *issuer_fpr = NULL; + int reason_code = 0; + char *reason_text = NULL; + char *reason_comment = NULL; + size_t reason_commentlen; if (sigrc != '%' && sigrc != '?' && !opt.fast_list_mode) { @@ -2651,6 +2718,9 @@ list_standalone_revocation (ctrl_t ctrl, PKT_signature *sig, int sigrc) sigrc = '?'; } + reason_code = get_revocation_reason (sig, &reason_text, + &reason_comment, &reason_commentlen); + if (opt.with_colons) { es_fputs ("rvs:", es_stdout); @@ -2665,13 +2735,25 @@ list_standalone_revocation (ctrl_t ctrl, PKT_signature *sig, int sigrc) if (siguid) es_write_sanitized (es_stdout, siguid, siguidlen, ":", NULL); - es_fprintf (es_stdout, ":%02x%c::", sig->sig_class, + es_fprintf (es_stdout, ":%02x%c", sig->sig_class, sig->flags.exportable ? 'x' : 'l'); + if (reason_text) + es_fprintf (es_stdout, ",%02x", reason_code); + es_fputs ("::", es_stdout); if ((issuer_fpr = issuer_fpr_string (sig))) es_fputs (issuer_fpr, es_stdout); - es_fprintf (es_stdout, ":::%d:\n", sig->digest_algo); + es_fprintf (es_stdout, ":::%d:", sig->digest_algo); + + if (reason_comment) + { + es_fputs ("::::", es_stdout); + es_write_sanitized (es_stdout, reason_comment, reason_commentlen, + ":", NULL); + es_putc (':', es_stdout); + } + es_putc ('\n', es_stdout); if (opt.show_subpackets) print_subpackets_colon (sig); @@ -2712,10 +2794,43 @@ list_standalone_revocation (ctrl_t ctrl, PKT_signature *sig, int sigrc) if (sig->flags.pref_ks && (opt.list_options & LIST_SHOW_KEYSERVER_URLS)) show_keyserver_url (sig, 3, 0); + + if (reason_text) + { + es_fprintf (es_stdout, " %s%s\n", + _("reason for revocation: "), reason_text); + if (reason_comment) + { + const byte *s, *s_lf; + size_t n, n_lf; + + s = reason_comment; + n = reason_commentlen; + s_lf = NULL; + do + { + /* We don't want any empty lines, so we skip them. */ + for (;n && *s == '\n'; s++, n--) + ; + if (n) + { + s_lf = memchr (s, '\n', n); + n_lf = s_lf? s_lf - s : n; + es_fprintf (es_stdout, " %s", + _("revocation comment: ")); + es_write_sanitized (es_stdout, s, n_lf, NULL, NULL); + es_putc ('\n', es_stdout); + s += n_lf; n -= n_lf; + } + } while (s_lf); + } + } } es_fflush (es_stdout); + xfree (reason_text); + xfree (reason_comment); xfree (siguid); xfree (issuer_fpr); } diff --git a/g10/pkclist.c b/g10/pkclist.c index 6aec13882..2322f7807 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -113,7 +113,7 @@ void show_revocation_reason (ctrl_t ctrl, PKT_public_key *pk, int mode) { /* Hmmm, this is not so easy because we have to duplicate the code - * used in the trustbd to calculate the keyflags. We need to find + * used in the trustdb to calculate the keyflags. We need to find * a clean way to check revocation certificates on keys and * signatures. And there should be no duplicate code. Because we * enter this function only when the trustdb told us that we have -- cgit v1.2.3 From 592deeddb9bf4ae9b3e236b439e2f39644eb6d46 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 21 Jun 2018 20:28:40 +0200 Subject: gpg: Print revocation reason for "rev" records. * g10/main.h: Add prototype. * g10/keylist.c (list_keyblock_print): Print revocation info. (list_keyblock_colon): Ditto. * g10/test-stubs.c (get_revocation_reason): New stub. * g10/gpgv.c (get_revocation_reason): New stub. -- GnuPG-bug-id: 1173 Signed-off-by: Werner Koch --- g10/gpgv.c | 15 ++++++++++++ g10/keylist.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- g10/main.h | 3 +++ g10/test-stubs.c | 14 +++++++++++ 4 files changed, 100 insertions(+), 4 deletions(-) (limited to 'g10') diff --git a/g10/gpgv.c b/g10/gpgv.c index c43067dfd..c142cef86 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -772,3 +772,18 @@ tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb) return 0; } + + +int +get_revocation_reason (PKT_signature *sig, char **r_reason, + char **r_comment, size_t *r_commentlen) +{ + (void)sig; + (void)r_commentlen; + + if (r_reason) + *r_reason = NULL; + if (r_comment) + *r_comment = NULL; + return 0; +} diff --git a/g10/keylist.c b/g10/keylist.c index 1f501fc97..39b87e49f 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -1107,6 +1107,9 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, PKT_signature *sig = node->pkt->pkt.signature; int sigrc; char *sigstr; + char *reason_text = NULL; + char *reason_comment = NULL; + size_t reason_commentlen; if (listctx->check_sigs) { @@ -1143,7 +1146,11 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, if (sig->sig_class == 0x20 || sig->sig_class == 0x28 || sig->sig_class == 0x30) - sigstr = "rev"; + { + sigstr = "rev"; + get_revocation_reason (sig, &reason_text, + &reason_comment, &reason_commentlen); + } else if ((sig->sig_class & ~3) == 0x10) sigstr = "sig"; else if (sig->sig_class == 0x18) @@ -1205,6 +1212,40 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, && (opt.list_options & LIST_SHOW_KEYSERVER_URLS)) show_keyserver_url (sig, 3, 0); + if (reason_text) + { + es_fprintf (es_stdout, " %s%s\n", + _("reason for revocation: "), reason_text); + if (reason_comment) + { + const byte *s, *s_lf; + size_t n, n_lf; + + s = reason_comment; + n = reason_commentlen; + s_lf = NULL; + do + { + /* We don't want any empty lines, so we skip them. */ + for (;n && *s == '\n'; s++, n--) + ; + if (n) + { + s_lf = memchr (s, '\n', n); + n_lf = s_lf? s_lf - s : n; + es_fprintf (es_stdout, " %s", + _("revocation comment: ")); + es_write_sanitized (es_stdout, s, n_lf, NULL, NULL); + es_putc ('\n', es_stdout); + s += n_lf; n -= n_lf; + } + } while (s_lf); + } + } + + xfree (reason_text); + xfree (reason_comment); + /* fixme: check or list other sigs here */ } } @@ -1554,10 +1595,19 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock, char *siguid; size_t siguidlen; char *issuer_fpr = NULL; + char *reason_text = NULL; + char *reason_comment = NULL; + size_t reason_commentlen; + int reason_code; if (sig->sig_class == 0x20 || sig->sig_class == 0x28 || sig->sig_class == 0x30) - sigstr = "rev"; + { + sigstr = "rev"; + reason_code = get_revocation_reason (sig, &reason_text, + &reason_comment, + &reason_commentlen); + } else if ((sig->sig_class & ~3) == 0x10) sigstr = "sig"; else if (sig->sig_class == 0x18) @@ -1651,8 +1701,11 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock, else if (siguid) es_write_sanitized (es_stdout, siguid, siguidlen, ":", NULL); - es_fprintf (es_stdout, ":%02x%c::", sig->sig_class, + es_fprintf (es_stdout, ":%02x%c", sig->sig_class, sig->flags.exportable ? 'x' : 'l'); + if (reason_text) + es_fprintf (es_stdout, ",%02x", reason_code); + es_fputs ("::", es_stdout); if (opt.no_sig_cache && opt.check_sigs && fprokay) { @@ -1662,12 +1715,23 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock, else if ((issuer_fpr = issuer_fpr_string (sig))) es_fputs (issuer_fpr, es_stdout); - es_fprintf (es_stdout, ":::%d:\n", sig->digest_algo); + es_fprintf (es_stdout, ":::%d:", sig->digest_algo); + + if (reason_comment) + { + es_fputs ("::::", es_stdout); + es_write_sanitized (es_stdout, reason_comment, reason_commentlen, + ":", NULL); + es_putc (':', es_stdout); + } + es_putc ('\n', es_stdout); if (opt.show_subpackets) print_subpackets_colon (sig); /* fixme: check or list other sigs here */ + xfree (reason_text); + xfree (reason_comment); xfree (siguid); xfree (issuer_fpr); } diff --git a/g10/main.h b/g10/main.h index 453d1226a..768f7cfb8 100644 --- a/g10/main.h +++ b/g10/main.h @@ -396,6 +396,9 @@ gpg_error_t transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats, int collapse_uids( KBNODE *keyblock ); +int get_revocation_reason (PKT_signature *sig, char **r_reason, + char **r_comment, size_t *r_commentlen); + /*-- export.c --*/ struct export_stats_s; diff --git a/g10/test-stubs.c b/g10/test-stubs.c index e5fd3ae9a..1e1363266 100644 --- a/g10/test-stubs.c +++ b/g10/test-stubs.c @@ -535,3 +535,17 @@ tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb) return 0; } + +int +get_revocation_reason (PKT_signature *sig, char **r_reason, + char **r_comment, size_t *r_commentlen) +{ + (void)sig; + (void)r_commentlen; + + if (r_reason) + *r_reason = NULL; + if (r_comment) + *r_comment = NULL; + return 0; +} -- cgit v1.2.3 From 996febbab21eb9283b0634e51303a36b318734a6 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Tue, 3 Jul 2018 09:07:03 +0900 Subject: g10: Fix memory leak for PKT_signature. * g10/getkey.c (buf_to_sig): Free by free_seckey_enc. * g10/gpgcompose.c (signature): Likewise. * g10/sign.c (write_signature_packets): Likewise. -- Reported-by: Philippe Antoine GnuPG-bug-id: 4047 Signed-off-by: NIIBE Yutaka --- g10/getkey.c | 2 +- g10/gpgcompose.c | 2 +- g10/sign.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'g10') diff --git a/g10/getkey.c b/g10/getkey.c index b111376c9..f0132bb08 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -3142,7 +3142,7 @@ buf_to_sig (const byte * buf, size_t len) if (parse_signature (iobuf, PKT_SIGNATURE, len, sig) != 0) { - xfree (sig); + free_seckey_enc (sig); sig = NULL; } diff --git a/g10/gpgcompose.c b/g10/gpgcompose.c index 094bc7614..b3f7ecdce 100644 --- a/g10/gpgcompose.c +++ b/g10/gpgcompose.c @@ -1835,7 +1835,7 @@ signature (const char *option, int argc, char *argv[], void *cookie) debug ("Wrote signature packet:\n"); dump_component (&pkt); - xfree (sig); + free_seckey_enc (sig); release_kbnode (si.issuer_kb); xfree (si.revocation_key); diff --git a/g10/sign.c b/g10/sign.c index df71ccce1..581a08f5b 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -772,7 +772,7 @@ write_signature_packets (ctrl_t ctrl, gpg_strerror (rc)); } else - xfree (sig); + free_seckey_enc (sig); if (rc) return rc; -- cgit v1.2.3 From 214b0077264e35c079e854a8b6374704aea45cd5 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 4 Jul 2018 08:59:12 +0200 Subject: gpg: Extra check for sign usage when verifying a data signature. * g10/sig-check.c (check_signature_end_simple): Check sign usage. -- Without this patch the signature verification fails only due to the missing back signature. This check better explains what went wrong. GnuPG-bug-id: 4014 Signed-off-by: Werner Koch --- g10/sig-check.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'g10') diff --git a/g10/sig-check.c b/g10/sig-check.c index fc6983993..a68e031f6 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -478,8 +478,17 @@ check_signature_end_simple (PKT_public_key *pk, PKT_signature *sig, sig->sig_class, pk->pubkey_usage); return rc; } - /* Fixme: Should we also check the signing capability here for data - * signature? */ + + /* For data signatures check that the key has sign usage. */ + if (IS_SIG (sig) && !(pk->pubkey_usage & PUBKEY_USAGE_SIG)) + { + rc = gpg_error (GPG_ERR_WRONG_KEY_USAGE); + if (!opt.quiet) + log_info (_("bad data signature from key %s: %s (0x%02x, 0x%x)\n"), + keystr_from_pk (pk), gpg_strerror (rc), + sig->sig_class, pk->pubkey_usage); + return rc; + } /* Make sure the digest algo is enabled (in case of a detached * signature). */ -- cgit v1.2.3 From 60e7e102a153a246d7e887a64e30dbb4c4f7b6dd Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 4 Jul 2018 09:45:52 +0200 Subject: indent: Fix indentation of read_block in g10/import.c -- Signed-off-by: Werner Koch --- g10/import.c | 133 ++++++++++++++++++++++++++++++----------------------------- 1 file changed, 67 insertions(+), 66 deletions(-) (limited to 'g10') diff --git a/g10/import.c b/g10/import.c index b20879c48..62bc6a2af 100644 --- a/g10/import.c +++ b/g10/import.c @@ -826,76 +826,77 @@ read_block( IOBUF a, int with_meta, continue; } - if (in_v3key && !(pkt->pkttype == PKT_PUBLIC_KEY - || pkt->pkttype == PKT_SECRET_KEY)) - { - free_packet (pkt, &parsectx); - init_packet(pkt); - continue; - } - in_v3key = 0; + if (in_v3key && !(pkt->pkttype == PKT_PUBLIC_KEY + || pkt->pkttype == PKT_SECRET_KEY)) + { + free_packet (pkt, &parsectx); + init_packet(pkt); + continue; + } + in_v3key = 0; - if (!root && pkt->pkttype == PKT_SIGNATURE - && IS_KEY_REV (pkt->pkt.signature) ) - { - /* This is a revocation certificate which is handled in a - * special way. */ - root = new_kbnode( pkt ); - pkt = NULL; - goto ready; - } + if (!root && pkt->pkttype == PKT_SIGNATURE + && IS_KEY_REV (pkt->pkt.signature) ) + { + /* This is a revocation certificate which is handled in a + * special way. */ + root = new_kbnode( pkt ); + pkt = NULL; + goto ready; + } - /* Make a linked list of all packets. */ - switch (pkt->pkttype) - { - case PKT_COMPRESSED: - if (check_compress_algo (pkt->pkt.compressed->algorithm)) - { - rc = GPG_ERR_COMPR_ALGO; - goto ready; - } - else - { - compress_filter_context_t *cfx = xmalloc_clear( sizeof *cfx ); - pkt->pkt.compressed->buf = NULL; - if (push_compress_filter2 (a, cfx, - pkt->pkt.compressed->algorithm, 1)) - xfree (cfx); /* e.g. in case of compression_algo NONE. */ - } - free_packet (pkt, &parsectx); - init_packet(pkt); - break; + /* Make a linked list of all packets. */ + switch (pkt->pkttype) + { + case PKT_COMPRESSED: + if (check_compress_algo (pkt->pkt.compressed->algorithm)) + { + rc = GPG_ERR_COMPR_ALGO; + goto ready; + } + else + { + compress_filter_context_t *cfx = xmalloc_clear( sizeof *cfx ); + pkt->pkt.compressed->buf = NULL; + if (push_compress_filter2 (a, cfx, + pkt->pkt.compressed->algorithm, 1)) + xfree (cfx); /* e.g. in case of compression_algo NONE. */ + } + free_packet (pkt, &parsectx); + init_packet(pkt); + break; - case PKT_RING_TRUST: - /* Skip those packets unless we are in restore mode. */ - if ((opt.import_options & IMPORT_RESTORE)) - goto x_default; - free_packet (pkt, &parsectx); - init_packet(pkt); - break; + case PKT_RING_TRUST: + /* Skip those packets unless we are in restore mode. */ + if ((opt.import_options & IMPORT_RESTORE)) + goto x_default; + free_packet (pkt, &parsectx); + init_packet(pkt); + break; - case PKT_PUBLIC_KEY: - case PKT_SECRET_KEY: - if (in_cert ) /* Store this packet. */ - { - *pending_pkt = pkt; - pkt = NULL; - goto ready; - } - in_cert = 1; /* fall through */ - default: - x_default: - if (in_cert && valid_keyblock_packet (pkt->pkttype)) - { - if (!root ) - root = new_kbnode (pkt); - else - add_kbnode (root, new_kbnode (pkt)); - pkt = xmalloc (sizeof *pkt); - } - init_packet(pkt); - break; - } + case PKT_PUBLIC_KEY: + case PKT_SECRET_KEY: + if (in_cert ) /* Store this packet. */ + { + *pending_pkt = pkt; + pkt = NULL; + goto ready; + } + in_cert = 1; + /* fall through */ + default: + x_default: + if (in_cert && valid_keyblock_packet (pkt->pkttype)) + { + if (!root ) + root = new_kbnode (pkt); + else + add_kbnode (root, new_kbnode (pkt)); + pkt = xmalloc (sizeof *pkt); + } + init_packet(pkt); + break; + } } ready: -- cgit v1.2.3 From 01cd66f9faf1623833e6afac84164de5a136ecff Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 4 Jul 2018 09:53:10 +0200 Subject: gpg: Ignore too large user ids during import. * g10/import.c (read_block): Add special treatment for bad user ids and comment packets. -- See GnuPG-bug-id: 4022 for an example of a bogus user id. Signed-off-by: Werner Koch --- g10/import.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'g10') diff --git a/g10/import.c b/g10/import.c index 62bc6a2af..5be7952d6 100644 --- a/g10/import.c +++ b/g10/import.c @@ -780,7 +780,7 @@ read_block( IOBUF a, int with_meta, struct parse_packet_ctx_s parsectx; PACKET *pkt; kbnode_t root = NULL; - int in_cert, in_v3key; + int in_cert, in_v3key, skip_sigs; *r_v3keys = 0; @@ -799,6 +799,7 @@ read_block( IOBUF a, int with_meta, if (!with_meta) parsectx.skip_meta = 1; in_v3key = 0; + skip_sigs = 0; while ((rc=parse_packet (&parsectx, pkt)) != -1) { if (rc && (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY @@ -813,8 +814,25 @@ read_block( IOBUF a, int with_meta, } else if (rc ) /* (ignore errors) */ { + skip_sigs = 0; if (gpg_err_code (rc) == GPG_ERR_UNKNOWN_PACKET) ; /* Do not show a diagnostic. */ + else if (gpg_err_code (rc) == GPG_ERR_INV_PACKET + && (pkt->pkttype == PKT_USER_ID + || pkt->pkttype == PKT_ATTRIBUTE)) + { + /* This indicates a too large user id or attribute + * packet. We skip this packet and all following + * signatures. Sure, this won't allow to repair a + * garbled keyring in case one of the signatures belong + * to another user id. However, this better mitigates + * DoS using inserted user ids. */ + skip_sigs = 1; + } + else if (gpg_err_code (rc) == GPG_ERR_INV_PACKET + && (pkt->pkttype == PKT_OLD_COMMENT + || pkt->pkttype == PKT_COMMENT)) + ; /* Ignore too large comment packets. */ else { log_error("read_block: read error: %s\n", gpg_strerror (rc) ); @@ -826,6 +844,17 @@ read_block( IOBUF a, int with_meta, continue; } + if (skip_sigs) + { + if (pkt->pkttype == PKT_SIGNATURE) + { + free_packet (pkt, &parsectx); + init_packet (pkt); + continue; + } + skip_sigs = 0; + } + if (in_v3key && !(pkt->pkttype == PKT_PUBLIC_KEY || pkt->pkttype == PKT_SECRET_KEY)) { -- cgit v1.2.3 From 9ea9b9db7e1b3e6a84104a2be48d492f12c6316c Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 5 Jul 2018 09:42:06 +0200 Subject: doc: Typo fix in a comment. -- --- g10/keygen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'g10') diff --git a/g10/keygen.c b/g10/keygen.c index 9e9cead07..fbead107b 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -3293,7 +3293,7 @@ parse_key_parameter_string (const char *string, int part, * part consider this to be the subkey algo. In case a * SUGGESTED_USE has been given and the usage of the secondary * part does not match SUGGESTED_USE try again using the primary - * part. Noet thar when falling back to the primary key we need + * part. Note that when falling back to the primary key we need * to force clearing the cert usage. */ if (secondary) { -- cgit v1.2.3 From f7526c7bc754acf68bde0b79c785e875a9365d60 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 5 Jul 2018 20:55:32 +0200 Subject: gpg: Prepare for signatures with ISSUER_FPR but without ISSUER. * g10/getkey.c (get_pubkey_for_sig): New. (get_pubkeyblock_for_sig): New. * g10/mainproc.c (issuer_fpr_raw): Give global scope. (check_sig_and_print): Use get_pubkeyblock_for_sig. * g10/pkclist.c (check_signatures_trust): Use get_pubkey_for_sig. * g10/sig-check.c (check_signature2): Ditto. (check_signature_over_key_or_uid): Ditto. -- GnuPG-bug-id: 4046 The whole getkey stuff is still a mess with way to much duplication and missing caching of already fetched data. Signed-off-by: Werner Koch --- g10/getkey.c | 47 +++++++++++++++++++++++++++++++++++++++++++++-- g10/keydb.h | 8 ++++++++ g10/mainproc.c | 6 +++--- g10/packet.h | 1 + g10/pkclist.c | 2 +- g10/sig-check.c | 4 ++-- 6 files changed, 60 insertions(+), 8 deletions(-) (limited to 'g10') diff --git a/g10/getkey.c b/g10/getkey.c index f0132bb08..08e17e930 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -677,6 +677,24 @@ pk_from_block (PKT_public_key *pk, kbnode_t keyblock, kbnode_t found_key) } +/* Specialized version of get_pubkey which retrieves the key based on + * information in SIG. In contrast to get_pubkey PK is required. */ +gpg_error_t +get_pubkey_for_sig (ctrl_t ctrl, PKT_public_key *pk, PKT_signature *sig) +{ + const byte *fpr; + size_t fprlen; + + /* First try the new ISSUER_FPR info. */ + fpr = issuer_fpr_raw (sig, &fprlen); + if (fpr && !get_pubkey_byfprint (ctrl, pk, NULL, fpr, fprlen)) + return 0; + + /* Fallback to use the ISSUER_KEYID. */ + return get_pubkey (ctrl, pk, sig->keyid); +} + + /* Return the public key with the key id KEYID and store it at PK. * The resources in *PK should be released using * release_public_key_parts(). This function also stores a copy of @@ -739,8 +757,9 @@ get_pubkey (ctrl_t ctrl, PKT_public_key * pk, u32 * keyid) /* Do a lookup. */ { struct getkey_ctx_s ctx; - KBNODE kb = NULL; - KBNODE found_key = NULL; + kbnode_t kb = NULL; + kbnode_t found_key = NULL; + memset (&ctx, 0, sizeof ctx); ctx.exact = 1; /* Use the key ID exactly as given. */ ctx.not_allocated = 1; @@ -863,6 +882,28 @@ get_pubkey_fast (PKT_public_key * pk, u32 * keyid) } +/* Return the entire keyblock used to create SIG. This is a + * specialized version of get_pubkeyblock. + * + * FIXME: This is a hack because get_pubkey_for_sig was already called + * and it could have used a cache to hold the key. */ +kbnode_t +get_pubkeyblock_for_sig (ctrl_t ctrl, PKT_signature *sig) +{ + const byte *fpr; + size_t fprlen; + kbnode_t keyblock; + + /* First try the new ISSUER_FPR info. */ + fpr = issuer_fpr_raw (sig, &fprlen); + if (fpr && !get_pubkey_byfprint (ctrl, NULL, &keyblock, fpr, fprlen)) + return keyblock; + + /* Fallback to use the ISSUER_KEYID. */ + return get_pubkeyblock (ctrl, sig->keyid); +} + + /* Return the key block for the key with key id KEYID or NULL, if an * error occurs. Use release_kbnode() to release the key block. * @@ -1802,6 +1843,8 @@ get_pubkey_byfprint (ctrl_t ctrl, PKT_public_key *pk, kbnode_t *r_keyblock, memset (&ctx, 0, sizeof ctx); ctx.exact = 1; ctx.not_allocated = 1; + /* FIXME: We should get the handle from the cache like we do in + * get_pubkey. */ ctx.kr_handle = keydb_new (); if (!ctx.kr_handle) return gpg_error_from_syserror (); diff --git a/g10/keydb.h b/g10/keydb.h index bd156a6a3..ea0fa9ddd 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -283,6 +283,10 @@ void cache_public_key( PKT_public_key *pk ); /* Disable and drop the public key cache. */ void getkey_disable_caches(void); +/* Return the public key used for signature SIG and store it at PK. */ +gpg_error_t get_pubkey_for_sig (ctrl_t ctrl, + PKT_public_key *pk, PKT_signature *sig); + /* Return the public key with the key id KEYID and store it at PK. */ int get_pubkey (ctrl_t ctrl, PKT_public_key *pk, u32 *keyid); @@ -291,6 +295,10 @@ int get_pubkey (ctrl_t ctrl, PKT_public_key *pk, u32 *keyid); also only considers primary keys. */ int get_pubkey_fast (PKT_public_key *pk, u32 *keyid); +/* Return the entire keyblock used to create SIG. This is a + * specialized version of get_pubkeyblock. */ +kbnode_t get_pubkeyblock_for_sig (ctrl_t ctrl, PKT_signature *sig); + /* Return the key block for the key with KEYID. */ kbnode_t get_pubkeyblock (ctrl_t ctrl, u32 *keyid); diff --git a/g10/mainproc.c b/g10/mainproc.c index a9da08f74..1d56f1f30 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -1751,7 +1751,7 @@ akl_has_wkd_method (void) /* Return the ISSUER fingerprint buffer and its lenbgth at R_LEN. * Returns NULL if not available. The returned buffer is valid as * long as SIG is not modified. */ -static const byte * +const byte * issuer_fpr_raw (PKT_signature *sig, size_t *r_len) { const byte *p; @@ -1768,7 +1768,7 @@ issuer_fpr_raw (PKT_signature *sig, size_t *r_len) } -/* Return the ISSUER fingerprint string in human readbale format if +/* Return the ISSUER fingerprint string in human readable format if * available. Caller must release the string. */ /* FIXME: Move to another file. */ char * @@ -2134,7 +2134,7 @@ check_sig_and_print (CTX c, kbnode_t node) * keyblock has already been fetched. Thus we could use the * fingerprint or PK itself to lookup the entire keyblock. That * would best be done with a cache. */ - keyblock = get_pubkeyblock (c->ctrl, sig->keyid); + keyblock = get_pubkeyblock_for_sig (c->ctrl, sig); snprintf (keyid_str, sizeof keyid_str, "%08lX%08lX [uncertain] ", (ulong)sig->keyid[0], (ulong)sig->keyid[1]); diff --git a/g10/packet.h b/g10/packet.h index 40a8c4bf6..695768695 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -621,6 +621,7 @@ int proc_signature_packets_by_fd (ctrl_t ctrl, int proc_encryption_packets (ctrl_t ctrl, void *ctx, iobuf_t a); int list_packets( iobuf_t a ); +const byte *issuer_fpr_raw (PKT_signature *sig, size_t *r_len); char *issuer_fpr_string (PKT_signature *sig); /*-- parse-packet.c --*/ diff --git a/g10/pkclist.c b/g10/pkclist.c index 2322f7807..e7484432a 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -548,7 +548,7 @@ check_signatures_trust (ctrl_t ctrl, PKT_signature *sig) unsigned int trustlevel = TRUST_UNKNOWN; int rc=0; - rc = get_pubkey (ctrl, pk, sig->keyid ); + rc = get_pubkey_for_sig (ctrl, pk, sig); if (rc) { /* this should not happen */ log_error("Ooops; the key vanished - can't check the trust\n"); diff --git a/g10/sig-check.c b/g10/sig-check.c index a68e031f6..0ec384347 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -156,7 +156,7 @@ check_signature2 (ctrl_t ctrl, log_info(_("WARNING: signature digest conflict in message\n")); rc = gpg_error (GPG_ERR_GENERAL); } - else if (get_pubkey (ctrl, pk, sig->keyid)) + else if (get_pubkey_for_sig (ctrl, pk, sig)) rc = gpg_error (GPG_ERR_NO_PUBKEY); else if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_VERIFICATION, pk->pubkey_algo, pk->pkey, @@ -926,7 +926,7 @@ check_signature_over_key_or_uid (ctrl_t ctrl, PKT_public_key *signer, if (IS_CERT (sig)) signer->req_usage = PUBKEY_USAGE_CERT; - rc = get_pubkey (ctrl, signer, sig->keyid); + rc = get_pubkey_for_sig (ctrl, signer, sig); if (rc) { xfree (signer); -- cgit v1.2.3 From 135e46ea480d749b8a9692f71d4d0bfdadd8ee2f Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 6 Jul 2018 11:40:16 +0200 Subject: gpg: Move key cleaning functions to a separate file. * g10/trust.c (mark_usable_uid_certs, clean_sigs_from_uid) (clean_uid_from_key, clean_one_uid, clean_key): Move to ... * g10/key-clean.c: new file. * g10/key-clean.h: New. * g10/Makefile.am (gpg_sources): Add new files. * g10/export.c, g10/import.c, g10/keyedit.c, g10/trustdb.c: Include new header. * g10/trustdb.h (struct key_item, is_in_klist): Move to ... * g10/keydb.h: here. -- Signed-off-by: Werner Koch --- g10/Makefile.am | 1 + g10/export.c | 2 + g10/import.c | 1 + g10/key-clean.c | 422 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ g10/key-clean.h | 37 +++++ g10/keydb.h | 30 ++++ g10/keyedit.c | 1 + g10/trust.c | 388 --------------------------------------------------- g10/trustdb.c | 1 + g10/trustdb.h | 41 ------ 10 files changed, 495 insertions(+), 429 deletions(-) create mode 100644 g10/key-clean.c create mode 100644 g10/key-clean.h (limited to 'g10') diff --git a/g10/Makefile.am b/g10/Makefile.am index b8b92d702..3b4464364 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -152,6 +152,7 @@ gpg_sources = server.c \ trust.c $(trust_source) $(tofu_source) \ $(card_source) \ exec.c exec.h \ + key-clean.c key-clean.h \ key-check.c key-check.h gpg_SOURCES = gpg.c \ diff --git a/g10/export.c b/g10/export.c index c538dc1f1..44cf075b0 100644 --- a/g10/export.c +++ b/g10/export.c @@ -41,6 +41,8 @@ #include "../common/init.h" #include "trustdb.h" #include "call-agent.h" +#include "key-clean.h" + /* An object to keep track of subkeys. */ struct subkey_list_s diff --git a/g10/import.c b/g10/import.c index 5be7952d6..757063eea 100644 --- a/g10/import.c +++ b/g10/import.c @@ -41,6 +41,7 @@ #include "../common/init.h" #include "../common/mbox-util.h" #include "key-check.h" +#include "key-clean.h" struct import_stats_s diff --git a/g10/key-clean.c b/g10/key-clean.c new file mode 100644 index 000000000..d022ff44d --- /dev/null +++ b/g10/key-clean.c @@ -0,0 +1,422 @@ +/* key-clean.c - Functions to clean a keyblock + * Copyright (C) 1998-2008, 2010-2011 Free Software Foundation, Inc. + * Copyright (C) 2014, 2016-2018 Werner Koch + * + * 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 . + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include +#include + +#include "gpg.h" +#include "keydb.h" +#include "../common/util.h" +#include "../common/host2net.h" +#include "../common/i18n.h" +#include "options.h" +#include "packet.h" +#include "main.h" +#include "key-clean.h" + + +/* + * Mark the signature of the given UID which are used to certify it. + * To do this, we first revmove all signatures which are not valid and + * from the remain ones we look for the latest one. If this is not a + * certification revocation signature we mark the signature by setting + * node flag bit 8. Revocations are marked with flag 11, and sigs + * from unavailable keys are marked with flag 12. Note that flag bits + * 9 and 10 are used for internal purposes. + */ +void +mark_usable_uid_certs (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, + u32 *main_kid, struct key_item *klist, + u32 curtime, u32 *next_expire) +{ + kbnode_t node; + PKT_signature *sig; + + /* First check all signatures. */ + for (node=uidnode->next; node; node = node->next) + { + int rc; + + node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); + if (node->pkt->pkttype == PKT_USER_ID + || node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY) + break; /* ready */ + if (node->pkt->pkttype != PKT_SIGNATURE) + continue; + sig = node->pkt->pkt.signature; + if (main_kid + && sig->keyid[0] == main_kid[0] && sig->keyid[1] == main_kid[1]) + continue; /* ignore self-signatures if we pass in a main_kid */ + if (!IS_UID_SIG(sig) && !IS_UID_REV(sig)) + continue; /* we only look at these signature classes */ + if(sig->sig_class>=0x11 && sig->sig_class<=0x13 && + sig->sig_class-0x10flag |= 1<<12; + continue; + } + node->flag |= 1<<9; + } + /* Reset the remaining flags. */ + for (; node; node = node->next) + node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); + + /* kbnode flag usage: bit 9 is here set for signatures to consider, + * bit 10 will be set by the loop to keep track of keyIDs already + * processed, bit 8 will be set for the usable signatures, and bit + * 11 will be set for usable revocations. */ + + /* For each cert figure out the latest valid one. */ + for (node=uidnode->next; node; node = node->next) + { + KBNODE n, signode; + u32 kid[2]; + u32 sigdate; + + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY) + break; + if ( !(node->flag & (1<<9)) ) + continue; /* not a node to look at */ + if ( (node->flag & (1<<10)) ) + continue; /* signature with a keyID already processed */ + node->flag |= (1<<10); /* mark this node as processed */ + sig = node->pkt->pkt.signature; + signode = node; + sigdate = sig->timestamp; + kid[0] = sig->keyid[0]; kid[1] = sig->keyid[1]; + + /* Now find the latest and greatest signature */ + for (n=uidnode->next; n; n = n->next) + { + if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY + || n->pkt->pkttype == PKT_SECRET_SUBKEY) + break; + if ( !(n->flag & (1<<9)) ) + continue; + if ( (n->flag & (1<<10)) ) + continue; /* shortcut already processed signatures */ + sig = n->pkt->pkt.signature; + if (kid[0] != sig->keyid[0] || kid[1] != sig->keyid[1]) + continue; + n->flag |= (1<<10); /* mark this node as processed */ + + /* If signode is nonrevocable and unexpired and n isn't, + then take signode (skip). It doesn't matter which is + older: if signode was older then we don't want to take n + as signode is nonrevocable. If n was older then we're + automatically fine. */ + + if(((IS_UID_SIG(signode->pkt->pkt.signature) && + !signode->pkt->pkt.signature->flags.revocable && + (signode->pkt->pkt.signature->expiredate==0 || + signode->pkt->pkt.signature->expiredate>curtime))) && + (!(IS_UID_SIG(n->pkt->pkt.signature) && + !n->pkt->pkt.signature->flags.revocable && + (n->pkt->pkt.signature->expiredate==0 || + n->pkt->pkt.signature->expiredate>curtime)))) + continue; + + /* If n is nonrevocable and unexpired and signode isn't, + then take n. Again, it doesn't matter which is older: if + n was older then we don't want to take signode as n is + nonrevocable. If signode was older then we're + automatically fine. */ + + if((!(IS_UID_SIG(signode->pkt->pkt.signature) && + !signode->pkt->pkt.signature->flags.revocable && + (signode->pkt->pkt.signature->expiredate==0 || + signode->pkt->pkt.signature->expiredate>curtime))) && + ((IS_UID_SIG(n->pkt->pkt.signature) && + !n->pkt->pkt.signature->flags.revocable && + (n->pkt->pkt.signature->expiredate==0 || + n->pkt->pkt.signature->expiredate>curtime)))) + { + signode = n; + sigdate = sig->timestamp; + continue; + } + + /* At this point, if it's newer, it goes in as the only + remaining possibilities are signode and n are both either + revocable or expired or both nonrevocable and unexpired. + If the timestamps are equal take the later ordered + packet, presuming that the key packets are hopefully in + their original order. */ + + if (sig->timestamp >= sigdate) + { + signode = n; + sigdate = sig->timestamp; + } + } + + sig = signode->pkt->pkt.signature; + if (IS_UID_SIG (sig)) + { /* this seems to be a usable one which is not revoked. + * Just need to check whether there is an expiration time, + * We do the expired certification after finding a suitable + * certification, the assumption is that a signator does not + * want that after the expiration of his certificate the + * system falls back to an older certification which has a + * different expiration time */ + const byte *p; + u32 expire; + + p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_EXPIRE, NULL ); + expire = p? sig->timestamp + buf32_to_u32(p) : 0; + + if (expire==0 || expire > curtime ) + { + signode->flag |= (1<<8); /* yeah, found a good cert */ + if (next_expire && expire && expire < *next_expire) + *next_expire = expire; + } + } + else + signode->flag |= (1<<11); + } +} + + +static int +clean_sigs_from_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, + int noisy, int self_only) +{ + int deleted = 0; + kbnode_t node; + u32 keyid[2]; + + log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY + || keyblock->pkt->pkttype == PKT_SECRET_KEY); + + keyid_from_pk (keyblock->pkt->pkt.public_key, keyid); + + /* Passing in a 0 for current time here means that we'll never weed + out an expired sig. This is correct behavior since we want to + keep the most recent expired sig in a series. */ + mark_usable_uid_certs (ctrl, keyblock, uidnode, NULL, NULL, 0, NULL); + + /* What we want to do here is remove signatures that are not + considered as part of the trust calculations. Thus, all invalid + signatures are out, as are any signatures that aren't the last of + a series of uid sigs or revocations It breaks down like this: + coming out of mark_usable_uid_certs, if a sig is unflagged, it is + not even a candidate. If a sig has flag 9 or 10, that means it + was selected as a candidate and vetted. If a sig has flag 8 it + is a usable signature. If a sig has flag 11 it is a usable + revocation. If a sig has flag 12 it was issued by an unavailable + key. "Usable" here means the most recent valid + signature/revocation in a series from a particular signer. + + Delete everything that isn't a usable uid sig (which might be + expired), a usable revocation, or a sig from an unavailable + key. */ + + for (node=uidnode->next; + node && node->pkt->pkttype==PKT_SIGNATURE; + node=node->next) + { + int keep; + + keep = self_only? (node->pkt->pkt.signature->keyid[0] == keyid[0] + && node->pkt->pkt.signature->keyid[1] == keyid[1]) : 1; + + /* Keep usable uid sigs ... */ + if ((node->flag & (1<<8)) && keep) + continue; + + /* ... and usable revocations... */ + if ((node->flag & (1<<11)) && keep) + continue; + + /* ... and sigs from unavailable keys. */ + /* disabled for now since more people seem to want sigs from + unavailable keys removed altogether. */ + /* + if(node->flag & (1<<12)) + continue; + */ + + /* Everything else we delete */ + + /* At this point, if 12 is set, the signing key was unavailable. + If 9 or 10 is set, it's superseded. Otherwise, it's + invalid. */ + + if (noisy) + log_info ("removing signature from key %s on user ID \"%s\": %s\n", + keystr (node->pkt->pkt.signature->keyid), + uidnode->pkt->pkt.user_id->name, + node->flag&(1<<12)? "key unavailable": + node->flag&(1<<9)? "signature superseded" + /* */ :"invalid signature" ); + + delete_kbnode (node); + deleted++; + } + + return deleted; +} + + +/* This is substantially easier than clean_sigs_from_uid since we just + have to establish if the uid has a valid self-sig, is not revoked, + and is not expired. Note that this does not take into account + whether the uid has a trust path to it - just whether the keyholder + themselves has certified the uid. Returns true if the uid was + compacted. To "compact" a user ID, we simply remove ALL signatures + except the self-sig that caused the user ID to be remove-worthy. + We don't actually remove the user ID packet itself since it might + be resurrected in a later merge. Note that this function requires + that the caller has already done a merge_keys_and_selfsig(). + + TODO: change the import code to allow importing a uid with only a + revocation if the uid already exists on the keyring. */ + +static int +clean_uid_from_key (kbnode_t keyblock, kbnode_t uidnode, int noisy) +{ + kbnode_t node; + PKT_user_id *uid = uidnode->pkt->pkt.user_id; + int deleted = 0; + + log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY + || keyblock->pkt->pkttype == PKT_SECRET_KEY); + log_assert (uidnode->pkt->pkttype==PKT_USER_ID); + + /* Skip valid user IDs, compacted user IDs, and non-self-signed user + IDs if --allow-non-selfsigned-uid is set. */ + if (uid->created + || uid->flags.compacted + || (!uid->flags.expired && !uid->flags.revoked && opt.allow_non_selfsigned_uid)) + return 0; + + for (node=uidnode->next; + node && node->pkt->pkttype == PKT_SIGNATURE; + node=node->next) + { + if (!node->pkt->pkt.signature->flags.chosen_selfsig) + { + delete_kbnode (node); + deleted = 1; + uidnode->pkt->pkt.user_id->flags.compacted = 1; + } + } + + if (noisy) + { + const char *reason; + char *user = utf8_to_native (uid->name, uid->len, 0); + + if (uid->flags.revoked) + reason = _("revoked"); + else if (uid->flags.expired) + reason = _("expired"); + else + reason = _("invalid"); + + log_info ("compacting user ID \"%s\" on key %s: %s\n", + user, keystr_from_pk (keyblock->pkt->pkt.public_key), + reason); + + xfree (user); + } + + return deleted; +} + + +/* Needs to be called after a merge_keys_and_selfsig() */ +void +clean_one_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, + int noisy, int self_only, int *uids_cleaned, int *sigs_cleaned) +{ + int dummy = 0; + + log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY + || keyblock->pkt->pkttype == PKT_SECRET_KEY); + log_assert (uidnode->pkt->pkttype==PKT_USER_ID); + + if (!uids_cleaned) + uids_cleaned = &dummy; + + if (!sigs_cleaned) + sigs_cleaned = &dummy; + + /* Do clean_uid_from_key first since if it fires off, we don't have + to bother with the other. */ + *uids_cleaned += clean_uid_from_key (keyblock, uidnode, noisy); + if (!uidnode->pkt->pkt.user_id->flags.compacted) + *sigs_cleaned += clean_sigs_from_uid (ctrl, keyblock, uidnode, + noisy, self_only); +} + + +/* NB: This function marks the deleted nodes only and the caller is + * responsible to skip or remove them. */ +void +clean_key (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only, + int *uids_cleaned, int *sigs_cleaned) +{ + kbnode_t node; + + merge_keys_and_selfsig (ctrl, keyblock); + + for (node = keyblock->next; + node && !(node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY); + node = node->next) + { + if (node->pkt->pkttype == PKT_USER_ID) + clean_one_uid (ctrl, keyblock, node, noisy, self_only, + uids_cleaned, sigs_cleaned); + } + + /* Remove bogus subkey binding signatures: The only signatures + * allowed are of class 0x18 and 0x28. */ + log_assert (!node || (node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY)); + for (; node; node = node->next) + { + if (is_deleted_kbnode (node)) + continue; + if (node->pkt->pkttype == PKT_SIGNATURE + && !(IS_SUBKEY_SIG (node->pkt->pkt.signature) + || IS_SUBKEY_REV (node->pkt->pkt.signature))) + { + delete_kbnode (node); + if (sigs_cleaned) + ++*sigs_cleaned; + } + } +} diff --git a/g10/key-clean.h b/g10/key-clean.h new file mode 100644 index 000000000..4dfd9509c --- /dev/null +++ b/g10/key-clean.h @@ -0,0 +1,37 @@ +/* key-clean.h - Functions to clean a keyblock + * Copyright (C) 2018 Werner Koch + * + * 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 . + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef GNUPG_G10_KEY_CLEAN_H +#define GNUPG_G10_KEY_CLEAN_H + +#include "gpg.h" + +void mark_usable_uid_certs (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, + u32 *main_kid, struct key_item *klist, + u32 curtime, u32 *next_expire); + +void clean_one_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, + int noisy, int self_only, + int *uids_cleaned, int *sigs_cleaned); +void clean_key (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only, + int *uids_cleaned,int *sigs_cleaned); + + +#endif /*GNUPG_G10_KEY_CLEAN_H*/ diff --git a/g10/keydb.h b/g10/keydb.h index ea0fa9ddd..9748e571e 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -64,6 +64,20 @@ struct kbnode_struct { #define is_cloned_kbnode(a) ((a)->private_flag & 2) +/* + * A structure to store key identification as well as some stuff + * needed for key validation. + */ +struct key_item { + struct key_item *next; + unsigned int ownertrust,min_ownertrust; + byte trust_depth; + byte trust_value; + char *trust_regexp; + u32 kid[2]; +}; + + /* Bit flags used with build_pk_list. */ enum { @@ -133,6 +147,22 @@ enum }; +/* + * Check whether the signature SIG is in the klist K. + */ +static inline struct key_item * +is_in_klist (struct key_item *k, PKT_signature *sig) +{ + for (; k; k = k->next) + { + if (k->kid[0] == sig->keyid[0] && k->kid[1] == sig->keyid[1]) + return k; + } + return NULL; +} + + + /*-- keydb.c --*/ #define KEYDB_RESOURCE_FLAG_PRIMARY 2 /* The primary resource. */ diff --git a/g10/keyedit.c b/g10/keyedit.c index 00b4e7280..9716ed9d6 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -49,6 +49,7 @@ #include "../common/host2net.h" #include "tofu.h" #include "key-check.h" +#include "key-clean.h" #include "keyedit.h" static void show_prefs (PKT_user_id * uid, PKT_signature * selfsig, diff --git a/g10/trust.c b/g10/trust.c index 6d4f0e74b..bd1c89458 100644 --- a/g10/trust.c +++ b/g10/trust.c @@ -437,391 +437,3 @@ get_validity_string (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid) return _("revoked"); return trust_value_to_string (trustlevel); } - - - -/* - * Mark the signature of the given UID which are used to certify it. - * To do this, we first revmove all signatures which are not valid and - * from the remain ones we look for the latest one. If this is not a - * certification revocation signature we mark the signature by setting - * node flag bit 8. Revocations are marked with flag 11, and sigs - * from unavailable keys are marked with flag 12. Note that flag bits - * 9 and 10 are used for internal purposes. - */ -void -mark_usable_uid_certs (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, - u32 *main_kid, struct key_item *klist, - u32 curtime, u32 *next_expire) -{ - kbnode_t node; - PKT_signature *sig; - - /* First check all signatures. */ - for (node=uidnode->next; node; node = node->next) - { - int rc; - - node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); - if (node->pkt->pkttype == PKT_USER_ID - || node->pkt->pkttype == PKT_PUBLIC_SUBKEY - || node->pkt->pkttype == PKT_SECRET_SUBKEY) - break; /* ready */ - if (node->pkt->pkttype != PKT_SIGNATURE) - continue; - sig = node->pkt->pkt.signature; - if (main_kid - && sig->keyid[0] == main_kid[0] && sig->keyid[1] == main_kid[1]) - continue; /* ignore self-signatures if we pass in a main_kid */ - if (!IS_UID_SIG(sig) && !IS_UID_REV(sig)) - continue; /* we only look at these signature classes */ - if(sig->sig_class>=0x11 && sig->sig_class<=0x13 && - sig->sig_class-0x10flag |= 1<<12; - continue; - } - node->flag |= 1<<9; - } - /* Reset the remaining flags. */ - for (; node; node = node->next) - node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); - - /* kbnode flag usage: bit 9 is here set for signatures to consider, - * bit 10 will be set by the loop to keep track of keyIDs already - * processed, bit 8 will be set for the usable signatures, and bit - * 11 will be set for usable revocations. */ - - /* For each cert figure out the latest valid one. */ - for (node=uidnode->next; node; node = node->next) - { - KBNODE n, signode; - u32 kid[2]; - u32 sigdate; - - if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY - || node->pkt->pkttype == PKT_SECRET_SUBKEY) - break; - if ( !(node->flag & (1<<9)) ) - continue; /* not a node to look at */ - if ( (node->flag & (1<<10)) ) - continue; /* signature with a keyID already processed */ - node->flag |= (1<<10); /* mark this node as processed */ - sig = node->pkt->pkt.signature; - signode = node; - sigdate = sig->timestamp; - kid[0] = sig->keyid[0]; kid[1] = sig->keyid[1]; - - /* Now find the latest and greatest signature */ - for (n=uidnode->next; n; n = n->next) - { - if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY - || n->pkt->pkttype == PKT_SECRET_SUBKEY) - break; - if ( !(n->flag & (1<<9)) ) - continue; - if ( (n->flag & (1<<10)) ) - continue; /* shortcut already processed signatures */ - sig = n->pkt->pkt.signature; - if (kid[0] != sig->keyid[0] || kid[1] != sig->keyid[1]) - continue; - n->flag |= (1<<10); /* mark this node as processed */ - - /* If signode is nonrevocable and unexpired and n isn't, - then take signode (skip). It doesn't matter which is - older: if signode was older then we don't want to take n - as signode is nonrevocable. If n was older then we're - automatically fine. */ - - if(((IS_UID_SIG(signode->pkt->pkt.signature) && - !signode->pkt->pkt.signature->flags.revocable && - (signode->pkt->pkt.signature->expiredate==0 || - signode->pkt->pkt.signature->expiredate>curtime))) && - (!(IS_UID_SIG(n->pkt->pkt.signature) && - !n->pkt->pkt.signature->flags.revocable && - (n->pkt->pkt.signature->expiredate==0 || - n->pkt->pkt.signature->expiredate>curtime)))) - continue; - - /* If n is nonrevocable and unexpired and signode isn't, - then take n. Again, it doesn't matter which is older: if - n was older then we don't want to take signode as n is - nonrevocable. If signode was older then we're - automatically fine. */ - - if((!(IS_UID_SIG(signode->pkt->pkt.signature) && - !signode->pkt->pkt.signature->flags.revocable && - (signode->pkt->pkt.signature->expiredate==0 || - signode->pkt->pkt.signature->expiredate>curtime))) && - ((IS_UID_SIG(n->pkt->pkt.signature) && - !n->pkt->pkt.signature->flags.revocable && - (n->pkt->pkt.signature->expiredate==0 || - n->pkt->pkt.signature->expiredate>curtime)))) - { - signode = n; - sigdate = sig->timestamp; - continue; - } - - /* At this point, if it's newer, it goes in as the only - remaining possibilities are signode and n are both either - revocable or expired or both nonrevocable and unexpired. - If the timestamps are equal take the later ordered - packet, presuming that the key packets are hopefully in - their original order. */ - - if (sig->timestamp >= sigdate) - { - signode = n; - sigdate = sig->timestamp; - } - } - - sig = signode->pkt->pkt.signature; - if (IS_UID_SIG (sig)) - { /* this seems to be a usable one which is not revoked. - * Just need to check whether there is an expiration time, - * We do the expired certification after finding a suitable - * certification, the assumption is that a signator does not - * want that after the expiration of his certificate the - * system falls back to an older certification which has a - * different expiration time */ - const byte *p; - u32 expire; - - p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_EXPIRE, NULL ); - expire = p? sig->timestamp + buf32_to_u32(p) : 0; - - if (expire==0 || expire > curtime ) - { - signode->flag |= (1<<8); /* yeah, found a good cert */ - if (next_expire && expire && expire < *next_expire) - *next_expire = expire; - } - } - else - signode->flag |= (1<<11); - } -} - - -static int -clean_sigs_from_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, - int noisy, int self_only) -{ - int deleted = 0; - kbnode_t node; - u32 keyid[2]; - - log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY - || keyblock->pkt->pkttype == PKT_SECRET_KEY); - - keyid_from_pk (keyblock->pkt->pkt.public_key, keyid); - - /* Passing in a 0 for current time here means that we'll never weed - out an expired sig. This is correct behavior since we want to - keep the most recent expired sig in a series. */ - mark_usable_uid_certs (ctrl, keyblock, uidnode, NULL, NULL, 0, NULL); - - /* What we want to do here is remove signatures that are not - considered as part of the trust calculations. Thus, all invalid - signatures are out, as are any signatures that aren't the last of - a series of uid sigs or revocations It breaks down like this: - coming out of mark_usable_uid_certs, if a sig is unflagged, it is - not even a candidate. If a sig has flag 9 or 10, that means it - was selected as a candidate and vetted. If a sig has flag 8 it - is a usable signature. If a sig has flag 11 it is a usable - revocation. If a sig has flag 12 it was issued by an unavailable - key. "Usable" here means the most recent valid - signature/revocation in a series from a particular signer. - - Delete everything that isn't a usable uid sig (which might be - expired), a usable revocation, or a sig from an unavailable - key. */ - - for (node=uidnode->next; - node && node->pkt->pkttype==PKT_SIGNATURE; - node=node->next) - { - int keep; - - keep = self_only? (node->pkt->pkt.signature->keyid[0] == keyid[0] - && node->pkt->pkt.signature->keyid[1] == keyid[1]) : 1; - - /* Keep usable uid sigs ... */ - if ((node->flag & (1<<8)) && keep) - continue; - - /* ... and usable revocations... */ - if ((node->flag & (1<<11)) && keep) - continue; - - /* ... and sigs from unavailable keys. */ - /* disabled for now since more people seem to want sigs from - unavailable keys removed altogether. */ - /* - if(node->flag & (1<<12)) - continue; - */ - - /* Everything else we delete */ - - /* At this point, if 12 is set, the signing key was unavailable. - If 9 or 10 is set, it's superseded. Otherwise, it's - invalid. */ - - if (noisy) - log_info ("removing signature from key %s on user ID \"%s\": %s\n", - keystr (node->pkt->pkt.signature->keyid), - uidnode->pkt->pkt.user_id->name, - node->flag&(1<<12)? "key unavailable": - node->flag&(1<<9)? "signature superseded" - /* */ :"invalid signature" ); - - delete_kbnode (node); - deleted++; - } - - return deleted; -} - - -/* This is substantially easier than clean_sigs_from_uid since we just - have to establish if the uid has a valid self-sig, is not revoked, - and is not expired. Note that this does not take into account - whether the uid has a trust path to it - just whether the keyholder - themselves has certified the uid. Returns true if the uid was - compacted. To "compact" a user ID, we simply remove ALL signatures - except the self-sig that caused the user ID to be remove-worthy. - We don't actually remove the user ID packet itself since it might - be resurrected in a later merge. Note that this function requires - that the caller has already done a merge_keys_and_selfsig(). - - TODO: change the import code to allow importing a uid with only a - revocation if the uid already exists on the keyring. */ - -static int -clean_uid_from_key (kbnode_t keyblock, kbnode_t uidnode, int noisy) -{ - kbnode_t node; - PKT_user_id *uid = uidnode->pkt->pkt.user_id; - int deleted = 0; - - log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY - || keyblock->pkt->pkttype == PKT_SECRET_KEY); - log_assert (uidnode->pkt->pkttype==PKT_USER_ID); - - /* Skip valid user IDs, compacted user IDs, and non-self-signed user - IDs if --allow-non-selfsigned-uid is set. */ - if (uid->created - || uid->flags.compacted - || (!uid->flags.expired && !uid->flags.revoked && opt.allow_non_selfsigned_uid)) - return 0; - - for (node=uidnode->next; - node && node->pkt->pkttype == PKT_SIGNATURE; - node=node->next) - { - if (!node->pkt->pkt.signature->flags.chosen_selfsig) - { - delete_kbnode (node); - deleted = 1; - uidnode->pkt->pkt.user_id->flags.compacted = 1; - } - } - - if (noisy) - { - const char *reason; - char *user = utf8_to_native (uid->name, uid->len, 0); - - if (uid->flags.revoked) - reason = _("revoked"); - else if (uid->flags.expired) - reason = _("expired"); - else - reason = _("invalid"); - - log_info ("compacting user ID \"%s\" on key %s: %s\n", - user, keystr_from_pk (keyblock->pkt->pkt.public_key), - reason); - - xfree (user); - } - - return deleted; -} - - -/* Needs to be called after a merge_keys_and_selfsig() */ -void -clean_one_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, - int noisy, int self_only, int *uids_cleaned, int *sigs_cleaned) -{ - int dummy = 0; - - log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY - || keyblock->pkt->pkttype == PKT_SECRET_KEY); - log_assert (uidnode->pkt->pkttype==PKT_USER_ID); - - if (!uids_cleaned) - uids_cleaned = &dummy; - - if (!sigs_cleaned) - sigs_cleaned = &dummy; - - /* Do clean_uid_from_key first since if it fires off, we don't have - to bother with the other. */ - *uids_cleaned += clean_uid_from_key (keyblock, uidnode, noisy); - if (!uidnode->pkt->pkt.user_id->flags.compacted) - *sigs_cleaned += clean_sigs_from_uid (ctrl, keyblock, uidnode, - noisy, self_only); -} - - -/* NB: This function marks the deleted nodes only and the caller is - * responsible to skip or remove them. */ -void -clean_key (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only, - int *uids_cleaned, int *sigs_cleaned) -{ - kbnode_t node; - - merge_keys_and_selfsig (ctrl, keyblock); - - for (node = keyblock->next; - node && !(node->pkt->pkttype == PKT_PUBLIC_SUBKEY - || node->pkt->pkttype == PKT_SECRET_SUBKEY); - node = node->next) - { - if (node->pkt->pkttype == PKT_USER_ID) - clean_one_uid (ctrl, keyblock, node, noisy, self_only, - uids_cleaned, sigs_cleaned); - } - - /* Remove bogus subkey binding signatures: The only signatures - * allowed are of class 0x18 and 0x28. */ - log_assert (!node || (node->pkt->pkttype == PKT_PUBLIC_SUBKEY - || node->pkt->pkttype == PKT_SECRET_SUBKEY)); - for (; node; node = node->next) - { - if (is_deleted_kbnode (node)) - continue; - if (node->pkt->pkttype == PKT_SIGNATURE - && !(IS_SUBKEY_SIG (node->pkt->pkt.signature) - || IS_SUBKEY_REV (node->pkt->pkt.signature))) - { - delete_kbnode (node); - if (sigs_cleaned) - ++*sigs_cleaned; - } - } -} diff --git a/g10/trustdb.c b/g10/trustdb.c index 2c2d2394a..8ef6db542 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -41,6 +41,7 @@ #include "tdbio.h" #include "trustdb.h" #include "tofu.h" +#include "key-clean.h" typedef struct key_item **KeyHashTable; /* see new_key_hash_table() */ diff --git a/g10/trustdb.h b/g10/trustdb.h index 4bc4ca971..d52fc53f2 100644 --- a/g10/trustdb.h +++ b/g10/trustdb.h @@ -46,36 +46,6 @@ #define NAMEHASH_LEN 20 -/* - * A structure to store key identification as well as some stuff needed - * for validation - */ -struct key_item { - struct key_item *next; - unsigned int ownertrust,min_ownertrust; - byte trust_depth; - byte trust_value; - char *trust_regexp; - u32 kid[2]; -}; - - -/* - * Check whether the signature SIG is in the klist K. - */ -static inline struct key_item * -is_in_klist (struct key_item *k, PKT_signature *sig) -{ - for (; k; k = k->next) - { - if (k->kid[0] == sig->keyid[0] && k->kid[1] == sig->keyid[1]) - return k; - } - return NULL; -} - - - /*-- trust.c --*/ int cache_disabled_value (ctrl_t ctrl, PKT_public_key *pk); void register_trusted_keyid (u32 *keyid); @@ -103,17 +73,6 @@ int get_validity_info (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk, const char *get_validity_string (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid); -void mark_usable_uid_certs (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, - u32 *main_kid, struct key_item *klist, - u32 curtime, u32 *next_expire); - -void clean_one_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, - int noisy, int self_only, - int *uids_cleaned, int *sigs_cleaned); -void clean_key (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only, - int *uids_cleaned,int *sigs_cleaned); - - /*-- trustdb.c --*/ void tdb_register_trusted_keyid (u32 *keyid); -- cgit v1.2.3 From 6c3567196f7e72552f326ce07dccbcce31926e5d Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 6 Jul 2018 11:48:38 +0200 Subject: gpg: Split key cleaning function for clarity. * g10/key-clean.c (clean_key): Rename to clean_all_uids and split subkey cleaning into ... (clean_all_subkeys): new. Call that always after the former clean_key invocations. -- Note that the clean_all_subkeys function will later be extended. Signed-off-by: Werner Koch --- g10/export.c | 8 ++++++-- g10/import.c | 27 ++++++++++++++++++++------- g10/key-clean.c | 29 ++++++++++++++++++++++++----- g10/key-clean.h | 6 ++++-- 4 files changed, 54 insertions(+), 16 deletions(-) (limited to 'g10') diff --git a/g10/export.c b/g10/export.c index 44cf075b0..21ff23c8d 100644 --- a/g10/export.c +++ b/g10/export.c @@ -2007,8 +2007,12 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, * UID sigs (0x10, 0x11, 0x12, and 0x13). A designated * revocation is never stripped, even with export-minimal set. */ if ((options & EXPORT_CLEAN)) - clean_key (ctrl, keyblock, opt.verbose, - (options&EXPORT_MINIMAL), NULL, NULL); + { + merge_keys_and_selfsig (ctrl, keyblock); + clean_all_uids (ctrl, keyblock, opt.verbose, + (options&EXPORT_MINIMAL), NULL, NULL); + clean_all_subkeys (ctrl, keyblock, opt.verbose, NULL, NULL); + } if (export_keep_uid) { diff --git a/g10/import.c b/g10/import.c index 757063eea..66ff925f5 100644 --- a/g10/import.c +++ b/g10/import.c @@ -1759,9 +1759,13 @@ import_one (ctrl_t ctrl, that we have to clean later. This has no practical impact on the end result, but does result in less logging which might confuse the user. */ - if (options&IMPORT_CLEAN) - clean_key (ctrl, keyblock, - opt.verbose, (options&IMPORT_MINIMAL), NULL, NULL); + if ((options & IMPORT_CLEAN)) + { + merge_keys_and_selfsig (ctrl, keyblock); + clean_all_uids (ctrl, keyblock, + opt.verbose, (options&IMPORT_MINIMAL), NULL, NULL); + clean_all_subkeys (ctrl, keyblock, opt.verbose, NULL, NULL); + } clear_kbnode_flags( keyblock ); @@ -1902,8 +1906,12 @@ import_one (ctrl_t ctrl, log_info (_("writing to '%s'\n"), keydb_get_resource_name (hd) ); if ((options & IMPORT_CLEAN)) - clean_key (ctrl, keyblock, opt.verbose, (options&IMPORT_MINIMAL), - &n_uids_cleaned,&n_sigs_cleaned); + { + merge_keys_and_selfsig (ctrl, keyblock); + clean_all_uids (ctrl, keyblock, opt.verbose, (options&IMPORT_MINIMAL), + &n_uids_cleaned,&n_sigs_cleaned); + clean_all_subkeys (ctrl, keyblock, opt.verbose, NULL, NULL); + } /* Unless we are in restore mode apply meta data to the * keyblock. Note that this will never change the first packet @@ -1988,8 +1996,13 @@ import_one (ctrl_t ctrl, goto leave; if ((options & IMPORT_CLEAN)) - clean_key (ctrl, keyblock_orig, opt.verbose, (options&IMPORT_MINIMAL), - &n_uids_cleaned,&n_sigs_cleaned); + { + merge_keys_and_selfsig (ctrl, keyblock_orig); + clean_all_uids (ctrl, keyblock_orig, opt.verbose, + (options&IMPORT_MINIMAL), + &n_uids_cleaned,&n_sigs_cleaned); + clean_all_subkeys (ctrl, keyblock_orig, opt.verbose, NULL, NULL); + } if (n_uids || n_sigs || n_subk || n_sigs_cleaned || n_uids_cleaned) { diff --git a/g10/key-clean.c b/g10/key-clean.c index d022ff44d..10478a46e 100644 --- a/g10/key-clean.c +++ b/g10/key-clean.c @@ -383,15 +383,14 @@ clean_one_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, /* NB: This function marks the deleted nodes only and the caller is - * responsible to skip or remove them. */ + * responsible to skip or remove them. Needs to be called after a + * merge_keys_and_selfsig(). */ void -clean_key (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only, - int *uids_cleaned, int *sigs_cleaned) +clean_all_uids (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only, + int *uids_cleaned, int *sigs_cleaned) { kbnode_t node; - merge_keys_and_selfsig (ctrl, keyblock); - for (node = keyblock->next; node && !(node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY); @@ -406,6 +405,26 @@ clean_key (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only, * allowed are of class 0x18 and 0x28. */ log_assert (!node || (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY)); +} + + +/* This function only marks the deleted nodes and the caller is + * responsible to skip or remove them. Needs to be called after a + * merge_keys_and_selfsig. */ +void +clean_all_subkeys (ctrl_t ctrl, kbnode_t keyblock, int noisy, + int *subkeys_cleaned, int *sigs_cleaned) +{ + kbnode_t node; + + for (node = keyblock->next; node; node = node->next) + if (!is_deleted_kbnode (node) + && (node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY)) + break; + + /* Remove bogus subkey binding signatures: The only signatures + * allowed are of class 0x18 and 0x28. */ for (; node; node = node->next) { if (is_deleted_kbnode (node)) diff --git a/g10/key-clean.h b/g10/key-clean.h index 4dfd9509c..693843064 100644 --- a/g10/key-clean.h +++ b/g10/key-clean.h @@ -30,8 +30,10 @@ void mark_usable_uid_certs (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, void clean_one_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, int noisy, int self_only, int *uids_cleaned, int *sigs_cleaned); -void clean_key (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only, - int *uids_cleaned,int *sigs_cleaned); +void clean_all_uids (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only, + int *uids_cleaned,int *sigs_cleaned); +void clean_all_subkeys (ctrl_t ctrl, kbnode_t keyblock, int noisy, + int *subkeys_cleaned, int *sigs_cleaned); #endif /*GNUPG_G10_KEY_CLEAN_H*/ -- cgit v1.2.3 From c2fd65ec8498a08ee36ca52d99b6b014f6db8d93 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 9 Jul 2018 09:49:09 +0200 Subject: gpg: Let export-clean remove expired subkeys. * g10/key-clean.h (KEY_CLEAN_NONE, KEY_CLEAN_INVALID) (KEY_CLEAN_ENCR, KEY_CLEAN_AUTHENCR, KEY_CLEAN_ALL): New. * g10/key-clean.c (clean_one_subkey): New. (clean_all_subkeys): Add arg CLEAN_LEVEL. * g10/import.c (import_one): Call clean_all_subkeys with KEY_CLEAN_NONE. * g10/export.c (do_export_stream): Call clean_all_subkeys depedning on the export clean options. -- GnuPG-bug-id: 3622 Signed-off-by: Werner Koch --- g10/export.c | 11 +++--- g10/import.c | 9 +++-- g10/key-clean.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- g10/key-clean.h | 15 ++++++++- 4 files changed, 125 insertions(+), 12 deletions(-) (limited to 'g10') diff --git a/g10/export.c b/g10/export.c index 21ff23c8d..e94e959fb 100644 --- a/g10/export.c +++ b/g10/export.c @@ -2003,15 +2003,18 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, } /* Always do the cleaning on the public key part if requested. - * Note that both export-clean and export-minimal only apply to - * UID sigs (0x10, 0x11, 0x12, and 0x13). A designated - * revocation is never stripped, even with export-minimal set. */ + * A designated revocation is never stripped, even with + * export-minimal set. */ if ((options & EXPORT_CLEAN)) { merge_keys_and_selfsig (ctrl, keyblock); clean_all_uids (ctrl, keyblock, opt.verbose, (options&EXPORT_MINIMAL), NULL, NULL); - clean_all_subkeys (ctrl, keyblock, opt.verbose, NULL, NULL); + clean_all_subkeys (ctrl, keyblock, opt.verbose, + (options&EXPORT_MINIMAL)? KEY_CLEAN_ALL + /**/ : KEY_CLEAN_AUTHENCR, + NULL, NULL); + commit_kbnode (&keyblock); } if (export_keep_uid) diff --git a/g10/import.c b/g10/import.c index 66ff925f5..1eb3ecceb 100644 --- a/g10/import.c +++ b/g10/import.c @@ -1764,7 +1764,8 @@ import_one (ctrl_t ctrl, merge_keys_and_selfsig (ctrl, keyblock); clean_all_uids (ctrl, keyblock, opt.verbose, (options&IMPORT_MINIMAL), NULL, NULL); - clean_all_subkeys (ctrl, keyblock, opt.verbose, NULL, NULL); + clean_all_subkeys (ctrl, keyblock, opt.verbose, KEY_CLEAN_NONE, + NULL, NULL); } clear_kbnode_flags( keyblock ); @@ -1910,7 +1911,8 @@ import_one (ctrl_t ctrl, merge_keys_and_selfsig (ctrl, keyblock); clean_all_uids (ctrl, keyblock, opt.verbose, (options&IMPORT_MINIMAL), &n_uids_cleaned,&n_sigs_cleaned); - clean_all_subkeys (ctrl, keyblock, opt.verbose, NULL, NULL); + clean_all_subkeys (ctrl, keyblock, opt.verbose, KEY_CLEAN_NONE, + NULL, NULL); } /* Unless we are in restore mode apply meta data to the @@ -2001,7 +2003,8 @@ import_one (ctrl_t ctrl, clean_all_uids (ctrl, keyblock_orig, opt.verbose, (options&IMPORT_MINIMAL), &n_uids_cleaned,&n_sigs_cleaned); - clean_all_subkeys (ctrl, keyblock_orig, opt.verbose, NULL, NULL); + clean_all_subkeys (ctrl, keyblock_orig, opt.verbose, KEY_CLEAN_NONE, + NULL, NULL); } if (n_uids || n_sigs || n_subk || n_sigs_cleaned || n_uids_cleaned) diff --git a/g10/key-clean.c b/g10/key-clean.c index 10478a46e..097ca17e9 100644 --- a/g10/key-clean.c +++ b/g10/key-clean.c @@ -408,24 +408,101 @@ clean_all_uids (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only, } +/* Helper for clean_all_subkeys. */ +static int +clean_one_subkey (ctrl_t ctrl, kbnode_t subkeynode, int noisy, int clean_level) +{ + kbnode_t node; + PKT_public_key *pk = subkeynode->pkt->pkt.public_key; + unsigned int use = pk->pubkey_usage; + int do_clean = 0; + + (void)ctrl; + (void)noisy; + + log_assert (subkeynode->pkt->pkttype == PKT_PUBLIC_SUBKEY + || subkeynode->pkt->pkttype == PKT_SECRET_SUBKEY); + + if (DBG_LOOKUP) + log_debug ("\tchecking subkey %08lX [%c%c%c%c%c]\n", + (ulong) keyid_from_pk (pk, NULL), + (use & PUBKEY_USAGE_ENC)? 'e':'-', + (use & PUBKEY_USAGE_SIG)? 's':'-', + (use & PUBKEY_USAGE_CERT)? 'c':'-', + (use & PUBKEY_USAGE_AUTH)? 'a':'-', + (use & PUBKEY_USAGE_UNKNOWN)? '?':'-'); + + if (!pk->flags.valid) + { + if (DBG_LOOKUP) + log_debug ("\tsubkey not valid\n"); + if (clean_level == KEY_CLEAN_INVALID) + do_clean = 1; + } + if (pk->has_expired) + { + if (DBG_LOOKUP) + log_debug ("\tsubkey has expired\n"); + if (clean_level == KEY_CLEAN_ALL) + do_clean = 1; + else if (clean_level == KEY_CLEAN_AUTHENCR + && (use & (PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH)) + && !(use & (PUBKEY_USAGE_SIG | PUBKEY_USAGE_CERT))) + do_clean = 1; + else if (clean_level == KEY_CLEAN_ENCR + && (use & PUBKEY_USAGE_ENC) + && !(use & (PUBKEY_USAGE_SIG | PUBKEY_USAGE_CERT + | PUBKEY_USAGE_AUTH))) + do_clean = 1; + } + if (pk->flags.revoked) + { + if (DBG_LOOKUP) + log_debug ("\tsubkey has been revoked (keeping)\n"); + /* Avoid any cleaning because revocations are important. */ + do_clean = 0; + } + if (!do_clean) + return 0; + + if (DBG_LOOKUP) + log_debug ("\t=> removing this subkey\n"); + + delete_kbnode (subkeynode); + for (node = subkeynode->next; + node && !(node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY); + node = node->next) + delete_kbnode (node); + + return 1; +} + + /* This function only marks the deleted nodes and the caller is * responsible to skip or remove them. Needs to be called after a - * merge_keys_and_selfsig. */ + * merge_keys_and_selfsig. CLEAN_LEVEL is one of the KEY_CLEAN_* + * values. */ void -clean_all_subkeys (ctrl_t ctrl, kbnode_t keyblock, int noisy, +clean_all_subkeys (ctrl_t ctrl, kbnode_t keyblock, int noisy, int clean_level, int *subkeys_cleaned, int *sigs_cleaned) { - kbnode_t node; + kbnode_t first_subkey, node; + + if (DBG_LOOKUP) + log_debug ("clean_all_subkeys: checking key %08lX\n", + (ulong) keyid_from_pk (keyblock->pkt->pkt.public_key, NULL)); for (node = keyblock->next; node; node = node->next) if (!is_deleted_kbnode (node) && (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY)) break; + first_subkey = node; /* Remove bogus subkey binding signatures: The only signatures * allowed are of class 0x18 and 0x28. */ - for (; node; node = node->next) + for (node = first_subkey; node; node = node->next) { if (is_deleted_kbnode (node)) continue; @@ -438,4 +515,21 @@ clean_all_subkeys (ctrl_t ctrl, kbnode_t keyblock, int noisy, ++*sigs_cleaned; } } + + /* Do the selected cleaning. */ + if (clean_level > KEY_CLEAN_NONE) + { + for (node = first_subkey; node; node = node->next) + { + if (is_deleted_kbnode (node)) + continue; + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY) + if (clean_one_subkey (ctrl, node, noisy, clean_level)) + { + if (subkeys_cleaned) + ++*subkeys_cleaned; + } + } + } } diff --git a/g10/key-clean.h b/g10/key-clean.h index 693843064..a0fb76950 100644 --- a/g10/key-clean.h +++ b/g10/key-clean.h @@ -23,6 +23,18 @@ #include "gpg.h" +/* No explict cleaning. */ +#define KEY_CLEAN_NONE 0 +/* Remove only invalid subkeys (ie. missing key-bindings) */ +#define KEY_CLEAN_INVALID 1 +/* Remove expired encryption keys */ +#define KEY_CLEAN_ENCR 2 +/* Remove expired authentication and encryption keys. */ +#define KEY_CLEAN_AUTHENCR 3 +/* Remove all expired subkeys. */ +#define KEY_CLEAN_ALL 4 + + void mark_usable_uid_certs (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, u32 *main_kid, struct key_item *klist, u32 curtime, u32 *next_expire); @@ -32,7 +44,8 @@ void clean_one_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, int *uids_cleaned, int *sigs_cleaned); void clean_all_uids (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only, int *uids_cleaned,int *sigs_cleaned); -void clean_all_subkeys (ctrl_t ctrl, kbnode_t keyblock, int noisy, +void clean_all_subkeys (ctrl_t ctrl, kbnode_t keyblock, + int noisy, int clean_level, int *subkeys_cleaned, int *sigs_cleaned); -- cgit v1.2.3 From 76989d5bd89ed11f5b3656dc4748fcfc939a46dc Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 9 Jul 2018 12:01:02 +0200 Subject: gpg: Remove multiple subkey bindings during export-clean. * g10/key-clean.c (clean_one_subkey_dupsigs): New. (clean_all_subkeys): Call it. -- GnuPG-bug-id: 3804 Signed-off-by: Werner Koch --- g10/key-clean.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 84 insertions(+), 5 deletions(-) (limited to 'g10') diff --git a/g10/key-clean.c b/g10/key-clean.c index 097ca17e9..f66a0dbb4 100644 --- a/g10/key-clean.c +++ b/g10/key-clean.c @@ -479,6 +479,67 @@ clean_one_subkey (ctrl_t ctrl, kbnode_t subkeynode, int noisy, int clean_level) } +/* Helper for clean_all_subkeys. Here duplicate signatures from a + * subkey are removed. This should in general not happen because + * import takes care of that. However, sometimes other tools are used + * to manage a keyring or key has been imported a long time ago. */ +static int +clean_one_subkey_dupsigs (ctrl_t ctrl, kbnode_t subkeynode) +{ + kbnode_t node; + PKT_public_key *pk = subkeynode->pkt->pkt.public_key; + int any_choosen = 0; + int count = 0; + + (void)ctrl; + + log_assert (subkeynode->pkt->pkttype == PKT_PUBLIC_SUBKEY + || subkeynode->pkt->pkttype == PKT_SECRET_SUBKEY); + + if (DBG_LOOKUP) + log_debug ("\tchecking subkey %08lX for dupsigs\n", + (ulong) keyid_from_pk (pk, NULL)); + + /* First check that the choosen flag has been set. Note that we + * only look at plain signatures so to keep all revocation + * signatures which may carry important information. */ + for (node = subkeynode->next; + node && !(node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY); + node = node->next) + { + if (!is_deleted_kbnode (node) + && node->pkt->pkttype == PKT_SIGNATURE + && IS_SUBKEY_SIG (node->pkt->pkt.signature) + && node->pkt->pkt.signature->flags.chosen_selfsig) + { + any_choosen = 1; + break; + } + } + + if (!any_choosen) + return 0; /* Ooops no choosen flag set - we can't decide. */ + + for (node = subkeynode->next; + node && !(node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY); + node = node->next) + { + if (!is_deleted_kbnode (node) + && node->pkt->pkttype == PKT_SIGNATURE + && IS_SUBKEY_SIG (node->pkt->pkt.signature) + && !node->pkt->pkt.signature->flags.chosen_selfsig) + { + delete_kbnode (node); + count++; + } + } + + return count; +} + + /* This function only marks the deleted nodes and the caller is * responsible to skip or remove them. Needs to be called after a * merge_keys_and_selfsig. CLEAN_LEVEL is one of the KEY_CLEAN_* @@ -488,6 +549,7 @@ clean_all_subkeys (ctrl_t ctrl, kbnode_t keyblock, int noisy, int clean_level, int *subkeys_cleaned, int *sigs_cleaned) { kbnode_t first_subkey, node; + int n; if (DBG_LOOKUP) log_debug ("clean_all_subkeys: checking key %08lX\n", @@ -519,17 +581,34 @@ clean_all_subkeys (ctrl_t ctrl, kbnode_t keyblock, int noisy, int clean_level, /* Do the selected cleaning. */ if (clean_level > KEY_CLEAN_NONE) { + /* Clean enitre subkeys. */ for (node = first_subkey; node; node = node->next) { if (is_deleted_kbnode (node)) continue; if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) - if (clean_one_subkey (ctrl, node, noisy, clean_level)) - { - if (subkeys_cleaned) - ++*subkeys_cleaned; - } + { + if (clean_one_subkey (ctrl, node, noisy, clean_level)) + { + if (subkeys_cleaned) + ++*subkeys_cleaned; + } + } + } + + /* Clean duplicate signatures from a subkey. */ + for (node = first_subkey; node; node = node->next) + { + if (is_deleted_kbnode (node)) + continue; + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY) + { + n = clean_one_subkey_dupsigs (ctrl, node); + if (sigs_cleaned) + *sigs_cleaned += n; + } } } } -- cgit v1.2.3