From 2b32eb15aac8cde4144243a67bd4f27f724bc78b Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 25 Jun 2012 16:27:04 +0200 Subject: [PATCH] gpg: Disallow the use of v3 keys. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * g10/gpg.c: Add options --allow-v3-keys and --no-allow-v3-keys. (main): Enable --allow-v3-keys in --pgp2 mode. * g10/options.h (opt): Add field allow_v3_keys. * g10/import.c (delete_v3_subkeys): New. (import_one): Skip v3 keys and delete v3 subkeys. (import_print_stats): Print stats on v3 keys and subkeys. * g10/getkey.c (finish_lookup): Skip v3 keys. -- This is a first take on disabling v3 keys. We may need to add some tweaks to make decryption using an existing v3 key easier. There is no need to disallow decryption. Thanks to Georgi Guninski to put some pressure on us to finally do what PGP 2 folks will probably don’t like. See the discussion on gnupg-devel starting 2012-06-22. --- NEWS | 4 +++ doc/DETAILS | 2 +- doc/gpg.texi | 14 ++++++++++ g10/getkey.c | 13 +++++++++- g10/gpg.c | 8 ++++++ g10/import.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++--- g10/options.h | 1 + 7 files changed, 108 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index a8352ca53..521ccf9aa 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,10 @@ Noteworthy changes in version 2.1.0beta4 (unreleased) * The hash algorithm is now printed for sig records in key listings. + * GPG does not anymore allow the use of the long deprecated v3 keys + (PGP 2 keys). The new option --allow-v3-keys can be used to + exceptionally allow them. + Noteworthy changes in version 2.1.0beta3 (2011-12-20) ----------------------------------------------------- diff --git a/doc/DETAILS b/doc/DETAILS index 59434f10c..55f9ce6dd 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -459,7 +459,7 @@ more arguments in future versions. IMPORT_RES - + Final statistics on import process (this is one long line) FILE_START diff --git a/doc/gpg.texi b/doc/gpg.texi index 420326b5d..d8e4bcdef 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -2637,6 +2637,20 @@ Disable all checks on the form of the user ID while generating a new one. This option should only be used in very special environments as it does not ensure the de-facto standard format of user IDs. +@ifset gpgtwoone +@item --allow-v3-keys +@itemx --no-allow-v3-keys +@opindex allow-v3-keys +Allow the use of deprecated v3 keys with @command{gpg}. The default +is not to allow their use. + +Since version 2.1 GnuPG does not anymore allow the import or use of v3 +keys. Those keys have been generated in the past by PGP 2 and exhibit +a couple of flaws. For example they rely on the broken MD5 algorithm. +OpenPGP has long deprecated their use (cf. RFC-4880, section 5.5.2). +This option may be used to exceptionally allow their use. +@end ifset + @item --ignore-time-conflict @opindex ignore-time-conflict GnuPG normally checks that the timestamps associated with keys and diff --git a/g10/getkey.c b/g10/getkey.c index 929427302..348755df1 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -2318,6 +2318,12 @@ finish_lookup (GETKEY_CTX ctx) if (DBG_CACHE) log_debug ("\tchecking subkey %08lX\n", (ulong) keyid_from_pk (pk, NULL)); + if (pk->version <= 3 && !opt.allow_v3_keys) + { + if (DBG_CACHE) + log_debug ("\tv3 subkey not allowed\n"); + continue; + } if (!pk->flags.valid) { if (DBG_CACHE) @@ -2373,7 +2379,12 @@ finish_lookup (GETKEY_CTX ctx) if (DBG_CACHE && !foundk && !req_prim) log_debug ("\tno suitable subkeys found - trying primary\n"); pk = keyblock->pkt->pkt.public_key; - if (!pk->flags.valid) + if (pk->version <= 3 && !opt.allow_v3_keys) + { + if (DBG_CACHE) + log_debug ("\tv3 primary key not allowed\n"); + } + else if (!pk->flags.valid) { if (DBG_CACHE) log_debug ("\tprimary key not valid\n"); diff --git a/g10/gpg.c b/g10/gpg.c index c8dbbfe83..74f34f6b4 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -305,6 +305,8 @@ enum cmd_and_opt_values oNoAllowNonSelfsignedUID, oAllowFreeformUID, oNoAllowFreeformUID, + oAllowV3Keys, + oNoAllowV3Keys, oAllowSecretKeyImport, oEnableSpecialFilenames, oNoLiteral, @@ -682,6 +684,9 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oNoAllowNonSelfsignedUID, "no-allow-non-selfsigned-uid", "@"), ARGPARSE_s_n (oAllowFreeformUID, "allow-freeform-uid", "@"), ARGPARSE_s_n (oNoAllowFreeformUID, "no-allow-freeform-uid", "@"), + ARGPARSE_s_n (oAllowFreeformUID, "allow-freeform-uid", "@"), + ARGPARSE_s_n (oAllowV3Keys, "allow-v3-keys", "@"), + ARGPARSE_s_n (oNoAllowV3Keys, "no-allow-v3-keys", "@"), ARGPARSE_s_n (oNoLiteral, "no-literal", "@"), ARGPARSE_p_u (oSetFilesize, "set-filesize", "@"), ARGPARSE_s_n (oHonorHttpProxy, "honor-http-proxy", "@"), @@ -2805,6 +2810,8 @@ main (int argc, char **argv) case oNoAllowNonSelfsignedUID: opt.allow_non_selfsigned_uid=0; break; case oAllowFreeformUID: opt.allow_freeform_uid = 1; break; case oNoAllowFreeformUID: opt.allow_freeform_uid = 0; break; + case oAllowV3Keys: opt.allow_v3_keys = 1; break; + case oNoAllowV3Keys: opt.allow_v3_keys = 0; break; case oNoLiteral: opt.no_literal = 1; break; case oSetFilesize: opt.set_filesize = pargs.r.ret_ulong; break; case oHonorHttpProxy: @@ -3184,6 +3191,7 @@ main (int argc, char **argv) xfree(s2k_digest_string); s2k_digest_string = xstrdup("md5"); opt.compress_algo = COMPRESS_ALGO_ZIP; + opt.allow_v3_keys = 1; } } else if(PGP6) diff --git a/g10/import.c b/g10/import.c index bfe02eb16..06a92af02 100644 --- a/g10/import.c +++ b/g10/import.c @@ -57,6 +57,8 @@ struct stats_s { ulong not_imported; ulong n_sigs_cleaned; ulong n_uids_cleaned; + ulong skipped_v3_keys; + ulong skipped_v3_subkeys; }; @@ -77,6 +79,7 @@ static int chk_self_sigs( const char *fname, KBNODE keyblock, PKT_public_key *pk, u32 *keyid, int *non_self ); static int delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid, unsigned int options ); +static int delete_v3_subkeys (kbnode_t keyblock); static int merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock, u32 *keyid, int *n_uids, int *n_sigs, int *n_subk ); @@ -330,6 +333,9 @@ import_print_stats (void *hd) if( stats->skipped_new_keys ) log_info(_(" skipped new keys: %lu\n"), stats->skipped_new_keys ); + if( stats->skipped_v3_keys ) + log_info(_(" skipped v3 keys: %lu\n"), + stats->skipped_v3_keys); if( stats->no_user_id ) log_info(_(" w/o user IDs: %lu\n"), stats->no_user_id ); if( stats->imported || stats->imported_rsa ) { @@ -344,6 +350,9 @@ import_print_stats (void *hd) log_info(_(" new user IDs: %lu\n"), stats->n_uids ); if( stats->n_subk ) log_info(_(" new subkeys: %lu\n"), stats->n_subk ); + if( stats->skipped_v3_subkeys) + log_info(_(" skipped v3 subkeys: %lu\n"), + stats->skipped_v3_subkeys); if( stats->n_sigs ) log_info(_(" new signatures: %lu\n"), stats->n_sigs ); if( stats->n_revoc ) @@ -363,8 +372,10 @@ import_print_stats (void *hd) } if( is_status_enabled() ) { - char buf[14*20]; - sprintf(buf, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", + char buf[16*20]; + snprintf (buf, sizeof buf, + "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu" + " %lu %lu", stats->count, stats->no_user_id, stats->imported, @@ -378,7 +389,9 @@ import_print_stats (void *hd) stats->secret_imported, stats->secret_dups, stats->skipped_new_keys, - stats->not_imported ); + stats->not_imported, + stats->skipped_v3_keys, + stats->skipped_v3_subkeys); write_status_text( STATUS_IMPORT_RES, buf ); } } @@ -771,6 +784,7 @@ import_one (ctrl_t ctrl, int mod_key = 0; int same_key = 0; int non_self = 0; + int count; /* get the key and print some info about it */ node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); @@ -795,6 +809,18 @@ import_one (ctrl_t ctrl, log_printf ("\n"); } + /* We don't allow to import v3 keys unless the --allow-v3-keys + option is active. Note that this checks only the primary key. + v3 subkeys will be removed later. */ + if (pk->version <= 3 && !opt.allow_v3_keys) + { + if (opt.verbose) + log_info (_("key %s: v3 keys are not allowed - skipped\n"), + keystr (keyid)); + stats->skipped_new_keys++; + stats->skipped_v3_keys++; + return 0; + } if( !uidnode ) { @@ -855,6 +881,14 @@ import_one (ctrl_t ctrl, return 0; } + if (!opt.allow_v3_keys && (count = delete_v3_subkeys (keyblock))) + { + stats->skipped_v3_subkeys += count; + if (!opt.quiet) + log_info (_("key %s: removed v3 subkeys: %d\n"), + keystr (keyid), count); + } + /* do we have this key already in one of our pubrings ? */ pk_orig = xmalloc_clear( sizeof *pk_orig ); rc = get_pubkey_fast ( pk_orig, keyid ); @@ -2094,6 +2128,37 @@ delete_inv_parts( const char *fname, KBNODE keyblock, } +/* Remove all v3 public subkeys from KEYBLOCK. Returns the number of + * removed subkeys. */ +static int +delete_v3_subkeys (kbnode_t keyblock) +{ + kbnode_t node; + int count = 0; + + for (node = keyblock->next; node; node = node->next ) + { + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY + && node->pkt->pkt.public_key->version == 3) + { + delete_kbnode (node); + while (node->next && node->next->pkt->pkttype == PKT_SIGNATURE) + { + delete_kbnode (node->next); + node = node->next; + } + count++; + } + } + + /* Because KEYBLOCK is the primary public key, it is never marked + * for deletion and thus commit_keyblock won't change KEYBLOCK. */ + if (count) + commit_kbnode (&keyblock); + return count; +} + + /**************** * It may happen that the imported keyblock has duplicated user IDs. * We check this here and collapse those user IDs together with their diff --git a/g10/options.h b/g10/options.h index e67d0ce04..3a9f43c36 100644 --- a/g10/options.h +++ b/g10/options.h @@ -170,6 +170,7 @@ struct strlist_t sig_subpackets; int allow_non_selfsigned_uid; int allow_freeform_uid; + int allow_v3_keys; /* Allow the use of v3 keys. */ int no_literal; ulong set_filesize; int fast_list_mode;