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.
This commit is contained in:
Werner Koch 2014-06-05 11:19:59 +02:00
parent 09a2d4ec74
commit 4f0625889b
7 changed files with 274 additions and 62 deletions

29
README
View File

@ -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.

View File

@ -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.

View File

@ -110,6 +110,7 @@ gpg2_SOURCES = gpg.c \
dearmor.c \
import.c \
export.c \
migrate.c \
delkey.c \
keygen.c \
helptext.c \

View File

@ -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:

View File

@ -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)

View File

@ -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*/

94
g10/migrate.c Normal file
View File

@ -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);
}