diff options
author | Werner Koch <wk@gnupg.org> | 2014-06-05 11:19:59 +0200 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2014-06-05 11:19:59 +0200 |
commit | 4f0625889b768eabdec52696bf15059a9e8d9c02 (patch) | |
tree | f523eab5acb0666c12c067ba27a159e21f434aa7 | |
parent | gpgsm: Fix commit be07ed65. (diff) | |
download | gnupg2-4f0625889b768eabdec52696bf15059a9e8d9c02.tar.xz gnupg2-4f0625889b768eabdec52696bf15059a9e8d9c02.zip |
gpg: Auto-migrate existing secring.gpg.
* g10/migrate.c: New.
* g10/import.c (import_old_secring): New.
(import_one): Add arg silent.
(transfer_secret_keys): Add arg batch.
(import_secret_one): Add args batch and for_migration.
* g10/gpg.c (main): Call migration function.
-rw-r--r-- | README | 29 | ||||
-rw-r--r-- | doc/gpg.texi | 30 | ||||
-rw-r--r-- | g10/Makefile.am | 1 | ||||
-rw-r--r-- | g10/gpg.c | 37 | ||||
-rw-r--r-- | g10/import.c | 140 | ||||
-rw-r--r-- | g10/main.h | 5 | ||||
-rw-r--r-- | g10/migrate.c | 94 |
7 files changed, 274 insertions, 62 deletions
@@ -85,21 +85,10 @@ MIGRATION FROM 1.4 or 2.0 to 2.1 The major change in 2.1 is gpg-agent taking care of the OpenPGP secret keys (those managed by GPG). The former file "secring.gpg" will not be used anymore. Newly generated keys are stored in the agent's key -store directory "~/.gnupg/private-keys-v1.d/". - -To migrate your existing keys you need to run the command - - gpg2 --batch --import ~/.gnupg/secring.gpg - -Secret keys already imported are skipped by this command. It is -advisable to keep the secring.gpg for use with older versions of GPG. - -The use of "--batch" with "--import" is highly recommended. If you do -not use "--batch" the agent would ask for the passphrase of each key. -In this case you may use the Cancel button of the Pinentry to skip -importing this key. If you want to stop the enite import process and -you use a decent version of Pinentry, you should close the Pinentry -window instead of hitting the Cancel button. +store directory "~/.gnupg/private-keys-v1.d/". The first time gpg +needs a secret key it checks whether a "secring.gpg" exists and +copies them to the new store. The old secring.gpg is kept for use by +older versions of gpg. Note that gpg-agent now uses a fixed socket by default. All tools will start the gpg-agent as needed. In general there is no more need @@ -111,11 +100,11 @@ of the card related sub-commands of --edit-key are not yet fully supported. However, signing and decryption with a smartcard does work. -The Dirmngr is now part of GnuPG proper. Thus there is no more need -to install the separate dirmngr package. The directroy layout of -Dirmngr changed to make use of the GnuPG directories; for example you -use /etc/gnupg/trusted-certs and /var/lib/gnupg/extra-certs. Dirmngr -needs to be started as a system daemon. +The Dirmngr is now part of GnuPG proper and also used to access +OpenPGP keyservers. The directroy layout of Dirmngr changed to make +use of the GnuPG directories. Dirmngr is started by gpg or gpgsm as +needed needed. There is no more need to install a separate dirmngr +package. diff --git a/doc/gpg.texi b/doc/gpg.texi index 71a3107d8..c8fae3a1b 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -3042,18 +3042,33 @@ files; They all live in in the current home directory (@pxref{option @table @file - @item ~/.gnupg/secring.gpg - The secret keyring. You should backup this file. - - @item ~/.gnupg/secring.gpg.lock - The lock file for the secret keyring. - @item ~/.gnupg/pubring.gpg The public keyring. You should backup this file. @item ~/.gnupg/pubring.gpg.lock The lock file for the public keyring. +@ifset gpgtwoone + @item ~/.gnupg/pubring.kbx + The public keyring using a different format. This file is sharred + with @command{gpgsm}. You should backup this file. + + @item ~/.gnupg/pubring.kbx.lock + The lock file for @file{pubring.kbx}. +@end ifset + + @item ~/.gnupg/secring.gpg +@ifclear gpgtwoone + The secret keyring. You should backup this file. +@end ifclear +@ifset gpgtwoone + A secret keyring as used by GnuPG versions before 2.1. It is not + used by GnuPG 2.1 and later. + + @item ~/.gnupg/.gpg-v21-migrated + File indicating that a migration to GnuPG 2.1 has taken place. +@end ifset + @item ~/.gnupg/trustdb.gpg The trust database. There is no need to backup this file; it is better to backup the ownertrust values (@pxref{option --export-ownertrust}). @@ -3064,6 +3079,9 @@ files; They all live in in the current home directory (@pxref{option @item ~/.gnupg/random_seed A file used to preserve the state of the internal random pool. + @item ~/.gnupg/secring.gpg.lock + The lock file for the secret keyring. + @item /usr[/local]/share/gnupg/options.skel The skeleton options file. diff --git a/g10/Makefile.am b/g10/Makefile.am index ba686481c..0ae4ef7c7 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -110,6 +110,7 @@ gpg2_SOURCES = gpg.c \ dearmor.c \ import.c \ export.c \ + migrate.c \ delkey.c \ keygen.c \ helptext.c \ @@ -3594,6 +3594,43 @@ main (int argc, char **argv) break; } + + /* Check for certain command whether we need to migrate a + secring.gpg to the gpg-agent. */ + switch (cmd) + { + case aListSecretKeys: + case aSign: + case aSignEncr: + case aSignEncrSym: + case aSignSym: + case aClearsign: + case aDecrypt: + case aSignKey: + case aLSignKey: + case aEditKey: + case aPasswd: + case aDeleteSecretKeys: + case aDeleteSecretAndPublicKeys: + case aKeygen: + case aImport: + case aExportSecret: + case aExportSecretSub: + case aGenRevoke: + case aDesigRevoke: + case aCardEdit: + case aChangePIN: + migrate_secring (ctrl); + break; + case aListKeys: + if (opt.with_secret) + migrate_secring (ctrl); + break; + default: + break; + } + + /* The command dispatcher. */ switch( cmd ) { case aServer: diff --git a/g10/import.c b/g10/import.c index 2b219a28d..774a727da 100644 --- a/g10/import.c +++ b/g10/import.c @@ -1,6 +1,7 @@ /* import.c - import a key into our key storage. * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, * 2007, 2010, 2011 Free Software Foundation, Inc. + * Copyright (C) 2014 Werner Koch * * This file is part of GnuPG. * @@ -68,9 +69,10 @@ static void revocation_present (ctrl_t ctrl, kbnode_t keyblock); static int import_one (ctrl_t ctrl, const char *fname, KBNODE keyblock,struct stats_s *stats, unsigned char **fpr,size_t *fpr_len, - unsigned int options,int from_sk); + unsigned int options,int from_sk, int silent); static int import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, - struct stats_s *stats, unsigned int options); + struct stats_s *stats, int batch, + unsigned int options, int for_migration); static int import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats); static int chk_self_sigs( const char *fname, KBNODE keyblock, @@ -227,6 +229,7 @@ import_keys_internal (ctrl_t ctrl, iobuf_t inp, char **fnames, int nnames, return rc; } + void import_keys (ctrl_t ctrl, char **fnames, int nnames, void *stats_handle, unsigned int options ) @@ -293,9 +296,10 @@ import (ctrl_t ctrl, IOBUF inp, const char* fname,struct stats_s *stats, while( !(rc = read_block( inp, &pending_pkt, &keyblock) )) { if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY ) rc = import_one (ctrl, fname, keyblock, - stats, fpr, fpr_len, options, 0); + stats, fpr, fpr_len, options, 0, 0); else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) - rc = import_secret_one (ctrl, fname, keyblock, stats, options); + rc = import_secret_one (ctrl, fname, keyblock, stats, + opt.batch, options, 0); else if( keyblock->pkt->pkttype == PKT_SIGNATURE && keyblock->pkt->pkt.signature->sig_class == 0x20 ) rc = import_revoke_cert( fname, keyblock, stats ); @@ -320,6 +324,57 @@ import (ctrl_t ctrl, IOBUF inp, const char* fname,struct stats_s *stats, } +/* 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; + + 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))) + { + if (keyblock->pkt->pkttype == PKT_SECRET_KEY) + err = import_secret_one (ctrl, fname, keyblock, stats, 1, 0, 1); + release_kbnode (keyblock); + if (err) + break; + } + import_release_stats_handle (stats); + if (err == -1) + err = 0; + else if (err && gpg_err_code (err) != G10ERR_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) { @@ -771,16 +826,17 @@ check_prefs (ctrl_t ctrl, kbnode_t keyblock) } /**************** - * Try to import one keyblock. Return an error only in serious cases, but - * never for an invalid keyblock. It uses log_error to increase the - * internal errorcount, so that invalid input can be detected by programs - * which called gpg. + * Try to import one keyblock. Return an error only in serious cases, + * but never for an invalid keyblock. It uses log_error to increase + * the internal errorcount, so that invalid input can be detected by + * programs which called gpg. If SILENT is no messages are printed - + * even most error messages are suppressed. */ static int import_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, struct stats_s *stats, unsigned char **fpr,size_t *fpr_len,unsigned int options, - int from_sk ) + int from_sk, int silent) { PKT_public_key *pk; PKT_public_key *pk_orig; @@ -804,7 +860,7 @@ import_one (ctrl_t ctrl, keyid_from_pk( pk, keyid ); uidnode = find_next_kbnode( keyblock, PKT_USER_ID ); - if( opt.verbose && !opt.interactive ) + if (opt.verbose && !opt.interactive && !silent) { log_info( "pub %s/%s %s ", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), @@ -819,11 +875,12 @@ import_one (ctrl_t ctrl, if( !uidnode ) { - log_error( _("key %s: no user ID\n"), keystr_from_pk(pk)); + if (!silent) + log_error( _("key %s: no user ID\n"), keystr_from_pk(pk)); return 0; } - if (opt.interactive) { + if (opt.interactive && !silent) { if(is_status_enabled()) print_import_check (pk, uidnode->pkt->pkt.user_id); merge_keys_and_selfsig (keyblock); @@ -856,7 +913,7 @@ import_one (ctrl_t ctrl, return rc== -1? 0:rc; /* If we allow such a thing, mark unsigned uids as valid */ - if( opt.allow_non_selfsigned_uid ) + if( opt.allow_non_selfsigned_uid) for( node=keyblock; node; node = node->next ) if( node->pkt->pkttype == PKT_USER_ID && !(node->flag & 1) ) { @@ -869,9 +926,11 @@ import_one (ctrl_t ctrl, } if( !delete_inv_parts( fname, keyblock, keyid, options ) ) { - log_error( _("key %s: no valid user IDs\n"), keystr_from_pk(pk)); - if( !opt.quiet ) - log_info(_("this may be caused by a missing self-signature\n")); + 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; } @@ -881,12 +940,13 @@ import_one (ctrl_t ctrl, rc = get_pubkey_fast ( pk_orig, keyid ); if( rc && rc != G10ERR_NO_PUBKEY && rc != G10ERR_UNU_PUBKEY ) { - log_error( _("key %s: public key not found: %s\n"), - keystr(keyid), g10_errstr(rc)); + if (!silent) + log_error (_("key %s: public key not found: %s\n"), + keystr(keyid), g10_errstr(rc)); } else if ( rc && (opt.import_options&IMPORT_MERGE_ONLY) ) { - if( opt.verbose ) + if( opt.verbose && !silent ) log_info( _("key %s: new key - skipped\n"), keystr(keyid)); rc = 0; stats->skipped_new_keys++; @@ -896,7 +956,7 @@ import_one (ctrl_t ctrl, rc = keydb_locate_writable (hd, NULL); if (rc) { - log_error (_("no writable keyring found: %s\n"), g10_errstr (rc)); + log_error (_("no writable keyring found: %s\n"), g10_errstr (rc)); keydb_release (hd); return G10ERR_GENERAL; } @@ -921,7 +981,7 @@ import_one (ctrl_t ctrl, keydb_release (hd); /* we are ready */ - if( !opt.quiet ) + if( !opt.quiet && !silent) { char *p=get_user_id_native (keyid); log_info( _("key %s: public key \"%s\" imported\n"), @@ -948,7 +1008,8 @@ import_one (ctrl_t ctrl, * weird is going on */ if( cmp_public_keys( pk_orig, pk ) ) { - log_error( _("key %s: doesn't match our copy\n"),keystr(keyid)); + if (!silent) + log_error( _("key %s: doesn't match our copy\n"),keystr(keyid)); goto leave; } @@ -1011,7 +1072,7 @@ import_one (ctrl_t ctrl, revalidation_mark (); /* we are ready */ - if( !opt.quiet ) + if( !opt.quiet && !silent) { char *p=get_user_id_native(keyid); if( n_uids == 1 ) @@ -1053,7 +1114,7 @@ import_one (ctrl_t ctrl, stats->n_sigs_cleaned +=n_sigs_cleaned; stats->n_uids_cleaned +=n_uids_cleaned; - if (is_status_enabled ()) + if (is_status_enabled () && !silent) print_import_ok (pk, ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0))); } else @@ -1062,7 +1123,7 @@ import_one (ctrl_t ctrl, if (is_status_enabled ()) print_import_ok (pk, 0); - if( !opt.quiet ) + if( !opt.quiet && !silent) { char *p=get_user_id_native(keyid); log_info( _("key %s: \"%s\" not changed\n"),keystr(keyid),p); @@ -1129,9 +1190,12 @@ import_one (ctrl_t ctrl, /* Transfer all the secret keys in SEC_KEYBLOCK to the gpg-agent. The - function prints diagnostics and returns an error code. */ + 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 stats_s *stats, kbnode_t sec_keyblock, + int batch) { gpg_error_t err = 0; void *kek = NULL; @@ -1358,7 +1422,7 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock) { char *desc = gpg_format_keydesc (pk, FORMAT_KEYDESC_IMPORT, 1); err = agent_import_key (ctrl, desc, &cache_nonce, - wrappedkey, wrappedkeylen, opt.batch); + wrappedkey, wrappedkeylen, batch); xfree (desc); } if (!err) @@ -1454,7 +1518,8 @@ sec_to_pub_keyblock (kbnode_t sec_keyblock) */ static int import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, - struct stats_s *stats, unsigned int options) + struct stats_s *stats, int batch, unsigned int options, + int for_migration) { PKT_public_key *pk; struct seckey_info *ski; @@ -1475,7 +1540,7 @@ import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, keyid_from_pk (pk, keyid); uidnode = find_next_kbnode (keyblock, PKT_USER_ID); - if (opt.verbose) + if (opt.verbose && !for_migration) { log_info ("sec %s/%s %s ", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), @@ -1489,13 +1554,15 @@ import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, if ((options & IMPORT_NO_SECKEY)) { - log_error (_("importing secret keys not allowed\n")); + if (!for_migration) + log_error (_("importing secret keys not allowed\n")); return 0; } if (!uidnode) { - log_error( _("key %s: no user ID\n"), keystr_from_pk (pk)); + if (!for_migration) + log_error( _("key %s: no user ID\n"), keystr_from_pk (pk)); return 0; } @@ -1511,8 +1578,9 @@ import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, cipher algorithm (only checks the primary key, though). */ if (ski->algo > 110) { - log_error (_("key %s: secret key with invalid cipher %d" - " - skipped\n"), keystr_from_pk (pk), ski->algo); + if (!for_migration) + log_error (_("key %s: secret key with invalid cipher %d" + " - skipped\n"), keystr_from_pk (pk), ski->algo); return 0; } @@ -1542,7 +1610,7 @@ import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, 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); + NULL, NULL, options, 1, for_migration); /* Fixme: We should check for an invalid keyblock and cancel the secret key import in this case. */ @@ -1564,7 +1632,7 @@ import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, else { nr_prev = stats->secret_imported; - if (!transfer_secret_keys (ctrl, stats, keyblock)) + if (!transfer_secret_keys (ctrl, stats, keyblock, batch)) { int status = 16; if (!opt.quiet) diff --git a/g10/main.h b/g10/main.h index 2802cb5c3..97c661239 100644 --- a/g10/main.h +++ b/g10/main.h @@ -290,6 +290,7 @@ int import_keys_stream (ctrl_t ctrl, iobuf_t inp, void *stats_hd, int import_keys_es_stream (ctrl_t ctrl, estream_t fp, void *stats_handle, unsigned char **fpr, size_t *fpr_len, unsigned int options); +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); @@ -379,4 +380,8 @@ int card_store_subkey (KBNODE node, int use); #define S2K_DECODE_COUNT(_val) ((16ul + ((_val) & 15)) << (((_val) >> 4) + 6)) +/*-- migrate.c --*/ +void migrate_secring (ctrl_t ctrl); + + #endif /*G10_MAIN_H*/ diff --git a/g10/migrate.c b/g10/migrate.c new file mode 100644 index 000000000..9a21cfe8e --- /dev/null +++ b/g10/migrate.c @@ -0,0 +1,94 @@ +/* migrate.c - Migrate from earlier GnupG versions. + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#include "gpg.h" +#include "options.h" +#include "keydb.h" +#include "util.h" +#include "main.h" + + +#ifdef HAVE_DOSISH_SYSTEM +# define V21_MIGRATION_FNAME "gpg-v21-migrated" +#else +# define V21_MIGRATION_FNAME ".gpg-v21-migrated" +#endif + + +/* Check whether a default secring.gpg from GnuPG < 2.1 exists and + import it if not yet done. */ +void +migrate_secring (ctrl_t ctrl) +{ + dotlock_t lockhd = NULL; + char *secring = NULL; + char *flagfile = NULL; + + secring = make_filename (opt.homedir, "secring" EXTSEP_S "gpg", NULL); + if (access (secring, F_OK)) + goto leave; /* Does not exist or is not readable. */ + flagfile = make_filename (opt.homedir, V21_MIGRATION_FNAME, NULL); + if (!access (flagfile, F_OK)) + goto leave; /* Does exist - fine. */ + + log_info ("starting migration from earlier GnuPG versions\n"); + + lockhd = dotlock_create (flagfile, 0); + if (!lockhd) + { + log_error ("can't allocate lock for '%s': %s\n", + flagfile, gpg_strerror (gpg_error_from_syserror ())); + goto leave; + } + if (dotlock_take (lockhd, -1)) + { + log_error ("can't lock '%s': %s\n", + flagfile, gpg_strerror (gpg_error_from_syserror ())); + dotlock_destroy (lockhd); + lockhd = NULL; + goto leave; + } + + log_info ("porting secret keys from '%s' to gpg-agent\n", secring); + if (!import_old_secring (ctrl, secring)) + { + FILE *fp = fopen (flagfile, "w"); + if (!fp || fclose (fp)) + log_error ("error creating flag file '%s': %s\n", + flagfile, gpg_strerror (gpg_error_from_syserror ())); + else + log_info ("migration succeeded\n"); + } + + leave: + if (lockhd) + { + dotlock_release (lockhd); + dotlock_destroy (lockhd); + } + xfree (flagfile); + xfree (secring); +} |