diff --git a/g10/import.c b/g10/import.c index 441dcca9d..e40141e94 100644 --- a/g10/import.c +++ b/g10/import.c @@ -1,2506 +1,2533 @@ /* import.c - import a key into our key storage. * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include "options.h" #include "packet.h" #include "errors.h" #include "keydb.h" #include "memory.h" #include "util.h" #include "trustdb.h" #include "main.h" #include "i18n.h" #include "ttyio.h" #include "status.h" #include "keyserver-internal.h" struct stats_s { ulong count; ulong no_user_id; ulong imported; ulong imported_rsa; ulong n_uids; ulong n_sigs; ulong n_subk; ulong unchanged; ulong n_revoc; ulong secret_read; ulong secret_imported; ulong secret_dups; ulong skipped_new_keys; ulong not_imported; ulong n_sigs_cleaned; ulong n_uids_cleaned; }; static int import( IOBUF inp, const char* fname,struct stats_s *stats, - unsigned char **fpr,size_t *fpr_len,unsigned int options ); + unsigned char **fpr,size_t *fpr_len,unsigned int options, + import_filter filter, void *filter_arg ); static int read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root ); static void revocation_present(KBNODE keyblock); static int import_one(const char *fname, KBNODE keyblock,struct stats_s *stats, unsigned char **fpr,size_t *fpr_len, - unsigned int options,int from_sk); + unsigned int options,int from_sk, + import_filter filter, void *filter_arg); static int import_secret_one( const char *fname, KBNODE keyblock, - struct stats_s *stats, unsigned int options); + struct stats_s *stats, unsigned int options, + import_filter filter, void *filter_arg); 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, int *non_self ); 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 ); static int append_uid( KBNODE keyblock, KBNODE node, int *n_sigs, const char *fname, u32 *keyid ); static int append_key( KBNODE keyblock, KBNODE node, int *n_sigs, const char *fname, u32 *keyid ); static int merge_sigs( KBNODE dst, KBNODE src, int *n_sigs, const char *fname, u32 *keyid ); static int merge_keysigs( KBNODE dst, KBNODE src, int *n_sigs, const char *fname, u32 *keyid ); int parse_import_options(char *str,unsigned int *options,int noisy) { struct parse_options import_opts[]= { {"import-local-sigs",IMPORT_LOCAL_SIGS,NULL, N_("import signatures that are marked as local-only")}, {"repair-pks-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL, N_("repair damage from the pks keyserver during import")}, {"fast-import",IMPORT_FAST,NULL, N_("do not update the trustdb after import")}, {"convert-sk-to-pk",IMPORT_SK2PK,NULL, N_("create a public key when importing a secret key")}, {"merge-only",IMPORT_MERGE_ONLY,NULL, N_("only accept updates to existing keys")}, {"import-clean",IMPORT_CLEAN,NULL, N_("remove unusable parts from key after import")}, {"import-minimal",IMPORT_MINIMAL|IMPORT_CLEAN,NULL, N_("remove as much as possible from key after import")}, /* Aliases for backward compatibility */ {"allow-local-sigs",IMPORT_LOCAL_SIGS,NULL,NULL}, {"repair-hkp-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL,NULL}, /* dummy */ {"import-unusable-sigs",0,NULL,NULL}, {"import-clean-sigs",0,NULL,NULL}, {"import-clean-uids",0,NULL,NULL}, {NULL,0,NULL,NULL} }; return parse_options(str,options,import_opts,noisy); } void * import_new_stats_handle (void) { return xmalloc_clear ( sizeof (struct stats_s) ); } void import_release_stats_handle (void *p) { xfree (p); } /**************** * Import the public keys from the given filename. Input may be armored. * This function rejects all keys which are not validly self signed on at * least one userid. Only user ids which are self signed will be imported. * Other signatures are not checked. * * Actually this function does a merge. It works like this: * * - get the keyblock * - check self-signatures and remove all userids and their signatures * without/invalid self-signatures. * - reject the keyblock, if we have no valid userid. * - See whether we have this key already in one of our pubrings. * If not, simply add it to the default keyring. * - Compare the key and the self-signatures of the new and the one in * our keyring. If they are different something weird is going on; * ask what to do. * - See whether we have only non-self-signature on one user id; if not * ask the user what to do. * - compare the signatures: If we already have this signature, check * that they compare okay; if not, issue a warning and ask the user. * (consider looking at the timestamp and use the newest?) * - Simply add the signature. Can't verify here because we may not have * the signature's public key yet; verification is done when putting it * into the trustdb, which is done automagically as soon as this pubkey * is used. * - Proceed with next signature. * * Key revocation certificates have special handling. * */ static int import_keys_internal( IOBUF inp, char **fnames, int nnames, void *stats_handle, unsigned char **fpr, size_t *fpr_len, - unsigned int options ) + unsigned int options, + import_filter filter, void *filter_arg) { int i, rc = 0; struct stats_s *stats = stats_handle; if (!stats) stats = import_new_stats_handle (); if (inp) { - rc = import( inp, "[stream]", stats, fpr, fpr_len, options); + rc = import (inp, "[stream]", stats, fpr, fpr_len, options, + filter, filter_arg); } else { int once = (!fnames && !nnames); for(i=0; once || i < nnames; once=0, i++ ) { const char *fname = fnames? fnames[i] : NULL; IOBUF inp2 = iobuf_open(fname); if( !fname ) fname = "[stdin]"; if (inp2 && is_secured_file (iobuf_get_fd (inp2))) { iobuf_close (inp2); inp2 = NULL; errno = EPERM; } if( !inp2 ) log_error(_("can't open `%s': %s\n"), fname, strerror(errno) ); else { - rc = import( inp2, fname, stats, fpr, fpr_len, options ); + rc = import (inp2, fname, stats, fpr, fpr_len, options, + NULL, NULL); iobuf_close(inp2); /* Must invalidate that ugly cache to actually close it. */ iobuf_ioctl (NULL, 2, 0, (char*)fname); if( rc ) log_error("import from `%s' failed: %s\n", fname, g10_errstr(rc) ); } } } if (!stats_handle) { import_print_stats (stats); import_release_stats_handle (stats); } /* If no fast import and the trustdb is dirty (i.e. we added a key or userID that had something other than a selfsig, a signature that was other than a selfsig, or any revocation), then update/check the trustdb if the user specified by setting interactive or by not setting no-auto-check-trustdb */ if(!(options&IMPORT_FAST)) trustdb_check_or_update(); return rc; } void import_keys( char **fnames, int nnames, void *stats_handle, unsigned int options ) { - import_keys_internal(NULL,fnames,nnames,stats_handle,NULL,NULL,options); + import_keys_internal (NULL, fnames, nnames, stats_handle, NULL, NULL, + options, NULL, NULL); } int import_keys_stream( IOBUF inp, void *stats_handle, - unsigned char **fpr, size_t *fpr_len,unsigned int options ) + unsigned char **fpr, size_t *fpr_len,unsigned int options, + import_filter filter, void *filter_arg ) { - return import_keys_internal(inp,NULL,0,stats_handle,fpr,fpr_len,options); + return import_keys_internal (inp, NULL, 0, stats_handle, fpr, fpr_len, + options, filter, filter_arg); } static int import( IOBUF inp, const char* fname,struct stats_s *stats, - unsigned char **fpr,size_t *fpr_len,unsigned int options ) + unsigned char **fpr,size_t *fpr_len,unsigned int options, + import_filter filter, void *filter_arg) { PACKET *pending_pkt = NULL; KBNODE keyblock = NULL; int rc = 0; getkey_disable_caches(); if( !opt.no_armor ) { /* armored reading is not disabled */ armor_filter_context_t *afx = new_armor_context (); afx->only_keyblocks = 1; push_armor_filter (afx, inp); release_armor_context (afx); } while( !(rc = read_block( inp, &pending_pkt, &keyblock) )) { if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY ) - rc = import_one( fname, keyblock, stats, fpr, fpr_len, options, 0); - else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) - rc = import_secret_one( fname, keyblock, stats, options ); + rc = import_one (fname, keyblock, stats, fpr, fpr_len, options, 0, + filter, filter_arg); + else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) + rc = import_secret_one (fname, keyblock, stats, options, + filter, filter_arg); else if( keyblock->pkt->pkttype == PKT_SIGNATURE && keyblock->pkt->pkt.signature->sig_class == 0x20 ) rc = import_revoke_cert( fname, keyblock, stats ); else { log_info( _("skipping block of type %d\n"), keyblock->pkt->pkttype ); } release_kbnode(keyblock); /* fixme: we should increment the not imported counter but this does only make sense if we keep on going despite of errors. */ if( rc ) break; if( !(++stats->count % 100) && !opt.quiet ) log_info(_("%lu keys processed so far\n"), stats->count ); } if( rc == -1 ) rc = 0; else if( rc && rc != G10ERR_INV_KEYRING ) log_error( _("error reading `%s': %s\n"), fname, g10_errstr(rc)); return rc; } void import_print_stats (void *hd) { struct stats_s *stats = hd; if( !opt.quiet ) { log_info(_("Total number processed: %lu\n"), stats->count ); if( stats->skipped_new_keys ) log_info(_(" skipped new keys: %lu\n"), stats->skipped_new_keys ); if( stats->no_user_id ) log_info(_(" w/o user IDs: %lu\n"), stats->no_user_id ); if( stats->imported || stats->imported_rsa ) { log_info(_(" imported: %lu"), stats->imported ); if( stats->imported_rsa ) fprintf(stderr, " (RSA: %lu)", stats->imported_rsa ); putc('\n', stderr); } if( stats->unchanged ) log_info(_(" unchanged: %lu\n"), stats->unchanged ); if( stats->n_uids ) 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->n_sigs ) log_info(_(" new signatures: %lu\n"), stats->n_sigs ); if( stats->n_revoc ) log_info(_(" new key revocations: %lu\n"), stats->n_revoc ); if( stats->secret_read ) log_info(_(" secret keys read: %lu\n"), stats->secret_read ); if( stats->secret_imported ) log_info(_(" secret keys imported: %lu\n"), stats->secret_imported ); if( stats->secret_dups ) log_info(_(" secret keys unchanged: %lu\n"), stats->secret_dups ); if( stats->not_imported ) log_info(_(" not imported: %lu\n"), stats->not_imported ); if( stats->n_sigs_cleaned) log_info(_(" signatures cleaned: %lu\n"),stats->n_sigs_cleaned); if( stats->n_uids_cleaned) log_info(_(" user IDs cleaned: %lu\n"),stats->n_uids_cleaned); } if( is_status_enabled() ) { char buf[14*20]; sprintf(buf, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", stats->count, stats->no_user_id, stats->imported, stats->imported_rsa, stats->unchanged, stats->n_uids, stats->n_subk, stats->n_sigs, stats->n_revoc, stats->secret_read, stats->secret_imported, stats->secret_dups, stats->skipped_new_keys, stats->not_imported ); write_status_text( STATUS_IMPORT_RES, buf ); } } /* Return true if PKTTYPE is valid in a keyblock. */ static int valid_keyblock_packet (int pkttype) { switch (pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SECRET_KEY: case PKT_SECRET_SUBKEY: case PKT_SIGNATURE: case PKT_USER_ID: case PKT_ATTRIBUTE: case PKT_RING_TRUST: return 1; default: return 0; } } /**************** * Read the next keyblock from stream A. * PENDING_PKT should be initialzed to NULL * and not chnaged form the caller. * Retunr: 0 = okay, -1 no more blocks or another errorcode. */ static int read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root ) { int rc; PACKET *pkt; KBNODE root = NULL; int in_cert; if( *pending_pkt ) { root = new_kbnode( *pending_pkt ); *pending_pkt = NULL; in_cert = 1; } else in_cert = 0; pkt = xmalloc( sizeof *pkt ); init_packet(pkt); while( (rc=parse_packet(a, pkt)) != -1 ) { if( rc ) { /* ignore errors */ if( rc != G10ERR_UNKNOWN_PACKET ) { log_error("read_block: read error: %s\n", g10_errstr(rc) ); rc = G10ERR_INV_KEYRING; goto ready; } free_packet( pkt ); init_packet(pkt); continue; } if( !root && pkt->pkttype == PKT_SIGNATURE && pkt->pkt.signature->sig_class == 0x20 ) { /* 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 = G10ERR_COMPR_ALGO; goto ready; } else { compress_filter_context_t *cfx = xmalloc_clear( sizeof *cfx ); pkt->pkt.compressed->buf = NULL; push_compress_filter2(a,cfx,pkt->pkt.compressed->algorithm,1); } free_packet( pkt ); init_packet(pkt); break; case PKT_RING_TRUST: /* skip those packets */ free_packet( 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; 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: if( rc == -1 && root ) rc = 0; if( rc ) release_kbnode( root ); else *ret_root = root; free_packet( pkt ); xfree( pkt ); return rc; } /* Walk through the subkeys on a pk to find if we have the PKS 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_pks_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; /* Note we aren't checking whether this binding sig is a selfsig. This is not necessary here as the subkey and binding sig will be rejected later if that is the case. */ 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; } /* Versions of GnuPG before 1.4.11 and 2.0.16 allowed to import bogus direct key signatures. A side effect of this was that a later import of the same good direct key signatures was not possible because the cmp_signature check in merge_blocks considered them equal. Although direct key signatures are now checked during import, there might still be bogus signatures sitting in a keyring. We need to detect and delete them before doing a merge. This fucntion returns the number of removed sigs. */ static int fix_bad_direct_key_sigs (KBNODE keyblock, u32 *keyid) { int rc; int count = 0; KBNODE node; for (node = keyblock->next; node; node=node->next) { if (node->pkt->pkttype == PKT_USER_ID) break; if (node->pkt->pkttype == PKT_SIGNATURE && IS_KEY_SIG (node->pkt->pkt.signature)) { rc = check_key_signature (keyblock, node, NULL); if (rc && rc != G10ERR_PUBKEY_ALGO ) { /* If we don't know the error, we can't decide; this is not a problem because cmp_signature can't compare the signature either. */ log_info ("key %s: invalid direct key signature removed\n", keystr (keyid)); delete_kbnode (node); count++; } } } return count; } static void print_import_ok (PKT_public_key *pk, PKT_secret_key *sk, unsigned int reason) { byte array[MAX_FINGERPRINT_LEN], *s; char buf[MAX_FINGERPRINT_LEN*2+30], *p; size_t i, n; sprintf (buf, "%u ", reason); p = buf + strlen (buf); if (pk) fingerprint_from_pk (pk, array, &n); else fingerprint_from_sk (sk, array, &n); s = array; for (i=0; i < n ; i++, s++, p += 2) sprintf (p, "%02X", *s); write_status_text (STATUS_IMPORT_OK, buf); } static void print_import_check (PKT_public_key * pk, PKT_user_id * id) { char * buf; byte fpr[24]; u32 keyid[2]; size_t i, pos = 0, n; buf = xmalloc (17+41+id->len+32); keyid_from_pk (pk, keyid); sprintf (buf, "%08X%08X ", keyid[0], keyid[1]); pos = 17; fingerprint_from_pk (pk, fpr, &n); for (i = 0; i < n; i++, pos += 2) sprintf (buf+pos, "%02X", fpr[i]); strcat (buf, " "); pos += 1; strcat (buf, id->name); write_status_text (STATUS_IMPORT_CHECK, buf); xfree (buf); } static void check_prefs_warning(PKT_public_key *pk) { log_info(_("WARNING: key %s contains preferences for unavailable\n"), keystr_from_pk(pk)); /* TRANSLATORS: This string is belongs to the previous one. They are only split up to allow printing of a common prefix. */ log_info(_(" algorithms on these user IDs:\n")); } static void check_prefs(KBNODE keyblock) { KBNODE node; PKT_public_key *pk; int problem=0; merge_keys_and_selfsig(keyblock); pk=keyblock->pkt->pkt.public_key; for(node=keyblock;node;node=node->next) { if(node->pkt->pkttype==PKT_USER_ID && node->pkt->pkt.user_id->created && node->pkt->pkt.user_id->prefs) { PKT_user_id *uid=node->pkt->pkt.user_id; prefitem_t *prefs=uid->prefs; char *user=utf8_to_native(uid->name,strlen(uid->name),0); for(;prefs->type;prefs++) { char num[10]; /* prefs->value is a byte, so we're over safe here */ sprintf(num,"%u",prefs->value); if(prefs->type==PREFTYPE_SYM) { if(check_cipher_algo(prefs->value)) { const char *algo=cipher_algo_to_string(prefs->value); if(!problem) check_prefs_warning(pk); log_info(_(" \"%s\": preference for cipher" " algorithm %s\n"),user,algo?algo:num); problem=1; } } else if(prefs->type==PREFTYPE_HASH) { if(check_digest_algo(prefs->value)) { const char *algo=digest_algo_to_string(prefs->value); if(!problem) check_prefs_warning(pk); log_info(_(" \"%s\": preference for digest" " algorithm %s\n"),user,algo?algo:num); problem=1; } } else if(prefs->type==PREFTYPE_ZIP) { if(check_compress_algo(prefs->value)) { const char *algo=compress_algo_to_string(prefs->value); if(!problem) check_prefs_warning(pk); log_info(_(" \"%s\": preference for compression" " algorithm %s\n"),user,algo?algo:num); problem=1; } } } xfree(user); } } if(problem) { log_info(_("it is strongly suggested that you update" " your preferences and\n")); log_info(_("re-distribute this key to avoid potential algorithm" " mismatch problems\n")); if(!opt.batch) { STRLIST sl=NULL,locusr=NULL; size_t fprlen=0; byte fpr[MAX_FINGERPRINT_LEN],*p; char username[(MAX_FINGERPRINT_LEN*2)+1]; unsigned int i; p=fingerprint_from_pk(pk,fpr,&fprlen); for(i=0;ipkt->pkt.public_key; keyid_from_pk( pk, keyid ); uidnode = find_next_kbnode( keyblock, PKT_USER_ID ); if( opt.verbose && !opt.interactive ) { log_info( "pub %4u%c/%s %s ", nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ), keystr_from_pk(pk), datestr_from_pk(pk) ); if( uidnode ) print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len ); putc('\n', stderr); } if( !uidnode ) { log_error( _("key %s: no user ID\n"), keystr_from_pk(pk)); return 0; } + if (filter && filter (pk, NULL, filter_arg)) + { + log_error (_("key %s: %s\n"), keystr_from_pk(pk), + _("rejected by import filter")); + return 0; + } + if (opt.interactive) { if(is_status_enabled()) print_import_check (pk, uidnode->pkt->pkt.user_id); merge_keys_and_selfsig (keyblock); tty_printf ("\n"); show_basic_key_info (keyblock); tty_printf ("\n"); if (!cpr_get_answer_is_yes ("import.okay", "Do you want to import this key? (y/N) ")) return 0; } collapse_uids(&keyblock); /* Clean the key that we're about to import, to cut down on things 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(keyblock,opt.verbose,options&IMPORT_MINIMAL,NULL,NULL); clear_kbnode_flags( keyblock ); if((options&IMPORT_REPAIR_PKS_SUBKEY_BUG) && fix_pks_corruption(keyblock) && opt.verbose) log_info(_("key %s: PKS subkey corruption repaired\n"), keystr_from_pk(pk)); rc = chk_self_sigs( fname, keyblock , pk, keyid, &non_self ); if( rc ) return rc== -1? 0:rc; /* If we allow such a thing, mark unsigned uids as valid */ if( opt.allow_non_selfsigned_uid ) for( node=keyblock; node; node = node->next ) if( node->pkt->pkttype == PKT_USER_ID && !(node->flag & 1) ) { char *user=utf8_to_native(node->pkt->pkt.user_id->name, node->pkt->pkt.user_id->len,0); node->flag |= 1; log_info( _("key %s: accepted non self-signed user ID \"%s\"\n"), keystr_from_pk(pk),user); xfree(user); } if( !delete_inv_parts( fname, keyblock, keyid, options ) ) { log_error( _("key %s: no valid user IDs\n"), keystr_from_pk(pk)); if( !opt.quiet ) log_info(_("this may be caused by a missing self-signature\n")); stats->no_user_id++; return 0; } /* 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 ); if( rc && rc != G10ERR_NO_PUBKEY && rc != G10ERR_UNU_PUBKEY ) { log_error( _("key %s: public key not found: %s\n"), keystr(keyid), g10_errstr(rc)); } else if ( rc && (opt.import_options&IMPORT_MERGE_ONLY) ) { if( opt.verbose ) log_info( _("key %s: new key - skipped\n"), keystr(keyid)); rc = 0; stats->skipped_new_keys++; } else if( rc ) { /* insert this key */ KEYDB_HANDLE hd = keydb_new (0); rc = keydb_locate_writable (hd, NULL); if (rc) { log_error (_("no writable keyring found: %s\n"), g10_errstr (rc)); keydb_release (hd); return G10ERR_GENERAL; } if( opt.verbose > 1 ) log_info (_("writing to `%s'\n"), keydb_get_resource_name (hd) ); rc = keydb_insert_keyblock (hd, keyblock ); if (rc) log_error (_("error writing keyring `%s': %s\n"), keydb_get_resource_name (hd), g10_errstr(rc)); else { /* This should not be possible since we delete the ownertrust when a key is deleted, but it can happen if the keyring and trustdb are out of sync. It can also be made to happen with the trusted-key command. */ clear_ownertrusts (pk); if(non_self) revalidation_mark (); } keydb_release (hd); /* we are ready */ if( !opt.quiet ) { char *p=get_user_id_native (keyid); log_info( _("key %s: public key \"%s\" imported\n"), keystr(keyid),p); xfree(p); } if( is_status_enabled() ) { char *us = get_long_user_id_string( keyid ); write_status_text( STATUS_IMPORTED, us ); xfree(us); print_import_ok (pk,NULL, 1); } stats->imported++; if( is_RSA( pk->pubkey_algo ) ) stats->imported_rsa++; new_key = 1; } else { /* merge */ KEYDB_HANDLE hd; int n_uids, n_sigs, n_subk, n_sigs_cleaned, n_uids_cleaned; /* Compare the original against the new key; just to be sure nothing * weird is going on */ if( cmp_public_keys( pk_orig, pk ) ) { log_error( _("key %s: doesn't match our copy\n"),keystr(keyid)); goto leave; } /* now read the original keyblock */ hd = keydb_new (0); { byte afp[MAX_FINGERPRINT_LEN]; size_t an; fingerprint_from_pk (pk_orig, afp, &an); while (an < MAX_FINGERPRINT_LEN) afp[an++] = 0; rc = keydb_search_fpr (hd, afp); } if( rc ) { log_error (_("key %s: can't locate original keyblock: %s\n"), keystr(keyid), g10_errstr(rc)); keydb_release (hd); goto leave; } rc = keydb_get_keyblock (hd, &keyblock_orig ); if (rc) { log_error (_("key %s: can't read original keyblock: %s\n"), keystr(keyid), g10_errstr(rc)); keydb_release (hd); goto leave; } /* Make sure the original direct key sigs are all sane. */ n_sigs_cleaned = fix_bad_direct_key_sigs (keyblock_orig, keyid); if (n_sigs_cleaned) commit_kbnode (&keyblock_orig); /* and try to merge the block */ clear_kbnode_flags( keyblock_orig ); clear_kbnode_flags( keyblock ); n_uids = n_sigs = n_subk = n_uids_cleaned = 0; rc = merge_blocks( fname, keyblock_orig, keyblock, keyid, &n_uids, &n_sigs, &n_subk ); if( rc ) { keydb_release (hd); goto leave; } if(options&IMPORT_CLEAN) clean_key(keyblock_orig,opt.verbose,options&IMPORT_MINIMAL, &n_uids_cleaned,&n_sigs_cleaned); if( n_uids || n_sigs || n_subk || n_sigs_cleaned || n_uids_cleaned) { mod_key = 1; /* keyblock_orig has been updated; write */ rc = keydb_update_keyblock (hd, keyblock_orig); if (rc) log_error (_("error writing keyring `%s': %s\n"), keydb_get_resource_name (hd), g10_errstr(rc) ); else if(non_self) revalidation_mark (); /* we are ready */ if( !opt.quiet ) { char *p=get_user_id_native(keyid); if( n_uids == 1 ) log_info( _("key %s: \"%s\" 1 new user ID\n"), keystr(keyid),p); else if( n_uids ) log_info( _("key %s: \"%s\" %d new user IDs\n"), keystr(keyid),p,n_uids); if( n_sigs == 1 ) log_info( _("key %s: \"%s\" 1 new signature\n"), keystr(keyid), p); else if( n_sigs ) log_info( _("key %s: \"%s\" %d new signatures\n"), keystr(keyid), p, n_sigs ); if( n_subk == 1 ) log_info( _("key %s: \"%s\" 1 new subkey\n"), keystr(keyid), p); else if( n_subk ) log_info( _("key %s: \"%s\" %d new subkeys\n"), keystr(keyid), p, n_subk ); if(n_sigs_cleaned==1) log_info(_("key %s: \"%s\" %d signature cleaned\n"), keystr(keyid),p,n_sigs_cleaned); else if(n_sigs_cleaned) log_info(_("key %s: \"%s\" %d signatures cleaned\n"), keystr(keyid),p,n_sigs_cleaned); if(n_uids_cleaned==1) log_info(_("key %s: \"%s\" %d user ID cleaned\n"), keystr(keyid),p,n_uids_cleaned); else if(n_uids_cleaned) log_info(_("key %s: \"%s\" %d user IDs cleaned\n"), keystr(keyid),p,n_uids_cleaned); xfree(p); } stats->n_uids +=n_uids; stats->n_sigs +=n_sigs; stats->n_subk +=n_subk; stats->n_sigs_cleaned +=n_sigs_cleaned; stats->n_uids_cleaned +=n_uids_cleaned; if (is_status_enabled ()) print_import_ok (pk, NULL, ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0))); } else { if (is_status_enabled ()) print_import_ok (pk, NULL, 0); if( !opt.quiet ) { char *p=get_user_id_native(keyid); log_info( _("key %s: \"%s\" not changed\n"),keystr(keyid),p); xfree(p); } stats->unchanged++; } keydb_release (hd); hd = NULL; } leave: /* Now that the key is definitely incorporated into the keydb, we need to check if a designated revocation is present or if the prefs are not rational so we can warn the user. */ if(mod_key) { revocation_present(keyblock_orig); if(!from_sk && seckey_available(keyid)==0) check_prefs(keyblock_orig); } else if(new_key) { /* A little explanation for this: we fill in the fingerprint when importing keys as it can be useful to know the fingerprint in certain keyserver-related cases (a keyserver asked for a particular name, but the key doesn't have that name). However, in cases where we're importing more than one key at a time, we cannot know which key to fingerprint. In these cases, rather than guessing, we do not fingerpring at all, and we must hope the user ID on the keys are useful. */ if(fpr) { xfree(*fpr); if(stats->imported==1) *fpr=fingerprint_from_pk(pk,NULL,fpr_len); else *fpr=NULL; } revocation_present(keyblock); if(!from_sk && seckey_available(keyid)==0) check_prefs(keyblock); } release_kbnode( keyblock_orig ); free_public_key( pk_orig ); return rc; } /* Walk a secret keyblock and produce a public keyblock out of it. */ static KBNODE sec_to_pub_keyblock(KBNODE sec_keyblock) { KBNODE secnode,pub_keyblock=NULL,ctx=NULL; while((secnode=walk_kbnode(sec_keyblock,&ctx,0))) { KBNODE pubnode; if(secnode->pkt->pkttype==PKT_SECRET_KEY || secnode->pkt->pkttype==PKT_SECRET_SUBKEY) { /* Make a public key. We only need to convert enough to write the keyblock out. */ PKT_secret_key *sk=secnode->pkt->pkt.secret_key; PACKET *pkt=xmalloc_clear(sizeof(PACKET)); PKT_public_key *pk=xmalloc_clear(sizeof(PKT_public_key)); int n; if(secnode->pkt->pkttype==PKT_SECRET_KEY) pkt->pkttype=PKT_PUBLIC_KEY; else pkt->pkttype=PKT_PUBLIC_SUBKEY; pkt->pkt.public_key=pk; pk->version=sk->version; pk->timestamp=sk->timestamp; pk->expiredate=sk->expiredate; pk->pubkey_algo=sk->pubkey_algo; n=pubkey_get_npkey(pk->pubkey_algo); if(n==0) { /* we can't properly extract the pubkey without knowing the number of MPIs */ release_kbnode(pub_keyblock); return NULL; } else { int i; for(i=0;ipkey[i]=mpi_copy(sk->skey[i]); } pubnode=new_kbnode(pkt); } else { pubnode=clone_kbnode(secnode); } if(pub_keyblock==NULL) pub_keyblock=pubnode; else add_kbnode(pub_keyblock,pubnode); } return pub_keyblock; } /**************** * Ditto for secret keys. Handling is simpler than for public keys. * We allow secret key importing only when allow is true, this is so * that a secret key can not be imported accidently and thereby tampering * with the trust calculation. */ static int import_secret_one( const char *fname, KBNODE keyblock, - struct stats_s *stats, unsigned int options) + struct stats_s *stats, unsigned int options, + import_filter filter, void *filter_arg) { PKT_secret_key *sk; KBNODE node, uidnode; u32 keyid[2]; int rc = 0; /* get the key and print some info about it */ node = find_kbnode( keyblock, PKT_SECRET_KEY ); if( !node ) BUG(); sk = node->pkt->pkt.secret_key; keyid_from_sk( sk, keyid ); uidnode = find_next_kbnode( keyblock, PKT_USER_ID ); + if (filter && filter (NULL, sk, filter_arg)) { + log_error (_("secret key %s: %s\n"), keystr_from_sk(sk), + _("rejected by import filter")); + return 0; + } + if( opt.verbose ) { log_info( "sec %4u%c/%s %s ", nbits_from_sk( sk ), pubkey_letter( sk->pubkey_algo ), keystr_from_sk(sk), datestr_from_sk(sk) ); if( uidnode ) print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len ); putc('\n', stderr); } stats->secret_read++; if ((options & IMPORT_NO_SECKEY)) { log_error (_("importing secret keys not allowed\n")); return 0; } if( !uidnode ) { log_error( _("key %s: no user ID\n"), keystr_from_sk(sk)); return 0; } if(sk->protect.algo>110) { log_error(_("key %s: secret key with invalid cipher %d" " - skipped\n"),keystr_from_sk(sk),sk->protect.algo); return 0; } #ifdef ENABLE_SELINUX_HACKS if (1) { /* We don't allow to import secret keys because that may be used to put a secret key into the keyring and the user might later be tricked into signing stuff with that key. */ log_error (_("importing secret keys not allowed\n")); return 0; } #endif clear_kbnode_flags( keyblock ); /* do we have this key already in one of our secrings ? */ rc = seckey_available( keyid ); if( rc == G10ERR_NO_SECKEY && !(opt.import_options&IMPORT_MERGE_ONLY) ) { /* simply insert this key */ KEYDB_HANDLE hd = keydb_new (1); /* get default resource */ rc = keydb_locate_writable (hd, NULL); if (rc) { log_error (_("no default secret keyring: %s\n"), g10_errstr (rc)); keydb_release (hd); return G10ERR_GENERAL; } rc = keydb_insert_keyblock (hd, keyblock ); if (rc) log_error (_("error writing keyring `%s': %s\n"), keydb_get_resource_name (hd), g10_errstr(rc) ); keydb_release (hd); /* we are ready */ if( !opt.quiet ) log_info( _("key %s: secret key imported\n"), keystr_from_sk(sk)); stats->secret_imported++; if (is_status_enabled ()) print_import_ok (NULL, sk, 1|16); if(options&IMPORT_SK2PK) { /* Try and make a public key out of this. */ KBNODE pub_keyblock=sec_to_pub_keyblock(keyblock); if(pub_keyblock) { - import_one(fname,pub_keyblock,stats, - NULL,NULL,opt.import_options,1); + import_one (fname, pub_keyblock, stats, + NULL, NULL, opt.import_options, 1, + NULL, NULL); release_kbnode(pub_keyblock); } } /* Now that the key is definitely incorporated into the keydb, if we have the public part of this key, we need to check if the prefs are rational. */ node=get_pubkeyblock(keyid); if(node) { check_prefs(node); release_kbnode(node); } } else if( !rc ) { /* we can't merge secret keys */ log_error( _("key %s: already in secret keyring\n"), keystr_from_sk(sk)); stats->secret_dups++; if (is_status_enabled ()) print_import_ok (NULL, sk, 16); /* TODO: if we ever do merge secret keys, make sure to handle the sec_to_pub_keyblock feature as well. */ } else log_error( _("key %s: secret key not found: %s\n"), keystr_from_sk(sk), g10_errstr(rc)); return rc; } /**************** * Import a revocation certificate; this is a single signature packet. */ static int import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats ) { PKT_public_key *pk=NULL; KBNODE onode, keyblock = NULL; KEYDB_HANDLE hd = NULL; u32 keyid[2]; int rc = 0; assert( !node->next ); assert( node->pkt->pkttype == PKT_SIGNATURE ); assert( node->pkt->pkt.signature->sig_class == 0x20 ); keyid[0] = node->pkt->pkt.signature->keyid[0]; keyid[1] = node->pkt->pkt.signature->keyid[1]; pk = xmalloc_clear( sizeof *pk ); rc = get_pubkey( pk, keyid ); if( rc == G10ERR_NO_PUBKEY ) { 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), g10_errstr(rc)); goto leave; } /* read the original keyblock */ hd = keydb_new (0); { byte afp[MAX_FINGERPRINT_LEN]; size_t an; fingerprint_from_pk (pk, afp, &an); while (an < MAX_FINGERPRINT_LEN) afp[an++] = 0; rc = keydb_search_fpr (hd, afp); } if (rc) { log_error (_("key %s: can't locate original keyblock: %s\n"), keystr(keyid), g10_errstr(rc)); goto leave; } rc = keydb_get_keyblock (hd, &keyblock ); if (rc) { log_error (_("key %s: can't read original keyblock: %s\n"), keystr(keyid), g10_errstr(rc)); goto leave; } /* it is okay, that node is not in keyblock because * check_key_signature works fine for sig_class 0x20 in this * special case. */ rc = check_key_signature( keyblock, node, NULL); if( rc ) { log_error( _("key %s: invalid revocation certificate" ": %s - rejected\n"), keystr(keyid), g10_errstr(rc)); goto leave; } /* check whether we already have this */ for(onode=keyblock->next; onode; onode=onode->next ) { if( onode->pkt->pkttype == PKT_USER_ID ) break; else if( onode->pkt->pkttype == PKT_SIGNATURE && !cmp_signatures(node->pkt->pkt.signature, onode->pkt->pkt.signature)) { rc = 0; goto leave; /* yes, we already know about it */ } } /* insert it */ insert_kbnode( keyblock, clone_kbnode(node), 0 ); /* and write the keyblock back */ rc = keydb_update_keyblock (hd, keyblock ); if (rc) log_error (_("error writing keyring `%s': %s\n"), keydb_get_resource_name (hd), g10_errstr(rc) ); keydb_release (hd); hd = NULL; /* we are ready */ if( !opt.quiet ) { char *p=get_user_id_native (keyid); log_info( _("key %s: \"%s\" revocation certificate imported\n"), keystr(keyid),p); xfree(p); } stats->n_revoc++; /* 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(pk)==TRUST_ULTIMATE) clear_ownertrusts(pk); revalidation_mark (); leave: keydb_release (hd); release_kbnode( keyblock ); free_public_key( pk ); return rc; } /**************** * loop over the keyblock and check all self signatures. * Mark all user-ids with a self-signature by setting flag bit 0. * Mark all user-ids with an invalid self-signature by setting bit 1. * This works also for subkeys, here the subkey is marked. Invalid or * extra subkey sigs (binding or revocation) are marked for deletion. * non_self is set to true if there are any sigs other than self-sigs * in this keyblock. */ static int chk_self_sigs( const char *fname, KBNODE keyblock, PKT_public_key *pk, u32 *keyid, int *non_self ) { KBNODE n,knode=NULL; PKT_signature *sig; int rc; u32 bsdate=0,rsdate=0; KBNODE bsnode=NULL,rsnode=NULL; for( n=keyblock; (n = find_next_kbnode(n, 0)); ) { if(n->pkt->pkttype==PKT_PUBLIC_SUBKEY) { knode=n; bsdate=0; rsdate=0; bsnode=NULL; rsnode=NULL; continue; } else if( n->pkt->pkttype != PKT_SIGNATURE ) continue; sig = n->pkt->pkt.signature; if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) { /* This just caches the sigs for later use. That way we import a fully-cached key which speeds things up. */ if(!opt.no_sig_cache) check_key_signature(keyblock,n,NULL); if( IS_UID_SIG(sig) || IS_UID_REV(sig) ) { KBNODE unode = find_prev_kbnode( keyblock, n, PKT_USER_ID ); if( !unode ) { log_error( _("key %s: no user ID for signature\n"), keystr(keyid)); return -1; /* the complete keyblock is invalid */ } /* If it hasn't been marked valid yet, keep trying */ if(!(unode->flag&1)) { rc = check_key_signature( keyblock, n, NULL); if( rc ) { if( opt.verbose ) { char *p=utf8_to_native(unode->pkt->pkt.user_id->name, strlen(unode->pkt->pkt.user_id->name),0); log_info( rc == G10ERR_PUBKEY_ALGO ? _("key %s: unsupported public key " "algorithm on user ID \"%s\"\n"): _("key %s: invalid self-signature " "on user ID \"%s\"\n"), keystr(keyid),p); xfree(p); } } else unode->flag |= 1; /* mark that signature checked */ } } else if (IS_KEY_SIG (sig)) { rc = check_key_signature (keyblock, n, NULL); if ( rc ) { if (opt.verbose) log_info (rc == G10ERR_PUBKEY_ALGO ? _("key %s: unsupported public key algorithm\n"): _("key %s: invalid direct key signature\n"), keystr (keyid)); n->flag |= 4; } } else if( sig->sig_class == 0x18 ) { /* Note that this works based solely on the timestamps like the rest of gpg. If the standard gets revocation targets, this may need to be revised. */ if( !knode ) { if(opt.verbose) log_info( _("key %s: no subkey for key binding\n"), keystr(keyid)); n->flag |= 4; /* delete this */ } else { rc = check_key_signature( keyblock, n, NULL); if( rc ) { if(opt.verbose) log_info(rc == G10ERR_PUBKEY_ALGO ? _("key %s: unsupported public key" " algorithm\n"): _("key %s: invalid subkey binding\n"), keystr(keyid)); n->flag|=4; } else { /* It's valid, so is it newer? */ if(sig->timestamp>=bsdate) { knode->flag |= 1; /* the subkey is valid */ if(bsnode) { bsnode->flag|=4; /* Delete the last binding sig since this one is newer */ if(opt.verbose) log_info(_("key %s: removed multiple subkey" " binding\n"),keystr(keyid)); } bsnode=n; bsdate=sig->timestamp; } else n->flag|=4; /* older */ } } } else if( sig->sig_class == 0x28 ) { /* We don't actually mark the subkey as revoked right now, so just check that the revocation sig is the most recent valid one. Note that we don't care if the binding sig is newer than the revocation sig. See the comment in getkey.c:merge_selfsigs_subkey for more */ if( !knode ) { if(opt.verbose) log_info( _("key %s: no subkey for key revocation\n"), keystr(keyid)); n->flag |= 4; /* delete this */ } else { rc = check_key_signature( keyblock, n, NULL); if( rc ) { if(opt.verbose) log_info(rc == G10ERR_PUBKEY_ALGO ? _("key %s: unsupported public" " key algorithm\n"): _("key %s: invalid subkey revocation\n"), keystr(keyid)); n->flag|=4; } else { /* It's valid, so is it newer? */ if(sig->timestamp>=rsdate) { if(rsnode) { rsnode->flag|=4; /* Delete the last revocation sig since this one is newer */ if(opt.verbose) log_info(_("key %s: removed multiple subkey" " revocation\n"),keystr(keyid)); } rsnode=n; rsdate=sig->timestamp; } else n->flag|=4; /* older */ } } } } else *non_self=1; } return 0; } /**************** * delete all parts which are invalid and those signatures whose * public key algorithm is not available in this implemenation; * but consider RSA as valid, because parse/build_packets knows * about it. * returns: true if at least one valid user-id is left over. */ static int delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid, unsigned int options) { KBNODE node; int nvalid=0, uid_seen=0, subkey_seen=0; for(node=keyblock->next; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID ) { uid_seen = 1; if( (node->flag & 2) || !(node->flag & 1) ) { if( opt.verbose ) { char *p=utf8_to_native(node->pkt->pkt.user_id->name, node->pkt->pkt.user_id->len,0); log_info( _("key %s: skipped user ID \"%s\"\n"), keystr(keyid),p); xfree(p); } delete_kbnode( node ); /* the user-id */ /* and all following packets up to the next user-id */ while( node->next && node->next->pkt->pkttype != PKT_USER_ID && node->next->pkt->pkttype != PKT_PUBLIC_SUBKEY && node->next->pkt->pkttype != PKT_SECRET_SUBKEY ){ delete_kbnode( node->next ); node = node->next; } } else nvalid++; } else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY ) { if( (node->flag & 2) || !(node->flag & 1) ) { if( opt.verbose ) log_info( _("key %s: skipped subkey\n"),keystr(keyid)); delete_kbnode( node ); /* the subkey */ /* and all following signature packets */ while( node->next && node->next->pkt->pkttype == PKT_SIGNATURE ) { delete_kbnode( node->next ); node = node->next; } } else subkey_seen = 1; } else if( node->pkt->pkttype == PKT_SIGNATURE && check_pubkey_algo( node->pkt->pkt.signature->pubkey_algo) && node->pkt->pkt.signature->pubkey_algo != PUBKEY_ALGO_RSA ) delete_kbnode( node ); /* build_packet() can't handle this */ else if( node->pkt->pkttype == PKT_SIGNATURE && !node->pkt->pkt.signature->flags.exportable && !(options&IMPORT_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 * the secret key used to create this signature - it * seems that this makes sense */ if(opt.verbose) log_info( _("key %s: non exportable signature" " (class 0x%02X) - skipped\n"), keystr(keyid), node->pkt->pkt.signature->sig_class ); delete_kbnode( node ); } else if( node->pkt->pkttype == PKT_SIGNATURE && node->pkt->pkt.signature->sig_class == 0x20 ) { if( uid_seen ) { if(opt.verbose) log_info( _("key %s: revocation certificate" " at wrong place - skipped\n"),keystr(keyid)); delete_kbnode( node ); } else { /* If the revocation cert is from a different key than the one we're working on don't check it - it's probably from a revocation key and won't be verifiable with this key anyway. */ if(node->pkt->pkt.signature->keyid[0]==keyid[0] && node->pkt->pkt.signature->keyid[1]==keyid[1]) { int rc = check_key_signature( keyblock, node, NULL); if( rc ) { if(opt.verbose) log_info( _("key %s: invalid revocation" " certificate: %s - skipped\n"), keystr(keyid), g10_errstr(rc)); delete_kbnode( node ); } } } } else if( node->pkt->pkttype == PKT_SIGNATURE && (node->pkt->pkt.signature->sig_class == 0x18 || node->pkt->pkt.signature->sig_class == 0x28) && !subkey_seen ) { if(opt.verbose) log_info( _("key %s: subkey signature" " in wrong place - skipped\n"), keystr(keyid)); delete_kbnode( node ); } else if( node->pkt->pkttype == PKT_SIGNATURE && !IS_CERT(node->pkt->pkt.signature)) { if(opt.verbose) log_info(_("key %s: unexpected signature class (0x%02X) -" " skipped\n"),keystr(keyid), node->pkt->pkt.signature->sig_class); delete_kbnode(node); } else if( (node->flag & 4) ) /* marked for deletion */ delete_kbnode( node ); } /* note: because keyblock is the public key, it is never marked * for deletion and so keyblock cannot change */ commit_kbnode( &keyblock ); return nvalid; } /**************** * It may happen that the imported keyblock has duplicated user IDs. * We check this here and collapse those user IDs together with their * sigs into one. * Returns: True if the keyblock has changed. */ int collapse_uids( KBNODE *keyblock ) { KBNODE uid1; int any=0; for(uid1=*keyblock;uid1;uid1=uid1->next) { KBNODE uid2; if(is_deleted_kbnode(uid1)) continue; if(uid1->pkt->pkttype!=PKT_USER_ID) continue; for(uid2=uid1->next;uid2;uid2=uid2->next) { if(is_deleted_kbnode(uid2)) continue; if(uid2->pkt->pkttype!=PKT_USER_ID) continue; if(cmp_user_ids(uid1->pkt->pkt.user_id, uid2->pkt->pkt.user_id)==0) { /* We have a duplicated uid */ KBNODE sig1,last; any=1; /* Now take uid2's signatures, and attach them to uid1 */ for(last=uid2;last->next;last=last->next) { if(is_deleted_kbnode(last)) continue; if(last->next->pkt->pkttype==PKT_USER_ID || last->next->pkt->pkttype==PKT_PUBLIC_SUBKEY || last->next->pkt->pkttype==PKT_SECRET_SUBKEY) break; } /* Snip out uid2 */ (find_prev_kbnode(*keyblock,uid2,0))->next=last->next; /* Now put uid2 in place as part of uid1 */ last->next=uid1->next; uid1->next=uid2; delete_kbnode(uid2); /* Now dedupe uid1 */ for(sig1=uid1->next;sig1;sig1=sig1->next) { KBNODE sig2; if(is_deleted_kbnode(sig1)) continue; if(sig1->pkt->pkttype==PKT_USER_ID || sig1->pkt->pkttype==PKT_PUBLIC_SUBKEY || sig1->pkt->pkttype==PKT_SECRET_SUBKEY) break; if(sig1->pkt->pkttype!=PKT_SIGNATURE) continue; for(sig2=sig1->next,last=sig1;sig2;last=sig2,sig2=sig2->next) { if(is_deleted_kbnode(sig2)) continue; if(sig2->pkt->pkttype==PKT_USER_ID || sig2->pkt->pkttype==PKT_PUBLIC_SUBKEY || sig2->pkt->pkttype==PKT_SECRET_SUBKEY) break; if(sig2->pkt->pkttype!=PKT_SIGNATURE) continue; if(cmp_signatures(sig1->pkt->pkt.signature, sig2->pkt->pkt.signature)==0) { /* We have a match, so delete the second signature */ delete_kbnode(sig2); sig2=last; } } } } } } commit_kbnode(keyblock); if(any && !opt.quiet) { const char *key="???"; if( (uid1=find_kbnode( *keyblock, PKT_PUBLIC_KEY )) ) key=keystr_from_pk(uid1->pkt->pkt.public_key); else if( (uid1 = find_kbnode( *keyblock, PKT_SECRET_KEY )) ) key=keystr_from_sk(uid1->pkt->pkt.secret_key); log_info(_("key %s: duplicated user ID detected - merged\n"),key); } return any; } /* Check for a 0x20 revocation from a revocation key that is not present. This may be called without the benefit of merge_xxxx so you can't rely on pk->revkey and friends. */ static void revocation_present(KBNODE keyblock) { KBNODE onode,inode; PKT_public_key *pk=keyblock->pkt->pkt.public_key; for(onode=keyblock->next;onode;onode=onode->next) { /* If we reach user IDs, we're done. */ if(onode->pkt->pkttype==PKT_USER_ID) break; if(onode->pkt->pkttype==PKT_SIGNATURE && onode->pkt->pkt.signature->sig_class==0x1F && onode->pkt->pkt.signature->revkey) { int idx; PKT_signature *sig=onode->pkt->pkt.signature; for(idx=0;idxnumrevkeys;idx++) { u32 keyid[2]; keyid_from_fingerprint(sig->revkey[idx]->fpr, MAX_FINGERPRINT_LEN,keyid); for(inode=keyblock->next;inode;inode=inode->next) { /* If we reach user IDs, we're done. */ if(inode->pkt->pkttype==PKT_USER_ID) break; if(inode->pkt->pkttype==PKT_SIGNATURE && inode->pkt->pkt.signature->sig_class==0x20 && inode->pkt->pkt.signature->keyid[0]==keyid[0] && inode->pkt->pkt.signature->keyid[1]==keyid[1]) { /* Okay, we have a revocation key, and a revocation issued by it. Do we have the key itself? */ int rc; rc=get_pubkey_byfprint_fast (NULL,sig->revkey[idx]->fpr, MAX_FINGERPRINT_LEN); if(rc==G10ERR_NO_PUBKEY || rc==G10ERR_UNU_PUBKEY) { char *tempkeystr=xstrdup(keystr_from_pk(pk)); /* No, so try and get it */ if(opt.keyserver && (opt.keyserver_options.options & KEYSERVER_AUTO_KEY_RETRIEVE)) { log_info(_("WARNING: key %s may be revoked:" " fetching revocation key %s\n"), tempkeystr,keystr(keyid)); keyserver_import_fprint(sig->revkey[idx]->fpr, MAX_FINGERPRINT_LEN, opt.keyserver); /* Do we have it now? */ rc=get_pubkey_byfprint_fast (NULL, sig->revkey[idx]->fpr, MAX_FINGERPRINT_LEN); } if(rc==G10ERR_NO_PUBKEY || rc==G10ERR_UNU_PUBKEY) log_info(_("WARNING: key %s may be revoked:" " revocation key %s not present.\n"), tempkeystr,keystr(keyid)); xfree(tempkeystr); } } } } } } } /**************** * compare and merge the blocks * * o compare the signatures: If we already have this signature, check * that they compare okay; if not, issue a warning and ask the user. * o Simply add the signature. Can't verify here because we may not have * the signature's public key yet; verification is done when putting it * into the trustdb, which is done automagically as soon as this pubkey * is used. * Note: We indicate newly inserted packets with flag bit 0 */ static int merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock, u32 *keyid, int *n_uids, int *n_sigs, int *n_subk ) { KBNODE onode, node; int rc, found; /* 1st: handle revocation certificates */ for(node=keyblock->next; node; node=node->next ) { if( node->pkt->pkttype == PKT_USER_ID ) break; else if( node->pkt->pkttype == PKT_SIGNATURE && node->pkt->pkt.signature->sig_class == 0x20 ) { /* check whether we already have this */ found = 0; for(onode=keyblock_orig->next; onode; onode=onode->next ) { if( onode->pkt->pkttype == PKT_USER_ID ) break; else if( onode->pkt->pkttype == PKT_SIGNATURE && onode->pkt->pkt.signature->sig_class == 0x20 && !cmp_signatures(onode->pkt->pkt.signature, node->pkt->pkt.signature)) { found = 1; break; } } if( !found ) { KBNODE n2 = clone_kbnode(node); insert_kbnode( keyblock_orig, n2, 0 ); n2->flag |= 1; ++*n_sigs; if(!opt.quiet) { char *p=get_user_id_native (keyid); log_info(_("key %s: \"%s\" revocation" " certificate added\n"), keystr(keyid),p); xfree(p); } } } } /* 2nd: merge in any direct key (0x1F) sigs */ for(node=keyblock->next; node; node=node->next ) { if( node->pkt->pkttype == PKT_USER_ID ) break; else if( node->pkt->pkttype == PKT_SIGNATURE && node->pkt->pkt.signature->sig_class == 0x1F ) { /* check whether we already have this */ found = 0; for(onode=keyblock_orig->next; onode; onode=onode->next ) { if( onode->pkt->pkttype == PKT_USER_ID ) break; else if( onode->pkt->pkttype == PKT_SIGNATURE && onode->pkt->pkt.signature->sig_class == 0x1F && !cmp_signatures(onode->pkt->pkt.signature, node->pkt->pkt.signature)) { found = 1; break; } } if( !found ) { KBNODE n2 = clone_kbnode(node); insert_kbnode( keyblock_orig, n2, 0 ); n2->flag |= 1; ++*n_sigs; if(!opt.quiet) log_info( _("key %s: direct key signature added\n"), keystr(keyid)); } } } /* 3rd: try to merge new certificates in */ for(onode=keyblock_orig->next; onode; onode=onode->next ) { if( !(onode->flag & 1) && onode->pkt->pkttype == PKT_USER_ID) { /* find the user id in the imported keyblock */ for(node=keyblock->next; node; node=node->next ) if( node->pkt->pkttype == PKT_USER_ID && !cmp_user_ids( onode->pkt->pkt.user_id, node->pkt->pkt.user_id ) ) break; if( node ) { /* found: merge */ rc = merge_sigs( onode, node, n_sigs, fname, keyid ); if( rc ) return rc; } } } /* 4th: add new user-ids */ for(node=keyblock->next; node; node=node->next ) { if( node->pkt->pkttype == PKT_USER_ID) { /* do we have this in the original keyblock */ for(onode=keyblock_orig->next; onode; onode=onode->next ) if( onode->pkt->pkttype == PKT_USER_ID && !cmp_user_ids( onode->pkt->pkt.user_id, node->pkt->pkt.user_id ) ) break; if( !onode ) { /* this is a new user id: append */ rc = append_uid( keyblock_orig, node, n_sigs, fname, keyid); if( rc ) return rc; ++*n_uids; } } } /* 5th: add new subkeys */ for(node=keyblock->next; node; node=node->next ) { onode = NULL; if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { /* do we have this in the original keyblock? */ for(onode=keyblock_orig->next; onode; onode=onode->next ) if( onode->pkt->pkttype == PKT_PUBLIC_SUBKEY && !cmp_public_keys( onode->pkt->pkt.public_key, node->pkt->pkt.public_key ) ) break; if( !onode ) { /* this is a new subkey: append */ rc = append_key( keyblock_orig, node, n_sigs, fname, keyid); if( rc ) return rc; ++*n_subk; } } else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) { /* do we have this in the original keyblock? */ for(onode=keyblock_orig->next; onode; onode=onode->next ) if( onode->pkt->pkttype == PKT_SECRET_SUBKEY && !cmp_secret_keys( onode->pkt->pkt.secret_key, node->pkt->pkt.secret_key ) ) break; if( !onode ) { /* this is a new subkey: append */ rc = append_key( keyblock_orig, node, n_sigs, fname, keyid); if( rc ) return rc; ++*n_subk; } } } /* 6th: merge subkey certificates */ for(onode=keyblock_orig->next; onode; onode=onode->next ) { if( !(onode->flag & 1) && ( onode->pkt->pkttype == PKT_PUBLIC_SUBKEY || onode->pkt->pkttype == PKT_SECRET_SUBKEY) ) { /* find the subkey in the imported keyblock */ for(node=keyblock->next; node; node=node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY && !cmp_public_keys( onode->pkt->pkt.public_key, node->pkt->pkt.public_key ) ) break; else if( node->pkt->pkttype == PKT_SECRET_SUBKEY && !cmp_secret_keys( onode->pkt->pkt.secret_key, node->pkt->pkt.secret_key ) ) break; } if( node ) { /* found: merge */ rc = merge_keysigs( onode, node, n_sigs, fname, keyid ); if( rc ) return rc; } } } return 0; } /**************** * append the userid starting with NODE and all signatures to KEYBLOCK. */ static int append_uid( KBNODE keyblock, KBNODE node, int *n_sigs, const char *fname, u32 *keyid ) { KBNODE n, n_where=NULL; assert(node->pkt->pkttype == PKT_USER_ID ); /* find the position */ for( n = keyblock; n; n_where = n, n = n->next ) { if( n->pkt->pkttype == PKT_PUBLIC_SUBKEY || n->pkt->pkttype == PKT_SECRET_SUBKEY ) break; } if( !n ) n_where = NULL; /* and append/insert */ while( node ) { /* we add a clone to the original keyblock, because this * one is released first */ n = clone_kbnode(node); if( n_where ) { insert_kbnode( n_where, n, 0 ); n_where = n; } else add_kbnode( keyblock, n ); n->flag |= 1; node->flag |= 1; if( n->pkt->pkttype == PKT_SIGNATURE ) ++*n_sigs; node = node->next; if( node && node->pkt->pkttype != PKT_SIGNATURE ) break; } return 0; } /**************** * Merge the sigs from SRC onto DST. SRC and DST are both a PKT_USER_ID. * (how should we handle comment packets here?) */ static int merge_sigs( KBNODE dst, KBNODE src, int *n_sigs, const char *fname, u32 *keyid ) { KBNODE n, n2; int found=0; assert(dst->pkt->pkttype == PKT_USER_ID ); assert(src->pkt->pkttype == PKT_USER_ID ); for(n=src->next; n && n->pkt->pkttype != PKT_USER_ID; n = n->next ) { if( n->pkt->pkttype != PKT_SIGNATURE ) continue; if( n->pkt->pkt.signature->sig_class == 0x18 || n->pkt->pkt.signature->sig_class == 0x28 ) continue; /* skip signatures which are only valid on subkeys */ found = 0; for(n2=dst->next; n2 && n2->pkt->pkttype != PKT_USER_ID; n2 = n2->next) if(!cmp_signatures(n->pkt->pkt.signature,n2->pkt->pkt.signature)) { found++; break; } if( !found ) { /* This signature is new or newer, append N to DST. * We add a clone to the original keyblock, because this * one is released first */ n2 = clone_kbnode(n); insert_kbnode( dst, n2, PKT_SIGNATURE ); n2->flag |= 1; n->flag |= 1; ++*n_sigs; } } return 0; } /**************** * Merge the sigs from SRC onto DST. SRC and DST are both a PKT_xxx_SUBKEY. */ static int merge_keysigs( KBNODE dst, KBNODE src, int *n_sigs, const char *fname, u32 *keyid ) { KBNODE n, n2; int found=0; assert( dst->pkt->pkttype == PKT_PUBLIC_SUBKEY || dst->pkt->pkttype == PKT_SECRET_SUBKEY ); for(n=src->next; n ; n = n->next ) { if( n->pkt->pkttype == PKT_PUBLIC_SUBKEY || n->pkt->pkttype == PKT_PUBLIC_KEY ) break; if( n->pkt->pkttype != PKT_SIGNATURE ) continue; found = 0; for(n2=dst->next; n2; n2 = n2->next){ if( n2->pkt->pkttype == PKT_PUBLIC_SUBKEY || n2->pkt->pkttype == PKT_PUBLIC_KEY ) break; if( n2->pkt->pkttype == PKT_SIGNATURE && n->pkt->pkt.signature->keyid[0] == n2->pkt->pkt.signature->keyid[0] && n->pkt->pkt.signature->keyid[1] == n2->pkt->pkt.signature->keyid[1] && n->pkt->pkt.signature->timestamp <= n2->pkt->pkt.signature->timestamp && n->pkt->pkt.signature->sig_class == n2->pkt->pkt.signature->sig_class ) { found++; break; } } if( !found ) { /* This signature is new or newer, append N to DST. * We add a clone to the original keyblock, because this * one is released first */ n2 = clone_kbnode(n); insert_kbnode( dst, n2, PKT_SIGNATURE ); n2->flag |= 1; n->flag |= 1; ++*n_sigs; } } return 0; } /**************** * append the subkey starting with NODE and all signatures to KEYBLOCK. * Mark all new and copied packets by setting flag bit 0. */ static int append_key( KBNODE keyblock, KBNODE node, int *n_sigs, const char *fname, u32 *keyid ) { KBNODE n; assert( node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY ); while( node ) { /* we add a clone to the original keyblock, because this * one is released first */ n = clone_kbnode(node); add_kbnode( keyblock, n ); n->flag |= 1; node->flag |= 1; if( n->pkt->pkttype == PKT_SIGNATURE ) ++*n_sigs; node = node->next; if( node && node->pkt->pkttype != PKT_SIGNATURE ) break; } return 0; } /* Walk a public keyblock and produce a secret keyblock out of it. Instead of inserting the secret key parameters (which we don't have), we insert a stub. */ static KBNODE pub_to_sec_keyblock (KBNODE pub_keyblock) { KBNODE pubnode, secnode; KBNODE sec_keyblock = NULL; KBNODE walkctx = NULL; while((pubnode = walk_kbnode (pub_keyblock,&walkctx,0))) { if (pubnode->pkt->pkttype == PKT_PUBLIC_KEY || pubnode->pkt->pkttype == PKT_PUBLIC_SUBKEY) { /* Make a secret key. We only need to convert enough to write the keyblock out. */ PKT_public_key *pk = pubnode->pkt->pkt.public_key; PACKET *pkt = xmalloc_clear (sizeof *pkt); PKT_secret_key *sk = xmalloc_clear (sizeof *sk); int i, n; if (pubnode->pkt->pkttype == PKT_PUBLIC_KEY) pkt->pkttype = PKT_SECRET_KEY; else pkt->pkttype = PKT_SECRET_SUBKEY; pkt->pkt.secret_key = sk; copy_public_parts_to_secret_key ( pk, sk ); sk->version = pk->version; sk->timestamp = pk->timestamp; n = pubkey_get_npkey (pk->pubkey_algo); if (!n) n = 1; /* Unknown number of parameters, however the data is stored in the first mpi. */ for (i=0; i < n; i++ ) sk->skey[i] = mpi_copy (pk->pkey[i]); sk->is_protected = 1; sk->protect.s2k.mode = 1001; secnode = new_kbnode (pkt); } else { secnode = clone_kbnode (pubnode); } if(!sec_keyblock) sec_keyblock = secnode; else add_kbnode (sec_keyblock, secnode); } return sec_keyblock; } /* Walk over the secret keyring SEC_KEYBLOCK and update any simple stub keys with the serial number SNNUM of the card if one of the fingerprints FPR1, FPR2 or FPR3 match. Print a note if the key is a duplicate (may happen in case of backed uped keys). Returns: True if anything changed. */ static int update_sec_keyblock_with_cardinfo (KBNODE sec_keyblock, const unsigned char *fpr1, const unsigned char *fpr2, const unsigned char *fpr3, const char *serialnostr) { KBNODE node; KBNODE walkctx = NULL; PKT_secret_key *sk; byte array[MAX_FINGERPRINT_LEN]; size_t n; int result = 0; const char *s; while((node = walk_kbnode (sec_keyblock, &walkctx, 0))) { if (node->pkt->pkttype != PKT_SECRET_KEY && node->pkt->pkttype != PKT_SECRET_SUBKEY) continue; sk = node->pkt->pkt.secret_key; fingerprint_from_sk (sk, array, &n); if (n != 20) continue; /* Can't be a card key. */ if ( !((fpr1 && !memcmp (array, fpr1, 20)) || (fpr2 && !memcmp (array, fpr2, 20)) || (fpr3 && !memcmp (array, fpr3, 20))) ) continue; /* No match. */ if (sk->is_protected == 1 && sk->protect.s2k.mode == 1001) { /* Standard case: migrate that stub to a key stub. */ sk->protect.s2k.mode = 1002; s = serialnostr; for (sk->protect.ivlen=0; sk->protect.ivlen < 16 && *s && s[1]; sk->protect.ivlen++, s += 2) sk->protect.iv[sk->protect.ivlen] = xtoi_2 (s); result = 1; } else if (sk->is_protected == 1 && sk->protect.s2k.mode == 1002) { s = serialnostr; for (sk->protect.ivlen=0; sk->protect.ivlen < 16 && *s && s[1]; sk->protect.ivlen++, s += 2) if (sk->protect.iv[sk->protect.ivlen] != xtoi_2 (s)) { log_info (_("NOTE: a key's S/N does not " "match the card's one\n")); break; } } else { if (node->pkt->pkttype != PKT_SECRET_KEY) log_info (_("NOTE: primary key is online and stored on card\n")); else log_info (_("NOTE: secondary key is online and stored on card\n")); } } return result; } /* Check whether a secret key stub exists for the public key PK. If not create such a stub key and store it into the secring. If it exists, add appropriate subkey stubs and update the secring. Return 0 if the key could be created. */ int auto_create_card_key_stub ( const char *serialnostr, const unsigned char *fpr1, const unsigned char *fpr2, const unsigned char *fpr3) { KBNODE pub_keyblock; KBNODE sec_keyblock; KEYDB_HANDLE hd; int rc; /* We only want to do this for an OpenPGP card. */ if (!serialnostr || strncmp (serialnostr, "D27600012401", 12) || strlen (serialnostr) != 32 ) return G10ERR_GENERAL; /* First get the public keyring from any of the provided fingerprints. */ if ( (fpr1 && !get_keyblock_byfprint (&pub_keyblock, fpr1, 20)) || (fpr2 && !get_keyblock_byfprint (&pub_keyblock, fpr2, 20)) || (fpr3 && !get_keyblock_byfprint (&pub_keyblock, fpr3, 20))) ; else return G10ERR_GENERAL; hd = keydb_new (1); /* Now check whether there is a secret keyring. */ { PKT_public_key *pk = pub_keyblock->pkt->pkt.public_key; byte afp[MAX_FINGERPRINT_LEN]; size_t an; fingerprint_from_pk (pk, afp, &an); if (an < MAX_FINGERPRINT_LEN) memset (afp+an, 0, MAX_FINGERPRINT_LEN-an); rc = keydb_search_fpr (hd, afp); } if (!rc) { rc = keydb_get_keyblock (hd, &sec_keyblock); if (rc) { log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) ); rc = G10ERR_GENERAL; } else { merge_keys_and_selfsig (sec_keyblock); /* FIXME: We need to add new subkeys first. */ if (update_sec_keyblock_with_cardinfo (sec_keyblock, fpr1, fpr2, fpr3, serialnostr)) { rc = keydb_update_keyblock (hd, sec_keyblock ); if (rc) log_error (_("error writing keyring `%s': %s\n"), keydb_get_resource_name (hd), g10_errstr(rc) ); } } } else /* A secret key does not exists - create it. */ { sec_keyblock = pub_to_sec_keyblock (pub_keyblock); update_sec_keyblock_with_cardinfo (sec_keyblock, fpr1, fpr2, fpr3, serialnostr); rc = keydb_locate_writable (hd, NULL); if (rc) { log_error (_("no default secret keyring: %s\n"), g10_errstr (rc)); rc = G10ERR_GENERAL; } else { rc = keydb_insert_keyblock (hd, sec_keyblock ); if (rc) log_error (_("error writing keyring `%s': %s\n"), keydb_get_resource_name (hd), g10_errstr(rc) ); } } release_kbnode (sec_keyblock); release_kbnode (pub_keyblock); keydb_release (hd); return rc; } diff --git a/g10/keyserver.c b/g10/keyserver.c index 7bf983064..dca5e18bf 100644 --- a/g10/keyserver.c +++ b/g10/keyserver.c @@ -1,2209 +1,2255 @@ /* keyserver.c - generic keyserver code * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2012 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include "filter.h" #include "keydb.h" #include "status.h" #include "exec.h" #include "main.h" #include "i18n.h" #include "iobuf.h" #include "memory.h" #include "ttyio.h" #include "options.h" #include "packet.h" #include "trustdb.h" #include "keyserver-internal.h" #include "util.h" #ifdef USE_DNS_SRV #include "srv.h" #endif #ifdef HAVE_W32_SYSTEM /* It seems Vista doesn't grok X_OK and so fails access() tests. Previous versions interpreted X_OK as F_OK anyway, so we'll just use F_OK directly. */ #undef X_OK #define X_OK F_OK #endif /* HAVE_W32_SYSTEM */ struct keyrec { KEYDB_SEARCH_DESC desc; u32 createtime,expiretime; int size,flags; byte type; IOBUF uidbuf; unsigned int lines; }; enum ks_action {KS_UNKNOWN=0,KS_GET,KS_GETNAME,KS_SEND,KS_SEARCH}; static struct parse_options keyserver_opts[]= { /* some of these options are not real - just for the help message */ {"max-cert-size",0,NULL,NULL}, {"include-revoked",0,NULL,N_("include revoked keys in search results")}, {"include-subkeys",0,NULL,N_("include subkeys when searching by key ID")}, {"use-temp-files",0,NULL, N_("use temporary files to pass data to keyserver helpers")}, {"keep-temp-files",KEYSERVER_KEEP_TEMP_FILES,NULL, N_("do not delete temporary files after using them")}, {"refresh-add-fake-v3-keyids",KEYSERVER_ADD_FAKE_V3,NULL, NULL}, {"auto-key-retrieve",KEYSERVER_AUTO_KEY_RETRIEVE,NULL, N_("automatically retrieve keys when verifying signatures")}, {"honor-keyserver-url",KEYSERVER_HONOR_KEYSERVER_URL,NULL, N_("honor the preferred keyserver URL set on the key")}, {"honor-pka-record",KEYSERVER_HONOR_PKA_RECORD,NULL, N_("honor the PKA record set on a key when retrieving keys")}, {NULL,0,NULL,NULL} }; static int keyserver_work(enum ks_action action,STRLIST list, KEYDB_SEARCH_DESC *desc,int count, unsigned char **fpr,size_t *fpr_len, struct keyserver_spec *keyserver); /* Reasonable guess */ #define DEFAULT_MAX_CERT_SIZE 16384 static size_t max_cert_size=DEFAULT_MAX_CERT_SIZE; static void add_canonical_option(char *option,STRLIST *list) { char *arg=argsplit(option); if(arg) { char *joined; joined=xmalloc(strlen(option)+1+strlen(arg)+1); /* Make a canonical name=value form with no spaces */ strcpy(joined,option); strcat(joined,"="); strcat(joined,arg); append_to_strlist(list,joined); xfree(joined); } else append_to_strlist(list,option); } int parse_keyserver_options(char *options) { int ret=1; char *tok; char *max_cert=NULL; keyserver_opts[0].value=&max_cert; while((tok=optsep(&options))) { if(tok[0]=='\0') continue; /* For backwards compatibility. 1.2.x used honor-http-proxy and there are a good number of documents published that recommend it. */ if(ascii_strcasecmp(tok,"honor-http-proxy")==0) tok="http-proxy"; else if(ascii_strcasecmp(tok,"no-honor-http-proxy")==0) tok="no-http-proxy"; /* We accept quite a few possible options here - some options to handle specially, the keyserver_options list, and import and export options that pertain to keyserver operations. Note that you must use strncasecmp here as there might be an =argument attached which will foil the use of strcasecmp. */ #ifdef EXEC_TEMPFILE_ONLY if(ascii_strncasecmp(tok,"use-temp-files",14)==0 || ascii_strncasecmp(tok,"no-use-temp-files",17)==0) log_info(_("WARNING: keyserver option `%s' is not used" " on this platform\n"),tok); #else if(ascii_strncasecmp(tok,"use-temp-files",14)==0) opt.keyserver_options.options|=KEYSERVER_USE_TEMP_FILES; else if(ascii_strncasecmp(tok,"no-use-temp-files",17)==0) opt.keyserver_options.options&=~KEYSERVER_USE_TEMP_FILES; #endif else if(!parse_options(tok,&opt.keyserver_options.options, keyserver_opts,0) && !parse_import_options(tok, &opt.keyserver_options.import_options,0) && !parse_export_options(tok, &opt.keyserver_options.export_options,0)) { /* All of the standard options have failed, so the option is destined for a keyserver plugin. */ add_canonical_option(tok,&opt.keyserver_options.other); } } if(max_cert) { max_cert_size=strtoul(max_cert,(char **)NULL,10); if(max_cert_size==0) max_cert_size=DEFAULT_MAX_CERT_SIZE; } return ret; } void free_keyserver_spec(struct keyserver_spec *keyserver) { xfree(keyserver->uri); xfree(keyserver->scheme); xfree(keyserver->auth); xfree(keyserver->host); xfree(keyserver->port); xfree(keyserver->path); xfree(keyserver->opaque); free_strlist(keyserver->options); xfree(keyserver); } /* Return 0 for match */ static int cmp_keyserver_spec(struct keyserver_spec *one,struct keyserver_spec *two) { if(ascii_strcasecmp(one->scheme,two->scheme)==0) { if(one->host && two->host && ascii_strcasecmp(one->host,two->host)==0) { if((one->port && two->port && ascii_strcasecmp(one->port,two->port)==0) || (!one->port && !two->port)) return 0; } else if(one->opaque && two->opaque && ascii_strcasecmp(one->opaque,two->opaque)==0) return 0; } return 1; } /* Try and match one of our keyservers. If we can, return that. If we can't, return our input. */ struct keyserver_spec * keyserver_match(struct keyserver_spec *spec) { struct keyserver_spec *ks; for(ks=opt.keyserver;ks;ks=ks->next) if(cmp_keyserver_spec(spec,ks)==0) return ks; return spec; } /* TODO: once we cut over to an all-curl world, we don't need this parser any longer so it can be removed, or at least moved to keyserver/ksutil.c for limited use in gpgkeys_ldap or the like. */ struct keyserver_spec * parse_keyserver_uri(const char *string,int require_scheme, const char *configname,unsigned int configlineno) { int assume_hkp=0; struct keyserver_spec *keyserver; const char *idx; int count; char *uri,*options; assert(string!=NULL); keyserver=xmalloc_clear(sizeof(struct keyserver_spec)); uri=xstrdup(string); options=strchr(uri,' '); if(options) { char *tok; *options='\0'; options++; while((tok=optsep(&options))) add_canonical_option(tok,&keyserver->options); } /* Get the scheme */ for(idx=uri,count=0;*idx && *idx!=':';idx++) { count++; /* Do we see the start of an RFC-2732 ipv6 address here? If so, there clearly isn't a scheme so get out early. */ if(*idx=='[') { /* Was the '[' the first thing in the string? If not, we have a mangled scheme with a [ in it so fail. */ if(count==1) break; else goto fail; } } if(count==0) goto fail; if(*idx=='\0' || *idx=='[') { if(require_scheme) return NULL; /* Assume HKP if there is no scheme */ assume_hkp=1; keyserver->scheme=xstrdup("hkp"); keyserver->uri=xmalloc(strlen(keyserver->scheme)+3+strlen(uri)+1); strcpy(keyserver->uri,keyserver->scheme); strcat(keyserver->uri,"://"); strcat(keyserver->uri,uri); } else { int i; keyserver->uri=xstrdup(uri); keyserver->scheme=xmalloc(count+1); /* Force to lowercase */ for(i=0;ischeme[i]=ascii_tolower(uri[i]); keyserver->scheme[i]='\0'; /* Skip past the scheme and colon */ uri+=count+1; } if(ascii_strcasecmp(keyserver->scheme,"x-broken-hkp")==0) { deprecated_warning(configname,configlineno,"x-broken-hkp", "--keyserver-options ","broken-http-proxy"); xfree(keyserver->scheme); keyserver->scheme=xstrdup("hkp"); append_to_strlist(&opt.keyserver_options.other,"broken-http-proxy"); } else if(ascii_strcasecmp(keyserver->scheme,"x-hkp")==0) { /* Canonicalize this to "hkp" so it works with both the internal and external keyserver interface. */ xfree(keyserver->scheme); keyserver->scheme=xstrdup("hkp"); } if(assume_hkp || (uri[0]=='/' && uri[1]=='/')) { /* Two slashes means network path. */ /* Skip over the "//", if any */ if(!assume_hkp) uri+=2; /* Do we have userinfo auth data present? */ for(idx=uri,count=0;*idx && *idx!='@' && *idx!='/';idx++) count++; /* We found a @ before the slash, so that means everything before the @ is auth data. */ if(*idx=='@') { if(count==0) goto fail; keyserver->auth=xmalloc(count+1); strncpy(keyserver->auth,uri,count); keyserver->auth[count]='\0'; uri+=count+1; } /* Is it an RFC-2732 ipv6 [literal address] ? */ if(*uri=='[') { for(idx=uri+1,count=1;*idx && ((isascii (*idx) && isxdigit(*idx)) || *idx==':' || *idx=='.');idx++) count++; /* Is the ipv6 literal address terminated? */ if(*idx==']') count++; else goto fail; } else for(idx=uri,count=0;*idx && *idx!=':' && *idx!='/';idx++) count++; if(count==0) goto fail; keyserver->host=xmalloc(count+1); strncpy(keyserver->host,uri,count); keyserver->host[count]='\0'; /* Skip past the host */ uri+=count; if(*uri==':') { /* It would seem to be reasonable to limit the range of the ports to values between 1-65535, but RFC 1738 and 1808 imply there is no limit. Of course, the real world has limits. */ for(idx=uri+1,count=0;*idx && *idx!='/';idx++) { count++; /* Ports are digits only */ if(!digitp(idx)) goto fail; } keyserver->port=xmalloc(count+1); strncpy(keyserver->port,uri+1,count); keyserver->port[count]='\0'; /* Skip past the colon and port number */ uri+=1+count; } /* Everything else is the path */ if(*uri) keyserver->path=xstrdup(uri); else keyserver->path=xstrdup("/"); if(keyserver->path[1]) keyserver->flags.direct_uri=1; } else if(uri[0]!='/') { /* No slash means opaque. Just record the opaque blob and get out. */ keyserver->opaque=xstrdup(uri); } else { /* One slash means absolute path. We don't need to support that yet. */ goto fail; } return keyserver; fail: free_keyserver_spec(keyserver); return NULL; } struct keyserver_spec * parse_preferred_keyserver(PKT_signature *sig) { struct keyserver_spec *spec=NULL; const byte *p; size_t plen; p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_PREF_KS,&plen); if(p && plen) { byte *dupe=xmalloc(plen+1); memcpy(dupe,p,plen); dupe[plen]='\0'; spec=parse_keyserver_uri(dupe,1,NULL,0); xfree(dupe); } return spec; } static void print_keyrec(int number,struct keyrec *keyrec) { int i; iobuf_writebyte(keyrec->uidbuf,0); iobuf_flush_temp(keyrec->uidbuf); printf("(%d)\t%s ",number,iobuf_get_temp_buffer(keyrec->uidbuf)); if(keyrec->size>0) printf("%d bit ",keyrec->size); if(keyrec->type) { const char *str=pubkey_algo_to_string(keyrec->type); if(str) printf("%s ",str); else printf("unknown "); } switch(keyrec->desc.mode) { /* If the keyserver helper gave us a short keyid, we have no choice but to use it. Do check --keyid-format to add a 0x if needed. */ case KEYDB_SEARCH_MODE_SHORT_KID: printf("key %s%08lX", (opt.keyid_format==KF_0xSHORT || opt.keyid_format==KF_0xLONG)?"0x":"", (ulong)keyrec->desc.u.kid[1]); break; /* However, if it gave us a long keyid, we can honor --keyid-format via keystr(). */ case KEYDB_SEARCH_MODE_LONG_KID: printf("key %s",keystr(keyrec->desc.u.kid)); break; /* If it gave us a PGP 2.x fingerprint, not much we can do beyond displaying it. */ case KEYDB_SEARCH_MODE_FPR16: printf("key "); for(i=0;i<16;i++) printf("%02X",keyrec->desc.u.fpr[i]); break; /* If we get a modern fingerprint, we have the most flexibility. */ case KEYDB_SEARCH_MODE_FPR20: { u32 kid[2]; keyid_from_fingerprint(keyrec->desc.u.fpr,20,kid); printf("key %s",keystr(kid)); } break; default: BUG(); break; } if(keyrec->createtime>0) { printf(", "); printf(_("created: %s"),strtimestamp(keyrec->createtime)); } if(keyrec->expiretime>0) { printf(", "); printf(_("expires: %s"),strtimestamp(keyrec->expiretime)); } if(keyrec->flags&1) printf(" (%s)",_("revoked")); if(keyrec->flags&2) printf(" (%s)",_("disabled")); if(keyrec->flags&4) printf(" (%s)",_("expired")); printf("\n"); } /* Returns a keyrec (which must be freed) once a key is complete, and NULL otherwise. Call with a NULL keystring once key parsing is complete to return any unfinished keys. */ static struct keyrec * parse_keyrec(char *keystring) { static struct keyrec *work=NULL; struct keyrec *ret=NULL; char *record; int i; if(keystring==NULL) { if(work==NULL) return NULL; else if(work->desc.mode==KEYDB_SEARCH_MODE_NONE) { xfree(work); return NULL; } else { ret=work; work=NULL; return ret; } } if(work==NULL) { work=xmalloc_clear(sizeof(struct keyrec)); work->uidbuf=iobuf_temp(); } /* Remove trailing whitespace */ for(i=strlen(keystring);i>0;i--) if(ascii_isspace(keystring[i-1])) keystring[i-1]='\0'; else break; if((record=strsep(&keystring,":"))==NULL) return ret; if(ascii_strcasecmp("pub",record)==0) { char *tok; if(work->desc.mode) { ret=work; work=xmalloc_clear(sizeof(struct keyrec)); work->uidbuf=iobuf_temp(); } if((tok=strsep(&keystring,":"))==NULL) return ret; classify_user_id(tok,&work->desc); if(work->desc.mode!=KEYDB_SEARCH_MODE_SHORT_KID && work->desc.mode!=KEYDB_SEARCH_MODE_LONG_KID && work->desc.mode!=KEYDB_SEARCH_MODE_FPR16 && work->desc.mode!=KEYDB_SEARCH_MODE_FPR20) { work->desc.mode=KEYDB_SEARCH_MODE_NONE; return ret; } /* Note all items after this are optional. This allows us to have a pub line as simple as pub:keyid and nothing else. */ work->lines++; if((tok=strsep(&keystring,":"))==NULL) return ret; work->type=atoi(tok); if((tok=strsep(&keystring,":"))==NULL) return ret; work->size=atoi(tok); if((tok=strsep(&keystring,":"))==NULL) return ret; if(atoi(tok)<=0) work->createtime=0; else work->createtime=atoi(tok); if((tok=strsep(&keystring,":"))==NULL) return ret; if(atoi(tok)<=0) work->expiretime=0; else { work->expiretime=atoi(tok); /* Force the 'e' flag on if this key is expired. */ if(work->expiretime<=make_timestamp()) work->flags|=4; } if((tok=strsep(&keystring,":"))==NULL) return ret; while(*tok) switch(*tok++) { case 'r': case 'R': work->flags|=1; break; - + case 'd': case 'D': work->flags|=2; break; case 'e': case 'E': work->flags|=4; break; } } else if(ascii_strcasecmp("uid",record)==0 && work->desc.mode) { char *userid,*tok,*decoded; if((tok=strsep(&keystring,":"))==NULL) return ret; if(strlen(tok)==0) return ret; userid=tok; /* By definition, de-%-encoding is always smaller than the original string so we can decode in place. */ i=0; while(*tok) if(tok[0]=='%' && tok[1] && tok[2]) { int c; userid[i] = (c=hextobyte(&tok[1])) == -1 ? '?' : c; i++; tok+=3; } else userid[i++]=*tok++; /* We don't care about the other info provided in the uid: line since no keyserver supports marking userids with timestamps or revoked/expired/disabled yet. */ /* No need to check for control characters, as utf8_to_native does this for us. */ decoded=utf8_to_native(userid,i,0); if(strlen(decoded)>opt.screen_columns-10) decoded[opt.screen_columns-10]='\0'; iobuf_writestr(work->uidbuf,decoded); xfree(decoded); iobuf_writestr(work->uidbuf,"\n\t"); work->lines++; } /* Ignore any records other than "pri" and "uid" for easy future growth. */ return ret; } /* TODO: do this as a list sent to keyserver_work rather than calling it once for each key to get the correct counts after the import (cosmetics, really) and to better take advantage of the keyservers that can do multiple fetches in one go (LDAP). */ static int show_prompt(KEYDB_SEARCH_DESC *desc,int numdesc,int count,const char *search) { char *answer; if(count && opt.command_fd==-1) { static int from=1; tty_printf("Keys %d-%d of %d for \"%s\". ",from,numdesc,count,search); from=numdesc+1; } answer=cpr_get_no_help("keysearch.prompt", _("Enter number(s), N)ext, or Q)uit > ")); /* control-d */ if(answer[0]=='\x04') { printf("Q\n"); answer[0]='q'; } if(answer[0]=='q' || answer[0]=='Q') { xfree(answer); return 1; } else if(atoi(answer)>=1 && atoi(answer)<=numdesc) { char *split=answer,*num; while((num=strsep(&split," ,"))!=NULL) if(atoi(num)>=1 && atoi(num)<=numdesc) keyserver_work(KS_GET,NULL,&desc[atoi(num)-1],1, NULL,NULL,opt.keyserver); xfree(answer); return 1; } return 0; } /* Count and searchstr are just for cosmetics. If the count is too small, it will grow safely. If negative it disables the "Key x-y of z" messages. searchstr should be UTF-8 (rather than native). */ static void keyserver_search_prompt(IOBUF buffer,const char *searchstr) { int i=0,validcount=0,started=0,header=0,count=1; unsigned int maxlen,buflen,numlines=0; KEYDB_SEARCH_DESC *desc; byte *line=NULL; char *localstr=NULL; if(searchstr) localstr=utf8_to_native(searchstr,strlen(searchstr),0); desc=xmalloc(count*sizeof(KEYDB_SEARCH_DESC)); for(;;) { struct keyrec *keyrec; int rl; maxlen=1024; rl=iobuf_read_line(buffer,&line,&buflen,&maxlen); if(opt.with_colons) { if(!header && ascii_strncasecmp("SEARCH ",line,7)==0 && ascii_strncasecmp(" BEGIN",&line[strlen(line)-7],6)==0) { header=1; continue; } else if(ascii_strncasecmp("SEARCH ",line,7)==0 && ascii_strncasecmp(" END",&line[strlen(line)-5],4)==0) continue; printf("%s",line); } /* Look for an info: line. The only current info: values defined are the version and key count. */ if(!started && rl>0 && ascii_strncasecmp("info:",line,5)==0) { char *tok,*str=&line[5]; if((tok=strsep(&str,":"))!=NULL) { int version; if(sscanf(tok,"%d",&version)!=1) version=1; if(version!=1) { log_error(_("invalid keyserver protocol " "(us %d!=handler %d)\n"),1,version); break; } } if((tok=strsep(&str,":"))!=NULL && sscanf(tok,"%d",&count)==1) { if(count==0) goto notfound; else if(count<0) count=10; else validcount=1; desc=xrealloc(desc,count*sizeof(KEYDB_SEARCH_DESC)); } started=1; continue; } if(rl==0) { keyrec=parse_keyrec(NULL); if(keyrec==NULL) { if(i==0) { count=0; break; } if(i!=count) validcount=0; for(;;) { if(show_prompt(desc,i,validcount?count:0,localstr)) break; validcount=0; } break; } } else keyrec=parse_keyrec(line); if(i==count) { /* keyserver helper sent more keys than they claimed in the info: line. */ count+=10; desc=xrealloc(desc,count*sizeof(KEYDB_SEARCH_DESC)); validcount=0; } if(keyrec) { desc[i]=keyrec->desc; if(!opt.with_colons) { /* screen_lines - 1 for the prompt. */ if(numlines+keyrec->lines>opt.screen_lines-1) { if(show_prompt(desc,i,validcount?count:0,localstr)) break; else numlines=0; } print_keyrec(i+1,keyrec); } numlines+=keyrec->lines; iobuf_close(keyrec->uidbuf); xfree(keyrec); started=1; i++; } } notfound: /* Leave this commented out or now, and perhaps for a very long time. All HKPish servers return HTML error messages for no-key-found. */ - /* + /* if(!started) log_info(_("keyserver does not support searching\n")); else */ if(count==0) { if(localstr) log_info(_("key \"%s\" not found on keyserver\n"),localstr); else log_info(_("key not found on keyserver\n")); } xfree(localstr); xfree(desc); xfree(line); } /* We sometimes want to use a different gpgkeys_xxx for a given protocol (for example, ldaps is handled by gpgkeys_ldap). Map these here. */ static const char * keyserver_typemap(const char *type) { if(strcmp(type,"ldaps")==0) return "ldap"; else if(strcmp(type,"hkps")==0) return "hkp"; else return type; } /* The PGP LDAP and the curl fetch-a-LDAP-object methodologies are sufficiently different that we can't use curl to do LDAP. */ static int direct_uri_map(const char *scheme,unsigned int is_direct) { if(is_direct && strcmp(scheme,"ldap")==0) return 1; return 0; } #define GPGKEYS_PREFIX "gpgkeys_" #define GPGKEYS_CURL GPGKEYS_PREFIX "curl" EXEEXT #define GPGKEYS_PREFIX_LEN (strlen(GPGKEYS_CURL)) #define KEYSERVER_ARGS_KEEP " -o \"%O\" \"%I\"" #define KEYSERVER_ARGS_NOKEEP " -o \"%o\" \"%i\"" -static int + +/* Check whether a key matches the search description. The filter + returns 0 if the key shall be imported. Note that this kind of + filter is not related to the iobuf filters. */ +static int +keyserver_retrieval_filter (PKT_public_key *pk, PKT_secret_key *sk, void *arg) +{ + KEYDB_SEARCH_DESC *desc = arg; + u32 keyid[2]; + byte fpr[MAX_FINGERPRINT_LEN]; + size_t fpr_len = 0; + + /* Secret keys are not expected from a keyserver. Do not import. */ + if (sk) + return G10ERR_GENERAL; + + fingerprint_from_pk (pk, fpr, &fpr_len); + keyid_from_pk (pk, keyid); + + /* Compare requested and returned fingerprints if available. */ + if (desc->mode == KEYDB_SEARCH_MODE_FPR20) + { + if (fpr_len != 20 || memcmp (fpr, desc->u.fpr, 20)) + return G10ERR_GENERAL; + } + else if (desc->mode == KEYDB_SEARCH_MODE_FPR16) + { + if (fpr_len != 16 || memcmp (fpr, desc->u.fpr, 16)) + return G10ERR_GENERAL; + } + else if (desc->mode == KEYDB_SEARCH_MODE_LONG_KID) + { + if (keyid[0] != desc->u.kid[0] || keyid[1] != desc->u.kid[1]) + return G10ERR_GENERAL; + } + else if (desc->mode == KEYDB_SEARCH_MODE_SHORT_KID) + { + if (keyid[1] != desc->u.kid[1]) + return G10ERR_GENERAL; + } + + return 0; +} + + +static int keyserver_spawn(enum ks_action action,STRLIST list,KEYDB_SEARCH_DESC *desc, int count,int *prog,unsigned char **fpr,size_t *fpr_len, struct keyserver_spec *keyserver) { int ret=0,i,gotversion=0,outofband=0; STRLIST temp; unsigned int maxlen,buflen; char *command,*end,*searchstr=NULL; byte *line=NULL; struct exec_info *spawn; const char *scheme; const char *libexecdir = get_libexecdir (); assert(keyserver); #ifdef EXEC_TEMPFILE_ONLY opt.keyserver_options.options|=KEYSERVER_USE_TEMP_FILES; #endif /* Build the filename for the helper to execute */ scheme=keyserver_typemap(keyserver->scheme); #ifdef DISABLE_KEYSERVER_PATH /* Destroy any path we might have. This is a little tricky, portability-wise. It's not correct to delete the PATH environment variable, as that may fall back to a system built-in PATH. Similarly, it is not correct to set PATH to the null string (PATH="") since this actually deletes the PATH environment variable under MinGW. The safest thing to do here is to force PATH to be GNUPG_LIBEXECDIR. All this is not that meaningful on Unix-like systems (since we're going to give a full path to gpgkeys_foo), but on W32 it prevents loading any DLLs from directories in %PATH%. After some more thinking about this we came to the conclusion that it is better to load the helpers from the directory where the program of this process lives. Fortunately Windows provides a way to retrieve this and our get_libexecdir function has been modified to return just this. Setting the exec-path is not - anymore required. + anymore required. set_exec_path(libexecdir); */ #else if(opt.exec_path_set) { /* If exec-path was set, and DISABLE_KEYSERVER_PATH is undefined, then don't specify a full path to gpgkeys_foo, so that the PATH can work. */ command=xmalloc(GPGKEYS_PREFIX_LEN+strlen(scheme)+3+strlen(EXEEXT)+1); command[0]='\0'; } else #endif { /* Specify a full path to gpgkeys_foo. */ command=xmalloc(strlen(libexecdir)+strlen(DIRSEP_S)+ GPGKEYS_PREFIX_LEN+strlen(scheme)+3+strlen(EXEEXT)+1); strcpy(command,libexecdir); #ifndef __VMS strcat (command, DIRSEP_S); #endif } end=command+strlen(command); /* Build a path for the keyserver helper. If it is direct_uri (i.e. an object fetch and not a keyserver), then add "_uri" to the end to distinguish the keyserver helper from an object fetcher that can speak that protocol (this is a problem for LDAP). */ - strcat(command,GPGKEYS_PREFIX); + strcat(command,GPGKEYS_PREFIX); strcat(command,scheme); /* This "_uri" thing is in case we need to call a direct handler instead of the keyserver handler. This lets us use gpgkeys_curl or gpgkeys_ldap_uri (we don't provide it, but a user might) instead of gpgkeys_ldap to fetch things like ldap://keyserver.pgp.com/o=PGP%20keys?pgpkey?sub?pgpkeyid=99242560 */ if(direct_uri_map(scheme,keyserver->flags.direct_uri)) strcat(command,"_uri"); strcat(command,EXEEXT); /* Can we execute it? If not, try curl as our catchall. */ if(path_access(command,X_OK)!=0) strcpy(end,GPGKEYS_CURL); if(opt.keyserver_options.options&KEYSERVER_USE_TEMP_FILES) { if(opt.keyserver_options.options&KEYSERVER_KEEP_TEMP_FILES) { command=xrealloc(command,strlen(command)+ strlen(KEYSERVER_ARGS_KEEP)+1); strcat(command,KEYSERVER_ARGS_KEEP); } else { command=xrealloc(command,strlen(command)+ strlen(KEYSERVER_ARGS_NOKEEP)+1); - strcat(command,KEYSERVER_ARGS_NOKEEP); + strcat(command,KEYSERVER_ARGS_NOKEEP); } ret=exec_write(&spawn,NULL,command,NULL,0,0); } else ret=exec_write(&spawn,command,NULL,NULL,0,0); xfree(command); if(ret) return ret; fprintf(spawn->tochild, "# This is a GnuPG %s keyserver communications file\n",VERSION); fprintf(spawn->tochild,"VERSION %d\n",KEYSERVER_PROTO_VERSION); fprintf(spawn->tochild,"PROGRAM %s\n",VERSION); fprintf(spawn->tochild,"SCHEME %s\n",keyserver->scheme); if(keyserver->opaque) fprintf(spawn->tochild,"OPAQUE %s\n",keyserver->opaque); else { if(keyserver->auth) fprintf(spawn->tochild,"AUTH %s\n",keyserver->auth); if(keyserver->host) fprintf(spawn->tochild,"HOST %s\n",keyserver->host); if(keyserver->port) fprintf(spawn->tochild,"PORT %s\n",keyserver->port); if(keyserver->path) fprintf(spawn->tochild,"PATH %s\n",keyserver->path); } /* Write global options */ for(temp=opt.keyserver_options.other;temp;temp=temp->next) fprintf(spawn->tochild,"OPTION %s\n",temp->d); /* Write per-keyserver options */ for(temp=keyserver->options;temp;temp=temp->next) fprintf(spawn->tochild,"OPTION %s\n",temp->d); switch(action) { case KS_GET: { fprintf(spawn->tochild,"COMMAND GET\n\n"); /* Which keys do we want? */ for(i=0;itochild,"0x"); for(f=0;ftochild,"%02X",desc[i].u.fpr[f]); fprintf(spawn->tochild,"\n"); } else if(desc[i].mode==KEYDB_SEARCH_MODE_FPR16) { int f; fprintf(spawn->tochild,"0x"); for(f=0;f<16;f++) fprintf(spawn->tochild,"%02X",desc[i].u.fpr[f]); fprintf(spawn->tochild,"\n"); } else if(desc[i].mode==KEYDB_SEARCH_MODE_LONG_KID) fprintf(spawn->tochild,"0x%08lX%08lX\n", (ulong)desc[i].u.kid[0], (ulong)desc[i].u.kid[1]); else if(desc[i].mode==KEYDB_SEARCH_MODE_SHORT_KID) fprintf(spawn->tochild,"0x%08lX\n", (ulong)desc[i].u.kid[1]); else if(desc[i].mode==KEYDB_SEARCH_MODE_EXACT) { fprintf(spawn->tochild,"0x0000000000000000\n"); quiet=1; } else if(desc[i].mode==KEYDB_SEARCH_MODE_NONE) continue; else BUG(); if(!quiet) { if(keyserver->host) log_info(_("requesting key %s from %s server %s\n"), keystr_from_desc(&desc[i]), keyserver->scheme,keyserver->host); else log_info(_("requesting key %s from %s\n"), keystr_from_desc(&desc[i]),keyserver->uri); } } fprintf(spawn->tochild,"\n"); break; } case KS_GETNAME: { STRLIST key; fprintf(spawn->tochild,"COMMAND GETNAME\n\n"); /* Which names do we want? */ for(key=list;key!=NULL;key=key->next) fprintf(spawn->tochild,"%s\n",key->d); fprintf(spawn->tochild,"\n"); if(keyserver->host) log_info(_("searching for names from %s server %s\n"), keyserver->scheme,keyserver->host); else log_info(_("searching for names from %s\n"),keyserver->uri); break; } case KS_SEND: { STRLIST key; /* Note the extra \n here to send an empty keylist block */ fprintf(spawn->tochild,"COMMAND SEND\n\n\n"); for(key=list;key!=NULL;key=key->next) { armor_filter_context_t *afx; IOBUF buffer=iobuf_temp(); KBNODE block; temp=NULL; add_to_strlist(&temp,key->d); afx = new_armor_context (); afx->what = 1; /* Tell the armor filter to use Unix-style \n line endings, since we're going to fprintf this to a file that (on Win32) is open in text mode. The win32 stdio will transform the \n to \r\n and we'll end up with the proper line endings on win32. This is a no-op on Unix. */ afx->eol[0]='\n'; push_armor_filter (afx, buffer); release_armor_context (afx); /* TODO: Remove Comment: lines from keys exported this way? */ if(export_pubkeys_stream(buffer,temp,&block, opt.keyserver_options.export_options)==-1) iobuf_close(buffer); else { KBNODE node; iobuf_flush_temp(buffer); merge_keys_and_selfsig(block); fprintf(spawn->tochild,"INFO %08lX%08lX BEGIN\n", (ulong)block->pkt->pkt.public_key->keyid[0], (ulong)block->pkt->pkt.public_key->keyid[1]); for(node=block;node;node=node->next) { switch(node->pkt->pkttype) { default: continue; case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: { PKT_public_key *pk=node->pkt->pkt.public_key; keyid_from_pk(pk,NULL); fprintf(spawn->tochild,"%sb:%08lX%08lX:%u:%u:%u:%u:", node->pkt->pkttype==PKT_PUBLIC_KEY?"pu":"su", (ulong)pk->keyid[0],(ulong)pk->keyid[1], pk->pubkey_algo, nbits_from_pk(pk), pk->timestamp, pk->expiredate); if(pk->is_revoked) fprintf(spawn->tochild,"r"); if(pk->has_expired) fprintf(spawn->tochild,"e"); fprintf(spawn->tochild,"\n"); } break; case PKT_USER_ID: { PKT_user_id *uid=node->pkt->pkt.user_id; int r; char *uidstr1,*uidstr2,*uidstr3; size_t uidstrlen; if(uid->attrib_data) continue; fprintf(spawn->tochild,"uid:"); /* Make sure it's real UTF8. What happens here is that first we heuristically try and convert the string (which may be mis-coded) into UTF8. We then bring it to native and then back to UTF8. For true UTF8, this whole process should be lossless. For the common Latin-1 mis-encoding, it will become UTF8. For other encodings, it will become UTF8 but with unknown characters quoted. This preserves the notion that anything in the stream to the keyserver handler program is UTF8. */ uidstr1=string_to_utf8(uid->name); uidstr2=utf8_to_native(uidstr1,strlen(uidstr1),-1); uidstr3=native_to_utf8(uidstr2); uidstrlen=strlen(uidstr3); /* Quote ':', '%', and anything not printable ASCII */ for(r=0;r'~') fprintf(spawn->tochild,"%%%02X", (byte)uidstr3[r]); else fprintf(spawn->tochild,"%c",uidstr3[r]); } xfree(uidstr1); xfree(uidstr2); xfree(uidstr3); fprintf(spawn->tochild,":%u:%u:", uid->created,uid->expiredate); if(uid->is_revoked) fprintf(spawn->tochild,"r"); if(uid->is_expired) fprintf(spawn->tochild,"e"); fprintf(spawn->tochild,"\n"); } break; /* This bit is really for the benefit of people who store their keys in LDAP servers. It makes it easy to do queries for things like "all keys signed by Isabella". */ case PKT_SIGNATURE: { PKT_signature *sig=node->pkt->pkt.signature; if(!IS_UID_SIG(sig)) continue; fprintf(spawn->tochild,"sig:%08lX%08lX:%X:%u:%u\n", (ulong)sig->keyid[0],(ulong)sig->keyid[1], sig->sig_class,sig->timestamp, sig->expiredate); } break; } } fprintf(spawn->tochild,"INFO %08lX%08lX END\n", (ulong)block->pkt->pkt.public_key->keyid[0], (ulong)block->pkt->pkt.public_key->keyid[1]); fprintf(spawn->tochild,"KEY %08lX%08lX BEGIN\n", (ulong)block->pkt->pkt.public_key->keyid[0], (ulong)block->pkt->pkt.public_key->keyid[1]); fwrite(iobuf_get_temp_buffer(buffer), iobuf_get_temp_length(buffer),1,spawn->tochild); fprintf(spawn->tochild,"KEY %08lX%08lX END\n", (ulong)block->pkt->pkt.public_key->keyid[0], (ulong)block->pkt->pkt.public_key->keyid[1]); iobuf_close(buffer); if(keyserver->host) log_info(_("sending key %s to %s server %s\n"), keystr(block->pkt->pkt.public_key->keyid), keyserver->scheme,keyserver->host); else log_info(_("sending key %s to %s\n"), keystr(block->pkt->pkt.public_key->keyid), keyserver->uri); release_kbnode(block); } free_strlist(temp); } break; } case KS_SEARCH: { STRLIST key; fprintf(spawn->tochild,"COMMAND SEARCH\n\n"); /* Which keys do we want? Remember that the gpgkeys_ program is going to lump these together into a search string. */ for(key=list;key!=NULL;key=key->next) { fprintf(spawn->tochild,"%s\n",key->d); if(key!=list) { searchstr=xrealloc(searchstr, strlen(searchstr)+strlen(key->d)+2); strcat(searchstr," "); } else { searchstr=xmalloc(strlen(key->d)+1); searchstr[0]='\0'; } strcat(searchstr,key->d); } fprintf(spawn->tochild,"\n"); if(keyserver->host) log_info(_("searching for \"%s\" from %s server %s\n"), searchstr,keyserver->scheme,keyserver->host); else log_info(_("searching for \"%s\" from %s\n"), searchstr,keyserver->uri); break; } default: log_fatal(_("no keyserver action!\n")); break; } /* Done sending, so start reading. */ ret=exec_read(spawn); if(ret) goto fail; /* Now handle the response */ for(;;) { int plen; char *ptr; maxlen=1024; if(iobuf_read_line(spawn->fromchild,&line,&buflen,&maxlen)==0) { ret=G10ERR_READ_FILE; goto fail; /* i.e. EOF */ } ptr=line; /* remove trailing whitespace */ plen=strlen(ptr); while(plen>0 && ascii_isspace(ptr[plen-1])) plen--; ptr[plen]='\0'; if(*ptr=='\0') break; if(ascii_strncasecmp(ptr,"VERSION ",8)==0) { gotversion=1; if(atoi(&ptr[8])!=KEYSERVER_PROTO_VERSION) { log_error(_("invalid keyserver protocol (us %d!=handler %d)\n"), KEYSERVER_PROTO_VERSION,atoi(&ptr[8])); goto fail; } } else if(ascii_strncasecmp(ptr,"PROGRAM ",8)==0) { if(ascii_strncasecmp(&ptr[8],VERSION,strlen(VERSION))!=0) log_info(_("WARNING: keyserver handler from a different" " version of GnuPG (%s)\n"),&ptr[8]); } else if(ascii_strncasecmp(ptr,"OPTION OUTOFBAND",16)==0) outofband=1; /* Currently the only OPTION */ } if(!gotversion) { log_error(_("keyserver did not send VERSION\n")); goto fail; } if(!outofband) switch(action) { case KS_GET: case KS_GETNAME: { void *stats_handle; stats_handle=import_new_stats_handle(); /* Slurp up all the key data. In the future, it might be nice to look for KEY foo OUTOFBAND and FAILED indicators. It's harmless to ignore them, but ignoring them does make gpg complain about "no valid OpenPGP data found". One way to do this could be to continue parsing this line-by-line and make a temp iobuf for each key. Note that we don't allow the import of secret keys from a keyserver. Keyservers should never accept or send them but we better protect against rogue keyservers. */ import_keys_stream (spawn->fromchild, stats_handle, fpr, fpr_len, - (opt.keyserver_options.import_options - | IMPORT_NO_SECKEY)); + (opt.keyserver_options.import_options + | IMPORT_NO_SECKEY), + keyserver_retrieval_filter, desc); import_print_stats(stats_handle); import_release_stats_handle(stats_handle); break; } /* Nothing to do here */ case KS_SEND: break; case KS_SEARCH: keyserver_search_prompt(spawn->fromchild,searchstr); break; default: log_fatal(_("no keyserver action!\n")); break; } fail: xfree(line); xfree(searchstr); *prog=exec_finish(spawn); return ret; } -static int +static int keyserver_work(enum ks_action action,STRLIST list,KEYDB_SEARCH_DESC *desc, int count,unsigned char **fpr,size_t *fpr_len, struct keyserver_spec *keyserver) { int rc=0,ret=0; if(!keyserver) { log_error(_("no keyserver known (use option --keyserver)\n")); return G10ERR_BAD_URI; } #ifdef DISABLE_KEYSERVER_HELPERS log_error(_("external keyserver calls are not supported in this build\n")); return G10ERR_KEYSERVER; #else /* Spawn a handler */ rc=keyserver_spawn(action,list,desc,count,&ret,fpr,fpr_len,keyserver); if(ret) { switch(ret) { case KEYSERVER_SCHEME_NOT_FOUND: log_error(_("no handler for keyserver scheme `%s'\n"), keyserver->scheme); break; case KEYSERVER_NOT_SUPPORTED: log_error(_("action `%s' not supported with keyserver " "scheme `%s'\n"), action==KS_GET?"get":action==KS_SEND?"send": action==KS_SEARCH?"search":"unknown", keyserver->scheme); break; case KEYSERVER_VERSION_ERROR: log_error(_(GPGKEYS_PREFIX "%s does not support" " handler version %d\n"), keyserver_typemap(keyserver->scheme), KEYSERVER_PROTO_VERSION); break; case KEYSERVER_TIMEOUT: log_error(_("keyserver timed out\n")); break; case KEYSERVER_INTERNAL_ERROR: default: log_error(_("keyserver internal error\n")); break; } return G10ERR_KEYSERVER; } if(rc) { log_error(_("keyserver communications error: %s\n"),g10_errstr(rc)); return rc; } return 0; #endif /* ! DISABLE_KEYSERVER_HELPERS*/ } -int +int keyserver_export(STRLIST users) { STRLIST sl=NULL; KEYDB_SEARCH_DESC desc; int rc=0; /* Weed out descriptors that we don't support sending */ for(;users;users=users->next) { classify_user_id (users->d, &desc); if(desc.mode!=KEYDB_SEARCH_MODE_SHORT_KID && desc.mode!=KEYDB_SEARCH_MODE_LONG_KID && desc.mode!=KEYDB_SEARCH_MODE_FPR16 && desc.mode!=KEYDB_SEARCH_MODE_FPR20) { log_error(_("\"%s\" not a key ID: skipping\n"),users->d); continue; } else append_to_strlist(&sl,users->d); } if(sl) { rc=keyserver_work(KS_SEND,sl,NULL,0,NULL,NULL,opt.keyserver); free_strlist(sl); } return rc; } -int +int keyserver_import(STRLIST users) { KEYDB_SEARCH_DESC *desc; int num=100,count=0; int rc=0; /* Build a list of key ids */ desc=xmalloc(sizeof(KEYDB_SEARCH_DESC)*num); for(;users;users=users->next) { classify_user_id (users->d, &desc[count]); if(desc[count].mode!=KEYDB_SEARCH_MODE_SHORT_KID && desc[count].mode!=KEYDB_SEARCH_MODE_LONG_KID && desc[count].mode!=KEYDB_SEARCH_MODE_FPR16 && desc[count].mode!=KEYDB_SEARCH_MODE_FPR20) { log_error(_("\"%s\" not a key ID: skipping\n"),users->d); continue; } count++; if(count==num) { num+=100; desc=xrealloc(desc,sizeof(KEYDB_SEARCH_DESC)*num); } } if(count>0) rc=keyserver_work(KS_GET,NULL,desc,count,NULL,NULL,opt.keyserver); xfree(desc); return rc; } int keyserver_import_fprint(const byte *fprint,size_t fprint_len, struct keyserver_spec *keyserver) { KEYDB_SEARCH_DESC desc; memset(&desc,0,sizeof(desc)); if(fprint_len==16) desc.mode=KEYDB_SEARCH_MODE_FPR16; else if(fprint_len==20) desc.mode=KEYDB_SEARCH_MODE_FPR20; else return -1; memcpy(desc.u.fpr,fprint,fprint_len); /* TODO: Warn here if the fingerprint we got doesn't match the one we asked for? */ return keyserver_work(KS_GET,NULL,&desc,1,NULL,NULL,keyserver); } -int +int keyserver_import_keyid(u32 *keyid,struct keyserver_spec *keyserver) { KEYDB_SEARCH_DESC desc; memset(&desc,0,sizeof(desc)); desc.mode=KEYDB_SEARCH_MODE_LONG_KID; desc.u.kid[0]=keyid[0]; desc.u.kid[1]=keyid[1]; return keyserver_work(KS_GET,NULL,&desc,1,NULL,NULL,keyserver); } /* code mostly stolen from do_export_stream */ -static int +static int keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3) { int rc=0,ndesc,num=100; KBNODE keyblock=NULL,node; KEYDB_HANDLE kdbhd; KEYDB_SEARCH_DESC *desc; STRLIST sl; *count=0; *klist=xmalloc(sizeof(KEYDB_SEARCH_DESC)*num); kdbhd=keydb_new(0); if(!users) { ndesc = 1; desc = xmalloc_clear ( ndesc * sizeof *desc); desc[0].mode = KEYDB_SEARCH_MODE_FIRST; } else { - for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++) + for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++) ; desc = xmalloc ( ndesc * sizeof *desc); - + for (ndesc=0, sl=users; sl; sl = sl->next) { if(classify_user_id (sl->d, desc+ndesc)) ndesc++; else log_error (_("key \"%s\" not found: %s\n"), sl->d, g10_errstr (G10ERR_INV_USER_ID)); } } while (!(rc = keydb_search (kdbhd, desc, ndesc))) { - if (!users) + if (!users) desc[0].mode = KEYDB_SEARCH_MODE_NEXT; /* read the keyblock */ rc = keydb_get_keyblock (kdbhd, &keyblock ); if( rc ) { log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) ); goto leave; } if((node=find_kbnode(keyblock,PKT_PUBLIC_KEY))) { /* This is to work around a bug in some keyservers (pksd and OKS) that calculate v4 RSA keyids as if they were v3 RSA. The answer is to refresh both the correct v4 keyid (e.g. 99242560) and the fake v3 keyid (e.g. 68FDDBC7). This only happens for key refresh using the HKP scheme and if the refresh-add-fake-v3-keyids keyserver option is set. */ if(fakev3 && is_RSA(node->pkt->pkt.public_key->pubkey_algo) && node->pkt->pkt.public_key->version>=4) { (*klist)[*count].mode=KEYDB_SEARCH_MODE_LONG_KID; mpi_get_keyid(node->pkt->pkt.public_key->pkey[0], (*klist)[*count].u.kid); (*count)++; if(*count==num) { num+=100; *klist=xrealloc(*klist,sizeof(KEYDB_SEARCH_DESC)*num); } } /* v4 keys get full fingerprints. v3 keys get long keyids. This is because it's easy to calculate any sort of keyid from a v4 fingerprint, but not a v3 fingerprint. */ if(node->pkt->pkt.public_key->version<4) { (*klist)[*count].mode=KEYDB_SEARCH_MODE_LONG_KID; keyid_from_pk(node->pkt->pkt.public_key, (*klist)[*count].u.kid); } else { size_t dummy; (*klist)[*count].mode=KEYDB_SEARCH_MODE_FPR20; fingerprint_from_pk(node->pkt->pkt.public_key, (*klist)[*count].u.fpr,&dummy); } /* This is a little hackish, using the skipfncvalue as a void* pointer to the keyserver spec, but we don't need the skipfnc here, and it saves having an additional field for this (which would be wasted space most of the time). */ (*klist)[*count].skipfncvalue=NULL; /* Are we honoring preferred keyservers? */ if(opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL) { PKT_user_id *uid=NULL; PKT_signature *sig=NULL; merge_keys_and_selfsig(keyblock); for(node=node->next;node;node=node->next) { if(node->pkt->pkttype==PKT_USER_ID && node->pkt->pkt.user_id->is_primary) uid=node->pkt->pkt.user_id; else if(node->pkt->pkttype==PKT_SIGNATURE && node->pkt->pkt.signature-> flags.chosen_selfsig && uid) { sig=node->pkt->pkt.signature; break; } } /* Try and parse the keyserver URL. If it doesn't work, then we end up writing NULL which indicates we are the same as any other key. */ if(sig) (*klist)[*count].skipfncvalue=parse_preferred_keyserver(sig); } (*count)++; if(*count==num) { num+=100; *klist=xrealloc(*klist,sizeof(KEYDB_SEARCH_DESC)*num); } } } if(rc==-1) rc=0; - + leave: if(rc) xfree(*klist); xfree(desc); keydb_release(kdbhd); release_kbnode(keyblock); return rc; } /* Note this is different than the original HKP refresh. It allows usernames to refresh only part of the keyring. */ int keyserver_refresh(STRLIST users) { int rc,count,numdesc,fakev3=0; KEYDB_SEARCH_DESC *desc; unsigned int options=opt.keyserver_options.import_options; /* We switch merge-only on during a refresh, as 'refresh' should never import new keys, even if their keyids match. */ opt.keyserver_options.import_options|=IMPORT_MERGE_ONLY; /* Similarly, we switch on fast-import, since refresh may make multiple import sets (due to preferred keyserver URLs). We don't want each set to rebuild the trustdb. Instead we do it once at the end here. */ opt.keyserver_options.import_options|=IMPORT_FAST; /* If refresh_add_fake_v3_keyids is on and it's a HKP or MAILTO scheme, then enable fake v3 keyid generation. */ if((opt.keyserver_options.options&KEYSERVER_ADD_FAKE_V3) && opt.keyserver && (ascii_strcasecmp(opt.keyserver->scheme,"hkp")==0 || ascii_strcasecmp(opt.keyserver->scheme,"mailto")==0)) fakev3=1; rc=keyidlist(users,&desc,&numdesc,fakev3); if(rc) return rc; count=numdesc; if(count>0) { int i; /* Try to handle preferred keyserver keys first */ for(i=0;iuri,g10_errstr(rc)); else { /* We got it, so mark it as NONE so we don't try and get it again from the regular keyserver. */ desc[i].mode=KEYDB_SEARCH_MODE_NONE; count--; } free_keyserver_spec(keyserver); } } } if(count>0) { if(opt.keyserver) { if(count==1) log_info(_("refreshing 1 key from %s\n"),opt.keyserver->uri); else log_info(_("refreshing %d keys from %s\n"), count,opt.keyserver->uri); } rc=keyserver_work(KS_GET,NULL,desc,numdesc,NULL,NULL,opt.keyserver); } xfree(desc); opt.keyserver_options.import_options=options; /* If the original options didn't have fast import, and the trustdb is dirty, rebuild. */ if(!(opt.keyserver_options.import_options&IMPORT_FAST)) trustdb_check_or_update(); return rc; } int keyserver_search(STRLIST tokens) { if(tokens) return keyserver_work(KS_SEARCH,tokens,NULL,0,NULL,NULL,opt.keyserver); else return 0; } int keyserver_fetch(STRLIST urilist) { KEYDB_SEARCH_DESC desc; STRLIST sl; unsigned int options=opt.keyserver_options.import_options; /* Switch on fast-import, since fetch can handle more than one import and we don't want each set to rebuild the trustdb. Instead we do it once at the end. */ opt.keyserver_options.import_options|=IMPORT_FAST; /* A dummy desc since we're not actually fetching a particular key ID */ memset(&desc,0,sizeof(desc)); desc.mode=KEYDB_SEARCH_MODE_EXACT; for(sl=urilist;sl;sl=sl->next) { struct keyserver_spec *spec; spec=parse_keyserver_uri(sl->d,1,NULL,0); if(spec) { int rc; rc=keyserver_work(KS_GET,NULL,&desc,1,NULL,NULL,spec); if(rc) log_info (_("WARNING: unable to fetch URI %s: %s\n"), sl->d,g10_errstr(rc)); free_keyserver_spec(spec); } else log_info (_("WARNING: unable to parse URI %s\n"),sl->d); } opt.keyserver_options.import_options=options; /* If the original options didn't have fast import, and the trustdb is dirty, rebuild. */ if(!(opt.keyserver_options.import_options&IMPORT_FAST)) trustdb_check_or_update(); return 0; } /* Import key in a CERT or pointed to by a CERT */ int keyserver_import_cert(const char *name,unsigned char **fpr,size_t *fpr_len) { char *domain,*look,*url; IOBUF key; int type,rc=G10ERR_GENERAL; look=xstrdup(name); domain=strrchr(look,'@'); if(domain) *domain='.'; type=get_cert(look,max_cert_size,&key,fpr,fpr_len,&url); if(type==1) { int armor_status=opt.no_armor; /* CERTs are always in binary format */ opt.no_armor=1; rc=import_keys_stream (key, NULL, fpr, fpr_len, (opt.keyserver_options.import_options - | IMPORT_NO_SECKEY)); + | IMPORT_NO_SECKEY), NULL, NULL); opt.no_armor=armor_status; iobuf_close(key); } else if(type==2 && *fpr) { /* We only consider the IPGP type if a fingerprint was provided. This lets us select the right key regardless of what a URL points to, or get the key from a keyserver. */ if(url) { struct keyserver_spec *spec; spec=parse_keyserver_uri(url,1,NULL,0); if(spec) { rc=keyserver_import_fprint(*fpr,*fpr_len,spec); free_keyserver_spec(spec); } } else if(opt.keyserver) { /* If only a fingerprint is provided, try and fetch it from our --keyserver */ rc=keyserver_import_fprint(*fpr,*fpr_len,opt.keyserver); } else log_info(_("no keyserver known (use option --keyserver)\n")); /* Give a better string here? "CERT fingerprint for \"%s\" found, but no keyserver" " known (use option --keyserver)\n" ? */ xfree(url); } xfree(look); return rc; } /* Import key pointed to by a PKA record. Return the requested fingerprint in fpr. */ int keyserver_import_pka(const char *name,unsigned char **fpr,size_t *fpr_len) { char *uri; int rc=-1; *fpr=xmalloc(20); *fpr_len=20; uri = get_pka_info (name, *fpr); if (uri) { struct keyserver_spec *spec; spec = parse_keyserver_uri (uri, 1, NULL, 0); if (spec) { rc=keyserver_import_fprint (*fpr, 20, spec); free_keyserver_spec (spec); } xfree (uri); } if(rc!=0) xfree(*fpr); return rc; } /* Import all keys that match name */ int keyserver_import_name(const char *name,unsigned char **fpr,size_t *fpr_len, struct keyserver_spec *keyserver) { STRLIST list=NULL; int rc; append_to_strlist(&list,name); rc=keyserver_work(KS_GETNAME,list,NULL,0,fpr,fpr_len,keyserver); free_strlist(list); return rc; } /* Import a key by name using LDAP */ int keyserver_import_ldap(const char *name,unsigned char **fpr,size_t *fpr_len) { char *domain; struct keyserver_spec *keyserver; STRLIST list=NULL; int rc,hostlen=1; #ifdef USE_DNS_SRV struct srventry *srvlist=NULL; int srvcount,i; char srvname[MAXDNAME]; #endif /* Parse out the domain */ domain=strrchr(name,'@'); if(!domain) return G10ERR_GENERAL; domain++; keyserver=xmalloc_clear(sizeof(struct keyserver_spec)); keyserver->scheme=xstrdup("ldap"); keyserver->host=xmalloc(1); keyserver->host[0]='\0'; #ifdef USE_DNS_SRV snprintf(srvname,MAXDNAME,"_pgpkey-ldap._tcp.%s",domain); srvcount=getsrv(srvname,&srvlist); for(i=0;ihost=xrealloc(keyserver->host,hostlen); strcat(keyserver->host,srvlist[i].target); if(srvlist[i].port!=389) { char port[7]; hostlen+=6; /* a colon, plus 5 digits (unsigned 16-bit value) */ keyserver->host=xrealloc(keyserver->host,hostlen); snprintf(port,7,":%u",srvlist[i].port); strcat(keyserver->host,port); } - + strcat(keyserver->host," "); } free(srvlist); #endif /* If all else fails, do the PGP Universal trick of ldap://keys.(domain) */ hostlen+=5+strlen(domain); keyserver->host=xrealloc(keyserver->host,hostlen); strcat(keyserver->host,"keys."); strcat(keyserver->host,domain); append_to_strlist(&list,name); - + rc=keyserver_work(KS_GETNAME,list,NULL,0,fpr,fpr_len,keyserver); free_strlist(list); free_keyserver_spec(keyserver); return rc; } diff --git a/g10/main.h b/g10/main.h index 784ade06d..e4c4385bf 100644 --- a/g10/main.h +++ b/g10/main.h @@ -1,300 +1,304 @@ /* main.h * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2008 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef G10_MAIN_H #define G10_MAIN_H #include "types.h" #include "iobuf.h" #include "mpi.h" #include "cipher.h" #include "keydb.h" /* It could be argued that the default cipher should be 3DES rather than CAST5, and the default compression should be 0 (i.e. uncompressed) rather than 1 (zip). However, the real world issues of speed and size come into play here. */ #define DEFAULT_CIPHER_ALGO CIPHER_ALGO_CAST5 #define DEFAULT_DIGEST_ALGO DIGEST_ALGO_SHA1 #define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_ZIP #define DEFAULT_S2K_DIGEST_ALGO DIGEST_ALGO_SHA1 #define S2K_DIGEST_ALGO (opt.s2k_digest_algo?opt.s2k_digest_algo:DEFAULT_S2K_DIGEST_ALGO) typedef struct { int header_okay; PK_LIST pk_list; DEK *symkey_dek; STRING2KEY *symkey_s2k; cipher_filter_context_t cfx; } encrypt_filter_context_t; struct groupitem { char *name; STRLIST values; struct groupitem *next; }; /*-- gpg.c --*/ extern int g10_errors_seen; #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) void g10_exit(int rc) __attribute__ ((noreturn)); #else void g10_exit(int rc); #endif void print_pubkey_algo_note( int algo ); void print_cipher_algo_note( int algo ); void print_digest_algo_note( int algo ); /*-- armor.c --*/ char *make_radix64_string( const byte *data, size_t len ); /*-- misc.c --*/ void trap_unaligned(void); int disable_core_dumps(void); void register_secured_file (const char *fname); void unregister_secured_file (const char *fname); int is_secured_file (int fd); int is_secured_filename (const char *fname); u16 checksum_u16( unsigned n ); u16 checksum( byte *p, unsigned n ); u16 checksum_mpi( MPI a ); u32 buffer_to_u32( const byte *buffer ); const byte *get_session_marker( size_t *rlen ); int openpgp_cipher_test_algo( int algo ); int openpgp_pk_test_algo( int algo, unsigned int usage_flags ); int openpgp_pk_algo_usage ( int algo ); int openpgp_md_test_algo( int algo ); void md5_digest_warn (int show); void not_in_gpg1_notice (void); struct expando_args { PKT_public_key *pk; PKT_secret_key *sk; byte imagetype; int validity_info; const char *validity_string; }; char *pct_expando(const char *string,struct expando_args *args); void deprecated_warning(const char *configname,unsigned int configlineno, const char *option,const char *repl1,const char *repl2); void deprecated_command (const char *name); const char *compress_algo_to_string(int algo); int string_to_compress_algo(const char *string); int check_compress_algo(int algo); int default_cipher_algo(void); int default_compress_algo(void); const char *compliance_option_string(void); void compliance_failure(void); struct parse_options { char *name; unsigned int bit; char **value; char *help; }; char *optsep(char **stringp); char *argsplit(char *string); int parse_options(char *str,unsigned int *options, struct parse_options *opts,int noisy); char *unescape_percent_string (const unsigned char *s); int has_invalid_email_chars (const char *s); int is_valid_mailbox (const char *name); char *default_homedir (void); const char *get_libexecdir (void); int path_access(const char *file,int mode); /*-- helptext.c --*/ void display_online_help( const char *keyword ); /*-- encode.c --*/ int setup_symkey(STRING2KEY **symkey_s2k,DEK **symkey_dek); int encode_symmetric( const char *filename ); int encode_store( const char *filename ); int encode_crypt( const char *filename, STRLIST remusr, int use_symkey ); void encode_crypt_files(int nfiles, char **files, STRLIST remusr); int encrypt_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len); /*-- sign.c --*/ int complete_sig( PKT_signature *sig, PKT_secret_key *sk, MD_HANDLE md ); int sign_file( STRLIST filenames, int detached, STRLIST locusr, int do_encrypt, STRLIST remusr, const char *outfile ); int clearsign_file( const char *fname, STRLIST locusr, const char *outfile ); int sign_symencrypt_file (const char *fname, STRLIST locusr); /*-- sig-check.c --*/ int check_revocation_keys (PKT_public_key *pk, PKT_signature *sig); int check_backsig(PKT_public_key *main_pk,PKT_public_key *sub_pk, PKT_signature *backsig); int check_key_signature( KBNODE root, KBNODE node, int *is_selfsig ); int check_key_signature2( KBNODE root, KBNODE node, PKT_public_key *check_pk, PKT_public_key *ret_pk, int *is_selfsig, u32 *r_expiredate, int *r_expired ); /*-- delkey.c --*/ int delete_keys( STRLIST names, int secret, int allow_both ); /*-- keyedit.c --*/ void keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, int quiet, int seckey_check ); void show_basic_key_info (KBNODE keyblock); /*-- keygen.c --*/ u32 parse_expire_string(u32 timestamp,const char *string); u32 ask_expire_interval(u32 timestamp,int object,const char *def_expire); void generate_keypair( const char *fname, const char *card_serialno, const char *backup_encryption_dir ); int keygen_set_std_prefs (const char *string,int personal); PKT_user_id *keygen_get_std_prefs (void); int keygen_add_key_expire( PKT_signature *sig, void *opaque ); int keygen_add_std_prefs( PKT_signature *sig, void *opaque ); int keygen_upd_std_prefs( PKT_signature *sig, void *opaque ); int keygen_add_keyserver_url(PKT_signature *sig, void *opaque); int keygen_add_notations(PKT_signature *sig,void *opaque); int keygen_add_revkey(PKT_signature *sig, void *opaque); int make_backsig(PKT_signature *sig,PKT_public_key *pk, PKT_public_key *sub_pk,PKT_secret_key *sub_sk, u32 timestamp); int generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ); #ifdef ENABLE_CARD_SUPPORT int generate_card_subkeypair (KBNODE pub_keyblock, KBNODE sec_keyblock, int keyno, const char *serialno); int save_unprotected_key_to_card (PKT_secret_key *sk, int keyno); #endif /*-- openfile.c --*/ int overwrite_filep( const char *fname ); char *make_outfile_name( const char *iname ); char *ask_outfile_name( const char *name, size_t namelen ); int open_outfile( const char *iname, int mode, IOBUF *a ); IOBUF open_sigfile( const char *iname, progress_filter_context_t *pfx ); void try_make_homedir( const char *fname ); /*-- seskey.c --*/ void make_session_key( DEK *dek ); MPI encode_session_key( DEK *dek, unsigned nbits ); MPI pkcs1_encode_md( MD_HANDLE md, int algo, size_t len, unsigned nbits, const byte *asn, size_t asnlen ); MPI encode_md_value( PKT_public_key *pk, PKT_secret_key *sk, MD_HANDLE md, int hash_algo ); /*-- import.c --*/ + +typedef int (*import_filter)(PKT_public_key *pk, PKT_secret_key *sk, void *arg); + int parse_import_options(char *str,unsigned int *options,int noisy); void import_keys( char **fnames, int nnames, void *stats_hd, unsigned int options ); -int import_keys_stream( IOBUF inp,void *stats_hd,unsigned char **fpr, - size_t *fpr_len,unsigned int options ); +int import_keys_stream (IOBUF inp,void *stats_hd,unsigned char **fpr, + size_t *fpr_len,unsigned int options, + import_filter filter, void *filter_arg); void *import_new_stats_handle (void); void import_release_stats_handle (void *p); void import_print_stats (void *hd); int collapse_uids( KBNODE *keyblock ); int auto_create_card_key_stub ( const char *serialnostr, const unsigned char *fpr1, const unsigned char *fpr2, const unsigned char *fpr3); /*-- export.c --*/ int parse_export_options(char *str,unsigned int *options,int noisy); int export_pubkeys( STRLIST users, unsigned int options ); int export_pubkeys_stream( IOBUF out, STRLIST users, KBNODE *keyblock_out, unsigned int options ); int export_seckeys( STRLIST users ); int export_secsubkeys( STRLIST users ); /* dearmor.c --*/ int dearmor_file( const char *fname ); int enarmor_file( const char *fname ); /*-- revoke.c --*/ struct revocation_reason_info; int gen_revoke( const char *uname ); int gen_desig_revoke( const char *uname, STRLIST locusr); int revocation_reason_build_cb( PKT_signature *sig, void *opaque ); struct revocation_reason_info * ask_revocation_reason( int key_rev, int cert_rev, int hint ); void release_revocation_reason_info( struct revocation_reason_info *reason ); /*-- keylist.c --*/ void public_key_list( STRLIST list ); void secret_key_list( STRLIST list ); void print_subpackets_colon(PKT_signature *sig); void reorder_keyblock (KBNODE keyblock); void list_keyblock( KBNODE keyblock, int secret, int fpr, void *opaque ); void print_fingerprint (PKT_public_key *pk, PKT_secret_key *sk, int mode); void print_revokers(PKT_public_key *pk); void show_policy_url(PKT_signature *sig,int indent,int mode); void show_keyserver_url(PKT_signature *sig,int indent,int mode); void show_notation(PKT_signature *sig,int indent,int mode,int which); void dump_attribs(const PKT_user_id *uid, PKT_public_key *pk,PKT_secret_key *sk); void set_attrib_fd(int fd); void print_seckey_info (PKT_secret_key *sk); void print_pubkey_info (FILE *fp, PKT_public_key *pk); void print_card_key_info (FILE *fp, KBNODE keyblock); /*-- verify.c --*/ void print_file_status( int status, const char *name, int what ); int verify_signatures( int nfiles, char **files ); int verify_files( int nfiles, char **files ); /*-- decrypt.c --*/ int decrypt_message( const char *filename ); void decrypt_messages(int nfiles, char *files[]); /*-- plaintext.c --*/ int hash_datafiles( MD_HANDLE md, MD_HANDLE md2, STRLIST files, const char *sigfilename, int textmode ); PKT_plaintext *setup_plaintext_name(const char *filename,IOBUF iobuf); /*-- pipemode.c --*/ void run_in_pipemode (void); /*-- signal.c --*/ void init_signals(void); void pause_on_sigusr( int which ); void block_all_signals(void); void unblock_all_signals(void); #ifdef ENABLE_CARD_SUPPORT /*-- card-util.c --*/ void change_pin (int no, int allow_admin); void card_status (FILE *fp, char *serialno, size_t serialnobuflen); void card_edit (STRLIST commands); int card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock); int card_store_subkey (KBNODE node, int use); #endif #define S2K_DECODE_COUNT(_val) ((16ul + ((_val) & 15)) << (((_val) >> 4) + 6)) #endif /*G10_MAIN_H*/