diff --git a/g10/import.c b/g10/import.c index 518e97f71..02d6b7617 100644 --- a/g10/import.c +++ b/g10/import.c @@ -1,2900 +1,2902 @@ /* import.c - import a key into our key storage. * Copyright (C) 1998-2007, 2010-2011 Free Software Foundation, Inc. * Copyright (C) 2014 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include "gpg.h" #include "options.h" #include "packet.h" #include "status.h" #include "keydb.h" #include "util.h" #include "trustdb.h" #include "main.h" #include "i18n.h" #include "ttyio.h" #include "status.h" #include "keyserver-internal.h" #include "call-agent.h" #include "../common/membuf.h" -struct stats_s +struct import_stats_s { ulong count; ulong no_user_id; ulong imported; 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; ulong v3keys; /* Number of V3 keys seen. */ }; static int import (ctrl_t ctrl, - IOBUF inp, const char* fname, struct stats_s *stats, + IOBUF inp, const char* fname, struct import_stats_s *stats, unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg); static int read_block (IOBUF a, PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys); static void revocation_present (ctrl_t ctrl, kbnode_t keyblock); static int import_one (ctrl_t ctrl, - const char *fname, kbnode_t keyblock,struct stats_s *stats, + const char *fname, kbnode_t keyblock, + struct import_stats_s *stats, unsigned char **fpr, size_t *fpr_len, unsigned int options, int from_sk, int silent, import_screener_t screener, void *screener_arg); static int import_secret_one (ctrl_t ctrl, const char *fname, kbnode_t keyblock, - struct stats_s *stats, int batch, + struct import_stats_s *stats, int batch, unsigned int options, int for_migration, import_screener_t screener, void *screener_arg); static int import_revoke_cert( const char *fname, kbnode_t node, - struct stats_s *stats); + struct import_stats_s *stats); static int chk_self_sigs (const char *fname, kbnode_t keyblock, PKT_public_key *pk, u32 *keyid, int *non_self ); static int delete_inv_parts (const char *fname, kbnode_t keyblock, u32 *keyid, unsigned int options ); static int merge_blocks (const char *fname, kbnode_t keyblock_orig, kbnode_t keyblock, u32 *keyid, int *n_uids, int *n_sigs, int *n_subk ); static int append_uid (kbnode_t keyblock, kbnode_t node, int *n_sigs, const char *fname, u32 *keyid ); static int append_key (kbnode_t keyblock, kbnode_t node, int *n_sigs, const char *fname, u32 *keyid ); static int merge_sigs (kbnode_t dst, kbnode_t src, int *n_sigs, const char *fname, u32 *keyid ); static int merge_keysigs (kbnode_t dst, kbnode_t 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")}, {"keep-ownertrust", IMPORT_KEEP_OWNERTTRUST, NULL, N_("do not clear the ownertrust values during import")}, {"fast-import",IMPORT_FAST,NULL, N_("do not update the trustdb after import")}, {"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}, {"convert-sk-to-pk",0, NULL,NULL}, /* Not anymore needed due to the new design. */ {NULL,0,NULL,NULL} }; return parse_options(str,options,import_opts,noisy); } -void * +import_stats_t import_new_stats_handle (void) { - return xmalloc_clear ( sizeof (struct stats_s) ); + return xmalloc_clear ( sizeof (struct import_stats_s) ); } void -import_release_stats_handle (void *p) +import_release_stats_handle (import_stats_t 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 (ctrl_t ctrl, iobuf_t inp, char **fnames, int nnames, - void *stats_handle, unsigned char **fpr, size_t *fpr_len, + import_stats_t stats_handle, + unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg) { int i; int rc = 0; - struct stats_s *stats = stats_handle; + struct import_stats_s *stats = stats_handle; if (!stats) stats = import_new_stats_handle (); if (inp) { rc = import (ctrl, inp, "[stream]", stats, fpr, fpr_len, options, screener, screener_arg); } else { if (!fnames && !nnames) nnames = 1; /* Ohh what a ugly hack to jump into the loop */ for (i=0; i < nnames; 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; gpg_err_set_errno (EPERM); } if (!inp2) log_error (_("can't open '%s': %s\n"), fname, strerror (errno)); else { rc = import (ctrl, inp2, fname, stats, fpr, fpr_len, options, screener, screener_arg); iobuf_close (inp2); /* Must invalidate that ugly cache to actually close it. */ iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname); if (rc) log_error ("import from '%s' failed: %s\n", fname, gpg_strerror (rc) ); } if (!fname) break; } } 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)) check_or_update_trustdb (); return rc; } void import_keys (ctrl_t ctrl, char **fnames, int nnames, - void *stats_handle, unsigned int options ) + import_stats_t stats_handle, unsigned int options ) { import_keys_internal (ctrl, NULL, fnames, nnames, stats_handle, NULL, NULL, options, NULL, NULL); } int -import_keys_stream (ctrl_t ctrl, IOBUF inp, void *stats_handle, +import_keys_stream (ctrl_t ctrl, IOBUF inp, import_stats_t stats_handle, unsigned char **fpr, size_t *fpr_len, unsigned int options) { return import_keys_internal (ctrl, inp, NULL, 0, stats_handle, fpr, fpr_len, options, NULL, NULL); } /* Variant of import_keys_stream reading from an estream_t. */ int -import_keys_es_stream (ctrl_t ctrl, estream_t fp, void *stats_handle, +import_keys_es_stream (ctrl_t ctrl, estream_t fp, + import_stats_t stats_handle, unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg) { int rc; iobuf_t inp; inp = iobuf_esopen (fp, "r", 1); if (!inp) { rc = gpg_error_from_syserror (); log_error ("iobuf_esopen failed: %s\n", gpg_strerror (rc)); return rc; } rc = import_keys_internal (ctrl, inp, NULL, 0, stats_handle, fpr, fpr_len, options, screener, screener_arg); iobuf_close (inp); return rc; } static int -import (ctrl_t ctrl, IOBUF inp, const char* fname,struct stats_s *stats, +import (ctrl_t ctrl, IOBUF inp, const char* fname,struct import_stats_s *stats, unsigned char **fpr,size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg) { PACKET *pending_pkt = NULL; kbnode_t keyblock = NULL; /* Need to initialize because gcc can't grasp the return semantics of read_block. */ int rc = 0; int v3keys; getkey_disable_caches (); if (!opt.no_armor) /* Armored reading is not disabled. */ { armor_filter_context_t *afx; 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, &v3keys))) { stats->v3keys += v3keys; if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY) rc = import_one (ctrl, fname, keyblock, stats, fpr, fpr_len, options, 0, 0, screener, screener_arg); else if (keyblock->pkt->pkttype == PKT_SECRET_KEY) rc = import_secret_one (ctrl, fname, keyblock, stats, opt.batch, options, 0, screener, screener_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. For now we do this only if the imported key is too large. */ if (gpg_err_code (rc) == GPG_ERR_TOO_LARGE && gpg_err_source (rc) == GPG_ERR_SOURCE_KEYBOX) { stats->not_imported++; rc = 0; } else if (rc) break; if (!(++stats->count % 100) && !opt.quiet) log_info (_("%lu keys processed so far\n"), stats->count ); } stats->v3keys += v3keys; if (rc == -1) rc = 0; else if (rc && gpg_err_code (rc) != GPG_ERR_INV_KEYRING) log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (rc)); return rc; } /* Helper to migrate secring.gpg to GnuPG 2.1. */ gpg_error_t import_old_secring (ctrl_t ctrl, const char *fname) { gpg_error_t err; iobuf_t inp; PACKET *pending_pkt = NULL; kbnode_t keyblock = NULL; /* Need to initialize because gcc can't grasp the return semantics of read_block. */ - struct stats_s *stats; + struct import_stats_s *stats; int v3keys; inp = iobuf_open (fname); if (inp && is_secured_file (iobuf_get_fd (inp))) { iobuf_close (inp); inp = NULL; gpg_err_set_errno (EPERM); } if (!inp) { err = gpg_error_from_syserror (); log_error (_("can't open '%s': %s\n"), fname, gpg_strerror (err)); return err; } getkey_disable_caches(); stats = import_new_stats_handle (); while (!(err = read_block (inp, &pending_pkt, &keyblock, &v3keys))) { if (keyblock->pkt->pkttype == PKT_SECRET_KEY) err = import_secret_one (ctrl, fname, keyblock, stats, 1, 0, 1, NULL, NULL); release_kbnode (keyblock); if (err) break; } import_release_stats_handle (stats); if (err == -1) err = 0; else if (err && gpg_err_code (err) != GPG_ERR_INV_KEYRING) log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (err)); else if (err) log_error ("import from '%s' failed: %s\n", fname, gpg_strerror (err)); iobuf_close (inp); iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname); return err; } void -import_print_stats (void *hd) +import_print_stats (import_stats_t stats) { - struct stats_s *stats = hd; - if (!opt.quiet) { log_info(_("Total number processed: %lu\n"), stats->count + stats->v3keys); if (stats->v3keys) log_info(_(" skipped PGP-2 keys: %lu\n"), stats->v3keys); 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) { log_info(_(" imported: %lu"), stats->imported ); log_printf ("\n"); } 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[15*20]; snprintf (buf, sizeof buf, "%lu %lu %lu 0 %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", stats->count + stats->v3keys, stats->no_user_id, stats->imported, 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, stats->v3keys ); 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 changed by the caller. * Return: 0 = okay, -1 no more blocks or another errorcode. * The int at at R_V3KEY counts the number of unsupported v3 * keyblocks. */ static int read_block( IOBUF a, PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys) { int rc; PACKET *pkt; kbnode_t root = NULL; int in_cert, in_v3key; *r_v3keys = 0; 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); in_v3key = 0; while ((rc=parse_packet(a, pkt)) != -1) { if (rc && (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY && (pkt->pkttype == PKT_PUBLIC_KEY || pkt->pkttype == PKT_SECRET_KEY))) { in_v3key = 1; ++*r_v3keys; free_packet (pkt); init_packet (pkt); continue; } else if (rc ) /* (ignore errors) */ { if (gpg_err_code (rc) == GPG_ERR_UNKNOWN_PACKET) ; /* Do not show a diagnostic. */ else { log_error("read_block: read error: %s\n", gpg_strerror (rc) ); rc = GPG_ERR_INV_KEYRING; goto ready; } free_packet( pkt ); init_packet(pkt); continue; } if (in_v3key && !(pkt->pkttype == PKT_PUBLIC_KEY || pkt->pkttype == PKT_SECRET_KEY)) { free_packet( pkt ); init_packet(pkt); continue; } in_v3key = 0; 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 = GPG_ERR_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_t keyblock) { int changed = 0; int keycount = 0; kbnode_t node; kbnode_t last = NULL; kbnode_t 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) { /* We might have the problem, as this key has two subkeys in a row without any intervening packets. */ /* Sanity check */ if (!last) 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 function returns the number of removed sigs. */ static int fix_bad_direct_key_sigs (kbnode_t keyblock, u32 *keyid) { gpg_error_t err; kbnode_t node; int count = 0; 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)) { err = check_key_signature (keyblock, node, NULL); if (err && gpg_err_code (err) != GPG_ERR_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, unsigned int reason) { byte array[MAX_FINGERPRINT_LEN], *s; char buf[MAX_FINGERPRINT_LEN*2+30], *p; size_t i, n; snprintf (buf, sizeof buf, "%u ", reason); p = buf + strlen (buf); fingerprint_from_pk (pk, 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, n; size_t pos = 0; 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" "algorithms on these user IDs:\n"), keystr_from_pk(pk)); } static void check_prefs (ctrl_t ctrl, kbnode_t keyblock) { kbnode_t 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 (openpgp_cipher_test_algo (prefs->value)) { const char *algo = (openpgp_cipher_test_algo (prefs->value) ? num : openpgp_cipher_algo_name (prefs->value)); if(!problem) check_prefs_warning(pk); log_info(_(" \"%s\": preference for cipher" " algorithm %s\n"), user, algo); problem=1; } } else if(prefs->type==PREFTYPE_HASH) { if(openpgp_md_test_algo(prefs->value)) { const char *algo = (gcry_md_test_algo (prefs->value) ? num : gcry_md_algo_name (prefs->value)); if(!problem) check_prefs_warning(pk); log_info(_(" \"%s\": preference for digest" " algorithm %s\n"), user, algo); 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_t sl = NULL; strlist_t 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; fingerprint_from_pk (pk, fpr2, &fpr2len); for (an = fpr2len; an < MAX_FINGERPRINT_LEN; an++) fpr2[an] = 0; keyid_from_pk( pk, keyid ); uidnode = find_next_kbnode( keyblock, PKT_USER_ID ); if (opt.verbose && !opt.interactive && !silent) { log_info( "pub %s/%s %s ", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk(pk), datestr_from_pk(pk) ); if (uidnode) print_utf8_buffer (log_get_stream (), uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len ); log_printf ("\n"); } if (!uidnode ) { if (!silent) log_error( _("key %s: no user ID\n"), keystr_from_pk(pk)); return 0; } if (screener && screener (keyblock, screener_arg)) { log_error (_("key %s: %s\n"), keystr_from_pk (pk), _("rejected by import screener")); return 0; } if (opt.interactive && !silent) { 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 ) ) { if (!silent) { 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_byfprint_fast (pk_orig, fpr2, fpr2len); if (rc && gpg_err_code (rc) != GPG_ERR_NO_PUBKEY && gpg_err_code (rc) != GPG_ERR_UNUSABLE_PUBKEY ) { if (!silent) log_error (_("key %s: public key not found: %s\n"), keystr(keyid), gpg_strerror (rc)); } else if ( rc && (opt.import_options&IMPORT_MERGE_ONLY) ) { if (opt.verbose && !silent ) 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; hd = keydb_new (); if (!hd) return gpg_error_from_syserror (); rc = keydb_locate_writable (hd); if (rc) { log_error (_("no writable keyring found: %s\n"), gpg_strerror (rc)); keydb_release (hd); return GPG_ERR_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), gpg_strerror (rc)); else if (!(opt.import_options & IMPORT_KEEP_OWNERTTRUST)) { /* 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 and by importing and locally exported key. */ clear_ownertrusts (pk); if (non_self) revalidation_mark (); } keydb_release (hd); /* We are ready. */ if (!opt.quiet && !silent) { char *p = get_user_id_byfpr_native (fpr2); 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, 1); } stats->imported++; 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 ) ) { if (!silent) log_error( _("key %s: doesn't match our copy\n"),keystr(keyid)); goto leave; } /* Now read the original keyblock again so that we can use that handle for updating the keyblock. */ hd = keydb_new (); if (!hd) { rc = gpg_error_from_syserror (); goto leave; } keydb_disable_caching (hd); rc = keydb_search_fpr (hd, fpr2); if (rc ) { log_error (_("key %s: can't locate original keyblock: %s\n"), keystr(keyid), gpg_strerror (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), gpg_strerror (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), gpg_strerror (rc) ); else if (non_self) revalidation_mark (); /* We are ready. */ if (!opt.quiet && !silent) { char *p = get_user_id_byfpr_native (fpr2); 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 () && !silent) print_import_ok (pk, ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0))); } else { same_key = 1; if (is_status_enabled ()) print_import_ok (pk, 0); if (!opt.quiet && !silent) { char *p = get_user_id_byfpr_native (fpr2); log_info( _("key %s: \"%s\" not changed\n"),keystr(keyid),p); xfree(p); } stats->unchanged++; } keydb_release (hd); hd = NULL; } leave: if (mod_key || new_key || same_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 fingerprinting at all, and we must hope the user ID on the keys are useful. Note that we need to do this for new keys, merged keys and even for unchanged keys. This is required because for example the --auto-key-locate feature may import an already imported key and needs to know the fingerprint of the key in all cases. */ if (fpr) { xfree (*fpr); /* Note that we need to compare against 0 here because COUNT gets only incremented after returning form this function. */ if (!stats->count) *fpr = fingerprint_from_pk (pk, NULL, fpr_len); else *fpr = NULL; } } /* 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 (ctrl, keyblock_orig); if (!from_sk && have_secret_key_with_kid (keyid)) check_prefs (ctrl, keyblock_orig); } else if (new_key) { revocation_present (ctrl, keyblock); if (!from_sk && have_secret_key_with_kid (keyid)) check_prefs (ctrl, keyblock); } release_kbnode( keyblock_orig ); free_public_key( pk_orig ); return rc; } /* Transfer all the secret keys in SEC_KEYBLOCK to the gpg-agent. The function prints diagnostics and returns an error code. If BATCH is true the secret keys are stored by gpg-agent in the transfer format (i.e. no re-protection and aksing for passphrases). */ static gpg_error_t -transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock, +transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats, kbnode_t sec_keyblock, int batch) { gpg_error_t err = 0; void *kek = NULL; size_t keklen; kbnode_t ctx = NULL; kbnode_t node; PKT_public_key *main_pk, *pk; struct seckey_info *ski; int nskey; membuf_t mbuf; int i, j; void *format_args[2*PUBKEY_MAX_NSKEY]; gcry_sexp_t skey, prot, tmpsexp; gcry_sexp_t curve = NULL; unsigned char *transferkey = NULL; size_t transferkeylen; gcry_cipher_hd_t cipherhd = NULL; unsigned char *wrappedkey = NULL; size_t wrappedkeylen; char *cache_nonce = NULL; int stub_key_skipped = 0; /* Get the current KEK. */ err = agent_keywrap_key (ctrl, 0, &kek, &keklen); if (err) { log_error ("error getting the KEK: %s\n", gpg_strerror (err)); goto leave; } /* Prepare a cipher context. */ err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, 0); if (!err) err = gcry_cipher_setkey (cipherhd, kek, keklen); if (err) goto leave; xfree (kek); kek = NULL; main_pk = NULL; while ((node = walk_kbnode (sec_keyblock, &ctx, 0))) { if (node->pkt->pkttype != PKT_SECRET_KEY && node->pkt->pkttype != PKT_SECRET_SUBKEY) continue; pk = node->pkt->pkt.public_key; if (!main_pk) main_pk = pk; /* Make sure the keyids are available. */ keyid_from_pk (pk, NULL); if (node->pkt->pkttype == PKT_SECRET_KEY) { pk->main_keyid[0] = pk->keyid[0]; pk->main_keyid[1] = pk->keyid[1]; } else { pk->main_keyid[0] = main_pk->keyid[0]; pk->main_keyid[1] = main_pk->keyid[1]; } ski = pk->seckey_info; if (!ski) BUG (); stats->count++; stats->secret_read++; /* We ignore stub keys. The way we handle them in other parts of the code is by asking the agent whether any secret key is available for a given keyblock and then concluding that we have a secret key; all secret (sub)keys of the keyblock the agent does not know of are then stub keys. This works also for card stub keys. The learn command or the card-status command may be used to check with the agent whether a card has been inserted and a stub key is in turn generated by the agent. */ if (ski->s2k.mode == 1001 || ski->s2k.mode == 1002) { stub_key_skipped = 1; continue; } /* Convert our internal secret key object into an S-expression. */ nskey = pubkey_get_nskey (pk->pubkey_algo); if (!nskey || nskey > PUBKEY_MAX_NSKEY) { err = gpg_error (GPG_ERR_BAD_SECKEY); log_error ("internal error: %s\n", gpg_strerror (err)); goto leave; } init_membuf (&mbuf, 50); put_membuf_str (&mbuf, "(skey"); if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA || pk->pubkey_algo == PUBKEY_ALGO_EDDSA || pk->pubkey_algo == PUBKEY_ALGO_ECDH) { /* The ECC case. */ char *curvestr = openpgp_oid_to_str (pk->pkey[0]); if (!curvestr) err = gpg_error_from_syserror (); else { const char *curvename = openpgp_oid_to_curve (curvestr, 1); err = gcry_sexp_build (&curve, NULL, "(curve %s)", curvename?curvename:curvestr); xfree (curvestr); if (!err) { j = 0; /* Append the public key element Q. */ put_membuf_str (&mbuf, " _ %m"); format_args[j++] = pk->pkey + 1; /* Append the secret key element D. For ECDH we skip PKEY[2] because this holds the KEK which is not needed by gpg-agent. */ i = pk->pubkey_algo == PUBKEY_ALGO_ECDH? 3 : 2; if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER1)) put_membuf_str (&mbuf, " e %m"); else put_membuf_str (&mbuf, " _ %m"); format_args[j++] = pk->pkey + i; } } } else { /* Standard case for the old (non-ECC) algorithms. */ for (i=j=0; i < nskey; i++) { if (!pk->pkey[i]) continue; /* Protected keys only have NPKEY+1 elements. */ if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER1)) put_membuf_str (&mbuf, " e %m"); else put_membuf_str (&mbuf, " _ %m"); format_args[j++] = pk->pkey + i; } } put_membuf_str (&mbuf, ")"); put_membuf (&mbuf, "", 1); if (err) xfree (get_membuf (&mbuf, NULL)); else { char *format = get_membuf (&mbuf, NULL); if (!format) err = gpg_error_from_syserror (); else err = gcry_sexp_build_array (&skey, NULL, format, format_args); xfree (format); } if (err) { log_error ("error building skey array: %s\n", gpg_strerror (err)); goto leave; } if (ski->is_protected) { char countbuf[35]; /* Note that the IVLEN may be zero if we are working on a dummy key. We can't express that in an S-expression and thus we send dummy data for the IV. */ snprintf (countbuf, sizeof countbuf, "%lu", (unsigned long)ski->s2k.count); err = gcry_sexp_build (&prot, NULL, " (protection %s %s %b %d %s %b %s)\n", ski->sha1chk? "sha1":"sum", openpgp_cipher_algo_name (ski->algo), ski->ivlen? (int)ski->ivlen:1, ski->ivlen? ski->iv: (const unsigned char*)"X", ski->s2k.mode, openpgp_md_algo_name (ski->s2k.hash_algo), (int)sizeof (ski->s2k.salt), ski->s2k.salt, countbuf); } else err = gcry_sexp_build (&prot, NULL, " (protection none)\n"); tmpsexp = NULL; xfree (transferkey); transferkey = NULL; if (!err) err = gcry_sexp_build (&tmpsexp, NULL, "(openpgp-private-key\n" " (version %d)\n" " (algo %s)\n" " %S%S\n" " (csum %d)\n" " %S)\n", pk->version, openpgp_pk_algo_name (pk->pubkey_algo), curve, skey, (int)(unsigned long)ski->csum, prot); gcry_sexp_release (skey); gcry_sexp_release (prot); if (!err) err = make_canon_sexp_pad (tmpsexp, 1, &transferkey, &transferkeylen); gcry_sexp_release (tmpsexp); if (err) { log_error ("error building transfer key: %s\n", gpg_strerror (err)); goto leave; } /* Wrap the key. */ wrappedkeylen = transferkeylen + 8; xfree (wrappedkey); wrappedkey = xtrymalloc (wrappedkeylen); if (!wrappedkey) err = gpg_error_from_syserror (); else err = gcry_cipher_encrypt (cipherhd, wrappedkey, wrappedkeylen, transferkey, transferkeylen); if (err) goto leave; xfree (transferkey); transferkey = NULL; /* Send the wrapped key to the agent. */ { char *desc = gpg_format_keydesc (pk, FORMAT_KEYDESC_IMPORT, 1); err = agent_import_key (ctrl, desc, &cache_nonce, wrappedkey, wrappedkeylen, batch); xfree (desc); } if (!err) { if (opt.verbose) log_info (_("key %s: secret key imported\n"), keystr_from_pk_with_sub (main_pk, pk)); stats->secret_imported++; } else if ( gpg_err_code (err) == GPG_ERR_EEXIST ) { if (opt.verbose) log_info (_("key %s: secret key already exists\n"), keystr_from_pk_with_sub (main_pk, pk)); err = 0; stats->secret_dups++; } else { log_error (_("key %s: error sending to agent: %s\n"), keystr_from_pk_with_sub (main_pk, pk), gpg_strerror (err)); if (gpg_err_code (err) == GPG_ERR_CANCELED || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) break; /* Don't try the other subkeys. */ } } if (!err && stub_key_skipped) /* We need to notify user how to migrate stub keys. */ err = gpg_error (GPG_ERR_NOT_PROCESSED); leave: gcry_sexp_release (curve); xfree (cache_nonce); xfree (wrappedkey); xfree (transferkey); gcry_cipher_close (cipherhd); xfree (kek); return err; } /* Walk a secret keyblock and produce a public keyblock out of it. Returns a new node or NULL on error. */ static kbnode_t sec_to_pub_keyblock (kbnode_t sec_keyblock) { kbnode_t pub_keyblock = NULL; kbnode_t ctx = NULL; kbnode_t secnode, pubnode; while ((secnode = walk_kbnode (sec_keyblock, &ctx, 0))) { if (secnode->pkt->pkttype == PKT_SECRET_KEY || secnode->pkt->pkttype == PKT_SECRET_SUBKEY) { /* Make a public key. */ PACKET *pkt; PKT_public_key *pk; pkt = xtrycalloc (1, sizeof *pkt); pk = pkt? copy_public_key (NULL, secnode->pkt->pkt.public_key): NULL; if (!pk) { xfree (pkt); release_kbnode (pub_keyblock); return NULL; } if (secnode->pkt->pkttype == PKT_SECRET_KEY) pkt->pkttype = PKT_PUBLIC_KEY; else pkt->pkttype = PKT_PUBLIC_SUBKEY; pkt->pkt.public_key = pk; pubnode = new_kbnode (pkt); } else { pubnode = clone_kbnode (secnode); } if (!pub_keyblock) 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 accidentally and thereby tampering * with the trust calculation. */ static int import_secret_one (ctrl_t ctrl, const char *fname, kbnode_t keyblock, - struct stats_s *stats, int batch, unsigned int options, + struct import_stats_s *stats, int batch, unsigned int options, int for_migration, import_screener_t screener, void *screener_arg) { PKT_public_key *pk; struct seckey_info *ski; kbnode_t node, uidnode; u32 keyid[2]; int rc = 0; int nr_prev; kbnode_t pub_keyblock; char pkstrbuf[PUBKEY_STRING_SIZE]; /* Get the key and print some info about it */ node = find_kbnode (keyblock, PKT_SECRET_KEY); if (!node) BUG (); pk = node->pkt->pkt.public_key; keyid_from_pk (pk, keyid); uidnode = find_next_kbnode (keyblock, PKT_USER_ID); if (screener && screener (keyblock, screener_arg)) { log_error (_("secret key %s: %s\n"), keystr_from_pk (pk), _("rejected by import screener")); return 0; } if (opt.verbose && !for_migration) { log_info ("sec %s/%s %s ", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk (pk), datestr_from_pk (pk)); if (uidnode) print_utf8_buffer (log_get_stream (), uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len); log_printf ("\n"); } stats->secret_read++; if ((options & IMPORT_NO_SECKEY)) { if (!for_migration) log_error (_("importing secret keys not allowed\n")); return 0; } if (!uidnode) { if (!for_migration) log_error( _("key %s: no user ID\n"), keystr_from_pk (pk)); return 0; } ski = pk->seckey_info; if (!ski) { /* Actually an internal error. */ log_error ("key %s: secret key info missing\n", keystr_from_pk (pk)); return 0; } /* A quick check to not import keys with an invalid protection cipher algorithm (only checks the primary key, though). */ if (ski->algo > 110) { if (!for_migration) log_error (_("key %s: secret key with invalid cipher %d" " - skipped\n"), keystr_from_pk (pk), ski->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); nr_prev = stats->skipped_new_keys; /* Make a public key out of the key. */ pub_keyblock = sec_to_pub_keyblock (keyblock); if (!pub_keyblock) log_error ("key %s: failed to create public key from secret key\n", keystr_from_pk (pk)); else { /* Note that this outputs an IMPORT_OK status message for the public key block, and below we will output another one for the secret keys. FIXME? */ import_one (ctrl, fname, pub_keyblock, stats, NULL, NULL, options, 1, for_migration, screener, screener_arg); /* Fixme: We should check for an invalid keyblock and cancel the secret key import in this case. */ release_kbnode (pub_keyblock); /* At least we cancel the secret key import when the public key import was skipped due to MERGE_ONLY option and a new key. */ if (stats->skipped_new_keys <= nr_prev) { /* Read the keyblock again to get the effects of a merge. */ /* Fixme: we should do this based on the fingerprint or even better let import_one return the merged keyblock. */ node = get_pubkeyblock (keyid); if (!node) log_error ("key %s: failed to re-lookup public key\n", keystr_from_pk (pk)); else { gpg_error_t err; nr_prev = stats->secret_imported; err = transfer_secret_keys (ctrl, stats, keyblock, batch); if (gpg_err_code (err) == GPG_ERR_NOT_PROCESSED) { /* TRANSLATORS: For smartcard, each private key on host has a reference (stub) to a smartcard and actual private key data is stored on the card. A single smartcard can have up to three private key data. Importing private key stub is always skipped in 2.1, and it returns GPG_ERR_NOT_PROCESSED. Instead, user should be suggested to run 'gpg --card-status', then, references to a card will be automatically created again. */ log_info (_("To migrate '%s', with each smartcard, " "run: %s\n"), "secring.gpg", "gpg --card-status"); err = 0; } if (!err) { int status = 16; if (!opt.quiet) log_info (_("key %s: secret key imported\n"), keystr_from_pk (pk)); if (stats->secret_imported > nr_prev) status |= 1; if (is_status_enabled ()) print_import_ok (pk, status); check_prefs (ctrl, node); } release_kbnode (node); } } } return rc; } /**************** * Import a revocation certificate; this is a single signature packet. */ static int -import_revoke_cert( const char *fname, kbnode_t node, struct stats_s *stats ) +import_revoke_cert (const char *fname, kbnode_t node, + struct import_stats_s *stats) { PKT_public_key *pk = NULL; kbnode_t onode; kbnode_t keyblock = NULL; KEYDB_HANDLE hd = NULL; u32 keyid[2]; int rc = 0; (void)fname; 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 (gpg_err_code (rc) == GPG_ERR_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), gpg_strerror (rc)); goto leave; } /* Read the original keyblock. */ hd = keydb_new (); if (!hd) { rc = gpg_error_from_syserror (); goto leave; } { 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), gpg_strerror (rc)); goto leave; } rc = keydb_get_keyblock (hd, &keyblock ); if (rc) { log_error (_("key %s: can't read original keyblock: %s\n"), keystr(keyid), gpg_strerror (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), gpg_strerror (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), gpg_strerror (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_t keyblock, PKT_public_key *pk, u32 *keyid, int *non_self ) { kbnode_t n, knode = NULL; PKT_signature *sig; int rc; u32 bsdate=0, rsdate=0; kbnode_t bsnode = NULL, rsnode = NULL; (void)fname; (void)pk; 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; } if ( n->pkt->pkttype != PKT_SIGNATURE ) continue; sig = n->pkt->pkt.signature; if ( keyid[0] != sig->keyid[0] || keyid[1] != sig->keyid[1] ) { *non_self = 1; continue; } /* 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_t 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 (gpg_err_code(rc) == GPG_ERR_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 (gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ? _("key %s: unsupported public key algorithm\n"): _("key %s: invalid direct key signature\n"), keystr (keyid)); n->flag |= 4; } } else if ( IS_SUBKEY_SIG (sig) ) { /* 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 (gpg_err_code (rc) == GPG_ERR_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) { /* Delete the last binding sig since this one is newer */ bsnode->flag |= 4; 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 ( IS_SUBKEY_REV (sig) ) { /* 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 (gpg_err_code (rc) == GPG_ERR_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) { /* Delete the last revocation sig since this one is newer. */ rsnode->flag |= 4; if (opt.verbose) log_info (_("key %s: removed multiple subkey" " revocation\n"),keystr(keyid)); } rsnode = n; rsdate = sig->timestamp; } else n->flag |= 4; /* older */ } } } } 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_t keyblock, u32 *keyid, unsigned int options) { kbnode_t node; int nvalid=0, uid_seen=0, subkey_seen=0; (void)fname; 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 && openpgp_pk_test_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) && !have_secret_key_with_kid (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), gpg_strerror (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_t *keyblock ) { kbnode_t uid1; int any=0; for(uid1=*keyblock;uid1;uid1=uid1->next) { kbnode_t 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_t 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_t 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_pk (uid1->pkt->pkt.public_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 (ctrl_t ctrl, kbnode_t keyblock) { kbnode_t 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 (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY || gpg_err_code (rc) == GPG_ERR_UNUSABLE_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 (ctrl, 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 (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY || gpg_err_code (rc) == GPG_ERR_UNUSABLE_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_t keyblock_orig, kbnode_t keyblock, u32 *keyid, int *n_uids, int *n_sigs, int *n_subk ) { kbnode_t 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_t 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_t 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_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; } } } /* 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 || node->pkt->pkttype == PKT_SECRET_SUBKEY) && !cmp_public_keys( onode->pkt->pkt.public_key, node->pkt->pkt.public_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_t keyblock, kbnode_t node, int *n_sigs, const char *fname, u32 *keyid ) { kbnode_t n; kbnode_t n_where = NULL; (void)fname; (void)keyid; 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_t dst, kbnode_t src, int *n_sigs, const char *fname, u32 *keyid) { kbnode_t n, n2; int found = 0; (void)fname; (void)keyid; 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_t dst, kbnode_t src, int *n_sigs, const char *fname, u32 *keyid) { kbnode_t n, n2; int found = 0; (void)fname; (void)keyid; 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_t keyblock, kbnode_t node, int *n_sigs, const char *fname, u32 *keyid) { kbnode_t n; (void)fname; (void)keyid; 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; } diff --git a/g10/keyserver.c b/g10/keyserver.c index cf671c903..bb7c10536 100644 --- a/g10/keyserver.c +++ b/g10/keyserver.c @@ -1,2083 +1,2083 @@ /* keyserver.c - generic keyserver code * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2011, 2012 Free Software Foundation, Inc. * Copyright (C) 2014 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include "gpg.h" #include "iobuf.h" #include "filter.h" #include "keydb.h" #include "status.h" #include "exec.h" #include "main.h" #include "i18n.h" #include "ttyio.h" #include "options.h" #include "packet.h" #include "trustdb.h" #include "keyserver-internal.h" #include "util.h" #include "membuf.h" #include "call-dirmngr.h" #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; }; /* Parameters for the search line handler. */ struct search_line_handler_parm_s { ctrl_t ctrl; /* The session control structure. */ char *searchstr_disp; /* Native encoded search string or NULL. */ KEYDB_SEARCH_DESC *desc; /* Array with search descriptions. */ int count; /* Number of keys we are currently prepared to handle. This is the size of the DESC array. If it is too small, it will grow safely. */ int validcount; /* Enable the "Key x-y of z" messages. */ int nkeys; /* Number of processed records. */ int any_lines; /* At least one line has been processed. */ unsigned int numlines; /* Counter for displayed lines. */ int eof_seen; /* EOF encountered. */ int not_found; /* Set if no keys have been found. */ }; 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}, /* MUST be the first in this array! */ {"http-proxy", KEYSERVER_HTTP_PROXY, NULL, /* MUST be the second! */ N_("override proxy options set for dirmngr")}, {"include-revoked",0,NULL,N_("include revoked keys in search results")}, {"include-subkeys",0,NULL,N_("include subkeys when searching by key ID")}, {"timeout", KEYSERVER_TIMEOUT, NULL, N_("override timeout options set for dirmngr")}, {"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 gpg_error_t keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc, struct keyserver_spec *override_keyserver, unsigned char **r_fpr, size_t *r_fprlen); static gpg_error_t keyserver_put (ctrl_t ctrl, strlist_t keyspecs, struct keyserver_spec *keyserver); /* Reasonable guess. The commonly used test key simon.josefsson.org is larger than 32k, thus we need at least this value. */ #define DEFAULT_MAX_CERT_SIZE 65536 static size_t max_cert_size=DEFAULT_MAX_CERT_SIZE; static void warn_kshelper_option(char *option, int noisy) { char *p; if ((p=strchr (option, '='))) *p = 0; if (!strcmp (option, "ca-cert-file")) log_info ("keyserver option '%s' is obsolete; please use " "'%s' in dirmngr.conf\n", "ca-cert-file", "hkp-cacert"); else if (!strcmp (option, "check-cert") || !strcmp (option, "broken-http-proxy")) log_info ("keyserver option '%s' is obsolete\n", option); else if (noisy || opt.verbose) log_info ("keyserver option '%s' is unknown\n", option); } /* Called from main to parse the args for --keyserver-options. */ int parse_keyserver_options(char *options) { int ret=1; char *tok; char *max_cert=NULL; keyserver_opts[0].value=&max_cert; keyserver_opts[1].value=&opt.keyserver_options.http_proxy; while((tok=optsep(&options))) { if(tok[0]=='\0') continue; /* 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. */ 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 was destined for a keyserver plugin as used by GnuPG < 2.1 */ warn_kshelper_option (tok, 1); } } 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. */ keyserver_spec_t parse_keyserver_uri (const char *string,int require_scheme) { 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))) warn_kshelper_option (tok, 0); } /* 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) { log_info ("keyserver option '%s' is obsolete\n", "x-broken-hkp"); } 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 (uri[0]=='/' && uri[1]=='/' && uri[2] == '/') { /* Three slashes means network path with a default host name. This is a hack because it does not crok all possible combiantions. We should better repalce all code bythe parser from http.c. */ keyserver->path = xstrdup (uri+2); } else 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); 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); es_printf ("(%d)\t%s ", number, iobuf_get_temp_buffer (keyrec->uidbuf)); if (keyrec->size>0) es_printf ("%d bit ", keyrec->size); if(keyrec->type) { const char *str; str = openpgp_pk_algo_name (keyrec->type); if (str && strcmp (str, "?")) es_printf ("%s ",str); else es_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: es_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: es_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: es_printf ("key "); for(i=0;i<16;i++) es_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); es_printf("key %s",keystr(kid)); } break; default: BUG(); break; } if(keyrec->createtime>0) { es_printf (", "); es_printf (_("created: %s"), strtimestamp(keyrec->createtime)); } if(keyrec->expiretime>0) { es_printf (", "); es_printf (_("expires: %s"), strtimestamp(keyrec->expiretime)); } if (keyrec->flags&1) es_printf (" (%s)", _("revoked")); if(keyrec->flags&2) es_printf (" (%s)", _("disabled")); if(keyrec->flags&4) es_printf (" (%s)", _("expired")); es_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) { /* FIXME: Remove the static and put the data into the parms we use for the caller anyway. */ 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(); } trim_trailing_ws (keystring, strlen (keystring)); if((record=strsep(&keystring,":"))==NULL) return ret; if(ascii_strcasecmp("pub",record)==0) { char *tok; gpg_error_t err; if(work->desc.mode) { ret=work; work=xmalloc_clear(sizeof(struct keyrec)); work->uidbuf=iobuf_temp(); } if((tok=strsep(&keystring,":"))==NULL) return ret; err = classify_user_id (tok, &work->desc, 1); if (err || (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; } /* Show a prompt and allow the user to select keys for retrieval. */ static gpg_error_t show_prompt (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int numdesc, int count, const char *search) { gpg_error_t err; char *answer = NULL; es_fflush (es_stdout); 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; } again: err = 0; xfree (answer); answer = cpr_get_no_help ("keysearch.prompt", _("Enter number(s), N)ext, or Q)uit > ")); /* control-d */ if (answer[0]=='\x04') { tty_printf ("Q\n"); answer[0] = 'q'; } if (answer[0]=='q' || answer[0]=='Q') err = gpg_error (GPG_ERR_CANCELED); else if (atoi (answer) >= 1 && atoi (answer) <= numdesc) { char *split = answer; char *num; int numarray[50]; int numidx = 0; int idx; while ((num = strsep (&split, " ,"))) if (atoi (num) >= 1 && atoi (num) <= numdesc) { if (numidx >= DIM (numarray)) { tty_printf ("Too many keys selected\n"); goto again; } numarray[numidx++] = atoi (num); } if (!numidx) goto again; { KEYDB_SEARCH_DESC *selarray; selarray = xtrymalloc (numidx * sizeof *selarray); if (!selarray) { err = gpg_error_from_syserror (); goto leave; } for (idx = 0; idx < numidx; idx++) selarray[idx] = desc[numarray[idx]-1]; err = keyserver_get (ctrl, selarray, numidx, NULL, NULL, NULL); xfree (selarray); } } leave: xfree (answer); return err; } /* This is a callback used by call-dirmngr.c to process the result of KS_SEARCH command. If SPECIAL is 0, LINE is the actual data line received with all escaping removed and guaranteed to be exactly one line with stripped LF; an EOF is indicated by LINE passed as NULL. If special is 1, the line contains the source of the information (usually an URL). LINE may be modified after return. */ static gpg_error_t search_line_handler (void *opaque, int special, char *line) { struct search_line_handler_parm_s *parm = opaque; gpg_error_t err = 0; struct keyrec *keyrec; if (special == 1) { log_info ("data source: %s\n", line); return 0; } else if (special) { log_debug ("unknown value %d for special search callback", special); return 0; } if (parm->eof_seen && line) { log_debug ("ooops: unexpected data after EOF\n"); line = NULL; } /* Print the received line. */ if (opt.with_colons && line) { es_printf ("%s\n", line); } /* Look for an info: line. The only current info: values defined are the version and key count. */ if (line && !parm->any_lines && !ascii_strncasecmp ("info:", line, 5)) { char *str = line + 5; char *tok; if ((tok = strsep (&str, ":"))) { 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); return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL); } } if ((tok = strsep (&str, ":")) && sscanf (tok, "%d", &parm->count) == 1) { if (!parm->count) parm->not_found = 1;/* Server indicated that no items follow. */ else if (parm->count < 0) parm->count = 10; /* Bad value - assume something reasonable. */ else parm->validcount = 1; /* COUNT seems to be okay. */ } parm->any_lines = 1; return 0; /* Line processing finished. */ } again: if (line) keyrec = parse_keyrec (line); else { /* Received EOF - flush data */ parm->eof_seen = 1; keyrec = parse_keyrec (NULL); if (!keyrec) { if (!parm->nkeys) parm->not_found = 1; /* No keys at all. */ else { if (parm->nkeys != parm->count) parm->validcount = 0; if (!(opt.with_colons && opt.batch)) { err = show_prompt (parm->ctrl, parm->desc, parm->nkeys, parm->validcount? parm->count : 0, parm->searchstr_disp); return err; } } } } /* Save the key in the key array. */ if (keyrec) { /* Allocate or enlarge the key array if needed. */ if (!parm->desc) { if (parm->count < 1) { parm->count = 10; parm->validcount = 0; } parm->desc = xtrymalloc (parm->count * sizeof *parm->desc); if (!parm->desc) { err = gpg_error_from_syserror (); iobuf_close (keyrec->uidbuf); xfree (keyrec); return err; } } else if (parm->nkeys == parm->count) { /* Keyserver sent more keys than claimed in the info: line. */ KEYDB_SEARCH_DESC *tmp; int newcount = parm->count + 10; tmp = xtryrealloc (parm->desc, newcount * sizeof *parm->desc); if (!tmp) { err = gpg_error_from_syserror (); iobuf_close (keyrec->uidbuf); xfree (keyrec); return err; } parm->count = newcount; parm->desc = tmp; parm->validcount = 0; } parm->desc[parm->nkeys] = keyrec->desc; if (!opt.with_colons) { /* SCREEN_LINES - 1 for the prompt. */ if (parm->numlines + keyrec->lines > opt.screen_lines - 1) { err = show_prompt (parm->ctrl, parm->desc, parm->nkeys, parm->validcount ? parm->count:0, parm->searchstr_disp); if (err) return err; parm->numlines = 0; } print_keyrec (parm->nkeys+1, keyrec); } parm->numlines += keyrec->lines; iobuf_close (keyrec->uidbuf); xfree (keyrec); parm->any_lines = 1; parm->nkeys++; /* If we are here due to a flush after the EOF, run again for the last prompt. Fixme: Make this code better readable. */ if (parm->eof_seen) goto again; } return 0; } int keyserver_export (ctrl_t ctrl, strlist_t users) { gpg_error_t err; strlist_t sl=NULL; KEYDB_SEARCH_DESC desc; int rc=0; /* Weed out descriptors that we don't support sending */ for(;users;users=users->next) { err = classify_user_id (users->d, &desc, 1); if (err || (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_put (ctrl, sl, opt.keyserver); free_strlist(sl); } return rc; } /* Structure to convey the arg to keyserver_retrieval_screener. */ struct ks_retrieval_screener_arg_s { KEYDB_SEARCH_DESC *desc; int ndesc; }; /* Check whether a key matches the search description. The function returns 0 if the key shall be imported. */ static gpg_error_t keyserver_retrieval_screener (kbnode_t keyblock, void *opaque) { struct ks_retrieval_screener_arg_s *arg = opaque; KEYDB_SEARCH_DESC *desc = arg->desc; int ndesc = arg->ndesc; kbnode_t node; PKT_public_key *pk; int n; u32 keyid[2]; byte fpr[MAX_FINGERPRINT_LEN]; size_t fpr_len = 0; /* Secret keys are not expected from a keyserver. We do not care about secret subkeys because the import code takes care of skipping them. Not allowing an import of a public key with a secret subkey would make it too easy to inhibit the downloading of a public key. Recall that keyservers do only limited checks. */ node = find_kbnode (keyblock, PKT_SECRET_KEY); if (node) return gpg_error (GPG_ERR_GENERAL); /* Do not import. */ if (!ndesc) return 0; /* Okay if no description given. */ /* Loop over all key packets. */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype != PKT_PUBLIC_KEY && node->pkt->pkttype != PKT_PUBLIC_SUBKEY) continue; pk = node->pkt->pkt.public_key; fingerprint_from_pk (pk, fpr, &fpr_len); keyid_from_pk (pk, keyid); /* Compare requested and returned fingerprints if available. */ for (n = 0; n < ndesc; n++) { if (desc[n].mode == KEYDB_SEARCH_MODE_FPR20) { if (fpr_len == 20 && !memcmp (fpr, desc[n].u.fpr, 20)) return 0; } else if (desc[n].mode == KEYDB_SEARCH_MODE_FPR16) { if (fpr_len == 16 && !memcmp (fpr, desc[n].u.fpr, 16)) return 0; } else if (desc[n].mode == KEYDB_SEARCH_MODE_LONG_KID) { if (keyid[0] == desc[n].u.kid[0] && keyid[1] == desc[n].u.kid[1]) return 0; } else if (desc[n].mode == KEYDB_SEARCH_MODE_SHORT_KID) { if (keyid[1] == desc[n].u.kid[1]) return 0; } else /* No keyid or fingerprint - can't check. */ return 0; /* allow import. */ } } return gpg_error (GPG_ERR_GENERAL); } int keyserver_import (ctrl_t ctrl, strlist_t users) { gpg_error_t err; 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) { err = classify_user_id (users->d, &desc[count], 1); if (err || (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_get (ctrl, desc, count, NULL, NULL, NULL); xfree(desc); return rc; } /* Import all keys that exactly match NAME */ int keyserver_import_name (ctrl_t ctrl, const char *name, unsigned char **fpr, size_t *fprlen, struct keyserver_spec *keyserver) { KEYDB_SEARCH_DESC desc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_EXACT; desc.u.name = name; return keyserver_get (ctrl, &desc, 1, keyserver, fpr, fprlen); } int keyserver_import_fprint (ctrl_t ctrl, 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_get (ctrl, &desc, 1, keyserver, NULL, NULL); } int keyserver_import_keyid (ctrl_t ctrl, 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_get (ctrl, &desc,1, keyserver, NULL, NULL); } /* code mostly stolen from do_export_stream */ static int keyidlist(strlist_t users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3) { int rc = 0; int num = 100; kbnode_t keyblock = NULL; kbnode_t node; KEYDB_HANDLE kdbhd; int ndesc; KEYDB_SEARCH_DESC *desc = NULL; strlist_t sl; *count=0; *klist=xmalloc(sizeof(KEYDB_SEARCH_DESC)*num); kdbhd = keydb_new (); if (!kdbhd) { rc = gpg_error_from_syserror (); goto leave; } keydb_disable_caching (kdbhd); /* We are looping the search. */ 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++) ; desc = xmalloc ( ndesc * sizeof *desc); for (ndesc=0, sl=users; sl; sl = sl->next) { gpg_error_t err; if (!(err = classify_user_id (sl->d, desc+ndesc, 1))) ndesc++; else log_error (_("key \"%s\" not found: %s\n"), sl->d, gpg_strerror (err)); } } for (;;) { rc = keydb_search (kdbhd, desc, ndesc, NULL); if (rc) break; /* ready. */ 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"), gpg_strerror (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; v3_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 (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; leave: if(rc) { xfree(*klist); *klist = NULL; } 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. */ gpg_error_t keyserver_refresh (ctrl_t ctrl, strlist_t users) { gpg_error_t err; int count, numdesc; int 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; err = keyidlist (users, &desc, &numdesc, fakev3); if (err) return err; count=numdesc; if(count>0) { int i; /* Try to handle preferred keyserver keys first */ for(i=0;iuri); /* We use the keyserver structure we parsed out before. Note that a preferred keyserver without a scheme:// will be interpreted as hkp:// */ err = keyserver_get (ctrl, &desc[i], 1, keyserver, NULL, NULL); if (err) log_info(_("WARNING: unable to refresh key %s" " via %s: %s\n"),keystr_from_desc(&desc[i]), keyserver->uri,gpg_strerror (err)); 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) { char *tmpuri; err = gpg_dirmngr_ks_list (ctrl, &tmpuri); if (!err) { if (!opt.quiet) { if(count==1) log_info(_("refreshing 1 key from %s\n"), tmpuri); else log_info(_("refreshing %d keys from %s\n"), count, tmpuri); } xfree (tmpuri); err = keyserver_get (ctrl, desc, numdesc, NULL, NULL, NULL); } } 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)) check_or_update_trustdb (); return err; } /* Search for keys on the keyservers. The patterns are given in the string list TOKENS. */ gpg_error_t keyserver_search (ctrl_t ctrl, strlist_t tokens) { gpg_error_t err; char *searchstr; struct search_line_handler_parm_s parm; memset (&parm, 0, sizeof parm); if (!tokens) return 0; /* Return success if no patterns are given. */ /* Write global options */ /* for(temp=opt.keyserver_options.other;temp;temp=temp->next) */ /* es_fprintf(spawn->tochild,"OPTION %s\n",temp->d); */ /* Write per-keyserver options */ /* for(temp=keyserver->options;temp;temp=temp->next) */ /* es_fprintf(spawn->tochild,"OPTION %s\n",temp->d); */ { membuf_t mb; strlist_t item; init_membuf (&mb, 1024); for (item = tokens; item; item = item->next) { if (item != tokens) put_membuf (&mb, " ", 1); put_membuf_str (&mb, item->d); } put_membuf (&mb, "", 1); /* Append Nul. */ searchstr = get_membuf (&mb, NULL); if (!searchstr) { err = gpg_error_from_syserror (); goto leave; } } /* FIXME: Enable the next line */ /* log_info (_("searching for \"%s\" from %s\n"), searchstr, keyserver->uri); */ parm.ctrl = ctrl; if (searchstr) parm.searchstr_disp = utf8_to_native (searchstr, strlen (searchstr), 0); err = gpg_dirmngr_ks_search (ctrl, searchstr, search_line_handler, &parm); if (parm.not_found) { if (parm.searchstr_disp) log_info (_("key \"%s\" not found on keyserver\n"), parm.searchstr_disp); else log_info (_("key not found on keyserver\n")); } if (gpg_err_code (err) == GPG_ERR_NO_KEYSERVER) log_error (_("no keyserver known (use option --keyserver)\n")); else if (err) log_error ("error searching keyserver: %s\n", gpg_strerror (err)); /* switch(ret) */ /* { */ /* case KEYSERVER_SCHEME_NOT_FOUND: */ /* log_error(_("no handler for keyserver scheme '%s'\n"), */ /* opt.keyserver->scheme); */ /* break; */ /* case KEYSERVER_NOT_SUPPORTED: */ /* log_error(_("action '%s' not supported with keyserver " */ /* "scheme '%s'\n"), "search", opt.keyserver->scheme); */ /* break; */ /* case KEYSERVER_TIMEOUT: */ /* log_error(_("keyserver timed out\n")); */ /* break; */ /* case KEYSERVER_INTERNAL_ERROR: */ /* default: */ /* log_error(_("keyserver internal error\n")); */ /* break; */ /* } */ /* return gpg_error (GPG_ERR_KEYSERVER); */ leave: xfree (parm.desc); xfree (parm.searchstr_disp); xfree(searchstr); return err; } /* Helper for keyserver_get. Here we only receive a chunk of the description to be processed in one batch. This is required due to the limited number of patterns the dirmngr interface (KS_GET) can grok and to limit the amount of temporary required memory. */ static gpg_error_t keyserver_get_chunk (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc, int *r_ndesc_used, - void *stats_handle, + import_stats_t stats_handle, struct keyserver_spec *override_keyserver, unsigned char **r_fpr, size_t *r_fprlen) { gpg_error_t err = 0; char **pattern; int idx, npat; estream_t datastream; char *source = NULL; size_t linelen; /* Estimated linelen for KS_GET. */ size_t n; #define MAX_KS_GET_LINELEN 950 /* Somewhat lower than the real limit. */ *r_ndesc_used = 0; /* Create an array filled with a search pattern for each key. The array is delimited by a NULL entry. */ pattern = xtrycalloc (ndesc+1, sizeof *pattern); if (!pattern) return gpg_error_from_syserror (); /* Note that we break the loop as soon as our estimation of the to be used line length reaches the limit. But we do this only if we have processed at leas one search requests so that an overlong single request will be rejected only later by gpg_dirmngr_ks_get but we are sure that R_NDESC_USED has been updated. This avoids a possible indefinite loop. */ linelen = 9; /* "KS_GET --" */ for (npat=idx=0; idx < ndesc; idx++) { int quiet = 0; if (desc[idx].mode == KEYDB_SEARCH_MODE_FPR20 || desc[idx].mode == KEYDB_SEARCH_MODE_FPR16) { n = 1+2+2*20; if (idx && linelen + n > MAX_KS_GET_LINELEN) break; /* Declare end of this chunk. */ linelen += n; pattern[npat] = xtrymalloc (n); if (!pattern[npat]) err = gpg_error_from_syserror (); else { strcpy (pattern[npat], "0x"); bin2hex (desc[idx].u.fpr, desc[idx].mode == KEYDB_SEARCH_MODE_FPR20? 20 : 16, pattern[npat]+2); npat++; } } else if(desc[idx].mode == KEYDB_SEARCH_MODE_LONG_KID) { n = 1+2+16; if (idx && linelen + n > MAX_KS_GET_LINELEN) break; /* Declare end of this chunk. */ linelen += n; pattern[npat] = xtryasprintf ("0x%08lX%08lX", (ulong)desc[idx].u.kid[0], (ulong)desc[idx].u.kid[1]); if (!pattern[npat]) err = gpg_error_from_syserror (); else npat++; } else if(desc[idx].mode == KEYDB_SEARCH_MODE_SHORT_KID) { n = 1+2+8; if (idx && linelen + n > MAX_KS_GET_LINELEN) break; /* Declare end of this chunk. */ linelen += n; pattern[npat] = xtryasprintf ("0x%08lX", (ulong)desc[idx].u.kid[1]); if (!pattern[npat]) err = gpg_error_from_syserror (); else npat++; } else if(desc[idx].mode == KEYDB_SEARCH_MODE_EXACT) { /* The Dirmngr also uses classify_user_id to detect the type of the search string. By adding the '=' prefix we force Dirmngr's KS_GET to consider this an exact search string. (In gpg 1.4 and gpg 2.0 the keyserver helpers used the KS_GETNAME command to indicate this.) */ n = 1+1+strlen (desc[idx].u.name); if (idx && linelen + n > MAX_KS_GET_LINELEN) break; /* Declare end of this chunk. */ linelen += n; pattern[npat] = strconcat ("=", desc[idx].u.name, NULL); if (!pattern[npat]) err = gpg_error_from_syserror (); else { npat++; quiet = 1; } } else if (desc[idx].mode == KEYDB_SEARCH_MODE_NONE) continue; else BUG(); if (err) { for (idx=0; idx < npat; idx++) xfree (pattern[idx]); xfree (pattern); return err; } if (!quiet && override_keyserver) { if (override_keyserver->host) log_info (_("requesting key %s from %s server %s\n"), keystr_from_desc (&desc[idx]), override_keyserver->scheme, override_keyserver->host); else log_info (_("requesting key %s from %s\n"), keystr_from_desc (&desc[idx]), override_keyserver->uri); } } /* Remember now many of search items were considered. Note that this is different from NPAT. */ *r_ndesc_used = idx; err = gpg_dirmngr_ks_get (ctrl, pattern, override_keyserver, &datastream, &source); for (idx=0; idx < npat; idx++) xfree (pattern[idx]); xfree (pattern); if (opt.verbose && source) log_info ("data source: %s\n", source); if (!err) { struct ks_retrieval_screener_arg_s screenerarg; /* FIXME: Check whether this comment should be moved to dirmngr. 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. */ screenerarg.desc = desc; screenerarg.ndesc = *r_ndesc_used; import_keys_es_stream (ctrl, datastream, stats_handle, r_fpr, r_fprlen, (opt.keyserver_options.import_options | IMPORT_NO_SECKEY), keyserver_retrieval_screener, &screenerarg); } es_fclose (datastream); xfree (source); return err; } /* Retrieve a key from a keyserver. The search pattern are in (DESC,NDESC). Allowed search modes are keyid, fingerprint, and exact searches. OVERRIDE_KEYSERVER gives an optional override keyserver. If (R_FPR,R_FPRLEN) are not NULL, they may return the fingerprint of a single imported key. */ static gpg_error_t keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc, struct keyserver_spec *override_keyserver, unsigned char **r_fpr, size_t *r_fprlen) { gpg_error_t err; - void *stats_handle; + import_stats_t stats_handle; int ndesc_used; int any_good = 0; stats_handle = import_new_stats_handle(); for (;;) { err = keyserver_get_chunk (ctrl, desc, ndesc, &ndesc_used, stats_handle, override_keyserver, r_fpr, r_fprlen); if (!err) any_good = 1; if (err || ndesc_used >= ndesc) break; /* Error or all processed. */ /* Prepare for the next chunk. */ desc += ndesc_used; ndesc -= ndesc_used; } if (any_good) import_print_stats (stats_handle); import_release_stats_handle (stats_handle); return err; } /* Send all keys specified by KEYSPECS to the KEYSERVERS. */ static gpg_error_t keyserver_put (ctrl_t ctrl, strlist_t keyspecs, struct keyserver_spec *keyserver) { gpg_error_t err; strlist_t kspec; if (!keyspecs) return 0; /* Return success if the list is empty. */ if (!opt.keyserver) { log_error (_("no keyserver known (use option --keyserver)\n")); return gpg_error (GPG_ERR_NO_KEYSERVER); } for (kspec = keyspecs; kspec; kspec = kspec->next) { void *data; size_t datalen; kbnode_t keyblock; err = export_pubkey_buffer (ctrl, kspec->d, opt.keyserver_options.export_options, NULL, &keyblock, &data, &datalen); if (err) log_error (_("skipped \"%s\": %s\n"), kspec->d, gpg_strerror (err)); else { if (keyserver->host) log_info (_("sending key %s to %s server %s\n"), keystr (keyblock->pkt->pkt.public_key->keyid), keyserver->scheme, keyserver->host); else log_info (_("sending key %s to %s\n"), keystr (keyblock->pkt->pkt.public_key->keyid), keyserver->uri); err = gpg_dirmngr_ks_put (ctrl, data, datalen, keyblock); release_kbnode (keyblock); xfree (data); if (err) { write_status_error ("keyserver_send", err); log_error (_("keyserver send failed: %s\n"), gpg_strerror (err)); } } } return err; } /* Loop over all URLs in STRLIST and fetch the key at that URL. Note that the fetch operation ignores the configured key servers and instead directly retrieves the keys. */ int keyserver_fetch (ctrl_t ctrl, strlist_t urilist) { gpg_error_t err; strlist_t sl; estream_t datastream; unsigned int save_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; for (sl=urilist; sl; sl=sl->next) { if (!opt.quiet) log_info (_("requesting key from '%s'\n"), sl->d); err = gpg_dirmngr_ks_fetch (ctrl, sl->d, &datastream); if (!err) { - void *stats_handle; + import_stats_t stats_handle; stats_handle = import_new_stats_handle(); import_keys_es_stream (ctrl, datastream, stats_handle, NULL, NULL, opt.keyserver_options.import_options, NULL, NULL); import_print_stats (stats_handle); import_release_stats_handle (stats_handle); } else log_info (_("WARNING: unable to fetch URI %s: %s\n"), sl->d, gpg_strerror (err)); es_fclose (datastream); } opt.keyserver_options.import_options = save_options; /* If the original options didn't have fast import, and the trustdb is dirty, rebuild. */ if (!(opt.keyserver_options.import_options&IMPORT_FAST)) check_or_update_trustdb (); return 0; } /* Import key in a CERT or pointed to by a CERT. In DANE_MODE fetch the certificate using the DANE method. */ int keyserver_import_cert (ctrl_t ctrl, const char *name, int dane_mode, unsigned char **fpr,size_t *fpr_len) { gpg_error_t err; char *look,*url; estream_t key; look = xstrdup(name); if (!dane_mode) { char *domain = strrchr (look,'@'); if (domain) *domain='.'; } err = gpg_dirmngr_dns_cert (ctrl, look, dane_mode? NULL : "*", &key, fpr, fpr_len, &url); if (err) ; else if (key) { int armor_status=opt.no_armor; /* CERTs and DANE records are always in binary format */ opt.no_armor=1; err = import_keys_es_stream (ctrl, key, NULL, fpr, fpr_len, (opt.keyserver_options.import_options | IMPORT_NO_SECKEY), NULL, NULL); opt.no_armor=armor_status; es_fclose (key); key = NULL; } else if (*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); if(spec) { err = keyserver_import_fprint (ctrl, *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 */ err = keyserver_import_fprint (ctrl, *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 err; } /* Import key pointed to by a PKA record. Return the requested fingerprint in fpr. */ gpg_error_t keyserver_import_pka (ctrl_t ctrl, const char *name, unsigned char **fpr, size_t *fpr_len) { gpg_error_t err; char *url; err = gpg_dirmngr_get_pka (ctrl, name, fpr, fpr_len, &url); if (url && *url && fpr && fpr_len) { /* An URL is available. Lookup the key. */ struct keyserver_spec *spec; spec = parse_keyserver_uri (url, 1); if (spec) { err = keyserver_import_fprint (ctrl, *fpr, *fpr_len, spec); free_keyserver_spec (spec); } } xfree (url); if (err) { xfree(*fpr); *fpr = NULL; *fpr_len = 0; } return err; } /* Import a key by name using LDAP */ int keyserver_import_ldap (ctrl_t ctrl, const char *name, unsigned char **fpr, size_t *fprlen) { (void)ctrl; (void)name; (void)fpr; (void)fprlen; return gpg_error (GPG_ERR_NOT_IMPLEMENTED); /*FIXME*/ #if 0 char *domain; struct keyserver_spec *keyserver; strlist_t 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 GPG_ERR_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); FIXME("network related - move to dirmngr or drop the code"); 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 = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /*FIXME*/ /* keyserver_work (ctrl, KS_GETNAME, list, NULL, */ /* 0, fpr, fpr_len, keyserver); */ free_strlist(list); free_keyserver_spec(keyserver); return rc; #endif } diff --git a/g10/main.h b/g10/main.h index 0c2d1a940..e9f557df0 100644 --- a/g10/main.h +++ b/g10/main.h @@ -1,433 +1,436 @@ /* main.h * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2008, 2009, 2010 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 "keydb.h" #include "util.h" /* It could be argued that the default cipher should be 3DES rather than AES128, 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. */ #if GPG_USE_AES128 # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_AES #elif GPG_USE_CAST5 # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_CAST5 #else # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_3DES #endif #define DEFAULT_DIGEST_ALGO ((GNUPG)? DIGEST_ALGO_SHA256:DIGEST_ALGO_SHA1) #define DEFAULT_S2K_DIGEST_ALGO DIGEST_ALGO_SHA1 #ifdef HAVE_ZIP # define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_ZIP #else # define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_NONE #endif #define S2K_DIGEST_ALGO (opt.s2k_digest_algo?opt.s2k_digest_algo:DEFAULT_S2K_DIGEST_ALGO) /* Various data objects. */ 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_t values; struct groupitem *next; }; struct weakhash { enum gcry_md_algos algo; int rejection_shown; struct weakhash *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 (pubkey_algo_t algo); void print_cipher_algo_note (cipher_algo_t algo); void print_digest_algo_note (digest_algo_t algo); void print_digest_rejected_note (enum gcry_md_algos algo); void additional_weak_digest (const char* digestname); /*-- 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( gcry_mpi_t a ); u32 buffer_to_u32( const byte *buffer ); const byte *get_session_marker( size_t *rlen ); enum gcry_cipher_algos map_cipher_openpgp_to_gcry (cipher_algo_t algo); #define openpgp_cipher_open(_a,_b,_c,_d) \ gcry_cipher_open((_a),map_cipher_openpgp_to_gcry((_b)),(_c),(_d)) #define openpgp_cipher_get_algo_keylen(_a) \ gcry_cipher_get_algo_keylen(map_cipher_openpgp_to_gcry((_a))) #define openpgp_cipher_get_algo_blklen(_a) \ gcry_cipher_get_algo_blklen(map_cipher_openpgp_to_gcry((_a))) int openpgp_cipher_blocklen (cipher_algo_t algo); int openpgp_cipher_test_algo(cipher_algo_t algo); const char *openpgp_cipher_algo_name (cipher_algo_t algo); pubkey_algo_t map_pk_gcry_to_openpgp (enum gcry_pk_algos algo); int openpgp_pk_test_algo (pubkey_algo_t algo); int openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use); int openpgp_pk_algo_usage ( int algo ); const char *openpgp_pk_algo_name (pubkey_algo_t algo); enum gcry_md_algos map_md_openpgp_to_gcry (digest_algo_t algo); int openpgp_md_test_algo (digest_algo_t algo); const char *openpgp_md_algo_name (int algo); struct expando_args { PKT_public_key *pk; PKT_public_key *pksk; byte imagetype; int validity_info; const char *validity_string; const byte *namehash; }; 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); void obsolete_scdaemon_option (const char *configname, unsigned int configlineno, const char *name); int string_to_cipher_algo (const char *string); int string_to_digest_algo (const char *string); 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); const char *get_libexecdir (void); int path_access(const char *file,int mode); int pubkey_get_npkey (pubkey_algo_t algo); int pubkey_get_nskey (pubkey_algo_t algo); int pubkey_get_nsig (pubkey_algo_t algo); int pubkey_get_nenc (pubkey_algo_t algo); /* Temporary helpers. */ unsigned int pubkey_nbits( int algo, gcry_mpi_t *pkey ); int mpi_print (estream_t stream, gcry_mpi_t a, int mode); unsigned int ecdsa_qbits_from_Q (unsigned int qbits); /*-- cpr.c --*/ void set_status_fd ( int fd ); int is_status_enabled ( void ); void write_status ( int no ); void write_status_error (const char *where, gpg_error_t err); void write_status_errcode (const char *where, int errcode); void write_status_failure (const char *where, gpg_error_t err); void write_status_text ( int no, const char *text ); void write_status_strings (int no, const char *text, ...) GPGRT_ATTR_SENTINEL(0); void write_status_buffer ( int no, const char *buffer, size_t len, int wrap ); void write_status_text_and_buffer ( int no, const char *text, const char *buffer, size_t len, int wrap ); void write_status_begin_signing (gcry_md_hd_t md); int cpr_enabled(void); char *cpr_get( const char *keyword, const char *prompt ); char *cpr_get_no_help( const char *keyword, const char *prompt ); char *cpr_get_utf8( const char *keyword, const char *prompt ); char *cpr_get_hidden( const char *keyword, const char *prompt ); void cpr_kill_prompt(void); int cpr_get_answer_is_yes_def (const char *keyword, const char *prompt, int def_yes); int cpr_get_answer_is_yes( const char *keyword, const char *prompt ); int cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt ); int cpr_get_answer_okay_cancel (const char *keyword, const char *prompt, int def_answer); /*-- helptext.c --*/ void display_online_help( const char *keyword ); /*-- encode.c --*/ int setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek); int use_mdc (pk_list_t pk_list,int algo); int encrypt_symmetric (const char *filename ); int encrypt_store (const char *filename ); int encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, strlist_t remusr, int use_symkey, pk_list_t provided_keys, int outputfd); void encrypt_crypt_files (ctrl_t ctrl, int nfiles, char **files, strlist_t remusr); int encrypt_filter (void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len); /*-- sign.c --*/ int complete_sig (PKT_signature *sig, PKT_public_key *pksk, gcry_md_hd_t md, const char *cache_nonce); int sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, int do_encrypt, strlist_t remusr, const char *outfile ); int clearsign_file (ctrl_t ctrl, const char *fname, strlist_t locusr, const char *outfile); int sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr); /*-- sig-check.c --*/ /* SIG is a revocation signature. Check if any of PK's designated revokers generated it. If so, return 0. Note: this function (correctly) doesn't care if the designated revoker is revoked. */ int check_revocation_keys (PKT_public_key *pk, PKT_signature *sig); /* Check that the backsig BACKSIG from the subkey SUB_PK to its primary key MAIN_PK is valid. */ int check_backsig(PKT_public_key *main_pk,PKT_public_key *sub_pk, PKT_signature *backsig); /* Check that the signature SIG over a key (e.g., a key binding or a key revocation) is valid. (To check signatures over data, use check_signature.) */ int check_key_signature( KBNODE root, KBNODE sig, int *is_selfsig ); /* Like check_key_signature, but with the ability to specify some additional parameters and get back additional information. See the documentation for the implementation for details. */ 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 --*/ gpg_error_t delete_keys (strlist_t names, int secret, int allow_both); /*-- keyedit.c --*/ void keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, strlist_t commands, int quiet, int seckey_check ); void keyedit_passwd (ctrl_t ctrl, const char *username); void keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid); void keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids, strlist_t locusr, int local); void show_basic_key_info (KBNODE keyblock); /*-- keygen.c --*/ u32 parse_expire_string(const char *string); u32 ask_expire_interval(int object,const char *def_expire); u32 ask_expiredate(void); void quick_generate_keypair (ctrl_t ctrl, const char *uid); void generate_keypair (ctrl_t ctrl, int full, const char *fname, const char *card_serialno, int card_backup_key); 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); gpg_error_t make_backsig (PKT_signature *sig, PKT_public_key *pk, PKT_public_key *sub_pk, PKT_public_key *sub_psk, u32 timestamp, const char *cache_nonce); gpg_error_t generate_subkeypair (ctrl_t ctrl, kbnode_t pub_keyblock); #ifdef ENABLE_CARD_SUPPORT gpg_error_t generate_card_subkeypair (kbnode_t pub_keyblock, int keyno, const char *serialno); int save_unprotected_key_to_card (PKT_public_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 (int inp_fd, const char *iname, int mode, int restrictedperm, iobuf_t *a); char *get_matching_datafile (const char *sigfilename); iobuf_t open_sigfile (const char *sigfilename, progress_filter_context_t *pfx); void try_make_homedir( const char *fname ); char *get_openpgp_revocdir (const char *home); /*-- seskey.c --*/ void make_session_key( DEK *dek ); gcry_mpi_t encode_session_key( int openpgp_pk_algo, DEK *dek, unsigned nbits ); gcry_mpi_t encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo ); /*-- import.c --*/ +struct import_stats_s; +typedef struct import_stats_s *import_stats_t; typedef gpg_error_t (*import_screener_t)(kbnode_t keyblock, void *arg); int parse_import_options(char *str,unsigned int *options,int noisy); void import_keys (ctrl_t ctrl, char **fnames, int nnames, - void *stats_hd, unsigned int options); -int import_keys_stream (ctrl_t ctrl, iobuf_t inp, void *stats_hd, + import_stats_t stats_hd, unsigned int options); +int import_keys_stream (ctrl_t ctrl, iobuf_t inp, import_stats_t stats_hd, unsigned char **fpr, size_t *fpr_len, unsigned int options); -int import_keys_es_stream (ctrl_t ctrl, estream_t fp, void *stats_handle, +int import_keys_es_stream (ctrl_t ctrl, estream_t fp, + import_stats_t stats_handle, unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg); gpg_error_t import_old_secring (ctrl_t ctrl, const char *fname); -void *import_new_stats_handle (void); -void import_release_stats_handle (void *p); -void import_print_stats (void *hd); +import_stats_t import_new_stats_handle (void); +void import_release_stats_handle (import_stats_t hd); +void import_print_stats (import_stats_t hd); int collapse_uids( KBNODE *keyblock ); /*-- export.c --*/ struct export_stats_s; typedef struct export_stats_s *export_stats_t; export_stats_t export_new_stats (void); void export_release_stats (export_stats_t stats); void export_print_stats (export_stats_t stats); int parse_export_options(char *str,unsigned int *options,int noisy); int export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats); int export_seckeys (ctrl_t ctrl, strlist_t users, export_stats_t stats); int export_secsubkeys (ctrl_t ctrl, strlist_t users, export_stats_t stats); gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options, export_stats_t stats, kbnode_t *r_keyblock, void **r_data, size_t *r_datalen); /*-- dearmor.c --*/ int dearmor_file( const char *fname ); int enarmor_file( const char *fname ); /*-- revoke.c --*/ struct revocation_reason_info; int gen_standard_revoke (PKT_public_key *psk, const char *cache_nonce); int gen_revoke( const char *uname ); int gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t 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 (ctrl_t ctrl, strlist_t list, int locate_mode ); void secret_key_list (ctrl_t ctrl, strlist_t list ); void print_subpackets_colon(PKT_signature *sig); void reorder_keyblock (KBNODE keyblock); void list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret, int fpr); void print_fingerprint (estream_t fp, PKT_public_key *pk, int mode); void print_revokers (estream_t fp, 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); void set_attrib_fd(int fd); char *format_seckey_info (PKT_public_key *pk); void print_seckey_info (PKT_public_key *pk); void print_pubkey_info (estream_t fp, PKT_public_key *pk); void print_card_key_info (estream_t fp, KBNODE keyblock); /*-- verify.c --*/ void print_file_status( int status, const char *name, int what ); int verify_signatures (ctrl_t ctrl, int nfiles, char **files ); int verify_files (ctrl_t ctrl, int nfiles, char **files ); int gpg_verify (ctrl_t ctrl, int sig_fd, int data_fd, estream_t out_fp); /*-- decrypt.c --*/ int decrypt_message (ctrl_t ctrl, const char *filename ); gpg_error_t decrypt_message_fd (ctrl_t ctrl, int input_fd, int output_fd); void decrypt_messages (ctrl_t ctrl, int nfiles, char *files[]); /*-- plaintext.c --*/ int hash_datafiles( gcry_md_hd_t md, gcry_md_hd_t md2, strlist_t files, const char *sigfilename, int textmode); int hash_datafile_by_fd ( gcry_md_hd_t md, gcry_md_hd_t md2, int data_fd, int textmode ); PKT_plaintext *setup_plaintext_name(const char *filename,IOBUF iobuf); /*-- server.c --*/ int gpg_server (ctrl_t); gpg_error_t gpg_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line); #ifdef ENABLE_CARD_SUPPORT /*-- card-util.c --*/ void change_pin (int no, int allow_admin); void card_status (estream_t fp, char *serialno, size_t serialnobuflen); void card_edit (ctrl_t ctrl, strlist_t commands); gpg_error_t card_generate_subkey (KBNODE pub_keyblock); int card_store_subkey (KBNODE node, int use); #endif #define S2K_DECODE_COUNT(_val) ((16ul + ((_val) & 15)) << (((_val) >> 4) + 6)) /*-- migrate.c --*/ void migrate_secring (ctrl_t ctrl); #endif /*G10_MAIN_H*/