gpg: Auto-migrate existing secring.gpg.
authorWerner Koch <wk@gnupg.org>
Thu, 5 Jun 2014 09:19:59 +0000 (11:19 +0200)
committerWerner Koch <wk@gnupg.org>
Thu, 5 Jun 2014 09:19:59 +0000 (11:19 +0200)
* 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.

README
doc/gpg.texi
g10/Makefile.am
g10/gpg.c
g10/import.c
g10/main.h
g10/migrate.c [new file with mode: 0644]

diff --git a/README b/README
index c64a14e..fd20d40 100644 (file)
--- a/README
+++ b/README
@@ -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.
 
 
 
index 71a3107..c8fae3a 100644 (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.
 
index ba68648..0ae4ef7 100644 (file)
@@ -110,6 +110,7 @@ gpg2_SOURCES  = gpg.c               \
              dearmor.c         \
              import.c          \
              export.c          \
+             migrate.c         \
              delkey.c          \
              keygen.c          \
              helptext.c        \
index bd4ca40..47cc851 100644 (file)
--- a/g10/gpg.c
+++ b/g10/gpg.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:
index 2b219a2..774a727 100644 (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)
index 2802cb5..97c6612 100644 (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*/
diff --git a/g10/migrate.c b/g10/migrate.c
new file mode 100644 (file)
index 0000000..9a21cfe
--- /dev/null
@@ -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);
+}