diff --git a/g10/ChangeLog b/g10/ChangeLog index 02de71b4e..8478edce6 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,5 +1,15 @@ 2002-07-24 David Shaw + * main.h, import.c (parse_import_options, fix_hkp_corruption, + import_one, delete_inv_parts), g10.c (main): New import-option + "repair-hkp-subkey-bug", which repairs as much as possible the HKP + mangling multiple subkeys bug. It is on by default for keyserver + receives, and off by default for regular --import. + + * main.h, import.c (import, import_one, delete_inv_parts), hkp.c + (hkp_ask_import), keyserver.c (keyserver_spawn): Use keyserver + import options when doing keyserver receives. + * options.h, exec.h, exec.c (set_exec_path, exec_write), g10.c (main), keyserver.c (keyserver_spawn): If the user does not use "exec-path", completely replace $PATH with GNUPG_LIBEXECDIR before diff --git a/g10/g10.c b/g10/g10.c index 7a1b624f3..fc50781a2 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -906,10 +906,12 @@ main( int argc, char **argv ) opt.pgp2_workarounds = 1; opt.force_v3_sigs = 1; opt.escape_from = 1; - opt.import_options=IMPORT_DEFAULT; - opt.export_options=EXPORT_DEFAULT; - opt.keyserver_options.import_options=IMPORT_DEFAULT; - opt.keyserver_options.export_options=EXPORT_DEFAULT; + opt.import_options=0; + opt.export_options= + EXPORT_INCLUDE_NON_RFC|EXPORT_INCLUDE_ATTRIBUTES; + opt.keyserver_options.import_options=IMPORT_REPAIR_HKP_SUBKEY_BUG; + opt.keyserver_options.export_options= + EXPORT_INCLUDE_NON_RFC|EXPORT_INCLUDE_ATTRIBUTES; opt.keyserver_options.include_subkeys=1; #if defined (__MINGW32__) || defined (__CYGWIN32__) opt.homedir = read_w32_registry_string( NULL, "Software\\GNU\\GnuPG", "HomeDir" ); @@ -2027,7 +2029,8 @@ main( int argc, char **argv ) case aFastImport: case aImport: - import_keys( argc? argv:NULL, argc, (cmd == aFastImport), NULL ); + import_keys( argc? argv:NULL, argc, (cmd == aFastImport), + NULL, opt.import_options ); break; case aExport: diff --git a/g10/hkp.c b/g10/hkp.c index 09fa5a12a..234a57835 100644 --- a/g10/hkp.c +++ b/g10/hkp.c @@ -98,7 +98,8 @@ hkp_ask_import( KEYDB_SEARCH_DESC *desc, void *stats_handle) : g10_errstr(rc) ); } else { - rc = import_keys_stream( hd.fp_read, 0, stats_handle); + rc = import_keys_stream( hd.fp_read, 0, stats_handle, + opt.keyserver_options.import_options); http_close( &hd ); } diff --git a/g10/import.c b/g10/import.c index 415e26ef6..4be313f74 100644 --- a/g10/import.c +++ b/g10/import.c @@ -55,19 +55,20 @@ struct stats_s { static int import( IOBUF inp, int fast, const char* fname, - struct stats_s *stats ); + struct stats_s *stats, unsigned int options ); static int read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root ); static void revocation_present(KBNODE keyblock); static void remove_bad_stuff (KBNODE keyblock); static int import_one( const char *fname, KBNODE keyblock, int fast, - struct stats_s *stats); + struct stats_s *stats, unsigned int options); static int import_secret_one( const char *fname, KBNODE keyblock, struct stats_s *stats ); static int import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats); static int chk_self_sigs( const char *fname, KBNODE keyblock, PKT_public_key *pk, u32 *keyid ); -static int delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid ); +static int delete_inv_parts( const char *fname, KBNODE keyblock, + u32 *keyid, unsigned int options ); static int merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock, u32 *keyid, int *n_uids, int *n_sigs, int *n_subk ); @@ -93,6 +94,7 @@ parse_import_options(char *str,unsigned int *options) } import_opts[]= { {"allow-local-sigs",IMPORT_ALLOW_LOCAL_SIGS}, + {"repair-hkp-subkey-bug",IMPORT_REPAIR_HKP_SUBKEY_BUG}, {NULL,0} }; @@ -170,7 +172,8 @@ import_release_stats_handle (void *p) * */ void -import_keys( char **fnames, int nnames, int fast, void *stats_handle ) +import_keys( char **fnames, int nnames, int fast, + void *stats_handle, unsigned int options ) { int i; struct stats_s *stats = stats_handle; @@ -189,7 +192,7 @@ import_keys( char **fnames, int nnames, int fast, void *stats_handle ) if( !inp ) log_error(_("can't open `%s': %s\n"), fname, strerror(errno) ); else { - int rc = import( inp, fast, fname, stats ); + int rc = import( inp, fast, fname, stats, options ); iobuf_close(inp); if( rc ) log_error("import from `%s' failed: %s\n", fname, @@ -206,7 +209,8 @@ import_keys( char **fnames, int nnames, int fast, void *stats_handle ) } int -import_keys_stream( IOBUF inp, int fast, void *stats_handle ) +import_keys_stream( IOBUF inp, int fast, + void *stats_handle, unsigned int options ) { int rc = 0; struct stats_s *stats = stats_handle; @@ -214,7 +218,7 @@ import_keys_stream( IOBUF inp, int fast, void *stats_handle ) if (!stats) stats = import_new_stats_handle (); - rc = import( inp, fast, "[stream]", stats); + rc = import( inp, fast, "[stream]", stats, options); if (!stats_handle) { import_print_stats (stats); import_release_stats_handle (stats); @@ -224,7 +228,8 @@ import_keys_stream( IOBUF inp, int fast, void *stats_handle ) } static int -import( IOBUF inp, int fast, const char* fname, struct stats_s *stats ) +import( IOBUF inp, int fast, const char* fname, + struct stats_s *stats, unsigned int options ) { PACKET *pending_pkt = NULL; KBNODE keyblock; @@ -241,7 +246,7 @@ import( IOBUF inp, int fast, const char* fname, struct stats_s *stats ) while( !(rc = read_block( inp, &pending_pkt, &keyblock) )) { remove_bad_stuff (keyblock); if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY ) - rc = import_one( fname, keyblock, fast, stats ); + rc = import_one( fname, keyblock, fast, stats, options ); else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) rc = import_secret_one( fname, keyblock, stats ); else if( keyblock->pkt->pkttype == PKT_SIGNATURE @@ -440,6 +445,70 @@ remove_bad_stuff (KBNODE keyblock) } } +/* Walk through the subkeys on a pk to find if we have the HKP + disease: multiple subkeys with their binding sigs stripped, and the + sig for the first subkey placed after the last subkey. That is, + instead of "pk uid sig sub1 bind1 sub2 bind2 sub3 bind3" we have + "pk uid sig sub1 sub2 sub3 bind1". We can't do anything about sub2 + and sub3, as they are already lost, but we can try and rescue sub1 + by reordering the keyblock so that it reads "pk uid sig sub1 bind1 + sub2 sub3". Returns TRUE if the keyblock was modified. */ + +static int +fix_hkp_corruption(KBNODE keyblock) +{ + int changed=0,keycount=0; + KBNODE node,last=NULL,sknode=NULL; + + /* First determine if we have the problem at all. Look for 2 or + more subkeys in a row, followed by a single binding sig. */ + for(node=keyblock;node;last=node,node=node->next) + { + if(node->pkt->pkttype==PKT_PUBLIC_SUBKEY) + { + keycount++; + if(!sknode) + sknode=node; + } + else if(node->pkt->pkttype==PKT_SIGNATURE && + node->pkt->pkt.signature->sig_class==0x18 && + keycount>=2 && node->next==NULL) + { + /* We might have the problem, as this key has two subkeys in + a row without any intervening packets. */ + + /* Sanity check */ + if(last==NULL) + break; + + /* Temporarily attach node to sknode. */ + node->next=sknode->next; + sknode->next=node; + last->next=NULL; + + if(check_key_signature(keyblock,node,NULL)) + { + /* Not a match, so undo the changes. */ + sknode->next=node->next; + last->next=node; + node->next=NULL; + break; + } + else + { + sknode->flag |= 1; /* Mark it good so we don't need to + check it again */ + changed=1; + break; + } + } + else + keycount=0; + } + + return changed; +} + /* Clean the subkeys on a pk so that they each have at most 1 binding sig and at most 1 revocation sig. This works based solely on the timestamps like the rest of gpg. If the standard does get @@ -538,7 +607,7 @@ clean_subkeys(KBNODE keyblock,u32 *keyid) */ static int import_one( const char *fname, KBNODE keyblock, int fast, - struct stats_s *stats ) + struct stats_s *stats, unsigned int options ) { PKT_public_key *pk; PKT_public_key *pk_orig; @@ -574,6 +643,11 @@ import_one( const char *fname, KBNODE keyblock, int fast, } clear_kbnode_flags( keyblock ); + + if((options&IMPORT_REPAIR_HKP_SUBKEY_BUG) && fix_hkp_corruption(keyblock)) + log_info(_("key %08lX: HKP subkey corruption repaired\n"), + (ulong)keyid[1]); + rc = chk_self_sigs( fname, keyblock , pk, keyid ); if( rc ) return rc== -1? 0:rc; @@ -591,7 +665,7 @@ import_one( const char *fname, KBNODE keyblock, int fast, m_free(user); } - if( !delete_inv_parts( fname, keyblock, keyid ) ) { + if( !delete_inv_parts( fname, keyblock, keyid, options ) ) { if( !opt.quiet ) { log_info( _("key %08lX: no valid user IDs\n"), (ulong)keyid[1]); @@ -1033,7 +1107,8 @@ chk_self_sigs( const char *fname, KBNODE keyblock, * returns: true if at least one valid user-id is left over. */ static int -delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid ) +delete_inv_parts( const char *fname, KBNODE keyblock, + u32 *keyid, unsigned int options) { KBNODE node; int nvalid=0, uid_seen=0, subkey_seen=0; @@ -1086,7 +1161,7 @@ delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid ) delete_kbnode( node ); /* build_packet() can't handle this */ else if( node->pkt->pkttype == PKT_SIGNATURE && !node->pkt->pkt.signature->flags.exportable && - !(opt.import_options&IMPORT_ALLOW_LOCAL_SIGS) && + !(options&IMPORT_ALLOW_LOCAL_SIGS) && seckey_available( node->pkt->pkt.signature->keyid ) ) { /* here we violate the rfc a bit by still allowing * to import non-exportable signature when we have the diff --git a/g10/keyserver.c b/g10/keyserver.c index c64ad4bc8..97745f0d6 100644 --- a/g10/keyserver.c +++ b/g10/keyserver.c @@ -598,7 +598,8 @@ keyserver_spawn(int action,STRLIST list, do this could be to continue parsing this line-by-line and make a temp iobuf for each key. */ - import_keys_stream(spawn->fromchild,0,stats_handle); + import_keys_stream(spawn->fromchild,0,stats_handle, + opt.keyserver_options.import_options); import_print_stats(stats_handle); import_release_stats_handle(stats_handle); diff --git a/g10/main.h b/g10/main.h index 05da9c80c..9edccaf21 100644 --- a/g10/main.h +++ b/g10/main.h @@ -150,13 +150,14 @@ KBNODE make_comment_node( const char *s ); KBNODE make_mpi_comment_node( const char *s, MPI a ); /*-- import.c --*/ -/* 1, 4, and 8 are reserved so they match the EXPORT_* flags below */ -#define IMPORT_ALLOW_LOCAL_SIGS 2 -#define IMPORT_DEFAULT 0 +#define IMPORT_ALLOW_LOCAL_SIGS 1 +#define IMPORT_REPAIR_HKP_SUBKEY_BUG 2 int parse_import_options(char *str,unsigned int *options); -void import_keys( char **fnames, int nnames, int fast, void *stats_hd ); -int import_keys_stream( IOBUF inp, int fast, void *stats_hd ); +void import_keys( char **fnames, int nnames, int fast, + void *stats_hd, unsigned int options ); +int import_keys_stream( IOBUF inp, int fast, + void *stats_hd, unsigned int options ); void *import_new_stats_handle (void); void import_release_stats_handle (void *p); void import_print_stats (void *hd); @@ -168,7 +169,6 @@ int collapse_uids( KBNODE *keyblock ); #define EXPORT_INCLUDE_LOCAL_SIGS 2 #define EXPORT_INCLUDE_ATTRIBUTES 4 #define EXPORT_INCLUDE_SENSITIVE_REVKEYS 8 -#define EXPORT_DEFAULT (1|4) int parse_export_options(char *str,unsigned int *options); int export_pubkeys( STRLIST users, unsigned int options );