diff options
author | Werner Koch <[email protected]> | 2010-08-31 15:58:39 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2010-08-31 15:58:39 +0000 |
commit | 87fac9911241310a4b601e126fa2e26b10bd370f (patch) | |
tree | 49e09cc881b44a1dba0e9474040cda1d5f9ce581 /g10 | |
parent | Fix for W32. (diff) | |
download | gnupg-87fac9911241310a4b601e126fa2e26b10bd370f.tar.gz gnupg-87fac9911241310a4b601e126fa2e26b10bd370f.zip |
Import OpenPGP keys into the agent.
Diffstat (limited to 'g10')
-rw-r--r-- | g10/ChangeLog | 29 | ||||
-rw-r--r-- | g10/call-agent.c | 101 | ||||
-rw-r--r-- | g10/call-agent.h | 8 | ||||
-rw-r--r-- | g10/gpg.c | 51 | ||||
-rw-r--r-- | g10/import.c | 568 | ||||
-rw-r--r-- | g10/keydb.h | 5 | ||||
-rw-r--r-- | g10/keyid.c | 67 | ||||
-rw-r--r-- | g10/keyserver.c | 12 | ||||
-rw-r--r-- | g10/main.h | 11 | ||||
-rw-r--r-- | g10/misc.c | 35 | ||||
-rw-r--r-- | g10/options.h | 1 | ||||
-rw-r--r-- | g10/pkglue.c | 38 |
12 files changed, 672 insertions, 254 deletions
diff --git a/g10/ChangeLog b/g10/ChangeLog index eb19158b0..abfa6f7af 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,32 @@ +2010-08-30 Werner Koch <[email protected]> + + * keyid.c (KEYID_STR_SIZE): New + (keystr): Use snprintf and new macro. + (keystr_with_sub): New. + (keystr_from_sk_with_sub): New. + (keystr_from_pk_with_sub): New. + +2010-08-27 Werner Koch <[email protected]> + + * gpg.c (main): Change scope of CTRL to the entire function. + + * import.c (import_secret_one, import, import_keys_internal) + (import_keys, import_keys_stream): Add arg CTRL. + * call-agent.c (agent_keywrap_key): New. + (agent_import_key, inq_import_key_parms): New. + +2010-08-26 Werner Koch <[email protected]> + + * misc.c (openpgp_pk_algo_name): New. + (openpgp_md_algo_name): New. + +2010-08-24 Werner Koch <[email protected]> + + * options.h (IMPORT_SK2PK): Remove. + * import.c (parse_import_options): Turn convert-sk-to-pk into a + dummy option. + (sec_to_pub_keyblock): Use modern functions. + 2010-08-16 Werner Koch <[email protected]> * gpg.c (list_config, gpgconf_list): Use es_printf. diff --git a/g10/call-agent.c b/g10/call-agent.c index ea81c6b9e..7f98cfba9 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -77,6 +77,13 @@ struct genkey_parm_s const char *keyparms; }; +struct import_key_parm_s +{ + ctrl_t ctrl; + assuan_context_t ctx; + const void *key; + size_t keylen; +}; static gpg_error_t learn_status_cb (void *opaque, const char *line); @@ -1706,3 +1713,97 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, *r_buf = buf; return 0; } + + + +/* Retrieve a key encryption key from the agent. With FOREXPORT true + the key shall be used for export, with false for import. On success + the new key is stored at R_KEY and its length at R_KEKLEN. */ +gpg_error_t +agent_keywrap_key (ctrl_t ctrl, int forexport, void **r_kek, size_t *r_keklen) +{ + gpg_error_t err; + membuf_t data; + size_t len; + unsigned char *buf; + char line[ASSUAN_LINELENGTH]; + + *r_kek = NULL; + err = start_agent (ctrl, 0); + if (err) + return err; + + snprintf (line, DIM(line)-1, "KEYWRAP_KEY %s", + forexport? "--export":"--import"); + + init_membuf_secure (&data, 64); + err = assuan_transact (agent_ctx, line, + membuf_data_cb, &data, + default_inq_cb, ctrl, NULL, NULL); + if (err) + { + xfree (get_membuf (&data, &len)); + return err; + } + buf = get_membuf (&data, &len); + if (!buf) + return gpg_error_from_syserror (); + *r_kek = buf; + *r_keklen = len; + return 0; +} + + + +/* Handle the inquiry for an IMPORT_KEY command. */ +static gpg_error_t +inq_import_key_parms (void *opaque, const char *line) +{ + struct import_key_parm_s *parm = opaque; + gpg_error_t err; + + if (!strncmp (line, "KEYDATA", 7) && (line[7]==' '||!line[7])) + { + err = assuan_send_data (parm->ctx, parm->key, parm->keylen); + } + else + err = default_inq_cb (parm->ctrl, line); + + return err; +} + + +/* Call the agent to import a key into the agent. */ +gpg_error_t +agent_import_key (ctrl_t ctrl, const char *desc, const void *key, size_t keylen) +{ + gpg_error_t err; + struct import_key_parm_s parm; + + err = start_agent (ctrl, 0); + if (err) + return err; + + if (desc) + { + char line[ASSUAN_LINELENGTH]; + + snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc); + line[DIM(line)-1] = 0; + err = assuan_transact (agent_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + if (err) + return err; + } + + parm.ctrl = ctrl; + parm.ctx = agent_ctx; + parm.key = key; + parm.keylen = keylen; + + err = assuan_transact (agent_ctx, "IMPORT_KEY", + NULL, NULL, inq_import_key_parms, &parm, NULL, NULL); + return err; +} + + diff --git a/g10/call-agent.h b/g10/call-agent.h index c8e920855..7495b2ac6 100644 --- a/g10/call-agent.h +++ b/g10/call-agent.h @@ -163,6 +163,14 @@ gpg_error_t agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, gcry_sexp_t s_ciphertext, unsigned char **r_buf, size_t *r_buflen); +/* Retrieve a key encryption key. */ +gpg_error_t agent_keywrap_key (ctrl_t ctrl, int forexport, + void **r_kek, size_t *r_keklen); + +/* Send a key to the agent. */ +gpg_error_t agent_import_key (ctrl_t ctrl, const char *desc, + const void *key, size_t keylen); + #endif /*GNUPG_G10_CALL_AGENT_H*/ @@ -1925,6 +1925,7 @@ main (int argc, char **argv) int any_explicit_recipient = 0; int require_secmem=0,got_secmem=0; struct assuan_malloc_hooks malloc_hooks; + ctrl_t ctrl; #ifdef __riscos__ opt.lock_once = 1; @@ -1984,23 +1985,24 @@ main (int argc, char **argv) opt.pgp2_workarounds = 1; opt.escape_from = 1; opt.flags.require_cross_cert = 1; - opt.import_options=IMPORT_SK2PK; - opt.export_options=EXPORT_ATTRIBUTES; - opt.keyserver_options.import_options=IMPORT_REPAIR_PKS_SUBKEY_BUG; - opt.keyserver_options.export_options=EXPORT_ATTRIBUTES; - opt.keyserver_options.options= - KEYSERVER_HONOR_KEYSERVER_URL|KEYSERVER_HONOR_PKA_RECORD; - opt.verify_options= - VERIFY_SHOW_POLICY_URLS|VERIFY_SHOW_STD_NOTATIONS|VERIFY_SHOW_KEYSERVER_URLS; - opt.trust_model=TM_AUTO; - opt.mangle_dos_filenames=0; - opt.min_cert_level=2; - set_screen_dimensions(); - opt.keyid_format=KF_SHORT; - opt.def_sig_expire="0"; - opt.def_cert_expire="0"; - set_homedir ( default_homedir () ); - opt.passphrase_repeat=1; + opt.import_options = 0; + opt.export_options = EXPORT_ATTRIBUTES; + opt.keyserver_options.import_options = IMPORT_REPAIR_PKS_SUBKEY_BUG; + opt.keyserver_options.export_options = EXPORT_ATTRIBUTES; + opt.keyserver_options.options = (KEYSERVER_HONOR_KEYSERVER_URL + | KEYSERVER_HONOR_PKA_RECORD ); + opt.verify_options = (VERIFY_SHOW_POLICY_URLS + | VERIFY_SHOW_STD_NOTATIONS + | VERIFY_SHOW_KEYSERVER_URLS); + opt.trust_model = TM_AUTO; + opt.mangle_dos_filenames = 0; + opt.min_cert_level = 2; + set_screen_dimensions (); + opt.keyid_format = KF_SHORT; + opt.def_sig_expire = "0"; + opt.def_cert_expire = "0"; + set_homedir (default_homedir ()); + opt.passphrase_repeat = 1; /* Check whether we have a config file on the command line. */ orig_argc = argc; @@ -3403,6 +3405,9 @@ main (int argc, char **argv) if(fname && utf8_strings) opt.flags.utf8_filename=1; + ctrl = xtrycalloc (1, sizeof *ctrl); + gpg_init_default_ctrl (ctrl); + switch( cmd ) { case aPrimegen: case aPrintMD: @@ -3438,13 +3443,7 @@ main (int argc, char **argv) switch( cmd ) { case aServer: - { - ctrl_t ctrl = xtrycalloc (1, sizeof *ctrl); - gpg_init_default_ctrl (ctrl); - gpg_server (ctrl); - gpg_deinit_default_ctrl (ctrl); - xfree (ctrl); - } + gpg_server (ctrl); break; case aStore: /* only store the file */ @@ -3704,7 +3703,7 @@ main (int argc, char **argv) case aFastImport: opt.import_options |= IMPORT_FAST; case aImport: - import_keys( argc? argv:NULL, argc, NULL, opt.import_options ); + import_keys (ctrl, argc? argv:NULL, argc, NULL, opt.import_options); break; /* TODO: There are a number of command that use this same @@ -4055,6 +4054,8 @@ main (int argc, char **argv) } /* cleanup */ + gpg_deinit_default_ctrl (ctrl); + xfree (ctrl); release_armor_context (afx); FREE_STRLIST(remusr); FREE_STRLIST(locusr); diff --git a/g10/import.c b/g10/import.c index 53349ee8d..13773da25 100644 --- a/g10/import.c +++ b/g10/import.c @@ -1,6 +1,6 @@ /* import.c - import a key into our key storage. * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, - * 2007 Free Software Foundation, Inc. + * 2007, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -37,6 +37,8 @@ #include "ttyio.h" #include "status.h" #include "keyserver-internal.h" +#include "call-agent.h" +#include "../common/membuf.h" struct stats_s { ulong count; @@ -58,14 +60,15 @@ struct stats_s { }; -static int import( IOBUF inp, const char* fname,struct stats_s *stats, - unsigned char **fpr,size_t *fpr_len,unsigned int options ); +static int import (ctrl_t ctrl, + IOBUF inp, const char* fname, struct stats_s *stats, + unsigned char **fpr, size_t *fpr_len, unsigned int options); static int read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root ); static void revocation_present(KBNODE keyblock); static int import_one(const char *fname, KBNODE keyblock,struct stats_s *stats, unsigned char **fpr,size_t *fpr_len, unsigned int options,int from_sk); -static int import_secret_one( const char *fname, KBNODE keyblock, +static int import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, struct stats_s *stats, unsigned int options); static int import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats); @@ -96,8 +99,6 @@ parse_import_options(char *str,unsigned int *options,int noisy) N_("repair damage from the pks keyserver during import")}, {"fast-import",IMPORT_FAST,NULL, N_("do not update the trustdb after import")}, - {"convert-sk-to-pk",IMPORT_SK2PK,NULL, - N_("create a public key when importing a secret key")}, {"merge-only",IMPORT_MERGE_ONLY,NULL, N_("only accept updates to existing keys")}, {"import-clean",IMPORT_CLEAN,NULL, @@ -111,6 +112,7 @@ parse_import_options(char *str,unsigned int *options,int noisy) {"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}, {NULL,0,NULL,NULL} }; @@ -161,7 +163,7 @@ import_release_stats_handle (void *p) * */ static int -import_keys_internal( IOBUF inp, char **fnames, int nnames, +import_keys_internal (ctrl_t ctrl, iobuf_t inp, char **fnames, int nnames, void *stats_handle, unsigned char **fpr, size_t *fpr_len, unsigned int options ) { @@ -172,7 +174,7 @@ import_keys_internal( IOBUF inp, char **fnames, int nnames, stats = import_new_stats_handle (); if (inp) { - rc = import( inp, "[stream]", stats, fpr, fpr_len, options); + rc = import (ctrl, inp, "[stream]", stats, fpr, fpr_len, options); } else { if( !fnames && !nnames ) @@ -193,7 +195,7 @@ import_keys_internal( IOBUF inp, char **fnames, int nnames, log_error(_("can't open `%s': %s\n"), fname, strerror(errno) ); else { - rc = import( inp2, fname, stats, fpr, fpr_len, options ); + rc = import (ctrl, inp2, fname, stats, fpr, fpr_len, options); iobuf_close(inp2); /* Must invalidate that ugly cache to actually close it. */ iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, @@ -224,21 +226,23 @@ import_keys_internal( IOBUF inp, char **fnames, int nnames, } void -import_keys( char **fnames, int nnames, +import_keys (ctrl_t ctrl, char **fnames, int nnames, void *stats_handle, unsigned int options ) { - import_keys_internal(NULL,fnames,nnames,stats_handle,NULL,NULL,options); + import_keys_internal (ctrl, NULL, fnames, nnames, stats_handle, + NULL, NULL, options); } int -import_keys_stream( IOBUF inp, void *stats_handle, - unsigned char **fpr, size_t *fpr_len,unsigned int options ) +import_keys_stream (ctrl_t ctrl, IOBUF inp, void *stats_handle, + unsigned char **fpr, size_t *fpr_len,unsigned int options) { - return import_keys_internal(inp,NULL,0,stats_handle,fpr,fpr_len,options); + return import_keys_internal (ctrl, inp, NULL, 0, stats_handle, + fpr, fpr_len, options); } static int -import( IOBUF inp, const char* fname,struct stats_s *stats, +import (ctrl_t ctrl, IOBUF inp, const char* fname,struct stats_s *stats, unsigned char **fpr,size_t *fpr_len,unsigned int options ) { PACKET *pending_pkt = NULL; @@ -262,7 +266,7 @@ import( IOBUF inp, const char* fname,struct stats_s *stats, if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY ) rc = import_one( fname, keyblock, stats, fpr, fpr_len, options, 0); else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) - rc = import_secret_one( fname, keyblock, stats, options ); + rc = import_secret_one (ctrl, fname, keyblock, stats, options); else if( keyblock->pkt->pkttype == PKT_SIGNATURE && keyblock->pkt->pkt.signature->sig_class == 0x20 ) rc = import_revoke_cert( fname, keyblock, stats ); @@ -528,7 +532,7 @@ fix_pks_corruption(KBNODE keyblock) equal. Although direct key signatures are now checked during import, there might still be bogus signatures sitting in a keyring. We need to detect and delete them before doing a merge. This - fucntion returns the number of removed sigs. */ + function returns the number of removed sigs. */ static int fix_bad_direct_key_sigs (kbnode_t keyblock, u32 *keyid) { @@ -1076,66 +1080,298 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats, return rc; } -/* Walk a secret keyblock and produce a public keyblock out of it. */ -static KBNODE -sec_to_pub_keyblock(KBNODE sec_keyblock) + +/* Transfer all the secret keys in SEC_KEYBLOCK to the gpg-agent. The + function prints diagnostics and returns an error code. */ +static gpg_error_t +transfer_secret_keys (ctrl_t ctrl, kbnode_t sec_keyblock) { - KBNODE secnode,pub_keyblock=NULL,ctx=NULL; + gpg_error_t err = 0; + void *kek = NULL; + size_t keklen; + kbnode_t ctx = NULL; + kbnode_t node; + PKT_secret_key *main_sk, *sk; + int nskey; + membuf_t mbuf; + int i, j; + size_t n; + void *format_args_buf_ptr[PUBKEY_MAX_NSKEY]; + int format_args_buf_int[PUBKEY_MAX_NSKEY]; + void *format_args[2*PUBKEY_MAX_NSKEY]; + gcry_sexp_t skey, prot, tmpsexp; + unsigned char *transferkey = NULL; + size_t transferkeylen; + gcry_cipher_hd_t cipherhd = NULL; + unsigned char *wrappedkey = NULL; + size_t wrappedkeylen; + + /* 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; + } - while((secnode=walk_kbnode(sec_keyblock,&ctx,0))) + /* 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_sk = NULL; + while ((node = walk_kbnode (sec_keyblock, &ctx, 0))) { - KBNODE pubnode; + if (node->pkt->pkttype != PKT_SECRET_KEY + && node->pkt->pkttype != PKT_SECRET_SUBKEY) + continue; + sk = node->pkt->pkt.secret_key; + if (!main_sk) + main_sk = sk; + + /* Convert our internal secret key object into an S-expression. */ + nskey = pubkey_get_nskey (sk->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"); + for (i=j=0; i < nskey; i++) + { + if (gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE)) + { + put_membuf_str (&mbuf, " e %b"); + format_args_buf_ptr[i] = gcry_mpi_get_opaque (sk->skey[i], &n); + format_args_buf_int[i] = (n+7)/8; + format_args[j++] = format_args_buf_int + i; + format_args[j++] = format_args_buf_ptr + i; + } + else + { + put_membuf_str (&mbuf, " _ %m"); + format_args[j++] = sk->skey + i; + } + } + put_membuf_str (&mbuf, ")\n"); + put_membuf (&mbuf, "", 1); + { + 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 (sk->is_protected) + { + char countbuf[35]; + + snprintf (countbuf, sizeof countbuf, "%lu", + (unsigned long)sk->protect.s2k.count); + err = gcry_sexp_build + (&prot, NULL, + " (protection %s %s %b %d %s %b %s)\n", + sk->protect.sha1chk? "sha1":"sum", + openpgp_cipher_algo_name (sk->protect.algo), + (int)sk->protect.ivlen, sk->protect.iv, + sk->protect.s2k.mode, + openpgp_md_algo_name (sk->protect.s2k.hash_algo), + (int)sizeof (sk->protect.s2k.salt), sk->protect.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\n" + " (csum %d)\n" + " %S)\n", + sk->version, + openpgp_pk_algo_name (sk->pubkey_algo), + skey, (int)(unsigned long)sk->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 *uid, *desc; + size_t uidlen; + u32 keyid[2]; + char *orig_codeset; + + keyid_from_sk (sk, keyid); + uid = get_user_id (keyid, &uidlen); + orig_codeset = i18n_switchto_utf8 (); + desc = xtryasprintf (_("Please enter the passphrase to import the" + " secret key for the OpenPGP certificate:\n" + "\"%.*s\"\n" \ + "%u-bit %s key, ID %s,\n" + "created %s.\n"), + (int)uidlen, uid, + nbits_from_sk (sk), + openpgp_pk_algo_name (sk->pubkey_algo), + (main_sk == sk + ? keystr_from_sk (sk) + : keystr_from_sk_with_sub (main_sk, sk)), + strtimestamp (sk->timestamp)); + i18n_switchback (orig_codeset); + xfree (uid); + if (desc) + { + uid = percent_plus_escape (desc); + xfree (desc); + desc = uid; + } + err = agent_import_key (ctrl, desc, wrappedkey, wrappedkeylen); + xfree (desc); + } + if (!err) + { + if (opt.verbose) + log_info (_("key %s: secret key imported\n"), + keystr_from_sk_with_sub (main_sk, sk)); + /* stats->count++; */ + /* stats->secret_read++; */ + /* 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_sk_with_sub (main_sk, sk)); + err = 0; + /* stats->count++; */ + /* stats->secret_read++; */ + /* stats->secret_dups++; */ + } + else + { + log_error (_("key %s: error sending to agent: %s\n"), + keystr_from_sk_with_sub (main_sk, sk), + gpg_strerror (err)); + if (sk->protect.algo == GCRY_CIPHER_IDEA + && gpg_err_code (err) == GPG_ERR_CIPHER_ALGO) + { + write_status (STATUS_RSA_OR_IDEA); + idea_cipher_warn (0); + } + if (gpg_err_code (err) == GPG_ERR_CANCELED) + break; /* Don't try the other subkeys. */ + } + } + + leave: + 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; - if(secnode->pkt->pkttype==PKT_SECRET_KEY || - secnode->pkt->pkttype==PKT_SECRET_SUBKEY) + 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. We only need to convert enough to write the keyblock out. */ + PACKET *pkt; + PKT_secret_key *sk; + PKT_public_key *pk; + int n, i; - PKT_secret_key *sk=secnode->pkt->pkt.secret_key; - PACKET *pkt=xmalloc_clear(sizeof(PACKET)); - PKT_public_key *pk=xmalloc_clear(sizeof(PKT_public_key)); - int n; + pkt = xcalloc (1, sizeof *pkt); + sk = secnode->pkt->pkt.secret_key; + pk = xcalloc (1, sizeof *pk); - if(secnode->pkt->pkttype==PKT_SECRET_KEY) - pkt->pkttype=PKT_PUBLIC_KEY; + if (secnode->pkt->pkttype == PKT_SECRET_KEY) + pkt->pkttype = PKT_PUBLIC_KEY; else - pkt->pkttype=PKT_PUBLIC_SUBKEY; + pkt->pkttype = PKT_PUBLIC_SUBKEY; - pkt->pkt.public_key=pk; + pkt->pkt.public_key = pk; - pk->version=sk->version; - pk->timestamp=sk->timestamp; - pk->expiredate=sk->expiredate; - pk->pubkey_algo=sk->pubkey_algo; + pk->version = sk->version; + pk->timestamp = sk->timestamp; + pk->expiredate = sk->expiredate; + pk->pubkey_algo = sk->pubkey_algo; - n=pubkey_get_npkey(pk->pubkey_algo); - if(n==0) + n = pubkey_get_npkey (pk->pubkey_algo); + if (!n) { - /* we can't properly extract the pubkey without knowing + /* We can't properly extract the pubkey without knowing the number of MPIs */ - release_kbnode(pub_keyblock); + release_kbnode (pub_keyblock); return NULL; } - else - { - int i; - - for(i=0;i<n;i++) - pk->pkey[i]=mpi_copy(sk->skey[i]); - } - pubnode=new_kbnode(pkt); + for (i=0; i < n; i++) + pk->pkey[i] = mpi_copy (sk->skey[i]); + pubnode = new_kbnode (pkt); } else { - pubnode=clone_kbnode(secnode); + pubnode = clone_kbnode (secnode); } - if(pub_keyblock==NULL) - pub_keyblock=pubnode; + if (!pub_keyblock) + pub_keyblock = pubnode; else - add_kbnode(pub_keyblock,pubnode); + add_kbnode (pub_keyblock, pubnode); } return pub_keyblock; @@ -1148,132 +1384,126 @@ sec_to_pub_keyblock(KBNODE sec_keyblock) * with the trust calculation. */ static int -import_secret_one( const char *fname, KBNODE keyblock, +import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, struct stats_s *stats, unsigned int options) { - PKT_secret_key *sk; - KBNODE node, uidnode; - u32 keyid[2]; - int rc = 0; - - /* get the key and print some info about it */ - node = find_kbnode( keyblock, PKT_SECRET_KEY ); - if( !node ) - BUG(); - - sk = node->pkt->pkt.secret_key; - keyid_from_sk( sk, keyid ); - uidnode = find_next_kbnode( keyblock, PKT_USER_ID ); - - if( opt.verbose ) - { - log_info( "sec %4u%c/%s %s ", - nbits_from_sk( sk ), - pubkey_letter( sk->pubkey_algo ), - keystr_from_sk(sk), datestr_from_sk(sk) ); - if( uidnode ) - print_utf8_buffer (es_stderr, uidnode->pkt->pkt.user_id->name, - uidnode->pkt->pkt.user_id->len ); - log_printf ("\n"); - } - stats->secret_read++; - - if( !uidnode ) - { - log_error( _("key %s: no user ID\n"), keystr_from_sk(sk)); - return 0; - } - - if(sk->protect.algo>110) - { - log_error(_("key %s: secret key with invalid cipher %d" - " - skipped\n"),keystr_from_sk(sk),sk->protect.algo); - return 0; - } + PKT_secret_key *sk; + KBNODE node, uidnode; + u32 keyid[2]; + int have_seckey; + int rc = 0; + + /* Get the key and print some info about it */ + node = find_kbnode (keyblock, PKT_SECRET_KEY); + if (!node) + BUG (); + + sk = node->pkt->pkt.secret_key; + keyid_from_sk (sk, keyid); + uidnode = find_next_kbnode (keyblock, PKT_USER_ID); + + if (opt.verbose) + { + log_info ("sec %4u%c/%s %s ", + nbits_from_sk (sk), + pubkey_letter (sk->pubkey_algo), + keystr_from_sk (sk), datestr_from_sk (sk)); + if (uidnode) + print_utf8_buffer (es_stderr, uidnode->pkt->pkt.user_id->name, + uidnode->pkt->pkt.user_id->len); + log_printf ("\n"); + } + stats->secret_read++; + + if (!uidnode) + { + log_error( _("key %s: no user ID\n"), keystr_from_sk(sk)); + return 0; + } + + /* A quick check to not import keys with an invalid protection + cipher algorithm (only checks the primary key, though). */ + if (sk->protect.algo > 110) + { + log_error (_("key %s: secret key with invalid cipher %d" + " - skipped\n"),keystr_from_sk(sk),sk->protect.algo); + return 0; + } #ifdef ENABLE_SELINUX_HACKS - if (1) - { - /* We don't allow to import secret keys because that may be used - to put a secret key into the keyring and the user might later - be tricked into signing stuff with that key. */ - log_error (_("importing secret keys not allowed\n")); - return 0; - } + if (1) + { + /* We don't allow to import secret keys because that may be used + to put a secret key into the keyring and the user might later + be tricked into signing stuff with that key. */ + log_error (_("importing secret keys not allowed\n")); + return 0; + } #endif - clear_kbnode_flags( keyblock ); - - /* do we have this key already in one of our secrings ? */ - rc = -1 /* fixme seckey_available( keyid ) is not anymore - available and has been replaced by - have_secret_key_with_kid. We need to rework the entire - secret key import code. The solution I am currently - thinking about is to move that code into a helper - program. */; - if( rc == G10ERR_NO_SECKEY && !(opt.import_options&IMPORT_MERGE_ONLY) ) - { - /* simply insert this key */ - KEYDB_HANDLE hd = keydb_new (); /* FIXME*/ - - /* get default resource */ - rc = keydb_locate_writable (hd, NULL); - if (rc) { - log_error (_("no default secret keyring: %s\n"), g10_errstr (rc)); - keydb_release (hd); - return G10ERR_GENERAL; - } - rc = keydb_insert_keyblock (hd, keyblock ); - if (rc) - log_error (_("error writing keyring `%s': %s\n"), - keydb_get_resource_name (hd), g10_errstr(rc) ); - keydb_release (hd); - /* we are ready */ - if( !opt.quiet ) - log_info( _("key %s: secret key imported\n"), keystr_from_sk(sk)); - stats->secret_imported++; - if (is_status_enabled ()) - print_import_ok (NULL, sk, 1|16); + clear_kbnode_flags( keyblock ); + + have_seckey = have_secret_key_with_kid (keyid); - if(options&IMPORT_SK2PK) - { - /* Try and make a public key out of this. */ + if (!have_seckey && !(opt.import_options&IMPORT_MERGE_ONLY) ) + { + /* We don't have this key, insert as a new key. */ + kbnode_t pub_keyblock; - KBNODE pub_keyblock=sec_to_pub_keyblock(keyblock); - if(pub_keyblock) - { - import_one(fname,pub_keyblock,stats, - NULL,NULL,opt.import_options,1); - release_kbnode(pub_keyblock); - } - } + stats->secret_imported++; + if (is_status_enabled ()) + print_import_ok (NULL, sk, 1|16); - /* Now that the key is definitely incorporated into the keydb, - if we have the public part of this key, we need to check if - the prefs are rational. */ - node=get_pubkeyblock(keyid); - if(node) - { - check_prefs(node); - release_kbnode(node); - } - } - else if( !rc ) - { /* we can't merge secret keys */ - log_error( _("key %s: already in secret keyring\n"), - keystr_from_sk(sk)); - stats->secret_dups++; - if (is_status_enabled ()) - print_import_ok (NULL, sk, 16); - - /* TODO: if we ever do merge secret keys, make sure to handle - the sec_to_pub_keyblock feature as well. */ - } - else - log_error( _("key %s: secret key not found: %s\n"), - keystr_from_sk(sk), g10_errstr(rc)); + /* Make a public key out of this. */ + pub_keyblock = sec_to_pub_keyblock (keyblock); + if (!pub_keyblock) + log_error ("oops: FIXME (bad error message)\n"); + else + { + import_one (fname, pub_keyblock, stats, + NULL, NULL, opt.import_options, 1); + /* Fixme: We should check for an invalid keyblock and + cancel the secret key import in this case. */ + release_kbnode (pub_keyblock); + + /* 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 ("oops: error getting public keyblock again\n"); + else + { + if (!transfer_secret_keys (ctrl, keyblock)) + { + if (!opt.quiet) + log_info (_("key %s: secret key imported\n"), + keystr_from_sk (sk)); + check_prefs (node); + } + release_kbnode (node); + } + } + } + else if (have_seckey) + { + /* We can't yet merge secret keys. - Well, with the new system + we can => FIXME */ + log_error( _("key %s: secret key part already available\n"), + keystr_from_sk(sk)); + stats->secret_dups++; + if (is_status_enabled ()) + print_import_ok (NULL, sk, 16); + + /* TODO: if we ever do merge secret keys, make sure to handle + the sec_to_pub_keyblock feature as well. */ + } + else + log_error( _("key %s: secret key not found: %s\n"), + keystr_from_sk(sk), g10_errstr(rc)); - return rc; + return rc; } diff --git a/g10/keydb.h b/g10/keydb.h index e860a9fbf..ca6b901c2 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -259,8 +259,13 @@ u32 v3_keyid (gcry_mpi_t a, u32 *ki); void hash_public_key( gcry_md_hd_t md, PKT_public_key *pk ); size_t keystrlen(void); const char *keystr(u32 *keyid); +const char *keystr_with_sub (u32 *main_kid, u32 *sub_kid); const char *keystr_from_pk(PKT_public_key *pk); +const char *keystr_from_pk_with_sub (PKT_public_key *main_pk, + PKT_public_key *sub_pk); const char *keystr_from_sk(PKT_secret_key *sk); +const char *keystr_from_sk_with_sub (PKT_secret_key *main_sk, + PKT_secret_key *sub_sk); const char *keystr_from_desc(KEYDB_SEARCH_DESC *desc); u32 keyid_from_sk( PKT_secret_key *sk, u32 *keyid ); u32 keyid_from_pk( PKT_public_key *pk, u32 *keyid ); diff --git a/g10/keyid.c b/g10/keyid.c index 8f2d8f7fd..a6e8b3728 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -35,6 +35,9 @@ #include "i18n.h" #include "rmd160.h" +#define KEYID_STR_SIZE 19 + + int pubkey_letter( int algo ) { @@ -204,35 +207,38 @@ keystrlen(void) } } + const char * -keystr(u32 *keyid) +keystr (u32 *keyid) { - static char keyid_str[19]; + static char keyid_str[KEYID_STR_SIZE]; - switch(opt.keyid_format) + switch (opt.keyid_format) { case KF_SHORT: - sprintf(keyid_str,"%08lX",(ulong)keyid[1]); + snprintf (keyid_str, sizeof keyid_str, "%08lX", (ulong)keyid[1]); break; case KF_LONG: - if(keyid[0]) - sprintf(keyid_str,"%08lX%08lX",(ulong)keyid[0],(ulong)keyid[1]); + if (keyid[0]) + snprintf (keyid_str, sizeof keyid_str, "%08lX%08lX", + (ulong)keyid[0], (ulong)keyid[1]); else - sprintf(keyid_str,"%08lX",(ulong)keyid[1]); + snprintf (keyid_str, sizeof keyid_str, "%08lX", (ulong)keyid[1]); break; case KF_0xSHORT: - sprintf(keyid_str,"0x%08lX",(ulong)keyid[1]); + snprintf (keyid_str, sizeof keyid_str, "0x%08lX", (ulong)keyid[1]); break; case KF_0xLONG: if(keyid[0]) - sprintf(keyid_str,"0x%08lX%08lX",(ulong)keyid[0],(ulong)keyid[1]); + snprintf (keyid_str, sizeof keyid_str, "0x%08lX%08lX", + (ulong)keyid[0],(ulong)keyid[1]); else - sprintf(keyid_str,"0x%08lX",(ulong)keyid[1]); + snprintf (keyid_str, sizeof keyid_str, "0x%08lX", (ulong)keyid[1]); break; - + default: BUG(); } @@ -240,6 +246,21 @@ keystr(u32 *keyid) return keyid_str; } + +const char * +keystr_with_sub (u32 *main_kid, u32 *sub_kid) +{ + static char buffer[KEYID_STR_SIZE+1+KEYID_STR_SIZE]; + char *p; + + mem2str (buffer, keystr (main_kid), KEYID_STR_SIZE); + p = buffer + strlen (buffer); + *p++ = '/'; + mem2str (p, keystr (sub_kid), KEYID_STR_SIZE); + return buffer; +} + + const char * keystr_from_pk(PKT_public_key *pk) { @@ -248,14 +269,36 @@ keystr_from_pk(PKT_public_key *pk) return keystr(pk->keyid); } + +const char * +keystr_from_pk_with_sub (PKT_public_key *main_pk, PKT_public_key *sub_pk) +{ + keyid_from_pk (main_pk, NULL); + keyid_from_pk (sub_pk, NULL); + + return keystr_with_sub (main_pk->keyid, sub_pk->keyid); +} + + const char * keystr_from_sk(PKT_secret_key *sk) { - keyid_from_sk(sk,NULL); + keyid_from_sk (sk,NULL); return keystr(sk->keyid); } + +const char * +keystr_from_sk_with_sub (PKT_secret_key *main_sk, PKT_secret_key *sub_sk) +{ + keyid_from_sk (main_sk, NULL); + keyid_from_sk (sub_sk, NULL); + + return keystr_with_sub (main_sk->keyid, sub_sk->keyid); +} + + const char * keystr_from_desc(KEYDB_SEARCH_DESC *desc) { diff --git a/g10/keyserver.c b/g10/keyserver.c index 27f52719d..39c3d69d9 100644 --- a/g10/keyserver.c +++ b/g10/keyserver.c @@ -1494,9 +1494,10 @@ keyserver_spawn(enum ks_action action,strlist_t list,KEYDB_SEARCH_DESC *desc, 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. */ - - import_keys_stream(spawn->fromchild,stats_handle,fpr,fpr_len, - opt.keyserver_options.import_options); + + /* FIXME: Pass CTRL. */ + import_keys_stream (NULL, spawn->fromchild,stats_handle,fpr,fpr_len, + opt.keyserver_options.import_options); import_print_stats(stats_handle); import_release_stats_handle(stats_handle); @@ -2037,8 +2038,9 @@ keyserver_import_cert(const char *name,unsigned char **fpr,size_t *fpr_len) /* CERTs are always in binary format */ opt.no_armor=1; - rc=import_keys_stream(key,NULL,fpr,fpr_len, - opt.keyserver_options.import_options); + /* FIXME: Pass CTRL. */ + rc = import_keys_stream (NULL, key, NULL, fpr, fpr_len, + opt.keyserver_options.import_options); opt.no_armor=armor_status; diff --git a/g10/main.h b/g10/main.h index 83723edc1..d5b8702f2 100644 --- a/g10/main.h +++ b/g10/main.h @@ -96,7 +96,9 @@ const char *openpgp_cipher_algo_name (int algo); int openpgp_pk_test_algo( int algo ); int openpgp_pk_test_algo2 ( int algo, unsigned int use ); int openpgp_pk_algo_usage ( int algo ); +const char *openpgp_pk_algo_name (int algo); int openpgp_md_test_algo( int algo ); +const char *openpgp_md_algo_name (int algo); #ifdef USE_IDEA void idea_cipher_warn( int show ); @@ -263,10 +265,11 @@ gcry_mpi_t encode_md_value (PKT_public_key *pk, /*-- import.c --*/ int parse_import_options(char *str,unsigned int *options,int noisy); -void import_keys( char **fnames, int nnames, - void *stats_hd, unsigned int options ); -int import_keys_stream( iobuf_t inp,void *stats_hd,unsigned char **fpr, - size_t *fpr_len,unsigned int options ); +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, + unsigned char **fpr, + size_t *fpr_len, unsigned int options); void *import_new_stats_handle (void); void import_release_stats_handle (void *p); void import_print_stats (void *hd); diff --git a/g10/misc.c b/g10/misc.c index eb3eceee9..91d1c310a 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -478,6 +478,28 @@ openpgp_pk_algo_usage ( int algo ) return use; } +/* Map the OpenPGP pubkey algorithm whose ID is contained in ALGO to a + string representation of the algorithm name. For unknown algorithm + IDs this function returns "?". */ +const char * +openpgp_pk_algo_name (int algo) +{ + switch (algo) + { + case PUBKEY_ALGO_RSA: + case PUBKEY_ALGO_RSA_E: + case PUBKEY_ALGO_RSA_S: return "rsa"; + + case PUBKEY_ALGO_ELGAMAL: + case PUBKEY_ALGO_ELGAMAL_E: return "elg"; + + case PUBKEY_ALGO_DSA: return "dsa"; + + default: return "?"; + } +} + + int openpgp_md_test_algo( int algo ) { @@ -491,6 +513,19 @@ openpgp_md_test_algo( int algo ) return gcry_md_test_algo (algo); } + +/* Map the OpenPGP digest algorithm whose ID is contained in ALGO to a + string representation of the algorithm name. For unknown algorithm + IDs this function returns "?". */ +const char * +openpgp_md_algo_name (int algo) +{ + if (algo < 0 || algo > 110) + return "?"; + return gcry_md_algo_name (algo); +} + + #ifdef USE_IDEA /* Special warning for the IDEA cipher */ void diff --git a/g10/options.h b/g10/options.h index 221d04021..cee248f25 100644 --- a/g10/options.h +++ b/g10/options.h @@ -320,7 +320,6 @@ EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; #define IMPORT_LOCAL_SIGS (1<<0) #define IMPORT_REPAIR_PKS_SUBKEY_BUG (1<<1) #define IMPORT_FAST (1<<2) -#define IMPORT_SK2PK (1<<3) #define IMPORT_MERGE_ONLY (1<<4) #define IMPORT_MINIMAL (1<<5) #define IMPORT_CLEAN (1<<6) diff --git a/g10/pkglue.c b/g10/pkglue.c index f3001f549..14a27535f 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -244,41 +244,3 @@ pk_decrypt (int algo, gcry_mpi_t * result, gcry_mpi_t * data, return 0; } - -/* Check whether SKEY is a suitable secret key. */ -int -REMOVE_ME_pk_check_secret_key (int algo, gcry_mpi_t *skey) -{ - gcry_sexp_t s_skey; - int rc; - - if (algo == GCRY_PK_DSA) - { - rc = gcry_sexp_build (&s_skey, NULL, - "(private-key(dsa(p%m)(q%m)(g%m)(y%m)(x%m)))", - skey[0], skey[1], skey[2], skey[3], skey[4]); - } - else if (algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E) - { - rc = gcry_sexp_build (&s_skey, NULL, - "(private-key(elg(p%m)(g%m)(y%m)(x%m)))", - skey[0], skey[1], skey[2], skey[3]); - } - else if (algo == GCRY_PK_RSA - || algo == GCRY_PK_RSA_S || algo == GCRY_PK_RSA_E) - { - rc = gcry_sexp_build (&s_skey, NULL, - "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", - skey[0], skey[1], skey[2], skey[3], skey[4], - skey[5]); - } - else - return GPG_ERR_PUBKEY_ALGO; - - if (!rc) - { - rc = gcry_pk_testkey (s_skey); - gcry_sexp_release (s_skey); - } - return rc; -} |